The /api/v1/caddycode/{generate,validate,templates} endpoints already
existed (commit 7f83151, GLM grade B); the frontend visual builder
page is the missing piece. This commit ships:
- status/js/caddy-builder.js — IIFE module that injects a modal with
a form-driven visual builder. Form state → JSON payload → live
POST /generate → preview pane. 5 presets loaded from
/api/v1/caddycode/templates (simple, websocket, auth-gated,
cors-api, subdirectory). Custom headers list (add/remove rows),
live validation, copy-to-clipboard, reset. State machine is a
single plain-object `state` snapshot — debounced regeneration
(250 ms). Exposes window.__caddyBuilder for testing.
- status/css/caddy-builder.css — page-specific styles, themed
via existing --bg / --border / --accent / --ok-fg / --warn-fg /
--err-fg CSS variables. Mobile-friendly single-column layout
below 880 px.
- status/index.html — adds /css/caddy-builder.css link + the
"🔧 Reverse Proxy Builder" button in the Tools menu (id
caddy-builder-btn). No other UI changes.
- status/build.js — registers caddy-builder.js in features.js
bundle. Same load-order conventions as the surrounding modules.
- dashcaddy-api/__tests__/unit/caddy-builder.unit.test.js — 19
pure-function tests covering state defaults, buildPayload
(whitespace trim, blank-key drop, authService/stripPrefix
nullification, all booleans explicit), applyTemplate (5
presets + unknown-id no-op), and generate() against mocked
fetch (POST shape, 400 error capture, network-failure
graceful handling, short-circuit on missing fields).
Includes XSS regression: verifies the global escapeHtml
contract used by renderHeadersList is safe against
<script>/quote-injection.
Verified:
- jest __tests__/unit/caddy-builder.unit.test.js: 19/19 pass
- jest __tests__/routes/caddycode-fleet.routes.test.js: 8/8 pass
- node build.js: features.js now bundles 27 files (was 26),
no errors, new SW cache tag dashcaddy-shell-1ceeb68cff
- Live dry-run of the backend /generate endpoint with the
state the form produces matches the existing 1633-test
baseline (no regression in caddycode routes)
- Frontend bundle grep finds 6 distinct caddy-builder
identifiers in dist/features.js
[grade=B]
- New src/utilities/validate.js: validateBody(schema) middleware + 9 schemas
(backupConfigUpdate, backupScheduleCreate, backupRestore, backupRestoreFile,
appDeploy, appRestore, appRevert, assetUpload, logoUpload)
- Uses Joi's authoritative CIDR validator (rejects malformed IPv6 like ::::/64
that the previous hex/colon regex would have accepted)
- appDeploy.config uses .unknown(true) for forward-compat with template-specific
fields (sslType, dnsType, plexClaimToken, etc.) — preserves fields the live
frontend posts, prevents a behavioural regression
- appRestore uses Joi.any().custom() so the empty-body semantics hold under
middleware stripUnknown (default) — body with extra keys now rejected
- Wired into 8 destructive routes: backups schedule/restore/config, apps
deploy/restore/revert, assets upload/logo
- Duplicate legacy POST /backups/schedule handler (line 519) marked LEGACY
with TODO removal note (Express only matches first registration; this
handler is unreachable under normal routing)
- Removed redundant manual appId check in /backups/schedule (Joi schema
enforces it)
- Removed unused 'mime' destructure in /assets/favicon (decodeImageData
validates MIME internally)
- 41 unit tests covering every exported schema + middleware integration
- 1539/1539 Jest tests pass, zero new ESLint warnings