From beb542e7cbd44e3cdfe61d85fab49ac8d7177c6e Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Fri, 24 Jan 2025 14:57:52 +0000 Subject: [PATCH] chore: migrate `/apps/[slug]` page (#18848) * migrate apps/[slug] page * remove future/apps/[slug] --- .env.example | 1 - apps/web/abTest/middlewareFactory.ts | 1 - .../(use-page-wrapper)/apps/[slug]/page.tsx | 51 +++++++++++++++++++ apps/web/app/_utils.tsx | 24 ++++++++- apps/web/app/future/apps/[slug]/page.tsx | 45 ---------------- apps/web/lib/apps/[slug]/getStaticProps.tsx | 30 ++++------- apps/web/modules/apps/[slug]/slug-view.tsx | 10 +--- apps/web/pages/apps/[slug]/index.tsx | 39 -------------- apps/web/scripts/vercel-app-router-deploy.sh | 1 - turbo.json | 1 - 10 files changed, 86 insertions(+), 117 deletions(-) create mode 100644 apps/web/app/(use-page-wrapper)/apps/[slug]/page.tsx delete mode 100644 apps/web/app/future/apps/[slug]/page.tsx delete mode 100644 apps/web/pages/apps/[slug]/index.tsx diff --git a/.env.example b/.env.example index a3b4d7b290..20d9aba8f1 100644 --- a/.env.example +++ b/.env.example @@ -357,7 +357,6 @@ E2E_TEST_OIDC_USER_PASSWORD= # redirected from the legacy to the future pages AB_TEST_BUCKET_PROBABILITY=50 APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED=0 -APP_ROUTER_APPS_SLUG_ENABLED=0 APP_ROUTER_APPS_SLUG_SETUP_ENABLED=0 # whether we redirect to the future/apps/categories from /apps/categories or not APP_ROUTER_APPS_CATEGORIES_ENABLED=0 diff --git a/apps/web/abTest/middlewareFactory.ts b/apps/web/abTest/middlewareFactory.ts index 2c0d74bc86..59e53e43d5 100644 --- a/apps/web/abTest/middlewareFactory.ts +++ b/apps/web/abTest/middlewareFactory.ts @@ -6,7 +6,6 @@ import { FUTURE_ROUTES_ENABLED_COOKIE_NAME, FUTURE_ROUTES_OVERRIDE_COOKIE_NAME } const ROUTES: [URLPattern, boolean][] = [ ["/apps/installed/:category", process.env.APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED === "1"] as const, - ["/apps/:slug", process.env.APP_ROUTER_APPS_SLUG_ENABLED === "1"] as const, ["/apps/:slug/setup", process.env.APP_ROUTER_APPS_SLUG_SETUP_ENABLED === "1"] as const, ["/apps/categories", process.env.APP_ROUTER_APPS_CATEGORIES_ENABLED === "1"] as const, ["/apps/categories/:category", process.env.APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED === "1"] as const, diff --git a/apps/web/app/(use-page-wrapper)/apps/[slug]/page.tsx b/apps/web/app/(use-page-wrapper)/apps/[slug]/page.tsx new file mode 100644 index 0000000000..94b15fd498 --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/apps/[slug]/page.tsx @@ -0,0 +1,51 @@ +import type { PageProps as _PageProps } from "app/_types"; +import { generateAppMetadata } from "app/_utils"; +import { notFound } from "next/navigation"; +import { z } from "zod"; + +import { getStaticProps } from "@lib/apps/[slug]/getStaticProps"; + +import AppView from "~/apps/[slug]/slug-view"; + +const paramsSchema = z.object({ + slug: z.string(), +}); + +export const generateMetadata = async ({ params }: _PageProps) => { + const p = paramsSchema.safeParse(params); + + if (!p.success) { + return notFound(); + } + + const props = await getStaticProps(p.data.slug); + + if (!props) { + notFound(); + } + const { name, logo, description } = props.data; + + return await generateAppMetadata( + { slug: logo, name, description }, + () => name, + () => description + ); +}; + +async function Page({ params }: _PageProps) { + const p = paramsSchema.safeParse(params); + + if (!p.success) { + return notFound(); + } + + const props = await getStaticProps(p.data.slug); + + if (!props) { + notFound(); + } + + return ; +} + +export default Page; diff --git a/apps/web/app/_utils.tsx b/apps/web/app/_utils.tsx index dd05e0d3ae..acda1dd520 100644 --- a/apps/web/app/_utils.tsx +++ b/apps/web/app/_utils.tsx @@ -3,8 +3,8 @@ import i18next from "i18next"; import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import { headers } from "next/headers"; -import type { MeetingImageProps } from "@calcom/lib/OgImages"; -import { constructGenericImage, constructMeetingImage } from "@calcom/lib/OgImages"; +import type { AppImageProps, MeetingImageProps } from "@calcom/lib/OgImages"; +import { constructAppImage, constructGenericImage, constructMeetingImage } from "@calcom/lib/OgImages"; import { IS_CALCOM, WEBAPP_URL, APP_NAME, SEO_IMG_OGIMG, CAL_URL } from "@calcom/lib/constants"; import { buildCanonical } from "@calcom/lib/next-seo.config"; import { truncateOnWord } from "@calcom/lib/text"; @@ -123,3 +123,23 @@ export const generateMeetingMetadata = async ( }, }; }; + +export const generateAppMetadata = async ( + app: AppImageProps, + getTitle: (t: TFunction) => string, + getDescription: (t: TFunction) => string, + hideBranding?: boolean, + origin?: string +) => { + const metadata = await _generateMetadataWithoutImage(getTitle, getDescription, hideBranding, origin); + + const image = SEO_IMG_OGIMG + constructAppImage({ ...app, description: metadata.description }); + + return { + ...metadata, + openGraph: { + ...metadata.openGraph, + images: [image], + }, + }; +}; diff --git a/apps/web/app/future/apps/[slug]/page.tsx b/apps/web/app/future/apps/[slug]/page.tsx deleted file mode 100644 index 3c7f717544..0000000000 --- a/apps/web/app/future/apps/[slug]/page.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Prisma } from "@prisma/client"; -import { withAppDirSsg } from "app/WithAppDirSsg"; -import type { PageProps as _PageProps } from "app/_types"; -import { _generateMetadata } from "app/_utils"; -import { WithLayout } from "app/layoutHOC"; -import { cookies, headers } from "next/headers"; - -import { AppRepository } from "@calcom/lib/server/repository/app"; - -import { getStaticProps } from "@lib/apps/[slug]/getStaticProps"; -import { buildLegacyCtx } from "@lib/buildLegacyCtx"; - -import type { PageProps } from "~/apps/[slug]/slug-view"; -import Page from "~/apps/[slug]/slug-view"; - -const getData = withAppDirSsg(getStaticProps, "future/apps/[slug]"); - -export const generateMetadata = async ({ params, searchParams }: _PageProps) => { - const legacyContext = buildLegacyCtx(headers(), cookies(), params, searchParams); - const res = await getData(legacyContext); - - return await _generateMetadata( - () => res?.data.name ?? "", - () => res?.data.description ?? "" - ); -}; - -export const generateStaticParams = async () => { - try { - const appStore = await AppRepository.findAppStore(); - return appStore.map(({ slug }) => ({ slug })); - } catch (e: unknown) { - if (e instanceof Prisma.PrismaClientInitializationError) { - // Database is not available at build time, but that's ok – we fall back to resolving paths on demand - } else { - throw e; - } - } - - return []; -}; - -export default WithLayout({ getLayout: null, Page, getData }); - -export const dynamic = "force-static"; diff --git a/apps/web/lib/apps/[slug]/getStaticProps.tsx b/apps/web/lib/apps/[slug]/getStaticProps.tsx index 9113ba9c4e..61079a2834 100644 --- a/apps/web/lib/apps/[slug]/getStaticProps.tsx +++ b/apps/web/lib/apps/[slug]/getStaticProps.tsx @@ -1,7 +1,5 @@ import fs from "fs"; import matter from "gray-matter"; -import MarkdownIt from "markdown-it"; -import type { GetStaticPropsContext } from "next"; import path from "path"; import { z } from "zod"; @@ -10,8 +8,6 @@ import { getAppAssetFullPath } from "@calcom/app-store/getAppAssetFullPath"; import { IS_PRODUCTION } from "@calcom/lib/constants"; import prisma from "@calcom/prisma"; -const md = new MarkdownIt("default", { html: true, breaks: true }); - export const sourceSchema = z.object({ content: z.string(), data: z.object({ @@ -29,15 +25,15 @@ export const sourceSchema = z.object({ }), }); -export const getStaticProps = async (ctx: GetStaticPropsContext) => { - if (typeof ctx.params?.slug !== "string") return { notFound: true } as const; +export type AppDataProps = NonNullable>>; +export const getStaticProps = async (slug: string) => { const appMeta = await getAppWithMetadata({ - slug: ctx.params?.slug, + slug, }); const appFromDb = await prisma.app.findUnique({ - where: { slug: ctx.params.slug.toLowerCase() }, + where: { slug: slug.toLowerCase() }, }); const isAppAvailableInFileSystem = appMeta; @@ -45,16 +41,14 @@ export const getStaticProps = async (ctx: GetStaticPropsContext) => { if (!IS_PRODUCTION && isAppDisabled) { return { - props: { - isAppDisabled: true as const, - data: { - ...appMeta, - }, + isAppDisabled: true as const, + data: { + ...appMeta, }, }; } - if (!appFromDb || !appMeta || isAppDisabled) return { notFound: true } as const; + if (!appFromDb || !appMeta || isAppDisabled) return null; const isTemplate = appMeta.isTemplate; const appDirname = path.join(isTemplate ? "templates" : "", appFromDb.dirName); @@ -85,10 +79,8 @@ export const getStaticProps = async (ctx: GetStaticPropsContext) => { }); } return { - props: { - isAppDisabled: false as const, - source: { content, data }, - data: appMeta, - }, + isAppDisabled: false as const, + source: { content, data }, + data: appMeta, }; }; diff --git a/apps/web/modules/apps/[slug]/slug-view.tsx b/apps/web/modules/apps/[slug]/slug-view.tsx index 6331f81e40..b0faacc283 100644 --- a/apps/web/modules/apps/[slug]/slug-view.tsx +++ b/apps/web/modules/apps/[slug]/slug-view.tsx @@ -1,7 +1,5 @@ "use client"; -import MarkdownIt from "markdown-it"; -import type { InferGetStaticPropsType } from "next"; import Link from "next/link"; import { IS_PRODUCTION } from "@calcom/lib/constants"; @@ -9,16 +7,12 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import { showToast } from "@calcom/ui"; -import type { getStaticProps } from "@lib/apps/[slug]/getStaticProps"; +import type { AppDataProps } from "@lib/apps/[slug]/getStaticProps"; import useRouterQuery from "@lib/hooks/useRouterQuery"; import App from "@components/apps/App"; -const md = new MarkdownIt("default", { html: true, breaks: true }); - -export type PageProps = InferGetStaticPropsType; - -function SingleAppPage(props: PageProps) { +function SingleAppPage(props: AppDataProps) { const { error, setQuery: setError } = useRouterQuery("error"); const { t } = useLocale(); if (error === "account_already_linked") { diff --git a/apps/web/pages/apps/[slug]/index.tsx b/apps/web/pages/apps/[slug]/index.tsx deleted file mode 100644 index b3419ceb9b..0000000000 --- a/apps/web/pages/apps/[slug]/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Prisma } from "@prisma/client"; -import type { GetStaticPaths } from "next"; - -import { AppRepository } from "@calcom/lib/server/repository/app"; - -import { getStaticProps } from "@lib/apps/[slug]/getStaticProps"; - -import PageWrapper from "@components/PageWrapper"; - -import type { PageProps } from "~/apps/[slug]/slug-view"; -import SingleAppPage from "~/apps/[slug]/slug-view"; - -const Page = (props: PageProps) => ; - -export const getStaticPaths: GetStaticPaths<{ slug: string }> = async () => { - let paths: { params: { slug: string } }[] = []; - - try { - const appStore = await AppRepository.findAppStore(); - paths = appStore.map(({ slug }) => ({ params: { slug } })); - } catch (e: unknown) { - if (e instanceof Prisma.PrismaClientInitializationError) { - // Database is not available at build time, but that's ok – we fall back to resolving paths on demand - } else { - throw e; - } - } - - return { - paths, - fallback: "blocking", - }; -}; - -export { getStaticProps }; - -Page.PageWrapper = PageWrapper; - -export default Page; diff --git a/apps/web/scripts/vercel-app-router-deploy.sh b/apps/web/scripts/vercel-app-router-deploy.sh index 6600bf0780..444c8aae1b 100755 --- a/apps/web/scripts/vercel-app-router-deploy.sh +++ b/apps/web/scripts/vercel-app-router-deploy.sh @@ -7,7 +7,6 @@ checkRoute () { # These conditionals are used to remove directories from the build that are not needed in production # This is to reduce the size of the build and prevent OOM errors checkRoute "$APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED" app/future/apps/installed -checkRoute "$APP_ROUTER_APPS_SLUG_ENABLED" app/future/apps/\[slug\] checkRoute "$APP_ROUTER_APPS_SLUG_SETUP_ENABLED" app/future/apps/\[slug\]/setup checkRoute "$APP_ROUTER_APPS_CATEGORIES_ENABLED" app/future/apps/categories checkRoute "$APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED" app/future/apps/categories/\[category\] diff --git a/turbo.json b/turbo.json index a511a9b179..b0880ec5c4 100644 --- a/turbo.json +++ b/turbo.json @@ -243,7 +243,6 @@ "APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED", "APP_ROUTER_APPS_CATEGORIES_ENABLED", "APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED", - "APP_ROUTER_APPS_SLUG_ENABLED", "APP_ROUTER_APPS_SLUG_SETUP_ENABLED", "APP_ROUTER_AUTH_FORGOT_PASSWORD_ENABLED", "APP_ROUTER_AUTH_LOGIN_ENABLED",