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
35 lines
1.1 KiB
Python
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)
|