Tweak a few things in the web UI

This commit is contained in:
Théophile Diot 2024-10-21 11:26:51 +02:00
parent e7cc450af9
commit e57f825ba5
No known key found for this signature in database
GPG key ID: FA995104A0BA376A
23 changed files with 282 additions and 261 deletions

View file

@ -6,7 +6,7 @@ from sqlalchemy.schema import UniqueConstraint
CONTEXTS_ENUM = Enum("global", "multisite", name="contexts_enum")
SETTINGS_TYPES_ENUM = Enum("password", "text", "check", "select", name="settings_types_enum")
METHODS_ENUM = Enum("ui", "scheduler", "autoconf", "manual", name="methods_enum")
METHODS_ENUM = Enum("ui", "scheduler", "autoconf", "manual", "wizard", name="methods_enum")
SCHEDULES_ENUM = Enum("once", "minute", "hour", "day", "week", name="schedules_enum")
CUSTOM_CONFIGS_TYPES_ENUM = Enum(
"http",

View file

@ -20,7 +20,7 @@ class Config:
self.__data = data
def gen_conf(
self, global_conf: dict, services_conf: list[dict], *, check_changes: bool = True, changed_service: Optional[str] = None
self, global_conf: dict, services_conf: list[dict], *, check_changes: bool = True, changed_service: Optional[str] = None, override_method: str = "ui"
) -> Union[str, Set[str]]:
"""Generates the nginx configuration file from the given configuration
@ -61,7 +61,7 @@ class Config:
conf["SERVER_NAME"] = " ".join(servers)
conf["DATABASE_URI"] = self.__db.database_uri
return self.__db.save_config(conf, "ui", changed=check_changes)
return self.__db.save_config(conf, override_method, changed=check_changes)
def get_plugins_settings(self) -> dict:
return {
@ -205,7 +205,7 @@ class Config:
return variables
def new_service(self, variables: dict, is_draft: bool = False) -> Tuple[str, int]:
def new_service(self, variables: dict, is_draft: bool = False, override_method: str = "ui") -> Tuple[str, int]:
"""Creates a new service from the given variables
Parameters
@ -230,12 +230,14 @@ class Config:
return f"Service {service['SERVER_NAME'].split(' ')[0]} already exists.", 1
services.append(variables | {"IS_DRAFT": "yes" if is_draft else "no"})
ret = self.gen_conf(self.get_config(methods=False), services, check_changes=not is_draft)
ret = self.gen_conf(self.get_config(methods=False), services, check_changes=not is_draft, override_method=override_method)
if isinstance(ret, str):
return ret, 1
return f"Configuration for {variables['SERVER_NAME'].split(' ')[0]} has been generated.", 0
def edit_service(self, old_server_name: str, variables: dict, *, check_changes: bool = True, is_draft: bool = False) -> Tuple[str, int]:
def edit_service(
self, old_server_name: str, variables: dict, *, check_changes: bool = True, is_draft: bool = False, override_method: str = "ui"
) -> Tuple[str, int]:
"""Edits a service
Parameters
@ -270,12 +272,12 @@ class Config:
if k.startswith(old_server_name_splitted[0]):
config.pop(k)
ret = self.gen_conf(config, services, check_changes=check_changes, changed_service=server_name_splitted[0])
ret = self.gen_conf(config, services, check_changes=check_changes, changed_service=server_name_splitted[0], override_method=override_method)
if isinstance(ret, str):
return ret, 1
return f"Configuration for {old_server_name_splitted[0]} has been edited.", 0
def edit_global_conf(self, variables: dict, *, check_changes: bool = True) -> Tuple[str, int]:
def edit_global_conf(self, variables: dict, *, check_changes: bool = True, override_method: str = "ui") -> Tuple[str, int]:
"""Edits the global conf
Parameters
@ -288,12 +290,14 @@ class Config:
str
the confirmation message
"""
ret = self.gen_conf(self.get_config(methods=False) | variables, self.get_services(methods=False), check_changes=check_changes)
ret = self.gen_conf(
self.get_config(methods=False) | variables, self.get_services(methods=False), check_changes=check_changes, override_method=override_method
)
if isinstance(ret, str):
return ret, 1
return "The global configuration has been edited.", 0
def delete_service(self, service_name: str, *, check_changes: bool = True) -> Tuple[str, int]:
def delete_service(self, service_name: str, *, check_changes: bool = True, override_method: str = "ui") -> Tuple[str, int]:
"""Deletes a service
Parameters
@ -338,7 +342,7 @@ class Config:
if k in service:
service.pop(k)
ret = self.gen_conf(new_env, new_services, check_changes=check_changes)
ret = self.gen_conf(new_env, new_services, check_changes=check_changes, override_method=override_method)
if isinstance(ret, str):
return ret, 1
return f"Configuration for {service_name} has been deleted.", 0

View file

@ -312,20 +312,25 @@ def configs_edit(service: str, config_type: str, name: str):
config_value = request.form["value"].replace("\r\n", "\n").strip()
DATA.load_from_file()
if config_value == db_config["data"].decode("utf-8").strip():
if (
db_config["type"] == new_type
and db_config["name"] == new_name
and db_config["service_id"] == new_service
and db_config["data"].decode("utf-8") == config_value
):
return handle_error("No values were changed.", "configs", True)
error = DB.upsert_custom_config(
config_type,
name,
{
"service_id": service,
"service_id": new_service,
"type": new_type,
"name": new_name,
"data": config_value,
"method": "ui",
},
service_id=new_service,
service_id=service,
)
if error:
flash(f"An error occurred while saving the custom configs: {error}", "error")
@ -335,7 +340,7 @@ def configs_edit(service: str, config_type: str, name: str):
return redirect(
url_for(
"configs.configs_edit",
service=new_service,
service=new_service or "global",
config_type=new_type,
name=new_name,
)

View file

@ -268,7 +268,7 @@ def services_service_page(service: str):
if mode != "easy" or service != "new":
# Remove already existing fields
for variable, value in variables.copy().items():
if (mode != "advanced" or variable != "SERVER_NAME") and value == db_config.get(variable, {"value": None})["value"]:
if (mode == "advanced" or variable != "SERVER_NAME") and value == db_config.get(variable, {"value": None})["value"]:
if match(r"^.+_\d+$", variable):
ignored_multiples.add(variable)
del variables[variable]

View file

@ -130,7 +130,7 @@ def setup_page():
target=manage_bunkerweb,
name="Reloading instances",
args=("services", config, request.form["server_name"], request.form["server_name"]),
kwargs={"operation": "new", "threaded": True},
kwargs={"operation": "new", "threaded": True, "override_method": "wizard"},
).start()
return Response(status=200)

View file

@ -49,20 +49,24 @@ def wait_applying():
# TODO: Find a more elegant way to handle this
def manage_bunkerweb(method: str, *args, operation: str = "reloads", is_draft: bool = False, was_draft: bool = False, threaded: bool = False) -> int:
def manage_bunkerweb(
method: str, *args, operation: str = "reloads", is_draft: bool = False, was_draft: bool = False, threaded: bool = False, override_method: str = "ui"
) -> int:
# Do the operation
error = 0
DATA.load_from_file()
if method == "services":
if operation == "new":
operation, error = BW_CONFIG.new_service(args[0], is_draft=is_draft)
operation, error = BW_CONFIG.new_service(args[0], is_draft=is_draft, override_method=override_method)
elif operation == "edit":
operation, error = BW_CONFIG.edit_service(args[1], args[0], check_changes=(was_draft != is_draft or not is_draft), is_draft=is_draft)
operation, error = BW_CONFIG.edit_service(
args[1], args[0], check_changes=(was_draft != is_draft or not is_draft), is_draft=is_draft, override_method=override_method
)
elif operation == "delete":
operation, error = BW_CONFIG.delete_service(args[2], check_changes=(was_draft != is_draft or not is_draft))
operation, error = BW_CONFIG.delete_service(args[2], check_changes=(was_draft != is_draft or not is_draft), override_method=override_method)
elif method == "global_config":
operation, error = BW_CONFIG.edit_global_conf(args[0], check_changes=True)
operation, error = BW_CONFIG.edit_global_conf(args[0], check_changes=True, override_method=override_method)
if operation == "reload":
instance = Instance.from_hostname(args[0], DB)

View file

@ -201,11 +201,9 @@ $(document).ready(function () {
buttons: [
{
extend: "copy",
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy current page',
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy visible',
exportOptions: {
modifier: {
page: "current",
},
columns: ":visible:not(:first-child):not(:last-child)",
},
},
{
@ -217,6 +215,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:first-child):not(:last-child)",
},
},
{
@ -227,6 +226,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:first-child):not(:last-child)",
},
},
],

View file

@ -75,11 +75,9 @@ $(document).ready(function () {
buttons: [
{
extend: "copy",
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy current page',
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy visible',
exportOptions: {
modifier: {
page: "current",
},
columns: ":visible:not(:first-child):not(:last-child)",
},
},
{
@ -91,6 +89,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:last-child)",
},
},
{
@ -101,6 +100,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:last-child)",
},
},
],

View file

@ -129,12 +129,13 @@ $(document).ready(function () {
}, 2000);
firstMultisiteType.find("button").tab("show");
selectedType = firstMultisiteType.text().trim();
}
});
$typeDropdownItems.on("click", function () {
editor.session.setMode("ace/mode/nginx");
// selectedType = $(this).text().trim();
selectedType = $(this).text().trim();
// if (selectedType.startsWith("CRS") || selectedType.startsWith("MODSEC")) {
// editor.session.setMode("ace/mode/text"); // TODO: Support ModSecurity
// } else {

View file

@ -175,11 +175,9 @@ $(document).ready(function () {
buttons: [
{
extend: "copy",
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy current page',
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy visible',
exportOptions: {
modifier: {
page: "current",
},
columns: ":visible:not(:first-child):not(:last-child)",
},
},
{
@ -191,6 +189,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:first-child):not(:last-child)",
},
},
{
@ -201,6 +200,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:first-child):not(:last-child)",
},
},
],

View file

@ -193,11 +193,9 @@ $(document).ready(function () {
buttons: [
{
extend: "copy",
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy current page',
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy visible',
exportOptions: {
modifier: {
page: "current",
},
columns: ":visible:not(:first-child):not(:last-child)",
},
},
{
@ -209,6 +207,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:first-child):not(:last-child)",
},
},
{
@ -219,6 +218,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:first-child):not(:last-child)",
},
},
],

View file

@ -55,11 +55,9 @@ $(document).ready(function () {
buttons: [
{
extend: "copy",
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy current page',
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy visible',
exportOptions: {
modifier: {
page: "current",
},
columns: ":visible:not(:first-child):not(:last-child)",
},
},
{
@ -71,6 +69,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:first-child):not(:last-child)",
},
},
{
@ -81,6 +80,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:first-child):not(:last-child)",
},
},
],

View file

@ -199,16 +199,6 @@ $(document).ready(function () {
text: '<span class="tf-icons bx bx-reset bx-18px me-2"></span>Reset<span class="d-none d-md-inline"> columns</span>',
className: "btn btn-sm btn-outline-primary",
},
// {
// extend: "searchPanes",
// text: '<span class="tf-icons bx bx-search bx-18px me-2"></span>Filters',
// className: "btn btn-sm btn-outline-primary",
// config: {
// viewTotal: true,
// cascadePanes: true,
// columns: [5, 6],
// },
// },
{
extend: "collection",
text: '<span class="tf-icons bx bx-export bx-18px me-2"></span>Export',
@ -216,11 +206,9 @@ $(document).ready(function () {
buttons: [
{
extend: "copy",
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy current page',
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy visible',
exportOptions: {
modifier: {
page: "current",
},
columns: ":visible:not(:first-child):not(:last-child)",
},
},
{
@ -232,6 +220,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:first-child):not(:last-child)",
},
},
{
@ -242,6 +231,7 @@ $(document).ready(function () {
modifier: {
search: "none",
},
columns: ":not(:first-child):not(:last-child)",
},
},
],
@ -366,8 +356,9 @@ $(document).ready(function () {
show: true,
options: [
{
label:
'<img src="/admin/img/diamond.svg" alt="Pro plugin" width="16px" height="12.9125px" class="mb-1">&nbsp;PRO',
label: `<img src="${$("#pro_diamond_url")
.val()
.trim()}" alt="Pro plugin" width="16px" height="12.9125px" class="mb-1">&nbsp;PRO`,
value: function (rowData, rowIdx) {
return rowData[6].includes("PRO");
},

View file

@ -334,9 +334,9 @@ $(function () {
buttons: [
{
extend: "copy",
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy current page',
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy visible',
exportOptions: {
modifier: { page: "current" },
columns: ":visible:not(:first-child):not(:last-child)",
},
},
{

View file

@ -129,21 +129,29 @@ $(function () {
buttons: [
{
extend: "copy",
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy current page',
exportOptions: { modifier: { page: "current" } },
text: '<span class="tf-icons bx bx-copy bx-18px me-2"></span>Copy visible',
exportOptions: {
columns: ":visible:not(:first-child):not(:last-child)",
},
},
{
extend: "csv",
text: '<span class="tf-icons bx bx-table bx-18px me-2"></span>CSV',
bom: true,
filename: "bw_services",
exportOptions: { modifier: { search: "none" } },
exportOptions: {
modifier: { search: "none" },
columns: ":not(:first-child):not(:last-child)",
},
},
{
extend: "excel",
text: '<span class="tf-icons bx bx-table bx-18px me-2"></span>Excel',
filename: "bw_services",
exportOptions: { modifier: { search: "none" } },
exportOptions: {
modifier: { search: "none" },
columns: ":not(:first-child):not(:last-child)",
},
},
],
},

View file

@ -14,6 +14,7 @@ $(document).ready(() => {
}
let currentTemplate = $("#selected-template").val();
let currentTemplateMethod = $("#selected-template-method").val();
let currentMode = $("#selected-mode").val();
let currentType = $("#selected-type").val();
@ -284,12 +285,6 @@ $(document).ready(() => {
if ($draftInput.length) {
appendHiddenInput(form, "IS_DRAFT", $draftInput.val());
}
// Append 'OLD_SERVER_NAME' if it exists
const $oldServerName = $("#old-server-name");
if ($oldServerName.length) {
appendHiddenInput(form, "OLD_SERVER_NAME", $oldServerName.val());
}
} else if (currentMode === undefined || currentMode === "advanced") {
addChildrenToForm(form, $("div[id^='navs-plugins-']"));
@ -356,12 +351,12 @@ $(document).ready(() => {
formKeys.add(key);
}
});
}
// Append 'OLD_SERVER_NAME' if it exists
const $oldServerName = $("#old-server-name");
if ($oldServerName.length) {
appendHiddenInput(form, "OLD_SERVER_NAME", $oldServerName.val());
}
// Append 'OLD_SERVER_NAME' if it exists
const $oldServerName = $("#old-server-name");
if ($oldServerName.length) {
appendHiddenInput(form, "OLD_SERVER_NAME", $oldServerName.val());
}
return form;
@ -777,6 +772,7 @@ $(document).ready(() => {
const isStepValid = validateCurrentStepInputs(currentStepContainer);
if (!isStepValid) return;
} else if (currentMode === "raw") {
const draftInput = $("#is-draft");
const wasDraft = draftInput.data("original") === "yes";
isDraft = form.find("input[name='IS_DRAFT']").val() === "yes";

View file

@ -4,8 +4,7 @@
<div class="footer-container d-flex align-items-center justify-content-between py-4 flex-md-row flex-column">
<div class="text-body">
©
<script nonce="{{ script_nonce }}">document.write(new Date().getFullYear());</script>
, made by
<script nonce="{{ script_nonce }}">document.write(new Date().getFullYear());</script>, made by
<a href="https://www.bunkerity.com/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
@ -16,15 +15,15 @@
target="_blank"
rel="noopener"
class="footer-link me-4">Documentation</a>
<a href="https://www.bunkerweb.io/privacy-policy/?utm_campaign=self&utm_source=ui"
<a href="https://www.bunkerweb.io/privacy-policy?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link me-4">Privacy</a>
<a href="https://www.bunkerweb.io/blog/?utm_campaign=self&utm_source=ui"
<a href="https://www.bunkerweb.io/blog?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link me-4">Blog</a>
<a href="https://panel.bunkerweb.io/order/support/?utm_campaign=self&utm_source=ui"
<a href="https://panel.bunkerweb.io/order/support?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link me-4">Support</a>

View file

@ -111,7 +111,7 @@
</small>
</a>
</div>
<div class="col-sm-6 mt-2 mb-2">
<div class="col-sm-7 mt-2 mb-2">
<div class="card p-4 position-relative shadow-sm rounded-3 h-100">
<div class="card-header p-2">
<div class="card-title mb-0">
@ -124,7 +124,7 @@
</div>
</div>
</div>
<div class="col-sm-6 mt-2 mb-2">
<div class="col-sm-5 mt-2 mb-2">
<div class="card p-4 position-relative shadow-sm rounded-3">
<div class="card-header p-2">
<div class="card-title mb-0 d-flex justify-content-between">

View file

@ -116,7 +116,7 @@
<p class="card-subtitle text-muted text-truncate mt-3 courier-prime">{{ plugin_data["description"] }}</p>
</div>
<div class="d-flex flex-grow-0 flex-shrink-0 justify-content-end align-items-center">
<a href="https://docs.bunkerweb.io/latest/quickstart-guide/#protect-udptcp-applications"
<a href="https://docs.bunkerweb.io/latest/quickstart-guide/?utm_campaign=self&utm_source=ui#protect-udptcp-applications"
class="btn btn-sm btn-outline-{% if plugin_data['stream'] == 'yes' %}bw-green{% elif plugin_data['stream'] == 'partial' %}warning{% else %}danger{% endif %} rounded-pill"
target="_blank"
rel="noopener"
@ -224,7 +224,6 @@
{% set plugin_multiples = get_multiples(filtered_settings, config) %}
{% if plugin_multiples %}
{% set setting_id_prefix = "multiple-setting-" + plugin +"-" %}
{% set multiple_plugin_multiples = plugin_multiples|length > 1 %}
<div class="card-header pb-2 mt-6">
<h5 class="card-title d-inline border p-2{{ plugin_types[plugin_data['type']].get('text-class', '') }}{{ plugin_types[plugin_data['type']].get('title-class', '') }}">
Multiple settings
@ -233,9 +232,8 @@
</div>
<div class="card-body row pt-0">
{% for multiple, multiples in plugin_multiples.items() %}
{% set multiple_settings = settings|length > 1 %}
<div id="multiple-{{ plugin }}-{{ multiple }}"
class="col-12{% if multiple_plugin_multiples %} col-md-6{% endif %}{% if plugin_multiples|length > 2 and not multiple_settings %} col-lg-4{% endif %}">
class="col-12{% if plugin_multiples|length > 1 %} col-md-6{% endif %}{% if plugin_multiples|length > 2 and not settings|length > 1 %} col-lg-4{% endif %}">
{% for setting_suffix, settings in multiples.items() %}
<div class="row multiple-container pt-2 pb-2">
{% set setting_id_suffix = "-" + setting_suffix %}
@ -300,7 +298,7 @@
{% set disabled = true %}
{% set setting_method = "readonly" %}
{% endif %}
<div class="col-12{% if multiple_settings %} col-md-6{% endif %}{% if settings|length > 2 and not multiple_multiples %} col-lg-4{% endif %} pb-2"
<div class="col-12{% if settings|length > 1 %} col-md-6{% endif %}{% if settings|length > 2 and not multiple_multiples %} col-lg-4{% endif %} pb-2"
{% if disabled %}data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Disabled by {{ setting_method }}"{% endif %}>
<div class="d-flex justify-content-between align-items-center">
<label id="label-multiple-setting-{{ plugin }}-{{ setting_data['id'] }}-{{ setting_suffix }}"

View file

@ -86,191 +86,200 @@
</div>
</div>
<div class="card-body">
{% if template_data["steps"] %}
<nav class="p-3 template-steps-container align-items-center mw-100{{ plugin_types[template_plugin['type']].get('title-class', '') }}"
aria-label="breadcrumb">
<ol class="breadcrumb nav nav-scroller mb-0 flex-nowrap overflow-hidden{% if loop.index == 1 %} active{% endif %}"
role="tablist">
{% if template_method == "ui" or not selected_template or template == selected_template %}
{% if template_data["steps"] %}
<nav class="p-3 template-steps-container align-items-center mw-100{{ plugin_types[template_plugin['type']].get('title-class', '') }}"
aria-label="breadcrumb">
<ol class="breadcrumb nav nav-scroller mb-0 flex-nowrap overflow-hidden{% if loop.index == 1 %} active{% endif %}"
role="tablist">
{% for step in template_data["steps"] %}
<li class="breadcrumb-item nav-link d-flex align-items-center pe-0">
<button class="btn btn-primary pt-3 pb-3 me-3{% if loop.index == 1 %} active{% else %} disabled{% endif %}"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-steps-{{ template }}-{{ loop.index }}"
aria-controls="navs-steps-{{ template }}-{{ loop.index }}"
{% if loop.index == 1 %}aria-selected="true"{% endif %}>
{{ loop.index }}
</button>
<div class="text-nowrap">
<div class="fw-bold{% if loop.index == 1 %} text-primary{% else %} text-muted{% endif %}">{{ step["title"] }}</div>
<small class="text-muted">{{ step["subtitle"] }}</small>
</div>
</li>
{% endfor %}
</ol>
</nav>
<div class="tab-content p-3 align-items-center mw-100{{ plugin_types[template_plugin['type']].get('title-class', '') }}">
{% for step in template_data["steps"] %}
<li class="breadcrumb-item nav-link d-flex align-items-center pe-0">
<button class="btn btn-primary pt-3 pb-3 me-3{% if loop.index == 1 %} active{% else %} disabled{% endif %}"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-steps-{{ template }}-{{ loop.index }}"
aria-controls="navs-steps-{{ template }}-{{ loop.index }}"
{% if loop.index == 1 %}aria-selected="true"{% endif %}>
{{ loop.index }}
</button>
<div class="text-nowrap">
<div class="fw-bold{% if loop.index == 1 %} text-primary{% else %} text-muted{% endif %}">{{ step["title"] }}</div>
<small class="text-muted">{{ step["subtitle"] }}</small>
<div id="navs-steps-{{ template }}-{{ loop.index }}"
class="ps-2 pe-2 tab-pane fade{% if loop.index == 1 %} show active{% endif %}"
role="tabpanel"
data-step="{{ loop.index }}"
aria-labelledby="navs-steps-{{ template }}-{{ loop.index }}-tab">
<div class="pt-1 pb-4">
<h5 class="mb-1 fw-bold{{ plugin_types[template_plugin['type']].get('text-class', '') }}">{{ step["title"] }}</h5>
<p class="card-subtitle text-muted">{{ step["subtitle"] }}</p>
</div>
</li>
{% endfor %}
</ol>
</nav>
<div class="tab-content p-3 align-items-center mw-100{{ plugin_types[template_plugin['type']].get('title-class', '') }}">
{% for step in template_data["steps"] %}
<div id="navs-steps-{{ template }}-{{ loop.index }}"
class="ps-2 pe-2 tab-pane fade{% if loop.index == 1 %} show active{% endif %}"
role="tabpanel"
data-step="{{ loop.index }}"
aria-labelledby="navs-steps-{{ template }}-{{ loop.index }}-tab">
<div class="pt-1 pb-4">
<h5 class="mb-1 fw-bold{{ plugin_types[template_plugin['type']].get('text-class', '') }}">{{ step["title"] }}</h5>
<p class="card-subtitle text-muted">{{ step["subtitle"] }}</p>
</div>
<div class="row pb-0">
{% for setting in step["settings"] %}
{% set setting_id_prefix = template + "-setting-" + template_data['plugin_id'] +"-" %}
{% set setting_config = config.get(setting, {}) %}
{% set setting_data = plugins_settings.get(setting, {}) %}
{% set setting_default = setting_data.get("default", "") %}
{% set setting_value = setting_config.get("value", setting_default) %}
{% set setting_method = setting_config.get("method", "default") %}
{% set setting_template = setting_config.get("template", "") %}
{% set disabled = setting_method not in ('ui', 'default') and (current_endpoint == "global-config" or not setting_config.get("global")) %}
{% set required = setting == "SERVER_NAME" %}
{% if current_endpoint == "new" %}
{% set disabled = false %}
{% if not clone %}
{% set setting_value = template_data["settings"].get(setting, setting_default) %}
<div class="row pb-0">
{% for setting in step["settings"] %}
{% set setting_id_prefix = template + "-setting-" + template_data['plugin_id'] +"-" %}
{% set setting_config = config.get(setting, {}) %}
{% set setting_data = plugins_settings.get(setting, {}) %}
{% set setting_default = setting_data.get("default", "") %}
{% set setting_value = setting_config.get("value", setting_default) %}
{% set setting_method = setting_config.get("method", "default") %}
{% set setting_template = setting_config.get("template", "") %}
{% set disabled = setting_method not in ('ui', 'default') and (current_endpoint == "global-config" or not setting_config.get("global")) %}
{% set required = setting == "SERVER_NAME" %}
{% if current_endpoint == "new" %}
{% set disabled = false %}
{% if not clone %}
{% set setting_value = template_data["settings"].get(setting, setting_default) %}
{% endif %}
{% endif %}
{% endif %}
{% if service_method == "autoconf" %}
{% set setting_method = "autoconf" %}
{% set disabled = true %}
{% endif %}
{% if is_readonly %}
{% set disabled = true %}
{% set setting_method = "readonly" %}
{% endif %}
<div class="col-12 col-sm-6 col-lg-4 pb-3"
{% if disabled %}data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Disabled by {{ setting_method }}"{% endif %}>
<div class="d-flex justify-content-between align-items-center">
<label id="label-{{ template }}-setting-{{ template_data['plugin_id'] }}-{{ setting_data['id'] }}"
for="{{ template }}-setting-{{ template_data['plugin_id'] }}-{{ setting_data['id'] }}"
class="form-label fw-semibold text-truncate">
{{ setting_data["label"] }}
</label>
<div class="d-flex align-items-center">
{% if current_endpoint == "global-config" and setting_data["context"] == "multisite" %}
<a role="badge"
href='https://docs.bunkerweb.io/latest/concepts/?utm_campaign=self&utm_source=ui#multisite-mode'
class="badge badge-center rounded-pill bg-secondary d-flex align-items-center justify-content-center p-1 me-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-original-title="Multisite setting"
target="_blank"
rel="noopener">
<span class="bx bx-server bx-xs"></span>
</a>
{% endif %}
{% if current_endpoint not in ("new", "global-config") and setting_config.get("global") and setting_value != setting_default %}
<span class="badge badge-center rounded-pill bg-primary-subtle text-dark d-flex align-items-center justify-content-center p-1 me-1"
{% if service_method == "autoconf" %}
{% set setting_method = "autoconf" %}
{% set disabled = true %}
{% endif %}
{% if is_readonly %}
{% set disabled = true %}
{% set setting_method = "readonly" %}
{% endif %}
<div class="col-12 col-sm-6 col-lg-4 pb-3"
{% if disabled %}data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Disabled by {{ setting_method }}"{% endif %}>
<div class="d-flex justify-content-between align-items-center">
<label id="label-{{ template }}-setting-{{ template_data['plugin_id'] }}-{{ setting_data['id'] }}"
for="{{ template }}-setting-{{ template_data['plugin_id'] }}-{{ setting_data['id'] }}"
class="form-label fw-semibold text-truncate">
{{ setting_data["label"] }}
</label>
<div class="d-flex align-items-center">
{% if current_endpoint == "global-config" and setting_data["context"] == "multisite" %}
<a role="badge"
href='https://docs.bunkerweb.io/latest/concepts/?utm_campaign=self&utm_source=ui#multisite-mode'
class="badge badge-center rounded-pill bg-secondary d-flex align-items-center justify-content-center p-1 me-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-original-title="Multisite setting"
target="_blank"
rel="noopener">
<span class="bx bx-server bx-xs"></span>
</a>
{% endif %}
{% if current_endpoint not in ("new", "global-config") and setting_config.get("global") and setting_value != setting_default %}
<span class="badge badge-center rounded-pill bg-primary-subtle text-dark d-flex align-items-center justify-content-center p-1 me-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-original-title="From global configuration">
<span class="bx bx-globe bx-xs"></span>
</span>
{% endif %}
<span class="badge rounded-pill bg-secondary-subtle text-dark d-flex align-items-center justify-content-center p-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-original-title="From global configuration">
<span class="bx bx-globe bx-xs"></span>
data-bs-original-title="{{ setting_data['help'] }}">
<span class="bx bx-question-mark bx-xs"></span>
</span>
{% endif %}
<span class="badge rounded-pill bg-secondary-subtle text-dark d-flex align-items-center justify-content-center p-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-original-title="{{ setting_data['help'] }}">
<span class="bx bx-question-mark bx-xs"></span>
</span>
</div>
</div>
<input id="{{ setting_id_prefix }}{{ setting_data['id'] }}{{ setting_id_suffix }}-template"
type="hidden"
value="{{ template_data['settings'].get(setting, setting_default) }}">
{% if setting_data["type"] == "select" %}
{% include "models/select_setting.html" %}
{% elif setting_data["type"] == "check" %}
{% include "models/checkbox_setting.html" %}
{% else %}
{% include "models/input_setting.html" %}
{% endif %}
</div>
{% endfor %}
</div>
{% set step_configs = step.get("configs") %}
{% if step_configs %}
<div class="pt-1 pb-4">
<h5 class="mb-1 fw-bold{{ plugin_types[template_plugin['type']].get('text-class', '') }}">
Custom configuration
{% if step_configs|length > 1 %}s{% endif %}
</h5>
</div>
{% for step_config in step_configs %}
{% set step_config_id = template + "-config-" + template_data['plugin_id'] + "-" + step_config.replace("/", "-").replace(".conf", "") %}
{% set step_config_value = configs.get(current_endpoint + "_" + step_config.replace(".conf", "").replace("/", "_")) %}
{% set step_config_method = "ui" %}
{% if step_config_value is none %}
{% set step_config_value = template_data["configs"].get(step_config, "") %}
{% else %}
{% set step_config_value = step_config_value["data"].decode("utf-8") %}
{% set step_config_method = step_config_value["method"] %}
{% endif %}
<div class="mb-3 pb-6 position-relative h-vh-40"
{% if service_method == "autoconf" or is_readonly %}data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="{% if is_readonly %}The database is in readonly{% else %}The service was created using the autoconf method{% endif %}
therefore
the
configuration
is
locked
"
{% endif %}>
<label for="{{ step_config_id }}" class="form-label fw-semibold fs-6">{{ step_config }}</label>
<textarea id="{{ step_config_id }}-default" class="visually-hidden">{{ template_data["configs"].get(step_config, "") }}</textarea>
<div id="{{ step_config_id }}" aria-labelledby="label-{{ step_config_id }}" data-language="{% if step_config.startswith(('crs', 'modsec')) %}ModSecurity{% else %}NGINX{% endif %}" data-name="CUSTOM_CONF_{{ step_config.split("/")[0] |upper }}_{{ step_config.split("/")[1] .replace(".conf", "") }}" data-method="{% if is_readonly %}readonly{% else %}{{ step_config_method }}{% endif %}" class="ace-editor border rounded position-absolute top-0 start-0 end-0 bottom-0 mt-6">
{{ step_config_value }}
</div>
</div>
<input id="{{ setting_id_prefix }}{{ setting_data['id'] }}{{ setting_id_suffix }}-template"
type="hidden"
value="{{ template_data['settings'].get(setting, setting_default) }}">
{% if setting_data["type"] == "select" %}
{% include "models/select_setting.html" %}
{% elif setting_data["type"] == "check" %}
{% include "models/checkbox_setting.html" %}
{% else %}
{% include "models/input_setting.html" %}
{% endif %}
</div>
{% endfor %}
</div>
{% set step_configs = step.get("configs") %}
{% if step_configs %}
<div class="pt-1 pb-4">
<h5 class="mb-1 fw-bold{{ plugin_types[template_plugin['type']].get('text-class', '') }}">
Custom configuration
{% if step_configs|length > 1 %}s{% endif %}
</h5>
</div>
{% for step_config in step_configs %}
{% set step_config_id = template + "-config-" + template_data['plugin_id'] + "-" + step_config.replace("/", "-").replace(".conf", "") %}
{% set step_config_value = configs.get(current_endpoint + "_" + step_config.replace(".conf", "").replace("/", "_")) %}
{% set step_config_method = "ui" %}
{% if step_config_value is none %}
{% set step_config_value = template_data["configs"].get(step_config, "") %}
{% else %}
{% set step_config_value = step_config_value["data"].decode("utf-8") %}
{% set step_config_method = step_config_value["method"] %}
{% endif %}
<div class="mb-3 pb-6 position-relative h-vh-40"
{% if service_method == "autoconf" or is_readonly %}data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="{% if is_readonly %}The database is in readonly{% else %}The service was created using the autoconf method{% endif %}
therefore
the
configuration
is
locked
"
{% endif %}>
<label for="{{ step_config_id }}" class="form-label fw-semibold fs-6">{{ step_config }}</label>
<textarea id="{{ step_config_id }}-default" class="visually-hidden">{{ template_data["configs"].get(step_config, "") }}</textarea>
<div id="{{ step_config_id }}" aria-labelledby="label-{{ step_config_id }}" data-language="{% if step_config.startswith(('crs', 'modsec')) %}ModSecurity{% else %}NGINX{% endif %}" data-name="CUSTOM_CONF_{{ step_config.split("/")[0] |upper }}_{{ step_config.split("/")[1] .replace(".conf", "") }}" data-method="{% if is_readonly %}readonly{% else %}{{ step_config_method }}{% endif %}" class="ace-editor border rounded position-absolute top-0 start-0 end-0 bottom-0 mt-6">
{{ step_config_value }}
</div>
</div>
{% endfor %}
{% endif %}
<div class="d-flex justify-content-between">
{% if loop.index > 1 %}
<button class="btn btn-primary btn-prev previous-step"
data-template="{{ template }}">
<i class="bx bx-chevron-left bx-sm ms-sm-n2"></i>
<span class="align-middle d-sm-inline-block d-none">Previous</span>
</button>
{% else %}
<div></div>
{% endfor %}
{% endif %}
{% if loop.index == template_data["steps"]|length %}
<div {% if service_method == "autoconf" or is_readonly %}data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="{% if is_readonly %}The database is in readonly{% else %}The service was created using the autoconf method{% endif %}
therefore
the
configuration
is
locked
"
{% endif %}>
<button type="button"
class="btn btn-outline-bw-green save-settings{% if service_method == "autoconf" or is_readonly %} disabled{% endif %}"
<div class="d-flex justify-content-between">
{% if loop.index > 1 %}
<button class="btn btn-primary btn-prev previous-step"
data-template="{{ template }}">
<i class="bx bx-save bx-sm ms-sm-n2"></i>
<span class="align-middle d-sm-inline-block d-none ms-sm-1">Save</span>
<i class="bx bx-chevron-left bx-sm ms-sm-n2"></i>
<span class="align-middle d-sm-inline-block d-none">Previous</span>
</button>
</div>
{% else %}
<button class="btn btn-primary btn-next next-step"
data-template="{{ template }}">
<span class="align-middle d-sm-inline-block d-none me-sm-1">Next</span>
<i class="bx bx-chevron-right bx-sm me-sm-n2"></i>
</button>
{% endif %}
{% else %}
<div></div>
{% endif %}
{% if loop.index == template_data["steps"]|length %}
<div {% if service_method == "autoconf" or is_readonly %}data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="{% if is_readonly %}The database is in readonly{% else %}The service was created using the autoconf method{% endif %}
therefore
the
configuration
is
locked
"
{% endif %}>
<button type="button"
class="btn btn-outline-bw-green save-settings{% if service_method == "autoconf" or is_readonly %} disabled{% endif %}"
data-template="{{ template }}">
<i class="bx bx-save bx-sm ms-sm-n2"></i>
<span class="align-middle d-sm-inline-block d-none ms-sm-1">Save</span>
</button>
</div>
{% else %}
<button class="btn btn-primary btn-next next-step"
data-template="{{ template }}">
<span class="align-middle d-sm-inline-block d-none me-sm-1">Next</span>
<i class="bx bx-chevron-right bx-sm me-sm-n2"></i>
</button>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center">
<h5 class="text-dark fw-bold mt-5">No steps available</h5>
<p class="text-muted">This template does not have any steps to configure</p>
</div>
{% endif %}
{% else %}
<div class="text-center">
<h5 class="text-dark fw-bold mt-5">No steps available</h5>
<p class="text-muted">This template does not have any steps to configure</p>
<h5 class="text-dark fw-bold mt-5">
This service currently uses template "{{ selected_template }}" which is of method "{{ template_method }}"
</h5>
<p class="text-muted">Therefore no changes outside of the current template are allowed, please make changes to the current template or via the advanced/raw mode</p>
</div>
{% endif %}
</div>

View file

@ -3,6 +3,7 @@
<!-- Content -->
<div class="card table-responsive text-nowrap p-4 min-vh-70">
<input type="hidden" id="plugins_number" value="{{ plugins|length }}" />
<input type="hidden" id="pro_diamond_url" value="{{ pro_diamond_url }}" />
<input type="hidden"
id="csrf_token"
name="csrf_token"

View file

@ -2,9 +2,10 @@
{% block content %}
<!-- Content -->
{% set blacklisted_settings = get_blacklisted_settings() %}
{% set service_method = "ui" %}
{% set service_method = config.get("SERVER_NAME", {"method": "ui"})["method"] %}
{% set is_draft = config.get("IS_DRAFT", {"value": "no"})["value"] %}
{% set template_method = config.get("USE_TEMPLATE", {"method": "ui"})["method"] %}
{% set selected_template = config.get("USE_TEMPLATE", {"value": ""})["value"] %}
<input type="hidden"
id="selected-mode"
name="selected_mode"
@ -16,6 +17,10 @@
id="selected-template"
name="selected_template"
value="{{ current_template }}">
<input type="hidden"
id="selected-template-method"
name="selected_template_method"
value="{{ template_method }}">
<input type="hidden"
id="service-method"
name="service_method"

View file

@ -475,7 +475,7 @@
{% include "models/input_setting.html" %}
</div>
{% endif %}
<div class="d-flex justify-content-around text-center mt-4">
<div class="d-flex justify-content-center text-center mt-4">
{% if not ui_user %}
<div>
<i class="bx bx-check text-success"></i> Secure password