From e31508288763f95951abec2ee4f9353b680226f8 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Sat, 15 Aug 2026 15:57:25 -0300 Subject: [PATCH] fix(video-bridge): clarify remote runtime status --- .../modalityBridge/ModalityBridgeVideoTab.tsx | 44 ++++++++++++++++--- .../ui/modality-bridge-video-tab.test.tsx | 18 +++++++- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/app/(dashboard)/dashboard/settings/components/modalityBridge/ModalityBridgeVideoTab.tsx b/src/app/(dashboard)/dashboard/settings/components/modalityBridge/ModalityBridgeVideoTab.tsx index c85302307e..d7cd0c7857 100644 --- a/src/app/(dashboard)/dashboard/settings/components/modalityBridge/ModalityBridgeVideoTab.tsx +++ b/src/app/(dashboard)/dashboard/settings/components/modalityBridge/ModalityBridgeVideoTab.tsx @@ -27,6 +27,11 @@ interface RuntimeStatus { ffmpegVersion: string | null; ffprobeVersion: string | null; reason?: string; + restricted?: boolean; +} + +interface ModalityBridgeVideoTabProps { + runtimeHostname?: string; } function asRecord(value: unknown): Record { @@ -46,6 +51,14 @@ function fromApi(value: unknown): VideoState { function parseRuntimeStatus(value: unknown): RuntimeStatus | null { const record = asRecord(value); + if (record.restricted === true) { + return { + available: false, + ffmpegVersion: null, + ffprobeVersion: null, + restricted: true, + }; + } if (typeof record.available !== "boolean") return null; return { available: record.available, @@ -55,12 +68,24 @@ function parseRuntimeStatus(value: unknown): RuntimeStatus | null { }; } +function isLoopbackDashboardHost(hostname: string): boolean { + const normalized = hostname.trim().toLowerCase(); + return ( + normalized === "localhost" || + normalized === "127.0.0.1" || + normalized === "::1" || + normalized === "[::1]" + ); +} + function clampNumber(raw: string, min: number, max: number, fallback: number): number { const parsed = Number.parseInt(raw, 10); return Math.min(max, Math.max(min, Number.isFinite(parsed) ? parsed : fallback)); } -export default function ModalityBridgeVideoTab() { +export default function ModalityBridgeVideoTab({ + runtimeHostname, +}: ModalityBridgeVideoTabProps = {}) { const t = useTranslations("settings"); const tRoot = useTranslations(); const [settings, setSettings] = useState(null); @@ -71,14 +96,18 @@ export default function ModalityBridgeVideoTab() { useEffect(() => { let cancelled = false; + const hostname = runtimeHostname ?? window.location.hostname; + const runtimeStatusRequest = isLoopbackDashboardHost(hostname) + ? fetch("/api/modality-bridge/video/runtime") + .then((response) => (response.ok ? response.json() : null)) + .catch(() => null) + : Promise.resolve({ restricted: true }); void Promise.all([ fetch("/api/settings").then((response) => { if (!response.ok) throw new Error("settings load failed"); return response.json(); }), - fetch("/api/modality-bridge/video/runtime") - .then((response) => (response.ok ? response.json() : null)) - .catch(() => null), + runtimeStatusRequest, ]) .then(([settingsValue, runtimeValue]: [unknown, unknown]) => { if (cancelled) return; @@ -94,7 +123,7 @@ export default function ModalityBridgeVideoTab() { return () => { cancelled = true; }; - }, []); + }, [runtimeHostname]); const update = async (patch: Partial) => { setErrorState(null); @@ -169,6 +198,11 @@ export default function ModalityBridgeVideoTab() { FFmpeg {runtime.ffmpegVersion} ยท ffprobe {runtime.ffprobeVersion} + ) : runtime?.restricted ? ( + <> + {t("authz.badge.strict")} +
{tRoot("endpoint.badgeLoopbackTooltip")}
+ ) : ( <> {t("modalityBridgeVideoRuntimeUnavailable")} diff --git a/tests/unit/ui/modality-bridge-video-tab.test.tsx b/tests/unit/ui/modality-bridge-video-tab.test.tsx index c7be54cfd2..c280a9e0ac 100644 --- a/tests/unit/ui/modality-bridge-video-tab.test.tsx +++ b/tests/unit/ui/modality-bridge-video-tab.test.tsx @@ -87,11 +87,11 @@ describe("ModalityBridgeVideoTab", () => { vi.unstubAllGlobals(); }); - async function render(): Promise { + async function render(props: { runtimeHostname?: string } = {}): Promise { const element = document.createElement("div"); document.body.appendChild(element); const root = createRoot(element); - await act(async () => root.render()); + await act(async () => root.render()); roots.push({ root, element }); await waitFor( () => element.querySelector('[data-testid="modality-bridge-video-frame-count"]') !== null, @@ -127,6 +127,20 @@ describe("ModalityBridgeVideoTab", () => { expect(element.textContent).not.toContain("modalityBridgeVideoComingSoon"); }); + it("labels runtime status as strict loopback without probing it from a LAN dashboard", async () => { + const element = await render({ runtimeHostname: "192.168.0.15" }); + + expect(element.textContent).toContain("authz.badge.strict"); + expect(element.textContent).toContain("endpoint.badgeLoopbackTooltip"); + expect(element.textContent).not.toContain("modalityBridgeVideoRuntimeUnavailable"); + expect(element.textContent).not.toContain("modalityBridgeVideoRuntimeInstall"); + expect( + fetchMock.mock.calls.some(([input]) => + String(input).includes("/api/modality-bridge/video/runtime") + ) + ).toBe(false); + }); + it("persists the enable toggle and clamps frame count to 16", async () => { const element = await render(); const toggle = element.querySelector('[role="switch"]') as HTMLButtonElement;