mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
automate builder and widgets dev to prod
* update widgets_generator.py in order to add widgets to client/builder * update build.py : now run widgets_generatoy.py to get latests widgets state + copy ui/client/builder to ui/builder + add sys_path logic + update from path
This commit is contained in:
parent
29332ff7f3
commit
055e005b34
45 changed files with 46416 additions and 146 deletions
|
|
@ -1,12 +1,4 @@
|
|||
from os.path import join, sep
|
||||
from sys import path as sys_path
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
|
||||
from builder.utils.form import get_forms
|
||||
from .utils.form import get_forms
|
||||
|
||||
|
||||
def global_config_builder(templates: list[dict], plugins: list, settings: dict) -> str:
|
||||
|
|
|
|||
|
|
@ -1,11 +1,4 @@
|
|||
from os.path import join, sep
|
||||
from sys import path as sys_path
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from builder.utils.widgets import stat_widget
|
||||
from .utils.widgets import stat_widget
|
||||
|
||||
|
||||
def home_builder(data: dict) -> str:
|
||||
|
|
|
|||
|
|
@ -1,14 +1,4 @@
|
|||
from os.path import join, sep
|
||||
from sys import path as sys_path
|
||||
from typing import List
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from src.instance import Instance
|
||||
|
||||
from builder.utils.widgets import instance_widget
|
||||
from .utils.widgets import instance_widget
|
||||
|
||||
|
||||
def instances_builder(instances: List[Instance]) -> str:
|
||||
|
|
|
|||
|
|
@ -1,11 +1,4 @@
|
|||
from os.path import join, sep
|
||||
from sys import path as sys_path
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from builder.utils.widgets import title_widget, table_widget
|
||||
from .utils.widgets import title_widget, table_widget
|
||||
|
||||
|
||||
def jobs_builder(jobs):
|
||||
|
|
|
|||
|
|
@ -1,11 +1,4 @@
|
|||
from os.path import join, sep
|
||||
from sys import path as sys_path
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from builder.utils.widgets import title_widget
|
||||
from .utils.widgets import title_widget
|
||||
|
||||
|
||||
def logs_builder(files: list[str] = [], current_file: str = "", raw_data: str = "") -> str:
|
||||
|
|
|
|||
|
|
@ -1,12 +1,4 @@
|
|||
from os.path import join, sep
|
||||
from sys import path as sys_path
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
|
||||
from builder.utils.form import get_forms, get_service_settings
|
||||
from .utils.form import get_forms, get_service_settings
|
||||
|
||||
|
||||
def raw_mode_builder(templates: list[dict], plugins: list, global_config: dict, total_config: dict, service_name: str, is_new: bool = False) -> str:
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
from typing import Union
|
||||
|
||||
from os.path import join, sep
|
||||
from sys import path as sys_path
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from builder.utils.widgets import title_widget, table_widget
|
||||
from .utils.widgets import title_widget, table_widget
|
||||
|
||||
|
||||
def services_builder(services):
|
||||
|
|
@ -195,8 +188,7 @@ def services_action(
|
|||
)
|
||||
|
||||
if operation == "draft":
|
||||
# get reverse of current state for update
|
||||
draft_value = "no" if is_draft else "yes"
|
||||
draft_value = "yes" if is_draft else "no"
|
||||
buttons.append(
|
||||
{
|
||||
"id": f"{operation}-service-btn-{server_name}",
|
||||
|
|
|
|||
|
|
@ -120,37 +120,44 @@ def set_easy(template: list, plugins_base: list, settings: dict, is_new: bool) -
|
|||
Prepare the easy form based on the template and plugins data.
|
||||
We need to loop on each steps and prepare settings and configs for each step.
|
||||
"""
|
||||
|
||||
template_settings = template.get("settings")
|
||||
plugins = copy.deepcopy(plugins_base)
|
||||
# Copy of the plugins base data
|
||||
plugins = copy.deepcopy(plugins_base)
|
||||
# Update settings with global config data
|
||||
for plugin in plugins:
|
||||
loop_id = 0
|
||||
total_settings = len(plugin.get("settings"))
|
||||
for setting, value in plugin.get("settings").items():
|
||||
loop_id += 1
|
||||
value = format_setting(setting, value, total_settings, loop_id, template_settings, settings, is_new)
|
||||
|
||||
set_multiples(template, plugins, settings)
|
||||
|
||||
steps = template.get("steps")
|
||||
print(steps)
|
||||
for step in steps:
|
||||
step_settings = step.get("settings", {})
|
||||
# Loop on step settings to set the settings value
|
||||
loop_id = 0
|
||||
step_settings_output = {}
|
||||
for setting in step_settings:
|
||||
loop_id += 1
|
||||
# Get relate setting from plugins using setting name
|
||||
plugin = next(
|
||||
(plugin for plugin in plugins if setting in plugin.get("settings")),
|
||||
None,
|
||||
)
|
||||
|
||||
if not plugin:
|
||||
continue
|
||||
for plugin in plugins:
|
||||
step_settings_output = {}
|
||||
for setting, value in plugin.get("settings").items():
|
||||
if setting not in step_settings:
|
||||
continue
|
||||
|
||||
if not plugin.get("settings").get(setting):
|
||||
continue
|
||||
|
||||
plugin_setting = copy.deepcopy(plugin.get("settings").get(setting))
|
||||
|
||||
plugin_setting = format_setting(setting, plugin_setting, len(step_settings), loop_id, template_settings, settings, is_new)
|
||||
|
||||
step_settings_output[setting] = plugin_setting
|
||||
|
||||
step["settings"] = step_settings_output
|
||||
step_settings_output[setting] = value
|
||||
|
||||
# Case at least one key in step settings, we can add the plugin settings to the step
|
||||
if len(step_settings_output) and not "plugins" in step:
|
||||
step["plugins"] = []
|
||||
|
||||
if len(step_settings_output):
|
||||
step_plugin = copy.deepcopy(plugin)
|
||||
step_plugin["settings"] = step_settings_output
|
||||
step["plugins"].append(step_plugin)
|
||||
|
||||
# remove settings key form step
|
||||
step.pop("settings", None)
|
||||
return steps
|
||||
|
||||
|
||||
|
|
@ -243,7 +250,7 @@ def get_multiple_from_template(template, multiples):
|
|||
# loop on settings of a multiple group
|
||||
for mult_name, mult_settings in multiple_plugin.items():
|
||||
|
||||
# Check if at least one settign is matching a multiple setting
|
||||
# Check if at least one setting is matching a multiple setting
|
||||
if not format_setting in mult_settings:
|
||||
continue
|
||||
|
||||
|
|
@ -307,7 +314,7 @@ def get_multiple_from_settings(settings, multiples):
|
|||
# loop on settings of a multiple group
|
||||
for mult_name, mult_settings in multiple_plugins.items():
|
||||
|
||||
# Check if at least one settign is matching a multiple setting
|
||||
# Check if at least one setting is matching a multiple setting
|
||||
if not format_setting in mult_settings:
|
||||
continue
|
||||
|
||||
|
|
@ -473,7 +480,7 @@ def format_setting(
|
|||
if "multiple" in value:
|
||||
return value
|
||||
|
||||
# Else, we can add additionnal final data
|
||||
# Else, we can add additional final data
|
||||
value["method"] = settings.get(name, {}).get("method", "ui")
|
||||
value["containerClass"] = f"z-{total_settings - loop_id}"
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -18,6 +18,10 @@ ui_dir_static = current_directory.parent.joinpath("static")
|
|||
ui_dir_templates = current_directory.parent.joinpath("templates")
|
||||
legacy_dir_static = current_directory.joinpath("legacy", "static")
|
||||
legacy_dir_templates = current_directory.joinpath("legacy", "templates")
|
||||
builder_dir_pages = current_directory.joinpath("builder", "pages")
|
||||
builder_dir_utils = current_directory.joinpath("builder", "utils")
|
||||
ui_dir_builder = current_directory.parent.joinpath("builder")
|
||||
ui_dir_builder_utils = current_directory.parent.joinpath("builder", "utils")
|
||||
|
||||
statics = ("assets", "css", "flags", "img", "js")
|
||||
|
||||
|
|
@ -29,6 +33,7 @@ def reset():
|
|||
remove_dir(opt_dir_setup)
|
||||
remove_dir(ui_dir_static)
|
||||
remove_dir(ui_dir_templates)
|
||||
remove_dir(ui_dir_builder)
|
||||
|
||||
|
||||
def set_dashboard():
|
||||
|
|
@ -146,6 +151,32 @@ def add_legacy():
|
|||
copytree(legacy_dir_templates.as_posix(), ui_dir_templates.as_posix())
|
||||
|
||||
|
||||
def add_builder_and_widgets():
|
||||
# First we want to generate widgets by executing "widgets_generator.py" that is on same level
|
||||
if run_command(["/usr/bin/python3", "widgets_generator.py"]):
|
||||
if run_command(["python", "widgets_generator.py"]):
|
||||
exit(1)
|
||||
|
||||
# Create output folders
|
||||
copytree(builder_dir_pages.as_posix(), ui_dir_builder.as_posix())
|
||||
# I want to loop on each file
|
||||
for file in ui_dir_builder.glob("*.py"):
|
||||
# I want to replace "from .utils." by "from builder.utils."
|
||||
content = file.read_text()
|
||||
content = content.replace("from .utils.", "from builder.utils.")
|
||||
content = """from os.path import join, sep
|
||||
from sys import path as sys_path
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
""" + content
|
||||
file.write_text(content)
|
||||
|
||||
copytree(builder_dir_utils.as_posix(), ui_dir_builder_utils.as_posix())
|
||||
|
||||
|
||||
def build():
|
||||
"""All steps to build the front end and set it to the flask app"""
|
||||
reset()
|
||||
|
|
@ -162,6 +193,7 @@ def build():
|
|||
set_dashboard()
|
||||
# run_command(["/usr/bin/npm", "run", "build-setup"])
|
||||
# set_setup()
|
||||
add_builder_and_widgets()
|
||||
|
||||
|
||||
build()
|
||||
|
|
|
|||
13770
src/ui/client/builder/advanced.json
Normal file
13770
src/ui/client/builder/advanced.json
Normal file
File diff suppressed because it is too large
Load diff
4313
src/ui/client/builder/advanced.py
Normal file
4313
src/ui/client/builder/advanced.py
Normal file
File diff suppressed because it is too large
Load diff
1
src/ui/client/builder/advanced.txt
Normal file
1
src/ui/client/builder/advanced.txt
Normal file
File diff suppressed because one or more lines are too long
38
src/ui/client/builder/advanced_mode.py
Normal file
38
src/ui/client/builder/advanced_mode.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
from .utils.form import get_forms, get_service_settings
|
||||
|
||||
|
||||
def advanced_mode_builder(templates: list[dict], plugins: list, global_config: dict, total_config: dict, service_name: str, is_new: bool = False) -> str:
|
||||
"""Render forms with global config data.
|
||||
ATM we don't need templates but we need to pass at least one to the function (it will simply not override anything).
|
||||
"""
|
||||
settings = get_service_settings(service_name, global_config, total_config)
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {
|
||||
"title": service_name,
|
||||
"type": "container",
|
||||
"lowercase": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "Subtitle",
|
||||
"data": {"subtitle": "services_manage_subtitle", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Templates",
|
||||
"data": {
|
||||
"templates": get_forms(templates, plugins, settings, ("advanced",), is_new, True),
|
||||
"operation": "new" if is_new else "edit",
|
||||
"oldServerName": service_name if service_name else "",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
669
src/ui/client/builder/bans.json
Normal file
669
src/ui/client/builder/bans.json
Normal file
|
|
@ -0,0 +1,669 @@
|
|||
[
|
||||
{
|
||||
"type": "void",
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Button",
|
||||
"data": {
|
||||
"text": "bans_not_found"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {
|
||||
"title": "bans_title"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Table",
|
||||
"data": {
|
||||
"title": "bans_table_title",
|
||||
"minWidth": "xl",
|
||||
"header": [
|
||||
"bans_table_select",
|
||||
"bans_table_ip",
|
||||
"bans_table_reason",
|
||||
"bans_table_ban_start",
|
||||
"bans_table_ban_end",
|
||||
"bans_table_remain",
|
||||
"bans_table_term"
|
||||
],
|
||||
"positions": [1, 1, 1, 3, 3, 2, 1],
|
||||
"items": [
|
||||
[
|
||||
{
|
||||
"select": false,
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": false,
|
||||
"value": "no",
|
||||
"inpType": "checkbox",
|
||||
"id": "select-ban-1",
|
||||
"name": "select-ban-1",
|
||||
"label": "select-ban-1",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ip": "127.0.0.1",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"reason": "ui",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_start": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-start-1",
|
||||
"name": "datepicker-ban-ban-start-1",
|
||||
"label": "datepicker-ban-ban-start-1",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_end": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-end-1",
|
||||
"name": "datepicker-ban-ban-end-1",
|
||||
"label": "datepicker-ban-ban-end-1",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "23 hours and 49 minutes"
|
||||
}
|
||||
},
|
||||
{
|
||||
"term": "hour(s)",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "hour(s)"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"select": false,
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": false,
|
||||
"value": "no",
|
||||
"inpType": "checkbox",
|
||||
"id": "select-ban-2",
|
||||
"name": "select-ban-2",
|
||||
"label": "select-ban-2",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ip": "127.0.0.1",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"reason": "ui",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_start": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-start-2",
|
||||
"name": "datepicker-ban-ban-start-2",
|
||||
"label": "datepicker-ban-ban-start-2",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_end": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-end-2",
|
||||
"name": "datepicker-ban-ban-end-2",
|
||||
"label": "datepicker-ban-ban-end-2",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "23 hours and 49 minutes"
|
||||
}
|
||||
},
|
||||
{
|
||||
"term": "day(s)",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "day(s)"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"select": false,
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": false,
|
||||
"value": "no",
|
||||
"inpType": "checkbox",
|
||||
"id": "select-ban-3",
|
||||
"name": "select-ban-3",
|
||||
"label": "select-ban-3",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ip": "127.0.0.1",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"reason": "cor",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "cor"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_start": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-start-3",
|
||||
"name": "datepicker-ban-ban-start-3",
|
||||
"label": "datepicker-ban-ban-start-3",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_end": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-end-3",
|
||||
"name": "datepicker-ban-ban-end-3",
|
||||
"label": "datepicker-ban-ban-end-3",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "23 hours and 49 minutes"
|
||||
}
|
||||
},
|
||||
{
|
||||
"term": "hour(s)",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "hour(s)"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"select": false,
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": false,
|
||||
"value": "no",
|
||||
"inpType": "checkbox",
|
||||
"id": "select-ban-4",
|
||||
"name": "select-ban-4",
|
||||
"label": "select-ban-4",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ip": "127.0.0.1",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"reason": "ui",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_start": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-start-4",
|
||||
"name": "datepicker-ban-ban-start-4",
|
||||
"label": "datepicker-ban-ban-start-4",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_end": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-end-4",
|
||||
"name": "datepicker-ban-ban-end-4",
|
||||
"label": "datepicker-ban-ban-end-4",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "23 hours and 49 minutes"
|
||||
}
|
||||
},
|
||||
{
|
||||
"term": "hour(s)",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "hour(s)"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"select": false,
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": false,
|
||||
"value": "no",
|
||||
"inpType": "checkbox",
|
||||
"id": "select-ban-5",
|
||||
"name": "select-ban-5",
|
||||
"label": "select-ban-5",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ip": "127.0.0.1",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"reason": "ui",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_start": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-start-5",
|
||||
"name": "datepicker-ban-ban-start-5",
|
||||
"label": "datepicker-ban-ban-start-5",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_end": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-end-5",
|
||||
"name": "datepicker-ban-ban-end-5",
|
||||
"label": "datepicker-ban-ban-end-5",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "23 hours and 49 minutes"
|
||||
}
|
||||
},
|
||||
{
|
||||
"term": "hour(s)",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "hour(s)"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"select": false,
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": false,
|
||||
"value": "no",
|
||||
"inpType": "checkbox",
|
||||
"id": "select-ban-6",
|
||||
"name": "select-ban-6",
|
||||
"label": "select-ban-6",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ip": "127.0.0.1",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "127.0.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"reason": "ui",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "ui"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_start": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-start-6",
|
||||
"name": "datepicker-ban-ban-start-6",
|
||||
"label": "datepicker-ban-ban-start-6",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"ban_end": "1719393920",
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"disabled": true,
|
||||
"value": 1719393920,
|
||||
"inpType": "datepicker",
|
||||
"id": "datepicker-ban-ban-end-6",
|
||||
"name": "datepicker-ban-ban-end-6",
|
||||
"label": "datepicker-ban-ban-end-6",
|
||||
"hideLabel": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "23 hours and 49 minutes"
|
||||
}
|
||||
},
|
||||
{
|
||||
"term": "hour(s)",
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "hour(s)"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "keyword",
|
||||
"type": "keyword",
|
||||
"value": "",
|
||||
"keys": ["ip", "ban_start", "ban_end"],
|
||||
"field": {
|
||||
"id": "bans-keyword",
|
||||
"value": "",
|
||||
"type": "text",
|
||||
"name": "bans-keyword",
|
||||
"label": "bans_search",
|
||||
"placeholder": "inp_keyword",
|
||||
"isClipboard": false,
|
||||
"popovers": [
|
||||
{
|
||||
"text": "bans_search_desc",
|
||||
"iconName": "info"
|
||||
}
|
||||
],
|
||||
"columns": {
|
||||
"pc": 3,
|
||||
"tablet": 4,
|
||||
"mobile": 12
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "reason",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["reason"],
|
||||
"field": {
|
||||
"id": "bans-reason",
|
||||
"value": "all",
|
||||
"values": ["all", "ui", "cor"],
|
||||
"name": "bans-reason",
|
||||
"onlyDown": true,
|
||||
"label": "bans_reason",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "bans_reason_desc",
|
||||
"iconName": "info"
|
||||
}
|
||||
],
|
||||
"columns": {
|
||||
"pc": 3,
|
||||
"tablet": 4,
|
||||
"mobile": 12
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "term",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["term"],
|
||||
"field": {
|
||||
"id": "bans-terms",
|
||||
"value": "all",
|
||||
"values": ["all", "hour(s)", "day(s)"],
|
||||
"name": "bans-terms",
|
||||
"onlyDown": true,
|
||||
"label": "bans_terms",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "bans_terms_desc",
|
||||
"iconName": "info"
|
||||
}
|
||||
],
|
||||
"columns": {
|
||||
"pc": 3,
|
||||
"tablet": 4,
|
||||
"mobile": 12
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
296
src/ui/client/builder/bans.py
Normal file
296
src/ui/client/builder/bans.py
Normal file
|
|
@ -0,0 +1,296 @@
|
|||
import json
|
||||
|
||||
no_bans = []
|
||||
|
||||
|
||||
bans = [
|
||||
{
|
||||
"reason": "ui",
|
||||
"date": 1719393920,
|
||||
"ip": "127.0.0.1",
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"term": "hour(s)",
|
||||
"ban_start": 1719393920,
|
||||
"ban_end": 1719393920,
|
||||
},
|
||||
{
|
||||
"reason": "ui",
|
||||
"date": 1719393920,
|
||||
"ip": "127.0.0.1",
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"term": "day(s)",
|
||||
"ban_start": 1719393920,
|
||||
"ban_end": 1719393920,
|
||||
},
|
||||
{
|
||||
"reason": "core",
|
||||
"date": 1719393920,
|
||||
"ip": "127.0.0.1",
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"term": "hour(s)",
|
||||
"ban_start": 1719393920,
|
||||
"ban_end": 1719393920,
|
||||
},
|
||||
{
|
||||
"reason": "ui",
|
||||
"date": 1719393920,
|
||||
"ip": "127.0.0.1",
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"term": "hour(s)",
|
||||
"ban_start": 1719393920,
|
||||
"ban_end": 1719393920,
|
||||
},
|
||||
{
|
||||
"reason": "ui",
|
||||
"date": 1719393920,
|
||||
"ip": "127.0.0.1",
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"term": "hour(s)",
|
||||
"ban_start": 1719393920,
|
||||
"ban_end": 1719393920,
|
||||
},
|
||||
{
|
||||
"reason": "ui",
|
||||
"date": 1719393920,
|
||||
"ip": "127.0.0.1",
|
||||
"remain": "23 hours and 49 minutes",
|
||||
"term": "hour(s)",
|
||||
"ban_start": 1719393920,
|
||||
"ban_end": 1719393920,
|
||||
},
|
||||
]
|
||||
# Reorder bans dict
|
||||
for ban in bans:
|
||||
ban.pop("date")
|
||||
ban["ip"] = ban.pop("ip")
|
||||
ban["reason"] = ban.pop("reason")
|
||||
ban["ban_start"] = ban.pop("ban_start")
|
||||
ban["ban_end"] = ban.pop("ban_end")
|
||||
ban["remain"] = ban.pop("remain")
|
||||
ban["term"] = ban.pop("term")
|
||||
|
||||
|
||||
def get_bans_filter(bans):
|
||||
|
||||
if len(bans) <= 5:
|
||||
return []
|
||||
|
||||
total_reasons = ["all"]
|
||||
total_terms = ["all"]
|
||||
|
||||
for ban in bans:
|
||||
if ban.get("reason") and ban.get("reason") not in total_reasons:
|
||||
total_reasons.append(ban.get("reason"))
|
||||
|
||||
if ban.get("term") and ban.get("term") not in total_terms:
|
||||
total_terms.append(ban.get("term"))
|
||||
|
||||
filters = []
|
||||
|
||||
filters.append(
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "keyword",
|
||||
"type": "keyword",
|
||||
"value": "",
|
||||
"keys": ["ip", "ban_start", "ban_end"],
|
||||
"field": {
|
||||
"id": "bans-keyword",
|
||||
"value": "",
|
||||
"type": "text",
|
||||
"name": "bans-keyword",
|
||||
"label": "bans_search",
|
||||
"placeholder": "inp_keyword",
|
||||
"isClipboard": False,
|
||||
"popovers": [
|
||||
{
|
||||
"text": "bans_search_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if len(total_reasons) > 2:
|
||||
filters.append(
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "reason",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["reason"],
|
||||
"field": {
|
||||
"id": "bans-reason",
|
||||
"value": "all",
|
||||
"values": total_reasons,
|
||||
"name": "bans-reason",
|
||||
"onlyDown": True,
|
||||
"label": "bans_reason",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "bans_reason_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if len(total_terms) > 2:
|
||||
filters.append(
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "term",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["term"],
|
||||
"field": {
|
||||
"id": "bans-terms",
|
||||
"value": "all",
|
||||
"values": total_terms,
|
||||
"name": "bans-terms",
|
||||
"onlyDown": True,
|
||||
"label": "bans_terms",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "bans_terms_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
)
|
||||
return filters
|
||||
|
||||
|
||||
def get_bans_list(bans):
|
||||
data = []
|
||||
# loop on each dict
|
||||
id = 0
|
||||
for ban in bans:
|
||||
id += 1
|
||||
item = []
|
||||
item.append(
|
||||
{
|
||||
"select": False,
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"disabled": False,
|
||||
"value": "no",
|
||||
"inpType": "checkbox",
|
||||
"id": f"select-ban-{id}",
|
||||
"name": f"select-ban-{id}",
|
||||
"label": f"select-ban-{id}",
|
||||
"hideLabel": True,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
for k, v in ban.items():
|
||||
|
||||
if k in ("date", "ban_start", "ban_end"):
|
||||
item.append(
|
||||
{
|
||||
k: json.dumps(v) if isinstance(v, dict) else str(v),
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"columns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"disabled": True,
|
||||
"value": v,
|
||||
"inpType": "datepicker",
|
||||
"id": f"datepicker-ban-{k}-{id}".replace("_", "-"),
|
||||
"name": f"datepicker-ban-{k}-{id}".replace("_", "-"),
|
||||
"label": f"datepicker-ban-{k}-{id}".replace("_", "-"),
|
||||
"hideLabel": True,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
continue
|
||||
|
||||
item.append(
|
||||
{
|
||||
k: json.dumps(v) if isinstance(v, dict) else str(v),
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": json.dumps(v) if isinstance(v, dict) else str(v),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
data.append(item)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def bans_builder(bans):
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "void",
|
||||
"widgets": [{"type": "Button", "data": {"text": "bans_not_found"}}],
|
||||
},
|
||||
]
|
||||
|
||||
if not bans:
|
||||
builder.append(
|
||||
{
|
||||
"type": "void",
|
||||
"widgets": [
|
||||
{"type": "MessageUnmatch", "data": {"text": "bans_not_found"}}
|
||||
],
|
||||
}
|
||||
)
|
||||
return builder
|
||||
|
||||
filters = get_bans_filter(bans)
|
||||
bans_list = get_bans_list(bans)
|
||||
|
||||
bans_table = {
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {"title": "bans_title"},
|
||||
},
|
||||
{
|
||||
"type": "Table",
|
||||
"data": {
|
||||
"title": "bans_table_title",
|
||||
"minWidth": "xl",
|
||||
"header": [
|
||||
"bans_table_select",
|
||||
"bans_table_ip",
|
||||
"bans_table_reason",
|
||||
"bans_table_ban_start",
|
||||
"bans_table_ban_end",
|
||||
"bans_table_remain",
|
||||
"bans_table_term",
|
||||
],
|
||||
"positions": [1, 1, 1, 3, 3, 2, 1],
|
||||
"items": bans_list,
|
||||
"filters": filters,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
builder.append(bans_table)
|
||||
|
||||
return builder
|
||||
|
||||
|
||||
output = bans_builder(bans)
|
||||
# output = bans_builder(no_bans)
|
||||
|
||||
# store on a file
|
||||
with open("bans.json", "w") as f:
|
||||
json.dump(output, f, indent=4)
|
||||
2646
src/ui/client/builder/easy.json
Normal file
2646
src/ui/client/builder/easy.json
Normal file
File diff suppressed because it is too large
Load diff
4313
src/ui/client/builder/easy.py
Normal file
4313
src/ui/client/builder/easy.py
Normal file
File diff suppressed because it is too large
Load diff
1
src/ui/client/builder/easy.txt
Normal file
1
src/ui/client/builder/easy.txt
Normal file
File diff suppressed because one or more lines are too long
39
src/ui/client/builder/easy_mode.py
Normal file
39
src/ui/client/builder/easy_mode.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
from .utils.form import get_forms, get_service_settings
|
||||
|
||||
|
||||
def easy_mode_builder(templates: list[dict], plugins: list, global_config: dict, total_config: dict, service_name: str, is_new: bool = False) -> str:
|
||||
"""Render forms with global config data.
|
||||
ATM we don't need templates but we need to pass at least one to the function (it will simply not override anything).
|
||||
"""
|
||||
|
||||
# We need
|
||||
settings = get_service_settings(service_name, global_config, total_config)
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {
|
||||
"title": service_name,
|
||||
"type": "container",
|
||||
"lowercase": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "Subtitle",
|
||||
"data": {"subtitle": "services_manage_subtitle", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Templates",
|
||||
"data": {
|
||||
"templates": get_forms(templates, plugins, settings, ("easy",), is_new, True),
|
||||
"operation": "new" if is_new else "edit",
|
||||
"oldServerName": service_name if service_name else "",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
31
src/ui/client/builder/global_config.py
Normal file
31
src/ui/client/builder/global_config.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
from .utils.form import get_forms
|
||||
|
||||
|
||||
def global_config_builder(templates: list[dict], plugins: list, settings: dict) -> str:
|
||||
"""Render forms with global config data.
|
||||
ATM we don't need templates but we need to pass at least one to the function (it will simply not override anything).
|
||||
"""
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {"title": "global_config_title", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Subtitle",
|
||||
"data": {"subtitle": "global_config_subtitle", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Templates",
|
||||
"data": {
|
||||
"templates": get_forms(templates, plugins, settings, ("advanced", "raw")),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
9988
src/ui/client/builder/globalconfig.json
Normal file
9988
src/ui/client/builder/globalconfig.json
Normal file
File diff suppressed because it is too large
Load diff
3343
src/ui/client/builder/globalconfig.py
Normal file
3343
src/ui/client/builder/globalconfig.py
Normal file
File diff suppressed because it is too large
Load diff
1
src/ui/client/builder/globalconfig64.txt
Normal file
1
src/ui/client/builder/globalconfig64.txt
Normal file
File diff suppressed because one or more lines are too long
107
src/ui/client/builder/home.json
Normal file
107
src/ui/client/builder/home.json
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
[
|
||||
{
|
||||
"type": "card",
|
||||
"link": "https://panel.bunkerweb.io/?utm_campaign=self&utm_source=ui#pro",
|
||||
"containerColumns": {
|
||||
"pc": 4,
|
||||
"tablet": 6,
|
||||
"mobile": 12
|
||||
},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Stat",
|
||||
"data": {
|
||||
"title": "home_version",
|
||||
"subtitle": "home_upgrade_to_pro",
|
||||
"subtitleColor": "warning",
|
||||
"stat": "home_free",
|
||||
"iconName": "key"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"link": "https://github.com/bunkerity/bunkerweb",
|
||||
"containerColumns": {
|
||||
"pc": 4,
|
||||
"tablet": 6,
|
||||
"mobile": 12
|
||||
},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Stat",
|
||||
"data": {
|
||||
"title": "home_version_number",
|
||||
"subtitle": "home_update_available",
|
||||
"subtitleColor": "warning",
|
||||
"stat": "1.5.8",
|
||||
"iconName": "wire"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"link": "/instances",
|
||||
"containerColumns": {
|
||||
"pc": 4,
|
||||
"tablet": 6,
|
||||
"mobile": 12
|
||||
},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Stat",
|
||||
"data": {
|
||||
"title": "home_instances",
|
||||
"subtitle": "home_total_number",
|
||||
"subtitleColor": "info",
|
||||
"stat": 1,
|
||||
"iconName": "box"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"link": "/services",
|
||||
"containerColumns": {
|
||||
"pc": 4,
|
||||
"tablet": 6,
|
||||
"mobile": 12
|
||||
},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Stat",
|
||||
"data": {
|
||||
"title": "home_services",
|
||||
"subtitle": "home_all_methods_included",
|
||||
"subtitleColor": "info",
|
||||
"stat": 2,
|
||||
"iconName": "disk"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "card",
|
||||
"link": "/plugins",
|
||||
"containerColumns": {
|
||||
"pc": 4,
|
||||
"tablet": 6,
|
||||
"mobile": 12
|
||||
},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Stat",
|
||||
"data": {
|
||||
"title": "home_plugins",
|
||||
"subtitle": "home_no_error",
|
||||
"subtitleColor": "success",
|
||||
"stat": "42",
|
||||
"iconName": "puzzle"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
84
src/ui/client/builder/home.py
Normal file
84
src/ui/client/builder/home.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
from .utils.widgets import stat_widget
|
||||
|
||||
|
||||
def home_builder(data: dict) -> str:
|
||||
"""
|
||||
It returns the needed format from data to render the home page in JSON format for the Vue.js builder
|
||||
"""
|
||||
version_card = stat_widget(
|
||||
link="https://panel.bunkerweb.io/?utm_campaign=self&utm_source=ui#pro",
|
||||
containerColums={"pc": 4, "tablet": 6, "mobile": 12},
|
||||
title="home_version",
|
||||
subtitle=(
|
||||
"home_all_features_available"
|
||||
if data.get("is_pro_version")
|
||||
else (
|
||||
"home_awaiting_compliance"
|
||||
if data.get("pro_status") == "active" and data.get("pro_overlapped")
|
||||
else (
|
||||
"home_renew_license"
|
||||
if data.get("pro_status") == "expired"
|
||||
else "home_talk_to_team" if data.get("pro_status") == "suspended" else "home_upgrade_to_pro"
|
||||
)
|
||||
)
|
||||
),
|
||||
subtitle_color="success" if data.get("is_pro_version") else "warning",
|
||||
stat=(
|
||||
"home_pro"
|
||||
if data.get("is_pro_version")
|
||||
else (
|
||||
"home_pro_locked"
|
||||
if data.get("pro_status") == "active" and data.get("pro_overlapped")
|
||||
else "home_expired" if data.get("pro_status") == "expired" else "home_suspended" if data.get("pro_status") == "suspended" else "home_free"
|
||||
)
|
||||
),
|
||||
icon_name="crown" if data.get("is_pro_version") else "key",
|
||||
)
|
||||
|
||||
version_num_card = stat_widget(
|
||||
link="https://github.com/bunkerity/bunkerweb",
|
||||
containerColums={"pc": 4, "tablet": 6, "mobile": 12},
|
||||
title="home_version_number",
|
||||
subtitle=(
|
||||
"home_couldnt_find_remote"
|
||||
if not data.get("remote_version")
|
||||
else "home_latest_version" if data.get("remote_version") and data.get("check_version") else "home_update_available"
|
||||
),
|
||||
subtitle_color=("error" if not data.get("remote_version") else "success" if data.get("remote_version") and data.get("check_version") else "warning"),
|
||||
stat=data.get("version"),
|
||||
icon_name="wire",
|
||||
)
|
||||
|
||||
instances_card = stat_widget(
|
||||
link="instances",
|
||||
containerColums={"pc": 4, "tablet": 6, "mobile": 12},
|
||||
title="home_instances",
|
||||
subtitle="home_total_number",
|
||||
subtitle_color="info",
|
||||
stat=data.get("instances_number"),
|
||||
icon_name="box",
|
||||
)
|
||||
|
||||
services_card = stat_widget(
|
||||
link="services",
|
||||
containerColums={"pc": 4, "tablet": 6, "mobile": 12},
|
||||
title="home_services",
|
||||
subtitle="home_all_methods_included",
|
||||
subtitle_color="info",
|
||||
stat=data.get("services_number"),
|
||||
icon_name="disk",
|
||||
)
|
||||
|
||||
plugins_card = stat_widget(
|
||||
link="plugins",
|
||||
containerColums={"pc": 4, "tablet": 6, "mobile": 12},
|
||||
title="home_plugins",
|
||||
subtitle="home_errors_found" if data.get("plugins_errors") > 0 else "home_no_error",
|
||||
subtitle_color="error" if data.get("plugins_errors") > 0 else "success",
|
||||
stat=data.get("plugins_number"),
|
||||
icon_name="puzzle",
|
||||
)
|
||||
|
||||
builder = [version_card, version_num_card, instances_card, services_card, plugins_card]
|
||||
|
||||
return builder
|
||||
42
src/ui/client/builder/instances.py
Normal file
42
src/ui/client/builder/instances.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
from .utils.widgets import instance_widget
|
||||
|
||||
|
||||
def instances_builder(instances: List[Instance]) -> str:
|
||||
"""
|
||||
It returns the needed format from data to render the instances page in JSON format for the Vue.js builder
|
||||
"""
|
||||
builder = []
|
||||
|
||||
for instance in instances:
|
||||
# setup actions buttons
|
||||
actions = ["reload", "stop"] if instance.status == "up" else ["start"]
|
||||
|
||||
buttons = [
|
||||
{
|
||||
"attrs": {
|
||||
"data-submit-form": f"""{{"INSTANCE_ID" : "{instance.hostname}", "operation" : "{action}" }}""",
|
||||
},
|
||||
"text": f"action_{action}",
|
||||
"color": "success" if action == "start" else "error" if action == "stop" else "warning",
|
||||
}
|
||||
for action in actions
|
||||
]
|
||||
|
||||
instance = instance_widget(
|
||||
containerColumns={"pc": 6, "tablet": 6, "mobile": 12},
|
||||
pairs=[
|
||||
{"key": "instances_name", "value": instance.name},
|
||||
{"key": "instances_hostname", "value": instance.hostname},
|
||||
{"key": "instances_type", "value": instance.type},
|
||||
{"key": "instances_method", "value": instance.method},
|
||||
{"key": "instances_creation_date", "value": instance.creation_date.strftime("%d-%m-%Y %H:%M:%S")},
|
||||
{"key": "instances_last_seen", "value": instance.last_seen.strftime("%d-%m-%Y %H:%M:%S")},
|
||||
],
|
||||
status="success" if instance.status == "up" else "error",
|
||||
title=instance.hostname,
|
||||
buttons=buttons,
|
||||
)
|
||||
|
||||
builder.append(instance)
|
||||
|
||||
return builder
|
||||
290
src/ui/client/builder/jobs.py
Normal file
290
src/ui/client/builder/jobs.py
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
from .utils.widgets import title_widget, table_widget
|
||||
|
||||
|
||||
def jobs_builder(jobs):
|
||||
|
||||
jobs_list = get_jobs_list(jobs)
|
||||
|
||||
intervals = ["all"]
|
||||
|
||||
# loop on each job
|
||||
for job in jobs_list:
|
||||
# loop on each item
|
||||
for item in job:
|
||||
# get the interval if not already in intervals
|
||||
if item.get("every") and item.get("every") not in intervals:
|
||||
intervals.append(item.get("every"))
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
title_widget("jobs_title"),
|
||||
table_widget(
|
||||
positions=[3, 2, 1, 1, 1, 1, 3],
|
||||
header=[
|
||||
"jobs_table_name",
|
||||
"jobs_table_plugin_id",
|
||||
"jobs_table_interval",
|
||||
"jobs_table_reload",
|
||||
"jobs_table_success",
|
||||
"jobs_table_history",
|
||||
"jobs_table_cache_downloadable",
|
||||
],
|
||||
items=jobs_list,
|
||||
filters=[
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "keyword",
|
||||
"type": "keyword",
|
||||
"value": "",
|
||||
"keys": ["name", "plugin_id"],
|
||||
"field": {
|
||||
"id": "jobs-keyword",
|
||||
"value": "",
|
||||
"type": "text",
|
||||
"name": "jobs-keyword",
|
||||
"label": "jobs_search",
|
||||
"placeholder": "inp_keyword",
|
||||
"isClipboard": False,
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_search_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "every",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["every"],
|
||||
"field": {
|
||||
"id": "jobs-every",
|
||||
"value": "all",
|
||||
"values": intervals,
|
||||
"name": "jobs-every",
|
||||
"onlyDown": True,
|
||||
"label": "jobs_interval",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_interval_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "reload",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["reload"],
|
||||
"field": {
|
||||
"id": "jobs-last-run",
|
||||
"value": "all",
|
||||
"values": ["all", "success", "failed"],
|
||||
"name": "jobs-last-run",
|
||||
"onlyDown": True,
|
||||
"label": "jobs_reload",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_reload_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "success",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["success"],
|
||||
"field": {
|
||||
"id": "jobs-success",
|
||||
"value": "all",
|
||||
"values": ["all", "success", "failed"],
|
||||
"name": "jobs-success",
|
||||
"onlyDown": True,
|
||||
"label": "jobs_success",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_success_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
],
|
||||
minWidth="lg",
|
||||
title="jobs_table_title",
|
||||
),
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
return builder
|
||||
|
||||
|
||||
def get_jobs_list(jobs):
|
||||
data = []
|
||||
# loop on each dict
|
||||
for key, value in jobs.items():
|
||||
item = []
|
||||
item.append({"name": key, "type": "Text", "data": {"text": key}})
|
||||
# loop on each value
|
||||
for k, v in value.items():
|
||||
# override widget type for some keys
|
||||
if k in ("reload", "history"):
|
||||
is_success = v if k == "reload" else v[0].get("success")
|
||||
item.append(
|
||||
{
|
||||
k: "success" if is_success else "failed",
|
||||
"type": "Icons",
|
||||
"data": {
|
||||
"iconName": "check" if is_success else "cross",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if k not in ("history"):
|
||||
continue
|
||||
|
||||
if k in ("plugin_id", "every"):
|
||||
item.append({k: v, "type": "Text", "data": {"text": v}})
|
||||
continue
|
||||
|
||||
if k in ("history"):
|
||||
items = []
|
||||
for hist in v:
|
||||
items.append(
|
||||
[
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": hist["start_date"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": hist["end_date"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "Icons",
|
||||
"data": {
|
||||
"iconName": "check" if hist["success"] else "cross",
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
item.append(
|
||||
{
|
||||
"type": "Button",
|
||||
"data": {
|
||||
"id": f"open-modal-history-{k}",
|
||||
"text": "jobs_history",
|
||||
"hideText": True,
|
||||
"color": "blue",
|
||||
"size": "normal",
|
||||
"iconName": "document",
|
||||
"iconColor": "white",
|
||||
"modal": {
|
||||
"widgets": [
|
||||
{"type": "Title", "data": {"title": key}},
|
||||
{"type": "Subtitle", "data": {"subtitle": "jobs_history_subtitle"}},
|
||||
{
|
||||
"type": "Table",
|
||||
"data": {
|
||||
"title": "jobs_history_table_title",
|
||||
"minWidth": "",
|
||||
"header": [
|
||||
"jobs_table_start_run",
|
||||
"jobs_table_end_run",
|
||||
"jobs_table_success",
|
||||
],
|
||||
"positions": [5, 5, 2],
|
||||
"items": items,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "ButtonGroup",
|
||||
"data": {
|
||||
"buttons": [
|
||||
{
|
||||
"id": f"close-history-{k}",
|
||||
"text": "action_close",
|
||||
"color": "close",
|
||||
"size": "normal",
|
||||
"attrs": {"data-close-modal": ""},
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if k in ("cache") and len(v) <= 0:
|
||||
item.append({k: v, "type": "Text", "data": {"text": ""}})
|
||||
continue
|
||||
|
||||
if k in ("cache") and len(v) > 0:
|
||||
files = []
|
||||
# loop on each cache item
|
||||
for cache in v:
|
||||
file_name = f"{cache['file_name']} [{cache['service_id']}]" if cache["service_id"] else f"{cache['file_name']}"
|
||||
files.append(file_name)
|
||||
|
||||
item.append(
|
||||
{
|
||||
k: " ".join(files),
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"attrs": {
|
||||
"data-plugin-id": value.get("plugin_id", ""),
|
||||
"data-job-name": key,
|
||||
},
|
||||
"id": f"{key}_cache",
|
||||
"label": f"{key}_cache",
|
||||
"hideLabel": True,
|
||||
"inpType": "select",
|
||||
"name": f"{key}_cache",
|
||||
"value": "download file",
|
||||
"values": files,
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12,
|
||||
},
|
||||
"overflowAttrEl": "data-table-body",
|
||||
"containerClass": "table download-cache-file",
|
||||
"maxBtnChars": 16,
|
||||
"popovers": [
|
||||
{
|
||||
"iconName": "info",
|
||||
"text": "jobs_download_cache_file",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
continue
|
||||
|
||||
data.append(item)
|
||||
|
||||
return data
|
||||
83
src/ui/client/builder/logs.py
Normal file
83
src/ui/client/builder/logs.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
from .utils.widgets import title_widget
|
||||
|
||||
|
||||
def logs_builder(files: list[str] = [], current_file: str = "", raw_data: str = "") -> str:
|
||||
|
||||
if not files:
|
||||
builder = [
|
||||
{
|
||||
"type": "void",
|
||||
"widgets": [{"type": "MessageUnmatch", "data": {"text": "logs_no_files_found"}}],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
|
||||
file_select = {
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"id": "logs-select-file",
|
||||
"label": "logs_log_file",
|
||||
"inpType": "select",
|
||||
"name": "logs-select-file",
|
||||
"onlyDown": True,
|
||||
"value": current_file or "Select a file",
|
||||
"values": files,
|
||||
"columns": {
|
||||
"pc": 4,
|
||||
"tablet": 6,
|
||||
"mobile": 12,
|
||||
},
|
||||
"maxBtnChars": 20,
|
||||
"attrs": {
|
||||
"data-log": "true",
|
||||
},
|
||||
"popovers": [
|
||||
{
|
||||
"iconName": "info",
|
||||
"text": "logs_select_file_info",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if not raw_data:
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [title_widget("logs_title"), file_select, {"type": "MessageUnmatch", "data": {"text": "logs_not_selected_or_not_found"}}],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
|
||||
editor = {
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"containerClass": "mt-4",
|
||||
"id": "logs-file-content",
|
||||
"label": "logs_file_content",
|
||||
"inpType": "editor",
|
||||
"name": "logs-file-content",
|
||||
"value": raw_data,
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12,
|
||||
},
|
||||
"editorClass" : "min-h-[500px]",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [title_widget("logs_title"), file_select, editor],
|
||||
}
|
||||
]
|
||||
|
||||
return builder
|
||||
38
src/ui/client/builder/pages/advanced_mode.py
Normal file
38
src/ui/client/builder/pages/advanced_mode.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
from .utils.form import get_forms, get_service_settings
|
||||
|
||||
|
||||
def advanced_mode_builder(templates: list[dict], plugins: list, global_config: dict, total_config: dict, service_name: str, is_new: bool = False) -> str:
|
||||
"""Render forms with global config data.
|
||||
ATM we don't need templates but we need to pass at least one to the function (it will simply not override anything).
|
||||
"""
|
||||
settings = get_service_settings(service_name, global_config, total_config)
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {
|
||||
"title": service_name,
|
||||
"type": "container",
|
||||
"lowercase": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "Subtitle",
|
||||
"data": {"subtitle": "services_manage_subtitle", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Templates",
|
||||
"data": {
|
||||
"templates": get_forms(templates, plugins, settings, ("advanced",), is_new, True),
|
||||
"operation": "new" if is_new else "edit",
|
||||
"oldServerName": service_name if service_name else "",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
39
src/ui/client/builder/pages/easy_mode.py
Normal file
39
src/ui/client/builder/pages/easy_mode.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
from .utils.form import get_forms, get_service_settings
|
||||
|
||||
|
||||
def easy_mode_builder(templates: list[dict], plugins: list, global_config: dict, total_config: dict, service_name: str, is_new: bool = False) -> str:
|
||||
"""Render forms with global config data.
|
||||
ATM we don't need templates but we need to pass at least one to the function (it will simply not override anything).
|
||||
"""
|
||||
|
||||
# We need
|
||||
settings = get_service_settings(service_name, global_config, total_config)
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {
|
||||
"title": service_name,
|
||||
"type": "container",
|
||||
"lowercase": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "Subtitle",
|
||||
"data": {"subtitle": "services_manage_subtitle", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Templates",
|
||||
"data": {
|
||||
"templates": get_forms(templates, plugins, settings, ("easy",), is_new, True),
|
||||
"operation": "new" if is_new else "edit",
|
||||
"oldServerName": service_name if service_name else "",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
31
src/ui/client/builder/pages/global_config.py
Normal file
31
src/ui/client/builder/pages/global_config.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
from .utils.form import get_forms
|
||||
|
||||
|
||||
def global_config_builder(templates: list[dict], plugins: list, settings: dict) -> str:
|
||||
"""Render forms with global config data.
|
||||
ATM we don't need templates but we need to pass at least one to the function (it will simply not override anything).
|
||||
"""
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {"title": "global_config_title", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Subtitle",
|
||||
"data": {"subtitle": "global_config_subtitle", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Templates",
|
||||
"data": {
|
||||
"templates": get_forms(templates, plugins, settings, ("advanced", "raw")),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
84
src/ui/client/builder/pages/home.py
Normal file
84
src/ui/client/builder/pages/home.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
from .utils.widgets import stat_widget
|
||||
|
||||
|
||||
def home_builder(data: dict) -> str:
|
||||
"""
|
||||
It returns the needed format from data to render the home page in JSON format for the Vue.js builder
|
||||
"""
|
||||
version_card = stat_widget(
|
||||
link="https://panel.bunkerweb.io/?utm_campaign=self&utm_source=ui#pro",
|
||||
containerColums={"pc": 4, "tablet": 6, "mobile": 12},
|
||||
title="home_version",
|
||||
subtitle=(
|
||||
"home_all_features_available"
|
||||
if data.get("is_pro_version")
|
||||
else (
|
||||
"home_awaiting_compliance"
|
||||
if data.get("pro_status") == "active" and data.get("pro_overlapped")
|
||||
else (
|
||||
"home_renew_license"
|
||||
if data.get("pro_status") == "expired"
|
||||
else "home_talk_to_team" if data.get("pro_status") == "suspended" else "home_upgrade_to_pro"
|
||||
)
|
||||
)
|
||||
),
|
||||
subtitle_color="success" if data.get("is_pro_version") else "warning",
|
||||
stat=(
|
||||
"home_pro"
|
||||
if data.get("is_pro_version")
|
||||
else (
|
||||
"home_pro_locked"
|
||||
if data.get("pro_status") == "active" and data.get("pro_overlapped")
|
||||
else "home_expired" if data.get("pro_status") == "expired" else "home_suspended" if data.get("pro_status") == "suspended" else "home_free"
|
||||
)
|
||||
),
|
||||
icon_name="crown" if data.get("is_pro_version") else "key",
|
||||
)
|
||||
|
||||
version_num_card = stat_widget(
|
||||
link="https://github.com/bunkerity/bunkerweb",
|
||||
containerColums={"pc": 4, "tablet": 6, "mobile": 12},
|
||||
title="home_version_number",
|
||||
subtitle=(
|
||||
"home_couldnt_find_remote"
|
||||
if not data.get("remote_version")
|
||||
else "home_latest_version" if data.get("remote_version") and data.get("check_version") else "home_update_available"
|
||||
),
|
||||
subtitle_color=("error" if not data.get("remote_version") else "success" if data.get("remote_version") and data.get("check_version") else "warning"),
|
||||
stat=data.get("version"),
|
||||
icon_name="wire",
|
||||
)
|
||||
|
||||
instances_card = stat_widget(
|
||||
link="instances",
|
||||
containerColums={"pc": 4, "tablet": 6, "mobile": 12},
|
||||
title="home_instances",
|
||||
subtitle="home_total_number",
|
||||
subtitle_color="info",
|
||||
stat=data.get("instances_number"),
|
||||
icon_name="box",
|
||||
)
|
||||
|
||||
services_card = stat_widget(
|
||||
link="services",
|
||||
containerColums={"pc": 4, "tablet": 6, "mobile": 12},
|
||||
title="home_services",
|
||||
subtitle="home_all_methods_included",
|
||||
subtitle_color="info",
|
||||
stat=data.get("services_number"),
|
||||
icon_name="disk",
|
||||
)
|
||||
|
||||
plugins_card = stat_widget(
|
||||
link="plugins",
|
||||
containerColums={"pc": 4, "tablet": 6, "mobile": 12},
|
||||
title="home_plugins",
|
||||
subtitle="home_errors_found" if data.get("plugins_errors") > 0 else "home_no_error",
|
||||
subtitle_color="error" if data.get("plugins_errors") > 0 else "success",
|
||||
stat=data.get("plugins_number"),
|
||||
icon_name="puzzle",
|
||||
)
|
||||
|
||||
builder = [version_card, version_num_card, instances_card, services_card, plugins_card]
|
||||
|
||||
return builder
|
||||
42
src/ui/client/builder/pages/instances.py
Normal file
42
src/ui/client/builder/pages/instances.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
from .utils.widgets import instance_widget
|
||||
|
||||
|
||||
def instances_builder(instances: List[Instance]) -> str:
|
||||
"""
|
||||
It returns the needed format from data to render the instances page in JSON format for the Vue.js builder
|
||||
"""
|
||||
builder = []
|
||||
|
||||
for instance in instances:
|
||||
# setup actions buttons
|
||||
actions = ["reload", "stop"] if instance.status == "up" else ["start"]
|
||||
|
||||
buttons = [
|
||||
{
|
||||
"attrs": {
|
||||
"data-submit-form": f"""{{"INSTANCE_ID" : "{instance.hostname}", "operation" : "{action}" }}""",
|
||||
},
|
||||
"text": f"action_{action}",
|
||||
"color": "success" if action == "start" else "error" if action == "stop" else "warning",
|
||||
}
|
||||
for action in actions
|
||||
]
|
||||
|
||||
instance = instance_widget(
|
||||
containerColumns={"pc": 6, "tablet": 6, "mobile": 12},
|
||||
pairs=[
|
||||
{"key": "instances_name", "value": instance.name},
|
||||
{"key": "instances_hostname", "value": instance.hostname},
|
||||
{"key": "instances_type", "value": instance.type},
|
||||
{"key": "instances_method", "value": instance.method},
|
||||
{"key": "instances_creation_date", "value": instance.creation_date.strftime("%d-%m-%Y %H:%M:%S")},
|
||||
{"key": "instances_last_seen", "value": instance.last_seen.strftime("%d-%m-%Y %H:%M:%S")},
|
||||
],
|
||||
status="success" if instance.status == "up" else "error",
|
||||
title=instance.hostname,
|
||||
buttons=buttons,
|
||||
)
|
||||
|
||||
builder.append(instance)
|
||||
|
||||
return builder
|
||||
290
src/ui/client/builder/pages/jobs.py
Normal file
290
src/ui/client/builder/pages/jobs.py
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
from .utils.widgets import title_widget, table_widget
|
||||
|
||||
|
||||
def jobs_builder(jobs):
|
||||
|
||||
jobs_list = get_jobs_list(jobs)
|
||||
|
||||
intervals = ["all"]
|
||||
|
||||
# loop on each job
|
||||
for job in jobs_list:
|
||||
# loop on each item
|
||||
for item in job:
|
||||
# get the interval if not already in intervals
|
||||
if item.get("every") and item.get("every") not in intervals:
|
||||
intervals.append(item.get("every"))
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
title_widget("jobs_title"),
|
||||
table_widget(
|
||||
positions=[3, 2, 1, 1, 1, 1, 3],
|
||||
header=[
|
||||
"jobs_table_name",
|
||||
"jobs_table_plugin_id",
|
||||
"jobs_table_interval",
|
||||
"jobs_table_reload",
|
||||
"jobs_table_success",
|
||||
"jobs_table_history",
|
||||
"jobs_table_cache_downloadable",
|
||||
],
|
||||
items=jobs_list,
|
||||
filters=[
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "keyword",
|
||||
"type": "keyword",
|
||||
"value": "",
|
||||
"keys": ["name", "plugin_id"],
|
||||
"field": {
|
||||
"id": "jobs-keyword",
|
||||
"value": "",
|
||||
"type": "text",
|
||||
"name": "jobs-keyword",
|
||||
"label": "jobs_search",
|
||||
"placeholder": "inp_keyword",
|
||||
"isClipboard": False,
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_search_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "every",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["every"],
|
||||
"field": {
|
||||
"id": "jobs-every",
|
||||
"value": "all",
|
||||
"values": intervals,
|
||||
"name": "jobs-every",
|
||||
"onlyDown": True,
|
||||
"label": "jobs_interval",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_interval_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "reload",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["reload"],
|
||||
"field": {
|
||||
"id": "jobs-last-run",
|
||||
"value": "all",
|
||||
"values": ["all", "success", "failed"],
|
||||
"name": "jobs-last-run",
|
||||
"onlyDown": True,
|
||||
"label": "jobs_reload",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_reload_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "success",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["success"],
|
||||
"field": {
|
||||
"id": "jobs-success",
|
||||
"value": "all",
|
||||
"values": ["all", "success", "failed"],
|
||||
"name": "jobs-success",
|
||||
"onlyDown": True,
|
||||
"label": "jobs_success",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "jobs_success_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
],
|
||||
minWidth="lg",
|
||||
title="jobs_table_title",
|
||||
),
|
||||
],
|
||||
}
|
||||
]
|
||||
|
||||
return builder
|
||||
|
||||
|
||||
def get_jobs_list(jobs):
|
||||
data = []
|
||||
# loop on each dict
|
||||
for key, value in jobs.items():
|
||||
item = []
|
||||
item.append({"name": key, "type": "Text", "data": {"text": key}})
|
||||
# loop on each value
|
||||
for k, v in value.items():
|
||||
# override widget type for some keys
|
||||
if k in ("reload", "history"):
|
||||
is_success = v if k == "reload" else v[0].get("success")
|
||||
item.append(
|
||||
{
|
||||
k: "success" if is_success else "failed",
|
||||
"type": "Icons",
|
||||
"data": {
|
||||
"iconName": "check" if is_success else "cross",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if k not in ("history"):
|
||||
continue
|
||||
|
||||
if k in ("plugin_id", "every"):
|
||||
item.append({k: v, "type": "Text", "data": {"text": v}})
|
||||
continue
|
||||
|
||||
if k in ("history"):
|
||||
items = []
|
||||
for hist in v:
|
||||
items.append(
|
||||
[
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": hist["start_date"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": hist["end_date"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "Icons",
|
||||
"data": {
|
||||
"iconName": "check" if hist["success"] else "cross",
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
item.append(
|
||||
{
|
||||
"type": "Button",
|
||||
"data": {
|
||||
"id": f"open-modal-history-{k}",
|
||||
"text": "jobs_history",
|
||||
"hideText": True,
|
||||
"color": "blue",
|
||||
"size": "normal",
|
||||
"iconName": "document",
|
||||
"iconColor": "white",
|
||||
"modal": {
|
||||
"widgets": [
|
||||
{"type": "Title", "data": {"title": key}},
|
||||
{"type": "Subtitle", "data": {"subtitle": "jobs_history_subtitle"}},
|
||||
{
|
||||
"type": "Table",
|
||||
"data": {
|
||||
"title": "jobs_history_table_title",
|
||||
"minWidth": "",
|
||||
"header": [
|
||||
"jobs_table_start_run",
|
||||
"jobs_table_end_run",
|
||||
"jobs_table_success",
|
||||
],
|
||||
"positions": [5, 5, 2],
|
||||
"items": items,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "ButtonGroup",
|
||||
"data": {
|
||||
"buttons": [
|
||||
{
|
||||
"id": f"close-history-{k}",
|
||||
"text": "action_close",
|
||||
"color": "close",
|
||||
"size": "normal",
|
||||
"attrs": {"data-close-modal": ""},
|
||||
}
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if k in ("cache") and len(v) <= 0:
|
||||
item.append({k: v, "type": "Text", "data": {"text": ""}})
|
||||
continue
|
||||
|
||||
if k in ("cache") and len(v) > 0:
|
||||
files = []
|
||||
# loop on each cache item
|
||||
for cache in v:
|
||||
file_name = f"{cache['file_name']} [{cache['service_id']}]" if cache["service_id"] else f"{cache['file_name']}"
|
||||
files.append(file_name)
|
||||
|
||||
item.append(
|
||||
{
|
||||
k: " ".join(files),
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"attrs": {
|
||||
"data-plugin-id": value.get("plugin_id", ""),
|
||||
"data-job-name": key,
|
||||
},
|
||||
"id": f"{key}_cache",
|
||||
"label": f"{key}_cache",
|
||||
"hideLabel": True,
|
||||
"inpType": "select",
|
||||
"name": f"{key}_cache",
|
||||
"value": "download file",
|
||||
"values": files,
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12,
|
||||
},
|
||||
"overflowAttrEl": "data-table-body",
|
||||
"containerClass": "table download-cache-file",
|
||||
"maxBtnChars": 16,
|
||||
"popovers": [
|
||||
{
|
||||
"iconName": "info",
|
||||
"text": "jobs_download_cache_file",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
continue
|
||||
|
||||
data.append(item)
|
||||
|
||||
return data
|
||||
83
src/ui/client/builder/pages/logs.py
Normal file
83
src/ui/client/builder/pages/logs.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
from .utils.widgets import title_widget
|
||||
|
||||
|
||||
def logs_builder(files: list[str] = [], current_file: str = "", raw_data: str = "") -> str:
|
||||
|
||||
if not files:
|
||||
builder = [
|
||||
{
|
||||
"type": "void",
|
||||
"widgets": [{"type": "MessageUnmatch", "data": {"text": "logs_no_files_found"}}],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
|
||||
file_select = {
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"id": "logs-select-file",
|
||||
"label": "logs_log_file",
|
||||
"inpType": "select",
|
||||
"name": "logs-select-file",
|
||||
"onlyDown": True,
|
||||
"value": current_file or "Select a file",
|
||||
"values": files,
|
||||
"columns": {
|
||||
"pc": 4,
|
||||
"tablet": 6,
|
||||
"mobile": 12,
|
||||
},
|
||||
"maxBtnChars": 20,
|
||||
"attrs": {
|
||||
"data-log": "true",
|
||||
},
|
||||
"popovers": [
|
||||
{
|
||||
"iconName": "info",
|
||||
"text": "logs_select_file_info",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if not raw_data:
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [title_widget("logs_title"), file_select, {"type": "MessageUnmatch", "data": {"text": "logs_not_selected_or_not_found"}}],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
|
||||
editor = {
|
||||
"type": "Fields",
|
||||
"data": {
|
||||
"setting": {
|
||||
"containerClass": "mt-4",
|
||||
"id": "logs-file-content",
|
||||
"label": "logs_file_content",
|
||||
"inpType": "editor",
|
||||
"name": "logs-file-content",
|
||||
"value": raw_data,
|
||||
"columns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12,
|
||||
},
|
||||
"editorClass" : "min-h-[500px]",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [title_widget("logs_title"), file_select, editor],
|
||||
}
|
||||
]
|
||||
|
||||
return builder
|
||||
40
src/ui/client/builder/pages/raw_mode.py
Normal file
40
src/ui/client/builder/pages/raw_mode.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
from .utils.form import get_forms, get_service_settings
|
||||
|
||||
|
||||
def raw_mode_builder(templates: list[dict], plugins: list, global_config: dict, total_config: dict, service_name: str, is_new: bool = False) -> str:
|
||||
"""Render forms with global config data.
|
||||
ATM we don't need templates but we need to pass at least one to the function (it will simply not override anything).
|
||||
"""
|
||||
|
||||
# We need
|
||||
settings = get_service_settings(service_name, global_config, total_config)
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {
|
||||
"title": service_name,
|
||||
"type": "container",
|
||||
"lowercase": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "Subtitle",
|
||||
"data": {"subtitle": "services_manage_subtitle", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Templates",
|
||||
"data": {
|
||||
"templates": get_forms(templates, plugins, settings, ("raw",), is_new, True),
|
||||
"operation": "new" if is_new else "edit",
|
||||
"oldServerName": service_name if service_name else "",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
379
src/ui/client/builder/pages/services.py
Normal file
379
src/ui/client/builder/pages/services.py
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
from typing import Union
|
||||
|
||||
from .utils.widgets import title_widget, table_widget
|
||||
|
||||
|
||||
def services_builder(services):
|
||||
# get method for each service["SERVER_NAME"]["method"]
|
||||
methods = list(set([service["SERVER_NAME"]["method"] for service in services]))
|
||||
|
||||
services_list = get_services_list(services)
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
title_widget("services_title"),
|
||||
{
|
||||
"type": "Button",
|
||||
"data": {
|
||||
"id": "services-new",
|
||||
"text": "services_new",
|
||||
"color": "success",
|
||||
"size": "normal",
|
||||
"iconName": "plus",
|
||||
"iconColor": "white",
|
||||
"modal": services_action(server_name="new", operation="new", title="services_new_title", subtitle="services_new_subtitle"),
|
||||
"containerClass": "col-span-12 flex justify-center",
|
||||
},
|
||||
},
|
||||
table_widget(
|
||||
positions=[4, 4, 4],
|
||||
header=[
|
||||
"services_table_name",
|
||||
"services_table_method",
|
||||
"services_table_actions",
|
||||
],
|
||||
items=services_list,
|
||||
filters=[
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "keyword",
|
||||
"type": "keyword",
|
||||
"value": "",
|
||||
"keys": ["name"],
|
||||
"field": {
|
||||
"id": "services-keyword",
|
||||
"value": "",
|
||||
"type": "text",
|
||||
"name": "services-keyword",
|
||||
"label": "services_search",
|
||||
"placeholder": "inp_keyword",
|
||||
"isClipboard": False,
|
||||
"popovers": [
|
||||
{
|
||||
"text": "services_search_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "method",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["method"],
|
||||
"field": {
|
||||
"id": "services-methods",
|
||||
"value": "all",
|
||||
"values": methods,
|
||||
"name": "services-methods",
|
||||
"onlyDown": True,
|
||||
"label": "services_methods",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "services_methods_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "draft",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["draft"],
|
||||
"field": {
|
||||
"id": "services-draft",
|
||||
"value": "all",
|
||||
"values": ["all", "online", "draft"],
|
||||
"name": "services-draft",
|
||||
"onlyDown": True,
|
||||
"label": "services_draft",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "services_draft_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
],
|
||||
minWidth="md",
|
||||
title="services_table_title",
|
||||
),
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
return builder
|
||||
|
||||
|
||||
def services_settings(settings: dict) -> dict:
|
||||
# deep copy settings dict
|
||||
settings = settings.copy()
|
||||
# remove "SERVER_NAME" and "IS_DRAFT" key
|
||||
settings.pop("SERVER_NAME", None)
|
||||
settings.pop("IS_DRAFT", None)
|
||||
# Create table with settings remaining keys
|
||||
settings_table_items = []
|
||||
for key, value in settings.items():
|
||||
format_key = key.replace("USE_", "").replace("_", " ")
|
||||
settings_table_items.append(
|
||||
[
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {"text": format_key},
|
||||
},
|
||||
{
|
||||
"type": "Icons",
|
||||
"data": {
|
||||
"iconName": "check" if value.get("value") == "yes" else "cross",
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
table = table_widget(
|
||||
positions=[8, 4],
|
||||
header=["services_settings_table_name", "services_settings_table_status"],
|
||||
items=settings_table_items,
|
||||
filters=[],
|
||||
minWidth="",
|
||||
title="services_settings_table_title",
|
||||
)
|
||||
|
||||
return table
|
||||
|
||||
|
||||
def services_action(
|
||||
server_name: str = "",
|
||||
operation: str = "",
|
||||
title: str = "",
|
||||
subtitle: str = "",
|
||||
additional: str = "",
|
||||
is_draft: Union[bool, None] = None,
|
||||
service: dict = None,
|
||||
) -> dict:
|
||||
|
||||
buttons = [
|
||||
{
|
||||
"id": f"close-service-btn-{server_name}",
|
||||
"text": "action_close",
|
||||
"disabled": False,
|
||||
"color": "close",
|
||||
"size": "normal",
|
||||
"attrs": {"data-close-modal": ""},
|
||||
},
|
||||
]
|
||||
|
||||
if operation == "delete":
|
||||
buttons.append(
|
||||
{
|
||||
"id": f"{operation}-service-btn-{server_name}",
|
||||
"text": f"action_{operation}",
|
||||
"disabled": False,
|
||||
"color": "delete",
|
||||
"size": "normal",
|
||||
"attrs": {
|
||||
"data-submit-form": f"""{{"SERVER_NAME" : "{server_name}", "operation" : "{operation}" }}""",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if operation == "draft":
|
||||
draft_value = "yes" if is_draft else "no"
|
||||
buttons.append(
|
||||
{
|
||||
"id": f"{operation}-service-btn-{server_name}",
|
||||
"text": "action_switch",
|
||||
"disabled": False,
|
||||
"color": "success",
|
||||
"size": "normal",
|
||||
"attrs": {
|
||||
"data-submit-form": f"""{{"SERVER_NAME" : "{server_name}", "OLD_SERVER_NAME" : "{server_name}", "operation" : "edit", "IS_DRAFT" : "{draft_value}" }}""",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
content = [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {
|
||||
"title": title,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
if subtitle:
|
||||
content.append(
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": subtitle,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if additional:
|
||||
content.append(
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"bold": True,
|
||||
"text": additional,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if operation == "plugins":
|
||||
settings = services_settings(service)
|
||||
content.append(settings)
|
||||
|
||||
if operation == "delete":
|
||||
content.append(
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "",
|
||||
"bold": True,
|
||||
"text": server_name,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if operation == "edit" or operation == "new":
|
||||
modes = ("easy", "advanced", "raw")
|
||||
mode_buttons = []
|
||||
for mode in modes:
|
||||
mode_buttons.append(
|
||||
{
|
||||
"id": f"{operation}-service-btn-{server_name}",
|
||||
"text": f"services_mode_{mode}",
|
||||
"disabled": False,
|
||||
"color": "info",
|
||||
"size": "normal",
|
||||
"attrs": {
|
||||
"role": "link",
|
||||
"data-link": f"modes?service_name={server_name}&mode={mode}" if operation != "new" else f"modes?mode={mode}",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
content.append(
|
||||
{
|
||||
"type": "ButtonGroup",
|
||||
"data": {"buttons": mode_buttons},
|
||||
}
|
||||
)
|
||||
|
||||
content.append(
|
||||
{
|
||||
"type": "ButtonGroup",
|
||||
"data": {"buttons": buttons},
|
||||
},
|
||||
)
|
||||
|
||||
modal = {
|
||||
"widgets": content,
|
||||
}
|
||||
|
||||
return modal
|
||||
|
||||
|
||||
def get_services_list(services):
|
||||
data = []
|
||||
for index, service in enumerate(services):
|
||||
server_name = service["SERVER_NAME"]["value"]
|
||||
server_method = service["SERVER_NAME"]["method"]
|
||||
is_draft = True if service["IS_DRAFT"]["value"] == "yes" else False
|
||||
is_deletable = False if server_method in ("autoconf", "scheduler") else True
|
||||
|
||||
item = []
|
||||
# Get name
|
||||
item.append({"name": server_name, "type": "Text", "data": {"text": server_name}})
|
||||
item.append({"method": server_method, "type": "Text", "data": {"text": server_method}})
|
||||
item.append(
|
||||
{
|
||||
"type": "ButtonGroup",
|
||||
"data": {
|
||||
"buttons": [
|
||||
{
|
||||
"id": f"open-modal-plugins-{index}",
|
||||
"text": "plugins",
|
||||
"hideText": True,
|
||||
"color": "success",
|
||||
"size": "normal",
|
||||
"iconName": "eye",
|
||||
"iconColor": "white",
|
||||
"modal": services_action(
|
||||
server_name=server_name,
|
||||
operation="plugins",
|
||||
title="services_plugins_title",
|
||||
subtitle="",
|
||||
service=service,
|
||||
),
|
||||
},
|
||||
{
|
||||
"attrs": {"data-server-name": server_name},
|
||||
"id": f"open-modal-manage-{index}",
|
||||
"text": "manage",
|
||||
"hideText": True,
|
||||
"color": "edit",
|
||||
"size": "normal",
|
||||
"iconName": "pen",
|
||||
"iconColor": "white",
|
||||
"modal": services_action(
|
||||
server_name=server_name,
|
||||
operation="edit",
|
||||
title="services_edit_title",
|
||||
subtitle="services_edit_subtitle",
|
||||
additional=server_name,
|
||||
),
|
||||
},
|
||||
{
|
||||
"attrs": {"data-server-name": server_name, "data-is-draft": "yes" if is_draft else "no"},
|
||||
"id": f"open-modal-draft-{index}",
|
||||
"text": "draft" if is_draft else "online",
|
||||
"hideText": True,
|
||||
"color": "blue",
|
||||
"size": "normal",
|
||||
"iconName": "document" if is_draft else "globe",
|
||||
"iconColor": "white",
|
||||
"modal": services_action(
|
||||
server_name=server_name,
|
||||
operation="draft",
|
||||
title="services_draft_title",
|
||||
subtitle="services_draft_subtitle" if is_draft else "services_online_subtitle",
|
||||
additional="services_draft_switch_subtitle" if is_draft else "services_online_switch_subtitle",
|
||||
is_draft=is_draft,
|
||||
),
|
||||
},
|
||||
{
|
||||
"attrs": {"data-server-name": server_name},
|
||||
"id": f"open-modal-delete-{index}",
|
||||
"text": "delete",
|
||||
"disabled": not is_deletable,
|
||||
"hideText": True,
|
||||
"color": "red",
|
||||
"size": "normal",
|
||||
"iconName": "trash",
|
||||
"iconColor": "white",
|
||||
"modal": services_action(
|
||||
server_name=server_name, operation="delete", title="services_delete_title", subtitle="services_delete_subtitle"
|
||||
),
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
data.append(item)
|
||||
|
||||
return data
|
||||
40
src/ui/client/builder/raw_mode.py
Normal file
40
src/ui/client/builder/raw_mode.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
from .utils.form import get_forms, get_service_settings
|
||||
|
||||
|
||||
def raw_mode_builder(templates: list[dict], plugins: list, global_config: dict, total_config: dict, service_name: str, is_new: bool = False) -> str:
|
||||
"""Render forms with global config data.
|
||||
ATM we don't need templates but we need to pass at least one to the function (it will simply not override anything).
|
||||
"""
|
||||
|
||||
# We need
|
||||
settings = get_service_settings(service_name, global_config, total_config)
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {
|
||||
"title": service_name,
|
||||
"type": "container",
|
||||
"lowercase": True,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "Subtitle",
|
||||
"data": {"subtitle": "services_manage_subtitle", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Templates",
|
||||
"data": {
|
||||
"templates": get_forms(templates, plugins, settings, ("raw",), is_new, True),
|
||||
"operation": "new" if is_new else "edit",
|
||||
"oldServerName": service_name if service_name else "",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
379
src/ui/client/builder/services.py
Normal file
379
src/ui/client/builder/services.py
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
from typing import Union
|
||||
|
||||
from .utils.widgets import title_widget, table_widget
|
||||
|
||||
|
||||
def services_builder(services):
|
||||
# get method for each service["SERVER_NAME"]["method"]
|
||||
methods = list(set([service["SERVER_NAME"]["method"] for service in services]))
|
||||
|
||||
services_list = get_services_list(services)
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
title_widget("services_title"),
|
||||
{
|
||||
"type": "Button",
|
||||
"data": {
|
||||
"id": "services-new",
|
||||
"text": "services_new",
|
||||
"color": "success",
|
||||
"size": "normal",
|
||||
"iconName": "plus",
|
||||
"iconColor": "white",
|
||||
"modal": services_action(server_name="new", operation="new", title="services_new_title", subtitle="services_new_subtitle"),
|
||||
"containerClass": "col-span-12 flex justify-center",
|
||||
},
|
||||
},
|
||||
table_widget(
|
||||
positions=[4, 4, 4],
|
||||
header=[
|
||||
"services_table_name",
|
||||
"services_table_method",
|
||||
"services_table_actions",
|
||||
],
|
||||
items=services_list,
|
||||
filters=[
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "keyword",
|
||||
"type": "keyword",
|
||||
"value": "",
|
||||
"keys": ["name"],
|
||||
"field": {
|
||||
"id": "services-keyword",
|
||||
"value": "",
|
||||
"type": "text",
|
||||
"name": "services-keyword",
|
||||
"label": "services_search",
|
||||
"placeholder": "inp_keyword",
|
||||
"isClipboard": False,
|
||||
"popovers": [
|
||||
{
|
||||
"text": "services_search_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "method",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["method"],
|
||||
"field": {
|
||||
"id": "services-methods",
|
||||
"value": "all",
|
||||
"values": methods,
|
||||
"name": "services-methods",
|
||||
"onlyDown": True,
|
||||
"label": "services_methods",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "services_methods_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
{
|
||||
"filter": "table",
|
||||
"filterName": "draft",
|
||||
"type": "select",
|
||||
"value": "all",
|
||||
"keys": ["draft"],
|
||||
"field": {
|
||||
"id": "services-draft",
|
||||
"value": "all",
|
||||
"values": ["all", "online", "draft"],
|
||||
"name": "services-draft",
|
||||
"onlyDown": True,
|
||||
"label": "services_draft",
|
||||
"popovers": [
|
||||
{
|
||||
"text": "services_draft_desc",
|
||||
"iconName": "info",
|
||||
},
|
||||
],
|
||||
"columns": {"pc": 3, "tablet": 4, "mobile": 12},
|
||||
},
|
||||
},
|
||||
],
|
||||
minWidth="md",
|
||||
title="services_table_title",
|
||||
),
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
return builder
|
||||
|
||||
|
||||
def services_settings(settings: dict) -> dict:
|
||||
# deep copy settings dict
|
||||
settings = settings.copy()
|
||||
# remove "SERVER_NAME" and "IS_DRAFT" key
|
||||
settings.pop("SERVER_NAME", None)
|
||||
settings.pop("IS_DRAFT", None)
|
||||
# Create table with settings remaining keys
|
||||
settings_table_items = []
|
||||
for key, value in settings.items():
|
||||
format_key = key.replace("USE_", "").replace("_", " ")
|
||||
settings_table_items.append(
|
||||
[
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {"text": format_key},
|
||||
},
|
||||
{
|
||||
"type": "Icons",
|
||||
"data": {
|
||||
"iconName": "check" if value.get("value") == "yes" else "cross",
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
table = table_widget(
|
||||
positions=[8, 4],
|
||||
header=["services_settings_table_name", "services_settings_table_status"],
|
||||
items=settings_table_items,
|
||||
filters=[],
|
||||
minWidth="",
|
||||
title="services_settings_table_title",
|
||||
)
|
||||
|
||||
return table
|
||||
|
||||
|
||||
def services_action(
|
||||
server_name: str = "",
|
||||
operation: str = "",
|
||||
title: str = "",
|
||||
subtitle: str = "",
|
||||
additional: str = "",
|
||||
is_draft: Union[bool, None] = None,
|
||||
service: dict = None,
|
||||
) -> dict:
|
||||
|
||||
buttons = [
|
||||
{
|
||||
"id": f"close-service-btn-{server_name}",
|
||||
"text": "action_close",
|
||||
"disabled": False,
|
||||
"color": "close",
|
||||
"size": "normal",
|
||||
"attrs": {"data-close-modal": ""},
|
||||
},
|
||||
]
|
||||
|
||||
if operation == "delete":
|
||||
buttons.append(
|
||||
{
|
||||
"id": f"{operation}-service-btn-{server_name}",
|
||||
"text": f"action_{operation}",
|
||||
"disabled": False,
|
||||
"color": "delete",
|
||||
"size": "normal",
|
||||
"attrs": {
|
||||
"data-submit-form": f"""{{"SERVER_NAME" : "{server_name}", "operation" : "{operation}" }}""",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if operation == "draft":
|
||||
draft_value = "yes" if is_draft else "no"
|
||||
buttons.append(
|
||||
{
|
||||
"id": f"{operation}-service-btn-{server_name}",
|
||||
"text": "action_switch",
|
||||
"disabled": False,
|
||||
"color": "success",
|
||||
"size": "normal",
|
||||
"attrs": {
|
||||
"data-submit-form": f"""{{"SERVER_NAME" : "{server_name}", "OLD_SERVER_NAME" : "{server_name}", "operation" : "edit", "IS_DRAFT" : "{draft_value}" }}""",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
content = [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {
|
||||
"title": title,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
if subtitle:
|
||||
content.append(
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": subtitle,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if additional:
|
||||
content.append(
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"bold": True,
|
||||
"text": additional,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if operation == "plugins":
|
||||
settings = services_settings(service)
|
||||
content.append(settings)
|
||||
|
||||
if operation == "delete":
|
||||
content.append(
|
||||
{
|
||||
"type": "Text",
|
||||
"data": {
|
||||
"text": "",
|
||||
"bold": True,
|
||||
"text": server_name,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if operation == "edit" or operation == "new":
|
||||
modes = ("easy", "advanced", "raw")
|
||||
mode_buttons = []
|
||||
for mode in modes:
|
||||
mode_buttons.append(
|
||||
{
|
||||
"id": f"{operation}-service-btn-{server_name}",
|
||||
"text": f"services_mode_{mode}",
|
||||
"disabled": False,
|
||||
"color": "info",
|
||||
"size": "normal",
|
||||
"attrs": {
|
||||
"role": "link",
|
||||
"data-link": f"modes?service_name={server_name}&mode={mode}" if operation != "new" else f"modes?mode={mode}",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
content.append(
|
||||
{
|
||||
"type": "ButtonGroup",
|
||||
"data": {"buttons": mode_buttons},
|
||||
}
|
||||
)
|
||||
|
||||
content.append(
|
||||
{
|
||||
"type": "ButtonGroup",
|
||||
"data": {"buttons": buttons},
|
||||
},
|
||||
)
|
||||
|
||||
modal = {
|
||||
"widgets": content,
|
||||
}
|
||||
|
||||
return modal
|
||||
|
||||
|
||||
def get_services_list(services):
|
||||
data = []
|
||||
for index, service in enumerate(services):
|
||||
server_name = service["SERVER_NAME"]["value"]
|
||||
server_method = service["SERVER_NAME"]["method"]
|
||||
is_draft = True if service["IS_DRAFT"]["value"] == "yes" else False
|
||||
is_deletable = False if server_method in ("autoconf", "scheduler") else True
|
||||
|
||||
item = []
|
||||
# Get name
|
||||
item.append({"name": server_name, "type": "Text", "data": {"text": server_name}})
|
||||
item.append({"method": server_method, "type": "Text", "data": {"text": server_method}})
|
||||
item.append(
|
||||
{
|
||||
"type": "ButtonGroup",
|
||||
"data": {
|
||||
"buttons": [
|
||||
{
|
||||
"id": f"open-modal-plugins-{index}",
|
||||
"text": "plugins",
|
||||
"hideText": True,
|
||||
"color": "success",
|
||||
"size": "normal",
|
||||
"iconName": "eye",
|
||||
"iconColor": "white",
|
||||
"modal": services_action(
|
||||
server_name=server_name,
|
||||
operation="plugins",
|
||||
title="services_plugins_title",
|
||||
subtitle="",
|
||||
service=service,
|
||||
),
|
||||
},
|
||||
{
|
||||
"attrs": {"data-server-name": server_name},
|
||||
"id": f"open-modal-manage-{index}",
|
||||
"text": "manage",
|
||||
"hideText": True,
|
||||
"color": "edit",
|
||||
"size": "normal",
|
||||
"iconName": "pen",
|
||||
"iconColor": "white",
|
||||
"modal": services_action(
|
||||
server_name=server_name,
|
||||
operation="edit",
|
||||
title="services_edit_title",
|
||||
subtitle="services_edit_subtitle",
|
||||
additional=server_name,
|
||||
),
|
||||
},
|
||||
{
|
||||
"attrs": {"data-server-name": server_name, "data-is-draft": "yes" if is_draft else "no"},
|
||||
"id": f"open-modal-draft-{index}",
|
||||
"text": "draft" if is_draft else "online",
|
||||
"hideText": True,
|
||||
"color": "blue",
|
||||
"size": "normal",
|
||||
"iconName": "document" if is_draft else "globe",
|
||||
"iconColor": "white",
|
||||
"modal": services_action(
|
||||
server_name=server_name,
|
||||
operation="draft",
|
||||
title="services_draft_title",
|
||||
subtitle="services_draft_subtitle" if is_draft else "services_online_subtitle",
|
||||
additional="services_draft_switch_subtitle" if is_draft else "services_online_switch_subtitle",
|
||||
is_draft=is_draft,
|
||||
),
|
||||
},
|
||||
{
|
||||
"attrs": {"data-server-name": server_name},
|
||||
"id": f"open-modal-delete-{index}",
|
||||
"text": "delete",
|
||||
"disabled": not is_deletable,
|
||||
"hideText": True,
|
||||
"color": "red",
|
||||
"size": "normal",
|
||||
"iconName": "trash",
|
||||
"iconColor": "white",
|
||||
"modal": services_action(
|
||||
server_name=server_name, operation="delete", title="services_delete_title", subtitle="services_delete_subtitle"
|
||||
),
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
data.append(item)
|
||||
|
||||
return data
|
||||
494
src/ui/client/builder/utils/form.py
Normal file
494
src/ui/client/builder/utils/form.py
Normal file
|
|
@ -0,0 +1,494 @@
|
|||
import copy
|
||||
from typing import Union
|
||||
|
||||
|
||||
def get_setting_data(template_settings: dict, settings: dict, setting: str, value: dict, is_multiple_setting: bool = False, is_new: bool = False) -> tuple:
|
||||
template_value = template_settings.get(setting, None)
|
||||
current_value = settings[setting].get("value", None) if setting in settings else None
|
||||
default_value = value.get("default")
|
||||
is_disabled_method = (
|
||||
True if settings.get(setting, {}).get("method", "ui") not in ("ui", "default", "manual") and not is_new and not is_multiple_setting else False
|
||||
)
|
||||
is_current_from_template = True if settings.get(setting, {}).get("template", None) is not None and template_value is not None else False
|
||||
is_current_default = current_value is not None and current_value == default_value
|
||||
setting_value = current_value if current_value is not None and not is_new and not is_multiple_setting else default_value
|
||||
return template_value, current_value, default_value, is_disabled_method, is_current_from_template, is_current_default, setting_value
|
||||
|
||||
|
||||
def get_service_settings(service_name: str, global_config: dict, total_config: dict) -> dict:
|
||||
"""
|
||||
total_config is a dict that contains global settings and services settings (format SERVICE_NAME_SETTING - www.example.com_USE_ANTIBOT for example -).
|
||||
We will only keep settings that are related to the service_name (with prefix SERVICE_NAME_).
|
||||
Then we will loop on global key and override value from global config by service config if exists.
|
||||
"""
|
||||
|
||||
# Get service settings
|
||||
service_settings = {}
|
||||
for key, value in total_config.items():
|
||||
if not key.startswith(f"{service_name}_"):
|
||||
continue
|
||||
|
||||
service_settings[key.replace(f"{service_name}_", "")] = value
|
||||
|
||||
# Loop on global settings to override by service settings
|
||||
for key, value in service_settings.items():
|
||||
global_config[key] = value
|
||||
|
||||
return global_config
|
||||
|
||||
|
||||
def get_plugins_multisite(plugins: list) -> list:
|
||||
# loop on plugins with list index
|
||||
plugins_multisite = []
|
||||
for index, plugin in enumerate(plugins):
|
||||
multisite_settings = {}
|
||||
# loop on settings
|
||||
for setting, value in plugin.get("settings").items():
|
||||
# check if setting is multisite
|
||||
if value.get("context") != "multisite":
|
||||
continue
|
||||
# add multisite key to plugin
|
||||
multisite_settings[setting] = value
|
||||
|
||||
# add multisite settings to plugin
|
||||
if len(multisite_settings):
|
||||
plugin_multisite = copy.deepcopy(plugin)
|
||||
plugin_multisite["settings"] = multisite_settings
|
||||
plugins_multisite.append(plugin_multisite)
|
||||
|
||||
return plugins_multisite
|
||||
|
||||
|
||||
def get_forms(
|
||||
templates_ui: list = [],
|
||||
plugins: list = [],
|
||||
settings: dict = {},
|
||||
render_forms: tuple = ("advanced", "easy", "raw"),
|
||||
is_new: bool = False,
|
||||
only_multisite: bool = False,
|
||||
) -> dict:
|
||||
"""
|
||||
Will generate every needed form using templates, plugins and settings.
|
||||
We will run on each plugins, set template value if one, and override by the custom settings value if exists.
|
||||
We will format to fit each form type (easy, advanced, raw) in case
|
||||
"""
|
||||
# Copy of the plugins, and get the plugins by context if needed
|
||||
# In services page, we want only multisite settings, but in global config we want both
|
||||
plugins_base = get_plugins_multisite(plugins) if only_multisite else plugins
|
||||
|
||||
# This template will be used to show default value or value if exists
|
||||
templates = [
|
||||
{
|
||||
"name": "default",
|
||||
"steps": [],
|
||||
"configs": {},
|
||||
"settings": {},
|
||||
}
|
||||
]
|
||||
|
||||
for key, value in templates_ui.items():
|
||||
value["label"] = value["name"]
|
||||
value["name"] = key
|
||||
templates.append(value)
|
||||
|
||||
# Update SERVER_NAME to be empty if new
|
||||
if is_new and "SERVER_NAME" in settings:
|
||||
settings["SERVER_NAME"]["value"] = ""
|
||||
|
||||
if is_new and not "SERVER_NAME" in settings:
|
||||
settings["SERVER_NAME"] = {"value": "", "method": "ui", "global": False}
|
||||
|
||||
forms = {}
|
||||
for form in render_forms:
|
||||
forms[form] = {}
|
||||
|
||||
for template in templates:
|
||||
if "advanced" in forms:
|
||||
forms["advanced"][template.get("name")] = set_advanced(template, plugins_base, settings, is_new)
|
||||
|
||||
if "raw" in forms:
|
||||
forms["raw"][template.get("name")] = set_raw(template, plugins_base, settings, is_new)
|
||||
|
||||
if "easy" in forms:
|
||||
forms["easy"][template.get("name")] = set_easy(template, plugins_base, settings, is_new)
|
||||
|
||||
return forms
|
||||
|
||||
|
||||
def set_easy(template: list, plugins_base: list, settings: dict, is_new: bool) -> dict:
|
||||
"""
|
||||
Prepare the easy form based on the template and plugins data.
|
||||
We need to loop on each steps and prepare settings and configs for each step.
|
||||
"""
|
||||
|
||||
template_settings = template.get("settings")
|
||||
plugins = copy.deepcopy(plugins_base)
|
||||
# Copy of the plugins base data
|
||||
plugins = copy.deepcopy(plugins_base)
|
||||
# Update settings with global config data
|
||||
for plugin in plugins:
|
||||
loop_id = 0
|
||||
total_settings = len(plugin.get("settings"))
|
||||
for setting, value in plugin.get("settings").items():
|
||||
loop_id += 1
|
||||
value = format_setting(setting, value, total_settings, loop_id, template_settings, settings, is_new)
|
||||
|
||||
set_multiples(template, plugins, settings)
|
||||
|
||||
steps = template.get("steps")
|
||||
for step in steps:
|
||||
step_settings = step.get("settings", {})
|
||||
|
||||
for plugin in plugins:
|
||||
step_settings_output = {}
|
||||
for setting, value in plugin.get("settings").items():
|
||||
if setting not in step_settings:
|
||||
continue
|
||||
|
||||
step_settings_output[setting] = value
|
||||
|
||||
# Case at least one key in step settings, we can add the plugin settings to the step
|
||||
if len(step_settings_output) and not "plugins" in step:
|
||||
step["plugins"] = []
|
||||
|
||||
if len(step_settings_output):
|
||||
step_plugin = copy.deepcopy(plugin)
|
||||
step_plugin["settings"] = step_settings_output
|
||||
step["plugins"].append(step_plugin)
|
||||
|
||||
# remove settings key form step
|
||||
step.pop("settings", None)
|
||||
return steps
|
||||
|
||||
|
||||
def set_raw(template: list, plugins_base: list, settings: dict, is_new: bool = False) -> dict:
|
||||
"""
|
||||
Set the raw form based on the template and plugins data.
|
||||
It consists of keeping only the value or default value for each plugin settings.
|
||||
"""
|
||||
template_settings = template.get("settings")
|
||||
raw_settings = {}
|
||||
# Copy of the plugins base
|
||||
plugins = copy.deepcopy(plugins_base)
|
||||
# Update settings with global config data
|
||||
for plugin in plugins:
|
||||
for setting, value in plugin.get("settings").items():
|
||||
|
||||
is_multiple_setting = "multiple" in value
|
||||
|
||||
# By default, we will loop on one setting (not multiple)
|
||||
total_settings = {setting: value}
|
||||
|
||||
# Case multiple, retrieve all settings that start with setting name
|
||||
if is_multiple_setting:
|
||||
# get all settings that start with setting name
|
||||
total_settings = {k: v for k, v in settings.items() if k.startswith(f"{setting}")}
|
||||
|
||||
# Loop in a same way it is a multiple or regular setting
|
||||
for mult_setting, mult_value in total_settings.items():
|
||||
|
||||
# Get setting data
|
||||
# We need to send setting and not mult_setting because mult_setting is unknown on plugin side
|
||||
template_value, current_value, default_value, is_disabled_method, is_current_from_template, is_current_default, setting_value = (
|
||||
get_setting_data(template_settings, settings, mult_setting, mult_value)
|
||||
)
|
||||
|
||||
if current_value is not None:
|
||||
raw_settings[mult_setting] = current_value
|
||||
continue
|
||||
|
||||
if template_value is not None:
|
||||
raw_settings[mult_setting] = template_value
|
||||
continue
|
||||
|
||||
return raw_settings
|
||||
|
||||
|
||||
def set_advanced(template: list, plugins_base: list, settings: dict, is_new: bool) -> dict:
|
||||
"""
|
||||
Set the advanced form based on the template and plugins data.
|
||||
It consists of formatting each plugin settings to be used in the advanced form.
|
||||
"""
|
||||
template_settings = template.get("settings")
|
||||
# Copy of the plugins base data
|
||||
plugins = copy.deepcopy(plugins_base)
|
||||
# Update settings with global config data
|
||||
for plugin in plugins:
|
||||
loop_id = 0
|
||||
total_settings = len(plugin.get("settings"))
|
||||
for setting, value in plugin.get("settings").items():
|
||||
loop_id += 1
|
||||
value = format_setting(setting, value, total_settings, loop_id, template_settings, settings, is_new)
|
||||
|
||||
set_multiples(template, plugins, settings)
|
||||
|
||||
return plugins
|
||||
|
||||
|
||||
def get_multiple_from_template(template, multiples):
|
||||
"""
|
||||
We are gonna loop on each plugins multiples group, in case a setting is matching a template setting,
|
||||
we will create a group using the prefix as key (or "0" if no prefix) with default settings at first.
|
||||
Then we will override by the template value in case there is one.
|
||||
This will return something of this type :
|
||||
{'0' : {'setting' : value, 'setting2': value2}, '1' : {'setting_1': value, 'setting2_1': value}} }
|
||||
"""
|
||||
# Loop on each plugin and loop on multiples key
|
||||
# Check if the name us matching a template key
|
||||
multiple_plugin = copy.deepcopy(multiples)
|
||||
|
||||
multiple_template = {}
|
||||
for setting, value in template.get("settings").items():
|
||||
# Sanitize setting name to remove prefix of type _1 if exists
|
||||
# Slipt by _ and check if last element is a digit
|
||||
format_setting = setting
|
||||
setting_split = setting.split("_")
|
||||
prefix = "0"
|
||||
if setting_split[-1].isdigit():
|
||||
prefix = setting_split[-1]
|
||||
format_setting = "_".join(setting_split[:-1])
|
||||
# loop on settings of a multiple group
|
||||
for mult_name, mult_settings in multiple_plugin.items():
|
||||
|
||||
# Check if at least one setting is matching a multiple setting
|
||||
if not format_setting in mult_settings:
|
||||
continue
|
||||
|
||||
# Case we have at least one multiple setting, we can check if multiple name exists or create it
|
||||
if not mult_name in multiple_template:
|
||||
multiple_template[mult_name] = {}
|
||||
|
||||
# Case it is, we will check if already a group with the right prefix exists
|
||||
# If not, we will create it
|
||||
if not prefix in multiple_template[mult_name]:
|
||||
# We want each settings to have the prefix if exists
|
||||
# We will get the value of the setting without the prefix and create a prefix key with the same value
|
||||
# And after that we can delete the original setting
|
||||
new_multiple_group = {}
|
||||
for multSett, multValue in mult_settings.items():
|
||||
new_multiple_group[f"{multSett}{f'_{prefix}' if prefix != '0' else ''}"] = multValue
|
||||
|
||||
new_multiple_group = copy.deepcopy(new_multiple_group)
|
||||
|
||||
# Update id for each settings
|
||||
for multSett, multValue in new_multiple_group.items():
|
||||
multValue["id"] = f"{multValue['id']}{f'-{prefix}' if prefix != '0' else ''}"
|
||||
|
||||
multiple_template[mult_name][prefix] = new_multiple_group
|
||||
|
||||
# We can now add the template value to setting using the same setting name with prefix
|
||||
multiple_template[mult_name][prefix][setting]["value"] = value
|
||||
multiple_template[mult_name][prefix][setting]["prev_value"] = value
|
||||
multiple_template[mult_name][prefix][setting]["method"] = "default"
|
||||
|
||||
# Sort key incrementally
|
||||
for mult_name, mult_settings in multiple_template.items():
|
||||
multiple_template[mult_name] = dict(sorted(mult_settings.items(), key=lambda item: int(item[0])))
|
||||
return multiple_template
|
||||
|
||||
|
||||
def get_multiple_from_settings(settings, multiples):
|
||||
"""
|
||||
We are gonna loop on each plugins multiples group, in case a setting is matching a service / global config setting,
|
||||
we will create a group using the prefix as key (or "0" if no prefix) with default settings at first.
|
||||
Then we will override by the service / global config value in case there is one.
|
||||
This will return something of this type :
|
||||
{'0' : {'setting' : value, 'setting2': value2}, '1' : {'setting_1': value, 'setting2_1': value}} }
|
||||
"""
|
||||
|
||||
# Loop on each plugin and loop on multiples key
|
||||
# Check if the name us matching a template key
|
||||
multiple_plugins = copy.deepcopy(multiples)
|
||||
|
||||
multiple_settings = {}
|
||||
for setting, value in settings.items():
|
||||
# Sanitize setting name to remove prefix of type _1 if exists
|
||||
# Slipt by _ and check if last element is a digit
|
||||
format_setting = setting
|
||||
setting_split = setting.split("_")
|
||||
prefix = "0"
|
||||
if setting_split[-1].isdigit():
|
||||
prefix = setting_split[-1]
|
||||
format_setting = "_".join(setting_split[:-1])
|
||||
|
||||
# loop on settings of a multiple group
|
||||
for mult_name, mult_settings in multiple_plugins.items():
|
||||
|
||||
# Check if at least one setting is matching a multiple setting
|
||||
if not format_setting in mult_settings:
|
||||
continue
|
||||
|
||||
# Case we have at least one multiple setting, we can check if multiple name exists or create it
|
||||
if not mult_name in multiple_settings:
|
||||
multiple_settings[mult_name] = {}
|
||||
# Now check if prefix exist for this mult
|
||||
if not prefix in multiple_settings[mult_name]:
|
||||
# We want each settings to have the prefix if exists
|
||||
# We will get the value of the setting without the prefix and create a prefix key with the same value
|
||||
# And after that we can delete the original setting
|
||||
new_multiple_group = {}
|
||||
for multSett, multValue in mult_settings.items():
|
||||
new_multiple_group[f"{multSett}{f'_{prefix}' if prefix != '0' else ''}"] = multValue
|
||||
|
||||
new_multiple_group = copy.deepcopy(new_multiple_group)
|
||||
|
||||
# Update id for each settings
|
||||
for multSett, multValue in new_multiple_group.items():
|
||||
multValue["id"] = f"{multValue['id']}{f'-{prefix}' if prefix != '0' else ''}"
|
||||
|
||||
multiple_settings[mult_name][prefix] = new_multiple_group
|
||||
|
||||
# Update multiple template with real data
|
||||
multiple_settings[mult_name][prefix][setting]["value"] = value.get("value", multiple_settings[mult_name][prefix][setting]["value"])
|
||||
multiple_settings[mult_name][prefix][setting]["prev_value"] = value.get("value", multiple_settings[mult_name][prefix][setting]["value"])
|
||||
multiple_settings[mult_name][prefix][setting]["method"] = value.get("method", "ui")
|
||||
multiple_settings[mult_name][prefix][setting]["disabled"] = False if value.get("method", "ui") in ("ui", "default", "manual") else True
|
||||
|
||||
# Add popovers if setting is disabled else stop
|
||||
if not multiple_settings[mult_name][prefix][setting].get("disabled", False):
|
||||
continue
|
||||
|
||||
multiple_settings[mult_name][prefix][setting]["popovers"] = [
|
||||
{
|
||||
"iconName": "trespass",
|
||||
"text": "inp_popover_method_disabled",
|
||||
}
|
||||
] + multiple_settings[
|
||||
mult_name
|
||||
][prefix][setting].get("popovers", [])
|
||||
|
||||
return multiple_settings
|
||||
|
||||
|
||||
def set_multiples(template, format_plugins, settings):
|
||||
"""
|
||||
Set the multiples settings for each plugin.
|
||||
"""
|
||||
# copy of format plugins
|
||||
for plugin in format_plugins:
|
||||
# Get multiples
|
||||
multiples = {}
|
||||
settings_to_delete = []
|
||||
total_settings = len(plugin.get("settings"))
|
||||
zindex = 0
|
||||
for setting, value in plugin.get("settings").items():
|
||||
|
||||
if not value.get("multiple"):
|
||||
continue
|
||||
|
||||
zindex += 1
|
||||
|
||||
value["containerClass"] = f"z-{total_settings - zindex}"
|
||||
|
||||
mult_name = value.get("multiple")
|
||||
# Get the multiple value and set it as key if not in multiples dict
|
||||
if mult_name not in multiples:
|
||||
multiples[mult_name] = {}
|
||||
|
||||
multiples[mult_name][setting] = value
|
||||
settings_to_delete.append(setting)
|
||||
|
||||
# Delete multiple settings from regular settings
|
||||
for setting in settings_to_delete:
|
||||
del plugin["settings"][setting]
|
||||
|
||||
if len(multiples):
|
||||
# Add multiple schema with default values to plugin
|
||||
plugin["multiples_schema"] = multiples
|
||||
# Now that we have for each plugin the multiples settings, we need to do the following
|
||||
# Get all settings from template that are multiples
|
||||
template_multiples = get_multiple_from_template(template, multiples)
|
||||
# Get all settings from service settings / global config that are multiples
|
||||
service_multiples = get_multiple_from_settings(settings, multiples)
|
||||
# Get service multiples if at least one, else use template multiples
|
||||
plugin["multiples"] = service_multiples if len(service_multiples) else template_multiples
|
||||
|
||||
return format_plugins
|
||||
|
||||
|
||||
def format_setting(
|
||||
name: str,
|
||||
value: Union[str, int],
|
||||
total_settings: Union[str, int],
|
||||
loop_id: Union[str, int],
|
||||
template_settings: dict,
|
||||
settings: dict,
|
||||
is_new: bool = False,
|
||||
) -> dict:
|
||||
"""
|
||||
Format a setting in order to be used with form builder.
|
||||
This will only set value for none multiple settings.
|
||||
Additionnel set_multiples function will handle multiple settings.
|
||||
"""
|
||||
|
||||
is_multiple_setting = value.get("multiple", False)
|
||||
|
||||
template_value, current_value, default_value, is_disabled_method, is_current_from_template, is_current_default, setting_value = get_setting_data(
|
||||
template_settings, settings, name, value, is_multiple_setting, is_new
|
||||
) # regex by pattern
|
||||
|
||||
value["pattern"] = value.get("regex", "")
|
||||
|
||||
# set inpType based on type define for each settings
|
||||
inpType = (
|
||||
"checkbox"
|
||||
if value.get("type") == "check"
|
||||
else ("select" if value.get("type") == "select" else "datepicker" if value.get("type") == "date" else "input")
|
||||
)
|
||||
value["inpType"] = inpType
|
||||
|
||||
if inpType == "select":
|
||||
# replace "select" key by "values"
|
||||
value["values"] = value.pop("select")
|
||||
|
||||
value["columns"] = {"pc": 4, "tablet": 6, "mobile": 12}
|
||||
value["disabled"] = is_disabled_method
|
||||
value["value"] = default_value
|
||||
value["name"] = value.get("label")
|
||||
value["prev_value"] = value.get("value")
|
||||
|
||||
# Prepare popover checking "help", "context"
|
||||
popovers = []
|
||||
|
||||
if is_disabled_method:
|
||||
popovers.append(
|
||||
{
|
||||
"iconName": "trespass",
|
||||
"text": "inp_popover_method_disabled",
|
||||
}
|
||||
)
|
||||
|
||||
if value.get("context"):
|
||||
popovers.append(
|
||||
{
|
||||
"iconName": ("disk" if value.get("context") == "multisite" else "globe"),
|
||||
"text": ("inp_popover_multisite" if value.get("context") == "multisite" else "inp_popover_global"),
|
||||
}
|
||||
)
|
||||
|
||||
if value.get("help"):
|
||||
popovers.append(
|
||||
{
|
||||
"iconName": "info",
|
||||
"text": value.get("help"),
|
||||
}
|
||||
)
|
||||
|
||||
value["popovers"] = popovers
|
||||
|
||||
# Case multiple, stop here
|
||||
if "multiple" in value:
|
||||
return value
|
||||
|
||||
# Else, we can add additional final data
|
||||
value["method"] = settings.get(name, {}).get("method", "ui")
|
||||
value["containerClass"] = f"z-{total_settings - loop_id}"
|
||||
|
||||
if current_value is not None and not is_current_default:
|
||||
value["value"] = current_value
|
||||
elif template_value is not None:
|
||||
value["value"] = template_value
|
||||
else:
|
||||
value["value"] = setting_value
|
||||
|
||||
return value
|
||||
2176
src/ui/client/builder/utils/widgets.py
Normal file
2176
src/ui/client/builder/utils/widgets.py
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -12,7 +12,7 @@ from typing import Union
|
|||
inputFolder = abspath("../client/dashboard/components")
|
||||
outputFolderMd = abspath("../client/.widgets-md")
|
||||
outputFolderPy = abspath("../client/.widgets")
|
||||
outputFolderWidgets = abspath("../client/widgets")
|
||||
outputFolderWidgets = abspath("../client/builder/utils")
|
||||
components_path_to_exclude = ("\components\Icons\\", "components\Forms\Error\\", "\components\Dashboard\\", "\components\Builder\\")
|
||||
|
||||
|
||||
|
|
@ -51,8 +51,7 @@ def reset():
|
|||
rmtree(outputFolderMd, ignore_errors=True)
|
||||
# Remove all files from the output folder
|
||||
rmtree(outputFolderPy, ignore_errors=True)
|
||||
# remove outputfilename
|
||||
rmtree(outputFolderWidgets, ignore_errors=True)
|
||||
|
||||
|
||||
|
||||
def vue2js():
|
||||
|
|
@ -431,6 +430,10 @@ def add_key_value(data, key, value, default):
|
|||
content += "\n"
|
||||
# Utils function to add key value to data dict if not default value
|
||||
|
||||
# Remove previous file if exists
|
||||
if Path(f"{outputFolderWidgets}/widgets.py").exists():
|
||||
Path(f"{outputFolderWidgets}/widgets.py").unlink()
|
||||
|
||||
Path(f"{outputFolderWidgets}/widgets.py").write_text(content)
|
||||
|
||||
# Remove py folder
|
||||
|
|
|
|||
Loading…
Reference in a new issue