"""Config-Klassen für die Projekt-Hub-App. Liest Werte aus Umgebungsvariablen (.env wird in run.py / wsgi.py via python-dotenv geladen). Keine Secrets im Code — alles über die Umgebung. """ import os def _as_bool(value: str | None, default: bool = False) -> bool: if value is None: return default return value.strip().lower() in {"1", "true", "yes", "on"} class Config: """Basis-Config, gemeinsam für alle Umgebungen.""" SECRET_KEY = os.environ.get("SECRET_KEY", "dev-insecure-change-me") # --- Datenbank --- # Default landet in der instance/-Datei (Pfad wird in der App-Factory gesetzt, # falls hier ein relativer sqlite-Name steht). SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL", "sqlite:///hub.sqlite") SQLALCHEMY_TRACK_MODIFICATIONS = False # --- Host / URL --- SERVER_NAME = os.environ.get("SERVER_NAME") or None PREFERRED_URL_SCHEME = "https" # --- Session-Cookie-Sicherheit --- SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = "Lax" # In Produktion über HTTPS erzwingen; in Dev (HTTP) abschaltbar. SESSION_COOKIE_SECURE = _as_bool(os.environ.get("SESSION_COOKIE_SECURE"), default=True) # --- Auth --- ALLOWED_EMAILS = [ e.strip().lower() for e in os.environ.get("ALLOWED_EMAILS", "").split(",") if e.strip() ] LOGIN_CODE_TTL_MINUTES = int(os.environ.get("LOGIN_CODE_TTL_MINUTES", "10")) LOGIN_CODE_RATE_LIMIT = os.environ.get("LOGIN_CODE_RATE_LIMIT", "5 per hour") # --- Rate-Limiting (Flask-Limiter) --- # Speicher-Backend. Default memory:// (pro Prozess, reicht für Dev/Einzelworker). # In Produktion mit mehreren gunicorn-Workern: gemeinsames Backend nötig, sonst # zählt jeder Worker eigene Limits. Empfehlung: redis://localhost:6379 RATELIMIT_STORAGE_URI = os.environ.get("RATELIMIT_STORAGE_URI", "memory://") # Restzeit-/Limit-Header in Antworten mitschicken. RATELIMIT_HEADERS_ENABLED = True # --- Mail --- MAIL_SERVER = os.environ.get("MAIL_SERVER", "127.0.0.1") MAIL_PORT = int(os.environ.get("MAIL_PORT", "1025")) MAIL_USE_TLS = _as_bool(os.environ.get("MAIL_USE_TLS")) MAIL_USE_SSL = _as_bool(os.environ.get("MAIL_USE_SSL")) MAIL_USERNAME = os.environ.get("MAIL_USERNAME") or None MAIL_PASSWORD = os.environ.get("MAIL_PASSWORD") or None MAIL_DEFAULT_SENDER = os.environ.get("MAIL_DEFAULT_SENDER", "hub@example.com") class DevelopmentConfig(Config): DEBUG = True SESSION_COOKIE_SECURE = _as_bool(os.environ.get("SESSION_COOKIE_SECURE"), default=False) class ProductionConfig(Config): DEBUG = False config_by_name = { "development": DevelopmentConfig, "production": ProductionConfig, } def get_config(name: str | None = None): """Wählt die Config-Klasse anhand von FLASK_ENV (Default: development).""" name = name or os.environ.get("FLASK_ENV", "development") return config_by_name.get(name, DevelopmentConfig)