mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Add some QOL tweaks discussed in an early reunion to web UI
This commit is contained in:
parent
793bef233b
commit
56ffc1b290
34 changed files with 821 additions and 497 deletions
|
|
@ -137,38 +137,38 @@ def configs_new():
|
|||
verify_data_in_form(
|
||||
data={"service": None},
|
||||
err_message="Missing service parameter on /configs/new.",
|
||||
redirect_url="configs/new",
|
||||
redirect_url="configs.configs_new",
|
||||
next=True,
|
||||
)
|
||||
service = request.form["service"]
|
||||
services = BW_CONFIG.get_config(global_only=True, with_drafts=True, methods=False, filtered_settings=("SERVER_NAME"))["SERVER_NAME"].split(" ")
|
||||
if service != "no service" and service not in services:
|
||||
return handle_error(f"Service {service} does not exist.", "configs/new", True)
|
||||
if service != "global" and service not in services:
|
||||
return handle_error(f"Service {service} does not exist.", "configs.configs_new", True)
|
||||
|
||||
verify_data_in_form(
|
||||
data={"type": None},
|
||||
err_message="Missing type parameter on /configs/new.",
|
||||
redirect_url="configs/new",
|
||||
redirect_url="configs.configs_new",
|
||||
next=True,
|
||||
)
|
||||
config_type = request.form["type"]
|
||||
if config_type not in CONFIG_TYPES:
|
||||
return handle_error("Invalid type parameter on /configs/new.", "configs/new", True)
|
||||
return handle_error("Invalid type parameter on /configs/new.", "configs.configs_new", True)
|
||||
|
||||
verify_data_in_form(
|
||||
data={"name": None},
|
||||
err_message="Missing name parameter on /configs/new.",
|
||||
redirect_url="configs/new",
|
||||
redirect_url="configs.configs_new",
|
||||
next=True,
|
||||
)
|
||||
config_name = request.form["name"]
|
||||
if not match(r"^[\w_-]{1,64}$", config_name):
|
||||
return handle_error("Invalid name parameter on /configs/new.", "configs/new", True)
|
||||
return handle_error("Invalid name parameter on /configs/new.", "configs.configs_new", True)
|
||||
|
||||
verify_data_in_form(
|
||||
data={"value": None},
|
||||
err_message="Missing value parameter on /configs/new.",
|
||||
redirect_url="configs/new",
|
||||
redirect_url="configs.configs_new",
|
||||
next=True,
|
||||
)
|
||||
config_value = request.form["value"].replace("\r\n", "\n").strip()
|
||||
|
|
@ -191,7 +191,7 @@ def configs_new():
|
|||
"data": config_value,
|
||||
"method": "ui",
|
||||
}
|
||||
if service != "no service":
|
||||
if service != "global":
|
||||
new_config["service_id"] = service
|
||||
|
||||
error = DB.upsert_custom_config(config_type, config_name, new_config, service_id=new_config.get("service_id"), new=True)
|
||||
|
|
@ -216,15 +216,13 @@ def configs_new():
|
|||
DATA["RELOADING"] = False
|
||||
|
||||
DATA.update({"RELOADING": True, "LAST_RELOAD": time(), "CONFIG_CHANGED": True})
|
||||
Thread(target=create_config, args=(service if service != "no service" else None, config_type, config_name, config_value)).start()
|
||||
Thread(target=create_config, args=(service if service != "global" else None, config_type, config_name, config_value)).start()
|
||||
|
||||
return redirect(
|
||||
url_for(
|
||||
"loading",
|
||||
next=url_for(
|
||||
"configs.configs_edit", service="global" if service == "no service" else service, config_type=config_type.lower(), name=config_name
|
||||
),
|
||||
message=f"Creating custom configuration {config_type}/{config_name}{' for service' + service if service != 'no service' else ''}",
|
||||
next=url_for("configs.configs_edit", service="global" if service == "global" else service, config_type=config_type.lower(), name=config_name),
|
||||
message=f"Creating custom configuration {config_type}/{config_name}{' for service' + service if service != 'global' else ''}",
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -271,42 +269,42 @@ def configs_edit(service: str, config_type: str, name: str):
|
|||
verify_data_in_form(
|
||||
data={"service": None},
|
||||
err_message="Missing service parameter on /configs/new.",
|
||||
redirect_url="configs/new",
|
||||
redirect_url="configs.configs_new",
|
||||
next=True,
|
||||
)
|
||||
new_service = request.form["service"]
|
||||
services = BW_CONFIG.get_config(global_only=True, with_drafts=True, methods=False, filtered_settings=("SERVER_NAME"))["SERVER_NAME"].split(" ")
|
||||
if new_service != "no service" and new_service not in services:
|
||||
return handle_error(f"Service {new_service} does not exist.", "configs/new", True)
|
||||
if new_service != "global" and new_service not in services:
|
||||
return handle_error(f"Service {new_service} does not exist.", "configs.configs_new", True)
|
||||
|
||||
if new_service == "no service":
|
||||
if new_service == "global":
|
||||
new_service = None
|
||||
|
||||
verify_data_in_form(
|
||||
data={"type": None},
|
||||
err_message="Missing type parameter on /configs/new.",
|
||||
redirect_url="configs/new",
|
||||
redirect_url="configs.configs_new",
|
||||
next=True,
|
||||
)
|
||||
new_type = request.form["type"]
|
||||
if new_type not in CONFIG_TYPES:
|
||||
return handle_error("Invalid type parameter on /configs/new.", "configs/new", True)
|
||||
return handle_error("Invalid type parameter on /configs/new.", "configs.configs_new", True)
|
||||
new_type = new_type.lower()
|
||||
|
||||
verify_data_in_form(
|
||||
data={"name": None},
|
||||
err_message="Missing name parameter on /configs/new.",
|
||||
redirect_url="configs/new",
|
||||
redirect_url="configs.configs_new",
|
||||
next=True,
|
||||
)
|
||||
new_name = secure_filename(request.form["name"])
|
||||
if not match(r"^[\w_-]{1,64}$", new_name):
|
||||
return handle_error("Invalid name parameter on /configs/new.", "configs/new", True)
|
||||
return handle_error("Invalid name parameter on /configs/new.", "configs.configs_new", True)
|
||||
|
||||
verify_data_in_form(
|
||||
data={"value": None},
|
||||
err_message="Missing value parameter on /configs/new.",
|
||||
redirect_url="configs/new",
|
||||
redirect_url="configs.configs_new",
|
||||
next=True,
|
||||
)
|
||||
config_value = request.form["value"].replace("\r\n", "\n").strip()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
from datetime import datetime
|
||||
|
||||
from flask import Blueprint, flash, redirect, render_template, request, session, url_for
|
||||
from flask import Blueprint, flash as flask_flash, redirect, render_template, request, session, url_for
|
||||
from flask_login import current_user, login_user
|
||||
|
||||
from app.dependencies import DB
|
||||
from app.utils import LOGGER
|
||||
from app.utils import LOGGER, flash
|
||||
|
||||
login = Blueprint("login", __name__)
|
||||
|
||||
|
|
@ -38,15 +38,21 @@ def login_page():
|
|||
session["session_id"] = ret
|
||||
|
||||
if not login_user(ui_user, remember=request.form.get("remember-me") == "on"):
|
||||
flash("Couldn't log you in, please try again", "error")
|
||||
flask_flash("Couldn't log you in, please try again", "error")
|
||||
return (render_template("login.html", error="Couldn't log you in, please try again"),)
|
||||
|
||||
LOGGER.info(f"User {ui_user.username} logged in successfully" + (" with remember me" if request.form.get("remember-me") == "on" else ""))
|
||||
|
||||
if not ui_user.totp_secret:
|
||||
flash(
|
||||
f'Please enable two-factor authentication to secure your account <a href="{url_for("profile.profile_page", _anchor="security")}">here</a>',
|
||||
"error",
|
||||
)
|
||||
|
||||
# redirect him to the page he originally wanted or to the home page
|
||||
return redirect(url_for("loading", next=request.form.get("next") or url_for("home.home_page")))
|
||||
else:
|
||||
flash("Invalid username or password", "error")
|
||||
flask_flash("Invalid username or password", "error")
|
||||
fail = True
|
||||
|
||||
kwargs = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from os import getenv
|
||||
from secrets import choice
|
||||
from string import ascii_letters, digits
|
||||
|
||||
# from secrets import choice
|
||||
# from string import ascii_letters, digits
|
||||
from threading import Thread
|
||||
from time import time
|
||||
|
||||
|
|
@ -43,7 +44,9 @@ def setup_page():
|
|||
if not ui_reverse_proxy:
|
||||
required_keys.extend(["server_name", "ui_host", "ui_url", "auto_lets_encrypt", "lets_encrypt_staging", "email_lets_encrypt"])
|
||||
if not admin_user:
|
||||
required_keys.extend(["admin_username", "admin_email", "admin_password", "admin_password_check", "2fa_code"])
|
||||
required_keys.extend(
|
||||
["admin_username", "admin_email", "admin_password", "admin_password_check"]
|
||||
) # TODO: add "2fa_code" back when TOTP is implemented in setup wizard
|
||||
|
||||
if not any(key in request.form for key in required_keys):
|
||||
return handle_error(f"Missing either one of the following parameters: {', '.join(required_keys)}.", "setup")
|
||||
|
|
@ -64,12 +67,12 @@ def setup_page():
|
|||
totp_secret = None
|
||||
totp_recovery_codes = None
|
||||
|
||||
if request.form["2fa_code"]:
|
||||
totp_secret = session.pop("tmp_totp_secret", "")
|
||||
if not TOTP.verify_totp(request.form["2fa_code"], totp_secret=totp_secret, user=current_user):
|
||||
return handle_error("The totp token is invalid.", "setup")
|
||||
# if request.form["2fa_code"]: # TODO: uncomment when TOTP is implemented in setup wizard
|
||||
# totp_secret = session.pop("tmp_totp_secret", "")
|
||||
# if not TOTP.verify_totp(request.form["2fa_code"], totp_secret=totp_secret, user=current_user):
|
||||
# return handle_error("The totp token is invalid.", "setup")
|
||||
|
||||
totp_recovery_codes = TOTP.generate_recovery_codes()
|
||||
# totp_recovery_codes = TOTP.generate_recovery_codes()
|
||||
|
||||
ret = DB.create_ui_user(
|
||||
request.form["admin_username"],
|
||||
|
|
@ -148,7 +151,6 @@ def setup_page():
|
|||
auto_lets_encrypt=db_config.get("AUTO_LETS_ENCRYPT", getenv("AUTO_LETS_ENCRYPT", "no")),
|
||||
lets_encrypt_staging=db_config.get("USE_LETS_ENCRYPT_STAGING", getenv("USE_LETS_ENCRYPT_STAGING", "no")),
|
||||
email_lets_encrypt=db_config.get("EMAIL_LETS_ENCRYPT", getenv("EMAIL_LETS_ENCRYPT", "")),
|
||||
random_url=f"/{''.join(choice(ascii_letters + digits) for _ in range(10))}",
|
||||
totp_qr_image=totp_qr_image,
|
||||
totp_secret=TOTP.get_totp_pretty_key(session.get("tmp_totp_secret", "")),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -158,10 +158,11 @@ def handle_error(err_message: str = "", redirect_url: str = "", next: bool = Fal
|
|||
if not redirect_url:
|
||||
return False
|
||||
|
||||
redirect_url = f"{redirect_url}.{redirect_url}_page" if "." not in redirect_url else redirect_url
|
||||
if next:
|
||||
return redirect(url_for("loading", next=url_for(f"{redirect_url}.{redirect_url}_page")))
|
||||
return redirect(url_for("loading", next=url_for(redirect_url)))
|
||||
|
||||
return redirect(url_for(f"{redirect_url}.{redirect_url}_page"))
|
||||
return redirect(url_for(redirect_url))
|
||||
|
||||
|
||||
def error_message(msg: str):
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -86,13 +86,13 @@ $(document).ready(function () {
|
|||
|
||||
const setupUnbanModal = (bans) => {
|
||||
const list = $(
|
||||
`<ul class="list-group list-group-horizontal d-flex w-100">
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 0;">
|
||||
`<ul class="list-group list-group-horizontal w-100">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 0;">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">IP Address</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 0;">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 0;">
|
||||
<div class="fw-bold">Time left</div>
|
||||
</li>
|
||||
</ul>`,
|
||||
|
|
@ -102,19 +102,17 @@ $(document).ready(function () {
|
|||
bans.forEach((ban) => {
|
||||
// Create the list item using template literals
|
||||
const list = $(
|
||||
`<ul class="list-group list-group-horizontal d-flex w-100"></ul>`,
|
||||
`<ul class="list-group list-group-horizontal w-100"></ul>`,
|
||||
);
|
||||
|
||||
const listItem =
|
||||
$(`<li class="list-group-item align-items-center" style="flex: 1 0;">
|
||||
const listItem = $(`<li class="list-group-item" style="flex: 1 0;">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">${ban.ip}</div>
|
||||
</div>
|
||||
</li>`);
|
||||
list.append(listItem);
|
||||
|
||||
const timeLeft =
|
||||
$(`<li class="list-group-item align-items-center" style="flex: 1 0;">
|
||||
const timeLeft = $(`<li class="list-group-item" style="flex: 1 0;">
|
||||
<div class="ms-2 me-auto">
|
||||
${ban.time_remaining}
|
||||
</div>
|
||||
|
|
@ -147,15 +145,28 @@ $(document).ready(function () {
|
|||
};
|
||||
|
||||
const layout = {
|
||||
topStart: {},
|
||||
bottomEnd: {},
|
||||
bottom1: {
|
||||
top1: {
|
||||
searchPanes: {
|
||||
viewTotal: true,
|
||||
cascadePanes: true,
|
||||
collapse: false,
|
||||
columns: [1, 4],
|
||||
},
|
||||
},
|
||||
topStart: {},
|
||||
topEnd: {
|
||||
buttons: [
|
||||
{
|
||||
extend: "toggle_filters",
|
||||
className: "btn btn-sm btn-outline-primary toggle-filters",
|
||||
},
|
||||
],
|
||||
search: true,
|
||||
},
|
||||
bottomStart: {
|
||||
info: true,
|
||||
},
|
||||
bottomEnd: {},
|
||||
};
|
||||
|
||||
if (banNumber > 10) {
|
||||
|
|
@ -170,8 +181,11 @@ $(document).ready(function () {
|
|||
menu.push(100);
|
||||
}
|
||||
menu.push({ label: "All", value: -1 });
|
||||
layout.topStart.pageLength = {
|
||||
menu: menu,
|
||||
layout.bottomStart = {
|
||||
pageLength: {
|
||||
menu: menu,
|
||||
},
|
||||
info: true,
|
||||
};
|
||||
layout.bottomEnd.paging = true;
|
||||
}
|
||||
|
|
@ -234,7 +248,7 @@ $(document).ready(function () {
|
|||
{
|
||||
extend: "collection",
|
||||
text: '<span class="tf-icons bx bx-play bx-18px me-2"></span>Actions',
|
||||
className: "btn btn-sm btn-outline-primary",
|
||||
className: "btn btn-sm btn-outline-primary action-button disabled",
|
||||
buttons: [
|
||||
{
|
||||
extend: "unban_ips",
|
||||
|
|
@ -275,6 +289,15 @@ $(document).ready(function () {
|
|||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
bans_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.unban_ips = {
|
||||
text: '<span class="tf-icons bx bxs-buoy bx-18px me-2"></span>Unban',
|
||||
action: function (e, dt, node, config) {
|
||||
|
|
@ -416,12 +439,6 @@ $(document).ready(function () {
|
|||
},
|
||||
targets: 4,
|
||||
},
|
||||
{
|
||||
targets: "_all", // Target all columns
|
||||
createdCell: function (td, cellData, rowData, row, col) {
|
||||
$(td).addClass("align-items-center"); // Apply 'text-center' class to <td>
|
||||
},
|
||||
},
|
||||
],
|
||||
order: [[4, "asc"]],
|
||||
autoFill: false,
|
||||
|
|
@ -448,7 +465,6 @@ $(document).ready(function () {
|
|||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#bans_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
$("#bans_wrapper th").addClass("text-center");
|
||||
if (isReadOnly)
|
||||
$("#bans_wrapper .dt-buttons")
|
||||
.attr(
|
||||
|
|
@ -460,6 +476,8 @@ $(document).ready(function () {
|
|||
},
|
||||
});
|
||||
|
||||
bans_table.searchPanes.container().hide();
|
||||
|
||||
$("#bans").removeClass("d-none");
|
||||
$("#bans-waiting").addClass("visually-hidden");
|
||||
|
||||
|
|
@ -488,6 +506,19 @@ $(document).ready(function () {
|
|||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
bans_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button").removeClass("disabled");
|
||||
});
|
||||
|
||||
bans_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (bans_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button").addClass("disabled");
|
||||
$("#select-all-rows").prop("checked", false);
|
||||
}
|
||||
});
|
||||
|
||||
// Event listener for the select-all checkbox
|
||||
$("#select-all-rows").on("change", function () {
|
||||
const isChecked = $(this).prop("checked");
|
||||
|
|
|
|||
|
|
@ -24,15 +24,28 @@ $(document).ready(function () {
|
|||
});
|
||||
|
||||
const layout = {
|
||||
topStart: {},
|
||||
bottomEnd: {},
|
||||
bottom1: {
|
||||
top1: {
|
||||
searchPanes: {
|
||||
viewTotal: true,
|
||||
cascadePanes: true,
|
||||
collapse: false,
|
||||
columns: [1, 2, 3, 4],
|
||||
},
|
||||
},
|
||||
topStart: {},
|
||||
topEnd: {
|
||||
buttons: [
|
||||
{
|
||||
extend: "toggle_filters",
|
||||
className: "btn btn-sm btn-outline-primary toggle-filters",
|
||||
},
|
||||
],
|
||||
search: true,
|
||||
},
|
||||
bottomStart: {
|
||||
info: true,
|
||||
},
|
||||
bottomEnd: {},
|
||||
};
|
||||
|
||||
if (cacheNumber > 10) {
|
||||
|
|
@ -47,8 +60,11 @@ $(document).ready(function () {
|
|||
menu.push(100);
|
||||
}
|
||||
menu.push({ label: "All", value: -1 });
|
||||
layout.topStart.pageLength = {
|
||||
menu: menu,
|
||||
layout.bottomStart = {
|
||||
pageLength: {
|
||||
menu: menu,
|
||||
},
|
||||
info: true,
|
||||
};
|
||||
layout.bottomEnd.paging = true;
|
||||
}
|
||||
|
|
@ -107,6 +123,15 @@ $(document).ready(function () {
|
|||
},
|
||||
];
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
cache_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$(".cache-last-update-date").each(function () {
|
||||
const isoDateStr = $(this).text().trim();
|
||||
|
||||
|
|
@ -182,12 +207,6 @@ $(document).ready(function () {
|
|||
},
|
||||
targets: 4,
|
||||
},
|
||||
{
|
||||
targets: "_all", // Target all columns
|
||||
createdCell: function (td, cellData, rowData, row, col) {
|
||||
$(td).addClass("align-items-center"); // Apply 'text-center' class to <td>
|
||||
},
|
||||
},
|
||||
],
|
||||
order: [[2, "asc"]],
|
||||
autoFill: false,
|
||||
|
|
@ -202,7 +221,6 @@ $(document).ready(function () {
|
|||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#cache_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
$("#cache_wrapper th").addClass("text-center");
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -218,6 +236,9 @@ $(document).ready(function () {
|
|||
"click",
|
||||
);
|
||||
|
||||
if (!cacheJobNameSelection && !cachePluginSelection && !cacheServiceSelection)
|
||||
cache_table.searchPanes.container().hide();
|
||||
|
||||
$("#cache").removeClass("d-none");
|
||||
$("#cache-waiting").addClass("visually-hidden");
|
||||
|
||||
|
|
|
|||
|
|
@ -59,8 +59,7 @@ $(document).ready(function () {
|
|||
$typeDropdownItems.each(function () {
|
||||
const item = $(this);
|
||||
item.toggle(
|
||||
selectedService === "no service" ||
|
||||
item.data("context") === "multisite",
|
||||
selectedService === "global" || item.data("context") === "multisite",
|
||||
);
|
||||
});
|
||||
};
|
||||
|
|
@ -104,7 +103,7 @@ $(document).ready(function () {
|
|||
selectedService = $(this).text().trim();
|
||||
changeTypesVisibility();
|
||||
if (
|
||||
selectedService !== "no service" &&
|
||||
selectedService !== "global" &&
|
||||
$(`#config-type-${selectedType}`).data("context") !== "multisite"
|
||||
) {
|
||||
const firstMultisiteType = $(
|
||||
|
|
|
|||
|
|
@ -47,16 +47,16 @@ $(document).ready(function () {
|
|||
const setupDeletionModal = (configs) => {
|
||||
const delete_modal = $("#modal-delete-configs");
|
||||
const list = $(
|
||||
`<ul class="list-group list-group-horizontal d-flex w-100">
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 1 0;">
|
||||
`<ul class="list-group list-group-horizontal w-100">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 1 0;">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">Name</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 1 0;">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 1 0;">
|
||||
<div class="fw-bold">Type</div>
|
||||
</li>
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 1 0;">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 1 0;">
|
||||
<div class="fw-bold">Service</div>
|
||||
</li>
|
||||
</ul>`,
|
||||
|
|
@ -65,12 +65,11 @@ $(document).ready(function () {
|
|||
|
||||
configs.forEach((config) => {
|
||||
const list = $(
|
||||
`<ul class="list-group list-group-horizontal d-flex w-100"></ul>`,
|
||||
`<ul class="list-group list-group-horizontal w-100"></ul>`,
|
||||
);
|
||||
|
||||
// Create the list item using template literals
|
||||
const listItem =
|
||||
$(`<li class="list-group-item align-items-center" style="flex: 1 1 0;">
|
||||
const listItem = $(`<li class="list-group-item" style="flex: 1 1 0;">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">${config.name}</div>
|
||||
</div>
|
||||
|
|
@ -85,7 +84,7 @@ $(document).ready(function () {
|
|||
// Clone the type element and append it to the list item
|
||||
const typeClone = $(`#type-${id}`).clone();
|
||||
const typeListItem = $(
|
||||
`<li class="list-group-item d-flex align-items-center" style="flex: 1 1 0;"></li>`,
|
||||
`<li class="list-group-item" style="flex: 1 1 0;"></li>`,
|
||||
);
|
||||
typeListItem.append(typeClone.removeClass("highlight"));
|
||||
list.append(typeListItem);
|
||||
|
|
@ -93,7 +92,7 @@ $(document).ready(function () {
|
|||
// Clone the service element and append it to the list item
|
||||
const serviceClone = $(`#service-${id}`).clone();
|
||||
const serviceListItem = $(
|
||||
`<li class="list-group-item d-flex align-items-center" style="flex: 1 1 0;"></li>`,
|
||||
`<li class="list-group-item" style="flex: 1 1 0;"></li>`,
|
||||
);
|
||||
serviceListItem.append(serviceClone.removeClass("highlight"));
|
||||
list.append(serviceListItem);
|
||||
|
|
@ -121,15 +120,28 @@ $(document).ready(function () {
|
|||
};
|
||||
|
||||
const layout = {
|
||||
topStart: {},
|
||||
bottomEnd: {},
|
||||
bottom1: {
|
||||
top1: {
|
||||
searchPanes: {
|
||||
viewTotal: true,
|
||||
cascadePanes: true,
|
||||
collapse: false,
|
||||
columns: [2, 3, 4, 5],
|
||||
},
|
||||
},
|
||||
topStart: {},
|
||||
topEnd: {
|
||||
buttons: [
|
||||
{
|
||||
extend: "toggle_filters",
|
||||
className: "btn btn-sm btn-outline-primary toggle-filters",
|
||||
},
|
||||
],
|
||||
search: true,
|
||||
},
|
||||
bottomStart: {
|
||||
info: true,
|
||||
},
|
||||
bottomEnd: {},
|
||||
};
|
||||
|
||||
if (configNumber > 10) {
|
||||
|
|
@ -144,8 +156,11 @@ $(document).ready(function () {
|
|||
menu.push(100);
|
||||
}
|
||||
menu.push({ label: "All", value: -1 });
|
||||
layout.topStart.pageLength = {
|
||||
menu: menu,
|
||||
layout.bottomStart = {
|
||||
pageLength: {
|
||||
menu: menu,
|
||||
},
|
||||
info: true,
|
||||
};
|
||||
layout.bottomEnd.paging = true;
|
||||
}
|
||||
|
|
@ -208,7 +223,7 @@ $(document).ready(function () {
|
|||
{
|
||||
extend: "collection",
|
||||
text: '<span class="tf-icons bx bx-play bx-18px me-2"></span>Actions',
|
||||
className: "btn btn-sm btn-outline-primary",
|
||||
className: "btn btn-sm btn-outline-primary action-button disabled",
|
||||
buttons: [
|
||||
{
|
||||
extend: "delete_configs",
|
||||
|
|
@ -248,6 +263,15 @@ $(document).ready(function () {
|
|||
return configs;
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
configs_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.create_config = {
|
||||
text: '<span class="tf-icons bx bx-plus"></span> Create<span class="d-none d-md-inline"> new custom config</span>',
|
||||
className: `btn btn-sm btn-outline-bw-green${
|
||||
|
|
@ -307,62 +331,58 @@ $(document).ready(function () {
|
|||
show: true,
|
||||
options: [
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-window-alt"></i> HTTP',
|
||||
label: '<i class="bx bx-xs bx-window-alt"></i>HTTP',
|
||||
value: function (rowData, rowIdx) {
|
||||
return / HTTP$/.test(rowData[2].trim());
|
||||
$(rowData[2]).text().trim() === "HTTP";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-window-alt"></i> SERVER_HTTP',
|
||||
label: '<i class="bx bx-xs bx-window-alt"></i>SERVER_HTTP',
|
||||
value: function (rowData, rowIdx) {
|
||||
return / SERVER_HTTP$/.test(rowData[2].trim());
|
||||
return $(rowData[2]).text().trim() === "SERVER_HTTP";
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-window-alt"></i> DEFAULT_SERVER_HTTP',
|
||||
'<i class="bx bx-xs bx-window-alt"></i>DEFAULT_SERVER_HTTP',
|
||||
value: function (rowData, rowIdx) {
|
||||
return / DEFAULT_SERVER_HTTP$/.test(rowData[2].trim());
|
||||
return $(rowData[2]).text().trim() === "DEFAULT_SERVER_HTTP";
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-shield-quarter"></i> MODSEC_CRS',
|
||||
label: '<i class="bx bx-xs bx-shield-quarter"></i>MODSEC_CRS',
|
||||
value: function (rowData, rowIdx) {
|
||||
return / MODSEC_CRS$/.test(rowData[2].trim());
|
||||
return $(rowData[2]).text().trim() === "MODSEC_CRS";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-shield-alt-2"></i> MODSEC',
|
||||
label: '<i class="bx bx-xs bx-shield-alt-2"></i>MODSEC',
|
||||
value: function (rowData, rowIdx) {
|
||||
return / MODSEC$/.test(rowData[2].trim());
|
||||
return $(rowData[2]).text().trim() === "MODSEC";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '<i class="bx bx-xs bx-network-chart"></i> STREAM',
|
||||
label: '<i class="bx bx-xs bx-network-chart"></i>STREAM',
|
||||
value: function (rowData, rowIdx) {
|
||||
return / STREAM$/.test(rowData[2].trim());
|
||||
return $(rowData[2]).text().trim() === "STREAM";
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-network-chart"></i> SERVER_STREAM',
|
||||
label: '<i class="bx bx-xs bx-network-chart"></i>SERVER_STREAM',
|
||||
value: function (rowData, rowIdx) {
|
||||
return / SERVER_STREAM$/.test(rowData[2].trim());
|
||||
return $(rowData[2]).text().trim() === "SERVER_STREAM";
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-shield-alt"></i> CRS_PLUGINS_BEFORE',
|
||||
label: '<i class="bx bx-xs bx-shield-alt"></i>CRS_PLUGINS_BEFORE',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[2].includes("BEFORE");
|
||||
return $(rowData[2]).text().trim() === "CRS_PLUGINS_BEFORE";
|
||||
},
|
||||
},
|
||||
{
|
||||
label:
|
||||
'<i class="bx bx-xs bx-shield-alt"></i> CRS_PLUGINS_AFTER',
|
||||
label: '<i class="bx bx-xs bx-shield-alt"></i>CRS_PLUGINS_AFTER',
|
||||
value: function (rowData, rowIdx) {
|
||||
return rowData[2].includes("AFTER");
|
||||
return $(rowData[2]).text().trim() === "CRS_PLUGINS_AFTER";
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
@ -394,12 +414,6 @@ $(document).ready(function () {
|
|||
},
|
||||
targets: 5,
|
||||
},
|
||||
{
|
||||
targets: "_all", // Target all columns
|
||||
createdCell: function (td, cellData, rowData, row, col) {
|
||||
$(td).addClass("align-items-center"); // Apply 'text-center' class to <td>
|
||||
},
|
||||
},
|
||||
],
|
||||
order: [[1, "asc"]],
|
||||
autoFill: false,
|
||||
|
|
@ -426,7 +440,6 @@ $(document).ready(function () {
|
|||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#configs_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
$("#configs_wrapper th").addClass("text-center");
|
||||
if (isReadOnly)
|
||||
$("#configs_wrapper .dt-buttons")
|
||||
.attr(
|
||||
|
|
@ -446,6 +459,9 @@ $(document).ready(function () {
|
|||
"click",
|
||||
);
|
||||
|
||||
if (!configTypeSelection && !configServiceSelection)
|
||||
configs_table.searchPanes.container().hide();
|
||||
|
||||
$("#configs").removeClass("d-none");
|
||||
$("#configs-waiting").addClass("visually-hidden");
|
||||
|
||||
|
|
@ -474,6 +490,19 @@ $(document).ready(function () {
|
|||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
configs_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button").removeClass("disabled");
|
||||
});
|
||||
|
||||
configs_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (configs_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button").addClass("disabled");
|
||||
$("#select-all-rows").prop("checked", false);
|
||||
}
|
||||
});
|
||||
|
||||
// Event listener for the select-all checkbox
|
||||
$("#select-all-rows").on("change", function () {
|
||||
const isChecked = $(this).prop("checked");
|
||||
|
|
|
|||
|
|
@ -66,13 +66,13 @@ $(document).ready(function () {
|
|||
$("#selected-instances-input").val(instances.join(","));
|
||||
|
||||
const list = $(
|
||||
`<ul class="list-group list-group-horizontal d-flex w-100">
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 0;">
|
||||
`<ul class="list-group list-group-horizontal w-100">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 0;">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">Hostname</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 0;">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 0;">
|
||||
<div class="fw-bold">Health</div>
|
||||
</li>
|
||||
</ul>`,
|
||||
|
|
@ -82,12 +82,11 @@ $(document).ready(function () {
|
|||
const delete_modal = $("#modal-delete-instances");
|
||||
instances.forEach((instance) => {
|
||||
const list = $(
|
||||
`<ul class="list-group list-group-horizontal d-flex w-100"></ul>`,
|
||||
`<ul class="list-group list-group-horizontal w-100"></ul>`,
|
||||
);
|
||||
|
||||
// Create the list item using template literals
|
||||
const listItem =
|
||||
$(`<li class="list-group-item align-items-center" style="flex: 1 0;">
|
||||
const listItem = $(`<li class="list-group-item" style="flex: 1 0;">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">${instance}</div>
|
||||
</div>
|
||||
|
|
@ -97,7 +96,7 @@ $(document).ready(function () {
|
|||
// Clone the status element and append it to the list item
|
||||
const statusClone = $("#status-" + instance).clone();
|
||||
const statusListItem = $(
|
||||
`<li class="list-group-item d-flex align-items-center justify-content-center" style="flex: 1 0;"></li>`,
|
||||
`<li class="list-group-item" style="flex: 1 0;"></li>`,
|
||||
);
|
||||
statusListItem.append(statusClone.removeClass("highlight"));
|
||||
list.append(statusListItem);
|
||||
|
|
@ -139,15 +138,28 @@ $(document).ready(function () {
|
|||
};
|
||||
|
||||
const layout = {
|
||||
topStart: {},
|
||||
bottomEnd: {},
|
||||
bottom1: {
|
||||
top1: {
|
||||
searchPanes: {
|
||||
viewTotal: true,
|
||||
cascadePanes: true,
|
||||
collapse: false,
|
||||
columns: [3, 4, 5, 6, 7],
|
||||
},
|
||||
},
|
||||
topStart: {},
|
||||
topEnd: {
|
||||
buttons: [
|
||||
{
|
||||
extend: "toggle_filters",
|
||||
className: "btn btn-sm btn-outline-primary toggle-filters",
|
||||
},
|
||||
],
|
||||
search: true,
|
||||
},
|
||||
bottomStart: {
|
||||
info: true,
|
||||
},
|
||||
bottomEnd: {},
|
||||
};
|
||||
|
||||
if (instanceNumber > 10) {
|
||||
|
|
@ -162,8 +174,11 @@ $(document).ready(function () {
|
|||
menu.push(100);
|
||||
}
|
||||
menu.push({ label: "All", value: -1 });
|
||||
layout.topStart.pageLength = {
|
||||
menu: menu,
|
||||
layout.bottomStart = {
|
||||
pageLength: {
|
||||
menu: menu,
|
||||
},
|
||||
info: true,
|
||||
};
|
||||
layout.bottomEnd.paging = true;
|
||||
}
|
||||
|
|
@ -226,7 +241,7 @@ $(document).ready(function () {
|
|||
{
|
||||
extend: "collection",
|
||||
text: '<span class="tf-icons bx bx-play bx-18px me-2"></span>Actions',
|
||||
className: "btn btn-sm btn-outline-primary",
|
||||
className: "btn btn-sm btn-outline-primary action-button disabled",
|
||||
buttons: [
|
||||
{
|
||||
extend: "ping_instances",
|
||||
|
|
@ -287,6 +302,15 @@ $(document).ready(function () {
|
|||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
instances_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.ping_instances = {
|
||||
text: '<span class="tf-icons bx bx-bell bx-18px me-2"></span>Ping',
|
||||
action: function (e, dt, node, config) {
|
||||
|
|
@ -527,12 +551,6 @@ $(document).ready(function () {
|
|||
},
|
||||
targets: 7,
|
||||
},
|
||||
{
|
||||
targets: "_all", // Target all columns
|
||||
createdCell: function (td, cellData, rowData, row, col) {
|
||||
$(td).addClass("align-items-center"); // Apply 'text-center' class to <td>
|
||||
},
|
||||
},
|
||||
],
|
||||
order: [[7, "desc"]],
|
||||
autoFill: false,
|
||||
|
|
@ -559,7 +577,6 @@ $(document).ready(function () {
|
|||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#instances_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
$("#instances_wrapper th").addClass("text-center");
|
||||
if (isReadOnly)
|
||||
$("#instances_wrapper .dt-buttons")
|
||||
.attr(
|
||||
|
|
@ -571,6 +588,8 @@ $(document).ready(function () {
|
|||
},
|
||||
});
|
||||
|
||||
instances_table.searchPanes.container().hide();
|
||||
|
||||
$("#instances").removeClass("d-none");
|
||||
$("#instances-waiting").addClass("visually-hidden");
|
||||
|
||||
|
|
@ -599,6 +618,19 @@ $(document).ready(function () {
|
|||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
instances_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button").removeClass("disabled");
|
||||
});
|
||||
|
||||
instances_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (instances_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button").addClass("disabled");
|
||||
$("#select-all-rows").prop("checked", false);
|
||||
}
|
||||
});
|
||||
|
||||
// Event listener for the select-all checkbox
|
||||
$("#select-all-rows").on("change", function () {
|
||||
const isChecked = $(this).prop("checked");
|
||||
|
|
|
|||
|
|
@ -4,15 +4,28 @@ $(document).ready(function () {
|
|||
const isReadOnly = $("#is-read-only").val().trim() === "True";
|
||||
|
||||
const layout = {
|
||||
topStart: {},
|
||||
bottomEnd: {},
|
||||
bottom1: {
|
||||
top1: {
|
||||
searchPanes: {
|
||||
viewTotal: true,
|
||||
cascadePanes: true,
|
||||
collapse: false,
|
||||
columns: [2, 3, 4, 5],
|
||||
},
|
||||
},
|
||||
topStart: {},
|
||||
topEnd: {
|
||||
buttons: [
|
||||
{
|
||||
extend: "toggle_filters",
|
||||
className: "btn btn-sm btn-outline-primary toggle-filters",
|
||||
},
|
||||
],
|
||||
search: true,
|
||||
},
|
||||
bottomStart: {
|
||||
info: true,
|
||||
},
|
||||
bottomEnd: {},
|
||||
};
|
||||
|
||||
if (jobNumber > 10) {
|
||||
|
|
@ -27,13 +40,20 @@ $(document).ready(function () {
|
|||
menu.push(100);
|
||||
}
|
||||
menu.push({ label: "All", value: -1 });
|
||||
layout.topStart.pageLength = {
|
||||
menu: menu,
|
||||
layout.bottomStart = {
|
||||
pageLength: {
|
||||
menu: menu,
|
||||
},
|
||||
info: true,
|
||||
};
|
||||
layout.bottomEnd.paging = true;
|
||||
}
|
||||
|
||||
layout.topStart.buttons = [
|
||||
{
|
||||
extend: "toggle_filters",
|
||||
className: "btn btn-sm btn-outline-primary toggle-filters",
|
||||
},
|
||||
{
|
||||
extend: "colvis",
|
||||
columns: "th:not(:first-child)",
|
||||
|
|
@ -88,7 +108,7 @@ $(document).ready(function () {
|
|||
{
|
||||
extend: "collection",
|
||||
text: '<span class="tf-icons bx bx-play bx-18px me-2"></span>Actions',
|
||||
className: "btn btn-sm btn-outline-primary",
|
||||
className: "btn btn-sm btn-outline-primary action-button disabled",
|
||||
buttons: [
|
||||
{
|
||||
extend: "run_jobs",
|
||||
|
|
@ -135,6 +155,15 @@ $(document).ready(function () {
|
|||
form.appendTo("body").submit();
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
jobs_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.run_jobs = {
|
||||
text: '<span class="tf-icons bx bx-play bx-18px me-2"></span>Run selected jobs',
|
||||
action: function (e, dt, node, config) {
|
||||
|
|
@ -280,12 +309,6 @@ $(document).ready(function () {
|
|||
},
|
||||
targets: 5,
|
||||
},
|
||||
{
|
||||
targets: "_all", // Target all columns
|
||||
createdCell: function (td, cellData, rowData, row, col) {
|
||||
$(td).addClass("align-items-center"); // Apply 'text-center' class to <td>
|
||||
},
|
||||
},
|
||||
],
|
||||
order: [[2, "asc"]],
|
||||
autoFill: false,
|
||||
|
|
@ -312,10 +335,11 @@ $(document).ready(function () {
|
|||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#jobs_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
$("#jobs_wrapper th").addClass("text-center");
|
||||
},
|
||||
});
|
||||
|
||||
jobs_table.searchPanes.container().hide();
|
||||
|
||||
$("#jobs").removeClass("d-none");
|
||||
$("#jobs-waiting").addClass("visually-hidden");
|
||||
|
||||
|
|
@ -344,6 +368,19 @@ $(document).ready(function () {
|
|||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
jobs_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button").removeClass("disabled");
|
||||
});
|
||||
|
||||
jobs_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (jobs_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button").addClass("disabled");
|
||||
$("#select-all-rows").prop("checked", false);
|
||||
}
|
||||
});
|
||||
|
||||
// Event listener for the select-all checkbox
|
||||
$("#select-all-rows").on("change", function () {
|
||||
const isChecked = $(this).prop("checked");
|
||||
|
|
|
|||
|
|
@ -9,16 +9,16 @@ $(document).ready(function () {
|
|||
const setupDeletionModal = (plugins) => {
|
||||
const delete_modal = $("#modal-delete-plugins");
|
||||
const list = $(
|
||||
`<ul class="list-group list-group-horizontal d-flex w-100">
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 1 0;">
|
||||
`<ul class="list-group list-group-horizontal w-100">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 1 0;">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">Name</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 1 0;">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 1 0;">
|
||||
<div class="fw-bold">Version</div>
|
||||
</li>
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 1 0;">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 1 0;">
|
||||
<div class="fw-bold">Type</div>
|
||||
</li>
|
||||
</ul>`,
|
||||
|
|
@ -27,12 +27,11 @@ $(document).ready(function () {
|
|||
|
||||
plugins.forEach((plugin) => {
|
||||
const list = $(
|
||||
`<ul class="list-group list-group-horizontal d-flex w-100"></ul>`,
|
||||
`<ul class="list-group list-group-horizontal w-100"></ul>`,
|
||||
);
|
||||
|
||||
// Create the list item using template literals
|
||||
const listItem =
|
||||
$(`<li class="list-group-item align-items-center" style="flex: 1 1 0;">
|
||||
const listItem = $(`<li class="list-group-item" style="flex: 1 1 0;">
|
||||
<div class="ms-2 me-auto">
|
||||
${$(`#name-${plugin}`).html()}
|
||||
</div>
|
||||
|
|
@ -42,7 +41,7 @@ $(document).ready(function () {
|
|||
// Clone the version element and append it to the list item
|
||||
const versionClone = $(`#version-${plugin}`).clone();
|
||||
const versionListItem = $(
|
||||
`<li class="list-group-item d-flex align-items-center" style="flex: 1 1 0;"></li>`,
|
||||
`<li class="list-group-item" style="flex: 1 1 0;"></li>`,
|
||||
);
|
||||
versionListItem.append(versionClone.removeClass("highlight"));
|
||||
list.append(versionListItem);
|
||||
|
|
@ -50,7 +49,7 @@ $(document).ready(function () {
|
|||
// Clone the type element and append it to the list item
|
||||
const typeClone = $(`#type-${plugin}`).clone();
|
||||
const typeListItem = $(
|
||||
`<li class="list-group-item d-flex align-items-center" style="flex: 1 1 0;"></li>`,
|
||||
`<li class="list-group-item" style="flex: 1 1 0;"></li>`,
|
||||
);
|
||||
typeListItem.append(typeClone.removeClass("highlight"));
|
||||
list.append(typeListItem);
|
||||
|
|
@ -152,15 +151,28 @@ $(document).ready(function () {
|
|||
};
|
||||
|
||||
const layout = {
|
||||
topStart: {},
|
||||
bottomEnd: {},
|
||||
bottom1: {
|
||||
top1: {
|
||||
searchPanes: {
|
||||
viewTotal: true,
|
||||
cascadePanes: true,
|
||||
collapse: false,
|
||||
columns: [5, 6, 7],
|
||||
},
|
||||
},
|
||||
topStart: {},
|
||||
topEnd: {
|
||||
buttons: [
|
||||
{
|
||||
extend: "toggle_filters",
|
||||
className: "btn btn-sm btn-outline-primary toggle-filters",
|
||||
},
|
||||
],
|
||||
search: true,
|
||||
},
|
||||
bottomStart: {
|
||||
info: true,
|
||||
},
|
||||
bottomEnd: {},
|
||||
};
|
||||
|
||||
if (pluginNumber > 10) {
|
||||
|
|
@ -175,8 +187,11 @@ $(document).ready(function () {
|
|||
menu.push(100);
|
||||
}
|
||||
menu.push({ label: "All", value: -1 });
|
||||
layout.topStart.pageLength = {
|
||||
menu: menu,
|
||||
layout.bottomStart = {
|
||||
pageLength: {
|
||||
menu: menu,
|
||||
},
|
||||
info: true,
|
||||
};
|
||||
layout.bottomEnd.paging = true;
|
||||
}
|
||||
|
|
@ -239,7 +254,7 @@ $(document).ready(function () {
|
|||
{
|
||||
extend: "collection",
|
||||
text: '<span class="tf-icons bx bx-play bx-18px me-2"></span>Actions',
|
||||
className: "btn btn-sm btn-outline-primary",
|
||||
className: "btn btn-sm btn-outline-primary action-button disabled",
|
||||
buttons: [
|
||||
{
|
||||
extend: "delete_plugins",
|
||||
|
|
@ -265,6 +280,15 @@ $(document).ready(function () {
|
|||
return plugins;
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
plugins_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.add_plugin = {
|
||||
text: '<span class="tf-icons bx bx-plus"></span> Add<span class="d-none d-md-inline"> plugin(s)</span>',
|
||||
className: `btn btn-sm btn-outline-bw-green${
|
||||
|
|
@ -395,12 +419,6 @@ $(document).ready(function () {
|
|||
},
|
||||
targets: 7,
|
||||
},
|
||||
{
|
||||
targets: "_all", // Target all columns
|
||||
createdCell: function (td, cellData, rowData, row, col) {
|
||||
$(td).addClass("align-items-center"); // Apply 'text-center' class to <td>
|
||||
},
|
||||
},
|
||||
],
|
||||
order: [[2, "asc"]],
|
||||
autoFill: false,
|
||||
|
|
@ -424,16 +442,9 @@ $(document).ready(function () {
|
|||
1: "Selected 1 plugin",
|
||||
},
|
||||
},
|
||||
// searchPanes: {
|
||||
// collapse: {
|
||||
// 0: '<span class="tf-icons bx bx-search bx-18px me-2"></span>Filters',
|
||||
// _: '<span class="tf-icons bx bx-search bx-18px me-2"></span>Filters (%d)',
|
||||
// },
|
||||
// },
|
||||
},
|
||||
initComplete: function (settings, json) {
|
||||
$("#plugins_wrapper .btn-secondary").removeClass("btn-secondary");
|
||||
$("#plugins_wrapper th").addClass("text-center");
|
||||
if (isReadOnly)
|
||||
$("#plugins_wrapper .dt-buttons")
|
||||
.attr(
|
||||
|
|
@ -445,6 +456,8 @@ $(document).ready(function () {
|
|||
},
|
||||
});
|
||||
|
||||
plugins_table.searchPanes.container().hide();
|
||||
|
||||
$("#plugins").removeClass("d-none");
|
||||
$("#plugins-waiting").addClass("visually-hidden");
|
||||
|
||||
|
|
@ -473,6 +486,19 @@ $(document).ready(function () {
|
|||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
plugins_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button").removeClass("disabled");
|
||||
});
|
||||
|
||||
plugins_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (plugins_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button").addClass("disabled");
|
||||
$("#select-all-rows").prop("checked", false);
|
||||
}
|
||||
});
|
||||
|
||||
// Event listener for the select-all checkbox
|
||||
$("#select-all-rows").on("change", function () {
|
||||
const isChecked = $(this).prop("checked");
|
||||
|
|
|
|||
|
|
@ -291,15 +291,28 @@ $(function () {
|
|||
};
|
||||
|
||||
const layout = {
|
||||
topStart: {},
|
||||
bottomEnd: {},
|
||||
bottom1: {
|
||||
top1: {
|
||||
searchPanes: {
|
||||
viewTotal: true,
|
||||
cascadePanes: true,
|
||||
collapse: false,
|
||||
columns: [1, 2, 3, 4, 5, 7],
|
||||
},
|
||||
},
|
||||
topStart: {},
|
||||
topEnd: {
|
||||
buttons: [
|
||||
{
|
||||
extend: "toggle_filters",
|
||||
className: "btn btn-sm btn-outline-primary toggle-filters",
|
||||
},
|
||||
],
|
||||
search: true,
|
||||
},
|
||||
bottomStart: {
|
||||
info: true,
|
||||
},
|
||||
bottomEnd: {},
|
||||
};
|
||||
|
||||
if (reportsNumber > 10) {
|
||||
|
|
@ -308,7 +321,12 @@ $(function () {
|
|||
if (reportsNumber > 50) menu.push(50);
|
||||
if (reportsNumber > 100) menu.push(100);
|
||||
menu.push({ label: "All", value: -1 });
|
||||
layout.topStart.pageLength = { menu };
|
||||
layout.bottomStart = {
|
||||
pageLength: {
|
||||
menu: menu,
|
||||
},
|
||||
info: true,
|
||||
};
|
||||
layout.bottomEnd.paging = true;
|
||||
}
|
||||
|
||||
|
|
@ -360,6 +378,15 @@ $(function () {
|
|||
},
|
||||
];
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
reports_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$(".report-date").each(function () {
|
||||
const $this = $(this);
|
||||
const isoDateStr = $this.text().trim();
|
||||
|
|
@ -386,12 +413,6 @@ $(function () {
|
|||
searchPanes: { show: true },
|
||||
targets: [1, 2, 3, 4, 5, 7],
|
||||
},
|
||||
{
|
||||
targets: "_all",
|
||||
createdCell: function (td) {
|
||||
$(td).addClass("align-items-center");
|
||||
},
|
||||
},
|
||||
],
|
||||
order: [[0, "desc"]],
|
||||
autoFill: false,
|
||||
|
|
@ -414,11 +435,12 @@ $(function () {
|
|||
initComplete: function () {
|
||||
const $wrapper = $("#reports_wrapper");
|
||||
$wrapper.find(".btn-secondary").removeClass("btn-secondary");
|
||||
$wrapper.find("th").addClass("text-center");
|
||||
updateCountryTooltips();
|
||||
},
|
||||
});
|
||||
|
||||
reports_table.searchPanes.container().hide();
|
||||
|
||||
$("#reports").removeClass("d-none");
|
||||
$("#reports-waiting").addClass("visually-hidden");
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ $(function () {
|
|||
|
||||
const setupModal = (services, modal) => {
|
||||
const headerList = $(`
|
||||
<ul class="list-group list-group-horizontal d-flex w-100">
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 0;">
|
||||
<ul class="list-group list-group-horizontal w-100">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 0;">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">Service name</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="list-group-item align-items-center text-center bg-secondary text-white" style="flex: 1 0;">
|
||||
<li class="list-group-item bg-secondary text-white" style="flex: 1 0;">
|
||||
<div class="fw-bold">Type</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -22,11 +22,11 @@ $(function () {
|
|||
services.forEach((service) => {
|
||||
const sanitizedService = service.replace(/\./g, "-");
|
||||
const serviceList = $(
|
||||
'<ul class="list-group list-group-horizontal d-flex w-100"></ul>',
|
||||
'<ul class="list-group list-group-horizontal w-100"></ul>',
|
||||
);
|
||||
|
||||
const listItem = $(`
|
||||
<li class="list-group-item align-items-center" style="flex: 1 0;">
|
||||
<li class="list-group-item" style="flex: 1 0;">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">${service}</div>
|
||||
</div>
|
||||
|
|
@ -38,7 +38,7 @@ $(function () {
|
|||
.clone()
|
||||
.removeClass("highlight");
|
||||
const typeListItem = $(`
|
||||
<li class="list-group-item d-flex align-items-center justify-content-center" style="flex: 1 0;"></li>
|
||||
<li class="list-group-item" style="flex: 1 0;"></li>
|
||||
`);
|
||||
typeListItem.append(typeClone);
|
||||
serviceList.append(typeListItem);
|
||||
|
|
@ -85,15 +85,28 @@ $(function () {
|
|||
};
|
||||
|
||||
const layout = {
|
||||
topStart: {},
|
||||
bottomEnd: {},
|
||||
bottom1: {
|
||||
top1: {
|
||||
searchPanes: {
|
||||
viewTotal: true,
|
||||
cascadePanes: true,
|
||||
collapse: false,
|
||||
columns: [2, 3, 4, 5],
|
||||
},
|
||||
},
|
||||
topStart: {},
|
||||
topEnd: {
|
||||
buttons: [
|
||||
{
|
||||
extend: "toggle_filters",
|
||||
className: "btn btn-sm btn-outline-primary toggle-filters",
|
||||
},
|
||||
],
|
||||
search: true,
|
||||
},
|
||||
bottomStart: {
|
||||
info: true,
|
||||
},
|
||||
bottomEnd: {},
|
||||
};
|
||||
|
||||
if (serviceNumber > 10) {
|
||||
|
|
@ -102,7 +115,12 @@ $(function () {
|
|||
if (serviceNumber > num) menu.push(num);
|
||||
});
|
||||
menu.push({ label: "All", value: -1 });
|
||||
layout.topStart.pageLength = { menu };
|
||||
layout.bottomStart = {
|
||||
pageLength: {
|
||||
menu: menu,
|
||||
},
|
||||
info: true,
|
||||
};
|
||||
layout.bottomEnd.paging = true;
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +176,7 @@ $(function () {
|
|||
{
|
||||
extend: "collection",
|
||||
text: '<span class="tf-icons bx bx-play bx-18px me-2"></span>Actions',
|
||||
className: "btn btn-sm btn-outline-primary",
|
||||
className: "btn btn-sm btn-outline-primary action-button disabled",
|
||||
buttons: [
|
||||
{
|
||||
extend: "convert_services",
|
||||
|
|
@ -205,6 +223,15 @@ $(function () {
|
|||
})
|
||||
.get();
|
||||
|
||||
$.fn.dataTable.ext.buttons.toggle_filters = {
|
||||
text: '<span class="tf-icons bx bx-filter bx-18px me-2"></span><span id="show-filters">Show</span><span id="hide-filters" class="d-none">Hide</span><span class="d-none d-md-inline"> filters</span>',
|
||||
action: function (e, dt, node, config) {
|
||||
services_table.searchPanes.container().slideToggle(); // Smoothly hide or show the container
|
||||
$("#show-filters").toggleClass("d-none"); // Toggle the visibility of the 'Show' span
|
||||
$("#hide-filters").toggleClass("d-none"); // Toggle the visibility of the 'Hide' span
|
||||
},
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.buttons.create_service = {
|
||||
text: '<span class="tf-icons bx bx-plus"></span> Create<span class="d-none d-md-inline"> new service</span>',
|
||||
className: `btn btn-sm btn-outline-bw-green${
|
||||
|
|
@ -373,10 +400,6 @@ $(function () {
|
|||
},
|
||||
targets: 5,
|
||||
},
|
||||
{
|
||||
targets: "_all",
|
||||
createdCell: (td) => $(td).addClass("align-items-center"),
|
||||
},
|
||||
],
|
||||
order: [[5, "desc"]],
|
||||
autoFill: false,
|
||||
|
|
@ -410,7 +433,6 @@ $(function () {
|
|||
initComplete: function () {
|
||||
const $wrapper = $("#services_wrapper");
|
||||
$wrapper.find(".btn-secondary").removeClass("btn-secondary");
|
||||
$wrapper.find("th").addClass("text-center");
|
||||
if (isReadOnly) {
|
||||
$wrapper
|
||||
.find(".dt-buttons")
|
||||
|
|
@ -424,6 +446,8 @@ $(function () {
|
|||
},
|
||||
});
|
||||
|
||||
services_table.searchPanes.container().hide();
|
||||
|
||||
$("#services").removeClass("d-none");
|
||||
$("#services-waiting").addClass("visually-hidden");
|
||||
|
||||
|
|
@ -452,6 +476,19 @@ $(function () {
|
|||
.each((el) => el.classList.remove("highlight"));
|
||||
});
|
||||
|
||||
services_table.on("select", function (e, dt, type, indexes) {
|
||||
// Enable the actions button
|
||||
$(".action-button").removeClass("disabled");
|
||||
});
|
||||
|
||||
services_table.on("deselect", function (e, dt, type, indexes) {
|
||||
// If no rows are selected, disable the actions button
|
||||
if (services_table.rows({ selected: true }).count() === 0) {
|
||||
$(".action-button").addClass("disabled");
|
||||
$("#select-all-rows").prop("checked", false);
|
||||
}
|
||||
});
|
||||
|
||||
// Event listener for the select-all checkbox
|
||||
$("#select-all-rows").on("change", function () {
|
||||
const isChecked = $(this).prop("checked");
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ $(document).ready(() => {
|
|||
// Cache jQuery selectors for performance
|
||||
const $window = $(window);
|
||||
const $passwordInput = $("#password");
|
||||
const $2faInput = $("#2fa_code");
|
||||
// const $2faInput = $("#2fa_code");
|
||||
const $confirmPasswordInput = $("#confirm_password");
|
||||
const $serverNameInput = $("#SERVER_NAME");
|
||||
const $overview2faEnabled = $("#overview-2fa-enabled");
|
||||
// const $overview2faEnabled = $("#overview-2fa-enabled");
|
||||
const $overviewUniqueServerName = $("#overview-unique-server-name");
|
||||
const $saveSettingsButton = $(".save-settings");
|
||||
const $previousStepButton = $(".previous-step");
|
||||
|
|
@ -355,19 +355,9 @@ $(document).ready(() => {
|
|||
|
||||
if (adminEmail) {
|
||||
$overviewEmail.val(adminEmail);
|
||||
$overviewEmail.closest(".col-12").removeClass("d-none");
|
||||
$overviewUsername.closest(".col-12").addClass("col-md-4");
|
||||
$overviewPassword
|
||||
.closest(".col-12")
|
||||
.addClass("col-md-4")
|
||||
.removeClass("col-sm-6");
|
||||
$overviewEmail.parent().removeClass("d-none");
|
||||
} else {
|
||||
$overviewEmail.closest(".col-12").addClass("d-none");
|
||||
$overviewUsername.closest(".col-12").removeClass("col-md-4");
|
||||
$overviewPassword
|
||||
.closest(".col-12")
|
||||
.removeClass("col-md-4")
|
||||
.addClass("col-sm-6");
|
||||
$overviewEmail.parent().addClass("d-none");
|
||||
}
|
||||
$overviewUsername.val($("#username").val());
|
||||
$overviewPassword.val($("#password").val());
|
||||
|
|
@ -500,7 +490,7 @@ $(document).ready(() => {
|
|||
formData.append("admin_email", $("#email").val());
|
||||
formData.append("admin_password", $("#password").val());
|
||||
formData.append("admin_password_check", $("#confirm_password").val());
|
||||
formData.append("2fa_code", $("#2fa_code").val());
|
||||
// formData.append("2fa_code", $("#2fa_code").val());
|
||||
}
|
||||
|
||||
if (!uiReverseProxy) {
|
||||
|
|
@ -526,7 +516,7 @@ $(document).ready(() => {
|
|||
if (!ui_url.startsWith("/")) {
|
||||
api = `${api}/`;
|
||||
}
|
||||
api = `${api}${ui_url}/check`;
|
||||
api = `${api}${ui_url}${ui_url !== "/" ? "/" : ""}check`;
|
||||
var redirect = `https://${server_name}/setup/loading?target_endpoint=${api}`;
|
||||
} else {
|
||||
var redirect = window.location.href.replace("setup", "login");
|
||||
|
|
@ -572,35 +562,41 @@ $(document).ready(() => {
|
|||
handleStepNavigation(isNext, confirmDNS);
|
||||
});
|
||||
|
||||
$2faInput.on("input", function () {
|
||||
if (uiUser) return;
|
||||
|
||||
const $this = $(this);
|
||||
const value = $this.val();
|
||||
const isValid = /^[0-9]{6}$/.test(value);
|
||||
updateValidationState(this, isValid);
|
||||
|
||||
$overview2faEnabled
|
||||
.find("i")
|
||||
.toggleClass("bx-x text-danger bx-check text-success", false)
|
||||
.toggleClass("bx-question-mark text-warning", value === "");
|
||||
$overview2faEnabled.tooltip("enable");
|
||||
if (value) {
|
||||
if (isValid) {
|
||||
$overview2faEnabled
|
||||
.find("i")
|
||||
.toggleClass("bx-x text-danger", false)
|
||||
.toggleClass("bx-check text-success", true);
|
||||
$overview2faEnabled.tooltip("disable");
|
||||
} else {
|
||||
$overview2faEnabled
|
||||
.find("i")
|
||||
.toggleClass("bx-check text-success", false)
|
||||
.toggleClass("bx-x text-danger", true);
|
||||
}
|
||||
$(document).on("keydown", ".plugin-setting", function (e) {
|
||||
if (e.key === "Enter" || e.keyCode === 13) {
|
||||
$("#next-step").trigger("click");
|
||||
}
|
||||
});
|
||||
|
||||
// $2faInput.on("input", function () {
|
||||
// if (uiUser) return;
|
||||
|
||||
// const $this = $(this);
|
||||
// const value = $this.val();
|
||||
// const isValid = /^[0-9]{6}$/.test(value);
|
||||
// updateValidationState(this, isValid);
|
||||
|
||||
// $overview2faEnabled
|
||||
// .find("i")
|
||||
// .toggleClass("bx-x text-danger bx-check text-success", false)
|
||||
// .toggleClass("bx-question-mark text-warning", value === "");
|
||||
// $overview2faEnabled.tooltip("enable");
|
||||
// if (value) {
|
||||
// if (isValid) {
|
||||
// $overview2faEnabled
|
||||
// .find("i")
|
||||
// .toggleClass("bx-x text-danger", false)
|
||||
// .toggleClass("bx-check text-success", true);
|
||||
// $overview2faEnabled.tooltip("disable");
|
||||
// } else {
|
||||
// $overview2faEnabled
|
||||
// .find("i")
|
||||
// .toggleClass("bx-check text-success", false)
|
||||
// .toggleClass("bx-x text-danger", true);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
// Before Unload Event to Warn Users About Unsaved Changes
|
||||
$window.on("beforeunload", function (e) {
|
||||
const message =
|
||||
|
|
|
|||
|
|
@ -260,9 +260,6 @@ class News {
|
|||
|
||||
// Initialize the News class
|
||||
$(document).ready(() => {
|
||||
const news = new News();
|
||||
news.init();
|
||||
|
||||
// Define news items array
|
||||
let newsItems = [
|
||||
`Get the most of BunkerWeb by upgrading to the PRO version. More info and free trial <a class="light-href text-white-80" target="_blank" rel="noopener" href="https://panel.bunkerweb.io/?utm_campaign=self&utm_source=banner#pro">here</a>.`,
|
||||
|
|
@ -274,6 +271,32 @@ $(document).ready(() => {
|
|||
const intervalTime = 7000;
|
||||
let interval;
|
||||
const $bannerText = $("#banner-text");
|
||||
// Create a hidden element to measure the max height
|
||||
const $measuringElement = $("<div>")
|
||||
.css({
|
||||
position: "absolute",
|
||||
visibility: "hidden",
|
||||
height: "auto",
|
||||
width: $bannerText.width(),
|
||||
whiteSpace: "nowrap",
|
||||
})
|
||||
.appendTo("body");
|
||||
|
||||
// Calculate the minimum height required for the banner text
|
||||
let minHeight = 0;
|
||||
newsItems.forEach((item) => {
|
||||
$measuringElement.html(item);
|
||||
minHeight = Math.max(minHeight, $measuringElement.outerHeight());
|
||||
});
|
||||
|
||||
// Set the minimum height to avoid layout shifts
|
||||
$bannerText.css("min-height", minHeight);
|
||||
|
||||
// Remove the measuring element from the DOM
|
||||
$measuringElement.remove();
|
||||
|
||||
const news = new News();
|
||||
news.init();
|
||||
|
||||
function loadData() {
|
||||
const nowStamp = Math.round(Date.now() / 1000);
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@
|
|||
{% endif %}
|
||||
<!-- Page CSS -->
|
||||
<!-- Page -->
|
||||
{% if current_endpoint in ("setup", "login", "totp") %}
|
||||
{% if current_endpoint in ("setup", "login", "totp", "loading") %}
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/pages/login.css') }}"
|
||||
nonce="{{ style_nonce }}" />
|
||||
|
|
@ -167,7 +167,7 @@
|
|||
<!-- Main JS -->
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
{% if current_endpoint not in ("setup", "login", "totp") %}
|
||||
{% if current_endpoint not in ("login", "totp") %}
|
||||
<script async
|
||||
defer
|
||||
src="{{ url_for('static', filename='js/utils.js') }}"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
{% set breadcrumbs_ns.working_url = breadcrumb_url %}
|
||||
{% endif %}
|
||||
{% if current_endpoint != "configs" and "configs" in request.path %}
|
||||
{% set breadcrumb_url = url_for("configs") %}
|
||||
{% if loop.index == 3 %}
|
||||
{% set breadcrumb_prefix = "?service=" + breadcrumb %}
|
||||
{% elif loop.index == 4 %}
|
||||
|
|
@ -22,6 +23,7 @@
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
{% if current_endpoint != "cache" and "cache" in request.path %}
|
||||
{% set breadcrumb_url = url_for("cache") %}
|
||||
{% if loop.index == 3 %}
|
||||
{% set breadcrumb_prefix = "?service=" + breadcrumb %}
|
||||
{% elif loop.index == 4 %}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
<tr>
|
||||
<td>
|
||||
<a href="{{ url_for("cache") }}/{{ service_id }}/{{ cache['plugin_id'] }}/{{ cache['job_name'] }}/{{ cache['file_name'].replace('/', '_') if cache['file_name'].startswith('folder:') else cache['file_name'] }}"
|
||||
class="d-flex align-items-center"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
data-bs-original-title="View {{ cache['file_name'] }}"><i class="bx bx-show bx-xs"></i> {{ cache['file_name'] }}</a>
|
||||
|
|
@ -60,6 +61,7 @@
|
|||
<td id="service-{{ cache['job_name'] }}-{{ service_id.replace('.', '_') }}-{{ cache['file_name'].replace('.', '_') }}">
|
||||
{% if cache["service_id"] %}
|
||||
<a href="{{ url_for("services") }}/{{ cache['service_id'] }}"
|
||||
class="d-flex align-items-center"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
data-bs-original-title="Edit service {{ cache['service_id'] }}"><i class="bx bx-edit bx-xs"></i> {{ cache["service_id"] }}</a>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<input type="hidden"
|
||||
id="selected-service"
|
||||
name="selected_service"
|
||||
value="{{ config_service if config_service and config_service != "global" else "no service" }}">
|
||||
value="{{ config_service if config_service and config_service != "global" else "global" }}">
|
||||
<input type="hidden"
|
||||
id="selected-type"
|
||||
name="selected_type"
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
{% if not config_template and config_method and config_method != "ui" or is_readonly %}
|
||||
<button type="button" class="btn btn-sm btn-secondary ms-2 disabled">
|
||||
<i class="bx bx-xs bx-cube"></i>
|
||||
Service: {{ config_service or "no service" }}
|
||||
Service: {{ config_service or "global" }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-secondary ms-2 disabled">
|
||||
<i class="bx bx-xs bx-window"></i>
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
class="dropdown-item{% if not config_service or config_service == 'global' %} active{% endif %}"
|
||||
role="tab"
|
||||
data-bs-toggle="tab"
|
||||
{% if not config_service %}aria-selected="true"{% endif %}>no service</button>
|
||||
{% if not config_service %}aria-selected="true"{% endif %}>global</button>
|
||||
</li>
|
||||
{% for service in services %}
|
||||
<li class="nav-item">
|
||||
|
|
|
|||
|
|
@ -75,18 +75,22 @@
|
|||
<td></td>
|
||||
<td>
|
||||
<a href="{{ url_for("configs") }}/{{ service_id }}/{{ config['type'] }}/{{ config['name'] }}"
|
||||
class="d-flex align-items-center"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
data-bs-original-title="{% if config['method'] != 'ui' and not config['template'] or is_readonly %}View{% else %}Edit{% endif %} custom config {{ config['name'] }}"><i class="bx bx-{% if config['method'] != 'ui' and not config['template'] or is_readonly %}show{% else %}edit{% endif %} bx-xs"></i> {{ config["name"] }}</a>
|
||||
</td>
|
||||
<td id="type-{{ config['type'] }}-{{ service_id.replace('.', '_') }}-{{ config['name'] }}">
|
||||
<i class="bx bx-{{ config_icon }}"></i>
|
||||
{{ config["type"]|upper }}
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bx bx-{{ config_icon }} me-1"></i>
|
||||
{{ config["type"]|upper }}
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ config["method"] }}</td>
|
||||
<td id="service-{{ config['type'] }}-{{ service_id.replace('.', '_') }}-{{ config['name'] }}">
|
||||
{% if config["service_id"] %}
|
||||
<a href="{{ url_for("services") }}/{{ config['service_id'] }}"
|
||||
class="d-flex align-items-center"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
data-bs-original-title="Edit service {{ config['service_id'] }}"><i class="bx bx-edit bx-xs"></i> {{ config["service_id"] }}</a>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
{% if category != 'message' %}
|
||||
{{ category|capitalize }}
|
||||
{% else %}
|
||||
Success
|
||||
Info
|
||||
{% endif %}
|
||||
</span>
|
||||
<small class="text-body-secondary">just now</small>
|
||||
|
|
|
|||
|
|
@ -81,13 +81,15 @@
|
|||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if instance.type == "container" %}
|
||||
<i class="bx bxl-docker"></i> Container
|
||||
{% elif instance.type == "pod" %}
|
||||
<i class="bx bxl-kubernetes"></i> Pod
|
||||
{% else %}
|
||||
<i class="bx bx-microchip"></i> Static
|
||||
{% endif %}
|
||||
<div class="d-flex align-items-center">
|
||||
{% if instance.type == "container" %}
|
||||
<i class="bx bxl-docker"></i> Container
|
||||
{% elif instance.type == "pod" %}
|
||||
<i class="bx bxl-kubernetes"></i> Pod
|
||||
{% else %}
|
||||
<i class="bx bx-microchip"></i> Static
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td class="instance-creation-date">{{ instance.creation_date.astimezone().isoformat() }}</td>
|
||||
<td class="instance-last-seen-date">{{ instance.last_seen.astimezone().isoformat() }}</td>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,9 @@
|
|||
<td>{{ job }}</td>
|
||||
<td>{{ job_data["plugin_id"] }}</td>
|
||||
<td>
|
||||
<i class="bx {% if job_data['every'] == 'once' %}bx-revision{% elif job_data['every'] == 'day' %}bx-calendar-event{% elif job_data['every'] == 'week' %}bx-calendar-week{% else %}bxs-hourglass{% endif %}"></i> {{ job_data["every"] }}
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bx {% if job_data['every'] == 'once' %}bx-revision{% elif job_data['every'] == 'day' %}bx-calendar-event{% elif job_data['every'] == 'week' %}bx-calendar-week{% else %}bxs-hourglass{% endif %}"></i> {{ job_data["every"] }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<i class="bx bx-sm bx-{% if job_data['reload'] %}check text-success{% else %}x text-danger{% endif %}"></i>
|
||||
|
|
@ -121,7 +123,7 @@
|
|||
<i class="bx bx-data"></i>
|
||||
<span class="d-none d-md-inline"> Cache</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu nav-pills max-vh-60 overflow-auto pt-0"
|
||||
<ul class="dropdown-menu nav-pills max-vh-60 overflow-auto pt-0 pb-0"
|
||||
role="tablist">
|
||||
{% for cache in job_data['cache'] %}
|
||||
{% set service_id = cache['service_id'] if cache['service_id'] else 'global' %}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,27 @@
|
|||
{% extends "base.html" %}
|
||||
{% block page %}
|
||||
<div class="layout-wrapper bg-primary">
|
||||
<div class="layout-container">
|
||||
<div class="content-wrapper">
|
||||
<div class="container-xxl d-flex justify-content-center align-items-center min-vh-100">
|
||||
<div class="layout-main-wrapper mt-0 pb-10">
|
||||
<div class="container-xxl">
|
||||
<div class="authentication-wrapper authentication-basic container-p-y">
|
||||
<div class="position-absolute top-0 p-4 pe-6 ps-6 w-70">
|
||||
<div class="bg-bw-green position-relative w-100 p-2 text-white rounded fw-bold overflow-hidden">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="flex-grow-1 overflow-hidden me-2">
|
||||
<div id="banner-container">
|
||||
<p id="banner-text" class="mb-0 slide-in">
|
||||
Get the most of BunkerWeb by upgrading to the PRO version. More info and free trial <a class="light-href text-white-80"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href="https://panel.bunkerweb.io/?utm_campaign=self&utm_source=banner#pro">here</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<i id="next-news" role="button" class='bx bx-sm bx-chevron-right'></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-xxl d-flex justify-content-center align-items-center">
|
||||
<div class="authentication-inner loading">
|
||||
<div class="layout-main-wrapper mt-0 mb-0">
|
||||
<div class="layout-main-placeholder d-flex justify-content-center align-items-center">
|
||||
<lottie-player src="{{ url_for('static', filename='json/blockhaus.min.json') }}" background="transparent" speed="1" class="img-fluid" loop autoplay></lottie-player>
|
||||
</div>
|
||||
|
|
@ -13,23 +30,6 @@
|
|||
<h3 class="mb-0 don-jose">{{ message }}</h3>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="fixed-bottom p-4 pe-6 ps-6">
|
||||
<div class="bg-bw-green position-relative w-100 p-2 text-white rounded fw-bold overflow-hidden">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div class="flex-grow-1 overflow-hidden me-2">
|
||||
<div id="banner-container">
|
||||
<p id="banner-text" class="mb-0 slide-in">
|
||||
Get the most of BunkerWeb by upgrading to the PRO version. More info and free trial <a class="light-href text-white-80"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
href="https://panel.bunkerweb.io/?utm_campaign=self&utm_source=banner#pro">here</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<i id="next-news" role="button" class='bx bx-sm bx-chevron-right'></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -75,5 +75,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Content -->
|
||||
</div>
|
||||
<!-- / Content -->
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -107,13 +107,13 @@
|
|||
data-type="{{ plugin_data['type'] }}">
|
||||
<div class="card-header d-flex justify-content-between align-items-center mw-100">
|
||||
<div class="pt-1 flex-grow-1 me-2" style="min-width: 0;">
|
||||
<h5 class="card-title d-inline border p-2 don-jose{{ plugin_types[plugin_data['type']].get('text-class', '') }}{{ plugin_types[plugin_data['type']].get('title-class', '') }}">
|
||||
<h5 class="card-title d-flex align-items-center don-jose{{ plugin_types[plugin_data['type']].get('text-class', '') }}{{ plugin_types[plugin_data['type']].get('title-class', '') }}">
|
||||
{{ plugin_data["name"] }} - v{{ plugin_data["version"] }} - {{ plugin_types[plugin_data["type"]].get('icon', '<img src="' + pro_diamond_url + '"
|
||||
alt="Pro plugin"
|
||||
width="18px"
|
||||
height="15.5px">') |safe }}
|
||||
</h5>
|
||||
<p class="card-subtitle text-muted text-truncate mt-3 courier-prime">{{ plugin_data["description"] }}</p>
|
||||
<p class="card-subtitle text-muted text-truncate mt-1 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/?utm_campaign=self&utm_source=ui#protect-udptcp-applications"
|
||||
|
|
@ -134,12 +134,12 @@
|
|||
{% if plugin_data["page"] %}
|
||||
<a href="{{ url_for("plugins") }}/{{ plugin }}"
|
||||
class="btn btn-sm btn-primary rounded-pill ms-2">
|
||||
<i class="bx bxs-file-html"></i> Custom page
|
||||
<i class="bx bxs-file-html"></i> Plugin page
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body row pb-0">
|
||||
<div class="card-body row g-2 gx-6 pb-0">
|
||||
{% for setting, setting_data in filtered_settings.items() if not setting_data.get('multiple', false) and setting not in blacklisted_settings and (not service_endpoint or setting_data['context'] == "multisite") %}
|
||||
{% set setting_id_prefix = "setting-" + plugin + "-" %}
|
||||
{% set setting_config = config.get(setting, {}) %}
|
||||
|
|
@ -230,7 +230,7 @@
|
|||
</h5>
|
||||
<p class="card-subtitle text-muted mt-2">This is where you can configure multiple settings for this plugin.</p>
|
||||
</div>
|
||||
<div class="card-body row pt-0">
|
||||
<div class="card-body row g-2 gx-6 pt-0">
|
||||
{% for multiple, multiples in plugin_multiples.items() %}
|
||||
<div id="multiple-{{ plugin }}-{{ multiple }}"
|
||||
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 %}">
|
||||
|
|
@ -279,7 +279,7 @@
|
|||
</div>
|
||||
<div id="multiple-{{ plugin }}-{{ multiple }}-{{ setting_suffix }}"
|
||||
class="collapse show multiple-collapse pt-0">
|
||||
<div class="row mt-2 pt-2">
|
||||
<div class="row g-2 gx-6 mt-2 pt-2">
|
||||
{% for setting, setting_data in settings.items() if setting not in blacklisted_settings %}
|
||||
{% set setting_config = config.get(setting, {}) %}
|
||||
{% set setting_default = setting_data.get("default", "") %}
|
||||
|
|
|
|||
|
|
@ -76,13 +76,13 @@
|
|||
aria-labelledby="navs-templates-{{ template }}-tab">
|
||||
<div class="card-header d-flex align-items-center mw-100">
|
||||
<div class="pt-1">
|
||||
<h5 class="card-title d-inline border p-2 don-jose{{ plugin_types[template_plugin['type']].get('text-class', '') }}{{ plugin_types[template_plugin['type']].get('title-class', '') }}">
|
||||
<h5 class="card-title d-flex align-items-center don-jose{{ plugin_types[template_plugin['type']].get('text-class', '') }}{{ plugin_types[template_plugin['type']].get('title-class', '') }}">
|
||||
{{ template|capitalize }} - {{ plugin_types[template_plugin["type"]].get('icon', '<img src="' + pro_diamond_url + '"
|
||||
alt="Pro plugin"
|
||||
width="18px"
|
||||
height="15.5px">') |safe }}
|
||||
</h5>
|
||||
<p class="card-subtitle text-muted text-truncate mt-3 courier-prime">{{ template_data["name"] }}</p>
|
||||
<p class="card-subtitle text-muted text-truncate mt-1 courier-prime">{{ template_data["name"] }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
|
@ -279,7 +279,9 @@
|
|||
<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>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@
|
|||
<li>
|
||||
<a class="dropdown-item d-flex align-items-center"
|
||||
href="{{ url_for('logout') }}">
|
||||
<i class="bx bx-power-off bx-md me-2"></i><span>Log Out</span>
|
||||
<i class="bx bx-power-off bx-sm me-2"></i><span>Log Out</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@
|
|||
</td>
|
||||
<td>{{ plugin_data["description"] }}</td>
|
||||
<td id="version-{{ plugin }}">
|
||||
<div class="{{ plugin_types[plugin_data['type']].get('text-class', '') }}">{{ plugin_data["version"] }}</div>
|
||||
<div class="{{ plugin_types[plugin_data['type']].get('text-class', '') }}">v{{ plugin_data["version"] }}</div>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<div data-bs-toggle="tooltip"
|
||||
|
|
@ -68,13 +68,13 @@
|
|||
</td>
|
||||
</div>
|
||||
<td id="type-{{ plugin }}">
|
||||
<div {% if not is_pro_version and plugin_data['type'] == "pro" %}data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true" data-bs-original-title="<i class='bx bx-diamond bx-xs'></i><span>Pro feature</span>"
|
||||
<div class="d-flex align-items-center" {% if not is_pro_version and plugin_data['type'] == "pro" %}data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true" data-bs-original-title="<i class='bx bx-diamond bx-xs'></i><span>Pro feature</span>"
|
||||
{% endif %}
|
||||
>
|
||||
{% if plugin_data["type"] == "pro" %}
|
||||
<a href="{{ url_for('pro') }}" target="_blank" rel="noopener" {% else %}
|
||||
<div {% endif %}
|
||||
class="{{ plugin_types[plugin_data['type']].get('text-class', '') }}">
|
||||
class="d-flex align-items-center{{ plugin_types[plugin_data['type']].get('text-class', '') }}">
|
||||
{{ plugin_types[plugin_data["type"]].get('icon', '<img src="' + pro_diamond_url + '"
|
||||
alt="Pro plugin"
|
||||
width="16px"
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
<td></td>
|
||||
<td>
|
||||
<a href="{{ url_for("services") }}/{{ service['id'] }}"
|
||||
class="d-flex align-items-center"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
data-bs-original-title="{% if service['method'] == 'autoconf' or is_readonly %}View{% else %}Edit{% endif %} service {{ service['id'] }}"><i class="bx bx-{% if service['method'] == 'autoconf' or is_readonly %}show{% else %}edit{% endif %} bx-xs"></i> {{ service["id"] }}</a>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,36 @@
|
|||
name="csrf_token"
|
||||
value="{{ csrf_token() }}" />
|
||||
<div class="container-xxl">
|
||||
<div class="position-fixed top-0 end-0 m-5">
|
||||
<!-- prettier-ignore -->
|
||||
<div class="d-flex justify-content-end align-items-center mb-5">
|
||||
<ul class="d-flex mb-0 list-unstyled">
|
||||
<li class="me-3">
|
||||
<a role="button"
|
||||
class="btn btn-sm btn-outline-dark d-flex align-items-center h-100"
|
||||
aria-pressed="true"
|
||||
href="https://panel.bunkerweb.io/order/support/?utm_campaign=self&utm_source=ui"
|
||||
target="_blank"
|
||||
rel="noopener">
|
||||
<span class="bx bx-help-circle me-0 me-md-2"></span>
|
||||
<span class="d-none d-md-inline">Need help?</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="position-relative">
|
||||
<button id="news-button"
|
||||
type="button"
|
||||
class="btn btn-sm btn-dark text-uppercase d-flex align-items-center"
|
||||
aria-pressed="true"
|
||||
data-bs-toggle="offcanvas"
|
||||
data-bs-target="#side-offcanvas-news"
|
||||
aria-controls="side-offcanvas-news">
|
||||
<span class="bx bx-news me-0 me-md-2"></span>
|
||||
<span class="d-none d-md-inline">News</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="authentication-wrapper authentication-basic container-p-y">
|
||||
<div class="authentication-inner w-100">
|
||||
<!-- Setup -->
|
||||
|
|
@ -96,11 +126,13 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "username" %}
|
||||
{% set setting_value = username %}
|
||||
{% set setting_data = {"type": "text", "id": "username", "regex": "^.{1,256}$"} %}
|
||||
{% set required = true %}
|
||||
{% include "models/input_setting.html" %}
|
||||
<input id="username"
|
||||
name="username"
|
||||
type="text"
|
||||
class="form-control plugin-setting mt-1"
|
||||
aria-labelledby="label-username"
|
||||
pattern="^.{1,256}$"
|
||||
required />
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
|
|
@ -116,15 +148,17 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "email" %}
|
||||
{% set setting_data = {"type": "email", "id": "email", "regex": "^.{1,256}$"} %}
|
||||
{% set required = false %}
|
||||
{% include "models/input_setting.html" %}
|
||||
<input id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="John.doe@example.com"
|
||||
class="form-control plugin-setting mt-1"
|
||||
aria-labelledby="label-email" />
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-username"
|
||||
for="username"
|
||||
<label id="label-password"
|
||||
for="password"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Admin Password
|
||||
</label>
|
||||
|
|
@ -137,11 +171,20 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "password" %}
|
||||
{% set setting_value = password %}
|
||||
{% set setting_data = {"type": "password", "id": "password", "regex": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[ -~]).{8,}$"} %}
|
||||
{% set required = true %}
|
||||
{% include "models/input_setting.html" %}
|
||||
<div class="form-password-toggle">
|
||||
<div class="input-group input-group-merge mt-1">
|
||||
<input class="form-control plugin-setting"
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
placeholder="············"
|
||||
aria-labelledby="label-password"
|
||||
autocomplete="off"
|
||||
required
|
||||
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[ -~]).{8,}$" />
|
||||
<span class="input-group-text cursor-pointer"><i class="bx bx-hide"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<ul class="list-unstyled" id="password-requirements">
|
||||
<li id="length-check">
|
||||
|
|
@ -165,8 +208,8 @@
|
|||
</div>
|
||||
<div class="col-12 col-sm-6 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-username"
|
||||
for="username"
|
||||
<label id="label-confirm_password"
|
||||
for="confirm_password"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Confirm Password
|
||||
</label>
|
||||
|
|
@ -179,34 +222,34 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "confirm_password" %}
|
||||
{% set setting_data = {"type": "password", "id": "confirm_password", "regex": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[ -~]).{8,}$"} %}
|
||||
{% set required = true %}
|
||||
{% include "models/input_setting.html" %}
|
||||
<div class="form-password-toggle">
|
||||
<div class="input-group input-group-merge mt-1">
|
||||
<input class="form-control plugin-setting"
|
||||
type="password"
|
||||
id="confirm_password"
|
||||
name="confirm_password"
|
||||
placeholder="············"
|
||||
aria-labelledby="label-confirm_password"
|
||||
autocomplete="off"
|
||||
required
|
||||
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[ -~]).{8,}$" />
|
||||
<span class="input-group-text cursor-pointer"><i class="bx bx-hide"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pt-1 pb-4">
|
||||
<!-- <div class="pt-1 pb-4">
|
||||
<h5 class="mb-1 fw-bold">Enable 2FA</h5>
|
||||
<p class="card-subtitle text-muted">Enable Two-Factor Authentication (Strongly Recommended)</p>
|
||||
</div>
|
||||
<div class="col-12 pb-3 d-flex justify-content-center">
|
||||
<div class="g-3 w-75">
|
||||
<div class="d-flex justify-content-center">
|
||||
<img class="img-fluid"
|
||||
src="{{ totp_qr_image }}"
|
||||
alt="2FA QR Code"
|
||||
height="200"
|
||||
width="200" />
|
||||
<img class="img-fluid" src="{{ totp_qr_image }}" alt="2FA QR Code" height="200" width="200" />
|
||||
</div>
|
||||
<div class="form-password-toggle">
|
||||
<label for="secret_token" class="form-label text-start d-block">Secret Token</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<input type="password"
|
||||
id="secret_token"
|
||||
name="secret_token"
|
||||
class="form-control"
|
||||
placeholder="Secret Token"
|
||||
value="{{ totp_secret }}"
|
||||
readonly />
|
||||
<input type="password" id="secret_token" name="secret_token" class="form-control" placeholder="Secret Token" value="{{ totp_secret }}" readonly />
|
||||
{% if request.is_secure %}
|
||||
<span class="input-group-text cursor-pointer copy-to-clipboard">
|
||||
<i class="bx bx-copy-alt"></i>
|
||||
|
|
@ -219,7 +262,7 @@
|
|||
<small class="form-text text-muted">Save it in a safe place, you will need the generated code to setup your 2FA in the last step.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-center relative w-full p-2 text-primary rounded-lg fw-bold">
|
||||
|
|
@ -240,8 +283,8 @@
|
|||
<div class="row pb-0">
|
||||
<div class="col-12 pb-6">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-username"
|
||||
for="username"
|
||||
<label id="label-SERVER_NAME"
|
||||
for="SERVER_NAME"
|
||||
class="form-label fw-semibold text-truncate">Server name</label>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge rounded-pill bg-secondary-subtle text-dark d-flex align-items-center justify-content-center p-1"
|
||||
|
|
@ -252,16 +295,20 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "SERVER_NAME" %}
|
||||
{% set setting_value = "www.example.com" %}
|
||||
{% set setting_data = {"type": "text", "id": "SERVER_NAME", "regex": "^.+$"} %}
|
||||
{% set required = true %}
|
||||
{% include "models/input_setting.html" %}
|
||||
<input id="SERVER_NAME"
|
||||
name="SERVER_NAME"
|
||||
type="text"
|
||||
placeholder="www.example.com"
|
||||
value="www.example.com"
|
||||
class="form-control plugin-setting mt-1"
|
||||
aria-labelledby="label-SERVER_NAME"
|
||||
pattern="^.+$"
|
||||
required />
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 pb-6">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-username"
|
||||
for="username"
|
||||
<label id="label-REVERSE_PROXY_HOST"
|
||||
for="REVERSE_PROXY_HOST"
|
||||
class="form-label fw-semibold text-truncate">UI Host</label>
|
||||
<div class="d-flex align-items-center">
|
||||
{% if ui_host %}
|
||||
|
|
@ -280,11 +327,14 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "REVERSE_PROXY_HOST" %}
|
||||
{% set setting_value = ui_host %}
|
||||
{% set setting_data = {"type": "text", "id": "REVERSE_PROXY_HOST", "regex": "^.+$"} %}
|
||||
{% set required = true %}
|
||||
{% include "models/input_setting.html" %}
|
||||
<input id="REVERSE_PROXY_HOST"
|
||||
name="REVERSE_PROXY_HOST"
|
||||
type="text"
|
||||
value="{{ ui_host }}"
|
||||
class="form-control plugin-setting mt-1"
|
||||
aria-labelledby="label-REVERSE_PROXY_HOST"
|
||||
pattern="^.+$"
|
||||
required />
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 pb-6">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
|
|
@ -292,6 +342,12 @@
|
|||
for="username"
|
||||
class="form-label fw-semibold text-truncate">UI URL</label>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge badge-center rounded-pill bg-warning 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="We recommend using a random URL">
|
||||
<span class="bx bx-shield bx-xs"></span>
|
||||
</span>
|
||||
<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"
|
||||
|
|
@ -300,16 +356,18 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "REVERSE_PROXY_URL" %}
|
||||
{% set setting_value = random_url %}
|
||||
{% set setting_data = {"type": "text", "id": "REVERSE_PROXY_URL", "regex": "^.+$"} %}
|
||||
{% set required = false %}
|
||||
{% include "models/input_setting.html" %}
|
||||
<input id="REVERSE_PROXY_URL"
|
||||
name="REVERSE_PROXY_URL"
|
||||
type="text"
|
||||
value="/"
|
||||
class="form-control plugin-setting mt-1"
|
||||
aria-labelledby="label-REVERSE_PROXY_URL"
|
||||
pattern="^.*$" />
|
||||
</div>
|
||||
<div class="col-6 col-sm-3 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-username"
|
||||
for="username"
|
||||
<label id="label-AUTO_LETS_ENCRYPT"
|
||||
for="AUTO_LETS_ENCRYPT"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Auto Let's Encrypt
|
||||
</label>
|
||||
|
|
@ -330,15 +388,20 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "AUTO_LETS_ENCRYPT" %}
|
||||
{% set setting_value = auto_lets_encrypt %}
|
||||
{% set setting_data = {"id": "AUTO_LETS_ENCRYPT"} %}
|
||||
{% include "models/checkbox_setting.html" %}
|
||||
<div class="form-check form-switch mt-1">
|
||||
<input id="AUTO_LETS_ENCRYPT"
|
||||
name="AUTO_LETS_ENCRYPT"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
aria-labelledby="label-AUTO_LETS_ENCRYPT"
|
||||
{% if auto_lets_encrypt == "yes" %}checked{% endif %} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-sm-3 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-username"
|
||||
for="username"
|
||||
<label id="label-USE_LETS_ENCRYPT_STAGING"
|
||||
for="USE_LETS_ENCRYPT_STAGING"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Use Let's Encrypt Staging
|
||||
</label>
|
||||
|
|
@ -359,15 +422,20 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "USE_LETS_ENCRYPT_STAGING" %}
|
||||
{% set setting_value = lets_encrypt_staging %}
|
||||
{% set setting_data = {"id": "USE_LETS_ENCRYPT_STAGING"} %}
|
||||
{% include "models/checkbox_setting.html" %}
|
||||
<div class="form-check form-switch mt-1">
|
||||
<input id="USE_LETS_ENCRYPT_STAGING"
|
||||
name="USE_LETS_ENCRYPT_STAGING"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
aria-labelledby="label-USE_LETS_ENCRYPT_STAGING"
|
||||
{% if lets_encrypt_staging == "yes" %}checked{% endif %} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-username"
|
||||
for="username"
|
||||
<label id="label-EMAIL_LETS_ENCRYPT"
|
||||
for="EMAIL_LETS_ENCRYPT"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Email for Let's Encrypt
|
||||
</label>
|
||||
|
|
@ -388,10 +456,13 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "EMAIL_LETS_ENCRYPT" %}
|
||||
{% set setting_value = email_lets_encrypt %}
|
||||
{% set setting_data = {"type": "text", "id": "EMAIL_LETS_ENCRYPT", "regex": "^.*$"} %}
|
||||
{% include "models/input_setting.html" %}
|
||||
<input id="EMAIL_LETS_ENCRYPT"
|
||||
name="EMAIL_LETS_ENCRYPT"
|
||||
type="text"
|
||||
placeholder="John.doe@example.com"
|
||||
class="form-control plugin-setting mt-1"
|
||||
aria-labelledby="label-EMAIL_LETS_ENCRYPT"
|
||||
pattern="^.*$" />
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
|
|
@ -409,63 +480,82 @@
|
|||
<h5 class="mb-1 fw-bold">Overview</h5>
|
||||
<p class="card-subtitle text-muted">Review your settings</p>
|
||||
</div>
|
||||
<div class="row pb-0 mb-4">
|
||||
{% if not ui_user %}
|
||||
<h6 class="mt-2 mb-1 fw-bold">Admin User</h6>
|
||||
<div class="d-flex justify-content-center pb-0 mb-4">
|
||||
<div class="col-12 col-md-8">
|
||||
{% set input_required = false %}
|
||||
{% set input_readonly = true %}
|
||||
<div class="col-12 col-sm-6 col-md-4 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-overview_username"
|
||||
for="overview_username"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Admin Username
|
||||
</label>
|
||||
{% if not ui_user %}
|
||||
<div class="pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-overview_username"
|
||||
for="overview_username"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Admin Username
|
||||
</label>
|
||||
</div>
|
||||
<input id="overview_username"
|
||||
name="overview_username"
|
||||
type="text"
|
||||
class="form-control plugin-setting mt-1"
|
||||
aria-labelledby="label-overview_username"
|
||||
pattern="^.{1,256}$"
|
||||
readonly />
|
||||
</div>
|
||||
{% set setting = "username" %}
|
||||
{% set setting_data = {"type": "text", "id": "overview_username", "regex": "^.{1,256}$"} %}
|
||||
{% include "models/input_setting.html" %}
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 col-md-4 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-overview_email"
|
||||
for="overview_email"
|
||||
class="form-label fw-semibold text-truncate">Admin Email</label>
|
||||
<div class="pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-overview_email"
|
||||
for="overview_email"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Admin Email
|
||||
</label>
|
||||
</div>
|
||||
<input id="overview_email"
|
||||
name="overview_email"
|
||||
type="text"
|
||||
class="form-control plugin-setting mt-1"
|
||||
aria-labelledby="label-overview_email"
|
||||
pattern="^.*$"
|
||||
readonly />
|
||||
</div>
|
||||
{% set setting = "email" %}
|
||||
{% set setting_data = {"type": "email", "id": "overview_email", "regex": "^.*$"} %}
|
||||
{% include "models/input_setting.html" %}
|
||||
</div>
|
||||
<div class="col-12 col-md-4 pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-overview_password"
|
||||
for="overview_password"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Admin Password
|
||||
</label>
|
||||
<div class="pb-3">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-overview_password"
|
||||
for="overview_password"
|
||||
class="form-label fw-semibold text-truncate">
|
||||
Admin Password
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-password-toggle">
|
||||
<div class="input-group input-group-merge mt-1">
|
||||
<input class="form-control plugin-setting"
|
||||
type="password"
|
||||
id="overview_password"
|
||||
name="overview_password"
|
||||
aria-labelledby="label-overview_password"
|
||||
autocomplete="off"
|
||||
readonly
|
||||
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[ -~]).{8,}$" />
|
||||
<span class="input-group-text cursor-pointer"><i class="bx bx-hide"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "password" %}
|
||||
{% set setting_data = {"type": "password", "id": "overview_password", "regex": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[ -~]).{8,}$"} %}
|
||||
{% include "models/input_setting.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not ui_reverse_proxy %}
|
||||
<div class="col-6 pb-3">
|
||||
<h6 class="mt-2 mb-2 fw-bold">BunkerWeb UI final URL</h6>
|
||||
{% set setting = "SERVER_NAME + REVERSE_PROXY_HOST" %}
|
||||
{% set setting_data = {"type": "text", "id": "overview_service_url", "regex": "^.+$"} %}
|
||||
{% include "models/input_setting.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if not ui_reverse_proxy %}
|
||||
<div class="pb-3">
|
||||
<h6 class="mt-2 mb-2 fw-bold">BunkerWeb UI final URL</h6>
|
||||
{% set setting = "SERVER_NAME + REVERSE_PROXY_HOST" %}
|
||||
{% set setting_data = {"type": "text", "id": "overview_service_url", "regex": "^.+$"} %}
|
||||
{% include "models/input_setting.html" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if not ui_user %}
|
||||
<div class="col-6">
|
||||
<!-- <div class="col-6">
|
||||
{% set input_readonly = false %}
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h6 class="mt-2 mb-1 fw-bold">2FA Setup</h6>
|
||||
<div class="d-flex align-items-center">
|
||||
<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="The 6-digit code generated by your 2FA app">
|
||||
<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="The 6-digit code generated by your 2FA app">
|
||||
<span class="bx bx-question-mark bx-xs"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
|
@ -473,33 +563,30 @@
|
|||
{% set setting = "2FA Code" %}
|
||||
{% set setting_data = {"type": "number", "id": "2fa_code", "regex": "^[0-9]{6}$"} %}
|
||||
{% include "models/input_setting.html" %}
|
||||
</div> -->
|
||||
{% endif %}
|
||||
</div>
|
||||
<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
|
||||
</div>
|
||||
<!-- <div id="overview-2fa-enabled" class="ms-4" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title='Please enter a valid 2FA code in the 2FA setup input'>
|
||||
<i class="bx bx-question-mark text-warning"></i> 2FA enabled
|
||||
</div> -->
|
||||
{% endif %}
|
||||
{% if not ui_reverse_proxy %}
|
||||
<div id="overview-unique-server-name" class="ms-4">
|
||||
<i class="bx bx-question-mark text-warning"></i> Unique Server Name
|
||||
</div>
|
||||
{% endif %}
|
||||
<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
|
||||
</div>
|
||||
<div id="overview-2fa-enabled"
|
||||
class="ms-4"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title='Please enter a valid 2FA code in the 2FA setup input'>
|
||||
<i class="bx bx-question-mark text-warning"></i> 2FA enabled
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not ui_reverse_proxy %}
|
||||
<div id="overview-unique-server-name" class="ms-4">
|
||||
<i class="bx bx-question-mark text-warning"></i> Unique Server Name
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mt-2">
|
||||
<button class="btn btn-primary btn-prev previous-step disabled">
|
||||
<button id="previous-step"
|
||||
class="btn btn-primary btn-prev previous-step disabled">
|
||||
<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>
|
||||
|
|
@ -507,7 +594,7 @@
|
|||
<i class="bx bx-save bx-sm ms-sm-n2"></i>
|
||||
<span class="align-middle d-sm-inline-block d-none ms-sm-1">Setup</span>
|
||||
</button>
|
||||
<button class="btn btn-primary btn-next next-step">
|
||||
<button id="next-step" class="btn btn-primary btn-next next-step">
|
||||
<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>
|
||||
|
|
@ -516,6 +603,8 @@
|
|||
<!-- /Setup -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- prettier-ignore -->
|
||||
{% include "sidebar-news.html" %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="feedback-toast"
|
||||
|
|
@ -557,14 +646,14 @@
|
|||
class="alert alert-danger text-center"
|
||||
role="alert">Are you sure you want to proceed to the next step?</div>
|
||||
<p class="mt-1 mb-0 text-center">
|
||||
In case of issues, you can also click <a id="check-url"
|
||||
In case of issues, you can manually accept the unsafe cert <a id="check-url"
|
||||
class="fw-semibold"
|
||||
href="https://www.example.com/setup/check"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title='If the shown text is "ok", that means that the server name is available'>here</a> to perform a manual check.
|
||||
data-bs-original-title='If the shown text is "ok", that means that the server name is available'>here</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-center">
|
||||
|
|
@ -595,48 +684,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="position-fixed bottom-0 start-0 m-3" id="floating-modes-menu">
|
||||
<button class="btn btn-sm btn-primary d-flex align-items-center justify-content-center rounded-pill me-1"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#newsletter-floating"
|
||||
aria-controls="newsletter-floating"
|
||||
aria-expanded="true"
|
||||
aria-label="Toggle navigation">
|
||||
<i class="bx bx-xs bx-menu me-1"></i>News from the Bunker
|
||||
</button>
|
||||
<!-- Collapsible floating menu -->
|
||||
<div class="collapse show mt-2" id="newsletter-floating">
|
||||
<div class="card card-body bg-white border-0 shadow-sm">
|
||||
<h5 class="mb-3 text-dark">Join the Newsletter</h5>
|
||||
<form action="https://bunkerity.us1.list-manage.com/subscribe/post?u=ec5b1577cf427972b9bd491a6&id=37076d9d67"
|
||||
method="POST"
|
||||
target="_blank"
|
||||
rel="noopener">
|
||||
<div class="mb-3">
|
||||
<input type="email"
|
||||
name="EMAIL"
|
||||
class="form-control"
|
||||
placeholder="John.doe@example.com"
|
||||
required />
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input type="checkbox"
|
||||
class="form-check-input"
|
||||
name="newsletter-check"
|
||||
required />
|
||||
<label class="form-check-label" for="privacyPolicyCheck">
|
||||
I've read and agree to the
|
||||
<a class="fst-italic"
|
||||
href="https://www.bunkerity.com/en/privacy-policy?utm_campaign=self&utm_source=ui"
|
||||
target="_blank"
|
||||
rel="noopener">privacy policy</a>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100 text-uppercase don-jose">Subscribe</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- / Content -->
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- Newsletter Signup Section -->
|
||||
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-white border-top">
|
||||
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-light-subtle border-top">
|
||||
<h5 class="mb-3 text-dark">Join the Newsletter</h5>
|
||||
<form action="https://bunkerity.us1.list-manage.com/subscribe/post?u=ec5b1577cf427972b9bd491a6&id=37076d9d67"
|
||||
method="POST">
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
{% if category != 'message' %}
|
||||
{{ category|capitalize }}
|
||||
{% else %}
|
||||
Success
|
||||
Info
|
||||
{% endif %}
|
||||
</span>
|
||||
<small class="text-body-secondary toast-datetime">{{ datetime }}</small>
|
||||
|
|
@ -74,7 +74,7 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
<!-- Newsletter Signup Section -->
|
||||
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-white border-top">
|
||||
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-light-subtle border-top">
|
||||
<h5 class="mb-3 text-dark">Join the Newsletter</h5>
|
||||
<form action="https://bunkerity.us1.list-manage.com/subscribe/post?u=ec5b1577cf427972b9bd491a6&id=37076d9d67"
|
||||
method="POST">
|
||||
|
|
|
|||
Loading…
Reference in a new issue