people-reading/web/app/api/[...path]/route.ts
2026-05-12 11:19:16 +08:00

81 lines
2.1 KiB
TypeScript

import { NextRequest } from "next/server";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
const HOP_BY_HOP_HEADERS = new Set([
"connection",
"keep-alive",
"proxy-authenticate",
"proxy-authorization",
"te",
"trailer",
"transfer-encoding",
"upgrade",
"host",
]);
type RouteContext = {
params: Promise<{ path: string[] }>;
};
function getApiInternalUrl() {
return (process.env.API_INTERNAL_URL || "http://127.0.0.1:8000").replace(/\/$/, "");
}
function buildHeaders(request: NextRequest) {
const headers = new Headers();
request.headers.forEach((value, key) => {
if (!HOP_BY_HOP_HEADERS.has(key.toLowerCase())) {
headers.set(key, value);
}
});
return headers;
}
async function proxy(request: NextRequest, context: RouteContext) {
const { path } = await context.params;
const apiPath = path.join("/");
const targetUrl = new URL(`/api/${apiPath}${request.nextUrl.search}`, getApiInternalUrl());
const method = request.method.toUpperCase();
const hasBody = !["GET", "HEAD"].includes(method);
try {
const upstream = await fetch(targetUrl, {
method,
headers: buildHeaders(request),
body: hasBody ? await request.arrayBuffer() : undefined,
cache: "no-store",
});
const responseHeaders = new Headers(upstream.headers);
responseHeaders.delete("content-encoding");
responseHeaders.delete("content-length");
return new Response(upstream.body, {
status: upstream.status,
statusText: upstream.statusText,
headers: responseHeaders,
});
} catch (error) {
console.error("API proxy failed", {
target: targetUrl.toString(),
error: error instanceof Error ? error.message : String(error),
});
return Response.json(
{
detail: "后端服务暂时不可用,请检查 API_INTERNAL_URL 或后端容器状态。",
target: targetUrl.origin,
},
{ status: 502 },
);
}
}
export const GET = proxy;
export const POST = proxy;
export const PUT = proxy;
export const PATCH = proxy;
export const DELETE = proxy;