import { cookies } from "next/headers" import { NextRequest, NextResponse } from "next/server" const HOP_BY_HOP = new Set([ "host", "connection", "content-length", "keep-alive", "transfer-encoding", "upgrade", "te", "trailer", "proxy-authorization", "proxy-authenticate", ]) async function forward(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) { const cookieStore = await cookies() const apiBase = cookieStore.get("api_base_url")?.value const wsUuid = cookieStore.get("workspace_uuid")?.value const token = cookieStore.get("auth_token")?.value if (!apiBase || !wsUuid) { return NextResponse.json( { message: "Workspace session not found. Please open your garage from the SaaS dashboard.", code: "workspace_session_missing", }, { status: 412 }, ) } const { path } = await ctx.params const url = new URL(req.url) // The OpenAPI clients keep `/api/...` in their route patterns, so when the // client uses baseUrl=`/api/proxy` the resulting URL is // `/api/proxy/api/customers`. Forward the path verbatim and rejoin so the // upstream becomes `{apiBase}/api/customers`. const target = `${apiBase.replace(/\/$/, "")}/${(path ?? []).join("/")}${url.search}` const headers = new Headers() req.headers.forEach((value, key) => { if (!HOP_BY_HOP.has(key.toLowerCase()) && key.toLowerCase() !== "cookie") { headers.set(key, value) } }) headers.set("Accept", "application/json") headers.set("X-Workspace-UUID", wsUuid) if (token) { headers.set("Authorization", `Bearer ${token}`) } const init: RequestInit = { method: req.method, headers, redirect: "manual", } if (req.method !== "GET" && req.method !== "HEAD") { init.body = await req.arrayBuffer() } let upstream: Response try { upstream = await fetch(target, init) } catch (err) { return NextResponse.json( { message: "Failed to reach garage backend.", code: "backend_unreachable", error: (err as Error).message, }, { status: 502 }, ) } const respHeaders = new Headers() upstream.headers.forEach((value, key) => { const k = key.toLowerCase() if (HOP_BY_HOP.has(k) || k === "set-cookie") return respHeaders.set(key, value) }) return new NextResponse(upstream.body, { status: upstream.status, statusText: upstream.statusText, headers: respHeaders, }) } export const GET = forward export const POST = forward export const PUT = forward export const PATCH = forward export const DELETE = forward export const OPTIONS = forward export const HEAD = forward