From 38766ca792d5566aef28bb771974e4aa91de3a5a Mon Sep 17 00:00:00 2001 From: walkpan Date: Sun, 29 Mar 2026 22:05:56 +0800 Subject: [PATCH] feat: production build with FastAPI static file serving for SPA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- src/mqtt_home/main.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/mqtt_home/main.py b/src/mqtt_home/main.py index f3ee40c..d9648dc 100644 --- a/src/mqtt_home/main.py +++ b/src/mqtt_home/main.py @@ -1,14 +1,17 @@ import asyncio import logging import sys +import pathlib from contextlib import asynccontextmanager # Windows compatibility: use SelectorEventLoop for paho-mqtt (add_reader/add_writer) if sys.platform == "win32": asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) -from fastapi import FastAPI +from fastapi import FastAPI, Request 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.database import init_db, get_session_factory, Base @@ -87,3 +90,15 @@ async def health(): "status": "ok", "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"))