nuke all old UIs — templates, React SPA, /v2 mount, legacy routes

Removed:
- src/web/templates/*.html (all 6 Jinja2 template files)
- src/web/ui-dist/ (old React SPA build)
- /v2 duplicate static mount (same as /)
- /ui static mount (old React SPA)

Server.py changes:
- Root handler now directly serves v2 UI (no fallback chain)
- Old template routes (/presets, /models, /irs, /settings) → redirect to /
- Removed Jinja2Templates, TEMPLATES_DIR, TEMPLATE_VERSION, UI_DIST_DIR
This commit is contained in:
2026-06-16 18:45:04 -04:00
parent 9cc08d16da
commit 7078217783
12 changed files with 23 additions and 860 deletions
+23 -81
View File
@@ -25,9 +25,8 @@ from typing import Any, Optional
import uvicorn
from fastapi import FastAPI, HTTPException, WebSocket, WebSocketDisconnect, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from ..dsp.nam_host import NAMHost
from ..dsp.ir_loader import IRLoader
@@ -51,38 +50,14 @@ logger = logging.getLogger(__name__)
# ── Cache buster version (bumped on deploy) ─────────────────────────
# Increment this when template/static changes need to bypass browser cache
TEMPLATE_VERSION = "8"
# ── Defaults ─────────────────────────────────────────────────────────────────
DEFAULT_HOST = "0.0.0.0"
DEFAULT_PORT = 8080
TEMPLATES_DIR = Path(__file__).resolve().parent / "templates"
STATIC_DIR = Path(__file__).resolve().parent / "static"
# React UI dist — built frontend served at /ui/
# Can be overridden via UI_DIST_DIR env var
_ui_dist_env = os.environ.get("UI_DIST_DIR")
if _ui_dist_env:
UI_DIST_DIR = Path(_ui_dist_env).resolve()
else:
# Priority: src/web/ui-dist is the canonical UI
_candidates = [
Path(__file__).resolve().parent / "ui-dist",
Path(__file__).resolve().parent.parent.parent / "frontend-react" / "dist",
]
UI_DIST_DIR = next((d for d in _candidates if d.is_dir()), None)
# ── Channel support ──────────────────────────────────────────────────────────────
VALID_CHANNELS: frozenset = frozenset({"guitar", "bass", "keys", "vocals", "backing_tracks"})
def _resolve_channel(channel: str = "guitar") -> str:
"""Normalize a channel string; fall back to 'guitar' for invalid values."""
if isinstance(channel, str) and channel.lower() in VALID_CHANNELS:
return channel.lower()
return "guitar"
# v2 UI dist (the only UI we serve)
V2_DIST_DIR = Path(__file__).resolve().parent / "ui-v2-dist"
# ── Channel mode persistence ────────────────────────────────────────────────
@@ -315,10 +290,6 @@ class WebServer:
def _build_app(self) -> FastAPI:
templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
# Inject version into all templates (cache buster for CSS/JS)
templates.env.globals["version"] = TEMPLATE_VERSION
@asynccontextmanager
async def lifespan(app: FastAPI):
logger.info("Web server lifespan started")
@@ -330,63 +301,34 @@ class WebServer:
# Mount static files
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
# Mount React UI (if dist exists)
if UI_DIST_DIR and UI_DIST_DIR.is_dir():
logger.info("Mounting React UI from %s at /ui", UI_DIST_DIR)
app.mount("/ui", StaticFiles(directory=str(UI_DIST_DIR), html=True), name="ui")
# Mount V2 React UI (if dist exists)
V2_DIST_DIR = Path(__file__).resolve().parent / "ui-v2-dist"
if V2_DIST_DIR.is_dir():
logger.info("Mounting V2 React UI from %s at /v2", V2_DIST_DIR)
app.mount("/v2", StaticFiles(directory=str(V2_DIST_DIR), html=True), name="ui-v2")
# ── HTML pages ──────────────────────────────────────────────
@app.get("/", response_class=HTMLResponse)
async def root(request: Request):
"""Serve the v2 UI (or React SPA / legacy dashboard as fallback)."""
# Prefer the v2 UI if available
v2_dir = Path(__file__).resolve().parent / "ui-v2-dist"
if v2_dir.is_dir():
v2_index = v2_dir / "index.html"
if v2_index.exists():
content = v2_index.read_text()
return HTMLResponse(content)
# Fallback: React build
if UI_DIST_DIR and UI_DIST_DIR.is_dir():
index_html = UI_DIST_DIR / "index.html"
if index_html.exists():
content = index_html.read_text()
return HTMLResponse(content)
# Fallback: legacy dashboard (guitar channel state)
state = self._gather_state(channel="guitar")
ctx = {"request": request}
ctx.update(state)
return templates.TemplateResponse(request, "dashboard.html", ctx)
"""Serve the v2 UI."""
v2_index = V2_DIST_DIR / "index.html"
if v2_index.exists():
content = v2_index.read_text()
return HTMLResponse(content)
return HTMLResponse("<h1>Pi Multi-FX Pedal</h1><p>UI build not found.</p>")
@app.get("/presets", response_class=HTMLResponse)
async def preset_manager(request: Request):
"""Preset manager page — browse banks, load/save/edit."""
return templates.TemplateResponse(request, "presets.html", {"request": request})
# Legacy routes — redirect to the v2 SPA
@app.get("/models", response_class=HTMLResponse)
async def model_manager(request: Request):
"""NAM model manager page."""
return templates.TemplateResponse(request, "models.html", {"request": request})
@app.get("/presets")
async def presets_redirect():
return RedirectResponse(url="/")
@app.get("/irs", response_class=HTMLResponse)
async def ir_manager(request: Request):
"""IR manager page."""
return templates.TemplateResponse(request, "irs.html", {"request": request})
@app.get("/models")
async def models_redirect():
return RedirectResponse(url="/")
@app.get("/settings", response_class=HTMLResponse)
async def settings_page(request: Request):
"""Settings page."""
state = self._gather_state(channel="guitar")
ctx = {"request": request}
ctx.update(state)
return templates.TemplateResponse(request, "settings.html", ctx)
@app.get("/irs")
async def irs_redirect():
return RedirectResponse(url="/")
@app.get("/settings")
async def settings_redirect():
return RedirectResponse(url="/")
# ── REST API ────────────────────────────────────────────────