feat(alerts): a sentence per alert, a link to what it watches, a count in the sidebar (#5124)
The Alerts page's active list now says each alert in one line around the resource it watches, e.g. "cpu_pct on web-1 is 94% (> 90%), firing for 12m", with web-1 linking to its host, monitor or container page (or the plugin's page for Traefik, UniFi and SNMP). Values take their unit from the metric name through formatting.metric_value. A silent series reads "No cpu_pct from web-1 for 20m"; an up/down metric says the word. Actions follow the routes' roles: acknowledge, edit, toggle and New rule for operators and admins, Delete for admins only, nothing for viewers. The sidebar shows how many alerts need attention beside Alerts, from an async context processor that skips htmx fragments, logged-out pages and the database-down page, and renders without the count if the query fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+19
-1
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from quart import Quart, render_template, request
|
||||
from quart import Quart, g, render_template, request
|
||||
from .config import load_bootstrap
|
||||
from .database import (
|
||||
init_db, ensure_database_reachable, DatabaseUnavailable,
|
||||
@@ -269,6 +269,23 @@ def create_app(
|
||||
"undecryptable_secrets": get_undecryptable_secrets(),
|
||||
}
|
||||
|
||||
@app.context_processor
|
||||
async def _inject_alert_count():
|
||||
"""The sidebar's count of alerts needing attention. Full pages only:
|
||||
an htmx fragment has no sidebar, and a logged-out page has no nav."""
|
||||
from quart import session
|
||||
if (request.headers.get("HX-Request") or not session.get("user_id")
|
||||
or g.get("database_down")):
|
||||
return {}
|
||||
try:
|
||||
from .alerts.service import active_count
|
||||
async with app.db_sessionmaker() as db:
|
||||
return {"active_alert_count": await active_count(db)}
|
||||
except Exception:
|
||||
# A count is decoration; the page renders without it.
|
||||
logging.getLogger(__name__).exception("Active alert count failed")
|
||||
return {}
|
||||
|
||||
# ── 11. Share-token middleware ─────────────────────────────────────────────
|
||||
@app.before_request
|
||||
async def _validate_share_token():
|
||||
@@ -312,6 +329,7 @@ def create_app(
|
||||
except DatabaseUnavailable as exc:
|
||||
logger.error("Database unreachable while serving %s: %s",
|
||||
request.path, exc)
|
||||
g.database_down = True
|
||||
return await render_template(
|
||||
"errors/database_unavailable.html"), 503
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user