diff --git a/diagnostic/index.html b/diagnostic/index.html index c567151..a09d2c0 100644 --- a/diagnostic/index.html +++ b/diagnostic/index.html @@ -127,6 +127,18 @@ + +

ARIA Diagnostic

@@ -753,6 +765,11 @@ return; } + if (msg.type === 'disk_status') { + updateDiskBanner(msg); + return; + } + if (msg.type === 'mode' && msg.payload) { // Bridge hat den Modus geaendert (evtl. von anderer App/Diagnostic) β€” UI syncen const mode = (msg.payload.mode || '').toLowerCase(); @@ -2155,6 +2172,55 @@ const ttsToggleEl = document.getElementById('tts-debug-toggle'); if (ttsToggleEl) ttsToggleEl.checked = showTtsDebug; + // Disk-Space Banner aktualisieren (wird vom Server via disk_status gepusht) + function updateDiskBanner(status) { + const banner = document.getElementById('disk-banner'); + const icon = document.getElementById('disk-banner-icon'); + const text = document.getElementById('disk-banner-text'); + if (!banner) return; + if (!status || status.level === 'ok') { + banner.style.display = 'none'; + return; + } + const gb = (n) => (n / 1024 / 1024 / 1024).toFixed(1); + const pct = status.percent; + const used = gb(status.usedBytes); + const total = gb(status.totalBytes); + const avail = gb(status.availBytes); + let bg, col, msg; + if (status.level === 'critical') { + bg = '#5C1A1A'; col = '#FF6B6B'; icon.innerHTML = '🚨'; // 🚨 + msg = `KRITISCH: Platte ${pct}% voll (${used}GB von ${total}GB, nur noch ${avail}GB frei). aria-core kann bald nicht mehr schreiben β€” sofort aufraeumen!`; + } else if (status.level === 'warn') { + bg = '#5C3A1A'; col = '#FFAA55'; icon.innerHTML = '⚠️'; // ⚠️ + msg = `Warnung: Platte ${pct}% voll (${avail}GB frei). Bald aufraeumen.`; + } else { + bg = '#4A3A1A'; col = '#FFD60A'; icon.innerHTML = 'ℹ️'; // ℹ️ + msg = `Hinweis: Platte ${pct}% voll (${avail}GB frei).`; + } + banner.style.background = bg; + banner.style.color = col; + banner.style.borderBottom = `2px solid ${col}`; + text.textContent = msg; + banner.style.display = 'block'; + } + + function copyDiskCmd() { + const cmd = document.getElementById('disk-banner-cmd').textContent.trim(); + navigator.clipboard.writeText(cmd).then(() => { + const btn = event.target; + const old = btn.textContent; + btn.textContent = 'Kopiert!'; + setTimeout(() => { btn.textContent = old; }, 1500); + }).catch(() => { + // Fallback: selektieren + const range = document.createRange(); + range.selectNode(document.getElementById('disk-banner-cmd')); + window.getSelection().removeAllRanges(); + window.getSelection().addRange(range); + }); + } + connectWS(); diff --git a/diagnostic/server.js b/diagnostic/server.js index 3738144..56f6287 100644 --- a/diagnostic/server.js +++ b/diagnostic/server.js @@ -1148,6 +1148,53 @@ function updateAgentActivity() { watchdogWarned = false; } +// ── Disk-Space Monitor ─────────────────────────────── +// Prueft regelmaessig die Host-Disk (via gemountetem /shared) und +// broadcastet bei kritischen Schwellwerten ein disk_status Event. +let lastDiskStatus = null; +let currentDiskStatus = null; // Vollstaendig fuer neu verbundene Clients +function checkDiskSpace() { + const { exec } = require("child_process"); + exec("df -B1 /shared", (err, stdout) => { + if (err) return; + const lines = stdout.trim().split("\n"); + if (lines.length < 2) return; + const cols = lines[1].split(/\s+/); + // Filesystem Size Used Avail Use% MountedOn + const total = parseInt(cols[1], 10); + const used = parseInt(cols[2], 10); + const avail = parseInt(cols[3], 10); + if (!total) return; + const pct = Math.round((used / total) * 100); + let level = "ok"; + if (pct >= 95) level = "critical"; + else if (pct >= 85) level = "warn"; + else if (pct >= 70) level = "info"; + const status = { + type: "disk_status", + level, + percent: pct, + usedBytes: used, + totalBytes: total, + availBytes: avail, + }; + currentDiskStatus = status; + // Nur broadcasten wenn sich was geaendert hat (oder alle 60s Refresh) + const key = `${level}-${pct}`; + if (lastDiskStatus !== key) { + lastDiskStatus = key; + broadcast(status); + if (level !== "ok") { + log(level === "critical" ? "error" : "warn", "server", + `Disk ${pct}% belegt (${(used/1024/1024/1024).toFixed(1)}GB von ${(total/1024/1024/1024).toFixed(1)}GB)`); + } + } + }); +} +// Beim Start + alle 30s +setTimeout(checkDiskSpace, 2000); +setInterval(checkDiskSpace, 30000); + // Watchdog prΓΌft alle 30s ob ARIA nach einer gesendeten Nachricht reagiert setInterval(async () => { if (pendingMessageTime === 0) return; // Keine Nachricht gesendet @@ -1281,6 +1328,8 @@ wss.on("connection", (ws) => { browserClients.add(ws); // Initialen State + letzte Logs senden ws.send(JSON.stringify({ type: "init", state, logs: logs.slice(-100) })); + // Letzten Disk-Status mitgeben damit der Client sofort weiss wie's um Platz steht + if (currentDiskStatus) ws.send(JSON.stringify(currentDiskStatus)); ws.on("message", (raw) => { try {