diff --git a/src/backend/index.js b/src/backend/index.js index 8bb25da..fad1b15 100644 --- a/src/backend/index.js +++ b/src/backend/index.js @@ -680,13 +680,28 @@ const app = new Elysia() try { const fullPath = `src/frontend/build${filePath}`; - // Use Bun.file() which doesn't throw for non-existent files + // For paths without extensions or directories, use SPA fallback immediately + // This prevents errors when trying to open directories as files + const ext = filePath.split('.').pop(); + const hasExtension = ext !== filePath && ext.length <= 5; // Simple check for file extension + + if (!hasExtension) { + // No extension means it's a route, not a file - serve index.html + const indexFile = Bun.file('src/frontend/build/index.html'); + return new Response(indexFile, { + headers: { + 'Content-Type': 'text/html; charset=utf-8', + 'Cache-Control': 'no-cache, no-store, must-revalidate', + }, + }); + } + + // Try to serve actual files (with extensions) const file = Bun.file(fullPath); const exists = file.exists(); if (exists) { // Determine content type - const ext = filePath.split('.').pop(); const contentTypes = { 'js': 'application/javascript', 'css': 'text/css', @@ -719,6 +734,7 @@ const app = new Elysia() } } catch (err) { // File not found or error, fall through to SPA fallback + logger.debug('Error serving static file, falling back to SPA', { path: pathname, error: err.message }); } // SPA fallback - serve index.html for all other routes @@ -730,8 +746,16 @@ const app = new Elysia() 'Cache-Control': 'no-cache, no-store, must-revalidate', }, }); - } catch { - return new Response('Frontend not built. Run `bun run build`', { status: 503 }); + } catch (err) { + logger.error('Frontend build not found', { error: err.message }); + return new Response( + '
The frontend application is not currently available. This usually means the application is being updated or restarted.
' + + 'Please try refreshing the page in a few moments.
', + { status: 503, headers: { 'Content-Type': 'text/html; charset=utf-8' } } + ); } })