-
-
Notifications
You must be signed in to change notification settings - Fork 103
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #282 from Sourabh782/forgotpassword
Feat: Implemented otp based forgot password #263
- Loading branch information
Showing
10 changed files
with
414 additions
and
55 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
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,86 @@ | ||
/* eslint-disable no-unused-vars */ | ||
const bcrypt = require("bcrypt"); | ||
const { z } = require("zod"); | ||
const Customer = require("../models/customer.model"); | ||
const { sendVerificationMail } = require("../config/nodemailer"); | ||
|
||
|
||
// Define the schema | ||
const forgotPasswordSchema = z.object({ | ||
email: z.string().email("Invalid email address"), | ||
}); | ||
|
||
async function verifyEmail(req, res) { | ||
// Validate the request body | ||
const validation = forgotPasswordSchema.safeParse(req.body); | ||
|
||
if (!validation.success) { | ||
return res.status(400).json({ error: validation.error.errors }); | ||
} | ||
|
||
const existingCustomer = await Customer.findOne({ email: req.body.email }); | ||
if (!existingCustomer) { | ||
return res.status(404).json({ error: "Email is not registered" }); | ||
} | ||
|
||
const verifyCode = Math.floor(100000 + Math.random() * 900000).toString(); | ||
|
||
existingCustomer.verificationCode = verifyCode; | ||
await existingCustomer.save(); | ||
|
||
sendVerificationMail(req.body.email, verifyCode) | ||
|
||
try { | ||
res.status(201).json({ id: existingCustomer._id, success: true }); | ||
} catch (error) { | ||
res.status(500).json({ error: "Internal server error" }); | ||
} | ||
} | ||
|
||
async function verifyOTP(req, res) { | ||
try { | ||
const { id, otp } = req.body; | ||
|
||
const existingCustomer = await Customer.findOne({ _id: id }); | ||
|
||
if (!existingCustomer) { | ||
return res.status(404).json({ error: "User not found" }); | ||
} | ||
|
||
if(existingCustomer.verificationCode !== otp){ | ||
return res.status(400).json({ error: "Invalid verification code", success: false }); | ||
} | ||
|
||
existingCustomer.verificationCode = "" | ||
await existingCustomer.save(); | ||
|
||
return res.status(200).json({ message: "verified", success: true}) | ||
} catch (error) { | ||
return res.status(500).json({ message: "Internal server error", success: false}) | ||
} | ||
|
||
|
||
} | ||
|
||
async function resetPassword(req, res) { | ||
try { | ||
const {id, password} = req.body | ||
|
||
const customer = await Customer.findOne({ _id: id }); | ||
if (!customer) { | ||
return res.status(401).json({ error: "User not found" }); | ||
} | ||
const hashedPassword = await bcrypt.hash(password, 10); | ||
customer.password = hashedPassword; | ||
await customer.save(); | ||
res.json({ message: "Password reset successful" }); | ||
} catch (error) { | ||
res.status(501).json({ error: "Internal server error" }); | ||
} | ||
} | ||
|
||
module.exports = { | ||
verifyEmail, | ||
verifyOTP, | ||
resetPassword, | ||
}; |
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,15 @@ | ||
const express = require("express"); | ||
const { | ||
verifyEmail, | ||
verifyOTP, | ||
resetPassword, | ||
} = require("../controller/forgotPassword.controller"); | ||
|
||
const router = express.Router(); | ||
require("dotenv").config(); | ||
|
||
router.post("/verify-email", verifyEmail); | ||
router.post("/verify-otp", verifyOTP); | ||
router.post("/resetpassword", resetPassword); | ||
|
||
module.exports = router; |
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,111 @@ | ||
import { Link, useNavigate } from 'react-router-dom'; | ||
import photo from '../../assets/login.png'; | ||
import React, { useState } from 'react'; | ||
import { message } from 'antd'; | ||
|
||
const EmailVerify = () => { | ||
const API_URL = import.meta.env.VITE_BACKEND_URL || 'http://localhost:3000'; | ||
const navigate = useNavigate(); // Use useNavigate for navigation | ||
const [email, setEmail] = useState("") | ||
|
||
const handleChange = (e) => { | ||
setEmail(e.target.value); | ||
}; | ||
|
||
const [isLoading, setIsLoading] = useState(false); | ||
const [error, setError] = useState(null); | ||
|
||
// Helper function for email validation | ||
const isValidEmail = (email) => { | ||
// Basic email regex, consider using a more robust solution in production | ||
return /\S+@\S+\.\S+/.test(email); | ||
}; | ||
|
||
const handleSubmit = async (e) => { | ||
e.preventDefault(); | ||
setIsLoading(true); | ||
setError(null); | ||
|
||
// Add input validation // Basic validation examples | ||
if (!isValidEmail(email)) { | ||
setError('Please enter a valid email address'); | ||
return; | ||
} | ||
|
||
try { | ||
const response = await fetch(`${API_URL}/api/forgot/verify-email`, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ | ||
email: email | ||
}), | ||
}); | ||
const result = await response.json(); | ||
if (!response.ok) { | ||
throw new Error(result.message || 'Reset password failed'); | ||
} | ||
console.log(result); | ||
|
||
|
||
// Display success message and navigate to login | ||
message.success('Password reset successfully! Please log in.'); | ||
navigate(`/verifyotp/${result.id}`); | ||
} catch (err) { | ||
setError(err.message); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="w-screen h-screen flex items-center justify-center pt-10"> | ||
<img src={photo} alt="login" loading="lazy" className=" w-3/4 absolute" /> | ||
<form | ||
onSubmit={(e) => handleSubmit(e)} | ||
className="form z-10 p-16 bg-lightblue flex flex-col items-start justify-center gap-5 rounded-lg border-2 border-black shadow-[4px_4px_0px_0px_black] bg-[#f1e9dc]" | ||
> | ||
<span className="block text-[#666] font-semibold text-2xl "> | ||
Hey User, | ||
</span> | ||
<div className="title text-[#323232] font-black text-7xl mb-6"> | ||
Reset Your<br></br> Password | ||
<br /> | ||
</div> | ||
|
||
{error && ( | ||
<div className="w-full p-2 bg-red-100 text-red-700 border border-red-400 rounded-md"> | ||
{error} | ||
</div> | ||
)} | ||
|
||
<input | ||
className="input w-full h-10 rounded-md border-2 border-black bg-beige shadow-[4px_4px_0px_0px_black] text-[15px] font-semibold text-[#323232] p-2.5 focus:outline-none focus:border-[#2d8cf0] placeholder-[#666] placeholder-opacity-80" | ||
name="email" | ||
placeholder="Email" | ||
type="email" | ||
aria-required="true" | ||
autoComplete="email" | ||
onChange={(e) => handleChange(e)} | ||
/> | ||
|
||
<h3 className="flex items-center justify-between w-full"> | ||
<span className="block text-[#666] font-semibold text-xl transform hover:scale-110 hover:-translate-y-1 hover:text-green-500 transition"> | ||
<Link to={'/login'}>Go Back To Login Page</Link> | ||
</span> | ||
</h3> | ||
|
||
<button | ||
type="submit" | ||
className="button-confirm mx-auto mt-12 px-4 w-30 h-10 rounded-md border-2 border-black bg-beige shadow-[4px_4px_0px_0px_black] text-[17px] font-semibold text-[#323232] cursor-pointer active:shadow-none active:translate-x-[3px] active:translate-y-[3px]" | ||
disabled={isLoading} | ||
> | ||
{isLoading ? 'Submitting...' : 'Let’s go →'} | ||
</button> | ||
</form> | ||
</div> | ||
); | ||
}; | ||
|
||
export default EmailVerify; |
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
Oops, something went wrong.