From c16c4b1e46359c687391f37329a1030162d091be Mon Sep 17 00:00:00 2001 From: fds2610-bot Date: Thu, 20 Aug 2026 20:49:33 +0200 Subject: [PATCH] =?UTF-8?q?Letzte=20erfolgreiche=20Ping-Pr=C3=BCfung=20im?= =?UTF-8?q?=20System-Monitor-Modal=20anzeigen=20(Issue=20#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die per PING ueberwachten lokalen Server (check_fds.sh -> status.txt -> LegacyStatusFileReader -> SystemRepository) landen bereits als Kacheln im System-Monitor, dem mittleren der drei Monitore an der Buero-Wand - das ist exakt der "alte Monitor" aus dem Issue. GET /api/v1/systems liefert last_check_at dafuer schon mit, das Klick-Modal hat den Wert aber bisher weder abgeholt noch angezeigt. buildSystemModalContent() (office-scene.js) formatiert last_check_at jetzt ueber die neue formatSystemCheckTime() - sie kommt sowohl mit ISO-8601 (API-gemeldete Systeme) als auch mit dem "YYYY-MM-DD HH:MM"-Format der Legacy-status.txt klar. renderSystemModalBody() (office-render.js) zeigt den Zeitpunkt nur bei Status "ok" an ("zuletzt erfolgreich geprüft: ..."), da nur dann Erreichbarkeit UND Ping-Erfolg zusammenfallen - bei "Fehler" bliebe eine Zeitangabe ohne Aenderung an check_fds.sh irrefuehrend. Bewusst nicht Teil dieser Aenderung: Die im Issue zusaetzlich genannte Portscan-Liste (Detailseite fuer weitere, nur manuell zu pruefende Server) ist als "koennten" markiert und ohne konkrete Vorgabe zu unscharf fuer eine automatisierte Umsetzung. Die sechs konkret genannten Hosts/IPs gehoeren in die lokale, nicht versionierte hosts.conf (Analogon zu credentials.conf) und damit auf den Produktivserver, nicht in dieses oeffentliche Repo. Co-Authored-By: Claude Sonnet 5 --- html/js/office-render.js | 6 +++++- html/js/office-scene.js | 29 ++++++++++++++++++++++++++++- tests/frontend/office-scene.test.js | 21 +++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/html/js/office-render.js b/html/js/office-render.js index 0674439..c32a0e3 100644 --- a/html/js/office-render.js +++ b/html/js/office-render.js @@ -1254,7 +1254,11 @@ function renderSystemModalBody(body, content) { for (const item of content.items) { const li = document.createElement("li"); 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); } body.appendChild(list); diff --git a/html/js/office-scene.js b/html/js/office-scene.js index d686766..ff531ee 100644 --- a/html/js/office-scene.js +++ b/html/js/office-scene.js @@ -329,11 +329,38 @@ export function buildSystemModalContent(systems = []) { status: system.status, type: system.type || "host", 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 ) fuer eine einfache * KPI-Sparkline - bewusst ohne Chart-Lib gezeichnet (F10). Die API liefert die Historie diff --git a/tests/frontend/office-scene.test.js b/tests/frontend/office-scene.test.js index 431eb06..8620fc8 100644 --- a/tests/frontend/office-scene.test.js +++ b/tests/frontend/office-scene.test.js @@ -20,6 +20,7 @@ import { EVENT_LINE_CHAR_WIDTH, buildEventModalContent, buildSystemModalContent, + formatSystemCheckTime, buildKpiSparklinePoints, buildKpiTiles, buildKpiModalContent, @@ -335,6 +336,26 @@ test("buildSystemModalContent uebernimmt Status/Detail je System", () => { ]); assert.equal(content.items[0].status, "ok"); 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", () => {