from pydantic import model_validator from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): DATABASE_URL: str = "postgresql://wealthy_user:wealthy_pass@db:5432/wealthysmart" # No defaults: the app must refuse to boot without real values (see _reject_weak_secrets). SECRET_KEY: str ADMIN_USERNAME: str ADMIN_PASSWORD: str ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 7 # 7 days COOKIE_SECURE: bool = True # dev compose sets "false"; prod serves over TLS CORS_ORIGINS: str = ( "https://wealth.cescalante.dev," "http://localhost:3000,http://localhost:3001,http://localhost:5175" ) BCCR_API_EMAIL: str = "" BCCR_API_TOKEN: str = "" VAPID_PRIVATE_KEY: str = "" VAPID_PUBLIC_KEY: str = "" VAPID_CLAIM_EMAIL: str = "mailto:admin@wealth.cescalante.dev" OPENAI_API_KEY: str = "" AGENT_MODEL: str = "gpt-5.4-mini" @property def cors_origins_list(self) -> list[str]: return [o.strip() for o in self.CORS_ORIGINS.split(",") if o.strip()] @model_validator(mode="after") def _reject_weak_secrets(self) -> "Settings": if len(self.SECRET_KEY) < 32 or self.SECRET_KEY == "change-me-in-production": raise ValueError( "SECRET_KEY must be a random value of at least 32 characters " "(generate one with: openssl rand -hex 32)" ) if not self.ADMIN_USERNAME: raise ValueError("ADMIN_USERNAME must be set") if not self.ADMIN_PASSWORD or self.ADMIN_PASSWORD == "admin": raise ValueError("ADMIN_PASSWORD must be set and must not be 'admin'") return self model_config = SettingsConfigDict(env_file=".env") settings = Settings()