v2.0.13 — oumta-dashboard via PHP proxy; quiet support poll
Dashboard now reachable across the internet: browser calls /api/dashboard/* same-origin, PHP forwards to OUMTA_DASHBOARD_URL (configurable via env, with docker-compose defaults pointing at host.docker.internal:8085). Keeps the bearer token server-side and lets the dashboard live on a private network. Plumbing bits: polling indicator now green (no reason to look anxious about a healthy poll loop), and the support plugin returns 200 pulled=0 when its relay isn't configured instead of flooding the network tab with 502s on every background tick. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
7bce6a3a36
commit
efd3f9d5ae
6 files changed
+92
-26
No files matched your search
@@ -1,17 +1,16 @@
|
||||
// ── Oumta Dashboard plugin ──
|
||||
//
|
||||
// Fullscreen view that consumes the Oumta Go backend (`oumta-dashboard`).
|
||||
// Backend location + poll interval are hardwired below — change them here
|
||||
// if the deployment topology shifts. No UI settings panel on purpose:
|
||||
// this plugin only makes sense in one specific deployment, so a settings
|
||||
// tab would just be noise and tempt accidental misconfiguration.
|
||||
// The browser talks to /api/dashboard/* same-origin; the hyprcrd PHP
|
||||
// backend forwards to the real upstream (see OUMTA_DASHBOARD_URL in
|
||||
// config.php). Keeps CORS out of the browser and lets the dashboard
|
||||
// live on a private network reachable only by the hyprcrd container.
|
||||
|
||||
const h = window.hyprcrd;
|
||||
const $ = id => document.getElementById(id);
|
||||
|
||||
const CONFIG = {
|
||||
baseUrl: 'http://localhost:8085',
|
||||
authToken: '', // Keycloak bearer; empty = no auth yet
|
||||
proxyPrefix: '/api/dashboard',
|
||||
pollIntervalSec: 15,
|
||||
};
|
||||
|
||||
@@ -28,21 +27,19 @@ const state = {
|
||||
|
||||
// ── Central API surface ─────────────────────────────────────────
|
||||
//
|
||||
// Every backend call routes through `api()`. Requests go against the
|
||||
// hardwired baseUrl; the active namespace (if any) is attached as a
|
||||
// `namespace` query parameter, and the bearer token (if any) is sent as
|
||||
// Authorization. Response shapes are defined by the Go backend in
|
||||
// Every backend call routes through `api()` against the same-origin
|
||||
// proxy prefix; the active namespace (if any) is attached as a
|
||||
// `namespace` query parameter. Auth / bearer tokens live server-side
|
||||
// in config.php, so nothing sensitive ships to the browser. Response
|
||||
// shapes are defined by the Go backend in
|
||||
// `internal/httpapi/contracts.go`.
|
||||
|
||||
async function api(path, opts = {}) {
|
||||
const url = new URL(path, CONFIG.baseUrl);
|
||||
const url = new URL(CONFIG.proxyPrefix + path, window.location.origin);
|
||||
if (!url.searchParams.has('namespace') && state.activeNamespace) {
|
||||
url.searchParams.set('namespace', state.activeNamespace.namespace);
|
||||
}
|
||||
const headers = CONFIG.authToken
|
||||
? { Authorization: 'Bearer ' + CONFIG.authToken }
|
||||
: {};
|
||||
const res = await fetch(url, { ...opts, headers });
|
||||
const res = await fetch(url, opts);
|
||||
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
|
||||
return res.json();
|
||||
}
|
||||
@@ -475,19 +472,16 @@ $('oumta-timeline-back')?.addEventListener('click', () => {
|
||||
}).catch(e => setLive('error', e.message));
|
||||
});
|
||||
|
||||
// ── Realtime (SSE) with polling fallback ──────────────────────
|
||||
// ── Polling loop ──────────────────────────────────────────────
|
||||
//
|
||||
// Plan: open an EventSource at <baseUrl>/api/events?namespace=… — backend
|
||||
// not implemented yet, so we go straight to polling. When SSE is available
|
||||
// we can try to connect and fall back on `error`.
|
||||
// SSE intentionally unsupported: the PHP proxy buffers whole responses,
|
||||
// so streaming through it would pin an Apache worker per client. If we
|
||||
// ever want live events, bypass the proxy and talk to the Go backend's
|
||||
// WebSocket / SSE endpoint directly — browsers don't enforce CORS on
|
||||
// WS, and SSE would need a short-lived signed URL from the proxy.
|
||||
|
||||
function startLive() {
|
||||
stopLive();
|
||||
// TODO(api): attempt SSE first:
|
||||
// state.sseHandle = new EventSource(url);
|
||||
// state.sseHandle.onmessage = ev => applyLiveEvent(JSON.parse(ev.data));
|
||||
// state.sseHandle.onerror = () => { stopLive(); startPolling(); setLive('polling'); };
|
||||
// state.sseHandle.onopen = () => setLive('live');
|
||||
startPolling();
|
||||
}
|
||||
function stopLive() {
|
||||
|
||||
Reference in new issue
Block a user