Covers the full BACKLOG DC-006 acceptance criteria:
- GET /api/totp/config — read current config
- POST /api/totp/setup — generate / import Base32 secret
- POST /api/totp/verify-setup — activate TOTP after setup
- POST /api/totp/verify — login with TOTP code → session + CSRF
- GET /api/totp/check-session — auth gate (200 / 401)
- POST /api/totp/disable — disable TOTP (requires valid code)
- POST /api/totp/config — update session duration
25 tests, all passing. Uses real otplib for code generation
(so we exercise actual TOTP math) but mocks credentialManager,
session, totpConfig, saveTotpConfig — those own their own state
machines (disk, cookies, file) that don't belong in a routes
test.
Also fixed a latent DC-005 bug: routes/auth/totp.js had wrong
require-path depth after the refactor (../../../src/... went 3
levels up instead of 2, breaking route load). Changed to
../../src/... for the 2-level depth. NOTE: the same depth bug
exists in many other depth-2 route files (auth/keys.js,
auth/sso-gate.js, auth/session-handlers.js, recipes/*, apps/*,
arr/*, config/*) — see BACKLOG.md DC-005 follow-up note. Tests
didn't catch this because no test previously imported the auth
routes; this new test exercises that import path.
Result: 904/904 Jest tests pass (879 baseline + 25 new).
ESLint: this file clean. Pre-existing 134 src/ warnings are
unrelated (DC-005 refactor moved files without re-applying
DC-004 lint cleanup — separate follow-up).
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.