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}}
Three cascading bugs in server.js's workflow engine init block:
1. fetchT was referenced but never imported from ./src/utils/http
2. notification-manager was called as factory function but the module
now exports a class (NotificationManager) - need 'new'
3. servicesStateManager was referenced in workflowCtx but only created
later inside an async IIFE (out of scope at workflow init time)
Result: every container start logged
Workflow engine failed to initialize - fetchT is not defined
and the workflow engine never actually wired to resourceMonitor/
updateManager event sources. The 'app' context workflow engine
still ran but didn't get those connections.
Fix:
- Import fetchT at top of file
- Use 'new' for NotificationManager instantiation
- Hoist servicesStateManager creation before workflow init and
remove the duplicate inside the health-checker async IIFE
Verified: container restart shows
[server] Workflow engine initialized
[ResourceMonitor] Workflow engine configured
[UpdateManager] Workflow engine configured
in the log, no more errors at startup.
Also bumps VERSION to current SHA (bump from c64bbe2).
- status/js/totp-recovery.js: NEW. Wires up recovery panel on the TOTP
gate. Pastes Base32 -> /api/v1/totp/setup -> /verify-setup -> session.
Exposes window._refreshRecoveryLink() called by totp-auth.js.
- status/js/totp-auth.js: showTotpOverlay() now calls
_refreshRecoveryLink() so the recovery link hides when TOTP is healthy
and appears when it's broken.
- status/js/totp-settings.js: removed setupSection.style.display='none'
so 'Import existing secret' is always visible; added 'Download backup
file' button after setup that exports the Base32 + recovery
instructions as JSON.
- status/index.html: added 'Lost access? Recover with saved Base32
key ->' link to the TOTP overlay plus the recovery panel itself;
added title tooltip to the auth card reminding users to save the
Base32 on first setup.
- status/build.js: include JS('totp-recovery.js') in the core bundle
after totp-auth.js (since recovery registers a hook auth calls).
- credential-manager.js: add diagnose(key) method that distinguishes
ok | missing | unreadable | corrupt instead of silently returning null
- crypto-utils.js: silent fallback to .encryption-key.bak when primary
can't decrypt existing credentials; first-run bootstrap writes .bak;
rotateKey() backs up old key before swap
- routes/auth/totp.js: new public /api/v1/totp/recovery-info endpoint
returns {status, isSetUp, hint} so UI can show meaningful errors
- middleware.js: add /totp/recovery-info to PUBLIC_ROUTES so the
locked-out user can read the diagnostic without being logged in
- Service Filter Bar: search by name, filter by status (online/offline)
- Batch Operations: multi-select containers for start/stop/restart
- Container Snapshots: create and manage Docker checkpoints
- Added filter bar and batch action bar to index.html
- Added snapshot button to Admin tools section
- New JS modules: service-filter.js, batch-operations.js, snapshot.js
- Updated build.js to include new modules in bundle
- Added window.openContainerLogsModal(containerId, containerName) function
- Service cards (grid.js) already call this when clicking the 📋 logs button
- Modal now pre-selects the correct container when called from a card
- Rebuilt dist files
- New container-logs.js module for viewing Docker container logs
- Integrated with existing API endpoints (/logs/containers, /logs/container/:id, /logs/stream/:id)
- Features:
- Select container from dropdown
- View logs with stdout/stderr color coding
- Real-time log streaming via SSE
- Search/filter within logs
- Download logs as text file
- Line count and filter indicators
- Added '📜 Container Logs' button to Tools section in index.html
- Added to features.js bundle via build.js
- Rebuilt dist files
- 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.
- monitoring.js: Added log dependency, replaced console.log with log.warn
- themes.js: Added log dependency, replaced console.error with log.error
- src/app.js: Pass log to monitoringRoutes and themesRoutes
This fixes error messages being lost to stdout instead of proper log files.
- Create src/config/paths.js for all file paths and env vars
- Create src/config/site.js for site configuration loading
- Create src/config/index.js as unified config export
- Prepare for server.js modularization (Phase 2.1)
Part of deslopification roadmap: break 1997-line server.js into layers
- Consolidated all error classes into single errors.js
- Removed duplicate error definitions (NotFoundError, etc.)
- Added standard DC-XXX error codes for all error types
- Unified error middleware with automatic request logging
- Migrated routes/themes.js to throw-based error pattern
- Updated routes/services.js to use ConflictError
- Cleaner server.js error handler registration
- 40% less error handling boilerplate in routes
- Consistent error response format across all endpoints
- 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
- Replaced god object ctx with explicit dependency injection
- Added JSDoc documenting all required dependencies
- Updated response calls to use response-helpers (success/error)
- Maintained all existing functionality
- Self-documenting: you can see exactly what this route needs
- Easier testing: mock only what's actually used (14 deps vs 50+ ctx properties)
- src/context/caddy.js: Caddyfile manipulation, reload, config generation
- src/context/dns.js: DNS API wrapper with token management
- All context modules use factory pattern with explicit dependencies
- src/context/caddy.js: Caddyfile manipulation, reload, config generation
- Uses dependency injection (init() pattern) for siteConfig, log, fetchT
- Atomic mutex-based modifications with rollback on failure
- All Caddy operations now in one module
- src/utils/logger.js: Structured JSON logging
- src/context/docker.js: Docker API wrapper (pull, findContainer, getUsedPorts)
- All modules can now be imported directly instead of via ctx
- Added automatic volume path translation in deployment (deploy.js)
- Updated FileBrowser template to use /opt/ instead of hard-coded E:/
- Migrated self-updater.js to use centralized platformPaths module
- Updated UI placeholders to use platform-neutral paths (/media/)
- All paths now automatically adapt to Windows or Linux at runtime via process.platform detection