63 lines
2.2 KiB
TypeScript
63 lines
2.2 KiB
TypeScript
import { NextRequest } from "next/server";
|
||
|
||
const BACKEND_URL = process.env.BACKEND_URL || "http://localhost:8000";
|
||
|
||
export async function GET(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) {
|
||
const { path } = await ctx.params;
|
||
const url = new URL(req.url);
|
||
const upstream = `${BACKEND_URL}/api/${path.join("/")}${url.search}`;
|
||
return await safeUpstreamFetch(() => fetch(upstream, { headers: forwardHeaders(req) }));
|
||
}
|
||
|
||
export async function POST(req: NextRequest, ctx: { params: Promise<{ path: string[] }> }) {
|
||
const { path } = await ctx.params;
|
||
const url = new URL(req.url);
|
||
const upstream = `${BACKEND_URL}/api/${path.join("/")}${url.search}`;
|
||
const body = await req.text();
|
||
return await safeUpstreamFetch(() =>
|
||
fetch(upstream, {
|
||
method: "POST",
|
||
headers: { ...forwardHeaders(req), "content-type": req.headers.get("content-type") || "application/json" },
|
||
body
|
||
})
|
||
);
|
||
}
|
||
|
||
function forwardHeaders(req: NextRequest) {
|
||
const h = new Headers();
|
||
const auth = req.headers.get("authorization");
|
||
if (auth) h.set("authorization", auth);
|
||
return h;
|
||
}
|
||
|
||
async function forwardResponse(r: Response) {
|
||
const headers = new Headers(r.headers);
|
||
headers.delete("access-control-allow-origin");
|
||
return new Response(await r.arrayBuffer(), { status: r.status, headers });
|
||
}
|
||
|
||
async function safeUpstreamFetch(doFetch: () => Promise<Response>) {
|
||
const maxAttempts = 3;
|
||
for (let i = 0; i < maxAttempts; i++) {
|
||
try {
|
||
const r = await doFetch();
|
||
return await forwardResponse(r);
|
||
} catch (e: any) {
|
||
const code = e?.cause?.code || e?.code;
|
||
const retryable = code === "ECONNREFUSED" || code === "ENOTFOUND" || code === "EAI_AGAIN";
|
||
if (!retryable || i === maxAttempts - 1) {
|
||
return new Response(
|
||
JSON.stringify({
|
||
detail: "上游后端不可达(backend 容器可能正在重启或未就绪)。",
|
||
error: String(code || e?.message || e)
|
||
}),
|
||
{ status: 503, headers: { "content-type": "application/json; charset=utf-8" } }
|
||
);
|
||
}
|
||
await new Promise((r) => setTimeout(r, 200 * (i + 1)));
|
||
}
|
||
}
|
||
return new Response(JSON.stringify({ detail: "unknown" }), { status: 503 });
|
||
}
|
||
|