""" Configuration management for The Scheduler. Uses Pydantic BaseSettings for type-safe environment variable loading. """ import tomllib from functools import lru_cache from pathlib import Path from pydantic_settings import BaseSettings from pydantic import Field def _get_version_from_pyproject() -> tuple[str, str, str]: """Read version info from pyproject.toml.""" pyproject_path = Path(__file__).parent.parent / "pyproject.toml" if pyproject_path.exists(): with open(pyproject_path, "rb") as f: data = tomllib.load(f) project = data.get("project", {}) return ( project.get("name", "the-scheduler"), project.get("version", "0.0.0"), project.get("description", ""), ) return ("the-scheduler", "0.0.0", "") _PROJECT_NAME, _PROJECT_VERSION, _PROJECT_DESCRIPTION = _get_version_from_pyproject() class Settings(BaseSettings): """Application settings loaded from environment variables.""" # Application app_name: str = Field(default=_PROJECT_NAME, alias="APP_NAME") app_version: str = Field(default=_PROJECT_VERSION, alias="APP_VERSION") debug: bool = Field(default=False, alias="DEBUG") host: str = Field(default="0.0.0.0", alias="HOST") port: int = Field(default=8090, alias="PORT") log_level: str = Field(default="INFO", alias="LOG_LEVEL") # Security scheduler_api_key: str = Field(default="dev-key-change-me", alias="SCHEDULER_API_KEY") # PostgreSQL postgres_host: str = Field(default="postgres-shared", alias="POSTGRES_HOST") postgres_port: int = Field(default=5432, alias="POSTGRES_PORT") postgres_db: str = Field(default="library_scheduler", alias="POSTGRES_DB") postgres_user: str = Field(default="library_scheduler_user", alias="POSTGRES_USER") postgres_password: str = Field(default="", alias="POSTGRES_PASSWORD") # Redis redis_host: str = Field(default="redis-shared", alias="REDIS_HOST") redis_port: int = Field(default=6379, alias="REDIS_PORT") redis_db: int = Field(default=3, alias="REDIS_DB") # Gitea gitea_url: str = Field(default="http://gitea:3000", alias="GITEA_URL") gitea_user: str = Field(default="library", alias="GITEA_USER") gitea_password: str = Field(default="", alias="GITEA_PASSWORD") gitea_token: str = Field(default="", alias="GITEA_TOKEN") # API token with write:repository scope gitea_ssh_host: str = Field(default="gitea", alias="GITEA_SSH_HOST") gitea_ssh_port: int = Field(default=22, alias="GITEA_SSH_PORT") # Backup Configuration backup_retention_daily: int = Field(default=7, alias="BACKUP_RETENTION_DAILY") backup_retention_weekly: int = Field(default=4, alias="BACKUP_RETENTION_WEEKLY") backup_retention_monthly: int = Field(default=12, alias="BACKUP_RETENTION_MONTHLY") # Google Cloud Storage gcs_credentials_file: str = Field(default="", alias="GCS_CREDENTIALS_FILE") # Documentation Mirroring docs_mirror_path: str = Field(default="/docs-mirror", alias="DOCS_MIRROR_PATH") docs_check_interval: int = Field(default=21600, alias="DOCS_CHECK_INTERVAL") # 6 hours @property def database_url(self) -> str: """PostgreSQL connection URL for APScheduler.""" return f"postgresql://{self.postgres_user}:{self.postgres_password}@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}" @property def redis_url(self) -> str: """Redis connection URL.""" return f"redis://{self.redis_host}:{self.redis_port}/{self.redis_db}" class Config: env_file = ".env" case_sensitive = False @lru_cache def get_settings() -> Settings: """ Get cached settings instance. Using lru_cache ensures we only create one Settings instance. """ return Settings()