98 lines
2.8 KiB
TypeScript

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