diff --git a/public/js/chart.js b/public/js/chart.js index 6467e8a..38e8b1e 100644 --- a/public/js/chart.js +++ b/public/js/chart.js @@ -30,7 +30,23 @@ class AreaChart { } setData(data) { - this.data = data; + if (!data || !data.timestamps) return; + + // Downsample if data is too dense (target ~1500 points for performance) + const MAX_POINTS = 1500; + if (data.timestamps.length > MAX_POINTS) { + const skip = Math.ceil(data.timestamps.length / MAX_POINTS); + const downsampled = { timestamps: [], rx: [], tx: [] }; + for (let i = 0; i < data.timestamps.length; i += skip) { + downsampled.timestamps.push(data.timestamps[i]); + downsampled.rx.push(data.rx[i]); + downsampled.tx.push(data.tx[i]); + } + this.data = downsampled; + } else { + this.data = data; + } + this.animProgress = 0; this.animate(); } @@ -132,16 +148,22 @@ class AreaChart { drawArea(ctx, values, getX, getY, chartH, p, fillColorTop, fillColorBottom, strokeColor, len) { if (!values || values.length === 0) return; + const useSimple = len > 500; + // Fill ctx.beginPath(); ctx.moveTo(getX(0), getY(values[0] || 0)); for (let i = 1; i < len; i++) { - const prevX = getX(i - 1); - const currX = getX(i); - const prevY = getY(values[i - 1] || 0); - const currY = getY(values[i] || 0); - const midX = (prevX + currX) / 2; - ctx.bezierCurveTo(midX, prevY, midX, currY, currX, currY); + if (useSimple) { + ctx.lineTo(getX(i), getY(values[i] || 0)); + } else { + const prevX = getX(i - 1); + const currX = getX(i); + const prevY = getY(values[i - 1] || 0); + const currY = getY(values[i] || 0); + const midX = (prevX + currX) / 2; + ctx.bezierCurveTo(midX, prevY, midX, currY, currX, currY); + } } ctx.lineTo(getX(len - 1), p.top + chartH); ctx.lineTo(getX(0), p.top + chartH); @@ -157,15 +179,20 @@ class AreaChart { ctx.beginPath(); ctx.moveTo(getX(0), getY(values[0] || 0)); for (let i = 1; i < len; i++) { - const prevX = getX(i - 1); - const currX = getX(i); - const prevY = getY(values[i - 1] || 0); - const currY = getY(values[i] || 0); - const midX = (prevX + currX) / 2; - ctx.bezierCurveTo(midX, prevY, midX, currY, currX, currY); + if (useSimple) { + ctx.lineTo(getX(i), getY(values[i] || 0)); + } else { + const prevX = getX(i - 1); + const currX = getX(i); + const prevY = getY(values[i - 1] || 0); + const currY = getY(values[i] || 0); + const midX = (prevX + currX) / 2; + ctx.bezierCurveTo(midX, prevY, midX, currY, currX, currY); + } } ctx.strokeStyle = strokeColor; ctx.lineWidth = 2; + ctx.lineJoin = 'round'; ctx.stroke(); } diff --git a/server/index.js b/server/index.js index 2bb5bee..fde6cb3 100644 --- a/server/index.js +++ b/server/index.js @@ -659,8 +659,8 @@ checkAndFixDatabase().then(() => { initialPreload(); }); -// Record traffic every 5 minutes -setInterval(recordTrafficStats, 5 * 60 * 1000); +// Record traffic every 5 seconds (17,280 points/day) +setInterval(recordTrafficStats, 5 * 1000); // Initial record after a short delay setTimeout(recordTrafficStats, 10000); diff --git a/server/prometheus-service.js b/server/prometheus-service.js index c5f5fc1..a740bbb 100644 --- a/server/prometheus-service.js +++ b/server/prometheus-service.js @@ -172,7 +172,8 @@ async function getOverviewMetrics(url, sourceName) { // Total traffic transmitted in last 24h query(url, 'sum by (instance, job) (increase(node_network_transmit_bytes_total{device!~"lo|veth.*|docker.*|br-.*"}[24h]))').catch(() => []), // Up instances (at least one successful scrape in last 5m) - query(url, 'max_over_time(up{job=~".*node.*|.*exporter.*"}[5m])').catch(() => []) + // We broaden the job filter to catch more variations of node-exporter jobs + query(url, 'max_over_time(up{job=~".*node.*|.*exporter.*|.*host.*"}[5m])').catch(() => []) ]); // Build per-instance data map @@ -252,6 +253,14 @@ async function getOverviewMetrics(url, sourceName) { inst.netTx = parseFloat(r.value[1]) || 0; } + // Final check: If an instance has non-zero CPU or Memory total data but is marked offline, + // it means we missed its 'up' metric due to job labels, but it's clearly sending data. + for (const inst of instances.values()) { + if (!inst.up && (inst.cpuPercent > 0 || inst.memTotal > 0)) { + inst.up = true; + } + } + // Aggregate let totalCpuUsed = 0, totalCpuCores = 0; let totalMemUsed = 0, totalMemTotal = 0; @@ -408,7 +417,7 @@ function mergeCpuHistories(histories) { async function getTrafficHistoryRange(url) { const now = Math.floor(Date.now() / 1000); const start = now - 86400; // 24h ago - const step = 300; // 5 minutes + const step = 5; // 5 seconds (17,280 points for 24h) const queries = [ 'sum(node_network_receive_bytes_total{device!~"lo|veth.*|docker.*|br-.*"})',