From e4dcb70e3f153600e5b48e75a20ef33696defc8f Mon Sep 17 00:00:00 2001
From: porkmagus <427085+porkmagus@users.noreply.github.com>
Date: Sun, 4 Oct 2026 21:19:58 -0500
Subject: [PATCH 1/2] dashboard: honor deploy-time API base for live metrics
stream
---
.../dashboard/stores/liveMetricsStore.test.ts | 54 +++++++++++++++++++
.../dashboard/stores/liveMetricsStore.ts | 12 +++--
2 files changed, 62 insertions(+), 4 deletions(-)
create mode 100644 src/features/dashboard/stores/liveMetricsStore.test.ts
diff --git a/src/features/dashboard/stores/liveMetricsStore.test.ts b/src/features/dashboard/stores/liveMetricsStore.test.ts
new file mode 100644
index 0000000..f447b65
--- /dev/null
+++ b/src/features/dashboard/stores/liveMetricsStore.test.ts
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: 2026 Coffey Labs
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { describe, it, expect, afterEach, vi } from 'vitest';
+
+const originalHead = document.head.innerHTML;
+const seenUrls: string[] = [];
+
+class FakeEventSource {
+ url: string;
+ constructor(url: string) {
+ this.url = url;
+ seenUrls.push(url);
+ }
+ addEventListener() {}
+ close() {}
+}
+
+async function subscribeWithMeta(meta: string) {
+ document.head.innerHTML = meta;
+ vi.resetModules();
+ vi.stubGlobal('EventSource', FakeEventSource);
+ vi.stubGlobal(
+ 'fetch',
+ vi.fn(async () => new Response('tok123', { status: 200 })),
+ );
+ const { useLiveMetricsStore } = await import('./liveMetricsStore');
+ useLiveMetricsStore.getState().subscribe(new Set(['cpu' as never]));
+ await new Promise((r) => setTimeout(r, 300));
+ useLiveMetricsStore.getState().unsubscribe();
+ return seenUrls.at(-1) ?? '';
+}
+
+afterEach(() => {
+ document.head.innerHTML = originalHead;
+ seenUrls.length = 0;
+ vi.resetModules();
+ vi.unstubAllGlobals();
+});
+
+describe('live metrics stream URL', () => {
+ it('honors the deploy-time api-base-url instead of the Admin origin', async () => {
+ const url = await subscribeWithMeta('');
+ expect(url.startsWith('https://mail.example.org/api/live/metrics?')).toBe(true);
+ });
+
+ it('falls back to the Admin origin when no api-base-url is injected', async () => {
+ const url = await subscribeWithMeta('');
+ expect(url.startsWith(`${window.location.origin}/api/live/metrics?`)).toBe(true);
+ });
+});
diff --git a/src/features/dashboard/stores/liveMetricsStore.ts b/src/features/dashboard/stores/liveMetricsStore.ts
index 54396bb..9c7a59f 100644
--- a/src/features/dashboard/stores/liveMetricsStore.ts
+++ b/src/features/dashboard/stores/liveMetricsStore.ts
@@ -6,7 +6,7 @@
import { create } from 'zustand';
import type { MetricId, Metric } from '../types/metrics';
-import { apiFetch } from '@/services/api';
+import { apiFetch, getApiBaseUrl } from '@/services/api';
import i18n from '@/i18n';
type LiveStatus = 'idle' | 'connecting' | 'open' | 'error' | 'closed';
@@ -107,9 +107,13 @@ async function openStream(ids: Set) {
}
function getOrigin(): string {
- const envUrl = import.meta.env.VITE_API_BASE_URL as string | undefined;
- if (envUrl && envUrl.length > 0) return envUrl.replace(/\/+$/, '');
- return window.location.origin;
+ // Honor the deploy-time API base (VITE_API_BASE_URL or the api-base-url
+ // meta tag) exactly like every other /api/live caller. Using
+ // window.location.origin here breaks Dashboard live metrics in any split
+ // deployment where the Admin origin is not the server (e.g. Admin in Docker
+ // pointed at the server via API_BASE_URL): the EventSource hits the Admin
+ // nginx instead, which answers with the SPA shell.
+ return getApiBaseUrl();
}
let visibilityTimer: ReturnType | null = null;
--
2.54.0
From 0c5ddeaf98ae20650ce03645fe5b88a5d1ed345f Mon Sep 17 00:00:00 2001
From: John Coffey
Date: Fri, 9 Oct 2026 00:20:17 -0700
Subject: [PATCH 2/2] dashboard: add the modification notice to
liveMetricsStore.ts
The live-metrics fix is the first change to this upstream-derived file, so it
now carries the AGPL modification notice the notice check requires.
---
src/features/dashboard/stores/liveMetricsStore.ts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/features/dashboard/stores/liveMetricsStore.ts b/src/features/dashboard/stores/liveMetricsStore.ts
index 9c7a59f..becaf57 100644
--- a/src/features/dashboard/stores/liveMetricsStore.ts
+++ b/src/features/dashboard/stores/liveMetricsStore.ts
@@ -2,6 +2,8 @@
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
+ *
+ * Modified by Coffey Labs in 2026 for INBUXA.
*/
import { create } from 'zustand';
--
2.54.0