diff --git a/kiauh/components/webui_client/base_data.py b/kiauh/components/webui_client/base_data.py index 3e7ce42..f46798b 100644 --- a/kiauh/components/webui_client/base_data.py +++ b/kiauh/components/webui_client/base_data.py @@ -9,7 +9,8 @@ from __future__ import annotations -from abc import ABC, abstractmethod +from abc import ABC +from dataclasses import dataclass from enum import Enum from pathlib import Path @@ -24,89 +25,32 @@ class WebClientConfigType(Enum): FLUIDD: str = "fluidd-config" +@dataclass() class BaseWebClient(ABC): """Base class for webclient data""" - @property - @abstractmethod - def client(self) -> WebClientType: - raise NotImplementedError - - @property - @abstractmethod - def name(self) -> str: - raise NotImplementedError - - @property - @abstractmethod - def display_name(self) -> str: - raise NotImplementedError - - @property - @abstractmethod - def client_dir(self) -> Path: - raise NotImplementedError - - @property - @abstractmethod - def backup_dir(self) -> Path: - raise NotImplementedError - - @property - @abstractmethod - def repo_path(self) -> str: - raise NotImplementedError - - @property - @abstractmethod - def download_url(self) -> str: - raise NotImplementedError - - @property - @abstractmethod - def client_config(self) -> BaseWebClientConfig: - raise NotImplementedError + client: WebClientType + name: str + display_name: str + client_dir: Path + config_file: Path + backup_dir: Path + repo_path: str + download_url: str + nginx_access_log: Path + nginx_error_log: Path + client_config: BaseWebClientConfig +@dataclass() class BaseWebClientConfig(ABC): """Base class for webclient config data""" - @property - @abstractmethod - def client_config(self) -> WebClientConfigType: - raise NotImplementedError - - @property - @abstractmethod - def name(self) -> str: - raise NotImplementedError - - @property - @abstractmethod - def display_name(self) -> str: - raise NotImplementedError - - @property - @abstractmethod - def config_filename(self) -> str: - raise NotImplementedError - - @property - @abstractmethod - def config_dir(self) -> Path: - raise NotImplementedError - - @property - @abstractmethod - def backup_dir(self) -> Path: - raise NotImplementedError - - @property - @abstractmethod - def repo_url(self) -> str: - raise NotImplementedError - - @property - @abstractmethod - def config_section(self) -> str: - raise NotImplementedError + client_config: WebClientConfigType + name: str + display_name: str + config_filename: str + config_dir: Path + backup_dir: Path + repo_url: str + config_section: str diff --git a/kiauh/components/webui_client/client_remove.py b/kiauh/components/webui_client/client_remove.py index 44603de..292d1af 100644 --- a/kiauh/components/webui_client/client_remove.py +++ b/kiauh/components/webui_client/client_remove.py @@ -6,26 +6,23 @@ # # # This file may be distributed under the terms of the GNU GPLv3 license # # ======================================================================= # - - from typing import List from components.klipper.klipper import Klipper from components.moonraker.moonraker import Moonraker from components.webui_client.base_data import ( BaseWebClient, - WebClientType, ) from components.webui_client.client_config.client_config_remove import ( run_client_config_removal, ) -from components.webui_client.client_utils import backup_mainsail_config_json +from core.backup_manager.backup_manager import BackupManager +from core.constants import NGINX_SITES_AVAILABLE, NGINX_SITES_ENABLED from core.instance_manager.instance_manager import InstanceManager from core.logger import Logger from utils.config_utils import remove_config_section from utils.fs_utils import ( - remove_nginx_config, - remove_nginx_logs, + remove_with_sudo, run_remove_routines, ) @@ -34,21 +31,22 @@ def run_client_removal( client: BaseWebClient, remove_client: bool, remove_client_cfg: bool, - backup_ms_config_json: bool, + backup_config: bool, ) -> None: mr_im = InstanceManager(Moonraker) mr_instances: List[Moonraker] = mr_im.instances kl_im = InstanceManager(Klipper) kl_instances: List[Klipper] = kl_im.instances - if backup_ms_config_json and client.client == WebClientType.MAINSAIL: - backup_mainsail_config_json() + if backup_config: + bm = BackupManager() + bm.backup_file(client.config_file) if remove_client: client_name = client.name remove_client_dir(client) - remove_nginx_config(client_name) - remove_nginx_logs(client_name, kl_instances) + remove_client_nginx_config(client_name) + remove_client_nginx_logs(client, kl_instances) section = f"update_manager {client_name}" remove_config_section(section, mr_instances) @@ -64,3 +62,24 @@ def run_client_removal( def remove_client_dir(client: BaseWebClient) -> None: Logger.print_status(f"Removing {client.display_name} ...") run_remove_routines(client.client_dir) + + +def remove_client_nginx_config(name: str) -> None: + Logger.print_status(f"Removing NGINX config for {name.capitalize()} ...") + + remove_with_sudo(NGINX_SITES_AVAILABLE.joinpath(name)) + remove_with_sudo(NGINX_SITES_ENABLED.joinpath(name)) + + +def remove_client_nginx_logs(client: BaseWebClient, instances: List[Klipper]) -> None: + Logger.print_status(f"Removing NGINX logs for {client.display_name} ...") + + remove_with_sudo(client.nginx_access_log) + remove_with_sudo(client.nginx_error_log) + + if not instances: + return + + for instance in instances: + run_remove_routines(instance.log_dir.joinpath(client.nginx_access_log.name)) + run_remove_routines(instance.log_dir.joinpath(client.nginx_error_log.name)) diff --git a/kiauh/components/webui_client/client_setup.py b/kiauh/components/webui_client/client_setup.py index 05a6c5e..22f6a2b 100644 --- a/kiauh/components/webui_client/client_setup.py +++ b/kiauh/components/webui_client/client_setup.py @@ -6,7 +6,8 @@ # # # This file may be distributed under the terms of the GNU GPLv3 license # # ======================================================================= # - +import shutil +import tempfile from pathlib import Path from typing import List @@ -27,10 +28,8 @@ from components.webui_client.client_dialogs import ( print_moonraker_not_found_dialog, ) from components.webui_client.client_utils import ( - backup_mainsail_config_json, detect_client_cfg_conflict, enable_mainsail_remotemode, - restore_mainsail_config_json, symlink_webui_nginx_log, ) from core.instance_manager.instance_manager import InstanceManager @@ -185,10 +184,10 @@ def update_client(client: BaseWebClient) -> None: ) return - if client.client == WebClientType.MAINSAIL: - backup_mainsail_config_json(is_temp=True) - - download_client(client) - - if client.client == WebClientType.MAINSAIL: - restore_mainsail_config_json() + with tempfile.NamedTemporaryFile(suffix=".json") as tmp_file: + Logger.print_status( + f"Creating temporary backup of {client.config_file} as {tmp_file.name} ..." + ) + shutil.copy(client.config_file, tmp_file.name) + download_client(client) + shutil.copy(tmp_file.name, client.config_file) diff --git a/kiauh/components/webui_client/client_utils.py b/kiauh/components/webui_client/client_utils.py index 9240bb0..332721c 100644 --- a/kiauh/components/webui_client/client_utils.py +++ b/kiauh/components/webui_client/client_utils.py @@ -9,7 +9,6 @@ from __future__ import annotations import json -import shutil from pathlib import Path from typing import List, get_args @@ -78,26 +77,6 @@ def get_current_client_config(clients: List[BaseWebClient]) -> str: return f"{COLOR_CYAN}-{RESET_FORMAT}" -def backup_mainsail_config_json(is_temp: bool = False) -> None: - c_json = MainsailData().client_dir.joinpath("config.json") - bm = BackupManager() - if is_temp: - fn = Path.home().joinpath("config.json.kiauh.bak") - bm.backup_file(c_json, custom_filename=fn) - else: - bm.backup_file(c_json) - - -def restore_mainsail_config_json() -> None: - try: - c_json = MainsailData().client_dir.joinpath("config.json") - Logger.print_status(f"Restore '{c_json}' ...") - source = Path.home().joinpath("config.json.kiauh.bak") - shutil.copy(source, c_json) - except OSError: - Logger.print_info("Unable to restore config.json. Skipped ...") - - def enable_mainsail_remotemode() -> None: Logger.print_status("Enable Mainsails remote mode ...") c_json = MainsailData().client_dir.joinpath("config.json") diff --git a/kiauh/components/webui_client/fluidd_data.py b/kiauh/components/webui_client/fluidd_data.py index fb96ec4..b499351 100644 --- a/kiauh/components/webui_client/fluidd_data.py +++ b/kiauh/components/webui_client/fluidd_data.py @@ -21,7 +21,7 @@ from components.webui_client.base_data import ( from core.backup_manager import BACKUP_ROOT_DIR -@dataclass(frozen=True) +@dataclass() class FluiddConfigWeb(BaseWebClientConfig): client_config: WebClientConfigType = WebClientConfigType.FLUIDD name: str = client_config.value @@ -33,7 +33,7 @@ class FluiddConfigWeb(BaseWebClientConfig): repo_url: str = "https://github.com/fluidd-core/fluidd-config.git" -@dataclass(frozen=True) +@dataclass() class FluiddData(BaseWebClient): BASE_DL_URL = "https://github.com/fluidd-core/fluidd/releases" @@ -41,16 +41,16 @@ class FluiddData(BaseWebClient): name: str = client.value display_name: str = name.capitalize() client_dir: Path = Path.home().joinpath("fluidd") + config_file: Path = client_dir.joinpath("config.json") backup_dir: Path = BACKUP_ROOT_DIR.joinpath("fluidd-backups") repo_path: str = "fluidd-core/fluidd" + nginx_access_log: Path = Path("/var/log/nginx/fluidd-access.log") + nginx_error_log: Path = Path("/var/log/nginx/fluidd-error.log") + client_config: BaseWebClientConfig = None + download_url: str | None = None - @property - def download_url(self) -> str: + def __post_init__(self): from components.webui_client.client_utils import get_download_url - url: str = get_download_url(self.BASE_DL_URL, self) - return url - - @property - def client_config(self) -> BaseWebClientConfig: - return FluiddConfigWeb() + self.client_config = FluiddConfigWeb() + self.download_url = get_download_url(self.BASE_DL_URL, self) diff --git a/kiauh/components/webui_client/mainsail_data.py b/kiauh/components/webui_client/mainsail_data.py index 7193f7c..1d520a1 100644 --- a/kiauh/components/webui_client/mainsail_data.py +++ b/kiauh/components/webui_client/mainsail_data.py @@ -21,7 +21,7 @@ from components.webui_client.base_data import ( from core.backup_manager import BACKUP_ROOT_DIR -@dataclass(frozen=True) +@dataclass() class MainsailConfigWeb(BaseWebClientConfig): client_config: WebClientConfigType = WebClientConfigType.MAINSAIL name: str = client_config.value @@ -33,7 +33,7 @@ class MainsailConfigWeb(BaseWebClientConfig): repo_url: str = "https://github.com/mainsail-crew/mainsail-config.git" -@dataclass(frozen=True) +@dataclass() class MainsailData(BaseWebClient): BASE_DL_URL: str = "https://github.com/mainsail-crew/mainsail/releases" @@ -41,16 +41,16 @@ class MainsailData(BaseWebClient): name: str = WebClientType.MAINSAIL.value display_name: str = name.capitalize() client_dir: Path = Path.home().joinpath("mainsail") + config_file: Path = client_dir.joinpath("config.json") backup_dir: Path = BACKUP_ROOT_DIR.joinpath("mainsail-backups") repo_path: str = "mainsail-crew/mainsail" + nginx_access_log: Path = Path("/var/log/nginx/mainsail-access.log") + nginx_error_log: Path = Path("/var/log/nginx/mainsail-error.log") + client_config: BaseWebClientConfig = None + download_url: str | None = None - @property - def download_url(self) -> str: + def __post_init__(self): from components.webui_client.client_utils import get_download_url - url: str = get_download_url(self.BASE_DL_URL, self) - return url - - @property - def client_config(self) -> BaseWebClientConfig: - return MainsailConfigWeb() + self.client_config = MainsailConfigWeb() + self.download_url = get_download_url(self.BASE_DL_URL, self) diff --git a/kiauh/components/webui_client/menus/client_remove_menu.py b/kiauh/components/webui_client/menus/client_remove_menu.py index 1c850a6..0041c0a 100644 --- a/kiauh/components/webui_client/menus/client_remove_menu.py +++ b/kiauh/components/webui_client/menus/client_remove_menu.py @@ -12,7 +12,7 @@ import textwrap from typing import Type from components.webui_client import client_remove -from components.webui_client.base_data import BaseWebClient, WebClientType +from components.webui_client.base_data import BaseWebClient from core.constants import COLOR_CYAN, COLOR_RED, RESET_FORMAT from core.menus import Option from core.menus.base_menu import BaseMenu @@ -28,7 +28,7 @@ class ClientRemoveMenu(BaseMenu): self.client: BaseWebClient = client self.remove_client: bool = False self.remove_client_cfg: bool = False - self.backup_mainsail_config_json: bool = False + self.backup_config_json: bool = False self.selection_state: bool = False def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: @@ -41,10 +41,9 @@ class ClientRemoveMenu(BaseMenu): "a": Option(method=self.toggle_all, menu=False), "1": Option(method=self.toggle_rm_client, menu=False), "2": Option(method=self.toggle_rm_client_config, menu=False), + "3": Option(method=self.toggle_backup_config_json, menu=False), "c": Option(method=self.run_removal_process, menu=False), } - if self.client.client == WebClientType.MAINSAIL: - self.options["3"] = Option(self.toggle_backup_mainsail_config_json, False) def print_menu(self) -> None: client_name = self.client.display_name @@ -58,6 +57,7 @@ class ClientRemoveMenu(BaseMenu): unchecked = "[ ]" o1 = checked if self.remove_client else unchecked o2 = checked if self.remove_client_cfg else unchecked + o3 = checked if self.backup_config_json else unchecked menu = textwrap.dedent( f""" ╔═══════════════════════════════════════════════════════╗ @@ -70,19 +70,7 @@ class ClientRemoveMenu(BaseMenu): ╟───────────────────────────────────────────────────────╢ ║ 1) {o1} Remove {client_name:16} ║ ║ 2) {o2} Remove {client_config_name:24} ║ - """ - )[1:] - - if self.client.client == WebClientType.MAINSAIL: - o3 = checked if self.backup_mainsail_config_json else unchecked - menu += textwrap.dedent( - f""" - ║ 3) {o3} Backup config.json ║ - """ - )[1:] - - menu += textwrap.dedent( - """ + ║ 3) {o3} Backup config.json ║ ╟───────────────────────────────────────────────────────╢ ║ C) Continue ║ ╟───────────────────────────────────────────────────────╢ @@ -94,7 +82,7 @@ class ClientRemoveMenu(BaseMenu): self.selection_state = not self.selection_state self.remove_client = self.selection_state self.remove_client_cfg = self.selection_state - self.backup_mainsail_config_json = self.selection_state + self.backup_config_json = self.selection_state def toggle_rm_client(self, **kwargs) -> None: self.remove_client = not self.remove_client @@ -102,14 +90,14 @@ class ClientRemoveMenu(BaseMenu): def toggle_rm_client_config(self, **kwargs) -> None: self.remove_client_cfg = not self.remove_client_cfg - def toggle_backup_mainsail_config_json(self, **kwargs) -> None: - self.backup_mainsail_config_json = not self.backup_mainsail_config_json + def toggle_backup_config_json(self, **kwargs) -> None: + self.backup_config_json = not self.backup_config_json def run_removal_process(self, **kwargs) -> None: if ( not self.remove_client and not self.remove_client_cfg - and not self.backup_mainsail_config_json + and not self.backup_config_json ): error = f"{COLOR_RED}Nothing selected ...{RESET_FORMAT}" print(error) @@ -119,12 +107,12 @@ class ClientRemoveMenu(BaseMenu): client=self.client, remove_client=self.remove_client, remove_client_cfg=self.remove_client_cfg, - backup_ms_config_json=self.backup_mainsail_config_json, + backup_config=self.backup_config_json, ) self.remove_client = False self.remove_client_cfg = False - self.backup_mainsail_config_json = False + self.backup_config_json = False self._go_back() diff --git a/kiauh/utils/fs_utils.py b/kiauh/utils/fs_utils.py index 734c9ad..d2b2dcc 100644 --- a/kiauh/utils/fs_utils.py +++ b/kiauh/utils/fs_utils.py @@ -16,7 +16,6 @@ from subprocess import DEVNULL, PIPE, CalledProcessError, check_output, run from typing import List from zipfile import ZipFile -from components.klipper.klipper import Klipper from core.constants import ( NGINX_CONFD, NGINX_SITES_AVAILABLE, @@ -236,24 +235,3 @@ def get_next_free_port(ports_in_use: List[int]) -> int: used_ports = set(map(int, ports_in_use)) return min(valid_ports - used_ports) - - -def remove_nginx_config(name: str) -> None: - Logger.print_status(f"Removing NGINX config for {name.capitalize()} ...") - - run_remove_routines(NGINX_SITES_AVAILABLE.joinpath(name)) - run_remove_routines(NGINX_SITES_ENABLED.joinpath(name)) - - -def remove_nginx_logs(name: str, instances: List[Klipper]) -> None: - Logger.print_status(f"Removing NGINX logs for {name.capitalize()} ...") - - run_remove_routines(Path(f"/var/log/nginx/{name}-access.log")) - run_remove_routines(Path(f"/var/log/nginx/{name}-error.log")) - - if not instances: - return - - for instance in instances: - run_remove_routines(instance.log_dir.joinpath(f"{name}-access.log")) - run_remove_routines(instance.log_dir.joinpath(f"{name}-error.log"))