config.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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. See https://pydantic-docs.helpmanual.io/usage/settings/
  11. Note, complex types like lists are read as json-encoded strings.
  12. """
  13. import tomllib
  14. from pathlib import Path
  15. from typing import Literal
  16. from pydantic import AnyHttpUrl, EmailStr
  17. from pydantic_settings import BaseSettings, SettingsConfigDict
  18. PROJECT_DIR = Path(__file__).parent.parent.parent
  19. with open(f"{PROJECT_DIR}/pyproject.toml", "rb") as f:
  20. PYPROJECT_CONTENT = tomllib.load(f)["tool"]["poetry"]
  21. def build_postgreuri(
  22. scheme: str, username: str, password: str, host: str, port: int = 0, path: str = ""
  23. ):
  24. fullhost = host + (f":{port}" if port > 0 else "")
  25. return f"{scheme}://{username}:{password}@{fullhost}{path}"
  26. class Settings(BaseSettings):
  27. # CORE SETTINGS
  28. FRONT_END_URL: str
  29. SECRET_KEY: str
  30. RESET_REQUEST_LIMIT: int = 3
  31. RESET_REQUEST_WINDOW_HOUR: int = 1
  32. ENVIRONMENT: Literal["DEV", "PYTEST", "STG", "PRD"] = "DEV"
  33. SECURITY_BCRYPT_ROUNDS: int = 12
  34. ACCESS_TOKEN_EXPIRE_MINUTES: int = 11520 # 8 days
  35. REFRESH_TOKEN_EXPIRE_MINUTES: int = 40320 # 28 days
  36. BACKEND_CORS_ORIGINS: list[AnyHttpUrl | Literal["*"]] = []
  37. ALLOWED_HOSTS: list[str] = ["localhost", "127.0.0.1"]
  38. INACTIVITY_SMS_SENDER_THRESHOLD_SECONDS: int = 180
  39. # PROJECT NAME, VERSION AND DESCRIPTION
  40. PROJECT_NAME: str = PYPROJECT_CONTENT["name"]
  41. VERSION: str = PYPROJECT_CONTENT["version"]
  42. DESCRIPTION: str = PYPROJECT_CONTENT["description"]
  43. # Email account to send reset password
  44. SMTP_HOST: str = ""
  45. SMTP_PORT: int = 587
  46. SMTP_USERNAME: str = ""
  47. SMTP_PASSWORD: str = ""
  48. SMTP_USE_TLS: bool = True
  49. EMAIL_FROM_ADDRESS: str = "no-reply@example.com"
  50. EMAIL_FROM_NAME: str = "BDLG Planner"
  51. # POSTGRESQL DEFAULT DATABASE
  52. DEFAULT_DATABASE_HOSTNAME: str
  53. DEFAULT_DATABASE_USER: str
  54. DEFAULT_DATABASE_PASSWORD: str
  55. DEFAULT_DATABASE_PORT: str
  56. DEFAULT_DATABASE_DB: str
  57. @property
  58. def DEFAULT_SQLALCHEMY_DATABASE_URI(self) -> str:
  59. return build_postgreuri(
  60. scheme="postgresql",
  61. username=self.DEFAULT_DATABASE_USER,
  62. password=self.DEFAULT_DATABASE_PASSWORD,
  63. host=self.DEFAULT_DATABASE_HOSTNAME,
  64. port=int(self.DEFAULT_DATABASE_PORT),
  65. path=f"/{self.DEFAULT_DATABASE_DB}",
  66. )
  67. # POSTGRESQL TEST DATABASE
  68. TEST_DATABASE_HOSTNAME: str = "postgres"
  69. TEST_DATABASE_USER: str = "postgres"
  70. TEST_DATABASE_PASSWORD: str = "postgres"
  71. TEST_DATABASE_PORT: str = "5432"
  72. TEST_DATABASE_DB: str = "postgres"
  73. @property
  74. def TEST_SQLALCHEMY_DATABASE_URI(self) -> str:
  75. return build_postgreuri(
  76. scheme="postgresql",
  77. username=self.TEST_DATABASE_USER,
  78. password=self.TEST_DATABASE_PASSWORD,
  79. host=self.TEST_DATABASE_HOSTNAME,
  80. port=int(self.TEST_DATABASE_PORT),
  81. path=f"/{self.TEST_DATABASE_DB}",
  82. )
  83. # SMS batch
  84. BATCH_SMS_PHONE_NUMBER: str = ""
  85. # FIRST SUPERUSER
  86. FIRST_SUPERUSER_EMAIL: EmailStr
  87. FIRST_SUPERUSER_PASSWORD: str
  88. model_config = SettingsConfigDict(env_file=f"{PROJECT_DIR}/.env", case_sensitive=True)
  89. settings: Settings = Settings() # type: ignore