fix: prevent app crash when frontend build is missing

StaticFiles defaults to check_dir=True, which raises at import time if
frontend/dist/assets is missing on restart — taking down /healthz and
/auth/* along with the frontend. Pass check_dir=False so the mount never
crashes the app, and make the SPA fallback return a clear 503 instead of
an unhandled 500 when index.html is absent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013PphXq1s43DNRj1uWKGXof
This commit is contained in:
Indiana
2026-07-20 17:52:43 +00:00
parent bd0c162172
commit cd9fc49eed
2 changed files with 23 additions and 3 deletions

View File

@@ -1,7 +1,7 @@
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from pathlib import Path from pathlib import Path
from fastapi import FastAPI from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
@@ -28,9 +28,19 @@ async def healthz():
return {"status": "ok"} return {"status": "ok"}
app.mount("/assets", StaticFiles(directory=FRONTEND_DIST / "assets"), name="frontend-assets") app.mount(
"/assets",
StaticFiles(directory=FRONTEND_DIST / "assets", check_dir=False),
name="frontend-assets",
)
@app.get("/{full_path:path}") @app.get("/{full_path:path}")
async def serve_spa(full_path: str): async def serve_spa(full_path: str):
return FileResponse(FRONTEND_DIST / "index.html") index_file = FRONTEND_DIST / "index.html"
if not index_file.exists():
raise HTTPException(
status_code=503,
detail="Frontend not built. Run `npm run build` in frontend/ and restart.",
)
return FileResponse(index_file)

View File

@@ -14,3 +14,13 @@ async def test_auth_routes_still_work_alongside_spa_fallback(client):
"/auth/register", json={"username": "frontendcheck", "password": "spookyspooky"} "/auth/register", json={"username": "frontendcheck", "password": "spookyspooky"}
) )
assert response.status_code == 201 assert response.status_code == 201
@pytest.mark.asyncio
async def test_missing_frontend_build_returns_clear_error(client, monkeypatch, tmp_path):
import app.main as main_module
monkeypatch.setattr(main_module, "FRONTEND_DIST", tmp_path)
response = await client.get("/some/route")
assert response.status_code == 503
assert "npm run build" in response.json()["detail"]