feat: homepage SERVER METRICS now live — real CPU/MEM/DISK/BW/network/uptime/health/containers

This commit is contained in:
jae 2026-04-19 12:48:37 +00:00
parent b03bba89f7
commit f90f81d44a

View file

@ -591,11 +591,13 @@
const ulData = [];
const maxPoints = 80;
// Seed initial data
// Seed with zeros — graph populates with real samples from initLiveStats
for (let i = 0; i < maxPoints; i++) {
dlData.push(30 + Math.random() * 40);
ulData.push(20 + Math.random() * 30);
dlData.push(0);
ulData.push(0);
}
// Auto-scaling peak (starts at 10 Mbps floor)
let peakMbps = 10;
function drawLine(data, color, alpha) {
const w = canvas.width;
@ -609,7 +611,8 @@
for (let i = 0; i < data.length; i++) {
const x = i * step;
const y = h - (data[i] / 100) * h;
const scaled = Math.min(100, (data[i] / peakMbps) * 100);
const y = h - (scaled / 100) * h;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
@ -647,32 +650,25 @@
}
}
function updateStats() {
const dl = dlData[dlData.length - 1];
const ul = ulData[ulData.length - 1];
const dlEl = document.getElementById('dlSpeed');
const ulEl = document.getElementById('ulSpeed');
const pkEl = document.getElementById('packetCount');
if (dlEl) dlEl.textContent = (dl * 0.032).toFixed(1) + ' Gbps';
if (ulEl) ulEl.textContent = (ul * 0.028).toFixed(1) + ' Gbps';
if (pkEl) pkEl.textContent = Math.floor(dl * 180).toLocaleString() + ' pkt/s';
}
// Readouts (dlSpeed/ulSpeed/packetCount) are updated by initLiveStats with real values
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawGrid();
// Push new data
const lastDl = dlData[dlData.length - 1];
const lastUl = ulData[ulData.length - 1];
dlData.push(Math.max(10, Math.min(95, lastDl + (Math.random() - 0.48) * 12)));
ulData.push(Math.max(5, Math.min(85, lastUl + (Math.random() - 0.48) * 10)));
// Pull latest real Mbps from shared state set by initLiveStats
const stats = window.__serverStats || { dlMbps: 0, ulMbps: 0 };
dlData.push(stats.dlMbps || 0);
ulData.push(stats.ulMbps || 0);
if (dlData.length > maxPoints) dlData.shift();
if (ulData.length > maxPoints) ulData.shift();
drawLine(dlData, '#333333', 0.9);
drawLine(ulData, '#00a8ff', 0.6);
updateStats();
// Smooth auto-scaling peak
const curMax = Math.max.apply(null, dlData.concat(ulData).concat([1]));
peakMbps = peakMbps * 0.95 + Math.max(10, curMax * 1.2) * 0.05;
drawLine(dlData, '#00ff9d', 0.9); // download = green
drawLine(ulData, '#00a8ff', 0.7); // upload = blue
requestAnimationFrame(animate);
}
@ -818,67 +814,125 @@
});
}
// ─── LIVE SERVER STATS (from API) ───
// ─── LIVE SERVER STATS (from API) — REAL DATA ───
// Shared state used by the network graph
window.__serverStats = {
lastRx: null, lastTx: null, lastT: null,
dlMbps: 0, ulMbps: 0, bwPct: 0
};
function formatUptime(sec) {
sec = Math.floor(sec);
const d = Math.floor(sec / 86400);
const h = Math.floor((sec % 86400) / 3600);
const m = Math.floor((sec % 3600) / 60);
return d + 'd ' + String(h).padStart(2, '0') + 'h ' + String(m).padStart(2, '0') + 'm';
}
function applyBar(barId, valId, value, unit) {
const barEl = document.getElementById(barId);
const valEl = document.getElementById(valId);
if (!barEl || !valEl) return;
const v = Math.max(0, Math.min(100, value));
barEl.style.width = v + '%';
valEl.textContent = Math.round(v) + (unit || '%');
if (v > 85) {
barEl.style.background = 'linear-gradient(90deg, #ff2d2d, rgba(255,71,87,0.4))';
barEl.style.boxShadow = '0 0 8px rgba(255,71,87,0.4)';
valEl.style.color = '#ff2d2d';
} else if (v > 70) {
barEl.style.background = 'linear-gradient(90deg, #c9a227, rgba(255,165,2,0.4))';
barEl.style.boxShadow = '0 0 8px rgba(255,165,2,0.3)';
valEl.style.color = '#c9a227';
} else {
barEl.style.background = '';
barEl.style.boxShadow = '';
valEl.style.color = '';
}
}
function initLiveStats() {
// Assumed max link capacity for bandwidth % scaling (1 Gbps)
const LINK_MBPS = 1000;
function fetchStats() {
fetch(API_BASE + '/stats')
.then(r => r.json())
.then(d => {
// Update metric bars with real data
const metrics = [
{ bar: 'cpuBar', val: 'cpuVal', value: d.cpu_percent },
{ bar: 'memBar', val: 'memVal', value: d.memory_percent },
{ bar: 'diskBar', val: 'diskVal', value: d.disk_percent },
];
metrics.forEach(m => {
const barEl = document.getElementById(m.bar);
const valEl = document.getElementById(m.val);
if (!barEl || !valEl) return;
barEl.style.width = m.value + '%';
valEl.textContent = Math.round(m.value) + '%';
if (m.value > 85) {
barEl.style.background = 'linear-gradient(90deg, #ff2d2d, rgba(255,71,87,0.4))';
barEl.style.boxShadow = '0 0 8px rgba(255,71,87,0.4)';
valEl.style.color = '#ff2d2d';
} else if (m.value > 70) {
barEl.style.background = 'linear-gradient(90deg, #c9a227, rgba(255,165,2,0.4))';
barEl.style.boxShadow = '0 0 8px rgba(255,165,2,0.3)';
valEl.style.color = '#c9a227';
} else {
barEl.style.background = '';
barEl.style.boxShadow = '';
valEl.style.color = '';
}
});
// CPU / MEM / DISK bars
applyBar('cpuBar', 'cpuVal', d.cpu_percent);
applyBar('memBar', 'memVal', d.memory_percent);
applyBar('diskBar', 'diskVal', d.disk_percent);
// Update uptime from real data
const uptimeEl = document.getElementById('uptime');
// Bandwidth — compute MB/s delta from network_rx_bytes + network_tx_bytes
const now = (d.timestamp || Date.now() / 1000);
const s = window.__serverStats;
if (s.lastRx !== null && s.lastT !== null && now > s.lastT) {
const dt = Math.max(0.1, now - s.lastT);
const rxBps = Math.max(0, (d.network_rx_bytes - s.lastRx) / dt);
const txBps = Math.max(0, (d.network_tx_bytes - s.lastTx) / dt);
const rxMbps = (rxBps * 8) / 1e6;
const txMbps = (txBps * 8) / 1e6;
s.dlMbps = rxMbps;
s.ulMbps = txMbps;
const totalMbps = rxMbps + txMbps;
s.bwPct = Math.min(100, (totalMbps / LINK_MBPS) * 100);
applyBar('bwBar', 'bwVal', s.bwPct);
// Network graph stat readouts
const dlEl = document.getElementById('dlSpeed');
const ulEl = document.getElementById('ulSpeed');
const pkEl = document.getElementById('packetCount');
const fmtSpeed = mbps => mbps >= 1000 ? (mbps / 1000).toFixed(2) + ' Gbps'
: mbps >= 1 ? mbps.toFixed(1) + ' Mbps'
: (mbps * 1000).toFixed(0) + ' Kbps';
if (dlEl) dlEl.textContent = fmtSpeed(rxMbps);
if (ulEl) ulEl.textContent = fmtSpeed(txMbps);
if (pkEl) pkEl.textContent = (d.active_connections || 0).toLocaleString() + ' conns';
} else {
// Seed on first sample
applyBar('bwBar', 'bwVal', 0);
}
s.lastRx = d.network_rx_bytes;
s.lastTx = d.network_tx_bytes;
s.lastT = now;
// Uptime (HTML id is serverUptime)
const uptimeEl = document.getElementById('serverUptime') || document.getElementById('uptime');
if (uptimeEl && d.uptime_seconds) {
const totalSec = Math.floor(d.uptime_seconds);
const days = Math.floor(totalSec / 86400);
const hours = Math.floor((totalSec % 86400) / 3600);
const mins = Math.floor((totalSec % 3600) / 60);
uptimeEl.textContent = days + 'd ' + String(hours).padStart(2, '0') + 'h ' + String(mins).padStart(2, '0') + 'm';
uptimeEl.textContent = formatUptime(d.uptime_seconds);
}
// Update server health
// Server health — weighted score from cpu/mem/disk
const healthEl = document.getElementById('serverHealth');
if (healthEl) {
const health = Math.round(100 - (d.cpu_percent * 0.3 + d.memory_percent * 0.3 + d.disk_percent * 0.4) / 3);
healthEl.textContent = Math.min(99, Math.max(80, health)) + '%';
const penalty = (d.cpu_percent * 0.35 + d.memory_percent * 0.35 + d.disk_percent * 0.30);
const health = Math.round(100 - penalty * 0.35);
healthEl.textContent = Math.min(99, Math.max(40, health)) + '%';
}
// Container counts
// Power panel — use inverse of CPU load as "reliability" %
const powerEl = document.getElementById('powerPct');
if (powerEl) {
const rel = Math.max(60, Math.min(100, 100 - d.cpu_percent * 0.4));
powerEl.innerHTML = Math.round(rel) + '<span class="power-unit">%</span>';
}
// Containers
const containerEl = document.getElementById('containerUp');
const containerTotalEl = document.querySelector('.container-total');
const containerBar = document.getElementById('containerBar');
if (containerEl) containerEl.textContent = d.container_running;
if (containerTotalEl) containerTotalEl.textContent = d.container_total;
if (containerBar && d.container_total) {
containerBar.style.width = ((d.container_running / d.container_total) * 100) + '%';
}
})
.catch(() => {});
}
fetchStats();
setInterval(fetchStats, 10000); // refresh every 10s
setInterval(fetchStats, 5000); // refresh every 5s
}
// ─── LIVE WEATHER ───
@ -940,10 +994,10 @@
initHUDFlickers();
initPanelGlow();
initNetworkGraph();
initMetricBars();
// initMetricBars(); // disabled — real data from /api/stats via initLiveStats
initScanBar();
initPowerFlicker();
initServerHealth();
// initPowerFlicker(); // disabled — real load_avg-based via initLiveStats
// initServerHealth(); // disabled — real health computed in initLiveStats
// Live API integrations
initBlogFeed();