move client/builder + continue updating base format

This commit is contained in:
Jordan Blasenhauer 2024-08-12 12:30:23 +02:00
parent 2b8d16aa0e
commit a9ea59c74c
81 changed files with 486 additions and 46197 deletions

View file

@ -1,38 +0,0 @@
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

View file

@ -1,39 +0,0 @@
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

View file

@ -1,31 +0,0 @@
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

View file

@ -1,84 +0,0 @@
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

View file

@ -1,42 +0,0 @@
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

View file

@ -1,290 +0,0 @@
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

View file

@ -1,83 +0,0 @@
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

View file

@ -1,40 +0,0 @@
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

View file

@ -1,379 +0,0 @@
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

View file

@ -1,494 +0,0 @@
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

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
import json
import base64
from pages.advanced_mode import advanced_mode_builder
from builder.advanced_mode import advanced_mode_builder
# Default plugins from docker-compose.ui.yml
plugins = [

View file

@ -1,38 +0,0 @@
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

View file

@ -1,6 +1,8 @@
import json
import base64
# TODO : REMOVE operation by custom endpoint
from builder.utils.widgets import button, button_group, title, text, tabulator, fields, upload, datepicker, input, select
bans_columns = [
@ -33,7 +35,7 @@ bans_filters = [
"name": "select-ban-reason",
"label": "bans_select_reason", # keep it (a18n)
"value": "all", # keep "all"
"values": ["all", "antibot"], # keep "all" and add your reasons
"values": ["all", "antibot"], # keep "all" and add your reasons dynamically
"inpType": "select",
"onlyDown": True,
"columns": {"pc": 3, "tablet": 4, " mobile": 12},
@ -165,7 +167,7 @@ builder = [
"display": ["main", 1],
"widgets": [
tabulator(
id="table-core-plugins",
id="table-bans-list",
columns=bans_columns,
items=bans_items,
filters=bans_filters,

View file

@ -241,7 +241,7 @@ configs_items = [
hideText=True,
color="yellow",
size="normal",
attrs={"data-display": "display_index"}, # replace by the display index of the related in order to display it
attrs={"data-display": "display_index"}, # replace by the display index of the related form in order to display it
)["data"],
# Delete button with modal to confirm
button(

View file

@ -1,7 +1,7 @@
import json
import base64
from pages.easy_mode import easy_mode_builder
from builder.easy_mode import easy_mode_builder
# Default plugins from docker-compose.ui.yml
plugins = [

View file

@ -1,39 +0,0 @@
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

View file

@ -1,31 +0,0 @@
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

View file

@ -1,7 +1,7 @@
import json
import base64
from pages.global_config import global_config_builder
from builder.global_config import global_config_builder
# Default plugins from docker-compose.ui.yml
plugins = [

View file

@ -1,84 +1,99 @@
from .utils.widgets import stat_widget
import json
import base64
home = [
{
"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",
},
}
],
},
]
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",
)
# store on a file
with open("home.json", "w") as f:
json.dump(home, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(home), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
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
with open("home.txt", "w") as f:
f.write(output_base64_string)

View file

@ -1,46 +1,33 @@
from .utils.widgets import instance_widget
import json
import base64
from typing import List
# from src.instance import Instance
from builder.instances import instances_builder
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 = []
# Create instance class using keys from the instances list
class Instance:
def __init__(self, _type, health, _id, hostname, name):
self._type = _type
self.health = health
self._id = _id
self.hostname = hostname
self.name = name
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
]
instances = [
Instance("manual", True, "bunkerweb", "bunkerweb", "bunkerweb"),
Instance("manual", True, "bunkerweb", "bunkerweb", "bunkerweb"),
]
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)
builder = instances_builder(instances)
return builder
# store on a file
with open("instances.json", "w") as f:
json.dump(builder, f)
output_base64_bytes = base64.b64encode(bytes(json.dumps(builder), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("instances.txt", "w") as f:
f.write(output_base64_string)

View file

@ -1,6 +1,8 @@
import json
import base64
# TODO : REMOVE operation by custom endpoint
from builder.utils.widgets import button, button_group, title, text, tabulator, input
columns = [
@ -10,6 +12,7 @@ columns = [
{"title": "Method", "field": "method", "formatter": "text"},
{"title": "Creation date", "field": "creation_date", "formatter": "text"},
{"title": "Last seen", "field": "last_seen", "formatter": "text"},
{"title": "health", "field": "health", "formatter": "text"}, # up, down, loading
{
"title": "Actions",
"field": "actions",
@ -157,6 +160,7 @@ items = [
]
# TODO : Add warning that port and server_name will be override by scheduler
instance_create_form_widgets = [
input(
id="instance-name",

View file

@ -1,290 +1,226 @@
from .utils.widgets import title_widget, table_widget
import json
import base64
from builder.jobs import jobs_builder
def jobs_builder(jobs):
jobs = {
"anonymous-report": {
"plugin_id": "misc",
"every": "day",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:03 PM", "end_date": "07/08/2024, 01:10:04 PM", "success": True}],
"cache": [],
},
"backup-data": {
"plugin_id": "backup",
"every": "day",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"blacklist-download": {
"plugin_id": "blacklist",
"every": "hour",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:01 PM", "end_date": "07/08/2024, 01:10:02 PM", "success": True}],
"cache": [],
},
"bunkernet-data": {
"plugin_id": "bunkernet",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"bunkernet-register": {
"plugin_id": "bunkernet",
"every": "hour",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:02 PM", "success": True}],
"cache": [],
},
"certbot-new": {
"plugin_id": "letsencrypt",
"every": "once",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"certbot-renew": {
"plugin_id": "letsencrypt",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:03 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"cleanup-excess-jobs-runs": {
"plugin_id": "db",
"every": "day",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"coreruleset-nightly": {
"plugin_id": "modsecurity",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:01 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"custom-cert": {
"plugin_id": "customcert",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"default-server-cert": {
"plugin_id": "misc",
"every": "once",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [
{
"service_id": None,
"file_name": "default-server-cert.pem",
"last_update": "07/08/2024, 01:10:03 PM",
"checksum": "203da9e16dabe522a3080c3b9efc5c2dc8054f47e98d995fe1812f4c498b4feb519ef080b7dfeaba0095c1393793815c23f22072daf5703b02762504b211db20",
},
{
"service_id": None,
"file_name": "default-server-cert.key",
"last_update": "07/08/2024, 01:10:03 PM",
"checksum": "7f86b1fffb8fe2011365d76e5a0955344a03c3bdb7b04aff13f8ad5b6178804290c0cd6c8f29dda9e981e3193cf5acda2a92f72312d514514305b8485667d573",
},
],
},
"download-crs-plugins": {
"plugin_id": "modsecurity",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:03 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"download-plugins": {
"plugin_id": "misc",
"every": "once",
"reload": False,
"history": [
{"start_date": "07/08/2024, 01:10:04 PM", "end_date": "07/08/2024, 01:10:05 PM", "success": True},
{"start_date": "07/08/2024, 01:09:59 PM", "end_date": "07/08/2024, 01:10:00 PM", "success": True},
],
"cache": [],
},
"download-pro-plugins": {
"plugin_id": "pro",
"every": "day",
"reload": True,
"history": [
{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:04 PM", "success": True},
{"start_date": "07/08/2024, 01:10:00 PM", "end_date": "07/08/2024, 01:10:01 PM", "success": False},
],
"cache": [],
},
"failover-backup": {
"plugin_id": "jobs",
"every": "once",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:07 PM", "end_date": "07/08/2024, 01:10:08 PM", "success": True}],
"cache": [
{
"service_id": None,
"file_name": "folder:/var/tmp/bunkerweb/failover.tgz",
"last_update": "07/08/2024, 01:10:14 PM",
"checksum": "d22a7a696d4b44bcef6a3ac06b2d7e2b2de128243000f58202c0e82b0bf54510ade7329eca14ca478a28d46201410ea1fd8002349b7b9aa51dd0d07d2fb2f51e",
}
],
},
"greylist-download": {
"plugin_id": "greylist",
"every": "hour",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"mmdb-asn": {
"plugin_id": "jobs",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:04 PM", "end_date": "07/08/2024, 01:10:06 PM", "success": True}],
"cache": [
{
"service_id": None,
"file_name": "asn.mmdb",
"last_update": "07/08/2024, 01:10:05 PM",
"checksum": "0beed65a84e63cf5dd6753ecc1aa6399dddaf5eb24fb22839f4cd72cbc9805cddf72be068649d111a3c21e2ac7de4a6f930c859286a25a7e937da017406d2596",
}
],
},
"mmdb-country": {
"plugin_id": "jobs",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:04 PM", "success": True}],
"cache": [
{
"service_id": None,
"file_name": "country.mmdb",
"last_update": "07/08/2024, 01:10:03 PM",
"checksum": "5f0d2e2c92840747886924adc1e6ff3668882990e0cd8a4d60750fe1bddb66c3e175c8717d073b48ebda41cce4c505d434dc2a6a469823fcd41c62c4f875b212",
}
],
},
"realip-download": {
"plugin_id": "realip",
"every": "hour",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"self-signed": {
"plugin_id": "selfsigned",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [
{
"service_id": "www.example.com",
"file_name": "cert.pem",
"last_update": "07/08/2024, 01:10:03 PM",
"checksum": "fc33700719f6a58336e3c3b735ad3fdf0b15ebd0afbe6b4a3b02a4a92e0ab4f1761409a7a1d1ca965d59b4196a81c1d150a12ae0170f7bb3a1bc7cf02300fbe9",
},
{
"service_id": "www.example.com",
"file_name": "key.pem",
"last_update": "07/08/2024, 01:10:03 PM",
"checksum": "0e6eee34ab7b2a41cb21e49ebd35ce29a1b8d12b55aad3911b6357c73792eef7084fbb4eeba8bff73eb7a8789b5f486f6edb6d4b1c38a54bd0dcee1bf438f23d",
},
],
},
"update-check": {
"plugin_id": "jobs",
"every": "day",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:06 PM", "end_date": "07/08/2024, 01:10:07 PM", "success": True}],
"cache": [],
},
"whitelist-download": {
"plugin_id": "whitelist",
"every": "hour",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:02 PM", "success": True}],
"cache": [],
},
}
jobs_list = get_jobs_list(jobs)
output = jobs_builder(jobs)
intervals = ["all"]
# store on a file
with open("jobs.json", "w") as f:
json.dump(output, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(output), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
# 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
with open("jobs.txt", "w") as f:
f.write(output_base64_string)

View file

@ -1,83 +1,17 @@
from .utils.widgets import title_widget
import json
import base64
from builder.logs import logs_builder
def logs_builder(files: list[str] = [], current_file: str = "", raw_data: str = "") -> str:
files = ["file1", "file2"]
current_file = ""
raw_data = "gefesfesfsefes"
output = logs_builder(files, current_file, raw_data)
if not files:
builder = [
{
"type": "void",
"widgets": [{"type": "MessageUnmatch", "data": {"text": "logs_no_files_found"}}],
}
]
return builder
with open("logs.json", "w") as f:
json.dump(output, f, indent=4)
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
output_base64_bytes = base64.b64encode(bytes(json.dumps(output), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("logs.txt", "w") as f:
f.write(output_base64_string)

View file

@ -1,17 +1,24 @@
import json
import base64
# TODO : REMOVE operation by custom endpoint
from builder.utils.widgets import button, button_group, title, text, tabulator, fields, upload, checkbox
core_columns = [
# Here all plugins unless external
core_pro_columns = [
{"title": "Name", "field": "name", "formatter": "text"},
{"title": "Description", "field": "description", "formatter": "text"},
{"title": "Type", "field": "type", "formatter": "text"},
{"title": "version", "field": "version", "formatter": "text"},
{"title": "stream", "field": "type", "stream": "text"}, # "yes" "no" "partial"
{"title": "Documentation page", "field": "documentation_page", "formatter": "buttonGroup"},
{"title": "Plugin page", "field": "plugin_page", "formatter": "buttonGroup"},
]
core_filters = [
core_pro_filters = [
{
"type": "like",
"fields": ["name"],
@ -27,7 +34,7 @@ core_filters = [
]
core_items = [
core_pro_items = [
{
"name": text(text="Name")["data"],
"description": text(text="Description")["data"],
@ -63,24 +70,25 @@ core_items = [
]
register_columns = [
registry_columns = [
{"title": "Select", "field": "select", "formatter": "fields"}, # checkbox
{"title": "Name", "field": "name", "formatter": "text"},
{"title": "Description", "field": "description", "formatter": "text"},
{"title": "Type", "field": "type", "formatter": "text"},
{"title": "Install", "field": "install", "formatter": "text"},
{"title": "Documentation page", "field": "documentation_page", "formatter": "buttonGroup"},
{"title": "version", "field": "version", "formatter": "text"},
{"title": "stream", "field": "type", "stream": "text"}, # "yes" "no" "partial"
{"title": "Plugin page", "field": "plugin_page", "formatter": "buttonGroup"},
]
register_filters = [
registry_filters = [
{
"type": "like",
"fields": ["name"],
"setting": {
"id": "input-search-register-name",
"name": "input-search-register-name",
"id": "input-search-registry-name",
"name": "input-search-registry-name",
"label": "plugins_search_name", # keep it (a18n)
"value": "",
"inpType": "input",
@ -91,8 +99,8 @@ register_filters = [
"type": "=",
"fields": ["type"],
"setting": {
"id": "select-register-type",
"name": "select-register-type",
"id": "select-registry-type",
"name": "select-registry-type",
"label": "plugins_select_type", # keep it (a18n)
"value": "all",
"values": ["all", "pro", "external"],
@ -105,8 +113,8 @@ register_filters = [
"type": "=",
"fields": ["install"],
"setting": {
"id": "select-register-install",
"name": "select-register-install",
"id": "select-registry-install",
"name": "select-registry-install",
"label": "plugins_select_install", # keep it (a18n)
"value": "all",
"values": ["all", "yes", "no"],
@ -118,7 +126,7 @@ register_filters = [
]
register_items = [
registry_items = [
{
"select": checkbox(
inputType="checkbox",
@ -164,6 +172,7 @@ register_items = [
},
]
# Here all plugins with type external
upload_columns = [
{"title": "Select", "field": "select", "formatter": "fields"}, # checkbox
{"title": "Name", "field": "name", "formatter": "text"},
@ -241,9 +250,9 @@ builder = [
"widgets": [
tabulator(
id="table-core-plugins",
columns=core_columns,
items=core_items,
filters=core_filters,
columns=core_pro_columns,
items=core_pro_items,
filters=core_pro_filters,
)
],
},
@ -252,10 +261,10 @@ builder = [
"display": ["main", 2],
"widgets": [
tabulator(
id="table-register-plugins",
columns=register_columns,
items=register_items,
filters=register_filters,
id="table-registry-plugins",
columns=registry_columns,
items=registry_items,
filters=registry_filters,
)
],
},

View file

@ -1,20 +1,37 @@
import json
import base64
# TODO : REMOVE operation by custom endpoint
from builder.utils.widgets import button, button_group, title, text, tabulator, fields, upload, input, combobox, checkbox, select, editor
# Define data to put in profile widgets
# - username
# - email
# - created_method
# - is_superadmin
# - role
# - role_description
# - permissions (liste of permissions [])
# - creation_date
# - last_update (last time update user info)
profile_widgets = []
# Define data to put in profile form widgets
# - update password
# - update email
# - update username
user_widgets = []
account_widgets = []
# Define data to put in totp widgets
# - enable totp: what we need ?
# - disable totp: what we need ?
# if want to enable totp (currently disabled):
# text with state
# show QRcode SVG
# - form (endpoint /totp-enable) : totp secret (type password), totp code, password
# Case currently enabled :
# text with state
# after first totp setup, show recovery codes
# form refresh recovery codes button that will redisplay recovery (endpoint /totp-refresh) : password (warning that will remove previous)
# form disabled (endpoint /totp-disable) : totp code || recovery code, password
totp_widgets = []
builder = [
@ -29,7 +46,7 @@ builder = [
"type": "card",
"display": ["main", 2],
"widgets": [
user_widgets,
account_widgets,
],
},
{

View file

@ -1,40 +0,0 @@
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

View file

@ -3,10 +3,13 @@ import base64
from builder.utils.widgets import button, button_group, title, text, tabulator, fields, upload, datepicker
# TODO : REMOVE operation by custom endpoint
reports_columns = [
{"title": "Date", "field": "date", "formatter": "fields"}, # datepicker
{"title": "IP", "field": "ip", "formatter": "text"},
{"title": "Country", "field": "country", "formatter": "text"},
{"title": "Server name", "field": "server_name", "formatter": "text"},
{"title": "Method", "field": "method", "formatter": "text"},
{"title": "URL", "field": "url", "formatter": "text"},
{"title": "Code", "field": "code", "formatter": "text"},

View file

@ -1,379 +1,46 @@
import json
import base64
from typing import Union
from .utils.widgets import title_widget, table_widget
from builder.services import services_builder
services = [
{
"USE_REVERSE_PROXY": {"value": "yes", "method": "scheduler", "global": False},
"IS_DRAFT": {"value": "no", "method": "default", "global": False},
"SERVE_FILES": {"value": "no", "method": "scheduler", "global": True},
"REMOTE_PHP": {"value": "", "method": "default", "global": True},
"AUTO_LETS_ENCRYPT": {"value": "no", "method": "default", "global": True},
"USE_CUSTOM_SSL": {"value": "no", "method": "default", "global": True},
"USE_MODSECURITY": {"value": "yes", "method": "default", "global": True},
"USE_BAD_BEHAVIOR": {"value": "yes", "method": "default", "global": True},
"USE_LIMIT_REQ": {"value": "yes", "method": "default", "global": True},
"USE_DNSBL": {"value": "yes", "method": "default", "global": True},
"SERVER_NAME": {"value": "app1.example.com", "method": "scheduler", "global": False},
},
{
"USE_REVERSE_PROXY": {"value": "yes", "method": "scheduler", "global": False},
"IS_DRAFT": {"value": "yes", "method": "default", "global": False},
"SERVE_FILES": {"value": "no", "method": "scheduler", "global": True},
"REMOTE_PHP": {"value": "", "method": "default", "global": True},
"AUTO_LETS_ENCRYPT": {"value": "no", "method": "default", "global": True},
"USE_CUSTOM_SSL": {"value": "no", "method": "default", "global": True},
"USE_MODSECURITY": {"value": "yes", "method": "default", "global": True},
"USE_BAD_BEHAVIOR": {"value": "yes", "method": "default", "global": True},
"USE_LIMIT_REQ": {"value": "yes", "method": "default", "global": True},
"USE_DNSBL": {"value": "yes", "method": "default", "global": True},
"SERVER_NAME": {"value": "www.example.com", "method": "ui", "global": False},
},
]
def services_builder(services):
# get method for each service["SERVER_NAME"]["method"]
methods = list(set([service["SERVER_NAME"]["method"] for service in services]))
output = services_builder(services)
services_list = get_services_list(services)
# store on a file
with open("services.json", "w") as f:
json.dump(output, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(output), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
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
with open("services.txt", "w") as f:
f.write(output_base64_string)

View file

@ -4,12 +4,14 @@ import base64
from builder.utils.widgets import button, button_group, title, text, tabulator, fields, upload, input, combobox, checkbox, select, editor, datepicker
# TODO : REMOVE operation by custom endpoint
def generate_form(
username: str = "",
password: str = "",
email: str = "",
is_new: bool = True,
role: str = "",
roles: list = [],
password: str = "",
display_index: int = 1,
):
@ -88,8 +90,10 @@ def get_forms():
users_columns = [
{"title": "Username", "field": "username", "formatter": "text"},
{"title": "Role", "field": "role", "formatter": "text"},
{"title": "creation_date", "field": "creation_date", "formatter": "fields"}, # datepicker
{"title": "last_login", "field": "last_login", "formatter": "fields"}, # datepicker
{"title": "Creation date", "field": "creation_date", "formatter": "fields"}, # datepicker
{"title": "Last login", "field": "last_login", "formatter": "fields"}, # datepicker of last login
{"title": "Last login IP", "field": "last_login_ip", "formatter": "fields"}, # datepicker of last login
{"title": "Login count", "field": "login_count", "formatter": "fields"}, # datepicker of last login
{"title": "totp (state)", "field": "totp", "formatter": "icons"}, # icon check or cross
{
"title": "actions",

View file

@ -146,7 +146,7 @@ import Container from "@components/Widget/Container.vue";
* }
* }
* },
* @param {object<object>} multiples - The multiples settings to display. This needs to be a dict of settings using default field format.
* @param {object} multiples - The multiples settings to display. This needs to be a dict of settings using default field format.
* @param {object} [columns={"pc": "12", "tablet": "12", "mobile": "12"}] - Field has a grid system. This allow to get multiple field in the same row if needed.
* @param {string} [containerClass=""] - Additionnal class to add to the container
* @param {string} [tadId=contentIndex] - The tabindex of the field, by default it is the contentIndex

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,669 +0,0 @@
[
{
"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
}
}
}
]
}
}
]
}
]

View file

@ -1,296 +0,0 @@
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)

View file

@ -1,38 +0,0 @@
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

View file

@ -1,39 +0,0 @@
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

View file

@ -1,31 +0,0 @@
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

View file

@ -1,84 +0,0 @@
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

View file

@ -1,42 +0,0 @@
from .utils.widgets import instance_widget
def instances_builder(instances) -> 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

View file

@ -1,290 +0,0 @@
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

View file

@ -1,83 +0,0 @@
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

View file

@ -1,40 +0,0 @@
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

View file

@ -1,379 +0,0 @@
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

View file

@ -1,494 +0,0 @@
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

View file

@ -1,64 +0,0 @@
from typing import Union
def title_widget(title: str) -> dict:
return {
"type": "Title",
"data": {"title": title},
}
def table_widget(positions: list[int], header: list[str], items: list[dict], filters: list[dict], minWidth: str, title: str) -> dict:
return {
"type": "Table",
"data": {
"title": title,
"minWidth": minWidth,
"header": header,
"positions": positions,
"items": items,
"filters": filters,
},
}
def stat_widget(
link: str, containerColums: dict, title: Union[str, int], subtitle: Union[str, int], subtitle_color: str, stat: Union[str, int], icon_name: str
) -> dict:
"""Return a valid format to render a Stat widget"""
return {
"type": "card",
"link": link,
"containerColumns": containerColums,
"widgets": [
{
"type": "Stat",
"data": {
"title": title,
"subtitle": subtitle,
"subtitleColor": subtitle_color,
"stat": stat,
"iconName": icon_name,
},
}
],
}
def instance_widget(containerColumns: dict, pairs: list[dict], status: str, title: Union[str, int], buttons: list[dict]) -> dict:
"""Return a valid format to render an Instance widget"""
return {
"type": "card",
"containerColumns": containerColumns,
"widgets": [
{
"type": "Instance",
"data": {
"pairs": pairs,
"status": status,
"title": title,
"buttons": buttons,
},
}
],
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1,107 +0,0 @@
[
{
"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"
}
}
]
}
]

View file

@ -1,99 +0,0 @@
import json
import base64
home = [
{
"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",
},
}
],
},
]
# store on a file
with open("home.json", "w") as f:
json.dump(home, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(home), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("home.txt", "w") as f:
f.write(output_base64_string)

View file

@ -1,33 +0,0 @@
import json
import base64
from builder.instances import instances_builder
# Create instance class using keys from the instances list
class Instance:
def __init__(self, _type, health, _id, hostname, name):
self._type = _type
self.health = health
self._id = _id
self.hostname = hostname
self.name = name
instances = [
Instance("manual", True, "bunkerweb", "bunkerweb", "bunkerweb"),
Instance("manual", True, "bunkerweb", "bunkerweb", "bunkerweb"),
]
builder = instances_builder(instances)
# store on a file
with open("instances.json", "w") as f:
json.dump(builder, f)
output_base64_bytes = base64.b64encode(bytes(json.dumps(builder), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("instances.txt", "w") as f:
f.write(output_base64_string)

View file

@ -1,226 +0,0 @@
import json
import base64
from builder.jobs import jobs_builder
jobs = {
"anonymous-report": {
"plugin_id": "misc",
"every": "day",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:03 PM", "end_date": "07/08/2024, 01:10:04 PM", "success": True}],
"cache": [],
},
"backup-data": {
"plugin_id": "backup",
"every": "day",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"blacklist-download": {
"plugin_id": "blacklist",
"every": "hour",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:01 PM", "end_date": "07/08/2024, 01:10:02 PM", "success": True}],
"cache": [],
},
"bunkernet-data": {
"plugin_id": "bunkernet",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"bunkernet-register": {
"plugin_id": "bunkernet",
"every": "hour",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:02 PM", "success": True}],
"cache": [],
},
"certbot-new": {
"plugin_id": "letsencrypt",
"every": "once",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"certbot-renew": {
"plugin_id": "letsencrypt",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:03 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"cleanup-excess-jobs-runs": {
"plugin_id": "db",
"every": "day",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"coreruleset-nightly": {
"plugin_id": "modsecurity",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:01 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"custom-cert": {
"plugin_id": "customcert",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"default-server-cert": {
"plugin_id": "misc",
"every": "once",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [
{
"service_id": None,
"file_name": "default-server-cert.pem",
"last_update": "07/08/2024, 01:10:03 PM",
"checksum": "203da9e16dabe522a3080c3b9efc5c2dc8054f47e98d995fe1812f4c498b4feb519ef080b7dfeaba0095c1393793815c23f22072daf5703b02762504b211db20",
},
{
"service_id": None,
"file_name": "default-server-cert.key",
"last_update": "07/08/2024, 01:10:03 PM",
"checksum": "7f86b1fffb8fe2011365d76e5a0955344a03c3bdb7b04aff13f8ad5b6178804290c0cd6c8f29dda9e981e3193cf5acda2a92f72312d514514305b8485667d573",
},
],
},
"download-crs-plugins": {
"plugin_id": "modsecurity",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:03 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"download-plugins": {
"plugin_id": "misc",
"every": "once",
"reload": False,
"history": [
{"start_date": "07/08/2024, 01:10:04 PM", "end_date": "07/08/2024, 01:10:05 PM", "success": True},
{"start_date": "07/08/2024, 01:09:59 PM", "end_date": "07/08/2024, 01:10:00 PM", "success": True},
],
"cache": [],
},
"download-pro-plugins": {
"plugin_id": "pro",
"every": "day",
"reload": True,
"history": [
{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:04 PM", "success": True},
{"start_date": "07/08/2024, 01:10:00 PM", "end_date": "07/08/2024, 01:10:01 PM", "success": False},
],
"cache": [],
},
"failover-backup": {
"plugin_id": "jobs",
"every": "once",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:07 PM", "end_date": "07/08/2024, 01:10:08 PM", "success": True}],
"cache": [
{
"service_id": None,
"file_name": "folder:/var/tmp/bunkerweb/failover.tgz",
"last_update": "07/08/2024, 01:10:14 PM",
"checksum": "d22a7a696d4b44bcef6a3ac06b2d7e2b2de128243000f58202c0e82b0bf54510ade7329eca14ca478a28d46201410ea1fd8002349b7b9aa51dd0d07d2fb2f51e",
}
],
},
"greylist-download": {
"plugin_id": "greylist",
"every": "hour",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"mmdb-asn": {
"plugin_id": "jobs",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:04 PM", "end_date": "07/08/2024, 01:10:06 PM", "success": True}],
"cache": [
{
"service_id": None,
"file_name": "asn.mmdb",
"last_update": "07/08/2024, 01:10:05 PM",
"checksum": "0beed65a84e63cf5dd6753ecc1aa6399dddaf5eb24fb22839f4cd72cbc9805cddf72be068649d111a3c21e2ac7de4a6f930c859286a25a7e937da017406d2596",
}
],
},
"mmdb-country": {
"plugin_id": "jobs",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:04 PM", "success": True}],
"cache": [
{
"service_id": None,
"file_name": "country.mmdb",
"last_update": "07/08/2024, 01:10:03 PM",
"checksum": "5f0d2e2c92840747886924adc1e6ff3668882990e0cd8a4d60750fe1bddb66c3e175c8717d073b48ebda41cce4c505d434dc2a6a469823fcd41c62c4f875b212",
}
],
},
"realip-download": {
"plugin_id": "realip",
"every": "hour",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [],
},
"self-signed": {
"plugin_id": "selfsigned",
"every": "day",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:03 PM", "success": True}],
"cache": [
{
"service_id": "www.example.com",
"file_name": "cert.pem",
"last_update": "07/08/2024, 01:10:03 PM",
"checksum": "fc33700719f6a58336e3c3b735ad3fdf0b15ebd0afbe6b4a3b02a4a92e0ab4f1761409a7a1d1ca965d59b4196a81c1d150a12ae0170f7bb3a1bc7cf02300fbe9",
},
{
"service_id": "www.example.com",
"file_name": "key.pem",
"last_update": "07/08/2024, 01:10:03 PM",
"checksum": "0e6eee34ab7b2a41cb21e49ebd35ce29a1b8d12b55aad3911b6357c73792eef7084fbb4eeba8bff73eb7a8789b5f486f6edb6d4b1c38a54bd0dcee1bf438f23d",
},
],
},
"update-check": {
"plugin_id": "jobs",
"every": "day",
"reload": False,
"history": [{"start_date": "07/08/2024, 01:10:06 PM", "end_date": "07/08/2024, 01:10:07 PM", "success": True}],
"cache": [],
},
"whitelist-download": {
"plugin_id": "whitelist",
"every": "hour",
"reload": True,
"history": [{"start_date": "07/08/2024, 01:10:02 PM", "end_date": "07/08/2024, 01:10:02 PM", "success": True}],
"cache": [],
},
}
output = jobs_builder(jobs)
# store on a file
with open("jobs.json", "w") as f:
json.dump(output, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(output), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("jobs.txt", "w") as f:
f.write(output_base64_string)

View file

@ -1,17 +0,0 @@
import json
import base64
from builder.logs import logs_builder
files = ["file1", "file2"]
current_file = ""
raw_data = "gefesfesfsefes"
output = logs_builder(files, current_file, raw_data)
with open("logs.json", "w") as f:
json.dump(output, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(output), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("logs.txt", "w") as f:
f.write(output_base64_string)

View file

@ -1,46 +0,0 @@
import json
import base64
from typing import Union
from builder.services import services_builder
services = [
{
"USE_REVERSE_PROXY": {"value": "yes", "method": "scheduler", "global": False},
"IS_DRAFT": {"value": "no", "method": "default", "global": False},
"SERVE_FILES": {"value": "no", "method": "scheduler", "global": True},
"REMOTE_PHP": {"value": "", "method": "default", "global": True},
"AUTO_LETS_ENCRYPT": {"value": "no", "method": "default", "global": True},
"USE_CUSTOM_SSL": {"value": "no", "method": "default", "global": True},
"USE_MODSECURITY": {"value": "yes", "method": "default", "global": True},
"USE_BAD_BEHAVIOR": {"value": "yes", "method": "default", "global": True},
"USE_LIMIT_REQ": {"value": "yes", "method": "default", "global": True},
"USE_DNSBL": {"value": "yes", "method": "default", "global": True},
"SERVER_NAME": {"value": "app1.example.com", "method": "scheduler", "global": False},
},
{
"USE_REVERSE_PROXY": {"value": "yes", "method": "scheduler", "global": False},
"IS_DRAFT": {"value": "yes", "method": "default", "global": False},
"SERVE_FILES": {"value": "no", "method": "scheduler", "global": True},
"REMOTE_PHP": {"value": "", "method": "default", "global": True},
"AUTO_LETS_ENCRYPT": {"value": "no", "method": "default", "global": True},
"USE_CUSTOM_SSL": {"value": "no", "method": "default", "global": True},
"USE_MODSECURITY": {"value": "yes", "method": "default", "global": True},
"USE_BAD_BEHAVIOR": {"value": "yes", "method": "default", "global": True},
"USE_LIMIT_REQ": {"value": "yes", "method": "default", "global": True},
"USE_DNSBL": {"value": "yes", "method": "default", "global": True},
"SERVER_NAME": {"value": "www.example.com", "method": "ui", "global": False},
},
]
output = services_builder(services)
# store on a file
with open("services.json", "w") as f:
json.dump(output, f, indent=4)
output_base64_bytes = base64.b64encode(bytes(json.dumps(output), "utf-8"))
output_base64_string = output_base64_bytes.decode("ascii")
with open("services.txt", "w") as f:
f.write(output_base64_string)

View file

@ -1,4 +1,4 @@
from os import cpu_count
from os import cpu_count, getpid
from os.path import abspath
from pathlib import Path
from threading import Semaphore, Thread
@ -73,15 +73,23 @@ def js2md():
def convert_json_to_md(file: Path):
semaphore.acquire()
# Run the command
output = run_command(["documentation", "build", file.as_posix(), "-f", "md"], with_output=True)
if output == 1:
print("Error while running command", flush=True)
exit(1)
print(f"Acquiring Semaphore for: {getpid()} (file {file})", flush=True)
try:
# Run the command
output = run_command(["documentation", "build", file.as_posix(), "-f", "md"], with_output=True)
if output == 1:
print("Error while running command", flush=True)
exit(1)
# Create a new file with the same name but with .md extension
file.with_suffix(".md").write_text(output)
semaphore.release()
# Create a new file with the same name but with .md extension
file.with_suffix(".md").write_text(output)
except BaseException:
print(format_exc(), flush=True)
print("Error while running documentation", str(file.name), flush=True)
exit(1)
finally:
print(f"Releasing Semaphore for: {getpid()} (file {file})", flush=True)
semaphore.release()
threads = []
# Create a markdown file for each JS file