from base64 import b64encode from datetime import datetime from typing import Union from bs4 import BeautifulSoup, Tag import magic import json import os import re def get_variables(): vars = {} vars["DOCKER_HOST"] = "unix:///var/run/docker.sock" vars["ABSOLUTE_URI"] = "" vars["FLASK_SECRET"] = os.urandom(32) vars["FLASK_ENV"] = "development" vars["ADMIN_USERNAME"] = "admin" vars["ADMIN_PASSWORD"] = "changeme" for k in vars: if k in os.environ: vars[k] = os.environ[k] return vars def log(event): with open("/var/log/nginx/ui.log", "a") as f: f.write("[" + str(datetime.now().replace(microsecond=0)) + "] " + event + "\n") def env_to_summary_class(var, value): if type(var) is list and type(value) is list: for i in range(0, len(var)): if not isinstance(var[i], str): continue if re.search(value[i], var[i]): return "check text-success" return "times text-danger" if not isinstance(var, str): return "times text-danger" if re.search(value, var): return "check text-success" return "times text-danger" def form_service_gen( _id, _help, label, _type, value, name, default, selects, regex, from_html: bool = True, root: Tag = None, ) -> str: if root is None: root = BeautifulSoup() tooltip = Tag( name="div", attrs={ "class": "px-2 d-sm-inline", "data-bs-toggle": "tooltip", "data-bs-placement": "bottom", "title": _help, }, ) tooltip.append(Tag(name="i", attrs={"class": "fas fa-question-circle"})) root.append(tooltip) pt = "" invalid_div = None if _type in ("text", "number"): edit = Tag( name="input", attrs={ "type": _type, "class": "form-control", "id": _id, "name": name, "value": value if value else default, "pattern": regex, }, ) invalid_div = Tag(name="div", attrs={"class": "invalid-feedback"}) invalid_div.append( BeautifulSoup( f"{label} is invalid and must match this pattern: {regex}", "html.parser", ) ) elif _type == "check": edit = Tag( name="div", attrs={"class": "form-check form-switch"}, ) edit.append( BeautifulSoup( f"", "html.parser", ) ) edit.append( Tag( name="input", attrs={ "type": "hidden", "id": f"{_id}-hidden", "name": name, "value": "off", }, ) ) pt = "pt-0" elif _type == "select": edit = Tag( name="select", attrs={ "type": "form-select", "class": "form-control form-select", "id": _id, "name": name, }, ) for select in selects: edit.append( BeautifulSoup( f"", "html.parser", ) ) label_tag = Tag( name="label", attrs={"for": _id, "class": f"flex-grow-1 d-sm-inline {pt}"} ) label_tag.append(label) root.append(label_tag) div = Tag(name="div", attrs={"class": "d-sm-inline"}) div.append(edit) if invalid_div: div.append(invalid_div) root.append(div) if from_html: return root.prettify() else: return root def form_service_gen_multiple(_id, label, params, root) -> Tag: label_tag = Tag( name="label", attrs={"for": f"{_id}-btn", "class": "flex-grow-1 d-sm-inline"}, ) label_tag.append(label) root.append(label_tag) buttons = Tag(name="div", attrs={"class": "d-sm-inline", "id": _id}) add_button = Tag( name="button", attrs={ "class": "btn btn-outline-success", "type": "button", "onClick": f"addMultiple('{_id}', '{json.dumps(params)}');", }, ) add_button.append(Tag(name="i", attrs={"class": "fas fa-plus"})) add_button.append(" Add") buttons.append(add_button) del_button = Tag( name="button", attrs={ "class": "btn btn-outline-danger", "type": "button", "onClick": f"delMultiple('{_id}', '{json.dumps(params)}');", }, ) del_button.append(Tag(name="i", attrs={"class": "fas fa-trash"})) del_button.append(" Delete") buttons.append(del_button) root.append(buttons) return root def form_service_gen_multiple_values(_id, params, service) -> Union[Tag, str]: script = Tag(name="script", attrs={"defer": True}) values = [] for env in sorted( service, reverse=True, key=lambda x: int(re.search(r"\d+$", x).group()) if x[-1].isdigit() else 0, ): for param, param_value in params.items(): suffix = env.replace(param, "") if env.startswith(param): values.append( { "default": service.get( f"{param}{suffix}", param_value["default"] ), "env": param, "help": param_value["help"], "id": param_value["id"], "label": param_value["label"], "selects": param_value.get("selects", []), "type": param_value["type"], } ) if len(values) >= len(params): script.append(f"addMultiple('{_id}', '{json.dumps(values)}');") values = [] return script if script.children else "" def form_plugin_gen( service: dict, plugin: dict, action: str, id_server_name: str = None, *, context: str = "multisite", ) -> str: soup = BeautifulSoup() soup.append( Tag( name="div", attrs={ "class": "tab-pane fade", "id": f"{action}-{plugin['id']}" + (f"-{id_server_name}" if id_server_name else ""), "role": "tabpanel", "aria-labelledby": f"{action}-{plugin['id']}" + (f"-{id_server_name}" if id_server_name else "") + "-tab", }, ) ) multiple_plugins = {} multiple = None for setting, value in plugin["settings"].items(): if value["context"] == context: if "multiple" in value: if multiple != value["multiple"]: multiple = value["multiple"] multiple_plugins[multiple] = {} multiple_plugins[multiple][setting] = value multiple_plugins[multiple][setting]["env"] = setting else: div = Tag( name="div", attrs={ "class": "d-flex flex-row justify-content-between align-items-center mb-3", "id": f"form-{action}" + (f"-{id_server_name}" if id_server_name else "") + f"-{value['id']}", }, ) div = form_service_gen( f"form-{action}" + (f"-{id_server_name}" if id_server_name else "") + f"-{value['id']}", value["help"], value["label"], value["type"], service.get(setting, value["default"]), setting, value["default"], value.get("select", None), value["regex"], False, div, ) soup.div.append(div) for multiple_plugin in multiple_plugins: div = Tag( name="div", attrs={ "class": "d-flex flex-row justify-content-between align-items-center mb-3", "id": f"form-{action}" + (f"-{id_server_name}" if id_server_name else "") + f"-{multiple_plugin}", }, ) div = form_service_gen_multiple( f"form-{action}" + (f"-{id_server_name}" if id_server_name else "") + f"-{multiple_plugin}", plugin["name"], multiple_plugins[multiple_plugin], div, ) if service: div.append( form_service_gen_multiple_values( f"form-{action}" + (f"-{id_server_name}" if id_server_name else "") + f"-{multiple_plugin}", multiple_plugins[multiple_plugin], service, ) ) soup.div.append(div) return soup.prettify() def path_to_dict(path, *, level: int = 0, is_cache: bool = False) -> dict: d = {"name": os.path.basename(path)} if os.path.isdir(path): d.update( { "type": "folder", "path": path, "can_create_files": level > 0 and not is_cache, "can_create_folders": level > 0 and not is_cache, "can_edit": level > 1 and not is_cache, "can_delete": level > 1 and not is_cache, "children": [ path_to_dict( os.path.join(path, x), level=level + 1, is_cache=is_cache ) for x in sorted(os.listdir(path)) ], } ) else: d.update( { "type": "file", "path": path, "can_edit": level > 1 and not is_cache, "can_download": is_cache, } ) magic_file = magic.from_file(path, mime=True) if ( not is_cache or magic_file.startswith("text/") or magic_file.startswith("application/json") ): with open(path, "rb") as f: d["content"] = b64encode(f.read()).decode("utf-8") return d options = { "can_edit": { "button_style": "btn-outline-primary", "data-bs-target": "#modal-edit-new-folder", "icon": "fa-solid fa-pen-to-square", }, "can_delete": { "button_style": "btn-outline-danger", "data-bs-target": "#modal-delete", "icon": "fa-solid fa-trash-can", }, "can_create_folders": { "button_style": "btn-outline-success", "data-bs-target": "#modal-edit-new-folder", "icon": "fa-solid fa-folder", }, "can_create_files": { "button_style": "btn-outline-success", "data-bs-target": "#modal-edit-new-file", "icon": "fa-solid fa-file", }, } def gen_folders_tree_html(data: list) -> str: html = "" else: h6 = Tag(name="h6", attrs={"class": "mb-0"}) h6.append(_dict["name"]) root_li.append(h6) div = Tag(name="div", attrs={"class": "ms-2 d-sm-flex align-items-between"}) if _dict["can_edit"]: badge = Tag(name="div", attrs={"class": "badge px-1"}) button = Tag( name="button", attrs={ "class": f"btn btn-outline-primary", "data-bs-toggle": "modal", "data-bs-target": "#modal-edit-new-file", "data-path": _dict["path"], "data-content": _dict["content"], "data-action": "edit", }, ) button.append( Tag(name="i", attrs={"class": "fa-solid fa-pen-to-square"}) ) badge.append(button) div.append(badge) badge = Tag(name="div", attrs={"class": "badge px-1"}) button = Tag( name="button", attrs={ "class": f"btn btn-outline-danger", "data-bs-toggle": "modal", "data-bs-target": "#modal-delete", "data-path": _dict["path"], }, ) button.append(Tag(name="i", attrs={"class": "fa-solid fa-trash-can"})) badge.append(button) div.append(badge) elif "content" in _dict: badge = Tag(name="div", attrs={"class": "badge px-1"}) button = Tag( name="button", attrs={ "class": f"btn btn-outline-secondary", "data-bs-toggle": "modal", "data-bs-target": "#modal-see-file", "data-path": _dict["path"], "data-content": _dict["content"], }, ) button.append(Tag(name="i", attrs={"class": "fa-solid fa-eye"})) badge.append(button) div.append(badge) if _dict["can_download"]: badge = Tag(name="div", attrs={"class": "badge px-1"}) button = Tag( name="button", attrs={ "class": f"btn btn-outline-primary download-button", "data-path": _dict["path"], }, ) button.append(Tag(name="i", attrs={"class": "fa-solid fa-download"})) badge.append(button) div.append(badge) root_li.append(div) html += root_li.prettify() return html def check_settings(settings: dict, check: str) -> bool: return any(setting["context"] == check for setting in settings.values())