feat: implement workspace session management and enhance API proxy handling
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
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
|
||||
Reference in New Issue
Block a user