from __future__ import annotations import os from pathlib import Path from pobsync.snapshot_meta import resolve_host_root from .models import GlobalConfig, HostConfig from .self_check import SelfCheck from .ssh_keys import identity_path HOST_BACKUP_SUBDIRS = ("scheduled", "manual", ".incomplete") def ensure_host_directories(host: HostConfig, global_config: GlobalConfig | None = None) -> Path: global_config = global_config or GlobalConfig.objects.get(name="default") host_root = resolve_host_root(global_config.backup_root, host.host) for subdir in HOST_BACKUP_SUBDIRS: (host_root / subdir).mkdir(parents=True, exist_ok=True) return host_root def collect_host_checks(host: HostConfig, global_config: GlobalConfig | None = None) -> list[SelfCheck]: checks: list[SelfCheck] = [] try: global_config = global_config or GlobalConfig.objects.get(name="default") except GlobalConfig.DoesNotExist: return [SelfCheck("Host global config", "failed", "Default global config does not exist.")] checks.append( SelfCheck( "Host enabled", "ok" if host.enabled else "warning", "Host is enabled." if host.enabled else "Host is disabled.", ) ) checks.append( SelfCheck( "Host address", "ok" if host.address.strip() else "failed", host.address.strip() or "Host address is empty.", ) ) credential = host.ssh_credential or global_config.default_ssh_credential if credential is None: checks.append(SelfCheck("Host SSH credential", "warning", "No host or global SSH credential selected.")) else: checks.append(SelfCheck("Host SSH credential", "ok", str(credential))) if credential.key_path: key_path = identity_path(credential) checks.append( _host_path_check("Host SSH key file", key_path, must_exist=True, must_be_writable=False, must_be_readable=True) ) elif credential.private_key: checks.append( SelfCheck( "Host SSH key storage", "warning", "Selected credential stores private key material in the database.", "Generated filesystem keys are recommended for native systemd installs.", ) ) host_root = resolve_host_root(global_config.backup_root, host.host) checks.append(_host_path_check("Host backup root", host_root, must_exist=True, must_be_writable=True)) for subdir in HOST_BACKUP_SUBDIRS: checks.append(_host_path_check(f"Host directory: {subdir}", host_root / subdir, must_exist=True, must_be_writable=True)) return checks def _host_path_check( name: str, path: Path, *, must_exist: bool, must_be_writable: bool, must_be_readable: bool = False, ) -> SelfCheck: if must_exist and not path.exists(): return SelfCheck(name, "failed", f"{path} does not exist.") target = path if path.exists() else path.parent if not target.exists(): return SelfCheck(name, "failed", f"{target} does not exist.") if must_be_writable and not os.access(target, os.W_OK): return SelfCheck(name, "failed", f"{target} is not writable by this process.") if must_be_readable and not os.access(target, os.R_OK): return SelfCheck(name, "failed", f"{target} is not readable by this process.") return SelfCheck(name, "ok", str(path))