From 213489c2026ba662e5c49a6e25b7028a97945dc2 Mon Sep 17 00:00:00 2001 From: zomars Date: Tue, 17 Jan 2023 16:18:54 -0700 Subject: [PATCH] Revert more stuff --- apps/auth/next.config.js | 5 +- apps/auth/pages/_app.tsx | 5 +- apps/auth/pages/auth/error.tsx | 57 ----- apps/auth/pages/auth/forgot-password/[id].tsx | 194 -------------- .../auth/pages/auth/forgot-password/index.tsx | 145 ----------- apps/auth/pages/auth/login.tsx | 239 ----------------- apps/auth/pages/auth/logout.tsx | 64 ----- apps/auth/pages/auth/new.tsx | 6 - apps/auth/pages/auth/setup/index.tsx | 70 ----- apps/auth/pages/auth/signin.tsx | 42 --- apps/auth/pages/auth/sso/[provider].tsx | 180 ------------- apps/auth/pages/auth/verify.tsx | 176 ------------- .../app-store/_utils/getInstalledAppPath.ts | 10 +- packages/features/auth/pages/error.tsx | 58 ----- .../auth/pages/forgot-password/[id].tsx | 195 -------------- .../auth/pages/forgot-password/index.tsx | 146 ----------- packages/features/auth/pages/login.tsx | 240 ------------------ packages/features/auth/pages/logout.tsx | 65 ----- packages/features/auth/pages/new.tsx | 6 - packages/features/auth/pages/setup/index.tsx | 70 ----- packages/features/auth/pages/signin.tsx | 42 --- .../features/auth/pages/sso/[provider].tsx | 180 ------------- packages/features/auth/pages/verify.tsx | 176 ------------- 23 files changed, 17 insertions(+), 2354 deletions(-) delete mode 100644 apps/auth/pages/auth/error.tsx delete mode 100644 apps/auth/pages/auth/forgot-password/[id].tsx delete mode 100644 apps/auth/pages/auth/forgot-password/index.tsx delete mode 100644 apps/auth/pages/auth/login.tsx delete mode 100644 apps/auth/pages/auth/logout.tsx delete mode 100644 apps/auth/pages/auth/new.tsx delete mode 100644 apps/auth/pages/auth/setup/index.tsx delete mode 100644 apps/auth/pages/auth/signin.tsx delete mode 100644 apps/auth/pages/auth/sso/[provider].tsx delete mode 100644 apps/auth/pages/auth/verify.tsx delete mode 100644 packages/features/auth/pages/error.tsx delete mode 100644 packages/features/auth/pages/forgot-password/[id].tsx delete mode 100644 packages/features/auth/pages/forgot-password/index.tsx delete mode 100644 packages/features/auth/pages/login.tsx delete mode 100644 packages/features/auth/pages/logout.tsx delete mode 100644 packages/features/auth/pages/new.tsx delete mode 100644 packages/features/auth/pages/setup/index.tsx delete mode 100644 packages/features/auth/pages/signin.tsx delete mode 100644 packages/features/auth/pages/sso/[provider].tsx delete mode 100644 packages/features/auth/pages/verify.tsx diff --git a/apps/auth/next.config.js b/apps/auth/next.config.js index ecdcc6eaec..6d7a25c423 100644 --- a/apps/auth/next.config.js +++ b/apps/auth/next.config.js @@ -3,17 +3,20 @@ const nextConfig = { reactStrictMode: true, swcMinify: true, transpilePackages: [ + "@calcom/app-store-cli", "@calcom/app-store", + "@calcom/config", "@calcom/core", "@calcom/dayjs", - "@calcom/emails", "@calcom/embed-core", "@calcom/embed-react", "@calcom/embed-snippet", "@calcom/features", + "@calcom/types", "@calcom/lib", "@calcom/prisma", "@calcom/trpc", + "@calcom/tsconfig", "@calcom/ui", ], }; diff --git a/apps/auth/pages/_app.tsx b/apps/auth/pages/_app.tsx index eed9c9b543..f69874acda 100644 --- a/apps/auth/pages/_app.tsx +++ b/apps/auth/pages/_app.tsx @@ -1,10 +1,13 @@ +import { TooltipProvider } from "@radix-ui/react-tooltip"; import { SessionProvider } from "next-auth/react"; import type { AppProps } from "next/app"; export default function App({ Component, pageProps: { session, ...pageProps } }: AppProps) { return ( - + + + ); } diff --git a/apps/auth/pages/auth/error.tsx b/apps/auth/pages/auth/error.tsx deleted file mode 100644 index 50d709b635..0000000000 --- a/apps/auth/pages/auth/error.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { GetStaticPropsContext } from "next"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import z from "zod"; - -import AuthContainer from "@calcom/features/auth/components/AuthContainer"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { ssgInit } from "@calcom/trpc/server/ssg"; -import { Button, Icon, SkeletonText } from "@calcom/ui"; - -const querySchema = z.object({ - error: z.string().optional(), -}); - -export default function Error() { - const { t } = useLocale(); - const router = useRouter(); - const { error } = querySchema.parse(router.query); - const isTokenVerificationError = error?.toLowerCase() === "verification"; - let errorMsg = ; - if (router.isReady) { - errorMsg = isTokenVerificationError ? t("token_invalid_expired") : t("error_during_login"); - } - - return ( - -
-
- -
-
- -
-

{errorMsg}

-
-
-
-
- - - -
-
- ); -} - -export const getStaticProps = async (context: GetStaticPropsContext) => { - const ssr = await ssgInit(context); - - return { - props: { - trpcState: ssr.dehydrate(), - }, - }; -}; diff --git a/apps/auth/pages/auth/forgot-password/[id].tsx b/apps/auth/pages/auth/forgot-password/[id].tsx deleted file mode 100644 index a4840b09b4..0000000000 --- a/apps/auth/pages/auth/forgot-password/[id].tsx +++ /dev/null @@ -1,194 +0,0 @@ -import { ResetPasswordRequest } from "@prisma/client"; -import debounce from "lodash/debounce"; -import { GetServerSidePropsContext } from "next"; -import { getCsrfToken } from "next-auth/react"; -import Link from "next/link"; -import React, { useMemo } from "react"; - -import dayjs from "@calcom/dayjs"; -import AuthContainer from "@calcom/features/auth/components/AuthContainer"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import prisma from "@calcom/prisma"; -import { Button, TextField } from "@calcom/ui"; - -type Props = { - id: string; - resetPasswordRequest: ResetPasswordRequest; - csrfToken: string; -}; - -export default function Page({ resetPasswordRequest, csrfToken }: Props) { - const { t } = useLocale(); - const [loading, setLoading] = React.useState(false); - const [error, setError] = React.useState<{ message: string } | null>(null); - const [success, setSuccess] = React.useState(false); - - const [password, setPassword] = React.useState(""); - - const submitChangePassword = async ({ password, requestId }: { password: string; requestId: string }) => { - try { - const res = await fetch("/api/auth/reset-password", { - method: "POST", - body: JSON.stringify({ requestId: requestId, password: password }), - headers: { - "Content-Type": "application/json", - }, - }); - - const json = await res.json(); - - if (!res.ok) { - setError(json); - } else { - setSuccess(true); - } - - return json; - } catch (reason) { - setError({ message: t("unexpected_error_try_again") }); - } finally { - setLoading(false); - } - }; - - const debouncedChangePassword = debounce(submitChangePassword, 250); - - const Success = () => { - return ( - <> -
-
-

- {t("password_updated")} -

-
- -
- - ); - }; - - const Expired = () => { - return ( - <> -
-
-

{t("whoops")}

-

{t("request_is_expired")}

-
-

{t("request_is_expired_instructions")}

- - - -
- - ); - }; - - const isRequestExpired = useMemo(() => { - const now = dayjs(); - return dayjs(resetPasswordRequest.expires).isBefore(now); - }, [resetPasswordRequest]); - - return ( - - {isRequestExpired && } - {!isRequestExpired && !success && ( - <> -
{ - e.preventDefault(); - - if (!password) { - return; - } - - if (loading) { - return; - } - - setLoading(true); - setError(null); - setSuccess(false); - - await debouncedChangePassword({ password, requestId: resetPasswordRequest.id }); - }} - action="#"> - -
- { - setPassword(e.target.value); - }} - id="password" - name="password" - type="password" - autoComplete="password" - required - /> -
- -
- -
-
- - )} - {!isRequestExpired && success && ( - <> - - - )} -
- ); -} - -export async function getServerSideProps(context: GetServerSidePropsContext) { - const id = context.params?.id as string; - - try { - const resetPasswordRequest = await prisma.resetPasswordRequest.findUniqueOrThrow({ - where: { - id, - }, - select: { - id: true, - expires: true, - }, - }); - - return { - props: { - resetPasswordRequest: { - ...resetPasswordRequest, - expires: resetPasswordRequest.expires.toString(), - }, - id, - csrfToken: await getCsrfToken({ req: context.req }), - }, - }; - } catch (reason) { - return { - notFound: true, - }; - } -} diff --git a/apps/auth/pages/auth/forgot-password/index.tsx b/apps/auth/pages/auth/forgot-password/index.tsx deleted file mode 100644 index ab849beeef..0000000000 --- a/apps/auth/pages/auth/forgot-password/index.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import debounce from "lodash/debounce"; -import { GetServerSidePropsContext } from "next"; -import { getCsrfToken } from "next-auth/react"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import React, { SyntheticEvent } from "react"; - -import AuthContainer from "@calcom/features/auth/components/AuthContainer"; -import { getSession } from "@calcom/features/auth/lib"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { Button, EmailField } from "@calcom/ui"; - -export default function ForgotPassword({ csrfToken }: { csrfToken: string }) { - const { t, i18n } = useLocale(); - const [loading, setLoading] = React.useState(false); - const [error, setError] = React.useState<{ message: string } | null>(null); - const [success, setSuccess] = React.useState(false); - const [email, setEmail] = React.useState(""); - const router = useRouter(); - - const handleChange = (e: SyntheticEvent) => { - const target = e.target as typeof e.target & { value: string }; - setEmail(target.value); - }; - - const submitForgotPasswordRequest = async ({ email }: { email: string }) => { - try { - const res = await fetch("/api/auth/forgot-password", { - method: "POST", - body: JSON.stringify({ email: email, language: i18n.language }), - headers: { - "Content-Type": "application/json", - }, - }); - - const json = await res.json(); - if (!res.ok) { - setError(json); - } else if ("resetLink" in json) { - router.push(json.resetLink); - } else { - setSuccess(true); - } - - return json; - } catch (reason) { - setError({ message: t("unexpected_error_try_again") }); - } finally { - setLoading(false); - } - }; - - const debouncedHandleSubmitPasswordRequest = debounce(submitForgotPasswordRequest, 250); - - const handleSubmit = async (e: SyntheticEvent) => { - e.preventDefault(); - - if (!email) { - return; - } - - if (loading) { - return; - } - - setLoading(true); - setError(null); - setSuccess(false); - - await debouncedHandleSubmitPasswordRequest({ email }); - }; - - const Success = () => { - return ( -
-

{t("password_reset_email", { email })}

-

{t("password_reset_leading")}

- {error &&

{error.message}

} - -
- ); - }; - - return ( - - - {t("back_to_signin")} - - - ) - }> - {success && } - {!success && ( - <> -
{error &&

{error.message}

}
-
- - -
- -
- - - )} -
- ); -} - -ForgotPassword.getInitialProps = async (context: GetServerSidePropsContext) => { - const { req, res } = context; - const session = await getSession({ req }); - - if (session) { - res.writeHead(302, { Location: "/" }); - res.end(); - return; - } - - return { - csrfToken: await getCsrfToken(context), - }; -}; diff --git a/apps/auth/pages/auth/login.tsx b/apps/auth/pages/auth/login.tsx deleted file mode 100644 index 52be8d1507..0000000000 --- a/apps/auth/pages/auth/login.tsx +++ /dev/null @@ -1,239 +0,0 @@ -import classNames from "classnames"; -import { GetServerSidePropsContext } from "next"; -import { getCsrfToken, signIn } from "next-auth/react"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import { useState } from "react"; -import { FormProvider, useForm } from "react-hook-form"; -import { FaGoogle } from "react-icons/fa"; - -import AuthContainer from "@calcom/features/auth/components/AuthContainer"; -import { SAMLLogin } from "@calcom/features/auth/components/SAMLLogin"; -import TwoFactor from "@calcom/features/auth/components/TwoFactor"; -import { ErrorCode, getSession } from "@calcom/features/auth/lib"; -import { isSAMLLoginEnabled, samlProductID, samlTenantID } from "@calcom/features/ee/sso/lib/saml"; -import { WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants"; -import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; -import prisma from "@calcom/prisma"; -import { ssrInit } from "@calcom/trpc/server/ssr"; -import { inferSSRProps } from "@calcom/types/inferSSRProps"; -import { AddToHomescreen, Alert, Button, EmailField, Icon, PasswordField } from "@calcom/ui"; -// TODO: Fix this import -import { IS_GOOGLE_LOGIN_ENABLED } from "@calcom/web/server/lib/constants"; - -interface LoginValues { - email: string; - password: string; - totpCode: string; - csrfToken: string; -} - -export default function Login({ - csrfToken, - isGoogleLoginEnabled, - isSAMLLoginEnabled, - samlTenantID, - samlProductID, -}: inferSSRProps) { - const { t } = useLocale(); - const router = useRouter(); - const methods = useForm(); - - const { register, formState } = methods; - - const [twoFactorRequired, setTwoFactorRequired] = useState(false); - const [errorMessage, setErrorMessage] = useState(null); - - const errorMessages: { [key: string]: string } = { - // [ErrorCode.SecondFactorRequired]: t("2fa_enabled_instructions"), - [ErrorCode.IncorrectPassword]: `${t("incorrect_password")} ${t("please_try_again")}`, - [ErrorCode.UserNotFound]: t("no_account_exists"), - [ErrorCode.IncorrectTwoFactorCode]: `${t("incorrect_2fa_code")} ${t("please_try_again")}`, - [ErrorCode.InternalServerError]: `${t("something_went_wrong")} ${t("please_try_again_and_contact_us")}`, - [ErrorCode.ThirdPartyIdentityProviderEnabled]: t("account_created_with_identity_provider"), - }; - - const telemetry = useTelemetry(); - - let callbackUrl = typeof router.query?.callbackUrl === "string" ? router.query.callbackUrl : ""; - - if (/"\//.test(callbackUrl)) callbackUrl = callbackUrl.substring(1); - - // If not absolute URL, make it absolute - if (!/^https?:\/\//.test(callbackUrl)) { - callbackUrl = `${WEBAPP_URL}/${callbackUrl}`; - } - - const safeCallbackUrl = getSafeRedirectUrl(callbackUrl); - - callbackUrl = safeCallbackUrl || ""; - - const LoginFooter = ( - - {t("dont_have_an_account")} - - ); - - const TwoFactorFooter = ( - - ); - - const onSubmit = async (values: LoginValues) => { - setErrorMessage(null); - telemetry.event(telemetryEventTypes.login, collectPageParameters()); - const res = await signIn<"credentials">("credentials", { - ...values, - callbackUrl, - redirect: false, - }); - if (!res) setErrorMessage(errorMessages[ErrorCode.InternalServerError]); - // we're logged in! let's do a hard refresh to the desired url - else if (!res.error) router.push(callbackUrl); - // reveal two factor input if required - else if (res.error === ErrorCode.SecondFactorRequired) setTwoFactorRequired(true); - // fallback if error not found - else setErrorMessage(errorMessages[res.error] || t("something_went_wrong")); - }; - - return ( - <> - - -
-
- -
-
-
- -
-
- - {t("forgot")} - -
- -
-
- - {twoFactorRequired && } - - {errorMessage && } - -
-
- {!twoFactorRequired && ( - <> - {(isGoogleLoginEnabled || isSAMLLoginEnabled) &&
} -
- {isGoogleLoginEnabled && ( - - )} - {isSAMLLoginEnabled && ( - - )} -
- - )} -
-
- - - ); -} - -export async function getServerSideProps(context: GetServerSidePropsContext) { - const { req } = context; - const session = await getSession({ req }); - const ssr = await ssrInit(context); - - if (session) { - return { - redirect: { - destination: "/", - permanent: false, - }, - }; - } - - const userCount = await prisma.user.count(); - if (userCount === 0) { - // Proceed to new onboarding to create first admin user - return { - redirect: { - destination: "/auth/setup", - permanent: false, - }, - }; - } - - return { - props: { - csrfToken: await getCsrfToken(context), - trpcState: ssr.dehydrate(), - isGoogleLoginEnabled: IS_GOOGLE_LOGIN_ENABLED, - isSAMLLoginEnabled, - samlTenantID, - samlProductID, - }, - }; -} diff --git a/apps/auth/pages/auth/logout.tsx b/apps/auth/pages/auth/logout.tsx deleted file mode 100644 index 0f75c48ed2..0000000000 --- a/apps/auth/pages/auth/logout.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { GetServerSidePropsContext } from "next"; -import { signOut, useSession } from "next-auth/react"; -import { useRouter } from "next/router"; -import { useEffect } from "react"; - -import AuthContainer from "@calcom/features/auth/components/AuthContainer"; -import { WEBSITE_URL } from "@calcom/lib/constants"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { ssrInit } from "@calcom/trpc/server/ssr"; -import { Button, Icon } from "@calcom/ui"; - -import { inferSSRProps } from "@lib/types/inferSSRProps"; - -type Props = inferSSRProps; - -export default function Logout(props: Props) { - const { status } = useSession(); - if (status === "authenticated") signOut({ redirect: false }); - const router = useRouter(); - useEffect(() => { - if (props.query?.survey === "true") { - router.push(`${WEBSITE_URL}/cancellation`); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.query?.survey]); - const { t } = useLocale(); - - return ( - -
-
- -
-
- -
-

{t("hope_to_see_you_soon")}

-
-
-
- -
- ); -} - -export async function getServerSideProps(context: GetServerSidePropsContext) { - const ssr = await ssrInit(context); - // Deleting old cookie manually, remove this code after all existing cookies have expired - context.res.setHeader( - "Set-Cookie", - "next-auth.session-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;" - ); - - return { - props: { - trpcState: ssr.dehydrate(), - query: context.query, - }, - }; -} diff --git a/apps/auth/pages/auth/new.tsx b/apps/auth/pages/auth/new.tsx deleted file mode 100644 index ea4eb23669..0000000000 --- a/apps/auth/pages/auth/new.tsx +++ /dev/null @@ -1,6 +0,0 @@ -export default function NewUserPage() { - if (typeof window !== "undefined") { - window.location.assign(process.env.NEXT_PUBLIC_WEBAPP_URL || "https://app.cal.com"); - } - return null; -} diff --git a/apps/auth/pages/auth/setup/index.tsx b/apps/auth/pages/auth/setup/index.tsx deleted file mode 100644 index 028764b490..0000000000 --- a/apps/auth/pages/auth/setup/index.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { UserPermissionRole } from "@prisma/client"; -import { GetServerSidePropsContext } from "next"; -import { useState } from "react"; - -import AdminAppsList from "@calcom/features/apps/AdminAppsList"; -import { getSession } from "@calcom/features/auth/lib"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import prisma from "@calcom/prisma"; -import { inferSSRProps } from "@calcom/types/inferSSRProps"; -import { WizardForm } from "@calcom/ui"; - -import SetupFormStep1 from "../../components/SetupFormStep1"; -import StepDone from "../../components/StepDone"; - -export default function Setup(props: inferSSRProps) { - const { t } = useLocale(); - const [isLoadingStep1, setIsLoadingStep1] = useState(false); - const shouldDisable = props.userCount !== 0; - - const steps = [ - { - title: t("administrator_user"), - description: t("lets_create_first_administrator_user"), - content: shouldDisable ? : , - isLoading: isLoadingStep1, - }, - { - title: t("enable_apps"), - description: t("enable_apps_description"), - content: , - isLoading: false, - }, - ]; - - return ( - <> -
- t("current_step_of_total", { currentStep, maxSteps })} - /> -
- - ); -} - -export const getServerSideProps = async (context: GetServerSidePropsContext) => { - const userCount = await prisma.user.count(); - const { req } = context; - const session = await getSession({ req }); - - if (session?.user.role && session?.user.role !== UserPermissionRole.ADMIN) { - return { - redirect: { - destination: `/404`, - permanent: false, - }, - }; - } - - return { - props: { - userCount, - }, - }; -}; diff --git a/apps/auth/pages/auth/signin.tsx b/apps/auth/pages/auth/signin.tsx deleted file mode 100644 index 0d0e78f173..0000000000 --- a/apps/auth/pages/auth/signin.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { GetServerSidePropsContext } from "next"; -import { getProviders, signIn, getSession, getCsrfToken } from "next-auth/react"; - -import { Button } from "@calcom/ui"; - -type Provider = { - name: string; - id: string; -}; - -function signin({ providers }: { providers: Provider[] }) { - return ( -
- {Object.values(providers).map((provider) => { - return ( -
- -
- ); - })} -
- ); -} - -export default signin; - -export async function getServerSideProps(context: GetServerSidePropsContext) { - const session = await getSession(context); - const csrfToken = await getCsrfToken(context); - const providers = await getProviders(); - if (session) { - return { - redirect: { destination: "/" }, - }; - } - return { - props: { - csrfToken, - providers, - }, - }; -} diff --git a/apps/auth/pages/auth/sso/[provider].tsx b/apps/auth/pages/auth/sso/[provider].tsx deleted file mode 100644 index 9adbc85ef8..0000000000 --- a/apps/auth/pages/auth/sso/[provider].tsx +++ /dev/null @@ -1,180 +0,0 @@ -import { GetServerSidePropsContext } from "next"; -import { signIn } from "next-auth/react"; -import { useRouter } from "next/router"; -import { useEffect } from "react"; -import { z } from "zod"; - -import { getPremiumMonthlyPlanPriceId } from "@calcom/app-store/stripepayment/lib/utils"; -import stripe from "@calcom/features/ee/payments/server/stripe"; -import { - hostedCal, - isSAMLLoginEnabled, - samlProductID, - samlTenantID, - samlTenantProduct, -} from "@calcom/features/ee/sso/lib/saml"; -import { checkUsername } from "@calcom/lib/server/checkUsername"; -import prisma from "@calcom/prisma"; -// TODO: Fix this import -import { ssrInit } from "@calcom/trpc/server/ssr"; -import { inferSSRProps } from "@calcom/types/inferSSRProps"; - -import { getSession } from "../../lib"; - -type SSOProviderPageProps = inferSSRProps; - -export default function Provider(props: SSOProviderPageProps) { - const router = useRouter(); - - useEffect(() => { - if (props.provider === "saml") { - const email = typeof router.query?.email === "string" ? router.query?.email : null; - - if (!email) { - router.push("/auth/error?error=" + "Email not provided"); - return; - } - - if (!props.isSAMLLoginEnabled) { - router.push("/auth/error?error=" + "SAML login not enabled"); - return; - } - - signIn("saml", {}, { tenant: props.tenant, product: props.product }); - } else { - signIn(props.provider); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return null; -} - -const querySchema = z.object({ - provider: z.union([z.string(), z.null()]).optional().default(null), - email: z.union([z.string(), z.null()]).optional().default(null), - username: z.union([z.string(), z.null()]).optional().default(null), -}); - -export const getServerSideProps = async (context: GetServerSidePropsContext) => { - const { - provider: providerParam, - email: emailParam, - username: usernameParam, - } = querySchema.parse(context.query); - const successDestination = "/getting-started" + (usernameParam ? `?username=${usernameParam}` : ""); - if (!providerParam) { - throw new Error(`File is not named sso/[provider]`); - } - - const { req } = context; - - const session = await getSession({ req }); - const ssr = await ssrInit(context); - - if (session) { - // Validating if username is Premium, while this is true an email its required for stripe user confirmation - if (usernameParam && session.user.email) { - const availability = await checkUsername(usernameParam); - if (availability.available && availability.premium) { - const stripePremiumUrl = await getStripePremiumUsernameUrl({ - userEmail: session.user.email, - username: usernameParam, - successDestination, - }); - if (stripePremiumUrl) { - return { - redirect: { - destination: stripePremiumUrl, - permanent: false, - }, - }; - } - } - } - - return { - redirect: { - destination: successDestination, - permanent: false, - }, - }; - } - - let error: string | null = null; - - let tenant = samlTenantID; - let product = samlProductID; - - if (providerParam === "saml" && hostedCal) { - if (!emailParam) { - error = "Email not provided"; - } else { - try { - const ret = await samlTenantProduct(prisma, emailParam); - tenant = ret.tenant; - product = ret.product; - } catch (e: any) { - error = e.message; - } - } - } - - if (error) { - return { - redirect: { - destination: "/auth/error?error=" + error, - permanent: false, - }, - }; - } - - return { - props: { - trpcState: ssr.dehydrate(), - provider: providerParam, - isSAMLLoginEnabled, - hostedCal, - tenant, - product, - error, - }, - }; -}; - -type GetStripePremiumUsernameUrl = { - userEmail: string; - username: string; - successDestination: string; -}; - -const getStripePremiumUsernameUrl = async ({ - userEmail, - username, - successDestination, -}: GetStripePremiumUsernameUrl): Promise => { - // @TODO: probably want to check if stripe user email already exists? or not - const customer = await stripe.customers.create({ - email: userEmail, - metadata: { - email: userEmail, - username, - }, - }); - - const checkoutSession = await stripe.checkout.sessions.create({ - mode: "subscription", - payment_method_types: ["card"], - customer: customer.id, - line_items: [ - { - price: getPremiumMonthlyPlanPriceId(), - quantity: 1, - }, - ], - success_url: `${process.env.NEXT_PUBLIC_WEBAPP_URL}${successDestination}&session_id={CHECKOUT_SESSION_ID}`, - cancel_url: process.env.NEXT_PUBLIC_WEBAPP_URL || "https://app.cal.com", - allow_promotion_codes: true, - }); - - return checkoutSession.url; -}; diff --git a/apps/auth/pages/auth/verify.tsx b/apps/auth/pages/auth/verify.tsx deleted file mode 100644 index 21c3c6b1b2..0000000000 --- a/apps/auth/pages/auth/verify.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import { CheckIcon, ExclamationIcon, MailOpenIcon } from "@heroicons/react/outline"; -import { signIn } from "next-auth/react"; -import Head from "next/head"; -import { useRouter } from "next/router"; -import { useEffect, useRef, useState } from "react"; -import z from "zod"; - -import { APP_NAME, WEBAPP_URL } from "@calcom/lib/constants"; -import { trpc } from "@calcom/trpc/react"; -import { Button, Loader, showToast } from "@calcom/ui"; - -async function sendVerificationLogin(email: string, username: string) { - await signIn("email", { - email: email.toLowerCase(), - username: username.toLowerCase(), - redirect: false, - callbackUrl: WEBAPP_URL || "https://app.cal.com", - }) - .then(() => { - showToast("Verification email sent", "success"); - }) - .catch((err) => { - showToast(err, "error"); - }); -} - -function useSendFirstVerificationLogin({ - email, - username, -}: { - email: string | undefined; - username: string | undefined; -}) { - const sent = useRef(false); - useEffect(() => { - if (!email || !username || sent.current) { - return; - } - (async () => { - await sendVerificationLogin(email, username); - sent.current = true; - })(); - }, [email, username]); -} - -const querySchema = z.object({ - stripeCustomerId: z.string().optional(), - sessionId: z.string().optional(), - t: z.string().optional(), -}); - -export default function Verify() { - const router = useRouter(); - const { t, sessionId, stripeCustomerId } = querySchema.parse(router.query); - const [secondsLeft, setSecondsLeft] = useState(30); - const { data } = trpc.viewer.public.stripeCheckoutSession.useQuery({ - stripeCustomerId, - checkoutSessionId: sessionId, - }); - useSendFirstVerificationLogin({ email: data?.customer?.email, username: data?.customer?.username }); - // @note: check for t=timestamp and apply disabled state and secondsLeft accordingly - // to avoid refresh to skip waiting 30 seconds to re-send email - useEffect(() => { - const lastSent = new Date(parseInt(`${t}`)); - // @note: This double round() looks ugly but it's the only way I came up to get the time difference in seconds - const difference = Math.round(Math.round(new Date().getTime() - lastSent.getTime()) / 1000); - if (difference < 30) { - // If less than 30 seconds, set the seconds left to 30 - difference - setSecondsLeft(30 - difference); - } else { - // else set the seconds left to 0 and disabled false - setSecondsLeft(0); - } - }, [t]); - // @note: here we make sure each second is decremented if disabled up to 0. - useEffect(() => { - if (secondsLeft > 0) { - const interval = setInterval(() => { - if (secondsLeft > 0) { - setSecondsLeft(secondsLeft - 1); - } - }, 1000); - return () => clearInterval(interval); - } - }, [secondsLeft]); - - if (!router.isReady || !data) { - // Loading state - return ; - } - const { valid, hasPaymentFailed, customer } = data; - if (!valid) { - throw new Error("Invalid session or customer id"); - } - - if (!stripeCustomerId && !sessionId) { - return
Invalid Link
; - } - - return ( -
- - - {/* @note: Ternary can look ugly ant his might be extracted later but I think at 3 it's not yet worth - it or too hard to read. */} - {hasPaymentFailed - ? "Your payment failed" - : sessionId - ? "Payment successful!" - : "Verify your email" + " | " + APP_NAME} - - -
-
-
- {hasPaymentFailed ? ( - - ) : sessionId ? ( - - ) : ( - - )} -
-

- {hasPaymentFailed - ? "Your payment failed" - : sessionId - ? "Payment successful!" - : "Check your Inbox"} -

- {hasPaymentFailed && ( -

Your account has been created, but your premium has not been reserved.

- )} -

- We have sent an email to {customer?.email} with a link to activate your account.{" "} - {hasPaymentFailed && - "Once you activate your account you will be able to try purchase your premium username again or select a different one."} -

-

- Don't see an email? Click the button below to send another email. -

- -
- - -
-
-
-
- ); -} diff --git a/packages/app-store/_utils/getInstalledAppPath.ts b/packages/app-store/_utils/getInstalledAppPath.ts index f6f8fd1511..cfc8cd9a7f 100644 --- a/packages/app-store/_utils/getInstalledAppPath.ts +++ b/packages/app-store/_utils/getInstalledAppPath.ts @@ -1,6 +1,14 @@ import z from "zod"; -import { InstalledAppVariants } from "../utils"; +const InstalledAppVariants = [ + "conferencing", + "calendar", + "payment", + "analytics", + "automation", + "other", + "web3", +] as const; const variantSchema = z.enum(InstalledAppVariants); diff --git a/packages/features/auth/pages/error.tsx b/packages/features/auth/pages/error.tsx deleted file mode 100644 index 76b26546aa..0000000000 --- a/packages/features/auth/pages/error.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { GetStaticPropsContext } from "next"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import z from "zod"; - -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { ssgInit } from "@calcom/trpc/server/ssg"; -import { Button, Icon, SkeletonText } from "@calcom/ui"; - -import AuthContainer from "../components/AuthContainer"; - -const querySchema = z.object({ - error: z.string().optional(), -}); - -export default function Error() { - const { t } = useLocale(); - const router = useRouter(); - const { error } = querySchema.parse(router.query); - const isTokenVerificationError = error?.toLowerCase() === "verification"; - let errorMsg = ; - if (router.isReady) { - errorMsg = isTokenVerificationError ? t("token_invalid_expired") : t("error_during_login"); - } - - return ( - -
-
- -
-
- -
-

{errorMsg}

-
-
-
-
- - - -
-
- ); -} - -export const getStaticProps = async (context: GetStaticPropsContext) => { - const ssr = await ssgInit(context); - - return { - props: { - trpcState: ssr.dehydrate(), - }, - }; -}; diff --git a/packages/features/auth/pages/forgot-password/[id].tsx b/packages/features/auth/pages/forgot-password/[id].tsx deleted file mode 100644 index 21c841079a..0000000000 --- a/packages/features/auth/pages/forgot-password/[id].tsx +++ /dev/null @@ -1,195 +0,0 @@ -import { ResetPasswordRequest } from "@prisma/client"; -import debounce from "lodash/debounce"; -import { GetServerSidePropsContext } from "next"; -import { getCsrfToken } from "next-auth/react"; -import Link from "next/link"; -import React, { useMemo } from "react"; - -import dayjs from "@calcom/dayjs"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import prisma from "@calcom/prisma"; -import { Button, TextField } from "@calcom/ui"; - -import AuthContainer from "../../components/AuthContainer"; - -type Props = { - id: string; - resetPasswordRequest: ResetPasswordRequest; - csrfToken: string; -}; - -export default function Page({ resetPasswordRequest, csrfToken }: Props) { - const { t } = useLocale(); - const [loading, setLoading] = React.useState(false); - const [error, setError] = React.useState<{ message: string } | null>(null); - const [success, setSuccess] = React.useState(false); - - const [password, setPassword] = React.useState(""); - - const submitChangePassword = async ({ password, requestId }: { password: string; requestId: string }) => { - try { - const res = await fetch("/api/auth/reset-password", { - method: "POST", - body: JSON.stringify({ requestId: requestId, password: password }), - headers: { - "Content-Type": "application/json", - }, - }); - - const json = await res.json(); - - if (!res.ok) { - setError(json); - } else { - setSuccess(true); - } - - return json; - } catch (reason) { - setError({ message: t("unexpected_error_try_again") }); - } finally { - setLoading(false); - } - }; - - const debouncedChangePassword = debounce(submitChangePassword, 250); - - const Success = () => { - return ( - <> -
-
-

- {t("password_updated")} -

-
- -
- - ); - }; - - const Expired = () => { - return ( - <> -
-
-

{t("whoops")}

-

{t("request_is_expired")}

-
-

{t("request_is_expired_instructions")}

- - - -
- - ); - }; - - const isRequestExpired = useMemo(() => { - const now = dayjs(); - return dayjs(resetPasswordRequest.expires).isBefore(now); - }, [resetPasswordRequest]); - - return ( - - {isRequestExpired && } - {!isRequestExpired && !success && ( - <> -
{ - e.preventDefault(); - - if (!password) { - return; - } - - if (loading) { - return; - } - - setLoading(true); - setError(null); - setSuccess(false); - - await debouncedChangePassword({ password, requestId: resetPasswordRequest.id }); - }} - action="#"> - -
- { - setPassword(e.target.value); - }} - id="password" - name="password" - type="password" - autoComplete="password" - required - /> -
- -
- -
-
- - )} - {!isRequestExpired && success && ( - <> - - - )} -
- ); -} - -export async function getServerSideProps(context: GetServerSidePropsContext) { - const id = context.params?.id as string; - - try { - const resetPasswordRequest = await prisma.resetPasswordRequest.findUniqueOrThrow({ - where: { - id, - }, - select: { - id: true, - expires: true, - }, - }); - - return { - props: { - resetPasswordRequest: { - ...resetPasswordRequest, - expires: resetPasswordRequest.expires.toString(), - }, - id, - csrfToken: await getCsrfToken({ req: context.req }), - }, - }; - } catch (reason) { - return { - notFound: true, - }; - } -} diff --git a/packages/features/auth/pages/forgot-password/index.tsx b/packages/features/auth/pages/forgot-password/index.tsx deleted file mode 100644 index c78c9d3225..0000000000 --- a/packages/features/auth/pages/forgot-password/index.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import debounce from "lodash/debounce"; -import { GetServerSidePropsContext } from "next"; -import { getCsrfToken } from "next-auth/react"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import React, { SyntheticEvent } from "react"; - -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { Button, EmailField } from "@calcom/ui"; - -import AuthContainer from "../../components/AuthContainer"; -import { getSession } from "../../lib"; - -export default function ForgotPassword({ csrfToken }: { csrfToken: string }) { - const { t, i18n } = useLocale(); - const [loading, setLoading] = React.useState(false); - const [error, setError] = React.useState<{ message: string } | null>(null); - const [success, setSuccess] = React.useState(false); - const [email, setEmail] = React.useState(""); - const router = useRouter(); - - const handleChange = (e: SyntheticEvent) => { - const target = e.target as typeof e.target & { value: string }; - setEmail(target.value); - }; - - const submitForgotPasswordRequest = async ({ email }: { email: string }) => { - try { - const res = await fetch("/api/auth/forgot-password", { - method: "POST", - body: JSON.stringify({ email: email, language: i18n.language }), - headers: { - "Content-Type": "application/json", - }, - }); - - const json = await res.json(); - if (!res.ok) { - setError(json); - } else if ("resetLink" in json) { - router.push(json.resetLink); - } else { - setSuccess(true); - } - - return json; - } catch (reason) { - setError({ message: t("unexpected_error_try_again") }); - } finally { - setLoading(false); - } - }; - - const debouncedHandleSubmitPasswordRequest = debounce(submitForgotPasswordRequest, 250); - - const handleSubmit = async (e: SyntheticEvent) => { - e.preventDefault(); - - if (!email) { - return; - } - - if (loading) { - return; - } - - setLoading(true); - setError(null); - setSuccess(false); - - await debouncedHandleSubmitPasswordRequest({ email }); - }; - - const Success = () => { - return ( -
-

{t("password_reset_email", { email })}

-

{t("password_reset_leading")}

- {error &&

{error.message}

} - -
- ); - }; - - return ( - - - {t("back_to_signin")} - - - ) - }> - {success && } - {!success && ( - <> -
{error &&

{error.message}

}
-
- - -
- -
- - - )} -
- ); -} - -ForgotPassword.getInitialProps = async (context: GetServerSidePropsContext) => { - const { req, res } = context; - const session = await getSession({ req }); - - if (session) { - res.writeHead(302, { Location: "/" }); - res.end(); - return; - } - - return { - csrfToken: await getCsrfToken(context), - }; -}; diff --git a/packages/features/auth/pages/login.tsx b/packages/features/auth/pages/login.tsx deleted file mode 100644 index 3477b3ca10..0000000000 --- a/packages/features/auth/pages/login.tsx +++ /dev/null @@ -1,240 +0,0 @@ -import classNames from "classnames"; -import { GetServerSidePropsContext } from "next"; -import { getCsrfToken, signIn } from "next-auth/react"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import { useState } from "react"; -import { FormProvider, useForm } from "react-hook-form"; -import { FaGoogle } from "react-icons/fa"; - -import { SAMLLogin } from "@calcom/features/auth/components/SAMLLogin"; -import { ErrorCode, getSession } from "@calcom/features/auth/lib"; -import { isSAMLLoginEnabled, samlProductID, samlTenantID } from "@calcom/features/ee/sso/lib/saml"; -import { WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants"; -import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry"; -import prisma from "@calcom/prisma"; -import { ssrInit } from "@calcom/trpc/server/ssr"; -import { inferSSRProps } from "@calcom/types/inferSSRProps"; -import { AddToHomescreen, Alert, Button, EmailField, Icon, PasswordField } from "@calcom/ui"; -// TODO: Fix this import -import { IS_GOOGLE_LOGIN_ENABLED } from "@calcom/web/server/lib/constants"; - -import AuthContainer from "../components/AuthContainer"; -import TwoFactor from "../components/TwoFactor"; - -interface LoginValues { - email: string; - password: string; - totpCode: string; - csrfToken: string; -} - -export default function Login({ - csrfToken, - isGoogleLoginEnabled, - isSAMLLoginEnabled, - samlTenantID, - samlProductID, -}: inferSSRProps) { - const { t } = useLocale(); - const router = useRouter(); - const methods = useForm(); - - const { register, formState } = methods; - - const [twoFactorRequired, setTwoFactorRequired] = useState(false); - const [errorMessage, setErrorMessage] = useState(null); - - const errorMessages: { [key: string]: string } = { - // [ErrorCode.SecondFactorRequired]: t("2fa_enabled_instructions"), - [ErrorCode.IncorrectPassword]: `${t("incorrect_password")} ${t("please_try_again")}`, - [ErrorCode.UserNotFound]: t("no_account_exists"), - [ErrorCode.IncorrectTwoFactorCode]: `${t("incorrect_2fa_code")} ${t("please_try_again")}`, - [ErrorCode.InternalServerError]: `${t("something_went_wrong")} ${t("please_try_again_and_contact_us")}`, - [ErrorCode.ThirdPartyIdentityProviderEnabled]: t("account_created_with_identity_provider"), - }; - - const telemetry = useTelemetry(); - - let callbackUrl = typeof router.query?.callbackUrl === "string" ? router.query.callbackUrl : ""; - - if (/"\//.test(callbackUrl)) callbackUrl = callbackUrl.substring(1); - - // If not absolute URL, make it absolute - if (!/^https?:\/\//.test(callbackUrl)) { - callbackUrl = `${WEBAPP_URL}/${callbackUrl}`; - } - - const safeCallbackUrl = getSafeRedirectUrl(callbackUrl); - - callbackUrl = safeCallbackUrl || ""; - - const LoginFooter = ( - - {t("dont_have_an_account")} - - ); - - const TwoFactorFooter = ( - - ); - - const onSubmit = async (values: LoginValues) => { - setErrorMessage(null); - telemetry.event(telemetryEventTypes.login, collectPageParameters()); - const res = await signIn<"credentials">("credentials", { - ...values, - callbackUrl, - redirect: false, - }); - if (!res) setErrorMessage(errorMessages[ErrorCode.InternalServerError]); - // we're logged in! let's do a hard refresh to the desired url - else if (!res.error) router.push(callbackUrl); - // reveal two factor input if required - else if (res.error === ErrorCode.SecondFactorRequired) setTwoFactorRequired(true); - // fallback if error not found - else setErrorMessage(errorMessages[res.error] || t("something_went_wrong")); - }; - - return ( - <> - - -
-
- -
-
-
- -
-
- - {t("forgot")} - -
- -
-
- - {twoFactorRequired && } - - {errorMessage && } - -
-
- {!twoFactorRequired && ( - <> - {(isGoogleLoginEnabled || isSAMLLoginEnabled) &&
} -
- {isGoogleLoginEnabled && ( - - )} - {isSAMLLoginEnabled && ( - - )} -
- - )} -
-
- - - ); -} - -export async function getServerSideProps(context: GetServerSidePropsContext) { - const { req } = context; - const session = await getSession({ req }); - const ssr = await ssrInit(context); - - if (session) { - return { - redirect: { - destination: "/", - permanent: false, - }, - }; - } - - const userCount = await prisma.user.count(); - if (userCount === 0) { - // Proceed to new onboarding to create first admin user - return { - redirect: { - destination: "/auth/setup", - permanent: false, - }, - }; - } - - return { - props: { - csrfToken: await getCsrfToken(context), - trpcState: ssr.dehydrate(), - isGoogleLoginEnabled: IS_GOOGLE_LOGIN_ENABLED, - isSAMLLoginEnabled, - samlTenantID, - samlProductID, - }, - }; -} diff --git a/packages/features/auth/pages/logout.tsx b/packages/features/auth/pages/logout.tsx deleted file mode 100644 index 31747b7694..0000000000 --- a/packages/features/auth/pages/logout.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { GetServerSidePropsContext } from "next"; -import { signOut, useSession } from "next-auth/react"; -import { useRouter } from "next/router"; -import { useEffect } from "react"; - -import { WEBSITE_URL } from "@calcom/lib/constants"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { ssrInit } from "@calcom/trpc/server/ssr"; -import { Button, Icon } from "@calcom/ui"; - -import { inferSSRProps } from "@lib/types/inferSSRProps"; - -import AuthContainer from "../components/AuthContainer"; - -type Props = inferSSRProps; - -export default function Logout(props: Props) { - const { status } = useSession(); - if (status === "authenticated") signOut({ redirect: false }); - const router = useRouter(); - useEffect(() => { - if (props.query?.survey === "true") { - router.push(`${WEBSITE_URL}/cancellation`); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [props.query?.survey]); - const { t } = useLocale(); - - return ( - -
-
- -
-
- -
-

{t("hope_to_see_you_soon")}

-
-
-
- -
- ); -} - -export async function getServerSideProps(context: GetServerSidePropsContext) { - const ssr = await ssrInit(context); - // Deleting old cookie manually, remove this code after all existing cookies have expired - context.res.setHeader( - "Set-Cookie", - "next-auth.session-token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;" - ); - - return { - props: { - trpcState: ssr.dehydrate(), - query: context.query, - }, - }; -} diff --git a/packages/features/auth/pages/new.tsx b/packages/features/auth/pages/new.tsx deleted file mode 100644 index ea4eb23669..0000000000 --- a/packages/features/auth/pages/new.tsx +++ /dev/null @@ -1,6 +0,0 @@ -export default function NewUserPage() { - if (typeof window !== "undefined") { - window.location.assign(process.env.NEXT_PUBLIC_WEBAPP_URL || "https://app.cal.com"); - } - return null; -} diff --git a/packages/features/auth/pages/setup/index.tsx b/packages/features/auth/pages/setup/index.tsx deleted file mode 100644 index 028764b490..0000000000 --- a/packages/features/auth/pages/setup/index.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { UserPermissionRole } from "@prisma/client"; -import { GetServerSidePropsContext } from "next"; -import { useState } from "react"; - -import AdminAppsList from "@calcom/features/apps/AdminAppsList"; -import { getSession } from "@calcom/features/auth/lib"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import prisma from "@calcom/prisma"; -import { inferSSRProps } from "@calcom/types/inferSSRProps"; -import { WizardForm } from "@calcom/ui"; - -import SetupFormStep1 from "../../components/SetupFormStep1"; -import StepDone from "../../components/StepDone"; - -export default function Setup(props: inferSSRProps) { - const { t } = useLocale(); - const [isLoadingStep1, setIsLoadingStep1] = useState(false); - const shouldDisable = props.userCount !== 0; - - const steps = [ - { - title: t("administrator_user"), - description: t("lets_create_first_administrator_user"), - content: shouldDisable ? : , - isLoading: isLoadingStep1, - }, - { - title: t("enable_apps"), - description: t("enable_apps_description"), - content: , - isLoading: false, - }, - ]; - - return ( - <> -
- t("current_step_of_total", { currentStep, maxSteps })} - /> -
- - ); -} - -export const getServerSideProps = async (context: GetServerSidePropsContext) => { - const userCount = await prisma.user.count(); - const { req } = context; - const session = await getSession({ req }); - - if (session?.user.role && session?.user.role !== UserPermissionRole.ADMIN) { - return { - redirect: { - destination: `/404`, - permanent: false, - }, - }; - } - - return { - props: { - userCount, - }, - }; -}; diff --git a/packages/features/auth/pages/signin.tsx b/packages/features/auth/pages/signin.tsx deleted file mode 100644 index 0d0e78f173..0000000000 --- a/packages/features/auth/pages/signin.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { GetServerSidePropsContext } from "next"; -import { getProviders, signIn, getSession, getCsrfToken } from "next-auth/react"; - -import { Button } from "@calcom/ui"; - -type Provider = { - name: string; - id: string; -}; - -function signin({ providers }: { providers: Provider[] }) { - return ( -
- {Object.values(providers).map((provider) => { - return ( -
- -
- ); - })} -
- ); -} - -export default signin; - -export async function getServerSideProps(context: GetServerSidePropsContext) { - const session = await getSession(context); - const csrfToken = await getCsrfToken(context); - const providers = await getProviders(); - if (session) { - return { - redirect: { destination: "/" }, - }; - } - return { - props: { - csrfToken, - providers, - }, - }; -} diff --git a/packages/features/auth/pages/sso/[provider].tsx b/packages/features/auth/pages/sso/[provider].tsx deleted file mode 100644 index 9adbc85ef8..0000000000 --- a/packages/features/auth/pages/sso/[provider].tsx +++ /dev/null @@ -1,180 +0,0 @@ -import { GetServerSidePropsContext } from "next"; -import { signIn } from "next-auth/react"; -import { useRouter } from "next/router"; -import { useEffect } from "react"; -import { z } from "zod"; - -import { getPremiumMonthlyPlanPriceId } from "@calcom/app-store/stripepayment/lib/utils"; -import stripe from "@calcom/features/ee/payments/server/stripe"; -import { - hostedCal, - isSAMLLoginEnabled, - samlProductID, - samlTenantID, - samlTenantProduct, -} from "@calcom/features/ee/sso/lib/saml"; -import { checkUsername } from "@calcom/lib/server/checkUsername"; -import prisma from "@calcom/prisma"; -// TODO: Fix this import -import { ssrInit } from "@calcom/trpc/server/ssr"; -import { inferSSRProps } from "@calcom/types/inferSSRProps"; - -import { getSession } from "../../lib"; - -type SSOProviderPageProps = inferSSRProps; - -export default function Provider(props: SSOProviderPageProps) { - const router = useRouter(); - - useEffect(() => { - if (props.provider === "saml") { - const email = typeof router.query?.email === "string" ? router.query?.email : null; - - if (!email) { - router.push("/auth/error?error=" + "Email not provided"); - return; - } - - if (!props.isSAMLLoginEnabled) { - router.push("/auth/error?error=" + "SAML login not enabled"); - return; - } - - signIn("saml", {}, { tenant: props.tenant, product: props.product }); - } else { - signIn(props.provider); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return null; -} - -const querySchema = z.object({ - provider: z.union([z.string(), z.null()]).optional().default(null), - email: z.union([z.string(), z.null()]).optional().default(null), - username: z.union([z.string(), z.null()]).optional().default(null), -}); - -export const getServerSideProps = async (context: GetServerSidePropsContext) => { - const { - provider: providerParam, - email: emailParam, - username: usernameParam, - } = querySchema.parse(context.query); - const successDestination = "/getting-started" + (usernameParam ? `?username=${usernameParam}` : ""); - if (!providerParam) { - throw new Error(`File is not named sso/[provider]`); - } - - const { req } = context; - - const session = await getSession({ req }); - const ssr = await ssrInit(context); - - if (session) { - // Validating if username is Premium, while this is true an email its required for stripe user confirmation - if (usernameParam && session.user.email) { - const availability = await checkUsername(usernameParam); - if (availability.available && availability.premium) { - const stripePremiumUrl = await getStripePremiumUsernameUrl({ - userEmail: session.user.email, - username: usernameParam, - successDestination, - }); - if (stripePremiumUrl) { - return { - redirect: { - destination: stripePremiumUrl, - permanent: false, - }, - }; - } - } - } - - return { - redirect: { - destination: successDestination, - permanent: false, - }, - }; - } - - let error: string | null = null; - - let tenant = samlTenantID; - let product = samlProductID; - - if (providerParam === "saml" && hostedCal) { - if (!emailParam) { - error = "Email not provided"; - } else { - try { - const ret = await samlTenantProduct(prisma, emailParam); - tenant = ret.tenant; - product = ret.product; - } catch (e: any) { - error = e.message; - } - } - } - - if (error) { - return { - redirect: { - destination: "/auth/error?error=" + error, - permanent: false, - }, - }; - } - - return { - props: { - trpcState: ssr.dehydrate(), - provider: providerParam, - isSAMLLoginEnabled, - hostedCal, - tenant, - product, - error, - }, - }; -}; - -type GetStripePremiumUsernameUrl = { - userEmail: string; - username: string; - successDestination: string; -}; - -const getStripePremiumUsernameUrl = async ({ - userEmail, - username, - successDestination, -}: GetStripePremiumUsernameUrl): Promise => { - // @TODO: probably want to check if stripe user email already exists? or not - const customer = await stripe.customers.create({ - email: userEmail, - metadata: { - email: userEmail, - username, - }, - }); - - const checkoutSession = await stripe.checkout.sessions.create({ - mode: "subscription", - payment_method_types: ["card"], - customer: customer.id, - line_items: [ - { - price: getPremiumMonthlyPlanPriceId(), - quantity: 1, - }, - ], - success_url: `${process.env.NEXT_PUBLIC_WEBAPP_URL}${successDestination}&session_id={CHECKOUT_SESSION_ID}`, - cancel_url: process.env.NEXT_PUBLIC_WEBAPP_URL || "https://app.cal.com", - allow_promotion_codes: true, - }); - - return checkoutSession.url; -}; diff --git a/packages/features/auth/pages/verify.tsx b/packages/features/auth/pages/verify.tsx deleted file mode 100644 index 21c3c6b1b2..0000000000 --- a/packages/features/auth/pages/verify.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import { CheckIcon, ExclamationIcon, MailOpenIcon } from "@heroicons/react/outline"; -import { signIn } from "next-auth/react"; -import Head from "next/head"; -import { useRouter } from "next/router"; -import { useEffect, useRef, useState } from "react"; -import z from "zod"; - -import { APP_NAME, WEBAPP_URL } from "@calcom/lib/constants"; -import { trpc } from "@calcom/trpc/react"; -import { Button, Loader, showToast } from "@calcom/ui"; - -async function sendVerificationLogin(email: string, username: string) { - await signIn("email", { - email: email.toLowerCase(), - username: username.toLowerCase(), - redirect: false, - callbackUrl: WEBAPP_URL || "https://app.cal.com", - }) - .then(() => { - showToast("Verification email sent", "success"); - }) - .catch((err) => { - showToast(err, "error"); - }); -} - -function useSendFirstVerificationLogin({ - email, - username, -}: { - email: string | undefined; - username: string | undefined; -}) { - const sent = useRef(false); - useEffect(() => { - if (!email || !username || sent.current) { - return; - } - (async () => { - await sendVerificationLogin(email, username); - sent.current = true; - })(); - }, [email, username]); -} - -const querySchema = z.object({ - stripeCustomerId: z.string().optional(), - sessionId: z.string().optional(), - t: z.string().optional(), -}); - -export default function Verify() { - const router = useRouter(); - const { t, sessionId, stripeCustomerId } = querySchema.parse(router.query); - const [secondsLeft, setSecondsLeft] = useState(30); - const { data } = trpc.viewer.public.stripeCheckoutSession.useQuery({ - stripeCustomerId, - checkoutSessionId: sessionId, - }); - useSendFirstVerificationLogin({ email: data?.customer?.email, username: data?.customer?.username }); - // @note: check for t=timestamp and apply disabled state and secondsLeft accordingly - // to avoid refresh to skip waiting 30 seconds to re-send email - useEffect(() => { - const lastSent = new Date(parseInt(`${t}`)); - // @note: This double round() looks ugly but it's the only way I came up to get the time difference in seconds - const difference = Math.round(Math.round(new Date().getTime() - lastSent.getTime()) / 1000); - if (difference < 30) { - // If less than 30 seconds, set the seconds left to 30 - difference - setSecondsLeft(30 - difference); - } else { - // else set the seconds left to 0 and disabled false - setSecondsLeft(0); - } - }, [t]); - // @note: here we make sure each second is decremented if disabled up to 0. - useEffect(() => { - if (secondsLeft > 0) { - const interval = setInterval(() => { - if (secondsLeft > 0) { - setSecondsLeft(secondsLeft - 1); - } - }, 1000); - return () => clearInterval(interval); - } - }, [secondsLeft]); - - if (!router.isReady || !data) { - // Loading state - return ; - } - const { valid, hasPaymentFailed, customer } = data; - if (!valid) { - throw new Error("Invalid session or customer id"); - } - - if (!stripeCustomerId && !sessionId) { - return
Invalid Link
; - } - - return ( -
- - - {/* @note: Ternary can look ugly ant his might be extracted later but I think at 3 it's not yet worth - it or too hard to read. */} - {hasPaymentFailed - ? "Your payment failed" - : sessionId - ? "Payment successful!" - : "Verify your email" + " | " + APP_NAME} - - -
-
-
- {hasPaymentFailed ? ( - - ) : sessionId ? ( - - ) : ( - - )} -
-

- {hasPaymentFailed - ? "Your payment failed" - : sessionId - ? "Payment successful!" - : "Check your Inbox"} -

- {hasPaymentFailed && ( -

Your account has been created, but your premium has not been reserved.

- )} -

- We have sent an email to {customer?.email} with a link to activate your account.{" "} - {hasPaymentFailed && - "Once you activate your account you will be able to try purchase your premium username again or select a different one."} -

-

- Don't see an email? Click the button below to send another email. -

- -
- - -
-
-
-
- ); -}