From ca7136afb8193622c6961f5535c825a3c8f15f69 Mon Sep 17 00:00:00 2001 From: marvin Date: Fri, 18 Sep 2026 13:51:55 -0400 Subject: [PATCH] Fix dashboard resync button wiring, attention color consistency, drift false-positives, and remove redundant Gateway Defaults callout (v0.15.2) --- README.md | 2 +- package.json | 2 +- src/public/app.js | 12 +++++++++++- src/public/features.js | 21 --------------------- src/public/index.html | 5 ++--- src/server.js | 41 ++++++++++++++++++++--------------------- 6 files changed, 35 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 22db7a9..1190741 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Docker Architectures Caddy - Version + Version

Why Site Gateway · diff --git a/package.json b/package.json index 95977b2..8bd49b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "site-gateway", - "version": "0.15.1", + "version": "0.15.2", "private": true, "description": "Site Gateway: simple self-hosted website publishing, reverse proxying, and automatic HTTPS.", "type": "module", diff --git a/src/public/app.js b/src/public/app.js index b33f01e..d22d021 100644 --- a/src/public/app.js +++ b/src/public/app.js @@ -189,7 +189,7 @@ function renderDashboard() { $("#dash-attention-total").textContent = data.attention.length; $("#dash-attention-detail").textContent = data.attention.length ? `${data.attention.length} item${data.attention.length === 1 ? "" : "s"} to review` : "No current issues"; applyTileAccents(data); - $("#dash-attention-chip").classList.toggle("accent-warning", data.attention.length > 0); + $("#dash-attention-chip").classList.toggle("accent-danger", data.attention.length > 0); $("#dash-attention-chip").classList.toggle("accent-green", data.attention.length === 0); $("#dash-attention-icon").textContent = data.attention.length > 0 ? "!" : "✓"; $("#dash-throughput-total").textContent = data.throughput?.liveRequests ?? 0; @@ -602,6 +602,16 @@ $("#logout").addEventListener("click", async () => { await fetch("/api/logout", $("#check-health").addEventListener("click", async event => { const button = event.currentTarget; button.disabled = true; button.textContent = "Checking…"; try { const result = await api("/api/health/check", { method:"POST" }); state.dashboard = result.dashboard; state.certificates = result.certificates; state.readiness = { routes:result.readiness }; renderCertificates(); toast("Certificate and domain checks completed."); } catch (error) { toast(error.message, "error"); } finally { button.disabled = false; button.textContent = "Run certificate check"; } }); $("#download-support")?.addEventListener("click", () => { location.href = "/api/support-report"; }); $("#attention-list").addEventListener("click", event => { const target = event.target.closest("[data-issue-target]")?.dataset.issueTarget; if (target) { const [view, adminTab] = target.split("/"); state.view = view; if (view === "administration" && adminTab) state.adminTab = adminTab; render(); loadFeatureView().catch(error => toast(error.message, "error")); } }); +$("#attention-list").addEventListener("click", async event => { + const button = event.target.closest("[data-drift-resync]"); + if (!button) return; + button.disabled = true; button.textContent = "Resyncing…"; + try { + await api("/api/gateway/resync", { method: "POST" }); + toast("Gateway configuration re-synced."); + await refresh(); + } catch (error) { toast(error.message, "error"); button.disabled = false; button.textContent = "Resync now"; } +}); // --- Primary navigation (sidebar view switching) ------------------------------------------- function closeMenus() { document.querySelectorAll(".menu-open").forEach(card => { card.classList.remove("menu-open"); card.querySelector(".menu-button")?.setAttribute("aria-expanded", "false"); }); } diff --git a/src/public/features.js b/src/public/features.js index eae90f2..957b2fe 100644 --- a/src/public/features.js +++ b/src/public/features.js @@ -326,27 +326,6 @@ document.addEventListener("click", async event => { const button = event.target. document.addEventListener("click", event => { const button = event.target.closest('[data-retention-action="download"]'); if (!button) return; window.location.href = "/api/logs/download"; }); document.addEventListener("click", async event => { const button = event.target.closest('[data-retention-action="prune"]'); if (!button) return; event.preventDefault(); event.stopImmediatePropagation(); try { const preview = await api("/api/logs/prune/preview"); const counts = preview.counts || {}; const total = Object.values(counts).reduce((sum, value) => sum + value, 0); let dialog = document.querySelector("#retention-prune-dialog"); if (!dialog) { dialog = document.createElement("dialog"); dialog.id = "retention-prune-dialog"; document.body.append(dialog); } dialog.innerHTML = `

Log Retention

Confirm pruning

This will remove records older than your saved retention periods.

Access: ${counts.access || 0} · Activity: ${counts.activity || 0} · Certificates: ${counts.certificate || 0} · Security: ${counts.security || 0} · Audit: ${counts.audit || 0}

`; dialog.showModal(); const result = await new Promise(resolve => dialog.addEventListener("close", () => resolve(dialog.returnValue), { once: true })); if (result !== "confirm") return; const response = await api("/api/logs/prune", { method: "POST" }); toast(`Pruning completed. ${Object.values(response.counts || {}).reduce((sum, value) => sum + value, 0)} record${total === 1 ? "" : "s"} removed.`); } catch (error) { toast(error.message); } }, true); -// --- Config drift: show a resync callout in Administration > Default site when drift is detected -------- -function renderConfigDriftCallout() { - const callout = document.querySelector("#config-drift-callout"); - if (!callout) return; - const drift = (state.dashboard?.attention || []).some(item => item.kind === "drift"); - callout.hidden = !drift; -} -setInterval(renderConfigDriftCallout, 1000); -document.addEventListener("click", async event => { - const button = event.target.closest("#config-resync"); - if (!button) return; - button.disabled = true; - try { - await api("/api/gateway/resync", { method: "POST" }); - toast("Gateway configuration re-synced."); - await refresh(); - renderConfigDriftCallout(); - } catch (error) { toast(error.message); } finally { button.disabled = false; } -}); - - // ============================================================================================ // v0.15.0 additions: API access tokens, backup history, and the Docker container picker. // ============================================================================================ diff --git a/src/public/index.html b/src/public/index.html index 5be4502..51abc88 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -220,7 +220,6 @@