Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added forgot password feature #300

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"react-icons": "^5.2.1",
"react-router-dom": "^6.23.1",
"react-scripts": "^5.0.1",
"react-toastify": "^10.0.5",
"styled-components": "^6.1.12",
"swiper": "^11.1.4",
"tailwindcss": "^3.4.4",
Expand Down
2 changes: 2 additions & 0 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Preloader from "./Components/Preloader.jsx";
import { Toast } from "./Toast/Toast.js";
import GoToTop from "./Components/GoToTop.jsx";
import License from "./Pages/Licensing.jsx";
import ResetPassword from "./Pages/ResetPassword.jsx";

function App() {
const [darkMode, setDarkMode] = useState(false);
Expand Down Expand Up @@ -58,6 +59,7 @@ function App() {
<Route path="/licensing" element={<License />} />
<Route path="/terms" element={<TermsAndConditions />} />
<Route path="/contributors" element={<Contributors />} />
<Route path="/resetpassword" element={<ResetPassword />} />
<Route path="*" element={<NotFound />} /> {/* Fallback route */}
</Routes>
<Toast position="bottom-right" />
Expand Down
10 changes: 7 additions & 3 deletions client/src/Pages/LoginPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,13 @@ const LoginPage = () => {
>
Login
</Button>
<Typography align="center" sx={{ mt: 2, mr: 2 }} className="text-black dark:text-white">
Don't have an account? <Link to="/signup">Sign up</Link>
</Typography>
<Typography align="left" sx={{ mt: 2, mr: 2 }} className="text-black dark:text-white">
Don't have an account? <Link to="/signup">Sign up</Link>
</Typography>
<Typography align="left" sx={{ mt: 2, mr: 2 }} className="text-black dark:text-white">
Forgot your password? <Link to="/resetpassword">Reset Password</Link>
</Typography>

</form>
</Grid>
</Grid>
Expand Down
111 changes: 111 additions & 0 deletions client/src/Pages/ResetPassword.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React, { useState } from "react";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton";
import VisibilityIcon from "@mui/icons-material/Visibility";
import VisibilityOffIcon from "@mui/icons-material/VisibilityOff";
import { Box, Container, Grid, Typography } from "@mui/material";
import Lottie from "lottie-react";
import loginAnimation from "../Lottie-animation/loginAnimation.json"; // Ensure the animation is correct for this page
import axios from "axios";
import toast, { Toaster } from "react-hot-toast";
import { Link, useNavigate } from "react-router-dom";

const ResetPassword = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [showPassword, setShowPassword] = useState(false); // State for toggling password visibility
const [error, setError] = useState("");
let navigate = useNavigate();

const handleSubmit = async (e) => {
e.preventDefault();

try {
const response = await axios.post("http://localhost:8080/customer/resetpassword", {
email,
newPassword: password, // Send new password
});
toast.success("Password reset successfully!");
navigate("/login", { replace: true });
} catch (err) {
setError(err.response.data.error); // Fix the error field
}
};

// Function to toggle password visibility
const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};

return (
<>
<Toaster />
<Container maxWidth="xl">
<div style={{ marginTop: "100px", marginBottom: "180px" }}>
<Grid container spacing={2} sx={{ justifyContent: "center" }}>
<Grid item xs={12} md={6}>
<Box sx={{ display: { xs: "none", md: "block" } }}>
<Lottie animationData={loginAnimation} style={{ height: "500px" }} />
</Box>
</Grid>
<Grid item xs={12} md={4} sx={{ maxWidth: "500px" }}> {/* Set maxWidth to 600px */}
<form onSubmit={handleSubmit}>
<Typography variant="h5" align="center" gutterBottom className="dark:text-white">
Reset Password
</Typography>
<TextField
variant="outlined"
label="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
margin="normal"
fullWidth // Use fullWidth for consistent sizing
/>
<Box sx={{ position: "relative", display: "flex", alignItems: "center", width: '100%' }}>
<TextField
variant="outlined"
label="New Password"
type={showPassword ? "text" : "password"}
value={password}
onChange={(e) => setPassword(e.target.value)}
margin="normal"
fullWidth // Use fullWidth for consistent sizing
/>
<IconButton
onClick={togglePasswordVisibility}
sx={{
position: "absolute",
right: "10px",
top: "50%",
transform: "translateY(-50%)",
height: '100%', // Make button height equal to input height
width: '40px', // Adjust width as needed
padding: '0', // Remove default padding
borderRadius: '0', // Remove border radius
}}
>
{showPassword ? <VisibilityIcon /> : <VisibilityOffIcon />}
</IconButton>
</Box>
{error && (
<Typography color="error" align="center">
{error}
</Typography>
)}
<Button variant="contained" type="submit" fullWidth sx={{ mt: 1 }}>
Reset Password
</Button>
<Typography align="left" sx={{ mt: 2, mr: 2 }}>
<Link to="/login">Go to Login</Link>
</Typography>
</form>
</Grid>
</Grid>
</div>
</Container>
</>
);
};

export default ResetPassword;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const sendToken = require("../utils/jwtToken");
const ErrorHandler = require("../utils/errorHandler.js");
const jwt = require("jsonwebtoken");
require("dotenv").config();
const bcrypt = require("bcryptjs");
const saltRounds = 10;
// CUSTOMER REGISTRATION ROUTE
const validator = require('validator');
const disposableEmailDomains = require('disposable-email-domains');
Expand Down Expand Up @@ -204,6 +206,37 @@ exports.updateProfile = catchAsyncErrors(async (req, res, next) => {
});
});

//RESET Password
exports.resetPassword = async (req, res) => {
const { email, newPassword } = req.body; // expecting newPassword

if (!newPassword) {
return res.status(400).json({ error: 'New password is required.' });
}

try {
const hashedPassword = await bcrypt.hash(newPassword, saltRounds); // Hash new password

const updatedUser = await Customer.findOneAndUpdate(
{ email }, // Find user by email
{ $set: { password: hashedPassword } }, // Set new hashed password
{ new: true }
);

if (updatedUser) {
return res.json({ message: 'Password updated successfully.' });
} else {
return res.status(404).json({ error: 'User not found.' });
}
} catch (error) {
console.error('Error updating password:', error.message);
res.status(500).json({ error: 'Internal Server Error' });
}
};





exports.addFeedback = catchAsyncErrors(async (req, res, next) => {
const { feedback,topic } = req.body;
Expand Down Expand Up @@ -282,4 +315,4 @@ exports.exchangeToken = catchAsyncErrors(async (req, res, next) => {
} catch (error) {
return next(new ErrorHandler("Invalid refresh token", 401));
}
});
});
File renamed without changes.
12 changes: 4 additions & 8 deletions index.js → server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,14 @@ const MONGO_URL = process.env.MONGO_URL;
const cors = require("cors");
app.use(
cors({

origin: "http://localhost:3000", // your frontend's origin
optionsSuccessStatus: 200,

origin: "http://localhost:3000", // Allow requests from localhost:3000
credentials: true, // Allow sending cookies from the frontend
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], // Allow the HTTP methods you use
allowedHeaders: ["Content-Type", "auth-token", "Origin", "X-Requested-With", "Accept"], // Allow headers

credentials: true, // Allow cookies
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], // Allow these methods
allowedHeaders: ["Content-Type", "auth-token", "Origin", "X-Requested-With", "Accept"], // Allow these headers
})
);


// Check if MONGO_URL is defined
if (!MONGO_URL) {
console.error("MONGO_URL is not defined in the environment variables.");
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 4 additions & 5 deletions models/customerSchema.js → server/models/customerSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const mongoose = require("mongoose");
const validator = require("validator");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const dotenv = require(`dotenv`);
const dotenv = require("dotenv");
dotenv.config({ path: `.env` });

const customerSchema = new mongoose.Schema({
Expand Down Expand Up @@ -38,8 +38,8 @@ const customerSchema = new mongoose.Schema({
type: String,
default: "user",
},
refreshTOken:{
type:String,
refreshToken: { // Corrected key name
type: String,
required: false,
},
createdAt: {
Expand All @@ -61,11 +61,10 @@ customerSchema.pre("save", async function (next) {

// JWT TOKEN
customerSchema.methods.getJWTToken = function () {
return jwt.sign({ id: this._id }, process.env.JWT_SECRET);
return jwt.sign({ id: this._id }, process.env.JWT_SECRET, { expiresIn: '1h' }); // Consider adding expiration
};

// Compare Password

customerSchema.methods.comparePassword = async function (password) {
return await bcrypt.compare(password, this.password);
};
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
32 changes: 16 additions & 16 deletions models/wishlistSchema.js → server/models/wishlistSchema.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
const mongoose = require("mongoose");
const wishlistSchema = mongoose.Schema({
user: {
type: mongoose.Schema.ObjectId,
ref: "Customer",
required: true,
},
product: {
type: mongoose.Schema.ObjectId,
ref: "Product",
required: true,
},
});
module.exports = mongoose.model("Wishlist", wishlistSchema);
const mongoose = require("mongoose");

const wishlistSchema = mongoose.Schema({
user: {
type: mongoose.Schema.ObjectId,
ref: "Customer",
required: true,
},
product: {
type: mongoose.Schema.ObjectId,
ref: "Product",
required: true,
},
});

module.exports = mongoose.model("Wishlist", wishlistSchema);
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
10 changes: 4 additions & 6 deletions routes/customerRoutes.js → server/routes/customerRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ const {
updatePassword,
updateProfile,
logoutCustomer,
addFeedback


addFeedback,
resetPassword
} = require("../controllers/customerController.js");
const {
addTocart,
Expand All @@ -32,8 +31,7 @@ router.route("/password/update").put(isAuthenticatedUser, updatePassword);

router.route("/me/update").put(isAuthenticatedUser, updateProfile);



router.route("/resetpassword").post(resetPassword);


//cart routes
Expand All @@ -56,4 +54,4 @@ router.route("/cart").get(isAuthenticatedUser,getCartItems);

//giving feedback
router.route("/add-feedback").post(isAuthenticatedUser,addFeedback);
module.exports = router;
module.exports = router;
File renamed without changes.
File renamed without changes.
Loading