Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

01 09 chore reuse get server side props #170

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
24 changes: 24 additions & 0 deletions apps/web/app/WithAppDirSsg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { GetStaticProps, GetStaticPropsContext } from "next";
import { notFound, redirect } from "next/navigation";

export const withAppDirSsg =
<T extends Record<string, any>>(getStaticProps: GetStaticProps<T>) =>
async (context: GetStaticPropsContext) => {
const ssgResponse = await getStaticProps(context);

if ("redirect" in ssgResponse) {
redirect(ssgResponse.redirect.destination);
}

if ("notFound" in ssgResponse) {
notFound();
}

const props = await Promise.resolve(ssgResponse.props);

return {
...ssgResponse.props,
// includes dehydratedState required for future page trpcPropvider
...("trpcState" in props && { dehydratedState: props.trpcState }),
};
};
11 changes: 7 additions & 4 deletions apps/web/app/AppDirSSRHOC.tsx → apps/web/app/WithAppDirSsr.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import type { GetServerSideProps, GetServerSidePropsContext } from "next";
import { notFound, redirect } from "next/navigation";

export const withAppDir =
export const withAppDirSsr =
<T extends Record<string, any>>(getServerSideProps: GetServerSideProps<T>) =>
async (context: GetServerSidePropsContext): Promise<T> => {
async (context: GetServerSidePropsContext) => {
const ssrResponse = await getServerSideProps(context);

if ("redirect" in ssrResponse) {
redirect(ssrResponse.redirect.destination);
}

if ("notFound" in ssrResponse) {
notFound();
}

const props = await Promise.resolve(ssrResponse.props);

return {
...ssrResponse.props,
...props,
// includes dehydratedState required for future page trpcPropvider
...("trpcState" in ssrResponse.props && { dehydratedState: ssrResponse.props.trpcState }),
...("trpcState" in props && { dehydratedState: props.trpcState }),
};
};
3 changes: 0 additions & 3 deletions apps/web/app/future/apps/[slug]/layout.tsx

This file was deleted.

121 changes: 23 additions & 98 deletions apps/web/app/future/apps/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,33 @@
import AppPage from "@pages/apps/[slug]/index";
import Page from "@pages/apps/[slug]/index";
import { Prisma } from "@prisma/client";
import { withAppDirSsg } from "app/WithAppDirSsg";
import { _generateMetadata } from "app/_utils";
import fs from "fs";
import matter from "gray-matter";
import { notFound } from "next/navigation";
import path from "path";
import { z } from "zod";
import { WithLayout } from "app/layoutHOC";
import type { InferGetStaticPropsType } from "next";
import { cookies, headers } from "next/headers";

import { getAppWithMetadata } from "@calcom/app-store/_appRegistry";
import { getAppAssetFullPath } from "@calcom/app-store/getAppAssetFullPath";
import { APP_NAME, IS_PRODUCTION } from "@calcom/lib/constants";
import { APP_NAME } from "@calcom/lib/constants";
import prisma from "@calcom/prisma";

const sourceSchema = z.object({
content: z.string(),
data: z.object({
description: z.string().optional(),
items: z
.array(
z.union([
z.string(),
z.object({
iframe: z.object({ src: z.string() }),
}),
])
)
.optional(),
}),
});
import { getStaticProps } from "@lib/apps/[slug]/getStaticProps";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";

export const generateMetadata = async ({ params }: { params: Record<string, string | string[]> }) => {
const { data } = await getPageProps({ params });
type Y = InferGetStaticPropsType<typeof getStaticProps>;
const getData = withAppDirSsg<Y>(getStaticProps);

export const generateMetadata = async ({
params,
searchParams,
}: {
params: Record<string, string | string[]>;
searchParams: { [key: string]: string | string[] | undefined };
}) => {
const legacyContext = buildLegacyCtx(headers(), cookies(), params, searchParams);
const res = await getData(legacyContext);

return await _generateMetadata(
() => `${data.name} | ${APP_NAME}`,
() => data.description
() => `${res?.data.name} | ${APP_NAME}`,
() => res?.data.description ?? ""
);
};

Expand All @@ -53,74 +46,6 @@ export const generateStaticParams = async () => {
return [];
};

const getPageProps = async ({ params }: { params: Record<string, string | string[]> }) => {
if (typeof params?.slug !== "string") {
notFound();
}

const appMeta = await getAppWithMetadata({
slug: params?.slug,
});

const appFromDb = await prisma.app.findUnique({
where: { slug: params.slug.toLowerCase() },
});

const isAppAvailableInFileSystem = appMeta;
const isAppDisabled = isAppAvailableInFileSystem && (!appFromDb || !appFromDb.enabled);

if (!IS_PRODUCTION && isAppDisabled) {
return {
isAppDisabled: true as const,
data: {
...appMeta,
},
};
}

if (!appFromDb || !appMeta || isAppDisabled) {
notFound();
}

const isTemplate = appMeta.isTemplate;
const appDirname = path.join(isTemplate ? "templates" : "", appFromDb.dirName);
const README_PATH = path.join(process.cwd(), "..", "..", `packages/app-store/${appDirname}/DESCRIPTION.md`);
const postFilePath = path.join(README_PATH);
let source = "";

try {
source = fs.readFileSync(postFilePath).toString();
source = source.replace(/{DESCRIPTION}/g, appMeta.description);
} catch (error) {
/* If the app doesn't have a README we fallback to the package description */
console.log(`No DESCRIPTION.md provided for: ${appDirname}`);
source = appMeta.description;
}

const result = matter(source);
const { content, data } = sourceSchema.parse({ content: result.content, data: result.data });
if (data.items) {
data.items = data.items.map((item) => {
if (typeof item === "string") {
return getAppAssetFullPath(item, {
dirName: appMeta.dirName,
isTemplate: appMeta.isTemplate,
});
}
return item;
});
}
return {
isAppDisabled: false as const,
source: { content, data },
data: appMeta,
};
};

export default async function Page({ params }: { params: Record<string, string | string[]> }) {
const pageProps = await getPageProps({ params });

return <AppPage {...pageProps} />;
}
export default WithLayout({ getLayout: null, Page, getData });

export const dynamic = "force-static";
29 changes: 7 additions & 22 deletions apps/web/app/future/apps/[slug]/setup/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import SetupPage from "@pages/apps/[slug]/setup";
import Page from "@pages/apps/[slug]/setup";
import { withAppDirSsr } from "app/WithAppDirSsr";
import { _generateMetadata } from "app/_utils";
import type { GetServerSidePropsContext } from "next";
import { cookies, headers } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { WithLayout } from "app/layoutHOC";
import type { InferGetServerSidePropsType } from "next";

import { getServerSideProps } from "@calcom/app-store/_pages/setup/_getServerSideProps";
import { APP_NAME } from "@calcom/lib/constants";
Expand All @@ -14,23 +14,8 @@ export const generateMetadata = async ({ params }: { params: Record<string, stri
);
};

const getPageProps = async ({ params }: { params: Record<string, string | string[]> }) => {
const req = { headers: headers(), cookies: cookies() };
type T = InferGetServerSidePropsType<typeof getServerSideProps>;

const result = await getServerSideProps({ params, req } as unknown as GetServerSidePropsContext);
const getData = withAppDirSsr<T>(getServerSideProps);

if (!result || "notFound" in result) {
notFound();
}

if ("redirect" in result) {
redirect(result.redirect.destination);
}

return result.props;
};

export default async function Page({ params }: { params: Record<string, string | string[]> }) {
const pageProps = await getPageProps({ params });
return <SetupPage {...pageProps} />;
}
export default WithLayout({ getLayout: null, Page, getData });
35 changes: 4 additions & 31 deletions apps/web/app/future/apps/categories/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import LegacyPage from "@pages/apps/categories/index";
import Page from "@pages/apps/categories/index";
import { withAppDirSsr } from "app/WithAppDirSsr";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import type { GetServerSidePropsContext } from "next";

import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { APP_NAME } from "@calcom/lib/constants";

import { ssrInit } from "@server/lib/ssr";
import { getServerSideProps } from "@lib/apps/categories/getServerSideProps";

export const generateMetadata = async () => {
return await _generateMetadata(
Expand All @@ -16,29 +14,4 @@ export const generateMetadata = async () => {
);
};

const getData = async (ctx: GetServerSidePropsContext) => {
const ssr = await ssrInit(ctx);

const session = await getServerSession({ req: ctx.req });

let appStore;
if (session?.user?.id) {
appStore = await getAppRegistryWithCredentials(session.user.id);
} else {
appStore = await getAppRegistry();
}

const categories = appStore.reduce((c, app) => {
for (const category of app.categories) {
c[category] = c[category] ? c[category] + 1 : 1;
}
return c;
}, {} as Record<string, number>);

return {
categories: Object.entries(categories).map(([name, count]) => ({ name, count })),
dehydratedState: ssr.dehydrate(),
};
};

export default WithLayout({ getData, Page: LegacyPage, getLayout: null })<"P">;
export default WithLayout({ getData: withAppDirSsr(getServerSideProps), Page, getLayout: null })<"P">;
3 changes: 0 additions & 3 deletions apps/web/app/future/apps/installed/[category]/layout.tsx

This file was deleted.

29 changes: 6 additions & 23 deletions apps/web/app/future/apps/installed/[category]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import LegacyPage from "@pages/apps/installed/[category]";
import Page from "@pages/apps/installed/[category]";
import { withAppDirSsr } from "app/WithAppDirSsr";
import { _generateMetadata } from "app/_utils";
import { notFound } from "next/navigation";
import { z } from "zod";
import { WithLayout } from "app/layoutHOC";

import { APP_NAME } from "@calcom/lib/constants";
import { AppCategories } from "@calcom/prisma/enums";

const querySchema = z.object({
category: z.nativeEnum(AppCategories),
});
import { getServerSideProps } from "@lib/apps/installed/[category]/getServerSideProps";

export const generateMetadata = async () => {
return await _generateMetadata(
Expand All @@ -17,20 +14,6 @@ export const generateMetadata = async () => {
);
};

const getPageProps = async ({ params }: { params: Record<string, string | string[]> }) => {
const p = querySchema.safeParse(params);
const getData = withAppDirSsr(getServerSideProps);

if (!p.success) {
return notFound();
}

return {
category: p.data.category,
};
};

export default async function Page({ params }: { params: Record<string, string | string[]> }) {
await getPageProps({ params });

return <LegacyPage />;
}
export default WithLayout({ getLayout: null, getData, Page });
51 changes: 3 additions & 48 deletions apps/web/app/future/apps/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import AppsPage from "@pages/apps";
import { withAppDirSsr } from "app/WithAppDirSsr";
import { _generateMetadata } from "app/_utils";
import { WithLayout } from "app/layoutHOC";
import type { GetServerSidePropsContext } from "next";

import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
import { getLayout } from "@calcom/features/MainLayoutAppDir";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import type { UserAdminTeams } from "@calcom/features/ee/teams/lib/getUserAdminTeams";
import getUserAdminTeams from "@calcom/features/ee/teams/lib/getUserAdminTeams";
import { APP_NAME } from "@calcom/lib/constants";
import type { AppCategories } from "@calcom/prisma/enums";

import { ssrInit } from "@server/lib/ssr";
import { getServerSideProps } from "@lib/apps/getServerSideProps";

export const generateMetadata = async () => {
return await _generateMetadata(
Expand All @@ -20,44 +15,4 @@ export const generateMetadata = async () => {
);
};

const getData = async (ctx: GetServerSidePropsContext) => {
const ssr = await ssrInit(ctx);

const session = await getServerSession({ req: ctx.req });

let appStore, userAdminTeams: UserAdminTeams;
if (session?.user?.id) {
userAdminTeams = await getUserAdminTeams({ userId: session.user.id, getUserInfo: true });
appStore = await getAppRegistryWithCredentials(session.user.id, userAdminTeams);
} else {
appStore = await getAppRegistry();
userAdminTeams = [];
}

const categoryQuery = appStore.map(({ categories }) => ({
categories: categories || [],
}));

const categories = categoryQuery.reduce((c, app) => {
for (const category of app.categories) {
c[category] = c[category] ? c[category] + 1 : 1;
}
return c;
}, {} as Record<string, number>);

return {
categories: Object.entries(categories)
.map(([name, count]): { name: AppCategories; count: number } => ({
name: name as AppCategories,
count,
}))
.sort(function (a, b) {
return b.count - a.count;
}),
appStore,
userAdminTeams,
dehydratedState: ssr.dehydrate(),
};
};

export default WithLayout({ getLayout, getData, Page: AppsPage });
export default WithLayout({ getLayout, getData: withAppDirSsr(getServerSideProps), Page: AppsPage });
Loading
Loading