Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf4859c41e | |||
| 54e9a95bd4 |
@@ -224,3 +224,7 @@ Roughly in priority order:
|
|||||||
`v0.16.49` fixes the My Account and Documentation pages' cramped spacing between the header subtitle and the first box below it, reported against several earlier releases. The cause was pinned down precisely by measuring pixel gaps across side-by-side screenshots of a correctly-spaced page (Logs) against the two broken ones: Certificates, Performance, and Logs all get their deliberate spacing from one shared rule, `#certificates-view,#performance-view,#logs-view{margin-top:var(--space-7)}`, and My Account and Documentation were simply never added to that selector, so both fell back to a 0px top margin. The fix adds `#account-view` and `#documentation-view` to that same existing rule -- reusing the app's own established spacing value rather than introducing a new one.
|
`v0.16.49` fixes the My Account and Documentation pages' cramped spacing between the header subtitle and the first box below it, reported against several earlier releases. The cause was pinned down precisely by measuring pixel gaps across side-by-side screenshots of a correctly-spaced page (Logs) against the two broken ones: Certificates, Performance, and Logs all get their deliberate spacing from one shared rule, `#certificates-view,#performance-view,#logs-view{margin-top:var(--space-7)}`, and My Account and Documentation were simply never added to that selector, so both fell back to a 0px top margin. The fix adds `#account-view` and `#documentation-view` to that same existing rule -- reusing the app's own established spacing value rather than introducing a new one.
|
||||||
|
|
||||||
`v0.16.50` adds a **Hide not configured** checkbox to the Performance page's Throughput by domain table, matching the API Access page's existing "Hide revoked" toggle in both behavior and placement: unchecked by default, resets on every page reload (no server round-trip, no persisted setting), and right-aligned inline with the descriptive text above the table rather than inside the table header itself. Checking it filters out any row already tagged with the "Not configured" chip -- domains Caddy has logged requests for that don't match a real Hosted Site, Proxy Host, or Redirect Host -- so a table with a lot of scanning/bot noise pointed at random hostnames can be narrowed down to just the domains actually configured in Site Gateway.
|
`v0.16.50` adds a **Hide not configured** checkbox to the Performance page's Throughput by domain table, matching the API Access page's existing "Hide revoked" toggle in both behavior and placement: unchecked by default, resets on every page reload (no server round-trip, no persisted setting), and right-aligned inline with the descriptive text above the table rather than inside the table header itself. Checking it filters out any row already tagged with the "Not configured" chip -- domains Caddy has logged requests for that don't match a real Hosted Site, Proxy Host, or Redirect Host -- so a table with a lot of scanning/bot noise pointed at random hostnames can be narrowed down to just the domains actually configured in Site Gateway.
|
||||||
|
|
||||||
|
`v0.16.51` reworks the Certificates page layout, which had three visually inconsistent, unevenly-spaced blocks stacked on top of each other (a bare certificate inventory list, a borderless "Domain readiness" panel, and a fully-bordered "Renewal thresholds" card) -- the last two had no spacing rule between them at all and rendered flush against one another. Certificate inventory and Domain readiness are now merged into a single sticky-header table (`Domain | Status | Days remaining | Issuer | DNS | TLS | Upstream`), reusing the same table pattern already used on the Performance and Access logs pages, with one row per configured domain instead of two separately-rendered lists keyed off the same data. The deep per-certificate fields that used to live in an inline expandable `<details>` row (issuer, serial number, SHA-256 fingerprint, covered domains, valid-from date, upstream check detail) now open in a click-to-view popup dialog instead, reusing the app's existing `.dialog-card` pattern -- keeping every table row a single fixed height for a clean continuous scroll. The "Renewal thresholds" settings form, which doesn't change per-domain, moved out of a permanent third block into a "Configure thresholds" popup opened from the page header, the same way page-level settings are already surfaced elsewhere in the app.
|
||||||
|
|
||||||
|
`v0.16.52` fixes the Certificates page's Upstream column always showing "Not configured" for Hosted Sites, even when the exact same upstream health check was clearly running and healthy on that site's own dashboard card. The cause was a single overly-narrow condition in `domainReadiness()`: `const upstream = item.kind === "Proxy host" ? upstreamHealth.get(item.id) || null : null;` only ever read cached health-check results back out for Proxy hosts, even though `checkAllProxies()` runs that identical check against Hosted Sites too and stores the result in the same `upstreamHealth` map under the same id -- the data existed the whole time, this function just refused to return it for anything that wasn't a Proxy host. The condition now also includes `"Hosted site"`; Redirect hosts are unaffected and correctly continue to show no upstream data, since they have none.
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "site-gateway",
|
"name": "site-gateway",
|
||||||
"version": "0.16.50",
|
"version": "0.16.52",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Site Gateway: simple self-hosted website publishing, reverse proxying, and automatic HTTPS.",
|
"description": "Site Gateway: simple self-hosted website publishing, reverse proxying, and automatic HTTPS.",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
+27
-16
@@ -265,26 +265,37 @@ function renderCertificates() {
|
|||||||
$("#cert-healthy").textContent = data.summary.healthy; $("#cert-30").textContent = data.summary.within30Days; $("#cert-7").textContent = data.summary.within7Days; $("#cert-warning").textContent = data.summary.warning + data.summary.critical + data.summary.expired + data.summary.mismatch; $("#cert-pending").textContent = data.summary.pending;
|
$("#cert-healthy").textContent = data.summary.healthy; $("#cert-30").textContent = data.summary.within30Days; $("#cert-7").textContent = data.summary.within7Days; $("#cert-warning").textContent = data.summary.warning + data.summary.critical + data.summary.expired + data.summary.mismatch; $("#cert-pending").textContent = data.summary.pending;
|
||||||
const ageMinutes = (Date.now() - new Date(data.checkedAt).getTime()) / 60000, stale = ageMinutes > (data.thresholds?.staleMinutes || 10);
|
const ageMinutes = (Date.now() - new Date(data.checkedAt).getTime()) / 60000, stale = ageMinutes > (data.thresholds?.staleMinutes || 10);
|
||||||
$("#cert-last-checked").textContent = `Last checked ${formatTime(data.checkedAt)} · ${stale ? "data may be stale" : "current"}`;
|
$("#cert-last-checked").textContent = `Last checked ${formatTime(data.checkedAt)} · ${stale ? "data may be stale" : "current"}`;
|
||||||
$("#certificate-list").innerHTML = data.certificates.length ? data.certificates.map(cert => `<details class="certificate-row"><summary><span class="status-dot ${cert.status === "healthy" ? "running" : cert.status === "pending" ? "idle" : "error"}"></span><span><strong>${escapeHtml(cert.domain)}</strong><small>${escapeHtml(cert.kind)} · ${escapeHtml(cert.name)} · ${escapeHtml(cert.source)}</small></span><span><strong>${cert.expiresAt ? `${cert.daysRemaining} days remaining` : cert.status === "mismatch" ? "Domain mismatch" : "Not detected"}</strong><small>${cert.expiresAt ? `Expires ${formatTime(cert.expiresAt)}` : cert.mismatch ? `Covers: ${(cert.coveredNames || []).map(escapeHtml).join(", ") || "no DNS names"}` : "No stored certificate was found"}</small></span></summary><dl class="certificate-details"><div><dt>Status</dt><dd>${escapeHtml(cert.status)}</dd></div><div><dt>Valid from</dt><dd>${cert.validFrom ? escapeHtml(formatTime(cert.validFrom)) : "—"}</dd></div><div><dt>Issuer</dt><dd>${escapeHtml(cert.issuer || "—")}</dd></div><div><dt>Covered domains</dt><dd>${escapeHtml((cert.coveredNames || []).join(", ") || "—")}</dd></div><div><dt>Serial number</dt><dd>${escapeHtml(cert.serialNumber || "—")}</dd></div><div><dt>SHA-256 fingerprint</dt><dd>${escapeHtml(cert.fingerprint || "—")}</dd></div><div><dt>Last detected update</dt><dd>${cert.updatedAt ? escapeHtml(formatTime(cert.updatedAt)) : "—"}</dd></div></dl></details>`).join("") : '<p class="quiet-state padded">No HTTPS domains are configured.</p>';
|
|
||||||
renderReadiness();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// --- Domain readiness (used inside the Certificates view) ---------------------------
|
|
||||||
function renderReadiness() {
|
|
||||||
const routes = state.readiness?.routes || [];
|
const routes = state.readiness?.routes || [];
|
||||||
$("#readiness-list").innerHTML = routes.length ? routes.map(item => {
|
state.certRows = data.certificates.map(cert => ({ cert, readiness: routes.find(item => item.domain === cert.domain) || null }));
|
||||||
const dnsOk = item.dns.healthy, portsOk = item.ports.http && item.ports.https !== false;
|
$("#certificate-list").innerHTML = state.certRows.length ? state.certRows.map((row, index) => {
|
||||||
const tlsOk = ["healthy", "warning", "critical", "not-configured"].includes(item.tls.status);
|
const cert = row.cert, item = row.readiness;
|
||||||
const upstreamOk = !item.upstream || item.upstream.status === "healthy";
|
const dnsOk = item ? item.dns.healthy : null;
|
||||||
const check = item.upstream;
|
const tlsOk = item ? ["healthy", "warning", "critical", "not-configured"].includes(item.tls.status) : null;
|
||||||
const message = !dnsOk ? `DNS failed${item.dns.error ? ` · ${item.dns.error}` : ""}` : !item.ports.http ? "HTTP port 80 is not responding inside the container" : item.ports.https === false ? "HTTPS port 443 is not responding inside the container" : !tlsOk ? `TLS ${item.tls.status.replaceAll("-", " ")}` : !upstreamOk ? `Upstream ${check?.error || "unavailable"}` : `Ready · DNS ${item.dns.addresses.join(", ")}${check ? ` · upstream ${check.httpStatus || "responding"}` : ""}`;
|
const upstreamOk = item ? (!item.upstream || item.upstream.status === "healthy") : null;
|
||||||
const upstreamDetail = check ? `<div><dt>Upstream</dt><dd>Expected ${escapeHtml(item.upstreamExpected || "200-499")} · received ${check.httpStatus ?? "no response"}${check.responseMs != null ? ` · ${check.responseMs} ms` : ""} · ${check.attempts || 1} attempt${(check.attempts || 1) === 1 ? "" : "s"}</dd></div><div><dt>Last checked</dt><dd>${escapeHtml(formatTime(check.checkedAt))}</dd></div>${check.error ? `<div><dt>Failure detail</dt><dd class="danger-text">${escapeHtml(check.error)}</dd></div>` : ""}` : "<div><dt>Upstream</dt><dd>No upstream health check configured.</dd></div>";
|
const dnsCell = item ? `<span class="status-dot ${dnsOk ? "running" : "error"}"></span>${dnsOk ? "Resolved" : "Failed"}` : `<span class="status-dot idle"></span>—`;
|
||||||
return `<details class="certificate-row readiness-row"><summary><span class="status-dot ${dnsOk && portsOk && tlsOk && upstreamOk ? "running" : "error"}"></span><span><strong>${escapeHtml(item.domain)}</strong><small>${escapeHtml(message)}</small></span></summary><dl class="certificate-details"><div><dt>DNS</dt><dd>${item.dns.healthy ? `Resolved${item.dns.addresses.length ? ` · ${escapeHtml(item.dns.addresses.join(", "))}` : ""}` : `Failed${item.dns.error ? ` · ${escapeHtml(item.dns.error)}` : ""}`}</dd></div><div><dt>Gateway ports</dt><dd>HTTP 80 ${item.ports.http ? "responding" : "not responding"} · HTTPS 443 ${item.ports.https === false ? "not responding" : "responding"}</dd></div><div><dt>TLS</dt><dd>${escapeHtml(item.tls.status.replaceAll("-", " "))}</dd></div>${upstreamDetail}</dl></details>`;
|
const tlsCell = item ? `<span class="status-dot ${tlsOk ? "running" : "error"}"></span>${escapeHtml(item.tls.status.replaceAll("-", " "))}` : `<span class="status-dot idle"></span>—`;
|
||||||
}).join("") : '<p class="quiet-state">No configured domains to check.</p>';
|
const upstreamCell = item ? (item.upstream ? `<span class="status-dot ${upstreamOk ? "running" : "error"}"></span>${item.upstream.httpStatus ?? "no response"}` : `<span class="status-dot idle"></span>Not configured`) : `<span class="status-dot idle"></span>—`;
|
||||||
|
const statusLabel = cert.status === "mismatch" ? "Domain mismatch" : cert.status.charAt(0).toUpperCase() + cert.status.slice(1);
|
||||||
|
return `<tr class="cert-table-row" data-index="${index}" tabindex="0"><td><strong>${escapeHtml(cert.domain)}</strong><br><small class="muted">${escapeHtml(cert.kind)} · ${escapeHtml(cert.source)}</small></td><td><span class="status-dot ${cert.status === "healthy" ? "running" : cert.status === "pending" ? "idle" : "error"}"></span>${escapeHtml(statusLabel)}</td><td>${cert.expiresAt ? `${cert.daysRemaining} days` : "—"}</td><td>${escapeHtml(cert.issuer || "—")}</td><td>${dnsCell}</td><td>${tlsCell}</td><td>${upstreamCell}</td></tr>`;
|
||||||
|
}).join("") : '<tr><td colspan="7" class="quiet-state">No HTTPS domains are configured.</td></tr>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Certificate detail popup (deep fields for a single certificate row) ------------
|
||||||
|
function openCertificateDetail(row) {
|
||||||
|
const cert = row.cert, item = row.readiness;
|
||||||
|
$("#cert-detail-title").textContent = cert.domain;
|
||||||
|
$("#cert-detail-eyebrow").textContent = `${cert.kind} · ${cert.source}`;
|
||||||
|
const certRows = `<div><dt>Status</dt><dd>${escapeHtml(cert.status)}</dd></div><div><dt>Valid from</dt><dd>${cert.validFrom ? escapeHtml(formatTime(cert.validFrom)) : "—"}</dd></div><div><dt>Expires</dt><dd>${cert.expiresAt ? escapeHtml(formatTime(cert.expiresAt)) : "—"}</dd></div><div><dt>Issuer</dt><dd>${escapeHtml(cert.issuer || "—")}</dd></div><div><dt>Covered domains</dt><dd>${escapeHtml((cert.coveredNames || []).join(", ") || "—")}</dd></div><div><dt>Serial number</dt><dd>${escapeHtml(cert.serialNumber || "—")}</dd></div><div><dt>SHA-256 fingerprint</dt><dd>${escapeHtml(cert.fingerprint || "—")}</dd></div><div><dt>Last detected update</dt><dd>${cert.updatedAt ? escapeHtml(formatTime(cert.updatedAt)) : "—"}</dd></div>`;
|
||||||
|
const readinessRows = item ? `<div><dt>DNS</dt><dd>${item.dns.healthy ? `Resolved${item.dns.addresses.length ? ` · ${escapeHtml(item.dns.addresses.join(", "))}` : ""}` : `Failed${item.dns.error ? ` · ${escapeHtml(item.dns.error)}` : ""}`}</dd></div><div><dt>Gateway ports</dt><dd>HTTP 80 ${item.ports.http ? "responding" : "not responding"} · HTTPS 443 ${item.ports.https === false ? "not responding" : "responding"}</dd></div><div><dt>TLS</dt><dd>${escapeHtml(item.tls.status.replaceAll("-", " "))}</dd></div>${item.upstream ? `<div><dt>Upstream</dt><dd>Expected ${escapeHtml(item.upstreamExpected || "200-499")} · received ${item.upstream.httpStatus ?? "no response"}${item.upstream.responseMs != null ? ` · ${item.upstream.responseMs} ms` : ""} · ${item.upstream.attempts || 1} attempt${(item.upstream.attempts || 1) === 1 ? "" : "s"}</dd></div><div><dt>Last checked</dt><dd>${escapeHtml(formatTime(item.upstream.checkedAt))}</dd></div>${item.upstream.error ? `<div><dt>Failure detail</dt><dd class="danger-text">${escapeHtml(item.upstream.error)}</dd></div>` : ""}` : "<div><dt>Upstream</dt><dd>No upstream health check configured.</dd></div>"}` : "<div><dt>Domain readiness</dt><dd>No readiness data available for this domain.</dd></div>";
|
||||||
|
$("#cert-detail-body").innerHTML = certRows + readinessRows;
|
||||||
|
$("#certificate-detail-dialog").showModal();
|
||||||
|
}
|
||||||
|
$("#certificate-list").addEventListener("click", event => { const row = event.target.closest(".cert-table-row"); if (!row) return; const data = state.certRows?.[Number(row.dataset.index)]; if (data) openCertificateDetail(data); });
|
||||||
|
$("#certificate-list").addEventListener("keydown", event => { if (event.key !== "Enter" && event.key !== " ") return; const row = event.target.closest(".cert-table-row"); if (!row) return; event.preventDefault(); const data = state.certRows?.[Number(row.dataset.index)]; if (data) openCertificateDetail(data); });
|
||||||
|
$("#cert-threshold-trigger").addEventListener("click", () => { renderHealthSettings(); $("#health-settings-dialog").showModal(); });
|
||||||
|
|
||||||
|
|
||||||
// --- Logs view -------------------------------------------------------------------------
|
// --- Logs view -------------------------------------------------------------------------
|
||||||
function renderLogs() {
|
function renderLogs() {
|
||||||
const data = state.logs; if (!data) return;
|
const data = state.logs; if (!data) return;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
+23
-4
@@ -8,7 +8,7 @@
|
|||||||
<title>Site Gateway</title>
|
<title>Site Gateway</title>
|
||||||
<meta name="description" content="Host sites, proxy services, and manage HTTPS from one simple dashboard.">
|
<meta name="description" content="Host sites, proxy services, and manage HTTPS from one simple dashboard.">
|
||||||
<link rel="icon" type="image/png" href="/site-gateway-icon-approved.png">
|
<link rel="icon" type="image/png" href="/site-gateway-icon-approved.png">
|
||||||
<link rel="stylesheet" href="/styles.css?v=0.16.50">
|
<link rel="stylesheet" href="/styles.css?v=0.16.52">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<!-- ================================================================
|
<!-- ================================================================
|
||||||
@@ -161,10 +161,29 @@
|
|||||||
<div class="feature-summary">
|
<div class="feature-summary">
|
||||||
<div><strong id="cert-healthy">0</strong><span>Healthy</span></div><div><strong id="cert-30">0</strong><span>Within 30 days</span></div><div><strong id="cert-7">0</strong><span>Within 7 days</span></div><div><strong id="cert-warning">0</strong><span>Needs attention</span></div><div><strong id="cert-pending">0</strong><span>Not detected</span></div>
|
<div><strong id="cert-healthy">0</strong><span>Healthy</span></div><div><strong id="cert-30">0</strong><span>Within 30 days</span></div><div><strong id="cert-7">0</strong><span>Within 7 days</span></div><div><strong id="cert-warning">0</strong><span>Needs attention</span></div><div><strong id="cert-pending">0</strong><span>Not detected</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="diagnostic-section-heading"><p class="eyebrow">Certificate inventory</p><h2>Certificates</h2><p class="muted">Managed and uploaded certificates assigned to configured domains.</p></div><div id="certificate-list" class="data-list diagnostic-list"><p class="quiet-state">Loading certificates…</p></div>
|
<div class="diagnostic-section-heading"><div><p class="eyebrow">Certificate inventory</p><h2>Certificates</h2><p class="muted">Managed certificates, TLS readiness, and renewal status for every configured domain. Select a row for full detail.</p></div><button type="button" id="cert-threshold-trigger" class="text-button">Configure thresholds →</button></div>
|
||||||
<section class="dashboard-panel readiness-panel"><div class="panel-heading"><div><p class="eyebrow">Guided diagnostics</p><h2>Domain readiness</h2><p class="muted">DNS, listener, TLS, and upstream checks for every configured domain.</p></div></div><div id="readiness-list" class="dashboard-list diagnostic-list"><p class="quiet-state">Checking configured domains…</p></div></section><section class="dashboard-panel"><div class="panel-heading"><div><p class="eyebrow">Certificate health</p><h2>Renewal thresholds</h2><p class="muted">Control when a certificate is flagged as renewing soon, critical, or stale on this page and the dashboard.</p></div></div><form id="health-settings-form" class="settings-form"><label>Renewing-soon warning<input name="warningDays" type="number" min="8" max="120" value="30"><small>Days remaining before a certificate is highlighted.</small></label><label>Critical warning<input name="criticalDays" type="number" min="1" max="119" value="7"><small>Must be lower than the renewing-soon threshold.</small></label><label>Stale health data<input name="staleMinutes" type="number" min="2" max="1440" value="10"><small>Minutes before a displayed check is considered old.</small></label><div class="dialog-actions"><button class="button primary">Save health settings</button></div></form></section>
|
<div class="table-wrap performance-table-wrap cert-table-wrap"><table class="performance-table cert-table"><thead><tr><th>Domain</th><th>Status</th><th>Days remaining</th><th>Issuer</th><th>DNS</th><th>TLS</th><th>Upstream</th></tr></thead><tbody id="certificate-list"><tr><td colspan="7" class="quiet-state">Loading certificates…</td></tr></tbody></table></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<dialog id="certificate-detail-dialog">
|
||||||
|
<div class="dialog-card">
|
||||||
|
<div class="dialog-heading"><div><p class="eyebrow" id="cert-detail-eyebrow">Certificate detail</p><h2 id="cert-detail-title">Domain</h2></div></div>
|
||||||
|
<dl class="certificate-details" id="cert-detail-body"></dl>
|
||||||
|
<div class="dialog-actions"><button type="button" class="button secondary close-dialog">Close</button></div>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
<dialog id="health-settings-dialog">
|
||||||
|
<form id="health-settings-form" class="dialog-card settings-form">
|
||||||
|
<div class="dialog-heading"><div><p class="eyebrow">Certificate health</p><h2>Renewal thresholds</h2><p class="muted">Control when a certificate is flagged as renewing soon, critical, or stale on this page and the dashboard.</p></div></div>
|
||||||
|
<label>Renewing-soon warning<input name="warningDays" type="number" min="8" max="120" value="30"><small>Days remaining before a certificate is highlighted.</small></label>
|
||||||
|
<label>Critical warning<input name="criticalDays" type="number" min="1" max="119" value="7"><small>Must be lower than the renewing-soon threshold.</small></label>
|
||||||
|
<label>Stale health data<input name="staleMinutes" type="number" min="2" max="1440" value="10"><small>Minutes before a displayed check is considered old.</small></label>
|
||||||
|
<div class="dialog-actions"><button type="button" class="button secondary close-dialog">Cancel</button><button class="button primary">Save health settings</button></div>
|
||||||
|
</form>
|
||||||
|
</dialog>
|
||||||
|
|
||||||
|
|
||||||
<!-- Logs view: access log table + gateway event/activity log -->
|
<!-- Logs view: access log table + gateway event/activity log -->
|
||||||
<section id="logs-view" class="feature-view hidden">
|
<section id="logs-view" class="feature-view hidden">
|
||||||
<section class="dashboard-panel">
|
<section class="dashboard-panel">
|
||||||
@@ -432,6 +451,6 @@
|
|||||||
<div id="toast" class="toast" role="status"></div>
|
<div id="toast" class="toast" role="status"></div>
|
||||||
<div id="update-banner" class="update-banner hidden" role="status"><span>A new version of Site Gateway is available.</span><div class="update-banner-actions"><button id="update-banner-refresh" class="button primary">Refresh</button><button id="update-banner-dismiss" class="text-button">Dismiss</button></div></div>
|
<div id="update-banner" class="update-banner hidden" role="status"><span>A new version of Site Gateway is available.</span><div class="update-banner-actions"><button id="update-banner-refresh" class="button primary">Refresh</button><button id="update-banner-dismiss" class="text-button">Dismiss</button></div></div>
|
||||||
<!-- App scripts: core (app.js) then extended views/admin (features.js) -->
|
<!-- App scripts: core (app.js) then extended views/admin (features.js) -->
|
||||||
<script src="/app.js?v=0.16.50" defer></script><script src="/features.js?v=0.16.50" defer></script><script src="/select-enhance.js?v=0.16.50" defer></script>
|
<script src="/app.js?v=0.16.52" defer></script><script src="/features.js?v=0.16.52" defer></script><script src="/select-enhance.js?v=0.16.52" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -824,6 +824,15 @@ select{appearance:none!important;-webkit-appearance:none!important;background-re
|
|||||||
.diagnostic-section-heading p:last-child{margin:var(--space-1) 0 0}
|
.diagnostic-section-heading p:last-child{margin:var(--space-1) 0 0}
|
||||||
.diagnostic-section-heading{margin-bottom:0}
|
.diagnostic-section-heading{margin-bottom:0}
|
||||||
.diagnostic-section-heading + .log-table-wrap{border-top:0;border-radius:0 0 15px 15px}
|
.diagnostic-section-heading + .log-table-wrap{border-top:0;border-radius:0 0 15px 15px}
|
||||||
|
.diagnostic-section-heading{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--space-4)}
|
||||||
|
.diagnostic-section-heading + .cert-table-wrap{border-top:0;border-radius:0 0 15px 15px}
|
||||||
|
.cert-table-row{cursor:pointer}
|
||||||
|
.cert-table-row:hover{background:rgba(var(--green-rgb),.06)}
|
||||||
|
.cert-table-row:focus{outline:none}
|
||||||
|
.cert-table-row:focus-visible{outline:2px solid var(--green);outline-offset:-2px}
|
||||||
|
.cert-table td{vertical-align:middle}
|
||||||
|
.cert-table td .status-dot{margin-right:6px;vertical-align:-1px}
|
||||||
|
#cert-detail-body{padding:0;background:transparent;border-top:0;margin-top:var(--space-4)}
|
||||||
#certificate-list.diagnostic-list,#readiness-list.diagnostic-list,#gateway-log-list.diagnostic-list,#audit-list.diagnostic-list{border-radius:0 0 15px 15px}
|
#certificate-list.diagnostic-list,#readiness-list.diagnostic-list,#gateway-log-list.diagnostic-list,#audit-list.diagnostic-list{border-radius:0 0 15px 15px}
|
||||||
.data-list>.quiet-state,.diagnostic-list>.quiet-state{padding:22px}
|
.data-list>.quiet-state,.diagnostic-list>.quiet-state{padding:22px}
|
||||||
.readiness-panel,.log-activity{background:transparent;border:0;padding:0}
|
.readiness-panel,.log-activity{background:transparent;border:0;padding:0}
|
||||||
|
|||||||
+1
-1
@@ -964,7 +964,7 @@ async function domainReadiness(precomputedCertificates) {
|
|||||||
let addresses = [], dnsError = null;
|
let addresses = [], dnsError = null;
|
||||||
try { addresses = [...new Set((await dns.lookup(item.domain, { all: true })).map(value => value.address))]; } catch (error) { dnsError = error.code || error.message; }
|
try { addresses = [...new Set((await dns.lookup(item.domain, { all: true })).map(value => value.address))]; } catch (error) { dnsError = error.code || error.message; }
|
||||||
const certificate = certs.certificates.find(cert => cert.domain === item.domain) || null;
|
const certificate = certs.certificates.find(cert => cert.domain === item.domain) || null;
|
||||||
const upstream = item.kind === "Proxy host" ? upstreamHealth.get(item.id) || null : null;
|
const upstream = (item.kind === "Proxy host" || item.kind === "Hosted site") ? upstreamHealth.get(item.id) || null : null;
|
||||||
return { id: item.id, domain: item.domain, name: item.name, kind: item.kind, dns: { healthy: addresses.length > 0, addresses, error: dnsError }, ports: { http: httpResponding, https: item.tls === "http" ? null : httpsResponding }, tls: item.tls === "http" ? { status: "not-configured" } : { status: certificate?.status || "pending" }, upstream };
|
return { id: item.id, domain: item.domain, name: item.name, kind: item.kind, dns: { healthy: addresses.length > 0, addresses, error: dnsError }, ports: { http: httpResponding, https: item.tls === "http" ? null : httpsResponding }, tls: item.tls === "http" ? { status: "not-configured" } : { status: certificate?.status || "pending" }, upstream };
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user