diff --git a/apps/web/hooks/__tests__/use-sse.test.ts b/apps/web/hooks/__tests__/use-sse.test.ts index d373b08..582bbb8 100644 --- a/apps/web/hooks/__tests__/use-sse.test.ts +++ b/apps/web/hooks/__tests__/use-sse.test.ts @@ -156,3 +156,30 @@ describe('useSSE hook', () => { vi.useRealTimers() }) }) + +describe('useSSE with relative NEXT_PUBLIC_API_URL', () => { + beforeEach(() => { + MockEventSource.reset() + vi.stubGlobal('EventSource', MockEventSource) + }) + + afterEach(() => { + vi.unstubAllGlobals() + vi.unstubAllEnvs() + vi.resetModules() + }) + + // Regression for issue #46: deployments behind nginx set NEXT_PUBLIC_API_URL + // to a relative path like "/api". `new URL("/api/events/abc")` throws + // "Failed to construct 'URL': Invalid URL" without a base, crashing the + // dashboard the moment UploadSSEBridge first opens an SSE connection. + it('builds a valid URL when NEXT_PUBLIC_API_URL is a relative path', async () => { + vi.stubEnv('NEXT_PUBLIC_API_URL', '/api') + vi.resetModules() + const { useSSE: useSSEFresh } = await import('../use-sse') + + expect(() => renderHook(() => useSSEFresh('project-123'))).not.toThrow() + expect(MockEventSource.instances).toHaveLength(1) + expect(MockEventSource.instances[0].url).toContain('/api/events/project-123') + }) +}) diff --git a/apps/web/hooks/use-sse.ts b/apps/web/hooks/use-sse.ts index c52c288..affb0db 100644 --- a/apps/web/hooks/use-sse.ts +++ b/apps/web/hooks/use-sse.ts @@ -126,7 +126,11 @@ export function useSSE(projectId: string | null | undefined, options: UseSSEOpti if (destroyed) return const token = getAccessToken() - const url = new URL(`${API_URL}/events/${projectId}`) + // Use window.location.origin as a base so deployments behind a reverse + // proxy can set NEXT_PUBLIC_API_URL to a relative path like "/api" + // without crashing the URL constructor. + const base = typeof window !== 'undefined' ? window.location.origin : 'http://localhost' + const url = new URL(`${API_URL}/events/${projectId}`, base) if (token) { url.searchParams.set('token', token) }