mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 04:22:32 +10:00
chore: refactor
This commit is contained in:
@ -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>
|
||||
);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
});
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -9,5 +9,8 @@ export const getUserByEmail = async ({ email }: GetUserByEmailOptions) => {
|
||||
where: {
|
||||
email: email.toLowerCase(),
|
||||
},
|
||||
include: {
|
||||
VerificationToken: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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.';
|
||||
|
||||
|
||||
@ -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>;
|
||||
|
||||
Reference in New Issue
Block a user