""" Configuration definition. Converts per-run (environment) variables and config files into the "python world" using `pydantic`. Pydantic models might have convenience methods attached. """ from typing import Any from pydantic import BaseModel, model_validator from pydantic_settings import BaseSettings, SettingsConfigDict class DAVSettings(BaseModel): """ Connection to a DAV server. """ protocol: str = "https" host: str = "example.com" username: str = "ovd_user" password: str = "password" cache_ttl: int = 60 * 10 @property def url(self) -> str: """ Combined DAV URL. """ return f"{self.protocol}://{self.host}" _DAV_DEFAULT = DAVSettings().model_dump() class CalDAVSettings(DAVSettings): """ Connection to a CalDAV server. """ path: str = "/remote.php/dav" @property def url(self) -> str: """ Combined DAV URL. """ return f"{super().url}{self.path}" class WebDAVSettings(CalDAVSettings): """ Connection to a WebDAV server. """ path: str = "/remote.php/webdav" config_filename: str = "config.txt" disable_check: bool = False retries: int = 20 retry_delay: int = 30 prefix: str = "/ovdashboard" @property def url(self) -> str: """ Combined DAV URL. """ return f"{super().url}{self.prefix}" _WEBDAV_DEFAULT = WebDAVSettings().model_dump() class RedisSettings(BaseModel): """ Connection to a redis server. """ host: str = "redis" port: int = 6379 db: int = 0 protocol: int = 3 class Settings(BaseSettings): """ Per-run settings. """ model_config = SettingsConfigDict( extra="ignore", env_file=".env", env_file_encoding="utf-8", env_nested_delimiter="__", ) ##### # general settings ##### log_level: str = "INFO" production_mode: bool = False ui_directory: str = "/usr/local/share/ovdashboard_ui/html" # doesn't even have to be reachable ping_host: str = "1.0.0.0" ping_port: int = 1 ##### # openapi settings ##### def __dev_value[T](self, value: T) -> T | None: if self.production_mode: return None return value @property def openapi_url(self) -> str | None: return self.__dev_value("/api/openapi.json") @property def docs_url(self) -> str | None: return self.__dev_value("/api/docs") @property def redoc_url(self) -> str | None: return self.__dev_value("/api/redoc") ##### # webdav settings ##### webdav: WebDAVSettings = WebDAVSettings() ##### # caldav settings ##### caldav: CalDAVSettings = CalDAVSettings() ##### # redis settings ##### redis: RedisSettings = RedisSettings() @model_validator(mode="before") def validate_dav_settings(cls, data) -> dict[str, Any]: assert isinstance(data, dict) # ensure both settings dicts are created for key in ("webdav", "caldav"): data[key] = data.get(key, {}) for key in _DAV_DEFAULT: # if "webdav" value is not specified, use default value data["webdav"][key] = data["webdav"].get(key, _WEBDAV_DEFAULT[key]) # if "caldav" value is not specified, use "webdav" value data["caldav"][key] = data["caldav"].get(key, data["webdav"][key]) return data SETTINGS = Settings()