diff --git a/.env b/.env index 6247272..4ef0c30 100644 --- a/.env +++ b/.env @@ -1,3 +1,5 @@ CONNECTION_STRING = 'mongodb+srv://mophoeniiiix:ZK6AdzDw6PbK8YoN@cluster0.ob8d1.mongodb.net/ums?retryWrites=true&w=majority' secret="this_is_my_jwt_secret" -PORT=3000 \ No newline at end of file +PORT=3000 +MAILJET_API_KEY='3509b8424511b49f7f77c97af1e74044' +MAILJET_API_SECRET='1d3be59ab9ebf2d3c9900fd19d5f13d4' \ No newline at end of file diff --git a/controllers/handelCourse.js b/controllers/handelCourse.js index 83e5fc6..2035f2e 100644 --- a/controllers/handelCourse.js +++ b/controllers/handelCourse.js @@ -184,7 +184,6 @@ const enrollInCourse = async (req, res)=>{ // Enroll the student in the course course.students.push(studentId); await course.save(); - // Add the course to the student's enrolled courses student.enrolledCourses.push(courseId); await student.save(); diff --git a/controllers/handelUser.js b/controllers/handelUser.js index d0f7178..ed24f15 100644 --- a/controllers/handelUser.js +++ b/controllers/handelUser.js @@ -1,6 +1,8 @@ var jwt = module.require('jsonwebtoken'); const bcrypt = module.require('bcrypt'); const usermodel = module.require("../models/usersModel") +const crypto = require('crypto'); +const nodemailer = require('nodemailer'); const getall = async (req, res) => { @@ -35,21 +37,19 @@ const getByid = async (req, res) => { const updateOne = async (req, res) => { try { const { id } = req.params; - const { password, ...newUpdate } = req.body; // Extract other fields from req.body + const { newPassword, ...newUpdate } = req.body; // Extract other fields from req.body const updateData = { ...newUpdate }; - + console.log(req.body) // Handle password hashing if provided - if (password) { - const hashedPassword = await bcrypt.hash(password, 10); + if (newPassword) { + const hashedPassword = await bcrypt.hash(newPassword, 10); updateData.password = hashedPassword; } - // If an image is provided, handle file upload if (req.file) { updateData.image = req.file.path; // Set image path from multer file upload } - // Find the user and update const updatedUser = await usermodel.findByIdAndUpdate(id, updateData, { new: true }); if (!updatedUser) { @@ -63,6 +63,7 @@ const updateOne = async (req, res) => { }; + const createone = async (req, res) => { try { // Extract user data from the request body @@ -199,4 +200,121 @@ const uploadImage = async (req, res) => { res.status(500).send(err.message); } }; -module.exports = { getall, getByid, updateOne,createone,deleteOne,deleteall,login, uploadImage,getAllProfessors } + + const forgotPassword = async (req, res) => { + const { email } = req.body; + + if (!email) { + return res.status(400).json({ message: "Email is required" }); + } + + try { + const user = await usermodel.findOne({ email }); + + if (!user) { + return res.status(404).json({ message: "User not found" }); + } + + const token = crypto.randomBytes(20).toString('hex'); + + user.resetPasswordToken = token; + user.resetPasswordExpires = Date.now() + 3600000; // 1 hour + await user.save(); + + const transporter = nodemailer.createTransport({ + host: 'in.mailjet.com', + port: 587, + auth: { + user: process.env.MAILJET_API_KEY, + pass: process.env.MAILJET_API_SECRET, + }, + }); + + const resetUrl = `http://localhost:5173/reset-password/${token}`; + const mailOptions = { + to: user.email, + from: 'gamestorrent2015@gmail.com', + subject: 'UMS Password Reset', + html: ` +
+
+

UMS

+

Password Reset Request [Valid for 1 hour]

+

Dear ${user.name},

+

+ You are receiving this email because we received a request to reset the password for your account. +

+

+ To complete the process, please click on the link below or copy and paste it into your browser: +

+

+ ${resetUrl} +

+

+ If you did not request this change, please ignore this email. Your password will remain unchanged. +

+

Thank you,

+

UMS Team

+
+
+ ` + }; + + transporter.sendMail(mailOptions, (err) => { + if (err) { + return res.status(500).json({ message: "Error sending email", error: err.message }); + } + res.status(200).json({ message: "Password reset link sent!" }); + }); + + } catch (error) { + res.status(500).json({ message: "Server error, please try again later" }); + } + +}; +const resetPassword = async (req, res) => { + const { token } = req.params; + const { password } = req.body; + if (!password) { + return res.status(400).json({ message: "Password is required" }); + } + + try { + const user = await usermodel.findOne({ + resetPasswordToken: token, + resetPasswordExpires: { $gt: Date.now() }, + }); + + if (!user) { + return res.status(400).json({ message: "Invalid or expired token" }); + } + + + user.password = password; + user.resetPasswordToken = undefined; + user.resetPasswordExpires = undefined; + + await user.save(); + + res.status(200).json({ message: "Password successfully reset!" }); + } catch (error) { + res.status(500).json({ message: "Server error, please try again later" }); + } +}; + const verifyPassword = async (req, res) => { + const { password } = req.body; + console.log("password is",password) + try { + const user = await usermodel.findById(req.params.id); + if (!user) return res.status(404).send('User not found'); + + const isValid = await bcrypt.compare(password, user.password); + if (!isValid) return res.status(401).send('passord is not valid'); + + res.send( isValid); + } catch (err) { + res.status(500).send(err.message); + } + } + +module.exports = { getall, getByid, updateOne,createone,deleteOne,deleteall,login, uploadImage,getAllProfessors, forgotPassword, resetPassword,verifyPassword } diff --git a/models/usersModel.js b/models/usersModel.js index 16f8a92..cd77f89 100644 --- a/models/usersModel.js +++ b/models/usersModel.js @@ -43,6 +43,8 @@ const userSchema = new mongoose.Schema({ totalScore: { type: Number, default: 0 }, }, ], + resetPasswordToken: { type: String }, + resetPasswordExpires: { type: Date }, }); userSchema.pre("save", async function (next) { diff --git a/package-lock.json b/package-lock.json index ab13990..bdff97b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "mongoose": "^8.6.1", "multer": "^1.4.4", "multer-gridfs-storage": "^5.0.2", + "nodemailer": "^6.9.15", "nodemon": "^3.1.4", "uuid": "^10.0.0", "validator": "^13.12.0" @@ -595,9 +596,9 @@ } }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -806,9 +807,9 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -816,7 +817,7 @@ "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -1847,6 +1848,15 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/nodemailer": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.15.tgz", + "integrity": "sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz", diff --git a/package.json b/package.json index d98ef61..b4b2639 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "mongoose": "^8.6.1", "multer": "^1.4.4", "multer-gridfs-storage": "^5.0.2", + "nodemailer": "^6.9.15", "nodemon": "^3.1.4", "uuid": "^10.0.0", "validator": "^13.12.0" diff --git a/routes/userRoute.js b/routes/userRoute.js index f09f23f..12a0246 100644 --- a/routes/userRoute.js +++ b/routes/userRoute.js @@ -1,6 +1,6 @@ const upload = require('../Middlewares/uploadConfig'); -const { getall, getByid, updateOne,createone,deleteOne,deleteall,login,uploadImage, getAllProfessors} = module.require("../controllers/handelUser"); +const { getall, getByid, updateOne,createone,deleteOne,deleteall,login,uploadImage, getAllProfessors, forgotPassword, resetPassword,verifyPassword} = module.require("../controllers/handelUser"); const express = module.require('express') const router=express.Router(); @@ -16,4 +16,8 @@ router.delete('/:id',deleteOne) router.delete('/',auth,restrict("admin"),deleteall) router.post('/login',login) router.post('/uploadUserImage/:id',upload.single('image'),uploadImage); +router.post('/forgot-password', forgotPassword); +router.post('/reset-password/:token', resetPassword); +router.post('/verify/:id',verifyPassword); + module.exports=router;