fix: New Booker Rewrites (#9557)

* Solve the problem using Next.config.js only

* Debug

* Avoid infinite rewritees

* Apply suggestions from code review

* Apply suggestions from code review

* Remove stale comments

* Update packages/embeds/embed-core/src/embed-iframe.ts
This commit is contained in:
Hariom Balhara authored and GitHub committed 2023-06-16 12:31:57 +02:00
1 parent 66ebf18fa9
commit 344241cfbf
5 files changed
+86 -77

No files matched your search

-18
View File
@@ -12,21 +12,10 @@ const middleware: NextMiddleware = async (req) => {
const url = req.nextUrl;
const requestHeaders = new Headers(req.headers);
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(req.headers.get("host") ?? "");
const isEmbedRequest = typeof url.searchParams.get("embed") === "string";
/**
* We are using env variable to toggle new-booker because using flags would be an unnecessary delay for booking pages
* Also, we can't easily identify the booker page requests here(to just fetch the flags for those requests)
*/
// Enable New Booker for All but embed Requests
if (process.env.NEW_BOOKER_ENABLED_FOR_NON_EMBED === "1" && !isEmbedRequest) {
requestHeaders.set("new-booker-enabled", "1");
}
// Enable New Booker for Embed Requests
if (process.env.NEW_BOOKER_ENABLED_FOR_EMBED === "1" && isEmbedRequest) {
requestHeaders.set("new-booker-enabled", "1");
}
// Make sure we are in the presence of an organization
if (isValidOrgDomain && url.pathname === "/") {
@@ -79,13 +68,6 @@ const middleware: NextMiddleware = async (req) => {
}
}
// Ensure that embed query param is there in when /embed is added.
// query param is the way in which client side code knows that it is in embed mode.
if (url.pathname.endsWith("/embed") && typeof url.searchParams.get("embed") !== "string") {
url.searchParams.set("embed", "");
return NextResponse.redirect(url);
}
// Don't 404 old routing_forms links
if (url.pathname.startsWith("/apps/routing_forms")) {
url.pathname = url.pathname.replace("/apps/routing_forms", "/apps/routing-forms");
+76 -54
View File
@@ -183,7 +183,12 @@ const nextConfig = {
return config;
},
async rewrites() {
return [
// .* matches / as well(Note: *(i.e wildcard) doesn't match / but .*(i.e. RegExp) does)
// It would match /free/30min but not /bookings/upcoming because 'bookings' is an item in pages
// It would also not match /free/30min/embed because we are ensuring just two slashes
const userTypeRouteRegExp = `/:user((?!${pages.join("|")})[^/]*)/:type`;
let rewrites = [
{
source: "/org/:slug",
destination: "/team/:slug",
@@ -219,68 +224,85 @@ const nextConfig = {
source: "/cancel/:path*",
destination: "/booking/:path*",
},
// Keep cookie based booker enabled just in case we disable new-booker globally
...[
{
source: userTypeRouteRegExp,
destination: "/new-booker/:user/:type",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type",
destination: "/new-booker/team/:slug/:type",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/d/:link/:slug",
destination: "/new-booker/d/:link/:slug",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
],
// Keep cookie based booker enabled to test new-booker embed in production
...[
{
source: `/:user((?!${pages.join("|")}).*)/:type/embed`,
destination: "/new-booker/:user/:type/embed",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type/embed",
destination: "/new-booker/team/:slug/:type/embed",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
],
/* TODO: have these files being served from another deployment or CDN {
source: "/embed/embed.js",
destination: process.env.NEXT_PUBLIC_EMBED_LIB_URL?,
}, */
/**
* Header allows us to enable new-booker using middleware which uses env variables to enable/disable new booker
*/
{
source: `/:user((?!${pages.join("|")}).*)/:type`,
destination: "/new-booker/:user/:type",
has: [{ type: "header", key: "new-booker-enabled" }],
},
{
source: `/:user((?!${pages.join("|")}).*)/:type/embed`,
destination: "/new-booker/:user/:type/embed",
has: [{ type: "header", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type",
destination: "/new-booker/team/:slug/:type",
has: [{ type: "header", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type/embed",
destination: "/new-booker/team/:slug/:type/embed",
has: [{ type: "header", key: "new-booker-enabled" }],
},
{
source: "/d/:link/:slug",
destination: "/new-booker/d/:link/:slug",
has: [{ type: "header", key: "new-booker-enabled" }],
},
/**
* Enables new booker using cookie. It works even if NEW_BOOKER_ENABLED_FOR_NON_EMBED, NEW_BOOKER_ENABLED_FOR_EMBED are disabled
*/
{
source: `/:user((?!${pages.join("|")}).*)/:type`,
destination: "/new-booker/:user/:type",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: `/:user((?!${pages.join("|")}).*)/:type/embed`,
destination: "/new-booker/:user/:type/embed",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type",
destination: "/new-booker/team/:slug/:type",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/team/:slug/:type/embed",
destination: "/new-booker/team/:slug/:type/embed",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
{
source: "/d/:link/:slug",
destination: "/new-booker/d/:link/:slug",
has: [{ type: "cookie", key: "new-booker-enabled" }],
},
];
// Enable New Booker for all Embed Requests
if (process.env.NEW_BOOKER_ENABLED_FOR_EMBED === "1") {
console.log("Enabling New Booker for Embed");
rewrites.push(
...[
{
source: `/:user((?!${pages.join("|")}).*)/:type/embed`,
destination: "/new-booker/:user/:type/embed",
},
{
source: "/team/:slug/:type/embed",
destination: "/new-booker/team/:slug/:type/embed",
},
]
);
}
// Enable New Booker for All but embed Requests
if (process.env.NEW_BOOKER_ENABLED_FOR_NON_EMBED === "1") {
console.log("Enabling New Booker for Non-Embed");
rewrites.push(
...[
{
source: userTypeRouteRegExp,
destination: "/new-booker/:user/:type",
},
{
source: "/team/:slug/:type",
destination: "/new-booker/team/:slug/:type",
},
{
source: "/d/:link/:slug",
destination: "/new-booker/d/:link/:slug",
},
]
);
}
return rewrites;
},
async headers() {
return [
@@ -5,7 +5,10 @@ export default function EmbedInitIframe() {
return;
}
const embedNameSpaceFromQueryParam = new URL(document.URL).searchParams.get("embed");
const url = new URL(document.URL);
const embedNameSpaceFromQueryParam = url.searchParams.get("embed");
const hasEmbedPath = url.pathname.endsWith("/embed");
const defaultNamespace = "";
// Namespace is initially set in query param `embed` but the query param might get lost during soft navigation
// So, we also check for the namespace in `window.name` which is set when iframe is created by embed.ts and persists for the duration of iframe's life
@@ -17,6 +20,8 @@ export default function EmbedInitIframe() {
? embedNameSpaceFromQueryParam
: window.name.includes("cal-embed=")
? window.name.replace(/cal-embed=(.*)/, "$1").trim()
: hasEmbedPath
? defaultNamespace // If query param is not there but /embed is used then assume default namespace.
: null;
window.isEmbed = () => {
@@ -2,7 +2,6 @@ import { useRouter } from "next/router";
import type { CSSProperties } from "react";
import { useState, useEffect } from "react";
import embedInit from "@calcom/embed-core/embed-iframe-init";
import type { BookerStore } from "@calcom/features/bookings/Booker/store";
import type { BookerLayouts } from "@calcom/prisma/zod-utils";
@@ -439,8 +438,6 @@ function keepParentInformedAboutDimensionChanges() {
if (isBrowser) {
log("Embed SDK loaded", { isEmbed: window?.isEmbed?.() || false });
// Exposes certain global variables/fns that are used by the app to get interface with the embed.
embedInit();
const url = new URL(document.URL);
embedStore.theme = window?.getEmbedTheme?.();
if (url.searchParams.get("prerender") !== "true" && window?.isEmbed?.()) {
+4 -1
View File
@@ -2,9 +2,12 @@
* @file
* This module is supposed to instantiate the SDK with appropriate namespace
*/
import embedInit from "@calcom/embed-core/embed-iframe-init";
import { SdkActionManager } from "./sdk-action-manager";
export let sdkActionManager: SdkActionManager | null = null;
if (typeof window !== "undefined") {
sdkActionManager = new SdkActionManager(new URL(document.URL).searchParams.get("embed"));
embedInit();
sdkActionManager = new SdkActionManager(window.getEmbedNamespace());
}