mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Refactor datatable filtering options in web UI
This commit is contained in:
parent
756af7becb
commit
c73e9bf161
18 changed files with 304 additions and 28 deletions
|
|
@ -457,6 +457,12 @@ def upload_plugin():
|
|||
return {"status": "ok"}, 201
|
||||
|
||||
|
||||
@plugins.route("/plugins/<string:plugin>", methods=["GET"])
|
||||
@login_required
|
||||
def custom_plugin_page(plugin: str):
|
||||
return render_template("plugin_page.html")
|
||||
|
||||
|
||||
# @plugins.route("/plugins/<plugin>", methods=["GET", "POST"])
|
||||
# @login_required
|
||||
# def custom_plugin(plugin: str):
|
||||
|
|
|
|||
|
|
@ -1,11 +1,88 @@
|
|||
from flask import Blueprint, render_template
|
||||
from datetime import datetime
|
||||
from threading import Thread
|
||||
from time import time
|
||||
from typing import Dict
|
||||
from flask import Blueprint, redirect, render_template, request, url_for
|
||||
from flask_login import login_required
|
||||
|
||||
from app.dependencies import BW_CONFIG, DATA, DB
|
||||
from app.routes.utils import get_remain, handle_error, manage_bunkerweb, verify_data_in_form, wait_applying
|
||||
from app.utils import LOGGER, flash
|
||||
|
||||
|
||||
pro = Blueprint("pro", __name__)
|
||||
|
||||
|
||||
@pro.route("/pro")
|
||||
@pro.route("/pro", methods=["GET"])
|
||||
@login_required
|
||||
def pro_page():
|
||||
return render_template("pro.html")
|
||||
online_services = 0
|
||||
draft_services = 0
|
||||
for service in DB.get_services(with_drafts=True):
|
||||
if service["is_draft"]:
|
||||
draft_services += 1
|
||||
continue
|
||||
online_services += 1
|
||||
|
||||
metadata = DB.get_metadata()
|
||||
current_day = datetime.now().astimezone().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
pro_expires_in = "Unknown"
|
||||
if metadata["pro_expire"]:
|
||||
exp = (metadata["pro_expire"].astimezone() - current_day).total_seconds()
|
||||
remain = ("Unknown", "Unknown") if exp <= 0 else get_remain(exp)
|
||||
pro_expires_in = remain[0]
|
||||
|
||||
return render_template(
|
||||
"pro.html",
|
||||
online_services=online_services,
|
||||
draft_services=draft_services,
|
||||
pro_expires_in=pro_expires_in,
|
||||
)
|
||||
|
||||
|
||||
@pro.route("/pro/key", methods=["POST"])
|
||||
@login_required
|
||||
def pro_key():
|
||||
if DB.readonly:
|
||||
return handle_error("Database is in read-only mode", "pro")
|
||||
|
||||
verify_data_in_form(
|
||||
data={"PRO_LICENSE_KEY": None},
|
||||
err_message="Missing license key parameter on /pro/key.",
|
||||
redirect_url="pro",
|
||||
next=True,
|
||||
)
|
||||
license_key = request.form["PRO_LICENSE_KEY"]
|
||||
if not license_key:
|
||||
return handle_error("Invalid license key", "pro")
|
||||
|
||||
global_config = DB.get_config(global_only=True, methods=True, filtered_settings=("PRO_LICENSE_KEY"))
|
||||
variables = BW_CONFIG.check_variables({"PRO_LICENSE_KEY": license_key}, global_config, global_config=True)
|
||||
|
||||
if not variables:
|
||||
flash("The license key is the same as the current one.", "warning")
|
||||
return redirect(url_for("pro.pro_page"))
|
||||
|
||||
DATA.load_from_file()
|
||||
|
||||
def update_license_key(license_key: str):
|
||||
wait_applying()
|
||||
manage_bunkerweb("global_config", {"PRO_LICENSE_KEY": license_key}, threaded=True)
|
||||
|
||||
DATA.update(
|
||||
{
|
||||
"RELOADING": True,
|
||||
"LAST_RELOAD": time(),
|
||||
"CONFIG_CHANGED": True,
|
||||
"PRO_LOADING": True,
|
||||
}
|
||||
)
|
||||
flash("Checking license key.")
|
||||
Thread(target=update_license_key, args=(license_key,)).start()
|
||||
return redirect(
|
||||
url_for(
|
||||
"loading",
|
||||
next=url_for("pro.pro_page"),
|
||||
message="Updating license key",
|
||||
)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -747,7 +747,8 @@ a.text-white-80:hover {
|
|||
padding-bottom: 0.625rem !important;
|
||||
}
|
||||
|
||||
.footer-container .footer-link:hover {
|
||||
.footer-container .footer-link:hover,
|
||||
a.courier-prime:hover {
|
||||
font-style: italic;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ $(document).ready(function () {
|
|||
}
|
||||
|
||||
// Select the Flatpickr input elements
|
||||
const flatpickrDatetime = $("[type='flapickr-datetime']");
|
||||
const flatpickrDatetime = $("[type='flatpickr-datetime']");
|
||||
|
||||
// Get the current date and times
|
||||
const currentDatetime = new Date();
|
||||
|
|
|
|||
9
src/ui/app/static/js/pages/pro.js
Normal file
9
src/ui/app/static/js/pages/pro.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
$(document).ready(function () {
|
||||
// Select the Flatpickr input elements
|
||||
const $flatpickrDate = $("#flatpickr-date");
|
||||
|
||||
// Initialize Flatpickr with altInput and altFormat
|
||||
$flatpickrDate.flatpickr({
|
||||
inline: true,
|
||||
});
|
||||
});
|
||||
|
|
@ -118,7 +118,7 @@
|
|||
required />
|
||||
</div>
|
||||
<div class="col-5 border-start">
|
||||
<input type="flapickr-datetime"
|
||||
<input type="flatpickr-datetime"
|
||||
name="datetime"
|
||||
class="form-control"
|
||||
required />
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@
|
|||
href="{{ url_for('static', filename='libs/datatables/datatables.min.css') }}"
|
||||
nonce="{{ style_nonce }}" />
|
||||
{% endif %}
|
||||
{% if current_endpoint == "bans" or current_endpoint != "plugins" and "plugins" in request.path %}
|
||||
{% if current_endpoint in ("bans", "pro") or current_endpoint != "plugins" and "plugins" in request.path %}
|
||||
<!-- Flatpickr -->
|
||||
<link rel="stylesheet"
|
||||
href="{{ url_for('static', filename='libs/flatpickr/flatpickr.min.css') }}"
|
||||
|
|
@ -147,7 +147,7 @@
|
|||
<script src="{{ url_for('static', filename='libs/leaflet/leaflet.min.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
{% endif %}
|
||||
{% if current_endpoint == "bans" or current_endpoint != "plugins" and "plugins" in request.path %}
|
||||
{% if current_endpoint in ("bans", "pro") or current_endpoint != "plugins" and "plugins" in request.path %}
|
||||
<script src="{{ url_for('static', filename='libs/flatpickr/flatpickr.min.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
<script src="{{ url_for('static', filename='libs/flatpickr/plugins/minMaxTimePlugin.js') }}"
|
||||
|
|
@ -227,6 +227,9 @@
|
|||
{% elif current_endpoint == "logs" %}
|
||||
<script src="{{ url_for('static', filename='js/pages/logs.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
{% elif current_endpoint == "pro" %}
|
||||
<script src="{{ url_for('static', filename='js/pages/pro.js') }}"
|
||||
nonce="{{ script_nonce }}"></script>
|
||||
{% endif %}
|
||||
<script async defer src="{{ url_for('static', filename='js/buttons.js') }}"></script>
|
||||
</body>
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
<!-- Content -->
|
||||
<div class="card table-responsive text-nowrap p-4 pb-8 min-vh-70">
|
||||
<input type="hidden" id="cache_number" value="{{ caches|length }}" />
|
||||
<input type="hidden" id="services" value="{{ services }}" />
|
||||
<input type="hidden"
|
||||
id="services"
|
||||
value="{{ services }}" />
|
||||
<input type="hidden" id="cache_service_selection" value="{{ cache_service }}" />
|
||||
id="cache_service_selection"
|
||||
value="{{ cache_service }}" />
|
||||
<input type="hidden" id="cache_plugin_selection" value="{{ cache_plugin }}" />
|
||||
<input type="hidden" id="cache_job_name_selection" value="{{ cache_job_name }}" />
|
||||
<input type="hidden"
|
||||
id="cache_job_name_selection"
|
||||
value="{{ cache_job_name }}" />
|
||||
<input type="hidden"
|
||||
id="csrf_token"
|
||||
name="csrf_token"
|
||||
|
|
|
|||
|
|
@ -3,13 +3,11 @@
|
|||
<!-- Content -->
|
||||
<div class="card table-responsive text-nowrap p-4 min-vh-70">
|
||||
<input type="hidden" id="configs_number" value="{{ configs|length }}" />
|
||||
<input type="hidden" id="services" value="{{ services }}" />
|
||||
<input type="hidden" id="templates" value="{{ db_templates }}" />
|
||||
<input type="hidden"
|
||||
id="services"
|
||||
value="{{ services }}" />
|
||||
<input type="hidden"
|
||||
id="templates"
|
||||
value="{{ db_templates }}" />
|
||||
<input type="hidden" id="configs_service_selection" value="{{ config_service }}" />
|
||||
id="configs_service_selection"
|
||||
value="{{ config_service }}" />
|
||||
<input type="hidden" id="configs_type_selection" value="{{ config_type }}" />
|
||||
<input type="hidden"
|
||||
id="csrf_token"
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@
|
|||
width="18px"
|
||||
height="15.5px">
|
||||
</span>
|
||||
Upgrade to Pro</a>
|
||||
Upgrade to PRO</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="content-backdrop fade"></div>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
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 pro_overlapped and current_endpoint != "pro" %}
|
||||
{% 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 %}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,16 @@
|
|||
<!-- Content -->
|
||||
<div class="row g-4">
|
||||
<div class="col-sm-3 mb-2">
|
||||
<div class="card p-4 position-relative shadow-sm rounded-3 h-100{% if not is_pro_version %} bg-bw-green text-white{% endif %}">
|
||||
<i class='bx bxs-rocket bx-sm position-absolute top-0 end-0 m-3'></i>
|
||||
<p class="ps-4 fs-4 mb-2{% if is_pro_version %} text-primary shine{% else %} text-white{% endif %}">
|
||||
<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'
|
||||
src="{{ pro_diamond_url }}"
|
||||
alt="Pro plugin"
|
||||
width="24px">
|
||||
{% else %}
|
||||
<i class='bx bxs-rocket bx-sm position-absolute top-0 end-0 m-3'></i>
|
||||
{% endif %}
|
||||
<p class="ps-4 fs-4 mb-2 text-white">
|
||||
Plan
|
||||
<br />
|
||||
<span class="fs-3 fw-bold don-jose">
|
||||
|
|
@ -16,8 +23,13 @@
|
|||
{% endif %}
|
||||
</span>
|
||||
</p>
|
||||
<a class="ps-4 text-underline{% if not is_pro_version %} text-white-80{% endif %}"
|
||||
href="{{ url_for('pro') }}">Upgrade?</a>
|
||||
<a class="ps-4 text-underline text-white-80" href="{{ url_for('pro') }}">
|
||||
{% if is_pro_version %}
|
||||
More info
|
||||
{% else %}
|
||||
Upgrade?
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3 mb-2">
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@
|
|||
<li class="menu-item{% if current_endpoint == plugin %} active{% endif %}"{% if not_pro_pro_plugin %}data-bs-toggle="tooltip" data-bs-placement="top" data-bs-html="true" data-bs-original-title="<i class='bx bx-diamond bx-xs'></i><span>Pro feature</span>"
|
||||
{% endif %}
|
||||
>
|
||||
<a href="{% if not_pro_pro_plugin %}{{ url_for("pro") }}{% else %}{{ url_for("plugins") }}/{{ plugin }}{% endif %}"
|
||||
<a href="{% if not_pro_pro_plugin %}{{ url_for('pro') }}{% else %}{{ url_for("plugins") }}/{{ plugin }}{% endif %}"
|
||||
class="menu-link"
|
||||
{% if not_pro_pro_plugin %}target="_blank" rel="noopener"{% endif %}>
|
||||
<i class="menu-icon tf-icons bx bx-puzzle"></i>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
{% elif current_endpoint == 'logs' %}
|
||||
{% set documentation_endpoint = "/web-ui" %}
|
||||
{% set documentation_fragment = "#accessing-logs" %}
|
||||
{% elif current_endpoint == 'pro' %}
|
||||
{% set documentation_endpoint = "/professional-services" %}
|
||||
{% endif %}
|
||||
<div class="navbar-nav align-items-center">
|
||||
<div class="nav-item d-flex align-items-center">
|
||||
|
|
|
|||
13
src/ui/app/templates/plugin_page.html
Normal file
13
src/ui/app/templates/plugin_page.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{% extends "dashboard.html" %}
|
||||
{% block content %}
|
||||
<!-- Content -->
|
||||
<div class="card text-nowrap p-4 min-vh-70">
|
||||
<div class="d-flex align-items-center justify-content-center h-100">
|
||||
<div class="text-center text-primary">
|
||||
<p id="config-waiting"
|
||||
class="text-center relative w-full p-2 text-primary rounded-lg fw-bold">Coming soon...</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--/ Content -->
|
||||
{% endblock %}
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
{% endif %}
|
||||
>
|
||||
{% if plugin_data["type"] == "pro" %}
|
||||
<a href="{{ url_for("pro") }}" target="_blank" rel="noopener" {% else %}
|
||||
<a href="{{ url_for('pro') }}" target="_blank" rel="noopener" {% else %}
|
||||
<div {% endif %}
|
||||
class="{{ plugin_types[plugin_data['type']].get('text-class', '') }}">
|
||||
{{ plugin_types[plugin_data["type"]].get('icon', '<img src="' + pro_diamond_url + '"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,158 @@
|
|||
{% extends "dashboard.html" %}
|
||||
{% block content %}
|
||||
<!-- Content -->
|
||||
{% set is_overlapped = pro_status == "active" and pro_overlapped %}
|
||||
{% set is_expired = pro_status == "expired" %}
|
||||
{% set is_suspended = pro_status == "suspended" %}
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-md-6 col-xl-4">
|
||||
<div class="card p-4 position-relative h-100">
|
||||
<img class='position-absolute top-0 end-0 m-3'
|
||||
src="{{ pro_diamond_url if is_pro_version else url_for('static', filename='img/diamond-blue.svg') }}"
|
||||
alt="Pro plugin"
|
||||
width="24px">
|
||||
<div class="card-header p-2">
|
||||
<div class="card-title">
|
||||
<h5 class="me-2 text-primary shine don-jose mb-0">Pro status</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body position-relative">
|
||||
<div class="d-flex align-items-center justify-content-center h-100">
|
||||
<div class="text-center text-primary">
|
||||
<h4 class="mt-0 mb-2 fw-semibold{% if is_pro_version %} text-primary shine courier-prime{% endif %}">
|
||||
{% if is_pro_version %}
|
||||
PRO version
|
||||
{% else %}
|
||||
{% if is_overlapped %}
|
||||
PRO version / <span class="text-danger fw-bold">OVERLAPPED</span>
|
||||
{% elif pro_status == "expired" %}
|
||||
PRO version / <span class="text-warning fw-bold">EXPIRED</span>
|
||||
{% elif pro_status == "suspended" %}
|
||||
PRO version / <span class="text-danger fw-bold">SUSPENDED</span>
|
||||
{% else %}
|
||||
FREE version
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</h4>
|
||||
<h5 class="mt-2 mb-2 courier-prime fw-medium{% if is_overlapped %} text-decoration-line-through{% endif %}">
|
||||
{{ online_services }} Online service{{ 's' if online_services > 1 else '' }} / {{ pro_services }} PRO service{{ 's' if pro_services > 1 else '' }}
|
||||
</h5>
|
||||
{% if is_overlapped %}
|
||||
<div class="mt-2 mb-2 alert alert-danger text-center" role="alert">
|
||||
You have more services than your license allows !
|
||||
<br />
|
||||
Delete or draft some services to reach the number of {{ pro_services }} services.
|
||||
</div>
|
||||
{% endif %}
|
||||
<a href="https://panel.bunkerweb.io/?utm_campaign=self&utm_source=ui#pro"
|
||||
class="fs-6 mt-2 p-2 courier-prime fst-italic text-primary shine">
|
||||
<i class="bx bx-link-external"></i>
|
||||
{% if is_pro_version %}
|
||||
All features available
|
||||
{% else %}
|
||||
{% if is_overlapped %}
|
||||
Awaiting compliance...
|
||||
{% elif pro_status == "expired" %}
|
||||
Renew license
|
||||
{% elif pro_status == "suspended" %}
|
||||
Suspended license
|
||||
{% else %}
|
||||
Upgrade to PRO
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<small class="position-absolute bottom-0 end-0 text-muted">
|
||||
{{ draft_services }} Draft service{{ 's' if draft_services > 1 else '' }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-xl-4">
|
||||
<div class="card p-4 position-relative h-100">
|
||||
<i class='bx bx-calendar bx-sm position-absolute top-0 end-0 m-3 {% if is_expired %}text-warning{% else %}text-primary{% endif %}'></i>
|
||||
<div class="card-header p-2">
|
||||
<div class="card-title">
|
||||
<h5 class="me-2 text-primary shine don-jose mb-0">Pro expiration date</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body position-relative">
|
||||
<div class="d-flex align-items-center justify-content-center h-100 pb-2">
|
||||
<input id="expiration-date" type="hidden" value="{{ pro_expire }}" />
|
||||
<input id="flatpickr-date"
|
||||
type="hidden"
|
||||
name="date"
|
||||
class="visually-hidden"
|
||||
value="{{ pro_expire }}" />
|
||||
</div>
|
||||
<small class="position-absolute bottom-0 end-0 text-primary">
|
||||
{% if pro_expires_in != "Unknown" %}
|
||||
Expires in: {{ pro_expires_in }}
|
||||
{% else %}
|
||||
Unknown expiration date
|
||||
{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 row g-3 m-0 p-0">
|
||||
<div class="col-12 col-md-6 col-xl-4">
|
||||
<div class="card p-4 position-relative">
|
||||
<i class='bx bx-key bx-sm position-absolute top-0 end-0 m-3 {% if is_expired %}text-warning{% else %}text-primary{% endif %}'></i>
|
||||
<div class="card-header p-2">
|
||||
<div class="card-title">
|
||||
<h5 class="me-2 text-primary shine don-jose mb-0">
|
||||
{% if is_pro_version %}
|
||||
Update
|
||||
{% else %}
|
||||
Activate
|
||||
{% endif %}
|
||||
key
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body position-relative">
|
||||
<form method="POST" action="{{ url_for("pro") }}/key">
|
||||
<input type="hidden"
|
||||
id="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token() }}" />
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<label id="label-username"
|
||||
for="username"
|
||||
class="form-label fw-semibold text-truncate">License key</label>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="badge rounded-pill bg-secondary-subtle text-dark d-flex align-items-center justify-content-center p-1"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-placement="top"
|
||||
data-bs-original-title="Your BunkerWeb PRO license key">
|
||||
<span class="bx bx-question-mark bx-xs"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% set setting = "PRO_LICENSE_KEY" %}
|
||||
{% set setting_data = {"type": "password", "id": "license_key", "regex": "^.+$"} %}
|
||||
{% set required = true %}
|
||||
{% if is_readonly %}
|
||||
{% set disabled = true %}
|
||||
{% set setting_method = "readonly" %}
|
||||
{% endif %}
|
||||
{% include "models/input_setting.html" %}
|
||||
<button type="submit"
|
||||
class="btn btn-primary w-100 text-uppercase don-jose mt-3">
|
||||
{% if is_pro_version %}
|
||||
Update
|
||||
{% else %}
|
||||
Activate
|
||||
{% endif %}
|
||||
key
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--/ Content -->
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ def inject_variables():
|
|||
is_pro_version=metadata["is_pro"],
|
||||
pro_status=metadata["pro_status"],
|
||||
pro_services=metadata["pro_services"],
|
||||
pro_expire=metadata["pro_expire"].strftime("%d-%m-%Y") if metadata["pro_expire"] else "Unknown",
|
||||
pro_expire=metadata["pro_expire"].strftime("%Y/%m/%d") if metadata["pro_expire"] else "Unknown",
|
||||
pro_overlapped=metadata["pro_overlapped"],
|
||||
plugins=BW_CONFIG.get_plugins(),
|
||||
flash_messages=session.get("flash_messages", []),
|
||||
|
|
|
|||
Loading…
Reference in a new issue