done
This commit is contained in:
parent
a2aa25ec47
commit
327ef99b6f
|
@ -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" });
|
||||
}
|
||||
};
|
|
@ -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:{
|
||||
|
|
|
@ -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
|
@ -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>
|
||||
|
||||
|
|
|
@ -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 (
|
||||
|
@ -27,6 +29,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 />}
|
||||
/>
|
||||
|
||||
|
||||
|
||||
</Routes>
|
||||
|
|
|
@ -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;
|
|
@ -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"
|
||||
>
|
||||
<NavLink to="/register" className="link-primary text-decoration-none">
|
||||
Register
|
||||
</a>
|
||||
</NavLink>
|
||||
|
||||
|
||||
<NavLink to="/forgotpassword" className="nav-link">
|
||||
Forgot Password
|
||||
</NavLink>
|
||||
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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/',
|
||||
},
|
||||
},
|
||||
|
||||
|
|
Loading…
Reference in New Issue