import type { IncomingMessage } from "http"; import { dir } from "i18next"; import type { NextPageContext } from "next"; import type { DocumentContext, DocumentProps } from "next/document"; import Document, { Head, Html, Main, NextScript } from "next/document"; import { z } from "zod"; import { IS_PRODUCTION } from "@calcom/lib/constants"; import { csp } from "@lib/csp"; type Props = Record & DocumentProps & { newLocale: string }; function setHeader(ctx: NextPageContext, name: string, value: string) { try { ctx.res?.setHeader(name, value); } catch (e) { // Getting "Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client" when revalidate calendar chache console.log(`Error setting header ${name}=${value} for ${ctx.asPath || "unknown asPath"}`, e); } } class MyDocument extends Document { static async getInitialProps(ctx: DocumentContext) { const { nonce } = csp(ctx.req || null, ctx.res || null); if (!process.env.CSP_POLICY) { setHeader(ctx, "x-csp", "not-opted-in"); } else if (!ctx.res?.getHeader("x-csp")) { // If x-csp not set by gSSP, then it's initialPropsOnly setHeader(ctx, "x-csp", "initialPropsOnly"); } const getLocaleModule = ctx.req ? await import("@calcom/features/auth/lib/getLocale") : null; const newLocale = ctx.req && getLocaleModule ? await getLocaleModule.getLocale(ctx.req as IncomingMessage & { cookies: Record }) : "en"; const asPath = ctx.asPath || ""; // Use a dummy URL as default so that URL parsing works for relative URLs as well. We care about searchParams and pathname only const parsedUrl = new URL(asPath, "https://dummyurl"); const isEmbedSnippetGeneratorPath = parsedUrl.pathname.startsWith("/event-types"); // FIXME: Revisit this logic to remove embedType query param check completely. Ideally, /embed should always be there at the end of the URL. Test properly and then remove it. const isEmbed = (parsedUrl.pathname.endsWith("/embed") || parsedUrl.searchParams.get("embedType") !== null) && !isEmbedSnippetGeneratorPath; const embedColorScheme = parsedUrl.searchParams.get("ui.color-scheme"); const initialProps = await Document.getInitialProps(ctx); return { isEmbed, embedColorScheme, nonce, ...initialProps, newLocale }; } render() { const { isEmbed, embedColorScheme } = this.props; const newLocale = this.props.newLocale || "en"; const newDir = dir(newLocale); const nonceParsed = z.string().safeParse(this.props.nonce); const nonce = nonceParsed.success ? nonceParsed.data : ""; return (