From 559d3973806787e66f700d779bf92381e242b6ed Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Wed, 3 Jun 2026 17:21:04 +0530 Subject: [PATCH] fix: use runtime BACKEND_URL for proxying Fixes #400 --- ui/next.config.ts | 5 -- ui/src/app/api/v1/[...path]/route.ts | 98 ++++++++++++++++++++++++++++ ui/src/lib/auth/config.ts | 4 +- ui/src/middleware.ts | 4 +- 4 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 ui/src/app/api/v1/[...path]/route.ts diff --git a/ui/next.config.ts b/ui/next.config.ts index 1b8a399..98242c2 100644 --- a/ui/next.config.ts +++ b/ui/next.config.ts @@ -9,11 +9,6 @@ const nextConfig: NextConfig = { }, async rewrites() { return [ - // API proxy for backend calls (excluding Next.js API routes) - { - source: "/api/:path((?!config|auth).*)*", - destination: `${process.env.BACKEND_URL || 'http://localhost:8000'}/api/:path*`, - }, { source: "/ingest/static/:path*", destination: "https://us-assets.i.posthog.com/static/:path*", diff --git a/ui/src/app/api/v1/[...path]/route.ts b/ui/src/app/api/v1/[...path]/route.ts new file mode 100644 index 0000000..09e8415 --- /dev/null +++ b/ui/src/app/api/v1/[...path]/route.ts @@ -0,0 +1,98 @@ +import { NextRequest, NextResponse } from "next/server"; + +import { getServerBackendUrl } from "@/lib/apiClient"; + +export const dynamic = "force-dynamic"; +export const runtime = "nodejs"; + +const HOP_BY_HOP_HEADERS = [ + "connection", + "keep-alive", + "proxy-authenticate", + "proxy-authorization", + "te", + "trailer", + "transfer-encoding", + "upgrade", +]; + +function trimTrailingSlash(url: string) { + return url.endsWith("/") ? url.slice(0, -1) : url; +} + +function buildBackendUrl(request: NextRequest) { + const backendUrl = trimTrailingSlash(getServerBackendUrl()); + return `${backendUrl}${request.nextUrl.pathname}${request.nextUrl.search}`; +} + +function createRequestHeaders(request: NextRequest) { + const headers = new Headers(request.headers); + + for (const header of HOP_BY_HOP_HEADERS) { + headers.delete(header); + } + + headers.delete("accept-encoding"); + headers.delete("content-length"); + headers.delete("host"); + + return headers; +} + +function createResponseHeaders(response: Response) { + const headers = new Headers(response.headers); + + for (const header of HOP_BY_HOP_HEADERS) { + headers.delete(header); + } + + headers.delete("content-encoding"); + headers.delete("content-length"); + + return headers; +} + +async function getRequestBody(request: NextRequest) { + if (request.method === "GET" || request.method === "HEAD") { + return undefined; + } + + return request.arrayBuffer(); +} + +async function proxyRequest(request: NextRequest) { + const backendUrl = buildBackendUrl(request); + + try { + const response = await fetch(backendUrl, { + method: request.method, + headers: createRequestHeaders(request), + body: await getRequestBody(request), + cache: "no-store", + }); + + return new Response(request.method === "HEAD" ? null : response.body, { + status: response.status, + statusText: response.statusText, + headers: createResponseHeaders(response), + }); + } catch (error) { + const message = + error instanceof Error ? error.message : "Unknown backend proxy error"; + + return NextResponse.json( + { + detail: `Backend request failed while proxying to ${backendUrl}: ${message}`, + }, + { status: 502 }, + ); + } +} + +export const GET = proxyRequest; +export const POST = proxyRequest; +export const PUT = proxyRequest; +export const PATCH = proxyRequest; +export const DELETE = proxyRequest; +export const OPTIONS = proxyRequest; +export const HEAD = proxyRequest; diff --git a/ui/src/lib/auth/config.ts b/ui/src/lib/auth/config.ts index b58927b..1958297 100644 --- a/ui/src/lib/auth/config.ts +++ b/ui/src/lib/auth/config.ts @@ -1,5 +1,7 @@ import "server-only"; +import { getServerBackendUrl } from "@/lib/apiClient"; + let cachedAuthProvider: string | null = null; /** @@ -12,7 +14,7 @@ export async function getAuthProvider(): Promise { } try { - const backendUrl = process.env.BACKEND_URL || "http://localhost:8000"; + const backendUrl = getServerBackendUrl(); const res = await fetch(`${backendUrl}/api/v1/health`, { next: { revalidate: 300 }, }); diff --git a/ui/src/middleware.ts b/ui/src/middleware.ts index f73231a..12014dc 100644 --- a/ui/src/middleware.ts +++ b/ui/src/middleware.ts @@ -1,6 +1,8 @@ import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; +import { getServerBackendUrl } from '@/lib/apiClient'; + const OSS_TOKEN_COOKIE = 'dograh_auth_token'; // Paths that don't require authentication in OSS mode @@ -14,7 +16,7 @@ async function fetchAuthProvider(): Promise { } try { - const backendUrl = process.env.BACKEND_URL || 'http://localhost:8000'; + const backendUrl = getServerBackendUrl(); const res = await fetch(`${backendUrl}/api/v1/health`); if (res.ok) { const data = await res.json();