111 lines
2.5 KiB
TypeScript
111 lines
2.5 KiB
TypeScript
import { zValidator } from "@hono/zod-validator";
|
|
import HonoEnv from "../../types/HonoEnv";
|
|
import { z } from "zod";
|
|
import { and, eq, isNull } from "drizzle-orm";
|
|
import { Hono } from "hono";
|
|
import db from "../../drizzle";
|
|
import { users } from "../../drizzle/schema/users";
|
|
import { notFound, unauthorized } from "../../errors/DashboardError";
|
|
import { generateResetPasswordToken, verifyResetPasswordToken } from "../../utils/authUtils";
|
|
import { sendResetPasswordEmail } from "../../utils/mailerUtils";
|
|
import { hashPassword } from "../../utils/passwordUtils";
|
|
|
|
const forgotPasswordRoutes = new Hono<HonoEnv>()
|
|
/**
|
|
* Forgot Password
|
|
*
|
|
* Checking emails in the database, generating tokens, and sending emails occurs.
|
|
*/
|
|
.post(
|
|
'/',
|
|
zValidator(
|
|
'json',
|
|
z.object({
|
|
email: z.string().email(),
|
|
})
|
|
),
|
|
async (c) => {
|
|
const { email } = c.req.valid('json');
|
|
|
|
const user = await db
|
|
.select()
|
|
.from(users)
|
|
.where(
|
|
and(
|
|
isNull(users.deletedAt),
|
|
eq(users.email, email)
|
|
)
|
|
);
|
|
|
|
if (!user.length) throw notFound();
|
|
|
|
// Generate reset password token
|
|
const resetPasswordToken = await generateResetPasswordToken({
|
|
uid: user[0].id,
|
|
});
|
|
|
|
await db
|
|
.update(users)
|
|
.set({
|
|
resetPasswordToken: resetPasswordToken
|
|
})
|
|
.where(eq(users.email, email));
|
|
|
|
// Send email with reset password token
|
|
await sendResetPasswordEmail(email, resetPasswordToken);
|
|
|
|
return c.json({
|
|
message: 'Email has been sent successfully',
|
|
});
|
|
}
|
|
)
|
|
/**
|
|
* Reset Password
|
|
*/
|
|
.patch(
|
|
'/verify',
|
|
zValidator(
|
|
'json',
|
|
z.object({
|
|
password: z.string(),
|
|
confirm_password: z.string()
|
|
})
|
|
),
|
|
async (c) => {
|
|
const formData = c.req.valid('json');
|
|
const token = c.req.query('token')
|
|
|
|
// Token validation
|
|
if (!token) {
|
|
return c.json({ message: 'Token is required' }, 400);
|
|
}
|
|
|
|
// Password validation
|
|
if (formData.password !== formData.confirm_password) {
|
|
return c.json({ message: 'Passwords do not match' }, 400);
|
|
}
|
|
|
|
const decoded = await verifyResetPasswordToken(token);
|
|
if (!decoded) {
|
|
return c.json({ message: 'Invalid or expired token' }, 401);
|
|
}
|
|
|
|
if (!decoded) throw unauthorized();
|
|
|
|
// Hash the password
|
|
const hashedPassword = await hashPassword(formData.password);
|
|
|
|
await db
|
|
.update(users)
|
|
.set({
|
|
password: hashedPassword,
|
|
updatedAt: new Date(),
|
|
})
|
|
.where(eq(users.id, decoded.uid));
|
|
|
|
return c.json({
|
|
message: 'Password has been reset successfully'
|
|
});
|
|
});
|
|
|
|
export default forgotPasswordRoutes; |