Spaces:
Sleeping
Sleeping
fix(backend): Correct SPA static files mounting order
Browse files- Move SPAStaticFiles mount to end of main.py (after all API routes)
- Fix 404 errors for API endpoints
- Static catchall now properly preserves /api routes
- backend/main.py +17 -17
- backend/spa_static_files.py +3 -6
backend/main.py
CHANGED
|
@@ -223,23 +223,6 @@ def get_websocket_manager() -> WebSocketManager:
|
|
| 223 |
"""Get WebSocket manager instance"""
|
| 224 |
return saap_app.websocket_manager
|
| 225 |
|
| 226 |
-
# =====================================================
|
| 227 |
-
# STATIC FILES - Serve Vue.js Frontend (SPA Mode)
|
| 228 |
-
# =====================================================
|
| 229 |
-
|
| 230 |
-
# Import custom SPA Static Files handler
|
| 231 |
-
from spa_static_files import SPAStaticFiles
|
| 232 |
-
|
| 233 |
-
# Mount static files for frontend (must be AFTER all API routes)
|
| 234 |
-
# SPAStaticFiles preserves API routes while serving Vue.js SPA
|
| 235 |
-
import os
|
| 236 |
-
frontend_dist = "/app/frontend/dist"
|
| 237 |
-
if os.path.exists(frontend_dist):
|
| 238 |
-
app.mount("/", SPAStaticFiles(directory=frontend_dist, html=True), name="static")
|
| 239 |
-
logger.info(f"✅ SPA Static files mounted: {frontend_dist}")
|
| 240 |
-
else:
|
| 241 |
-
logger.warning(f"⚠️ Frontend dist directory not found: {frontend_dist}")
|
| 242 |
-
|
| 243 |
# =====================================================
|
| 244 |
# ENHANCED ROOT ENDPOINT WITH HYBRID INFO (API)
|
| 245 |
# =====================================================
|
|
@@ -1639,3 +1622,20 @@ async def startup_message():
|
|
| 1639 |
# NOTE: No if __name__ == "__main__" block needed
|
| 1640 |
# Supervisor/Uvicorn will start the app directly
|
| 1641 |
# ==========================================
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
"""Get WebSocket manager instance"""
|
| 224 |
return saap_app.websocket_manager
|
| 225 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
# =====================================================
|
| 227 |
# ENHANCED ROOT ENDPOINT WITH HYBRID INFO (API)
|
| 228 |
# =====================================================
|
|
|
|
| 1622 |
# NOTE: No if __name__ == "__main__" block needed
|
| 1623 |
# Supervisor/Uvicorn will start the app directly
|
| 1624 |
# ==========================================
|
| 1625 |
+
|
| 1626 |
+
# =====================================================
|
| 1627 |
+
# STATIC FILES - Serve Vue.js Frontend (SPA Mode)
|
| 1628 |
+
# ⚠️ CRITICAL: Must be AFTER all API routes!
|
| 1629 |
+
# =====================================================
|
| 1630 |
+
|
| 1631 |
+
# Import custom SPA Static Files handler
|
| 1632 |
+
from spa_static_files import SPAStaticFiles
|
| 1633 |
+
|
| 1634 |
+
# Mount static files for frontend (must be AFTER all API routes)
|
| 1635 |
+
# SPAStaticFiles preserves API routes while serving Vue.js SPA
|
| 1636 |
+
frontend_dist = "/app/frontend/dist"
|
| 1637 |
+
if os.path.exists(frontend_dist):
|
| 1638 |
+
app.mount("/", SPAStaticFiles(directory=frontend_dist, html=True), name="static")
|
| 1639 |
+
logger.info(f"✅ SPA Static files mounted: {frontend_dist}")
|
| 1640 |
+
else:
|
| 1641 |
+
logger.warning(f"⚠️ Frontend dist directory not found: {frontend_dist}")
|
backend/spa_static_files.py
CHANGED
|
@@ -17,15 +17,12 @@ class SPAStaticFiles(StaticFiles):
|
|
| 17 |
async def get_response(self, path: str, scope: Scope):
|
| 18 |
"""
|
| 19 |
Handle requests:
|
| 20 |
-
- API routes (/api/*) → 404 (let FastAPI handle)
|
| 21 |
- Static files (exist) → serve file
|
| 22 |
- Everything else → index.html (SPA routing)
|
| 23 |
-
"""
|
| 24 |
-
# Let API routes pass through to FastAPI
|
| 25 |
-
if path.startswith("api/") or path.startswith("docs") or path.startswith("redoc") or path.startswith("openapi.json"):
|
| 26 |
-
# Return 404 to let FastAPI handle it
|
| 27 |
-
raise RuntimeError("Not found")
|
| 28 |
|
|
|
|
|
|
|
|
|
|
| 29 |
try:
|
| 30 |
# Try to serve the file
|
| 31 |
return await super().get_response(path, scope)
|
|
|
|
| 17 |
async def get_response(self, path: str, scope: Scope):
|
| 18 |
"""
|
| 19 |
Handle requests:
|
|
|
|
| 20 |
- Static files (exist) → serve file
|
| 21 |
- Everything else → index.html (SPA routing)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
+
NOTE: This should be mounted AFTER API routes in main.py
|
| 24 |
+
so API routes are handled first
|
| 25 |
+
"""
|
| 26 |
try:
|
| 27 |
# Try to serve the file
|
| 28 |
return await super().get_response(path, scope)
|