fix(monitoring): flatten CPU/mem data, add health summary, public + rate-limit monitoring/stats
CI / Test & Lint (push) Has been cancelled
CI / Security audit (push) Has been cancelled

Three coordinated fixes for the System Overview widget:

1. routes/monitoring.js — flatten getAllStats() shape from
   {current:{cpu:{percent},memory:{percent}}} to {cpu,memory,memoryUsage}
   so the widget's Number() coercion actually produces numbers, not NaN.
   Skill reference: references/totp-and-system-overview-pitfalls.md §3.

2. routes/health.js — add summary block to /health-checks/status response.
   Widget looks for {healthy, unhealthy, total} but only per-service objects
   existed. Permissive on healthy side (up|healthy|online), strict on
   unhealthy (down|unhealthy|offline|error); anything else counted as
   unknown. Same skill §3 reference.

3. middleware.js — add /api/v1/monitoring/stats to PUBLIC_ROUTES and the
   rate-limit skip list. The widget polls it every 5s from the dashboard;
   cookie-auth works but listing it explicitly makes it future-proof
   against auth-cookie expiry and prevents per-second 429s.

End-to-end test (unauthenticated):
  GET /api/v1/monitoring/stats  -> {cpu: 8.71, memory: 0.37, ...}
  GET /api/v1/health-checks/status -> {summary: {healthy:11, unhealthy:4, total:15}}
This commit is contained in:
Krystie
2026-06-18 21:17:16 -07:00
parent 4853f1feb8
commit 6809fc5cca
4 changed files with 37 additions and 4 deletions
+4 -1
View File
@@ -306,6 +306,9 @@ module.exports = function configureMiddleware(app, {
{ path: '/api/v1/config', exact: true, method: 'GET' },
{ path: '/api/v1/services/status', exact: true, method: 'GET' },
{ path: '/api/v1/health-checks/status', exact: true, method: 'GET' },
// System Overview widget on the dashboard — needs the flattened CPU/mem
// data without going through auth. See skill references/totp-and-system-overview-pitfalls.md §3.
{ path: '/api/v1/monitoring/stats', exact: true, method: 'GET' },
// Read-only update/version info shown on the dashboard view (verification
// modal, topbar version, update badges). Mutating actions — update-apply,
// rollback (POST) — are NOT listed here and stay TOTP-protected.
@@ -402,7 +405,7 @@ module.exports = function configureMiddleware(app, {
...RATE_LIMITS.GENERAL,
standardHeaders: true,
legacyHeaders: false,
skip: (req) => isTest || req.path === '/health' || req.path === '/api/v1/health' || req.path.startsWith('/probe/') || req.path.startsWith('/api/v1/auth/gate/') || req.path === '/api/v1/totp/check-session' || req.path.endsWith('/health-checks/status') || req.path.endsWith('/csrf-token') || req.path === '/api/v1/dns/logs' || req.path === '/api/v1/license/status' || req.path.startsWith('/api/v1/license/feature/') || req.path === '/api/v1/services' || req.path === '/api/v1/config',
skip: (req) => isTest || req.path === '/health' || req.path === '/api/v1/health' || req.path.startsWith('/probe/') || req.path.startsWith('/api/v1/auth/gate/') || req.path === '/api/v1/totp/check-session' || req.path.endsWith('/health-checks/status') || req.path.endsWith('/monitoring/stats') || req.path.endsWith('/csrf-token') || req.path === '/api/v1/dns/logs' || req.path === '/api/v1/license/status' || req.path.startsWith('/api/v1/license/feature/') || req.path === '/api/v1/services' || req.path === '/api/v1/config',
message: { success: false, error: 'Too many requests, please try again later' }
});