From c40ff2e6bb33010323c71f573d4a4a04bfba8554 Mon Sep 17 00:00:00 2001 From: Peer Richelsen Date: Wed, 12 Oct 2022 13:52:59 +0100 Subject: [PATCH] added new license required /signup page (#4966) --- apps/web/next.config.js | 5 + apps/web/pages/auth/signup.tsx | 201 ------------------ apps/web/pages/signup.tsx | 194 +++++++++++++++++ packages/trpc/server/routers/viewer/teams.tsx | 2 +- 4 files changed, 200 insertions(+), 202 deletions(-) delete mode 100644 apps/web/pages/auth/signup.tsx create mode 100644 apps/web/pages/signup.tsx diff --git a/apps/web/next.config.js b/apps/web/next.config.js index f168334982..0c6542cfaf 100644 --- a/apps/web/next.config.js +++ b/apps/web/next.config.js @@ -154,6 +154,11 @@ const nextConfig = { destination: "/app-store/:path*", permanent: true, }, + { + source: "/auth/signup", + destination: "/signup", + permanent: true, + }, { source: "/settings", destination: "/settings/my-account/profile", diff --git a/apps/web/pages/auth/signup.tsx b/apps/web/pages/auth/signup.tsx deleted file mode 100644 index 7d4e96a4bf..0000000000 --- a/apps/web/pages/auth/signup.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import { GetServerSidePropsContext } from "next"; -import { signIn } from "next-auth/react"; -import { useRouter } from "next/router"; -import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; - -import prisma from "@calcom/prisma"; -import { Alert } from "@calcom/ui/Alert"; -import Button from "@calcom/ui/Button"; -import { EmailField, PasswordField, TextField } from "@calcom/ui/form/fields"; - -import { asStringOrNull } from "@lib/asStringOrNull"; -import { NEXT_PUBLIC_BASE_URL } from "@lib/config/constants"; -import { useLocale } from "@lib/hooks/useLocale"; -import { isSAMLLoginEnabled } from "@lib/saml"; -import { inferSSRProps } from "@lib/types/inferSSRProps"; - -import { HeadSeo } from "@components/seo/head-seo"; - -import { IS_GOOGLE_LOGIN_ENABLED } from "@server/lib/constants"; -import { ssrInit } from "@server/lib/ssr"; - -type Props = inferSSRProps; - -type FormValues = { - username: string; - email: string; - password: string; - passwordcheck: string; - apiError: string; -}; - -export default function Signup({ email }: Props) { - const { t } = useLocale(); - const router = useRouter(); - const methods = useForm(); - const { - register, - formState: { errors, isSubmitting }, - } = methods; - - methods.setValue("email", email); - - const handleErrors = async (resp: Response) => { - if (!resp.ok) { - const err = await resp.json(); - throw new Error(err.message); - } - }; - - const signUp: SubmitHandler = async (data) => { - await fetch("/api/auth/signup", { - body: JSON.stringify({ - ...data, - }), - headers: { - "Content-Type": "application/json", - }, - method: "POST", - }) - .then(handleErrors) - .then( - async () => - await signIn("Cal.com", { - callbackUrl: (`${NEXT_PUBLIC_BASE_URL}/${router.query.callbackUrl}` || "") as string, - }) - ) - .catch((err) => { - methods.setError("apiError", { message: err.message }); - }); - }; - - return ( -
- -
-

- {t("create_your_account")} -

-
-
-
- {/* TODO: Refactor as soon as /availability is live */} - -
- {errors.apiError && } -
- - {process.env.NEXT_PUBLIC_WEBSITE_URL}/ - - } - labelProps={{ className: "block text-sm font-medium text-gray-700" }} - className="block w-full min-w-0 flex-grow rounded-none rounded-r-sm border-gray-300 text-sm lowercase focus:border-black focus:ring-black" - {...register("username")} - required - /> - - - - value === methods.watch("password") || (t("error_password_mismatch") as string), - })} - className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-black focus:outline-none focus:ring-black" - /> -
-
- - -
- -
-
-
-
- ); -} - -export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { - const ssr = await ssrInit(ctx); - const token = asStringOrNull(ctx.query.token); - if (!token) { - return { - notFound: true, - }; - } - const verificationToken = await prisma.verificationToken.findUnique({ - where: { - token, - }, - }); - - // for now, disable if no verificationToken given or token expired - if (!verificationToken || verificationToken.expires < new Date()) { - return { - notFound: true, - }; - } - - const existingUser = await prisma.user.findFirst({ - where: { - AND: [ - { - email: verificationToken.identifier, - }, - { - emailVerified: { - not: null, - }, - }, - ], - }, - }); - - if (existingUser) { - return { - redirect: { - permanent: false, - destination: "/auth/login?callbackUrl=" + `${NEXT_PUBLIC_BASE_URL}/${ctx.query.callbackUrl}`, - }, - }; - } - - return { - props: { - isGoogleLoginEnabled: IS_GOOGLE_LOGIN_ENABLED, - isSAMLLoginEnabled, - email: verificationToken.identifier, - trpcState: ssr.dehydrate(), - }, - }; -}; diff --git a/apps/web/pages/signup.tsx b/apps/web/pages/signup.tsx new file mode 100644 index 0000000000..7a85866e27 --- /dev/null +++ b/apps/web/pages/signup.tsx @@ -0,0 +1,194 @@ +import type { GetServerSidePropsContext } from "next"; +import { signIn } from "next-auth/react"; +import { useRouter } from "next/router"; +import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; + +import LicenseRequired from "@calcom/features/ee/common/components/v2/LicenseRequired"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { inferSSRProps } from "@calcom/types/inferSSRProps"; +import { Alert } from "@calcom/ui/Alert"; +import Button from "@calcom/ui/v2/core/Button"; +import { EmailField, PasswordField, TextField } from "@calcom/ui/v2/core/form/fields"; +import { HeadSeo } from "@calcom/web/components/seo/head-seo"; +import { asStringOrNull } from "@calcom/web/lib/asStringOrNull"; +import { WEBAPP_URL } from "@calcom/web/lib/config/constants"; +import prisma from "@calcom/web/lib/prisma"; +import { isSAMLLoginEnabled } from "@calcom/web/lib/saml"; +import { IS_GOOGLE_LOGIN_ENABLED } from "@calcom/web/server/lib/constants"; +import { ssrInit } from "@calcom/web/server/lib/ssr"; + +type FormValues = { + username: string; + email: string; + password: string; + passwordcheck: string; + apiError: string; +}; + +export default function Signup({ prepopulateFormValues }: inferSSRProps) { + const { t } = useLocale(); + const router = useRouter(); + const methods = useForm({ + defaultValues: prepopulateFormValues, + }); + const { + register, + formState: { errors, isSubmitting }, + } = methods; + + const handleErrors = async (resp: Response) => { + if (!resp.ok) { + const err = await resp.json(); + throw new Error(err.message); + } + }; + + const signUp: SubmitHandler = async (data) => { + await fetch("/api/auth/signup", { + body: JSON.stringify({ + ...data, + }), + headers: { + "Content-Type": "application/json", + }, + method: "POST", + }) + .then(handleErrors) + .then( + async () => + await signIn("Cal.com", { + callbackUrl: (`${WEBAPP_URL}/${router.query.callbackUrl}` || "") as string, + }) + ) + .catch((err) => { + methods.setError("apiError", { message: err.message }); + }); + }; + + return ( + +
+ +
+

+ {t("create_your_account")} +

+
+
+
+ +
+ {errors.apiError && } +
+ + + + + value === methods.watch("password") || (t("error_password_mismatch") as string), + })} + /> +
+
+ + +
+ +
+
+
+
+
+ ); +} + +export const getServerSideProps = async (ctx: GetServerSidePropsContext) => { + const ssr = await ssrInit(ctx); + const token = asStringOrNull(ctx.query.token); + + const props = { + isGoogleLoginEnabled: IS_GOOGLE_LOGIN_ENABLED, + isSAMLLoginEnabled, + trpcState: ssr.dehydrate(), + prepopulateFormValues: undefined, + }; + + // no token given, treat as a normal signup without verification token + if (!token) { + return { + props, + }; + } + + const verificationToken = await prisma.verificationToken.findUnique({ + where: { + token, + }, + }); + + if (!verificationToken || verificationToken.expires < new Date()) { + return { + notFound: true, + }; + } + + const existingUser = await prisma.user.findFirst({ + where: { + AND: [ + { + email: verificationToken?.identifier, + }, + { + emailVerified: { + not: null, + }, + }, + ], + }, + }); + + if (existingUser) { + return { + redirect: { + permanent: false, + destination: "/auth/login?callbackUrl=" + `${WEBAPP_URL}/${ctx.query.callbackUrl}`, + }, + }; + } + + return { + props: { + ...props, + prepopulateFormValues: { + email: verificationToken.identifier, + }, + }, + }; +}; diff --git a/packages/trpc/server/routers/viewer/teams.tsx b/packages/trpc/server/routers/viewer/teams.tsx index 0ff27f690d..bf0104c675 100644 --- a/packages/trpc/server/routers/viewer/teams.tsx +++ b/packages/trpc/server/routers/viewer/teams.tsx @@ -302,7 +302,7 @@ export const viewerTeamsRouter = createProtectedRouter() from: ctx.user.name, to: input.usernameOrEmail, teamName: team.name, - joinLink: `${WEBAPP_URL}/auth/signup?token=${token}&callbackUrl=/settings/teams`, + joinLink: `${WEBAPP_URL}/signup?token=${token}&callbackUrl=/settings/teams`, }); } } else {