feat: Docker deploy serving SPA and API on :8080
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+34
-1
@@ -1,7 +1,10 @@
|
||||
import asyncio
|
||||
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
|
||||
|
||||
from app.api.admin import router as admin_router
|
||||
from app.api.public import router as public_router
|
||||
@@ -11,6 +14,20 @@ from app import models # noqa: F401 — register models with Base.metadata
|
||||
from app.services import cleaner as cleaner_service
|
||||
|
||||
|
||||
def _resolve_frontend_dist() -> Path | None:
|
||||
"""Locate frontend/dist in Docker (/app/frontend/dist) or monorepo checkout."""
|
||||
here = Path(__file__).resolve()
|
||||
candidates = (
|
||||
here.parents[2] / "frontend" / "dist", # .../backend/app/main.py → repo root
|
||||
here.parents[1] / "frontend" / "dist", # /app/app/main.py → /app/frontend/dist
|
||||
Path("/app/frontend/dist"),
|
||||
)
|
||||
for path in candidates:
|
||||
if (path / "index.html").is_file():
|
||||
return path
|
||||
return None
|
||||
|
||||
|
||||
async def _cleaner_loop() -> None:
|
||||
while True:
|
||||
db = SessionLocal()
|
||||
@@ -47,3 +64,19 @@ app.include_router(admin_router)
|
||||
@app.get("/api/health")
|
||||
def health():
|
||||
return {"ok": True, "name": "MincedPad"}
|
||||
|
||||
|
||||
_dist = _resolve_frontend_dist()
|
||||
if _dist is not None:
|
||||
assets_dir = _dist / "assets"
|
||||
if assets_dir.is_dir():
|
||||
app.mount("/assets", StaticFiles(directory=assets_dir), name="assets")
|
||||
|
||||
@app.api_route("/{full_path:path}", methods=["GET", "HEAD"])
|
||||
async def spa(full_path: str):
|
||||
if full_path == "api" or full_path.startswith("api/"):
|
||||
raise HTTPException(status_code=404, detail="Not Found")
|
||||
file_path = _dist / full_path
|
||||
if full_path and file_path.is_file():
|
||||
return FileResponse(file_path)
|
||||
return FileResponse(_dist / "index.html")
|
||||
|
||||
Reference in New Issue
Block a user