Compare commits

...

2 Commits

Author SHA1 Message Date
dw-0
5d678c4ff2 refactor: rework webclient removal
* also backup fluidds config.json on update

Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-08-18 13:42:27 +02:00
dw-0
184c5ac3ff fix: skip if a symlink is broken
Signed-off-by: Dominik Willner <th33xitus@gmail.com>
2024-08-18 12:28:58 +02:00
8 changed files with 96 additions and 186 deletions

View File

@@ -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

View File

@@ -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))

View File

@@ -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)

View File

@@ -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")

View File

@@ -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)

View File

@@ -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)

View File

@@ -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()

View File

@@ -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,
@@ -211,6 +210,9 @@ def read_ports_from_nginx_configs() -> List[int]:
port_list = []
for config in NGINX_SITES_ENABLED.iterdir():
if not config.is_file():
continue
with open(config, "r") as cfg:
lines = cfg.readlines()
@@ -233,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"))