Make advancements on the profile page and optimize a few thinks here and there + provide small fixes

This commit is contained in:
Théophile Diot 2024-08-27 19:40:04 +01:00
parent efe8835d3c
commit 8228e60b8b
No known key found for this signature in database
GPG key ID: FA995104A0BA376A
25 changed files with 2053 additions and 2896 deletions

View file

@ -11,13 +11,6 @@ CODE_OF_CONDUCT.md
LICENSE.md
README.md
SECURITY.md
tsparticles.bundle.min.js
flatpickr.*
src/ui/static/js/lottie-web.min.js
src/ui/static/js/editor/*
src/ui/static/js/utils/purify/*
src/ui/static/json/particles.json
src/ui/templates/*
src/ui/app/templates/*
src/common/core/*/ui/*
datepicker-foundation.css
examples/

View file

@ -23,8 +23,8 @@ class AnonymousUser(AnonymousUserMixin):
method = "manual"
admin = False
totp_secret = None
creation_date = datetime.now()
update_date = datetime.now()
creation_date = datetime.now().astimezone()
update_date = datetime.now().astimezone()
list_roles = []
list_permissions = []
list_recovery_codes = []

View file

@ -47,7 +47,7 @@ class UIDatabase(Database):
if db_version != bunkerweb_version:
self.logger.warning(f"UI tables version ({db_version}) is different from BunkerWeb version ({bunkerweb_version}), migrating them ...")
current_time = datetime.now()
current_time = datetime.now().astimezone()
error = True
while error:
try:
@ -55,7 +55,7 @@ class UIDatabase(Database):
metadata.reflect(self.sql_engine)
error = False
except BaseException as e:
if (datetime.now() - current_time).total_seconds() > 10:
if (datetime.now().astimezone() - current_time).total_seconds() > 10:
raise e
sleep(1)
@ -201,7 +201,7 @@ class UIDatabase(Database):
return f"Role {role} doesn't exist"
session.add(RolesUsers(user_name=username, role_name=role))
current_time = datetime.now()
current_time = datetime.now().astimezone()
session.add(
Users(
username=username,
@ -263,7 +263,7 @@ class UIDatabase(Database):
user.password = password.decode("utf-8")
user.totp_secret = totp_secret
user.method = method
user.update_date = datetime.now()
user.update_date = datetime.now().astimezone()
try:
session.commit()
@ -354,7 +354,7 @@ class UIDatabase(Database):
if session.query(Roles).with_entities(Roles.name).filter_by(name=name).first():
return f"Role {name} already exists"
session.add(Roles(name=name, description=description, update_datetime=datetime.now()))
session.add(Roles(name=name, description=description, update_datetime=datetime.now().astimezone()))
for permission in permissions:
if not session.query(Permissions).with_entities(Permissions.name).filter_by(name=permission).first():
@ -479,15 +479,10 @@ class UIDatabase(Database):
return ""
def get_ui_user_last_session(self, username: str) -> Optional[UserSessions]:
"""Get ui user last session."""
with self._db_session() as session:
return session.query(UserSessions).filter_by(user_name=username).order_by(UserSessions.creation_date.desc()).first()
def get_ui_user_sessions(self, username: str) -> List[UserSessions]:
"""Get ui user sessions."""
with self._db_session() as session:
return session.query(UserSessions).filter_by(user_name=username).order_by(UserSessions.creation_date.desc()).limit(10).all()
return session.query(UserSessions).filter_by(user_name=username).order_by(UserSessions.creation_date.desc()).all()
def delete_ui_user_old_sessions(self, username: str) -> str:
"""Delete ui user old sessions."""

View file

@ -165,7 +165,8 @@ def bans_page():
ban_end = float(ban["ban_end"])
except ValueError:
continue
ban_end = (datetime.fromtimestamp(ban_end) - datetime.now()).total_seconds()
current_time = datetime.now().astimezone()
ban_end = (datetime.fromtimestamp(ban_end, tz=current_time.tzinfo) - current_time).total_seconds()
if redis_client:
ok = redis_client.set(f"bans_ip_{ban['ip']}", dumps({"reason": reason, "date": time()}))

View file

@ -25,15 +25,13 @@ def login_page():
ui_user = DB.get_ui_user(username=request.form["username"])
if ui_user and ui_user.username == request.form["username"] and ui_user.check_password(request.form["password"]):
# log the user in
session["creation_date"] = datetime.now().astimezone()
session["ip"] = request.remote_addr
session["user_agent"] = request.headers.get("User-Agent")
session["totp_validated"] = False
session["flash_messages"] = []
ret = DB.mark_ui_user_login(
ui_user.username,
datetime.now().astimezone(),
request.remote_addr,
request.headers.get("User-Agent"),
)
ret = DB.mark_ui_user_login(ui_user.username, session["creation_date"], session["ip"], session["user_agent"])
if isinstance(ret, str):
LOGGER.error(f"Couldn't mark the user login: {ret}")
else:

View file

@ -11,6 +11,25 @@ from app.routes.utils import handle_error, verify_data_in_form
profile = Blueprint("profile", __name__)
BROWSERS = {
"Chrome": "bxl-chrome",
"Firefox": "bxl-firefox",
"Safari": "bx-compass",
"Edge": "bxl-edge",
"Opera": "bxl-opera",
"Internet Explorer": "bxl-internet-explorer",
}
OS = {
"Windows": "bxl-windows",
"Mac": "bxl-apple",
"Linux": "bxl-tux",
}
DEVICES = {
"PC": "bx-desktop",
}
@profile.route("/profile", methods=["GET"])
@login_required
@ -23,15 +42,32 @@ def profile_page():
last_sessions = []
for db_session in DB.get_ui_user_sessions(current_user.username):
ua_data = parse(db_session.user_agent)
last_sessions.append(
{
"browser": ua_data.get_browser(),
"os": ua_data.get_os(),
"ip": db_session.ip,
"creation_date": db_session.creation_date.strftime("%Y-%m-%d %H:%M:%S %Z"),
"last_activity": db_session.last_activity.strftime("%Y-%m-%d %H:%M:%S %Z"),
}
)
last_session = {
"current": db_session.id == session.get("session_id"),
"browser": ua_data.get_browser(),
"os": ua_data.get_os(),
"device": ua_data.get_device(),
"ip": db_session.ip,
"creation_date": db_session.creation_date.astimezone().strftime("%Y-%m-%d %H:%M:%S %Z"),
"last_activity": db_session.last_activity.astimezone().strftime("%Y-%m-%d %H:%M:%S %Z"),
}
for browser, icon in BROWSERS.items():
if browser in last_session["browser"]:
last_session["browser"] = f"<i class='bx {icon} text-primary'></i>&nbsp;{last_session['browser']}"
break
for os, icon in OS.items():
if os in last_session["os"]:
last_session["os"] = f"<i class='bx {icon} text-primary'></i>&nbsp;{last_session['os']}"
break
last_session["device"] = f"<i class='bx {DEVICES.get(last_session['device'], 'bx-mobile')} text-primary'></i>&nbsp;{last_session['device']}"
if last_session["current"] and last_sessions:
last_sessions.insert(0, last_session)
continue
last_sessions.append(last_session)
return render_template(
"profile.html",

View file

@ -23,9 +23,9 @@ PLUGIN_ID_RX = re_compile(r"^[\w_-]{1,64}$")
def wait_applying():
current_time = datetime.now()
current_time = datetime.now().astimezone()
ready = False
while not ready and (datetime.now() - current_time).seconds < 120:
while not ready and (datetime.now().astimezone() - current_time).seconds < 120:
db_metadata = DB.get_metadata()
if isinstance(db_metadata, str):
LOGGER.error(f"An error occurred when checking for changes in the database : {db_metadata}")
@ -112,7 +112,7 @@ def manage_bunkerweb(method: str, *args, operation: str = "reloads", is_draft: b
flash(f["content"])
if "flash_messages" in session:
session["flash_messages"].append((f["content"], f["type"], datetime.now().strftime("%Y-%m-%d %H:%M:%S %Z")))
session["flash_messages"].append((f["content"], f["type"], datetime.now().astimezone().strftime("%Y-%m-%d %H:%M:%S %Z")))
DATA["TO_FLASH"] = []

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -1,127 +1,79 @@
{% set current_endpoint = url_for(request.endpoint).split("/")[-1] %}
<!doctype html>
<html
lang="en"
class="light-style layout-navbar-fixed layout-menu-fixed"
data-theme="theme-default"
>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="description" content="BunkerWeb user interface" />
<meta name="author" content="Bunkerity" />
<title>BunkerWeb UI</title>
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="img/favicon.ico" />
<!-- Fonts -->
<link
rel="stylesheet"
href="fonts/Public_sans.css"
nonce="{{ style_nonce }}"
/>
<!-- Icons -->
<link
rel="stylesheet"
href="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"
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"
nonce="{{ style_nonce }}"
/>
<link
rel="stylesheet"
href="libs/apexcharts/apexcharts.css"
nonce="{{ style_nonce }}"
/>
<!-- Page CSS -->
<!-- Page -->
{% if current_endpoint == "login" or current_endpoint == "totp" %}
<link
rel="stylesheet"
href="css/pages/login.css"
nonce="{{ style_nonce }}"
/>
{% elif current_endpoint == "loading" %}
<link
rel="stylesheet"
href="css/pages/loading.css"
nonce="{{ style_nonce }}"
/>
{% endif %}
<!-- Helpers -->
<script src="js/helpers.js" nonce="{{ script_nonce }}"></script>
<!-- Config -->
<script src="js/config.js" nonce="{{ script_nonce }}"></script>
</head>
<body>
<!-- prettier-ignore -->
{% if current_endpoint != "loading" %}
{% include "flash.html"%}
{% 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"
nonce="{{ script_nonce }}"
></script>
<script
src="libs/perfect-scrollbar/perfect-scrollbar.min.js"
nonce="{{ script_nonce }}"
></script>
<script src="libs/hammer/hammer.min.js" nonce="{{ script_nonce }}"></script>
<script
src="libs/masonry/masonry.pkgd.min.js"
nonce="{{ script_nonce }}"
></script>
<script src="libs/purify/purify.min.js" nonce="{{ script_nonce }}"></script>
<!-- Core JS -->
<script src="js/menu.js" nonce="{{ script_nonce }}"></script>
<script
src="libs/apexcharts/apexcharts.min.js"
nonce="{{ script_nonce }}"
></script>
<!-- Main JS -->
<script src="js/main.js" nonce="{{ script_nonce }}"></script>
<script
src="js/dashboards-analytics.js"
nonce="{{ script_nonce }}"
></script>
<!-- Page JS -->
{% if current_endpoint == "profile" %}
<script src="js/pages/profile.js" nonce="{{ script_nonce }}"></script>
{% endif %}
<script async defer src="js/buttons.js"></script>
</body>
<!DOCTYPE html>
<html lang="en"
class="light-style layout-navbar-fixed layout-menu-fixed"
data-theme="theme-default">
<head>
<meta charset="utf-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="description" content="BunkerWeb user interface" />
<meta name="author" content="Bunkerity" />
<title>BunkerWeb UI</title>
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="img/favicon.ico" />
<!-- Fonts -->
<link rel="stylesheet"
href="fonts/Public_sans.css"
nonce="{{ style_nonce }}" />
<!-- Icons -->
<link rel="stylesheet"
href="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"
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"
nonce="{{ style_nonce }}" />
<link rel="stylesheet"
href="libs/apexcharts/apexcharts.css"
nonce="{{ style_nonce }}" />
<!-- Page CSS -->
<!-- Page -->
{% if current_endpoint == "login" or current_endpoint == "totp" %}
<link rel="stylesheet" href="css/pages/login.css" nonce="{{ style_nonce }}" />
{% elif current_endpoint == "loading" %}
<link rel="stylesheet"
href="css/pages/loading.css"
nonce="{{ style_nonce }}" />
{% endif %}
<!-- Helpers -->
<script src="js/helpers.js" nonce="{{ script_nonce }}"></script>
<!-- Config -->
<script src="js/config.js" nonce="{{ script_nonce }}"></script>
</head>
<body>
<!-- prettier-ignore -->
{% if current_endpoint != "loading" %}
{% include "flash.html" %}
{% 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"
nonce="{{ script_nonce }}"></script>
<script src="libs/perfect-scrollbar/perfect-scrollbar.min.js"
nonce="{{ script_nonce }}"></script>
<script src="libs/hammer/hammer.min.js" nonce="{{ script_nonce }}"></script>
<script src="libs/masonry/masonry.pkgd.min.js" nonce="{{ script_nonce }}"></script>
<script src="libs/purify/purify.min.js" nonce="{{ script_nonce }}"></script>
<!-- Core JS -->
<script src="js/menu.js" nonce="{{ script_nonce }}"></script>
<script src="libs/apexcharts/apexcharts.min.js" nonce="{{ script_nonce }}"></script>
<!-- Main JS -->
<script src="js/main.js" nonce="{{ script_nonce }}"></script>
<script src="js/dashboards-analytics.js" nonce="{{ script_nonce }}"></script>
<!-- Page JS -->
{% if current_endpoint == "profile" %}
<script src="js/pages/profile.js" nonce="{{ script_nonce }}"></script>
{% endif %}
<script async defer src="js/buttons.js"></script>
</body>
</html>

View file

@ -1,22 +1,17 @@
<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:] %}
<li
class="breadcrumb-item {% if breadcrumb == last_crumb %}active{% endif %}"
{%
if
breadcrumb=""
="last_crumb"
%}aria-current="page"
{%
endif
%}
>
<a href="{{ url_for(breadcrumb) }}">{{ breadcrumb }}</a>
</li>
{% endfor %} {% endwith %} {% endwith %}
</ol>
<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 %}
{% endwith %}
</ol>
</nav>

View file

@ -1,122 +1,88 @@
{% extends "base.html" %} {% block page %}
<!-- Layout wrapper -->
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
{% include "menu.html" %}
<!-- Layout container -->
<div class="layout-page">
{% include "navbar.html" %}
<!-- Content wrapper -->
<div class="content-wrapper">
<div class="container-xxl flex-grow-1 container-p-y">
<!-- prettier-ignore -->
<div class="d-flex justify-content-between align-items-center mb-5">
{% include "breadcrumb.html" %}
<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"
aria-pressed="true"
href="https://panel.bunkerweb.io/order/support/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
>
<span class="tf-icons bx bx-help-circle bx-18px me-2"></span>
Need Help?
</a>
</li>
<li>
<a
id="news-button"
role="button"
class="btn btn-sm btn-dark text-uppercase d-flex align-items-center position-relative"
aria-pressed="true"
href="javascript:void(0)"
data-bs-toggle="offcanvas"
data-bs-target="#side-offcanvas"
aria-controls="side-offcanvas"
>
News
</a>
</li>
</ul>
</div>
{% block content %}{% endblock %}
{% extends "base.html" %}
{% block page %}
<!-- Layout wrapper -->
<div class="layout-wrapper layout-content-navbar">
<div class="layout-container">
{% include "menu.html" %}
<!-- Layout container -->
<div class="layout-page">
{% include "navbar.html" %}
<!-- Content wrapper -->
<div class="content-wrapper">
<div class="container-xxl flex-grow-1 container-p-y">
<!-- prettier-ignore -->
<div class="d-flex justify-content-between align-items-center mb-5">
{% include "breadcrumb.html" %}
<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"
aria-pressed="true"
href="https://panel.bunkerweb.io/order/support/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener">
<span class="tf-icons bx bx-help-circle bx-18px me-2"></span>
Need Help?
</a>
</li>
<li>
<a id="news-button"
role="button"
class="btn btn-sm btn-dark text-uppercase d-flex align-items-center position-relative"
aria-pressed="true"
href="javascript:void(0)"
data-bs-toggle="offcanvas"
data-bs-target="#side-offcanvas"
aria-controls="side-offcanvas">News</a>
</li>
</ul>
</div>
{% block content %}{% endblock %}
</div>
<!-- prettier-ignore -->
{% include "footer.html" %}
{% include "sidebar.html" %}
{% if not is_pro_version %}
<div class="buy-now">
<a class="btn btn-success btn-buy-now"
role="button"
aria-pressed="true"
href="https://panel.bunkerweb.io/order/bunkerweb-pro/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener">
<span class="tf-icons me-2 d-flex h-100 justify-content-center align-items-center">
<svg width="19"
height="15"
viewBox="0 0 19 15"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_226_466)">
<path d="M16.8356 1.98041L13.0948 3.96082H5.88436L2.16443 1.98041L5.80095 0H13.1991L16.8356 1.98041Z" fill="#349F53" />
<path d="M19 4.90339L14.542 7.42183L13.0948 3.96088L16.8356 1.98047L19 4.90339Z" fill="#227848" />
<path d="M19 4.90332L9.47913 15L14.5419 7.42176L19 4.90332Z" fill="#194B34" />
<path d="M5.88433 3.96088L4.30795 7.42183L0 4.90339L2.1644 1.98047L5.88433 3.96088Z" fill="#249C59" />
<path d="M9.47915 15L0 4.90332L4.30795 7.42176L9.47915 15Z" fill="#237F4C" />
<path d="M14.542 7.42175L9.47919 15L4.30798 7.42175H14.542Z" fill="#349F53" />
<path d="M14.542 7.42177H4.30798L5.88437 3.96082H13.0949L14.542 7.42177Z" fill="#65B278" />
</g>
<defs>
<clipPath id="clip0_226_466">
<rect width="19" height="15" fill="white" />
</clipPath>
</defs>
</svg>
</span>
Upgrade to Pro</a>
</div>
{% endif %}
<div class="content-backdrop fade"></div>
</div>
<!-- Content wrapper -->
</div>
<!-- / Layout page -->
</div>
<!-- prettier-ignore -->
{% include "footer.html" %}
{% include "sidebar.html" %}
{% if not is_pro_version %}
<div class="buy-now">
<a
class="btn btn-success btn-buy-now"
role="button"
aria-pressed="true"
href="https://panel.bunkerweb.io/order/bunkerweb-pro/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
>
<span
class="tf-icons me-2 d-flex h-100 justify-content-center align-items-center"
>
<svg
width="19"
height="15"
viewBox="0 0 19 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_226_466)">
<path
d="M16.8356 1.98041L13.0948 3.96082H5.88436L2.16443 1.98041L5.80095 0H13.1991L16.8356 1.98041Z"
fill="#349F53"
/>
<path
d="M19 4.90339L14.542 7.42183L13.0948 3.96088L16.8356 1.98047L19 4.90339Z"
fill="#227848"
/>
<path
d="M19 4.90332L9.47913 15L14.5419 7.42176L19 4.90332Z"
fill="#194B34"
/>
<path
d="M5.88433 3.96088L4.30795 7.42183L0 4.90339L2.1644 1.98047L5.88433 3.96088Z"
fill="#249C59"
/>
<path
d="M9.47915 15L0 4.90332L4.30795 7.42176L9.47915 15Z"
fill="#237F4C"
/>
<path
d="M14.542 7.42175L9.47919 15L4.30798 7.42175H14.542Z"
fill="#349F53"
/>
<path
d="M14.542 7.42177H4.30798L5.88437 3.96082H13.0949L14.542 7.42177Z"
fill="#65B278"
/>
</g>
<defs>
<clipPath id="clip0_226_466">
<rect width="19" height="15" fill="white" />
</clipPath>
</defs></svg
></span>
Upgrade to Pro</a
>
</div>
{% endif %}
<div class="content-backdrop fade"></div>
</div>
<!-- Content wrapper -->
<!-- Overlay -->
<div class="layout-overlay layout-menu-toggle"></div>
</div>
<!-- / Layout page -->
</div>
<!-- Overlay -->
<div class="layout-overlay layout-menu-toggle"></div>
</div>
<!-- / Layout wrapper -->
<!-- / Layout wrapper -->
{% endblock %}

View file

@ -1,33 +1,34 @@
<div class="toast-container position-fixed bottom-0 end-0 mb-3 me-3">
<!-- prettier-ignore -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if pro_overlapped %}
{% if messages.append(('error', 'You have more services than allowed by your pro license. Upgrade your license or move some services to draft mode to unlock your pro license.')) %} {% endif %}
{% endif %}
<!-- flash message-->
{% for category, message in messages %}
<div
class="bs-toast toast fade show {% if category == 'error' %}bg-danger{% elif category == 'warning' %}bg-warning{% else %}bg-primary text-white{% endif %}"
role="alert"
aria-live="assertive"
aria-atomic="true"
>
<div class="toast-header">
<i class="d-block w-px-20 h-auto rounded me-2 tf-icons bx bx-bell"></i>
<span class="fw-medium me-auto"
>{% if category == 'error' %}Error{% else %}Success{% endif %}</span
>
<small class="text-body-secondary">just now</small>
<button
type="button"
class="btn-close"
data-bs-dismiss="toast"
aria-label="Close"
></button>
</div>
<div class="toast-body">{{ message|safe }}</div>
</div>
{% endfor %}
<!-- end flash message-->
{% endwith %}
<!-- prettier-ignore -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if pro_overlapped %}
{% if messages.append(('error', 'You have more services than allowed by your pro license. Upgrade your license or move some services to draft mode to unlock your pro license.')) %}
{% endif %}
{% endif %}
<!-- flash message-->
{% for category, message in messages %}
<div class="bs-toast toast fade show {% if category == 'error' %}bg-danger{% elif category == 'warning' %}bg-warning{% else %}bg-primary text-white{% endif %}"
role="alert"
aria-live="assertive"
aria-atomic="true">
<div class="toast-header">
<i class="d-block w-px-20 h-auto rounded me-2 tf-icons bx bx-bell"></i>
<span class="fw-medium me-auto">
{% if category == 'error' %}
Error
{% else %}
Success
{% endif %}
</span>
<small class="text-body-secondary">just now</small>
<button type="button"
class="btn-close"
data-bs-dismiss="toast"
aria-label="Close"></button>
</div>
<div class="toast-body">{{ message|safe }}</div>
</div>
{% endfor %}
<!-- end flash message-->
{% endwith %}
</div>

View file

@ -1,76 +1,39 @@
<!-- Footer -->
<footer class="content-footer footer bg-footer-theme">
<div class="container-xxl">
<div
class="footer-container d-flex align-items-center justify-content-between py-4 flex-md-row flex-column"
>
<div class="text-body">
©
<script nonce="{{ script_nonce }}">
document.write(new Date().getFullYear());
</script>
, made by
<a
href="https://www.bunkerity.com/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link"
>Bunkerity</a
>
</div>
<div class="d-none d-lg-inline-block">
<a
href="https://docs.bunkerweb.io/latest/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link me-4"
>Documentation</a
>
<a
href="https://www.bunkerweb.io/privacy-policy/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link me-4"
>Privacy</a
>
<a
href="https://www.bunkerweb.io/blog/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link me-4"
>Blog</a
>
<a
href="https://panel.bunkerweb.io/order/support/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link me-4"
>Support</a
>
<a
{%
if
bw_version=""
="dev"
or
bw_version=""
="testing"
%}
href="https://github.com/bunkerity/bunkerweb/blob/{{ bw_version }}/LICENSE.md"
{%
else
%}
href="https://github.com/bunkerity/bunkerweb/blob/v{{ bw_version }}/LICENSE.md"
{%
endif
%}
class="footer-link"
target="_blank"
rel="noopener"
>License</a
>
</div>
<div class="container-xxl">
<div class="footer-container d-flex align-items-center justify-content-between py-4 flex-md-row flex-column">
<div class="text-body">
©
<script nonce="{{ script_nonce }}">document.write(new Date().getFullYear());</script>
, made by
<a href="https://www.bunkerity.com/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link">Bunkerity</a>
</div>
<div class="d-none d-lg-inline-block">
<a href="https://docs.bunkerweb.io/latest/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link me-4">Documentation</a>
<a href="https://www.bunkerweb.io/privacy-policy/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link me-4">Privacy</a>
<a href="https://www.bunkerweb.io/blog/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link me-4">Blog</a>
<a href="https://panel.bunkerweb.io/order/support/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="footer-link me-4">Support</a>
<a href="https://github.com/bunkerity/bunkerweb/blob/{% if bw_version != 'dev' and bw_version != 'testing' %}v{% endif %}{{ bw_version }}/LICENSE.md"
class="footer-link"
target="_blank"
rel="noopener">License</a>
</div>
</div>
</div>
</div>
</footer>
<!-- / Footer -->

File diff suppressed because it is too large Load diff

View file

@ -1,31 +1,25 @@
{% extends "base.html" %} {% block page %}
<div class="layout-wrapper bg-primary">
<div class="layout-container">
<div class="content-wrapper">
<div
class="container-xxl d-flex justify-content-center align-items-center min-vh-100"
>
<div class="layout-main-wrapper">
<div class="layout-main-placeholder">
<img
src="img/logo-menu-2.png"
class="img-fluid pulsating"
alt="Logo"
/>
</div>
{% if message %}
<div class="layout-main-info">
<h2>{{ message }}</h2>
</div>
{% endif %}
{% extends "base.html" %}
{% block page %}
<div class="layout-wrapper bg-primary">
<div class="layout-container">
<div class="content-wrapper">
<div class="container-xxl d-flex justify-content-center align-items-center min-vh-100">
<div class="layout-main-wrapper">
<div class="layout-main-placeholder">
<img src="img/logo-menu-2.png" class="img-fluid pulsating" alt="Logo" />
</div>
{% if message %}
<div class="layout-main-info">
<h2>{{ message }}</h2>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- / Content -->
<script nonce="{{ script_nonce }}">
<!-- / Content -->
<script nonce="{{ script_nonce }}">
const reloading = setInterval(check_reloading, 2000);
check_reloading();
@ -47,5 +41,5 @@
}
}
}
</script>
</script>
{% endblock %}

View file

@ -1,31 +1,28 @@
{% extends "base.html" %} {% block page %}
<!-- Content -->
<div class="container-xxl">
<div class="authentication-wrapper authentication-basic container-p-y">
<div class="authentication-inner">
<!-- Register -->
<div class="card px-sm-6 px-0">
<div class="card-body">
<!-- Logo -->
<div class="app-brand justify-content-center">
<a
href="https://www.bunkerweb.io/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="app-brand-link gap-2"
>
<span class="app-brand-logo login">
<svg
version="1.1"
width="25"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 272.68 76.37"
xml:space="preserve"
>
<style type="text/css" nonce="{{ style_nonce }}">
{% extends "base.html" %}
{% block page %}
<!-- Content -->
<div class="container-xxl">
<div class="authentication-wrapper authentication-basic container-p-y">
<div class="authentication-inner">
<!-- Register -->
<div class="card px-sm-6 px-0">
<div class="card-body">
<!-- Logo -->
<div class="app-brand justify-content-center">
<a href="https://www.bunkerweb.io/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="app-brand-link gap-2">
<span class="app-brand-logo login">
<svg version="1.1"
width="25"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 272.68 76.37"
xml:space="preserve">
<style type="text/css" nonce="{{ style_nonce }}">
.st0 {
fill: #ffffff;
}
@ -35,196 +32,95 @@
.st2 {
fill: #0b5577;
}
</style>
<g>
<path
class="st2"
d="M244.87,44.46c0.16-1.27,1.3-2.12,2.59-1.95c1.01,0.13,1.85,0.96,1.92,2.05c0.14,1.52,0.26,2.71,0.36,3.85
h16.02c-0.24-1.79-0.49-3.61-0.82-6c-0.1-0.8,0.25-1.61,0.9-2.02c3.4-2.43,5.46-6.75,6.49-12.5c2.34-13.05-7.24-18.86-18.92-20.2
c-7.49-1-11.24-1.45-18.75-2.29c-1.35-0.15-2.51,0.72-2.65,2c-1.48,13.67-2.97,27.34-4.45,41.01h16.79
C244.51,47.22,244.66,46.05,244.87,44.46z M243.31,21.88c2.52,0.56,3.78,0.85,6.29,1.43c0.87-0.8,2.08-1.22,3.37-1.03
c2.21,0.32,3.75,2.31,3.43,4.42c-0.32,2.11-2.36,3.55-4.55,3.23c-1.27-0.19-2.31-0.93-2.91-1.94c-2.56-0.14-3.84-0.2-6.41-0.32
C242.84,25.36,242.99,24.2,243.31,21.88z"
/>
<g>
<path
class="st2"
d="M224.95,41c0.11-1.06-0.73-2.08-1.81-2.19c-6.15-0.63-9.23-0.91-15.38-1.43c-1.16-0.1-2.02-1.09-1.93-2.23
c0.01-0.08,0.01-0.13,0.02-0.21c0.09-1.06,1.11-1.91,2.27-1.81c4.44,0.37,6.67,0.57,11.11,1c1.09,0.11,2.12-0.72,2.24-1.85
c0.35-3.56,0.53-5.34,0.88-8.91c0.1-1.06-0.76-2.07-1.87-2.18c-4.51-0.44-6.76-0.64-11.28-1.02c-1.18-0.1-2.06-1.1-1.97-2.23
c0.09-1.06,1.12-1.9,2.31-1.8c6.31,0.53,9.47,0.82,15.77,1.46c1.11,0.11,2.17-0.7,2.28-1.84c0.38-3.62,0.57-5.43,0.94-9.04
c0.11-1.06-0.77-2.08-1.9-2.19c-11.16-1.06-22.29-1.98-33.47-2.77c-1.2-0.08-2.24,0.79-2.3,1.85
c-0.89,14.49-1.77,28.97-2.66,43.46c-0.03,0.5,0.13,0.96,0.4,1.33h35.57C224.45,45.8,224.63,44.03,224.95,41z"
/>
<g>
<path
class="st2"
d="M185.93,46.8c-2.49-8.91-3.77-13.36-6.42-22.25c-0.06-0.29-0.04-0.71,0.05-0.99
c3.19-8.01,4.82-12,8.14-19.98c0.44-1.04-0.33-2.15-1.53-2.22c-5.45-0.3-8.18-0.43-13.64-0.65c-0.75-0.03-1.45,0.44-1.7,1.07
c-2.18,5.75-3.25,8.62-5.37,14.38c-0.24,0.63-0.92,1.11-1.66,1.08c-0.96-0.03-1.68-0.76-1.65-1.68
c0.16-5.42,0.23-8.14,0.39-13.56c0.03-0.92-0.71-1.65-1.69-1.68c-5.61-0.15-8.41-0.2-14.02-0.26c-0.98-0.01-1.74,0.69-1.75,1.62
c-0.13,14.77-0.26,29.55-0.39,44.32c-0.01,0.92,0.7,1.64,1.63,1.65c5.31,0.06,7.96,0.11,13.26,0.25
c0.93,0.02,1.66-0.66,1.69-1.59c0.14-5.03,0.22-7.54,0.36-12.57c0.03-0.92,0.77-1.61,1.72-1.58c0.06,0,0.09,0,0.15,0
c0.73,0.02,1.29,0.47,1.56,1.12c1.82,5.55,2.72,8.32,4.48,13.88c0.26,0.65,0.81,1.1,1.53,1.13c1.23,0.05,2.31,0.1,3.32,0.14
h11.12C185.9,48,186.1,47.41,185.93,46.8z"
/>
<g>
<path
class="st2"
d="M109.16,48.06c1.64-0.06,2.95-1.44,2.9-3.08c-0.14-4.49-0.2-6.73-0.34-11.22
c-0.03-0.92,0.68-1.51,1.48-1.54c0.51-0.01,0.95,0.19,1.25,0.68c3.54,5.28,5.29,7.92,8.72,13.24c0.52,0.99,1.52,1.54,2.67,1.52
c4.62-0.06,6.93-0.08,11.55-0.08c1.64,0,3-1.34,3.01-2.98c0.05-13.87,0.1-27.75,0.16-41.62c0.01-1.63-1.42-2.99-3.16-2.99
c-4.46,0-6.7,0.01-11.16,0.07c-1.73,0.02-3.14,1.39-3.12,3.03c0.07,4.09,0.1,6.14,0.17,10.23c0.01,0.85-0.72,1.43-1.53,1.45
c-0.45,0.01-0.97-0.2-1.27-0.62c-3.6-4.96-5.42-7.43-9.12-12.35c-0.55-0.84-1.62-1.37-2.76-1.34
c-4.88,0.16-7.33,0.26-12.21,0.48c-1.73,0.08-3.1,1.5-3.01,3.13c0.69,13.86,1.39,27.71,2.08,41.57
c0.07,1.37,1.09,2.48,2.38,2.76h2.4C103.45,48.26,105.53,48.18,109.16,48.06z"
/>
<g>
<path
class="st2"
d="M90.68,31.54c-0.59-10.75-0.89-16.13-1.48-26.88c-0.1-1.84-1.76-3.25-3.72-3.13
C81.33,1.77,79.25,1.91,75.1,2.2c-1.96,0.14-3.43,1.74-3.29,3.59c0.83,11.11,1.25,16.66,2.08,27.76
c0.23,3.05-1.38,3.88-4.2,4.1c-2.82,0.23-4.61-0.33-4.87-3.37c-0.95-11.1-1.43-16.64-2.38-27.74
c-0.16-1.84-1.86-3.19-3.82-3.02c-4.12,0.37-6.18,0.57-10.29,0.98c-2.03,0.21-3.45,1.86-3.25,3.69
c1.14,10.82,1.72,16.23,2.86,27.05c0.61,6.46,3.39,10.67,7.45,13.16H81.6C87.6,45.46,91.41,40.09,90.68,31.54z"
/>
<g>
<path
class="st2"
d="M45.57,39.08c-0.36-3.32-1.02-6.96-4.2-8.75c-1.34-0.76-1.85-2.58-0.95-3.82
c1.9-2.86,1.84-6.21,1.48-9.46c-0.87-7.84-7.46-11.1-19.67-9.49c-7.79,1.03-11.68,1.58-19.45,2.79
c-1.79,0.28-3,1.84-2.74,3.52c1.82,11.52,3.65,23.04,5.47,34.55h37.72C45.2,45.98,45.98,42.89,45.57,39.08z M19.57,41.45
c-0.16-3.39-0.24-5.08-0.39-8.47c-1.42-0.79-2.47-2.16-2.7-3.84c-0.41-2.91,1.72-5.62,4.76-6.03
c3.04-0.41,5.81,1.63,6.19,4.55c0.22,1.69-0.43,3.28-1.59,4.42c0.76,3.31,1.13,4.96,1.88,8.27
C24.46,40.77,22.83,40.99,19.57,41.45z"
/>
</g>
</g>
<path
class="st2"
d="M100.25,48.41h-2.4c0.25,0.05,0.5,0.09,0.76,0.08C99.2,48.46,99.73,48.43,100.25,48.41z"
/>
</g>
</g>
</g>
</g>
<g>
<rect
x="89.49"
y="51.91"
class="st1"
width="93.69"
height="24.46"
/>
<g>
<g>
<path
class="st0"
d="M121.14,73.93l-1.91,0.03l-3.43-9.9l-3.34,9.93l-1.91-0.01l-3.25-14.33l-0.75,0c-0.37,0-0.63-0.07-0.79-0.22
s-0.24-0.34-0.24-0.57c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22l4.24-0.02c0.37,0,0.63,0.07,0.79,0.22
c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55s-0.42,0.22-0.79,0.22l-1.96,0.01l2.78,12.22l3.25-9.78l1.84-0.01
l3.42,9.76l2.58-12.24l-1.95,0.01c-0.37,0-0.63-0.07-0.8-0.22c-0.16-0.15-0.25-0.34-0.25-0.57c0-0.22,0.08-0.4,0.24-0.55
c0.16-0.15,0.43-0.22,0.81-0.22l4.22-0.02c0.38,0,0.65,0.07,0.81,0.22c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55
s-0.43,0.22-0.81,0.22l-0.73,0L121.14,73.93z"
/>
<path
class="st0"
d="M146.71,66.25l-15.72,0.06c0.28,1.99,1.12,3.6,2.52,4.81c1.41,1.21,3.14,1.82,5.21,1.81
c1.15,0,2.36-0.2,3.62-0.58c1.26-0.38,2.29-0.89,3.08-1.52c0.23-0.18,0.43-0.28,0.6-0.28c0.2,0,0.37,0.08,0.51,0.23
c0.15,0.15,0.22,0.33,0.22,0.54s-0.1,0.41-0.29,0.61c-0.59,0.61-1.63,1.19-3.12,1.73c-1.5,0.54-3.04,0.81-4.62,0.82
c-2.64,0.01-4.85-0.85-6.63-2.57c-1.78-1.72-2.67-3.82-2.68-6.28c-0.01-2.24,0.81-4.17,2.47-5.78s3.7-2.42,6.15-2.43
c2.52-0.01,4.6,0.81,6.23,2.45C145.91,61.5,146.72,63.63,146.71,66.25z M145.14,64.7c-0.31-1.7-1.12-3.08-2.43-4.14
c-1.31-1.06-2.86-1.59-4.66-1.58c-1.8,0.01-3.35,0.54-4.64,1.6s-2.1,2.45-2.41,4.18L145.14,64.7z"
/>
<path
class="st0"
d="M155.06,50.76l0.04,10.23c1.85-2.43,4.09-3.65,6.73-3.66c2.25-0.01,4.18,0.8,5.79,2.43
c1.61,1.63,2.42,3.63,2.43,6.01c0.01,2.4-0.8,4.43-2.41,6.11c-1.62,1.67-3.53,2.51-5.75,2.52c-2.69,0.01-4.94-1.19-6.75-3.61
l0.01,3.03l-3.62,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.33-0.24-0.55c0-0.23,0.08-0.42,0.24-0.56
c0.16-0.14,0.42-0.21,0.79-0.21l2.07-0.01l-0.07-19.94l-2.07,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.34-0.24-0.57
c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22L155.06,50.76z M168.5,65.84c-0.01-1.95-0.68-3.59-2.02-4.94
c-1.34-1.35-2.9-2.02-4.69-2.01s-3.35,0.69-4.67,2.05c-1.33,1.36-1.99,3.01-1.98,4.96c0.01,1.95,0.68,3.59,2.02,4.94
c1.34,1.35,2.9,2.02,4.69,2.01c1.79-0.01,3.35-0.69,4.67-2.05C167.85,69.44,168.51,67.79,168.5,65.84z"
/>
</g>
</g>
</g>
</svg>
</span>
</a>
</div>
<!-- /Logo -->
<p class="mb-6">Please sign-in to your account</p>
<form class="mb-6" method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input
type="hidden"
name="next"
value="{{ request.values.get('next', '') }}"
/>
<div class="mb-6">
<label for="username" class="form-label">Username</label>
<input
type="text"
class="form-control"
id="username"
name="username"
placeholder="Enter your username"
autofocus
required
/>
</div>
<div class="mb-6 form-password-toggle">
<label class="form-label" for="password">Password</label>
<div class="input-group input-group-merge">
<input
type="password"
id="password"
class="form-control"
name="password"
placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;"
aria-describedby="password"
required
/>
<span class="input-group-text cursor-pointer"
><i class="bx bx-hide"></i
></span>
</div>
</div>
<div class="mb-8">
<div class="d-flex justify-content-between mt-8">
<div class="form-check mb-0 ms-2">
<input
class="form-check-input"
type="checkbox"
id="remember-me"
name="remember-me"
/>
<label class="form-check-label" for="remember-me">
Remember Me
</label>
</style>
<g>
<path class="st2" d="M244.87,44.46c0.16-1.27,1.3-2.12,2.59-1.95c1.01,0.13,1.85,0.96,1.92,2.05c0.14,1.52,0.26,2.71,0.36,3.85 h16.02c-0.24-1.79-0.49-3.61-0.82-6c-0.1-0.8,0.25-1.61,0.9-2.02c3.4-2.43,5.46-6.75,6.49-12.5c2.34-13.05-7.24-18.86-18.92-20.2 c-7.49-1-11.24-1.45-18.75-2.29c-1.35-0.15-2.51,0.72-2.65,2c-1.48,13.67-2.97,27.34-4.45,41.01h16.79 C244.51,47.22,244.66,46.05,244.87,44.46z M243.31,21.88c2.52,0.56,3.78,0.85,6.29,1.43c0.87-0.8,2.08-1.22,3.37-1.03 c2.21,0.32,3.75,2.31,3.43,4.42c-0.32,2.11-2.36,3.55-4.55,3.23c-1.27-0.19-2.31-0.93-2.91-1.94c-2.56-0.14-3.84-0.2-6.41-0.32 C242.84,25.36,242.99,24.2,243.31,21.88z" />
<g>
<path class="st2" d="M224.95,41c0.11-1.06-0.73-2.08-1.81-2.19c-6.15-0.63-9.23-0.91-15.38-1.43c-1.16-0.1-2.02-1.09-1.93-2.23 c0.01-0.08,0.01-0.13,0.02-0.21c0.09-1.06,1.11-1.91,2.27-1.81c4.44,0.37,6.67,0.57,11.11,1c1.09,0.11,2.12-0.72,2.24-1.85 c0.35-3.56,0.53-5.34,0.88-8.91c0.1-1.06-0.76-2.07-1.87-2.18c-4.51-0.44-6.76-0.64-11.28-1.02c-1.18-0.1-2.06-1.1-1.97-2.23 c0.09-1.06,1.12-1.9,2.31-1.8c6.31,0.53,9.47,0.82,15.77,1.46c1.11,0.11,2.17-0.7,2.28-1.84c0.38-3.62,0.57-5.43,0.94-9.04 c0.11-1.06-0.77-2.08-1.9-2.19c-11.16-1.06-22.29-1.98-33.47-2.77c-1.2-0.08-2.24,0.79-2.3,1.85 c-0.89,14.49-1.77,28.97-2.66,43.46c-0.03,0.5,0.13,0.96,0.4,1.33h35.57C224.45,45.8,224.63,44.03,224.95,41z" />
<g>
<path class="st2" d="M185.93,46.8c-2.49-8.91-3.77-13.36-6.42-22.25c-0.06-0.29-0.04-0.71,0.05-0.99 c3.19-8.01,4.82-12,8.14-19.98c0.44-1.04-0.33-2.15-1.53-2.22c-5.45-0.3-8.18-0.43-13.64-0.65c-0.75-0.03-1.45,0.44-1.7,1.07 c-2.18,5.75-3.25,8.62-5.37,14.38c-0.24,0.63-0.92,1.11-1.66,1.08c-0.96-0.03-1.68-0.76-1.65-1.68 c0.16-5.42,0.23-8.14,0.39-13.56c0.03-0.92-0.71-1.65-1.69-1.68c-5.61-0.15-8.41-0.2-14.02-0.26c-0.98-0.01-1.74,0.69-1.75,1.62 c-0.13,14.77-0.26,29.55-0.39,44.32c-0.01,0.92,0.7,1.64,1.63,1.65c5.31,0.06,7.96,0.11,13.26,0.25 c0.93,0.02,1.66-0.66,1.69-1.59c0.14-5.03,0.22-7.54,0.36-12.57c0.03-0.92,0.77-1.61,1.72-1.58c0.06,0,0.09,0,0.15,0 c0.73,0.02,1.29,0.47,1.56,1.12c1.82,5.55,2.72,8.32,4.48,13.88c0.26,0.65,0.81,1.1,1.53,1.13c1.23,0.05,2.31,0.1,3.32,0.14 h11.12C185.9,48,186.1,47.41,185.93,46.8z" />
<g>
<path class="st2" d="M109.16,48.06c1.64-0.06,2.95-1.44,2.9-3.08c-0.14-4.49-0.2-6.73-0.34-11.22 c-0.03-0.92,0.68-1.51,1.48-1.54c0.51-0.01,0.95,0.19,1.25,0.68c3.54,5.28,5.29,7.92,8.72,13.24c0.52,0.99,1.52,1.54,2.67,1.52 c4.62-0.06,6.93-0.08,11.55-0.08c1.64,0,3-1.34,3.01-2.98c0.05-13.87,0.1-27.75,0.16-41.62c0.01-1.63-1.42-2.99-3.16-2.99 c-4.46,0-6.7,0.01-11.16,0.07c-1.73,0.02-3.14,1.39-3.12,3.03c0.07,4.09,0.1,6.14,0.17,10.23c0.01,0.85-0.72,1.43-1.53,1.45 c-0.45,0.01-0.97-0.2-1.27-0.62c-3.6-4.96-5.42-7.43-9.12-12.35c-0.55-0.84-1.62-1.37-2.76-1.34 c-4.88,0.16-7.33,0.26-12.21,0.48c-1.73,0.08-3.1,1.5-3.01,3.13c0.69,13.86,1.39,27.71,2.08,41.57 c0.07,1.37,1.09,2.48,2.38,2.76h2.4C103.45,48.26,105.53,48.18,109.16,48.06z" />
<g>
<path class="st2" d="M90.68,31.54c-0.59-10.75-0.89-16.13-1.48-26.88c-0.1-1.84-1.76-3.25-3.72-3.13 C81.33,1.77,79.25,1.91,75.1,2.2c-1.96,0.14-3.43,1.74-3.29,3.59c0.83,11.11,1.25,16.66,2.08,27.76 c0.23,3.05-1.38,3.88-4.2,4.1c-2.82,0.23-4.61-0.33-4.87-3.37c-0.95-11.1-1.43-16.64-2.38-27.74 c-0.16-1.84-1.86-3.19-3.82-3.02c-4.12,0.37-6.18,0.57-10.29,0.98c-2.03,0.21-3.45,1.86-3.25,3.69 c1.14,10.82,1.72,16.23,2.86,27.05c0.61,6.46,3.39,10.67,7.45,13.16H81.6C87.6,45.46,91.41,40.09,90.68,31.54z" />
<g>
<path class="st2" d="M45.57,39.08c-0.36-3.32-1.02-6.96-4.2-8.75c-1.34-0.76-1.85-2.58-0.95-3.82 c1.9-2.86,1.84-6.21,1.48-9.46c-0.87-7.84-7.46-11.1-19.67-9.49c-7.79,1.03-11.68,1.58-19.45,2.79 c-1.79,0.28-3,1.84-2.74,3.52c1.82,11.52,3.65,23.04,5.47,34.55h37.72C45.2,45.98,45.98,42.89,45.57,39.08z M19.57,41.45 c-0.16-3.39-0.24-5.08-0.39-8.47c-1.42-0.79-2.47-2.16-2.7-3.84c-0.41-2.91,1.72-5.62,4.76-6.03 c3.04-0.41,5.81,1.63,6.19,4.55c0.22,1.69-0.43,3.28-1.59,4.42c0.76,3.31,1.13,4.96,1.88,8.27 C24.46,40.77,22.83,40.99,19.57,41.45z" />
</g>
</g>
<path class="st2" d="M100.25,48.41h-2.4c0.25,0.05,0.5,0.09,0.76,0.08C99.2,48.46,99.73,48.43,100.25,48.41z" />
</g>
</g>
</g>
</g>
<g>
<rect x="89.49" y="51.91" class="st1" width="93.69" height="24.46" />
<g>
<g>
<path class="st0" d="M121.14,73.93l-1.91,0.03l-3.43-9.9l-3.34,9.93l-1.91-0.01l-3.25-14.33l-0.75,0c-0.37,0-0.63-0.07-0.79-0.22 s-0.24-0.34-0.24-0.57c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22l4.24-0.02c0.37,0,0.63,0.07,0.79,0.22 c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55s-0.42,0.22-0.79,0.22l-1.96,0.01l2.78,12.22l3.25-9.78l1.84-0.01 l3.42,9.76l2.58-12.24l-1.95,0.01c-0.37,0-0.63-0.07-0.8-0.22c-0.16-0.15-0.25-0.34-0.25-0.57c0-0.22,0.08-0.4,0.24-0.55 c0.16-0.15,0.43-0.22,0.81-0.22l4.22-0.02c0.38,0,0.65,0.07,0.81,0.22c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55 s-0.43,0.22-0.81,0.22l-0.73,0L121.14,73.93z" />
<path class="st0" d="M146.71,66.25l-15.72,0.06c0.28,1.99,1.12,3.6,2.52,4.81c1.41,1.21,3.14,1.82,5.21,1.81 c1.15,0,2.36-0.2,3.62-0.58c1.26-0.38,2.29-0.89,3.08-1.52c0.23-0.18,0.43-0.28,0.6-0.28c0.2,0,0.37,0.08,0.51,0.23 c0.15,0.15,0.22,0.33,0.22,0.54s-0.1,0.41-0.29,0.61c-0.59,0.61-1.63,1.19-3.12,1.73c-1.5,0.54-3.04,0.81-4.62,0.82 c-2.64,0.01-4.85-0.85-6.63-2.57c-1.78-1.72-2.67-3.82-2.68-6.28c-0.01-2.24,0.81-4.17,2.47-5.78s3.7-2.42,6.15-2.43 c2.52-0.01,4.6,0.81,6.23,2.45C145.91,61.5,146.72,63.63,146.71,66.25z M145.14,64.7c-0.31-1.7-1.12-3.08-2.43-4.14 c-1.31-1.06-2.86-1.59-4.66-1.58c-1.8,0.01-3.35,0.54-4.64,1.6s-2.1,2.45-2.41,4.18L145.14,64.7z" />
<path class="st0" d="M155.06,50.76l0.04,10.23c1.85-2.43,4.09-3.65,6.73-3.66c2.25-0.01,4.18,0.8,5.79,2.43 c1.61,1.63,2.42,3.63,2.43,6.01c0.01,2.4-0.8,4.43-2.41,6.11c-1.62,1.67-3.53,2.51-5.75,2.52c-2.69,0.01-4.94-1.19-6.75-3.61 l0.01,3.03l-3.62,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.33-0.24-0.55c0-0.23,0.08-0.42,0.24-0.56 c0.16-0.14,0.42-0.21,0.79-0.21l2.07-0.01l-0.07-19.94l-2.07,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.34-0.24-0.57 c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22L155.06,50.76z M168.5,65.84c-0.01-1.95-0.68-3.59-2.02-4.94 c-1.34-1.35-2.9-2.02-4.69-2.01s-3.35,0.69-4.67,2.05c-1.33,1.36-1.99,3.01-1.98,4.96c0.01,1.95,0.68,3.59,2.02,4.94 c1.34,1.35,2.9,2.02,4.69,2.01c1.79-0.01,3.35-0.69,4.67-2.05C167.85,69.44,168.51,67.79,168.5,65.84z" />
</g>
</g>
</g>
</svg>
</span>
</a>
</div>
<!-- /Logo -->
<p class="mb-6">Please sign-in to your account</p>
<form class="mb-6" method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden"
name="next"
value="{{ request.values.get('next', '') }}" />
<div class="mb-6">
<label for="username" class="form-label">Username</label>
<input type="text"
class="form-control"
id="username"
name="username"
placeholder="Enter your username"
autofocus
required />
</div>
<div class="mb-6 form-password-toggle">
<label class="form-label" for="password">Password</label>
<div class="input-group input-group-merge">
<input type="password"
id="password"
class="form-control"
name="password"
placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;"
aria-describedby="password"
required />
<span class="input-group-text cursor-pointer"><i class="bx bx-hide"></i></span>
</div>
</div>
<div class="mb-8">
<div class="d-flex justify-content-between mt-8">
<div class="form-check mb-0 ms-2">
<input class="form-check-input"
type="checkbox"
id="remember-me"
name="remember-me" />
<label class="form-check-label" for="remember-me">Remember Me</label>
</div>
<a href="https://docs.bunkerweb.io/latest/troubleshooting/?utm_campaign=self&utm_source=ui#__tabbed_6_2"
target="_blank"
rel="noopener">
<span>Forgot Password?</span>
</a>
</div>
</div>
<div class="mb-6">
<button class="btn btn-primary d-grid w-100" type="submit">Login</button>
</div>
</form>
</div>
</div>
<a
href="https://docs.bunkerweb.io/latest/troubleshooting/?utm_campaign=self&utm_source=ui#__tabbed_6_2"
target="_blank"
rel="noopener"
>
<span>Forgot Password?</span>
</a>
</div>
<!-- /Register -->
</div>
<div class="mb-6">
<button class="btn btn-primary d-grid w-100" type="submit">
Login
</button>
</div>
</form>
</div>
</div>
<!-- /Register -->
</div>
</div>
</div>
<!-- / Content -->
<!-- / Content -->
{% endblock %}

View file

@ -1,23 +1,18 @@
<!-- Menu -->
<aside id="layout-menu" class="layout-menu menu-vertical menu bg-menu-theme">
<div class="app-brand main">
<a
href="https://www.bunkerweb.io/?utm_campaign=self&utm_source=ui"
target="_blank"
class="app-brand-link"
>
<span class="app-brand-logo main">
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 272.68 76.37"
xml:space="preserve"
>
<style type="text/css" nonce="{{ style_nonce }}">
<div class="app-brand main">
<a href="https://www.bunkerweb.io/?utm_campaign=self&utm_source=ui"
target="_blank"
class="app-brand-link">
<span class="app-brand-logo main">
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 272.68 76.37"
xml:space="preserve">
<style type="text/css" nonce="{{ style_nonce }}">
.st0 {
fill: #ffffff;
}
@ -27,357 +22,155 @@
.st2 {
fill: #0b5577;
}
</style>
<g>
<path
class="st2"
d="M244.87,44.46c0.16-1.27,1.3-2.12,2.59-1.95c1.01,0.13,1.85,0.96,1.92,2.05c0.14,1.52,0.26,2.71,0.36,3.85
h16.02c-0.24-1.79-0.49-3.61-0.82-6c-0.1-0.8,0.25-1.61,0.9-2.02c3.4-2.43,5.46-6.75,6.49-12.5c2.34-13.05-7.24-18.86-18.92-20.2
c-7.49-1-11.24-1.45-18.75-2.29c-1.35-0.15-2.51,0.72-2.65,2c-1.48,13.67-2.97,27.34-4.45,41.01h16.79
C244.51,47.22,244.66,46.05,244.87,44.46z M243.31,21.88c2.52,0.56,3.78,0.85,6.29,1.43c0.87-0.8,2.08-1.22,3.37-1.03
c2.21,0.32,3.75,2.31,3.43,4.42c-0.32,2.11-2.36,3.55-4.55,3.23c-1.27-0.19-2.31-0.93-2.91-1.94c-2.56-0.14-3.84-0.2-6.41-0.32
C242.84,25.36,242.99,24.2,243.31,21.88z"
/>
<g>
<path
class="st2"
d="M224.95,41c0.11-1.06-0.73-2.08-1.81-2.19c-6.15-0.63-9.23-0.91-15.38-1.43c-1.16-0.1-2.02-1.09-1.93-2.23
c0.01-0.08,0.01-0.13,0.02-0.21c0.09-1.06,1.11-1.91,2.27-1.81c4.44,0.37,6.67,0.57,11.11,1c1.09,0.11,2.12-0.72,2.24-1.85
c0.35-3.56,0.53-5.34,0.88-8.91c0.1-1.06-0.76-2.07-1.87-2.18c-4.51-0.44-6.76-0.64-11.28-1.02c-1.18-0.1-2.06-1.1-1.97-2.23
c0.09-1.06,1.12-1.9,2.31-1.8c6.31,0.53,9.47,0.82,15.77,1.46c1.11,0.11,2.17-0.7,2.28-1.84c0.38-3.62,0.57-5.43,0.94-9.04
c0.11-1.06-0.77-2.08-1.9-2.19c-11.16-1.06-22.29-1.98-33.47-2.77c-1.2-0.08-2.24,0.79-2.3,1.85
c-0.89,14.49-1.77,28.97-2.66,43.46c-0.03,0.5,0.13,0.96,0.4,1.33h35.57C224.45,45.8,224.63,44.03,224.95,41z"
/>
<g>
<path
class="st2"
d="M185.93,46.8c-2.49-8.91-3.77-13.36-6.42-22.25c-0.06-0.29-0.04-0.71,0.05-0.99
c3.19-8.01,4.82-12,8.14-19.98c0.44-1.04-0.33-2.15-1.53-2.22c-5.45-0.3-8.18-0.43-13.64-0.65c-0.75-0.03-1.45,0.44-1.7,1.07
c-2.18,5.75-3.25,8.62-5.37,14.38c-0.24,0.63-0.92,1.11-1.66,1.08c-0.96-0.03-1.68-0.76-1.65-1.68
c0.16-5.42,0.23-8.14,0.39-13.56c0.03-0.92-0.71-1.65-1.69-1.68c-5.61-0.15-8.41-0.2-14.02-0.26c-0.98-0.01-1.74,0.69-1.75,1.62
c-0.13,14.77-0.26,29.55-0.39,44.32c-0.01,0.92,0.7,1.64,1.63,1.65c5.31,0.06,7.96,0.11,13.26,0.25
c0.93,0.02,1.66-0.66,1.69-1.59c0.14-5.03,0.22-7.54,0.36-12.57c0.03-0.92,0.77-1.61,1.72-1.58c0.06,0,0.09,0,0.15,0
c0.73,0.02,1.29,0.47,1.56,1.12c1.82,5.55,2.72,8.32,4.48,13.88c0.26,0.65,0.81,1.1,1.53,1.13c1.23,0.05,2.31,0.1,3.32,0.14
h11.12C185.9,48,186.1,47.41,185.93,46.8z"
/>
<g>
<path
class="st2"
d="M109.16,48.06c1.64-0.06,2.95-1.44,2.9-3.08c-0.14-4.49-0.2-6.73-0.34-11.22
c-0.03-0.92,0.68-1.51,1.48-1.54c0.51-0.01,0.95,0.19,1.25,0.68c3.54,5.28,5.29,7.92,8.72,13.24c0.52,0.99,1.52,1.54,2.67,1.52
c4.62-0.06,6.93-0.08,11.55-0.08c1.64,0,3-1.34,3.01-2.98c0.05-13.87,0.1-27.75,0.16-41.62c0.01-1.63-1.42-2.99-3.16-2.99
c-4.46,0-6.7,0.01-11.16,0.07c-1.73,0.02-3.14,1.39-3.12,3.03c0.07,4.09,0.1,6.14,0.17,10.23c0.01,0.85-0.72,1.43-1.53,1.45
c-0.45,0.01-0.97-0.2-1.27-0.62c-3.6-4.96-5.42-7.43-9.12-12.35c-0.55-0.84-1.62-1.37-2.76-1.34
c-4.88,0.16-7.33,0.26-12.21,0.48c-1.73,0.08-3.1,1.5-3.01,3.13c0.69,13.86,1.39,27.71,2.08,41.57
c0.07,1.37,1.09,2.48,2.38,2.76h2.4C103.45,48.26,105.53,48.18,109.16,48.06z"
/>
<g>
<path
class="st2"
d="M90.68,31.54c-0.59-10.75-0.89-16.13-1.48-26.88c-0.1-1.84-1.76-3.25-3.72-3.13
C81.33,1.77,79.25,1.91,75.1,2.2c-1.96,0.14-3.43,1.74-3.29,3.59c0.83,11.11,1.25,16.66,2.08,27.76
c0.23,3.05-1.38,3.88-4.2,4.1c-2.82,0.23-4.61-0.33-4.87-3.37c-0.95-11.1-1.43-16.64-2.38-27.74
c-0.16-1.84-1.86-3.19-3.82-3.02c-4.12,0.37-6.18,0.57-10.29,0.98c-2.03,0.21-3.45,1.86-3.25,3.69
c1.14,10.82,1.72,16.23,2.86,27.05c0.61,6.46,3.39,10.67,7.45,13.16H81.6C87.6,45.46,91.41,40.09,90.68,31.54z"
/>
</style>
<g>
<path
class="st2"
d="M45.57,39.08c-0.36-3.32-1.02-6.96-4.2-8.75c-1.34-0.76-1.85-2.58-0.95-3.82
c1.9-2.86,1.84-6.21,1.48-9.46c-0.87-7.84-7.46-11.1-19.67-9.49c-7.79,1.03-11.68,1.58-19.45,2.79
c-1.79,0.28-3,1.84-2.74,3.52c1.82,11.52,3.65,23.04,5.47,34.55h37.72C45.2,45.98,45.98,42.89,45.57,39.08z M19.57,41.45
c-0.16-3.39-0.24-5.08-0.39-8.47c-1.42-0.79-2.47-2.16-2.7-3.84c-0.41-2.91,1.72-5.62,4.76-6.03
c3.04-0.41,5.81,1.63,6.19,4.55c0.22,1.69-0.43,3.28-1.59,4.42c0.76,3.31,1.13,4.96,1.88,8.27
C24.46,40.77,22.83,40.99,19.57,41.45z"
/>
<path class="st2" d="M244.87,44.46c0.16-1.27,1.3-2.12,2.59-1.95c1.01,0.13,1.85,0.96,1.92,2.05c0.14,1.52,0.26,2.71,0.36,3.85 h16.02c-0.24-1.79-0.49-3.61-0.82-6c-0.1-0.8,0.25-1.61,0.9-2.02c3.4-2.43,5.46-6.75,6.49-12.5c2.34-13.05-7.24-18.86-18.92-20.2 c-7.49-1-11.24-1.45-18.75-2.29c-1.35-0.15-2.51,0.72-2.65,2c-1.48,13.67-2.97,27.34-4.45,41.01h16.79 C244.51,47.22,244.66,46.05,244.87,44.46z M243.31,21.88c2.52,0.56,3.78,0.85,6.29,1.43c0.87-0.8,2.08-1.22,3.37-1.03 c2.21,0.32,3.75,2.31,3.43,4.42c-0.32,2.11-2.36,3.55-4.55,3.23c-1.27-0.19-2.31-0.93-2.91-1.94c-2.56-0.14-3.84-0.2-6.41-0.32 C242.84,25.36,242.99,24.2,243.31,21.88z" />
<g>
<path class="st2" d="M224.95,41c0.11-1.06-0.73-2.08-1.81-2.19c-6.15-0.63-9.23-0.91-15.38-1.43c-1.16-0.1-2.02-1.09-1.93-2.23 c0.01-0.08,0.01-0.13,0.02-0.21c0.09-1.06,1.11-1.91,2.27-1.81c4.44,0.37,6.67,0.57,11.11,1c1.09,0.11,2.12-0.72,2.24-1.85 c0.35-3.56,0.53-5.34,0.88-8.91c0.1-1.06-0.76-2.07-1.87-2.18c-4.51-0.44-6.76-0.64-11.28-1.02c-1.18-0.1-2.06-1.1-1.97-2.23 c0.09-1.06,1.12-1.9,2.31-1.8c6.31,0.53,9.47,0.82,15.77,1.46c1.11,0.11,2.17-0.7,2.28-1.84c0.38-3.62,0.57-5.43,0.94-9.04 c0.11-1.06-0.77-2.08-1.9-2.19c-11.16-1.06-22.29-1.98-33.47-2.77c-1.2-0.08-2.24,0.79-2.3,1.85 c-0.89,14.49-1.77,28.97-2.66,43.46c-0.03,0.5,0.13,0.96,0.4,1.33h35.57C224.45,45.8,224.63,44.03,224.95,41z" />
<g>
<path class="st2" d="M185.93,46.8c-2.49-8.91-3.77-13.36-6.42-22.25c-0.06-0.29-0.04-0.71,0.05-0.99 c3.19-8.01,4.82-12,8.14-19.98c0.44-1.04-0.33-2.15-1.53-2.22c-5.45-0.3-8.18-0.43-13.64-0.65c-0.75-0.03-1.45,0.44-1.7,1.07 c-2.18,5.75-3.25,8.62-5.37,14.38c-0.24,0.63-0.92,1.11-1.66,1.08c-0.96-0.03-1.68-0.76-1.65-1.68 c0.16-5.42,0.23-8.14,0.39-13.56c0.03-0.92-0.71-1.65-1.69-1.68c-5.61-0.15-8.41-0.2-14.02-0.26c-0.98-0.01-1.74,0.69-1.75,1.62 c-0.13,14.77-0.26,29.55-0.39,44.32c-0.01,0.92,0.7,1.64,1.63,1.65c5.31,0.06,7.96,0.11,13.26,0.25 c0.93,0.02,1.66-0.66,1.69-1.59c0.14-5.03,0.22-7.54,0.36-12.57c0.03-0.92,0.77-1.61,1.72-1.58c0.06,0,0.09,0,0.15,0 c0.73,0.02,1.29,0.47,1.56,1.12c1.82,5.55,2.72,8.32,4.48,13.88c0.26,0.65,0.81,1.1,1.53,1.13c1.23,0.05,2.31,0.1,3.32,0.14 h11.12C185.9,48,186.1,47.41,185.93,46.8z" />
<g>
<path class="st2" d="M109.16,48.06c1.64-0.06,2.95-1.44,2.9-3.08c-0.14-4.49-0.2-6.73-0.34-11.22 c-0.03-0.92,0.68-1.51,1.48-1.54c0.51-0.01,0.95,0.19,1.25,0.68c3.54,5.28,5.29,7.92,8.72,13.24c0.52,0.99,1.52,1.54,2.67,1.52 c4.62-0.06,6.93-0.08,11.55-0.08c1.64,0,3-1.34,3.01-2.98c0.05-13.87,0.1-27.75,0.16-41.62c0.01-1.63-1.42-2.99-3.16-2.99 c-4.46,0-6.7,0.01-11.16,0.07c-1.73,0.02-3.14,1.39-3.12,3.03c0.07,4.09,0.1,6.14,0.17,10.23c0.01,0.85-0.72,1.43-1.53,1.45 c-0.45,0.01-0.97-0.2-1.27-0.62c-3.6-4.96-5.42-7.43-9.12-12.35c-0.55-0.84-1.62-1.37-2.76-1.34 c-4.88,0.16-7.33,0.26-12.21,0.48c-1.73,0.08-3.1,1.5-3.01,3.13c0.69,13.86,1.39,27.71,2.08,41.57 c0.07,1.37,1.09,2.48,2.38,2.76h2.4C103.45,48.26,105.53,48.18,109.16,48.06z" />
<g>
<path class="st2" d="M90.68,31.54c-0.59-10.75-0.89-16.13-1.48-26.88c-0.1-1.84-1.76-3.25-3.72-3.13 C81.33,1.77,79.25,1.91,75.1,2.2c-1.96,0.14-3.43,1.74-3.29,3.59c0.83,11.11,1.25,16.66,2.08,27.76 c0.23,3.05-1.38,3.88-4.2,4.1c-2.82,0.23-4.61-0.33-4.87-3.37c-0.95-11.1-1.43-16.64-2.38-27.74 c-0.16-1.84-1.86-3.19-3.82-3.02c-4.12,0.37-6.18,0.57-10.29,0.98c-2.03,0.21-3.45,1.86-3.25,3.69 c1.14,10.82,1.72,16.23,2.86,27.05c0.61,6.46,3.39,10.67,7.45,13.16H81.6C87.6,45.46,91.41,40.09,90.68,31.54z" />
<g>
<path class="st2" d="M45.57,39.08c-0.36-3.32-1.02-6.96-4.2-8.75c-1.34-0.76-1.85-2.58-0.95-3.82 c1.9-2.86,1.84-6.21,1.48-9.46c-0.87-7.84-7.46-11.1-19.67-9.49c-7.79,1.03-11.68,1.58-19.45,2.79 c-1.79,0.28-3,1.84-2.74,3.52c1.82,11.52,3.65,23.04,5.47,34.55h37.72C45.2,45.98,45.98,42.89,45.57,39.08z M19.57,41.45 c-0.16-3.39-0.24-5.08-0.39-8.47c-1.42-0.79-2.47-2.16-2.7-3.84c-0.41-2.91,1.72-5.62,4.76-6.03 c3.04-0.41,5.81,1.63,6.19,4.55c0.22,1.69-0.43,3.28-1.59,4.42c0.76,3.31,1.13,4.96,1.88,8.27 C24.46,40.77,22.83,40.99,19.57,41.45z" />
</g>
</g>
<path
class="st2"
d="M100.25,48.41h-2.4c0.25,0.05,0.5,0.09,0.76,0.08C99.2,48.46,99.73,48.43,100.25,48.41z"
/>
</g>
</g>
</g>
</g>
<g>
<rect
x="89.49"
y="51.91"
class="st1"
width="93.69"
height="24.46"
/>
<g>
<g>
<path
class="st0"
d="M121.14,73.93l-1.91,0.03l-3.43-9.9l-3.34,9.93l-1.91-0.01l-3.25-14.33l-0.75,0c-0.37,0-0.63-0.07-0.79-0.22
s-0.24-0.34-0.24-0.57c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22l4.24-0.02c0.37,0,0.63,0.07,0.79,0.22
c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55s-0.42,0.22-0.79,0.22l-1.96,0.01l2.78,12.22l3.25-9.78l1.84-0.01
l3.42,9.76l2.58-12.24l-1.95,0.01c-0.37,0-0.63-0.07-0.8-0.22c-0.16-0.15-0.25-0.34-0.25-0.57c0-0.22,0.08-0.4,0.24-0.55
c0.16-0.15,0.43-0.22,0.81-0.22l4.22-0.02c0.38,0,0.65,0.07,0.81,0.22c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55
s-0.43,0.22-0.81,0.22l-0.73,0L121.14,73.93z"
/>
<path
class="st0"
d="M146.71,66.25l-15.72,0.06c0.28,1.99,1.12,3.6,2.52,4.81c1.41,1.21,3.14,1.82,5.21,1.81
c1.15,0,2.36-0.2,3.62-0.58c1.26-0.38,2.29-0.89,3.08-1.52c0.23-0.18,0.43-0.28,0.6-0.28c0.2,0,0.37,0.08,0.51,0.23
c0.15,0.15,0.22,0.33,0.22,0.54s-0.1,0.41-0.29,0.61c-0.59,0.61-1.63,1.19-3.12,1.73c-1.5,0.54-3.04,0.81-4.62,0.82
c-2.64,0.01-4.85-0.85-6.63-2.57c-1.78-1.72-2.67-3.82-2.68-6.28c-0.01-2.24,0.81-4.17,2.47-5.78s3.7-2.42,6.15-2.43
c2.52-0.01,4.6,0.81,6.23,2.45C145.91,61.5,146.72,63.63,146.71,66.25z M145.14,64.7c-0.31-1.7-1.12-3.08-2.43-4.14
c-1.31-1.06-2.86-1.59-4.66-1.58c-1.8,0.01-3.35,0.54-4.64,1.6s-2.1,2.45-2.41,4.18L145.14,64.7z"
/>
<path
class="st0"
d="M155.06,50.76l0.04,10.23c1.85-2.43,4.09-3.65,6.73-3.66c2.25-0.01,4.18,0.8,5.79,2.43
c1.61,1.63,2.42,3.63,2.43,6.01c0.01,2.4-0.8,4.43-2.41,6.11c-1.62,1.67-3.53,2.51-5.75,2.52c-2.69,0.01-4.94-1.19-6.75-3.61
l0.01,3.03l-3.62,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.33-0.24-0.55c0-0.23,0.08-0.42,0.24-0.56
c0.16-0.14,0.42-0.21,0.79-0.21l2.07-0.01l-0.07-19.94l-2.07,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.34-0.24-0.57
c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22L155.06,50.76z M168.5,65.84c-0.01-1.95-0.68-3.59-2.02-4.94
c-1.34-1.35-2.9-2.02-4.69-2.01s-3.35,0.69-4.67,2.05c-1.33,1.36-1.99,3.01-1.98,4.96c0.01,1.95,0.68,3.59,2.02,4.94
c1.34,1.35,2.9,2.02,4.69,2.01c1.79-0.01,3.35-0.69,4.67-2.05C167.85,69.44,168.51,67.79,168.5,65.84z"
/>
</g>
</g>
</g>
</svg>
</span>
</a>
<a
href="javascript:void(0);"
class="layout-menu-toggle menu-link text-large ms-auto d-block d-xl-none"
>
<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>
<ul class="menu-inner py-1">
<!-- Home -->
<li class="menu-item {% if current_endpoint == 'home' %}active{% endif %}">
<a href="{{ url_for('home') }}" class="menu-link">
<i class="menu-icon tf-icons bx bx-home-smile"></i>
<div class="text-truncate" data-i18n="Basic">Home</div>
</a>
</li>
<!-- Instances -->
<li
class="menu-item {% if current_endpoint == 'instances' %}active{% endif %}"
>
<a href="{{ url_for('instances') }}" class="menu-link">
<i class="menu-icon tf-icons bx bx-server"></i>
<div class="text-truncate" data-i18n="Instances">Instances</div>
</a>
</li>
<!-- Global config -->
<li
class="menu-item {% if current_endpoint == 'global-config' %}active{% endif %}"
>
<a href="{{ url_for('global_config') }}" class="menu-link">
<i class="menu-icon tf-icons bx bx-cog"></i>
<div class="text-truncate" data-i18n="Global Config">Global Config</div>
</a>
</li>
<!-- Services -->
<li
class="menu-item {% if current_endpoint == 'services' %}active{% endif %} open"
>
<a href="{{ url_for('services') }}" class="menu-link menu-toggle">
<i class="menu-icon tf-icons bx bx-cube"></i>
<div class="text-truncate" data-i18n="Services">Services</div>
</a>
<ul class="menu-sub">
{% for service in services[:5] %}
<li
class="menu-item {% if current_endpoint == service %}active{% endif %}"
>
<a href="{{ url_for('services') }}/{{ service }}" class="menu-link">
<div class="text-truncate" data-i18n="Without menu">
{{ service }}
</div>
</a>
</li>
{% endfor %} {% if services|length > 5 %}
<li class="menu-item">
<a href="{{ url_for('services') }}" class="menu-link">
<div class="text-truncate" data-i18n="Without menu">More...</div>
</a>
</li>
{% endif %}
</ul>
</li>
<!-- Configs -->
<li
class="menu-item {% if current_endpoint == 'configs' %}active{% endif %}"
>
<a href="{{ url_for('configs') }}" class="menu-link">
<i class="menu-icon tf-icons bx bx-wrench"></i>
<div class="text-truncate" data-i18n="Configs">Configs</div>
</a>
</li>
<!-- Plugins -->
<li
class="menu-item {% if current_endpoint == 'plugins' %}active{% endif %}"
>
<a href="{{ url_for('plugins') }}" class="menu-link">
<i class="menu-icon tf-icons bx bx-plug"></i>
<div class="text-truncate" data-i18n="Plugins">Plugins</div>
</a>
</li>
<!-- Cache -->
<li class="menu-item {% if current_endpoint == 'cache' %}active{% endif %}">
<a href="{{ url_for('cache') }}" class="menu-link">
<i class="menu-icon tf-icons bx bx-data"></i>
<div class="text-truncate" data-i18n="Cache">Cache</div>
</a>
</li>
<!-- Reports -->
<li
class="menu-item {% if current_endpoint == 'reports' %}active{% endif %}"
>
<a href="{{ url_for('reports') }}" class="menu-link">
<i class="menu-icon tf-icons bx bx-bar-chart-alt-2"></i>
<div class="text-truncate" data-i18n="Reports">Reports</div>
</a>
</li>
<!-- Bans -->
<li class="menu-item {% if current_endpoint == 'bans' %}active{% endif %}">
<a href="{{ url_for('bans') }}" class="menu-link">
<i class="menu-icon tf-icons bx bx-block"></i>
<div class="text-truncate" data-i18n="Bans">Bans</div>
</a>
</li>
<!-- Jobs -->
<li class="menu-item {% if current_endpoint == 'jobs' %}active{% endif %}">
<a href="{{ url_for('jobs') }}" class="menu-link">
<i class="menu-icon tf-icons bx bx-time-five"></i>
<div class="text-truncate" data-i18n="Jobs">Jobs</div>
</a>
</li>
<!-- Logs -->
<li class="menu-item {% if current_endpoint == 'logs' %}active{% endif %}">
<a href="{{ url_for('logs') }}" class="menu-link">
<i class="menu-icon tf-icons bx bx-file-find"></i>
<div class="text-truncate" data-i18n="Logs">Logs</div>
</a>
</li>
<!-- Plugins Pages -->
<li class="menu-header small text-uppercase">
<span class="menu-header-text">Plugins Pages</span>
</li>
{% for plugin in plugins %} {% if (not is_pro_version and plugin['type'] ==
"pro") or plugin['page'] %}
<li
class="menu-item {% if current_endpoint == plugin['id'] %}active{% endif %}"
>
<a
href="{% if not is_pro_version and plugin['type'] == 'pro' %}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"
>
<svg
width="19"
height="15"
viewBox="0 0 19 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_226_466)">
<path
d="M16.8356 1.98041L13.0948 3.96082H5.88436L2.16443 1.98041L5.80095 0H13.1991L16.8356 1.98041Z"
fill="#349F53"
/>
<path
d="M19 4.90339L14.542 7.42183L13.0948 3.96088L16.8356 1.98047L19 4.90339Z"
fill="#227848"
/>
<path
d="M19 4.90332L9.47913 15L14.5419 7.42176L19 4.90332Z"
fill="#194B34"
/>
<path
d="M5.88433 3.96088L4.30795 7.42183L0 4.90339L2.1644 1.98047L5.88433 3.96088Z"
fill="#249C59"
/>
<path
d="M9.47915 15L0 4.90332L4.30795 7.42176L9.47915 15Z"
fill="#237F4C"
/>
<path
d="M14.542 7.42175L9.47919 15L4.30798 7.42175H14.542Z"
fill="#349F53"
/>
<path
d="M14.542 7.42177H4.30798L5.88437 3.96082H13.0949L14.542 7.42177Z"
fill="#65B278"
/>
</g>
<defs>
<clipPath id="clip0_226_466">
<rect width="19" height="15" fill="white" />
</clipPath>
</defs>
</svg>
</div>
{% endif %}
</a>
</li>
{% endif %} {% endfor %}
<!-- Misc -->
<li class="menu-header small text-uppercase">
<span class="menu-header-text">Misc</span>
</li>
<li class="menu-item">
<a
href="https://panel.bunkerweb.io/order/support/?utm_campaign=self&utm_source=ui"
target="_blank"
class="menu-link"
>
<i class="menu-icon tf-icons bx bx-support"></i>
<div class="text-truncate" data-i18n="Support">Support</div>
</a>
</li>
<li class="menu-item">
<a
href="https://docs.bunkerweb.io/latest/?utm_campaign=self&utm_source=ui"
target="_blank"
class="menu-link"
>
<i class="menu-icon tf-icons bx bx-file"></i>
<div class="text-truncate" data-i18n="Documentation">Documentation</div>
</a>
</li>
</ul>
</g>
<path class="st2" d="M100.25,48.41h-2.4c0.25,0.05,0.5,0.09,0.76,0.08C99.2,48.46,99.73,48.43,100.25,48.41z" />
</g>
</g>
</g>
</g>
<g>
<rect x="89.49" y="51.91" class="st1" width="93.69" height="24.46" />
<g>
<g>
<path class="st0" d="M121.14,73.93l-1.91,0.03l-3.43-9.9l-3.34,9.93l-1.91-0.01l-3.25-14.33l-0.75,0c-0.37,0-0.63-0.07-0.79-0.22 s-0.24-0.34-0.24-0.57c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22l4.24-0.02c0.37,0,0.63,0.07,0.79,0.22 c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55s-0.42,0.22-0.79,0.22l-1.96,0.01l2.78,12.22l3.25-9.78l1.84-0.01 l3.42,9.76l2.58-12.24l-1.95,0.01c-0.37,0-0.63-0.07-0.8-0.22c-0.16-0.15-0.25-0.34-0.25-0.57c0-0.22,0.08-0.4,0.24-0.55 c0.16-0.15,0.43-0.22,0.81-0.22l4.22-0.02c0.38,0,0.65,0.07,0.81,0.22c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55 s-0.43,0.22-0.81,0.22l-0.73,0L121.14,73.93z" />
<path class="st0" d="M146.71,66.25l-15.72,0.06c0.28,1.99,1.12,3.6,2.52,4.81c1.41,1.21,3.14,1.82,5.21,1.81 c1.15,0,2.36-0.2,3.62-0.58c1.26-0.38,2.29-0.89,3.08-1.52c0.23-0.18,0.43-0.28,0.6-0.28c0.2,0,0.37,0.08,0.51,0.23 c0.15,0.15,0.22,0.33,0.22,0.54s-0.1,0.41-0.29,0.61c-0.59,0.61-1.63,1.19-3.12,1.73c-1.5,0.54-3.04,0.81-4.62,0.82 c-2.64,0.01-4.85-0.85-6.63-2.57c-1.78-1.72-2.67-3.82-2.68-6.28c-0.01-2.24,0.81-4.17,2.47-5.78s3.7-2.42,6.15-2.43 c2.52-0.01,4.6,0.81,6.23,2.45C145.91,61.5,146.72,63.63,146.71,66.25z M145.14,64.7c-0.31-1.7-1.12-3.08-2.43-4.14 c-1.31-1.06-2.86-1.59-4.66-1.58c-1.8,0.01-3.35,0.54-4.64,1.6s-2.1,2.45-2.41,4.18L145.14,64.7z" />
<path class="st0" d="M155.06,50.76l0.04,10.23c1.85-2.43,4.09-3.65,6.73-3.66c2.25-0.01,4.18,0.8,5.79,2.43 c1.61,1.63,2.42,3.63,2.43,6.01c0.01,2.4-0.8,4.43-2.41,6.11c-1.62,1.67-3.53,2.51-5.75,2.52c-2.69,0.01-4.94-1.19-6.75-3.61 l0.01,3.03l-3.62,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.33-0.24-0.55c0-0.23,0.08-0.42,0.24-0.56 c0.16-0.14,0.42-0.21,0.79-0.21l2.07-0.01l-0.07-19.94l-2.07,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.34-0.24-0.57 c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22L155.06,50.76z M168.5,65.84c-0.01-1.95-0.68-3.59-2.02-4.94 c-1.34-1.35-2.9-2.02-4.69-2.01s-3.35,0.69-4.67,2.05c-1.33,1.36-1.99,3.01-1.98,4.96c0.01,1.95,0.68,3.59,2.02,4.94 c1.34,1.35,2.9,2.02,4.69,2.01c1.79-0.01,3.35-0.69,4.67-2.05C167.85,69.44,168.51,67.79,168.5,65.84z" />
</g>
</g>
</g>
</svg>
</span>
</a>
<a href="javascript:void(0);"
class="layout-menu-toggle menu-link text-large ms-auto d-block d-xl-none">
<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>
{% with menu_items = {
"home": {"url": url_for('home'), "icon": "bx-home-smile"},
"instances": {"url": url_for('instances'), "icon": "bx-server"},
"global-config": {"url": url_for('global_config'), "icon": "bx-cog"},
"services": {"url": url_for('services'), "icon": "bx-cube", "sub": services, "max": 5},
"configs": {"url": url_for('configs'), "icon": "bx-wrench"},
"plugins": {"url": url_for('plugins'), "icon": "bx-plug"},
"cache": {"url": url_for('cache'), "icon": "bx-data"},
"reports": {"url": url_for('reports'), "icon": "bx-bar-chart-alt-2"},
"bans": {"url": url_for('bans'), "icon": "bx-block"},
"jobs": {"url": url_for('jobs'), "icon": "bx-time-five"},
"logs": {"url": url_for('logs'), "icon": "bx-file-find"},
} %}
<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 %}">
<a href="{{ item['url'] }}"
class="menu-link {% if item.get('sub') %}menu-toggle{% endif %}">
<i class="menu-icon tf-icons bx {{ item['icon'] }}"></i>
<div class="text-truncate"
data-i18n="{{ endpoint.replace('-', ' ') |title }}">
{{ endpoint.replace('-', ' ') |title }}
</div>
</a>
{% if item.get('sub') %}
<ul class="menu-sub">
{% for sub in item['sub'][:item.get('max', 5)] %}
<li class="menu-item {% if current_endpoint == sub %}active{% endif %}">
<a href="{{ item['url'] }}/{{ sub }}" class="menu-link">
<div class="text-truncate" data-i18n="Without menu">{{ sub }}</div>
</a>
</li>
{% endfor %}
{% if item['sub']|length > item.get('max', 5) %}
<li class="menu-item">
<a href="{{ item['url'] }}" class="menu-link">
<div class="text-truncate" data-i18n="Without menu">More...</div>
</a>
</li>
{% endif %}
</ul>
{% endif %}
</li>
{% endfor %}
<!-- Plugins Pages -->
<li class="menu-header small text-uppercase">
<span class="menu-header-text">Plugins Pages</span>
</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">
<svg width="19"
height="15"
viewBox="0 0 19 15"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_226_466)">
<path d="M16.8356 1.98041L13.0948 3.96082H5.88436L2.16443 1.98041L5.80095 0H13.1991L16.8356 1.98041Z" fill="#349F53" />
<path d="M19 4.90339L14.542 7.42183L13.0948 3.96088L16.8356 1.98047L19 4.90339Z" fill="#227848" />
<path d="M19 4.90332L9.47913 15L14.5419 7.42176L19 4.90332Z" fill="#194B34" />
<path d="M5.88433 3.96088L4.30795 7.42183L0 4.90339L2.1644 1.98047L5.88433 3.96088Z" fill="#249C59" />
<path d="M9.47915 15L0 4.90332L4.30795 7.42176L9.47915 15Z" fill="#237F4C" />
<path d="M14.542 7.42175L9.47919 15L4.30798 7.42175H14.542Z" fill="#349F53" />
<path d="M14.542 7.42177H4.30798L5.88437 3.96082H13.0949L14.542 7.42177Z" fill="#65B278" />
</g>
<defs>
<clipPath id="clip0_226_466">
<rect width="19" height="15" fill="white" />
</clipPath>
</defs>
</svg>
</div>
{% endif %}
</a>
</li>
{% endif %}
{% endwith %}
{% endfor %}
<!-- Misc -->
<li class="menu-header small text-uppercase">
<span class="menu-header-text">Misc</span>
</li>
<li class="menu-item">
<a href="https://panel.bunkerweb.io/order/support/?utm_campaign=self&utm_source=ui"
target="_blank"
class="menu-link">
<i class="menu-icon tf-icons bx bx-support"></i>
<div class="text-truncate" data-i18n="Support">Support</div>
</a>
</li>
<li class="menu-item">
<a href="https://docs.bunkerweb.io/latest/?utm_campaign=self&utm_source=ui"
target="_blank"
class="menu-link">
<i class="menu-icon tf-icons bx bx-file"></i>
<div class="text-truncate" data-i18n="Documentation">Documentation</div>
</a>
</li>
</ul>
{% endwith %}
</aside>
<!-- / Menu -->

View file

@ -1,166 +1,127 @@
<!-- Navbar -->
<nav
class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar"
>
<div
class="layout-menu-toggle navbar-nav align-items-xl-center me-4 me-xl-0 d-xl-none"
>
<a class="nav-item nav-link px-0 me-xl-6" href="javascript:void(0)">
<i class="bx bx-menu bx-md"></i>
</a>
</div>
<div
class="navbar-collapse collapse navbar-nav-right d-flex align-items-center"
id="navbar-collapse"
>
<!-- Buttons -->
<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"
target="_blank"
rel="noopener"
>
<span class="tf-icons bx bx-file bx-18px me-md-2"></span>
<span class="d-none d-md-inline">Documentation</span>
<nav class="layout-navbar container-xxl navbar navbar-expand-xl navbar-detached align-items-center bg-navbar-theme"
id="layout-navbar">
<div class="layout-menu-toggle navbar-nav align-items-xl-center me-4 me-xl-0 d-xl-none">
<a class="nav-item nav-link px-0 me-xl-6" href="javascript:void(0)">
<i class="bx bx-menu bx-md"></i>
</a>
<a
role="button"
class="btn btn-outline-github p-1 p-md-2 ms-1 ms-md-2"
aria-pressed="true"
href="https://https://github.com/bunkerity/bunkerweb"
target="_blank"
rel="noopener"
>
<i class="tf-icons bx bxl-github bx-18px me-md-2"></i>
<span class="d-none d-md-inline">Github</span>
</a>
<a
role="button"
class="btn btn-outline-discord p-1 p-md-2 ms-1 ms-md-2"
aria-pressed="true"
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>
<span class="d-none d-md-inline">Discord</span>
</a>
</div>
</div>
<!-- /Buttons -->
<ul class="navbar-nav flex-row align-items-center ms-auto">
<!-- Stars -->
<li class="d-none d-md-inline nav-item lh-1 me-4">
<a
class="github-button"
href="https://github.com/bunkerity/bunkerweb"
data-icon="octicon-star"
data-size="large"
data-show-count="true"
aria-label="Star bunkerity/bunkerweb on GitHub"
>Stars</a
>
</li>
<!--/ Stars -->
<!-- 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"
aria-pressed="true"
href="https://github.com/bunkerity/bunkerweb/releases/latest"
target="_blank"
rel="noopener"
>
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"
>
<span class="visually-hidden">New version</span>
</span>
{% endif %}
</a>
</li>
<!--/ Version -->
<!-- User -->
<li class="nav-item navbar-dropdown dropdown-user dropdown">
<a
class="nav-link dropdown-toggle hide-arrow p-0 d-flex align-items-center"
href="javascript:void(0);"
data-bs-toggle="dropdown"
>
<div class="avatar avatar-online">
<img
src="img/square-blue.png"
alt="User Avatar"
class="w-px-40 h-auto rounded-circle"
/>
</div>
</a>
<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="img/square-blue.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
>
</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('profile') }}"
>
<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>
<div class="navbar-collapse collapse navbar-nav-right d-flex align-items-center"
id="navbar-collapse">
<!-- Buttons -->
<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"
target="_blank"
rel="noopener">
<span class="tf-icons bx bx-file bx-18px 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"
target="_blank"
rel="noopener">
<i class="tf-icons bx bxl-github bx-18px me-md-2"></i>
<span class="d-none d-md-inline">Github</span>
</a>
<a role="button"
class="btn btn-outline-discord p-1 p-md-2 ms-1 ms-md-2"
aria-pressed="true"
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>
<span class="d-none d-md-inline">Discord</span>
</a>
</div>
</div>
<!-- /Buttons -->
<ul class="navbar-nav flex-row align-items-center ms-auto">
<!-- Stars -->
<li class="d-none d-md-inline nav-item lh-1 me-4">
<a class="github-button"
href="https://github.com/bunkerity/bunkerweb"
data-icon="octicon-star"
data-size="large"
data-show-count="true"
aria-label="Star bunkerity/bunkerweb on GitHub">Stars</a>
</li>
<!--/ Stars -->
<!-- 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"
aria-pressed="true"
href="https://github.com/bunkerity/bunkerweb/releases/latest"
target="_blank"
rel="noopener">
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">
<span class="visually-hidden">New version</span>
</span>
{% endif %}
</a>
</li>
<!--/ Version -->
<!-- User -->
<li class="nav-item navbar-dropdown dropdown-user dropdown">
<a class="nav-link dropdown-toggle hide-arrow p-0 d-flex align-items-center"
href="javascript:void(0);"
data-bs-toggle="dropdown">
<div class="avatar avatar-online">
<img src="img/avatar_profil_BW.png"
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>
</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 %}
</li>
<!--/ User -->
</ul>
</li>
<!--/ User -->
</ul>
</div>
</div>
</nav>
<!-- / Navbar -->

File diff suppressed because it is too large Load diff

View file

@ -1,134 +1,100 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="side-offcanvas"
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 mb-1 mb-sm-0 me-3" 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>
<div class="offcanvas offcanvas-end"
tabindex="-1"
id="side-offcanvas"
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>
<li class="nav-item mb-1 mb-sm-0" 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>
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>
</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 -->
</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>
</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>

View file

@ -1,35 +1,31 @@
{% extends "base.html" %} {% block page %}
<!-- Content -->
<div class="container-xxl">
<div class="authentication-wrapper authentication-basic container-p-y">
<div class="authentication-inner">
<!-- Register -->
<div class="card px-sm-6 px-0">
<a href="{{ url_for('login') }}">
<i class="bx bx-arrow-back me-1"></i>
<span>back to login</span></a
>
<div class="card-body">
<!-- Logo -->
<div class="app-brand justify-content-center">
<a
href="https://www.bunkerweb.io/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="app-brand-link gap-2"
>
<span class="app-brand-logo login">
<svg
version="1.1"
width="25"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 272.68 76.37"
xml:space="preserve"
>
<style type="text/css" nonce="{{ style_nonce }}">
{% extends "base.html" %}
{% block page %}
<!-- Content -->
<div class="container-xxl">
<div class="authentication-wrapper authentication-basic container-p-y">
<div class="authentication-inner">
<!-- Register -->
<div class="card px-sm-6 px-0">
<a href="{{ url_for("login") }}">
<i class="bx bx-arrow-back me-1"></i>
<span>back to login</span></a>
<div class="card-body">
<!-- Logo -->
<div class="app-brand justify-content-center">
<a href="https://www.bunkerweb.io/?utm_campaign=self&utm_source=ui"
target="_blank"
rel="noopener"
class="app-brand-link gap-2">
<span class="app-brand-logo login">
<svg version="1.1"
width="25"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 272.68 76.37"
xml:space="preserve">
<style type="text/css" nonce="{{ style_nonce }}">
.st0 {
fill: #ffffff;
}
@ -39,162 +35,69 @@
.st2 {
fill: #0b5577;
}
</style>
<g>
<path
class="st2"
d="M244.87,44.46c0.16-1.27,1.3-2.12,2.59-1.95c1.01,0.13,1.85,0.96,1.92,2.05c0.14,1.52,0.26,2.71,0.36,3.85
h16.02c-0.24-1.79-0.49-3.61-0.82-6c-0.1-0.8,0.25-1.61,0.9-2.02c3.4-2.43,5.46-6.75,6.49-12.5c2.34-13.05-7.24-18.86-18.92-20.2
c-7.49-1-11.24-1.45-18.75-2.29c-1.35-0.15-2.51,0.72-2.65,2c-1.48,13.67-2.97,27.34-4.45,41.01h16.79
C244.51,47.22,244.66,46.05,244.87,44.46z M243.31,21.88c2.52,0.56,3.78,0.85,6.29,1.43c0.87-0.8,2.08-1.22,3.37-1.03
c2.21,0.32,3.75,2.31,3.43,4.42c-0.32,2.11-2.36,3.55-4.55,3.23c-1.27-0.19-2.31-0.93-2.91-1.94c-2.56-0.14-3.84-0.2-6.41-0.32
C242.84,25.36,242.99,24.2,243.31,21.88z"
/>
<g>
<path
class="st2"
d="M224.95,41c0.11-1.06-0.73-2.08-1.81-2.19c-6.15-0.63-9.23-0.91-15.38-1.43c-1.16-0.1-2.02-1.09-1.93-2.23
c0.01-0.08,0.01-0.13,0.02-0.21c0.09-1.06,1.11-1.91,2.27-1.81c4.44,0.37,6.67,0.57,11.11,1c1.09,0.11,2.12-0.72,2.24-1.85
c0.35-3.56,0.53-5.34,0.88-8.91c0.1-1.06-0.76-2.07-1.87-2.18c-4.51-0.44-6.76-0.64-11.28-1.02c-1.18-0.1-2.06-1.1-1.97-2.23
c0.09-1.06,1.12-1.9,2.31-1.8c6.31,0.53,9.47,0.82,15.77,1.46c1.11,0.11,2.17-0.7,2.28-1.84c0.38-3.62,0.57-5.43,0.94-9.04
c0.11-1.06-0.77-2.08-1.9-2.19c-11.16-1.06-22.29-1.98-33.47-2.77c-1.2-0.08-2.24,0.79-2.3,1.85
c-0.89,14.49-1.77,28.97-2.66,43.46c-0.03,0.5,0.13,0.96,0.4,1.33h35.57C224.45,45.8,224.63,44.03,224.95,41z"
/>
<g>
<path
class="st2"
d="M185.93,46.8c-2.49-8.91-3.77-13.36-6.42-22.25c-0.06-0.29-0.04-0.71,0.05-0.99
c3.19-8.01,4.82-12,8.14-19.98c0.44-1.04-0.33-2.15-1.53-2.22c-5.45-0.3-8.18-0.43-13.64-0.65c-0.75-0.03-1.45,0.44-1.7,1.07
c-2.18,5.75-3.25,8.62-5.37,14.38c-0.24,0.63-0.92,1.11-1.66,1.08c-0.96-0.03-1.68-0.76-1.65-1.68
c0.16-5.42,0.23-8.14,0.39-13.56c0.03-0.92-0.71-1.65-1.69-1.68c-5.61-0.15-8.41-0.2-14.02-0.26c-0.98-0.01-1.74,0.69-1.75,1.62
c-0.13,14.77-0.26,29.55-0.39,44.32c-0.01,0.92,0.7,1.64,1.63,1.65c5.31,0.06,7.96,0.11,13.26,0.25
c0.93,0.02,1.66-0.66,1.69-1.59c0.14-5.03,0.22-7.54,0.36-12.57c0.03-0.92,0.77-1.61,1.72-1.58c0.06,0,0.09,0,0.15,0
c0.73,0.02,1.29,0.47,1.56,1.12c1.82,5.55,2.72,8.32,4.48,13.88c0.26,0.65,0.81,1.1,1.53,1.13c1.23,0.05,2.31,0.1,3.32,0.14
h11.12C185.9,48,186.1,47.41,185.93,46.8z"
/>
<g>
<path
class="st2"
d="M109.16,48.06c1.64-0.06,2.95-1.44,2.9-3.08c-0.14-4.49-0.2-6.73-0.34-11.22
c-0.03-0.92,0.68-1.51,1.48-1.54c0.51-0.01,0.95,0.19,1.25,0.68c3.54,5.28,5.29,7.92,8.72,13.24c0.52,0.99,1.52,1.54,2.67,1.52
c4.62-0.06,6.93-0.08,11.55-0.08c1.64,0,3-1.34,3.01-2.98c0.05-13.87,0.1-27.75,0.16-41.62c0.01-1.63-1.42-2.99-3.16-2.99
c-4.46,0-6.7,0.01-11.16,0.07c-1.73,0.02-3.14,1.39-3.12,3.03c0.07,4.09,0.1,6.14,0.17,10.23c0.01,0.85-0.72,1.43-1.53,1.45
c-0.45,0.01-0.97-0.2-1.27-0.62c-3.6-4.96-5.42-7.43-9.12-12.35c-0.55-0.84-1.62-1.37-2.76-1.34
c-4.88,0.16-7.33,0.26-12.21,0.48c-1.73,0.08-3.1,1.5-3.01,3.13c0.69,13.86,1.39,27.71,2.08,41.57
c0.07,1.37,1.09,2.48,2.38,2.76h2.4C103.45,48.26,105.53,48.18,109.16,48.06z"
/>
<g>
<path
class="st2"
d="M90.68,31.54c-0.59-10.75-0.89-16.13-1.48-26.88c-0.1-1.84-1.76-3.25-3.72-3.13
C81.33,1.77,79.25,1.91,75.1,2.2c-1.96,0.14-3.43,1.74-3.29,3.59c0.83,11.11,1.25,16.66,2.08,27.76
c0.23,3.05-1.38,3.88-4.2,4.1c-2.82,0.23-4.61-0.33-4.87-3.37c-0.95-11.1-1.43-16.64-2.38-27.74
c-0.16-1.84-1.86-3.19-3.82-3.02c-4.12,0.37-6.18,0.57-10.29,0.98c-2.03,0.21-3.45,1.86-3.25,3.69
c1.14,10.82,1.72,16.23,2.86,27.05c0.61,6.46,3.39,10.67,7.45,13.16H81.6C87.6,45.46,91.41,40.09,90.68,31.54z"
/>
<g>
<path
class="st2"
d="M45.57,39.08c-0.36-3.32-1.02-6.96-4.2-8.75c-1.34-0.76-1.85-2.58-0.95-3.82
c1.9-2.86,1.84-6.21,1.48-9.46c-0.87-7.84-7.46-11.1-19.67-9.49c-7.79,1.03-11.68,1.58-19.45,2.79
c-1.79,0.28-3,1.84-2.74,3.52c1.82,11.52,3.65,23.04,5.47,34.55h37.72C45.2,45.98,45.98,42.89,45.57,39.08z M19.57,41.45
c-0.16-3.39-0.24-5.08-0.39-8.47c-1.42-0.79-2.47-2.16-2.7-3.84c-0.41-2.91,1.72-5.62,4.76-6.03
c3.04-0.41,5.81,1.63,6.19,4.55c0.22,1.69-0.43,3.28-1.59,4.42c0.76,3.31,1.13,4.96,1.88,8.27
C24.46,40.77,22.83,40.99,19.57,41.45z"
/>
</g>
</g>
<path
class="st2"
d="M100.25,48.41h-2.4c0.25,0.05,0.5,0.09,0.76,0.08C99.2,48.46,99.73,48.43,100.25,48.41z"
/>
</g>
</g>
</g>
</g>
<g>
<rect
x="89.49"
y="51.91"
class="st1"
width="93.69"
height="24.46"
/>
<g>
<g>
<path
class="st0"
d="M121.14,73.93l-1.91,0.03l-3.43-9.9l-3.34,9.93l-1.91-0.01l-3.25-14.33l-0.75,0c-0.37,0-0.63-0.07-0.79-0.22
s-0.24-0.34-0.24-0.57c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22l4.24-0.02c0.37,0,0.63,0.07,0.79,0.22
c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55s-0.42,0.22-0.79,0.22l-1.96,0.01l2.78,12.22l3.25-9.78l1.84-0.01
l3.42,9.76l2.58-12.24l-1.95,0.01c-0.37,0-0.63-0.07-0.8-0.22c-0.16-0.15-0.25-0.34-0.25-0.57c0-0.22,0.08-0.4,0.24-0.55
c0.16-0.15,0.43-0.22,0.81-0.22l4.22-0.02c0.38,0,0.65,0.07,0.81,0.22c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55
s-0.43,0.22-0.81,0.22l-0.73,0L121.14,73.93z"
/>
<path
class="st0"
d="M146.71,66.25l-15.72,0.06c0.28,1.99,1.12,3.6,2.52,4.81c1.41,1.21,3.14,1.82,5.21,1.81
c1.15,0,2.36-0.2,3.62-0.58c1.26-0.38,2.29-0.89,3.08-1.52c0.23-0.18,0.43-0.28,0.6-0.28c0.2,0,0.37,0.08,0.51,0.23
c0.15,0.15,0.22,0.33,0.22,0.54s-0.1,0.41-0.29,0.61c-0.59,0.61-1.63,1.19-3.12,1.73c-1.5,0.54-3.04,0.81-4.62,0.82
c-2.64,0.01-4.85-0.85-6.63-2.57c-1.78-1.72-2.67-3.82-2.68-6.28c-0.01-2.24,0.81-4.17,2.47-5.78s3.7-2.42,6.15-2.43
c2.52-0.01,4.6,0.81,6.23,2.45C145.91,61.5,146.72,63.63,146.71,66.25z M145.14,64.7c-0.31-1.7-1.12-3.08-2.43-4.14
c-1.31-1.06-2.86-1.59-4.66-1.58c-1.8,0.01-3.35,0.54-4.64,1.6s-2.1,2.45-2.41,4.18L145.14,64.7z"
/>
<path
class="st0"
d="M155.06,50.76l0.04,10.23c1.85-2.43,4.09-3.65,6.73-3.66c2.25-0.01,4.18,0.8,5.79,2.43
c1.61,1.63,2.42,3.63,2.43,6.01c0.01,2.4-0.8,4.43-2.41,6.11c-1.62,1.67-3.53,2.51-5.75,2.52c-2.69,0.01-4.94-1.19-6.75-3.61
l0.01,3.03l-3.62,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.33-0.24-0.55c0-0.23,0.08-0.42,0.24-0.56
c0.16-0.14,0.42-0.21,0.79-0.21l2.07-0.01l-0.07-19.94l-2.07,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.34-0.24-0.57
c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22L155.06,50.76z M168.5,65.84c-0.01-1.95-0.68-3.59-2.02-4.94
c-1.34-1.35-2.9-2.02-4.69-2.01s-3.35,0.69-4.67,2.05c-1.33,1.36-1.99,3.01-1.98,4.96c0.01,1.95,0.68,3.59,2.02,4.94
c1.34,1.35,2.9,2.02,4.69,2.01c1.79-0.01,3.35-0.69,4.67-2.05C167.85,69.44,168.51,67.79,168.5,65.84z"
/>
</g>
</g>
</g>
</svg>
</span>
</a>
</div>
<!-- /Logo -->
<p class="mb-6">Please enter your TOTP code</p>
<form id="formAuthentication" class="mb-6" method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input
type="hidden"
name="next"
value="{{ request.values.get('next', '') }}"
/>
<div class="mb-6 form-password-toggle">
<label class="form-label" for="totp_token">2FA Code</label>
<div class="input-group input-group-merge">
<input
type="password"
id="totp_token"
class="form-control"
name="totp_token"
placeholder="2FA Code"
aria-describedby="2FA Code"
required
/>
<span class="input-group-text cursor-pointer"
><i class="bx bx-hide"></i
></span>
</div>
</style>
<g>
<path class="st2" d="M244.87,44.46c0.16-1.27,1.3-2.12,2.59-1.95c1.01,0.13,1.85,0.96,1.92,2.05c0.14,1.52,0.26,2.71,0.36,3.85 h16.02c-0.24-1.79-0.49-3.61-0.82-6c-0.1-0.8,0.25-1.61,0.9-2.02c3.4-2.43,5.46-6.75,6.49-12.5c2.34-13.05-7.24-18.86-18.92-20.2 c-7.49-1-11.24-1.45-18.75-2.29c-1.35-0.15-2.51,0.72-2.65,2c-1.48,13.67-2.97,27.34-4.45,41.01h16.79 C244.51,47.22,244.66,46.05,244.87,44.46z M243.31,21.88c2.52,0.56,3.78,0.85,6.29,1.43c0.87-0.8,2.08-1.22,3.37-1.03 c2.21,0.32,3.75,2.31,3.43,4.42c-0.32,2.11-2.36,3.55-4.55,3.23c-1.27-0.19-2.31-0.93-2.91-1.94c-2.56-0.14-3.84-0.2-6.41-0.32 C242.84,25.36,242.99,24.2,243.31,21.88z" />
<g>
<path class="st2" d="M224.95,41c0.11-1.06-0.73-2.08-1.81-2.19c-6.15-0.63-9.23-0.91-15.38-1.43c-1.16-0.1-2.02-1.09-1.93-2.23 c0.01-0.08,0.01-0.13,0.02-0.21c0.09-1.06,1.11-1.91,2.27-1.81c4.44,0.37,6.67,0.57,11.11,1c1.09,0.11,2.12-0.72,2.24-1.85 c0.35-3.56,0.53-5.34,0.88-8.91c0.1-1.06-0.76-2.07-1.87-2.18c-4.51-0.44-6.76-0.64-11.28-1.02c-1.18-0.1-2.06-1.1-1.97-2.23 c0.09-1.06,1.12-1.9,2.31-1.8c6.31,0.53,9.47,0.82,15.77,1.46c1.11,0.11,2.17-0.7,2.28-1.84c0.38-3.62,0.57-5.43,0.94-9.04 c0.11-1.06-0.77-2.08-1.9-2.19c-11.16-1.06-22.29-1.98-33.47-2.77c-1.2-0.08-2.24,0.79-2.3,1.85 c-0.89,14.49-1.77,28.97-2.66,43.46c-0.03,0.5,0.13,0.96,0.4,1.33h35.57C224.45,45.8,224.63,44.03,224.95,41z" />
<g>
<path class="st2" d="M185.93,46.8c-2.49-8.91-3.77-13.36-6.42-22.25c-0.06-0.29-0.04-0.71,0.05-0.99 c3.19-8.01,4.82-12,8.14-19.98c0.44-1.04-0.33-2.15-1.53-2.22c-5.45-0.3-8.18-0.43-13.64-0.65c-0.75-0.03-1.45,0.44-1.7,1.07 c-2.18,5.75-3.25,8.62-5.37,14.38c-0.24,0.63-0.92,1.11-1.66,1.08c-0.96-0.03-1.68-0.76-1.65-1.68 c0.16-5.42,0.23-8.14,0.39-13.56c0.03-0.92-0.71-1.65-1.69-1.68c-5.61-0.15-8.41-0.2-14.02-0.26c-0.98-0.01-1.74,0.69-1.75,1.62 c-0.13,14.77-0.26,29.55-0.39,44.32c-0.01,0.92,0.7,1.64,1.63,1.65c5.31,0.06,7.96,0.11,13.26,0.25 c0.93,0.02,1.66-0.66,1.69-1.59c0.14-5.03,0.22-7.54,0.36-12.57c0.03-0.92,0.77-1.61,1.72-1.58c0.06,0,0.09,0,0.15,0 c0.73,0.02,1.29,0.47,1.56,1.12c1.82,5.55,2.72,8.32,4.48,13.88c0.26,0.65,0.81,1.1,1.53,1.13c1.23,0.05,2.31,0.1,3.32,0.14 h11.12C185.9,48,186.1,47.41,185.93,46.8z" />
<g>
<path class="st2" d="M109.16,48.06c1.64-0.06,2.95-1.44,2.9-3.08c-0.14-4.49-0.2-6.73-0.34-11.22 c-0.03-0.92,0.68-1.51,1.48-1.54c0.51-0.01,0.95,0.19,1.25,0.68c3.54,5.28,5.29,7.92,8.72,13.24c0.52,0.99,1.52,1.54,2.67,1.52 c4.62-0.06,6.93-0.08,11.55-0.08c1.64,0,3-1.34,3.01-2.98c0.05-13.87,0.1-27.75,0.16-41.62c0.01-1.63-1.42-2.99-3.16-2.99 c-4.46,0-6.7,0.01-11.16,0.07c-1.73,0.02-3.14,1.39-3.12,3.03c0.07,4.09,0.1,6.14,0.17,10.23c0.01,0.85-0.72,1.43-1.53,1.45 c-0.45,0.01-0.97-0.2-1.27-0.62c-3.6-4.96-5.42-7.43-9.12-12.35c-0.55-0.84-1.62-1.37-2.76-1.34 c-4.88,0.16-7.33,0.26-12.21,0.48c-1.73,0.08-3.1,1.5-3.01,3.13c0.69,13.86,1.39,27.71,2.08,41.57 c0.07,1.37,1.09,2.48,2.38,2.76h2.4C103.45,48.26,105.53,48.18,109.16,48.06z" />
<g>
<path class="st2" d="M90.68,31.54c-0.59-10.75-0.89-16.13-1.48-26.88c-0.1-1.84-1.76-3.25-3.72-3.13 C81.33,1.77,79.25,1.91,75.1,2.2c-1.96,0.14-3.43,1.74-3.29,3.59c0.83,11.11,1.25,16.66,2.08,27.76 c0.23,3.05-1.38,3.88-4.2,4.1c-2.82,0.23-4.61-0.33-4.87-3.37c-0.95-11.1-1.43-16.64-2.38-27.74 c-0.16-1.84-1.86-3.19-3.82-3.02c-4.12,0.37-6.18,0.57-10.29,0.98c-2.03,0.21-3.45,1.86-3.25,3.69 c1.14,10.82,1.72,16.23,2.86,27.05c0.61,6.46,3.39,10.67,7.45,13.16H81.6C87.6,45.46,91.41,40.09,90.68,31.54z" />
<g>
<path class="st2" d="M45.57,39.08c-0.36-3.32-1.02-6.96-4.2-8.75c-1.34-0.76-1.85-2.58-0.95-3.82 c1.9-2.86,1.84-6.21,1.48-9.46c-0.87-7.84-7.46-11.1-19.67-9.49c-7.79,1.03-11.68,1.58-19.45,2.79 c-1.79,0.28-3,1.84-2.74,3.52c1.82,11.52,3.65,23.04,5.47,34.55h37.72C45.2,45.98,45.98,42.89,45.57,39.08z M19.57,41.45 c-0.16-3.39-0.24-5.08-0.39-8.47c-1.42-0.79-2.47-2.16-2.7-3.84c-0.41-2.91,1.72-5.62,4.76-6.03 c3.04-0.41,5.81,1.63,6.19,4.55c0.22,1.69-0.43,3.28-1.59,4.42c0.76,3.31,1.13,4.96,1.88,8.27 C24.46,40.77,22.83,40.99,19.57,41.45z" />
</g>
</g>
<path class="st2" d="M100.25,48.41h-2.4c0.25,0.05,0.5,0.09,0.76,0.08C99.2,48.46,99.73,48.43,100.25,48.41z" />
</g>
</g>
</g>
</g>
<g>
<rect x="89.49" y="51.91" class="st1" width="93.69" height="24.46" />
<g>
<g>
<path class="st0" d="M121.14,73.93l-1.91,0.03l-3.43-9.9l-3.34,9.93l-1.91-0.01l-3.25-14.33l-0.75,0c-0.37,0-0.63-0.07-0.79-0.22 s-0.24-0.34-0.24-0.57c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22l4.24-0.02c0.37,0,0.63,0.07,0.79,0.22 c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55s-0.42,0.22-0.79,0.22l-1.96,0.01l2.78,12.22l3.25-9.78l1.84-0.01 l3.42,9.76l2.58-12.24l-1.95,0.01c-0.37,0-0.63-0.07-0.8-0.22c-0.16-0.15-0.25-0.34-0.25-0.57c0-0.22,0.08-0.4,0.24-0.55 c0.16-0.15,0.43-0.22,0.81-0.22l4.22-0.02c0.38,0,0.65,0.07,0.81,0.22c0.16,0.15,0.24,0.34,0.24,0.57c0,0.22-0.08,0.4-0.24,0.55 s-0.43,0.22-0.81,0.22l-0.73,0L121.14,73.93z" />
<path class="st0" d="M146.71,66.25l-15.72,0.06c0.28,1.99,1.12,3.6,2.52,4.81c1.41,1.21,3.14,1.82,5.21,1.81 c1.15,0,2.36-0.2,3.62-0.58c1.26-0.38,2.29-0.89,3.08-1.52c0.23-0.18,0.43-0.28,0.6-0.28c0.2,0,0.37,0.08,0.51,0.23 c0.15,0.15,0.22,0.33,0.22,0.54s-0.1,0.41-0.29,0.61c-0.59,0.61-1.63,1.19-3.12,1.73c-1.5,0.54-3.04,0.81-4.62,0.82 c-2.64,0.01-4.85-0.85-6.63-2.57c-1.78-1.72-2.67-3.82-2.68-6.28c-0.01-2.24,0.81-4.17,2.47-5.78s3.7-2.42,6.15-2.43 c2.52-0.01,4.6,0.81,6.23,2.45C145.91,61.5,146.72,63.63,146.71,66.25z M145.14,64.7c-0.31-1.7-1.12-3.08-2.43-4.14 c-1.31-1.06-2.86-1.59-4.66-1.58c-1.8,0.01-3.35,0.54-4.64,1.6s-2.1,2.45-2.41,4.18L145.14,64.7z" />
<path class="st0" d="M155.06,50.76l0.04,10.23c1.85-2.43,4.09-3.65,6.73-3.66c2.25-0.01,4.18,0.8,5.79,2.43 c1.61,1.63,2.42,3.63,2.43,6.01c0.01,2.4-0.8,4.43-2.41,6.11c-1.62,1.67-3.53,2.51-5.75,2.52c-2.69,0.01-4.94-1.19-6.75-3.61 l0.01,3.03l-3.62,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.33-0.24-0.55c0-0.23,0.08-0.42,0.24-0.56 c0.16-0.14,0.42-0.21,0.79-0.21l2.07-0.01l-0.07-19.94l-2.07,0.01c-0.37,0-0.63-0.07-0.79-0.22c-0.16-0.15-0.24-0.34-0.24-0.57 c0-0.22,0.08-0.4,0.24-0.55c0.16-0.15,0.42-0.22,0.79-0.22L155.06,50.76z M168.5,65.84c-0.01-1.95-0.68-3.59-2.02-4.94 c-1.34-1.35-2.9-2.02-4.69-2.01s-3.35,0.69-4.67,2.05c-1.33,1.36-1.99,3.01-1.98,4.96c0.01,1.95,0.68,3.59,2.02,4.94 c1.34,1.35,2.9,2.02,4.69,2.01c1.79-0.01,3.35-0.69,4.67-2.05C167.85,69.44,168.51,67.79,168.5,65.84z" />
</g>
</g>
</g>
</svg>
</span>
</a>
</div>
<!-- /Logo -->
<p class="mb-6">Please enter your TOTP code</p>
<form id="formAuthentication" class="mb-6" method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden"
name="next"
value="{{ request.values.get('next', '') }}" />
<div class="mb-6 form-password-toggle">
<label class="form-label" for="totp_token">2FA Code</label>
<div class="input-group input-group-merge">
<input type="password"
id="totp_token"
class="form-control"
name="totp_token"
placeholder="2FA Code"
aria-describedby="2FA Code"
required />
<span class="input-group-text cursor-pointer"><i class="bx bx-hide"></i></span>
</div>
</div>
<div class="mb-6">
<button class="btn btn-primary d-grid w-100" type="submit">Submit</button>
</div>
</form>
</div>
</div>
<!-- /Register -->
</div>
<div class="mb-6">
<button class="btn btn-primary d-grid w-100" type="submit">
Submit
</button>
</div>
</form>
</div>
</div>
<!-- /Register -->
</div>
</div>
</div>
<!-- / Content -->
<!-- / Content -->
{% endblock %}

View file

@ -200,7 +200,7 @@ def on_starting(server):
latest_version = latest_release["tag_name"].removeprefix("v")
TMP_DIR.joinpath("ui_data.json").write_text(
dumps({"LATEST_VERSION": latest_version, "LATEST_VERSION_LAST_CHECK": datetime.now().isoformat()}), encoding="utf-8"
dumps({"LATEST_VERSION": latest_version, "LATEST_VERSION_LAST_CHECK": datetime.now().astimezone().isoformat()}), encoding="utf-8"
)
LOGGER.info("UI is ready")

View file

@ -14,6 +14,7 @@ for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in ((
from datetime import datetime, timedelta
from flask import Flask, Response, flash, jsonify, make_response, redirect, render_template, request, session, url_for
from flask_executor import Executor
from flask_login import current_user, LoginManager, login_required, logout_user
from flask_principal import ActionNeed, identity_loaded, Permission, Principal, RoleNeed, TypeNeed, UserNeed
from flask_wtf.csrf import CSRFProtect, CSRFError
@ -104,6 +105,9 @@ with app.app_context():
csrf = CSRFProtect()
csrf.init_app(app)
# Executor
executor = Executor(app)
@app.context_processor
def inject_variables():
@ -148,42 +152,6 @@ def inject_variables():
)
@app.after_request
def set_security_headers(response):
"""Set the security headers."""
# * Content-Security-Policy header to prevent XSS attacks
response.headers["Content-Security-Policy"] = (
"object-src 'none';"
+ " frame-ancestors 'self';"
+ " default-src 'self' https://www.bunkerweb.io https://assets.bunkerity.com https://bunkerity.us1.list-manage.com https://api.github.com;"
+ f" script-src 'self' 'nonce-{app.config['SCRIPT_NONCE']}';"
+ " style-src 'self' 'unsafe-inline';"
+ " img-src 'self' data: https://assets.bunkerity.com;"
+ " font-src 'self' data:;"
+ " base-uri 'self';"
+ " block-all-mixed-content;"
+ (" connect-src *;" if request.path.startswith(("/check", "/setup")) else "")
)
if request.headers.get("X-Forwarded-Proto") == "https":
if not request.path.startswith("/setup/loading"):
response.headers["Content-Security-Policy"] += " upgrade-insecure-requests;"
# * Strict-Transport-Security header to force HTTPS if accessed via a reverse proxy
response.headers["Strict-Transport-Security"] = "max-age=63072000; includeSubDomains; preload"
# * X-Frames-Options header to prevent clickjacking
response.headers["X-Frame-Options"] = "DENY"
# * X-Content-Type-Options header to prevent MIME sniffing
response.headers["X-Content-Type-Options"] = "nosniff"
# * Referrer-Policy header to prevent leaking of sensitive data
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
return response
@login_manager.user_loader
def load_user(username):
ui_user = DB.get_ui_user(username=username)
@ -247,7 +215,57 @@ def handle_csrf_error(_):
flash("Wrong CSRF token !", "error")
if not current_user:
return render_template("setup.html"), 403
return render_template("login.html", is_totp=bool(current_user.totp_secret)), 403
return render_template("login.html"), 403
def update_latest_stable_release():
DATA["LATEST_VERSION"] = get_latest_stable_release()
def check_database_state():
DATA.load_from_file()
if (
DB.database_uri
and DB.readonly
and (datetime.now().astimezone() - datetime.fromisoformat(DATA.get("LAST_DATABASE_RETRY", "1970-01-01T00:00:00")).astimezone() > timedelta(minutes=1))
):
failed = False
try:
DB.retry_connection(pool_timeout=1)
DB.retry_connection(log=False)
LOGGER.info("The database is no longer read-only, defaulting to read-write mode")
except BaseException:
failed = True
try:
DB.retry_connection(readonly=True, pool_timeout=1)
DB.retry_connection(readonly=True, log=False)
except BaseException:
if DB.database_uri_readonly:
with suppress(BaseException):
DB.retry_connection(fallback=True, pool_timeout=1)
DB.retry_connection(fallback=True, log=False)
DATA.update(
{
"READONLY_MODE": failed,
"LAST_DATABASE_RETRY": DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now().astimezone().isoformat(),
}
)
elif not DATA.get("READONLY_MODE", False) and request.method == "POST" and not ("/totp" in request.path or "/login" in request.path):
try:
DB.test_write()
DATA["READONLY_MODE"] = False
except BaseException:
DATA.update(
{
"READONLY_MODE": True,
"LAST_DATABASE_RETRY": DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now().astimezone().isoformat(),
}
)
else:
try:
DB.test_read()
except BaseException:
DATA["LAST_DATABASE_RETRY"] = DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now().astimezone().isoformat()
@app.before_request
@ -262,57 +280,10 @@ def before_request():
if not request.path.startswith(("/css", "/img", "/js", "/fonts", "/libs")):
if datetime.now().astimezone() - datetime.fromisoformat(DATA.get("LATEST_VERSION_LAST_CHECK", "1970-01-01T00:00:00")).astimezone() > timedelta(hours=1):
DATA.update(
{
"LATEST_VERSION": get_latest_stable_release(),
"LATEST_VERSION_LAST_CHECK": datetime.now().isoformat(),
}
)
DATA["LATEST_VERSION_LAST_CHECK"] = datetime.now().astimezone().isoformat()
executor.submit(update_latest_stable_release)
if (
DB.database_uri
and DB.readonly
and (
datetime.now().astimezone() - datetime.fromisoformat(DATA.get("LAST_DATABASE_RETRY", "1970-01-01T00:00:00")).astimezone() > timedelta(minutes=1)
)
):
failed = False
try:
DB.retry_connection(pool_timeout=1)
DB.retry_connection(log=False)
LOGGER.info("The database is no longer read-only, defaulting to read-write mode")
except BaseException:
failed = True
try:
DB.retry_connection(readonly=True, pool_timeout=1)
DB.retry_connection(readonly=True, log=False)
except BaseException:
if DB.database_uri_readonly:
with suppress(BaseException):
DB.retry_connection(fallback=True, pool_timeout=1)
DB.retry_connection(fallback=True, log=False)
DATA.update(
{
"READONLY_MODE": failed,
"LAST_DATABASE_RETRY": DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now().isoformat(),
}
)
elif not DATA.get("READONLY_MODE", False) and request.method == "POST" and not ("/totp" in request.path or "/login" in request.path):
try:
DB.test_write()
DATA["READONLY_MODE"] = False
except BaseException:
DATA.update(
{
"READONLY_MODE": True,
"LAST_DATABASE_RETRY": DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now().isoformat(),
}
)
else:
try:
DB.test_read()
except BaseException:
DATA["LAST_DATABASE_RETRY"] = DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now().isoformat()
executor.submit(check_database_state)
DB.readonly = DATA.get("READONLY_MODE", False)
@ -321,27 +292,67 @@ def before_request():
if current_user.is_authenticated:
passed = True
last_user_session = DB.get_ui_user_last_session(username=current_user.get_id())
# Case not login page, keep on 2FA before any other access
if not session.get("totp_validated", False) and bool(current_user.totp_secret) and "/totp" not in request.path:
if not request.path.endswith("/login"):
return redirect(url_for("totp.totp_page", next=request.form.get("next")))
passed = False
elif last_user_session and last_user_session.ip != request.remote_addr:
elif session["ip"] != request.remote_addr:
LOGGER.warning(f"User {current_user.get_id()} tried to access his session with a different IP address.")
passed = False
elif last_user_session and last_user_session.user_agent != request.headers.get("User-Agent"):
elif session["user_agent"] != request.headers.get("User-Agent"):
LOGGER.warning(f"User {current_user.get_id()} tried to access his session with a different User-Agent.")
passed = False
if not passed:
return logout_page()
if "session_id" in session:
ret = DB.mark_ui_user_access(session["session_id"], datetime.now().astimezone())
if isinstance(ret, str):
LOGGER.error(f"Couldn't mark the user access: {ret}")
def mark_user_access(session_id):
ret = DB.mark_ui_user_access(session_id, datetime.now().astimezone())
if ret:
LOGGER.error(f"Couldn't mark the user access: {ret}")
LOGGER.debug(f"Marked the user access for session {session_id}")
@app.after_request
def set_security_headers(response):
"""Set the security headers."""
# * Content-Security-Policy header to prevent XSS attacks
response.headers["Content-Security-Policy"] = (
"object-src 'none';"
+ " frame-ancestors 'self';"
+ " default-src 'self' https://www.bunkerweb.io https://assets.bunkerity.com https://bunkerity.us1.list-manage.com https://api.github.com;"
+ f" script-src 'self' 'nonce-{app.config['SCRIPT_NONCE']}';"
+ " style-src 'self' 'unsafe-inline';"
+ " img-src 'self' data: https://assets.bunkerity.com;"
+ " font-src 'self' data:;"
+ " base-uri 'self';"
+ " block-all-mixed-content;"
+ (" connect-src *;" if request.path.startswith(("/check", "/setup")) else "")
)
if request.headers.get("X-Forwarded-Proto") == "https":
if not request.path.startswith("/setup/loading"):
response.headers["Content-Security-Policy"] += " upgrade-insecure-requests;"
# * Strict-Transport-Security header to force HTTPS if accessed via a reverse proxy
response.headers["Strict-Transport-Security"] = "max-age=63072000; includeSubDomains; preload"
# * X-Frames-Options header to prevent clickjacking
response.headers["X-Frame-Options"] = "DENY"
# * X-Content-Type-Options header to prevent MIME sniffing
response.headers["X-Content-Type-Options"] = "nosniff"
# * Referrer-Policy header to prevent leaking of sensitive data
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
if current_user.is_authenticated and "session_id" in session:
executor.submit(mark_user_access, session["session_id"])
return response
### * MISC ROUTES * ###
@ -386,7 +397,7 @@ def check_reloading():
flash(f["content"])
if "flash_messages" in session:
session["flash_messages"].append((f["content"], f["type"], datetime.now().strftime("%Y-%m-%d %H:%M:%S %Z")))
session["flash_messages"].append((f["content"], f["type"], datetime.now().astimezone().strftime("%Y-%m-%d %H:%M:%S %Z")))
DATA["TO_FLASH"] = []

View file

@ -1,6 +1,7 @@
bcrypt==4.2.0
beautifulsoup4==4.12.3
Flask==3.0.3
Flask-executor==1.0.0
Flask-Login==0.6.3
Flask-Principal==0.4.0
Flask-WTF==1.2.1

View file

@ -52,9 +52,14 @@ flask==3.0.3 \
--hash=sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842
# via
# -r requirements.in
# flask-executor
# flask-login
# flask-principal
# flask-wtf
flask-executor==1.0.0 \
--hash=sha256:4bc113def5d9f1c7ff272ff7ba09f843468eab80469bdbd21625a111b2c8ae7b \
--hash=sha256:c044dc6393326a83351e69e4ddf3686270abffeac2b599260cebf0ea5d1e93de
# via -r requirements.in
flask-login==0.6.3 \
--hash=sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333 \
--hash=sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d