Skip to content

Commit

Permalink
move all vercel-edge functions into fern-docs-edge package
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity committed Oct 7, 2024
1 parent c9a931c commit 424cdfb
Show file tree
Hide file tree
Showing 50 changed files with 590 additions and 201 deletions.
2 changes: 2 additions & 0 deletions packages/ui/docs-bundle/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ const nextConfig = {
"@fern-ui/components",
"@fern-ui/core-utils",
"@fern-ui/fdr-utils",
"@fern-ui/fern-docs-utils",
"@fern-ui/fern-docs-edge",
"@fern-ui/loadable",
"@fern-ui/next-seo",
"@fern-ui/react-commons",
Expand Down
1 change: 1 addition & 0 deletions packages/ui/docs-bundle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@fern-ui/core-utils": "workspace:*",
"@fern-ui/fdr-utils": "workspace:*",
"@fern-ui/fern-docs-utils": "workspace:*",
"@fern-ui/fern-docs-edge": "workspace:*",
"@fern-ui/search-utils": "workspace:*",
"@fern-ui/ui": "workspace:*",
"@sentry/nextjs": "^8.30.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/docs-bundle/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { extractBuildId, extractNextDataPathname } from "@/server/extractNextDat
import { getPageRoute, getPageRouteMatch, getPageRoutePath } from "@/server/pageRoutes";
import { rewritePosthog } from "@/server/rewritePosthog";
import { getXFernHostEdge } from "@/server/xfernhost/edge";
import { getAuthEdgeConfig } from "@fern-ui/fern-docs-edge";
import { COOKIE_FERN_TOKEN, HEADER_X_FERN_HOST } from "@fern-ui/fern-docs-utils";
import type { FernUser } from "@fern-ui/ui/auth";
import { removeTrailingSlash } from "next/dist/shared/lib/router/utils/remove-trailing-slash";
import { NextRequest, NextResponse, type NextMiddleware } from "next/server";
import urlJoin from "url-join";
import { verifyFernJWTConfig } from "./server/auth/FernJWT";
import { getAuthEdgeConfig } from "./server/auth/getAuthEdgeConfig";
import { COOKIE_FERN_TOKEN, HEADER_X_FERN_HOST } from "./server/constants";
import { withBasicTokenPublic } from "./server/withBasicTokenPublic";

const API_FERN_DOCS_PATTERN = /^(?!\/api\/fern-docs\/).*(\/api\/fern-docs\/)/;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ApiDefinitionLoader } from "@/server/ApiDefinitionLoader";
import { getXFernHostNode } from "@/server/xfernhost/node";
import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition";
import { getFeatureFlags } from "@fern-ui/fern-docs-edge";
import { NextApiHandler, NextApiResponse } from "next";
import { getFeatureFlags } from "../../../feature-flags";

const resolveApiHandler: NextApiHandler = async (req, res: NextApiResponse<ApiDefinition.ApiDefinition>) => {
const xFernHost = getXFernHostNode(req);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ApiDefinitionLoader } from "@/server/ApiDefinitionLoader";
import { getXFernHostNode } from "@/server/xfernhost/node";
import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition";
import { getFeatureFlags } from "@fern-ui/fern-docs-edge";
import { NextApiHandler, NextApiResponse } from "next";
import { getFeatureFlags } from "../../../feature-flags";

const resolveApiHandler: NextApiHandler = async (req, res: NextApiResponse<ApiDefinition.ApiDefinition>) => {
const xFernHost = getXFernHostNode(req);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ApiDefinitionLoader } from "@/server/ApiDefinitionLoader";
import { getXFernHostNode } from "@/server/xfernhost/node";
import * as ApiDefinition from "@fern-api/fdr-sdk/api-definition";
import { getFeatureFlags } from "@fern-ui/fern-docs-edge";
import { NextApiHandler, NextApiResponse } from "next";
import { getFeatureFlags } from "../../../feature-flags";

const resolveApiHandler: NextApiHandler = async (req, res: NextApiResponse<ApiDefinition.ApiDefinition>) => {
const xFernHost = getXFernHostNode(req);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { OAuth2Client } from "@/server/auth/OAuth2Client";
import { getAPIKeyInjectionConfig } from "@/server/auth/getApiKeyInjectionConfig";
import { getAuthEdgeConfig } from "@/server/auth/getAuthEdgeConfig";
import { withSecureCookie } from "@/server/auth/withSecure";
import { COOKIE_FERN_TOKEN } from "@/server/constants";
import { getXFernHostEdge } from "@/server/xfernhost/edge";
import { getAuthEdgeConfig } from "@fern-ui/fern-docs-edge";
import { COOKIE_FERN_TOKEN } from "@fern-ui/fern-docs-utils";
import { APIKeyInjectionConfig, OryAccessTokenSchema } from "@fern-ui/ui/auth";
import { NextRequest, NextResponse } from "next/server";
import { WebflowClient } from "webflow-api";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { signFernJWT } from "@/server/auth/FernJWT";
import { getAuthEdgeConfig } from "@/server/auth/getAuthEdgeConfig";
import { withSecureCookie } from "@/server/auth/withSecure";
import { COOKIE_FERN_TOKEN } from "@/server/constants";
import { getWorkOS, getWorkOSClientId } from "@/server/workos";
import { getXFernHostEdge } from "@/server/xfernhost/edge";
import { getAuthEdgeConfig } from "@fern-ui/fern-docs-edge";
import { COOKIE_FERN_TOKEN } from "@fern-ui/fern-docs-utils";
import { FernUser } from "@fern-ui/ui/auth";
import { NextRequest, NextResponse } from "next/server";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { verifyFernJWTConfig } from "@/server/auth/FernJWT";
import { getAuthEdgeConfig } from "@/server/auth/getAuthEdgeConfig";
import { withSecureCookie } from "@/server/auth/withSecure";
import { COOKIE_FERN_TOKEN } from "@/server/constants";
import { getXFernHostEdge } from "@/server/xfernhost/edge";
import { getAuthEdgeConfig } from "@fern-ui/fern-docs-edge";
import { COOKIE_FERN_TOKEN } from "@fern-ui/fern-docs-utils";
import { NextRequest, NextResponse } from "next/server";

export const runtime = "edge";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { COOKIE_ACCESS_TOKEN, COOKIE_FERN_TOKEN, COOKIE_REFRESH_TOKEN } from "@/server/constants";
import { getXFernHostEdge } from "@/server/xfernhost/edge";
import { COOKIE_ACCESS_TOKEN, COOKIE_FERN_TOKEN, COOKIE_REFRESH_TOKEN } from "@fern-ui/fern-docs-utils";
import { NextRequest, NextResponse } from "next/server";

export const runtime = "edge";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { DocsLoader } from "@/server/DocsLoader";
import { COOKIE_FERN_TOKEN } from "@/server/constants";
import { getXFernHostNode } from "@/server/xfernhost/node";
import type { DocsV1Read } from "@fern-api/fdr-sdk/client/types";
import * as FernNavigation from "@fern-api/fdr-sdk/navigation";
import { NodeCollector } from "@fern-api/fdr-sdk/navigation";
import { assertNever } from "@fern-ui/core-utils";
import { COOKIE_FERN_TOKEN } from "@fern-ui/fern-docs-utils";
import { getFrontmatter } from "@fern-ui/ui";
import * as Sentry from "@sentry/nextjs";
import { Feed, Item } from "feed";
Expand Down
164 changes: 1 addition & 163 deletions packages/ui/docs-bundle/src/pages/api/fern-docs/feature-flags.ts
Original file line number Diff line number Diff line change
@@ -1,173 +1,11 @@
import { isCustomDomain, isFern } from "@/server/isCustomDomain";
import { isDevelopment } from "@/server/isDevelopment";
import { getXFernHostEdge } from "@/server/xfernhost/edge";
import { getFeatureFlags } from "@fern-ui/fern-docs-edge";
import { FeatureFlags } from "@fern-ui/ui";
import { getAll } from "@vercel/edge-config";
import { NextRequest, NextResponse } from "next/server";

export const runtime = "edge";

const FEATURE_FLAGS = [
"api-playground-enabled" as const,
"api-scrolling-disabled" as const,
"whitelabeled" as const,
"seo-disabled" as const,
"toc-default-enabled" as const,
"snippet-template-enabled" as const,
"http-snippets-enabled" as const,
"inline-feedback-enabled" as const,
"dark-code-enabled" as const,
"proxy-uses-app-buildwithfern" as const,
"image-zoom-disabled" as const,
"use-javascript-as-typescript" as const,
"always-enable-javascript-fetch" as const,
"scroll-in-container-enabled" as const,
"use-mdx-bundler" as const,
"batch-stream-toggle-disabled" as const,
"enabled-auth-in-generated-docs" as const,
"ai-chat-preview" as const,
"audio-file-download-span-summary" as const,
"docs-logo-text-enabled" as const,
"audio-example-internal" as const,
"uses-application-json-in-form-data-value" as const,
"binary-octet-stream-audio-player" as const,
"voice-id-playground-form" as const,
"cohere-theme" as const,
"file-forge-hack-enabled" as const,
"hide-404-page" as const,
"new-search-experience" as const,
"grpc-endpoints" as const,
];

type FeatureFlag = (typeof FEATURE_FLAGS)[number];

type EdgeConfigResponse = Record<FeatureFlag, string[]>;

export default async function handler(req: NextRequest): Promise<NextResponse<FeatureFlags>> {
const domain = getXFernHostEdge(req);
return NextResponse.json(await getFeatureFlags(domain));
}

export async function getFeatureFlags(domain: string): Promise<FeatureFlags> {
try {
const config = await getAll<EdgeConfigResponse>(FEATURE_FLAGS);

const isApiPlaygroundEnabled = checkDomainMatchesCustomers(domain, config["api-playground-enabled"]);
const isApiScrollingDisabled = checkDomainMatchesCustomers(domain, config["api-scrolling-disabled"]);
const isWhitelabeled = checkDomainMatchesCustomers(domain, config.whitelabeled);
const isSeoDisabled = checkDomainMatchesCustomers(domain, config["seo-disabled"]);
const isTocDefaultEnabled = checkDomainMatchesCustomers(domain, config["toc-default-enabled"]);
const isSnippetTemplatesEnabled = checkDomainMatchesCustomers(domain, config["snippet-template-enabled"]);
const isHttpSnippetsEnabled = checkDomainMatchesCustomers(domain, config["http-snippets-enabled"]);
const isInlineFeedbackEnabled = checkDomainMatchesCustomers(domain, config["inline-feedback-enabled"]);
const isDarkCodeEnabled = checkDomainMatchesCustomers(domain, config["dark-code-enabled"]);
const proxyShouldUseAppBuildwithfernCom = checkDomainMatchesCustomers(
domain,
config["proxy-uses-app-buildwithfern"],
);
const isImageZoomDisabled = checkDomainMatchesCustomers(domain, config["image-zoom-disabled"]);
const useJavaScriptAsTypeScript = checkDomainMatchesCustomers(domain, config["use-javascript-as-typescript"]);
const alwaysEnableJavaScriptFetch = checkDomainMatchesCustomers(
domain,
config["always-enable-javascript-fetch"],
);
const scrollInContainerEnabled = checkDomainMatchesCustomers(domain, config["scroll-in-container-enabled"]);
const useMdxBundler = checkDomainMatchesCustomers(domain, config["use-mdx-bundler"]);
const isBatchStreamToggleDisabled = checkDomainMatchesCustomers(domain, config["batch-stream-toggle-disabled"]);
const isAuthEnabledInDocs = checkDomainMatchesCustomers(domain, config["enabled-auth-in-generated-docs"]);
const isAiChatbotEnabledInPreview = checkDomainMatchesCustomers(domain, config["ai-chat-preview"]);
const isAudioFileDownloadSpanSummary = checkDomainMatchesCustomers(
domain,
config["audio-file-download-span-summary"],
);
const isDocsLogoTextEnabled = checkDomainMatchesCustomers(domain, config["docs-logo-text-enabled"]);
const isAudioExampleInternal = checkDomainMatchesCustomers(domain, config["audio-example-internal"]);
const usesApplicationJsonInFormDataValue = checkDomainMatchesCustomers(
domain,
config["uses-application-json-in-form-data-value"],
);
const isBinaryOctetStreamAudioPlayer = checkDomainMatchesCustomers(
domain,
config["binary-octet-stream-audio-player"],
);
const hasVoiceIdPlaygroundForm = checkDomainMatchesCustomers(domain, config["voice-id-playground-form"]);
const isCohereTheme = checkDomainMatchesCustomers(domain, config["cohere-theme"]);
const isFileForgeHackEnabled = checkDomainMatchesCustomers(domain, config["file-forge-hack-enabled"]);
const is404PageHidden = checkDomainMatchesCustomers(domain, config["hide-404-page"]);
const isNewSearchExperienceEnabled = checkDomainMatchesCustomers(domain, config["new-search-experience"]);
const grpcEndpoints = config["grpc-endpoints"];

return {
isApiPlaygroundEnabled: isDevelopment(domain) || isApiPlaygroundEnabled,
isApiScrollingDisabled,
isWhitelabeled,
isSeoDisabled: !isCustomDomain(domain) || isSeoDisabled,
isTocDefaultEnabled,
isSnippetTemplatesEnabled: isSnippetTemplatesEnabled || isDevelopment(domain),
isHttpSnippetsEnabled,
isInlineFeedbackEnabled,
isDarkCodeEnabled,
proxyShouldUseAppBuildwithfernCom,
isImageZoomDisabled,
useJavaScriptAsTypeScript,
alwaysEnableJavaScriptFetch,
scrollInContainerEnabled,
useMdxBundler,
isBatchStreamToggleDisabled,
isAuthEnabledInDocs,
isAiChatbotEnabledInPreview,
isAudioFileDownloadSpanSummary,
isDocsLogoTextEnabled,
isAudioExampleInternal,
usesApplicationJsonInFormDataValue,
isBinaryOctetStreamAudioPlayer,
hasVoiceIdPlaygroundForm,
isCohereTheme,
isFileForgeHackEnabled,
is404PageHidden,
isNewSearchExperienceEnabled,
grpcEndpoints,
};
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);
return {
isApiPlaygroundEnabled: isDevelopment(domain),
isApiScrollingDisabled: false,
isWhitelabeled: false,
isSeoDisabled: !isCustomDomain(domain),
isTocDefaultEnabled: false,
isSnippetTemplatesEnabled: isDevelopment(domain),
isHttpSnippetsEnabled: false,
isInlineFeedbackEnabled: isFern(domain),
isDarkCodeEnabled: false,
proxyShouldUseAppBuildwithfernCom: false,
isImageZoomDisabled: false,
useJavaScriptAsTypeScript: false,
alwaysEnableJavaScriptFetch: false,
scrollInContainerEnabled: false,
useMdxBundler: false,
isBatchStreamToggleDisabled: false,
isAuthEnabledInDocs: false,
isAiChatbotEnabledInPreview: false,
isAudioFileDownloadSpanSummary: false,
isDocsLogoTextEnabled: false,
isAudioExampleInternal: false,
usesApplicationJsonInFormDataValue: false,
isBinaryOctetStreamAudioPlayer: false,
hasVoiceIdPlaygroundForm: false,
isCohereTheme: false,
isFileForgeHackEnabled: false,
is404PageHidden: false,
isNewSearchExperienceEnabled: false,
grpcEndpoints: [],
};
}
}

function checkDomainMatchesCustomers(domain: string, customers: readonly string[]): boolean {
if (customers == null) {
return false;
}
return customers.some((customer) => domain.toLowerCase().includes(customer.toLowerCase()));
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { verifyFernJWTConfig } from "@/server/auth/FernJWT";
import { getXFernHostEdge } from "@/server/xfernhost/edge";
import { getAuthEdgeConfig } from "@fern-ui/fern-docs-edge";
import { COOKIE_EMAIL, COOKIE_FERN_TOKEN } from "@fern-ui/fern-docs-utils";
import { get } from "@vercel/edge-config";
import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";

Expand All @@ -14,6 +18,19 @@ type LaunchDarklyEdgeConfigSchema = z.infer<typeof LaunchDarklyEdgeConfigSchema>

export default async function handler(req: NextRequest): Promise<NextResponse<LaunchDarklyEdgeConfigSchema>> {
const domain = getXFernHostEdge(req);
const jar = cookies();
let email = jar.get(COOKIE_EMAIL)?.value;
const fernToken = jar.get(COOKIE_FERN_TOKEN)?.value;

if (fernToken && !email) {
try {
const user = await verifyFernJWTConfig(fernToken, await getAuthEdgeConfig(domain));
email = user.email;
} catch (e) {
// do nothing
}
}

try {
const config = (await get<Record<string, LaunchDarklyEdgeConfigSchema>>("launchdarkly"))?.[domain];
if (config == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { COOKIE_EMAIL } from "@fern-ui/fern-docs-utils";
import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";

export const runtime = "edge";

export default async function handler(req: NextRequest): Promise<NextResponse> {
const email = req.nextUrl.searchParams.get(COOKIE_EMAIL);

if (email) {
cookies().set({ name: COOKIE_EMAIL, value: email });
}

return NextResponse.redirect(new URL("/", req.url));
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { signFernJWT } from "@/server/auth/FernJWT";
import { OAuth2Client } from "@/server/auth/OAuth2Client";
import { getAuthEdgeConfig } from "@/server/auth/getAuthEdgeConfig";
import { withSecureCookie } from "@/server/auth/withSecure";
import { COOKIE_ACCESS_TOKEN, COOKIE_FERN_TOKEN, COOKIE_REFRESH_TOKEN } from "@/server/constants";
import { getXFernHostEdge } from "@/server/xfernhost/edge";
import { getAuthEdgeConfig } from "@fern-ui/fern-docs-edge";
import { COOKIE_ACCESS_TOKEN, COOKIE_FERN_TOKEN, COOKIE_REFRESH_TOKEN } from "@fern-ui/fern-docs-utils";
import { FernUser, OryAccessTokenSchema } from "@fern-ui/ui/auth";
import { NextRequest, NextResponse } from "next/server";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getAuthEdgeConfig } from "@/server/auth/getAuthEdgeConfig";
import { withSecureCookie } from "@/server/auth/withSecure";
import { getXFernHostEdge } from "@/server/xfernhost/edge";
import { getAuthEdgeConfig } from "@fern-ui/fern-docs-edge";
import { NextRequest, NextResponse } from "next/server";
import { WebflowClient } from "webflow-api";

Expand Down
2 changes: 1 addition & 1 deletion packages/ui/docs-bundle/src/pages/api/fern-docs/preview.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { COOKIE_FERN_DOCS_PREVIEW } from "@/server/constants";
import { notFoundResponse, redirectResponse } from "@/server/serverResponse";
import { COOKIE_FERN_DOCS_PREVIEW } from "@fern-ui/fern-docs-utils";
import { NextRequest, NextResponse } from "next/server";

export const runtime = "edge";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { getXFernHostNode } from "@/server/xfernhost/node";
import { FdrAPI } from "@fern-api/fdr-sdk";
import * as FernNavigation from "@fern-api/fdr-sdk/navigation";
import { ApiDefinitionHolder } from "@fern-api/fdr-sdk/navigation";
import { getFeatureFlags } from "@fern-ui/fern-docs-edge";
import { ApiDefinitionResolver, provideRegistryService, type ResolvedRootPackage } from "@fern-ui/ui";
import { getMdxBundler } from "@fern-ui/ui/bundlers";
import { NextApiHandler, NextApiResponse } from "next";
import { getFeatureFlags } from "./feature-flags";

export const dynamic = "force-dynamic";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getSeoDisabled } from "@/server/disabledSeo";
import { getXFernHostEdge } from "@/server/xfernhost/edge";
import { getSeoDisabled } from "@fern-ui/fern-docs-edge";
import { NextRequest, NextResponse } from "next/server";
import urlJoin from "url-join";

Expand Down
2 changes: 1 addition & 1 deletion packages/ui/docs-bundle/src/pages/api/fern-docs/search.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { checkViewerAllowedEdge } from "@/server/auth/checkViewerAllowed";
import { getAuthEdgeConfig } from "@/server/auth/getAuthEdgeConfig";
import { loadWithUrl } from "@/server/loadWithUrl";
import { getXFernHostEdge } from "@/server/xfernhost/edge";
import { getAuthEdgeConfig } from "@fern-ui/fern-docs-edge";
import { SearchConfig, getSearchConfig } from "@fern-ui/search-utils";
import { provideRegistryService } from "@fern-ui/ui";
import { captureException } from "@sentry/nextjs";
Expand Down
Loading

0 comments on commit 424cdfb

Please sign in to comment.