From 7b0d2c9ebc16480db6ab6637d393cf003bc902ab Mon Sep 17 00:00:00 2001 From: host Date: Sun, 2 Aug 2026 22:46:59 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D1=81=D1=82=D1=80=D0=BE=D0=B3=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3=D1=83=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/config.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 backend/app/config.py diff --git a/backend/app/config.py b/backend/app/config.py new file mode 100644 index 0000000..dce4191 --- /dev/null +++ b/backend/app/config.py @@ -0,0 +1,42 @@ +from functools import lru_cache + +from pydantic import Field, SecretStr, field_validator +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class Settings(BaseSettings): + """Конфигурация приложения с обязательными production-секретами.""" + + database_url: str + secret_key: SecretStr = Field(min_length=32) + access_token_expire_minutes: int = Field(default=30, ge=5, le=1440) + allow_origins: str = "" + pve_host: str + pve_node: str = "pve" + pve_token_name: str + pve_token_value: SecretStr + pve_verify_ssl: bool = True + environment: str = "production" + + model_config = SettingsConfigDict( + env_file=".env", + env_file_encoding="utf-8", + extra="ignore", + ) + + @field_validator("secret_key") + @classmethod + def reject_placeholder_secret(cls, value: SecretStr) -> SecretStr: + """Запрещает запуск с очевидным демонстрационным секретом.""" + if value.get_secret_value() in {"change-me", "generate-a-long-random-value"}: + raise ValueError("SECRET_KEY must be replaced") + return value + + +@lru_cache +def get_settings() -> Settings: + """Возвращает единый экземпляр конфигурации.""" + return Settings() + + +settings = get_settings()