From cb71bcaaefada295d534e4a0d0257ec3e90083e9 Mon Sep 17 00:00:00 2001 From: Leo Date: Fri, 22 May 2026 15:02:00 -0400 Subject: [PATCH] Fix: move SPA catch-all route to end so API routes match first The catch-all @app.get('/{full_path:path}') was defined before API routes, causing GET requests for /api/* endpoints to be intercepted and served the admin SPA index.html instead of the actual API response. Moving the catch-all to after all API routes ensures proper route resolution. --- admin_server.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/admin_server.py b/admin_server.py index b03043f..9f20939 100644 --- a/admin_server.py +++ b/admin_server.py @@ -23,7 +23,7 @@ from typing import Optional from fastapi import FastAPI, HTTPException, Query, Request, UploadFile, File from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import JSONResponse, StreamingResponse +from fastapi.responses import FileResponse, JSONResponse, StreamingResponse from fastapi.staticfiles import StaticFiles from pydantic import BaseModel @@ -355,21 +355,6 @@ app.add_middleware( # Mount Cantaloupe sync routes app.include_router(cantaloupe_router) -# Serve admin frontend SPA (single index.html, no external assets needed) -from fastapi.responses import FileResponse - -FRONTEND_INDEX = Path(__file__).parent / "static" / "index.html" - - -@app.get("/") -@app.get("/{full_path:path}") -async def serve_frontend(full_path: str = ""): - """Serve the admin SPA — all routes return index.html.""" - if FRONTEND_INDEX.is_file(): - return FileResponse(str(FRONTEND_INDEX)) - return JSONResponse({"detail": "Frontend not found"}, status_code=404) - - # ─── Auth Middleware ──────────────────────────────────────────────────────── @@ -1621,3 +1606,17 @@ def reset_database(request: Request): raise HTTPException(status_code=500, detail=f"Reset failed: {str(e)}") conn.close() return {"detail": "Database reset complete — all tables recreated with seed data."} + + +# ─── SPA Frontend (catch-all — MUST be last route) ──────────────────────── + +FRONTEND_INDEX = Path(__file__).parent / "static" / "index.html" + + +@app.get("/") +@app.get("/{full_path:path}") +async def serve_frontend(full_path: str = ""): + """Serve the admin SPA — all routes return index.html.""" + if FRONTEND_INDEX.is_file(): + return FileResponse(str(FRONTEND_INDEX)) + return JSONResponse({"detail": "Frontend not found"}, status_code=404)