import os from dataclasses import dataclass from decimal import Decimal from functools import lru_cache from pathlib import Path def _env_bool(value: str) -> bool: """Interpretiert einen Env-Wert als Wahrheitswert. Truthy sind (case- insensitiv) 1/true/yes/on; alles andere (inkl. leer) ist False.""" return value.strip().lower() in ("1", "true", "yes", "on") @dataclass(frozen=True) class Settings: database_url: str api_key: str gui_user: str gui_password_hash: str session_secret: str inbox_dir: Path uploads_dir: Path warn_threshold: Decimal horizon_days: int env_file: Path grafana_url: str grafana_public_url: str session_cookie_secure: bool @lru_cache def get_settings() -> Settings: e = os.environ.get return Settings( database_url=e("FB_DATABASE_URL", "sqlite:///./fb.sqlite"), api_key=e("FB_API_KEY", ""), gui_user=e("FB_GUI_USER", "admin"), gui_password_hash=e("FB_GUI_PASSWORD_HASH", ""), session_secret=e("FB_SESSION_SECRET", "dev-secret"), inbox_dir=Path(e("FB_INBOX_DIR", "./inbox")), uploads_dir=Path(e("FB_UPLOADS_DIR", "./uploads")), warn_threshold=Decimal(e("FB_WARN_THRESHOLD", "0")), horizon_days=int(e("FB_HORIZON_DAYS", "548")), # Bind-gemountete .env (siehe create_pod_finance.sh): Laufzeit-Lesen # der GUI-Passwort-Hashes fuer die Admin-Passwortaenderung (Ausbaustufe # 4 Task 2). Default passt zum Container-Mountpunkt "/data/.env". env_file=Path(e("FB_ENV_FILE", "/data/.env")), grafana_url=e("FB_GRAFANA_URL", "http://localhost:3000"), # Öffentliche Grafana-Basis-URL für Browser-Links/iframes hinter einem # Reverse Proxy (z.B. 'https://fb.example.de/grafana/'). Leer = # Direktbetrieb, Templates fallen auf http://:8097 zurück. NICHT # zu verwechseln mit grafana_url (intern, Server→Grafana). grafana_public_url=e("FB_GRAFANA_PUBLIC_URL", ""), # Session-Cookie mit secure-Flag ausliefern (nur über HTTPS gültig). # Für den Reverse-Proxy-Betrieb; Default False für lokalen HTTP-Zugriff. session_cookie_secure=_env_bool(e("FB_SESSION_COOKIE_SECURE", "")), )