mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Finish dark mode in web UI (awaiting review)
This commit is contained in:
parent
c9c479b04f
commit
f6c343b547
17 changed files with 430 additions and 211 deletions
|
|
@ -264,7 +264,7 @@ def edit_profile():
|
|||
|
||||
ret = DB.update_ui_user(**user_data, old_username=current_user.get_id())
|
||||
if ret:
|
||||
return handle_error(f"Couldn't update the admin user in the database: {ret}", "profile")
|
||||
return handle_error(f"Couldn't update the {current_user.get_id()} user in the database: {ret}", "profile")
|
||||
|
||||
if "new_password" in request.form:
|
||||
session.clear()
|
||||
|
|
|
|||
|
|
@ -910,11 +910,8 @@ a.courier-prime:hover {
|
|||
.dark-style,
|
||||
[data-bs-theme="dark"] {
|
||||
--bs-primary: #fff;
|
||||
--bs-secondary: #9caeb7;
|
||||
--bs-primary-rgb: 255, 255, 255;
|
||||
--bs-secondary-rgb: 156, 174, 183;
|
||||
--bs-primary-text-emphasis: #fff;
|
||||
--bs-secondary-text-emphasis: #b3cad4;
|
||||
--bs-link-color: #fff;
|
||||
--bs-link-color-rgb: 255, 255, 255;
|
||||
--bs-breadcrumb-divider-color: #fff;
|
||||
|
|
@ -976,8 +973,8 @@ a.courier-prime:hover {
|
|||
}
|
||||
|
||||
.dark-style .nav-pills .nav-link.active,
|
||||
.nav-pills .nav-link.active:hover,
|
||||
.nav-pills .nav-link.active:focus {
|
||||
.dark-style .nav-pills .nav-link.active:hover,
|
||||
.dark-style .nav-pills .nav-link.active:focus {
|
||||
background-color: #0a4b69;
|
||||
}
|
||||
|
||||
|
|
@ -996,10 +993,10 @@ a.courier-prime:hover {
|
|||
}
|
||||
|
||||
.dark-style .btn-light {
|
||||
background-color: #0a4b69;
|
||||
border-color: #0a4b69;
|
||||
background-color: #fff;
|
||||
border-color: #fff;
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.dark-style .card {
|
||||
|
|
@ -1027,17 +1024,17 @@ a.courier-prime:hover {
|
|||
}
|
||||
|
||||
.dark-style h6,
|
||||
.h6,
|
||||
h5,
|
||||
.h5,
|
||||
h4,
|
||||
.h4,
|
||||
h3,
|
||||
.h3,
|
||||
h2,
|
||||
.h2,
|
||||
h1,
|
||||
.h1 {
|
||||
.dark-style .h6,
|
||||
.dark-style h5,
|
||||
.dark-style .h5,
|
||||
.dark-style h4,
|
||||
.dark-style .h4,
|
||||
.dark-style h3,
|
||||
.dark-style .h3,
|
||||
.dark-style h2,
|
||||
.dark-style .h2,
|
||||
.dark-style h1,
|
||||
.dark-style .h1 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
|
@ -1058,6 +1055,7 @@ h1,
|
|||
.dark-style .text-muted {
|
||||
color: #9caeb7 !important;
|
||||
}
|
||||
|
||||
.dark-style .sticky-card {
|
||||
background-color: rgba(12, 40, 58, 0.95) !important;
|
||||
}
|
||||
|
|
@ -1181,3 +1179,22 @@ html[dir="rtl"].dark-style .border-primary {
|
|||
color: #fff;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
|
||||
.dark-style .btn-check:checked + .btn-primary,
|
||||
.dark-style .btn-check:active + .btn-primary,
|
||||
.dark-style .btn-primary:active,
|
||||
.dark-style .btn-primary.active,
|
||||
.dark-style .btn-primary.show.dropdown-toggle,
|
||||
.dark-style .show > .btn-primary.dropdown-toggle {
|
||||
color: #07354a !important;
|
||||
}
|
||||
|
||||
.dark-style .leaflet-container {
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.dark-style .leaflet-layer,
|
||||
.dark-style .leaflet-control-zoom-in,
|
||||
.dark-style .leaflet-control-zoom-out {
|
||||
filter: invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,26 @@ $(document).ready(function () {
|
|||
const editorElement = $("#cache-value");
|
||||
const initialContent = editorElement.text().trim();
|
||||
const editor = ace.edit(editorElement[0]);
|
||||
editor.setTheme("ace/theme/cloud9_day"); // cloud9_night when dark mode is supported
|
||||
|
||||
var theme = $("#theme").val();
|
||||
|
||||
function setEditorTheme() {
|
||||
if (theme === "dark") {
|
||||
editor.setTheme("ace/theme/cloud9_night");
|
||||
} else {
|
||||
editor.setTheme("ace/theme/cloud9_day");
|
||||
}
|
||||
}
|
||||
|
||||
setEditorTheme();
|
||||
|
||||
$("#dark-mode-toggle").on("change", function () {
|
||||
setTimeout(() => {
|
||||
theme = $("#theme").val();
|
||||
setEditorTheme();
|
||||
}, 30);
|
||||
});
|
||||
|
||||
editor.session.setMode("ace/mode/text");
|
||||
editor.setReadOnly(true);
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,25 @@ $(document).ready(function () {
|
|||
const editorElement = $("#config-value");
|
||||
const initialContent = editorElement.text().trim();
|
||||
const editor = ace.edit(editorElement[0]);
|
||||
editor.setTheme("ace/theme/cloud9_day"); // cloud9_night when dark mode is supported
|
||||
|
||||
var theme = $("#theme").val();
|
||||
|
||||
function setEditorTheme() {
|
||||
if (theme === "dark") {
|
||||
editor.setTheme("ace/theme/cloud9_night");
|
||||
} else {
|
||||
editor.setTheme("ace/theme/cloud9_day");
|
||||
}
|
||||
}
|
||||
|
||||
setEditorTheme();
|
||||
|
||||
$("#dark-mode-toggle").on("change", function () {
|
||||
setTimeout(() => {
|
||||
theme = $("#theme").val();
|
||||
setEditorTheme();
|
||||
}, 30);
|
||||
});
|
||||
|
||||
if (isReadOnly && window.location.pathname.endsWith("/new"))
|
||||
window.location.href = window.location.href.split("/new")[0];
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ $(function () {
|
|||
var headingColor = config.colors.headingColor;
|
||||
var legendColor = config.colors.bodyColor;
|
||||
|
||||
const theme = $("#theme").val();
|
||||
var theme = $("#theme").val();
|
||||
|
||||
if (theme === "dark") {
|
||||
headingColor = config.colors.white;
|
||||
|
|
@ -232,94 +232,105 @@ $(function () {
|
|||
100
|
||||
).toFixed(2);
|
||||
|
||||
const requestsOptions = {
|
||||
chart: {
|
||||
type: "donut",
|
||||
},
|
||||
labels: Object.keys(requestsData),
|
||||
series: Object.values(requestsData).map((value) => parseInt(value, 10)),
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 480,
|
||||
options: {
|
||||
chart: {
|
||||
width: 200,
|
||||
},
|
||||
legend: {
|
||||
position: "bottom",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
formatter: function (val, opt) {
|
||||
return ((parseInt(val) / totalRequests) * 100).toFixed(2) + "%";
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
padding: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 15,
|
||||
},
|
||||
},
|
||||
states: {
|
||||
hover: {
|
||||
filter: { type: "none" },
|
||||
},
|
||||
active: {
|
||||
filter: { type: "none" },
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
donut: {
|
||||
size: "75%",
|
||||
labels: {
|
||||
show: true,
|
||||
value: {
|
||||
fontSize: "18px",
|
||||
fontFamily: "Public Sans",
|
||||
fontWeight: 500,
|
||||
color: headingColor,
|
||||
offsetY: -17,
|
||||
formatter: function (val) {
|
||||
return ((parseInt(val) / totalRequests) * 100).toFixed(2) + "%";
|
||||
},
|
||||
},
|
||||
name: {
|
||||
offsetY: 17,
|
||||
fontFamily: "Public Sans",
|
||||
},
|
||||
total: {
|
||||
show: true,
|
||||
fontSize: "13px",
|
||||
color: legendColor,
|
||||
label: "Blocked",
|
||||
formatter: function (w) {
|
||||
return blockedRequestsPercent + "%";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
var requestsChart;
|
||||
|
||||
const requestsChart = new ApexCharts(
|
||||
document.querySelector("#requests-stats"),
|
||||
requestsOptions,
|
||||
);
|
||||
requestsChart.render();
|
||||
function renderStatsChart() {
|
||||
const requestsOptions = {
|
||||
chart: {
|
||||
type: "donut",
|
||||
},
|
||||
labels: Object.keys(requestsData),
|
||||
series: Object.values(requestsData).map((value) => parseInt(value, 10)),
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 480,
|
||||
options: {
|
||||
chart: {
|
||||
width: 200,
|
||||
},
|
||||
legend: {
|
||||
position: "bottom",
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
formatter: function (val, opt) {
|
||||
return ((parseInt(val) / totalRequests) * 100).toFixed(2) + "%";
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
padding: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 15,
|
||||
},
|
||||
},
|
||||
states: {
|
||||
hover: {
|
||||
filter: { type: "none" },
|
||||
},
|
||||
active: {
|
||||
filter: { type: "none" },
|
||||
},
|
||||
},
|
||||
plotOptions: {
|
||||
pie: {
|
||||
donut: {
|
||||
size: "75%",
|
||||
labels: {
|
||||
show: true,
|
||||
value: {
|
||||
fontSize: "18px",
|
||||
fontFamily: "Public Sans",
|
||||
fontWeight: 500,
|
||||
color: headingColor,
|
||||
offsetY: -17,
|
||||
formatter: function (val) {
|
||||
return (
|
||||
((parseInt(val) / totalRequests) * 100).toFixed(2) + "%"
|
||||
);
|
||||
},
|
||||
},
|
||||
name: {
|
||||
offsetY: 17,
|
||||
fontFamily: "Public Sans",
|
||||
},
|
||||
total: {
|
||||
show: true,
|
||||
fontSize: "13px",
|
||||
color: legendColor,
|
||||
label: "Blocked",
|
||||
formatter: function (w) {
|
||||
return blockedRequestsPercent + "%";
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
requestsChart = new ApexCharts(
|
||||
document.querySelector("#requests-stats"),
|
||||
requestsOptions,
|
||||
);
|
||||
requestsChart.render();
|
||||
}
|
||||
|
||||
renderStatsChart();
|
||||
|
||||
// Requests IPs chart
|
||||
|
||||
const $ipsData = $("#requests-ips-data");
|
||||
if ($ipsData.length) {
|
||||
|
||||
var ipsChart;
|
||||
|
||||
function renderIpsChart() {
|
||||
const requestsIpsData = JSON.parse($("#requests-ips-data").text());
|
||||
|
||||
const topIpsData = Object.entries(requestsIpsData)
|
||||
|
|
@ -401,13 +412,17 @@ $(function () {
|
|||
},
|
||||
};
|
||||
|
||||
const ipsChart = new ApexCharts(
|
||||
ipsChart = new ApexCharts(
|
||||
document.querySelector("#requests-ips"),
|
||||
ipsOptions,
|
||||
);
|
||||
ipsChart.render();
|
||||
}
|
||||
|
||||
if ($ipsData.length) {
|
||||
renderIpsChart();
|
||||
}
|
||||
|
||||
// Requests Blocking status
|
||||
|
||||
const blockingData = JSON.parse($("#requests-blocking-data").text());
|
||||
|
|
@ -434,97 +449,124 @@ $(function () {
|
|||
return getColorFromRatio(ratio);
|
||||
});
|
||||
|
||||
const blockingOptions = {
|
||||
chart: {
|
||||
type: "bar",
|
||||
width: "100%",
|
||||
height: 400,
|
||||
toolbar: {
|
||||
var blockingChart;
|
||||
|
||||
function renderBlockingStatus() {
|
||||
const blockingOptions = {
|
||||
chart: {
|
||||
type: "bar",
|
||||
width: "100%",
|
||||
height: 400,
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: "Blocked Requests per Hour",
|
||||
align: "center",
|
||||
style: {
|
||||
color: headingColor,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "Blocked Requests",
|
||||
data: dataValues,
|
||||
},
|
||||
],
|
||||
colors: colorValues,
|
||||
plotOptions: {
|
||||
bar: {
|
||||
distributed: true,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
categories: categories,
|
||||
labels: {
|
||||
rotate: -45,
|
||||
hideOverlappingLabels: true,
|
||||
style: {
|
||||
colors: headingColor,
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: "Time",
|
||||
style: {
|
||||
color: headingColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
text: "Number of Blocked Requests",
|
||||
style: {
|
||||
color: headingColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: "Blocked Requests per Hour",
|
||||
align: "center",
|
||||
style: {
|
||||
color: headingColor,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: "Blocked Requests",
|
||||
data: dataValues,
|
||||
},
|
||||
],
|
||||
colors: colorValues,
|
||||
plotOptions: {
|
||||
bar: {
|
||||
distributed: true,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
categories: categories,
|
||||
labels: {
|
||||
rotate: -45,
|
||||
hideOverlappingLabels: true,
|
||||
style: {
|
||||
colors: headingColor,
|
||||
grid: {
|
||||
padding: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 15,
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: "Time",
|
||||
style: {
|
||||
color: headingColor,
|
||||
states: {
|
||||
hover: {
|
||||
filter: { type: "none" },
|
||||
},
|
||||
active: {
|
||||
filter: { type: "none" },
|
||||
},
|
||||
},
|
||||
},
|
||||
yaxis: {
|
||||
title: {
|
||||
text: "Number of Blocked Requests",
|
||||
style: {
|
||||
color: headingColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
},
|
||||
grid: {
|
||||
padding: {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 15,
|
||||
},
|
||||
},
|
||||
states: {
|
||||
hover: {
|
||||
filter: { type: "none" },
|
||||
},
|
||||
active: {
|
||||
filter: { type: "none" },
|
||||
},
|
||||
},
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 480,
|
||||
options: {
|
||||
chart: {
|
||||
width: "100%",
|
||||
},
|
||||
legend: {
|
||||
position: "bottom",
|
||||
labels: {
|
||||
color: legendColor,
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 480,
|
||||
options: {
|
||||
chart: {
|
||||
width: "100%",
|
||||
},
|
||||
legend: {
|
||||
position: "bottom",
|
||||
labels: {
|
||||
color: legendColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
],
|
||||
};
|
||||
|
||||
const blockingChart = new ApexCharts(
|
||||
document.querySelector("#requests-blocking"),
|
||||
blockingOptions,
|
||||
);
|
||||
blockingChart.render();
|
||||
blockingChart = new ApexCharts(
|
||||
document.querySelector("#requests-blocking"),
|
||||
blockingOptions,
|
||||
);
|
||||
blockingChart.render();
|
||||
}
|
||||
|
||||
renderBlockingStatus();
|
||||
|
||||
$("#dark-mode-toggle").on("change", function () {
|
||||
setTimeout(() => {
|
||||
theme = $("#theme").val();
|
||||
headingColor = config.colors.headingColor;
|
||||
legendColor = config.colors.bodyColor;
|
||||
if (theme === "dark") {
|
||||
headingColor = config.colors.white;
|
||||
legendColor = config.colors.white;
|
||||
}
|
||||
|
||||
requestsChart.destroy();
|
||||
renderStatsChart();
|
||||
if ($ipsData.length) {
|
||||
ipsChart.destroy();
|
||||
renderIpsChart();
|
||||
}
|
||||
blockingChart.destroy();
|
||||
renderBlockingStatus();
|
||||
}, 30);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ $(document).ready(() => {
|
|||
let currentPlugin = "general";
|
||||
let currentStep = 1;
|
||||
const isReadOnly = $("#is-read-only").val().trim() === "True";
|
||||
var isInit = true;
|
||||
|
||||
if (isReadOnly && window.location.pathname.endsWith("/new"))
|
||||
window.location.href = window.location.href.split("/new")[0];
|
||||
|
|
@ -139,7 +140,7 @@ $(document).ready(() => {
|
|||
updateUrlParams(params);
|
||||
}
|
||||
} else if (targetClass.includes("navs-templates-")) {
|
||||
resetTemplateConfig();
|
||||
if (!isInit) resetTemplateConfig();
|
||||
setTimeout(() => {
|
||||
currentTemplate = targetClass
|
||||
.substring(1)
|
||||
|
|
@ -1064,10 +1065,11 @@ $(document).ready(() => {
|
|||
}
|
||||
}
|
||||
|
||||
var editors = [];
|
||||
|
||||
$(".ace-editor").each(function () {
|
||||
const initialContent = $(this).text().trim();
|
||||
const editor = ace.edit(this);
|
||||
editor.setTheme("ace/theme/cloud9_day"); // cloud9_night when dark mode is supported
|
||||
|
||||
editor.session.setMode("ace/mode/nginx");
|
||||
// const language = $(this).data("language"); // TODO: Support ModSecurity
|
||||
|
|
@ -1094,6 +1096,28 @@ $(document).ready(() => {
|
|||
});
|
||||
|
||||
editor.renderer.setScrollMargin(10, 10);
|
||||
editors.push(editor);
|
||||
});
|
||||
|
||||
var theme = $("#theme").val();
|
||||
|
||||
function setEditorTheme() {
|
||||
editors.forEach((editor) => {
|
||||
if (theme === "dark") {
|
||||
editor.setTheme("ace/theme/cloud9_night");
|
||||
} else {
|
||||
editor.setTheme("ace/theme/cloud9_day");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setEditorTheme();
|
||||
|
||||
$("#dark-mode-toggle").on("change", function () {
|
||||
setTimeout(() => {
|
||||
theme = $("#theme").val();
|
||||
setEditorTheme();
|
||||
}, 30);
|
||||
});
|
||||
|
||||
$(window).on("beforeunload", function (e) {
|
||||
|
|
@ -1119,4 +1143,6 @@ $(document).ready(() => {
|
|||
e.returnValue = message; // Standard for most browsers
|
||||
return message; // Required for some browsers
|
||||
});
|
||||
|
||||
isInit = false;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -422,4 +422,55 @@ $(document).ready(() => {
|
|||
sessionStorage.setItem("notificationsRead", notificationsRead);
|
||||
updateNotificationsBadge();
|
||||
});
|
||||
|
||||
$("#dark-mode-toggle").on("change", function () {
|
||||
const darkMode = $(this).prop("checked");
|
||||
if (darkMode) {
|
||||
$("html").removeClass("light-style").addClass("dark-style");
|
||||
$(".btn-outline-dark")
|
||||
.addClass("btn-outline-light")
|
||||
.removeClass("btn-outline-dark");
|
||||
$(".btn-dark").addClass("btn-light").removeClass("btn-dark");
|
||||
$(".bg-white").addClass("bg-dark").removeClass("bg-white");
|
||||
$(".bg-light-subtle")
|
||||
.addClass("bg-dark-subtle")
|
||||
.removeClass("bg-light-subtle");
|
||||
} else {
|
||||
$("html").removeClass("dark-style").addClass("light-style");
|
||||
$(".btn-outline-light")
|
||||
.addClass("btn-outline-dark")
|
||||
.removeClass("btn-outline-light");
|
||||
$(".btn-light").addClass("btn-dark").removeClass("btn-light");
|
||||
$(".bg-dark").addClass("bg-white").removeClass("bg-dark");
|
||||
$(".bg-dark-subtle")
|
||||
.addClass("bg-light-subtle")
|
||||
.removeClass("bg-dark-subtle");
|
||||
}
|
||||
|
||||
$("#theme").val(darkMode ? "dark" : "light");
|
||||
|
||||
const rootUrl = $(this)
|
||||
.data("root-url")
|
||||
.replace(/\/profile$/, "/set_theme");
|
||||
const csrfToken = $("#csrf_token").val();
|
||||
const theme = darkMode ? "dark" : "light";
|
||||
|
||||
const data = new FormData();
|
||||
data.append("theme", theme);
|
||||
data.append("csrf_token", csrfToken);
|
||||
|
||||
fetch(rootUrl, {
|
||||
method: "POST",
|
||||
body: data,
|
||||
})
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
// Handle success, redirect, etc.
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("There was a problem with the fetch operation:", error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
} %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
class="{{ theme }}-style layout-navbar-fixed layout-menu-fixed"
|
||||
class="{{ current_user.theme }}-style layout-navbar-fixed layout-menu-fixed"
|
||||
data-theme="theme-default">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
|
@ -65,7 +65,7 @@
|
|||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='libs/flatpickr/themes/airbnb.css') }}"
|
||||
nonce="{{ style_nonce }}" />
|
||||
{% if theme == 'dark' %}
|
||||
{% if current_user.theme == 'dark' %}
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='libs/flatpickr/themes/airbnb.dark.css') }}"
|
||||
nonce="{{ style_nonce }}" />
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<input type="hidden" id="is-read-only" value="{{ is_readonly }}" />
|
||||
<input type="hidden" id="theme" value="{{ theme }}" />
|
||||
<input type="hidden" id="theme" value="{{ current_user.theme }}" />
|
||||
<!-- prettier-ignore -->
|
||||
{% if current_endpoint != "loading" %}
|
||||
{% include "flash.html" %}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
<!-- Collapsible floating menu -->
|
||||
<div class="collapse mt-2" id="service-modes-floating">
|
||||
<ul id="service-modes-menu"
|
||||
class="nav nav-pills flex-column bg-{% if theme == 'light' %}white{% else %}dark{% endif %} p-2 rounded shadow-sm"
|
||||
class="nav nav-pills flex-column bg-{% if current_user.theme == 'light' %}white{% else %}dark{% endif %} p-2 rounded shadow-sm"
|
||||
role="tablist">
|
||||
<li class="nav-item mb-1" role="presentation">
|
||||
<button type="button"
|
||||
|
|
@ -120,7 +120,7 @@
|
|||
<ul class="d-flex mb-0 list-unstyled">
|
||||
<li class="me-3">
|
||||
<a role="button"
|
||||
class="btn btn-sm btn-outline-{% if theme == 'light' %}dark{% else %}light{% endif %} d-flex align-items-center h-100"
|
||||
class="btn btn-sm btn-outline-{% if current_user.theme == 'light' %}dark{% else %}light{% endif %} 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"
|
||||
|
|
@ -151,7 +151,7 @@
|
|||
<li class="position-relative">
|
||||
<button id="news-button"
|
||||
type="button"
|
||||
class="btn btn-sm btn-{% if theme == 'light' %}dark{% else %}light{% endif %} text-uppercase d-flex align-items-center"
|
||||
class="btn btn-sm btn-{% if current_user.theme == 'light' %}dark{% else %}light{% endif %} text-uppercase d-flex align-items-center"
|
||||
aria-pressed="true"
|
||||
data-bs-toggle="offcanvas"
|
||||
data-bs-target="#side-offcanvas-news"
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
{% endif %}
|
||||
<!-- flash message-->
|
||||
{% for category, message in messages %}
|
||||
<div class="bs-toast toast fade show bg-{% if theme == 'light' %}white{% else %}dark{% endif %} border{% if category == 'error' %} border-danger{% elif category == 'warning' %} border-warning{% else %} border-primary{% endif %}"
|
||||
<div class="bs-toast toast fade show bg-{% if current_user.theme == 'light' %}white{% else %}dark{% endif %} border{% if category == 'error' %} border-danger{% elif category == 'warning' %} border-warning{% else %} border-primary{% endif %}"
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-atomic="true">
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@
|
|||
</table>
|
||||
</div>
|
||||
<div id="feedback-toast"
|
||||
class="bs-toast toast fade bg-{% if theme == 'light' %}white{% else %}dark{% endif %} border"
|
||||
class="bs-toast toast fade bg-{% if current_user.theme == 'light' %}white{% else %}dark{% endif %} border"
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-atomic="true"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{% block page %}
|
||||
<div class="bg-light-subtle">
|
||||
<div class="bg-{% if current_user.theme == 'light' %}light{% else %}dark{% endif %}-subtle">
|
||||
<div class="login-background">
|
||||
<div class="container-xxl">
|
||||
<div class="authentication-wrapper authentication-basic container-p-y">
|
||||
|
|
|
|||
|
|
@ -65,6 +65,30 @@
|
|||
</div>
|
||||
<!-- /Buttons -->
|
||||
<ul class="navbar-nav flex-row align-items-center ms-auto">
|
||||
<!-- Dark mode toggle -->
|
||||
<li class="nav-item lh-1 me-4">
|
||||
<div class="d-flex mt-1">
|
||||
<input type="hidden"
|
||||
id="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token() }}" />
|
||||
<label class="setting-checkbox-label d-flex align-items-center ps-0"
|
||||
for="dark-mode-toggle">Light</label>
|
||||
<div class="form-switch ms-2 mb-1" {% if is_readonly %}data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="The database is in readonly mode, therefore the theme cannot be changed"{% endif %}>
|
||||
<input id="dark-mode-toggle"
|
||||
name="dark-mode-toggle"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
data-root-url="{{ url_for('profile') }}"
|
||||
{% if current_user.theme == "dark" %}checked{% endif %}
|
||||
{% if is_readonly %}disabled{% endif %} />
|
||||
</div>
|
||||
<label class="setting-checkbox-label d-flex align-items-center ps-0"
|
||||
for="dark-mode-toggle">Dark</label>
|
||||
</div>
|
||||
</li>
|
||||
<!--/ Dark mode toggle -->
|
||||
<!-- Stars -->
|
||||
<li class="d-none d-md-inline nav-item lh-1 me-4">
|
||||
<a class="github-button"
|
||||
|
|
@ -78,7 +102,7 @@
|
|||
<!-- Version -->
|
||||
<li class="nav-item lh-1 me-2 me-md-4">
|
||||
<a role="button"
|
||||
class="btn btn-sm btn-outline-{% if theme == 'light' %}dark{% else %}light{% endif %} px-2 px-md-3 position-relative"
|
||||
class="btn btn-sm btn-outline-{% if current_user.theme == 'light' %}dark{% else %}light{% endif %} px-2 px-md-3 position-relative"
|
||||
aria-pressed="true"
|
||||
href="https://github.com/bunkerity/bunkerweb/releases/latest"
|
||||
target="_blank"
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@
|
|||
<div class="col-md-6">
|
||||
<!-- Theme -->
|
||||
<div class="card position-relative">
|
||||
<i class='bx bx-{% if theme == 'light' %}sun{% else %}moon{% endif %} bx-sm position-absolute top-0 end-0 m-3'></i>
|
||||
<i class='bx bx-{% if current_user.theme == 'light' %}sun{% else %}moon{% endif %} bx-sm position-absolute top-0 end-0 m-3'></i>
|
||||
<h5 class="card-header">Change Theme</h5>
|
||||
<div class="card-body pb-4">
|
||||
<form method="POST" action="{{ profile_url }}/edit">
|
||||
|
|
@ -182,10 +182,10 @@
|
|||
<div class="col-md-12 form-floating">
|
||||
<select class="form-select" id="theme" name="theme">
|
||||
<option value="light"
|
||||
{% if theme == "light" %}selected{% endif %}>
|
||||
{% if current_user.theme == "light" %}selected{% endif %}>
|
||||
Light
|
||||
</option>
|
||||
<option value="dark" {% if theme == "dark" %}selected{% endif %}>Dark</option>
|
||||
<option value="dark" {% if current_user.theme == "dark" %}selected{% endif %}>Dark</option>
|
||||
</select>
|
||||
<label for="theme">Theme</label>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- Newsletter Signup Section -->
|
||||
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-{% if theme == 'light' %}light{% else %}dark{% endif %}-subtle border-top">
|
||||
<h5 class="mb-3 text-{% if theme == 'light' %}dark{% else %}primary{% endif %}">Join the Newsletter</h5>
|
||||
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-{% if current_user.theme == 'light' %}light{% else %}dark{% endif %}-subtle border-top">
|
||||
<h5 class="mb-3 text-primary">Join the Newsletter</h5>
|
||||
<form action="https://bunkerity.us1.list-manage.com/subscribe/post?u=ec5b1577cf427972b9bd491a6&id=37076d9d67"
|
||||
method="POST">
|
||||
<div class="mb-3">
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
{% set content = message[0] %}
|
||||
{% set category = message[1] %}
|
||||
{% set datetime = message[2] %}
|
||||
<div class="col-12 bs-toast toast show bg-{% if theme == 'light' %}white{% else %}dark{% endif %} border{% if category == 'error' %} border-danger{% elif category == 'warning' %} border-warning{% else %} border-primary{% endif %}">
|
||||
<div class="col-12 bs-toast toast show bg-{% if current_user.theme == 'light' %}white{% else %}dark{% endif %} border{% if category == 'error' %} border-danger{% elif category == 'warning' %} border-warning{% else %} border-primary{% endif %}">
|
||||
<div class="toast-header d-flex align-items-center{% if category == 'error' %} text-danger{% elif category == 'warning' %} text-warning{% else %} text-primary{% endif %}">
|
||||
<i class="d-block h-auto rounded tf-icons bx bx-xs bx-bell me-2"></i>
|
||||
<span class="fw-medium me-auto">
|
||||
|
|
@ -78,8 +78,8 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
<!-- Newsletter Signup Section -->
|
||||
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-{% if theme == 'light' %}light{% else %}dark{% endif %}-subtle border-top">
|
||||
<h5 class="mb-3 text-{% if theme == 'light' %}dark{% else %}primary{% endif %}">Join the Newsletter</h5>
|
||||
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-{% if current_user.theme == 'light' %}light{% else %}dark{% endif %}-subtle border-top">
|
||||
<h5 class="mb-3 text-primary">Join the Newsletter</h5>
|
||||
<form action="https://bunkerity.us1.list-manage.com/subscribe/post?u=ec5b1577cf427972b9bd491a6&id=37076d9d67"
|
||||
method="POST">
|
||||
<div class="mb-3">
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from signal import SIGINT, signal, SIGTERM
|
|||
from sys import path as sys_path
|
||||
from time import time
|
||||
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
|
@ -175,7 +176,6 @@ def inject_variables():
|
|||
plugins=BW_CONFIG.get_plugins(),
|
||||
flash_messages=session.get("flash_messages", []),
|
||||
is_readonly=DATA.get("READONLY_MODE", False),
|
||||
theme=current_user.theme if current_user.is_authenticated else "light",
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -442,6 +442,28 @@ def check_reloading():
|
|||
return jsonify({"reloading": DATA.get("RELOADING", False)})
|
||||
|
||||
|
||||
@app.route("/set_theme", methods=["POST"])
|
||||
@login_required
|
||||
def set_theme():
|
||||
if DB.readonly or request.form["theme"] not in ("dark", "light"):
|
||||
return
|
||||
|
||||
user_data = {
|
||||
"username": current_user.get_id(),
|
||||
"password": current_user.password.encode("utf-8"),
|
||||
"email": current_user.email,
|
||||
"totp_secret": current_user.totp_secret,
|
||||
"method": current_user.method,
|
||||
"theme": request.form["theme"],
|
||||
}
|
||||
|
||||
ret = DB.update_ui_user(**user_data, old_username=current_user.get_id())
|
||||
if ret:
|
||||
LOGGER.error(f"Couldn't update the user {current_user.get_id()}: {ret}")
|
||||
|
||||
return Response(status=200, response=dumps({"message": "ok"}), content_type="application/json")
|
||||
|
||||
|
||||
BLUEPRINTS = (about, services, profile, jobs, reports, totp, home, logout, instances, plugins, global_config, pro, cache, logs, login, configs, bans, setup)
|
||||
for blueprint in BLUEPRINTS:
|
||||
app.register_blueprint(blueprint)
|
||||
|
|
|
|||
Loading…
Reference in a new issue