From dab9748e51d442d0ce56070321eb9cc0ad6b90cd Mon Sep 17 00:00:00 2001 From: Andrew Jiang Date: Wed, 29 May 2024 15:25:32 -0400 Subject: [PATCH] fix: sitemap.xml should fetch directly (#939) --- packages/ui/docs-bundle/next.config.js | 2 + .../src/pages/api/fern-docs/resolve-api.ts | 17 ++------ .../src/pages/api/fern-docs/sitemap.ts | 41 +++++++----------- .../src/pages/api/fern-docs/sitemap.xml.ts | 42 ++++++++++--------- .../docs-bundle/src/utils/buildUrlFromApi.ts | 20 +++++++++ .../docs-bundle/src/utils/serverResponse.ts | 14 +++---- 6 files changed, 69 insertions(+), 67 deletions(-) create mode 100644 packages/ui/docs-bundle/src/utils/buildUrlFromApi.ts diff --git a/packages/ui/docs-bundle/next.config.js b/packages/ui/docs-bundle/next.config.js index 56ef040bb0..675a76c621 100644 --- a/packages/ui/docs-bundle/next.config.js +++ b/packages/ui/docs-bundle/next.config.js @@ -45,6 +45,8 @@ const nextConfig = { */ { source: "/:prefix*/_next/:path*", destination: "/_next/:path*" }, { source: "/:prefix*/api/fern-docs/:path*", destination: "/api/fern-docs/:path*" }, + { source: "/:prefix*/robots.txt", destination: "/api/fern-docs/robots.txt" }, + { source: "/:prefix*/sitemap.xml", destination: "/api/fern-docs/sitemap.xml" }, /** * Since we use cookie rewrites to determine if the path should be rewritten to /static or /dynamic, prefetch requests * do not have access to these cookies, and will always be matched to /static. This rewrite rule will ensure that diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/resolve-api.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/resolve-api.ts index 3405c1e645..ed4d1c4048 100644 --- a/packages/ui/docs-bundle/src/pages/api/fern-docs/resolve-api.ts +++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/resolve-api.ts @@ -1,7 +1,7 @@ -import { buildUrl, resolveSidebarNodesRoot, visitSidebarNodeRaw } from "@fern-ui/fdr-utils"; +import { resolveSidebarNodesRoot, visitSidebarNodeRaw } from "@fern-ui/fdr-utils"; import { ApiDefinitionResolver, REGISTRY_SERVICE, type ResolvedRootPackage } from "@fern-ui/ui"; import { NextApiHandler, NextApiResponse } from "next"; -import { toValidPathname } from "../../../utils/toValidPathname"; +import { buildUrlFromApiNode } from "../../../utils/buildUrlFromApi"; import { getXFernHostNode } from "../../../utils/xFernHost"; import { getFeatureFlags } from "./feature-flags"; @@ -20,18 +20,7 @@ const resolveApiHandler: NextApiHandler = async ( const xFernHost = getXFernHostNode(req); res.setHeader("host", xFernHost); - const fullUrl = req.url; - - if (fullUrl == null) { - res.status(400).json(null); - return; - } - - const maybePathName = fullUrl.split("/api/fern-docs/resolve-api")[0] ?? ""; - const url = buildUrl({ - host: xFernHost, - pathname: toValidPathname(maybePathName), - }); + const url = buildUrlFromApiNode(xFernHost, req); // eslint-disable-next-line no-console console.log("[resolve-api] Loading docs for", url); const docsResponse = await REGISTRY_SERVICE.docs.v2.read.getDocsForUrl({ diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.ts index 403fb03e9a..6d5f137c19 100644 --- a/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.ts +++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.ts @@ -1,8 +1,8 @@ -import { buildUrl, getAllUrlsFromDocsConfig } from "@fern-ui/fdr-utils"; +import { getAllUrlsFromDocsConfig } from "@fern-ui/fdr-utils"; import { NextRequest, NextResponse } from "next/server"; +import { buildUrlFromApiEdge } from "../../../utils/buildUrlFromApi"; import { loadWithUrl } from "../../../utils/loadWithUrl"; import { jsonResponse } from "../../../utils/serverResponse"; -import { toValidPathname } from "../../../utils/toValidPathname"; import { getXFernHostEdge } from "../../../utils/xFernHost"; export const runtime = "edge"; @@ -13,31 +13,22 @@ export default async function GET(req: NextRequest): Promise { } const xFernHost = getXFernHostEdge(req); - const headers: Record = { - "x-fern-host": xFernHost, - }; + const headers = new Headers(); + headers.set("x-fern-host", xFernHost); - try { - const url = buildUrl({ host: xFernHost, pathname: toValidPathname(req.nextUrl.searchParams.get("basePath")) }); - // eslint-disable-next-line no-console - console.log("[sitemap] Loading docs for", url); - const docs = await loadWithUrl(url); + const url = buildUrlFromApiEdge(xFernHost, req); + const docs = await loadWithUrl(url); - if (docs == null) { - return jsonResponse(404, [], headers); - } + if (docs == null) { + return jsonResponse(404, [], { headers }); + } - const urls = getAllUrlsFromDocsConfig( - xFernHost, - docs.baseUrl.basePath, - docs.definition.config.navigation, - docs.definition.apis, - ); + const urls = getAllUrlsFromDocsConfig( + xFernHost, + docs.baseUrl.basePath, + docs.definition.config.navigation, + docs.definition.apis, + ); - return jsonResponse(200, urls, headers); - } catch (err) { - // eslint-disable-next-line no-console - console.error(err); - return jsonResponse(500, [], headers); - } + return jsonResponse(200, urls, { headers }); } diff --git a/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.xml.ts b/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.xml.ts index e8e3533375..884ab710ad 100644 --- a/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.xml.ts +++ b/packages/ui/docs-bundle/src/pages/api/fern-docs/sitemap.xml.ts @@ -1,38 +1,40 @@ +import { getAllUrlsFromDocsConfig } from "@fern-ui/fdr-utils"; import { NextRequest, NextResponse } from "next/server"; -import { notFoundResponse } from "../../../utils/serverResponse"; +import { buildUrlFromApiEdge } from "../../../utils/buildUrlFromApi"; +import { loadWithUrl } from "../../../utils/loadWithUrl"; import { getXFernHostEdge } from "../../../utils/xFernHost"; export const runtime = "edge"; export const revalidate = 60 * 60 * 24; export default async function GET(req: NextRequest): Promise { - const xFernHost = getXFernHostEdge(req); - - const hostWithoutTrailingSlash = xFernHost.endsWith("/") ? xFernHost.slice(0, -1) : xFernHost; + if (req.method !== "GET") { + return new NextResponse(null, { status: 405 }); + } - const hostnameAndProtocol = req.nextUrl.host.includes("localhost") - ? "http://localhost:3000" - : `https://${hostWithoutTrailingSlash}`; + const xFernHost = getXFernHostEdge(req); + const headers = new Headers(); + headers.set("x-fern-host", xFernHost); - const sitemapResponse = await fetch(`${hostnameAndProtocol}/api/fern-docs/sitemap`, { - headers: { "x-fern-host": xFernHost }, - }); + const url = buildUrlFromApiEdge(xFernHost, req); + const docs = await loadWithUrl(url); - if (sitemapResponse.status !== 200) { - // eslint-disable-next-line no-console - console.error("Failed to fetch docs", sitemapResponse.status, sitemapResponse.statusText); - return notFoundResponse(); + if (docs == null) { + return new NextResponse(null, { status: 404 }); } - const urls: string[] = await sitemapResponse.json(); + const urls = getAllUrlsFromDocsConfig( + xFernHost, + docs.baseUrl.basePath, + docs.definition.config.navigation, + docs.definition.apis, + ); const sitemap = getSitemapXml(urls.map((url) => `https://${url}`)); - return new NextResponse(sitemap, { - headers: { - "Content-Type": "text/xml", - }, - }); + headers.set("Content-Type", "text/xml"); + + return new NextResponse(sitemap, { headers }); } function getSitemapXml(urls: string[]): string { diff --git a/packages/ui/docs-bundle/src/utils/buildUrlFromApi.ts b/packages/ui/docs-bundle/src/utils/buildUrlFromApi.ts new file mode 100644 index 0000000000..a6d27e5047 --- /dev/null +++ b/packages/ui/docs-bundle/src/utils/buildUrlFromApi.ts @@ -0,0 +1,20 @@ +import { buildUrl } from "@fern-ui/fdr-utils"; +import type { NextRequest } from "next/server"; +import type { NextApiRequest } from "next/types"; +import { toValidPathname } from "./toValidPathname"; + +export function buildUrlFromApiEdge(xFernHost: string, req: NextRequest): string { + const maybePathName = req.nextUrl.pathname.split("/api/fern-docs")[0] ?? ""; + return buildUrl({ + host: xFernHost, + pathname: toValidPathname(maybePathName), + }); +} + +export function buildUrlFromApiNode(xFernHost: string, req: NextApiRequest): string { + const maybePathName = req.url?.split("/api/fern-docs")[0] ?? ""; + return buildUrl({ + host: xFernHost, + pathname: toValidPathname(maybePathName), + }); +} diff --git a/packages/ui/docs-bundle/src/utils/serverResponse.ts b/packages/ui/docs-bundle/src/utils/serverResponse.ts index f5213216a9..999971659e 100644 --- a/packages/ui/docs-bundle/src/utils/serverResponse.ts +++ b/packages/ui/docs-bundle/src/utils/serverResponse.ts @@ -4,24 +4,22 @@ import { NextResponse } from "next/server"; * Returns a Response object with a JSON body */ export function jsonResponse(status: number, data: Data, init?: ResponseInit): NextResponse { + const headers = new Headers(init?.headers); + headers.set("Content-Type", "application/json"); return new NextResponse(JSON.stringify(data), { ...init, status, - headers: { - ...init?.headers, - "Content-Type": "application/json", - }, + headers, }); } export function redirectResponse(location: string, init?: ResponseInit): NextResponse { + const headers = new Headers(init?.headers); + headers.set("Location", location); return new NextResponse(undefined, { ...init, status: 302, - headers: { - ...init?.headers, - Location: location, - }, + headers, }); }