update plugin ui with pre_render

* plugin ui now run pre_render function from action.py, this allow to access a variable pre_render with wanted data to only use jinja logic to build html page
* delete SetupPlugin js class because now useless
* add a utils script with the possibility to try some test from ui (like ping server, webhook testing...)
* update all core template with a standard way to render metrics
*update main.py to run action.py new pre_render function on plugin ui GET
* update Instance get_data to know when an instance is not reachable
This commit is contained in:
Jordan Blasenhauer 2024-03-14 17:12:55 +01:00
parent 677cfbc420
commit 3d0749d695
36 changed files with 1697 additions and 1148 deletions

View file

@ -594,7 +594,7 @@ Allow access based on internal and external IP/network/rDNS/ASN whitelists.
<path style="fill:#eab308" d="M21.2803 45.5H26.7198C33.8098 45.5 37.3545 45.5 39.7198 43.383C40.7523 42.4588 41.4057 40.793 41.8775 38.625H6.1224C6.59413 40.793 7.24783 42.4588 8.2802 43.383C10.6454 45.5 14.1903 45.5 21.2803 45.5Z" fill="#1C274C" />
</svg>
</div>
STREAM support :x:
Prometheus export for BunkerWeb
@ -607,4 +607,3 @@ Prometheus export for BunkerWeb
|`PROMETHEUS_EXPORTER_DICT_SIZE`|`10M` |global |no |Size of the dict to store Prometheus metrics. |
|`PROMETHEUS_EXPORTER_ALLOW_IP` |`127.0.0.1/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16`|global |no |List of IP/networks allowed to contact the Prometheus exporter endpoint.|
|`PROMETHEUS_EXPORTER_URL` |`/metrics` |global |no |HTTP URL of the Prometheus exporter. |

View file

@ -1,11 +1,18 @@
def antibot(**kwargs):
def pre_render(**kwargs):
try:
data = kwargs["app"].config["INSTANCES"].get_metrics("antibot")
if data.get("counter_failed_challenges") is None:
data["counter_failed_challenges"] = 0
return data
return {
"counter_failed_challenges": {
"value": data.get("counter_failed_challenges", 0),
"title": "Challenge",
"subtitle": "Failed",
"subtitle_color": "info",
"svg_color": "blue",
}
}
except:
return {"counter_failed_challenges": 0}
return {"counter_failed_challenges": {"value": "unknown", "title": "Challenge", "subtitle": "Failed", "subtitle_color": "info", "svg_color": "blue"}}
def antibot(**kwargs):
pass

View file

@ -7,52 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used and is_metrics %}
<!-- info-->
<div class="core-card">
<h5 class="core-card-info-title">INFO</h5>
<div class="core-card-info-list">
<p data-info class="core-card-info-text"></p>
</div>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<!-- end info -->
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">Challenges</p>
<h5 data-count class="core-card-title">"unknown"</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content error">total failed</span>
</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-metrics-svg-container blue">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-base core-card-metrics-svg">
<path d="M11.7 2.805a.75.75 0 0 1 .6 0A60.65 60.65 0 0 1 22.83 8.72a.75.75 0 0 1-.231 1.337 49.948 49.948 0 0 0-9.902 3.912l-.003.002c-.114.06-.227.119-.34.18a.75.75 0 0 1-.707 0A50.88 50.88 0 0 0 7.5 12.173v-.224c0-.131.067-.248.172-.311a54.615 54.615 0 0 1 4.653-2.52.75.75 0 0 0-.65-1.352 56.123 56.123 0 0 0-4.78 2.589 1.858 1.858 0 0 0-.859 1.228 49.803 49.803 0 0 0-4.634-1.527.75.75 0 0 1-.231-1.337A60.653 60.653 0 0 1 11.7 2.805Z" />
<path d="M13.06 15.473a48.45 48.45 0 0 1 7.666-3.282c.134 1.414.22 2.843.255 4.284a.75.75 0 0 1-.46.711 47.87 47.87 0 0 0-8.105 4.342.75.75 0 0 1-.832 0 47.87 47.87 0 0 0-8.104-4.342.75.75 0 0 1-.461-.71c.035-1.442.121-2.87.255-4.286.921.304 1.83.634 2.726.99v1.27a1.5 1.5 0 0 0-.14 2.508c-.09.38-.222.753-.397 1.11.452.213.901.434 1.346.66a6.727 6.727 0 0 0 .551-1.607 1.5 1.5 0 0 0 .14-2.67v-.645a48.549 48.549 0 0 1 3.44 1.667 2.25 2.25 0 0 0 2.12 0Z" />
<path d="M4.462 19.462c.42-.419.753-.89 1-1.395.453.214.902.435 1.347.662a6.742 6.742 0 0 1-1.286 1.794.75.75 0 0 1-1.06-1.06Z" />
</svg>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
<!-- end icon -->
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
counter_failed_challenges: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
});
</script>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -71,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,13 +1,17 @@
from operator import itemgetter
def badbehavior(**kwargs):
def pre_render(**kwargs):
try:
# Here we will have a list { 'counter_403': X, 'counter_401': Y ... }
data = kwargs["app"].config["INSTANCES"].get_metrics("badbehavior")
# Format to fit [{code: 403, count: X}, {code: 401, count: Y} ...]
format_data = [{"code": int(key.split("_")[1]), "count": int(value)} for key, value in data.items()]
format_data.sort(key=itemgetter("count"), reverse=True)
return {"items": format_data}
return {"top_bad_behavior": format_data}
except:
return {"items": []}
return {"top_bad_behavior": "unknown"}
def badbehavior(**kwargs):
pass

View file

@ -7,53 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used and is_metrics %}
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
</div>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<!-- end info -->
<div data-fetch-success-show class="hidden core-card-list w-small">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">BAD BEHAVIOR LIST</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="w-small core-card-list-wrap">
<!-- header-->
<p class="core-card-list-header col-span-6">Error code</p>
<p class="core-card-list-header col-span-6">Count</p>
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
<li data-item class="core-card-list-item col-span-6">
<p data-name="code" class="core-card-list-item-content col-span-6"></p>
<p data-name="count" class="core-card-list-item-content col-span-6"></p>
</li>
</ul>
<!-- end list-->
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<!-- end list container-->
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["code", "count"],
},
});
</script>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -72,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,23 +1,20 @@
def blacklist(**kwargs):
keys = [
"counter_blacklist_url",
"counter_blacklist_ip",
"counter_blacklist_rdns",
"counter_blacklist_asn",
"counter_blacklist_ua",
]
def pre_render(**kwargs):
metrics = {
"counter_blacklist_url": {"value": "unknown", "title": "URL", "subtitle": "denied", "subtitle_color": "error", "svg_color": "red"},
"counter_blacklist_ip": {"value": "unknown", "title": "IP", "subtitle": "denied", "subtitle_color": "error", "svg_color": "orange"},
"counter_blacklist_rdns": {"value": "unknown", "title": "RDNS", "subtitle": "denied", "subtitle_color": "error", "svg_color": "amber"},
"counter_blacklist_asn": {"value": "unknown", "title": "ASN", "subtitle": "denied", "subtitle_color": "error", "svg_color": "emerald"},
"counter_blacklist_ua": {"value": "unknown", "title": "UA", "subtitle": "denied", "subtitle_color": "error", "svg_color": "pink"},
}
try:
data = kwargs["app"].config["INSTANCES"].get_metrics("blacklist")
for key in keys:
if data.get(key) is None:
data[key] = 0
return data
for key in metrics:
metrics[key]["value"] = data.get(key, 0)
return metrics
except:
data = {}
for key in keys:
data[key] = 0
return data
return metrics
def blacklist(**kwargs):
pass

View file

@ -7,158 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used and is_metrics %}
<div class="core-layout">
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
<!-- end info -->
</div>
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">URL</p>
<h5 data-count-url class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content error">denied</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container red">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-medium core-card-metrics-svg">
<path fill-rule="evenodd" d="M19.902 4.098a3.75 3.75 0 0 0-5.304 0l-4.5 4.5a3.75 3.75 0 0 0 1.035 6.037.75.75 0 0 1-.646 1.353 5.25 5.25 0 0 1-1.449-8.45l4.5-4.5a5.25 5.25 0 1 1 7.424 7.424l-1.757 1.757a.75.75 0 1 1-1.06-1.06l1.757-1.757a3.75 3.75 0 0 0 0-5.304Zm-7.389 4.267a.75.75 0 0 1 1-.353 5.25 5.25 0 0 1 1.449 8.45l-4.5 4.5a5.25 5.25 0 1 1-7.424-7.424l1.757-1.757a.75.75 0 1 1 1.06 1.06l-1.757 1.757a3.75 3.75 0 1 0 5.304 5.304l4.5-4.5a3.75 3.75 0 0 0-1.035-6.037.75.75 0 0 1-.354-1Z" clip-rule="evenodd" />
</svg>
</div>
<!-- end icon -->
</div>
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">IP</p>
<h5 data-count-ip class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content error">denied</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container lime">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg">
<path d="M3.53 2.47a.75.75 0 0 0-1.06 1.06l18 18a.75.75 0 1 0 1.06-1.06l-18-18ZM20.25 5.507v11.561L5.853 2.671c.15-.043.306-.075.467-.094a49.255 49.255 0 0 1 11.36 0c1.497.174 2.57 1.46 2.57 2.93ZM3.75 21V6.932l14.063 14.063L12 18.088l-7.165 3.583A.75.75 0 0 1 3.75 21Z" />
</svg>
</div>
<!-- end icon -->
</div>
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">RDNS</p>
<h5 data-count-rdns class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content error">denied</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-metrics-svg-container indigo">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-medium core-card-metrics-svg">
<path d="M11.625 16.5a1.875 1.875 0 1 0 0-3.75 1.875 1.875 0 0 0 0 3.75Z" />
<path fill-rule="evenodd" d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875Zm6 16.5c.66 0 1.277-.19 1.797-.518l1.048 1.048a.75.75 0 0 0 1.06-1.06l-1.047-1.048A3.375 3.375 0 1 0 11.625 18Z" clip-rule="evenodd" />
<path d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z" />
</svg>
</div>
<!-- end icon -->
</div>
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">ASN</p>
<h5 data-count-asn class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content error">denied</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container blue">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-medium core-card-metrics-svg">
<path d="M21.721 12.752a9.711 9.711 0 0 0-.945-5.003 12.754 12.754 0 0 1-4.339 2.708 18.991 18.991 0 0 1-.214 4.772 17.165 17.165 0 0 0 5.498-2.477ZM14.634 15.55a17.324 17.324 0 0 0 .332-4.647c-.952.227-1.945.347-2.966.347-1.021 0-2.014-.12-2.966-.347a17.515 17.515 0 0 0 .332 4.647 17.385 17.385 0 0 0 5.268 0ZM9.772 17.119a18.963 18.963 0 0 0 4.456 0A17.182 17.182 0 0 1 12 21.724a17.18 17.18 0 0 1-2.228-4.605ZM7.777 15.23a18.87 18.87 0 0 1-.214-4.774 12.753 12.753 0 0 1-4.34-2.708 9.711 9.711 0 0 0-.944 5.004 17.165 17.165 0 0 0 5.498 2.477ZM21.356 14.752a9.765 9.765 0 0 1-7.478 6.817 18.64 18.64 0 0 0 1.988-4.718 18.627 18.627 0 0 0 5.49-2.098ZM2.644 14.752c1.682.971 3.53 1.688 5.49 2.099a18.64 18.64 0 0 0 1.988 4.718 9.765 9.765 0 0 1-7.478-6.816ZM13.878 2.43a9.755 9.755 0 0 1 6.116 3.986 11.267 11.267 0 0 1-3.746 2.504 18.63 18.63 0 0 0-2.37-6.49ZM12 2.276a17.152 17.152 0 0 1 2.805 7.121c-.897.23-1.837.353-2.805.353-.968 0-1.908-.122-2.805-.353A17.151 17.151 0 0 1 12 2.276ZM10.122 2.43a18.629 18.629 0 0 0-2.37 6.49 11.266 11.266 0 0 1-3.746-2.504 9.754 9.754 0 0 1 6.116-3.985Z" />
</svg>
</div>
<!-- end icon -->
</div>
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">User Agent</p>
<h5 data-count-user-agent class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content error">denied</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container amber">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg">
<path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z" clip-rule="evenodd" />
</svg>
</div>
<!-- end icon -->
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
counter_blacklist_url: {
el: document.querySelector("[data-count-url]"),
value: "unknown",
type: "text",
},
counter_blacklist_ip: {
el: document.querySelector("[data-count-ip]"),
value: "unknown",
type: "text",
},
counter_blacklist_rdns: {
el: document.querySelector("[data-count-rdns]"),
value: "unknown",
type: "text",
},
counter_blacklist_asn: {
el: document.querySelector("[data-count-asn]"),
value: "unknown",
type: "text",
},
counter_blacklist_ua: {
el: document.querySelector("[data-count-user-agent]"),
value: "unknown",
type: "text",
},
});
</script>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -177,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,6 +1,10 @@
def bunkernet(**kwargs):
def pre_render(**kwargs):
try:
ping_data = kwargs["app"].config["INSTANCES"].get_ping("bunkernet")
return {"ping_status": ping_data["status"]}
return {"ping_status": {"title": "BUNKERNET STATUS", "value": ping_data["status"]}}
except:
return {"ping_status": "error"}
return {"ping_status": {"title": "BUNKERNET STATUS", "value": "error"}}
def bunkernet(**kwargs):
pass

View file

@ -7,44 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used %}
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
</div>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<!-- end info -->
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">STATUS</h5>
<svg data-status-svg
class="core-card-status-svg info"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
<p data-status-text class="core-card-text"></p>
</div>
<!-- end status -->
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
// value : active / inactive / unknown
ping_status: {
el: document.querySelector("[data-status-svg]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
});
</script>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -63,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,11 +1,19 @@
def cors(**kwargs):
def pre_render(**kwargs):
try:
data = kwargs["app"].config["INSTANCES"].get_metrics("cors")
if data.get("counter_failed_cors") is None:
data["counter_failed_cors"] = 0
return data
return {
"counter_failed_cors": {
"value": data.get("counter_failed_cors", 0),
"title": "CORS",
"subtitle": "request blocked",
"subtitle_color": "error",
"svg_color": "red",
}
}
except:
return {"counter_failed_cors": 0}
return {"counter_failed_cors": {"value": "unknown", "title": "CORS", "subtitle": "request blocked", "subtitle_color": "error", "svg_color": "red"}}
def cors(**kwargs):
pass

View file

@ -7,52 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used and is_metrics %}
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
</div>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<!-- end info -->
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">CORS</p>
<h5 data-count class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content error">request blocked</span>
</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container red">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
</svg>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
<!-- end icon -->
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
counter_failed_cors: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
});
</script>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -71,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,11 +1,20 @@
def country(**kwargs):
def pre_render(**kwargs):
try:
data = kwargs["app"].config["INSTANCES"].get_metrics("country")
if data.get("counter_failed_country") is None:
data["counter_failed_country"] = 0
return data
return {
"counter_failed_country": {
"value": data.get("counter_failed_country", 0),
"title": "Country",
"subtitle": "request blocked",
"subtitle_color": "error",
"svg_color": "red",
}
}
except:
return {"counter_failed_country": 0}
return {
"counter_failed_country": {"value": "unknown", "title": "Country", "subtitle": "request blocked", "subtitle_color": "error", "svg_color": "red"}
}
def country(**kwargs):
pass

View file

@ -7,52 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used and is_metrics %}
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
</div>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<!-- end info -->
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">Country</p>
<h5 data-count class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content error">request blocked</span>
</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container red">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
</svg>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
<!-- end icon -->
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
counter_failed_country: {
el: document.querySelector("[data-count]"),
value: "",
type: "text",
},
});
</script>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -71,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,11 +1,18 @@
def dnsbl(**kwargs):
def pre_render(**kwargs):
try:
data = kwargs["app"].config["INSTANCES"].get_metrics("dnsbl")
if data.get("counter_failed_dnsbl") is None:
data["counter_failed_dnsbl"] = 0
return data
return {
"counter_failed_dnsbl": {
"value": data.get("counter_failed_dnsbl", 0),
"title": "DNSBL",
"subtitle": "request blocked",
"subtitle_color": "error",
"svg_color": "red",
}
}
except:
return {"counter_failed_dnsbl": 0}
return {"counter_failed_dnsbl": {"value": "unknown", "title": "DNSBL", "subtitle": "request blocked", "subtitle_color": "error", "svg_color": "red"}}
def dnsbl(**kwargs):
pass

View file

@ -7,52 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used and is_metrics %}
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
</div>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<!-- end info -->
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">DNSBL</p>
<h5 data-count class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content error">request blocked</span>
</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container red">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
</svg>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
<!-- end icon -->
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
counter_failed_dnsbl: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
});
</script>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -71,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,13 +1,17 @@
from operator import itemgetter
def errors(**kwargs):
def pre_render(**kwargs):
try:
# Here we will have a list { 'counter_403': X, 'counter_401': Y ... }
data = kwargs["app"].config["INSTANCES"].get_metrics("errors")
# Format to fit [{code: 403, count: X}, {code: 401, count: Y} ...]
format_data = [{"code": int(key.split("_")[1]), "count": int(value)} for key, value in data.items()]
format_data.sort(key=itemgetter("count"), reverse=True)
return {"items": format_data}
return {"top_errors": format_data}
except:
return {"items": []}
return {"top_errors": []}
def errors(**kwargs):
pass

View file

@ -6,52 +6,98 @@
class="hidden"
hidden />
<div class="core-layout">
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
</div>
<!-- end info -->
<div data-fetch-success-show class="hidden core-card-list w-medium">
<div class="core-card-list-container">
<h5 class="core-card-list-title">ERRORS LIST</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap w-medium">
<!-- header-->
<p class="core-card-list-header col-span-8">Code error</p>
<p class="core-card-list-header col-span-4">Count</p>
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
<li data-item class="core-card-list-item">
<p data-name="code" class="core-card-list-item-content col-span-8"></p>
<p data-name="count" class="core-card-list-item-content col-span-4"></p>
</li>
</ul>
<!-- end list-->
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
<!-- end list container-->
</div>
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["code", "count"],
},
});
</script>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
</div>
{% endblock %}

View file

@ -1,11 +1,20 @@
def greylist(**kwargs):
def pre_render(**kwargs):
try:
data = kwargs["app"].config["INSTANCES"].get_metrics("greylist")
if data.get("counter_failed_greylist") is None:
data["counter_failed_greylist"] = 0
return data
return {
"counter_failed_greylist": {
"value": data.get("counter_failed_greylist", 0),
"title": "GREYLIST",
"subtitle": "request blocked",
"subtitle_color": "error",
"svg_color": "red",
}
}
except:
return {"counter_failed_greylist": 0}
return {
"counter_failed_greylist": {"value": "unknown", "title": "GREYLIST", "subtitle": "request blocked", "subtitle_color": "error", "svg_color": "red"}
}
def greylist(**kwargs):
pass

View file

@ -7,52 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used and is_metrics %}
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
</div>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<!-- end info -->
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">GREYLIST</p>
<h5 data-count class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content error">request blocked</span>
</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container red">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
</svg>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
<!-- end icon -->
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
counter_failed_greylist: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
});
</script>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -71,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,7 +1,7 @@
from operator import itemgetter
def limit(**kwargs):
def pre_render(**kwargs):
try:
# Here we will have a list { 'limit_uri_url1': X, 'limit_uri_url2': Y ... }
data = kwargs["app"].config["INSTANCES"].get_metrics("limit")
@ -15,6 +15,10 @@ def limit(**kwargs):
key = ""
format_data.append({"url": f"/{key}", "count": int(value)})
format_data.sort(key=itemgetter("count"), reverse=True)
return {"items": format_data}
return {"top_limit": format_data}
except:
return {"items": []}
return {"top_limit": []}
def limit(**kwargs):
pass

View file

@ -7,53 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used and is_metrics %}
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
</div>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<!-- end info -->
<div data-fetch-success-show class="hidden core-card-list w-large">
<div class="core-card-list-container">
<h5 class="core-card-list-title">LIMIT AND REQUEST LIST</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap w-large">
<!-- header-->
<p class="core-card-list-header col-span-8">URL</p>
<p class="core-card-list-header col-span-4">Count</p>
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
<li data-item class="core-card-list-item">
<p data-name="url" class="core-card-list-item-content col-span-8"></p>
<p data-name="count" class="core-card-list-item-content col-span-4"></p>
</li>
</ul>
<!-- end list-->
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<!-- end list container-->
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["url", "count"],
},
});
</script>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -72,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,14 +1,36 @@
def misc(**kwargs):
def pre_render(**kwargs):
try:
data = kwargs["app"].config["INSTANCES"].get_metrics("misc")
if "counter_failed_default" not in data:
data["counter_failed_default"] = 0
if "counter_failed_method" not in data:
data["counter_failed_method"] = 0
return data
return {
"counter_failed_default": {
"value": data.get("counter_failed_default", 0),
"title": "DEFAULT SERVER DISABLED",
"subtitle": "total",
"subtitle_color": "info",
"svg_color": "sky",
},
"counter_failed_method": {
"value": data.get("counter_failed_method", 0),
"title": "DISALLOWED METHODS",
"subtitle": "count",
"subtitle_color": "info",
"svg_color": "lime",
},
}
except:
return {"counter_failed_default": 0, "counter_failed_method": 0}
return {
"counter_failed_default": {
"value": "unknown",
"title": "DEFAULT SERVER DISABLED",
"subtitle": "total",
"subtitle_color": "info",
"svg_color": "sky",
},
"counter_failed_method": {"value": "unknown", "title": "DISALLOWED METHODS", "subtitle": "count", "subtitle_color": "info", "svg_color": "lime"},
}
def misc(**kwargs):
pass

View file

@ -7,79 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used and is_metrics %}
<div class="core-layout">
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
<!-- end info -->
</div>
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">DEFAULT SERVER DISABLED</p>
<h5 data-count-server-disabled class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content info">total</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container orange">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="scale-[0.55] core-card-metrics-svg">
<path d="M4.08 5.227A3 3 0 0 1 6.979 3H17.02a3 3 0 0 1 2.9 2.227l2.113 7.926A5.228 5.228 0 0 0 18.75 12H5.25a5.228 5.228 0 0 0-3.284 1.153L4.08 5.227Z" />
<path fill-rule="evenodd" d="M5.25 13.5a3.75 3.75 0 1 0 0 7.5h13.5a3.75 3.75 0 1 0 0-7.5H5.25Zm10.5 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm3.75-.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" clip-rule="evenodd" />
</svg>
</div>
<!-- end icon -->
</div>
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">DISALLOWED METHODS</p>
<h5 data-count-disallowed-methods class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content info">count</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container lime">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg">
<path d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z" />
</svg>
</div>
<!-- end icon -->
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
counter_failed_default: {
el: document.querySelector("[data-count-server-disabled]"),
value: "unknown",
type: "text",
},
counter_failed_method: {
el: document.querySelector("[data-count-disallowed-methods]"),
value: "unknown",
type: "text",
},
});
</script>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -98,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,20 +1,29 @@
def redis(**kwargs):
def pre_render(**kwargs):
ping = {}
data = {}
try:
ping_data = kwargs["app"].config["INSTANCES"].get_ping("redis")
ping = {"ping_status": ping_data["status"]}
ping = {"ping_status": {"title": "REDIS STATUS", "value": ping_data["status"]}}
except:
ping = {"ping_status": "error"}
ping = {"ping_status": {"title": "REDIS STATUS", "value": "error"}}
try:
metrics = kwargs["app"].config["INSTANCES"].get_metrics("redis")
data = {
"counter_redis_nb_keys": {
"value": metrics.get("redis_nb_keys", 0),
"title": "REDIS KEYS",
"subtitle": "total number",
"subtitle_color": "info",
"svg_color": "sky",
}
}
if metrics.get("redis_nb_keys") is None:
metrics["redis_nb_keys"] = 0
data = metrics
except:
data = {"redis_nb_keys": 0}
data = {"counter_redis_nb_keys": {"value": "unknown", "title": "REDIS KEYS", "subtitle": "total number", "subtitle_color": "info", "svg_color": "sky"}}
return ping | data
def redis(**kwargs):
pass

View file

@ -7,72 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used %}
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
</div>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<!-- end info -->
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">Keys</p>
<h5 data-count class="core-card-title">"unknown"</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content info">total number</span>
</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container sky">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="scale-[0.6] leading-none text-lg relative-sky-700 stroke-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.625 4.5h12.75a1.875 1.875 0 0 1 0 3.75H5.625a1.875 1.875 0 0 1 0-3.75Z" />
</svg>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
<!-- end icon -->
</div>
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">STATUS</h5>
<svg data-status-svg
class="core-card-status-svg info"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
<p data-status-text class="core-card-text"></p>
</div>
<!-- end status -->
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
redis_nb_keys: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
// value : active / inactive / unknown
ping_status: {
el: document.querySelector("[data-status-svg]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
});
</script>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -91,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,13 +1,17 @@
from operator import itemgetter
def reversescan(**kwargs):
def pre_render(**kwargs):
try:
# Here we will have a list { 'counter_403': X, 'counter_401': Y ... }
data = kwargs["app"].config["INSTANCES"].get_metrics("reversescan")
# Format to fit [{code: 403, count: X}, {code: 401, count: Y} ...]
format_data = [{"port": int(key.split("_")[1]), "count": int(value)} for key, value in data.items()]
format_data.sort(key=itemgetter("count"), reverse=True)
return {"items": format_data}
return {"top_reverse_scan": format_data}
except:
return {"items": []}
return {"top_reverse_scan": []}
def reversescan(**kwargs):
pass

View file

@ -7,53 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used and is_metrics %}
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
</div>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<!-- end info -->
<div data-fetch-success-show class="hidden core-card-list w-large">
<div class="core-card-list-container">
<h5 class="core-card-list-title">REVERSE SCAN LIST</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap w-large">
<!-- header-->
<p class="core-card-list-header col-span-5">Port</p>
<p class="core-card-list-header col-span-7">Block count</p>
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
<li data-item class="core-card-list-item">
<p data-name="port" class="core-card-list-item-content col-span-5"></p>
<p data-name="count" class="core-card-list-item-content col-span-7"></p>
</li>
</ul>
<!-- end list-->
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<!-- end list container-->
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["port", "count"],
},
});
</script>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -72,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -1,11 +1,27 @@
def whitelist(**kwargs):
def pre_render(**kwargs):
try:
data = kwargs["app"].config["INSTANCES"].get_metrics("whitelist")
if "counter_passed_whitelist" not in data:
data["counter_passed_whitelist"] = 0
return data
return {
"counter_passed_whitelist": {
"value": data.get("counter_passed_whitelist", 0),
"title": "WHITELIST",
"subtitle": "request passed",
"subtitle_color": "success",
"svg_color": "green",
}
}
except:
return {"counter_passed_whitelist": 0}
return {
"counter_passed_whitelist": {
"value": "unknown",
"title": "WHITELIST",
"subtitle": "request passed",
"subtitle_color": "success",
"svg_color": "green",
}
}
def whitelist(**kwargs):
pass

View file

@ -7,50 +7,99 @@
hidden />
<div class="core-layout">
{% if is_used and is_metrics %}
<!-- info-->
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text"></p>
</div>
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<!-- end info -->
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">WHITELIST</p>
<h5 data-count class="core-card-title"></h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content text-green-500 mx-0.5">request passed</span>
</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ok" %}
{% for key, value in pre_render["data"].items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">
<h5 class="core-card-status-title">{{ pre_render['data'][key].get('title', 'STATUS')}}</h5>
<svg data-status-svg
class="core-card-status-svg {{ 'fill-green-500' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'fill-red-500' }}"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="50" />
</svg>
</div>
<p data-status-text class="core-card-text">{{ 'Active' if pre_render['data'][key].get('value') in ('up', 'yes', 'success', 'true') else 'Inactive' }}</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container green">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-base core-card-metrics-svg">
<path fill-rule="evenodd" d="M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm13.36-1.814a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" clip-rule="evenodd" />
</svg>
{% endif %}
{% if key.startswith("count_") or key.startswith("counter_") %}
<div class="core-card-metrics">
<!-- text -->
<div>
<p class="core-card-metrics-name">{{pre_render['data'][key].get("title")}}</p>
<h5 data-count class="core-card-title">{{pre_render['data'][key].get("value")}}</h5>
<p class="core-card-metrics-subtitle">
<span class="core-card-metrics-subtitle-content {{pre_render['data'][key].get("subtitle_color", "info")}}">{{pre_render['data'][key].get("subtitle")}}</span>
</p>
</div>
<!-- end text -->
<!-- icon -->
<div role="img" class="core-card-svg-container {{pre_render['data'][key].get("svg_color")}}">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-small core-card-metrics-svg"
>
<path
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
/>
</svg>
</div>
<!-- end icon -->
</div>
<!-- end icon -->
</div>
<script nonce="{{script_nonce}}">
// Use SetupPlugin class that is on static/js/plugins/setup.js
const setPlugin = new SetupPlugin({
info: {
el: document.querySelector("[data-info]"),
value: "{{ plugin['description'] or ''}}",
type: "text",
},
counter_passed_whitelist: {
el: document.querySelector("[data-count]"),
value: "unknown",
type: "text",
},
});
</script>
{% endif %}
{% if (key.startswith("top_") and pre_render['data'][key]|length > 0) or (key.startswith("list_") and pre_render['data'][key]|length > 0) %}
<div class="core-card-list">
<div class="core-card-list-title-container">
<h5 class="core-card-list-title">{{ key.replace('_', ' ').upper()}}</h5>
</div>
<div class="core-card-list-container">
<!-- list container-->
<div class="core-card-list-wrap">
<!-- header-->
{% for val_key, val_value in pre_render['data'][key][0].items() %}
<p class="core-card-list-header {{'col-span-6' if pre_render['data'][key][0].keys()|length == 2 else "col-span-4" if pre_render['data'][key][0].keys()|length == 3 else "col-span-3" if pre_render['data'][key][0].keys()|length == 4}}">{{ val_key }}</p>
{% endfor%}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full">
{% for item in pre_render['data'][key] %}
<li class="core-card-list-item">
{% for top_key, top_value in item.items() %}
<p class="core-card-list-item-content {{'col-span-6' if item.keys()|length == 2 else "col-span-4" if item.keys()|length == 3 else "col-span-3" if item.keys()|length == 4}}">{{ top_value }}</p>
{% endfor %}
</li>
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% else %}
<div class="core-card">
<div class="core-card-wrap">
@ -69,7 +118,7 @@
<!-- end icon -->
</div>
<div class="core-card-text-container">
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
<p data-info class="core-card-text">This plugin need to be activated to access page.</p>
</div>
</div>
<!-- end info -->

View file

@ -293,6 +293,53 @@ def manage_bunkerweb(method: str, *args, operation: str = "reloads", is_draft: b
# UTILS
def run_action(plugin: str, function_name: str = ""):
message = ""
module = db.get_plugin_actions(plugin)
if module is None:
return {"status": "ko", "code": 404, "message": "The actions.py file for the plugin does not exist"}
try:
# Try to import the custom plugin
with NamedTemporaryFile(mode="wb", suffix=".py", delete=True) as temp:
temp.write(module)
temp.flush()
temp.seek(0)
loader = SourceFileLoader("actions", temp.name)
actions = loader.load_module()
except:
return {"status": "ko", "code": 500, "message": "An error occurred while importing the plugin, see logs for more details"}
res = None
try:
# Try to get the custom plugin custom function and call it
method = getattr(actions, function_name or plugin)
queries = request.args.to_dict()
try:
data = request.json or False
except:
data = {}
res = method(app=app, args=queries, data=data)
except AttributeError:
message = "The plugin does not have a method, see logs for more details"
except:
message = "An error occurred while executing the plugin, see logs for more details"
finally:
if sbin_nginx_path.is_file():
# Remove the custom plugin from the shared library
sys_path.pop()
sys_modules.pop("actions")
del actions
if message or not isinstance(res, dict) and not res:
return {"status": "ko", "code": 500, "message": message or "The plugin did not return a valid response"}
return {"status": "ok", "code": 200, "data": res}
def is_request_form(url_name: str, next: bool = False):
if not request.form:
flash("Missing form data.", "error")
@ -1362,7 +1409,6 @@ def upload_plugin():
@app.route("/plugins/<plugin>", methods=["GET", "POST"])
@login_required
def custom_plugin(plugin: str):
message = ""
if not plugin_id_rx.match(plugin):
return error_message("Invalid plugin id, (must be between 1 and 64 characters, only letters, numbers, underscores and hyphens)"), 400
@ -1462,60 +1508,29 @@ def custom_plugin(plugin: str):
is_used = True
break
# Get prerender from action.py
pre_render = run_action(plugin, "pre_render")
print(pre_render, flush=True)
return render_template(
Environment(loader=FileSystemLoader(join(sep, "usr", "share", "bunkerweb", "ui", "templates") + "/")).from_string(page.decode("utf-8")),
username=current_user.get_id(),
current_endpoint=plugin,
plugin=curr_plugin,
pre_render=pre_render,
is_used=is_used,
is_metrics=is_metrics_on,
**app.jinja_env.globals,
)
module = db.get_plugin_actions(plugin)
if module is None:
return error_message("The actions.py file for the plugin does not exist"), 404
try:
# Try to import the custom plugin
with NamedTemporaryFile(mode="wb", suffix=".py", delete=True) as temp:
temp.write(module)
temp.flush()
temp.seek(0)
loader = SourceFileLoader("actions", temp.name)
actions = loader.load_module()
except:
return error_message("An error occurred while importing the plugin, see logs for more details"), 500
res = None
try:
# Try to get the custom plugin custom function and call it
method = getattr(actions, plugin)
queries = request.args.to_dict()
try:
data = request.json or False
except:
data = {}
res = method(app=app, args=queries, data=data)
except AttributeError:
message = "The plugin does not have a method, see logs for more details"
except:
message = "An error occurred while executing the plugin, see logs for more details"
finally:
if sbin_nginx_path.is_file():
# Remove the custom plugin from the shared library
sys_path.pop()
sys_modules.pop("actions")
del actions
if message or not isinstance(res, dict) and not res:
return error_message(message or "The plugin did not return a valid response"), 500
action_result = run_action(plugin)
# case error
if action_result["status"] == "ko":
return error_message(action_result["message"]), action_result["code"]
app.logger.info(f"Plugin {plugin} action executed successfully")
return jsonify({"message": "ok", "data": res}), 200
return jsonify({"message": "ok", "data": action_result["data"]}), 200
@app.route("/cache", methods=["GET"])

View file

@ -473,12 +473,15 @@ class Instances:
try:
resp, instance_data = instance.data(plugin_endpoint)
except:
data.append({instance_name: {"status": "error"}})
continue
if not resp:
data.append({instance_name: {"status": "error"}})
continue
if instance_data[instance_name].get("status", "error") == "error":
data.append({instance_name: {"status": "error"}})
continue
data.append({instance_name: instance_data[instance_name].get("msg", {})})

File diff suppressed because one or more lines are too long

View file

@ -1,242 +0,0 @@
class SetupPlugin {
constructor(data, url = location.href) {
this.url = url;
// Set data defaults elements and variables
// Key of this.data need to match key of fetch data json object to update values
// type<str> : text (target el), list (el need to be first element of list)
// listNames<arr> : list of names key on item, need to set data-name="nameKey" on el
this.data = data;
/* EXAMPLE
{
info: {
el: document.querySelector("[data-info]"),
value: `Anti-bot technology is designed to detect and mitigate suspicious or
malicious bots, preventing them from reaching an organization's websites
or IT ecosystem.`,
type: "text",
},
items: {
el: document.querySelector("[data-item]"),
value: [],
type: "list",
listNames: ["server_name", "cn", "expire"],
},
// value : active / inactive / unknown
status: {
el: document.querySelector("[data-status]"),
value: "unknown",
type: "status",
textEl: document.querySelector("[data-status-text]"),
},
*/
// Hidden elements that will be shown on success, like ping buttons or list rendering
this.showOnSuccessEls = document.querySelectorAll(
"[data-fetch-success-show]",
);
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
this.createAlertEl();
// Set default values and fetch
this.updateDataDOM();
this.updateAlert("fetch");
fetch(this.url, {
method: "POST",
headers: {
"X-CSRFToken": document.querySelector('input[name="csrf_token"]')
.value,
},
})
.then((res) => res.json())
.then((res) => {
// Update data and DOM
this.getFetchDataByKey(res.data);
this.updateDataDOM();
// Show hidden elements
this.showSuccessEls();
// Feedback
this.updateAlert("success");
})
.catch((error) => {
this.updateAlert("error");
});
});
}
createAlertEl() {
// Container
this.alertEl = this.createEl(
"div",
[
["data-fetch", ""],
["role", "alert"],
],
"bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:shadow-dark-xl bg-clip-border",
"",
"",
);
// Status
this.alertStatusEl = this.createEl(
"h5",
[["data-fetch-status", ""]],
"text-lg mb-0 text-white dark:text-gray-300",
"Fetching",
this.alertEl,
);
this.alertMsgEl = this.createEl(
"p",
[["data-fetch-msg", ""]],
"text-white dark:text-gray-300 mb-0 text-sm",
"Please wait...",
this.alertEl,
);
document.body.appendChild(this.alertEl);
}
createEl(tag, attArr, className, text, parent) {
const el = document.createElement(tag);
attArr.forEach((att) => {
el.setAttribute(att[0], att[1]);
});
if (className) el.className = className;
if (text) el.textContent = text;
if (parent) parent.appendChild(el);
return el;
}
showSuccessEls() {
this.showOnSuccessEls.forEach((el) => {
el.classList.remove("hidden");
});
}
// Key of fetch data need to match key of this.data
getFetchDataByKey(fetchDataObj) {
for (const [key, value] of Object.entries(this.data)) {
// Case list
if (Array.isArray(fetchDataObj[key])) {
value["value"] = fetchDataObj[key] || value["value"] || "";
continue;
}
// Case number
if (!isNaN(fetchDataObj[key])) {
value["value"] = fetchDataObj[key] == 0 ? "0" : fetchDataObj[key];
continue;
}
// Others
value["value"] = fetchDataObj[key] || value["value"] || "";
}
}
updateDataDOM() {
for (const [key, val] of Object.entries(this.data)) {
const el = val["el"];
const type = val["type"];
const value = val["value"];
// Case text
if (type === "text") {
el.textContent = value || "";
continue;
}
// Case status
if (type === "status") {
const textEl = val["textEl"] || null;
if (
value === "active" ||
value === "up" ||
value === "yes" ||
value === "success" ||
value === "true"
) {
this.setStatus(el, textEl, "success", "Active");
continue;
}
if (
value === "inactive" ||
value === "down" ||
value === "no" ||
value === "error" ||
value === "false"
) {
this.setStatus(el, textEl, "error", "Inactive");
continue;
}
//default
this.setStatus(el, textEl, "info", "Unknown");
continue;
}
// Case list, we will render elements after the selected elements
if (type === "list") {
// Case no list to render
if (!value || value.length <= 0) continue;
// Clone item element
const itemEl = el.cloneNode(true);
itemEl.classList.remove("hidden");
const parentEl = el.parentNode;
// Add item element after selected element
const items = value.forEach((item) => {
const newItemEl = itemEl.cloneNode(true);
// Update item element values
for (const [nameKey, nameValue] of Object.entries(item)) {
newItemEl.querySelector(`[data-name="${nameKey}"]`).textContent =
nameValue;
}
// Add item element after selected element
parentEl.appendChild(newItemEl);
});
// Delete schema
el.remove();
continue;
}
}
}
setStatus(el, textEl, colorClass, text) {
el.classList.remove("success", "error", "info");
el ? el.classList.add(colorClass) : null;
textEl ? (textEl.textContent = text) : null;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove("bg-sky-500", "bg-green-500", "bg-red-500");
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch") return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}

View file

@ -0,0 +1,159 @@
class Ping {
constructor(
url = location.href,
statusTextEl = null,
statusColorEl = null,
key_to_check = "ping",
) {
this.url = url;
this.data = data;
this.statusColorEl = statusColorEl;
this.statusTextEl = statusTextEl;
this.key_to_check = key_to_check;
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
this.createAlertEl();
this.updateAlert("fetch");
// Case no status element
if (!this.statusColorEl || !this.statusTextEl)
return this.updateAlert("error");
fetch(this.url, {
method: "POST",
headers: {
"X-CSRFToken": document.querySelector('input[name="csrf_token"]')
.value,
},
})
.then((res) => res.json())
.then((res) => {
// Update data
this.updateEl(res);
})
.catch((error) => {
this.updateAlert("error");
});
});
}
createAlertEl() {
// Container
this.alertEl = this.createEl(
"div",
[
["data-fetch", ""],
["role", "alert"],
],
"bg-sky-500 p-4 mb-1 md:mb-3 md:mr-3 z-[1001] flex flex-col fixed bottom-0 right-0 w-full md:w-1/2 max-w-[300px] min-h-20 rounded-lg dark:brightness-110 hover:scale-102 transition shadow-md break-words dark:shadow-dark-xl bg-clip-border",
"",
"",
);
// Status
this.alertStatusEl = this.createEl(
"h5",
[["data-fetch-status", ""]],
"text-lg mb-0 text-white dark:text-gray-300",
"Fetching",
this.alertEl,
);
this.alertMsgEl = this.createEl(
"p",
[["data-fetch-msg", ""]],
"text-white dark:text-gray-300 mb-0 text-sm",
"Please wait...",
this.alertEl,
);
document.body.appendChild(this.alertEl);
}
createEl(tag, attArr, className, text, parent) {
const el = document.createElement(tag);
attArr.forEach((att) => {
el.setAttribute(att[0], att[1]);
});
if (className) el.className = className;
if (text) el.textContent = text;
if (parent) parent.appendChild(el);
return el;
}
// Key of fetch data need to match key of this.data
updateEl(data) {
try {
const successValues = [
"success",
"ok",
"200",
"201",
"202",
"203",
"204",
"205",
"206",
"207",
"208",
"226",
];
const isSuccess = successValues.includes(
data[this.key_to_check].toString(),
);
if (isSuccess) {
this.setStatus("success", "Success");
this.updateAlert("success");
}
if (!isSuccess) {
this.setStatus("error", "Error");
this.updateAlert("error");
}
} catch (e) {
this.setStatus("error", "Error");
this.updateAlert("error");
return;
}
// Feedback
this.updateAlert("success");
}
setStatus(colorClass, text) {
this.statusColorEl.classList.remove("success", "error", "info");
this.statusColorEl.classList.add(colorClass);
this.statusTextEl.textContent = text;
}
// Show fetch state alert
// type<str> : fetch, success, error
updateAlert(type) {
if (!type) return;
const [status, msg, color] = this.getAlertType(type);
this.alertEl.classList.remove("bg-sky-500", "bg-green-500", "bg-red-500");
this.alertStatusEl.textContent = status;
this.alertMsgEl.textContent = msg;
this.alertEl.classList.add(color);
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
getAlertType(type) {
if (type === "fetch") return ["Fetching", "Please wait...", "bg-sky-500"];
if (type === "error")
return ["Error", "Something went wrong", "bg-red-500"];
if (type === "success")
return ["Success", "Data fetched successfully", "bg-green-500"];
}
}

View file

@ -13,7 +13,7 @@
<link rel="stylesheet" type="text/css" href="./css/plugins.css" />
<link rel="stylesheet" type="text/css" href="./css/dashboard.css" />
<script type="module" src="./js/global.js" nonce="{{ script_nonce }}"></script>
<script src="./js/plugins/setup.js" nonce="{{ script_nonce }}"></script>
<script src="./js/plugins/utils.js" nonce="{{ script_nonce }}"></script>
<script async
src="./js/utils/purify/purify.min.js"
nonce="{{ script_nonce }}"></script>

View file

@ -57,7 +57,7 @@
{% if multList.append(value['multiple']) %}{% endif %}
{% endif %}
{% if not value['multiple'] %}
<div data-setting-container data-{{ current_endpoint }}-context="{{ value['context'] }}" class="relative mx-0 sm:mx-2 my-2 col-span-12 md:my-3 md:col-span-6 2xl:my-3 2xl:col-span-4" id="form-edit-{{ current_endpoint }}-{{ value["id"] }}">
<div data-setting-container data-{{ current_endpoint }}-context="{{ value['context'] }}" class="relative mx-0 sm:mx-2 md:mx-3 lg:mx-4 my-2 col-span-12 md:my-3 md:col-span-6 2xl:my-3 2xl:col-span-4" id="form-edit-{{ current_endpoint }}-{{ value["id"] }}">
<!-- title and info -->
<div class="flex items-center my-1 relative z-10">
<h5 class="input-title">{{ value["label"] }}</h5>