Files
Varaverk/claude-data/file-history/d6d51683-5f05-41b4-ad5d-afbe06985ad9/b12f95d3d850b17c@v2
T

144 lines
6.3 KiB
Plaintext

<style>
/* ── Cert page ───────────────────────────────────────────────── */
.vv-cert-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px; }
.vv-cert-card { background:#161616;border:1px solid #2a2a2a;border-radius:6px;padding:12px 14px; }
.vv-cert-domain { font-size:12px;font-weight:700;color:#aaa;word-break:break-all;margin-bottom:8px; }
.vv-cert-days { font-size:28px;font-weight:700;line-height:1;margin-bottom:2px; }
.vv-cert-sub { font-size:10px;color:#444;margin-bottom:6px; }
.vv-cert-pill { display:inline-block;font-size:9px;padding:2px 8px;border-radius:3px;font-weight:700; }
.vv-cert-pill.ok { background:#0d1f0d;color:#4caf50;border:1px solid #1a3a1a; }
.vv-cert-pill.warn { background:#1f1500;color:#ffb74d;border:1px solid #3a2800; }
.vv-cert-pill.crit { background:#200d0d;color:#ef5350;border:1px solid #3a1a1a; }
.vv-cert-pill.fail { background:#200d0d;color:#ef5350;border:1px solid #3a1a1a; }
.vv-cert-pill.unkn { background:#1e1e1e;color:#444;border:1px solid #2a2a2a; }
.vv-cert-bar { height:3px;border-radius:2px;background:#1a1a1a;overflow:hidden;margin-top:8px; }
.vv-cert-fill { height:100%;border-radius:2px;transition:width .3s; }
.vv-cert-run-btn { background:#1a1a1a;border:1px solid #333;color:#888;font-size:10px;
padding:4px 12px;border-radius:3px;cursor:pointer;transition:border-color .15s; }
.vv-cert-run-btn:hover { border-color:#555;color:#aaa; }
.vv-cert-run-btn:disabled { opacity:.4;cursor:default; }
</style>
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;padding:0 2px;">
<span style="font-size:13px;font-weight:bold;color:#888;text-transform:uppercase;letter-spacing:.06em;">Cert Monitor</span>
<div style="display:flex;align-items:center;gap:10px;">
<span id="vv-cert-ts" style="font-size:11px;color:#3a3a3a;"></span>
<button class="vv-cert-run-btn" id="vv-cert-run" onclick="vvCertRunNow()">Run now</button>
</div>
</div>
<div id="vv-cert-grid" class="vv-cert-grid">
<div style="grid-column:1/-1;color:#444;font-size:12px;padding:24px 0;text-align:center;">Loading…</div>
</div>
<div id="vv-cert-log" style="display:none;margin-top:12px;background:#0d0d0d;border:1px solid #1e1e1e;
border-radius:4px;padding:10px 12px;font-size:10px;color:#555;font-family:monospace;
max-height:160px;overflow-y:auto;white-space:pre-wrap;"></div>
<div id="vv-cert-cfg" style="margin-top:12px;font-size:10px;color:#3a3a3a;padding:0 2px;"></div>
<script>
(function() {
function _pillCls(s) {
return ({OK:'ok', WARN:'warn', CRIT:'crit', FAIL:'fail'})[s] || 'unkn';
}
function _pillTxt(s) {
return ({OK:'healthy', WARN:'warning', CRIT:'critical', FAIL:'failed', UNKN:'not checked'})[s] || s;
}
function _dayColor(days, warnDays, critDays) {
if (days == null) return '#3a3a3a';
if (days <= critDays) return '#ef5350';
if (days <= warnDays) return '#ffb74d';
return '#4caf50';
}
function _barColor(days, warnDays, critDays) {
if (days == null) return '#1e1e1e';
if (days <= critDays) return '#ef5350';
if (days <= warnDays) return '#ffb74d';
return '#4caf50';
}
function _rel(ts) {
if (!ts) return '—';
const d = Math.floor(Date.now() / 1000) - ts;
if (d < 60) return 'just now';
if (d < 3600) return Math.floor(d/60) + 'm ago';
if (d < 86400) return Math.floor(d/3600) + 'h ago';
if (d < 604800)return Math.floor(d/86400) + 'd ago';
return Math.floor(d/604800) + 'w ago';
}
function _render(data) {
const grid = document.getElementById('vv-cert-grid');
const ts = document.getElementById('vv-cert-ts');
const cfg = document.getElementById('vv-cert-cfg');
const warnDays = data.warn_days || 30;
const critDays = data.crit_days || 7;
const checked = data.checked_at;
ts.textContent = checked ? 'Last checked: ' + _rel(checked) : '';
cfg.textContent = `Warn: ${warnDays}d · Crit: ${critDays}d`;
const domains = data.domains || [];
if (!domains.length) {
grid.innerHTML = `<div style="grid-column:1/-1;color:#444;font-size:12px;padding:24px 0;text-align:center;">
No domains configured — add HOST*_CERT_MONITOR_DOMAINS to your host.conf</div>`;
return;
}
grid.innerHTML = domains.map(d => {
const s = d.status || 'UNKN';
const days = d.days;
const barPct = (days != null && days >= 0) ? Math.min(Math.round(days / 90 * 100), 100) : 0;
const dayCol = _dayColor(days, warnDays, critDays);
const barCol = _barColor(days, warnDays, critDays);
const pillCls = _pillCls(s);
const pillTxt = _pillTxt(s);
const daysStr = days != null ? String(days) : '—';
const expStr = d.expires ? 'Expires ' + d.expires : (s === 'UNKN' ? 'Not yet checked' : 'Unknown expiry');
return `<div class="vv-cert-card">
<div class="vv-cert-domain">${d.domain}</div>
<div class="vv-cert-days" style="color:${dayCol};">${daysStr}</div>
<div class="vv-cert-sub">${days != null ? 'days remaining' : ''}</div>
<span class="vv-cert-pill ${pillCls}">${pillTxt}</span>
<div style="font-size:9px;color:#3a3a3a;margin-top:5px;">${expStr}</div>
${days != null ? `<div class="vv-cert-bar"><div class="vv-cert-fill" style="width:${barPct}%;background:${barCol};"></div></div>` : ''}
</div>`;
}).join('');
}
function vvCertLoad() {
fetch('/plugins/varaverk/api/cert.php')
.then(r => r.json()).then(d => { if (d.ok) _render(d); })
.catch(() => {});
}
vvCertLoad();
window.vvCertRunNow = function() {
const btn = document.getElementById('vv-cert-run');
const log = document.getElementById('vv-cert-log');
btn.disabled = true; btn.textContent = 'Checking…';
log.style.display = 'none'; log.textContent = '';
const fd = new FormData();
fd.append('action', 'run');
fetch('/plugins/varaverk/api/cert.php', { method: 'POST', body: fd })
.then(r => r.json())
.then(d => {
btn.disabled = false; btn.textContent = 'Run now';
if (d.data) _render(d.data);
if (d.output && d.output.length) {
log.style.display = 'block';
// Strip ANSI escape codes
log.textContent = d.output.join('\n').replace(/\x1b\[[0-9;]*m/g, '');
}
})
.catch(() => { btn.disabled = false; btn.textContent = 'Run now'; });
};
})();
</script>