Optimize and fix stuff in web UI

This commit is contained in:
Théophile Diot 2024-09-21 14:40:39 +02:00
parent c021b24082
commit 4f9e0e1fea
No known key found for this signature in database
GPG key ID: FA995104A0BA376A
14 changed files with 766 additions and 514 deletions

View file

@ -1,8 +1,11 @@
from flask import Blueprint, render_template
from json import JSONDecodeError, loads
from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import login_required
from app.dependencies import DB
from app.routes.utils import handle_error, verify_data_in_form
jobs = Blueprint("jobs", __name__)
@ -10,3 +13,34 @@ jobs = Blueprint("jobs", __name__)
@login_required
def jobs_page():
return render_template("jobs.html", jobs=DB.get_jobs())
@jobs.route("/jobs/run", methods=["POST"])
@login_required
def jobs_run():
verify_data_in_form(
data={"jobs": None},
err_message="Missing jobs parameter on /jobs/run.",
redirect_url="jobs",
next=True,
)
jobs = request.form["jobs"]
if not jobs:
return handle_error("No jobs selected.", "jobs", True)
try:
jobs = loads(jobs)
except JSONDecodeError:
return handle_error("Invalid jobs parameter on /jobs/run.", "jobs", True)
ret = DB.checked_changes(["config"], [job.get("plugin") for job in jobs if job.get("plugin")], True)
if ret:
return handle_error(ret, "jobs", True)
flash(f"Job{'s' if len(jobs) > 1 else ''}'s plugins will be run in the background by the scheduler.", "success")
return redirect(
url_for(
"loading",
next=url_for("jobs.jobs_page"),
message=f"Run selected job{'s' if len(jobs) > 1 else ''}'s plugins: {', '.join([job.get('plugin') + '/' + job.get('name') for job in jobs])}",
)
)

View file

@ -23,7 +23,7 @@ def logs_page():
files.append(file.name)
current_file = secure_filename(request.args.get("file", ""))
page = request.args.get("page")
page = request.args.get("page", 0)
if current_file and current_file not in files:
return Response("No such file", 404)
@ -31,7 +31,11 @@ def logs_page():
if isabs(current_file) or ".." in current_file:
return error_message("Invalid file path", 400)
raw_logs = "Select a log file to view its contents"
raw_logs = (
"Select a log file to view its contents"
if files
else "There are no log files to display, check the documentation for more information on how to enable logging"
)
page_num = 1
if current_file:
with logs_path.joinpath(current_file).open(encoding="utf-8") as f:
@ -41,4 +45,4 @@ def logs_page():
page = page_num
raw_logs = "\n".join(raw_logs[int(page) * 10000 - 10000 : int(page) * 10000]) # noqa: E203
return render_template("logs.html", logs=raw_logs, files=files, current_file=current_file, current_page=int(page), page_num=page_num)
return render_template("logs.html", logs=raw_logs, files=files, current_file=current_file, current_page=int(page) or page_num, page_num=page_num)

View file

@ -142,485 +142,3 @@
text-align: center;
margin-top: 1rem;
}
/*
* Custom
******************************************************************************/
:root {
--dt-row-selected: 29, 123, 167;
}
.badge-dot {
padding: 0.35rem;
font-size: 0.6rem;
animation: pulsate 1.7s infinite;
}
.badge-dot-text {
font-size: 0.6rem;
animation: pulsate 1.7s infinite;
}
@keyframes pulsate {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.5);
opacity: 0.5;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.w-30 {
width: 30% !important;
}
.badge-center-sm {
padding: 2.5px;
line-height: 1.2;
height: 1.25rem;
width: 1.25rem;
font-size: 0.7rem;
}
.badge-center-sm i {
font-size: 0.8rem;
}
.bg-bw-green {
background-color: var(--bs-bw-green);
}
/* .pro-icon {
position: relative;
width: 18px;
height: 15.5px;
overflow: hidden;
}
.pro-icon::before,
.pro-icon::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
transition: opacity 1.5s ease-in-out;
}
.pro-icon::before {
background-image: url("../img/diamond.svg");
opacity: 1;
animation: fadeOut 1.5s infinite alternate;
}
.pro-icon::after {
background-image: url("../img/diamond-blue.svg");
opacity: 0;
animation: fadeIn 1.5s infinite alternate;
} */
@media (max-width: 768px) {
.btn-responsive {
padding: 4px 9px;
font-size: 80%;
line-height: 1;
}
/* .pro-icon {
width: 9px;
height: 7.75px;
} */
}
@media (min-width: 769px) and (max-width: 992px) {
.btn-responsive {
padding: 8px 18px;
font-size: 90%;
line-height: 1.2;
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.min-vh-70 {
min-height: 70vh !important;
}
.max-vh-60 {
max-height: 60vh !important;
}
td.highlight {
background-color: rgba(var(--bs-primary-rgb), 0.1) !important;
}
.text-bw-green {
color: var(--bs-bw-green) !important;
}
.btn-bw-green {
color: #fff;
background-color: var(--bs-bw-green);
border-color: var(--bs-bw-green);
}
.btn-outline-bw-green {
color: var(--bs-bw-green);
border-color: var(--bs-bw-green);
}
.btn-bw-green:hover,
.btn-outline-bw-green:hover {
color: #fff;
background-color: var(--bs-bw-green);
}
.btn-text-bw-green,
.btn-text-bw-green:hover {
color: var(--bs-bw-green) !important;
}
.btn-text-secondary,
.btn-text-secondary:hover {
color: var(--bs-secondary) !important;
}
.btn-text-danger,
.btn-text-danger.disabled,
.btn-text-danger:hover {
color: var(--bs-danger) !important;
border: none;
}
.col.form-floating > .form-control-sm,
.col-auto.form-floating > .form-select.form-select-sm {
height: calc(1.9em + 0.9rem + 1px);
min-height: calc(1.9em + 0.9rem + 1px);
font-size: 0.85rem;
line-height: 1;
}
.form-floating > .form-control:focus ~ label {
transform: scale(0.8) translateY(-0.7rem) translateX(0.15rem);
}
.col.form-floating > label,
.col-auto.form-floating > label {
padding: 0.8rem 0.9375rem;
font-size: 0.8rem;
}
.form-floating > .form-select:focus {
padding-top: 1.2rem;
}
.col-auto.form-floating > .form-select.form-select-sm,
.col-auto.form-floating > .form-select.form-select-sm:focus {
padding-top: calc(-2px + 1.2rem);
}
.form-floating > .form-control.form-control-sm:focus {
padding-top: 1.1rem !important;
}
.shine {
background-image: linear-gradient(
120deg,
rgba(46, 172, 104, 0) 0%,
rgba(46, 172, 104, 0.8) 50%,
rgba(46, 172, 104, 0) 100%
);
background-size: 40%;
background-position: -100% center;
background-repeat: no-repeat;
background-clip: text;
-webkit-background-clip: text;
color: transparent;
animation: shine 8s linear infinite;
animation-fill-mode: forwards;
}
.shine-sm {
animation: shineSm 5s linear infinite;
}
@keyframes shine {
0% {
background-position: -200% center;
}
100% {
background-position: 200% center;
}
}
@keyframes shineSm {
0% {
background-position: -200% center;
}
60% {
background-position: 200% center;
}
100% {
background-position: 200% center;
}
}
.text-primary.shine {
color: rgba(var(--bs-primary-rgb), 0.1) !important;
background-color: var(--bs-primary) !important;
}
.form-floating > :disabled ~ label::after,
.form-floating > .form-control:disabled ~ label::after {
background-color: transparent;
}
.chevron-icon {
padding-bottom: 0.125rem !important;
}
.chevron-rotate {
transform: rotate(90deg);
transition: transform 0.3s ease;
}
.chevron-rotate-back {
transform: rotate(0deg);
transition: transform 0.3s ease;
}
.multiple-container:not(:first-child) {
border-top: 1px solid var(--bs-primary);
}
a.badge:hover {
color: #fff;
}
.setting-checkbox-label {
font-size: calc(var(--bs-body-font-size) * 0.85);
}
.sticky-card {
position: sticky;
background-color: rgba(255, 255, 255, 0.88) !important;
backdrop-filter: saturate(200%) blur(6px);
top: 85px;
z-index: 1000;
}
#floating-modes-menu {
bottom: 2.1rem !important;
left: 1.5rem !important;
z-index: 1080;
}
.setting-highlight {
background-color: rgba(var(--bs-bw-green-rgb), 0.5);
transition:
background-color 2s ease,
opacity 2s ease;
opacity: 1;
}
.setting-highlight-fade {
background-color: transparent;
opacity: 1; /* You can set this to 0 if you want it to fade out */
}
.template-steps-container {
--bs-breadcrumb-divider: url("../img/bxs-chevron-right.svg");
}
.template-steps-container .breadcrumb-item + .breadcrumb-item::before {
padding: 0 1rem 0 1rem;
}
.h-vh-40 {
height: 40vh;
}
/* Base styles for the ModSecurity mode */
.ace-modsecurity .ace_editor {
font-family: monospace;
font-size: 12px;
}
/* Comments */
.ace-modsecurity .ace_comment {
color: #6a9955; /* Green color for comments */
font-style: italic;
}
/* Strings */
.ace-modsecurity .ace_string {
color: #ce9178; /* Orange color for strings */
}
/* Keywords */
.ace-modsecurity .ace_keyword {
color: #569cd6; /* Blue color for keywords */
font-weight: bold;
}
/* Constants (numbers, IP addresses) */
.ace-modsecurity .ace_constant {
color: #b5cea8; /* Light green color for constants */
}
/* Variables */
.ace-modsecurity .ace_variable {
color: #9cdcfe; /* Light blue color for variables */
}
/* Operators */
.ace-modsecurity .ace_keyword.ace_operator {
color: #c586c0; /* Purple color for operators */
}
/* Functions (actions) */
.ace-modsecurity .ace_support.ace_function {
color: #dcdcaa; /* Yellow color for functions */
font-weight: bold;
}
/* Invalid or illegal syntax */
.ace-modsecurity .ace_invalid {
color: #ffffff;
background-color: #e51400; /* Red background for errors */
}
/* Text */
.ace-modsecurity .ace_text {
color: #d4d4d4; /* Default text color */
}
/* Specific token styles based on the highlight rules */
/* ModSecurity directives */
.ace-modsecurity .ace_keyword\.headers\.modsecurity\.directive,
.ace-modsecurity .ace_keyword\.headers\.modsecurity\.directive\.marker {
color: #569cd6;
font-weight: bold;
}
/* ModSecurity variables */
.ace-modsecurity .ace_variable\.parameter\.modsecurity,
.ace-modsecurity .ace_keyword\.macro\.modsecurity {
color: #9cdcfe;
}
/* ModSecurity actions */
.ace-modsecurity .ace_keyword\.operator\.modsecurity\.action,
.ace-modsecurity .ace_keyword\.operator\.modsecurity\.action\.ctl,
.ace-modsecurity .ace_keyword\.operator\.modsecurity\.action\.phase,
.ace-modsecurity .ace_keyword\.operator\.modsecurity\.action\.severity {
color: #c586c0;
font-weight: bold;
}
/* ModSecurity action parameters */
.ace-modsecurity .ace_string\.modsecurity\.action_parameter,
.ace-modsecurity .ace_constant\.numeric\.modsecurity\.action\.phase_name,
.ace-modsecurity .ace_constant\.numeric\.modsecurity\.action\.severity_name,
.ace-modsecurity .ace_constant\.numeric\.modsecurity\.action\.ctl\.parameter,
.ace-modsecurity .ace_constant\.numeric\.modsecurity\.action\.transform_name {
color: #ce9178;
}
/* Disruptive actions */
.ace-modsecurity
.ace_entity\.name\.function\.modsecurity\.action\.disruptive_pass {
color: #d16969; /* Light red color for disruptive actions */
font-weight: bold;
}
/* Regular expressions and strings */
.ace-modsecurity .ace_string\.regexp\.modsecurity,
.ace-modsecurity .ace_string\.unquoted\.modsecurity,
.ace-modsecurity .ace_string\.quoted\.modsecurity {
color: #ce9178;
}
/* Operators */
.ace-modsecurity .ace_keyword\.control\.modsecurity {
color: #c586c0;
}
/* IP addresses */
.ace-modsecurity .ace_constant\.other\.modsecurity\.ip {
color: #b5cea8;
}
/* Numbers */
.ace-modsecurity .ace_constant\.numeric\.modsecurity {
color: #b5cea8;
}
/* Invalid syntax */
.ace-modsecurity .ace_invalid\.illegal\.modsecurity {
color: #ffffff;
background-color: #e51400;
font-weight: bold;
}
/* Punctuation */
.ace-modsecurity .ace_punctuation\.definition\.tag\.apacheconf,
.ace-modsecurity .ace_punctuation\.colon\.modsecurity,
.ace-modsecurity .ace_punctuation\.equals\.modsecurity {
color: #d4d4d4;
}
/* Apache configuration tags */
.ace-modsecurity .ace_entity\.tag\.apacheconf {
color: #569cd6;
font-weight: bold;
}
/* Comments in Apache configuration */
.ace-modsecurity .ace_punctuation\.definition\.comment\.apacheconf,
.ace-modsecurity .ace_comment\.line\.hash\.ini {
color: #6a9955;
font-style: italic;
}
table.table.dataTable > tbody > tr.selected a {
color: #fff;
}
table.table.dataTable > tbody > tr.selected .btn-outline-secondary {
color: #fff !important;
border-color: #fff !important;
}
table.table.dataTable > tbody > tr.selected .btn-outline-secondary:hover {
color: black !important;
background-color: #fff !important;
border-color: #fff !important;
}

View file

@ -1,3 +1,7 @@
/*
* Core overrides
******************************************************************************/
@charset "UTF-8";
:root,
[data-bs-theme="light"] {
@ -190,3 +194,489 @@ button.list-group-item-secondary.active {
.navbar.bg-secondary .search-input-wrapper .search-toggler {
background-color: var(--bs-secondary) !important;
}
.toast-container {
z-index: 1100;
}
/*
* Main overrides
******************************************************************************/
:root {
--dt-row-selected: 29, 123, 167;
}
.badge-dot {
padding: 0.35rem;
font-size: 0.6rem;
animation: pulsate 1.7s infinite;
}
.badge-dot-text {
font-size: 0.6rem;
animation: pulsate 1.7s infinite;
}
@keyframes pulsate {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(1.5);
opacity: 0.5;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.w-30 {
width: 30% !important;
}
.badge-center-sm {
padding: 2.5px;
line-height: 1.2;
height: 1.25rem;
width: 1.25rem;
font-size: 0.7rem;
}
.badge-center-sm i {
font-size: 0.8rem;
}
.bg-bw-green {
background-color: var(--bs-bw-green);
}
/* .pro-icon {
position: relative;
width: 18px;
height: 15.5px;
overflow: hidden;
}
.pro-icon::before,
.pro-icon::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
transition: opacity 1.5s ease-in-out;
}
.pro-icon::before {
background-image: url("../img/diamond.svg");
opacity: 1;
animation: fadeOut 1.5s infinite alternate;
}
.pro-icon::after {
background-image: url("../img/diamond-blue.svg");
opacity: 0;
animation: fadeIn 1.5s infinite alternate;
} */
@media (max-width: 768px) {
.btn-responsive {
padding: 4px 9px;
font-size: 80%;
line-height: 1;
}
/* .pro-icon {
width: 9px;
height: 7.75px;
} */
}
@media (min-width: 769px) and (max-width: 992px) {
.btn-responsive {
padding: 8px 18px;
font-size: 90%;
line-height: 1.2;
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.min-vh-70 {
min-height: 70vh !important;
}
.max-vh-60 {
max-height: 60vh !important;
}
td.highlight {
background-color: rgba(var(--bs-primary-rgb), 0.1) !important;
}
.text-bw-green {
color: var(--bs-bw-green) !important;
}
.btn-bw-green {
color: #fff;
background-color: var(--bs-bw-green);
border-color: var(--bs-bw-green);
}
.btn-outline-bw-green {
color: var(--bs-bw-green);
border-color: var(--bs-bw-green);
}
.btn-bw-green:hover,
.btn-outline-bw-green:hover {
color: #fff;
background-color: var(--bs-bw-green);
}
.btn-text-bw-green,
.btn-text-bw-green:hover {
color: var(--bs-bw-green) !important;
}
.btn-text-secondary,
.btn-text-secondary:hover {
color: var(--bs-secondary) !important;
}
.btn-text-danger,
.btn-text-danger.disabled,
.btn-text-danger:hover {
color: var(--bs-danger) !important;
border: none;
}
.col.form-floating > .form-control-sm,
.col-auto.form-floating > .form-select.form-select-sm {
height: calc(1.9em + 0.9rem + 1px);
min-height: calc(1.9em + 0.9rem + 1px);
font-size: 0.85rem;
line-height: 1;
}
.form-floating > .form-control:focus ~ label {
transform: scale(0.8) translateY(-0.7rem) translateX(0.15rem);
}
.col.form-floating > label,
.col-auto.form-floating > label {
padding: 0.8rem 0.9375rem;
font-size: 0.8rem;
}
.form-floating > .form-select:focus {
padding-top: 1.2rem;
}
.col-auto.form-floating > .form-select.form-select-sm,
.col-auto.form-floating > .form-select.form-select-sm:focus {
padding-top: calc(-2px + 1.2rem);
}
.form-floating > .form-control.form-control-sm:focus {
padding-top: 1.1rem !important;
}
.shine {
background-image: linear-gradient(
120deg,
rgba(46, 172, 104, 0) 0%,
rgba(46, 172, 104, 0.8) 50%,
rgba(46, 172, 104, 0) 100%
);
background-size: 40%;
background-position: -100% center;
background-repeat: no-repeat;
background-clip: text;
-webkit-background-clip: text;
color: transparent;
animation: shine 8s linear infinite;
animation-fill-mode: forwards;
}
.shine-sm {
animation: shineSm 5s linear infinite;
}
@keyframes shine {
0% {
background-position: -200% center;
}
100% {
background-position: 200% center;
}
}
@keyframes shineSm {
0% {
background-position: -200% center;
}
60% {
background-position: 200% center;
}
100% {
background-position: 200% center;
}
}
.text-primary.shine {
color: rgba(var(--bs-primary-rgb), 0.1) !important;
background-color: var(--bs-primary) !important;
}
.form-floating > :disabled ~ label::after,
.form-floating > .form-control:disabled ~ label::after {
background-color: transparent;
}
.chevron-icon {
padding-bottom: 0.125rem !important;
}
.chevron-rotate {
transform: rotate(90deg);
transition: transform 0.3s ease;
}
.chevron-rotate-back {
transform: rotate(0deg);
transition: transform 0.3s ease;
}
.multiple-container:not(:first-child) {
border-top: 1px solid var(--bs-primary);
}
a.badge:hover {
color: #fff;
}
.setting-checkbox-label {
font-size: calc(var(--bs-body-font-size) * 0.85);
}
.sticky-card {
position: sticky;
background-color: rgba(255, 255, 255, 0.88) !important;
backdrop-filter: saturate(200%) blur(6px);
top: 85px;
z-index: 1000;
}
#floating-modes-menu {
bottom: 2.1rem !important;
left: 1.5rem !important;
z-index: 1080;
}
.setting-highlight {
background-color: rgba(var(--bs-bw-green-rgb), 0.5);
transition:
background-color 2s ease,
opacity 2s ease;
opacity: 1;
}
.setting-highlight-fade {
background-color: transparent;
opacity: 1; /* You can set this to 0 if you want it to fade out */
}
.template-steps-container {
--bs-breadcrumb-divider: url("../img/bxs-chevron-right.svg");
}
.template-steps-container .breadcrumb-item + .breadcrumb-item::before {
padding: 0 1rem 0 1rem;
}
.h-vh-40 {
height: 40vh;
}
/* Base styles for the ModSecurity mode */
.ace-modsecurity .ace_editor {
font-family: monospace;
font-size: 12px;
}
/* Comments */
.ace-modsecurity .ace_comment {
color: #6a9955; /* Green color for comments */
font-style: italic;
}
/* Strings */
.ace-modsecurity .ace_string {
color: #ce9178; /* Orange color for strings */
}
/* Keywords */
.ace-modsecurity .ace_keyword {
color: #569cd6; /* Blue color for keywords */
font-weight: bold;
}
/* Constants (numbers, IP addresses) */
.ace-modsecurity .ace_constant {
color: #b5cea8; /* Light green color for constants */
}
/* Variables */
.ace-modsecurity .ace_variable {
color: #9cdcfe; /* Light blue color for variables */
}
/* Operators */
.ace-modsecurity .ace_keyword.ace_operator {
color: #c586c0; /* Purple color for operators */
}
/* Functions (actions) */
.ace-modsecurity .ace_support.ace_function {
color: #dcdcaa; /* Yellow color for functions */
font-weight: bold;
}
/* Invalid or illegal syntax */
.ace-modsecurity .ace_invalid {
color: #ffffff;
background-color: #e51400; /* Red background for errors */
}
/* Text */
.ace-modsecurity .ace_text {
color: #d4d4d4; /* Default text color */
}
/* Specific token styles based on the highlight rules */
/* ModSecurity directives */
.ace-modsecurity .ace_keyword\.headers\.modsecurity\.directive,
.ace-modsecurity .ace_keyword\.headers\.modsecurity\.directive\.marker {
color: #569cd6;
font-weight: bold;
}
/* ModSecurity variables */
.ace-modsecurity .ace_variable\.parameter\.modsecurity,
.ace-modsecurity .ace_keyword\.macro\.modsecurity {
color: #9cdcfe;
}
/* ModSecurity actions */
.ace-modsecurity .ace_keyword\.operator\.modsecurity\.action,
.ace-modsecurity .ace_keyword\.operator\.modsecurity\.action\.ctl,
.ace-modsecurity .ace_keyword\.operator\.modsecurity\.action\.phase,
.ace-modsecurity .ace_keyword\.operator\.modsecurity\.action\.severity {
color: #c586c0;
font-weight: bold;
}
/* ModSecurity action parameters */
.ace-modsecurity .ace_string\.modsecurity\.action_parameter,
.ace-modsecurity .ace_constant\.numeric\.modsecurity\.action\.phase_name,
.ace-modsecurity .ace_constant\.numeric\.modsecurity\.action\.severity_name,
.ace-modsecurity .ace_constant\.numeric\.modsecurity\.action\.ctl\.parameter,
.ace-modsecurity .ace_constant\.numeric\.modsecurity\.action\.transform_name {
color: #ce9178;
}
/* Disruptive actions */
.ace-modsecurity
.ace_entity\.name\.function\.modsecurity\.action\.disruptive_pass {
color: #d16969; /* Light red color for disruptive actions */
font-weight: bold;
}
/* Regular expressions and strings */
.ace-modsecurity .ace_string\.regexp\.modsecurity,
.ace-modsecurity .ace_string\.unquoted\.modsecurity,
.ace-modsecurity .ace_string\.quoted\.modsecurity {
color: #ce9178;
}
/* Operators */
.ace-modsecurity .ace_keyword\.control\.modsecurity {
color: #c586c0;
}
/* IP addresses */
.ace-modsecurity .ace_constant\.other\.modsecurity\.ip {
color: #b5cea8;
}
/* Numbers */
.ace-modsecurity .ace_constant\.numeric\.modsecurity {
color: #b5cea8;
}
/* Invalid syntax */
.ace-modsecurity .ace_invalid\.illegal\.modsecurity {
color: #ffffff;
background-color: #e51400;
font-weight: bold;
}
/* Punctuation */
.ace-modsecurity .ace_punctuation\.definition\.tag\.apacheconf,
.ace-modsecurity .ace_punctuation\.colon\.modsecurity,
.ace-modsecurity .ace_punctuation\.equals\.modsecurity {
color: #d4d4d4;
}
/* Apache configuration tags */
.ace-modsecurity .ace_entity\.tag\.apacheconf {
color: #569cd6;
font-weight: bold;
}
/* Comments in Apache configuration */
.ace-modsecurity .ace_punctuation\.definition\.comment\.apacheconf,
.ace-modsecurity .ace_comment\.line\.hash\.ini {
color: #6a9955;
font-style: italic;
}
table.table.dataTable > tbody > tr.selected a {
color: #fff;
}
table.table.dataTable > tbody > tr.selected .btn-outline-secondary {
color: #fff !important;
border-color: #fff !important;
}
table.table.dataTable > tbody > tr.selected .btn-outline-secondary:hover {
color: black !important;
background-color: #fff !important;
border-color: #fff !important;
}

View file

@ -151,8 +151,19 @@ $(document).ready(function () {
};
if (banNumber > 10) {
const menu = [10];
if (banNumber > 25) {
menu.push(25);
}
if (banNumber > 50) {
menu.push(50);
}
if (banNumber > 100) {
menu.push(100);
}
menu.push({ label: "All", value: -1 });
layout.topStart.pageLength = {
menu: [10, 25, 50, 100, { label: "All", value: -1 }],
menu: menu,
};
layout.bottomEnd.paging = true;
}
@ -534,14 +545,13 @@ $(document).ready(function () {
});
});
console.log(bans);
const form = $("<form>", {
method: "POST",
action: `${window.location.pathname}/ban`,
class: "visually-hidden",
});
// Add CSRF token and instances as hidden inputs
// Add CSRF token and bans as hidden inputs
form.append(
$("<input>", {
type: "hidden",

View file

@ -7,8 +7,19 @@ $(document).ready(function () {
};
if (cacheNumber > 10) {
const menu = [10];
if (cacheNumber > 25) {
menu.push(25);
}
if (cacheNumber > 50) {
menu.push(50);
}
if (cacheNumber > 100) {
menu.push(100);
}
menu.push({ label: "All", value: -1 });
layout.topStart.pageLength = {
menu: [10, 25, 50, 100, { label: "All", value: -1 }],
menu: menu,
};
layout.bottomEnd.paging = true;
}

View file

@ -174,12 +174,6 @@ $(document).ready(function () {
return;
}
console.log("Saving configuration...");
console.log("Service:", selectedService);
console.log("Type:", selectedType);
console.log("Name:", configName);
console.log("Value:", value);
const form = $("<form>", {
method: "POST",
action: window.location.href,

View file

@ -84,8 +84,19 @@ $(document).ready(function () {
};
if (configNumber > 10) {
const menu = [10];
if (configNumber > 25) {
menu.push(25);
}
if (configNumber > 50) {
menu.push(50);
}
if (configNumber > 100) {
menu.push(100);
}
menu.push({ label: "All", value: -1 });
layout.topStart.pageLength = {
menu: [10, 25, 50, 100, { label: "All", value: -1 }],
menu: menu,
};
layout.bottomEnd.paging = true;
}
@ -312,7 +323,6 @@ $(document).ready(function () {
type: $(this).data("config-type"),
service: $(this).data("config-service"),
};
console.log(config);
setupDeletionModal([config]);
});
});

View file

@ -95,8 +95,19 @@ $(document).ready(function () {
};
if (instanceNumber > 10) {
const menu = [10];
if (instanceNumber > 25) {
menu.push(25);
}
if (instanceNumber > 50) {
menu.push(50);
}
if (instanceNumber > 100) {
menu.push(100);
}
menu.push({ label: "All", value: -1 });
layout.topStart.pageLength = {
menu: [10, 25, 50, 100, { label: "All", value: -1 }],
menu: menu,
};
layout.bottomEnd.paging = true;
}

View file

@ -1,4 +1,5 @@
$(document).ready(function () {
let actionLock = false;
const jobNumber = parseInt($("#job_number").val());
const layout = {
@ -7,8 +8,19 @@ $(document).ready(function () {
};
if (jobNumber > 10) {
const menu = [10];
if (jobNumber > 25) {
menu.push(25);
}
if (jobNumber > 50) {
menu.push(50);
}
if (jobNumber > 100) {
menu.push(100);
}
menu.push({ label: "All", value: -1 });
layout.topStart.pageLength = {
menu: [10, 25, 50, 100, { label: "All", value: -1 }],
menu: menu,
};
layout.bottomEnd.paging = true;
}
@ -65,8 +77,77 @@ $(document).ready(function () {
},
],
},
{
extend: "collection",
text: '<span class="tf-icons bx bx-play bx-18px me-2"></span>Actions',
className: "btn btn-sm btn-outline-primary",
buttons: [
{
extend: "run_jobs",
},
],
},
];
const getSelectedJobs = () => {
const jobs = [];
$("tr.selected").each(function () {
const $this = $(this);
const name = $this.find("td:eq(1)").text().trim();
const plugin = $this.find("td:eq(2)").text().trim();
jobs.push({ name: name, plugin: plugin });
});
return jobs;
};
const executeForm = (jobs) => {
const form = $("<form>", {
method: "POST",
action: `${window.location.pathname}/run`,
class: "visually-hidden",
});
// Add CSRF token and jobs as hidden inputs
form.append(
$("<input>", {
type: "hidden",
name: "csrf_token",
value: $("#csrf_token").val(),
}),
);
form.append(
$("<input>", {
type: "hidden",
name: "jobs",
value: JSON.stringify(jobs),
}),
);
// Append the form to the body and submit it
form.appendTo("body").submit();
};
$.fn.dataTable.ext.buttons.run_jobs = {
text: '<span class="tf-icons bx bx-play bx-18px me-2"></span>Run selected jobs',
action: function (e, dt, node, config) {
if (actionLock) {
return;
}
actionLock = true;
$(".dt-button-background").click();
const jobs = getSelectedJobs();
if (jobs.length === 0) {
actionLock = false;
return;
}
executeForm(jobs);
actionLock = false;
},
};
$(".history-start-date, .history-end-date").each(function () {
const isoDateStr = $(this).text().trim();
@ -89,6 +170,11 @@ $(document).ready(function () {
const jobs_table = new DataTable("#jobs", {
columnDefs: [
{
orderable: false,
render: DataTable.render.select(),
targets: 0,
},
{
orderable: false,
targets: -1,
@ -103,6 +189,11 @@ $(document).ready(function () {
order: [[1, "asc"]],
autoFill: false,
responsive: true,
select: {
style: "multi+shift",
selector: "td:first-child",
headerCheckbox: false,
},
layout: layout,
language: {
info: "Showing _START_ to _END_ of _TOTAL_ jobs",
@ -110,6 +201,13 @@ $(document).ready(function () {
infoFiltered: "(filtered from _MAX_ total jobs)",
lengthMenu: "Display _MENU_ jobs",
zeroRecords: "No matching jobs found",
select: {
rows: {
_: "Selected %d jobs",
0: "No jobs selected",
1: "Selected 1 job",
},
},
},
initComplete: function (settings, json) {
$("#jobs_wrapper .btn-secondary").removeClass("btn-secondary");
@ -142,17 +240,45 @@ $(document).ready(function () {
.each((el) => el.classList.remove("highlight"));
});
// Event listener for the select-all checkbox
$("#select-all-rows").on("change", function () {
const isChecked = $(this).prop("checked");
if (isChecked) {
// Select all rows on the current page
jobs_table.rows({ page: "current" }).select();
} else {
// Deselect all rows on the current page
jobs_table.rows({ page: "current" }).deselect();
}
});
$(".show-history").on("click", function () {
const historyModal = $("#modal-job-history");
const job = $(this).data("job");
const plugin = $(this).data("plugin");
historyModal.find(".modal-title").text(`Job ${job} History`);
const history = $(`#job-${job}-${plugin}-history`).clone();
const historyCount = history.find("ul").length - 1;
historyModal
.find(".modal-title")
.html(
`Last${historyCount > 1 ? " " + historyCount : ""} execution${
historyCount > 1 ? "s" : ""
} of Job <span class="fw-bold fst-italic">${job}</span> from plugin <span class="fw-bold fst-italic">${plugin}</span>`,
);
history.removeClass("visually-hidden");
historyModal.find(".modal-body").html(history);
const modal = new bootstrap.Modal(historyModal);
modal.show();
});
$(".run-job").on("click", function () {
const job = {
name: $(this).data("job"),
plugin: $(this).data("plugin"),
};
executeForm([job]);
});
});

View file

@ -7,8 +7,19 @@ $(document).ready(function () {
};
if (reportsNumber > 10) {
const menu = [10];
if (reportsNumber > 25) {
menu.push(25);
}
if (reportsNumber > 50) {
menu.push(50);
}
if (reportsNumber > 100) {
menu.push(100);
}
menu.push({ label: "All", value: -1 });
layout.topStart.pageLength = {
menu: [10, 25, 50, 100, { label: "All", value: -1 }],
menu: menu,
};
layout.bottomEnd.paging = true;
}

View file

@ -89,8 +89,19 @@ $(document).ready(function () {
};
if (serviceNumber > 10) {
const menu = [10];
if (serviceNumber > 25) {
menu.push(25);
}
if (serviceNumber > 50) {
menu.push(50);
}
if (serviceNumber > 100) {
menu.push(100);
}
menu.push({ label: "All", value: -1 });
layout.topStart.pageLength = {
menu: [10, 25, 50, 100, { label: "All", value: -1 }],
menu: menu,
};
layout.bottomEnd.paging = true;
}

View file

@ -44,15 +44,15 @@
<link rel="stylesheet"
href="{{ url_for('static', filename='css/core.css') }}"
nonce="{{ style_nonce }}" />
<link rel="stylesheet"
href="{{ url_for('static', filename='css/overrides.css') }}"
nonce="{{ style_nonce }}" />
<link rel="stylesheet"
href="{{ url_for('static', filename='css/theme-default.css') }}"
nonce="{{ style_nonce }}" />
<link rel="stylesheet"
href="{{ url_for('static', filename='css/main.css') }}"
nonce="{{ style_nonce }}" />
<link rel="stylesheet"
href="{{ url_for('static', filename='css/overrides.css') }}"
nonce="{{ style_nonce }}" />
<!-- Vendors CSS -->
<link rel="stylesheet"
href="{{ url_for('static', filename='libs/perfect-scrollbar/perfect-scrollbar.css') }}"

View file

@ -3,9 +3,20 @@
<!-- Content -->
<div class="card table-responsive text-nowrap p-4 min-vh-70">
<input type="hidden" id="job_number" value="{{ jobs|length }}" />
<table id="jobs" class="table position-relative w-100">
<input type="hidden"
id="csrf_token"
name="csrf_token"
value="{{ csrf_token() }}" />
<table id="jobs" class="table w-100">
<thead>
<tr>
<th>
<input id="select-all-rows"
aria-label="Select all rows"
class="dt-select-checkbox mb-1"
type="checkbox">
Select All
</th>
<th data-bs-toggle="tooltip"
data-bs-placement="bottom"
data-bs-original-title="The Job's name">Name</th>
@ -23,12 +34,13 @@
data-bs-original-title="Does the last Job's execution was successful?">Last run</th>
<th data-bs-toggle="tooltip"
data-bs-placement="bottom"
data-bs-original-title="The Job's information">Information</th>
data-bs-original-title="The actions that can be performed on the Job">Actions</th>
</tr>
</thead>
<tbody>
{% for job, job_data in jobs.items() %}
<tr>
<td></td>
<td>{{ job }}</td>
<td>{{ job_data["plugin_id"] }}</td>
<td>
@ -46,6 +58,16 @@
</td>
<td>
<div class="d-flex justify-content-center">
<div data-bs-toggle="tooltip"
data-bs-placement="bottom"
data-bs-original-title="Run the Job">
<button type="button"
data-job="{{ job }}"
data-plugin="{{ job_data['plugin_id'] }}"
class="btn btn-primary btn-sm me-1 run-job">
<i class="bx bx-play bx-xs"></i>&nbsp;Run
</button>
</div>
{% if job_data['history'] %}
<div id="job-{{ job }}-{{ job_data['plugin_id'] }}-history"
class="visually-hidden mb-3">
@ -118,9 +140,6 @@
</tr>
{% endfor %}
</tbody>
<span class="position-absolute bottom-0 start-50 translate-middle badge rounded-pill bg-secondary">
TZ: <script nonce="{{ script_nonce }}">document.write(Intl.DateTimeFormat().resolvedOptions().timeZone);</script>
</span>
</table>
</div>
<div class="modal modal-lg fade"
@ -130,15 +149,18 @@
aria-hidden="true"
role="dialog">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-content position-relative">
<div class="modal-header">
<h5 class="modal-title">Job History</h5>
<h5 class="modal-title">Last 10 Job's executions</h5>
<button type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body"></div>
<div class="modal-body mb-2"></div>
<span class="position-absolute bottom-0 start-50 translate-middle badge rounded-pill bg-secondary">
TZ: <script nonce="{{ script_nonce }}">document.write(Intl.DateTimeFormat().resolvedOptions().timeZone);</script>
</span>
</div>
</div>
</div>