"""Audiofy-Web – Konfiguration aus der .env. Pfad über die Umgebungsvariable AUDIOFY_ENV, Standard /opt/audiofy/.env. Geheimnisse stehen nur dort (Rechte 600), nie im Code oder in Logs. """ import os from dataclasses import dataclass from functools import lru_cache from pathlib import Path def env_lesen(pfad: Path) -> dict: """Einfacher KEY=VALUE-Parser (Kommentare und Anführungszeichen erlaubt).""" werte = {} for zeile in pfad.read_text().splitlines(): zeile = zeile.strip() if not zeile or zeile.startswith("#") or "=" not in zeile: continue key, _, wert = zeile.partition("=") werte[key.strip()] = wert.strip().strip('"').strip("'") return werte @dataclass(frozen=True) class Konfig: env_pfad: Path db_pfad: Path player_api_token: str spotify_client_id: str spotify_client_secret: str spotify_refresh_token: str admin_passwort_hash: str session_secret: str playlist_id: str # nur Startwert – danach gilt die Einstellung in der Datenbank cookie_secure: bool = True # nur für lokale Tests über http auf 0 setzen public_url: str = "" # z. B. https://musik.example.org (für Einladungslink/QR-Code) turnstile_site_key: str = "" # Cloudflare Turnstile (öffentlich) turnstile_secret: str = "" # Cloudflare Turnstile (geheim) def env_wert_setzen(pfad: Path, key: str, wert: str) -> None: """Setzt KEY=wert in der .env – direkt in der Datei (der Ordner ist für den Dienst nicht beschreibbar).""" zeilen = pfad.read_text().splitlines() if pfad.is_file() else [] neu = [z for z in zeilen if not z.strip().startswith(f"{key}=")] neu.append(f"{key}={wert}") fd = os.open(pfad, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) with os.fdopen(fd, "w") as f: f.write("\n".join(neu) + "\n") @lru_cache def konfig() -> Konfig: pfad = Path(os.environ.get("AUDIOFY_ENV", "/opt/audiofy/.env")) werte = env_lesen(pfad) if pfad.is_file() else {} return Konfig( env_pfad=pfad, db_pfad=Path(werte.get("DB_PATH") or "/opt/audiofy/data/audiofy.db"), player_api_token=werte.get("PLAYER_API_TOKEN") or werte.get("PI_API_TOKEN", ""), # alter Name spotify_client_id=werte.get("SPOTIFY_CLIENT_ID", ""), spotify_client_secret=werte.get("SPOTIFY_CLIENT_SECRET", ""), spotify_refresh_token=werte.get("SPOTIFY_REFRESH_TOKEN", ""), admin_passwort_hash=werte.get("ADMIN_PASSWORD_HASH", ""), session_secret=werte.get("SESSION_SECRET", ""), playlist_id=werte.get("SPOTIFY_PLAYLIST_ID", ""), cookie_secure=werte.get("COOKIE_SECURE", "1") != "0", public_url=werte.get("PUBLIC_URL", "").rstrip("/"), turnstile_site_key=werte.get("TURNSTILE_SITE_KEY", ""), turnstile_secret=werte.get("TURNSTILE_SECRET_KEY", ""), )