Skip to content

Commit

Permalink
Revert "chore: middleware routing" (#1449)
Browse files Browse the repository at this point in the history
  • Loading branch information
abvthecity authored Sep 11, 2024
1 parent 409b1c7 commit 36adbeb
Show file tree
Hide file tree
Showing 12 changed files with 291 additions and 200 deletions.
4 changes: 2 additions & 2 deletions packages/ui/app/src/atoms/store.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { createStore } from "jotai";
import { getDefaultStore } from "jotai";

export const store = createStore();
export const store = getDefaultStore();
10 changes: 1 addition & 9 deletions packages/ui/app/src/auth/FernJWT.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SignJWT, jwtVerify } from "jose";
import { AuthEdgeConfig, FernUser, FernUserSchema } from "./types";
import { FernUser, FernUserSchema } from "./types";

// "user" is reserved for workos

Expand All @@ -20,14 +20,6 @@ export async function verifyFernJWT(token: string, secret?: string, issuer?: str
return FernUserSchema.parse(verified.payload.fern);
}

export async function verifyFernJWTConfig(token: string, authConfig: AuthEdgeConfig | undefined): Promise<FernUser> {
if (authConfig?.type === "basic_token_verification") {
return verifyFernJWT(token, authConfig.secret, authConfig.issuer);
} else {
return verifyFernJWT(token);
}
}

const encoder = new TextEncoder();

function getJwtTokenSecret(secret?: string): Uint8Array {
Expand Down
49 changes: 22 additions & 27 deletions packages/ui/app/src/docs/NextApp.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { FernTooltipProvider, Toaster } from "@fern-ui/components";
import { EMPTY_OBJECT } from "@fern-ui/core-utils";
import { Provider as JotaiProvider } from "jotai";
import type { AppProps } from "next/app";
import { ReactElement } from "react";
import { SWRConfig } from "swr";
import { DocsProps, HydrateAtoms, store } from "../atoms";
import { DocsProps, HydrateAtoms } from "../atoms";
import { FernErrorBoundary } from "../components/FernErrorBoundary";
import { LocalPreviewContextProvider } from "../contexts/local-preview";
import "../css/globals.scss";
Expand All @@ -22,36 +21,32 @@ export function NextApp({ Component, pageProps, router }: AppProps<DocsProps | u
});

return (
<JotaiProvider store={store}>
<HydrateAtoms pageProps={pageProps}>
<ThemeScript colors={pageProps?.colors} />
<NextNProgress options={{ showSpinner: false, speed: 400 }} showOnShallow={false} />
<Toaster />
<FernTooltipProvider>
<SWRConfig value={{ fallback: pageProps?.fallback ?? EMPTY_OBJECT }}>
<FernErrorBoundary className="flex h-screen items-center justify-center" refreshOnError>
<Component {...pageProps} />
</FernErrorBoundary>
</SWRConfig>
</FernTooltipProvider>
</HydrateAtoms>
</JotaiProvider>
<HydrateAtoms pageProps={pageProps}>
<ThemeScript colors={pageProps?.colors} />
<NextNProgress options={{ showSpinner: false, speed: 400 }} showOnShallow={false} />
<Toaster />
<FernTooltipProvider>
<SWRConfig value={{ fallback: pageProps?.fallback ?? EMPTY_OBJECT }}>
<FernErrorBoundary className="flex h-screen items-center justify-center" refreshOnError>
<Component {...pageProps} />
</FernErrorBoundary>
</SWRConfig>
</FernTooltipProvider>
</HydrateAtoms>
);
}

// local preview doesn't use getServerSideProps, so pageProps is always undefined
export function LocalPreviewNextApp({ Component }: AppProps): ReactElement {
return (
<JotaiProvider store={store}>
<LocalPreviewContextProvider>
<NextNProgress options={{ showSpinner: false, speed: 400 }} showOnShallow={false} />
<Toaster />
<FernTooltipProvider>
<FernErrorBoundary className="flex h-screen items-center justify-center" refreshOnError>
<Component />
</FernErrorBoundary>
</FernTooltipProvider>
</LocalPreviewContextProvider>
</JotaiProvider>
<LocalPreviewContextProvider>
<NextNProgress options={{ showSpinner: false, speed: 400 }} showOnShallow={false} />
<Toaster />
<FernTooltipProvider>
<FernErrorBoundary className="flex h-screen items-center justify-center" refreshOnError>
<Component />
</FernErrorBoundary>
</FernTooltipProvider>
</LocalPreviewContextProvider>
);
}
189 changes: 189 additions & 0 deletions packages/ui/docs-bundle/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ const DOCS_FILES_ALLOWLIST = [
},
];

// const DOCS_FILES_URLS = DOCS_FILES_ALLOWLIST.map(
// ({ protocol, hostname, port }) => `${protocol}://${hostname}${port ? `:${port}` : ""}`,
// );

function isTruthy(value) {
if (value == null) {
return false;
Expand Down Expand Up @@ -59,6 +63,92 @@ const nextConfig = {
*/
assetPrefix: cdnUri != null ? cdnUri.href : undefined,
headers: async () => {
// const defaultSrc = ["'self'", "https://*.buildwithfern.com", "https://*.ferndocs.com", ...DOCS_FILES_URLS];

// const connectSrc = [
// "'self'",
// "https://*.buildwithfern.com",
// "https://*.ferndocs.com",
// "wss://websocket.proxy.ferndocs.com",
// "https://*.algolia.net",
// "https://*.algolianet.com",
// "https://*.algolia.io",
// "https://*.posthog.com",
// "https://cdn.segment.com",
// "https://api.segment.io",
// "wss://api.getkoala.com",
// "https://www.google-analytics.com",
// "https://*.intercom.io",
// "wss://*.intercom.io",
// "https://*.fullstory.com",
// ];

// const scriptSrc = [
// "'self'",
// "'unsafe-eval'",
// "'unsafe-inline'",
// "https://*.posthog.com",
// "https://cdn.segment.com",
// "https://www.googletagmanager.com",
// "https://*.intercomcdn.com",
// "https://*.intercom.io",
// "https://*.fullstory.com",
// ...DOCS_FILES_URLS,
// ];

// const styleSrc = ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net"];

// const fontSrc = ["'self'", "data:", "https://*.intercomcdn.com", ...DOCS_FILES_URLS];

// if (cdnUri != null) {
// scriptSrc.push(`${cdnUri.origin}`);
// connectSrc.push(`${cdnUri.origin}`);
// styleSrc.push(`${cdnUri.origin}`);
// }

// // enable vercel toolbar
// scriptSrc.push("https://vercel.live");
// connectSrc.push("https://vercel.live");
// connectSrc.push("wss://*.pusher.com");
// styleSrc.push("https://vercel.live");
// styleSrc.push("https://fonts.googleapis.com");

// const ContentSecurityPolicy = [
// `default-src ${defaultSrc.join(" ")}`,
// `script-src ${scriptSrc.join(" ")}`,
// `style-src ${styleSrc.join(" ")}`,
// "img-src 'self' https: blob: data:",
// `connect-src ${connectSrc.join(" ")}`,
// "frame-src 'self' https:",
// "object-src 'none'",
// "base-uri 'self'",
// "form-action 'self'",
// "frame-ancestors 'none'",
// `font-src ${fontSrc.join(" ")}`,
// // "upgrade-insecure-requests", <-- this is ignored because Report-Only mode is enabled
// ];

// BEGIN CSP REPORT SUPPRESSION
// CSP reports to sentry have been disabled because they often come from downstream custom js
// that we can't do much about. This results in a very expensive sentry bill for very little value or marginal security.
//
// const reportUri =
// "https://o4507138224160768.ingest.sentry.io/api/4507148139495424/security/?sentry_key=216ad381a8f652e036b1833af58627e5";
//
// const ReportTo = `{"group":"csp-endpoint","max_age":10886400,"endpoints":[{"url":"${reportUri}"}],"include_subdomains":true}`;

// ContentSecurityPolicy.push("worker-src 'self' blob:");

// ContentSecurityPolicy.push(`report-uri ${reportUri}`);
// ContentSecurityPolicy.push("report-to csp-endpoint");

// const ContentSecurityHeaders = [
// { key: "Content-Security-Policy-Report-Only", value: ContentSecurityPolicy.join("; ") },
// // { key: "Report-To", value: ReportTo },
// ];

// END CSP REPORT SUPPRESSION

const AccessControlHeaders = [
{
key: "Access-Control-Allow-Origin",
Expand Down Expand Up @@ -87,8 +177,107 @@ const nextConfig = {
source: "/:prefix*/api/fern-docs/auth/:path*",
headers: AccessControlHeaders,
},
// {
// source: "/:path*",
// headers: ContentSecurityHeaders,
// },
];
},
rewrites: async () => {
const HAS_FERN_DOCS_PREVIEW = { type: "cookie", key: "_fern_docs_preview", value: "(?<host>.*)" };
// const HAS_X_FORWARDED_HOST = { type: "header", key: "x-forwarded-host", value: "(?<host>.*)" };
const HAS_X_FERN_HOST = { type: "header", key: "x-fern-host", value: "(?<host>.*)" };
const HAS_HOST = { type: "host", value: "(?<host>.*)" };

// The order of the following array is important. The first match will be used.
const WITH_MATCHED_HOST = [HAS_FERN_DOCS_PREVIEW, HAS_X_FERN_HOST, HAS_HOST];

const HAS_FERN_TOKEN = { type: "cookie", key: "fern_token" };
const THREW_ERROR = { type: "query", key: "error", value: "true" };
return {
beforeFiles: [
/**
* while /_next/static routes are handled by the assetPrefix config, we need to handle the /_next/data routes separately
* when the user is hovering over a link, Next.js will prefetch the data route using `/_next/data` routes. We intercept
* the prefetch request at packages/ui/app/src/docs/NextApp.tsx and append the customer-defined basepath:
*
* i.e. /base/path/_next/data/*
*
* This rewrite rule will ensure that /base/path/_next/data/* is rewritten to /_next/data/* on the server
*/
{ 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
* when the fern_token cookie is present, the /static route will be rewritten to /dynamic
*/
{
source: "/_next/data/:hash/static/:host/:path*",
has: [HAS_FERN_TOKEN],
destination: "/_next/data/:hash/dynamic/:host/:path*",
},
/**
* This rewrite rule will ensure that when the `_fern_docs_preview` cookie is present, the /_next/data route will be
* rewritten to the host specified in the cookie. This is necessary for the PR Preview feature to work.
*/
{
source: "/_next/data/:hash/:subpath/:oldhost/:path*",
has: [HAS_FERN_DOCS_PREVIEW],
destination: "/_next/data/:hash/:subpath/:host/:path*",
},
],
afterFiles: [
{ source: "/_next/:path*", destination: "/_next/:path*" },
{ source: "/_vercel/:path*", destination: "/_vercel/:path*" },
{ source: "/robots.txt", destination: "/api/fern-docs/robots.txt" },
{ source: "/sitemap.xml", destination: "/api/fern-docs/sitemap.xml" },
{ source: "/:path*.rss", destination: "/api/fern-docs/changelog?format=rss&path=:path*" },
{ source: "/:path*.atom", destination: "/api/fern-docs/changelog?format=atom&path=:path*" },

// backwards compatibility with currently deployed FDR
{ source: "/api/revalidate-all", destination: "/api/fern-docs/revalidate-all" },
],
fallback: [
/**
* The following rewrite rules are used to determine if the path should be rewritten to /static or /dynamic
* On the presence of fern_token, or if the query contains error=true, the path will be rewritten to /dynamic
*/
...WITH_MATCHED_HOST.map((HOST_RULE) => ({
has: [HOST_RULE, HAS_FERN_TOKEN],
source: "/:path*",
destination: "/dynamic/:host/:path*",
})),
...WITH_MATCHED_HOST.map((HOST_RULE) => ({
has: [HOST_RULE, THREW_ERROR],
source: "/:path*",
destination: "/dynamic/:host/:path*",
})),
...WITH_MATCHED_HOST.map((HOST_RULE) => ({
has: [HOST_RULE],
source: "/:path*",
destination: "/static/:host/:path*",
})),
...WITH_MATCHED_HOST.map((HOST_RULE) => ({
has: [HOST_RULE, HAS_FERN_TOKEN],
source: "/",
destination: "/dynamic/:host/",
})),
...WITH_MATCHED_HOST.map((HOST_RULE) => ({
has: [HOST_RULE, THREW_ERROR],
source: "/",
destination: "/dynamic/:host/",
})),
...WITH_MATCHED_HOST.map((HOST_RULE) => ({
has: [HOST_RULE],
source: "/",
destination: "/static/:host/",
})),
],
};
},
images: {
remotePatterns: DOCS_FILES_ALLOWLIST,
path: cdnUri != null ? `${cdnUri.href}_next/image` : undefined,
Expand Down
Loading

0 comments on commit 36adbeb

Please sign in to comment.