This commit is contained in:
omkieit 2024-09-08 18:00:09 +05:30
parent a2aa25ec47
commit 327ef99b6f
11 changed files with 338 additions and 25 deletions

View File

@ -127,3 +127,65 @@ export const showUser = async (req, res) => {
res.status(500).json({ message: "Internal servers error" });
}
};
//forgot password
export const forgotPassword = async (req, res) => {
const { email } = req.body;
try {
const user = await UserModal.findOne({ email });
if (!user) {
return res.status(404).json({ message: "User not found" });
}
const secrets = secret + user.password;
const token = jwt.sign({ email: user.email, id: user._id }, secrets, {
expiresIn: "5m",
});
user.resetToken = token;
user.resetTokenExpiration = Date.now() + 300000; // 5 minutes
await user.save();
res.status(200).send({ message: "Reset Email alert sent successfully" });
const url = `CLick on the link ${process.env.RETURN_URL}/users/resetpassword/${user._id}/${token}`;
await sendEmail(user.email, "Reset Email", url);
// res.json({ message: 'Password reset successful' });
} catch (err) {
console.error("Forgot Password Error:", err);
res.status(500).json({ message: "Something went wrong" });
}
};
// To reset password
export const resetPassword = async (req, res) => {
const { id, token } = req.params;
const { password } = req.body;
try {
if (!password || password.trim() === "") {
return res.status(400).json({ message: "Password not entered" });
}
const user = await UserModal.findOne({ _id: id });
if (!user) {
return res.json({ status: "User Not Exists!!" });
}
// Update the user's password and clear the reset token
user.password = await bcrypt.hash(password, 12);
// user.resetToken = undefined;
// user.resetTokenExpiration = undefined;
user.resetToken = token;
user.resetTokenExpiration = Date.now() + 300000; // 5 minutes
await user.save();
res.json({ message: "Password reset successful" });
} catch (err) {
console.error("Password Reset Error:", err);
res.status(500).json({ message: "Something went wrong" });
}
};

View File

@ -10,6 +10,9 @@ const userSchema = new mongoose.Schema({
termsconditions:{type: String,},
userType:{ type: String, required: true },
verified: { type: Boolean, default: false },
id: { type: String },
resetToken: { type: String },
resetTokenExpiration: { type: String },
tokens:[
{
token:{

View File

@ -1,12 +1,15 @@
import express from "express";
const router = express.Router();
import { signup, signin, verifyUser, showUser } from "../controllers/user.js";
import { signup, signin, verifyUser, showUser, forgotPassword, resetPassword, } from "../controllers/user.js";
router.post("/signin", signin);
router.post("/signup", signup);
router.get('/:id/verify/:token/', verifyUser);
router.get('/:id', showUser);
router.post("/forgotpassword", forgotPassword);
router.post("/resetpassword/:id/:token", resetPassword);

File diff suppressed because one or more lines are too long

View File

@ -27,7 +27,7 @@
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css">
<!-- fonts -->
<link href="https://fonts.googleapis.com/css2?family=Raleway:wght@400;500;600;800&family=Sen:wght@400;700;800&display=swap" rel="stylesheet">
<script type="module" crossorigin src="/assets/index-CdnFdl54.js"></script>
<script type="module" crossorigin src="/assets/index-CTPhgL6j.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CyAHZLBw.css">
</head>

View File

@ -7,6 +7,8 @@ import Login from "./components/Login";
import Dashboard from "./components/Dashboard";
import PrivateRoute from "./components/PrivateRoute";
import VerifyUser from "./components/EmailVerify";
import ForgotPassword from "./components/ForgotPassword";
import ResetPassword from "./components/ResetPassword";
const App = () => {
return (
@ -26,6 +28,13 @@ const App = () => {
}
/>
<Route path="/users/:id/verify/:token" element={<VerifyUser />} />
<Route path="/forgotpassword" element={<ForgotPassword />}></Route>
<Route
path="/users/resetpassword/:userId/:token"
element={<ResetPassword />}
/>

View File

@ -0,0 +1,107 @@
import { useState } from "react";
import axios from "axios";
import Navbar from "./Navbar";
import Footer from "../components/Footer";
const ForgotPassword = () => {
const [email, setEmail] = useState("");
const [message, setMessage] = useState("");
const BASE_URL = import.meta.env.VITE_REACT_APP_SECRET;
const handleResetPassword = async () => {
try {
const response = await axios.post(`${BASE_URL}/users/forgotpassword`, {
email,
});
setMessage(response.data.message);
} catch (error) {
console.error("Forgot Password Error:", error);
setMessage(error.response.data.message);
}
};
return (
<div>
<Navbar />
<br />
<br />
<br />
<section className="card mb-0 vh-100">
<div className="container py-10 h-100">
<div className="row d-flex align-items-center justify-content-center h-100">
<div className="col-md-10 col-lg-5 col-xl-5 offset-xl-1 card mb-10">
<h3 className="card-header" style={{ color: "#F74B02" }}>
<span className="pi pi-lock" style={{ color: "#067ADC" }}>
{" "}
</span>{" "}
{" "}
Forgot{" "}
<span
style={{
color: "#067ADC",
marginRight: "3px",
marginLeft: "3px",
fontWeight: "bold",
fontSize: "30px",
}}
>
/
</span>{" "}
Change Password
</h3>
<br />
<p className="card-title text-center">
Enter your email address to receive a password reset link:
</p>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
className="container d-flex align-items-center justify-content-center vh-50"
>
<input
type="email"
value={email}
className="form-control"
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter your email"
style={{ width: "80%", marginBottom: "15px" }}
/>
<button
className="btn btn-primary"
style={{
width: "35%",
border: "1px solid #FFFFFF",
borderRadius: "10px",
color: "#FFFFFF",
}}
onClick={handleResetPassword}
>
Reset Password
</button>
</div>
<p
style={{ color: "#067ADC" }}
className="card-title text-center"
>
{message}
</p>
</div>
</div>
</div>
</section>
<Footer />
</div>
);
};
export default ForgotPassword;

View File

@ -1,4 +1,5 @@
import { useState, useEffect } from "react";
import { NavLink } from "react-router-dom";
import Footer from "./Footer";
import Navbar from "./Navbar";
import { toast } from "react-toastify";
@ -164,12 +165,16 @@ const Login = () => {
<div className="d-flex gap-2 gap-md-4 flex-column flex-md-row justify-content-md-end mt-4">
<p className="m-0 text-secondary text-center">
Don't have an account?{" "}
<a
href="#!"
className="link-primary text-decoration-none"
>
Register
</a>
<NavLink to="/register" className="link-primary text-decoration-none">
Register
</NavLink>
<NavLink to="/forgotpassword" className="nav-link">
Forgot Password
</NavLink>
</p>
</div>
</div>

View File

@ -0,0 +1,123 @@
import { useState, useEffect } from "react";
import axios from "axios";
import { useParams } from "react-router-dom";
import { toast } from "react-toastify";
import Navbar from "./Navbar";
import Footer from "./Footer";
const ResetPassword = () => {
const { userId, token } = useParams();
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [message, setMessage] = useState("");
const BASE_URL = import.meta.env.VITE_REACT_APP_SECRET;
const handleResetPassword = async () => {
try {
if (!password || password.trim() === "") {
setMessage("Password not entered");
toast.error("Password not entered"); // Use toast.error instead of toast.success
return;
}
const response = await axios.post(
`${BASE_URL}/users/resetpassword/${userId}/${token}`,
{ userId, token, password }
);
if (password !== confirmPassword) {
setMessage("Passwords do not match.");
toast.error("Passwords do not match.");
return;
} else {
setMessage("Password reset successfull");
toast.success(response.data.message);
}
} catch (error) {
console.error("Reset Password Error:", error);
}
};
useEffect(() => {
// Clear any existing toasts when the component mounts
toast.dismiss();
}, []);
return (
<>
<Navbar />
<br />
<br />
<br />
<br />
<section className="card mb-0 vh-100">
<div className="container py-10 h-100">
<div className="row d-flex align-items-center justify-content-center h-100">
<div className="col-md-10 col-lg-5 col-xl-5 offset-xl-1 card mb-10">
<h2 className="card-header" style={{ color: '#F74B02' }}>
<span className="pi pi-lock-open" style={{ color: '#067ADC' }}> </span> {" "}
Reset Password
</h2>
<br />
<p className="card-title text-center" style={{ color: '#F74B02' }}>
Enter your new password:
</p>
<input
className="form-control vh-10"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter your new password"
style={{ display: "flex", gap: "35px" }}
/>
<br />
<input
className="form-control"
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
placeholder="Confirm your new password"
/>
<br />
<button onClick={handleResetPassword} className="btn btn-primary" style={{
width: "35%" }}>
Reset Password
</button>
<p style={{ color: "#067ADC" }} className="card-title text-center">
{message}
</p>
</div>
</div>
</div>
</section>
<Footer />
</>
);
};
export default ResetPassword;

View File

@ -21,7 +21,7 @@ export const verifyEmail = createAsyncThunk(
const response = await api.verifyEmail(id, token, data);
return response.data.message;
} catch (error) {
return rejectWithValue("Error verifying email");
return rejectWithValue(error.response.data);
}
}
);

View File

@ -14,6 +14,7 @@ export default defineConfig({
'/users/signup': 'http://67.225.129.127:3002/',
'/users/signin': 'http://67.225.129.127:3002/',
'/users/forgotpassword': 'http://67.225.129.127:3002/',
},
},