""" Application configuration using Pydantic Settings. Loads configuration from environment variables with validation. """ from typing import Literal, List, Union from pydantic import Field, field_validator from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): """Application settings loaded from environment variables.""" model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", case_sensitive=False, ) # Database DATABASE_URL: str = Field( default="postgresql+asyncpg://postgres:postgres@localhost:5432/irt_bank_soal", description="PostgreSQL database URL with asyncpg driver", ) # FastAPI SECRET_KEY: str = Field( default="dev-secret-key-change-in-production", description="Secret key for JWT token signing", ) API_V1_STR: str = Field(default="/api/v1", description="API v1 prefix") PROJECT_NAME: str = Field(default="IRT Bank Soal", description="Project name") ENVIRONMENT: Literal["development", "staging", "production"] = Field( default="development", description="Environment name" ) # OpenRouter (AI Generation) OPENROUTER_API_KEY: str = Field( default="", description="OpenRouter API key for AI generation" ) OPENROUTER_MODEL_QWEN: str = Field( default="qwen/qwen-2.5-coder-32b-instruct", description="Qwen model identifier", ) OPENROUTER_MODEL_LLAMA: str = Field( default="meta-llama/llama-3.3-70b-instruct", description="Llama model identifier", ) OPENROUTER_TIMEOUT: int = Field(default=30, description="OpenRouter API timeout in seconds") # WordPress Integration WORDPRESS_API_URL: str = Field( default="", description="WordPress REST API base URL" ) WORDPRESS_AUTH_TOKEN: str = Field( default="", description="WordPress JWT authentication token" ) # Redis (Celery) REDIS_URL: str = Field( default="redis://localhost:6379/0", description="Redis connection URL" ) CELERY_BROKER_URL: str = Field( default="redis://localhost:6379/0", description="Celery broker URL" ) CELERY_RESULT_BACKEND: str = Field( default="redis://localhost:6379/0", description="Celery result backend URL" ) # CORS - stored as list, accepts comma-separated string from env ALLOWED_ORIGINS: List[str] = Field( default=["http://localhost:3000"], description="List of allowed CORS origins", ) @field_validator("ALLOWED_ORIGINS", mode="before") @classmethod def parse_allowed_origins(cls, v: Union[str, List[str]]) -> List[str]: """Parse comma-separated origins into list.""" if isinstance(v, str): return [origin.strip() for origin in v.split(",") if origin.strip()] return v # Global settings instance _settings: Union[Settings, None] = None def get_settings() -> Settings: """ Get application settings instance. Returns: Settings: Application settings Raises: ValueError: If settings not initialized """ global _settings if _settings is None: _settings = Settings() return _settings def init_settings(settings: Settings) -> None: """ Initialize settings with custom instance (useful for testing). Args: settings: Settings instance to use """ global _settings _settings = settings