P5: SPA cutover — retire Next.js, gateway serves the Vite bundle (#1444) #1453

Merged
fred merged 4 commits from feat/p5-spa-cutover into next 2026-08-27 13:06:50 +00:00
4 Commits
Author SHA1 Message Date
fred 752ee40edd preflight: retire the .next generated-state certification (#1444)
ci/woodpecker/pr/ci Pipeline was successful
CI's test step caught the gap: sourceFingerprint resolved
next/package.json from apps/web, which P5 removed. The certification
(fingerprint incl. @next/env expansion, symlink manifest, foreign-uid
scan, build lock) defended typecheck against stale apps/web/.next
output; the Vite SPA has no generated tree later gates consume, so the
class it defended against is gone. Preflight keeps its other job, the
missing-binaries check with exit 42. clean-generated.mjs (a .next
quarantine helper) goes with it.
2026-08-27 07:48:40 -05:00
fred c5a45784ac ci: retire the standalone web image and build-web step (#1444)
ci/woodpecker/pr/ci Pipeline failed
The gateway image now carries the SPA bundle, so web.Dockerfile,
scripts/build-web.mjs (+ test), and the publish build-web step go away.
The two CI-structure tests drop build-web from their expected-effects
lists. .env.example replaces the Next block with WEB_DIST_DIR docs.
2026-08-27 07:32:38 -05:00
fred 385b2b6886 gateway: serve the SPA bundle same-origin (#1444)
New spa/serve-spa.ts: @fastify/static with wildcard:false plus a GET /*
catch-all that returns index.html for non-backend paths and a JSON 404
for unknown /api, /mcp, /socket.io paths. WEB_DIST_DIR unset disables
serving (dev uses the Vite dev server, which proxies to the gateway);
set but invalid fails loud at boot.

A wildcard route, not setNotFoundHandler: Nest installs its own
not-found handler at init and Fastify allows only one. find-my-way
matches most-specific-first, so declared routes win over the catch-all.
Cache semantics stay the library default (max-age=0 + ETag
revalidation); immutable hashed-asset caching is deferred to P6.

gateway.Dockerfile builds the web bundle and ships it at /app/web-dist
with WEB_DIST_DIR set.
2026-08-27 07:32:27 -05:00
fred 70c7311a46 web: cut over to Vite SPA, retire the Next.js shell (#1444)
Delete the src/app tree, next.config.ts, next-env.d.ts, and the Next-only
guard/header components. Port AppShell/Sidebar/Topbar to react-router and
mount them as a DashboardLayout route over all authenticated routes. Strip
'use client' directives, move globals.css up from the deleted app/ tree,
rewrite tsconfig for Vite/Bundler resolution, drop the next dependency.

Test fixes the port surfaced: jsdom v29 has no window.matchMedia (sidebar
breakpoint) so setup.ts stubs it; the router-boundary specs render inside
ThemeProvider because the chrome's ThemeToggle requires the context.
2026-08-27 07:32:14 -05:00