diff --git a/backend/app/main.py b/backend/app/main.py index 46b2ceb..88e5157 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -17,6 +17,7 @@ from typing import Any import psycopg from fastapi import Depends, FastAPI, Response from fastapi.responses import JSONResponse +from fastapi.staticfiles import StaticFiles from pydantic import BaseModel from app.domains import accounts, storefronts @@ -89,7 +90,7 @@ def _set_session_cookie(response: Response, account: accounts.Account) -> None: ) -def create_app(database_url: str | None = None) -> FastAPI: +def create_app(database_url: str | None = None, static_dir: str | Path | None = None) -> FastAPI: _ensure_app_logging() dsn = database_url or config.database_url() @@ -202,6 +203,13 @@ def create_app(database_url: str | None = None) -> FastAPI: ) return JSONResponse(status_code=201, content={"id": sf.id, "name": sf.name}) + # Deployed topology (launch-app SPEC §2): nginx proxies everything here, so the + # backend serves the built SPA. Mounted LAST so /healthz and /api/* win. In dev the + # dist dir doesn't exist (Vite serves the frontend) and the mount is skipped. + spa_dir = Path(static_dir) if static_dir is not None else _REPO_ROOT / "frontend" / "dist" + if (spa_dir / "index.html").is_file(): + app.mount("/", StaticFiles(directory=spa_dir, html=True), name="spa") + return app diff --git a/backend/tests/test_static_spa.py b/backend/tests/test_static_spa.py new file mode 100644 index 0000000..e07a643 --- /dev/null +++ b/backend/tests/test_static_spa.py @@ -0,0 +1,24 @@ +"""Deployed topology (launch-app SPEC §2): nginx proxies EVERYTHING to the backend, so +the backend must serve the built SPA (frontend/dist) itself. Dev is unaffected — Vite +serves the frontend and the default dist dir simply doesn't exist.""" +from fastapi.testclient import TestClient + +from app.main import create_app + + +def test_spa_served_when_dist_present(fresh_db_url, tmp_path): + (tmp_path / "index.html").write_text("