feat: new file
This commit is contained in:
62
frontend/app/api/[...path]/route.ts
Normal file
62
frontend/app/api/[...path]/route.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
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 });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user