""" File with environment variables and general configuration logic. `SECRET_KEY`, `ENVIRONMENT` etc. map to env variables with the same names. Pydantic priority ordering: 1. (Most important, will overwrite everything) - environment variables 2. `.env` file in root folder of project 3. Default values For project name, version, description we use pyproject.toml For the rest, we use file `.env` (gitignored), see `.env.example` `DEFAULT_SQLALCHEMY_DATABASE_URI` and `TEST_SQLALCHEMY_DATABASE_URI`: Both are ment to be validated at the runtime, do not change unless you know what are you doing. All the two validators do is to build full URI (TCP protocol) to databases to avoid typo bugs. See https://pydantic-docs.helpmanual.io/usage/settings/ Note, complex types like lists are read as json-encoded strings. """ import tomllib from pathlib import Path from typing import Literal from pydantic import AnyHttpUrl, BaseSettings, EmailStr, PostgresDsn, validator PROJECT_DIR = Path(__file__).parent.parent.parent with open(f"{PROJECT_DIR}/pyproject.toml", "rb") as f: PYPROJECT_CONTENT = tomllib.load(f)["tool"]["poetry"] class Settings(BaseSettings): # CORE SETTINGS SECRET_KEY: str ENVIRONMENT: Literal["DEV", "PYTEST", "STG", "PRD"] = "DEV" SECURITY_BCRYPT_ROUNDS: int = 12 ACCESS_TOKEN_EXPIRE_MINUTES: int = 11520 # 8 days REFRESH_TOKEN_EXPIRE_MINUTES: int = 40320 # 28 days BACKEND_CORS_ORIGINS: list[AnyHttpUrl] = [] ALLOWED_HOSTS: list[str] = ["localhost", "127.0.0.1"] # PROJECT NAME, VERSION AND DESCRIPTION PROJECT_NAME: str = PYPROJECT_CONTENT["name"] VERSION: str = PYPROJECT_CONTENT["version"] DESCRIPTION: str = PYPROJECT_CONTENT["description"] # POSTGRESQL DEFAULT DATABASE DEFAULT_DATABASE_HOSTNAME: str DEFAULT_DATABASE_USER: str DEFAULT_DATABASE_PASSWORD: str DEFAULT_DATABASE_PORT: str DEFAULT_DATABASE_DB: str DEFAULT_SQLALCHEMY_DATABASE_URI: str = "" # POSTGRESQL TEST DATABASE TEST_DATABASE_HOSTNAME: str = "postgres" TEST_DATABASE_USER: str = "postgres" TEST_DATABASE_PASSWORD: str = "postgres" TEST_DATABASE_PORT: str = "5432" TEST_DATABASE_DB: str = "postgres" TEST_SQLALCHEMY_DATABASE_URI: str = "" # SMS batch BATCH_SMS_PHONE_NUMBER: str = "" # FIRST SUPERUSER FIRST_SUPERUSER_EMAIL: EmailStr FIRST_SUPERUSER_PASSWORD: str @validator("DEFAULT_SQLALCHEMY_DATABASE_URI") def _assemble_default_db_connection(cls, v: str, values: dict[str, str]) -> str: return PostgresDsn.build( scheme="postgresql", user=values["DEFAULT_DATABASE_USER"], password=values["DEFAULT_DATABASE_PASSWORD"], host=values["DEFAULT_DATABASE_HOSTNAME"], port=values["DEFAULT_DATABASE_PORT"], path=f"/{values['DEFAULT_DATABASE_DB']}", ) @validator("TEST_SQLALCHEMY_DATABASE_URI") def _assemble_test_db_connection(cls, v: str, values: dict[str, str]) -> str: return PostgresDsn.build( scheme="postgresql", user=values["TEST_DATABASE_USER"], password=values["TEST_DATABASE_PASSWORD"], host=values["TEST_DATABASE_HOSTNAME"], port=values["TEST_DATABASE_PORT"], path=f"/{values['TEST_DATABASE_DB']}", ) class Config: env_file = f"{PROJECT_DIR}/.env" case_sensitive = True settings: Settings = Settings() # type: ignore