0.2 在线版本打通

This commit is contained in:
liaibo
2026-01-15 20:54:21 +08:00
parent 725a60d3aa
commit 94957dc361
1596 changed files with 153254 additions and 309 deletions
+86
View File
@@ -0,0 +1,86 @@
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
export const dynamic = "force-dynamic";
// 说明:ONLYOFFICE 文档服务器在编辑过程中会从 `/cache/*` 拉取二进制缓存(例如 Editor.bin)。
// 当我们通过 `/onlyoffice-server/*` 反代文档服务器时,这些 `/cache/*` 请求会落到 Next 上,
// 若未额外反代,会导致 404,进而触发 ONLYOFFICE “下载失败(-4)/无法打开文档”。
// 因此这里把 `/cache/*` 同样反代到本机 ONLYOFFICE。
const ONLYOFFICE_INTERNAL_URL = (process.env.ONLYOFFICE_INTERNAL_URL || "http://127.0.0.1:8081").replace(
/\/+$/,
"",
);
const stripHopByHopHeaders = (headers: Headers) => {
// 说明:Hop-by-hop headers 不应被代理转发/透传
const hopByHop = [
"connection",
"keep-alive",
"proxy-authenticate",
"proxy-authorization",
"te",
"trailer",
"transfer-encoding",
"upgrade",
];
for (const key of hopByHop) {
headers.delete(key);
}
};
const proxyCache = async (request: NextRequest, pathParts: string[]) => {
const incomingUrl = new URL(request.url);
const target = new URL(
`${ONLYOFFICE_INTERNAL_URL}/cache/${(pathParts ?? []).map(encodeURIComponent).join("/")}`,
);
target.search = incomingUrl.search;
const headers = new Headers(request.headers);
headers.delete("host");
// 说明:避免上游 gzip 后被自动解压但仍带 content-encoding,导致浏览器二次解压错误
headers.set("accept-encoding", "identity");
stripHopByHopHeaders(headers);
const method = request.method.toUpperCase();
const hasBody = !["GET", "HEAD"].includes(method);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const init: any = {
method,
headers,
body: hasBody ? request.body : undefined,
duplex: "half",
redirect: "follow",
};
const upstream = await fetch(target, init);
const outHeaders = new Headers(upstream.headers);
stripHopByHopHeaders(outHeaders);
outHeaders.delete("content-encoding");
outHeaders.delete("content-length");
return new NextResponse(upstream.body, {
status: upstream.status,
headers: outHeaders,
});
};
type RouteCtx = { params: Promise<{ path: string[] }> };
export async function GET(request: NextRequest, ctx: RouteCtx) {
const { path } = await ctx.params;
return proxyCache(request, path ?? []);
}
export async function HEAD(request: NextRequest, ctx: RouteCtx) {
const { path } = await ctx.params;
return proxyCache(request, path ?? []);
}
export async function OPTIONS(request: NextRequest, ctx: RouteCtx) {
const { path } = await ctx.params;
return proxyCache(request, path ?? []);
}