38 lines
1.1 KiB
Python
38 lines
1.1 KiB
Python
"""
|
|
Lightweight preview server — serves frontend/out/ on port 3001.
|
|
Used only for Claude Code preview panel. The real backend runs on 7000.
|
|
"""
|
|
import os
|
|
from pathlib import Path
|
|
from fastapi import FastAPI
|
|
from fastapi.responses import FileResponse, HTMLResponse
|
|
from fastapi.staticfiles import StaticFiles
|
|
import uvicorn
|
|
|
|
OUT = Path(__file__).parent / "frontend" / "out"
|
|
app = FastAPI()
|
|
|
|
# Serve _next assets directly
|
|
if (OUT / "_next").exists():
|
|
app.mount("/_next", StaticFiles(directory=str(OUT / "_next")), name="next-assets")
|
|
|
|
@app.get("/{full_path:path}")
|
|
async def spa(full_path: str):
|
|
# Try exact file match
|
|
candidate = OUT / full_path
|
|
if candidate.is_file():
|
|
return FileResponse(str(candidate))
|
|
# Try with .html
|
|
html = OUT / (full_path.rstrip("/") + ".html") if full_path else OUT / "index.html"
|
|
if html.is_file():
|
|
return FileResponse(str(html))
|
|
# SPA fallback
|
|
return FileResponse(str(OUT / "index.html"))
|
|
|
|
@app.get("/")
|
|
async def root():
|
|
return FileResponse(str(OUT / "index.html"))
|
|
|
|
if __name__ == "__main__":
|
|
uvicorn.run(app, host="0.0.0.0", port=3001, log_level="warning")
|