chore: refactor

This commit is contained in:
Catalin Pit
2024-01-30 12:54:48 +02:00
parent 1676f5bf6c
commit cc090adce0
8 changed files with 32 additions and 69 deletions

View File

@ -1,51 +1,8 @@
'use client';
import { useState } from 'react';
import { useSearchParams } from 'next/navigation';
import { Mails } from 'lucide-react';
import { ONE_SECOND } from '@documenso/lib/constants/time';
import { trpc } from '@documenso/trpc/react';
import { Button } from '@documenso/ui/primitives/button';
import { useToast } from '@documenso/ui/primitives/use-toast';
const RESEND_CONFIRMATION_EMAIL_TIMEOUT = 20 * ONE_SECOND;
import { SendConfirmationEmailForm } from '~/components/forms/send-confirmation-email';
export default function UnverifiedAccount() {
const [isButtonDisabled, setIsButtonDisabled] = useState(false);
const searchParams = useSearchParams();
const { toast } = useToast();
const encryptedEmail = searchParams?.get('token') ?? '';
const { mutateAsync: sendConfirmationEmail } = trpc.profile.sendConfirmationEmail.useMutation();
const onResendConfirmationEmail = async () => {
try {
setIsButtonDisabled(true);
await sendConfirmationEmail({ encryptedEmail });
toast({
title: 'Success',
description: 'Verification email sent successfully.',
duration: 5000,
});
setTimeout(() => setIsButtonDisabled(false), RESEND_CONFIRMATION_EMAIL_TIMEOUT);
} catch (err) {
setIsButtonDisabled(false);
toast({
title: 'Error',
description: 'Something went wrong while sending the confirmation email.',
variant: 'destructive',
});
}
};
return (
<div className="flex w-full items-start">
<div className="mr-4 mt-1 hidden md:block">
@ -55,13 +12,11 @@ export default function UnverifiedAccount() {
<h2 className="text-2xl font-bold md:text-4xl">Confirm email</h2>
<p className="text-muted-foreground mt-4">
To gain full access to your account and unlock all its features, please confirm your email
address by clicking on the link sent to your email address.
To gain access to your account, please confirm your email address by clicking on the
confirmation link from your inbox.
</p>
<Button className="mt-4" disabled={isButtonDisabled} onClick={onResendConfirmationEmail}>
Resend email
</Button>
<SendConfirmationEmailForm />
</div>
</div>
);

View File

@ -11,7 +11,6 @@ import { FcGoogle } from 'react-icons/fc';
import { z } from 'zod';
import { ErrorCode, isErrorCode } from '@documenso/lib/next-auth/error-codes';
import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@documenso/ui/primitives/dialog';
@ -62,8 +61,6 @@ export const SignInForm = ({ className, isGoogleSSOEnabled }: SignInFormProps) =
useState(false);
const router = useRouter();
const { mutateAsync: encryptSecondaryData } = trpc.crypto.encryptSecondaryData.useMutation();
const [twoFactorAuthenticationMethod, setTwoFactorAuthenticationMethod] = useState<
'totp' | 'backup'
>('totp');
@ -132,9 +129,12 @@ export const SignInForm = ({ className, isGoogleSSOEnabled }: SignInFormProps) =
const errorMessage = ERROR_MESSAGES[result.error];
if (result.error === ErrorCode.UNVERIFIED_EMAIL) {
const encryptedEmail = await encryptSecondaryData({ data: email });
router.push(`/unverified-account`);
router.push(`/unverified-account?token=${encryptedEmail}`);
toast({
title: 'Unable to sign in',
description: errorMessage ?? 'An unknown error occurred',
});
return;
}

View File

@ -62,19 +62,17 @@ export const SignUpForm = ({ className, isGoogleSSOEnabled }: SignUpFormProps) =
const isSubmitting = form.formState.isSubmitting;
const { mutateAsync: signup } = trpc.auth.signup.useMutation();
const { mutateAsync: encryptSecondaryData } = trpc.crypto.encryptSecondaryData.useMutation();
const onFormSubmit = async ({ name, email, password, signature }: TSignUpFormSchema) => {
try {
await signup({ name, email, password, signature });
const encryptedEmail = await encryptSecondaryData({ data: email });
router.push(`/unverified-account?token=${encryptedEmail}`);
router.push(`/unverified-account}`);
toast({
title: 'Registration Successful',
description: 'You have successfully registered. Please sign in to continue.',
description:
'You have successfully registered. Please verify your account by clicking on the link you received in the email.',
duration: 5000,
});

View File

@ -14,6 +14,7 @@ import { IdentityProvider } from '@documenso/prisma/client';
import { isTwoFactorAuthenticationEnabled } from '../server-only/2fa/is-2fa-availble';
import { validateTwoFactorAuthentication } from '../server-only/2fa/validate-2fa';
import { getUserByEmail } from '../server-only/user/get-user-by-email';
import { sendConfirmationToken } from '../server-only/user/send-confirmation-token';
import { ErrorCode } from './error-codes';
export const NEXT_AUTH_OPTIONS: AuthOptions = {
@ -71,6 +72,15 @@ export const NEXT_AUTH_OPTIONS: AuthOptions = {
}
if (!user.emailVerified) {
const totalUserVerificationTokens = user.VerificationToken.length;
const lastUserVerificationToken = user.VerificationToken[totalUserVerificationTokens - 1];
const expiredToken =
DateTime.fromJSDate(lastUserVerificationToken.expires) <= DateTime.now();
if (totalUserVerificationTokens < 1 || expiredToken) {
await sendConfirmationToken({ email });
}
throw new Error(ErrorCode.UNVERIFIED_EMAIL);
}

View File

@ -9,5 +9,8 @@ export const getUserByEmail = async ({ email }: GetUserByEmailOptions) => {
where: {
email: email.toLowerCase(),
},
include: {
VerificationToken: true,
},
});
};

View File

@ -20,6 +20,10 @@ export const sendConfirmationToken = async ({ email }: { email: string }) => {
throw new Error('User not found');
}
if (user.emailVerified) {
throw new Error('Email verified');
}
const createdToken = await prisma.verificationToken.create({
data: {
identifier: IDENTIFIER,

View File

@ -1,6 +1,5 @@
import { TRPCError } from '@trpc/server';
import { decryptSecondaryData } from '@documenso/lib/server-only/crypto/decrypt';
import { forgotPassword } from '@documenso/lib/server-only/user/forgot-password';
import { getUserById } from '@documenso/lib/server-only/user/get-user-by-id';
import { resetPassword } from '@documenso/lib/server-only/user/reset-password';
@ -118,15 +117,9 @@ export const profileRouter = router({
.input(ZConfirmEmailMutationSchema)
.mutation(async ({ input }) => {
try {
const { encryptedEmail } = input;
const { email } = input;
const decryptedEmail = decryptSecondaryData(encryptedEmail);
if (!decryptedEmail) {
throw new Error('Email is required');
}
return await sendConfirmationToken({ email: decryptedEmail });
return await sendConfirmationToken({ email });
} catch (err) {
let message = 'We were unable to send a confirmation email. Please try again.';

View File

@ -24,7 +24,7 @@ export const ZResetPasswordFormSchema = z.object({
});
export const ZConfirmEmailMutationSchema = z.object({
encryptedEmail: z.string().min(1),
email: z.string().email().min(1),
});
export type TRetrieveUserByIdQuerySchema = z.infer<typeof ZRetrieveUserByIdQuerySchema>;