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}}
After the DC-005 module reorganization (41 files moved into src/ subdirs),
138 test suites failed because the refactor script's path-rewrite logic
missed three categories:
1. Files inside src/ doing 'require("./src/...")' — should be 'require("../...")'
2. Files in src/X/Y/ doing 'require("../../../src/...")' — should be 'require("../../...")'
3. Test files in __tests__/ with leftover 'require("../../../src/...")' paths
Root cause: the original refactor script ran before all files were moved,
so it computed relative paths against stale filesystem state.
Result:
- 30/30 test suites pass
- 879/879 tests pass (was: 18/30 suites, 614/687 tests)
Also fixed:
- routes/apps/restore.js: wrong responses import path
- routes/*/*.js: '../../src/utilities/X' → '../src/utilities/X' (depth 2 routes)
Convert ~160 raw res.json()/res.status().json() calls across 32+ files
to use centralized helpers from src/utils/responses.js (ok, errorResponse,
successMessage, notFound, validationError, forbidden, unauthorized, conflict).
No behavior changes — response shapes are identical. Future schema changes
(e.g., requestId envelope) only need to update one module.
Fix error vs errorResponse signature mismatch in routes/health.js CA cert
endpoint where error(res, message, statusCode) was being called with
errorResponse(res, statusCode, message, extras) argument order.
Files changed: middleware.js, csrf-protection.js, error-handler.js,
license-manager.js, src/app.js, and 27 route files.
Test suite: 755 pass / 4 pre-existing failures (services credential tests).
Three small cleanups for v1.14.0:
1. /caddy/cas now uses standard success envelope
Was: { status: 'success', data: { cas: caList } }
Now: { success: true, cas: caList }
Updated frontend service-infrastructure.js to match.
2. /api/health/ca now uses standard envelope + meaningful HTTP codes
Was: { status, message, daysUntilExpiration } with 200 on every error
Now: { success, caStatus, message|error, daysUntilExpiration }
with 200 / 404 / 500 as appropriate
caStatus field preserves the original 'healthy'/'warning'/'critical'/'error'
semantic so any future consumer of the CA-health state still has it.
Tests updated to match.
3. Dead timeout: keys in fetchT opts are now a warning, not a silent strip
src/utils/http.js:41 used to do without telling
anyone. Callers that wrote fetchT(url, { timeout: 5000 }) got the default
5s timeout with no indication that their explicit value was ignored.
Now it logs a warning naming the call site, then strips the key.
Fixed 4 call sites that had stale timeout: keys:
- src/context/caddy.js
- src/context/dns.js
- src/context/provider-dns.js
- routes/dns.js (2 places)
Two cleanups in one pass for the v1.14.0 'works on any platform' theme:
1. Response helpers — merged src/utils/responses.js and the root-level
response-helpers.js into a single module at src/utils/responses.js.
The old module had a richer set (created, noContent, validationError,
unauthorized, forbidden, notFound, conflict) and is now re-exported
from the new location. Updated 15 routes to import from
src/utils/responses and deleted the root response-helpers.js.
2. Error logger — error-handler.js now uses the unified
src/utils/logging.js#logError (same one src/app.js uses), so all errors
go to one log file with one rotation policy. Removed the dead
asyncHandler export (the real one is in src/utils/async-handler.js
and is used everywhere). Deleted the legacy error-logger.js.
Both are invisible to users — same HTTP response shapes, same log file
path, same error format. Internal-only refactor.
- Add /api/v1/monitoring/stats and /api/v1/health-checks/status to PUBLIC_ROUTES
so the frontend widget can fetch without auth
- Transform monitoring stats response from nested {cpu:{percent}} to flat
{cpu: number, memory: number, memoryUsage: number} for the widget
- Add summary {healthy, unhealthy, total} to health-checks/status response
- health.js: replace magic number 5000 with TIMEOUTS.HTTP_DEFAULT (twice)
- services.js: replace magic number 5000 with TIMEOUTS.HTTP_DEFAULT
Both files already import TIMEOUTS from constants but weren't using it.
- Replaced god object ctx with explicit dependency injection
- Added JSDoc documenting required dependencies (8 deps vs 50+)
- Updated response calls to use response-helpers (success/error)
- Self-documenting: you can see exactly what this route needs
- Health checks, pylon relay, CA cert validation all preserved
DashCaddy Pylon is a lightweight probe agent that runs on remote
networks to relay health checks for services the main DashCaddy
instance can't reach directly (e.g., .sami domains, LAN IPs).
- Standalone zero-dependency Node.js script (pylon/dashcaddy-pylon.js)
- Optional API key auth, HEAD→GET fallback, batch probe support
- Health routes now try direct check first, fall back to pylon relay
- New endpoints: /health/probe (act as pylon), /health/pylon (status)
- Config: add "pylon": { "url": "...", "key": "..." } to config.json
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add url-resolver.js with single resolveServiceUrl() used by all 5 consumers
(probes, health routes, health checker auto-config)
- Health checker now does full sync (add/update/remove) instead of add-only,
and re-syncs automatically after every services.json mutation
- docker-maintenance and log-digest are now optional imports with try/catch,
preventing container crashes when these files are absent
- Add null guards in routes/logs.js for graceful 503 responses
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full codebase including API server (32 modules + routes), dashboard frontend,
DashCA certificate distribution, installer script, and deployment skills.