"""Application configuration.""" from typing import List from pydantic import model_validator from pydantic_settings import BaseSettings, SettingsConfigDict DEFAULT_SECRET_KEY = "your-secret-key-change-in-production" # Known weak/placeholder secrets that must never reach production. WEAK_SECRET_KEYS = { DEFAULT_SECRET_KEY, "change-me-in-production", # docker-compose fallback "change-me", "secret", "changeme", } class Settings(BaseSettings): """Application settings.""" model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", case_sensitive=False ) # App app_name: str = "Space Booking API" debug: bool = False # Database database_url: str = "sqlite:///./space_booking.db" # JWT secret_key: str = DEFAULT_SECRET_KEY algorithm: str = "HS256" access_token_expire_minutes: int = 1440 # 24 hours # SMTP smtp_host: str = "localhost" smtp_port: int = 1025 # MailHog default smtp_user: str = "" smtp_password: str = "" smtp_from_address: str = "noreply@space-booking.local" smtp_enabled: bool = False # Disable by default for dev # Frontend frontend_url: str = "http://localhost:5173" # Google Calendar OAuth google_client_id: str = "" google_client_secret: str = "" google_redirect_uri: str = "http://localhost:8000/api/integrations/google/callback" google_scopes: List[str] = [ "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/calendar.events" ] @model_validator(mode="after") def _enforce_production_secrets(self) -> "Settings": """Refuse to boot in production with a weak SECRET_KEY.""" if not self.debug: if self.secret_key in WEAK_SECRET_KEYS or len(self.secret_key) < 32: raise ValueError( "SECRET_KEY is weak or a placeholder. Set a strong, random value " "(>= 32 chars) before running in production (DEBUG=false). " "Generate one with: python -c \"import secrets; print(secrets.token_hex(32))\"" ) return self settings = Settings()