From 335bfd959112b73b067563c933faa772f89f9094 Mon Sep 17 00:00:00 2001 From: Ivo Oskamp Date: Fri, 19 Jun 2026 13:05:17 +0200 Subject: [PATCH] Dev build 2026-06-19 13:05 --- containers/clearview/site/app.js | 14 +++--- containers/clearview/site/login.html | 8 +-- containers/clearview/site/setup.html | 8 +-- .../clearview/src/clearview_app/main.py | 50 +++++++++++++++++-- .../clearview/src/clearview_app/version.py | 2 +- docs/changelog-develop.md | 14 ++++++ 6 files changed, 77 insertions(+), 19 deletions(-) diff --git a/containers/clearview/site/app.js b/containers/clearview/site/app.js index c451e2c..7e5d9d5 100644 --- a/containers/clearview/site/app.js +++ b/containers/clearview/site/app.js @@ -10,11 +10,11 @@ window.__authReady = (async function authGate() { const r = await fetch('/api/auth/me', { credentials: 'same-origin' }); if (r.status === 401) { const setup = await fetch('/api/auth/setup-required').then(function (x) { return x.json(); }).catch(function () { return { setup_required: false }; }); - window.location.replace(setup.setup_required ? '/setup.html' : '/login.html'); + window.location.replace((window.__BASE_PATH__ || '') + (setup.setup_required ? '/setup.html' : '/login.html')); return false; } if (!r.ok) { - window.location.replace('/login.html'); + window.location.replace((window.__BASE_PATH__ || '') + '/login.html'); return false; } const me = await r.json(); @@ -27,7 +27,7 @@ window.__authReady = (async function authGate() { delete document.documentElement.dataset.authPending; return true; } catch (e) { - window.location.replace('/login.html'); + window.location.replace((window.__BASE_PATH__ || '') + '/login.html'); return false; } })(); @@ -53,7 +53,7 @@ function renderUserBadge(me) { btn.textContent = 'Sign out'; btn.addEventListener('click', async function () { await fetch('/api/auth/logout', { method: 'POST', credentials: 'same-origin' }); - window.location.replace('/login.html'); + window.location.replace((window.__BASE_PATH__ || '') + '/login.html'); }); wrap.append(btn); slot.append(wrap); @@ -176,7 +176,7 @@ function renderUserBadge(me) { async function requestJson(url, options) { const response = await fetch(url, Object.assign({ credentials: 'same-origin' }, options || {})); if (response.status === 401) { - window.location.replace('/login.html'); + window.location.replace((window.__BASE_PATH__ || '') + '/login.html'); throw new Error('unauthenticated'); } if (!response.ok) { @@ -1335,7 +1335,9 @@ function renderUserBadge(me) { if (siteFilter) { url += '?site_url=' + encodeURIComponent(siteFilter); } - window.location.href = url; + // Browser navigation (file download), not fetch — the fetch shim doesn't + // apply here, so prefix the path explicitly when behind the proxy. + window.location.href = (window.__BASE_PATH__ || '') + url; }); // ------------------------------------------------------------------------- diff --git a/containers/clearview/site/login.html b/containers/clearview/site/login.html index e40309c..d0eb885 100644 --- a/containers/clearview/site/login.html +++ b/containers/clearview/site/login.html @@ -3,7 +3,7 @@ Clearview — Sign in - +
@@ -17,12 +17,12 @@
- + + " + ) + html = raw.replace("", head, 1) + # no-store on the HTML shell so a browser never serves a stale page after a + # redeploy or a move behind/along the proxy. + return HTMLResponse(html, headers={"Cache-Control": "no-store"}) + + +def _prefix(request: Request) -> str: + return request.headers.get("x-forwarded-prefix", "").rstrip("/") + + @app.get("/") -def index() -> FileResponse: - return FileResponse(SITE_DIR / "index.html") +def index(request: Request) -> HTMLResponse: + return _render_page("index.html", _prefix(request)) + + +@app.get("/login.html") +def login_page(request: Request) -> HTMLResponse: + return _render_page("login.html", _prefix(request)) + + +@app.get("/setup.html") +def setup_page(request: Request) -> HTMLResponse: + return _render_page("setup.html", _prefix(request)) app.mount("/", StaticFiles(directory=SITE_DIR, html=True), name="site") diff --git a/containers/clearview/src/clearview_app/version.py b/containers/clearview/src/clearview_app/version.py index 5e296ba..201ae44 100644 --- a/containers/clearview/src/clearview_app/version.py +++ b/containers/clearview/src/clearview_app/version.py @@ -7,7 +7,7 @@ history, so operators can see exactly which image build is running. from __future__ import annotations VERSION = "v0.2.0" -BUILD = 0 +BUILD = 1 def display_version() -> str: diff --git a/docs/changelog-develop.md b/docs/changelog-develop.md index 7040b88..404dfb1 100644 --- a/docs/changelog-develop.md +++ b/docs/changelog-develop.md @@ -2,6 +2,20 @@ This file documents changes on the develop branch of this project. +## 2026-06-19 — Path-prefix routing support (run behind the landing proxy) + +### Added +- Clearview can now be served under a path prefix (e.g. `http://10.19.3.32:8210/clearview/`) by the landing reverse proxy, which strips the prefix and passes it in `X-Forwarded-Prefix` — part of the shared path-prefix routing effort. With no header the app behaves exactly as on direct access. +- `main.py` now serves the three HTML pages (`index.html`, `login.html`, `setup.html`) through `_render_page`, which injects `` plus a small **fetch shim** that prefixes same-origin absolute API calls (`fetch('/api/...')`). This is a vanilla-JS app (no build step) with ~35 absolute `/api` calls across `app.js`/`auth.js`/inline scripts, so shimming `fetch` in one place is far less invasive than rewriting each call. `index.html`/`login.html`/`setup.html` got explicit routes (registered before the `StaticFiles` mount) and the pages are now sent with `Cache-Control: no-store` so a stale shell is never served after a redeploy. + +### Changed +- Made the few **non-fetch** URLs prefix-aware in the site files, since the fetch shim only covers `fetch()`: + - `login.html`/`setup.html`: stylesheet/`auth.js` references made relative (`/styles.css` → `styles.css`, `/auth.js` → `auth.js`) so they resolve under the prefix via the document URL. + - Page navigations (`window.location.replace('/login.html' | '/setup.html' | '/')` in `app.js`, `login.html`, `setup.html`) now prepend `window.__BASE_PATH__`. + - The scan-job export download (`window.location.href = '/api/scan-jobs/.../export'`, a browser navigation, not `fetch`) prepends `window.__BASE_PATH__`. +- `index.html` needed no changes — its asset refs were already relative and it uses hash routing. `auth.js` needed none — its `fetch()` calls go through the shim. +- Verified: `main.py` compiles, the injection produces a single `window.__BASE_PATH__` assignment + shim per page (empty-prefix variant is a no-op), and no breaking absolute asset/nav refs remain in the site. + ## 2026-05-28 — Released as v0.2.0 ## 2026-05-28 — Release: drop unused `version.txt`