fix: add layout and minor updates

This commit is contained in:
Mythie
2023-09-19 13:34:54 +00:00
parent 34c652ab54
commit 6e791b6e91
20 changed files with 217 additions and 289 deletions

View File

@ -14,18 +14,18 @@
"customizations": {
"vscode": {
"extensions": [
"GitHub.vscode-pull-request-github",
"GitHub.copilot-labs",
"GitHub.copilot-chat",
"GitHub.copilot",
"aaron-bond.better-comments",
"mikestead.dotenv",
"VisualStudioExptTeam.vscodeintellicode",
"Prisma.prisma",
"bradlc.vscode-tailwindcss",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"mikestead.dotenv",
"unifiedjs.vscode-mdx",
"GitHub.copilot-chat",
"GitHub.copilot-labs",
"GitHub.copilot",
"GitHub.vscode-pull-request-github",
"Prisma.prisma",
"VisualStudioExptTeam.vscodeintellicode",
"esbenp.prettier-vscode"
]
}
}

View File

@ -1,34 +1,20 @@
import Image from 'next/image';
import Link from 'next/link';
import backgroundPattern from '~/assets/background-pattern.png';
import { Button } from '@documenso/ui/primitives/button';
export default function ForgotPasswordPage() {
return (
<main className="bg-sand-100 relative flex min-h-screen flex-col items-center justify-center overflow-hidden px-4 py-12 md:p-12 lg:p-24">
<div className="relative flex w-1/5 items-center gap-x-24">
<div className="absolute -inset-96 -z-[1] flex items-center justify-center opacity-50">
<Image
src={backgroundPattern}
alt="background pattern"
className="dark:brightness-95 dark:invert dark:sepia"
/>
</div>
<div>
<h1 className="text-4xl font-semibold">Email sent!</h1>
<div className="w-full text-center">
<h1 className="text-4xl font-semibold">Reset Pasword</h1>
<p className="text-muted-foreground mb-4 mt-2 text-sm">
A password reset email has been sent, if you have an account you should see it in your inbox
shortly.
</p>
<p className="text-muted-foreground/60 mb-4 mt-2 text-sm">
Please check your email for reset instructions
</p>
<p className="text-muted-foreground mt-6 text-center text-sm">
<Link href="/signin" className="text-primary duration-200 hover:opacity-70">
Sign in
</Link>
</p>
</div>
</div>
</main>
<Button asChild>
<Link href="/signin">Return to sign in</Link>
</Button>
</div>
);
}

View File

@ -1,38 +1,25 @@
import Image from 'next/image';
import Link from 'next/link';
import backgroundPattern from '~/assets/background-pattern.png';
import { ForgotPasswordForm } from '~/components/forms/forgot-password';
export default function ForgotPasswordPage() {
return (
<main className="bg-sand-100 relative flex min-h-screen flex-col items-center justify-center overflow-hidden px-4 py-12 md:p-12 lg:p-24">
<div className="relative flex items-center gap-x-24 md:w-[500px]">
<div className="absolute -inset-96 -z-[1] flex items-center justify-center opacity-50">
<Image
src={backgroundPattern}
alt="background pattern"
className="dark:brightness-95 dark:invert dark:sepia"
/>
</div>
<div>
<h1 className="text-4xl font-semibold">Forgotten your password?</h1>
<div className="w-full">
<h1 className="text-4xl font-semibold">Forgot Password?</h1>
<p className="text-muted-foreground mt-2 text-sm">
No worries, it happens! Enter your email and we'll email you a special link to reset your
password.
</p>
<p className="text-muted-foreground/60 mt-2 text-sm">
No worries, we'll send you reset instructions.
</p>
<ForgotPasswordForm className="mt-4" />
<ForgotPasswordForm className="mt-4" />
<p className="text-muted-foreground mt-6 text-center text-sm">
Don't have an account?{' '}
<Link href="/signup" className="text-primary duration-200 hover:opacity-70">
Sign up
</Link>
</p>
</div>
</div>
</main>
<p className="text-muted-foreground mt-6 text-center text-sm">
Remembered your password?{' '}
<Link href="/signin" className="text-primary duration-200 hover:opacity-70">
Sign In
</Link>
</p>
</div>
);
}

View File

@ -0,0 +1,27 @@
import React from 'react';
import Image from 'next/image';
import backgroundPattern from '~/assets/background-pattern.png';
type UnauthenticatedLayoutProps = {
children: React.ReactNode;
};
export default function UnauthenticatedLayout({ children }: UnauthenticatedLayoutProps) {
return (
<main className="bg-sand-100 relative flex min-h-screen flex-col items-center justify-center overflow-hidden px-4 py-12 md:p-12 lg:p-24">
<div className="relative flex w-full max-w-md items-center gap-x-24">
<div className="absolute -inset-96 -z-[1] flex items-center justify-center opacity-50">
<Image
src={backgroundPattern}
alt="background pattern"
className="dark:brightness-95 dark:invert dark:sepia"
/>
</div>
<div className="w-full">{children}</div>
</div>
</main>
);
}

View File

@ -1,36 +1,37 @@
import Image from 'next/image';
import Link from 'next/link';
import { redirect } from 'next/navigation';
import { getResetTokenValidity } from '@documenso/lib/server-only/user/get-reset-token-validity';
import backgroundPattern from '~/assets/background-pattern.png';
import { ResetPasswordForm } from '~/components/forms/reset-password';
export default function ResetPasswordPage({ params }: { params: { token: string } }) {
type ResetPasswordPageProps = {
params: {
token: string;
};
};
export default async function ResetPasswordPage({ params: { token } }: ResetPasswordPageProps) {
const isValid = await getResetTokenValidity({ token });
if (!isValid) {
redirect('/reset-password');
}
return (
<main className="bg-sand-100 relative flex min-h-screen flex-col items-center justify-center overflow-hidden px-4 py-12 md:p-12 lg:p-24">
<div className="relative flex items-center gap-x-24 md:w-[500px]">
<div className="absolute -inset-96 -z-[1] flex items-center justify-center opacity-50">
<Image
src={backgroundPattern}
alt="background pattern"
className="dark:brightness-95 dark:invert dark:sepia"
/>
</div>
<div className="w-full">
<h1 className="text-4xl font-semibold">Reset Password</h1>
<div className="w-full">
<h1 className="text-4xl font-semibold">Reset Password</h1>
<p className="text-muted-foreground mt-2 text-sm">Please choose your new password </p>
<p className="text-muted-foreground/60 mt-2 text-sm">Please choose your new password </p>
<ResetPasswordForm token={token} className="mt-4" />
<ResetPasswordForm token={params.token} className="mt-4" />
<p className="text-muted-foreground mt-6 text-center text-sm">
Don't have an account?{' '}
<Link href="/signup" className="text-primary duration-200 hover:opacity-70">
Sign up
</Link>
</p>
</div>
</div>
</main>
<p className="text-muted-foreground mt-6 text-center text-sm">
Don't have an account?{' '}
<Link href="/signup" className="text-primary duration-200 hover:opacity-70">
Sign up
</Link>
</p>
</div>
);
}

View File

@ -1,34 +1,20 @@
import Image from 'next/image';
import Link from 'next/link';
import backgroundPattern from '~/assets/background-pattern.png';
import { Button } from '@documenso/ui/primitives/button';
export default function ResetPasswordPage() {
return (
<main className="bg-sand-100 relative flex min-h-screen flex-col items-center justify-center overflow-hidden px-4 py-12 md:p-12 lg:p-24">
<div className="relative flex items-center md:w-[500px]">
<div className="absolute -inset-96 -z-[1] flex items-center justify-center opacity-50">
<Image
src={backgroundPattern}
alt="background pattern"
className="dark:brightness-95 dark:invert dark:sepia"
/>
</div>
<div>
<h1 className="text-4xl font-semibold">Unable to reset password</h1>
<div className="w-full text-center">
<h1 className="text-4xl font-semibold">Reset Password</h1>
<p className="text-muted-foreground mt-2 text-sm">
The token you have used to reset your password is either expired or it never existed. If you
have still forgotten your password, please request a new reset link.
</p>
<p className="text-muted-foreground/60 mt-2 text-sm">
The token you provided is invalid. Please try again.
</p>
<p className="text-muted-foreground mt-6 text-center text-sm">
<Link href="/signup" className="text-primary duration-200 hover:opacity-70">
Sign in
</Link>
</p>
</div>
</div>
</main>
<Button className="mt-4" asChild>
<Link href="/signin">Return to sign in</Link>
</Button>
</div>
);
}

View File

@ -1,43 +1,33 @@
import Image from 'next/image';
import Link from 'next/link';
import backgroundPattern from '~/assets/background-pattern.png';
import connections from '~/assets/card-sharing-figure.png';
import { SignInForm } from '~/components/forms/signin';
export default function SignInPage() {
return (
<main className="bg-sand-100 relative flex min-h-screen flex-col items-center justify-center overflow-hidden px-4 py-12 md:p-12 lg:p-24">
<div className="relative flex max-w-4xl items-center gap-x-24">
<div className="absolute -inset-96 -z-[1] flex items-center justify-center opacity-50">
<Image
src={backgroundPattern}
alt="background pattern"
className="dark:brightness-95 dark:invert dark:sepia"
/>
</div>
<div>
<h1 className="text-4xl font-semibold">Sign in to your account</h1>
<div className="max-w-md">
<h1 className="text-4xl font-semibold">Sign in to your account</h1>
<p className="text-muted-foreground/60 mt-2 text-sm">
Welcome back, we are lucky to have you.
</p>
<p className="text-muted-foreground/60 mt-2 text-sm">
Welcome back, we are lucky to have you.
</p>
<SignInForm className="mt-4" />
<SignInForm className="mt-4" />
<p className="text-muted-foreground mt-6 text-center text-sm">
Don't have an account?{' '}
<Link href="/signup" className="text-primary duration-200 hover:opacity-70">
Sign up
</Link>
</p>
<p className="text-muted-foreground mt-6 text-center text-sm">
Don't have an account?{' '}
<Link href="/signup" className="text-primary duration-200 hover:opacity-70">
Sign up
</Link>
</p>
</div>
<div className="hidden flex-1 lg:block">
<Image src={connections} alt="documenso connections" />
</div>
</div>
</main>
<p className="mt-2.5 text-center">
<Link
href="/forgot-password"
className="text-muted-foreground text-sm duration-200 hover:opacity-70"
>
Forgotten your password?
</Link>
</p>
</div>
);
}

View File

@ -1,44 +1,25 @@
import Image from 'next/image';
import Link from 'next/link';
import backgroundPattern from '~/assets/background-pattern.png';
import connections from '~/assets/connections.png';
import { SignUpForm } from '~/components/forms/signup';
export default function SignUpPage() {
return (
<main className="bg-sand-100 relative flex min-h-screen flex-col items-center justify-center overflow-hidden px-4 py-12 md:p-12 lg:p-24">
<div className="relative flex max-w-4xl items-center gap-x-24">
<div className="absolute -inset-96 -z-[1] flex items-center justify-center opacity-50">
<Image
src={backgroundPattern}
alt="background pattern"
className="dark:brightness-95 dark:invert dark:sepia"
/>
</div>
<div>
<h1 className="text-4xl font-semibold">Create a new account</h1>
<div className="max-w-md">
<h1 className="text-4xl font-semibold">Create a shiny, new Documenso Account </h1>
<p className="text-muted-foreground/60 mt-2 text-sm">
Create your account and start using state-of-the-art document signing. Open and beautiful
signing is within your grasp.
</p>
<p className="text-muted-foreground/60 mt-2 text-sm">
Create your account and start using state-of-the-art document signing. Open and
beautiful signing is within your grasp.
</p>
<SignUpForm className="mt-4" />
<SignUpForm className="mt-4" />
<p className="text-muted-foreground mt-6 text-center text-sm">
Already have an account?{' '}
<Link href="/signin" className="text-primary duration-200 hover:opacity-70">
Sign in instead
</Link>
</p>
</div>
<div className="hidden flex-1 lg:block">
<Image src={connections} alt="documenso connections" />
</div>
</div>
</main>
<p className="text-muted-foreground mt-6 text-center text-sm">
Already have an account?{' '}
<Link href="/signin" className="text-primary duration-200 hover:opacity-70">
Sign in instead
</Link>
</p>
</div>
);
}

View File

@ -3,14 +3,13 @@
import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { Loader } from 'lucide-react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { TRPCClientError } from '@documenso/trpc/client';
import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-message';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import { useToast } from '@documenso/ui/primitives/use-toast';
@ -44,33 +43,18 @@ export const ForgotPasswordForm = ({ className }: ForgotPasswordFormProps) => {
const { mutateAsync: forgotPassword } = trpc.profile.forgotPassword.useMutation();
const onFormSubmit = async ({ email }: TForgotPasswordFormSchema) => {
try {
await forgotPassword({ email });
await forgotPassword({ email }).catch(() => null);
toast({
title: 'Reset email sent',
description: 'Your password reset mail has been sent successfully.',
duration: 5000,
});
toast({
title: 'Reset email sent',
description:
'A password reset email has been sent, if you have an account you should see it in your inbox shortly.',
duration: 5000,
});
reset();
router.push('/check-email');
} catch (err) {
if (err instanceof TRPCClientError && err.data?.code === 'BAD_REQUEST') {
toast({
title: 'An error occurred',
description: err.message,
variant: 'destructive',
});
} else {
toast({
title: 'An unknown error occurred',
variant: 'destructive',
description:
'We encountered an unknown error while attempting to send your email. Please try again later.',
});
}
}
reset();
router.push('/check-email');
};
return (
@ -79,17 +63,16 @@ export const ForgotPasswordForm = ({ className }: ForgotPasswordFormProps) => {
onSubmit={handleSubmit(onFormSubmit)}
>
<div>
<Label htmlFor="email" className="text-slate-500">
<Label htmlFor="email" className="text-muted-foreground">
Email
</Label>
<Input id="email" type="email" className="bg-background mt-2" {...register('email')} />
{errors.email && <span className="mt-1 text-xs text-red-500">{errors.email.message}</span>}
<FormErrorMessage className="mt-1.5" error={errors.email} />
</div>
<Button size="lg" disabled={isSubmitting} className="dark:bg-documenso dark:hover:opacity-90">
{isSubmitting && <Loader className="mr-2 h-5 w-5 animate-spin" />}
<Button size="lg" loading={isSubmitting}>
Reset Password
</Button>
</form>

View File

@ -92,7 +92,7 @@ export const PasswordForm = ({ className }: PasswordFormProps) => {
onSubmit={handleSubmit(onFormSubmit)}
>
<div>
<Label htmlFor="password" className="text-slate-500">
<Label htmlFor="password" className="text-muted-foreground">
Password
</Label>
@ -110,7 +110,7 @@ export const PasswordForm = ({ className }: PasswordFormProps) => {
</div>
<div>
<Label htmlFor="repeated-password" className="text-slate-500">
<Label htmlFor="repeated-password" className="text-muted-foreground">
Repeat Password
</Label>

View File

@ -89,7 +89,7 @@ export const ProfileForm = ({ className, user }: ProfileFormProps) => {
onSubmit={handleSubmit(onFormSubmit)}
>
<div>
<Label htmlFor="full-name" className="text-slate-500">
<Label htmlFor="full-name" className="text-muted-foreground">
Full Name
</Label>
@ -99,7 +99,7 @@ export const ProfileForm = ({ className, user }: ProfileFormProps) => {
</div>
<div>
<Label htmlFor="email" className="text-slate-500">
<Label htmlFor="email" className="text-muted-foreground">
Email
</Label>
@ -107,7 +107,7 @@ export const ProfileForm = ({ className, user }: ProfileFormProps) => {
</div>
<div>
<Label htmlFor="signature" className="text-slate-500">
<Label htmlFor="signature" className="text-muted-foreground">
Signature
</Label>

View File

@ -3,7 +3,6 @@
import { useRouter } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
import { Loader } from 'lucide-react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
@ -11,6 +10,7 @@ import { TRPCClientError } from '@documenso/trpc/client';
import { trpc } from '@documenso/trpc/react';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-message';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import { useToast } from '@documenso/ui/primitives/use-toast';
@ -22,7 +22,7 @@ export const ZResetPasswordFormSchema = z
})
.refine((data) => data.password === data.repeatedPassword, {
path: ['repeatedPassword'],
message: "Password don't match",
message: "Passwords don't match",
});
export type TResetPasswordFormSchema = z.infer<typeof ZResetPasswordFormSchema>;
@ -92,7 +92,7 @@ export const ResetPasswordForm = ({ className, token }: ResetPasswordFormProps)
onSubmit={handleSubmit(onFormSubmit)}
>
<div>
<Label htmlFor="password" className="flex justify-between text-slate-500">
<Label htmlFor="password" className="text-muted-foreground">
<span>Password</span>
</Label>
@ -106,13 +106,11 @@ export const ResetPasswordForm = ({ className, token }: ResetPasswordFormProps)
{...register('password')}
/>
{errors.password && (
<span className="mt-1 text-xs text-red-500">{errors.password.message}</span>
)}
<FormErrorMessage className="mt-1.5" error={errors.password} />
</div>
<div>
<Label htmlFor="repeatedPassword" className="flex justify-between text-slate-500">
<Label htmlFor="repeatedPassword" className="text-muted-foreground">
<span>Repeat Password</span>
</Label>
@ -126,13 +124,10 @@ export const ResetPasswordForm = ({ className, token }: ResetPasswordFormProps)
{...register('repeatedPassword')}
/>
{errors.repeatedPassword && (
<span className="mt-1 text-xs text-red-500">{errors.repeatedPassword.message}</span>
)}
<FormErrorMessage className="mt-1.5" error={errors.repeatedPassword} />
</div>
<Button size="lg" disabled={isSubmitting} className="dark:bg-documenso dark:hover:opacity-90">
{isSubmitting && <Loader className="mr-2 h-5 w-5 animate-spin" />}
<Button size="lg" loading={isSubmitting}>
Reset Password
</Button>
</form>

View File

@ -2,7 +2,6 @@
import { useEffect } from 'react';
import Link from 'next/link';
import { useSearchParams } from 'next/navigation';
import { zodResolver } from '@hookform/resolvers/zod';
@ -15,6 +14,7 @@ import { z } from 'zod';
import { ErrorCode, isErrorCode } from '@documenso/lib/next-auth/error-codes';
import { cn } from '@documenso/ui/lib/utils';
import { Button } from '@documenso/ui/primitives/button';
import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-message';
import { Input } from '@documenso/ui/primitives/input';
import { Label } from '@documenso/ui/primitives/label';
import { useToast } from '@documenso/ui/primitives/use-toast';
@ -114,21 +114,18 @@ export const SignInForm = ({ className }: SignInFormProps) => {
onSubmit={handleSubmit(onFormSubmit)}
>
<div>
<Label htmlFor="email" className="text-slate-500">
<Label htmlFor="email" className="text-muted-forground">
Email
</Label>
<Input id="email" type="email" className="bg-background mt-2" {...register('email')} />
{errors.email && <span className="mt-1 text-xs text-red-500">{errors.email.message}</span>}
<FormErrorMessage className="mt-1.5" error={errors.email} />
</div>
<div>
<Label htmlFor="password" className="flex justify-between text-slate-500">
<Label htmlFor="password" className="text-muted-forground">
<span>Password</span>
<Link className="text-xs text-slate-500" href="/forgot-password">
Forgot?
</Link>
</Label>
<Input
@ -141,9 +138,7 @@ export const SignInForm = ({ className }: SignInFormProps) => {
{...register('password')}
/>
{errors.password && (
<span className="mt-1 text-xs text-red-500">{errors.password.message}</span>
)}
<FormErrorMessage className="mt-1.5" error={errors.password} />
</div>
<Button size="lg" disabled={isSubmitting} className="dark:bg-documenso dark:hover:opacity-90">

View File

@ -68,7 +68,7 @@ export const ResetPasswordTemplate = ({
<Section>
<Text className="my-4 text-base font-semibold">
Hi, {userName}{' '}
<Link className="font-normal text-slate-400" href="mailto:{userEmail}">
<Link className="font-normal text-slate-400" href={`mailto:${userEmail}`}>
({userEmail})
</Link>
</Text>

View File

@ -29,8 +29,8 @@ export const sendForgotPassword = async ({ userId }: SendForgotPasswordOptions)
}
const token = user.PasswordResetToken[0].token;
const assetBaseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000';
const resetPasswordLink = `${process.env.NEXT_PUBLIC_SITE_URL}/reset-password/${token}`;
const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000';
const resetPasswordLink = `${process.env.NEXT_PUBLIC_WEBAPP_URL}/reset-password/${token}`;
const template = createElement(ForgotPasswordTemplate, {
assetBaseUrl,

View File

@ -10,19 +10,15 @@ export interface SendResetPasswordOptions {
}
export const sendResetPassword = async ({ userId }: SendResetPasswordOptions) => {
// TODO: Better Error Handling
const user = await prisma.user.findFirstOrThrow({
where: {
id: userId,
},
});
if (!user) {
throw new Error('User not found');
}
const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000';
const assetBaseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000';
console.log({ assetBaseUrl });
const template = createElement(ResetPasswordTemplate, {
assetBaseUrl,

View File

@ -3,54 +3,51 @@ import crypto from 'crypto';
import { prisma } from '@documenso/prisma';
import { TForgotPasswordFormSchema } from '@documenso/trpc/server/profile-router/schema';
import { ONE_DAY, ONE_HOUR } from '../../constants/time';
import { sendForgotPassword } from '../auth/send-forgot-password';
export const forgotPassword = async ({ email }: TForgotPasswordFormSchema) => {
let user;
try {
user = await prisma.user.findFirstOrThrow({
where: {
email: email.toLowerCase(),
const user = await prisma.user.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
});
} catch (error) {
throw new Error('No account found with that email address.');
}
},
});
if (!user) {
throw new Error('No account found with that email address.');
return;
}
// Find a token that was created in the last day and hasn't expired
const existingToken = await prisma.passwordResetToken.findFirst({
where: {
userId: user.id,
expiry: {
lt: new Date(),
},
createdAt: {
gte: new Date(Date.now() - 1000 * 60 * 60),
gt: new Date(Date.now() - ONE_HOUR),
},
},
});
if (existingToken) {
throw new Error('A password reset email has been sent.');
return;
}
const token = crypto.randomBytes(64).toString('hex');
const expiry = new Date();
expiry.setHours(expiry.getHours() + 24); // Set expiry to one hour from now
const token = crypto.randomBytes(18).toString('hex');
try {
await prisma.passwordResetToken.create({
data: {
token,
expiry,
userId: user.id,
},
});
} catch (error) {
throw new Error('We were unable to send your email. Please try again.');
}
return await sendForgotPassword({
userId: user.id,
await prisma.passwordResetToken.create({
data: {
token,
expiry: new Date(Date.now() + ONE_DAY),
userId: user.id,
},
});
await sendForgotPassword({
userId: user.id,
}).catch((err) => console.error(err));
};

View File

@ -0,0 +1,18 @@
import { prisma } from '@documenso/prisma';
type GetResetTokenValidityOptions = {
token: string;
};
export const getResetTokenValidity = async ({ token }: GetResetTokenValidityOptions) => {
const found = await prisma.passwordResetToken.findFirst({
select: {
id: true,
},
where: {
token,
},
});
return !!found;
};

View File

@ -15,7 +15,7 @@ export const resetPassword = async ({ token, password }: ResetPasswordOptions) =
throw new Error('Invalid token provided. Please try again.');
}
const foundToken = await prisma.passwordResetToken.findFirstOrThrow({
const foundToken = await prisma.passwordResetToken.findFirst({
where: {
token,
},
@ -34,7 +34,7 @@ export const resetPassword = async ({ token, password }: ResetPasswordOptions) =
throw new Error('Token has expired. Please try again.');
}
const isSamePassword = await compare(password, foundToken.User.password!);
const isSamePassword = await compare(password, foundToken.User.password || '');
if (isSamePassword) {
throw new Error('Your new password cannot be the same as your old password.');
@ -42,7 +42,7 @@ export const resetPassword = async ({ token, password }: ResetPasswordOptions) =
const hashedPassword = await hash(password, SALT_ROUNDS);
const transactions = await prisma.$transaction([
await prisma.$transaction([
prisma.user.update({
where: {
id: foundToken.userId,
@ -58,10 +58,5 @@ export const resetPassword = async ({ token, password }: ResetPasswordOptions) =
}),
]);
if (!transactions) {
throw new Error('We were unable to reset your password. Please try again.');
}
await sendResetPassword({ userId: foundToken.userId });
return transactions;
};

View File

@ -69,16 +69,7 @@ export const profileRouter = router({
email,
});
} catch (err) {
let message = 'We were unable to send your email. Please try again.';
if (err instanceof Error) {
message = err.message;
}
throw new TRPCError({
code: 'BAD_REQUEST',
message,
});
console.error(err);
}
}),