config.py 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. """
  2. File with environment variables and general configuration logic.
  3. `SECRET_KEY`, `ENVIRONMENT` etc. map to env variables with the same names.
  4. Pydantic priority ordering:
  5. 1. (Most important, will overwrite everything) - environment variables
  6. 2. `.env` file in root folder of project
  7. 3. Default values
  8. For project name, version, description we use pyproject.toml
  9. For the rest, we use file `.env` (gitignored), see `.env.example`
  10. `DEFAULT_SQLALCHEMY_DATABASE_URI` and `TEST_SQLALCHEMY_DATABASE_URI`:
  11. Both are ment to be validated at the runtime, do not change unless you know
  12. what are you doing. All the two validators do is to build full URI (TCP protocol)
  13. to databases to avoid typo bugs.
  14. See https://pydantic-docs.helpmanual.io/usage/settings/
  15. Note, complex types like lists are read as json-encoded strings.
  16. """
  17. import tomllib
  18. from pathlib import Path
  19. from typing import Literal
  20. from pydantic import AnyHttpUrl, BaseSettings, EmailStr, PostgresDsn, validator
  21. PROJECT_DIR = Path(__file__).parent.parent.parent
  22. with open(f"{PROJECT_DIR}/pyproject.toml", "rb") as f:
  23. PYPROJECT_CONTENT = tomllib.load(f)["tool"]["poetry"]
  24. class Settings(BaseSettings):
  25. # CORE SETTINGS
  26. SECRET_KEY: str
  27. ENVIRONMENT: Literal["DEV", "PYTEST", "STG", "PRD"] = "DEV"
  28. SECURITY_BCRYPT_ROUNDS: int = 12
  29. ACCESS_TOKEN_EXPIRE_MINUTES: int = 11520 # 8 days
  30. REFRESH_TOKEN_EXPIRE_MINUTES: int = 40320 # 28 days
  31. BACKEND_CORS_ORIGINS: list[AnyHttpUrl] = []
  32. ALLOWED_HOSTS: list[str] = ["localhost", "127.0.0.1"]
  33. # PROJECT NAME, VERSION AND DESCRIPTION
  34. PROJECT_NAME: str = PYPROJECT_CONTENT["name"]
  35. VERSION: str = PYPROJECT_CONTENT["version"]
  36. DESCRIPTION: str = PYPROJECT_CONTENT["description"]
  37. # POSTGRESQL DEFAULT DATABASE
  38. DEFAULT_DATABASE_HOSTNAME: str
  39. DEFAULT_DATABASE_USER: str
  40. DEFAULT_DATABASE_PASSWORD: str
  41. DEFAULT_DATABASE_PORT: str
  42. DEFAULT_DATABASE_DB: str
  43. DEFAULT_SQLALCHEMY_DATABASE_URI: str = ""
  44. # POSTGRESQL TEST DATABASE
  45. TEST_DATABASE_HOSTNAME: str = "postgres"
  46. TEST_DATABASE_USER: str = "postgres"
  47. TEST_DATABASE_PASSWORD: str = "postgres"
  48. TEST_DATABASE_PORT: str = "5432"
  49. TEST_DATABASE_DB: str = "postgres"
  50. TEST_SQLALCHEMY_DATABASE_URI: str = ""
  51. # FIRST SUPERUSER
  52. FIRST_SUPERUSER_EMAIL: EmailStr
  53. FIRST_SUPERUSER_PASSWORD: str
  54. @validator("DEFAULT_SQLALCHEMY_DATABASE_URI")
  55. def _assemble_default_db_connection(cls, v: str, values: dict[str, str]) -> str:
  56. return PostgresDsn.build(
  57. scheme="postgresql+asyncpg",
  58. user=values["DEFAULT_DATABASE_USER"],
  59. password=values["DEFAULT_DATABASE_PASSWORD"],
  60. host=values["DEFAULT_DATABASE_HOSTNAME"],
  61. port=values["DEFAULT_DATABASE_PORT"],
  62. path=f"/{values['DEFAULT_DATABASE_DB']}",
  63. )
  64. @validator("TEST_SQLALCHEMY_DATABASE_URI")
  65. def _assemble_test_db_connection(cls, v: str, values: dict[str, str]) -> str:
  66. return PostgresDsn.build(
  67. scheme="postgresql+asyncpg",
  68. user=values["TEST_DATABASE_USER"],
  69. password=values["TEST_DATABASE_PASSWORD"],
  70. host=values["TEST_DATABASE_HOSTNAME"],
  71. port=values["TEST_DATABASE_PORT"],
  72. path=f"/{values['TEST_DATABASE_DB']}",
  73. )
  74. class Config:
  75. env_file = f"{PROJECT_DIR}/.env"
  76. case_sensitive = True
  77. settings: Settings = Settings() # type: ignore