Probe every domain every minute, and keep it out of its own traffic numbers
NPM's access log only describes hosts somebody visited; the host most likely to be quietly broken is the one nobody does. Probes carry a User-Agent npm_access_stats.sh drops — unmarked, this monitor would be fifty thousand requests a day in the very logs it reports on.
This commit is contained in:
@@ -266,6 +266,15 @@ require_once dirname(__DIR__) . '/include/ai_chat.php';
|
||||
.vv-au-stat-s { font-size:9px;color:#3a3a3a;margin-top:1px; }
|
||||
.vv-au-stat-s.warn { color:#8a6a2a; }
|
||||
.vv-au-stat-s.bad { color:#a34; }
|
||||
/* Healthy uptime is deliberately quiet — a page where thirty green numbers shout is a page where
|
||||
the two red ones do not. */
|
||||
.vv-au-stat-n.ok2 { color:#4a7a4a; }
|
||||
|
||||
/* One bar per probe, last hour. The shape is the part a percentage throws away: one long outage
|
||||
and sixty scattered blips are the same 50% and completely different problems. */
|
||||
.vv-au-spark { display:flex;gap:1px;justify-content:flex-end;align-items:flex-end;height:11px;margin-top:3px; }
|
||||
.vv-au-spark i { width:2px;height:100%;background:#2d5a2d;border-radius:1px;flex-shrink:0; }
|
||||
.vv-au-spark i.d{ background:#ef5350; }
|
||||
/* A disabled host is still listed — it is a thing you might re-enable — but it should not read
|
||||
as part of what is currently serving. */
|
||||
.vv-au-off td { opacity:.42; }
|
||||
@@ -354,7 +363,7 @@ $tabs = ['proxies' => 'Proxies', 'users' => 'Users & Groups',
|
||||
<div class="vv-au-loading" id="vv-au-proxy-loading">Loading…</div>
|
||||
<table class="vv-au-tbl" id="vv-au-proxy-tbl" style="display:none">
|
||||
<thead><tr>
|
||||
<th>Host</th><th>Flags</th><th style="text-align:right">Requests</th><th style="text-align:right">Errors</th><th>On</th><th></th>
|
||||
<th>Host</th><th>Flags</th><th style="text-align:right">Uptime</th><th style="text-align:right">Requests</th><th style="text-align:right">Errors</th><th>On</th><th></th>
|
||||
</tr></thead>
|
||||
<tbody id="vv-au-proxy-body"></tbody>
|
||||
</table>
|
||||
@@ -510,6 +519,8 @@ const IS_OWNER = <?= $isOwner ? 'true' : 'false' ?>;
|
||||
let _proxies = [], _certs = [];
|
||||
// Keyed by proxy-host id, from Tools/npm_access_stats.sh. Empty until it has run once.
|
||||
let _proxyStats = {};
|
||||
// Keyed by hostname, from Tools/uptime_probe.sh. Empty until the probe has run once.
|
||||
let _uptime = {};
|
||||
let _users = [], _groups = [];
|
||||
let _rules = [], _defaultPolicy = 'deny';
|
||||
// The trailing comment on the default_policy line, carried so a save puts it back. The block is
|
||||
@@ -646,7 +657,8 @@ function _loadProxies() {
|
||||
|
||||
// Stats are optional decoration — the list must render whether or not the aggregator has run,
|
||||
// so this neither blocks _check() nor fails the load.
|
||||
_get('npm_stats', r => { _proxyStats = (r && r.ok && r.hosts) ? r.hosts : {}; if (proxiesLoaded) _renderProxies(); });
|
||||
_get('npm_stats', r => { _proxyStats = (r && r.ok && r.hosts) ? r.hosts : {}; if (proxiesLoaded) _renderProxies(); });
|
||||
_get('npm_uptime', r => { _uptime = (r && r.ok && r.domains) ? r.domains : {}; if (proxiesLoaded) _renderProxies(); });
|
||||
_get('npm_certs', r => { _certs = r.certs || []; certsLoaded = true; _check(); });
|
||||
_get('npm_proxies', r => {
|
||||
if (!r.ok) { loading.innerHTML = '<span style="color:#ef5350">'+_esc(r.error)+'</span>'; return; }
|
||||
@@ -716,6 +728,7 @@ function _renderProxies() {
|
||||
<div class="vv-au-fwd">→ ${_esc(fwd)}</div>
|
||||
</td>
|
||||
<td><div class="vv-au-marks">${marks}</div></td>
|
||||
<td class="vv-au-num">${_upCell(p)}</td>
|
||||
<td class="vv-au-num">${reqCell}</td>
|
||||
<td class="vv-au-num">${errCell}</td>
|
||||
<td>${_togHtml('proxy-' + p.id, enabled, enabled ? 'Enabled — click to disable' : 'Disabled — click to enable')}</td>
|
||||
@@ -729,6 +742,33 @@ function _renderProxies() {
|
||||
}).join('');
|
||||
}
|
||||
|
||||
// Uptime for the host, from Tools/uptime_probe.sh. A proxy host can carry several domains, so the
|
||||
// worst of them is what the row reports — a host is only as reachable as its least reachable name,
|
||||
// and averaging would hide one dead domain behind three healthy ones.
|
||||
function _upCell(p) {
|
||||
const doms = (p.domain_names || []).map(d => String(d).toLowerCase()).filter(d => !d.includes('*'));
|
||||
const recs = doms.map(d => _uptime[d]).filter(Boolean);
|
||||
if (!recs.length) return '<div class="vv-au-stat-n dim">—</div>';
|
||||
|
||||
const worst = recs.reduce((a, b) => ((a.h24 ?? 101) <= (b.h24 ?? 101) ? a : b));
|
||||
const down = recs.some(r => r.state === 'down');
|
||||
const pct = worst.h24;
|
||||
// Banded on the same three-state rule as the error column: fine, worth a look, not working.
|
||||
const cls = down ? ' bad' : (pct === null ? ' dim' : (pct >= 99.5 ? ' ok2' : (pct >= 95 ? ' warn' : ' bad')));
|
||||
|
||||
// Sixty samples is the last hour at a one-minute cadence. Drawn as bars rather than a number
|
||||
// because the shape — one long outage or sixty scattered blips — is the part a percentage loses.
|
||||
const bars = (worst.samples || []).map(s =>
|
||||
`<i class="${s ? '' : 'd'}"></i>`).join('');
|
||||
|
||||
const title = down
|
||||
? 'DOWN — ' + (worst.last_detail || 'no response')
|
||||
: 'up' + (worst.last_ms ? ' · ' + worst.last_ms + 'ms' : '');
|
||||
|
||||
return `<div class="vv-au-stat-n${cls}" title="${_esc(title)}">${pct === null ? '—' : (pct >= 99.95 ? '100' : pct.toFixed(pct >= 99 ? 2 : 1))}<span class="vv-au-stat-u">% 24h</span></div>
|
||||
<div class="vv-au-spark" title="last hour, one bar per minute">${bars}</div>`;
|
||||
}
|
||||
|
||||
function _certName(id) {
|
||||
const c = _certs.find(x => String(x.id) === String(id));
|
||||
return c ? (c.nice_name || (c.domain_names || []).join(', ')) : 'certificate ' + id;
|
||||
|
||||
@@ -1996,6 +1996,18 @@ Saved into `master.conf`, which does not need to be opened by hand.
|
||||
| `TRANSCODE_LOG_RETENTION` | a number box, in days | in this section | days before old entries purged |
|
||||
| `TRANSCODE_CHECK_EMBY` | a switch | in this section | — |
|
||||
|
||||
## Uptime Probe
|
||||
|
||||
Route: Settings tab → All settings → *Uptime Probe*
|
||||
|
||||
Saved into `master.conf`, which does not need to be opened by hand.
|
||||
|
||||
| Setting | Control | Where | What it does |
|
||||
|---|---|---|---|
|
||||
| `UPTIME_PROBE_ENABLED` | a switch | in this section | master switch for the per-minute probe |
|
||||
| `UPTIME_PROBE_TIMEOUT` | a number box, in seconds | in this section | seconds per domain before it counts as down |
|
||||
| `UPTIME_PROBE_LIST_TTL` | a number box, in seconds | in this section | seconds to reuse the domain list from NPM before re-reading it |
|
||||
|
||||
## Version Parity
|
||||
|
||||
Route: Settings tab → All settings → *Version Parity*
|
||||
|
||||
Reference in New Issue
Block a user