Files
hunjing/frontend/src/lib/backend/proxy.ts
T
2026-08-21 16:00:13 +08:00

88 lines
2.2 KiB
TypeScript

/**
* BFF 通用代理服务(仅服务端使用)。
*
* 封装请求转发逻辑:构建请求头、注入 Bearer token、响应头透传过滤。
* proxy route handler 只负责编排:读路径 → 转发 → 401 重试 → 返回。
*/
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
/** 客户端 → 后端允许透传的请求头白名单(剔除 host / cookie / cf-* 等环境头)。 */
const FORWARD_HEADERS = [
"content-type",
"accept",
"accept-language",
"idempotency-key",
];
/**
* 构建转发给后端的请求头:白名单透传 + 注入 Bearer。
*/
export function buildHeaders(
req: NextRequest,
token: string | null,
): Record<string, string> {
const headers: Record<string, string> = {};
for (const key of FORWARD_HEADERS) {
const v = req.headers.get(key);
if (v) headers[key] = v;
}
if (token) headers["authorization"] = `Bearer ${token}`;
return headers;
}
/**
* 转发请求到后端目标地址。
* body 为已缓冲的 ArrayBuffer,可在 401 重试时复用。
*/
export function forward(
target: string,
req: NextRequest,
token: string | null,
body: ArrayBuffer | null,
): Promise<Response> {
const init: RequestInit = {
method: req.method,
headers: buildHeaders(req, token),
cache: "no-store",
};
if (body && req.method !== "GET" && req.method !== "HEAD") {
init.body = body;
}
return fetch(target, init);
}
/**
* 响应头透传,过滤 set-cookie(避免后端 cookie 干扰 BFF 的 cookie 管理)。
*/
export function passthroughHeaders(src: Headers): Headers {
const h = new Headers();
src.forEach((value, key) => {
if (key.toLowerCase() === "set-cookie") return;
h.set(key, value);
});
return h;
}
/**
* 构建后端目标 URL。
*/
export function buildTargetUrl(
backend: string,
path: string[],
search: string,
): string {
return `${backend}/${path.join("/")}${search}`;
}
/**
* 构造代理响应的 NextResponse。
*/
export function proxyResponse(resp: Response): NextResponse {
return new NextResponse(resp.body, {
status: resp.status,
statusText: resp.statusText,
headers: passthroughHeaders(resp.headers),
});
}