finished stats page

This commit is contained in:
JakeBreath
2026-08-05 14:17:41 -05:00
parent 6325c67790
commit 2d73638a06
5 changed files with 225 additions and 6 deletions
+47 -1
View File
@@ -55,7 +55,7 @@
<h2><i class="fas fa-hdd"></i> Disk</h2>
{% for label in disk_labels %}
<div data-disk="{{ label }}">
<p class="stat-bar-label">{{ label }} <span class="stat-muted" data-f="path"></span><span data-f="percent"></span>% used</p>
<p class="stat-bar-label">{{ label }} <span class="stat-muted" data-f="path"></span><span data-f="percent"></span>% used <span class="stat-muted" data-f="scope"></span></p>
<div class="bar-track"><div class="bar-fill" data-f="bar"></div></div>
<p class="stat-bar-caption"><span data-f="used"></span> of <span data-f="total"></span> · <span data-f="free"></span> free</p>
</div>
@@ -80,6 +80,13 @@
</table>
</section>
<section class="card" id="traffic-card">
<h2><i class="fas fa-traffic-light"></i> Traffic</h2>
<div id="traffic-body">
<p class="empty-hint">Loading…</p>
</div>
</section>
<section class="card">
<h2><i class="fas fa-database"></i> Site content</h2>
<div class="stat-tiles">
@@ -206,6 +213,7 @@
}
setF('path', disk.path);
setF('percent', disk.percent);
setF('scope', disk.scope === 'folder' ? '· folder size' : '');
if (bar) bar.style.width = disk.percent + '%';
setF('used', fmtBytes(disk.used));
setF('total', fmtBytes(disk.total));
@@ -240,6 +248,43 @@
ul.innerHTML = items.length ? lists[key](items) : '<li class="empty-hint">None yet.</li>';
});
}
function buildTraffic(d) {
const body = document.getElementById('traffic-body');
if (!body) return;
const t = d.traffic;
if (!t) {
body.innerHTML = '<p class="empty-hint">Gunicorn access log is not enabled — start via run_prod.sh to see live traffic.</p>';
return;
}
const st = t.status || {};
const dur = (v) => v != null ? v + ' ms' : '—';
body.innerHTML =
'<div class="stat-tiles">' +
`<div class="stat-tile"><span class="stat-tile-value">${t.requests_min}</span><span class="stat-tile-label">Req / min</span></div>` +
`<div class="stat-tile"><span class="stat-tile-value">${t.requests_total}</span><span class="stat-tile-label">Requests (tail)</span></div>` +
`<div class="stat-tile"><span class="stat-tile-value">${dur(t.avg_ms)}</span><span class="stat-tile-label">Avg resp</span></div>` +
`<div class="stat-tile"><span class="stat-tile-value">${dur(t.p50_ms)}</span><span class="stat-tile-label">Median resp</span></div>` +
`<div class="stat-tile"><span class="stat-tile-value">${dur(t.max_ms)}</span><span class="stat-tile-label">Max resp</span></div>` +
'</div>' +
'<p class="stat-line">' +
`<span class="stat-chip">2xx: ${st['2xx'] || 0}</span>` +
`<span class="stat-chip">3xx: ${st['3xx'] || 0}</span>` +
`<span class="stat-chip">4xx: ${st['4xx'] || 0}</span>` +
`<span class="stat-chip">5xx: ${st['5xx'] || 0}</span>` +
'</p>' +
'<h3 class="stat-subhead">Recent requests</h3>' +
'<ul class="stat-list">' +
(t.recent || []).map(r => {
const cls = r.status >= 500 ? 'stat-down' : (r.status >= 400 ? 'stat-muted' : 'stat-up');
return `<li><span class="stat-muted">${timeAgo(r.time)}</span> <code class="stat-mono">${esc(r.method)}</code> ` +
`<span class="stat-mono">${esc(r.path)}</span> ` +
`<span class="${cls}">${r.status}</span>` +
(r.dur_ms != null ? ` <span class="stat-muted">${r.dur_ms} ms</span>` : '') +
'</li>';
}).join('') +
'</ul>';
}
function apply(d) {
const a = d.app || {};
setHtml('app-env', a.app_env + (a.debug ? ' <span class="badge badge-staff">DEBUG</span>' : ''));
@@ -275,6 +320,7 @@
buildProcesses(s.top_mem, 'top-mem');
}
setTiles(d);
buildTraffic(d);
buildRecent(d);
set('stats-updated', new Date(d.generated_at).toLocaleTimeString());
}