From cd9fc49eed913e176cf6e18f88053544bda27889 Mon Sep 17 00:00:00 2001 From: Indiana Date: Mon, 20 Jul 2026 17:52:43 +0000 Subject: [PATCH] fix: prevent app crash when frontend build is missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Claude-Session: https://claude.ai/code/session_013PphXq1s43DNRj1uWKGXof --- backend/app/main.py | 16 +++++++++++++--- backend/tests/test_frontend_serving.py | 10 ++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/backend/app/main.py b/backend/app/main.py index a41e62e..d4ee9c2 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1,7 +1,7 @@ from contextlib import asynccontextmanager from pathlib import Path -from fastapi import FastAPI +from fastapi import FastAPI, HTTPException from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles @@ -28,9 +28,19 @@ async def healthz(): 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}") 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) diff --git a/backend/tests/test_frontend_serving.py b/backend/tests/test_frontend_serving.py index 4037837..a2f591d 100644 --- a/backend/tests/test_frontend_serving.py +++ b/backend/tests/test_frontend_serving.py @@ -14,3 +14,13 @@ async def test_auth_routes_still_work_alongside_spa_fallback(client): "/auth/register", json={"username": "frontendcheck", "password": "spookyspooky"} ) 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"]