mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Merge branch '1.6' of https://github.com/bunkerity/bunkerweb into 1.6
This commit is contained in:
commit
d721bde76e
23 changed files with 14509 additions and 105 deletions
|
|
@ -11,20 +11,11 @@ for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in ((
|
|||
from builder.utils.form import get_forms
|
||||
|
||||
|
||||
def global_config_builder(plugins: list, settings: dict) -> str:
|
||||
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).
|
||||
"""
|
||||
|
||||
templates = [
|
||||
{
|
||||
"name": "default",
|
||||
"steps": [],
|
||||
"configs": {},
|
||||
"settings": {},
|
||||
}
|
||||
]
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
|
|
|
|||
44
src/ui/builder/raw_mode.py
Normal file
44
src/ui/builder/raw_mode.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import base64
|
||||
import json
|
||||
from os.path import join, sep
|
||||
from sys import path as sys_path
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
|
||||
from builder.utils.form import get_forms, get_service_settings
|
||||
|
||||
|
||||
def raw_mode_builder(templates: list[dict], plugins: list, global_config: dict, total_config: dict, service_name: str) -> 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)
|
||||
print("settings", settings)
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {"title": "services_mode_title", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Subtitle",
|
||||
"data": {"subtitle": "services_mode_subtitle", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Templates",
|
||||
"data": {
|
||||
"templates": get_forms(templates, plugins, settings, ("raw",)),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
return base64.b64encode(bytes(json.dumps(builder), "utf-8")).decode("ascii")
|
||||
|
|
@ -2,6 +2,28 @@ import copy
|
|||
from typing import Union
|
||||
|
||||
|
||||
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_forms(templates: list = [], plugins: list = [], settings: dict = {}, render_forms: tuple = ("advanced", "easy", "raw")) -> dict:
|
||||
"""
|
||||
Will generate every needed form using templates, plugins and settings.
|
||||
|
|
@ -83,22 +105,24 @@ def set_raw(template: list, plugins_base: list, settings: dict) -> dict:
|
|||
# Update settings with global config data
|
||||
for plugin in plugins:
|
||||
for setting, value in plugin.get("settings").items():
|
||||
|
||||
# avoid some methods from services_settings
|
||||
if setting in settings and settings[setting].get("method", "ui") not in ("ui", "default", "manual"):
|
||||
continue
|
||||
|
||||
raw_value = None
|
||||
|
||||
# Start by setting template value if exists
|
||||
if setting in template_settings:
|
||||
# Update value or set default as value
|
||||
raw_value = template_settings.get(setting, value.get("default"))
|
||||
raw_value = template_settings.get(setting, None)
|
||||
|
||||
# Then override by service settings
|
||||
if setting in settings:
|
||||
|
||||
# Check if the service setting is not default value to add it
|
||||
default_val = value.get("default")
|
||||
val = settings[setting].get("value", value.get("value", value.get("default")))
|
||||
|
||||
if val != default_val:
|
||||
raw_value = val
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ def move_template(folder: Path, target_folder: Path):
|
|||
|
||||
for file in folder.rglob("index.html"):
|
||||
file_html = base_html
|
||||
if "global-config" in file.parts or "jobs" in file.parts or "services" in file.parts:
|
||||
if "global-config" in file.parts or "jobs" in file.parts or "services" in file.parts or "raw" in file.parts:
|
||||
file_html = base_html.replace("data_server_builder[1:-1]", "data_server_builder")
|
||||
|
||||
content = file.read_text()
|
||||
|
|
|
|||
|
|
@ -293,5 +293,7 @@
|
|||
"services_delete_subtitle": "Are you sure you want to delete the service below ?",
|
||||
"services_settings_table_title": "Get the activate setting state of main plugins for this service.",
|
||||
"services_settings_table_name": "Plugin",
|
||||
"services_settings_table_status": "Status"
|
||||
"services_settings_table_status": "Status",
|
||||
"services_mode_title": "Service mode",
|
||||
"services_mode_subtitle": "Manage your service settings."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,20 +4,11 @@ import json
|
|||
from .utils.form import get_forms
|
||||
|
||||
|
||||
def global_config_builder(plugins: list, settings: dict) -> str:
|
||||
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).
|
||||
"""
|
||||
|
||||
templates = [
|
||||
{
|
||||
"name": "default",
|
||||
"steps": [],
|
||||
"configs": {},
|
||||
"settings": {},
|
||||
}
|
||||
]
|
||||
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
|
|
@ -40,4 +31,4 @@ def global_config_builder(plugins: list, settings: dict) -> str:
|
|||
],
|
||||
}
|
||||
]
|
||||
return base64.b64encode(bytes(json.dumps(builder), "utf-8")).decode("ascii")
|
||||
return builder
|
||||
|
|
|
|||
37
src/ui/client/tests/builder/raw_mode.py
Normal file
37
src/ui/client/tests/builder/raw_mode.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import base64
|
||||
import json
|
||||
|
||||
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) -> 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)
|
||||
print("settings", settings)
|
||||
builder = [
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {"pc": 12, "tablet": 12, "mobile": 12},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {"title": "services_mode_title", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Subtitle",
|
||||
"data": {"subtitle": "services_mode_subtitle", "type": "container"},
|
||||
},
|
||||
{
|
||||
"type": "Templates",
|
||||
"data": {
|
||||
"templates": get_forms(templates, plugins, settings, ("raw",)),
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
]
|
||||
return builder
|
||||
|
|
@ -2,6 +2,28 @@ import copy
|
|||
from typing import Union
|
||||
|
||||
|
||||
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_forms(templates: list = [], plugins: list = [], settings: dict = {}, render_forms: tuple = ("advanced", "easy", "raw")) -> dict:
|
||||
"""
|
||||
Will generate every needed form using templates, plugins and settings.
|
||||
|
|
@ -83,22 +105,24 @@ def set_raw(template: list, plugins_base: list, settings: dict) -> dict:
|
|||
# Update settings with global config data
|
||||
for plugin in plugins:
|
||||
for setting, value in plugin.get("settings").items():
|
||||
|
||||
# avoid some methods from services_settings
|
||||
if setting in settings and settings[setting].get("method", "ui") not in ("ui", "default", "manual"):
|
||||
continue
|
||||
|
||||
raw_value = None
|
||||
|
||||
# Start by setting template value if exists
|
||||
if setting in template_settings:
|
||||
# Update value or set default as value
|
||||
raw_value = template_settings.get(setting, value.get("default"))
|
||||
raw_value = template_settings.get(setting, None)
|
||||
|
||||
# Then override by service settings
|
||||
if setting in settings:
|
||||
|
||||
# Check if the service setting is not default value to add it
|
||||
default_val = value.get("default")
|
||||
val = settings[setting].get("value", value.get("value", value.get("default")))
|
||||
|
||||
if val != default_val:
|
||||
raw_value = val
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -3324,8 +3324,16 @@ global_config = {
|
|||
"BUNKERWEB_INSTANCES": {"value": "bunkerweb", "global": True, "method": "scheduler"},
|
||||
}
|
||||
|
||||
templates = [
|
||||
{
|
||||
"name": "default",
|
||||
"steps": [],
|
||||
"configs": {},
|
||||
"settings": {},
|
||||
}
|
||||
]
|
||||
|
||||
output = global_config_builder(plugins, global_config)
|
||||
output = global_config_builder(templates, plugins, global_config)
|
||||
with open("globalconfig.json", "w") as f:
|
||||
json.dump(output, f, indent=4)
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
47
src/ui/client/tests/raw.json
Normal file
47
src/ui/client/tests/raw.json
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
[
|
||||
{
|
||||
"type": "card",
|
||||
"containerColumns": {
|
||||
"pc": 12,
|
||||
"tablet": 12,
|
||||
"mobile": 12
|
||||
},
|
||||
"widgets": [
|
||||
{
|
||||
"type": "Title",
|
||||
"data": {
|
||||
"title": "services_mode_title",
|
||||
"type": "container"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Subtitle",
|
||||
"data": {
|
||||
"subtitle": "services_mode_subtitle",
|
||||
"type": "container"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Templates",
|
||||
"data": {
|
||||
"templates": {
|
||||
"raw": {
|
||||
"default": {
|
||||
"SERVER_NAME": "app1.example.com",
|
||||
"CORS_ALLOW_ORIGIN": "self",
|
||||
"CROSS_ORIGIN_OPENER_POLICY": "same-origin",
|
||||
"CROSS_ORIGIN_EMBEDDER_POLICY": "require-corp",
|
||||
"CROSS_ORIGIN_RESOURCE_POLICY": "same-site",
|
||||
"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",
|
||||
"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=()",
|
||||
"MODSECURITY_CRS_VERSION": "4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
4119
src/ui/client/tests/raw.py
Normal file
4119
src/ui/client/tests/raw.py
Normal file
File diff suppressed because it is too large
Load diff
1
src/ui/client/tests/raw.txt
Normal file
1
src/ui/client/tests/raw.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
W3sidHlwZSI6ICJjYXJkIiwgImNvbnRhaW5lckNvbHVtbnMiOiB7InBjIjogMTIsICJ0YWJsZXQiOiAxMiwgIm1vYmlsZSI6IDEyfSwgIndpZGdldHMiOiBbeyJ0eXBlIjogIlRpdGxlIiwgImRhdGEiOiB7InRpdGxlIjogImdsb2JhbF9jb25maWdfdGl0bGUiLCAidHlwZSI6ICJjb250YWluZXIifX0sIHsidHlwZSI6ICJTdWJ0aXRsZSIsICJkYXRhIjogeyJzdWJ0aXRsZSI6ICJnbG9iYWxfY29uZmlnX3N1YnRpdGxlIiwgInR5cGUiOiAiY29udGFpbmVyIn19LCB7InR5cGUiOiAiVGVtcGxhdGVzIiwgImRhdGEiOiB7InRlbXBsYXRlcyI6IHsicmF3IjogeyJkZWZhdWx0IjogeyJTRVJWRVJfTkFNRSI6ICJhcHAxLmV4YW1wbGUuY29tIiwgIkNPUlNfQUxMT1dfT1JJR0lOIjogInNlbGYiLCAiQ1JPU1NfT1JJR0lOX09QRU5FUl9QT0xJQ1kiOiAic2FtZS1vcmlnaW4iLCAiQ1JPU1NfT1JJR0lOX0VNQkVEREVSX1BPTElDWSI6ICJyZXF1aXJlLWNvcnAiLCAiQ1JPU1NfT1JJR0lOX1JFU09VUkNFX1BPTElDWSI6ICJzYW1lLXNpdGUiLCAiUkVNT1ZFX0hFQURFUlMiOiAiU2VydmVyIEV4cGVjdC1DVCBYLVBvd2VyZWQtQnkgWC1Bc3BOZXQtVmVyc2lvbiBYLUFzcE5ldE12Yy1WZXJzaW9uIFB1YmxpYy1LZXktUGlucyIsICJLRUVQX1VQU1RSRUFNX0hFQURFUlMiOiAiQ29udGVudC1TZWN1cml0eS1Qb2xpY3kgUGVybWlzc2lvbnMtUG9saWN5IFgtRnJhbWUtT3B0aW9ucyIsICJTVFJJQ1RfVFJBTlNQT1JUX1NFQ1VSSVRZIjogIm1heC1hZ2U9MzE1MzYwMDA7IGluY2x1ZGVTdWJEb21haW5zOyBwcmVsb2FkIiwgIlBFUk1JU1NJT05TX1BPTElDWSI6ICJhY2NlbGVyb21ldGVyPSgpLCBhbWJpZW50LWxpZ2h0LXNlbnNvcj0oKSwgYXR0cmlidXRpb24tcmVwb3J0aW5nPSgpLCBhdXRvcGxheT0oKSwgYmF0dGVyeT0oKSwgYmx1ZXRvb3RoPSgpLCBicm93c2luZy10b3BpY3M9KCksIGNhbWVyYT0oKSwgY29tcHV0ZS1wcmVzc3VyZT0oKSwgZGlzcGxheS1jYXB0dXJlPSgpLCBkb2N1bWVudC1kb21haW49KCksIGVuY3J5cHRlZC1tZWRpYT0oKSwgZXhlY3V0aW9uLXdoaWxlLW5vdC1yZW5kZXJlZD0oKSwgZXhlY3V0aW9uLXdoaWxlLW91dC1vZi12aWV3cG9ydD0oKSwgZnVsbHNjcmVlbj0oKSwgZ2FtZXBhZD0oKSwgZ2VvbG9jYXRpb249KCksIGd5cm9zY29wZT0oKSwgaGlkPSgpLCBpZGVudGl0eS1jcmVkZW50aWFscy1nZXQ9KCksIGlkbGUtZGV0ZWN0aW9uPSgpLCBsb2NhbC1mb250cz0oKSwgbWFnbmV0b21ldGVyPSgpLCBtaWNyb3Bob25lPSgpLCBtaWRpPSgpLCBvdHAtY3JlZGVudGlhbHM9KCksIHBheW1lbnQ9KCksIHBpY3R1cmUtaW4tcGljdHVyZT0oKSwgcHVibGlja2V5LWNyZWRlbnRpYWxzLWNyZWF0ZT0oKSwgcHVibGlja2V5LWNyZWRlbnRpYWxzLWdldD0oKSwgc2NyZWVuLXdha2UtbG9jaz0oKSwgc2VyaWFsPSgpLCBzcGVha2VyLXNlbGVjdGlvbj0oKSwgc3RvcmFnZS1hY2Nlc3M9KCksIHVzYj0oKSwgd2ViLXNoYXJlPSgpLCB3aW5kb3ctbWFuYWdlbWVudD0oKSwgeHItc3BhdGlhbC10cmFja2luZz0oKSIsICJNT0RTRUNVUklUWV9DUlNfVkVSU0lPTiI6ICI0In19fX19XX1d
|
||||
1
src/ui/client/tests/raw64.txt
Normal file
1
src/ui/client/tests/raw64.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
W3sidHlwZSI6ICJjYXJkIiwgImNvbnRhaW5lckNvbHVtbnMiOiB7InBjIjogMTIsICJ0YWJsZXQiOiAxMiwgIm1vYmlsZSI6IDEyfSwgIndpZGdldHMiOiBbeyJ0eXBlIjogIlRpdGxlIiwgImRhdGEiOiB7InRpdGxlIjogImdsb2JhbF9jb25maWdfdGl0bGUiLCAidHlwZSI6ICJjb250YWluZXIifX0sIHsidHlwZSI6ICJTdWJ0aXRsZSIsICJkYXRhIjogeyJzdWJ0aXRsZSI6ICJnbG9iYWxfY29uZmlnX3N1YnRpdGxlIiwgInR5cGUiOiAiY29udGFpbmVyIn19LCB7InR5cGUiOiAiVGVtcGxhdGVzIiwgImRhdGEiOiB7InRlbXBsYXRlcyI6IHsiciI6IHt9LCAiYSI6IHt9LCAidyI6IHt9fX19XX1d
|
||||
|
|
@ -51,6 +51,7 @@ export default defineConfig({
|
|||
),
|
||||
jobs: resolve(__dirname, "./dashboard/pages/jobs/index.html"),
|
||||
services: resolve(__dirname, "./dashboard/pages/services/index.html"),
|
||||
raw: resolve(__dirname, "./dashboard/pages/raw/index.html"),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
165
src/ui/main.py
165
src/ui/main.py
|
|
@ -57,6 +57,7 @@ from builder.instances import instances_builder
|
|||
from builder.global_config import global_config_builder
|
||||
from builder.jobs import jobs_builder
|
||||
from builder.services import services_builder
|
||||
from builder.raw_mode import raw_mode_builder
|
||||
|
||||
from common_utils import get_version # type: ignore
|
||||
from logger import setup_logger # type: ignore
|
||||
|
|
@ -98,6 +99,17 @@ signal(SIGTERM, handle_stop)
|
|||
|
||||
sbin_nginx_path = Path(sep, "usr", "sbin", "nginx")
|
||||
|
||||
|
||||
TEMPLATE_PLACEHOLDER = [
|
||||
{
|
||||
"name": "default",
|
||||
"steps": [],
|
||||
"configs": {},
|
||||
"settings": {},
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
# Flask app
|
||||
app = Flask(__name__, static_url_path="/", static_folder="static", template_folder="templates")
|
||||
|
||||
|
|
@ -1191,55 +1203,41 @@ def get_service_data():
|
|||
return config, variables, format_configs, server_name, old_server_name, operation, is_draft, was_draft, is_draft_unchanged
|
||||
|
||||
|
||||
@app.route("/services", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def services():
|
||||
if request.method == "POST":
|
||||
if DB.readonly:
|
||||
return handle_error("Database is in read-only mode", "services")
|
||||
def update_service(config, variables, format_configs, server_name, old_server_name, operation, is_draft, was_draft, is_draft_unchanged, redirect_name):
|
||||
if request.form["operation"] == "edit":
|
||||
if is_draft_unchanged and len(variables) == 1 and "SERVER_NAME" in variables and server_name == old_server_name:
|
||||
return handle_error("The service was not edited because no values were changed.", "services", True)
|
||||
|
||||
verify_data_in_form(
|
||||
data={"operation": ("edit", "new", "delete")},
|
||||
err_message="Invalid operation parameter on /services.",
|
||||
redirect_url="services",
|
||||
if request.form["operation"] == "new" and not variables:
|
||||
return handle_error("The service was not created because all values had the default value.", "services", True)
|
||||
|
||||
# Delete
|
||||
if request.form["operation"] == "delete":
|
||||
|
||||
is_service = app.bw_config.check_variables({"SERVER_NAME": request.form["SERVER_NAME"]}, config)
|
||||
|
||||
if not is_service:
|
||||
error_message(f"Error while deleting the service {request.form['SERVER_NAME']}")
|
||||
|
||||
if config.get(f"{request.form['SERVER_NAME'].split(' ')[0]}_SERVER_NAME", {"method": "scheduler"})["method"] != "ui":
|
||||
return handle_error("The service cannot be deleted because it has not been created with the UI.", "services", True)
|
||||
|
||||
db_metadata = DB.get_metadata()
|
||||
|
||||
def update_services(threaded: bool = False):
|
||||
wait_applying()
|
||||
|
||||
manage_bunkerweb(
|
||||
"services",
|
||||
variables,
|
||||
old_server_name,
|
||||
variables.get("SERVER_NAME", ""),
|
||||
operation=operation,
|
||||
is_draft=is_draft,
|
||||
was_draft=was_draft,
|
||||
threaded=threaded,
|
||||
)
|
||||
|
||||
config, variables, format_configs, server_name, old_server_name, operation, is_draft, was_draft, is_draft_unchanged = get_service_data()
|
||||
|
||||
if request.form["operation"] == "edit":
|
||||
if is_draft_unchanged and len(variables) == 1 and "SERVER_NAME" in variables and server_name == old_server_name:
|
||||
return handle_error("The service was not edited because no values were changed.", "services", True)
|
||||
|
||||
if request.form["operation"] == "new" and not variables:
|
||||
return handle_error("The service was not created because all values had the default value.", "services", True)
|
||||
|
||||
# Delete
|
||||
if request.form["operation"] == "delete":
|
||||
|
||||
is_service = app.bw_config.check_variables({"SERVER_NAME": request.form["SERVER_NAME"]}, config)
|
||||
|
||||
if not is_service:
|
||||
error_message(f"Error while deleting the service {request.form['SERVER_NAME']}")
|
||||
|
||||
if config.get(f"{request.form['SERVER_NAME'].split(' ')[0]}_SERVER_NAME", {"method": "scheduler"})["method"] != "ui":
|
||||
return handle_error("The service cannot be deleted because it has not been created with the UI.", "services", True)
|
||||
|
||||
db_metadata = DB.get_metadata()
|
||||
|
||||
def update_services(threaded: bool = False):
|
||||
wait_applying()
|
||||
|
||||
manage_bunkerweb(
|
||||
"services",
|
||||
variables,
|
||||
old_server_name,
|
||||
variables.get("SERVER_NAME", ""),
|
||||
operation=operation,
|
||||
is_draft=is_draft,
|
||||
was_draft=was_draft,
|
||||
threaded=threaded,
|
||||
)
|
||||
|
||||
if any(
|
||||
v
|
||||
for k, v in db_metadata.items()
|
||||
|
|
@ -1253,16 +1251,69 @@ def services():
|
|||
|
||||
app.data["CONFIG_CHANGED"] = True
|
||||
|
||||
message = ""
|
||||
message = ""
|
||||
|
||||
if request.form["operation"] == "new":
|
||||
message = f"Creating {'draft ' if is_draft else ''}service {variables.get('SERVER_NAME', '').split(' ')[0]}"
|
||||
elif request.form["operation"] == "edit":
|
||||
message = f"Saving configuration for {'draft ' if is_draft else ''}service {old_server_name.split(' ')[0]}"
|
||||
elif request.form["operation"] == "delete":
|
||||
message = f"Deleting {'draft ' if was_draft and is_draft else ''}service {request.form.get('SERVER_NAME', '').split(' ')[0]}"
|
||||
if request.form["operation"] == "new":
|
||||
message = f"Creating {'draft ' if is_draft else ''}service {variables.get('SERVER_NAME', '').split(' ')[0]}"
|
||||
elif request.form["operation"] == "edit":
|
||||
message = f"Saving configuration for {'draft ' if is_draft else ''}service {old_server_name.split(' ')[0]}"
|
||||
elif request.form["operation"] == "delete":
|
||||
message = f"Deleting {'draft ' if was_draft and is_draft else ''}service {request.form.get('SERVER_NAME', '').split(' ')[0]}"
|
||||
|
||||
return redirect(url_for("loading", next=url_for("services"), message=message))
|
||||
return redirect(url_for("loading", next=url_for(redirect_name, service_name=[server_name]), message=message))
|
||||
|
||||
|
||||
@app.route("/raw-mode", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def services_raw():
|
||||
if request.method == "POST":
|
||||
if DB.readonly:
|
||||
return handle_error("Database is in read-only mode", "services")
|
||||
|
||||
verify_data_in_form(
|
||||
data={"operation": ("edit", "new", "delete")},
|
||||
err_message="Invalid operation parameter on /services.",
|
||||
redirect_url="services",
|
||||
)
|
||||
|
||||
config, variables, format_configs, server_name, old_server_name, operation, is_draft, was_draft, is_draft_unchanged = get_service_data()
|
||||
update_service(config, variables, format_configs, server_name, old_server_name, operation, is_draft, was_draft, is_draft_unchanged, "raw-mode")
|
||||
|
||||
if not request.args.get("service_name"):
|
||||
return handle_error("Service name missing to access raw mode.", "services")
|
||||
|
||||
service_name = request.args.get("service_name")
|
||||
total_config = DB.get_config(methods=True, with_drafts=True)
|
||||
service_names = total_config["SERVER_NAME"]["value"].split(" ")
|
||||
# Case new service
|
||||
service_names.append("new")
|
||||
|
||||
if service_name not in service_names:
|
||||
return handle_error("Service name not found to access raw mode.", "services")
|
||||
|
||||
global_config = app.bw_config.get_config(global_only=True, methods=True)
|
||||
plugins = app.bw_config.get_plugins()
|
||||
|
||||
data_server_builder = raw_mode_builder(TEMPLATE_PLACEHOLDER, plugins, global_config, total_config, service_name)
|
||||
|
||||
return render_template("raw.html", data_server_builder=data_server_builder)
|
||||
|
||||
|
||||
@app.route("/services", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def services():
|
||||
if request.method == "POST":
|
||||
if DB.readonly:
|
||||
return handle_error("Database is in read-only mode", "services")
|
||||
|
||||
verify_data_in_form(
|
||||
data={"operation": ("edit", "new", "delete")},
|
||||
err_message="Invalid operation parameter on /services.",
|
||||
redirect_url="services",
|
||||
)
|
||||
|
||||
config, variables, format_configs, server_name, old_server_name, operation, is_draft, was_draft, is_draft_unchanged = get_service_data()
|
||||
update_service(config, variables, format_configs, server_name, old_server_name, operation, is_draft, was_draft, is_draft_unchanged, "services")
|
||||
|
||||
# Display services
|
||||
services = []
|
||||
|
|
@ -1304,7 +1355,7 @@ def services():
|
|||
|
||||
@app.route("/services/raw/{service_name}", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def services_raw(service_name: str):
|
||||
def services_raw(service_name: str): # noqa: F811
|
||||
if request.method == "POST":
|
||||
if DB.readonly:
|
||||
return handle_error("Database is in read-only mode", "services")
|
||||
|
|
@ -1490,9 +1541,9 @@ def global_config():
|
|||
)
|
||||
)
|
||||
|
||||
global_config = DB.get_config(global_only=True, methods=True)
|
||||
global_config = app.bw_config.get_config(global_only=True, methods=True)
|
||||
plugins = app.bw_config.get_plugins()
|
||||
data_server_builder = global_config_builder(plugins, global_config)
|
||||
data_server_builder = global_config_builder(TEMPLATE_PLACEHOLDER, plugins, global_config)
|
||||
return render_template("global-config.html", data_server_builder=data_server_builder)
|
||||
|
||||
|
||||
|
|
|
|||
11
src/ui/templates/global-config.html
vendored
11
src/ui/templates/global-config.html
vendored
|
|
@ -7,10 +7,13 @@
|
|||
<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-_p4bx2iA.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-BvrU_MzZ.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/ButtonGroup-Bmy1qIwo.js">
|
||||
<link rel="stylesheet" crossorigin href="assets/ButtonGroup-D2kv0NCW.css">
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/global_config-CYnbkMMn.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Layout-RaTf2L6T.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-xpxpyVZa.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/theme-dawn-Bo1pPESL.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/ButtonGroup-CClxClGv.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/form-j3jOuvnO.js">
|
||||
<link rel="stylesheet" crossorigin href="assets/theme-dawn-D2kv0NCW.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
|||
5
src/ui/templates/home.html
vendored
5
src/ui/templates/home.html
vendored
|
|
@ -7,8 +7,9 @@
|
|||
<link rel="stylesheet" href="css/flag-icons.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BunkerWeb | Home</title>
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/home-C5vSVPv_.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-BvrU_MzZ.js">
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/home-C2BYy_dW.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Layout-RaTf2L6T.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-xpxpyVZa.js">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
|||
10
src/ui/templates/instances.html
vendored
10
src/ui/templates/instances.html
vendored
|
|
@ -7,10 +7,12 @@
|
|||
<link rel="stylesheet" href="css/flag-icons.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BunkerWeb | Instances</title>
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/instances-DlYdXYfk.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-BvrU_MzZ.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/ButtonGroup-Bmy1qIwo.js">
|
||||
<link rel="stylesheet" crossorigin href="assets/ButtonGroup-D2kv0NCW.css">
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/instances-W4YarpdG.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Layout-RaTf2L6T.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-xpxpyVZa.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/theme-dawn-Bo1pPESL.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/ButtonGroup-CClxClGv.js">
|
||||
<link rel="stylesheet" crossorigin href="assets/theme-dawn-D2kv0NCW.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
|||
10
src/ui/templates/jobs.html
vendored
10
src/ui/templates/jobs.html
vendored
|
|
@ -7,10 +7,12 @@
|
|||
<link rel="stylesheet" href="css/flag-icons.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BunkerWeb | Jobs</title>
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/jobs-DsETM5wn.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-BvrU_MzZ.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/ButtonGroup-Bmy1qIwo.js">
|
||||
<link rel="stylesheet" crossorigin href="assets/ButtonGroup-D2kv0NCW.css">
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/jobs-DR0hphyk.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Layout-RaTf2L6T.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-xpxpyVZa.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/theme-dawn-Bo1pPESL.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/ButtonGroup-CClxClGv.js">
|
||||
<link rel="stylesheet" crossorigin href="assets/theme-dawn-D2kv0NCW.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
|||
30
src/ui/templates/raw.html
vendored
Normal file
30
src/ui/templates/raw.html
vendored
Normal 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 | Raw mode</title>
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/raw-SXGFOvXL.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Layout-RaTf2L6T.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/theme-dawn-Bo1pPESL.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/form-j3jOuvnO.js">
|
||||
<link rel="stylesheet" crossorigin href="assets/theme-dawn-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>
|
||||
10
src/ui/templates/services.html
vendored
10
src/ui/templates/services.html
vendored
|
|
@ -7,10 +7,12 @@
|
|||
<link rel="stylesheet" href="css/flag-icons.min.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>BunkerWeb | Services</title>
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/services-CSE-2AMK.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-BvrU_MzZ.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/ButtonGroup-Bmy1qIwo.js">
|
||||
<link rel="stylesheet" crossorigin href="assets/ButtonGroup-D2kv0NCW.css">
|
||||
<script type="module" crossorigin nonce="{{ script_nonce }}" src="assets/services-B2ZXRax3.js"></script>
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Layout-RaTf2L6T.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/Text-xpxpyVZa.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/theme-dawn-Bo1pPESL.js">
|
||||
<link rel="modulepreload" crossorigin nonce="{{ script_nonce }}" href="assets/ButtonGroup-CClxClGv.js">
|
||||
<link rel="stylesheet" crossorigin href="assets/theme-dawn-D2kv0NCW.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
|||
Loading…
Reference in a new issue