Closes the coverage-gap audit's sharpest item (H8/D-auth): mountAuth's
return is captured (authGuards) and design-route ownership is passed
explicitly to mountEditorApi (customerId/isAdmin off req.customer/
req.admin — matches the module defaults but survives default drift);
/api/fonts/registered is admin-gated by a host-level requireAdmin
registered ahead of the module route (auth.middleware can't do this:
the module applies it to the whole /designs group, which would lock
guests out of design persistence). mockupsDir/storageDir now threaded
so design-route thumbnails get the mockup backdrop. Dev harness
unchanged: without DATABASE_URL the roster stays open and ownership
resolves to the guest model.
Also fixes a real bug the boot test surfaced: the host hardcoded
001_designs.sql, silently skipping 002_design_family.sql — the store
SELECTs design_family_id unconditionally, so every GET /api/designs
500'd ('column does not exist'). Now applies every *.sql in the
module's migrations dir in filename order (all idempotent).
Verified by booting both modes: auth ON → roster 403 admin_required,
designs unknown-id 404 (was 500), both migrations applied on a fresh
db; auth OFF → roster 200 with the font list, warning logged. NOTE:
goods-editor's ./server export resolves to dist/, which was stale
(built Jul 7 — predating the module's auth option, roster route, and
Aug security fixes); rebuilt from the branch source. Any deploy must
run the module's build, not just npm install the file: dep.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Server (spans the v0.6→v0.8 arc in one file):
- template-manager mounted with the productsStore catalog, PNG-only
mockup multer, facet personalization config (§R10, FACET_KEY-gated,
inert when unset), and the marketing-preview drainer (2s tick, one
render per tick, .unref()).
- auth-manager bootstrap: DATABASE_URL gates the pg Pool, migrations,
and mountAuth (magic-link customer + Google SSO admin + facet);
absent DATABASE_URL logs a warning and the host boots without auth.
- goods-editor design persistence (#23): idempotent 001_designs.sql
applied at boot, db handle passed to mountEditorApi so /api/designs
routes mount; without Postgres the client falls back to
cart-without-design_id.
- reference-protected TTL sweep (#24): before each pass, every
/uploads/ basename referenced by template-manager JSON or persisted
designs is collected and protected, and exports/designs (db-driven
lifecycle, unrecoverable sources) is skipped entirely. Sweep kickoff
moves below the bootstrap (pgPool TDZ). sweep-worker.mjs is the
standalone process variant for production.
Client: main.jsx wraps the tree in AuthProvider (single /api/auth/me
fetch shared by useCustomer/useAdmin, degrades on 404) and mounts
AppRouter. eslint bumps server.js to ecmaVersion 2022 (top-level
await); .env.example documents the full v0.8 env surface.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>