"""Configuration loaded from environment variables.""" import os from datetime import timedelta def _bool(val: str, default: bool = False) -> bool: if val is None: return default return val.strip().lower() in ("1", "true", "yes", "on") class Config: SECRET_KEY = os.environ.get("SECRET_KEY", "dev-only-replace-me") FLASK_ENV = os.environ.get("FLASK_ENV", "production") DATABASE_PATH = os.environ.get("DATABASE_PATH", "/data/alerthub.db") SQLALCHEMY_DATABASE_URI = f"sqlite:///{DATABASE_PATH}" SQLALCHEMY_TRACK_MODIFICATIONS = False SQLALCHEMY_ENGINE_OPTIONS = { "pool_pre_ping": True, # Wait (instead of erroring) when another process holds the SQLite # write lock — both gunicorn apps share one database file. "connect_args": {"timeout": 30}, } # Bootstrap admin (only used on first DB init) INITIAL_ADMIN_USERNAME = os.environ.get("INITIAL_ADMIN_USERNAME", "admin") INITIAL_ADMIN_EMAIL = os.environ.get("INITIAL_ADMIN_EMAIL", "admin@example.com") INITIAL_ADMIN_PASSWORD = os.environ.get("INITIAL_ADMIN_PASSWORD", "ChangeMeOnFirstLogin!") # Session security SESSION_COOKIE_SECURE = _bool(os.environ.get("SESSION_COOKIE_SECURE"), False) SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = "Lax" PERMANENT_SESSION_LIFETIME = timedelta( hours=int(os.environ.get("SESSION_LIFETIME_HOURS", "8")) ) PASSWORD_MIN_LENGTH = int(os.environ.get("PASSWORD_MIN_LENGTH", "10")) # MFA MFA_ISSUER_NAME = os.environ.get("MFA_ISSUER_NAME", "AlertHub") # Scheduler SCHEDULER_INTERVAL_MINUTES = int(os.environ.get("SCHEDULER_INTERVAL_MINUTES", "60")) # Default reminder lead times in days DEFAULT_REMINDER_DAYS = os.environ.get("DEFAULT_REMINDER_DAYS", "30,14,7,1") # SMTP defaults (seed only — admins edit live values via backoffice) # Purelymail outgoing: smtp.purelymail.com : 465 (SSL/TLS) SMTP_HOST = os.environ.get("SMTP_HOST", "smtp.purelymail.com") SMTP_PORT = int(os.environ.get("SMTP_PORT", "465")) SMTP_USERNAME = os.environ.get("SMTP_USERNAME", "") SMTP_PASSWORD = os.environ.get("SMTP_PASSWORD", "") SMTP_ENCRYPTION = os.environ.get("SMTP_ENCRYPTION", "ssl") SMTP_FROM_ADDRESS = os.environ.get("SMTP_FROM_ADDRESS", "") SMTP_FROM_NAME = os.environ.get("SMTP_FROM_NAME", "AlertHub") # IMAP defaults (seed only) — used to save a copy of sent mail to the # mailbox's Sent folder. Purelymail incoming: imap.purelymail.com : 993 (SSL/TLS) IMAP_HOST = os.environ.get("IMAP_HOST", "imap.purelymail.com") IMAP_PORT = int(os.environ.get("IMAP_PORT", "993")) IMAP_USERNAME = os.environ.get("IMAP_USERNAME", "") IMAP_PASSWORD = os.environ.get("IMAP_PASSWORD", "") IMAP_SENT_FOLDER = os.environ.get("IMAP_SENT_FOLDER", "Sent") # Ports (used by run scripts only) FRONTOFFICE_PORT = int(os.environ.get("FRONTOFFICE_PORT", "8080")) BACKOFFICE_PORT = int(os.environ.get("BACKOFFICE_PORT", "9001")) # Optional explicit public URLs for the cross-console links in the top bar. # If empty, the app derives them from the request host + the ports above # (e.g. http://:9001/). Set these when behind a reverse proxy # or when the consoles are reached at custom hostnames/paths. BACKOFFICE_PUBLIC_URL = os.environ.get("BACKOFFICE_PUBLIC_URL", "") FRONTOFFICE_PUBLIC_URL = os.environ.get("FRONTOFFICE_PUBLIC_URL", "") # Resource limits MAX_CONTENT_LENGTH = 2 * 1024 * 1024 # 2MB upload cap WTF_CSRF_TIME_LIMIT = 3600