"""Configuration settings for YouTube Summarizer backend.""" import os from typing import Optional from pydantic_settings import BaseSettings from pydantic import Field class Settings(BaseSettings): """Application settings.""" # Database DATABASE_URL: str = Field( default="sqlite:///./data/youtube_summarizer.db", env="DATABASE_URL" ) # Authentication JWT_SECRET_KEY: str = Field( default="your-secret-key-change-in-production", env="JWT_SECRET_KEY" ) JWT_ALGORITHM: str = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES: int = 15 REFRESH_TOKEN_EXPIRE_DAYS: int = 7 EMAIL_VERIFICATION_EXPIRE_HOURS: int = 24 PASSWORD_RESET_EXPIRE_MINUTES: int = 30 # Email settings (for development use MailHog) SMTP_HOST: str = Field(default="localhost", env="SMTP_HOST") SMTP_PORT: int = Field(default=1025, env="SMTP_PORT") # MailHog default SMTP_USER: Optional[str] = Field(default=None, env="SMTP_USER") SMTP_PASSWORD: Optional[str] = Field(default=None, env="SMTP_PASSWORD") SMTP_FROM_EMAIL: str = Field( default="noreply@youtube-summarizer.local", env="SMTP_FROM_EMAIL" ) SMTP_TLS: bool = Field(default=False, env="SMTP_TLS") SMTP_SSL: bool = Field(default=False, env="SMTP_SSL") # OAuth2 Google (optional) GOOGLE_CLIENT_ID: Optional[str] = Field(default=None, env="GOOGLE_CLIENT_ID") GOOGLE_CLIENT_SECRET: Optional[str] = Field(default=None, env="GOOGLE_CLIENT_SECRET") GOOGLE_REDIRECT_URI: str = Field( default="http://localhost:3000/auth/google/callback", env="GOOGLE_REDIRECT_URI" ) # Security CORS_ORIGINS: list[str] = Field( default=["http://localhost:3000", "http://localhost:8000"], env="CORS_ORIGINS" ) SECRET_KEY: str = Field( default="your-app-secret-key-change-in-production", env="SECRET_KEY" ) # API Rate Limiting RATE_LIMIT_PER_MINUTE: int = Field(default=60, env="RATE_LIMIT_PER_MINUTE") # AI Services (DeepSeek required, others optional) DEEPSEEK_API_KEY: Optional[str] = Field(default=None, env="DEEPSEEK_API_KEY") # Primary AI service OPENAI_API_KEY: Optional[str] = Field(default=None, env="OPENAI_API_KEY") # Alternative model ANTHROPIC_API_KEY: Optional[str] = Field(default=None, env="ANTHROPIC_API_KEY") # Alternative model GOOGLE_API_KEY: Optional[str] = Field(default=None, env="GOOGLE_API_KEY") # YouTube Data API YOUTUBE_API_KEY: Optional[str] = Field(default=None, env="YOUTUBE_API_KEY") # Service Configuration USE_MOCK_SERVICES: bool = Field(default=False, env="USE_MOCK_SERVICES") ENABLE_REAL_TRANSCRIPT_EXTRACTION: bool = Field(default=True, env="ENABLE_REAL_TRANSCRIPT_EXTRACTION") ENABLE_REAL_CACHE: bool = Field(default=False, env="ENABLE_REAL_CACHE") # Redis Configuration (for real cache) REDIS_URL: Optional[str] = Field(default="redis://localhost:6379", env="REDIS_URL") REDIS_ENABLED: bool = Field(default=False, env="REDIS_ENABLED") # Password Requirements PASSWORD_MIN_LENGTH: int = 8 PASSWORD_REQUIRE_UPPERCASE: bool = True PASSWORD_REQUIRE_LOWERCASE: bool = True PASSWORD_REQUIRE_DIGITS: bool = True PASSWORD_REQUIRE_SPECIAL: bool = False # Session Management SESSION_TIMEOUT_MINUTES: int = Field(default=30, env="SESSION_TIMEOUT_MINUTES") MAX_LOGIN_ATTEMPTS: int = 5 LOCKOUT_DURATION_MINUTES: int = 15 # Application APP_NAME: str = "YouTube Summarizer" APP_VERSION: str = "3.1.0" DEBUG: bool = Field(default=False, env="DEBUG") ENVIRONMENT: str = Field(default="development", env="ENVIRONMENT") FRONTEND_URL: str = Field(default="http://localhost:3001", env="FRONTEND_URL") class Config: env_file = ".env" env_file_encoding = "utf-8" case_sensitive = False extra = "ignore" # Ignore extra environment variables # Create global settings instance settings = Settings() class AuthSettings: """Authentication-specific settings.""" _cached_jwt_key: Optional[str] = None @classmethod def get_jwt_secret_key(cls) -> str: """Get JWT secret key, generate if needed and cache it.""" if settings.JWT_SECRET_KEY != "your-secret-key-change-in-production": return settings.JWT_SECRET_KEY # Generate and cache a secure key for development if cls._cached_jwt_key is None: import secrets cls._cached_jwt_key = secrets.token_urlsafe(32) return cls._cached_jwt_key @staticmethod def get_password_hash_rounds() -> int: """Get bcrypt hash rounds based on environment.""" if settings.ENVIRONMENT == "production": return 12 # Higher security in production return 10 # Faster in development @staticmethod def validate_password_requirements(password: str) -> tuple[bool, str]: """Validate password against requirements.""" if len(password) < settings.PASSWORD_MIN_LENGTH: return False, f"Password must be at least {settings.PASSWORD_MIN_LENGTH} characters long" if settings.PASSWORD_REQUIRE_UPPERCASE and not any(c.isupper() for c in password): return False, "Password must contain at least one uppercase letter" if settings.PASSWORD_REQUIRE_LOWERCASE and not any(c.islower() for c in password): return False, "Password must contain at least one lowercase letter" if settings.PASSWORD_REQUIRE_DIGITS and not any(c.isdigit() for c in password): return False, "Password must contain at least one digit" if settings.PASSWORD_REQUIRE_SPECIAL: special_chars = "!@#$%^&*()_+-=[]{}|;:,.<>?" if not any(c in special_chars for c in password): return False, "Password must contain at least one special character" return True, "Password meets all requirements" # Export auth settings instance auth_settings = AuthSettings()