Merge pull request 'Letzte erfolgreiche Ping-Prüfung im System-Monitor-Modal anzeigen (Issue #57)' (#58) from fds2610-bot/monitor:main into main

Reviewed-on: https://codeberg.org/fds2610/monitor/pulls/58
This commit is contained in:
fds2610 2026-08-21 08:13:32 +02:00
commit 988a145e31
3 changed files with 54 additions and 2 deletions

View file

@ -1254,7 +1254,11 @@ function renderSystemModalBody(body, content) {
for (const item of content.items) { for (const item of content.items) {
const li = document.createElement("li"); const li = document.createElement("li");
li.className = `office-modal-item status-${item.status}`; li.className = `office-modal-item status-${item.status}`;
li.textContent = `${item.name} (${item.type}) – ${item.status === "ok" ? "OK" : "Fehler"}${item.detail ? `: ${item.detail}` : ""}`; const isReachable = item.status === "ok";
const lastCheckText = isReachable && item.lastCheckAt
? ` · zuletzt erfolgreich geprüft: ${item.lastCheckAt}`
: "";
li.textContent = `${item.name} (${item.type}) – ${isReachable ? "OK" : "Fehler"}${item.detail ? `: ${item.detail}` : ""}${lastCheckText}`;
list.appendChild(li); list.appendChild(li);
} }
body.appendChild(list); body.appendChild(list);

View file

@ -329,11 +329,38 @@ export function buildSystemModalContent(systems = []) {
status: system.status, status: system.status,
type: system.type || "host", type: system.type || "host",
detail: system.detail || "", detail: system.detail || "",
lastCheckAt: system.last_check_at, lastCheckAt: formatSystemCheckTime(system.last_check_at),
})), })),
}; };
} }
/**
* Formatiert last_check_at eines Systems (GET /api/v1/systems, Issue #57: "wann der letzte
* erfolgreiche Ping war") fuers Modal. Zwei Quellformate kommen vor: ISO-8601 UTC (per API
* gemeldete Systeme) und "YYYY-MM-DD HH:MM" ohne Zeitzone (Legacy-Import aus status.txt,
* geschrieben von check_fds.sh in Serverzeit) - beide werden von `Date` geparst, fehlende/
* ungueltige Werte liefern `null` statt eines irrefuehrenden Zeitstempels.
*
* @param {string|null|undefined} value
* @returns {string|null}
*/
export function formatSystemCheckTime(value) {
if (!value) {
return null;
}
const date = new Date(value);
if (Number.isNaN(date.getTime())) {
return null;
}
return date.toLocaleString("de-DE", {
day: "2-digit",
month: "2-digit",
year: "2-digit",
hour: "2-digit",
minute: "2-digit",
});
}
/** /**
* Erzeugt die Punkte-Liste (SVG <polyline points="...">) fuer eine einfache * Erzeugt die Punkte-Liste (SVG <polyline points="...">) fuer eine einfache
* KPI-Sparkline - bewusst ohne Chart-Lib gezeichnet (F10). Die API liefert die Historie * KPI-Sparkline - bewusst ohne Chart-Lib gezeichnet (F10). Die API liefert die Historie

View file

@ -20,6 +20,7 @@ import {
EVENT_LINE_CHAR_WIDTH, EVENT_LINE_CHAR_WIDTH,
buildEventModalContent, buildEventModalContent,
buildSystemModalContent, buildSystemModalContent,
formatSystemCheckTime,
buildKpiSparklinePoints, buildKpiSparklinePoints,
buildKpiTiles, buildKpiTiles,
buildKpiModalContent, buildKpiModalContent,
@ -335,6 +336,26 @@ test("buildSystemModalContent uebernimmt Status/Detail je System", () => {
]); ]);
assert.equal(content.items[0].status, "ok"); assert.equal(content.items[0].status, "ok");
assert.equal(content.items[0].detail, ""); assert.equal(content.items[0].detail, "");
assert.equal(content.items[0].lastCheckAt, null);
});
test("buildSystemModalContent formatiert einen gueltigen last_check_at (Issue #57)", () => {
const content = buildSystemModalContent([
{ id: "srv1", name: "Webserver", status: "ok", type: "host", detail: "1.2.3.4", last_check_at: "2026-08-20 18:40" },
]);
assert.match(content.items[0].lastCheckAt, /^\d{2}\.\d{2}\.\d{2}, \d{2}:\d{2}$/);
});
test("formatSystemCheckTime akzeptiert ISO-8601 (API) und 'YYYY-MM-DD HH:MM' (Legacy-status.txt)", () => {
assert.match(formatSystemCheckTime("2026-07-27T10:00:00Z"), /^\d{2}\.\d{2}\.\d{2}, \d{2}:\d{2}$/);
assert.match(formatSystemCheckTime("2026-08-20 18:40"), /^\d{2}\.\d{2}\.\d{2}, \d{2}:\d{2}$/);
});
test("formatSystemCheckTime liefert null fuer fehlende/ungueltige Werte", () => {
assert.equal(formatSystemCheckTime(null), null);
assert.equal(formatSystemCheckTime(undefined), null);
assert.equal(formatSystemCheckTime(""), null);
assert.equal(formatSystemCheckTime("nicht-valide"), null);
}); });
test("buildKpiSparklinePoints liefert leeren String ohne Historie und einen Punkt pro Messwert sonst", () => { test("buildKpiSparklinePoints liefert leeren String ohne Historie und einen Punkt pro Messwert sonst", () => {