feat: production build with FastAPI static file serving for SPA
- Add StaticFiles mount for /assets - Add catch-all route serving index.html for SPA client routing - Frontend build: 101KB gzipped total 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,14 +1,17 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
import pathlib
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
# Windows compatibility: use SelectorEventLoop for paho-mqtt (add_reader/add_writer)
|
# Windows compatibility: use SelectorEventLoop for paho-mqtt (add_reader/add_writer)
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
from fastapi.responses import FileResponse
|
||||||
|
|
||||||
from mqtt_home.config import get_settings
|
from mqtt_home.config import get_settings
|
||||||
from mqtt_home.database import init_db, get_session_factory, Base
|
from mqtt_home.database import init_db, get_session_factory, Base
|
||||||
@@ -87,3 +90,15 @@ async def health():
|
|||||||
"status": "ok",
|
"status": "ok",
|
||||||
"mqtt_connected": mqtt.is_connected if mqtt else False,
|
"mqtt_connected": mqtt.is_connected if mqtt else False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Serve frontend static files in production
|
||||||
|
_frontend_dist = pathlib.Path(__file__).parent.parent.parent / "frontend" / "dist"
|
||||||
|
if _frontend_dist.exists():
|
||||||
|
app.mount("/assets", StaticFiles(directory=str(_frontend_dist / "assets")), name="assets")
|
||||||
|
|
||||||
|
@app.get("/{full_path:path}")
|
||||||
|
async def serve_spa(full_path: str):
|
||||||
|
file = _frontend_dist / full_path
|
||||||
|
if file.exists() and file.is_file():
|
||||||
|
return FileResponse(str(file))
|
||||||
|
return FileResponse(str(_frontend_dist / "index.html"))
|
||||||
|
|||||||
Reference in New Issue
Block a user