/* AES-Suite Live-Monitoring — pollt monitor.json und rendert Dienste + Backends grafisch. Von /static ausgeliefert (CSP script-src 'self'). */ (function () { "use strict"; var root = document.getElementById("aes-monitor"); if (!root) return; var src = root.getAttribute("data-src") || "/erweiterungen/aes/monitor.json"; var INTERVAL = 5000; function el(tag, cls, text) { var e = document.createElement(tag); if (cls) e.className = cls; if (text != null) e.textContent = text; return e; } function bar(label, pct) { var wrap = el("div", "sz"); wrap.style.margin = "2px 0"; var v = Math.max(0, Math.min(100, Math.round(pct || 0))); wrap.appendChild(document.createTextNode(label + " ")); var track = el("span"); track.style.display = "inline-block"; track.style.width = "120px"; track.style.height = "8px"; track.style.borderRadius = "4px"; track.style.background = "var(--border, #333)"; track.style.verticalAlign = "middle"; track.style.margin = "0 6px"; var fill = el("span"); fill.style.display = "block"; fill.style.height = "8px"; fill.style.borderRadius = "4px"; fill.style.width = v + "%"; fill.style.background = v > 85 ? "#d9534f" : (v > 60 ? "#e0a800" : "#3fb950"); track.appendChild(fill); wrap.appendChild(track); wrap.appendChild(document.createTextNode(v + "%")); return wrap; } function render(data) { root.textContent = ""; // Dienst-Ampeln var svcRow = el("div"); svcRow.style.display = "flex"; svcRow.style.flexWrap = "wrap"; svcRow.style.gap = "10px"; (data.services || []).forEach(function (s) { var card = el("div", "card"); card.style.flex = "1"; card.style.minWidth = "180px"; var head = el("div"); head.style.display = "flex"; head.style.justifyContent = "space-between"; head.style.alignItems = "center"; head.appendChild(el("b", null, s.name)); head.appendChild(el("span", "badge " + (s.ok ? "ok" : "err"), s.ok ? "up" : "down")); card.appendChild(head); card.appendChild(el("div", "sz", s.detail || "")); svcRow.appendChild(card); }); root.appendChild(svcRow); // Backends var h = el("h3", null, "Backends / Agenten"); h.style.margin = "16px 0 6px"; root.appendChild(h); var bes = data.backends || []; if (!bes.length) { root.appendChild(el("p", "sz", "Keine Backends online.")); } else { bes.forEach(function (b) { var card = el("div", "card"); var head = el("div"); head.style.display = "flex"; head.style.justifyContent = "space-between"; head.appendChild(el("b", null, b.url)); head.appendChild(el("span", "badge " + (b.healthy ? "ok" : "err"), b.healthy ? "healthy" : "offline")); card.appendChild(head); card.appendChild(bar("CPU", b.cpu)); card.appendChild(bar("RAM", b.ram)); card.appendChild(bar("VRAM", b.vram)); var meta = el("div", "sz"); meta.textContent = "Latenz " + (b.latency || 0) + "ms · Agent " + (b.agent_online ? "online" : "offline") + " · Modelle: " + ((b.models && b.models.length) ? b.models.join(", ") : "-"); card.appendChild(meta); root.appendChild(card); }); } // Runner + Projekte + Zeitstempel var foot = el("p", "sz"); foot.style.marginTop = "10px"; var r = data.runner || {}; foot.textContent = "Runner: " + (r.available ? "aktiv" : "offline") + " · beobachtete Repos: " + ((r.watched && r.watched.length) || 0) + " · Projekte: " + (data.projects_total || 0) + " · Stand: " + new Date().toLocaleTimeString(); root.appendChild(foot); } function tick() { fetch(src, { headers: { "Accept": "application/json" }, credentials: "same-origin" }) .then(function (r) { return r.json(); }) .then(render) .catch(function () { var p = document.getElementById("aes-monitor-err"); if (!p) { p = el("p", "err", "Live-Daten momentan nicht abrufbar — naechster Versuch laeuft."); p.id = "aes-monitor-err"; root.appendChild(p); } }); } tick(); setInterval(tick, INTERVAL); })();