diff --git a/kiauh/utils/assets/common_vars.conf b/kiauh/components/webui_client/assets/common_vars.conf similarity index 100% rename from kiauh/utils/assets/common_vars.conf rename to kiauh/components/webui_client/assets/common_vars.conf diff --git a/kiauh/utils/assets/upstreams.conf b/kiauh/components/webui_client/assets/upstreams.conf similarity index 100% rename from kiauh/utils/assets/upstreams.conf rename to kiauh/components/webui_client/assets/upstreams.conf diff --git a/kiauh/components/webui_client/client_setup.py b/kiauh/components/webui_client/client_setup.py index 593911a..001a7c5 100644 --- a/kiauh/components/webui_client/client_setup.py +++ b/kiauh/components/webui_client/client_setup.py @@ -28,8 +28,14 @@ from components.webui_client.client_dialogs import ( print_moonraker_not_found_dialog, ) from components.webui_client.client_utils import ( + copy_common_vars_nginx_cfg, + copy_upstream_nginx_cfg, + create_nginx_cfg, detect_client_cfg_conflict, enable_mainsail_remotemode, + get_next_free_port, + is_valid_port, + read_ports_from_nginx_configs, symlink_webui_nginx_log, ) from core.instance_manager.instance_manager import InstanceManager @@ -37,15 +43,7 @@ from core.logger import Logger from core.settings.kiauh_settings import KiauhSettings from utils.common import check_install_dependencies from utils.config_utils import add_config_section -from utils.fs_utils import ( - copy_common_vars_nginx_cfg, - copy_upstream_nginx_cfg, - create_nginx_cfg, - get_next_free_port, - is_valid_port, - read_ports_from_nginx_configs, - unzip, -) +from utils.fs_utils import unzip from utils.input_utils import get_confirm, get_number_input from utils.sys_utils import ( cmd_sysctl_service, diff --git a/kiauh/components/webui_client/client_utils.py b/kiauh/components/webui_client/client_utils.py index 00fdc96..fb93e44 100644 --- a/kiauh/components/webui_client/client_utils.py +++ b/kiauh/components/webui_client/client_utils.py @@ -9,9 +9,14 @@ from __future__ import annotations import json +import re +import shutil +from pathlib import Path +from subprocess import PIPE, CalledProcessError, run from typing import List, get_args from components.klipper.klipper import Klipper +from components.webui_client import MODULE_PATH from components.webui_client.base_data import ( BaseWebClient, WebClientType, @@ -24,12 +29,14 @@ from core.constants import ( COLOR_YELLOW, NGINX_CONFD, NGINX_SITES_AVAILABLE, + NGINX_SITES_ENABLED, RESET_FORMAT, ) from core.logger import Logger from core.settings.kiauh_settings import KiauhSettings from core.types import ComponentStatus from utils.common import get_install_status +from utils.fs_utils import create_symlink, remove_file from utils.git_utils import ( get_latest_tag, get_latest_unstable_tag, @@ -205,3 +212,132 @@ def get_download_url(base_url: str, client: BaseWebClient) -> str: return f"{base_url}/download/{unstable_tag}/{client.name}.zip" except Exception: return stable_url + + +################################################# +## NGINX RELATED FUNCTIONS +################################################# + + +def copy_upstream_nginx_cfg() -> None: + """ + Creates an upstream.conf in /etc/nginx/conf.d + :return: None + """ + source = MODULE_PATH.joinpath("assets/upstreams.conf") + target = NGINX_CONFD.joinpath("upstreams.conf") + try: + command = ["sudo", "cp", source, target] + run(command, stderr=PIPE, check=True) + except CalledProcessError as e: + log = f"Unable to create upstreams.conf: {e.stderr.decode()}" + Logger.print_error(log) + raise + + +def copy_common_vars_nginx_cfg() -> None: + """ + Creates a common_vars.conf in /etc/nginx/conf.d + :return: None + """ + source = MODULE_PATH.joinpath("assets/common_vars.conf") + target = NGINX_CONFD.joinpath("common_vars.conf") + try: + command = ["sudo", "cp", source, target] + run(command, stderr=PIPE, check=True) + except CalledProcessError as e: + log = f"Unable to create upstreams.conf: {e.stderr.decode()}" + Logger.print_error(log) + raise + + +def generate_nginx_cfg_from_template(name: str, template_src: Path, **kwargs) -> None: + """ + Creates an NGINX config from a template file and + replaces all placeholders passed as kwargs. A placeholder must be defined + in the template file as %{placeholder}%. + :param name: name of the config to create + :param template_src: the path to the template file + :return: None + """ + tmp = Path.home().joinpath(f"{name}.tmp") + shutil.copy(template_src, tmp) + with open(tmp, "r+") as f: + content = f.read() + + for key, value in kwargs.items(): + content = content.replace(f"%{key}%", str(value)) + + f.seek(0) + f.write(content) + f.truncate() + + target = NGINX_SITES_AVAILABLE.joinpath(name) + try: + command = ["sudo", "mv", tmp, target] + run(command, stderr=PIPE, check=True) + except CalledProcessError as e: + log = f"Unable to create '{target}': {e.stderr.decode()}" + Logger.print_error(log) + raise + + +def create_nginx_cfg( + display_name: str, + cfg_name: str, + template_src: Path, + **kwargs, +) -> None: + from utils.sys_utils import set_nginx_permissions + + try: + Logger.print_status(f"Creating NGINX config for {display_name} ...") + + source = NGINX_SITES_AVAILABLE.joinpath(cfg_name) + target = NGINX_SITES_ENABLED.joinpath(cfg_name) + remove_file(Path("/etc/nginx/sites-enabled/default"), True) + generate_nginx_cfg_from_template(cfg_name, template_src=template_src, **kwargs) + create_symlink(source, target, True) + set_nginx_permissions() + + Logger.print_ok(f"NGINX config for {display_name} successfully created.") + except Exception: + Logger.print_error(f"Creating NGINX config for {display_name} failed!") + raise + + +def read_ports_from_nginx_configs() -> List[int]: + """ + Helper function to iterate over all NGINX configs and read all ports defined for listen + :return: A sorted list of listen ports + """ + if not NGINX_SITES_ENABLED.exists(): + return [] + + port_list = [] + for config in NGINX_SITES_ENABLED.iterdir(): + if not config.is_file(): + continue + + with open(config, "r") as cfg: + lines = cfg.readlines() + + for line in lines: + line = line.replace("default_server", "") + line = re.sub(r"[;:\[\]]", "", line.strip()) + if line.startswith("listen") and line.split()[-1] not in port_list: + port_list.append(line.split()[-1]) + + ports_to_ints_list = [int(port) for port in port_list] + return sorted(ports_to_ints_list, key=lambda x: int(x)) + + +def is_valid_port(port: int, ports_in_use: List[int]) -> bool: + return port not in ports_in_use + + +def get_next_free_port(ports_in_use: List[int]) -> int: + valid_ports = set(range(80, 7125)) + used_ports = set(map(int, ports_in_use)) + + return min(valid_ports - used_ports) diff --git a/kiauh/utils/fs_utils.py b/kiauh/utils/fs_utils.py index 397cf4d..28c2149 100644 --- a/kiauh/utils/fs_utils.py +++ b/kiauh/utils/fs_utils.py @@ -9,21 +9,13 @@ # This file may be distributed under the terms of the GNU GPLv3 license # # ======================================================================= # -import re import shutil from pathlib import Path from subprocess import DEVNULL, PIPE, CalledProcessError, check_output, run -from typing import List from zipfile import ZipFile -from core.constants import ( - NGINX_CONFD, - NGINX_SITES_AVAILABLE, - NGINX_SITES_ENABLED, -) from core.decorators import deprecated from core.logger import Logger -from utils import MODULE_PATH def check_file_exist(file_path: Path, sudo=False) -> bool: @@ -111,127 +103,3 @@ def unzip(filepath: Path, target_dir: Path) -> None: """ with ZipFile(filepath, "r") as _zip: _zip.extractall(target_dir) - - -def copy_upstream_nginx_cfg() -> None: - """ - Creates an upstream.conf in /etc/nginx/conf.d - :return: None - """ - source = MODULE_PATH.joinpath("assets/upstreams.conf") - target = NGINX_CONFD.joinpath("upstreams.conf") - try: - command = ["sudo", "cp", source, target] - run(command, stderr=PIPE, check=True) - except CalledProcessError as e: - log = f"Unable to create upstreams.conf: {e.stderr.decode()}" - Logger.print_error(log) - raise - - -def copy_common_vars_nginx_cfg() -> None: - """ - Creates a common_vars.conf in /etc/nginx/conf.d - :return: None - """ - source = MODULE_PATH.joinpath("assets/common_vars.conf") - target = NGINX_CONFD.joinpath("common_vars.conf") - try: - command = ["sudo", "cp", source, target] - run(command, stderr=PIPE, check=True) - except CalledProcessError as e: - log = f"Unable to create upstreams.conf: {e.stderr.decode()}" - Logger.print_error(log) - raise - - -def generate_nginx_cfg_from_template(name: str, template_src: Path, **kwargs) -> None: - """ - Creates an NGINX config from a template file and - replaces all placeholders passed as kwargs. A placeholder must be defined - in the template file as %{placeholder}%. - :param name: name of the config to create - :param template_src: the path to the template file - :return: None - """ - tmp = Path.home().joinpath(f"{name}.tmp") - shutil.copy(template_src, tmp) - with open(tmp, "r+") as f: - content = f.read() - - for key, value in kwargs.items(): - content = content.replace(f"%{key}%", str(value)) - - f.seek(0) - f.write(content) - f.truncate() - - target = NGINX_SITES_AVAILABLE.joinpath(name) - try: - command = ["sudo", "mv", tmp, target] - run(command, stderr=PIPE, check=True) - except CalledProcessError as e: - log = f"Unable to create '{target}': {e.stderr.decode()}" - Logger.print_error(log) - raise - - -def create_nginx_cfg( - display_name: str, - cfg_name: str, - template_src: Path, - **kwargs, -) -> None: - from utils.sys_utils import set_nginx_permissions - - try: - Logger.print_status(f"Creating NGINX config for {display_name} ...") - - source = NGINX_SITES_AVAILABLE.joinpath(cfg_name) - target = NGINX_SITES_ENABLED.joinpath(cfg_name) - remove_file(Path("/etc/nginx/sites-enabled/default"), True) - generate_nginx_cfg_from_template(cfg_name, template_src=template_src, **kwargs) - create_symlink(source, target, True) - set_nginx_permissions() - - Logger.print_ok(f"NGINX config for {display_name} successfully created.") - except Exception: - Logger.print_error(f"Creating NGINX config for {display_name} failed!") - raise - - -def read_ports_from_nginx_configs() -> List[int]: - """ - Helper function to iterate over all NGINX configs and read all ports defined for listen - :return: A sorted list of listen ports - """ - if not NGINX_SITES_ENABLED.exists(): - return [] - - port_list = [] - for config in NGINX_SITES_ENABLED.iterdir(): - if not config.is_file(): - continue - - with open(config, "r") as cfg: - lines = cfg.readlines() - - for line in lines: - line = line.replace("default_server", "") - line = re.sub(r"[;:\[\]]", "", line.strip()) - if line.startswith("listen") and line.split()[-1] not in port_list: - port_list.append(line.split()[-1]) - - ports_to_ints_list = [int(port) for port in port_list] - return sorted(ports_to_ints_list, key=lambda x: int(x)) - - -def is_valid_port(port: int, ports_in_use: List[int]) -> bool: - return port not in ports_in_use - - -def get_next_free_port(ports_in_use: List[int]) -> int: - valid_ports = set(range(80, 7125)) - used_ports = set(map(int, ports_in_use)) - - return min(valid_ports - used_ports)