-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement user forgot password
- Loading branch information
1 parent
4cbf6e4
commit 305fb1a
Showing
8 changed files
with
192 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { validateSessionToken } from '@/server/auth/validateSession'; | ||
import { redirect } from 'next/navigation'; | ||
import React, { ReactNode } from 'react'; | ||
|
||
const layout = async ({ children }: { children: ReactNode }) => { | ||
const user = await validateSessionToken(); | ||
|
||
if (user !== null) { | ||
return redirect('/dashboard'); | ||
} | ||
|
||
return children; | ||
}; | ||
|
||
export default layout; |
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
'use client'; | ||
import Logo from '@/components/icons/logo'; | ||
import SubmitButton from '@/components/submitButton'; | ||
import { Input } from '@/components/ui/input'; | ||
import { forgotPassword as origFormAction } from '@/server/auth/forgotPassword'; | ||
import { User, UserForgotPassword } from '@prisma/client'; | ||
import { redirect } from 'next/navigation'; | ||
import { useEffect } from 'react'; | ||
import { useFormState } from 'react-dom'; | ||
import { toast } from 'sonner'; | ||
|
||
const Client = ({ | ||
forgotPassword, | ||
}: { | ||
forgotPassword: UserForgotPassword & { user: User }; | ||
}) => { | ||
const [state, formAction] = useFormState(origFormAction, {}); | ||
|
||
useEffect(() => { | ||
if (state.message) { | ||
return redirect('/auth/login'); | ||
} | ||
|
||
if (state.error) { | ||
toast.error(state.error); | ||
} | ||
}, [state]); | ||
|
||
return ( | ||
<form className="grid w-[350px] gap-8" action={formAction}> | ||
<input type="hidden" name="forgotPasswordId" value={forgotPassword.id} /> | ||
<div className="grid gap-2"> | ||
<Logo className="mb-2" /> | ||
<h3 className="text-[22px] font-bold leading-7 tracking-[-0.02em]"> | ||
Reset password for {forgotPassword.user.username} | ||
</h3> | ||
<p className="text-sm text-muted-foreground"> | ||
Here you can reset the password for your account | ||
</p> | ||
</div> | ||
<div className="grid gap-4"> | ||
<div className="grid gap-2"> | ||
<Input placeholder="Password" type="password" name="password" /> | ||
<Input | ||
placeholder="Confirm Password" | ||
type="password" | ||
name="confirmPassword" | ||
/> | ||
</div> | ||
<SubmitButton>Reset Password</SubmitButton> | ||
</div> | ||
</form> | ||
); | ||
}; | ||
|
||
export default Client; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import prisma from '@/server/db'; | ||
import { redirect } from 'next/navigation'; | ||
import Client from './client'; | ||
|
||
const Page = async ({ searchParams }: { searchParams: { id: string } }) => { | ||
const forgotPassword = await prisma.userForgotPassword.findFirst({ | ||
where: { | ||
id: searchParams.id, | ||
used: false, | ||
}, | ||
include: { | ||
user: true, | ||
}, | ||
}); | ||
|
||
if (!forgotPassword) { | ||
return redirect('/'); | ||
} | ||
|
||
return ( | ||
<main className="grid h-[100dvh] w-full items-center justify-center bg-neutral-100"> | ||
<Client forgotPassword={forgotPassword} /> | ||
</main> | ||
); | ||
}; | ||
|
||
export default Page; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
'use server'; | ||
|
||
import { z } from 'zod'; | ||
import prisma from '../db'; | ||
import bcrypt from 'bcrypt'; | ||
import { addMonths } from '@/lib/addMonths'; | ||
import { cookies, headers } from 'next/headers'; | ||
import { generateToken } from '@/lib/generateToken'; | ||
|
||
export const forgotPassword = async ( | ||
prevState: any, | ||
formData: FormData | ||
): Promise<{ error?: string; message?: string }> => { | ||
const schema = z.object({ | ||
password: z.string(), | ||
confirmPassword: z.string(), | ||
forgotPasswordId: z.string(), | ||
}); | ||
|
||
const validatedFields = schema.safeParse({ | ||
password: formData.get('password'), | ||
confirmPassword: formData.get('confirmPassword'), | ||
forgotPasswordId: formData.get('forgotPasswordId'), | ||
}); | ||
|
||
if (!validatedFields.success) { | ||
return { | ||
error: Object.entries(validatedFields.error.flatten().fieldErrors) | ||
.map(entry => `${entry[0]}: ${entry[1].map(error => error).join(' ')},`) | ||
.join(' '), | ||
}; | ||
} | ||
|
||
const forgotPasswordId = validatedFields.data.forgotPasswordId; | ||
const password = validatedFields.data.password; | ||
const confirmPassword = validatedFields.data.confirmPassword; | ||
|
||
if (password != confirmPassword) { | ||
return { | ||
error: "Passwords don't match", | ||
}; | ||
} | ||
|
||
const forgotPassword = await prisma.userForgotPassword.findFirst({ | ||
where: { | ||
id: forgotPasswordId, | ||
used: false, | ||
}, | ||
}); | ||
|
||
if (!forgotPassword) { | ||
return { | ||
error: 'Invalid forgot password', | ||
}; | ||
} | ||
|
||
await prisma.userForgotPassword.update({ | ||
where: { | ||
id: forgotPasswordId, | ||
}, | ||
data: { | ||
used: true, | ||
}, | ||
}); | ||
|
||
await prisma.user.update({ | ||
where: { | ||
id: forgotPassword.userId, | ||
}, | ||
data: { | ||
password: bcrypt.hashSync(password, 10), | ||
}, | ||
}); | ||
|
||
await cookies().delete('mlSessionToken'); | ||
|
||
return { | ||
message: 'Updated password', | ||
}; | ||
}; |