Auth stack certs tab, arrs db fallbacks, cert monitor cache, conf parser fix
- Auth stack: fold cert monitor into Auth Stack page as fourth tab (Certs); remove standalone cert page and top-level tab - cert_monitor.sh: write JSON status cache to State_Files/cert_status.json after each run; expose per-domain days/expiry via _CERT_DAYS/_CERT_EXPIRY globals - api/cert.php: new — serves cached cert status; falls back to configured domains as UNKN when no cache exists; POST action=run triggers live check - arrs db fallbacks: vv_arr_cleanup_stats/discovery_stats/recovery_stats now read from data/*.db files when log JSON files don't yet exist - config.php vv_conf_vars(): unescape bash \$ → $ so passwords with dollar signs read correctly from conf files - host1.conf: fill in HOST1_NPM_USER/PASS and HOST1_LLDAP_USER/PASS - Partnership adapter pattern: Unraid-specific container logic extracted to Plugin/unraid/Partnership/; platform-agnostic structure stays in Partnership/ - First-run wizard: uniform multi-step flow for all hosts; HOST2 pull moved to checklist; auto SSH keygen and API key creation on save - api/checklist.php: live setup checklist with pull_master action - Fullscreen toggle: hide Unraid header/menu; state persists via localStorage
This commit is contained in:
@@ -111,6 +111,12 @@
|
||||
.vv-au-domain { font-size:12px;color:#bbb;font-weight:bold; }
|
||||
.vv-au-fwd { font-size:10px;color:#444; }
|
||||
.vv-au-loading { color:#333;font-size:11px;padding:16px;text-align:center; }
|
||||
|
||||
/* ── Certs panel ─────────────────────────────────────────────────────────── */
|
||||
.vv-au-cert-grid { display:grid;grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:10px; }
|
||||
.vv-au-cert-days { font-size:28px;font-weight:700;line-height:1;margin:6px 0 2px; }
|
||||
.vv-au-cert-bar { height:3px;border-radius:2px;background:#1a1a1a;overflow:hidden;margin-top:8px; }
|
||||
.vv-au-cert-fill { height:100%;border-radius:2px;transition:width .3s; }
|
||||
</style>
|
||||
|
||||
<?php
|
||||
@@ -123,6 +129,7 @@ $isOwner = vv_is_owner();
|
||||
<button class="vv-au-tab active" data-tab="proxies">Proxies</button>
|
||||
<button class="vv-au-tab" data-tab="users">Users & Groups</button>
|
||||
<button class="vv-au-tab" data-tab="acl">Access Control</button>
|
||||
<button class="vv-au-tab" data-tab="certs">Certs</button>
|
||||
<button class="vv-au-btn" id="vv-au-refresh" title="Refresh current tab">↻ Refresh</button>
|
||||
</div>
|
||||
|
||||
@@ -203,6 +210,21 @@ $isOwner = vv_is_owner();
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Certs ───────────────────────────────────────────────────────────────── -->
|
||||
<div class="vv-au-panel" id="vv-au-panel-certs">
|
||||
<div class="vv-au-sec-bar">
|
||||
<span class="vv-au-sec-title" id="vv-au-cert-ts"></span>
|
||||
<button class="vv-au-btn prim" id="vv-au-cert-run">Run now</button>
|
||||
</div>
|
||||
<div class="vv-au-cert-grid" id="vv-au-cert-grid">
|
||||
<div class="vv-au-loading">Loading…</div>
|
||||
</div>
|
||||
<div id="vv-au-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:140px;overflow-y:auto;white-space:pre-wrap;"></div>
|
||||
<div id="vv-au-cert-cfg" style="margin-top:10px;font-size:10px;color:#3a3a3a;"></div>
|
||||
</div>
|
||||
|
||||
<!-- ── Modal overlay ──────────────────────────────────────────────────────── -->
|
||||
<div class="vv-au-overlay" id="vv-au-overlay">
|
||||
<div class="vv-au-modal" id="vv-au-modal"></div>
|
||||
@@ -212,7 +234,8 @@ $isOwner = vv_is_owner();
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const API = '/plugins/varaverk/api/auth.php';
|
||||
const API = '/plugins/varaverk/api/auth.php';
|
||||
const CERT_API = '/plugins/varaverk/api/cert.php';
|
||||
const IS_OWNER = <?= $isOwner ? 'true' : 'false' ?>;
|
||||
|
||||
// ── State ─────────────────────────────────────────────────────────────────────
|
||||
@@ -287,6 +310,7 @@ function _loadTab(tab) {
|
||||
if (tab === 'proxies') _loadProxies();
|
||||
if (tab === 'users') { _loadUsers(); _loadGroups(); }
|
||||
if (tab === 'acl') _loadAcl();
|
||||
if (tab === 'certs') _loadCerts();
|
||||
}
|
||||
|
||||
// ── Proxies ───────────────────────────────────────────────────────────────────
|
||||
@@ -910,6 +934,86 @@ document.getElementById('vv-au-overlay').addEventListener('click', e => {
|
||||
if (e.target === document.getElementById('vv-au-overlay')) _closeModal();
|
||||
});
|
||||
|
||||
// ── Certs ─────────────────────────────────────────────────────────────────────
|
||||
function _certBadgeCls(s) {
|
||||
return ({OK:'ssl', WARN:'one_factor', CRIT:'deny', FAIL:'deny'})[s] || 'nossl';
|
||||
}
|
||||
function _certBadgeTxt(s) {
|
||||
return ({OK:'healthy', WARN:'warning', CRIT:'critical', FAIL:'failed', UNKN:'not checked'})[s] || s;
|
||||
}
|
||||
function _certDayColor(days, warn, crit) {
|
||||
if (days == null) return '#3a3a3a';
|
||||
return days <= crit ? '#ef5350' : days <= warn ? '#ffb74d' : '#4caf50';
|
||||
}
|
||||
function _certRel(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';
|
||||
return Math.floor(d/86400) + 'd ago';
|
||||
}
|
||||
|
||||
function _renderCerts(data) {
|
||||
const grid = document.getElementById('vv-au-cert-grid');
|
||||
const ts = document.getElementById('vv-au-cert-ts');
|
||||
const cfg = document.getElementById('vv-au-cert-cfg');
|
||||
const warn = data.warn_days || 30, crit = data.crit_days || 7;
|
||||
|
||||
ts.textContent = data.checked_at ? 'Last checked: ' + _certRel(data.checked_at) : 'Not yet checked';
|
||||
cfg.textContent = `Warn: ${warn}d · Crit: ${crit}d`;
|
||||
|
||||
const domains = data.domains || [];
|
||||
if (!domains.length) {
|
||||
grid.innerHTML = '<div class="vv-au-empty">No domains configured — add HOST*_CERT_MONITOR_DOMAINS to host.conf</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
grid.innerHTML = domains.map(d => {
|
||||
const s = d.status || 'UNKN';
|
||||
const days = d.days;
|
||||
const col = _certDayColor(days, warn, crit);
|
||||
const barPct = days != null ? Math.min(Math.round(days/90*100), 100) : 0;
|
||||
const expStr = d.expires ? 'Expires ' + d.expires : (s === 'UNKN' ? 'Not yet checked' : '');
|
||||
return `<div class="vv-au-card" style="padding:12px 14px;">
|
||||
<div class="vv-au-domain">${_esc(d.domain)}</div>
|
||||
<div class="vv-au-cert-days" style="color:${col}">${days != null ? days : '—'}</div>
|
||||
<div style="font-size:9px;color:#444;margin-bottom:6px;">${days != null ? 'days remaining' : ''}</div>
|
||||
<span class="vv-au-badge ${_certBadgeCls(s)}">${_certBadgeTxt(s)}</span>
|
||||
<div style="font-size:9px;color:#3a3a3a;margin-top:5px;">${_esc(expStr)}</div>
|
||||
${days != null ? `<div class="vv-au-cert-bar"><div class="vv-au-cert-fill" style="width:${barPct}%;background:${col};"></div></div>` : ''}
|
||||
</div>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function _loadCerts() {
|
||||
const grid = document.getElementById('vv-au-cert-grid');
|
||||
grid.innerHTML = '<div class="vv-au-loading">Loading…</div>';
|
||||
fetch(CERT_API)
|
||||
.then(r => r.json())
|
||||
.then(d => { if (d.ok) _renderCerts(d); })
|
||||
.catch(() => { grid.innerHTML = '<div class="vv-au-empty" style="color:#ef5350">Failed to load cert data</div>'; });
|
||||
}
|
||||
|
||||
document.getElementById('vv-au-cert-run').addEventListener('click', function() {
|
||||
const btn = this;
|
||||
const log = document.getElementById('vv-au-cert-log');
|
||||
btn.disabled = true; btn.textContent = 'Checking…';
|
||||
log.style.display = 'none'; log.textContent = '';
|
||||
const fd = new FormData(); fd.append('action', 'run');
|
||||
fetch(CERT_API, { method: 'POST', body: fd })
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
btn.disabled = false; btn.textContent = 'Run now';
|
||||
if (d.data) _renderCerts(d.data);
|
||||
if (d.output && d.output.length) {
|
||||
log.style.display = 'block';
|
||||
log.textContent = d.output.join('\n').replace(/\x1b\[[0-9;]*m/g, '');
|
||||
}
|
||||
})
|
||||
.catch(() => { btn.disabled = false; btn.textContent = 'Run now'; });
|
||||
});
|
||||
|
||||
// ── Boot ──────────────────────────────────────────────────────────────────────
|
||||
_loadProxies();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user