""" 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 | None = None host: str | None = None path: str | None = None username: str | None = None password: str | None = None cache_ttl: int = 60 * 30 cache_size: int = 1024 @property def url(self) -> str: """ Combined DAV URL. """ return f"{self.protocol}://{self.host}{self.path}" class WebDAVSettings(DAVSettings): """ Connection to a WebDAV server. """ protocol: str = "https" host: str = "example.com" path: str = "/remote.php/dav" prefix: str = "/ovdashboard" username: str = "ovd_user" password: str = "password" config_filename: str = "config.toml" disable_check: bool = False retries: int = 20 retry_delay: int = 30 prefix: str = "/ovdashboard" @property def url(self) -> str: """ Combined DAV URL. """ return f"{self.protocol}://{self.host}{self.path}{self.prefix}" class Settings(BaseSettings): """ Per-run settings. """ model_config = SettingsConfigDict( 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: DAVSettings = DAVSettings() @model_validator(mode='before') @classmethod def validate_dav_settings(cls, data) -> dict[str, Any]: assert isinstance(data, dict) # ensure both settings dicts are created for key in ("webdav", "caldav"): if key not in data: data[key] = {} default_dav = DAVSettings( protocol="https", host="example.com", username="ovdashboard", password="secret", ).model_dump() for key in default_dav: # if "webdav" value is not specified, use default if key not in data["webdav"] or data["webdav"][key] is None: data["webdav"][key] = default_dav[key] # if "caldav" value is not specified, use "webdav" value if key not in data["caldav"] or data["caldav"][key] is None: data["caldav"][key] = data["webdav"][key] # add default "path"s if None if data["webdav"]["path"] is None: data["webdav"]["path"] = "/remote.php/webdav" if data["caldav"]["path"] is None: data["caldav"]["path"] = "/remote.php/dav" return data SETTINGS = Settings()