diff --git a/package.json b/package.json index 896b581..ee03a39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "site-gateway", - "version": "0.11.60", + "version": "0.11.61", "private": true, "description": "Site Gateway: simple self-hosted website publishing, reverse proxying, and automatic HTTPS.", "type": "module", diff --git a/src/public/app.js b/src/public/app.js index 46ed233..b3e6c24 100644 --- a/src/public/app.js +++ b/src/public/app.js @@ -245,11 +245,23 @@ function renderPerformance() { $("#performance-trend-title").textContent = `Requests · last 6 hours${selected ? ` · ${selected}` : ""}`; const points = data.trend || []; const max = Math.max(1, ...points.map(point => point.count)); - const stepX = points.length > 1 ? 600 / (points.length - 1) : 600; - const path = points.map((point, index) => `${index === 0 ? "M" : "L"}${(index * stepX).toFixed(1)},${(120 - (point.count / max) * 110 - 4).toFixed(1)}`).join(" "); - $("#performance-sparkline").innerHTML = points.length ? `` : ""; + const left = 34, right = 8, top = 10, bottom = 20, width = 600, height = 140; + const plotWidth = width - left - right, plotHeight = height - top - bottom; + const xAt = index => left + (points.length > 1 ? (index / (points.length - 1)) * plotWidth : plotWidth); + const yAt = count => top + plotHeight - (count / max) * plotHeight; + const gridLines = [0, 0.5, 1].map(fraction => { + const y = (top + plotHeight * (1 - fraction)).toFixed(1); + const value = Math.round(max * fraction); + return `${value}`; + }).join(""); + const firstPoint = points[0], lastPoint = points[points.length - 1]; + const timeLabels = points.length ? `${escapeHtml(formatTime(firstPoint.at))}${escapeHtml(formatTime(lastPoint.at))}` : ""; + const linePoints = points.map((point, index) => `${xAt(index).toFixed(1)},${yAt(point.count).toFixed(1)}`).join(" "); + const areaPath = points.length ? `M${xAt(0).toFixed(1)},${(top + plotHeight).toFixed(1)} L${linePoints.replaceAll(" ", " L")} L${xAt(points.length - 1).toFixed(1)},${(top + plotHeight).toFixed(1)} Z` : ""; + $("#performance-sparkline").setAttribute("viewBox", `0 0 ${width} ${height}`); + $("#performance-sparkline").innerHTML = points.length ? `${gridLines}${timeLabels}` : 'No request data for this window yet.'; const routes = data.routes || []; - $("#performance-rows").innerHTML = routes.length ? routes.map(route => `${escapeHtml(route.host)}${route.hourRequests}${route.dayRequests}${route.dayErrors ? `${route.dayErrors}` : "0"}${route.dayAvgMs == null ? "—" : `${route.dayAvgMs} ms`}`).join("") : 'No requests have been logged yet.'; + $("#performance-rows").innerHTML = routes.length ? routes.map(route => `${escapeHtml(route.host)}${route.hourRequests}${route.dayRequests}${route.dayErrors ? `${route.dayErrors}` : "0"}${route.dayAvgMs == null ? "—" : `${route.dayAvgMs} ms`}`).join("") : 'No requests have been logged yet.'; if (selected) $(`#performance-rows tr.row-highlight`)?.scrollIntoView({ block: "nearest" }); } diff --git a/src/public/index.html b/src/public/index.html index c2e4eef..376e4a5 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -145,10 +145,13 @@

No requests recorded yet. Not checked yet.

Trend

Requests · last 6 hours

- + +
+
+

Per-route

Throughput by domain

+

Requests, error rate, and average response time for each configured domain.

+
DomainLast hourLast 24hErrors (24h)Avg. response
-

Per-route

Throughput by domain

Requests, error rate, and average response time for each configured domain.

-
DomainLast hourLast 24hErrors (24h)Avg. response