From 431ab31fb81cfaa110ed66c3b12dd26904b4b9f2 Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Sat, 6 Jun 2026 21:57:42 -0400 Subject: [PATCH] fix(api): serve the full ./public tree, not just /assets/* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root-level PWA files (manifest.webmanifest, sw.js, registerSW.js, workbox-*.js, icon-*.png, apple-touch-icon.png) were falling through to the index.html catch-all and returning HTML — breaking the manifest (syntax error) and preventing the service worker from ever registering. serveStatic('/*') serves any existing file and calls next() for SPA routes, so index.html stays the fallback. Registered after /health, /api/*, /callback so those still win. --- apps/api/src/index.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 3d101d1..fb791a1 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -66,8 +66,13 @@ startBrokerPoller() // Drain the D-05 outbox every 15s: dispatches pending CalDAV writes to Fastmail startOutboxWorker() -// Serve React PWA static assets from ./public (Vite build output) -app.use('/assets/*', serveStatic({ root: './public' })) +// Serve React PWA static assets from ./public (Vite build output). +// MUST serve the whole ./public tree, not just /assets/* — root-level PWA files +// (manifest.webmanifest, sw.js, registerSW.js, workbox-*.js, icon-*.png, +// apple-touch-icon.png) live at the root. serveStatic calls next() when a file +// is not found, so SPA routes fall through to the index.html catch-all below. +// (Registered AFTER /health, /api/*, and /callback, so those win.) +app.use('/*', serveStatic({ root: './public' })) app.get('*', serveStatic({ path: './public/index.html' })) // Only start the HTTP server when this module is run directly (not imported in tests)