enhance builder + pages + simplify raw mode

* update pages header to fit content
* add typing to builder utils
* simplify raw mode json to key=value format + fix value rendered
* update vite config
* update builder to logic to work with global config json
* update i18n
* update main.py global config route
This commit is contained in:
Jordan Blasenhauer 2024-07-23 11:52:18 +02:00
parent d1cafdc3c0
commit ed9b9ba27e
17 changed files with 205 additions and 390 deletions

View file

@ -1,9 +1,12 @@
import base64
import json
import copy
from typing import Union
def stat_widget(link, containerColums, title, subtitle, subtitle_color, stat, icon_name):
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",
@ -24,7 +27,7 @@ def stat_widget(link, containerColums, title, subtitle, subtitle_color, stat, ic
}
def instance_widget(containerColumns, pairs, status, title, buttons):
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",
@ -43,7 +46,7 @@ def instance_widget(containerColumns, pairs, status, title, buttons):
}
def home_builder(data):
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
"""
@ -126,7 +129,7 @@ def home_builder(data):
return base64.b64encode(bytes(json.dumps(builder), "utf-8")).decode("ascii")
def instances_builder(instances: list):
def instances_builder(instances: list) -> str:
"""
It returns the needed format from data to render the instances page in JSON format for the Vue.js builder
"""
@ -172,23 +175,30 @@ def instances_builder(instances: list):
return base64.b64encode(bytes(json.dumps(builder), "utf-8")).decode("ascii")
def get_forms(templates=[], plugins=[], settings={}):
def get_forms(templates: list = [], plugins: list = [], settings: dict = {}, render_forms: tuple = ("advanced", "easy", "raw")) -> 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).
We will format to fit each form type (easy, advanced, raw) in case
"""
forms = {"advanced": {}, "easy": {}, "raw": {}}
forms = {}
for form in render_forms:
forms[form] = {}
for template in templates:
forms["advanced"][template.get("name")] = set_advanced(template, plugins, settings)
forms["raw"][template.get("name")] = set_raw(template, plugins, settings)
forms["easy"][template.get("name")] = set_easy(template, plugins, settings)
if "advanced" in forms:
forms["advanced"][template.get("name")] = set_advanced(template, plugins, settings)
if "raw" in forms:
forms["raw"][template.get("name")] = set_raw(template, plugins, settings)
if "easy" in forms:
forms["easy"][template.get("name")] = set_easy(template, plugins, settings)
return forms
def set_easy(template, plugins_base, settings):
def set_easy(template: list, plugins_base: list, settings: dict) -> 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.
@ -234,7 +244,7 @@ def set_easy(template, plugins_base, settings):
return steps
def set_raw(template, plugins_base, settings):
def set_raw(template: list, plugins_base: list, settings: dict) -> 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.
@ -259,7 +269,13 @@ def set_raw(template, plugins_base, settings):
# Then override by service settings
if setting in settings:
raw_value = settings[setting].get("value", value.get("value", value.get("default")))
val = settings[setting].get("value", value.get("value"))
# Check if value is same as default
# If case, we don't need to add it
default_val = value.get("default")
if val == default_val:
raw_value = None
# Add value only if exists
if raw_value:
@ -268,7 +284,7 @@ def set_raw(template, plugins_base, settings):
return raw_settings
def set_advanced(template, plugins_base, settings):
def set_advanced(template: list, plugins_base: list, settings: dict) -> 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.
@ -413,7 +429,7 @@ def get_multiple_from_settings(settings, multiples):
return multiple_settings
def set_multiples(template, format_plugins, service_settings):
def set_multiples(template, format_plugins, settings):
"""
Set the multiples settings for each plugin.
"""
@ -452,7 +468,7 @@ def set_multiples(template, format_plugins, service_settings):
# 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(service_settings, 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
@ -460,13 +476,13 @@ def set_multiples(template, format_plugins, service_settings):
def format_setting(
setting_name,
setting_value,
total_settings,
loop_id,
template_settings,
service_settings,
):
setting_name: str,
setting_value: Union[str, int],
total_settings: Union[str, int],
loop_id: Union[str, int],
template_settings: dict,
settings: dict,
) -> dict:
"""
Format a setting in order to be used with form builder.
This will only set value for none multiple settings.
@ -511,18 +527,18 @@ def format_setting(
# Then override by service settings if not a multiple
# Case multiple, we need to keep the default value and override only each multiple group
if setting_name in service_settings and not "multiple" in setting_value:
setting_value["value"] = service_settings[setting_name].get("value", setting_value.get("value", setting_value.get("default")))
setting_value["method"] = service_settings[setting_name].get("method", "ui")
if setting_name in settings and not "multiple" in setting_value:
setting_value["value"] = settings[setting_name].get("value", setting_value.get("value", setting_value.get("default")))
setting_value["method"] = settings[setting_name].get("method", "ui")
# Then override by service settings
if setting_name in service_settings:
setting_value["disabled"] = False if service_settings[setting_name].get("method", "ui") in ("ui", "default", "manual") else True
if setting_name in settings:
setting_value["disabled"] = False if settings[setting_name].get("method", "ui") in ("ui", "default", "manual") else True
# Prepare popover checking "help", "context"
popovers = []
if (setting_value.get("disabled", False)) and service_settings[setting_name].get("method", "ui") not in ("ui", "default", "manual"):
if (setting_value.get("disabled", False)) and settings[setting_name].get("method", "ui") not in ("ui", "default", "manual"):
popovers.append(
{
"iconName": "trespass",
@ -550,7 +566,7 @@ def format_setting(
return setting_value
def global_config_builder(plugins, settings):
def global_config_builder(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).
"""
@ -580,11 +596,10 @@ def global_config_builder(plugins, settings):
{
"type": "Templates",
"data": {
"templates": get_forms(templates, plugins, settings),
"templates": get_forms(templates, plugins, settings, ("advanced", "raw")),
},
},
],
}
]
return base64.b64encode(bytes(json.dumps(builder), "utf-8")).decode("ascii")

View file

@ -67,7 +67,9 @@ def create_base_dirs():
def move_template(folder, target_folder):
"""For the define folder, loop on each files and move them to the target folder with some modification (replace relative path to absolute path for example)"""
base_html = """
async def move_template_file(root, file, target_folder):
"""Move the template file to the target folder. This will replace relative path on file to absolute path to work with flask static"""
base_html = """
<body>
{% set data_server_flash = [] %}
{% with messages = get_flashed_messages(with_categories=true) %}
@ -83,8 +85,9 @@ def move_template(folder, target_folder):
</body>
</html>"""
async def move_template_file(root, file, target_folder, base_html):
"""Move the template file to the target folder. This will replace relative path on file to absolute path to work with flask static"""
if "global-config" in root:
base_html = base_html.replace("data_server_builder[1:-1]", "data_server_builder")
file_path = os.path.join(root, file)
def format_template(m):
@ -115,7 +118,7 @@ def move_template(folder, target_folder):
# I want to get all subfollder of a folder
for root, dirs, files in os.walk(folder):
for file in files:
asyncio.run(move_template_file(root, file, target_folder, base_html))
asyncio.run(move_template_file(root, file, target_folder))
def move_statics(folder, target_folder):

View file

@ -1,5 +1,5 @@
<script setup>
import { defineProps, reactive, onMounted, computed } from "vue";
import { defineProps, reactive, onMounted, computed, onBeforeMount } from "vue";
import Container from "@components/Widget/Container.vue";
import Title from "@components/Widget/Title.vue";
import Subtitle from "@components/Widget/Subtitle.vue";
@ -44,39 +44,11 @@ const props = defineProps({
});
const data = reactive({
str: JSON.stringify(props.template),
// Data on creation of editor
entry: computed(() => {
let dataStr = data.str;
// Remove first and last curly brackets
dataStr = dataStr.slice(1, -1);
// Remove all '\"' from stringified JSON
dataStr = dataStr.replace(/\\"/g, '"');
// Remove all newlines inside values
dataStr = dataStr.replace(/\n/g, "");
// Add new line only at the end of each key value
dataStr = dataStr.replace(/",/g, "\n");
const lines = dataStr.split("\n");
dataStr = lines.map((line) => {
// Get index of the first colon
const index = line.indexOf(":");
// Update colon by equal sign and remove quotes
return line.slice(1, index - 1) + "=" + line.slice(index + 2);
});
dataStr = dataStr.join("\n");
// Remove first char if it is a quote
dataStr = dataStr[0] === '"' ? dataStr.slice(1) : dataStr;
// Remove last char if it is a quote
dataStr = dataStr.slice(-1) === '"' ? dataStr.slice(0, -1) : dataStr;
return dataStr;
}),
str: "",
// Data retrieve from editor after creation
inp: "",
isValid: computed(() => {
// Transform to a possible valid JSON
let dataToCheck = data.inp || data.entry;
let dataToCheck = data.str;
// Replace quotes "" with quotes ''
dataToCheck = dataToCheck.replace(/"/g, "'");
@ -111,8 +83,35 @@ const data = reactive({
}),
});
function json2raw(json) {
let dataStr = JSON.stringify(json);
// Remove first and last curly brackets
dataStr = dataStr.slice(1, -1);
// Remove all '\"' from stringified JSON
dataStr = dataStr.replace(/\\"/g, '"');
// Remove all newlines inside values
dataStr = dataStr.replace(/\n/g, "");
// Add new line only at the end of each key value
dataStr = dataStr.replace(/",/g, "\n");
const lines = dataStr.split("\n");
dataStr = lines.map((line) => {
// Get index of the first colon
const index = line.indexOf(":");
// Update colon by equal sign and remove quotes
return line.slice(1, index - 1) + "=" + line.slice(index + 2);
});
dataStr = dataStr.join("\n");
// Remove first char if it is a quote
dataStr = dataStr[0] === '"' ? dataStr.slice(1) : dataStr;
// Remove last char if it is a quote
dataStr = dataStr.slice(-1) === '"' ? dataStr.slice(0, -1) : dataStr;
return dataStr;
}
const editorData = {
value: data.inp || data.entry,
value: data.str,
name: `raw-editor-${uuidv4()}`,
label: `raw-editor-${uuidv4()}`,
hideLabel: true,
@ -127,6 +126,10 @@ const buttonSave = {
size: "normal",
containerClass: "flex justify-center",
};
onBeforeMount(() => {
data.str = json2raw(props.template);
});
</script>
<template>
@ -142,7 +145,7 @@ const buttonSave = {
<Subtitle type="card" :subtitle="'dashboard_raw_mode_subtitle'" />
<Container class="form-raw-editor-container layout-settings">
<Editor @inp="(v) => (data.inp = v)" v-bind="editorData" />
<Editor @inp="(v) => (data.str = v)" v-bind="editorData" />
</Container>
<Button :disabled="data.isValid ? false : true" v-bind="buttonSave" />

View file

@ -108,7 +108,7 @@
"inp_input_error_taken": "value already taken",
"inp_popover_multisite": "This setting is multisite.",
"inp_popover_global": "This setting is global.",
"inp_popover_method_disabled": "This setting method (scheduler, autoconf...) unable value change.",
"inp_popover_method_disabled": "The setting method disabled any change.",
"inp_combobox": "Combobox input for relate select radio group.",
"inp_select_dropdown_button_desc": "Toggle hide/show radio group (dropdown) to change value.",
"inp_select_dropdown_desc": "Radio group (dropdown) to change value.",

View file

@ -1,5 +1,6 @@
<script setup>
import { reactive, onBeforeMount } from "vue";
import { reactive, onBeforeMount, onMounted } from "vue";
import { useGlobal } from "@utils/global.js";
import DashboardLayout from "@components/Dashboard/Layout.vue";
import BuilderGlobalConfig from "@components/Builder/GlobalConfig.vue";
@ -23,6 +24,10 @@ onBeforeMount(() => {
: {};
globalConfig.builder = data;
});
onMounted(() => {
useGlobal();
});
</script>
<template>

File diff suppressed because one or more lines are too long

View file

@ -6,16 +6,18 @@
<link rel="stylesheet" href="/css/style.css" />
<link rel="stylesheet" href="/css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | DASHBOARD</title>
<title>BunkerWeb | Home</title>
</head>
<body>
<div class="hidden" data-server-global='{"username" : "admin"}'></div>
<div class="hidden"
data-server-flash='[{"type" : "success", "title" : "success", "message" : "Success feedback"}, {"type" : "error", "title" : "error", "message" : "Error feedback"}, {"type" : "warning", "title" : "warning", "message" : "Warning feedback"}, {"type" : "info", "title" : "info", "message" : "Info feedback"}]'>
</div>
<div class="hidden"
data-server-builder='W3sidHlwZSI6ICJjYXJkIiwgImxpbmsiOiAiaHR0cHM6Ly9wYW5lbC5idW5rZXJ3ZWIuaW8vP3V0bV9jYW1wYWlnbj1zZWxmJnV0bV9zb3VyY2U9dWkjcHJvIiwgImNvbnRhaW5lckNvbHVtbnMiOiB7InBjIjogNCwgInRhYmxldCI6IDYsICJtb2JpbGUiOiAxMn0sICJ3aWRnZXRzIjogW3sidHlwZSI6ICJTdGF0IiwgImRhdGEiOiB7InRpdGxlIjogImhvbWVfdmVyc2lvbiIsICJzdWJ0aXRsZSI6ICJob21lX3VwZ3JhZGVfdG9fcHJvIiwgInN1YnRpdGxlQ29sb3IiOiAid2FybmluZyIsICJzdGF0IjogImhvbWVfZnJlZSIsICJpY29uTmFtZSI6ICJrZXkifX1dfSwgeyJ0eXBlIjogImNhcmQiLCAibGluayI6ICJodHRwczovL2dpdGh1Yi5jb20vYnVua2VyaXR5L2J1bmtlcndlYiIsICJjb250YWluZXJDb2x1bW5zIjogeyJwYyI6IDQsICJ0YWJsZXQiOiA2LCAibW9iaWxlIjogMTJ9LCAid2lkZ2V0cyI6IFt7InR5cGUiOiAiU3RhdCIsICJkYXRhIjogeyJ0aXRsZSI6ICJob21lX3ZlcnNpb25fbnVtYmVyIiwgInN1YnRpdGxlIjogImhvbWVfdXBkYXRlX2F2YWlsYWJsZSIsICJzdWJ0aXRsZUNvbG9yIjogIndhcm5pbmciLCAic3RhdCI6ICIxLjUuOCIsICJpY29uTmFtZSI6ICJ3aXJlIn19XX0sIHsidHlwZSI6ICJjYXJkIiwgImxpbmsiOiAiL2luc3RhbmNlcyIsICJjb250YWluZXJDb2x1bW5zIjogeyJwYyI6IDQsICJ0YWJsZXQiOiA2LCAibW9iaWxlIjogMTJ9LCAid2lkZ2V0cyI6IFt7InR5cGUiOiAiU3RhdCIsICJkYXRhIjogeyJ0aXRsZSI6ICJob21lX2luc3RhbmNlcyIsICJzdWJ0aXRsZSI6ICJob21lX3RvdGFsX251bWJlciIsICJzdWJ0aXRsZUNvbG9yIjogImluZm8iLCAic3RhdCI6IDEsICJpY29uTmFtZSI6ICJib3gifX1dfSwgeyJ0eXBlIjogImNhcmQiLCAibGluayI6ICIvc2VydmljZXMiLCAiY29udGFpbmVyQ29sdW1ucyI6IHsicGMiOiA0LCAidGFibGV0IjogNiwgIm1vYmlsZSI6IDEyfSwgIndpZGdldHMiOiBbeyJ0eXBlIjogIlN0YXQiLCAiZGF0YSI6IHsidGl0bGUiOiAiaG9tZV9zZXJ2aWNlcyIsICJzdWJ0aXRsZSI6ICJob21lX2FsbF9tZXRob2RzX2luY2x1ZGVkIiwgInN1YnRpdGxlQ29sb3IiOiAiaW5mbyIsICJzdGF0IjogMiwgImljb25OYW1lIjogImRpc2sifX1dfSwgeyJ0eXBlIjogImNhcmQiLCAibGluayI6ICIvcGx1Z2lucyIsICJjb250YWluZXJDb2x1bW5zIjogeyJwYyI6IDQsICJ0YWJsZXQiOiA2LCAibW9iaWxlIjogMTJ9LCAid2lkZ2V0cyI6IFt7InR5cGUiOiAiU3RhdCIsICJkYXRhIjogeyJ0aXRsZSI6ICJob21lX3BsdWdpbnMiLCAic3VidGl0bGUiOiAiaG9tZV9ub19lcnJvciIsICJzdWJ0aXRsZUNvbG9yIjogInN1Y2Nlc3MiLCAic3RhdCI6ICI0MiIsICJpY29uTmFtZSI6ICJwdXp6bGUifX1dfV0'>
</div>
<div
class="hidden"
data-server-flash='[{"type" : "success", "title" : "success", "message" : "Success feedback"}, {"type" : "error", "title" : "error", "message" : "Error feedback"}, {"type" : "warning", "title" : "warning", "message" : "Warning feedback"}, {"type" : "info", "title" : "info", "message" : "Info feedback"}]'
></div>
<div
class="hidden"
data-server-builder="W3sidHlwZSI6ICJjYXJkIiwgImxpbmsiOiAiaHR0cHM6Ly9wYW5lbC5idW5rZXJ3ZWIuaW8vP3V0bV9jYW1wYWlnbj1zZWxmJnV0bV9zb3VyY2U9dWkjcHJvIiwgImNvbnRhaW5lckNvbHVtbnMiOiB7InBjIjogNCwgInRhYmxldCI6IDYsICJtb2JpbGUiOiAxMn0sICJ3aWRnZXRzIjogW3sidHlwZSI6ICJTdGF0IiwgImRhdGEiOiB7InRpdGxlIjogImhvbWVfdmVyc2lvbiIsICJzdWJ0aXRsZSI6ICJob21lX3VwZ3JhZGVfdG9fcHJvIiwgInN1YnRpdGxlQ29sb3IiOiAid2FybmluZyIsICJzdGF0IjogImhvbWVfZnJlZSIsICJpY29uTmFtZSI6ICJrZXkifX1dfSwgeyJ0eXBlIjogImNhcmQiLCAibGluayI6ICJodHRwczovL2dpdGh1Yi5jb20vYnVua2VyaXR5L2J1bmtlcndlYiIsICJjb250YWluZXJDb2x1bW5zIjogeyJwYyI6IDQsICJ0YWJsZXQiOiA2LCAibW9iaWxlIjogMTJ9LCAid2lkZ2V0cyI6IFt7InR5cGUiOiAiU3RhdCIsICJkYXRhIjogeyJ0aXRsZSI6ICJob21lX3ZlcnNpb25fbnVtYmVyIiwgInN1YnRpdGxlIjogImhvbWVfdXBkYXRlX2F2YWlsYWJsZSIsICJzdWJ0aXRsZUNvbG9yIjogIndhcm5pbmciLCAic3RhdCI6ICIxLjUuOCIsICJpY29uTmFtZSI6ICJ3aXJlIn19XX0sIHsidHlwZSI6ICJjYXJkIiwgImxpbmsiOiAiL2luc3RhbmNlcyIsICJjb250YWluZXJDb2x1bW5zIjogeyJwYyI6IDQsICJ0YWJsZXQiOiA2LCAibW9iaWxlIjogMTJ9LCAid2lkZ2V0cyI6IFt7InR5cGUiOiAiU3RhdCIsICJkYXRhIjogeyJ0aXRsZSI6ICJob21lX2luc3RhbmNlcyIsICJzdWJ0aXRsZSI6ICJob21lX3RvdGFsX251bWJlciIsICJzdWJ0aXRsZUNvbG9yIjogImluZm8iLCAic3RhdCI6IDEsICJpY29uTmFtZSI6ICJib3gifX1dfSwgeyJ0eXBlIjogImNhcmQiLCAibGluayI6ICIvc2VydmljZXMiLCAiY29udGFpbmVyQ29sdW1ucyI6IHsicGMiOiA0LCAidGFibGV0IjogNiwgIm1vYmlsZSI6IDEyfSwgIndpZGdldHMiOiBbeyJ0eXBlIjogIlN0YXQiLCAiZGF0YSI6IHsidGl0bGUiOiAiaG9tZV9zZXJ2aWNlcyIsICJzdWJ0aXRsZSI6ICJob21lX2FsbF9tZXRob2RzX2luY2x1ZGVkIiwgInN1YnRpdGxlQ29sb3IiOiAiaW5mbyIsICJzdGF0IjogMiwgImljb25OYW1lIjogImRpc2sifX1dfSwgeyJ0eXBlIjogImNhcmQiLCAibGluayI6ICIvcGx1Z2lucyIsICJjb250YWluZXJDb2x1bW5zIjogeyJwYyI6IDQsICJ0YWJsZXQiOiA2LCAibW9iaWxlIjogMTJ9LCAid2lkZ2V0cyI6IFt7InR5cGUiOiAiU3RhdCIsICJkYXRhIjogeyJ0aXRsZSI6ICJob21lX3BsdWdpbnMiLCAic3VidGl0bGUiOiAiaG9tZV9ub19lcnJvciIsICJzdWJ0aXRsZUNvbG9yIjogInN1Y2Nlc3MiLCAic3RhdCI6ICI0MiIsICJpY29uTmFtZSI6ICJwdXp6bGUifX1dfV0"
></div>
<div id="app"></div>
<script type="module" src="home.js"></script>
</body>

View file

@ -6,16 +6,18 @@
<link rel="stylesheet" href="/css/style.css" />
<link rel="stylesheet" href="/css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | DASHBOARD</title>
<title>BunkerWeb | Instances</title>
</head>
<body>
<div class="hidden" data-server-global='{"username" : "admin"}'></div>
<div class="hidden"
data-server-flash='[{"type" : "success", "title" : "title", "message" : "Success feedback"}, {"type" : "error", "title" : "title", "message" : "Error feedback"}, {"type" : "warning", "title" : "title", "message" : "Warning feedback"}, {"type" : "info", "title" : "title", "message" : "Info feedback"}]'>
</div>
<div class="hidden"
data-server-builder='W3sidHlwZSI6ICJjYXJkIiwgImNvbnRhaW5lckNvbHVtbnMiOiB7InBjIjogNiwgInRhYmxldCI6IDYsICJtb2JpbGUiOiAxMn0sICJ3aWRnZXRzIjogW3sidHlwZSI6ICJJbnN0YW5jZSIsICJkYXRhIjogeyJwYWlycyI6IFt7ImtleSI6ICJpbnN0YW5jZXNfaG9zdG5hbWUiLCAidmFsdWUiOiAiYnVua2Vyd2ViIn0sIHsia2V5IjogImluc3RhbmNlc190eXBlIiwgInZhbHVlIjogIm1hbnVhbCJ9LCB7ImtleSI6ICJpbnN0YW5jZXNfc3RhdHVzIiwgInZhbHVlIjogImluc3RhbmNlc19hY3RpdmUifV0sICJzdGF0dXMiOiAic3VjY2VzcyIsICJ0aXRsZSI6ICJidW5rZXJ3ZWIiLCAiYnV0dG9ucyI6IFt7ImF0dHJzIjogeyJkYXRhLXN1Ym1pdC1mb3JtIjogIntcIklOU1RBTkNFX0lEXCIgOiBcImJ1bmtlcndlYlwiLCBcIm9wZXJhdGlvblwiIDogXCJyZWxvYWRcIiB9In0sICJ0ZXh0IjogImFjdGlvbl9yZWxvYWQiLCAiY29sb3IiOiAid2FybmluZyJ9LCB7ImF0dHJzIjogeyJkYXRhLXN1Ym1pdC1mb3JtIjogIntcIklOU1RBTkNFX0lEXCIgOiBcImJ1bmtlcndlYlwiLCBcIm9wZXJhdGlvblwiIDogXCJzdG9wXCIgfSJ9LCAidGV4dCI6ICJhY3Rpb25fc3RvcCIsICJjb2xvciI6ICJlcnJvciJ9XX19XX0sIHsidHlwZSI6ICJjYXJkIiwgImNvbnRhaW5lckNvbHVtbnMiOiB7InBjIjogNiwgInRhYmxldCI6IDYsICJtb2JpbGUiOiAxMn0sICJ3aWRnZXRzIjogW3sidHlwZSI6ICJJbnN0YW5jZSIsICJkYXRhIjogeyJwYWlycyI6IFt7ImtleSI6ICJpbnN0YW5jZXNfaG9zdG5hbWUiLCAidmFsdWUiOiAiYnVua2Vyd2ViIn0sIHsia2V5IjogImluc3RhbmNlc190eXBlIiwgInZhbHVlIjogIm1hbnVhbCJ9LCB7ImtleSI6ICJpbnN0YW5jZXNfc3RhdHVzIiwgInZhbHVlIjogImluc3RhbmNlc19hY3RpdmUifV0sICJzdGF0dXMiOiAic3VjY2VzcyIsICJ0aXRsZSI6ICJidW5rZXJ3ZWIiLCAiYnV0dG9ucyI6IFt7ImF0dHJzIjogeyJkYXRhLXN1Ym1pdC1mb3JtIjogIntcIklOU1RBTkNFX0lEXCIgOiBcImJ1bmtlcndlYlwiLCBcIm9wZXJhdGlvblwiIDogXCJyZWxvYWRcIiB9In0sICJ0ZXh0IjogImFjdGlvbl9yZWxvYWQiLCAiY29sb3IiOiAid2FybmluZyJ9LCB7ImF0dHJzIjogeyJkYXRhLXN1Ym1pdC1mb3JtIjogIntcIklOU1RBTkNFX0lEXCIgOiBcImJ1bmtlcndlYlwiLCBcIm9wZXJhdGlvblwiIDogXCJzdG9wXCIgfSJ9LCAidGV4dCI6ICJhY3Rpb25fc3RvcCIsICJjb2xvciI6ICJlcnJvciJ9XX19XX1d'>
</div>
<div
class="hidden"
data-server-flash='[{"type" : "success", "title" : "title", "message" : "Success feedback"}, {"type" : "error", "title" : "title", "message" : "Error feedback"}, {"type" : "warning", "title" : "title", "message" : "Warning feedback"}, {"type" : "info", "title" : "title", "message" : "Info feedback"}]'
></div>
<div
class="hidden"
data-server-builder="W3sidHlwZSI6ICJjYXJkIiwgImNvbnRhaW5lckNvbHVtbnMiOiB7InBjIjogNiwgInRhYmxldCI6IDYsICJtb2JpbGUiOiAxMn0sICJ3aWRnZXRzIjogW3sidHlwZSI6ICJJbnN0YW5jZSIsICJkYXRhIjogeyJwYWlycyI6IFt7ImtleSI6ICJpbnN0YW5jZXNfaG9zdG5hbWUiLCAidmFsdWUiOiAiYnVua2Vyd2ViIn0sIHsia2V5IjogImluc3RhbmNlc190eXBlIiwgInZhbHVlIjogIm1hbnVhbCJ9LCB7ImtleSI6ICJpbnN0YW5jZXNfc3RhdHVzIiwgInZhbHVlIjogImluc3RhbmNlc19hY3RpdmUifV0sICJzdGF0dXMiOiAic3VjY2VzcyIsICJ0aXRsZSI6ICJidW5rZXJ3ZWIiLCAiYnV0dG9ucyI6IFt7ImF0dHJzIjogeyJkYXRhLXN1Ym1pdC1mb3JtIjogIntcIklOU1RBTkNFX0lEXCIgOiBcImJ1bmtlcndlYlwiLCBcIm9wZXJhdGlvblwiIDogXCJyZWxvYWRcIiB9In0sICJ0ZXh0IjogImFjdGlvbl9yZWxvYWQiLCAiY29sb3IiOiAid2FybmluZyJ9LCB7ImF0dHJzIjogeyJkYXRhLXN1Ym1pdC1mb3JtIjogIntcIklOU1RBTkNFX0lEXCIgOiBcImJ1bmtlcndlYlwiLCBcIm9wZXJhdGlvblwiIDogXCJzdG9wXCIgfSJ9LCAidGV4dCI6ICJhY3Rpb25fc3RvcCIsICJjb2xvciI6ICJlcnJvciJ9XX19XX0sIHsidHlwZSI6ICJjYXJkIiwgImNvbnRhaW5lckNvbHVtbnMiOiB7InBjIjogNiwgInRhYmxldCI6IDYsICJtb2JpbGUiOiAxMn0sICJ3aWRnZXRzIjogW3sidHlwZSI6ICJJbnN0YW5jZSIsICJkYXRhIjogeyJwYWlycyI6IFt7ImtleSI6ICJpbnN0YW5jZXNfaG9zdG5hbWUiLCAidmFsdWUiOiAiYnVua2Vyd2ViIn0sIHsia2V5IjogImluc3RhbmNlc190eXBlIiwgInZhbHVlIjogIm1hbnVhbCJ9LCB7ImtleSI6ICJpbnN0YW5jZXNfc3RhdHVzIiwgInZhbHVlIjogImluc3RhbmNlc19hY3RpdmUifV0sICJzdGF0dXMiOiAic3VjY2VzcyIsICJ0aXRsZSI6ICJidW5rZXJ3ZWIiLCAiYnV0dG9ucyI6IFt7ImF0dHJzIjogeyJkYXRhLXN1Ym1pdC1mb3JtIjogIntcIklOU1RBTkNFX0lEXCIgOiBcImJ1bmtlcndlYlwiLCBcIm9wZXJhdGlvblwiIDogXCJyZWxvYWRcIiB9In0sICJ0ZXh0IjogImFjdGlvbl9yZWxvYWQiLCAiY29sb3IiOiAid2FybmluZyJ9LCB7ImF0dHJzIjogeyJkYXRhLXN1Ym1pdC1mb3JtIjogIntcIklOU1RBTkNFX0lEXCIgOiBcImJ1bmtlcndlYlwiLCBcIm9wZXJhdGlvblwiIDogXCJzdG9wXCIgfSJ9LCAidGV4dCI6ICJhY3Rpb25fc3RvcCIsICJjb2xvciI6ICJlcnJvciJ9XX19XX1d"
></div>
<div id="app"></div>
<script type="module" src="instances.js"></script>
</body>

View file

@ -9697,195 +9697,8 @@
}
]
},
"easy": {
"default": []
},
"raw": {
"default": {
"IS_LOADING": "no",
"NGINX_PREFIX": "/etc/nginx/",
"HTTP_PORT": "8080",
"HTTPS_PORT": "8443",
"SERVER_NAME": "app1.example.com www.example.com",
"WORKER_PROCESSES": "auto",
"WORKER_RLIMIT_NOFILE": "2048",
"WORKER_CONNECTIONS": "1024",
"LOG_FORMAT": "$host $remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\"",
"DNS_RESOLVERS": "127.0.0.11",
"DATASTORE_MEMORY_SIZE": "64m",
"CACHESTORE_MEMORY_SIZE": "64m",
"CACHESTORE_IPC_MEMORY_SIZE": "16m",
"CACHESTORE_MISS_MEMORY_SIZE": "16m",
"CACHESTORE_LOCKS_MEMORY_SIZE": "16m",
"USE_API": "yes",
"API_HTTP_PORT": "5000",
"API_LISTEN_IP": "0.0.0.0",
"API_SERVER_NAME": "bwapi",
"AUTOCONF_MODE": "no",
"SWARM_MODE": "no",
"KUBERNETES_MODE": "no",
"SERVER_TYPE": "http",
"LISTEN_STREAM": "yes",
"LISTEN_STREAM_PORT": "1337",
"LISTEN_STREAM_PORT_SSL": "4242",
"USE_UDP": "no",
"USE_IPV6": "no",
"IS_DRAFT": "no",
"TIMERS_LOG_LEVEL": "debug",
"USE_ANTIBOT": "no",
"ANTIBOT_URI": "/challenge",
"ANTIBOT_TIME_RESOLVE": "60",
"ANTIBOT_TIME_VALID": "86400",
"ANTIBOT_RECAPTCHA_SCORE": "0.7",
"USE_AUTH_BASIC": "no",
"AUTH_BASIC_LOCATION": "sitewide",
"AUTH_BASIC_USER": "changeme",
"AUTH_BASIC_PASSWORD": "changeme",
"AUTH_BASIC_TEXT": "Restricted area",
"USE_BACKUP": "yes",
"BACKUP_SCHEDULE": "daily",
"BACKUP_ROTATION": "7",
"BACKUP_DIRECTORY": "/var/lib/bunkerweb/backups",
"USE_BAD_BEHAVIOR": "yes",
"BAD_BEHAVIOR_STATUS_CODES": "400 401 403 404 405 429 444",
"BAD_BEHAVIOR_THRESHOLD": "10",
"BAD_BEHAVIOR_COUNT_TIME": "60",
"BAD_BEHAVIOR_BAN_TIME": "86400",
"BLACKLIST_RDNS": ".shodan.io .censys.io",
"BLACKLIST_RDNS_GLOBAL": "yes",
"BLACKLIST_IP_URLS": "https://www.dan.me.uk/torlist/?exit",
"BLACKLIST_USER_AGENT_URLS": "https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list",
"USE_BROTLI": "no",
"BROTLI_TYPES": "application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml",
"BROTLI_MIN_LENGTH": "1000",
"BROTLI_COMP_LEVEL": "6",
"BUNKERNET_SERVER": "https://api.bunkerweb.io",
"USE_CORS": "no",
"CORS_ALLOW_ORIGIN": "self",
"CORS_ALLOW_METHODS": "GET, POST, OPTIONS",
"CORS_ALLOW_HEADERS": "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range",
"CORS_ALLOW_CREDENTIALS": "no",
"CORS_EXPOSE_HEADERS": "Content-Length,Content-Range",
"CROSS_ORIGIN_OPENER_POLICY": "same-origin",
"CROSS_ORIGIN_EMBEDDER_POLICY": "require-corp",
"CROSS_ORIGIN_RESOURCE_POLICY": "same-site",
"CORS_MAX_AGE": "86400",
"CORS_DENY_REQUEST": "yes",
"CLIENT_CACHE_EXTENSIONS": "jpg|jpeg|png|bmp|ico|svg|tif|css|js|otf|ttf|eot|woff|woff2",
"CLIENT_CACHE_ETAG": "yes",
"CLIENT_CACHE_CONTROL": "public, max-age=15552000",
"USE_CUSTOM_SSL": "no",
"DATABASE_URI": "sqlite:////var/lib/bunkerweb/db.sqlite3",
"DATABASE_LOG_LEVEL": "warning",
"USE_DNSBL": "yes",
"DNSBL_LIST": "bl.blocklist.de problems.dnsbl.sorbs.net sbl.spamhaus.org xbl.spamhaus.org",
"INTERCEPTED_ERROR_CODES": "400 401 403 404 405 413 429 500 501 502 503 504",
"USE_GREYLIST": "no",
"GREYLIST_RDNS_GLOBAL": "yes",
"GZIP_TYPES": "application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-opentype application/x-font-truetype application/x-font-ttf application/x-javascript application/xhtml+xml application/xml font/eot font/opentype font/otf font/truetype image/svg+xml image/vnd.microsoft.icon image/x-icon image/x-win-bitmap text/css text/javascript text/plain text/xml",
"GZIP_MIN_LENGTH": "1000",
"GZIP_COMP_LEVEL": "5",
"GZIP_PROXIED": "no-cache no-store private expired auth",
"REMOVE_HEADERS": "Server Expect-CT X-Powered-By X-AspNet-Version X-AspNetMvc-Version Public-Key-Pins",
"KEEP_UPSTREAM_HEADERS": "Content-Security-Policy Permissions-Policy X-Frame-Options",
"STRICT_TRANSPORT_SECURITY": "max-age=31536000; includeSubDomains; preload",
"COOKIE_FLAGS": "* HttpOnly SameSite=Lax",
"COOKIE_AUTO_SECURE_FLAG": "yes",
"CONTENT_SECURITY_POLICY": "object-src 'none'; form-action 'self'; frame-ancestors 'self';",
"CONTENT_SECURITY_POLICY_REPORT_ONLY": "no",
"REFERRER_POLICY": "strict-origin-when-cross-origin",
"PERMISSIONS_POLICY": "accelerometer=(), ambient-light-sensor=(), attribution-reporting=(), autoplay=(), battery=(), bluetooth=(), browsing-topics=(), camera=(), compute-pressure=(), display-capture=(), document-domain=(), encrypted-media=(), execution-while-not-rendered=(), execution-while-out-of-viewport=(), fullscreen=(), gamepad=(), geolocation=(), gyroscope=(), hid=(), identity-credentials-get=(), idle-detection=(), local-fonts=(), magnetometer=(), microphone=(), midi=(), otp-credentials=(), payment=(), picture-in-picture=(), publickey-credentials-create=(), publickey-credentials-get=(), screen-wake-lock=(), serial=(), speaker-selection=(), storage-access=(), usb=(), web-share=(), window-management=(), xr-spatial-tracking=()",
"X_FRAME_OPTIONS": "SAMEORIGIN",
"X_CONTENT_TYPE_OPTIONS": "nosniff",
"X_XSS_PROTECTION": "1; mode=block",
"AUTO_LETS_ENCRYPT": "no",
"USE_LETS_ENCRYPT_STAGING": "no",
"LETS_ENCRYPT_CLEAR_OLD_CERTS": "no",
"USE_LIMIT_REQ": "yes",
"LIMIT_REQ_URL": "/",
"LIMIT_REQ_RATE": "2r/s",
"USE_LIMIT_CONN": "yes",
"LIMIT_CONN_MAX_HTTP1": "10",
"LIMIT_CONN_MAX_HTTP2": "100",
"LIMIT_CONN_MAX_STREAM": "10",
"USE_METRICS": "yes",
"METRICS_MEMORY_SIZE": "16m",
"METRICS_MAX_BLOCKED_REQUESTS": "100",
"DISABLE_DEFAULT_SERVER_STRICT_SNI": "no",
"REDIRECT_HTTP_TO_HTTPS": "no",
"AUTO_REDIRECT_HTTP_TO_HTTPS": "yes",
"ALLOWED_METHODS": "GET|POST|HEAD",
"MAX_CLIENT_SIZE": "10m",
"SSL_PROTOCOLS": "TLSv1.2 TLSv1.3",
"HTTP2": "yes",
"HTTP3": "no",
"HTTP3_ALT_SVC_PORT": "443",
"LISTEN_HTTP": "yes",
"USE_OPEN_FILE_CACHE": "no",
"OPEN_FILE_CACHE": "max=1000 inactive=20s",
"OPEN_FILE_CACHE_ERRORS": "yes",
"OPEN_FILE_CACHE_MIN_USES": "2",
"OPEN_FILE_CACHE_VALID": "30s",
"DENY_HTTP_STATUS": "403",
"USE_MODSECURITY": "yes",
"USE_MODSECURITY_CRS": "yes",
"MODSECURITY_CRS_VERSION": "4",
"MODSECURITY_SEC_AUDIT_ENGINE": "RelevantOnly",
"MODSECURITY_SEC_RULE_ENGINE": "On",
"MODSECURITY_SEC_AUDIT_LOG_PARTS": "ABCFHZ",
"USE_REAL_IP": "no",
"USE_PROXY_PROTOCOL": "no",
"REAL_IP_FROM": "192.168.0.0/16 172.16.0.0/12 10.0.0.0/8",
"REAL_IP_HEADER": "X-Forwarded-For",
"REAL_IP_RECURSIVE": "yes",
"REDIRECT_TO_REQUEST_URI": "no",
"REDIRECT_TO_STATUS_CODE": "301",
"USE_REDIS": "no",
"REDIS_PORT": "6379",
"REDIS_DATABASE": "0",
"REDIS_SSL": "no",
"REDIS_SSL_VERIFY": "no",
"REDIS_TIMEOUT": "1000",
"REDIS_KEEPALIVE_IDLE": "30000",
"REDIS_KEEPALIVE_POOL": "10",
"USE_REVERSE_PROXY": "no",
"REVERSE_PROXY_INTERCEPT_ERRORS": "yes",
"REVERSE_PROXY_URL": "/",
"REVERSE_PROXY_WS": "no",
"REVERSE_PROXY_BUFFERING": "yes",
"REVERSE_PROXY_KEEPALIVE": "no",
"REVERSE_PROXY_CONNECT_TIMEOUT": "60s",
"REVERSE_PROXY_READ_TIMEOUT": "60s",
"REVERSE_PROXY_SEND_TIMEOUT": "60s",
"USE_PROXY_CACHE": "no",
"PROXY_CACHE_PATH_LEVELS": "1:2",
"PROXY_CACHE_PATH_ZONE_SIZE": "10m",
"PROXY_CACHE_PATH_PARAMS": "max_size=100m",
"PROXY_CACHE_METHODS": "GET HEAD",
"PROXY_CACHE_MIN_USES": "2",
"PROXY_CACHE_KEY": "$scheme$host$request_uri",
"PROXY_CACHE_VALID": "200=24h 301=1h 302=24h",
"PROXY_NO_CACHE": "$http_pragma $http_authorization",
"PROXY_CACHE_BYPASS": "0",
"USE_REVERSE_SCAN": "no",
"REVERSE_SCAN_PORTS": "22 80 443 3128 8000 8080",
"REVERSE_SCAN_TIMEOUT": "500",
"GENERATE_SELF_SIGNED_SSL": "no",
"SELF_SIGNED_SSL_EXPIRY": "365",
"SELF_SIGNED_SSL_SUBJ": "/CN=www.example.com/",
"SESSIONS_SECRET": "random",
"SESSIONS_NAME": "random",
"SESSIONS_IDLING_TIMEOUT": "1800",
"SESSIONS_ROLLING_TIMEOUT": "3600",
"SESSIONS_ABSOLUTE_TIMEOUT": "86400",
"SESSIONS_CHECK_IP": "yes",
"SESSIONS_CHECK_USER_AGENT": "yes",
"USE_UI": "no",
"WHITELIST_IP": "20.191.45.212 40.88.21.235 40.76.173.151 40.76.163.7 20.185.79.47 52.142.26.175 20.185.79.15 52.142.24.149 40.76.162.208 40.76.163.23 40.76.162.191 40.76.162.247",
"WHITELIST_RDNS": ".google.com .googlebot.com .yandex.ru .yandex.net .yandex.com .search.msn.com .baidu.com .baidu.jp .crawl.yahoo.net .fwd.linkedin.com .twitter.com .twttr.com .discord.com",
"WHITELIST_RDNS_GLOBAL": "yes",
"WHITELIST_ASN": "32934"
}
"default": {}
}
}
}

View file

@ -1,6 +1,7 @@
import json
import copy
import base64
from typing import Union
# Default plugins from docker-compose.ui.yml
plugins = [
@ -3313,23 +3314,30 @@ global_config = {
}
def get_forms(templates=[], plugins=[], settings={}):
def get_forms(templates: list = [], plugins: list = [], settings: dict = {}, render_forms: tuple = ("advanced", "easy", "raw")) -> 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).
We will format to fit each form type (easy, advanced, raw) in case
"""
forms = {"advanced": {}, "easy": {}, "raw": {}}
forms = {}
for form in render_forms:
forms[form] = {}
for template in templates:
forms["advanced"][template.get("name")] = set_advanced(template, plugins, settings)
forms["raw"][template.get("name")] = set_raw(template, plugins, settings)
forms["easy"][template.get("name")] = set_easy(template, plugins, settings)
if "advanced" in forms:
forms["advanced"][template.get("name")] = set_advanced(template, plugins, settings)
if "raw" in forms:
forms["raw"][template.get("name")] = set_raw(template, plugins, settings)
if "easy" in forms:
forms["easy"][template.get("name")] = set_easy(template, plugins, settings)
return forms
def set_easy(template, plugins_base, settings):
def set_easy(template: list, plugins_base: list, settings: dict) -> 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.
@ -3375,7 +3383,7 @@ def set_easy(template, plugins_base, settings):
return steps
def set_raw(template, plugins_base, settings):
def set_raw(template: list, plugins_base: list, settings: dict) -> 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.
@ -3400,7 +3408,14 @@ def set_raw(template, plugins_base, settings):
# Then override by service settings
if setting in settings:
raw_value = settings[setting].get("value", value.get("value", value.get("default")))
val = settings[setting].get("value", value.get("value"))
# Check if value is same as default
# If case, we don't need to add it
default_val = value.get("default")
if val == default_val:
raw_value = None
# Add value only if exists
if raw_value:
@ -3409,7 +3424,7 @@ def set_raw(template, plugins_base, settings):
return raw_settings
def set_advanced(template, plugins_base, settings):
def set_advanced(template: list, plugins_base: list, settings: dict) -> 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.
@ -3554,7 +3569,7 @@ def get_multiple_from_settings(settings, multiples):
return multiple_settings
def set_multiples(template, format_plugins, service_settings):
def set_multiples(template, format_plugins, settings):
"""
Set the multiples settings for each plugin.
"""
@ -3593,7 +3608,7 @@ def set_multiples(template, format_plugins, service_settings):
# 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(service_settings, 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
@ -3601,13 +3616,13 @@ def set_multiples(template, format_plugins, service_settings):
def format_setting(
setting_name,
setting_value,
total_settings,
loop_id,
template_settings,
service_settings,
):
setting_name: str,
setting_value: Union[str, int],
total_settings: Union[str, int],
loop_id: Union[str, int],
template_settings: dict,
settings: dict,
) -> dict:
"""
Format a setting in order to be used with form builder.
This will only set value for none multiple settings.
@ -3652,18 +3667,18 @@ def format_setting(
# Then override by service settings if not a multiple
# Case multiple, we need to keep the default value and override only each multiple group
if setting_name in service_settings and not "multiple" in setting_value:
setting_value["value"] = service_settings[setting_name].get("value", setting_value.get("value", setting_value.get("default")))
setting_value["method"] = service_settings[setting_name].get("method", "ui")
if setting_name in settings and not "multiple" in setting_value:
setting_value["value"] = settings[setting_name].get("value", setting_value.get("value", setting_value.get("default")))
setting_value["method"] = settings[setting_name].get("method", "ui")
# Then override by service settings
if setting_name in service_settings:
setting_value["disabled"] = False if service_settings[setting_name].get("method", "ui") in ("ui", "default", "manual") else True
if setting_name in settings:
setting_value["disabled"] = False if settings[setting_name].get("method", "ui") in ("ui", "default", "manual") else True
# Prepare popover checking "help", "context"
popovers = []
if (setting_value.get("disabled", False)) and service_settings[setting_name].get("method", "ui") not in ("ui", "default", "manual"):
if (setting_value.get("disabled", False)) and settings[setting_name].get("method", "ui") not in ("ui", "default", "manual"):
popovers.append(
{
"iconName": "trespass",
@ -3691,7 +3706,7 @@ def format_setting(
return setting_value
def global_config_builder(plugins, settings):
def global_config_builder(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).
"""
@ -3721,19 +3736,17 @@ def global_config_builder(plugins, settings):
{
"type": "Templates",
"data": {
"templates": get_forms(templates, plugins, settings),
"templates": get_forms(templates, plugins, settings, ("advanced", "raw")),
},
},
],
}
]
return builder
# return base64.b64encode(bytes(json.dumps(builder), "utf-8")).decode("ascii")
output = global_config_builder(plugins, global_config)
print(output)
with open("globalconfig.json", "w") as f:
json.dump(output, f, indent=4)

File diff suppressed because one or more lines are too long

View file

@ -41,6 +41,10 @@ export default defineConfig({
input: {
home: resolve(__dirname, "./dashboard/pages/home/index.html"),
instances: resolve(__dirname, "./dashboard/pages/instances/index.html"),
global_config: resolve(
__dirname,
"./dashboard/pages/global-config/index.html"
),
},
},
},

View file

@ -1161,7 +1161,7 @@ def services():
)
@app.route("/global_config", methods=["GET", "POST"])
@app.route("/global-config", methods=["GET", "POST"])
@login_required
def global_config():
if request.method == "POST":
@ -1232,13 +1232,10 @@ def global_config():
)
)
# Display global config
global_config = app.config["DB"].get_config(global_only=True, methods=True)
# Display global config
plugins = app.config["CONFIG"].get_plugins()
print(global_config, flush=True)
data_server_builder = global_config_builder(plugins, global_config)
return render_template("global_config.html", data_server_builder=global_config, dumped_global_config=dumps(global_config))
return render_template("global-config.html", data_server_builder=data_server_builder)
@app.route("/configs", methods=["GET", "POST"])

30
src/ui/templates/global-config.html vendored Normal file
View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/x-icon" href="img/favicon.ico" />
<link rel="stylesheet" href="css/style.css" />
<link rel="stylesheet" href="css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | Global config</title>
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/global_config-CkvHODXe.js"></script>
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Title-B1qYFCEw.js">
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-CEsjkeUs.js">
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/form-BpBCT1YO.js">
<link rel="stylesheet" crossorigin href="assets/global_config-D2kv0NCW.css">
</head>
<body>
{% set data_server_flash = [] %}
{% with messages = get_flashed_messages(with_categories=true) %}
{% for category, message in messages %}
{% if data_server_flash.append({"type": "error" if category == "error" else "success", "title": "dashboard_error" if category == "error" else "dashboard_success", "message": message}) %}{% endif %}
{% endfor %}
{% endwith %}
<div class='hidden' data-csrf-token='{{ csrf_token() }}'></div>
<div class='hidden' data-server-global='{{data_server_global if data_server_global else {}}}'></div>
<div class='hidden' data-server-flash='{{data_server_flash|tojson}}'></div>
<div class='hidden' data-server-builder='{{data_server_builder}}'></div>
<div id='app'></div>
</body>
</html>

View file

@ -1,74 +0,0 @@
{% extends "base.html" %}
{% block content %}
{% set attribute_name = "global-config" %}
<div data-global-config-tabs-select-container
class="z-100 w-full grid grid-cols-12 h-fit max-h-100 sm:max-h-125 col-span-12 md:col-span-6 lg:col-span-4 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<div data-{{ current_endpoint }}-tabs-select-header class="col-span-12">
<div class="flex flex-col xs:flex-row xs:justify-start xs:items-center gap-x-4 gap-y-2 mb-4">
<h5 class="transition duration-300 ease-in-out 0 ml-2 font-bold text-md uppercase dark:text-white/90 mb-0">PLUGINS</h5>
</div>
{% include "settings_tabs_select.html" %}
</div>
</div>
<!-- filter -->
{% set filters = [
{
"type": "input",
"name": "Search",
"label": "search",
"id": "keyword",
"placeholder": "keyword",
"pattern": "(.*?)"
},
{
"type": "select",
"name": "Context",
"id": "context",
"value": "all",
"values": [
"all",
"global",
"multisite"
]
},
{
"type": "select",
"name": "Type",
"id": "type",
"value": "all",
"values": [
"all",
"core",
"external",
"pro"
]
}
] %}
{% include "card_filter.html" %}
<!-- end filter -->
<div data-global-config-plugins-container
class="col-span-12 gap-y-4 grid grid-cols-12">
<div data-global-config-settings
class="hidden"
data-value="{{ dumped_global_config }}"></div>
<!-- form global conf -->
<form data-global-config-form
id="form-edit-global-config"
method="post"
class="flex flex-col justify-between overflow-hidden overflow-y-auto col-span-12 break-words bg-white shadow-xl p-4 dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<!-- plugin item -->
{% include "settings_plugins.html" %}
<!-- end plugin item -->
<!-- submit -->
<div class="flex w-full justify-center mt-8 mb-2">
<button {% if is_readonly %}disabled{% endif %}
type="submit"
class="valid-btn">SAVE</button>
</div>
<!-- end submit -->
</form>
<!--end form global conf -->
{% include "filter_nomatch.html" %}
</div>
{% endblock content %}

View file

@ -6,9 +6,10 @@
<link rel="stylesheet" href="css/style.css" />
<link rel="stylesheet" href="css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | DASHBOARD</title>
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/home-BnrNYFnz.js"></script>
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Title-B_4IDnhH.js">
<title>BunkerWeb | Home</title>
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/home-DH_UDl3D.js"></script>
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Title-B1qYFCEw.js">
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-CEsjkeUs.js">
</head>
<body>

View file

@ -6,9 +6,10 @@
<link rel="stylesheet" href="css/style.css" />
<link rel="stylesheet" href="css/flag-icons.min.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BunkerWeb | DASHBOARD</title>
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/instances-DfV5PghU.js"></script>
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Title-B_4IDnhH.js">
<title>BunkerWeb | Instances</title>
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/instances-LtBmJEvG.js"></script>
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Title-B1qYFCEw.js">
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/form-BpBCT1YO.js">
</head>
<body>