From 54c1e9d92cfab48c866746bb5378f979e80f6b24 Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Sun, 5 Apr 2026 11:07:44 -0400 Subject: [PATCH] fix: use catch-all GET not mount for SPA, preserve WS routes --- backend/main.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/backend/main.py b/backend/main.py index 77188d0..76baed3 100644 --- a/backend/main.py +++ b/backend/main.py @@ -990,6 +990,10 @@ STATIC_DIR = Path("/app/static") if not STATIC_DIR.exists(): STATIC_DIR = Path("/app/frontend/dist") +# Mount /assets for Vite-built JS/CSS bundles +if STATIC_DIR.exists() and (STATIC_DIR / "assets").exists(): + app.mount("/assets", StaticFiles(directory=str(STATIC_DIR / "assets")), name="assets") + @app.get("/") async def serve_index(): @@ -999,7 +1003,17 @@ async def serve_index(): return {"message": "Claude Persistent Agent API v3.0", "docs": "/docs"} -# Mount static assets AFTER all API/WS routes are registered -# This ensures /api/* and WebSocket routes take priority -if STATIC_DIR.exists(): - app.mount("/", StaticFiles(directory=str(STATIC_DIR), html=True), name="spa") +@app.get("/{full_path:path}") +async def serve_spa(full_path: str): + """Serve React SPA for all non-API routes. API and WS are handled above.""" + if full_path.startswith("api"): + raise HTTPException(404) + # Try serving the file directly (e.g. vite.svg, favicon) + file_path = STATIC_DIR / full_path + if file_path.is_file(): + return FileResponse(str(file_path)) + # Fall back to index.html for SPA routing + index = STATIC_DIR / "index.html" + if index.exists(): + return FileResponse(str(index)) + raise HTTPException(404)