Crea servizio watchdog configurabile
This commit is contained in:
153
src/bakrest/config.py
Normal file
153
src/bakrest/config.py
Normal file
@@ -0,0 +1,153 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import tomllib
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .default_config import DEFAULT_CONFIG_TOML
|
||||
from .paths import default_config_path, expand_path
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ServerCheckConfig:
|
||||
type: str
|
||||
port: int
|
||||
interval_seconds: int
|
||||
timeout_seconds: int
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BackupConfig:
|
||||
server_host: str
|
||||
remote_destination: str
|
||||
rsync_path: str
|
||||
shutdown_on_success: bool
|
||||
shutdown_command: str
|
||||
server_check: ServerCheckConfig
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class WatchConfig:
|
||||
include_dirs: tuple[Path, ...]
|
||||
include_extensions: frozenset[str]
|
||||
exclude_dirs: tuple[Path | str, ...]
|
||||
exclude_patterns: tuple[str, ...]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StorageConfig:
|
||||
change_registry: Path
|
||||
logs_dir: Path
|
||||
status_file: Path
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StabilityConfig:
|
||||
min_age_seconds: int
|
||||
unchanged_check_interval_seconds: int
|
||||
unchanged_checks_required: int
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AppConfig:
|
||||
path: Path
|
||||
service_autostart: bool
|
||||
tray_autostart: bool
|
||||
watch: WatchConfig
|
||||
backup: BackupConfig
|
||||
storage: StorageConfig
|
||||
stability: StabilityConfig
|
||||
|
||||
|
||||
def ensure_default_config(path: Path | None = None) -> Path:
|
||||
config_path = path or default_config_path()
|
||||
if config_path.exists():
|
||||
return config_path
|
||||
|
||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
config_path.write_text(DEFAULT_CONFIG_TOML, encoding="utf-8")
|
||||
return config_path
|
||||
|
||||
|
||||
def copy_example_config(destination: Path) -> None:
|
||||
destination.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copyfile(Path("config/config.example.toml"), destination)
|
||||
|
||||
|
||||
def load_config(path: Path | None = None) -> AppConfig:
|
||||
config_path = ensure_default_config(path)
|
||||
data = tomllib.loads(config_path.read_text(encoding="utf-8"))
|
||||
return parse_config(data, config_path)
|
||||
|
||||
|
||||
def parse_config(data: dict[str, Any], path: Path) -> AppConfig:
|
||||
service = data.get("service", {})
|
||||
tray = data.get("tray", {})
|
||||
watch = data.get("watch", {})
|
||||
backup = data.get("backup", {})
|
||||
server_check = backup.get("server_check", {})
|
||||
storage = data.get("storage", {})
|
||||
stability = data.get("stability", {})
|
||||
|
||||
include_dirs = tuple(expand_path(item) for item in watch.get("include_dirs", []))
|
||||
include_extensions = frozenset(
|
||||
_normalize_extension(item) for item in watch.get("include_extensions", [])
|
||||
)
|
||||
exclude_dirs = tuple(_normalize_exclude_dir(item) for item in watch.get("exclude_dirs", []))
|
||||
|
||||
storage_config = StorageConfig(
|
||||
change_registry=expand_path(
|
||||
storage.get("change_registry", "%PROGRAMDATA%\\BakRest\\changes.sqlite")
|
||||
),
|
||||
logs_dir=expand_path(storage.get("logs_dir", "%PROGRAMDATA%\\BakRest\\logs")),
|
||||
status_file=expand_path(storage.get("status_file", "%PROGRAMDATA%\\BakRest\\status.json")),
|
||||
)
|
||||
|
||||
return AppConfig(
|
||||
path=path,
|
||||
service_autostart=bool(service.get("autostart", True)),
|
||||
tray_autostart=bool(tray.get("autostart", True)),
|
||||
watch=WatchConfig(
|
||||
include_dirs=include_dirs,
|
||||
include_extensions=include_extensions,
|
||||
exclude_dirs=exclude_dirs,
|
||||
exclude_patterns=tuple(watch.get("exclude_patterns", [])),
|
||||
),
|
||||
backup=BackupConfig(
|
||||
server_host=str(backup.get("server_host", "backup-server")),
|
||||
remote_destination=str(backup.get("remote_destination", "")),
|
||||
rsync_path=str(backup.get("rsync_path", "rsync")),
|
||||
shutdown_on_success=bool(backup.get("shutdown_on_success", True)),
|
||||
shutdown_command=str(backup.get("shutdown_command", "shutdown /s /t 0")),
|
||||
server_check=ServerCheckConfig(
|
||||
type=str(server_check.get("type", "tcp")).lower(),
|
||||
port=int(server_check.get("port", 22)),
|
||||
interval_seconds=int(server_check.get("interval_seconds", 300)),
|
||||
timeout_seconds=int(server_check.get("timeout_seconds", 3)),
|
||||
),
|
||||
),
|
||||
storage=storage_config,
|
||||
stability=StabilityConfig(
|
||||
min_age_seconds=int(stability.get("min_age_seconds", 60)),
|
||||
unchanged_check_interval_seconds=int(
|
||||
stability.get("unchanged_check_interval_seconds", 5)
|
||||
),
|
||||
unchanged_checks_required=int(stability.get("unchanged_checks_required", 2)),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _normalize_extension(value: str) -> str:
|
||||
value = value.strip().lower()
|
||||
if not value:
|
||||
return value
|
||||
return value if value.startswith(".") else f".{value}"
|
||||
|
||||
|
||||
def _normalize_exclude_dir(value: str) -> Path | str:
|
||||
expanded = expand_path(value)
|
||||
if "%" in value or not expanded.is_absolute():
|
||||
return value.lower()
|
||||
return expanded
|
||||
Reference in New Issue
Block a user