import base64 import binascii from functools import lru_cache from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict(extra="ignore") environment: str = "development" database_url: str = "postgresql+psycopg://fin:fin@localhost:5432/fin" redis_url: str = "redis://localhost:6379/0" admin_bootstrap_token: str = "" imports_kek_b64: str = "" blob_dir: str = "./var/blobs" s3_endpoint_url: str = "" s3_region: str = "us-east-1" s3_access_key_id: str = "" s3_secret_access_key: str = "" s3_bucket_statements: str = "finance-statements" smtp_host: str = "" smtp_port: int = 1025 smtp_use_tls: bool = False smtp_from: str = "no-reply@finance.local" public_base_url: str = "http://localhost:8080" session_cookie_name: str = "fin_session" csrf_cookie_name: str = "csrf_token" cookie_secure: bool = True session_ttl_days: int = 14 invite_ttl_days: int = 7 reset_ttl_hours: int = 4 max_upload_bytes: int = 20 * 1024 * 1024 max_xlsx_rows: int = 10_000 max_pdf_pages: int = 200 max_archive_uncompressed_bytes: int = 64 * 1024 * 1024 deletion_window_days: int = 30 celery_task_always_eager: bool = False celery_broker_url: str = "" default_locale: str = "ru" default_timezone: str = "Europe/Moscow" @property def broker_url(self) -> str: return self.celery_broker_url or self.redis_url @property def kek(self) -> bytes: if not self.imports_kek_b64: raise RuntimeError("IMPORTS_KEK_B64 must be configured (base64-encoded 32-byte KEK)") try: raw = base64.b64decode(self.imports_kek_b64, validate=True) except (binascii.Error, ValueError) as exc: raise RuntimeError("IMPORTS_KEK_B64 is not valid base64") from exc if len(raw) != 32: raise RuntimeError("IMPORTS_KEK_B64 must decode to exactly 32 bytes") return raw @lru_cache def get_settings() -> Settings: return Settings()