fix: preserve historical OpenAPI redirects

This commit is contained in:
dadachann
2026-09-04 11:24:05 +00:00
parent 5fb96b2d26
commit 3422d22808
3 changed files with 40 additions and 4 deletions
+26 -3
View File
@@ -115,11 +115,33 @@ function cloudflarePattern(value, destination = false) {
return value.replace(/:([A-Za-z][A-Za-z0-9_]*)\*/g, destination ? ":splat" : "*");
}
export function renderCloudflareRedirects(docs) {
function slugifyOpenApiSummary(value) {
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
}
export function collectOpenApiRedirectTargets(spec) {
const targets = new Map();
for (const [apiPath, pathItem] of Object.entries(spec?.paths ?? {})) {
for (const [method, operation] of Object.entries(pathItem)) {
if (!/^(?:get|post|put|patch|delete|head|options)$/i.test(method) || !operation?.summary) continue;
const summarySlug = slugifyOpenApiSummary(operation.summary);
const routePath = apiPath.replaceAll("{", "").replaceAll("}", "");
targets.set(summarySlug, `/en/api-reference${routePath}/${method.toLowerCase()}`);
}
}
return targets;
}
export function renderCloudflareRedirects(docs, englishOpenApiSpec) {
const apiTargets = collectOpenApiRedirectTargets(englishOpenApiSpec);
const lines = ["/ /en/insight/guide 302"];
for (const redirect of docs.redirects ?? []) {
const status = redirect.permanent === false ? 307 : 308;
lines.push(`${cloudflarePattern(redirect.source)} ${cloudflarePattern(redirect.destination, true)} ${status}`);
const destinationSlug = redirect.destination.match(/^\/en\/api-reference\/[^/]+\/([^/:*]+)$/)?.[1];
const destination = destinationSlug && apiTargets.get(destinationSlug)
? apiTargets.get(destinationSlug)
: redirect.destination;
lines.push(`${cloudflarePattern(redirect.source)} ${cloudflarePattern(destination, true)} ${status}`);
}
return `${lines.join("\n")}\n`;
}
@@ -137,6 +159,7 @@ export async function prepareFumapress({ root = ROOT, outRoot = root } = {}) {
if (openapi.length !== LOCALES.length) {
throw new Error(`Expected one nested OpenAPI source per locale, found ${openapi.length}`);
}
const englishOpenApi = JSON.parse(await readFile(path.join(root, openapi.find((item) => item.locale === "en").source), "utf8"));
const contentRoot = path.join(outRoot, "content");
const publicRoot = path.join(outRoot, "public");
@@ -177,7 +200,7 @@ export async function prepareFumapress({ root = ROOT, outRoot = root } = {}) {
}
await cp(path.join(root, "robots.txt"), path.join(publicRoot, "robots.txt"), { force: true });
await writeFile(path.join(publicRoot, "_redirects"), renderCloudflareRedirects(docs));
await writeFile(path.join(publicRoot, "_redirects"), renderCloudflareRedirects(docs, englishOpenApi));
return {
documents: documents.length,
+4 -1
View File
@@ -144,11 +144,14 @@ test("canonical MDX and nested OpenAPI sources are discovered", async () => {
test("Cloudflare redirects preserve all Mintlify routes and add root", async () => {
const docs = JSON.parse(await readFile(path.join(root, "docs.json"), "utf8"));
const lines = renderCloudflareRedirects(docs).trim().split("\n");
const spec = JSON.parse(await readFile(path.join(root, "openapi/service-api-en.json"), "utf8"));
const lines = renderCloudflareRedirects(docs, spec).trim().split("\n");
assert.equal(lines.length, docs.redirects.length + 1);
assert.equal(lines[0], "/ /en/insight/guide 302");
assert.ok(lines.includes("/README_EN /en/insight/guide 308"));
assert.ok(lines.some((line) => line.startsWith("/en/deploy/platforms/* /en/usage/platforms/:splat ")));
assert.ok(lines.includes("/en/api-reference/system/获取系统信息 /en/api-reference/api/v1/system/info/get 308"));
assert.ok(lines.includes("/en/api-reference/model-providers/创建供应商 /en/api-reference/api/v1/provider/providers/post 308"));
});
test("Fumapress config wires static localized docs, OpenAPI, and Mintlify support", async () => {
+10
View File
@@ -37,6 +37,16 @@ test("all 302 canonical localized documents have static HTML", async () => {
assert.deepEqual(missing, []);
});
test("every fixed redirect destination resolves to generated output", async () => {
const redirects = (await readFile(path.join(publicRoot, "_redirects"), "utf8"))
.trim().split("\n").map((line) => line.split(/\s+/));
for (const [source, destination] of redirects) {
if (destination.includes(":splat")) continue;
const target = destination.replace(/^\//, "").replace(/\/$/, "");
await access(path.join(publicRoot, target, "index.html"), undefined, `${source} -> ${destination}`);
}
});
test("static routes never repeat their locale prefix", async () => {
for (const locale of ["en", "zh", "ja"]) {
await assert.rejects(access(path.join(publicRoot, locale, locale)), { code: "ENOENT" });