import os from dataclasses import dataclass, field from pathlib import Path def secret(name: str) -> str: path = os.getenv(name + "_FILE") return Path(path).read_text().strip() if path else os.getenv(name, "") @dataclass class Settings: data_dir: Path = field(default_factory=lambda: Path(os.getenv("DATA_DIR", "/data"))) config_dir: Path = field( default_factory=lambda: Path(os.getenv("CONFIG_DIR", "/config")) ) mock: bool = field( default_factory=lambda: os.getenv("KOKONNA_MOCK", "true").lower() == "true" ) write_enabled: bool = field( default_factory=lambda: ( os.getenv("KOKONNA_WRITE_ENABLED", "false").lower() == "true" ) ) api_key: str = field(default_factory=lambda: secret("KOKONNA_API_KEY"), repr=False) api_token: str = field( default_factory=lambda: secret("FRAME_FLOW_TOKEN"), repr=False ) additional_secrets: list[str] = field(default_factory=list, repr=False) storage_name: str = field( default_factory=lambda: ( os.getenv("FRAME_FLOW_STORAGE_NAME", "NAS").strip() or "NAS" ) ) device_id: str = "living-room" timezone: str = "Asia/Shanghai" cors_origins: list[str] = field( default_factory=lambda: [ x.strip() for x in os.getenv("CORS_ALLOWED_ORIGINS", "").split(",") if x.strip() ] ) image_url_hosts: list[str] = field( default_factory=lambda: [ x.strip().lower() for x in os.getenv("IMAGE_URL_ALLOWED_HOSTS", "").split(",") if x.strip() ] ) font_path: str = field( default_factory=lambda: os.getenv( "FONT_PATH", "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc" ) ) photo_root: Path = field( default_factory=lambda: Path(os.getenv("PHOTO_ROOT", "/photos")) ) photos_cache_bytes: int = field( default_factory=lambda: max( 1024 * 1024, int(os.getenv("SYNOLOGY_PHOTOS_CACHE_BYTES", str(256 * 1024 * 1024))), ) ) max_bytes: int = 20 * 1024 * 1024 idempotency_hours: int = 168 sync_timeout: int = 600 def sanitize(self, value): import json encoded = json.dumps(value, ensure_ascii=False) for key in (self.api_key, self.api_token, *self.additional_secrets): if key: encoded = encoded.replace(key, "[REDACTED]") return json.loads(encoded)