Completes the JS→TS server migration. server.js → server.cts (Node
type-stripping, no build step), and every launch/config reference now
points at it: package.json (main/dev:api/start/check:server) and the
Docker CMD.
scripts/seedDemoData.js is required at runtime by routes/user.cts, so it
crosses into the server runtime — migrated to .cts and its require of
db/database updated to the .cts path. The remaining scripts/*.js are
standalone dev/ops tooling (outside the runtime and the check:server
boundary); their stale extensionless requires of now-.cts modules were
repaired so they still run, and the PM2 ecosystem config entry point was
updated to server.cts.
Verified: 0 runtime .js remain in the server tree; typecheck:server and
check:server clean; 226/226 tests pass; `node server.cts` boots and
/api/health returns 200.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Dockerfile: npm ci (reproducible, lock-pinned, matches CI) instead of npm
install; add a HEALTHCHECK against the /api/health liveness route.
- package.json: pin engines node>=22.18.0 — the server require()s .mts/.cts and
relies on Node's default TS type-stripping (unflagged only on >=22.18), which
was previously undocumented on a floating base image.
- .dockerignore: exclude .env / .env.* so a stray env file can't be baked into
the image by COPY . .
- docker-compose: set TRUST_PROXY=true (behind the reverse proxy, so Secure
cookies + client IP for rate-limit/audit are correct); flip CSRF_HTTP_ONLY to
true (SPA reads the token from an endpoint, so no JS cookie access needed);
remove the active default INIT_ADMIN_PASS; document TOKEN_ENCRYPTION_KEY (set
it to move at-rest secrets off the DB-resident key); drop obsolete version key.
Not included: TOKEN_ENCRYPTION_KEY itself — that's a secret to generate and
inject into the deployment env (the app self-migrates to the env-key scheme on
next boot); documented in compose + .env.example.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Dockerfile was pinned to node:18-alpine which can't load crypto
synchronously in @rollup/plugin-terser -> serialize-javascript.
Upgrading build stage to node:22-alpine to match host Node.