"""Configuration management for Sneaky Klaus application. This module defines configuration classes for different environments (development, production, testing) with environment variable support. """ import os from datetime import timedelta from pathlib import Path class Config: """Base configuration class with common settings.""" # Base paths BASE_DIR = Path(__file__).parent.parent DATA_DIR = BASE_DIR / "data" # Security SECRET_KEY = os.environ.get("SECRET_KEY") or "dev-secret-key-change-in-production" # Database SQLALCHEMY_DATABASE_URI = os.environ.get( "DATABASE_URL", f"sqlite:///{DATA_DIR / 'sneaky-klaus.db'}" ) SQLALCHEMY_TRACK_MODIFICATIONS = False # Session management SESSION_TYPE = "sqlalchemy" SESSION_PERMANENT = True SESSION_USE_SIGNER = True SESSION_KEY_PREFIX = "sk:" PERMANENT_SESSION_LIFETIME = timedelta(days=7) SESSION_COOKIE_SECURE = True # HTTPS only SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = "Lax" # Flask-WTF CSRF Protection WTF_CSRF_ENABLED = True WTF_CSRF_TIME_LIMIT = None # No time limit for CSRF tokens # Email service (Resend) RESEND_API_KEY = os.environ.get("RESEND_API_KEY") EMAIL_FROM = os.environ.get("EMAIL_FROM", "noreply@sneaky-klaus.com") # Application URLs APP_URL = os.environ.get("APP_URL", "http://localhost:5000") # Timezone TIMEZONE = os.environ.get("TZ", "UTC") # Password requirements MIN_PASSWORD_LENGTH = 12 class DevelopmentConfig(Config): """Development environment configuration.""" DEBUG = True TESTING = False SESSION_COOKIE_SECURE = False # Allow HTTP in development SQLALCHEMY_ECHO = True # Log SQL queries class ProductionConfig(Config): """Production environment configuration.""" DEBUG = False TESTING = False # Ensure critical environment variables are set @classmethod def validate(cls): """Validate that required production configuration is present.""" required_vars = ["SECRET_KEY", "RESEND_API_KEY", "APP_URL"] missing_vars = [var for var in required_vars if not os.environ.get(var)] if missing_vars: raise ValueError( f"Missing required environment variables: {', '.join(missing_vars)}" ) class TestConfig(Config): """Test environment configuration.""" TESTING = True DEBUG = True SESSION_COOKIE_SECURE = False # Use in-memory database for tests SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:" # Disable CSRF for easier testing WTF_CSRF_ENABLED = False # Use a predictable secret key for tests SECRET_KEY = "test-secret-key" # Configuration dictionary for easy access config = { "development": DevelopmentConfig, "production": ProductionConfig, "testing": TestConfig, "default": DevelopmentConfig, } def get_config(env: str | None = None) -> type[Config]: """Get configuration class based on environment. Args: env: Environment name (development, production, testing). If None, uses FLASK_ENV environment variable. Returns: Configuration class for the specified environment. """ if env is None: env = os.environ.get("FLASK_ENV", "development") return config.get(env, config["default"])