Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 58c2b9d996 | |||
| 2e8bcb630d | |||
| 338711181b | |||
| 43487f17e7 |
+1
-1
@@ -14,4 +14,4 @@ Install options:
|
||||
"""
|
||||
|
||||
__all__ = ["__version__"]
|
||||
__version__ = "5.2.5"
|
||||
__version__ = "5.2.6"
|
||||
|
||||
+6
-2
@@ -627,10 +627,12 @@ async def start(
|
||||
|
||||
gitea_button = ""
|
||||
if oauth_mod.is_enabled(config):
|
||||
logo_url = config.get("oauth", {}).get("gitea", {}).get("logo", "")
|
||||
logo_img = f'<img src="{logo_url}" alt="" class="gitea-logo">' if logo_url else ""
|
||||
gitea_button = f"""
|
||||
<div class="divider">or</div>
|
||||
<a href="/login/oauth/gitea" class="gitea-btn">
|
||||
Sign in with Gitea
|
||||
{logo_img}Sign in with Gitea
|
||||
</a>"""
|
||||
|
||||
html = f"""<!DOCTYPE html>
|
||||
@@ -654,10 +656,12 @@ async def start(
|
||||
.field {{ margin-bottom: .9em; }}
|
||||
.divider {{ text-align: center; margin: 1.2em 0 .8em; color: #999;
|
||||
font-size: .85em; border-top: 1px solid #eee; padding-top: .8em; }}
|
||||
.gitea-btn {{ display: block; width: 100%; padding: .6em; background: #609926;
|
||||
.gitea-btn {{ display: flex; align-items: center; justify-content: center;
|
||||
gap: .5em; width: 100%; padding: .6em; background: #16191d;
|
||||
color: #fff; border-radius: 4px; font-size: 1em; text-align: center;
|
||||
text-decoration: none; box-sizing: border-box; }}
|
||||
.gitea-btn:hover {{ background: #4e7d1e; }}
|
||||
.gitea-logo {{ height: 1.2em; width: auto; vertical-align: middle; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -401,7 +401,7 @@ def _build_url(host_name: str) -> str:
|
||||
base_url = _config.get("base_url", "").rstrip("/")
|
||||
if not base_url:
|
||||
return ""
|
||||
return f"{base_url}/plugins#{host_name}"
|
||||
return f"{base_url}/alerts?filter={host_name}"
|
||||
|
||||
|
||||
async def send_notification(host_name: str, notif: Notification) -> dict:
|
||||
|
||||
@@ -94,6 +94,24 @@
|
||||
border-color: #2196f3;
|
||||
}
|
||||
|
||||
.filter-input {
|
||||
padding: 7px 12px;
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 20px;
|
||||
font-size: 0.9em;
|
||||
outline: none;
|
||||
width: 200px;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.filter-input:focus {
|
||||
border-color: #2196f3;
|
||||
}
|
||||
|
||||
.filter-input.invalid {
|
||||
border-color: #f44336;
|
||||
}
|
||||
|
||||
.alerts-container {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
@@ -316,6 +334,7 @@
|
||||
<button class="filter-button active" onclick="filterAlerts('all')">All</button>
|
||||
<button class="filter-button" onclick="filterAlerts('critical')">Critical Only</button>
|
||||
<button class="filter-button" onclick="filterAlerts('warning')">Warning Only</button>
|
||||
<input id="host-filter" class="filter-input" type="text" placeholder="host filter (regex)" oninput="onHostFilterInput(this)">
|
||||
</div>
|
||||
|
||||
<div class="alerts-container">
|
||||
@@ -332,6 +351,7 @@
|
||||
<script>
|
||||
let currentFilter = 'all';
|
||||
let allAlerts = [];
|
||||
let hostFilterRe = null;
|
||||
|
||||
async function loadAlerts() {
|
||||
try {
|
||||
@@ -366,10 +386,13 @@
|
||||
// Filter alerts based on current filter
|
||||
let filteredAlerts = alerts;
|
||||
if (currentFilter !== 'all') {
|
||||
filteredAlerts = alerts.filter(alert =>
|
||||
filteredAlerts = filteredAlerts.filter(alert =>
|
||||
alert.level.toLowerCase() === currentFilter
|
||||
);
|
||||
}
|
||||
if (hostFilterRe) {
|
||||
filteredAlerts = filteredAlerts.filter(alert => hostFilterRe.test(alert.hostname));
|
||||
}
|
||||
|
||||
if (filteredAlerts.length === 0) {
|
||||
if (currentFilter === 'all' && alerts.length === 0) {
|
||||
@@ -538,9 +561,36 @@
|
||||
}
|
||||
}
|
||||
|
||||
function onHostFilterInput(input) {
|
||||
const val = input.value.trim();
|
||||
if (!val) {
|
||||
hostFilterRe = null;
|
||||
input.classList.remove('invalid');
|
||||
} else {
|
||||
try {
|
||||
hostFilterRe = new RegExp(val, 'i');
|
||||
input.classList.remove('invalid');
|
||||
} catch (_) {
|
||||
hostFilterRe = null;
|
||||
input.classList.add('invalid');
|
||||
}
|
||||
}
|
||||
renderAlerts(allAlerts);
|
||||
}
|
||||
|
||||
// Auto-refresh every 15 seconds
|
||||
setInterval(loadAlerts, 15000);
|
||||
|
||||
// Initialise filter from URL query string (?filter=...)
|
||||
(function () {
|
||||
const param = new URLSearchParams(window.location.search).get('filter');
|
||||
if (param) {
|
||||
const input = document.getElementById('host-filter');
|
||||
input.value = param;
|
||||
onHostFilterInput(input);
|
||||
}
|
||||
})();
|
||||
|
||||
// Initial load
|
||||
loadAlerts();
|
||||
</script>
|
||||
|
||||
+13
-2
@@ -1393,6 +1393,17 @@ class ThresholdChecker:
|
||||
else:
|
||||
self._check_renotify(host_name, alert_state, metric_path, value, threshold, plugin_data, check_name=check_name, metric_name=metric_name)
|
||||
|
||||
@staticmethod
|
||||
def _human_duration(seconds: float) -> str:
|
||||
s = int(seconds)
|
||||
if s < 120:
|
||||
return f"{s}s"
|
||||
if s < 3600:
|
||||
return f"{s // 60}m {s % 60}s"
|
||||
h, rem = divmod(s, 3600)
|
||||
m = rem // 60
|
||||
return f"{h}h {m}m" if m else f"{h}h"
|
||||
|
||||
def _check_renotify(
|
||||
self,
|
||||
host_name: str,
|
||||
@@ -1454,9 +1465,9 @@ class ThresholdChecker:
|
||||
check_name=check_name,
|
||||
metric_name=metric_name,
|
||||
)
|
||||
body = f"{value} {threshold_info}, ongoing for {int(now - alert_state.since)}s"
|
||||
body = f"{value} {threshold_info}, ongoing for {self._human_duration(now - alert_state.since)}"
|
||||
else:
|
||||
body = f"{value} (ongoing for {int(now - alert_state.since)}s)"
|
||||
body = f"{value} (ongoing for {self._human_duration(now - alert_state.since)})"
|
||||
message = f"REMINDER ({alert_state.level.name}): {host_name} - {short_path} = {body}"
|
||||
|
||||
from . import hbdclass
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "hbd"
|
||||
version = "5.2.5"
|
||||
version = "5.2.6"
|
||||
description = "Heartbeat monitoring system — client (hbc) and server (hbd)"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
|
||||
# updated by scripts/bumpminor.sh
|
||||
__version__ = "5.2.5"
|
||||
__version__ = "5.2.6"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Protocol (mirrors hbd/common/proto.py)
|
||||
|
||||
Reference in New Issue
Block a user