mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Start adding dark mode in best effort for web UI
This commit is contained in:
parent
3ead15cdd1
commit
d20f926078
16 changed files with 538 additions and 55 deletions
|
|
@ -9,10 +9,12 @@ for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in ((
|
|||
from bcrypt import checkpw
|
||||
from flask_login import AnonymousUserMixin, UserMixin
|
||||
from sqlalchemy.orm import declarative_base, relationship
|
||||
from sqlalchemy import TEXT, Boolean, DateTime, Column, Identity, Integer, String, ForeignKey, UnicodeText
|
||||
from sqlalchemy import TEXT, Boolean, DateTime, Column, Enum, Identity, Integer, String, ForeignKey, UnicodeText
|
||||
|
||||
from model import METHODS_ENUM # type: ignore
|
||||
|
||||
THEMES_ENUM = Enum("light", "dark", name="themes_enum")
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
|
|
@ -44,6 +46,7 @@ class Users(Base, UserMixin):
|
|||
password = Column(String(60), nullable=False)
|
||||
method = Column(METHODS_ENUM, nullable=False, default="manual")
|
||||
admin = Column(Boolean, nullable=False, default=False)
|
||||
theme = Column(THEMES_ENUM, nullable=False, default="light")
|
||||
|
||||
# 2FA
|
||||
totp_secret = Column(String(256), nullable=True)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from os import sep
|
|||
from os.path import join
|
||||
from sys import path as sys_path
|
||||
from time import sleep
|
||||
from typing import List, Optional, Tuple, Union
|
||||
from typing import List, Literal, Optional, Tuple, Union
|
||||
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
|
||||
|
|
@ -163,6 +163,7 @@ class UIDatabase(Database):
|
|||
"email": ui_user.email,
|
||||
"password": ui_user.password.encode("utf-8"),
|
||||
"method": ui_user.method,
|
||||
"theme": ui_user.theme,
|
||||
"totp_secret": ui_user.totp_secret,
|
||||
"creation_date": ui_user.creation_date,
|
||||
"update_date": ui_user.update_date,
|
||||
|
|
@ -179,6 +180,7 @@ class UIDatabase(Database):
|
|||
roles: List[str],
|
||||
email: Optional[str] = None,
|
||||
*,
|
||||
theme: Union[Literal["light"], Literal["dark"]] = "light",
|
||||
totp_secret: Optional[str] = None,
|
||||
totp_recovery_codes: Optional[List[str]] = None,
|
||||
method: str = "manual",
|
||||
|
|
@ -209,6 +211,7 @@ class UIDatabase(Database):
|
|||
password=password.decode("utf-8"),
|
||||
method=method,
|
||||
admin=admin,
|
||||
theme=theme,
|
||||
totp_secret=totp_secret,
|
||||
creation_date=current_time,
|
||||
update_date=current_time,
|
||||
|
|
@ -231,6 +234,7 @@ class UIDatabase(Database):
|
|||
password: bytes,
|
||||
totp_secret: Optional[str],
|
||||
*,
|
||||
theme: Union[Literal["light"], Literal["dark"]] = "light",
|
||||
old_username: Optional[str] = None,
|
||||
email: Optional[str] = None,
|
||||
totp_recovery_codes: Optional[List[str]] = None,
|
||||
|
|
@ -263,6 +267,7 @@ class UIDatabase(Database):
|
|||
user.password = password.decode("utf-8")
|
||||
user.totp_secret = totp_secret
|
||||
user.method = method
|
||||
user.theme = theme
|
||||
user.update_date = datetime.now().astimezone()
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -202,20 +202,21 @@ def edit_profile():
|
|||
if DB.readonly:
|
||||
return handle_error("Database is in read-only mode", "profile")
|
||||
|
||||
verify_data_in_form(data={"password": None}, err_message="Missing current password parameter on /profile/edit.", redirect_url="profile")
|
||||
|
||||
if not current_user.check_password(request.form["password"]):
|
||||
return handle_error("The current password is incorrect.", "profile")
|
||||
|
||||
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": current_user.theme,
|
||||
}
|
||||
|
||||
if "username" in request.form:
|
||||
verify_data_in_form(data={"password": None}, err_message="Missing current password parameter on /profile/edit.", redirect_url="profile")
|
||||
|
||||
if not current_user.check_password(request.form["password"]):
|
||||
return handle_error("The current password is incorrect.", "profile")
|
||||
|
||||
verify_data_in_form(data={"email": None}, err_message="Missing email parameter on /profile/edit.", redirect_url="profile")
|
||||
|
||||
if request.form["email"] and request.form["email"] != current_user.email:
|
||||
|
|
@ -231,6 +232,11 @@ def edit_profile():
|
|||
if request.form["email"] == (current_user.email or "") and request.form["username"] == current_user.get_id():
|
||||
return handle_error("The username and email are the same as the current ones.", "profile")
|
||||
elif "new_password" in request.form:
|
||||
verify_data_in_form(data={"password": None}, err_message="Missing current password parameter on /profile/edit.", redirect_url="profile")
|
||||
|
||||
if not current_user.check_password(request.form["password"]):
|
||||
return handle_error("The current password is incorrect.", "profile")
|
||||
|
||||
verify_data_in_form(
|
||||
data={"new_password_confirm": None},
|
||||
err_message="Missing new password confirm parameter on /profile/edit.",
|
||||
|
|
@ -248,6 +254,11 @@ def edit_profile():
|
|||
return handle_error("The new password is the same as the current one.", "profile")
|
||||
|
||||
user_data["password"] = gen_password_hash(request.form["new_password"])
|
||||
elif "theme" in request.form:
|
||||
if request.form["theme"] not in ("dark", "light"):
|
||||
return handle_error("The theme is invalid.", "profile")
|
||||
|
||||
user_data["theme"] = request.form["theme"]
|
||||
else:
|
||||
return handle_error("No fields were updated.", "profile")
|
||||
|
||||
|
|
|
|||
|
|
@ -892,3 +892,292 @@ a.courier-prime:hover {
|
|||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.layout-page::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dark mode overrides
|
||||
******************************************************************************/
|
||||
|
||||
.dark-style body {
|
||||
color: #fff;
|
||||
background-color: #0d2f45;
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
.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;
|
||||
--bs-body-color-rgb: 255, 255, 255;
|
||||
--dt-row-selected: 31, 31, 31;
|
||||
--bs-breadcrumb-item-active-color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .list-group {
|
||||
--bs-list-group-color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .btn {
|
||||
--bs-btn-color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .btn:hover {
|
||||
--bs-btn-hover-color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .card {
|
||||
--bs-card-title-color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .dropdown-menu {
|
||||
--bs-dropdown-bg: #0c283a;
|
||||
}
|
||||
|
||||
.dark-style .offcanvas,
|
||||
.dark-style .offcanvas-xxl,
|
||||
.dark-style .offcanvas-xl,
|
||||
.dark-style .offcanvas-lg,
|
||||
.dark-style .offcanvas-md,
|
||||
.dark-style .offcanvas-sm {
|
||||
--bs-offcanvas-bg: #0d2f45;
|
||||
}
|
||||
|
||||
.dark-style .nav-link,
|
||||
.dark-style .breadcrumb-item.active,
|
||||
.dark-style .card .card-header,
|
||||
.dark-style .card .card-body,
|
||||
.dark-style .form-label,
|
||||
.dark-style .form-control,
|
||||
.dark-style .form-select,
|
||||
.dark-style .input-group-text {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.dark-style .breadcrumb-item,
|
||||
.dark-style .breadcrumb-item a {
|
||||
color: #9caeb7;
|
||||
}
|
||||
|
||||
.dark-style .breadcrumb-item:hover,
|
||||
.dark-style .breadcrumb-item:focus,
|
||||
.dark-style .breadcrumb-item a:hover,
|
||||
.dark-style .breadcrumb-item a:focus {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .nav-pills .nav-link.active,
|
||||
.nav-pills .nav-link.active:hover,
|
||||
.nav-pills .nav-link.active:focus {
|
||||
background-color: #0a4b69;
|
||||
}
|
||||
|
||||
.dark-style .btn-primary {
|
||||
background-color: var(--bs-primary);
|
||||
border-color: var(--bs-primary);
|
||||
color: #07354a;
|
||||
}
|
||||
|
||||
.dark-style .breadcrumb-item + .breadcrumb-item::before {
|
||||
color: #9caeb7;
|
||||
}
|
||||
|
||||
.dark-style .btn-primary:hover {
|
||||
color: #07354a !important;
|
||||
}
|
||||
|
||||
.dark-style .btn-light {
|
||||
background-color: #0a4b69;
|
||||
border-color: #0a4b69;
|
||||
box-shadow: none;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .card {
|
||||
background-color: #0c283a;
|
||||
border-color: #0c283a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .modal {
|
||||
--bs-modal-color: #fff;
|
||||
--bs-modal-bg: #0c283a;
|
||||
}
|
||||
|
||||
.dark-style .input-group:focus-within .form-control,
|
||||
.dark-style .input-group:focus-within .input-group-text {
|
||||
border-color: #fff !important;
|
||||
}
|
||||
|
||||
.dark-style .text-body {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.dark-style .bg-footer-theme .footer-link {
|
||||
color: #cdd7db;
|
||||
}
|
||||
|
||||
.dark-style h6,
|
||||
.h6,
|
||||
h5,
|
||||
.h5,
|
||||
h4,
|
||||
.h4,
|
||||
h3,
|
||||
.h3,
|
||||
h2,
|
||||
.h2,
|
||||
h1,
|
||||
.h1 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .img-fluid.qr-code,
|
||||
.dark-style .x-logo {
|
||||
filter: brightness(0) invert(1);
|
||||
}
|
||||
|
||||
.dark-style .text-primary {
|
||||
color: var(--bs-primary) !important;
|
||||
}
|
||||
|
||||
.dark-style .text-primary.shine {
|
||||
color: rgba(var(--bs-primary-rgb), 0.1) !important;
|
||||
background-color: var(--bs-primary) !important;
|
||||
}
|
||||
|
||||
.dark-style .text-muted {
|
||||
color: #9caeb7 !important;
|
||||
}
|
||||
.dark-style .sticky-card {
|
||||
background-color: rgba(12, 40, 58, 0.95) !important;
|
||||
}
|
||||
|
||||
.dark-style .layout-navbar,
|
||||
.dark-style .layout-menu {
|
||||
background-color: #0c283a !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.dark-style .bg-menu-theme .menu-link,
|
||||
.dark-style .bg-menu-theme .menu-horizontal-prev,
|
||||
.dark-style .bg-menu-theme .menu-horizontal-next {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .bg-menu-theme .menu-inner > .menu-item.active > .menu-link {
|
||||
color: #07354a !important;
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.dark-style .bg-menu-theme .menu-inner > .menu-item.active:before {
|
||||
background-color: #fff !important;
|
||||
}
|
||||
|
||||
.dark-style .btn-outline-primary {
|
||||
color: var(--bs-primary);
|
||||
border-color: var(--bs-primary);
|
||||
}
|
||||
|
||||
html:not([dir="rtl"]).dark-style .border-primary,
|
||||
html[dir="rtl"].dark-style .border-primary {
|
||||
border-color: var(--bs-primary) !important;
|
||||
}
|
||||
|
||||
.dark-style .form-floating > .form-control:focus ~ label,
|
||||
.dark-style
|
||||
.form-floating
|
||||
> .form-control:focus:not(:placeholder-shown)
|
||||
~ label,
|
||||
.dark-style .form-floating > .form-select:focus ~ label,
|
||||
.dark-style
|
||||
.form-floating
|
||||
> .form-select:focus:not(:placeholder-shown)
|
||||
~ label {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .form-select {
|
||||
--bs-form-select-bg-img: url('data:image/svg+xml,%3csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 22" fill="none"%3e%3cpath d="M10.9999 12.0743L15.5374 7.53676L16.8336 8.83292L10.9999 14.6666L5.16626 8.83292L6.46243 7.53676L10.9999 12.0743Z" fill="%23ffffff" fill-opacity="0.9"/%3e%3c/svg%3e');
|
||||
background-color: #0c283a;
|
||||
}
|
||||
|
||||
.dark-style .dropdown-item {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .dropdown-item:not(.disabled).active,
|
||||
.dark-style .dropdown-item:not(.disabled):active {
|
||||
color: #fff !important;
|
||||
background-color: #0a4b69;
|
||||
}
|
||||
|
||||
.dark-style .bg-secondary {
|
||||
color: #07354a;
|
||||
}
|
||||
|
||||
.dark-style .table th {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .table > :not(caption) > * > * {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .form-control:focus,
|
||||
.dark-style .form-select:focus {
|
||||
border-color: #fff !important;
|
||||
}
|
||||
|
||||
.dark-style .btn-outline-primary.disabled,
|
||||
.dark-style .btn-outline-primary:disabled {
|
||||
color: var(--bs-primary) !important;
|
||||
border-color: var(--bs-primary) !important;
|
||||
}
|
||||
|
||||
.dark-style td.highlight {
|
||||
background-color: rgba(255, 255, 255, 0.1) !important;
|
||||
}
|
||||
|
||||
.dark-style .form-check-label {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style a {
|
||||
color: #cdd7db;
|
||||
}
|
||||
|
||||
.dark-style a:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .bg-label-secondary {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.dark-style .bg-bw-green {
|
||||
background-color: var(--bs-bw-green);
|
||||
}
|
||||
|
||||
.dark-style .apexcharts-text {
|
||||
fill: #fff;
|
||||
}
|
||||
.dark-style .apexcharts-tooltip,
|
||||
.dark-style .apexcharts-tooltip-title {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
.dark-style .apexcharts-legend-text {
|
||||
color: #fff;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
$(function () {
|
||||
const headingColor = config.colors.headingColor;
|
||||
const legendColor = config.colors.bodyColor;
|
||||
var headingColor = config.colors.headingColor;
|
||||
var legendColor = config.colors.bodyColor;
|
||||
|
||||
const theme = $("#theme").val();
|
||||
|
||||
if (theme === "dark") {
|
||||
headingColor = config.colors.white;
|
||||
legendColor = config.colors.white;
|
||||
}
|
||||
|
||||
// Requests countries map
|
||||
|
||||
|
|
@ -432,10 +439,16 @@ $(function () {
|
|||
type: "bar",
|
||||
width: "100%",
|
||||
height: 400,
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: "Blocked Requests per Hour",
|
||||
align: "center",
|
||||
style: {
|
||||
color: headingColor,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
|
|
@ -446,7 +459,7 @@ $(function () {
|
|||
colors: colorValues,
|
||||
plotOptions: {
|
||||
bar: {
|
||||
distributed: true, // This line enables individual bar colors
|
||||
distributed: true,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
|
|
@ -454,14 +467,23 @@ $(function () {
|
|||
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: {
|
||||
|
|
@ -491,6 +513,9 @@ $(function () {
|
|||
},
|
||||
legend: {
|
||||
position: "bottom",
|
||||
labels: {
|
||||
color: legendColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
137
src/ui/app/static/libs/flatpickr/themes/airbnb.dark.css
Normal file
137
src/ui/app/static/libs/flatpickr/themes/airbnb.dark.css
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
.dark-style .flatpickr-calendar {
|
||||
background: #222;
|
||||
color: #e0e0e0;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-calendar .hasTime {
|
||||
border-top: 1px solid #333;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-calendar .hasWeeks .dayContainer {
|
||||
border-left: 1px solid #333;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-months .flatpickr-month {
|
||||
background: #333;
|
||||
color: #ddd;
|
||||
fill: #ddd;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-months .flatpickr-prev-month,
|
||||
.dark-style .flatpickr-months .flatpickr-next-month {
|
||||
color: #aaa;
|
||||
fill: #aaa;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-months .flatpickr-prev-month:hover,
|
||||
.dark-style .flatpickr-months .flatpickr-next-month:hover {
|
||||
color: #ff6666;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-current-month {
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-weekdays .flatpickr-weekday {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-day {
|
||||
color: #ccc;
|
||||
background: none;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-day:hover,
|
||||
.dark-style .flatpickr-day:focus {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-day.today {
|
||||
border-color: #ff6666;
|
||||
background: #333;
|
||||
color: #ff6666;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-day.today:hover,
|
||||
.dark-style .flatpickr-day.today:focus {
|
||||
background: #ff6666;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-day.selected,
|
||||
.dark-style .flatpickr-day.startRange,
|
||||
.dark-style .flatpickr-day.endRange {
|
||||
background: #4f9aff;
|
||||
border-color: #4f9aff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-day.inRange {
|
||||
background: #333;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-time input,
|
||||
.dark-style .flatpickr-time .flatpickr-am-pm {
|
||||
color: #ccc;
|
||||
background: #333;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-time input:hover,
|
||||
.dark-style .flatpickr-time input:focus,
|
||||
.dark-style .flatpickr-time .flatpickr-am-pm:hover,
|
||||
.dark-style .flatpickr-time .flatpickr-am-pm:focus {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.dark-style .numInputWrapper span {
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.dark-style .numInputWrapper span:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
.dark-style .numInputWrapper span.arrowUp:after {
|
||||
border-bottom-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.dark-style .numInputWrapper span.arrowDown:after {
|
||||
border-top-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-monthDropdown-months {
|
||||
background: #333;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-weekwrapper span.flatpickr-day {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-day.prevMonthDay,
|
||||
.dark-style .flatpickr-day.nextMonthDay {
|
||||
color: #666;
|
||||
background: #222;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-day.prevMonthDay:hover,
|
||||
.dark-style .flatpickr-day.nextMonthDay:hover {
|
||||
background: #333;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.dark-style span.flatpickr-day.today:not(.selected), .dark-style span.flatpickr-day.prevMonthDay.today:not(.selected), .dark-style span.flatpickr-day.nextMonthDay.today:not(.selected) {
|
||||
border-left-color: transparent;
|
||||
}
|
||||
|
||||
.dark-style span.flatpickr-day:nth-child(n+8), .dark-style span.flatpickr-day.prevMonthDay, .dark-style span.flatpickr-day.nextMonthDay {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.dark-style .flatpickr-current-month .flatpickr-monthDropdown-months:hover {
|
||||
background: #444;
|
||||
}
|
||||
|
|
@ -200,6 +200,7 @@
|
|||
target="_blank"
|
||||
rel="noreferrer">
|
||||
<img src="{{ url_for('static', filename='img/brands/Twitter-X-Logo.svg') }}"
|
||||
class="x-logo"
|
||||
alt="X logo"
|
||||
width="20px"
|
||||
height="20px">
|
||||
|
|
@ -247,6 +248,7 @@
|
|||
target="_blank"
|
||||
rel="noreferrer">
|
||||
<img src="{{ url_for('static', filename='img/brands/Twitter-X-Logo.svg') }}"
|
||||
class="x-logo"
|
||||
alt="X logo"
|
||||
width="20px"
|
||||
height="20px">
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
} %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"
|
||||
class="light-style layout-navbar-fixed layout-menu-fixed"
|
||||
class="{{ theme }}-style layout-navbar-fixed layout-menu-fixed"
|
||||
data-theme="theme-default">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
|
@ -65,6 +65,11 @@
|
|||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='libs/flatpickr/themes/airbnb.css') }}"
|
||||
nonce="{{ style_nonce }}" />
|
||||
{% if theme == 'dark' %}
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='libs/flatpickr/themes/airbnb.dark.css') }}"
|
||||
nonce="{{ style_nonce }}" />
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<!-- Core CSS -->
|
||||
<link rel="stylesheet"
|
||||
|
|
@ -119,6 +124,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<input type="hidden" id="is-read-only" value="{{ is_readonly }}" />
|
||||
<input type="hidden" id="theme" value="{{ 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-white p-2 rounded shadow-sm"
|
||||
class="nav nav-pills flex-column bg-{% if 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-dark d-flex align-items-center h-100"
|
||||
class="btn btn-sm btn-outline-{% if 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-dark text-uppercase d-flex align-items-center"
|
||||
class="btn btn-sm btn-{% if 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-white border{% if category == 'error' %} border-danger{% elif category == 'warning' %} border-warning{% else %} border-primary{% endif %}"
|
||||
<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 %}"
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-atomic="true">
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@
|
|||
</table>
|
||||
</div>
|
||||
<div id="feedback-toast"
|
||||
class="bs-toast toast fade bg-white border"
|
||||
class="bs-toast toast fade bg-{% if theme == 'light' %}white{% else %}dark{% endif %} border"
|
||||
role="alert"
|
||||
aria-live="assertive"
|
||||
aria-atomic="true"
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@
|
|||
<!-- Version -->
|
||||
<li class="nav-item lh-1 me-2 me-md-4">
|
||||
<a role="button"
|
||||
class="btn btn-sm btn-outline-dark px-2 px-md-3 position-relative"
|
||||
class="btn btn-sm btn-outline-{% if 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"
|
||||
|
|
|
|||
|
|
@ -79,10 +79,11 @@
|
|||
<div class="tab-pane fade show active"
|
||||
id="navs-pills-profile"
|
||||
role="tabpanel">
|
||||
<div class="d-flex row">
|
||||
<div class="d-flex row g-4">
|
||||
<div class="col-md-6">
|
||||
<!-- Profile -->
|
||||
<div class="card">
|
||||
<div class="card position-relative">
|
||||
<i class='bx bx-user bx-sm position-absolute top-0 end-0 m-3'></i>
|
||||
<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">
|
||||
|
|
@ -171,32 +172,29 @@
|
|||
</div>
|
||||
<div class="col-md-6">
|
||||
<!-- Theme -->
|
||||
<div class="card">
|
||||
<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>
|
||||
<h5 class="card-header">Change Theme</h5>
|
||||
<div class="card-body pb-4"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-html="true"
|
||||
data-bs-original-title="<i class='bx bx-rocket bx-xs'></i><span>Coming soon</span>">
|
||||
<!-- <form method="POST" action="{{ profile_url }}/edit"> -->
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<div class="row g-3">
|
||||
<div class="col-md-12 form-floating">
|
||||
<select class="form-select" id="theme" name="theme" disabled>
|
||||
<option value="light"
|
||||
{% if current_user.theme == "light" %}selected{% endif %}>
|
||||
Light
|
||||
</option>
|
||||
<option value="dark" {% if current_user.theme == "dark" %}selected{% endif %}>Dark</option>
|
||||
</select>
|
||||
<label for="theme">Theme</label>
|
||||
<div class="card-body pb-4">
|
||||
<form method="POST" action="{{ profile_url }}/edit">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<div class="row g-3">
|
||||
<div class="col-md-12 form-floating">
|
||||
<select class="form-select" id="theme" name="theme">
|
||||
<option value="light"
|
||||
{% if theme == "light" %}selected{% endif %}>
|
||||
Light
|
||||
</option>
|
||||
<option value="dark" {% if theme == "dark" %}selected{% endif %}>Dark</option>
|
||||
</select>
|
||||
<label for="theme">Theme</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 justify-content-center d-flex">
|
||||
<button type="submit" class="btn btn-primary me-2 disabled">Save Changes</button>
|
||||
<button type="reset" class="btn btn-outline-secondary">Cancel</button>
|
||||
</div>
|
||||
<!-- </form> -->
|
||||
<div class="mt-4 justify-content-center d-flex">
|
||||
<button type="submit" class="btn btn-primary me-2">Save Changes</button>
|
||||
<button type="reset" class="btn btn-outline-secondary">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /Theme -->
|
||||
|
|
@ -204,10 +202,11 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="navs-pills-security" role="tabpanel">
|
||||
<div class="d-flex row">
|
||||
<div class="d-flex row g-4">
|
||||
<div class="col-md-6">
|
||||
<!-- Password -->
|
||||
<div class="card mb-4">
|
||||
<div class="card mb-4 position-relative">
|
||||
<i class='bx bx-check-shield bx-sm position-absolute top-0 end-0 m-3'></i>
|
||||
<h5 class="card-header">Change Password</h5>
|
||||
<div class="card-body pb-4">
|
||||
<form method="POST" action="{{ profile_url }}/edit" autocomplete="off">
|
||||
|
|
@ -291,7 +290,8 @@
|
|||
</div>
|
||||
<div class="col-md-6">
|
||||
<!-- Two-Factor Authentication (2FA) -->
|
||||
<div class="card mb-4">
|
||||
<div class="card mb-4 position-relative">
|
||||
<i class='bx bx-qr bx-sm position-absolute top-0 end-0 m-3'></i>
|
||||
<h5 class="card-header text-center">Two-Factor Authentication (2FA)</h5>
|
||||
<div class="card-body">
|
||||
{% if not is_totp %}
|
||||
|
|
@ -302,7 +302,7 @@
|
|||
<!-- QR Code Section -->
|
||||
<div class="col-md-12 text-center">
|
||||
<h5 class="mb-0">Enable 2FA</h5>
|
||||
<img class="img-fluid"
|
||||
<img class="img-fluid qr-code"
|
||||
src="{{ totp_qr_image }}"
|
||||
alt="2FA QR Code"
|
||||
height="200"
|
||||
|
|
@ -417,7 +417,8 @@
|
|||
<!-- /Two-Factor Authentication (2FA) -->
|
||||
{% if is_totp %}
|
||||
<!-- Refresh Recovery Codes -->
|
||||
<div class="card mb-4">
|
||||
<div class="card mb-4 position-relative">
|
||||
<i class='bx bx-data bx-sm position-absolute top-0 end-0 m-3'></i>
|
||||
<h5 class="card-header">Refresh Recovery Codes</h5>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-danger text-center" role="alert">
|
||||
|
|
@ -461,11 +462,12 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="navs-pills-sessions" role="tabpanel">
|
||||
<div class="d-flex row justify-content-center">
|
||||
<div class="d-flex row g-2 justify-content-center">
|
||||
<div class="col-md-4">
|
||||
<form method="POST" action="{{ profile_url }}/wipe-other-sessions">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<div class="card mb-4">
|
||||
<div class="card mb-4 position-relative">
|
||||
<i class='bx bx-history bx-sm position-absolute top-0 end-0 m-3'></i>
|
||||
<h5 class="card-header">Sessions</h5>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
target="_blank"
|
||||
rel="noreferrer">
|
||||
<img src="{{ url_for('static', filename='img/brands/Twitter-X-Logo.svg') }}"
|
||||
class="x-logo"
|
||||
alt="X logo"
|
||||
width="20px"
|
||||
height="20px">
|
||||
|
|
@ -56,8 +57,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- Newsletter Signup Section -->
|
||||
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-light-subtle border-top">
|
||||
<h5 class="mb-3 text-dark">Join the Newsletter</h5>
|
||||
<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>
|
||||
<form action="https://bunkerity.us1.list-manage.com/subscribe/post?u=ec5b1577cf427972b9bd491a6&id=37076d9d67"
|
||||
method="POST">
|
||||
<div class="mb-3">
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
target="_blank"
|
||||
rel="noreferrer">
|
||||
<img src="{{ url_for('static', filename='img/brands/Twitter-X-Logo.svg') }}"
|
||||
class="x-logo"
|
||||
alt="X logo"
|
||||
width="20px"
|
||||
height="20px">
|
||||
|
|
@ -53,7 +54,7 @@
|
|||
{% set content = message[0] %}
|
||||
{% set category = message[1] %}
|
||||
{% set datetime = message[2] %}
|
||||
<div class="col-12 bs-toast toast show bw-white 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 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">
|
||||
|
|
@ -77,8 +78,8 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
<!-- Newsletter Signup Section -->
|
||||
<div class="newsletter-signup position-sticky bottom-0 start-0 w-100 p-4 bg-light-subtle border-top">
|
||||
<h5 class="mb-3 text-dark">Join the Newsletter</h5>
|
||||
<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>
|
||||
<form action="https://bunkerity.us1.list-manage.com/subscribe/post?u=ec5b1577cf427972b9bd491a6&id=37076d9d67"
|
||||
method="POST">
|
||||
<div class="mb-3">
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ 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",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue