Made a lot of optimizations and tweaks in web UI + Made advancements on Global Config and Services pages

This commit is contained in:
Théophile Diot 2024-09-04 23:09:03 +02:00
parent 69d0358f58
commit 974cfcaeb3
No known key found for this signature in database
GPG key ID: FA995104A0BA376A
29 changed files with 1144 additions and 564 deletions

View file

@ -79,6 +79,8 @@ def global_config_page():
)
)
global_config = BW_CONFIG.get_config(global_only=True, methods=True)
keywords = request.args.get("keywords", "")
search_type = request.args.get("type", "all")
global_config = DB.get_config(global_only=True, methods=True)
plugins = BW_CONFIG.get_plugins()
return render_template("plugins_settings.html", config=global_config, plugins=plugins)
return render_template("global_config.html", config=global_config, plugins=plugins, keywords=keywords, type=search_type)

View file

@ -1,7 +1,7 @@
from flask import Blueprint, redirect, render_template, request, url_for
from flask import Blueprint, Response, redirect, render_template, request, url_for
from flask_login import login_required
from app.dependencies import DB
from app.dependencies import BW_CONFIG, DB
from app.routes.utils import get_service_data, handle_error, update_service
@ -21,39 +21,18 @@ def services_page():
return redirect(url_for("loading", next=url_for("services.services_page"), message=message))
# Display services
services = []
tmp_config = DB.get_config(methods=True, with_drafts=True).copy()
service_names = tmp_config["SERVER_NAME"]["value"].split(" ")
return render_template("services.html") # TODO
table_settings = (
"USE_REVERSE_PROXY",
"IS_DRAFT",
"SERVE_FILES",
"REMOTE_PHP",
"AUTO_LETS_ENCRYPT",
"USE_CUSTOM_SSL",
"USE_MODSECURITY",
"USE_BAD_BEHAVIOR",
"USE_LIMIT_REQ",
"USE_DNSBL",
"SERVER_NAME",
)
for service in service_names:
service_settings = {}
# For each needed setting, get the service value if one, else the global (value), else default value
for setting in table_settings:
value = tmp_config.get(f"{service}_{setting}", tmp_config.get(setting, {"value": None}))["value"]
method = tmp_config.get(f"{service}_{setting}", tmp_config.get(setting, {"method": None}))["method"]
is_global = tmp_config.get(f"{service}_{setting}", tmp_config.get(setting, {"global": None}))["global"]
service_settings[setting] = {"value": value, "method": method, "global": is_global}
services.append(service_settings)
services.sort(key=lambda x: x["SERVER_NAME"]["value"])
# builder = services_builder(services)
# return render_template("services.html", data_server_builder=b64encode(dumps(builder).encode("utf-8")).decode("ascii"))
return render_template("services.html")
@services.route("/services/<string:service>", methods=["GET", "POST"])
@login_required
def services_service_page(service: str):
services = BW_CONFIG.get_config(global_only=True, methods=False, filtered_settings=("SERVER_NAME"))["SERVER_NAME"].split(" ")
if service not in services:
return Response("Service not found", status=404)
mode = request.args.get("mode", "easy")
keywords = request.args.get("keywords", "")
search_type = request.args.get("type", "all")
db_config = DB.get_config(methods=True, with_drafts=True, service=service)
plugins = BW_CONFIG.get_plugins()
return render_template("service_settings.html", config=db_config, plugins=plugins, mode=mode, keywords=keywords, type=search_type)

View file

@ -7,6 +7,7 @@
:root,
[data-bs-theme="light"] {
--bs-bw-green: #2eac68;
--bs-bw-blue: #0b5577;
--bs-blue: #007bff;
--bs-indigo: #6610f2;
--bs-purple: #696cff;
@ -24,8 +25,8 @@
--bs-gray-25: rgba(34, 48, 62, 0.025);
--bs-gray-60: rgba(34, 48, 62, 0.06);
--bs-gray-80: rgba(34, 48, 62, 0.08);
--bs-primary: #0b5577;
--bs-secondary: #8592a3;
--bs-primary: #0b354a;
--bs-secondary: #35728e;
--bs-success: #71dd37;
--bs-info: #03c3ec;
--bs-warning: #ffab00;
@ -33,8 +34,8 @@
--bs-light: #dbdee0;
--bs-dark: #2b2c40;
--bs-gray: rgba(34, 48, 62, 0.5);
--bs-primary-rgb: 11, 85, 119;
--bs-secondary-rgb: 133, 146, 163;
--bs-primary-rgb: 11, 53, 74;
--bs-secondary-rgb: 53, 114, 142;
--bs-success-rgb: 113, 221, 55;
--bs-info-rgb: 3, 195, 236;
--bs-warning-rgb: 255, 171, 0;
@ -42,8 +43,8 @@
--bs-light-rgb: 219, 222, 224;
--bs-dark-rgb: 43, 44, 64;
--bs-gray-rgb: 34, 48, 62;
--bs-primary-text-emphasis: #2a2b66;
--bs-secondary-text-emphasis: #353a41;
--bs-primary-text-emphasis: #07354a;
--bs-secondary-text-emphasis: #395d6e;
--bs-success-text-emphasis: #2d5816;
--bs-info-text-emphasis: #014e5e;
--bs-warning-text-emphasis: #664400;
@ -98,10 +99,10 @@
--bs-tertiary-bg: rgba(34, 48, 62, 0.1);
--bs-tertiary-bg-rgb: 34, 48, 62;
--bs-heading-color: #384551;
--bs-link-color: #0b5577;
--bs-link-color: #0b354a;
--bs-link-color-rgb: 105, 108, 255;
--bs-link-decoration: none;
--bs-link-hover-color: #0b5577;
--bs-link-hover-color: #0b354a;
--bs-link-hover-color-rgb: 95, 97, 230;
--bs-code-color: #e83e8c;
--bs-highlight-color: #646e78;
@ -7556,7 +7557,7 @@ h6,
.bg-label-secondary {
background-color: #ebeef0 !important;
color: #8592a3 !important;
color: var(--bs-secondary) !important;
}
.border-label-secondary {
@ -8095,21 +8096,21 @@ label.btn {
.btn-secondary {
color: #fff;
background-color: #8592a3;
border-color: #8592a3;
background-color: var(--bs-secondary);
border-color: var(--bs-secondary);
box-shadow: 0 0.125rem 0.25rem 0 rgba(133, 146, 163, 0.4);
}
.btn-secondary:hover {
color: #fff !important;
background-color: #788393 !important;
border-color: #788393 !important;
background-color: var(--bs-secondary) !important;
border-color: var(--bs-secondary) !important;
}
.btn-check:focus + .btn-secondary,
.btn-secondary:focus,
.btn-secondary.focus {
color: #fff;
background-color: #788393;
border-color: #788393;
background-color: var(--bs-secondary);
border-color: var(--bs-secondary);
}
.btn-check:checked + .btn-secondary,
.btn-check:active + .btn-secondary,
@ -8118,43 +8119,43 @@ label.btn {
.btn-secondary.show.dropdown-toggle,
.show > .btn-secondary.dropdown-toggle {
color: #fff !important;
background-color: #788393 !important;
border-color: #788393 !important;
background-color: var(--bs-secondary) !important;
border-color: var(--bs-secondary) !important;
}
.btn-secondary.disabled,
.btn-secondary:disabled {
color: #fff !important;
background-color: #8592a3 !important;
border-color: #8592a3 !important;
background-color: var(--bs-secondary) !important;
border-color: var(--bs-secondary) !important;
}
.btn-group .btn-secondary,
.input-group .btn-secondary {
border-right: var(--bs-border-width) solid #788393;
border-left: var(--bs-border-width) solid #788393;
border-right: var(--bs-border-width) solid var(--bs-secondary);
border-left: var(--bs-border-width) solid var(--bs-secondary);
}
.btn-group-vertical .btn-secondary {
border-top-color: #788393;
border-bottom-color: #788393;
border-top-color: var(--bs-secondary);
border-bottom-color: var(--bs-secondary);
}
.btn-outline-secondary {
color: #8592a3;
border-color: #8592a3;
color: var(--bs-secondary);
border-color: var(--bs-secondary);
background: transparent;
}
.btn-outline-secondary:hover {
color: #fff !important;
background-color: #788393 !important;
border-color: #788393 !important;
background-color: var(--bs-secondary) !important;
border-color: var(--bs-secondary) !important;
box-shadow: 0 0.125rem 0.25rem 0 rgba(133, 146, 163, 0.4) !important;
}
.btn-check:focus + .btn-outline-secondary,
.btn-outline-secondary:focus {
color: #fff;
background-color: #788393;
border-color: #788393;
background-color: var(--bs-secondary);
border-color: var(--bs-secondary);
}
.btn-check:checked + .btn-outline-secondary,
.btn-check:active + .btn-outline-secondary,
@ -8162,18 +8163,18 @@ label.btn {
.btn-outline-secondary.active,
.btn-outline-secondary.dropdown-toggle.show {
color: #fff !important;
background-color: #788393 !important;
border-color: #788393 !important;
background-color: var(--bs-secondary) !important;
border-color: var(--bs-secondary) !important;
}
.btn-outline-secondary.disabled,
.btn-outline-secondary:disabled {
color: #8592a3 !important;
border-color: #8592a3 !important;
color: var(--bs-secondary) !important;
border-color: var(--bs-secondary) !important;
}
.btn-outline-secondary .badge {
background: #8592a3;
border-color: #8592a3;
background: var(--bs-secondary);
border-color: var(--bs-secondary);
color: #fff;
}
@ -8184,7 +8185,7 @@ label.btn {
.show > .btn-outline-secondary.dropdown-toggle .badge {
background: #fff;
border-color: #fff;
color: #8592a3;
color: var(--bs-secondary);
}
.btn-success {
@ -9537,20 +9538,20 @@ li:not(:first-child) .dropdown-item,
.alert-secondary {
background-color: #ebeef0;
border-color: #ebeef0;
color: #8592a3;
color: var(--bs-secondary);
}
.alert-secondary .btn-close {
background-image: url("data:image/svg+xml,%3Csvg width='150px' height='151px' viewBox='0 0 150 151' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cdefs%3E%3Cpolygon id='path-1' points='131.251657 0 74.9933705 56.25 18.7483426 0 0 18.75 56.2450278 75 0 131.25 18.7483426 150 74.9933705 93.75 131.251657 150 150 131.25 93.7549722 75 150 18.75'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='🎨-%5BSetup%5D:-Colors-&amp;-Shadows' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cg id='Artboard' transform='translate(-225.000000, -250.000000)'%3E%3Cg id='Icon-Color' transform='translate(225.000000, 250.500000)'%3E%3Cuse fill='%238592a3' xlink:href='%23path-1'%3E%3C/use%3E%3Cuse fill-opacity='0.5' fill='%238592a3' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
background-image: url("data:image/svg+xml,%3Csvg width='150px' height='151px' viewBox='0 0 150 151' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cdefs%3E%3Cpolygon id='path-1' points='131.251657 0 74.9933705 56.25 18.7483426 0 0 18.75 56.2450278 75 0 131.25 18.7483426 150 74.9933705 93.75 131.251657 150 150 131.25 93.7549722 75 150 18.75'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='🎨-%5BSetup%5D:-Colors-&amp;-Shadows' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cg id='Artboard' transform='translate(-225.000000, -250.000000)'%3E%3Cg id='Icon-Color' transform='translate(225.000000, 250.500000)'%3E%3Cuse fill='%2335728e' xlink:href='%23path-1'%3E%3C/use%3E%3Cuse fill-opacity='0.5' fill='%2335728e' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}
.alert-secondary .alert-link {
color: #8592a3;
color: var(--bs-secondary);
}
.alert-secondary hr {
color: #8592a3 !important;
color: var(--bs-secondary) !important;
}
.alert-secondary .alert-icon {
background-color: #8592a3;
background-color: var(--bs-secondary);
box-shadow: 0 0 0 0.125rem rgba(133, 146, 163, 0.16);
}
@ -10465,27 +10466,27 @@ form select.selectpicker.is-invalid ~ .btn {
}
.list-group-item-secondary {
border-color: #8592a3;
border-color: var(--bs-secondary);
background-color: #ebeef0;
color: #8592a3 !important;
color: var(--bs-secondary) !important;
}
a.list-group-item-secondary,
button.list-group-item-secondary {
color: #8592a3;
color: var(--bs-secondary);
}
a.list-group-item-secondary:hover,
a.list-group-item-secondary:focus,
button.list-group-item-secondary:hover,
button.list-group-item-secondary:focus {
border-color: #8592a3;
border-color: var(--bs-secondary);
background-color: #dde0e2;
color: #8592a3;
color: var(--bs-secondary);
}
a.list-group-item-secondary.active,
button.list-group-item-secondary.active {
border-color: #8592a3 !important;
background-color: #8592a3 !important;
border-color: var(--bs-secondary) !important;
background-color: var(--bs-secondary) !important;
color: #fff !important;
}
@ -11018,7 +11019,7 @@ button.list-group-item-gray.active {
}
.bg-secondary.toast .toast-header .btn-close,
.bg-secondary.bs-toast .toast-header .btn-close {
background-color: #8592a3 !important;
background-color: var(--bs-secondary) !important;
background-image: url("data:image/svg+xml,%3Csvg width='150px' height='151px' viewBox='0 0 150 151' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cdefs%3E%3Cpolygon id='path-1' points='131.251657 0 74.9933705 56.25 18.7483426 0 0 18.75 56.2450278 75 0 131.25 18.7483426 150 74.9933705 93.75 131.251657 150 150 131.25 93.7549722 75 150 18.75'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='🎨-%5BSetup%5D:-Colors-&amp;-Shadows' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cg id='Artboard' transform='translate(-225.000000, -250.000000)'%3E%3Cg id='Icon-Color' transform='translate(225.000000, 250.500000)'%3E%3Cuse fill='%23fff' xlink:href='%23path-1'%3E%3C/use%3E%3Cuse fill-opacity='1' fill='%23fff' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
box-shadow: 0 0.1875rem 0.375rem 0 rgba(133, 146, 163, 0.4) !important;
}
@ -11508,7 +11509,7 @@ button.list-group-item-gray.active {
}
.border-secondary {
border-color: #8592a3 !important;
border-color: var(--bs-secondary) !important;
}
.border-success {
@ -22564,19 +22565,13 @@ body {
@keyframes colorPhase {
0% {
background-color: var(--bs-primary); /* Start with primary color */
box-shadow: 0 1px 20px 1px var(--bs-primary); /* Primary shadow */
border-color: var(--bs-primary); /* Primary border color */
}
50% {
background-color: var(--bs-bw-green); /* Transition to secondary color */
box-shadow: 0 1px 20px 1px var(--bs-bw-green); /* Secondary shadow */
border-color: var(--bs-bw-green); /* Secondary border color */
}
100% {
background-color: var(--bs-primary); /* Back to primary color */
box-shadow: 0 1px 20px 1px var(--bs-primary); /* Primary shadow */
border-color: var(--bs-primary); /* Primary border color */
}
}
@ -22586,7 +22581,7 @@ body {
right: 1.625rem;
z-index: 1080;
box-shadow: 0 1px 20px 1px var(--bs-primary); /* Initial shadow */
background-color: var(--bs-primary); /* Initial background color */
background-color: var(--bs-bw-green); /* Initial background color */
color: #fff;
animation: colorPhase 3s infinite; /* Apply the color phasing animation */
transition:
@ -22599,9 +22594,6 @@ body {
background-color: var(
--bs-bw-green
) !important; /* Keep the primary color on hover */
border-color: var(
--bs-bw-green
) !important; /* Keep the primary color on hover */
}
.ui-square,
@ -23930,7 +23922,7 @@ html:not(.layout-footer-fixed) .content-wrapper {
background-color: #71dd37;
}
.avatar.avatar-offline:after {
background-color: #8592a3;
background-color: var(--bs-secondary);
}
.avatar.avatar-away:after {
background-color: #ffab00;
@ -24124,7 +24116,7 @@ html:not(.layout-footer-fixed) .content-wrapper {
.divider.divider.divider-secondary .divider-text:before,
.divider.divider.divider-secondary .divider-text:after {
border-color: #8592a3;
border-color: var(--bs-secondary);
}
.divider.divider.divider-success .divider-text:before,
@ -24211,7 +24203,7 @@ html:not(.layout-footer-fixed) .content-wrapper {
}
.navbar.bg-secondary .search-input-wrapper .search-input,
.navbar.bg-secondary .search-input-wrapper .search-toggler {
background-color: #8592a3 !important;
background-color: var(--bs-secondary) !important;
color: #eaecef;
}
.navbar.bg-secondary .navbar-nav > .nav-link,

View file

@ -193,7 +193,7 @@
background-color: var(--bs-bw-green);
}
.pro-icon {
/* .pro-icon {
position: relative;
width: 18px;
height: 15.5px;
@ -223,7 +223,7 @@
background-image: url("../img/diamond-blue.svg");
opacity: 0;
animation: fadeIn 1.5s infinite alternate;
}
} */
@media (max-width: 768px) {
.btn-responsive {
@ -232,10 +232,10 @@
line-height: 1;
}
.pro-icon {
/* .pro-icon {
width: 9px;
height: 7.75px;
}
} */
}
@media (min-width: 769px) and (max-width: 992px) {
@ -287,7 +287,7 @@ td.highlight {
.btn-outline-bw-green:hover {
color: #fff;
background-color: var(--bs-primary);
background-color: var(--bs-bw-green);
}
.col.form-floating > .form-control-sm,
@ -320,3 +320,81 @@ td.highlight {
.form-floating > .form-control.form-control-sm:focus {
padding-top: 1.1rem !important;
}
.shine {
background-image: linear-gradient(
120deg,
rgba(46, 172, 104, 0) 0%,
rgba(46, 172, 104, 0.8) 50%,
rgba(46, 172, 104, 0) 100%
);
background-size: 40%;
background-position: -100% center;
background-repeat: no-repeat;
background-clip: text;
-webkit-background-clip: text;
color: transparent;
animation: shine 8s linear infinite;
animation-fill-mode: forwards;
}
.shine-sm {
animation: shineSm 5s linear infinite;
}
@keyframes shine {
0% {
background-position: -200% center;
}
100% {
background-position: 200% center;
}
}
@keyframes shineSm {
0% {
background-position: -200% center;
}
60% {
background-position: 200% center;
}
100% {
background-position: 200% center;
}
}
.text-primary.shine {
color: rgba(var(--bs-primary-rgb), 0.1) !important;
background-color: var(--bs-primary) !important;
}
.form-floating > :disabled ~ label::after,
.form-floating > .form-control:disabled ~ label::after {
background-color: transparent;
}
.chevron-icon {
padding-bottom: 0.125rem !important;
}
.chevron-rotate {
transform: rotate(90deg);
transition: transform 0.3s ease;
}
.chevron-rotate-back {
transform: rotate(0deg);
transition: transform 0.3s ease;
}
.multiple-container:not(:first-child) {
border-top: 1px solid var(--bs-primary);
}
a.badge:hover {
color: #fff;
}
.setting-checkbox-label {
font-size: calc(var(--bs-body-font-size) * 0.85);
}

View file

@ -126,7 +126,7 @@
}
.text-primary {
color: #0b5577 !important;
color: #0b354a !important;
}
.text-body[href]:hover,
@ -135,23 +135,23 @@
}
.bg-primary {
background-color: #0b5577 !important;
background-color: #0b354a !important;
}
a.bg-primary:hover,
a.bg-primary:focus {
background-color: #0b5577 !important;
background-color: #0b354a !important;
}
.dropdown-notifications-item:not(.mark-as-read)
.dropdown-notifications-read
span {
background-color: #0b5577;
background-color: #0b354a;
}
.bg-label-primary {
background-color: #e7e7ff !important;
color: #0b5577 !important;
color: #0b354a !important;
}
.border-label-primary {
@ -168,40 +168,40 @@ a.bg-primary:focus {
.pagination li.active > a:not(.page-link),
.pagination li.active > a:not(.page-link):hover,
.pagination li.active > a:not(.page-link):focus {
border-color: #0b5577;
background-color: #0b5577;
border-color: #0b354a;
background-color: #0b354a;
color: #fff;
box-shadow: 0 0.125rem 0.25rem rgba(11, 85, 119, 0.4);
}
.progress-bar {
background-color: #0b5577;
background-color: #0b354a;
color: #fff;
box-shadow: 0 2px 4px 0 rgba(11, 85, 119, 0.4);
}
.list-group-item-primary {
border-color: #0b5577;
border-color: #0b354a;
background-color: #e7e7ff;
color: #0b5577 !important;
color: #0b354a !important;
}
a.list-group-item-primary,
button.list-group-item-primary {
color: #0b5577;
color: #0b354a;
}
a.list-group-item-primary:hover,
a.list-group-item-primary:focus,
button.list-group-item-primary:hover,
button.list-group-item-primary:focus {
border-color: #0b5577;
border-color: #0b354a;
background-color: #d9d9f0;
color: #0b5577;
color: #0b354a;
}
a.list-group-item-primary.active,
button.list-group-item-primary.active {
border-color: #0b5577 !important;
background-color: #0b5577 !important;
border-color: #0b354a !important;
background-color: #0b354a !important;
color: #fff !important;
}
@ -209,26 +209,26 @@ button.list-group-item-primary.active {
.list-group-item.active:hover,
.list-group-item.active:focus {
background-color: #e7e7ff;
color: #0b5577;
color: #0b354a;
}
.alert-primary {
background-color: #e7e7ff;
border-color: #e7e7ff;
color: #0b5577;
color: #0b354a;
}
.alert-primary .btn-close {
background-image: url("data:image/svg+xml,%3Csvg width='150px' height='151px' viewBox='0 0 150 151' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cdefs%3E%3Cpolygon id='path-1' points='131.251657 0 74.9933705 56.25 18.7483426 0 0 18.75 56.2450278 75 0 131.25 18.7483426 150 74.9933705 93.75 131.251657 150 150 131.25 93.7549722 75 150 18.75'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='🎨-%5BSetup%5D:-Colors-&amp;-Shadows' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cg id='Artboard' transform='translate(-225.000000, -250.000000)'%3E%3Cg id='Icon-Color' transform='translate(225.000000, 250.500000)'%3E%3Cuse fill='%230b5577' xlink:href='%23path-1'%3E%3C/use%3E%3Cuse fill-opacity='0.5' fill='%230b5577' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
background-image: url("data:image/svg+xml,%3Csvg width='150px' height='151px' viewBox='0 0 150 151' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cdefs%3E%3Cpolygon id='path-1' points='131.251657 0 74.9933705 56.25 18.7483426 0 0 18.75 56.2450278 75 0 131.25 18.7483426 150 74.9933705 93.75 131.251657 150 150 131.25 93.7549722 75 150 18.75'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='🎨-%5BSetup%5D:-Colors-&amp;-Shadows' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cg id='Artboard' transform='translate(-225.000000, -250.000000)'%3E%3Cg id='Icon-Color' transform='translate(225.000000, 250.500000)'%3E%3Cuse fill='%230B354A' xlink:href='%23path-1'%3E%3C/use%3E%3Cuse fill-opacity='0.5' fill='%230B354A' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}
.alert-primary .alert-link {
color: #0b5577;
color: #0b354a;
}
.alert-primary hr {
color: #0b5577 !important;
color: #0b354a !important;
}
.alert-primary .alert-icon {
background-color: #0b5577;
background-color: #0b354a;
box-shadow: 0 0 0 0.125rem rgba(11, 85, 119, 0.16);
}
@ -250,8 +250,8 @@ button.list-group-item-primary.active {
.btn-primary {
color: #fff;
background-color: #0b5577;
border-color: #0b5577;
background-color: #0b354a;
border-color: #0b354a;
box-shadow: 0 0.125rem 0.25rem 0 rgba(11, 85, 119, 0.4);
}
.btn-primary:hover {
@ -279,8 +279,8 @@ button.list-group-item-primary.active {
.btn-primary.disabled,
.btn-primary:disabled {
color: #fff !important;
background-color: #0b5577 !important;
border-color: #0b5577 !important;
background-color: #0b354a !important;
border-color: #0b354a !important;
}
.btn-group .btn-primary,
@ -295,21 +295,21 @@ button.list-group-item-primary.active {
}
.btn-outline-primary {
color: #0b5577;
border-color: #0b5577;
color: #0b354a;
border-color: #0b354a;
background: transparent;
}
.btn-outline-primary:hover {
color: #fff !important;
background-color: #2eac68 !important;
border-color: #2eac68 !important;
background-color: #0b354a !important;
border-color: #0b354a !important;
box-shadow: 0 0.125rem 0.25rem 0 rgba(11, 85, 119, 0.4) !important;
}
.btn-check:focus + .btn-outline-primary,
.btn-outline-primary:focus {
color: #fff;
background-color: #2eac68;
border-color: #2eac68;
background-color: #0b354a;
border-color: #0b354a;
}
.btn-check:checked + .btn-outline-primary,
.btn-check:active + .btn-outline-primary,
@ -317,18 +317,18 @@ button.list-group-item-primary.active {
.btn-outline-primary.active,
.btn-outline-primary.dropdown-toggle.show {
color: #fff !important;
background-color: #2eac68 !important;
border-color: #2eac68 !important;
background-color: #0b354a !important;
border-color: #0b354a !important;
}
.btn-outline-primary.disabled,
.btn-outline-primary:disabled {
color: #0b5577 !important;
border-color: #0b5577 !important;
color: #0b354a !important;
border-color: #0b354a !important;
}
.btn-outline-primary .badge {
background: #0b5577;
border-color: #0b5577;
background: #0b354a;
border-color: #0b354a;
color: #fff;
}
@ -339,19 +339,19 @@ button.list-group-item-primary.active {
.show > .btn-outline-primary.dropdown-toggle .badge {
background: #fff;
border-color: #fff;
color: #0b5577;
color: #0b354a;
}
.dropdown-item:not(.disabled).active,
.dropdown-item:not(.disabled):active {
background-color: rgba(11, 85, 119, 0.16);
color: #0b5577 !important;
color: #0b354a !important;
}
.dropdown-menu > li:not(.disabled) > a:not(.dropdown-item):active,
.dropdown-menu > li.active:not(.disabled) > a:not(.dropdown-item) {
background-color: rgba(11, 85, 119, 0.16);
color: #0b5577 !important;
color: #0b354a !important;
}
.dt-button-collection .dropdown-item:not(.disabled):active {
@ -364,7 +364,7 @@ button.list-group-item-primary.active {
.nav-pills .nav-link.active,
.nav-pills .nav-link.active:hover,
.nav-pills .nav-link.active:focus {
background-color: #0b5577;
background-color: #0b354a;
color: #fff;
}
@ -374,7 +374,7 @@ button.list-group-item-primary.active {
.nav-tabs .nav-link.active,
.nav-tabs .nav-link.active:hover,
.nav-tabs .nav-link.active:focus {
box-shadow: 0 -2px 0 #0b5577 inset;
box-shadow: 0 -2px 0 #0b354a inset;
}
.nav-align-bottom .nav-tabs .nav-link.active,
@ -383,7 +383,7 @@ button.list-group-item-primary.active {
.nav-align-bottom .nav-tabs .nav-link.active,
.nav-align-bottom .nav-tabs .nav-link.active:hover,
.nav-align-bottom .nav-tabs .nav-link.active:focus {
box-shadow: 0 2px 0 #0b5577 inset;
box-shadow: 0 2px 0 #0b354a inset;
}
.nav-align-left .nav-tabs .nav-link.active,
@ -392,7 +392,7 @@ button.list-group-item-primary.active {
.nav-align-left .nav-tabs .nav-link.active,
.nav-align-left .nav-tabs .nav-link.active:hover,
.nav-align-left .nav-tabs .nav-link.active:focus {
box-shadow: -2px 0px 0 #0b5577 inset;
box-shadow: -2px 0px 0 #0b354a inset;
}
.nav-align-right .nav-tabs .nav-link.active,
@ -401,32 +401,32 @@ button.list-group-item-primary.active {
.nav-align-right .nav-tabs .nav-link.active,
.nav-align-right .nav-tabs .nav-link.active:hover,
.nav-align-right .nav-tabs .nav-link.active:focus {
box-shadow: 2px 0px 0 #0b5577 inset;
box-shadow: 2px 0px 0 #0b354a inset;
}
.form-control:focus,
.form-select:focus {
border-color: #0b5577 !important;
border-color: #0b354a !important;
}
.input-group:focus-within .form-control,
.input-group:focus-within .input-group-text {
border-color: #0b5577 !important;
border-color: #0b354a !important;
}
.form-check-input:checked {
background-color: #0b5577;
border-color: #0b5577;
background-color: #0b354a;
border-color: #0b354a;
box-shadow: 0 0.125rem 0.25rem 0 rgba(11, 85, 119, 0.4);
}
.form-check-input[type="checkbox"]:indeterminate {
background-color: #0b5577;
border-color: #0b5577;
background-color: #0b354a;
border-color: #0b354a;
box-shadow: 0 0.125rem 0.25rem 0 rgba(11, 85, 119, 0.4);
}
.custom-option.checked {
border: 1px solid #0b5577;
border: 1px solid #0b354a;
}
.form-switch .form-check-input:focus {
@ -437,7 +437,7 @@ button.list-group-item-primary.active {
}
.form-control:focus ~ .form-label {
border-color: #0b5577;
border-color: #0b354a;
}
.form-control:focus ~ .form-label::after {
border-color: inherit;
@ -445,7 +445,7 @@ button.list-group-item-primary.active {
.divider.divider-primary .divider-text:before,
.divider.divider-primary .divider-text:after {
border-color: #0b5577;
border-color: #0b354a;
}
.navbar.bg-primary {
@ -468,7 +468,7 @@ button.list-group-item-primary.active {
}
.navbar.bg-primary .search-input-wrapper .search-input,
.navbar.bg-primary .search-input-wrapper .search-toggler {
background-color: #0b5577 !important;
background-color: #0b354a !important;
color: #e0e1ff;
}
.navbar.bg-primary .navbar-nav > .nav-link,
@ -517,7 +517,7 @@ button.list-group-item-primary.active {
}
.menu.bg-primary {
background-color: #0b5577 !important;
background-color: #0b354a !important;
color: #e0e1ff;
}
.menu.bg-primary .menu-link,
@ -578,7 +578,7 @@ button.list-group-item-primary.active {
}
.menu.bg-primary .menu-inner-shadow {
background: linear-gradient(
#0b5577 41%,
#0b354a 41%,
rgba(11, 85, 119, 0.11) 95%,
rgba(11, 85, 119, 0)
);
@ -630,7 +630,7 @@ button.list-group-item-primary.active {
@media (max-width: 1199.98px) {
.app-brand .layout-menu-toggle {
border: 7px solid #0b5577 !important;
border: 7px solid #0b354a !important;
}
}
.footer.bg-primary {
@ -671,7 +671,7 @@ button.list-group-item-primary.active {
}
.bg-primary.toast .toast-header .btn-close,
.bg-primary.bs-toast .toast-header .btn-close {
background-color: #0b5577 !important;
background-color: #0b354a !important;
background-image: url("data:image/svg+xml,%3Csvg width='150px' height='151px' viewBox='0 0 150 151' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Cdefs%3E%3Cpolygon id='path-1' points='131.251657 0 74.9933705 56.25 18.7483426 0 0 18.75 56.2450278 75 0 131.25 18.7483426 150 74.9933705 93.75 131.251657 150 150 131.25 93.7549722 75 150 18.75'%3E%3C/polygon%3E%3C/defs%3E%3Cg id='🎨-%5BSetup%5D:-Colors-&amp;-Shadows' stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Cg id='Artboard' transform='translate(-225.000000, -250.000000)'%3E%3Cg id='Icon-Color' transform='translate(225.000000, 250.500000)'%3E%3Cuse fill='%23fff' xlink:href='%23path-1'%3E%3C/use%3E%3Cuse fill-opacity='1' fill='%23fff' xlink:href='%23path-1'%3E%3C/use%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
box-shadow: 0 0.1875rem 0.375rem 0 rgba(11, 85, 119, 0.4) !important;
}
@ -680,27 +680,27 @@ button.list-group-item-primary.active {
.form-floating > .form-control:focus:not(:placeholder-shown) ~ label,
.form-floating > .form-select:focus ~ label,
.form-floating > .form-select:focus:not(:placeholder-shown) ~ label {
color: #0b5577;
color: #0b354a;
}
.svg-illustration svg {
fill: #0b5577;
fill: #0b354a;
}
html:not([dir="rtl"]) .border-primary,
html[dir="rtl"] .border-primary {
border-color: #0b5577 !important;
border-color: #0b354a !important;
}
a {
color: #0b5577;
color: #0b354a;
}
a:hover {
color: #2eac68;
}
.fill-primary {
fill: #0b5577;
fill: #0b354a;
}
.bg-navbar-theme {
@ -945,14 +945,14 @@ html:not(.layout-menu-collapsed)
.menu-sub
> .menu-item.active:not(:has(.menu-sub))
.menu-icon {
color: #0b5577;
color: #0b354a;
}
.bg-menu-theme .menu-inner > .menu-item.active > .menu-link {
color: #0b5577;
color: #0b354a;
background-color: rgba(11, 85, 119, 0.16) !important;
}
.bg-menu-theme .menu-inner > .menu-item.active:before {
background: #0b5577;
background: #0b354a;
}
.bg-menu-theme .menu-sub > .menu-item > .menu-link:before {
background-color: #a7acb2 !important;
@ -961,12 +961,12 @@ html:not(.layout-menu-collapsed)
.menu-sub
> .menu-item.active
> .menu-link:not(.menu-toggle):before {
background-color: #0b5577 !important;
background-color: #0b354a !important;
border: 3px solid #e7e7ff !important;
}
.app-brand .layout-menu-toggle {
background-color: #0b5577;
background-color: #0b354a;
border: 7px solid #f5f5f9;
}
.app-brand .layout-menu-toggle i {
@ -980,11 +980,11 @@ html:not(.layout-menu-collapsed)
color: #646e78;
}
.bg-footer-theme .footer-link {
color: #0b5577;
color: #0b354a;
}
.bg-footer-theme .footer-link:hover,
.bg-footer-theme .footer-link:focus {
color: #0b5577;
color: #0b354a;
}
.bg-footer-theme .footer-link.disabled {
color: #a2a8ae !important;
@ -996,7 +996,7 @@ html:not(.layout-menu-collapsed)
.bg-footer-theme .active > .footer-link,
.bg-footer-theme .footer-link.show,
.bg-footer-theme .footer-link.active {
color: #0b5577;
color: #0b354a;
}
.bg-footer-theme hr {
border-color: rgba(11, 85, 119, 0.075);

View file

@ -154,47 +154,15 @@ $(document).ready(function () {
});
});
$("#select-plugin").on("click", function () {
$("#plugin-search").focus();
$("#pluginsCollapse").on("shown.bs.collapse", () => {
$(".chevron-icon")
.addClass("chevron-rotate")
.removeClass("chevron-rotate-back");
});
$("#plugin-search").on("input", function () {
const inputValue = $(this).val().toLowerCase();
const dropdownItems = $("#plugins-dropdown-menu li.nav-item");
dropdownItems.each(function () {
const item = $(this);
const text = item.text().toLowerCase();
console.log(item);
const pluginId = item
.find("button")
.data("bs-target")
.replace("#navs-plugins-", "");
if (text.includes(inputValue) || pluginId.includes(inputValue)) {
item.show();
} else {
item.hide();
}
});
// Show "No Item" message if no items match
if (dropdownItems.filter(":visible").length === 0) {
if ($(".no-items").length === 0) {
$("#plugins-dropdown-menu").append(
'<li class="no-items dropdown-item text-muted">No Item</li>',
);
}
} else {
$(".no-items").remove();
}
});
$(document).on("click", function (event) {
if (!$(event.target).closest(".card").length) {
$("#plugins-dropdown-menu").removeClass("show");
$("#plugin-search").val("");
$("#plugin-search").trigger("input");
}
$("#pluginsCollapse").on("hidden.bs.collapse", () => {
$(".chevron-icon")
.removeClass("chevron-rotate")
.addClass("chevron-rotate-back");
});
});

View file

@ -5,26 +5,7 @@ $(document).ready(function () {
const layout = {
topStart: {},
bottomEnd: {
buttons: [
{
extend: "ping_instances",
},
{
extend: "exec_form",
text: '<span class="tf-icons bx bx-refresh bx-18px me-2"></span>Reload',
className: "btn btn-sm btn-outline-primary",
},
{
extend: "exec_form",
text: '<span class="tf-icons bx bx-stop bx-18px me-2"></span>Stop',
className: "btn btn-sm btn-outline-primary",
},
{
extend: "delete_instances",
},
],
},
bottomEnd: {},
};
if (instanceNumber > 10) {
@ -37,7 +18,7 @@ $(document).ready(function () {
layout.topStart.buttons = [
{
extend: "colvis",
columns: "th:not(:first-child)",
columns: "th:not(:first-child):not(:nth-child(2))",
text: '<span class="tf-icons bx bx-columns bx-18px me-2"></span>Columns',
className: "btn btn-sm btn-outline-primary",
columnText: function (dt, idx, title) {
@ -86,6 +67,28 @@ $(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",
buttons: [
{
extend: "ping_instances",
},
{
extend: "exec_form",
text: '<span class="tf-icons bx bx-refresh bx-18px me-2"></span>Reload',
},
{
extend: "exec_form",
text: '<span class="tf-icons bx bx-stop bx-18px me-2"></span>Stop',
},
{
extend: "delete_instances",
className: "text-danger",
},
],
},
{
extend: "create_instance",
},
@ -107,14 +110,14 @@ $(document).ready(function () {
const getSelectedInstances = () => {
const instances = [];
$("tr.selected").each(function () {
instances.push($(this).find("td:first").text());
instances.push($(this).find("td:eq(1)").text());
});
return instances;
};
$.fn.dataTable.ext.buttons.create_instance = {
text: '<span class="tf-icons bx bx-plus-circle bx-18px me-2"></span>Create<span class="d-none d-md-inline"> new instance</span>',
className: "btn btn-sm btn-outline-primary",
className: "btn btn-sm btn-outline-bw-green",
action: function (e, dt, node, config) {
const modal = new bootstrap.Modal($("#modal-create-instance"));
modal.show();
@ -127,7 +130,6 @@ $(document).ready(function () {
$.fn.dataTable.ext.buttons.ping_instances = {
text: '<span class="tf-icons bx bx-bell bx-18px me-2"></span>Ping',
className: "btn btn-sm btn-outline-primary",
action: function (e, dt, node, config) {
if (actionLock) {
return;
@ -142,6 +144,7 @@ $(document).ready(function () {
setTimeout(() => {
if (actionLock) {
$(".dt-button-background").click();
$("#loadingModal").modal("show");
setTimeout(() => {
$("#loadingModal").modal("hide");
@ -250,7 +253,6 @@ $(document).ready(function () {
$.fn.dataTable.ext.buttons.delete_instances = {
text: '<span class="tf-icons bx bx-trash bx-18px me-2"></span>Delete',
className: "btn btn-sm btn-outline-danger",
action: function (e, dt, node, config) {
if (actionLock) {
return;
@ -292,6 +294,11 @@ $(document).ready(function () {
const instances_table = new DataTable("#instances", {
columnDefs: [
{
orderable: false,
render: DataTable.render.select(),
targets: 0,
},
{
targets: "_all", // Target all columns
createdCell: function (td, cellData, rowData, row, col) {
@ -299,18 +306,37 @@ $(document).ready(function () {
},
},
],
order: [[6, "desc"]],
order: [[7, "desc"]],
autoFill: false,
colReorder: true,
responsive: true,
select: {
style: "multi+shift",
selector: "td:first-child",
headerCheckbox: false,
},
layout: layout,
language: {
info: "Showing _START_ to _END_ of _TOTAL_ instances",
infoEmpty: "No instances available",
infoFiltered: "(filtered from _MAX_ total instances)",
lengthMenu: "Display _MENU_ instances",
zeroRecords: "No matching instances found",
select: {
rows: {
_: "Selected %d instances",
0: "No instances selected",
1: "Selected 1 instance",
},
},
},
initComplete: function (settings, json) {
$("#instances_wrapper .btn-secondary").removeClass("btn-secondary");
},
});
instances_table.on("mouseenter", "td", function () {
const colIdx = instances_table.cell(this).index().column;
const rowIdx = instances_table.cell(this).index().row;
instances_table
.cells()
@ -318,8 +344,31 @@ $(document).ready(function () {
.each((el) => el.classList.remove("highlight"));
instances_table
.column(colIdx)
.cells()
.nodes()
.each((el) => el.classList.add("highlight"));
.each(function (el) {
if (instances_table.cell(el).index().row === rowIdx)
el.classList.add("highlight");
});
});
instances_table.on("mouseleave", "td", function () {
instances_table
.cells()
.nodes()
.each((el) => el.classList.remove("highlight"));
});
// Event listener for the select-all checkbox
$("#select-all-rows").on("change", function () {
const isChecked = $(this).prop("checked");
if (isChecked) {
// Select all rows on the current page
instances_table.rows({ page: "current" }).select();
} else {
// Deselect all rows on the current page
instances_table.rows({ page: "current" }).deselect();
}
});
});

View file

@ -152,7 +152,9 @@ $(document).ready(function () {
setTimeout(() => {
fadeInPlaceholders();
loadSessionData(page);
setTimeout(() => {
loadSessionData(page);
}, 200);
}, 50);
});

View file

@ -0,0 +1,186 @@
$(document).ready(() => {
let currentPlugin = "general";
let currentMode = "easy";
let currentType = "all";
let currentKeywords = "";
const pluginDropdownItems = $("#plugins-dropdown-menu li.nav-item");
const updateUrlParams = (params) => {
// Create a new URL based on the current location (this keeps both the search params and the hash)
const newUrl = new URL(window.location.href);
// Merge existing search parameters with new parameters
const searchParams = new URLSearchParams(newUrl.search);
// Add or update the parameters with the new values passed in the `params` object
Object.keys(params).forEach((key) => {
searchParams.set(key, params[key]);
});
// Update the search params of the URL
newUrl.search = searchParams.toString();
// Push the updated URL (this keeps the hash and the merged search params)
history.pushState(params, document.title, newUrl.toString());
};
$("#select-plugin").on("click", () => $("#plugin-search").focus());
// Debounce for search input to avoid triggering the search on every keystroke
let debounceTimer;
$("#plugin-search").on("input", (e) => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
const inputValue = e.target.value.toLowerCase();
pluginDropdownItems.each(function () {
const item = $(this);
if (currentType !== "all" && item.data("type") !== currentType) return;
const text = item.text().toLowerCase();
const pluginId = item
.find("button")
.data("bs-target")
.replace("#navs-plugins-", "");
text.includes(inputValue) || pluginId.includes(inputValue)
? item.show()
: item.hide();
});
// Show "No Item" message if no items match
const noVisibleItems =
pluginDropdownItems.filter(":visible").length === 0;
if (noVisibleItems && $(".no-items").length === 0) {
$("#plugins-dropdown-menu").append(
'<li class="no-items dropdown-item text-muted">No Item</li>',
);
} else {
$(".no-items").remove();
}
}, 300); // 300ms delay for debounce
});
$("#select-plugin").on("hidden.bs.dropdown", () => {
$("#plugin-search").val("").trigger("input");
$(".no-items").remove();
});
const handleModeChange = (targetClass) => {
currentMode = targetClass.substring(1).replace("navs-modes-", "");
if (currentMode === "easy") {
updateUrlParams(currentType !== "all" ? { type: currentType } : {});
} else {
updateUrlParams({ mode: currentMode });
}
};
$('#service-modes-menu button[data-bs-toggle="tab"]').on(
"shown.bs.tab",
(e) => {
handleModeChange($(e.target).data("bs-target"));
},
);
const handleTabChange = (targetClass) => {
currentPlugin = targetClass.substring(1).replace("navs-plugins-", "");
if (currentPlugin === "general" && window.location.hash) {
const params = {};
if (currentType !== "all") params.type = currentType;
if (currentMode !== "easy") params.mode = currentMode;
updateUrlParams(params);
} else {
window.location.hash = currentPlugin;
}
};
$('#plugins-dropdown-menu button[data-bs-toggle="tab"]').on(
"shown.bs.tab",
(e) => {
handleTabChange($(e.target).data("bs-target"));
},
);
$(".plugin-setting").on("input", function () {
const isValid = $(this).data("pattern")
? new RegExp($(this).data("pattern")).test($(this).val())
: true;
$(this)
.toggleClass("is-valid", isValid)
.toggleClass("is-invalid", !isValid);
});
$(".plugin-setting").on("focusout", function () {
$(this).removeClass("is-valid");
});
$(".show-multiple").on("click", function () {
const toggleText = $(this).text().trim() === "SHOW" ? "HIDE" : "SHOW";
$(this).html(
`<i class="bx bx-${
toggleText === "SHOW" ? "hide" : "show-alt"
} bx-sm"></i>&nbsp;${toggleText}`,
);
});
$(".add-multiple").on("click", function () {
const showButtonId = $(this).attr("id").replace("add", "show");
if ($(`#${showButtonId}`).text().trim() === "SHOW") {
$(`#${showButtonId}`).trigger("click");
}
});
$('div[id^="multiple-"]')
.filter(function () {
return !/^multiple-.*-\d+$/.test($(this).attr("id"));
})
.each(function () {
let defaultValues = true;
$(this)
.find("input, select")
.each(function () {
const type = $(this).attr("type");
const defaultVal = $(this).data("default");
const isChecked =
type === "checkbox" &&
$(this).prop("checked") === (defaultVal === "yes");
const isMatchingValue =
type !== "checkbox" && $(this).val() === defaultVal;
if (!isChecked && !isMatchingValue) defaultValues = false;
});
if (defaultValues) $(`#show-${$(this).attr("id")}`).trigger("click");
});
$("#plugin-type-select").on("change", function () {
currentType = $(this).val();
const params = currentType === "all" ? {} : { type: currentType };
updateUrlParams(params);
pluginDropdownItems.each(function () {
$(this).toggle(
currentType === "all" || $(this).data("type") === currentType,
);
});
const currentPane = $('div[id^="navs-plugins-"].active').first();
if (currentPane.data("type") !== currentType) {
$(`#plugins-dropdown-menu li.nav-item[data-type="${currentType}"]`)
.first()
.find("button")
.tab("show");
}
});
const hash = window.location.hash;
if (hash) {
const targetTab = $(
`button[data-bs-target="#navs-plugins-${hash.substring(1)}"]`,
);
if (targetTab.length) targetTab.tab("show");
}
$("#plugin-type-select").trigger("change");
});

View file

@ -1,4 +1,6 @@
{% set current_endpoint = url_for(request.endpoint).split("/")[-1] %}
{% set current_endpoint = request.path.split("/")[-1] %}
{% set pro_diamond_url = url_for('static', filename='img/diamond.svg') %}
{% set avatar_url = url_for('static', filename='img/avatar_profil_BW.png') %}
<!DOCTYPE html>
<html lang="en"
class="light-style layout-navbar-fixed layout-menu-fixed"
@ -12,54 +14,64 @@
<meta name="author" content="Bunkerity" />
<title>BunkerWeb UI</title>
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="img/favicon.ico" />
<link rel="icon"
type="image/x-icon"
href="{{ url_for('static', filename='img/favicon.ico') }}" />
<!-- Fonts -->
<link rel="stylesheet"
href="fonts/Public_sans.css"
href="{{ url_for('static', filename='fonts/Public_sans.css') }}"
nonce="{{ style_nonce }}" />
<!-- Icons -->
<link rel="stylesheet"
href="fonts/boxicons.min.css"
href="{{ url_for('static', filename='fonts/boxicons.min.css') }}"
nonce="{{ style_nonce }}" />
<!-- Core CSS -->
<link rel="stylesheet" href="css/core.css" nonce="{{ style_nonce }}" />
<link rel="stylesheet"
href="css/theme-default.css"
href="{{ url_for('static', filename='css/core.css') }}"
nonce="{{ style_nonce }}" />
<link rel="stylesheet"
href="{{ url_for('static', filename='css/theme-default.css') }}"
nonce="{{ style_nonce }}" />
<link rel="stylesheet"
href="{{ url_for('static', filename='css/main.css') }}"
nonce="{{ style_nonce }}" />
<link rel="stylesheet" href="css/main.css" nonce="{{ style_nonce }}" />
<!-- Vendors CSS -->
<link rel="stylesheet"
href="libs/perfect-scrollbar/perfect-scrollbar.css"
href="{{ url_for('static', filename='libs/perfect-scrollbar/perfect-scrollbar.css') }}"
nonce="{{ style_nonce }}" />
{% if current_endpoint == "home" %}
<link rel="stylesheet"
href="libs/apexcharts/apexcharts.css"
href="{{ url_for('static', filename='libs/apexcharts/apexcharts.css') }}"
nonce="{{ style_nonce }}" />
{% elif current_endpoint == "instances" %}
<link rel="stylesheet"
href="libs/datatables/datatables.min.css"
href="{{ url_for('static', filename='libs/datatables/datatables.min.css') }}"
nonce="{{ style_nonce }}" />
<link rel="stylesheet"
href="css/pages/instances.css"
href="{{ url_for('static', filename='css/pages/instances.css') }}"
nonce="{{ style_nonce }}" />
{% endif %}
<!-- Page CSS -->
<!-- Page -->
{% if current_endpoint == "login" or current_endpoint == "totp" %}
<link rel="stylesheet" href="css/pages/login.css" nonce="{{ style_nonce }}" />
<link rel="stylesheet"
href="{{ url_for('static', filename='css/pages/login.css') }}"
nonce="{{ style_nonce }}" />
{% elif current_endpoint == "loading" %}
<link rel="stylesheet"
href="css/pages/loading.css"
href="{{ url_for('static', filename='css/pages/loading.css') }}"
nonce="{{ style_nonce }}" />
{% elif current_endpoint == "profile" %}
<link rel="stylesheet"
href="css/pages/profile.css"
href="{{ url_for('static', filename='css/pages/profile.css') }}"
nonce="{{ style_nonce }}" />
{% endif %}
<!-- Helpers -->
<script src="js/helpers.js" nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='js/helpers.js') }}"
nonce="{{ script_nonce }}"></script>
<!-- Config -->
<script src="js/config.js" nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='js/config.js') }}"
nonce="{{ script_nonce }}"></script>
</head>
<body>
<!-- prettier-ignore -->
@ -68,33 +80,52 @@
{% endif %}
{% block page %}{% endblock %}
<!-- Vendors JS -->
<script src="libs/jquery/jquery.min.js" nonce="{{ script_nonce }}"></script>
<script src="libs/popper/popper.min.js" nonce="{{ script_nonce }}"></script>
<script src="libs/bootstrap/bootstrap.bundle.min.js"
<script src="{{ url_for('static', filename='libs/jquery/jquery.min.js') }}"
nonce="{{ script_nonce }}"></script>
<script src="libs/perfect-scrollbar/perfect-scrollbar.min.js"
<script src="{{ url_for('static', filename='libs/popper/popper.min.js') }}"
nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='libs/bootstrap/bootstrap.bundle.min.js') }}"
nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='libs/perfect-scrollbar/perfect-scrollbar.min.js') }}"
nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='libs/purify/purify.min.js') }}"
nonce="{{ script_nonce }}"></script>
<script src="libs/purify/purify.min.js" nonce="{{ script_nonce }}"></script>
{% if current_endpoint == "instances" %}
<script src="libs/datatables/datatables.min.js" nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='libs/datatables/datatables.min.js') }}"
nonce="{{ script_nonce }}"></script>
{% endif %}
<!-- Core JS -->
<script src="js/menu.js" nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='js/menu.js') }}"
nonce="{{ script_nonce }}"></script>
{% if current_endpoint == "home" %}
<script src="libs/apexcharts/apexcharts.min.js" nonce="{{ script_nonce }}"></script>
<script src="js/dashboards-analytics.js" nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='libs/apexcharts/apexcharts.min.js') }}"
nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='js/dashboards-analytics.js') }}"
nonce="{{ script_nonce }}"></script>
{% endif %}
<!-- Main JS -->
<script src="js/main.js" nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='js/main.js') }}"
nonce="{{ script_nonce }}"></script>
{% if current_endpoint != "login" and current_endpoint != "totp" %}
<script async defer src="js/sidebar.js" nonce="{{ script_nonce }}"></script>
<script async
defer
src="{{ url_for('static', filename='js/sidebar.js') }}"
nonce="{{ script_nonce }}"></script>
{% endif %}
{% if current_endpoint == "global-config" or (current_endpoint != "services" and "services" in request.path) %}
<script async
defer
src="{{ url_for('static', filename='js/plugins-settings.js') }}"
nonce="{{ script_nonce }}"></script>
{% endif %}
<!-- Page JS -->
{% if current_endpoint == "profile" %}
<script src="js/pages/profile.js" nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='js/pages/profile.js') }}"
nonce="{{ script_nonce }}"></script>
{% elif current_endpoint == "instances" %}
<script src="js/pages/instances.js" nonce="{{ script_nonce }}"></script>
<script src="{{ url_for('static', filename='js/pages/instances.js') }}"
nonce="{{ script_nonce }}"></script>
{% endif %}
<script async defer src="js/buttons.js"></script>
<script async defer src="{{ url_for('static', filename='js/buttons.js') }}"></script>
</body>
</html>

View file

@ -1,17 +1,21 @@
<nav class="d-flex align-items-center" aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<!-- prettier-ignore -->
{% with breadcrumbs = url_for(request.endpoint).split("/") %}
{% with last_crumb = breadcrumbs[-1] %}
{% for breadcrumb in breadcrumbs[1:] %}
{% if breadcrumb == last_crumb %}
<li class="breadcrumb-item active" aria-current="page">{% else %}</li>
<li class="breadcrumb-item">
{% endif %}
<a href="{{ url_for(breadcrumb) }}">{{ breadcrumb }}</a>
</li>
{% endfor %}
{% endwith %}
{% set breadcrumbs_url = url_for(request.endpoint) %}
{% if current_endpoint != "services" and "services" in request.path %}
{% set breadcrumbs_url = url_for("services") + "/" + current_endpoint %}
{% endif %}
{% with breadcrumbs = breadcrumbs_url.split("/") %}
{% for breadcrumb in breadcrumbs[1:] %}
{% set breadcrumb_url = url_for(breadcrumb.replace('_', '-')) %}
{% if breadcrumb == current_endpoint %}
{% set breadcrumb_url = breadcrumbs_url %}
{% endif %}
<li class="breadcrumb-item{% if breadcrumb == current_endpoint %} active{% endif %}"
{% if breadcrumb == current_endpoint %}aria-current="page"{% endif %}>
<a href="{{ breadcrumb_url }}">{{ breadcrumb }}</a>
</li>
{% endfor %}
{% endwith %}
</ol>
</nav>

View file

@ -13,16 +13,18 @@
<!-- prettier-ignore -->
<div class="d-flex justify-content-between align-items-center mb-5">
{% include "breadcrumb.html" %}
{% if "services" in request.path %}
<ul class="nav nav-pills flex-column flex-md-row" role="tablist">
{% if current_endpoint != "services" and "services" in request.path %}
<ul id="service-modes-menu"
class="nav nav-pills flex-column flex-md-row"
role="tablist">
<li class="nav-item me-0 me-sm-3" role="presentation">
<button type="button"
class="rounded-pill nav-link d-flex align-items-center active"
class="rounded-pill nav-link d-flex align-items-center{% if mode == 'easy' %} active{% endif %}"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-pills-easy"
aria-controls="navs-pills-easy"
aria-selected="true">
data-bs-target="#navs-modes-easy"
aria-controls="navs-modes-easy"
{% if mode == 'easy' %}aria-selected="true"{% endif %}>
<i class="bx bx-customize bx-sm"></i>
&nbsp;
<span class="d-none d-sm-inline">Easy</span>
@ -30,11 +32,12 @@
</li>
<li class="nav-item me-0 me-sm-3" role="presentation">
<button type="button"
class="rounded-pill nav-link d-flex align-items-center"
class="rounded-pill nav-link d-flex align-items-center{% if mode == 'advanced' %} active{% endif %}"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-pills-advanced"
aria-controls="navs-pills-advanced">
data-bs-target="#navs-modes-advanced"
aria-controls="navs-modes-advanced"
{% if mode == 'advanced' %}aria-selected="true"{% endif %}>
<i class="bx bx-shield-quarter bx-sm"></i>
&nbsp;
<span class="d-none d-sm-inline">Advanced</span>
@ -42,11 +45,12 @@
</li>
<li class="nav-item" role="presentation">
<button type="button"
class="rounded-pill nav-link d-flex align-items-center"
class="rounded-pill nav-link d-flex align-items-center{% if mode == 'raw' %} active{% endif %}"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-pills-raw"
aria-controls="navs-pills-raw">
data-bs-target="#navs-modes-raw"
aria-controls="navs-modes-raw"
{% if mode == 'raw' %}aria-selected="true"{% endif %}>
<i class="bx bx-notepad bx-sm"></i>
&nbsp;
<span class="d-none d-sm-inline">Raw</span>
@ -62,7 +66,7 @@
href="https://panel.bunkerweb.io/order/support/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener">
<span class="bx bx-help-circle bx-18px me-0 me-md-2"></span>
<span class="bx bx-help-circle me-0 me-md-2"></span>
<span class="d-none d-md-inline">Need help?</span>
</a>
</li>
@ -74,7 +78,7 @@
data-bs-toggle="offcanvas"
data-bs-target="#side-offcanvas"
aria-controls="side-offcanvas">
<span class="bx bx-news bx-18px me-0 me-md-2"></span>
<span class="bx bx-news me-0 me-md-2"></span>
<span class="d-none d-md-inline">News</span>
</button>
</li>
@ -94,7 +98,10 @@
target="_blank"
rel="noopener">
<span class="me-1 me-md-2 d-flex h-100 justify-content-center align-items-center">
<div class="pro-icon"></div>
<img src="{{ pro_diamond_url }}"
alt="Pro plugin"
width="18px"
height="15.5px">
</span>
Upgrade to Pro</a>
</div>

View file

@ -0,0 +1,6 @@
{% extends "dashboard.html" %}
{% block content %}
<!-- Content -->
{% include "models/plugins_settings.html" %}
<!-- / Content -->
{% endblock %}

View file

@ -26,32 +26,40 @@
<table id="instances" class="table w-100">
<thead>
<tr>
<th>
<input id="select-all-rows"
aria-label="Select all rows"
class="dt-select-checkbox mb-1"
type="checkbox">
Select All
</th>
<th data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-placement="bottom"
data-bs-original-title="The hostname of the Instance">Hostname</th>
<th data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-placement="bottom"
data-bs-original-title="The user defined or auto-generated Instance's name">Name</th>
<th data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-placement="bottom"
data-bs-original-title="The creation method of the Instance">Method</th>
<th data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-placement="bottom"
data-bs-original-title="The Instance's health status">Health</th>
<th data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-placement="bottom"
data-bs-original-title="The Instance's type">Type</th>
<th data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-placement="bottom"
data-bs-original-title="The date and time when the Instance was created">Created</th>
<th data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-placement="bottom"
data-bs-original-title="The date and time when the Instance was last seen">Last Seen</th>
</tr>
</thead>
<tbody>
{% for instance in instances %}
<tr>
<td></td>
<td>{{ instance.hostname }}</td>
<td>{{ instance.name }}</td>
<td id="method-{{ instance.hostname }}">{{ instance.method }}</td>
@ -69,11 +77,11 @@
</td>
<td>
{% if instance.type == "container" %}
<i class="bx bx-18px bxl-docker"></i>&nbsp;Container
<i class="bx bxl-docker"></i>&nbsp;Container
{% elif instance.type == "pod" %}
<i class="bx bx-18px bxl-kubernetes"></i>&nbsp;Pod
<i class="bx bxl-kubernetes"></i>&nbsp;Pod
{% else %}
<i class="bx bx-18px bx-microchip"></i>&nbsp;Static
<i class="bx bx-microchip"></i>&nbsp;Static
{% endif %}
</td>
<td>{{ instance.creation_date.astimezone().strftime("%Y-%m-%d %H:%M:%S %Z") }}</td>
@ -98,7 +106,7 @@
data-bs-dismiss="toast"
aria-label="Close"></button>
</div>
<div class="toast-body">If you read this, ck-t means that you're curious 👀</div>
<div class="toast-body">If you read this, it means that you're curious 👀</div>
</div>
<div class="modal fade"
id="modal-create-instance"

View file

@ -6,7 +6,9 @@
<div class="container-xxl d-flex justify-content-center align-items-center min-vh-100">
<div class="layout-main-wrapper">
<div class="layout-main-placeholder">
<img src="img/logo-menu-2.png" class="img-fluid pulsating" alt="Logo" />
<img src="{{ url_for('static', filename='img/logo-menu-2.png') }}"
class="img-fluid pulsating"
alt="Logo" />
</div>
{% if message %}
<div class="layout-main-info">

View file

@ -14,7 +14,9 @@
rel="noopener"
class="app-brand-link gap-2">
<span class="app-brand-logo login w-75">
<img class="img-fluid" src="img/logo-menu.png" alt="BunkerWeb logo">
<img class="img-fluid"
src="{{ url_for('static', filename='img/logo-menu.png') }}"
alt="BunkerWeb logo">
</span>
</a>
</div>

View file

@ -1,11 +1,13 @@
<!-- Menu -->
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
<div class="app-brand main">
<div class="app-brand main mb-2">
<a href="https://www.bunkerweb.io/?utm_campaign=self&utm_source=ui"
target="_blank"
class="app-brand-link">
<span class="app-brand-logo main w-100">
<img class="img-fluid" src="img/logo-menu.png" alt="BunkerWeb logo">
<img class="img-fluid"
src="{{ url_for('static', filename='img/logo-menu.png') }}"
alt="BunkerWeb logo">
</span>
</a>
<a href="javascript:void(0);"
@ -13,7 +15,7 @@
<i class="bx bx-chevron-left bx-sm d-flex align-items-center justify-content-center"></i>
</a>
</div>
<div class="menu-inner-shadow"></div>
<div class="border border-primary border-0 border-top"></div>
{% with menu_items = {
"home": {"url": url_for('home'), "icon": "bx-home-smile"},
"instances": {"url": url_for('instances'), "icon": "bx-server"},
@ -29,7 +31,7 @@
} %}
<ul class="menu-inner py-1">
{% for endpoint, item in menu_items.items() %}
<li class="menu-item {% if current_endpoint == endpoint %}active{% endif %} {% if item.get('sub') and item.get('open', True) %}open{% endif %}">
<li class="menu-item {% if endpoint in request.path %}active{% endif %} {% if item.get('sub') and item.get('open', True) %}open{% endif %}">
<a href="{{ item['url'] }}"
class="menu-link {% if item.get('sub') %}menu-toggle{% endif %}">
<i class="menu-icon tf-icons bx {{ item['icon'] }}"></i>
@ -59,31 +61,51 @@
</li>
{% endfor %}
<!-- Plugins Pages -->
<li class="menu-header small text-uppercase">
<li class="menu-header text-uppercase align-items-center">
<span class="menu-header-text">Plugins Pages</span>
<button class="btn btn-link menu-header-text p-0"
type="button"
data-bs-toggle="collapse"
data-bs-target="#pluginsCollapse"
aria-expanded="true"
aria-controls="pluginsCollapse">
<i class="bx bx-chevron-right chevron-icon chevron-rotate"></i>
</button>
</li>
{% for plugin in plugins %}
{% with not_pro_pro_plugin = not is_pro_version and plugin['type'] == "pro" %}
{% if not_pro_pro_plugin or plugin['page'] %}
<li class="menu-item {% if current_endpoint == plugin['id'] %}active{% endif %}">
<a href="{% if not_pro_pro_plugin %}https://panel.bunkerweb.io/?utm_campaign=self&utm_source=ui#pro{% else %}{{ url_for("plugins") }}/{{ plugin['id'] }}{% endif %}"
class="menu-link">
<i class="menu-icon tf-icons bx bx-puzzle"></i>
<div class="text-truncate" data-i18n="{{ plugin['name'] }}">{{ plugin['name'] }}</div>
{% if plugin['type'] != "pro" %}
<div class="badge rounded-pill bg-label-{% if plugin['type'] == 'core' %}secondary{% else %}primary{% endif %} text-uppercase fs-tiny ms-auto">
{{ plugin['type'].title() }}
</div>
{% else %}
<div class="badge badge-center rounded-pill text-uppercase fs-tiny ms-auto">
<img src="img/diamond.svg" alt="Pro plugin" width="18px" height="15.5px">
</div>
{% endif %}
</a>
</li>
{% endif %}
{% endwith %}
{% endfor %}
<div class="collapse show" id="pluginsCollapse">
{% for plugin in plugins %}
{% with not_pro_pro_plugin = not is_pro_version and plugin['type'] == "pro" %}
{% if not_pro_pro_plugin or plugin['page'] %}
<li class="menu-item {% if current_endpoint == plugin['id'] %}active{% endif %}">
<a href="{% if not_pro_pro_plugin %}https://panel.bunkerweb.io/?utm_campaign=self&utm_source=ui#pro{% else %}{{ url_for("plugins") }}/{{ plugin['id'] }}{% endif %}"
class="menu-link"
{% if not_pro_pro_plugin %}target="_blank" rel="noopener"{% endif %}>
<i class="menu-icon tf-icons bx bx-puzzle"></i>
<div class="text-truncate{% if plugin['type'] == 'pro' %} text-primary shine shine-sm{% endif %}"
data-i18n="{{ plugin['name'] }}">{{ plugin['name'] }}</div>
{% if plugin['type'] != "pro" %}
<div class="badge rounded-pill bg-label-{% if plugin['type'] == 'core' %}secondary{% else %}primary{% endif %} text-uppercase fs-tiny ms-auto">
{{ plugin['type'].title() }}
</div>
{% else %}
<div class="badge badge-center rounded-pill text-uppercase fs-tiny ms-auto"
data-bs-toggle="tooltip"
data-bs-placement="top"
data-bs-html="true"
title="<i class='bx bx-diamond bx-xs'></i><span>Pro feature</span>">
<img src="{{ pro_diamond_url }}"
alt="Pro plugin"
width="18px"
height="15.5px">
</div>
{% endif %}
</a>
</li>
{% endif %}
{% endwith %}
{% endfor %}
</div>
<!-- / Plugins Pages -->
<!-- Misc -->
<li class="menu-header small text-uppercase">
<span class="menu-header-text">Misc</span>

View file

@ -0,0 +1,13 @@
<div class="form-check form-switch mt-1">
<input id="{{ setting_id_prefix }}setting-{{ plugin['id'] }}-{{ setting_data['id'] }}"
name="{{ setting }}"
class="form-check-input"
type="checkbox"
role="switch"
aria-labelledby="label-{{ plugin['id'] }}-{{ setting_data['id'] }}"
data-default="{{ setting_data['default'] }}"
{% if setting_value == "yes" %}checked{% endif %}
{% if disabled %}disabled{% endif %}>
<label class="form-check setting-checkbox-label d-flex align-items-center ps-0"
for="{{ setting_id_prefix }}setting-{{ plugin['id'] }}-{{ setting_data['id'] }}">{{ setting }}</label>
</div>

View file

@ -0,0 +1,15 @@
<div class="form-floating mt-1{% if setting_data['type'] == 'password' %} input-group input-group-merge form-password-toggle{% endif %}">
<input id="{{ setting_id_prefix }}setting-{{ plugin['id'] }}-{{ setting_data['id'] }}"
name="{{ setting }}"
type="{{ setting_data['type'] }}"
class="form-control plugin-setting"
aria-labelledby="label-{{ plugin['id'] }}-{{ setting_data['id'] }}"
pattern="{{ setting_data['regex'] }}"
value="{{ setting_value }}"
data-default="{{ setting_data['default'] }}"
{% if disabled %}disabled{% endif %}>
<label for="{{ setting_id_prefix }}setting-{{ plugin['id'] }}-{{ setting_data['id'] }}">{{ setting }}</label>
{% if setting_data['type'] == 'password' %}
<span class="input-group-text cursor-pointer"><i class="bx bx-hide"></i></span>
{% endif %}
</div>

View file

@ -0,0 +1,240 @@
{% set blacklisted_settings = get_blacklisted_settings(current_endpoint == "global-config") %}
<div class="card p-1 mb-4">
<div class="d-flex flex-wrap justify-content-around align-items-center">
<div class="dropdown btn-group">
<button id="select-plugin"
type="button"
class="btn btn-outline-primary dropdown-toggle"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="bx bx-extension"></i>
<span class="d-none d-md-inline">&nbsp;Plugins</span>
</button>
<ul id="plugins-dropdown-menu"
class="dropdown-menu nav-pills max-vh-60 overflow-auto pt-0"
role="tablist">
<div class="input-group input-group-merge mb-2">
<span class="input-group-text p-2 border-0 border-primary border-bottom shadow-none"><i class="bx fs-6 bx-search"></i></span>
<input id="plugin-search"
type="text"
class="form-control border-0 border-primary border-bottom shadow-none"
placeholder="Search..."
aria-label="Search...">
</div>
{% for plugin in plugins if get_filtered_settings(plugin["settings"], current_endpoint == "global-config") %}
<li class="nav-item" data-type="{{ plugin['type'] }}">
<button type="button"
class="dropdown-item{% if loop.index == 1 %} active{% endif %}"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-plugins-{{ plugin['id'] }}"
aria-controls="navs-plugins-{{ plugin['id'] }}"
{% if loop.index == 1 %}aria-selected="true"{% endif %}>{{ plugin["name"] }}</button>
</li>
{% endfor %}
</ul>
</div>
<div class="row align-items-center">
<div class="col form-floating ps-0 pe-0 pe-md-2">
<input id="plugin-keyword-search"
type="text"
class="form-control form-control-sm border-0 border-primary border-bottom shadow-none"
placeholder="Keywords...">
<label for="plugin-keyword-search">Keywords</label>
</div>
<div class="col-auto form-floating ps-0 pe-0 d-none d-md-block">
<select id="plugin-type-select"
class="form-select form-select-sm border-0 border-primary border-bottom shadow-none">
<option value="all"{% if type == "all" %} selected{% endif %} value="all">All</option>
<option value="core"{% if type == "core" %} selected{% endif %}>Core</option>
<option value="external"{% if type == "external" %} selected{% endif %}>External</option>
<option value="pro"{% if type == "pro" %} selected{% endif %}>Pro</option>
</select>
<label for="plugin-type-select">Type</label>
</div>
</div>
<button id="save-settings"
type="button"
class="btn btn-sm btn-outline-bw-green">
<i class="bx bx-save bx-sm"></i>
<span class="d-none d-md-inline">&nbsp;Save</span>
</button>
</div>
</div>
{% set plugin_types = {
"core": {
"icon": "<i class=\"bx bx-cube\"></i>",
"title-class": " border-dark"
},
"external": {
"icon": "<i class=\"bx bx-plug\"></i>",
"title-class": " border-secondary text-secondary fw-bold"
},
"pro": {
"title-class": " border-primary text-primary fw-bold shine"
}
} %}
<div class="card tab-content position-relative">
{% for plugin in plugins if get_filtered_settings(plugin["settings"], current_endpoint == "global-config") %}
<div id="navs-plugins-{{ plugin['id'] }}"
class="tab-pane fade{% if loop.index == 1 %} show active{% endif %}"
role="tabpanel"
aria-labelledby="navs-plugins-{{ plugin['id'] }}-tab"
data-type="{{ plugin['type'] }}">
<div class="card-header">
<h5 class="card-title d-inline border p-2{{ plugin_types[plugin['type']].get('title-class', '') }}">
{{ plugin["name"] }}&nbsp;&nbsp;v{{ plugin["version"] }}&nbsp;&nbsp;{{ plugin_types[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 mt-2">{{ plugin["description"] }}</p>
</div>
<div class="card-body row pb-0">
{% for setting, setting_data in get_filtered_settings(plugin["settings"], current_endpoint == "global-config").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_config = config.get(setting, {}) %}
{% set setting_value = setting_config.get("value", setting_data["default"]) %}
{% set setting_method = setting_config.get("method", "default") %}
{% set setting_template = setting_config.get("template", "") %}
{% set disabled = setting_method not in ('ui', 'default') %}
<div class="col-12 col-sm-6 col-lg-4 pb-3"
{% if disabled %}data-bs-toggle="tooltip" data-bs-placement="top" title="Disabled by {{ setting_method }}"{% endif %}>
<div class="d-flex justify-content-between align-items-center">
<label id="label-{{ plugin['id'] }}-{{ setting_data['id'] }}"
for="setting-{{ plugin['id'] }}-{{ setting_data['id'] }}"
class="form-label fw-semibold text-truncate">
{{ setting_data["label"]|capitalize }}
</label>
<div class="d-flex align-items-center">
{% if current_endpoint == "global-config" and setting_data["context"] == "multisite" %}
<a role="badge"
href='https://docs.bunkerweb.io/latest/concepts/?utm_campaign=self&utm_source=ui#multisite-mode'
class="badge badge-center rounded-pill bg-secondary d-flex align-items-center justify-content-center p-1 me-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Multisite setting"
target="_blank"
rel="noopener">
<span class="bx bx-server bx-xs"></span>
</a>
{% endif %}
{% if setting_template %}
<span class="badge badge-center rounded-pill bg-secondary d-flex align-items-center justify-content-center p-1 me-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="From template: {{ setting_template }}">
<span class="bx bx-spreadsheet bx-xs"></span>
</span>
{% endif %}
<span class="badge rounded-pill bg-secondary-subtle text-dark d-flex align-items-center justify-content-center p-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="{{ setting_data['help']|capitalize }}">
<span class="bx bx-question-mark bx-xs"></span>
</span>
</div>
</div>
{% if setting_data["type"] == "select" %}
{% include "models/select_setting.html" %}
{% elif setting_data["type"] == "check" %}
{% include "models/checkbox_setting.html" %}
{% else %}
{% include "models/input_setting.html" %}
{% endif %}
</div>
{% endfor %}
</div>
{% set multiples = get_multiples(get_filtered_settings(plugin["settings"], current_endpoint == "global-config")) %}
{% if multiples %}
{% set setting_id_prefix = "multiple-" %}
{% set multiple_multiples = multiples|length > 1 %}
<div class="card-body row card-body row border-primary border-top pt-3">
<h5 class="row card-title text-uppercase">Multiple settings</h5>
{% for multiple, settings in multiples.items() %}
{% set multiple_settings = settings|length > 1 %}
<div class="col-12{% if multiple_multiples %} col-md-6{% endif %}{% if multiples|length > 2 and not multiple_settings %} col-lg-4{% endif %} pb-3">
<div class="row">
<div class="ps-0 d-flex align-items-center justify-content-start">
<h6 class="mb-0">{{ multiple.replace('-', ' ') | capitalize }}</h6>
<button id="add-multiple-{{ plugin['id'] }}-{{ multiple }}"
type="button"
class="btn btn-xs btn-outline-bw-green rounded-pill add-multiple ms-2">
<i class="bx bx-plus-circle bx-sm"></i>&nbsp;ADD
</button>
<button id="show-multiple-{{ plugin['id'] }}-{{ multiple }}"
type="button"
class="btn btn-xs btn-outline-secondary rounded-pill show-multiple ms-2"
data-bs-toggle="collapse"
data-bs-target="#multiple-{{ plugin['id'] }}-{{ multiple }}"
aria-expanded="true"
aria-controls="multiple-{{ plugin['id'] }}-{{ multiple }}">
<i class="bx bx-show-alt bx-sm"></i>&nbsp;HIDE
</button>
</div>
<div id="multiple-{{ plugin['id'] }}-{{ multiple }}"
class="collapse show multiple-collapse">
<div class="row multiple-container 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_value = setting_config.get("value", setting_data["default"]) %}
{% set setting_method = setting_config.get("method", "default") %}
{% set setting_template = setting_config.get("template", "") %}
{% set disabled = setting_method not in ('ui', 'default') %}
<div id="multiple-{{ plugin['id'] }}-{{ multiple }}-{{ loop.index - 1 }}"
class="col-12{% if multiple_multiples and multiple_settings %} col-md-6{% endif %}{% if not multiple_multiples %} col-lg-4{% endif %} ps-0 pb-2"
{% if disabled %}data-bs-toggle="tooltip" data-bs-placement="top" title="Disabled by {{ setting_method }}"{% endif %}>
<div class="d-flex justify-content-between align-items-center">
<label id="multiple-label-{{ plugin['id'] }}-{{ setting_data['id'] }}"
for="multiple-setting-{{ plugin['id'] }}-{{ setting_data['id'] }}"
class="form-label fw-semibold text-truncate">
{{ setting_data["label"]|capitalize }}
</label>
<div class="d-flex align-items-center">
{% if current_endpoint == "global-config" and setting_data["context"] == "multisite" %}
<a role="badge"
href='https://docs.bunkerweb.io/latest/concepts/?utm_campaign=self&utm_source=ui#multisite-mode'
class="badge badge-center rounded-pill bg-secondary d-flex align-items-center justify-content-center p-1 me-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="Multisite setting"
target="_blank"
rel="noopener">
<span class="bx bx-server bx-xs"></span>
</a>
{% endif %}
{% if setting_template %}
<span class="badge badge-center rounded-pill bg-secondary d-flex align-items-center justify-content-center p-1 me-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="From template: {{ setting_template }}">
<span class="bx bx-spreadsheet bx-xs"></span>
</span>
{% endif %}
<span class="badge rounded-pill bg-secondary-subtle text-dark d-flex align-items-center justify-content-center p-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="{{ setting_data['help']|capitalize }}">
<span class="bx bx-question-mark bx-xs"></span>
</span>
</div>
</div>
{% if setting_data["type"] == "select" %}
{% include "models/select_setting.html" %}
{% elif setting_data["type"] == "check" %}
{% include "models/checkbox_setting.html" %}
{% else %}
{% include "models/input_setting.html" %}
{% endif %}
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endfor %}
</div>

View file

@ -0,0 +1,14 @@
<div class="form-floating mt-1">
<select id="{{ setting_id_prefix }}setting-{{ plugin['id'] }}-{{ setting_data['id'] }}"
name="{{ setting }}"
class="form-select"
aria-labelledby="label-{{ plugin['id'] }}-{{ setting_data['id'] }}"
data-default="{{ setting_data['default'] }}"
{% if disabled %}disabled{% endif %}>
{% for option in setting_data["select"] %}
<option value="{{ option }}"
{% if setting_value == option %}selected{% endif %}>{{ option }}</option>
{% endfor %}
</select>
<label for="{{ setting_id_prefix }}setting-{{ plugin['id'] }}-{{ setting_data['id'] }}">{{ setting }}</label>
</div>

View file

@ -9,24 +9,45 @@
<div class="navbar-collapse collapse navbar-nav-right d-flex align-items-center"
id="navbar-collapse">
<!-- Buttons -->
{% if current_endpoint == 'instances' %}
{% set documentation_endpoint = "/integrations" %}
{% elif current_endpoint == 'global-config' or 'services' in request.path %}
{% set documentation_endpoint = "/settings" %}
{% elif current_endpoint == 'configs' %}
{% set documentation_endpoint = "/concepts" %}
{% set documentation_fragment = "#custom-configurations" %}
{% elif 'plugins' in request.path %}
{% set documentation_endpoint = "/plugins" %}
{% elif current_endpoint == 'cache' %}
{% set documentation_endpoint = "/concepts" %}
{% set documentation_fragment = "#database" %}
{% elif current_endpoint == 'reports' or current_endpoint == 'bans' %}
{% set documentation_fragment = "#security-features" %}
{% elif current_endpoint == 'jobs' %}
{% set documentation_endpoint = "/concepts" %}
{% set documentation_fragment = "#scheduler" %}
{% elif current_endpoint == 'logs' %}
{% set documentation_endpoint = "/web-ui" %}
{% set documentation_fragment = "#accessing-logs" %}
{% endif %}
<div class="navbar-nav align-items-center">
<div class="nav-item d-flex align-items-center">
<a role="button"
class="btn btn-outline-secondary p-1 p-md-2"
aria-pressed="true"
href="https://docs.bunkerweb.io/latest/?utm_campaign=self&utm_source=ui"
href="https://docs.bunkerweb.io/latest{{ documentation_endpoint }}/?utm_campaign=self&utm_source=ui{{ documentation_fragment }}"
target="_blank"
rel="noopener">
<span class="tf-icons bx bx-file bx-18px me-md-2"></span>
<span class="tf-icons bx bx-file me-md-2"></span>
<span class="d-none d-md-inline">Documentation</span>
</a>
<a role="button"
class="btn btn-outline-github p-1 p-md-2 ms-1 ms-md-2"
aria-pressed="true"
href="https://github.com/bunkerity/bunkerweb"
href="https://github.com/bunkerity/bunkerweb/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener">
<i class="tf-icons bx bxl-github bx-18px me-md-2"></i>
<i class="tf-icons bx bxl-github me-md-2"></i>
<span class="d-none d-md-inline">Github</span>
</a>
<a role="button"
@ -35,7 +56,7 @@
href="https://discord.bunkerity.com/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener">
<i class="tf-icons bx bxl-discord bx-18px me-md-2"></i>
<i class="tf-icons bx bxl-discord me-md-2"></i>
<span class="d-none d-md-inline">Discord</span>
</a>
</div>
@ -59,7 +80,8 @@
aria-pressed="true"
href="https://github.com/bunkerity/bunkerweb/releases/latest"
target="_blank"
rel="noopener">
rel="noopener"
{% if bw_version != latest_version %} data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-original-title="New version available" {% endif %}>
Version {{ bw_version }}
{% if bw_version != latest_version %}
<span class="badge-dot position-absolute top-0 start-100 translate-middle bg-danger border border-light rounded-circle">
@ -75,50 +97,39 @@
href="javascript:void(0);"
data-bs-toggle="dropdown">
<div class="avatar avatar-online">
<img src="img/avatar_profil_BW.png"
<img src="{{ avatar_url }}"
alt="User Avatar"
class="w-px-40 h-auto rounded-circle" />
</div>
</a>
{% with profile_url = url_for('profile') %}
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="{{ profile_url }}">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 me-2 me-md-3">
<div class="avatar avatar-online">
<img src="img/avatar_profil_BW.png"
alt="Admin Avatar"
class="w-px-40 h-auto rounded-circle" />
</div>
</div>
<div class="flex-grow-1">
<h6 class="mb-0 text-truncate">{{ current_user.get_id() |title }}</h6>
<small class="text-muted text-truncate">{{ current_user.list_roles[0] }}</small>
<ul class="dropdown-menu dropdown-menu-end">
<li>
<a class="dropdown-item" href="{{ url_for('profile') }}">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 me-2 me-md-3">
<div class="avatar avatar-online">
<img src="{{ avatar_url }}"
alt="Admin Avatar"
class="w-px-40 h-auto rounded-circle" />
</div>
</div>
</a>
</li>
<li>
<div class="dropdown-divider my-1"></div>
</li>
<li>
<a class="dropdown-item d-flex align-items-center"
href="{{ profile_url }}">
<i class="bx bx-user bx-md me-2"></i><span>My Profile</span>
</a>
</li>
<li>
<div class="dropdown-divider my-1"></div>
</li>
<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>
</a>
</li>
</ul>
{% endwith %}
<div class="flex-grow-1">
<h6 class="mb-0 text-truncate">{{ current_user.get_id() |title }}</h6>
<small class="text-muted text-truncate">{{ current_user.list_roles[0] }}</small>
</div>
</div>
</a>
</li>
<li>
<div class="dropdown-divider my-1"></div>
</li>
<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>
</a>
</li>
</ul>
</li>
<!--/ User -->
</ul>

View file

@ -1,75 +0,0 @@
{% extends "dashboard.html" %}
{% block content %}
<!-- Content -->
<div class="d-flex row">
<div class="card p-1">
<div class="d-flex flex-wrap justify-content-around align-items-center">
<div class="btn-group">
<button id="select-plugin"
type="button"
class="btn btn-outline-primary dropdown-toggle"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<i class="bx bx-extension"></i>
<span class="d-none d-md-inline">&nbsp;Plugins</span>
</button>
<ul id="plugins-dropdown-menu"
class="dropdown-menu nav-pills max-vh-60 overflow-auto"
role="tablist">
<input id="plugin-search"
type="text"
class="form-control border-0 border-bottom shadow-none mb-2"
placeholder="Search...">
{% for plugin in plugins %}
<li class="nav-item">
<button type="button"
class="dropdown-item{% if loop.index == 1 %} active{% endif %}"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-plugins-{{ plugin['id'] }}"
aria-controls="navs-plugins-{{ plugin['id'] }}"
{% if loop.index == 1 %}aria-selected="true"{% endif %}>
{{ plugin["name"] }}
</button>
</li>
{% endfor %}
</ul>
</div>
<div class="row align-items-center">
<div class="col form-floating ps-0 pe-0 pe-md-1">
<input id="plugin-keyword-search"
type="text"
class="form-control form-control-sm border-0 border-primary border-bottom shadow-none"
placeholder="Keywords...">
<label for="plugin-keyword-search">Keywords</label>
</div>
<div class="col form-floating ps-0 pe-1 d-none d-md-block">
<input id="plugin-context-search"
type="text"
class="form-control form-control-sm border-0 border-primary border-bottom shadow-none"
placeholder="Context...">
<label for="plugin-context-search">Context</label>
</div>
<div class="col-auto form-floating ps-0 pe-0 d-none d-md-block">
<select id="plugin-type-select"
class="form-select form-select-sm border-0 border-primary border-bottom shadow-none">
<option selected>All</option>
<option value="core">Core</option>
<option value="external">External</option>
<option value="pro">Pro</option>
</select>
<label for="plugin-type-select">Type</label>
</div>
</div>
<button id="save-settings"
type="button"
class="btn btn-sm btn-outline-bw-green">
<i class="bx bx-save bx-sm"></i>
<span class="d-none d-md-inline">&nbsp;Save</span>
</button>
</div>
</div>
</div>
<!-- / Content -->
{% endblock %}

View file

@ -86,7 +86,7 @@
<h5 class="card-header">Edit Profile</h5>
<div class="card-body pb-2">
<div class="d-flex align-items-start align-items-sm-center gap-6 pb-4 border-bottom">
<img src="img/avatar_profil_BW.png"
<img src="{{ avatar_url }}"
alt="Admin Avatar"
class="d-block w-px-100 h-px-100 rounded"
id="uploadedAvatar">

View file

@ -0,0 +1,16 @@
{% extends "dashboard.html" %}
{% block content %}
<!-- Content -->
<div class="tab-content p-0 position-relative">
<div class="tab-pane fade{% if mode == 'easy' %} show active{% endif %}"
id="navs-modes-easy"
role="tabpanel">TODO: Add Easy mode</div>
<div class="tab-pane fade{% if mode == 'advanced' %} show active{% endif %}"
id="navs-modes-advanced"
role="tabpanel">{% include "models/plugins_settings.html" %}</div>
<div class="tab-pane fade{% if mode == 'raw' %} show active{% endif %}"
id="navs-modes-raw"
role="tabpanel">TODO: Add Raw mode</div>
</div>
<!-- / Content -->
{% endblock %}

View file

@ -4,97 +4,77 @@
data-bs-keyboard="false"
data-bs-backdrop="false">
<div class="offcanvas-header">
<div class="nav-align-top">
<ul class="nav nav-pills nav-fill" role="tablist">
<li class="nav-item me-0 me-sm-2" role="presentation">
<button type="button"
class="nav-link active"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-pills-justified-news"
aria-controls="navs-pills-justified-news"
aria-selected="true">
<span class="d-none d-sm-block" id="news-pill"><i class="tf-icons bx bx-news bx-xs me-1_5 align-text-bottom"></i>
News</span><i class="bx bx-news bx-xs d-sm-none"></i>
</button>
</li>
<li class="nav-item" role="presentation">
<button type="button"
class="nav-link"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-pills-justified-notifications"
aria-controls="navs-pills-justified-notifications"
aria-selected="false"
tabindex="-1">
<span class="d-none d-sm-block"><i class="tf-icons bx bx-notification bx-xs me-1_5 align-text-bottom"></i>
Notifications</span><i class="bx bx-notification bx-xs d-sm-none"></i>
</button>
</li>
</ul>
</div>
<button type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Close"></button>
</div>
<div class="offcanvas-body px-0 position-relative">
<div class="tab-content position-relative">
<div class="tab-pane fade show active"
id="navs-pills-justified-news"
role="tabpanel">
<div data-news-container class="flex-auto">
<p class="text-center col-span-12 relative w-full p-4 text-primary rounded-lg fw-bold">
Impossible to connect to blog news.
</p>
</div>
</div>
<div class="tab-pane fade"
id="navs-pills-justified-notifications"
role="tabpanel">
<p>
<!-- TODO -->
Donut dragée jelly pie halvah. Danish gingerbread bonbon cookie wafer
candy oat cake ice cream. Gummies halvah tootsie roll muffin biscuit
icing dessert gingerbread. Pastry ice cream cheesecake fruitcake.
</p>
<p class="mb-0">
Jelly-o jelly beans icing pastry cake cake lemon drops. Muffin muffin
pie tiramisu halvah cotton candy liquorice caramels.
</p>
<div class="social-buttons">
<a href="https://discord.bunkerity.com/?utm_campaign=self&utm_source=ui"
class="btn btn-link"
target="_blank"
rel="noopener">
<i class='bx bxl-discord-alt bx-sm'></i>
</a>
<a href="https://www.linkedin.com/company/bunkerity/"
class="btn btn-link"
target="_blank"
rel="noopener">
<i class='bx bxl-linkedin bx-sm'></i>
</a>
<a href="https://www.reddit.com/r/BunkerWeb/"
class="btn btn-link"
target="_blank"
rel="noopener">
<i class='bx bxl-reddit bx-sm'></i>
</a>
<a href="https://www.instagram.com/bunkerweb/"
class="btn btn-link"
target="_blank"
rel="noopener">
<i class='bx bxl-instagram bx-sm'></i>
</a>
<a href="https://x.com/bunkerity/"
class="btn btn-link"
target="_blank"
rel="noopener">
<i class='bx bxl-twitter bx-sm'></i>
</a>
</div>
<button type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Close"></button>
</div>
</div>
<!-- Newsletter Signup Section -->
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-white border-top">
<h5 class="mb-3 text-dark">Join the Newsletter</h5>
<form action="https://bunkerity.us1.list-manage.com/subscribe/post?u=ec5b1577cf427972b9bd491a6&amp;id=37076d9d67"
method="POST"
id="subscribe-newsletter">
<div class="mb-3">
<input type="email"
id="newsletter-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"
id="newsletter-check"
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">Subscribe</button>
</form>
</div>
<!-- End Newsletter Signup Section -->
<div data-news-container
class="offcanvas-body position-relative ps-3 pe-3">
<p class="text-center relative w-full p-2 text-primary rounded-lg fw-bold">Impossible to connect to blog news.</p>
</div>
<!-- Newsletter Signup Section -->
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-white border-top">
<h5 class="mb-3 text-dark">Join the Newsletter</h5>
<form action="https://bunkerity.us1.list-manage.com/subscribe/post?u=ec5b1577cf427972b9bd491a6&amp;id=37076d9d67"
method="POST"
id="subscribe-newsletter">
<div class="mb-3">
<input type="email"
id="newsletter-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"
id="newsletter-check"
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">Subscribe</button>
</form>
</div>
<!-- End Newsletter Signup Section -->
</div>

View file

@ -17,7 +17,9 @@
rel="noopener"
class="app-brand-link gap-2">
<span class="app-brand-logo login w-75">
<img class="img-fluid" src="img/logo-menu.png" alt="BunkerWeb logo">
<img class="img-fluid"
src="{{ url_for('static', filename='img/logo-menu.png') }}"
alt="BunkerWeb logo">
</span>
</a>
</div>

View file

@ -4,11 +4,11 @@ from os import _exit, getenv
from os.path import join, sep
from pathlib import Path
from subprocess import PIPE, Popen, call
from typing import List, Optional
from typing import Dict, List, Optional, Set
from bcrypt import checkpw, gensalt, hashpw
from magic import Magic
from regex import compile as re_compile
from regex import compile as re_compile, match
from requests import get
from logger import setup_logger # type: ignore
@ -43,8 +43,31 @@ def handle_stop(signum, frame):
stop(0, False)
def check_settings(settings: dict, check: str) -> bool:
return any(setting["context"] == check for setting in settings.values())
def get_multiples(settings: dict) -> Dict[str, dict]:
multiples = {}
for setting, data in settings.items():
multiple = data.get("multiple")
if multiple:
if multiple not in multiples:
multiples[multiple] = {}
multiples[multiple].update({setting: data | {"setting_no_suffix": setting.rsplit("_", 1)[0] if match(r".+_\d+$", setting) else setting}})
return multiples
def get_filtered_settings(settings: dict, global_config: bool = False) -> Dict[str, dict]:
multisites = {}
for setting, data in settings.items():
if not global_config and data["context"] == "global":
continue
multisites[setting] = data
return multisites
def get_blacklisted_settings(global_config: bool = False) -> Set[str]:
blacklisted_settings = {"IS_LOADING", "AUTOCONF_MODE", "SWARM_MODE", "KUBERNETES_MODE", "IS_DRAFT", "BUNKERWEB_INSTANCES"}
if global_config:
blacklisted_settings.update({"SERVER_NAME", "USE_TEMPLATE"})
return blacklisted_settings
def gen_password_hash(password: str) -> bytes:

View file

@ -42,7 +42,7 @@ from app.routes.totp import totp
from app.dependencies import BW_CONFIG, DATA, DB
from app.models.models import AnonymousUser
from app.utils import TMP_DIR, LOGGER, check_settings, get_latest_stable_release, handle_stop, stop
from app.utils import TMP_DIR, LOGGER, get_blacklisted_settings, get_filtered_settings, get_latest_stable_release, get_multiples, handle_stop, stop
signal(SIGINT, handle_stop)
signal(SIGTERM, handle_stop)
@ -97,15 +97,18 @@ with app.app_context():
def custom_url_for(endpoint, **values):
try:
if endpoint not in ("index", "loading", "check", "check_reloading") and "_page" not in endpoint:
if endpoint not in ("static", "index", "loading", "check", "check_reloading") and "_page" not in endpoint:
return url_for(f"{endpoint}.{endpoint}_page", **values)
return url_for(endpoint, **values)
except BuildError:
except BuildError as e:
LOGGER.debug(f"Couldn't build the URL for {endpoint}: {e}")
return "#"
# Declare functions for jinja2
app.jinja_env.globals.update(
check_settings=check_settings,
get_multiples=get_multiples,
get_filtered_settings=get_filtered_settings,
get_blacklisted_settings=get_blacklisted_settings,
url_for=custom_url_for,
)