From cea5730804f46eb5aa376e3248c628d0dbec1c26 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 22 May 2026 12:24:32 +0800 Subject: [PATCH] P0: Add StaticFiles mount + root redirect to install wizard Critical fix: FastAPI was not serving any static files, causing all frontend pages (install.html, login.html, etc.) to return 404. Changes: - Add StaticFiles mount at /app/ for web/app/ directory (html=True) - Redirect root path / to /app/install.html in install mode - Remove redundant "/" path bypass in InstallModeMiddleware - Production Nginx still serves static files directly (no conflict) Co-Authored-By: Claude Sonnet 4.6 --- server/main.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/server/main.py b/server/main.py index 7643d01e..b438ed0c 100644 --- a/server/main.py +++ b/server/main.py @@ -25,6 +25,7 @@ from pathlib import Path from fastapi import FastAPI, Request, Response from fastapi.middleware.cors import CORSMiddleware +from fastapi.staticfiles import StaticFiles from starlette.middleware.base import BaseHTTPMiddleware from server.config import settings @@ -268,10 +269,14 @@ class InstallModeMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): if INSTALL_MODE: path = request.url.path + # Redirect root to install wizard + if path == "/": + from fastapi.responses import RedirectResponse + return RedirectResponse(url="/app/install.html") # Allow: install API, static files, health check if path.startswith("/api/install/") or path.startswith("/app/") or path == "/health": return await call_next(request) - if path == "/" or path.startswith("/ws/"): + if path.startswith("/ws/"): return await call_next(request) # Block everything else from fastapi.responses import JSONResponse @@ -327,4 +332,10 @@ app.include_router(webssh_router) app.include_router(sync_v2_router) # Global Search -app.include_router(search_router) \ No newline at end of file +app.include_router(search_router) + +# ── Static files: serve web/app/ at /app/ (HTML pages + JS/CSS assets) ── +# In production, Nginx serves these directly; this is for dev/standalone mode. +WEB_APP_DIR = ROOT_DIR / "web" / "app" +if WEB_APP_DIR.is_dir(): + app.mount("/app", StaticFiles(directory=str(WEB_APP_DIR), html=True), name="static_app") \ No newline at end of file