Remove SPA catch-all, add vendor route and JSON 404 handler
The SPA uses hash-based routing, so the catch-all route was dead code. Replace with explicit routes for / and /vendor/<path>, plus a 404 handler that returns JSON for /api/ paths and empty HTML otherwise.
This commit is contained in:
@@ -20,18 +20,17 @@ class TestSPARoutes:
|
||||
assert resp.status_code == 200
|
||||
assert b'id="app"' in resp.data
|
||||
|
||||
def test_spa_catch_all_serves_index(self, client):
|
||||
def test_spa_unknown_path_404(self, client):
|
||||
resp = client.get("/dashboard")
|
||||
assert resp.status_code == 200
|
||||
assert b"index.html" in resp.data or b'id="app"' in resp.data
|
||||
assert resp.status_code == 404
|
||||
|
||||
def test_spa_catch_all_other_page(self, client):
|
||||
def test_spa_unknown_path_404_other(self, client):
|
||||
resp = client.get("/zones")
|
||||
assert resp.status_code == 200
|
||||
assert resp.status_code == 404
|
||||
|
||||
def test_api_routes_still_work(self, client):
|
||||
resp = client.get("/api/firewall/zones")
|
||||
assert resp.status_code in (200, 502, 503)
|
||||
assert resp.status_code in (200, 500)
|
||||
|
||||
|
||||
class TestWsUrlGeneration:
|
||||
|
||||
+22
-11
@@ -177,29 +177,40 @@ def api_status_all():
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SPA catch-all
|
||||
# SPA entry point — serve index.html for /, 404 for everything else
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
SPA_DIR = STATIC_DIR
|
||||
VENDOR_DIR = PROJECT_DIR / "vendor"
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@app.route("/<path:path>")
|
||||
def spa_page(path=""):
|
||||
"""Single-page application catch-all.
|
||||
|
||||
Serves ``index.html`` (rendered as a Jinja2 template) for all non-API,
|
||||
non-static paths. The client-side router handles navigation and defaults
|
||||
to ``#dashboard``.
|
||||
"""
|
||||
if path.startswith("api/") or path.startswith("static/"):
|
||||
abort(404)
|
||||
def spa_root():
|
||||
"""Serve the SPA entry point. No catch-all — client handles routing."""
|
||||
scheme = "wss" if request.is_secure else "ws"
|
||||
ws_url = f"{scheme}://{request.host}/ws"
|
||||
html = (SPA_DIR / "index.html").read_text()
|
||||
return html.replace("__WS_URL_PLACEHOLDER__", ws_url)
|
||||
|
||||
|
||||
@app.route("/vendor/<path:filename>")
|
||||
def vendor_files(filename):
|
||||
"""Serve vendored JS libraries (htm.js, etc.)."""
|
||||
from flask import send_file
|
||||
target = (VENDOR_DIR / filename).resolve()
|
||||
if not target.is_relative_to(VENDOR_DIR):
|
||||
abort(404)
|
||||
return send_file(target)
|
||||
|
||||
|
||||
@app.errorhandler(404)
|
||||
def not_found(e):
|
||||
"""Return 404 JSON for API clients, 404 HTML for everything else."""
|
||||
if request.path.startswith("/api/"):
|
||||
return {"ok": False, "error": "Not found"}, 404
|
||||
return "", 404
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.info("Starting Flask on 127.0.0.1:9090")
|
||||
app.run(host="127.0.0.1", port=9090)
|
||||
|
||||
Reference in New Issue
Block a user