chore: migrate /apps/[slug] page (#18848)
* migrate apps/[slug] page * remove future/apps/[slug]
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 <AppView {...props} />;
|
||||
}
|
||||
|
||||
export default Page;
|
||||
+22
-2
@@ -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, undefined>) => string,
|
||||
getDescription: (t: TFunction<string, undefined>) => 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],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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<PageProps>(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";
|
||||
@@ -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<Awaited<ReturnType<typeof getStaticProps>>>;
|
||||
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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<typeof getStaticProps>;
|
||||
|
||||
function SingleAppPage(props: PageProps) {
|
||||
function SingleAppPage(props: AppDataProps) {
|
||||
const { error, setQuery: setError } = useRouterQuery("error");
|
||||
const { t } = useLocale();
|
||||
if (error === "account_already_linked") {
|
||||
|
||||
@@ -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) => <SingleAppPage {...props} />;
|
||||
|
||||
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;
|
||||
@@ -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\]
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user