import json from functools import lru_cache from typing import Annotated, Literal from pydantic import Field, field_validator from pydantic_settings import BaseSettings, NoDecode, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore", populate_by_name=True) hermes_bin: str = Field(default="hermes", alias="HERMES_BIN") hermes_home: str | None = Field(default=None, alias="HERMES_HOME") default_board: str = Field(default="default", alias="HERMES_KANBAN_BOARD") auth_mode: Literal["telegram", "none", "dev"] = Field(default="dev", alias="KANBAN_UI_AUTH_MODE") telegram_bot_token: str | None = Field(default=None, alias="TELEGRAM_BOT_TOKEN") telegram_allowed_user_ids: Annotated[list[int], NoDecode] = Field(default_factory=list, alias="TELEGRAM_ALLOWED_USER_IDS") allowed_origins: Annotated[list[str], NoDecode] = Field(default_factory=lambda: ["http://localhost:5173"], alias="KANBAN_UI_ALLOWED_ORIGINS") command_timeout_seconds: int = Field(default=30, alias="KANBAN_UI_COMMAND_TIMEOUT_SECONDS") frontend_dist_dir: str | None = Field(default=None, alias="KANBAN_UI_FRONTEND_DIST_DIR") @field_validator("telegram_allowed_user_ids", mode="before") @classmethod def parse_user_ids(cls, value): if value is None or value == "": return [] if isinstance(value, str): value = value.strip() if value.startswith("["): try: value = json.loads(value) except json.JSONDecodeError: pass if isinstance(value, str): return [int(part.strip()) for part in value.split(",") if part.strip()] if isinstance(value, (list, tuple)): return [int(v) for v in value] return value @field_validator("allowed_origins", mode="before") @classmethod def parse_origins(cls, value): if value is None or value == "": return [] if isinstance(value, str): value = value.strip() if value.startswith("["): try: value = json.loads(value) except json.JSONDecodeError: pass if isinstance(value, str): return [part.strip() for part in value.split(",") if part.strip()] if isinstance(value, (list, tuple)): return [str(v) for v in value] return value @lru_cache def get_settings() -> Settings: return Settings()