fix(admin): /admin.html retombait sur la page principale (ROADMAP #71)
CI / lint (push) Successful in 54s
CI / security (push) Successful in 26s
CI / test (push) Successful in 50s
CI / build (push) Successful in 23s
CI / e2e (push) Successful in 6m2s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
CI / lint (push) Successful in 54s
CI / security (push) Successful in 26s
CI / test (push) Successful in 50s
CI / build (push) Successful in 23s
CI / e2e (push) Successful in 6m2s
Desktop Build / build-windows (push) Canceled after 0s
Desktop Build / build-linux (push) Canceled after 0s
Le menu Admin redirigeait vers /admin.html mais aucune route backend ne le
servait : le catch-all SPA /{full_path:path} renvoyait index.html, d'où le
retour à la page principale.
- backend/main.py: route GET /admin.html (gate require_admin) déclarée avant
le catch-all SPA + correction des imports JS /frontend/js -> /static/js
- frontend/admin.html: chemins d'assets alignés sur le mount /static
- tests/test_admin.py: 3 tests de régression (page servie admin, refus 401/403)
This commit is contained in:
@@ -4675,6 +4675,19 @@ if FRONTEND_DIR.exists():
|
||||
return HTMLResponse(content=poc_file.read_text(encoding="utf-8"))
|
||||
raise HTTPException(status_code=404, detail="Editor POC not found")
|
||||
|
||||
@app.get("/admin.html", response_class=HTMLResponse)
|
||||
async def serve_admin_page(_current_user=Depends(require_admin)):
|
||||
"""Serve the admin dashboard page (ROADMAP #71) — admin-gated.
|
||||
|
||||
Must be declared BEFORE the SPA catch-all ``/{full_path:path}`` or the
|
||||
admin page would be shadowed by ``index.html`` (the reported bug: the
|
||||
Admin menu kept returning to the main page).
|
||||
"""
|
||||
admin_file = FRONTEND_DIR / "admin.html"
|
||||
if admin_file.exists():
|
||||
return HTMLResponse(content=admin_file.read_text(encoding="utf-8"))
|
||||
raise HTTPException(status_code=404, detail="Admin page not found")
|
||||
|
||||
@app.get("/{full_path:path}")
|
||||
async def serve_spa(full_path: str):
|
||||
"""Serve the SPA index.html for all non-API routes."""
|
||||
|
||||
+2
-2
@@ -373,8 +373,8 @@
|
||||
// We import admin.js which provides init() — but the i18n module
|
||||
// is global (window.t), so we can use t() right away to translate
|
||||
// static DOM. admin.js will then call init() after DOMContentLoaded.
|
||||
import { initI18n } from "/frontend/js/i18n.js";
|
||||
import * as Admin from "/frontend/js/admin.js";
|
||||
import { initI18n } from "/static/js/i18n.js";
|
||||
import * as Admin from "/static/js/admin.js";
|
||||
|
||||
await initI18n();
|
||||
// Re-apply DOM translations now that locale strings are loaded
|
||||
|
||||
+34
-1
@@ -302,4 +302,37 @@ class TestAdminStream:
|
||||
|
||||
def test_stream_requires_admin(self, admin_client):
|
||||
resp = admin_client.get("/api/admin/stream")
|
||||
assert resp.status_code in (401, 403)
|
||||
assert resp.status_code in (401, 403)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Admin dashboard PAGE (/admin.html)
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
class TestAdminPage:
|
||||
"""Regression for ROADMAP #71 — Admin menu bounced back to the main page.
|
||||
|
||||
Root cause: /admin.html had no explicit route, so the SPA catch-all
|
||||
``/{full_path:path}`` served index.html. Guard the fix.
|
||||
"""
|
||||
|
||||
def test_page_served_as_admin(self, admin_client):
|
||||
token = _login(admin_client)
|
||||
resp = admin_client.get("/admin.html", headers=_bearer(token))
|
||||
assert resp.status_code == 200
|
||||
body = resp.text
|
||||
# Real admin page markers (NOT the main SPA index.html)
|
||||
assert "admin-main" in body or "ObsiGate — Admin" in body or "admin.js" in body
|
||||
# The regression: index.html markers must be absent
|
||||
assert "boot-splash" not in body
|
||||
# Asset paths must point to the actual static mount (/static), not /frontend
|
||||
assert "/static/js/admin.js" in body
|
||||
assert "/frontend/js/admin.js" not in body
|
||||
|
||||
def test_page_requires_auth(self, admin_client):
|
||||
resp = admin_client.get("/admin.html")
|
||||
assert resp.status_code in (401, 403)
|
||||
|
||||
def test_page_requires_admin_role(self, admin_client):
|
||||
token = _login(admin_client, username="normaluser", password="normal123")
|
||||
resp = admin_client.get("/admin.html", headers=_bearer(token))
|
||||
assert resp.status_code == 403
|
||||
Reference in New Issue
Block a user