root a1167df4f1 feat: User-Verwaltung mit Login, Sessions und Owner-Filter
Backend:
- User-Modell (sqlmodel) mit bcrypt-Passwort-Hash und is_admin-Flag
- Job.owner_id (FK auf user.id), per Migration nachgerüstet (SQLite ALTER)
- Starlette SessionMiddleware mit signiertem Cookie (SESSION_SECRET)
- /api/auth: login / logout / me / needs-setup / setup (Initial-Admin)
- /api/admin: User-CRUD + Passwort-Reset (Admin-only); letzter Admin /
  eigener Account vor Löschung geschützt
- /api/jobs: Owner-Filter (Admin sieht alle inkl. owner_username),
  404 statt 403 bei fremden Jobs (kein Existenz-Leak)

Frontend:
- Auth-Overlay mit Login bzw. Erst-Setup-Modus (auto-Detection)
- Header zeigt User + Logout, Admin-Badge bei Admin-Konten
- Admin-Karte: User anlegen, Passwort zurücksetzen, Löschen (mit Confirm)
- 401 → Login-Overlay (auch bei XHR-Upload)

Deploy:
- install.sh generiert SESSION_SECRET einmalig per python-secrets
- .env wird auf chmod 600 gesetzt
- Erstaufruf zeigt automatisch das Setup-Formular; bestehende Jobs werden
  dem ersten Admin zugeordnet
2026-05-14 02:38:52 +00:00

35 lines
1.1 KiB
Python

from pathlib import Path
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore")
app_name: str = "Voice-Agent"
app_host: str = "0.0.0.0"
app_port: int = 8000
upload_dir: Path = Path("/var/lib/voice-agent/uploads")
result_dir: Path = Path("/var/lib/voice-agent/results")
db_url: str = "sqlite:////var/lib/voice-agent/voice-agent.db"
mac_api_url: str = "http://192.168.85.10:8080"
mac_api_timeout: int = 1800
max_upload_mb: int = 500
allowed_exts: str = "mp3,wav,m4a,mp4,ogg,flac"
# Wird beim Deploy automatisch generiert (siehe install.sh).
session_secret: str = "CHANGE-ME-SET-IN-ENV"
session_cookie_name: str = "voice_agent_session"
session_max_age: int = 60 * 60 * 24 * 14 # 14 Tage
@property
def allowed_ext_set(self) -> set[str]:
return {e.strip().lower().lstrip(".") for e in self.allowed_exts.split(",") if e.strip()}
settings = Settings()
settings.upload_dir.mkdir(parents=True, exist_ok=True)
settings.result_dir.mkdir(parents=True, exist_ok=True)