from functools import lru_cache from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict(env_file=".env", extra="ignore") database_url: str = "postgresql+asyncpg://finance_app:password@postgres:5432/financedb" redis_url: str = "redis://localhost:6379/0" encryption_key: str # 32-byte hex string backup_passphrase: str = "" environment: str = "production" allow_registration: bool = False base_currency: str = "GBP" # JWT — keys read from /run/secrets/ at runtime jwt_private_key_file: str = "/run/secrets/jwt_private.pem" jwt_public_key_file: str = "/run/secrets/jwt_public.pem" jwt_algorithm: str = "RS256" access_token_expire_minutes: int = 15 refresh_token_expire_days: int = 7 # Security csrf_token_expire_hours: int = 24 max_login_attempts: int = 5 lockout_base_seconds: int = 1800 # 30 min, doubles each time # Rate limits (requests per minute) rate_limit_auth: int = 10 rate_limit_api: int = 300 rate_limit_predictions: int = 20 # File uploads upload_dir: str = "/app/uploads" max_attachment_bytes: int = 10 * 1024 * 1024 # 10 MB max_attachments_per_txn: int = 10 # Background jobs price_sync_interval_minutes: int = 15 fx_sync_interval_minutes: int = 60 snapshot_hour: int = 2 # 2 AM daily backup_hour: int = 3 # 3 AM daily ml_retrain_day: str = "sun" # weekly on Sunday @property def is_development(self) -> bool: return self.environment == "development" @lru_cache def get_settings() -> Settings: return Settings()