2026-09-20 12:27:47 +03:00
|
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
|
|
|
model_config = SettingsConfigDict(env_file=".env", extra="ignore")
|
|
|
|
|
|
|
|
|
|
database_url: str = "postgresql+psycopg://ipam:ipam@localhost:55432/ipam"
|
2026-09-26 21:33:50 +03:00
|
|
|
jwt_secret: str = "" # обязателен: проверяется при старте приложения (validate_secrets), миграциям не нужен
|
2026-09-20 12:27:47 +03:00
|
|
|
jwt_ttl_minutes: int = 480
|
|
|
|
|
admin_username: str = "admin"
|
|
|
|
|
admin_password: str = ""
|
|
|
|
|
trusted_proxies: str = "" # CIDR через запятую: от них принимается X-Forwarded-For
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
settings = Settings()
|
2026-09-26 21:33:50 +03:00
|
|
|
|
|
|
|
|
_WEAK_SECRETS = {"change-me", "changeme", "dev-only-secret", "secret", "password"}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_secrets(s: Settings = settings) -> None:
|
|
|
|
|
"""Fail-fast при старте: подпись токенов известным/коротким ключом позволяет подделать токен любого пользователя."""
|
|
|
|
|
if len(s.jwt_secret) < 32 or s.jwt_secret.lower() in _WEAK_SECRETS:
|
|
|
|
|
raise RuntimeError("JWT_SECRET не задан, короче 32 символов или является заглушкой: сгенерируйте значение (python scripts/gen_env.py)")
|
|
|
|
|
if s.admin_password and len(s.admin_password) < 8:
|
|
|
|
|
raise RuntimeError("ADMIN_PASSWORD короче 8 символов: пароль слабее, чем допускает UI")
|