from pydantic import computed_field, model_validator from pydantic_settings import BaseSettings, SettingsConfigDict from typing import Literal, Self import warnings class Config(BaseSettings): model_config = SettingsConfigDict( env_file=".env", env_ignore_empty=True, extra="ignore" ) SECRET_KEY: str = "changethis" ENVIRONMENT: Literal["local", "staging", "production"] = "local" DB_NAME: str = "app" DB_HOST: str = "db" DB_PORT: int = 5432 DB_USER: str = "app" DB_PASSWORD: str = "changethis" @computed_field @property def DATABASE_URI(self) -> str: return f"postgresql+asyncpg://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}" DOMAIN: str = "localhost" @computed_field @property def API_DOMAIN(self) -> str: if self.ENVIRONMENT == "local": return self.DOMAIN return f"api.{self.DOMAIN}" @computed_field @property def API_URL(self) -> str: if self.USE_NGROK: return self.NGROK_URL return ( f"{'http' if self.ENVIRONMENT == 'local' else 'https'}://{self.API_DOMAIN}" ) API_PORT: int = 8000 TELEGRAM_BOT_TOKEN: str = "changethis" ADMIN_TELEGRAM_ID: int USE_NGROK: bool = False NGROK_AUTH_TOKEN: str = "changethis" NGROK_URL: str = "" LOG_LEVEL: str = "DEBUG" def _check_default_secret(self, var_name: str, value: str | None) -> None: if value == "changethis": message = ( f'The value of {var_name} is "changethis", ' "for security, please change it, at least for deployments." ) if self.ENVIRONMENT == "local": warnings.warn(message, stacklevel=1) else: raise ValueError(message) @model_validator(mode="after") def _enforce_non_default_secrets(self) -> Self: self._check_default_secret("SECRET_KEY", self.SECRET_KEY) self._check_default_secret("DB_PASSWORD", self.DB_PASSWORD) self._check_default_secret("TELEGRAM_BOT_TOKEN", self.TELEGRAM_BOT_TOKEN) if self.USE_NGROK: self._check_default_secret("NGROK_AUTH_TOKEN", self.NGROK_AUTH_TOKEN) return self config = Config()