From c8df9427b396ab51debdbce6c86678f35caece2b Mon Sep 17 00:00:00 2001 From: dw-0 Date: Sat, 31 Jan 2026 10:59:53 +0100 Subject: [PATCH] fix(backup): improve reusability of backup service and enhance file handling - Refactor `BackupService` instance management for better reuse across methods. - Avoid redundant file backups by checking for existing files. - Enhance directory backup logic to handle nested files and directories more efficiently. - Standardize timestamp initialization for consistent time-based backup operations. --- .../client_config/client_config_remove.py | 8 +++-- .../components/webui_client/client_remove.py | 5 +-- kiauh/core/services/backup_service.py | 34 ++++++++++++++----- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/kiauh/components/webui_client/client_config/client_config_remove.py b/kiauh/components/webui_client/client_config/client_config_remove.py index 5e89f00..a6d8343 100644 --- a/kiauh/components/webui_client/client_config/client_config_remove.py +++ b/kiauh/components/webui_client/client_config/client_config_remove.py @@ -8,7 +8,7 @@ # ======================================================================= # -from typing import List +from typing import List, Optional from components.klipper.klipper import Klipper from components.moonraker.moonraker import Moonraker @@ -27,6 +27,7 @@ def run_client_config_removal( client_config: BaseWebClientConfig, kl_instances: List[Klipper], mr_instances: List[Moonraker], + svc: Optional[BackupService] = None, ) -> Message: completion_msg = Message( title=f"{client_config.display_name} Removal Process completed", @@ -36,12 +37,15 @@ def run_client_config_removal( if run_remove_routines(client_config.config_dir): completion_msg.text.append(f"● {client_config.display_name} removed") - BackupService().backup_printer_config_dir() + if svc is None: + svc = BackupService() + svc.backup_moonraker_conf() completion_msg = remove_moonraker_config_section( completion_msg, client_config, mr_instances ) + svc.backup_printer_cfg() completion_msg = remove_printer_config_section( completion_msg, client_config, kl_instances ) diff --git a/kiauh/components/webui_client/client_remove.py b/kiauh/components/webui_client/client_remove.py index 6417d0f..8a6fe86 100644 --- a/kiauh/components/webui_client/client_remove.py +++ b/kiauh/components/webui_client/client_remove.py @@ -41,6 +41,7 @@ def run_client_removal( ) mr_instances: List[Moonraker] = get_instances(Moonraker) kl_instances: List[Klipper] = get_instances(Klipper) + svc = BackupService() if backup_config: version = "" @@ -49,7 +50,6 @@ def run_client_removal( with open(src.joinpath(".version"), "r") as v: version = v.readlines()[0] - svc = BackupService() target_path = svc.backup_root.joinpath(f"{client.client_dir.name}_{version}") success = svc.backup_file( source_path=client.config_file, @@ -67,7 +67,7 @@ def run_client_removal( if remove_client_nginx_logs(client, kl_instances): completion_msg.text.append("● NGINX logs removed") - BackupService().backup_moonraker_conf() + svc.backup_moonraker_conf() section = f"update_manager {client_name}" handled_instances: List[Moonraker] = remove_config_section( section, mr_instances @@ -83,6 +83,7 @@ def run_client_removal( client.client_config, kl_instances, mr_instances, + svc, ) if cfg_completion_msg.color == Color.GREEN: completion_msg.text.extend(cfg_completion_msg.text[1:]) diff --git a/kiauh/core/services/backup_service.py b/kiauh/core/services/backup_service.py index c912b5e..f88a8c3 100644 --- a/kiauh/core/services/backup_service.py +++ b/kiauh/core/services/backup_service.py @@ -22,6 +22,7 @@ from utils.instance_utils import get_instances class BackupService: def __init__(self): self._backup_root = Path.home().joinpath("kiauh_backups") + self._timestamp = datetime.now().strftime("%Y%m%d-%H%M%S") @property def backup_root(self) -> Path: @@ -29,7 +30,7 @@ class BackupService: @property def timestamp(self) -> str: - return datetime.now().strftime("%Y%m%d-%H%M%S") + return self._timestamp ################################################ # GENERIC BACKUP METHODS @@ -69,6 +70,10 @@ class BackupService: backup_dir.mkdir(parents=True, exist_ok=True) target_path = backup_dir.joinpath(filename) + if target_path.exists(): + Logger.print_info(f"File '{target_path}' already exists. Skipping ...") + return True + shutil.copy2(source_path, target_path) Logger.print_ok( @@ -112,14 +117,25 @@ class BackupService: if backup_path.exists(): Logger.print_info(f"Reusing existing backup directory '{backup_path}'") - - shutil.copytree( - source_path, - backup_path, - dirs_exist_ok=True, - symlinks=True, - ignore_dangling_symlinks=True, - ) + for item in source_path.rglob("*"): + relative_path = item.relative_to(source_path) + target_item = backup_path.joinpath(relative_path) + if item.is_file(): + if not target_item.exists(): + target_item.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(item, target_item) + else: + Logger.print_info(f"File '{target_item}' already exists. Skipping...") + elif item.is_dir(): + target_item.mkdir(parents=True, exist_ok=True) + else: + shutil.copytree( + source_path, + backup_path, + dirs_exist_ok=True, + symlinks=True, + ignore_dangling_symlinks=True, + ) Logger.print_ok( f"Successfully backed up '{source_path}' to '{backup_path}'"