From 81b7b156b9e6c49c13268ee3d317f177a13dc853 Mon Sep 17 00:00:00 2001 From: dw-0 Date: Thu, 24 Oct 2024 12:25:44 +0200 Subject: [PATCH] feat: implement port reconfiguration for webclients (#586) Signed-off-by: Dominik Willner --- .../client_config/client_config_setup.py | 5 +- .../components/webui_client/client_dialogs.py | 4 +- kiauh/components/webui_client/client_setup.py | 73 +++++++----- kiauh/components/webui_client/client_utils.py | 37 ++++++- .../webui_client/menus/client_install_menu.py | 104 ++++++++++++++++++ kiauh/core/menus/install_menu.py | 24 +++- kiauh/utils/config_utils.py | 3 + 7 files changed, 205 insertions(+), 45 deletions(-) create mode 100644 kiauh/components/webui_client/menus/client_install_menu.py diff --git a/kiauh/components/webui_client/client_config/client_config_setup.py b/kiauh/components/webui_client/client_config/client_config_setup.py index 1e9a54c..905ad8e 100644 --- a/kiauh/components/webui_client/client_config/client_config_setup.py +++ b/kiauh/components/webui_client/client_config/client_config_setup.py @@ -34,7 +34,7 @@ from utils.input_utils import get_confirm from utils.instance_utils import get_instances -def install_client_config(client_data: BaseWebClient) -> None: +def install_client_config(client_data: BaseWebClient, cfg_backup=True) -> None: client_config: BaseWebClientConfig = client_data.client_config display_name = client_config.display_name @@ -56,7 +56,8 @@ def install_client_config(client_data: BaseWebClient) -> None: download_client_config(client_config) create_client_config_symlink(client_config, kl_instances) - backup_printer_config_dir() + if cfg_backup: + backup_printer_config_dir() add_config_section( section=f"update_manager {client_config.name}", diff --git a/kiauh/components/webui_client/client_dialogs.py b/kiauh/components/webui_client/client_dialogs.py index e6fd5ab..0bb889c 100644 --- a/kiauh/components/webui_client/client_dialogs.py +++ b/kiauh/components/webui_client/client_dialogs.py @@ -53,8 +53,8 @@ def print_client_port_select_dialog( dialog_content.extend( [ "\n\n", - "The following ports were found to be in use already:", - *[f"● {port}" for port in ports_in_use], + "The following ports were found to be already in use:", + *[f"● {p}" for p in ports_in_use if p != port], ] ) diff --git a/kiauh/components/webui_client/client_setup.py b/kiauh/components/webui_client/client_setup.py index 7db8218..89bcac8 100644 --- a/kiauh/components/webui_client/client_setup.py +++ b/kiauh/components/webui_client/client_setup.py @@ -36,8 +36,9 @@ from components.webui_client.client_utils import ( symlink_webui_nginx_log, ) from core.instance_manager.instance_manager import InstanceManager -from core.logger import Logger -from utils.common import check_install_dependencies +from core.logger import DialogCustomColor, DialogType, Logger +from core.settings.kiauh_settings import KiauhSettings +from utils.common import backup_printer_config_dir, check_install_dependencies from utils.config_utils import add_config_section from utils.fs_utils import unzip from utils.input_utils import get_confirm @@ -49,16 +50,11 @@ from utils.sys_utils import ( ) -def install_client(client: BaseWebClient) -> None: - if client is None: - raise ValueError("Missing parameter client_data!") - - if client.client_dir.exists(): - Logger.print_info( - f"{client.display_name} seems to be already installed! Skipped ..." - ) - return - +def install_client( + client: BaseWebClient, + settings: KiauhSettings, + reinstall: bool = False, +) -> None: mr_instances: List[Moonraker] = get_instances(Moonraker) enable_remotemode = False @@ -88,7 +84,10 @@ def install_client(client: BaseWebClient) -> None: question = f"Download the recommended {client_config.display_name}?" install_client_cfg = get_confirm(question, allow_go_back=False) - port: int = get_client_port_selection(client) + default_port: int = int(settings.get(client.name, "port")) + port: int = ( + default_port if reinstall else get_client_port_selection(client, settings) + ) check_install_dependencies({"nginx"}) @@ -96,20 +95,22 @@ def install_client(client: BaseWebClient) -> None: download_client(client) if enable_remotemode and client.client == WebClientType.MAINSAIL: enable_mainsail_remotemode() - if mr_instances: - add_config_section( - section=f"update_manager {client.name}", - instances=mr_instances, - options=[ - ("type", "web"), - ("channel", "stable"), - ("repo", str(client.repo_path)), - ("path", str(client.client_dir)), - ], - ) - InstanceManager.restart_all(mr_instances) + + backup_printer_config_dir() + add_config_section( + section=f"update_manager {client.name}", + instances=mr_instances, + options=[ + ("type", "web"), + ("channel", "stable"), + ("repo", str(client.repo_path)), + ("path", str(client.client_dir)), + ], + ) + InstanceManager.restart_all(mr_instances) + if install_client_cfg and kl_instances: - install_client_config(client) + install_client_config(client, False) copy_upstream_nginx_cfg() copy_common_vars_nginx_cfg() @@ -127,12 +128,24 @@ def install_client(client: BaseWebClient) -> None: cmd_sysctl_service("nginx", "restart") except Exception as e: - Logger.print_error(f"{client.display_name} installation failed!\n{e}") + Logger.print_error(e) + Logger.print_dialog( + DialogType.ERROR, + center_content=True, + content=[f"{client.display_name} installation failed!"], + ) return - log = f"Open {client.display_name} now on: http://{get_ipv4_addr()}:{port}" - Logger.print_ok(f"{client.display_name} installation complete!", start="\n") - Logger.print_ok(log, prefix=False, end="\n\n") + # noinspection HttpUrlsUsage + Logger.print_dialog( + DialogType.CUSTOM, + custom_title=f"{client.display_name} installation complete!", + custom_color=DialogCustomColor.GREEN, + center_content=True, + content=[ + f"Open {client.display_name} now on: http://{get_ipv4_addr()}:{port}", + ], + ) def download_client(client: BaseWebClient) -> None: diff --git a/kiauh/components/webui_client/client_utils.py b/kiauh/components/webui_client/client_utils.py index 206f0b2..0141160 100644 --- a/kiauh/components/webui_client/client_utils.py +++ b/kiauh/components/webui_client/client_utils.py @@ -370,19 +370,26 @@ def read_ports_from_nginx_configs() -> List[int]: return sorted(ports_to_ints_list, key=lambda x: int(x)) -def get_client_port_selection(client: BaseWebClient) -> int: - settings = KiauhSettings() +def get_client_port_selection( + client: BaseWebClient, + settings: KiauhSettings, + reconfigure=False, +) -> int: default_port: int = int(settings.get(client.name, "port")) - ports_in_use: List[int] = read_ports_from_nginx_configs() next_free_port: int = get_next_free_port(ports_in_use) - port: int = next_free_port if default_port in ports_in_use else default_port + port: int = ( + next_free_port + if not reconfigure and default_port in ports_in_use + else default_port + ) print_client_port_select_dialog(client.display_name, port, ports_in_use) while True: - question = f"Configure {client.display_name} for port" + _type = "Reconfigure" if reconfigure else "Configure" + question = f"{_type} {client.display_name} for port" port_input = get_number_input(question, min_count=80, default=port) if port_input not in ports_in_use: @@ -400,3 +407,23 @@ 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 set_listen_port(client: BaseWebClient, curr_port: int, new_port: int) -> None: + """ + Set the port the client should listen on in the NGINX config + :param curr_port: The current port the client listens on + :param new_port: The new port to set + :param client: The client to set the port for + :return: None + """ + config = NGINX_SITES_AVAILABLE.joinpath(client.name) + with open(config, "r") as f: + lines = f.readlines() + + for i, line in enumerate(lines): + if "listen" in line: + lines[i] = line.replace(str(curr_port), str(new_port)) + + with open(config, "w") as f: + f.writelines(lines) diff --git a/kiauh/components/webui_client/menus/client_install_menu.py b/kiauh/components/webui_client/menus/client_install_menu.py new file mode 100644 index 0000000..a741888 --- /dev/null +++ b/kiauh/components/webui_client/menus/client_install_menu.py @@ -0,0 +1,104 @@ +# ======================================================================= # +# Copyright (C) 2020 - 2024 Dominik Willner # +# # +# This file is part of KIAUH - Klipper Installation And Update Helper # +# https://github.com/dw-0/kiauh # +# # +# This file may be distributed under the terms of the GNU GPLv3 license # +# ======================================================================= # +from __future__ import annotations + +import textwrap +from typing import Type + +from components.webui_client.base_data import BaseWebClient +from components.webui_client.client_setup import install_client +from components.webui_client.client_utils import ( + get_client_port_selection, + set_listen_port, +) +from core.constants import COLOR_CYAN, COLOR_GREEN, RESET_FORMAT +from core.logger import DialogCustomColor, DialogType, Logger +from core.menus import Option +from core.menus.base_menu import BaseMenu +from core.settings.kiauh_settings import KiauhSettings, WebUiSettings +from utils.sys_utils import cmd_sysctl_service, get_ipv4_addr + + +# noinspection PyUnusedLocal +class ClientInstallMenu(BaseMenu): + def __init__( + self, client: BaseWebClient, previous_menu: Type[BaseMenu] | None = None + ): + super().__init__() + self.previous_menu: Type[BaseMenu] | None = previous_menu + self.client: BaseWebClient = client + self.settings = KiauhSettings() + self.client_settings: WebUiSettings = self.settings[client.name] + + def set_previous_menu(self, previous_menu: Type[BaseMenu] | None) -> None: + from core.menus.install_menu import InstallMenu + + self.previous_menu = previous_menu if previous_menu is not None else InstallMenu + + def set_options(self) -> None: + self.options = { + "1": Option(method=self.reinstall_client), + "2": Option(method=self.change_listen_port), + } + + def print_menu(self) -> None: + client_name = self.client.display_name + + header = f" [ Installation Menu > {client_name} ] " + color = COLOR_GREEN + count = 62 - len(color) - len(RESET_FORMAT) + port = f"(Current: {COLOR_CYAN}{int(self.client_settings.port)}{RESET_FORMAT})" + menu = textwrap.dedent( + f""" + ╔═══════════════════════════════════════════════════════╗ + ║ {color}{header:~^{count}}{RESET_FORMAT} ║ + ╟───────────────────────────────────────────────────────╢ + ║ 1) Reinstall {client_name:16} ║ + ║ 2) Reconfigure Listen Port {port:<34} ║ + ╟───────────────────────────────────────────────────────╢ + """ + )[1:] + print(menu, end="") + + def reinstall_client(self, **kwargs) -> None: + install_client(self.client, settings=self.settings, reinstall=True) + + def change_listen_port(self, **kwargs) -> None: + curr_port = int(self.client_settings.port) + new_port = get_client_port_selection( + self.client, + self.settings, + reconfigure=True, + ) + + cmd_sysctl_service("nginx", "stop") + set_listen_port(self.client, curr_port, new_port) + + Logger.print_status("Saving new port configuration ...") + self.client_settings.port = new_port + self.settings.save() + Logger.print_ok("Port configuration saved!") + + cmd_sysctl_service("nginx", "start") + + # noinspection HttpUrlsUsage + Logger.print_dialog( + DialogType.CUSTOM, + custom_title="Port reconfiguration complete!", + custom_color=DialogCustomColor.GREEN, + center_content=True, + content=[ + f"Open {self.client.display_name} now on: " + f"http://{get_ipv4_addr()}:{new_port}", + ], + ) + + def _go_back(self, **kwargs) -> None: + if self.previous_menu is not None: + self.previous_menu().run() diff --git a/kiauh/core/menus/install_menu.py b/kiauh/core/menus/install_menu.py index 8318006..b3f7576 100644 --- a/kiauh/core/menus/install_menu.py +++ b/kiauh/core/menus/install_menu.py @@ -15,13 +15,17 @@ from components.crowsnest.crowsnest import install_crowsnest from components.klipper import klipper_setup from components.klipperscreen.klipperscreen import install_klipperscreen from components.moonraker import moonraker_setup -from components.webui_client import client_setup -from components.webui_client.client_config import client_config_setup +from components.webui_client.client_config.client_config_setup import ( + install_client_config, +) +from components.webui_client.client_setup import install_client from components.webui_client.fluidd_data import FluiddData from components.webui_client.mainsail_data import MainsailData +from components.webui_client.menus.client_install_menu import ClientInstallMenu from core.constants import COLOR_GREEN, RESET_FORMAT from core.menus import Option from core.menus.base_menu import BaseMenu +from core.settings.kiauh_settings import KiauhSettings # noinspection PyUnusedLocal @@ -80,16 +84,24 @@ class InstallMenu(BaseMenu): moonraker_setup.install_moonraker() def install_mainsail(self, **kwargs) -> None: - client_setup.install_client(MainsailData()) + client: MainsailData = MainsailData() + if client.client_dir.exists(): + ClientInstallMenu(client, self.__class__).run() + else: + install_client(client, settings=KiauhSettings()) def install_mainsail_config(self, **kwargs) -> None: - client_config_setup.install_client_config(MainsailData()) + install_client_config(MainsailData()) def install_fluidd(self, **kwargs) -> None: - client_setup.install_client(FluiddData()) + client: FluiddData = FluiddData() + if client.client_dir.exists(): + ClientInstallMenu(client, self.__class__).run() + else: + install_client(client, settings=KiauhSettings()) def install_fluidd_config(self, **kwargs) -> None: - client_config_setup.install_client_config(FluiddData()) + install_client_config(FluiddData()) def install_klipperscreen(self, **kwargs) -> None: install_klipperscreen() diff --git a/kiauh/utils/config_utils.py b/kiauh/utils/config_utils.py index 8264bc9..e9bbf14 100644 --- a/kiauh/utils/config_utils.py +++ b/kiauh/utils/config_utils.py @@ -27,6 +27,9 @@ def add_config_section( instances: List[InstanceType], options: List[ConfigOption] | None = None, ) -> None: + if not instances: + return + for instance in instances: cfg_file = instance.cfg_file Logger.print_status(f"Add section '[{section}]' to '{cfg_file}' ...")