Skip to content

Commit

Permalink
enhance builder + pages + simplify raw mode
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
syrk4web committed Jul 23, 2024
1 parent d1cafdc commit ed9b9ba
Show file tree
Hide file tree
Showing 17 changed files with 205 additions and 390 deletions.
79 changes: 47 additions & 32 deletions src/ui/builder.py
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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",
Expand All @@ -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
"""
Expand Down Expand Up @@ -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
"""
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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:
Expand All @@ -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.
Expand Down Expand Up @@ -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.
"""
Expand Down Expand Up @@ -452,21 +468,21 @@ 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

return format_plugins


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.
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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).
"""
Expand Down Expand Up @@ -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")
11 changes: 7 additions & 4 deletions src/ui/client/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -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) %}
Expand All @@ -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):
Expand Down Expand Up @@ -115,7 +118,7 @@ def format_template(m):
# 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):
Expand Down
69 changes: 36 additions & 33 deletions src/ui/client/dashboard/components/Form/Raw.vue
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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, "'");
Expand Down Expand Up @@ -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,
Expand All @@ -127,6 +126,10 @@ const buttonSave = {
size: "normal",
containerClass: "flex justify-center",
};
onBeforeMount(() => {
data.str = json2raw(props.template);
});
</script>

<template>
Expand All @@ -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" />

Expand Down
2 changes: 1 addition & 1 deletion src/ui/client/dashboard/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
7 changes: 6 additions & 1 deletion src/ui/client/dashboard/pages/global-config/globalConfig.vue
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -23,6 +24,10 @@ onBeforeMount(() => {
: {};
globalConfig.builder = data;
});
onMounted(() => {
useGlobal();
});
</script>

<template>
Expand Down
4 changes: 2 additions & 2 deletions src/ui/client/dashboard/pages/global-config/index.html

Large diffs are not rendered by default.

16 changes: 9 additions & 7 deletions src/ui/client/dashboard/pages/home/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading

0 comments on commit ed9b9ba

Please sign in to comment.