From adfded0e408a7b4cbeb90a0ad1103b5ef99820f0 Mon Sep 17 00:00:00 2001 From: zetaloop Date: Fri, 24 Apr 2026 09:19:28 +0800 Subject: [PATCH] fix(api): resolve server-side api urls from request context --- lib/api/http.ts | 48 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/lib/api/http.ts b/lib/api/http.ts index 0d61260..6a58e1a 100644 --- a/lib/api/http.ts +++ b/lib/api/http.ts @@ -6,6 +6,39 @@ type JsonRequestInit = Omit & { json?: unknown } +async function getServerHeaders() { + if (typeof window !== "undefined") return null + + try { + const mod = await import("next/headers") + return await mod.headers() + } catch { + return null + } +} + +async function resolveServerUrl(path: string) { + if (typeof window !== "undefined" || !path.startsWith("/")) return path + + const requestHeaders = await getServerHeaders() + const host = requestHeaders?.get("x-forwarded-host") ?? requestHeaders?.get("host") + if (host) { + const proto = + requestHeaders?.get("x-forwarded-proto") ?? + (host.startsWith("localhost") || host.startsWith("127.0.0.1") ? "http" : "https") + return `${proto}://${host}${path}` + } + + const explicitBase = + process.env.NEXT_PUBLIC_BACKEND_URL?.replace(/\/$/, "") || + process.env.BACKEND_URL?.replace(/\/$/, "") || + process.env.INTERNAL_API_ORIGIN?.replace(/\/$/, "") + + if (explicitBase) return `${explicitBase}${path}` + + throw new Error(`Cannot resolve server-side API URL for ${path}`) +} + async function readJsonBody(res: Response): Promise<{ json: unknown | null; text: string }> { const text = await res.text() if (!text) return { json: null, text: "" } @@ -38,20 +71,19 @@ export async function httpJson(path: string, init?: JsonRequestInit): Promise throw new Error("Absolute URLs are not allowed") } - let url = path - if ( - typeof window === "undefined" && - path.startsWith("/") && - process.env.NEXT_PUBLIC_BACKEND_URL - ) { - url = `${process.env.NEXT_PUBLIC_BACKEND_URL.replace(/\/$/, "")}${path}` - } + const url = await resolveServerUrl(path) const { json, headers: headersInit, body: bodyInit, ...rest } = init ?? {} const headers = new Headers(headersInit) headers.set("Accept", "application/json") + const requestHeaders = await getServerHeaders() + const cookie = requestHeaders?.get("cookie") + if (cookie && !headers.has("cookie")) { + headers.set("cookie", cookie) + } + const body = json === undefined ? bodyInit : JSON.stringify(json) if (json !== undefined && !headers.has("Content-Type")) {