98 lines
2.8 KiB
TypeScript
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
|