feat(nina): aktive Warnmeldungen beim Seitenaufruf laden (GET /api/nina/alerts)
_active speichert jetzt msgType/area/monitor_only. get_active_alerts() gibt sortierte Liste zurück. nina.js lädt beim Init und dedupliziert per ID. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6adc3cedcf
commit
f36a126200
|
|
@ -1,5 +1,14 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [0.8.9] - 2026-02-19
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- **NINA aktive Warnmeldungen beim Seitenaufruf laden**: Neuer Endpunkt
|
||||||
|
`GET /api/nina/alerts` gibt alle aktuell aktiven Warnungen aus `_active` zurück.
|
||||||
|
Die NINA-Seite lädt diese beim Init und zeigt sie in der Tabelle an — auch
|
||||||
|
Warnungen die bereits vor dem Seitenaufruf ins Mesh gesendet wurden.
|
||||||
|
Neu eintreffende WS-Events (`nina_alert`) werden per ID dedupliziert.
|
||||||
|
|
||||||
## [0.8.8] - 2026-02-19
|
## [0.8.8] - 2026-02-19
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
version: "0.8.8"
|
version: "0.8.9"
|
||||||
|
|
||||||
bot:
|
bot:
|
||||||
name: "MeshDD-Bot"
|
name: "MeshDD-Bot"
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,9 @@ class NinaBot:
|
||||||
def get_config(self) -> dict:
|
def get_config(self) -> dict:
|
||||||
return {**self.config, "last_poll": self._last_poll}
|
return {**self.config, "last_poll": self._last_poll}
|
||||||
|
|
||||||
|
def get_active_alerts(self) -> list[dict]:
|
||||||
|
return sorted(self._active.values(), key=lambda x: x.get("sent", ""), reverse=True)
|
||||||
|
|
||||||
def update_config(self, updates: dict) -> dict:
|
def update_config(self, updates: dict) -> dict:
|
||||||
if "sources" in updates:
|
if "sources" in updates:
|
||||||
self.config["sources"] = {
|
self.config["sources"] = {
|
||||||
|
|
@ -444,8 +447,11 @@ class NinaBot:
|
||||||
"channel": channel,
|
"channel": channel,
|
||||||
"headline": headline,
|
"headline": headline,
|
||||||
"severity": severity,
|
"severity": severity,
|
||||||
|
"msgType": msg_type,
|
||||||
"id": identifier,
|
"id": identifier,
|
||||||
"sent": sent,
|
"sent": sent,
|
||||||
|
"area": area,
|
||||||
|
"monitor_only": not self.config.get("send_to_mesh", True),
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.config.get("send_to_mesh", True):
|
if self.config.get("send_to_mesh", True):
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,7 @@ class WebServer:
|
||||||
self.app.router.add_get("/api/node/config", self._api_node_config)
|
self.app.router.add_get("/api/node/config", self._api_node_config)
|
||||||
self.app.router.add_get("/api/nina/config", self._api_nina_get)
|
self.app.router.add_get("/api/nina/config", self._api_nina_get)
|
||||||
self.app.router.add_put("/api/nina/config", self._api_nina_update)
|
self.app.router.add_put("/api/nina/config", self._api_nina_update)
|
||||||
|
self.app.router.add_get("/api/nina/alerts", self._api_nina_alerts)
|
||||||
self.app.router.add_get("/login", self._serve_login)
|
self.app.router.add_get("/login", self._serve_login)
|
||||||
self.app.router.add_get("/register", self._serve_login)
|
self.app.router.add_get("/register", self._serve_login)
|
||||||
self.app.router.add_get("/admin", self._serve_admin)
|
self.app.router.add_get("/admin", self._serve_admin)
|
||||||
|
|
@ -222,6 +223,12 @@ class WebServer:
|
||||||
asyncio.create_task(self.nina.trigger_poll())
|
asyncio.create_task(self.nina.trigger_poll())
|
||||||
return web.json_response(cfg)
|
return web.json_response(cfg)
|
||||||
|
|
||||||
|
async def _api_nina_alerts(self, request: web.Request) -> web.Response:
|
||||||
|
require_admin_api(request)
|
||||||
|
if not self.nina:
|
||||||
|
return web.json_response([])
|
||||||
|
return web.json_response(self.nina.get_active_alerts())
|
||||||
|
|
||||||
async def _api_scheduler_get(self, request: web.Request) -> web.Response:
|
async def _api_scheduler_get(self, request: web.Request) -> web.Response:
|
||||||
if not self.scheduler:
|
if not self.scheduler:
|
||||||
return web.json_response([], status=200)
|
return web.json_response([], status=200)
|
||||||
|
|
|
||||||
|
|
@ -241,11 +241,30 @@ function renderAlerts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function addAlert(alert) {
|
function addAlert(alert) {
|
||||||
|
// Replace existing entry with same id (dedup)
|
||||||
|
const idx = alerts.findIndex(a => a.id === alert.id);
|
||||||
|
if (idx !== -1) alerts.splice(idx, 1);
|
||||||
alerts.unshift(alert);
|
alerts.unshift(alert);
|
||||||
if (alerts.length > MAX_ALERTS) alerts.pop();
|
if (alerts.length > MAX_ALERTS) alerts.pop();
|
||||||
renderAlerts();
|
renderAlerts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadAlerts() {
|
||||||
|
try {
|
||||||
|
const resp = await fetch('/api/nina/alerts');
|
||||||
|
if (!resp.ok) return;
|
||||||
|
const data = await resp.json();
|
||||||
|
if (!Array.isArray(data) || data.length === 0) return;
|
||||||
|
data.forEach(a => {
|
||||||
|
alerts.push(a);
|
||||||
|
});
|
||||||
|
if (alerts.length > MAX_ALERTS) alerts.length = MAX_ALERTS;
|
||||||
|
renderAlerts();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('NINA alerts load failed:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── WebSocket ─────────────────────────────────────────────────────────────────
|
// ── WebSocket ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function connectWebSocket() {
|
function connectWebSocket() {
|
||||||
|
|
@ -267,4 +286,5 @@ function connectWebSocket() {
|
||||||
|
|
||||||
fillDatalist();
|
fillDatalist();
|
||||||
loadConfig();
|
loadConfig();
|
||||||
|
loadAlerts();
|
||||||
connectWebSocket();
|
connectWebSocket();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue