fix(diagnostic): 'Sicher aufraeumen' fuehrt wirklich auf statt nur zu kopieren
Der Button rief copyDiskCmd() → kopierte nur den docker-Befehl in die Zwischenablage (ueber http scheitert navigator.clipboard oft still → 'keine Funktion'). Jetzt fuehrt runDiskCleanup() das Aufraeumen echt aus: Server-seitig ueber die Docker-Daemon-API (Socket ist gemountet) — /build/prune?all=true + /images/prune (dangling=false), ohne Volumes = keine Daten weg. 'Aggressiv' (zusaetzlich Container+Volumes) als eigener 'Jetzt ausfuehren'-Button mit Warnung. Rueckmeldung: wieviel frei wurde; Banner aktualisiert sich per periodischem disk_status. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+28
-1
@@ -198,7 +198,7 @@
|
|||||||
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
|
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
|
||||||
<span id="disk-banner-icon" style="font-size:18px;">⚠️</span>
|
<span id="disk-banner-icon" style="font-size:18px;">⚠️</span>
|
||||||
<span id="disk-banner-text" style="flex:1;min-width:200px;font-weight:600;"></span>
|
<span id="disk-banner-text" style="flex:1;min-width:200px;font-weight:600;"></span>
|
||||||
<button onclick="copyDiskCmd('safe')" class="btn secondary" style="padding:4px 10px;font-size:11px;" title="docker builder prune -a -f && docker image prune -a -f">
|
<button id="disk-clean-btn" onclick="runDiskCleanup('safe')" class="btn secondary" style="padding:4px 10px;font-size:11px;" title="Fuehrt aus: docker builder prune -a && docker image prune -a (ohne Volumes)">
|
||||||
Sicher aufraeumen
|
Sicher aufraeumen
|
||||||
</button>
|
</button>
|
||||||
<button onclick="document.getElementById('disk-banner-aggressive').style.display=(document.getElementById('disk-banner-aggressive').style.display==='none'?'flex':'none')"
|
<button onclick="document.getElementById('disk-banner-aggressive').style.display=(document.getElementById('disk-banner-aggressive').style.display==='none'?'flex':'none')"
|
||||||
@@ -216,6 +216,7 @@
|
|||||||
<div style="color:#FFAA55;">
|
<div style="color:#FFAA55;">
|
||||||
<b>Aggressiv</b> — zusaetzlich ungenutzte Volumes. <b>Nur wenn alle ARIA-Container laufen</b>, sonst riskierst du Daten-Verlust (Sessions, SSH-Keys, Shared):<br>
|
<b>Aggressiv</b> — zusaetzlich ungenutzte Volumes. <b>Nur wenn alle ARIA-Container laufen</b>, sonst riskierst du Daten-Verlust (Sessions, SSH-Keys, Shared):<br>
|
||||||
<code style="font-family:monospace;">docker system prune -a --volumes -f</code>
|
<code style="font-family:monospace;">docker system prune -a --volumes -f</code>
|
||||||
|
<button onclick="runDiskCleanup('aggressive')" class="btn secondary" style="padding:2px 8px;font-size:10px;margin-left:6px;border-color:#FF3B30;color:#FF3B30;">Jetzt ausfuehren</button>
|
||||||
<button onclick="copyDiskCmd('aggressive')" class="btn secondary" style="padding:2px 8px;font-size:10px;margin-left:6px;">Kopieren</button>
|
<button onclick="copyDiskCmd('aggressive')" class="btn secondary" style="padding:2px 8px;font-size:10px;margin-left:6px;">Kopieren</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1739,6 +1740,20 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (msg.type === 'disk_cleanup') {
|
||||||
|
const btn = document.getElementById('disk-clean-btn');
|
||||||
|
if (msg.status === 'running') {
|
||||||
|
if (btn) { btn.disabled = true; btn.textContent = 'Raeume auf...'; }
|
||||||
|
} else if (msg.status === 'done') {
|
||||||
|
if (btn) { btn.disabled = false; btn.textContent = 'Sicher aufraeumen'; }
|
||||||
|
alert('Aufgeraeumt: ' + (msg.freed || '0 MB') + ' frei\n(' + (msg.steps || []).join(', ') + ')');
|
||||||
|
} else if (msg.status === 'error') {
|
||||||
|
if (btn) { btn.disabled = false; btn.textContent = 'Sicher aufraeumen'; }
|
||||||
|
alert('Aufraeumen fehlgeschlagen: ' + (msg.error || ''));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.type === 'mode' && msg.payload) {
|
if (msg.type === 'mode' && msg.payload) {
|
||||||
// Bridge hat den Modus geaendert (evtl. von anderer App/Diagnostic) — UI syncen
|
// Bridge hat den Modus geaendert (evtl. von anderer App/Diagnostic) — UI syncen
|
||||||
const mode = (msg.payload.mode || '').toLowerCase();
|
const mode = (msg.payload.mode || '').toLowerCase();
|
||||||
@@ -6734,6 +6749,18 @@
|
|||||||
banner.style.display = 'block';
|
banner.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fuehrt das Aufraeumen WIRKLICH aus (Server-seitig via Docker-API), statt
|
||||||
|
// nur den Befehl zu kopieren.
|
||||||
|
function runDiskCleanup(variant) {
|
||||||
|
const aggressive = variant === 'aggressive';
|
||||||
|
const q = aggressive
|
||||||
|
? 'AGGRESSIV aufraeumen? Loescht zusaetzlich ungenutzte Volumes — nur wenn ALLE ARIA-Container laufen, sonst Datenverlust!'
|
||||||
|
: 'Sicher aufraeumen? Loescht Build-Cache + ungenutzte Images (keine Volumes, keine Daten gehen verloren).';
|
||||||
|
if (!confirm(q)) return;
|
||||||
|
const btn = document.getElementById('disk-clean-btn');
|
||||||
|
if (btn && !aggressive) { btn.disabled = true; btn.textContent = 'Raeume auf...'; }
|
||||||
|
send({ action: 'disk_cleanup', variant });
|
||||||
|
}
|
||||||
function copyDiskCmd(variant) {
|
function copyDiskCmd(variant) {
|
||||||
const cmd = variant === 'aggressive'
|
const cmd = variant === 'aggressive'
|
||||||
? 'docker system prune -a --volumes -f'
|
? 'docker system prune -a --volumes -f'
|
||||||
|
|||||||
@@ -1559,6 +1559,70 @@ function dockerExec(containerName, cmd) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// POST gegen die Docker-Daemon-API (via gemountetem Socket). Fuer prune-
|
||||||
|
// Endpoints — die geben SpaceReclaimed (Bytes) zurueck.
|
||||||
|
function dockerApiPost(apiPath) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = http.request({
|
||||||
|
socketPath: "/var/run/docker.sock",
|
||||||
|
path: apiPath,
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json", "Content-Length": 0 },
|
||||||
|
}, (res) => {
|
||||||
|
let data = "";
|
||||||
|
res.on("data", (c) => data += c);
|
||||||
|
res.on("end", () => {
|
||||||
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
|
try { resolve(JSON.parse(data || "{}")); } catch { resolve({}); }
|
||||||
|
} else {
|
||||||
|
reject(new Error(`Docker API ${apiPath}: HTTP ${res.statusCode} — ${String(data).slice(0, 200)}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
req.on("error", reject);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Sicher aufraeumen": Build-Cache + ungenutzte Images prunen — OHNE Volumes
|
||||||
|
// (keine Daten weg). "aggressive": zusaetzlich gestoppte Container + ungenutzte
|
||||||
|
// Volumes (kann Daten kosten → nur auf ausdrueckliche Wahl). Fuehrt es WIRKLICH
|
||||||
|
// aus (frueher kopierte der Button nur den Befehl in die Zwischenablage).
|
||||||
|
async function handleDiskCleanup(clientWs, variant) {
|
||||||
|
const aggressive = variant === "aggressive";
|
||||||
|
const send = (o) => { try { clientWs.send(JSON.stringify(o)); } catch (_) {} };
|
||||||
|
send({ type: "disk_cleanup", status: "running", variant });
|
||||||
|
log("warn", "server", `Disk-Cleanup gestartet (${aggressive ? "aggressive" : "safe"})`);
|
||||||
|
try {
|
||||||
|
let reclaimed = 0;
|
||||||
|
const steps = [];
|
||||||
|
const bp = await dockerApiPost("/build/prune?all=true");
|
||||||
|
reclaimed += (bp.SpaceReclaimed || 0);
|
||||||
|
steps.push("Build-Cache");
|
||||||
|
// dangling=false → ALLE ungenutzten Images (nicht nur dangling).
|
||||||
|
// Docker-API-Filterformat: map[string][]string.
|
||||||
|
const imgFilter = encodeURIComponent(JSON.stringify({ dangling: ["false"] }));
|
||||||
|
const ip = await dockerApiPost("/images/prune?filters=" + imgFilter);
|
||||||
|
reclaimed += (ip.SpaceReclaimed || 0);
|
||||||
|
steps.push("ungenutzte Images");
|
||||||
|
if (aggressive) {
|
||||||
|
const cp = await dockerApiPost("/containers/prune");
|
||||||
|
reclaimed += (cp.SpaceReclaimed || 0);
|
||||||
|
steps.push("gestoppte Container");
|
||||||
|
const vp = await dockerApiPost("/volumes/prune");
|
||||||
|
reclaimed += (vp.SpaceReclaimed || 0);
|
||||||
|
steps.push("ungenutzte Volumes");
|
||||||
|
}
|
||||||
|
const mb = (reclaimed / (1024 * 1024));
|
||||||
|
const freed = mb >= 1024 ? (mb / 1024).toFixed(2) + " GB" : mb.toFixed(0) + " MB";
|
||||||
|
log("info", "server", `Disk-Cleanup fertig: ${freed} frei (${steps.join(", ")})`);
|
||||||
|
send({ type: "disk_cleanup", status: "done", variant, reclaimedBytes: reclaimed, freed, steps });
|
||||||
|
} catch (err) {
|
||||||
|
log("error", "server", `Disk-Cleanup fehlgeschlagen: ${err.message}`);
|
||||||
|
send({ type: "disk_cleanup", status: "error", variant, error: String(err && err.message || err) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Hilfsfunktionen ─────────────────────────────────────
|
// ── Hilfsfunktionen ─────────────────────────────────────
|
||||||
|
|
||||||
function waitForMessage(ws, timeoutMs) {
|
function waitForMessage(ws, timeoutMs) {
|
||||||
@@ -2463,6 +2527,8 @@ wss.on("connection", (ws) => {
|
|||||||
sendToRVS_raw({ type: "interject", payload: { projectId: msg.projectId || "", text: t }, timestamp: Date.now() });
|
sendToRVS_raw({ type: "interject", payload: { projectId: msg.projectId || "", text: t }, timestamp: Date.now() });
|
||||||
log("info", "server", "Zwischenruf an RVS (project=" + (msg.projectId || "(main)") + "): " + t.slice(0, 60));
|
log("info", "server", "Zwischenruf an RVS (project=" + (msg.projectId || "(main)") + "): " + t.slice(0, 60));
|
||||||
}
|
}
|
||||||
|
} else if (msg.action === "disk_cleanup") {
|
||||||
|
handleDiskCleanup(ws, msg.variant === "aggressive" ? "aggressive" : "safe");
|
||||||
} else if (msg.action === "reconnect_gateway") {
|
} else if (msg.action === "reconnect_gateway") {
|
||||||
connectGateway();
|
connectGateway();
|
||||||
} else if (msg.action === "reconnect_rvs") {
|
} else if (msg.action === "reconnect_rvs") {
|
||||||
|
|||||||
Reference in New Issue
Block a user