mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Optimize and made some tweaks for QOL reasons in web UI
This commit is contained in:
parent
5529f312c1
commit
3a6caf1644
15 changed files with 206 additions and 138 deletions
|
|
@ -46,7 +46,7 @@ def login_page():
|
|||
if not ui_user.totp_secret:
|
||||
flash(
|
||||
f'Please enable two-factor authentication to secure your account <a href="{url_for("profile.profile_page", _anchor="security")}">here</a>',
|
||||
"error",
|
||||
"warning",
|
||||
)
|
||||
|
||||
# redirect him to the page he originally wanted or to the home page
|
||||
|
|
|
|||
|
|
@ -13,25 +13,6 @@ from app.routes.utils import cors_required, 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",
|
||||
}
|
||||
|
||||
|
||||
def get_last_sessions(page: int, per_page: int) -> Tuple[Generator[Dict[str, Union[str, bool]], None, None], int]:
|
||||
db_sessions = DB.get_ui_user_sessions(current_user.username, session.get("session_id"))
|
||||
|
|
@ -53,7 +34,7 @@ def get_last_sessions(page: int, per_page: int) -> Tuple[Generator[Dict[str, Uni
|
|||
|
||||
for db_session in additional_sessions + db_sessions[(page - 1) * per_page : page * per_page]: # noqa: E203
|
||||
ua_data = parse(db_session["user_agent"])
|
||||
last_session = {
|
||||
yield {
|
||||
"current": db_session["id"] == session.get("session_id") if "session_id" in session else "id" not in db_session,
|
||||
"browser": ua_data.get_browser(),
|
||||
"os": ua_data.get_os(),
|
||||
|
|
@ -67,20 +48,6 @@ def get_last_sessions(page: int, per_page: int) -> Tuple[Generator[Dict[str, Uni
|
|||
),
|
||||
}
|
||||
|
||||
for browser, icon in BROWSERS.items():
|
||||
if browser in last_session["browser"]:
|
||||
last_session["browser"] = f"<i class='bx {icon} text-primary'></i> {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> {last_session['os']}"
|
||||
break
|
||||
|
||||
last_session["device"] = f"<i class='bx {DEVICES.get(last_session['device'], 'bx-mobile')} text-primary'></i> {last_session['device']}"
|
||||
|
||||
yield last_session
|
||||
|
||||
return session_generator(page, per_page), total_sessions
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -768,3 +768,8 @@ a.courier-prime:hover {
|
|||
.courier-prime {
|
||||
font-family: var(--font-courier-prime);
|
||||
}
|
||||
|
||||
.warning-tooltip {
|
||||
--bs-tooltip-bg: var(--bs-warning);
|
||||
--bs-tooltip-color: var(--bs-white);
|
||||
}
|
||||
|
|
|
|||
48
src/ui/app/static/img/diamond-white.svg
Normal file
48
src/ui/app/static/img/diamond-white.svg
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 45.6 39.3">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #eaeaea;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #efefef;
|
||||
}
|
||||
|
||||
.cls-3 {
|
||||
fill: #e5e5e5;
|
||||
}
|
||||
|
||||
.cls-4 {
|
||||
fill: #fff;
|
||||
}
|
||||
|
||||
.cls-5 {
|
||||
fill: #fafafa;
|
||||
}
|
||||
|
||||
.cls-6 {
|
||||
fill: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<!-- Generator: Adobe Illustrator 28.7.1, SVG Export Plug-In . SVG Version: 1.2.0 Build 142) -->
|
||||
<g>
|
||||
<g id="Calque_2">
|
||||
<g id="Calque_2-2">
|
||||
<g id="Calque_1-2">
|
||||
<g>
|
||||
<polygon class="cls-5" points="40.4 5.2 31.4 10.4 14.1 10.4 5.2 5.2 13.9 0 31.6 0 40.4 5.2"/>
|
||||
<polygon class="cls-3" points="45.6 12.8 34.9 19.4 31.4 10.4 40.4 5.2 45.6 12.8"/>
|
||||
<polygon class="cls-1" points="45.6 12.8 22.7 39.3 34.9 19.4 45.6 12.8"/>
|
||||
<polygon class="cls-2" points="14.1 10.4 10.3 19.4 0 12.8 5.2 5.2 14.1 10.4"/>
|
||||
<polygon class="cls-1" points="22.7 39.3 0 12.8 10.3 19.4 22.7 39.3"/>
|
||||
<polygon class="cls-6" points="34.9 19.4 22.7 39.3 10.3 19.4 34.9 19.4"/>
|
||||
<polygon class="cls-4" points="34.9 19.4 10.3 19.4 14.1 10.4 31.4 10.4 34.9 19.4"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -111,6 +111,7 @@ $(document).ready(function () {
|
|||
).first();
|
||||
$("#select-type")
|
||||
.parent()
|
||||
.attr("data-bs-custom-class", "warning-tooltip")
|
||||
.attr(
|
||||
"data-bs-original-title",
|
||||
`Switched to ${firstMultisiteType
|
||||
|
|
|
|||
|
|
@ -431,6 +431,7 @@ $(function () {
|
|||
chart: {
|
||||
type: "bar",
|
||||
width: "100%",
|
||||
height: 400,
|
||||
},
|
||||
title: {
|
||||
text: "Blocked Requests per Hour",
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ $(document).ready(() => {
|
|||
|
||||
$currentStepContainer.find(".plugin-setting").each(function () {
|
||||
const $input = $(this);
|
||||
const value = $input.val().trim();
|
||||
const value = $input.val();
|
||||
const isRequired = $input.prop("required");
|
||||
const pattern = $input.attr("pattern");
|
||||
const fieldName =
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ $(document).ready(() => {
|
|||
}
|
||||
|
||||
let currentTemplate = $("#selected-template").val();
|
||||
let currentTemplateMethod = $("#selected-template-method").val();
|
||||
let currentMode = $("#selected-mode").val();
|
||||
let currentType = $("#selected-type").val();
|
||||
|
||||
|
|
@ -159,7 +158,7 @@ $(document).ready(() => {
|
|||
|
||||
currentStepContainer.find(".plugin-setting").each(function () {
|
||||
const $input = $(this);
|
||||
const value = $input.val().trim();
|
||||
const value = $input.val();
|
||||
const isRequired = $input.prop("required");
|
||||
const pattern = $input.attr("pattern");
|
||||
const fieldName =
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class News {
|
|||
if (lastNews) {
|
||||
this.render(JSON.parse(lastNews));
|
||||
} else {
|
||||
$.getJSON("https://www.bunkerweb.io/api/posts/0/2")
|
||||
$.getJSON("https://www.bunkerweb.io/api/posts/0/3")
|
||||
.done((res) => {
|
||||
const reverseData = res.data.reverse();
|
||||
this.render(reverseData);
|
||||
|
|
@ -271,30 +271,41 @@ $(document).ready(() => {
|
|||
const intervalTime = 7000;
|
||||
let interval;
|
||||
const $bannerText = $("#banner-text");
|
||||
// Create a hidden element to measure the max height
|
||||
const $measuringElement = $("<div>")
|
||||
.css({
|
||||
position: "absolute",
|
||||
visibility: "hidden",
|
||||
height: "auto",
|
||||
width: $bannerText.width(),
|
||||
whiteSpace: "nowrap",
|
||||
})
|
||||
.appendTo("body");
|
||||
|
||||
// Calculate the minimum height required for the banner text
|
||||
let minHeight = 0;
|
||||
newsItems.forEach((item) => {
|
||||
$measuringElement.html(item);
|
||||
minHeight = Math.max(minHeight, $measuringElement.outerHeight());
|
||||
function calculateMinHeight() {
|
||||
// Create a hidden element to measure the max height
|
||||
const $measuringElement = $("<div>")
|
||||
.css({
|
||||
position: "absolute",
|
||||
visibility: "hidden",
|
||||
height: "auto",
|
||||
width: $bannerText.width(), // Set width to match the current width of the banner text container
|
||||
whiteSpace: "normal", // Change to normal for wrapping if needed
|
||||
})
|
||||
.appendTo("body");
|
||||
|
||||
// Calculate the minimum height required for the banner text
|
||||
let minHeight = 0;
|
||||
newsItems.forEach((item) => {
|
||||
$measuringElement.html(item);
|
||||
minHeight = Math.max(minHeight, $measuringElement.outerHeight());
|
||||
});
|
||||
|
||||
// Set the minimum height to avoid layout shifts
|
||||
$bannerText.css("min-height", minHeight);
|
||||
|
||||
// Remove the measuring element from the DOM
|
||||
$measuringElement.remove();
|
||||
}
|
||||
|
||||
// Calculate the min-height on page load
|
||||
calculateMinHeight();
|
||||
|
||||
// Recalculate the min-height on window resize
|
||||
$(window).on("resize", function () {
|
||||
calculateMinHeight();
|
||||
});
|
||||
|
||||
// Set the minimum height to avoid layout shifts
|
||||
$bannerText.css("min-height", minHeight);
|
||||
|
||||
// Remove the measuring element from the DOM
|
||||
$measuringElement.remove();
|
||||
|
||||
const news = new News();
|
||||
news.init();
|
||||
|
||||
|
|
@ -380,4 +391,35 @@ $(document).ready(() => {
|
|||
loadData();
|
||||
$("#next-news").trigger("click");
|
||||
}
|
||||
|
||||
var notificationsRead =
|
||||
parseInt(sessionStorage.getItem("notificationsRead")) || 0;
|
||||
|
||||
function updateNotificationsBadge() {
|
||||
const $notificationsBadge = $("#unread-notifications");
|
||||
let unreadNotifications = parseInt($notificationsBadge.text());
|
||||
|
||||
unreadNotifications = isNaN(unreadNotifications) ? 0 : unreadNotifications;
|
||||
|
||||
if ($notificationsBadge.length) {
|
||||
const updatedUnread = unreadNotifications - notificationsRead;
|
||||
if (updatedUnread > 0) {
|
||||
$notificationsBadge.text(updatedUnread);
|
||||
$notificationsBadge.removeClass("d-none");
|
||||
} else {
|
||||
$notificationsBadge.addClass("d-none");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateNotificationsBadge();
|
||||
|
||||
$("#notifications-button").on("click", function () {
|
||||
const readNotifications = $(
|
||||
"#notifications-toast-container .bs-toast",
|
||||
).length;
|
||||
notificationsRead = readNotifications;
|
||||
sessionStorage.setItem("notificationsRead", notificationsRead);
|
||||
updateNotificationsBadge();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -141,7 +141,8 @@
|
|||
<span class="d-none d-md-inline">Notifications</span>
|
||||
</button>
|
||||
{% if flash_messages %}
|
||||
<span class="badge-dot-text position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
|
||||
<span id="unread-notifications"
|
||||
class="badge-dot-text position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger d-none">
|
||||
{{ flash_messages|length }}
|
||||
<span class="visually-hidden">unread notifications</span>
|
||||
</span>
|
||||
|
|
@ -174,7 +175,7 @@
|
|||
aria-pressed="true"
|
||||
href="{{ url_for('pro') }}">
|
||||
<span class="me-1 me-md-2 d-flex h-100 justify-content-center align-items-center">
|
||||
<img src="{{ pro_diamond_url }}"
|
||||
<img src="{{ url_for('static', filename='img/diamond-white.svg') }}"
|
||||
alt="Pro plugin"
|
||||
width="18px"
|
||||
height="15.5px">
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@
|
|||
{% 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 %}"
|
||||
<div class="bs-toast toast fade show bg-white border{% if category == 'error' %} border-danger{% elif category == 'warning' %} border-warning{% else %} border-primary{% 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>
|
||||
<div class="toast-header d-flex align-items-center{% if category == 'error' %} text-danger{% elif category == 'warning' %} text-warning{% else %} text-primary{% endif %}">
|
||||
<i class="d-block h-auto rounded tf-icons bx bx-xs bx-bell bx-tada me-2"></i>
|
||||
<span class="fw-medium me-auto">
|
||||
{% if category != 'message' %}
|
||||
{{ category|capitalize }}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
{% block content %}
|
||||
<!-- Content -->
|
||||
<div class="row g-4">
|
||||
<div class="col-sm-3 mb-2">
|
||||
<div class="col-md-3 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3 h-100 text-white {% if is_pro_version %}bg-primary{% else %}bg-bw-green{% endif %}">
|
||||
{% if is_pro_version %}
|
||||
<img class='position-absolute top-0 end-0 m-3'
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 mb-2">
|
||||
<div class="col-md-3 mb-2">
|
||||
<a role="button"
|
||||
href="{{ url_for('instances') }}"
|
||||
class="card p-4 position-relative shadow-sm rounded-3 h-100">
|
||||
|
|
@ -59,7 +59,7 @@
|
|||
</small>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-3 mb-2">
|
||||
<div class="col-md-3 mb-2">
|
||||
<a role="button"
|
||||
href="{{ url_for('services') }}"
|
||||
class="card p-4 position-relative shadow-sm rounded-3 h-100">
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
</small>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-3 mb-2">
|
||||
<div class="col-md-3 mb-2">
|
||||
<a role="button"
|
||||
href="{{ url_for('plugins') }}"
|
||||
class="card p-4 position-relative shadow-sm rounded-3 h-100">
|
||||
|
|
@ -111,21 +111,63 @@
|
|||
</small>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-7 mt-2 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3 h-100">
|
||||
<div class="card-header p-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="me-2 don-jose mb-0">Blocked Requests countries</h5>
|
||||
<div class="col-md-7 row g-4 m-0 p-0">
|
||||
<div class="col-12 col-lg-6 mt-2 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3 h-100">
|
||||
<div class="card-header p-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="mb-1 me-2 don-jose">Request status</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-center align-items-center">
|
||||
<div id="requests-data" class="visually-hidden">{{ request_errors|tojson }}</div>
|
||||
<div id="requests-stats"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="requests-map-data" class="visually-hidden">{{ request_countries|tojson }}</div>
|
||||
<div id="requests-map" class="rounded h-100"></div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 mt-2 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3 h-100">
|
||||
<div class="card-header p-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="mb-1 me-2 don-jose">Top 10 - Blocked ips</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if request_ips %}
|
||||
{% set limited_request_ips = request_ips.items() | list %}
|
||||
{% set top_ips = limited_request_ips[:10] %}
|
||||
<div class="d-flex justify-content-center align-items-center mb-6">
|
||||
<div id="requests-ips-data" class="visually-hidden">{{ request_ips|tojson }}</div>
|
||||
<div id="requests-ips"></div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="d-flex align-items-center justify-content-center h-100">
|
||||
<div class="text-center mt-2">
|
||||
<p class="g-3 p-2 text-primary rounded-lg fw-bold">No data to show</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 mt-2 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3 h-100">
|
||||
<div class="card-header p-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="me-2 don-jose mb-0">Blocked Requests countries</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="requests-map-data" class="visually-hidden">{{ request_countries|tojson }}</div>
|
||||
<div id="requests-map" class="rounded h-100"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5 mt-2 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3">
|
||||
<div class="col-md-5 mt-2 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3 h-100">
|
||||
<div class="card-header p-2">
|
||||
<div class="card-title mb-0 d-flex justify-content-between">
|
||||
<h5 class="don-jose mb-0">News</h5>
|
||||
|
|
@ -155,7 +197,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 mb-2">
|
||||
<div class="col-md-6 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3 bg-secondary text-white h-100">
|
||||
<i class='bx bx-broadcast bx-sm position-absolute top-0 end-0 m-3 text-white'></i>
|
||||
<p class="ps-4 fs-4 mb-2">
|
||||
|
|
@ -165,7 +207,7 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 mb-2">
|
||||
<div class="col-md-3 mb-2">
|
||||
<a role="button"
|
||||
class="card p-4 position-relative shadow-sm rounded-3 h-100 text-color-hover-danger"
|
||||
href="{{ url_for('loading', next=url_for('reports') ) }}">
|
||||
|
|
@ -185,7 +227,7 @@
|
|||
</p>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-sm-3 mb-2">
|
||||
<div class="col-md-3 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3 h-100">
|
||||
<i class='bx bx-globe bx-sm position-absolute top-0 end-0 m-3 text-danger'></i>
|
||||
<p class="ps-4 fs-4 mb-2">
|
||||
|
|
@ -201,47 +243,7 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 mt-2 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3">
|
||||
<div class="card-header p-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="mb-1 me-2 don-jose">Request status</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-center align-items-center mb-6">
|
||||
<div id="requests-data" class="visually-hidden">{{ request_errors|tojson }}</div>
|
||||
<div id="requests-stats"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 mt-2 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3">
|
||||
<div class="card-header p-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="mb-1 me-2 don-jose">Blocked Request ips</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if request_ips %}
|
||||
{% set limited_request_ips = request_ips.items() | list %}
|
||||
{% set top_ips = limited_request_ips[:10] %}
|
||||
<div class="d-flex justify-content-center align-items-center mb-6">
|
||||
<div id="requests-ips-data" class="visually-hidden">{{ request_ips|tojson }}</div>
|
||||
<div id="requests-ips"></div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="d-flex align-items-center justify-content-center">
|
||||
<div class="text-center mt-2">
|
||||
<p class="g-3 p-2 text-primary rounded-lg fw-bold">No data to show</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 mt-2 mb-2">
|
||||
<div class="col-12 mt-2 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3">
|
||||
<div class="card-header p-2">
|
||||
<div class="card-title mb-0">
|
||||
|
|
|
|||
|
|
@ -66,16 +66,18 @@
|
|||
<td class="service-last-update-date">{{ service['last_update'].astimezone().isoformat() }}</td>
|
||||
<td>
|
||||
<div class="d-flex justify-content-center">
|
||||
<a role="button"
|
||||
class="btn btn-outline-primary btn-sm me-1"
|
||||
href="https://{{ service['id'] }}"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
data-bs-original-title="Access service {{ service['id'] }}"
|
||||
target="_blank"
|
||||
rel="noreferrer">
|
||||
<i class="bx bx-link-external bx-xs"></i>
|
||||
</a>
|
||||
<div {% if service['is_draft'] %}data-bs-toggle="tooltip" data-bs-placement="bottom" data-bs-original-title="Disabled by draft mode"{% endif %}>
|
||||
<a role="button"
|
||||
class="btn btn-outline-primary btn-sm me-1{% if service['is_draft'] %} disabled{% endif %}"
|
||||
href="https://{{ service['id'] }}"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="bottom"
|
||||
data-bs-original-title="Access service {{ service['id'] }}"
|
||||
target="_blank"
|
||||
rel="noreferrer">
|
||||
<i class="bx bx-link-external bx-xs"></i>
|
||||
</a>
|
||||
</div>
|
||||
<a role="button"
|
||||
class="btn btn-primary btn-sm me-1"
|
||||
href="{{ url_for("services") }}/{{ service['id'] }}"
|
||||
|
|
|
|||
|
|
@ -44,15 +44,15 @@
|
|||
<div id="data-notifications-container"
|
||||
class="offcanvas-body row justify-content-center ps-3 pe-3{% if flash_messages %} pt-0{% endif %}">
|
||||
{% if flash_messages %}
|
||||
<div id="feedback-toast-container"
|
||||
<div id="notifications-toast-container"
|
||||
class="toast-container position-relative g-3 pt-6">
|
||||
{% for message in flash_messages|reverse %}
|
||||
{% set content = message[0] %}
|
||||
{% set category = message[1] %}
|
||||
{% set datetime = message[2] %}
|
||||
<div class="col-12 bs-toast toast show border{% if category == 'error' %} border-danger{% elif category == 'warning' %} border-warning{% else %} border-primary{% endif %}">
|
||||
<div class="toast-header{% if category == 'error' %} text-danger{% elif category == 'warning' %} text-warning{% else %} text-primary{% endif %}">
|
||||
<i class="d-block w-px-20 h-auto rounded me-2 tf-icons bx bx-bell"></i>
|
||||
<div class="col-12 bs-toast toast show bw-white border{% if category == 'error' %} border-danger{% elif category == 'warning' %} border-warning{% else %} border-primary{% endif %}">
|
||||
<div class="toast-header d-flex align-items-center{% if category == 'error' %} text-danger{% elif category == 'warning' %} text-warning{% else %} text-primary{% endif %}">
|
||||
<i class="d-block h-auto rounded tf-icons bx bx-xs bx-bell me-2"></i>
|
||||
<span class="fw-medium me-auto">
|
||||
{% if category != 'message' %}
|
||||
{{ category|capitalize }}
|
||||
|
|
|
|||
|
|
@ -370,7 +370,7 @@ def set_security_headers(response):
|
|||
+ (
|
||||
" connect-src *;"
|
||||
if request.path.startswith(("/check", "/setup"))
|
||||
else " connect-src 'self' https://api.github.com/repos/bunkerity/bunkerweb https://www.bunkerweb.io/api/posts/0/2;"
|
||||
else " connect-src 'self' https://api.github.com/repos/bunkerity/bunkerweb https://www.bunkerweb.io/api/posts/0/3;"
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue