From a40b6d514bd8330a78a23ca44349861a39b8ce18 Mon Sep 17 00:00:00 2001 From: knighthawk4227 Date: Tue, 6 May 2025 23:33:05 -0700 Subject: [PATCH] forgot password js --- app.js | 25 ++++++- package-lock.json | 7 ++ package.json | 1 + src/auth/forgotPass.js | 55 ++++++++++++++ src/router/user.js | 150 +++++++++++++++++++-------------------- src/views/forgotPass.ejs | 25 +++++++ src/views/signup.ejs | 92 ++++++++++++------------ 7 files changed, 231 insertions(+), 124 deletions(-) create mode 100644 src/auth/forgotPass.js create mode 100644 src/views/forgotPass.ejs diff --git a/app.js b/app.js index fa59c80..8b23e80 100644 --- a/app.js +++ b/app.js @@ -71,7 +71,30 @@ app.get('/aboutUs', (req, res) => { return res.status(status.Ok); }); -app.post('/api/location', async (req,res) => { +app.get('/forgotPassword', (req, res) => { + res.render('forgotPass'); + return res.status(status.Ok); +}); + +app.get('/reset/:token', async (req, res) => { + const token = req.params.token; + + const user = await users.findOne({ + resetToken: token, + resetTokenExpires: { $gt: Date.now() }, + }); + + if (!user) { + req.session.error = 'reset link not valid or has expired'; + return res.redirect('/forgotPassword'); + } + res.render('resetPassword', { + token: token, + errMessage: req.session.error || '', + }); +}); + +app.post('/api/location', async (req, res) => { const { latitude, longitude } = req.body; const response = await fetch(`https://maps.googleapis.com/maps/api/geocode/json?latlng=${latitude},${longitude}&result_type=country&key=${process.env.geolocation_api}`); diff --git a/package-lock.json b/package-lock.json index 95750e3..a416b45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "bcrypt": "^5.1.1", "connect-mongo": "^5.1.0", + "crypto": "^1.0.1", "dotenv": "^16.5.0", "ejs": "^3.1.10", "express": "^5.1.0", @@ -483,6 +484,12 @@ "node": ">=6.6.0" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", diff --git a/package.json b/package.json index 4f1048f..08ad27d 100755 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "dependencies": { "bcrypt": "^5.1.1", "connect-mongo": "^5.1.0", + "crypto": "^1.0.1", "dotenv": "^16.5.0", "ejs": "^3.1.10", "express": "^5.1.0", diff --git a/src/auth/forgotPass.js b/src/auth/forgotPass.js new file mode 100644 index 0000000..2f079c1 --- /dev/null +++ b/src/auth/forgotPass.js @@ -0,0 +1,55 @@ +const express = require('express'); +const { server } = require('ws'); +const crypto = require('crypto'); +const nodeMail = require('nodemailer'); +require('dotenv').config(); + +const transporter = nodeMail.createTransport({ + service: 'gmail', + auth: { + user: process.env.EMAIL_USER, + pass: process.env.PASS, + } +}); + +module.exports = (users) => { + const router = express.Router(); + + + router.post('/resetPass', async (req, res) => { + const { email } = req.body; + const user = await users.findOne({ email }); + + if (!user) { + req.session.error = 'No user found' + return res.redirect('/forgotPassword'); + } + + const token = crypto.randomBytes(32).toString('hex'); + const expiration = Date.now() + 360000; + + await users.updateOne({ email }, { + $set: { resetToken: token, resetTokenExpires: expiration } + }); + + const resetUrl = `http://localhost:3000/reset/${token}`; + + const mailSend = { + from: process.env.EMAIL_USER, + to: email, + subject: 'Password reset', + text: `reset your password here ${resetUrl} \n this link will expire within 1 hour`, + + }; + + try { + await transporter.sendMail(mailSend); + req.session.reset = 'Reset link sent Check your email'; + res.redirect('/login'); + } catch (err) { + console.log('there was an error', err); + res.status(500).send('email failed to send try again'); + } + }); + return router; +} diff --git a/src/router/user.js b/src/router/user.js index 9daa7cb..39fb28b 100644 --- a/src/router/user.js +++ b/src/router/user.js @@ -5,7 +5,7 @@ const salt = 12; module.exports = (middleware, users, plans) => { const router = require("express").Router(); - + router.use(middleware); router.get('/home', async (req, res) => { @@ -22,17 +22,17 @@ module.exports = (middleware, users, plans) => { if (!req.session.email) { return res.status(status.Unauthorized).redirect('/login'); } - + try { // console.log(req.user.email); - const userPlans = await plans.find({userEmail: req.user.email }).toArray(); + const userPlans = await plans.find({ userEmail: req.user.email }).toArray(); // console.log(userPlans); res.render('plans', { user: req.user, plans: userPlans }); - + } catch (err) { console.error("Error fetching plans:", err); req.session.errMessage = "Could not load your plans. Please try again."; @@ -42,7 +42,7 @@ module.exports = (middleware, users, plans) => { router.get('/newPlan', (req, res) => { const errMessage = req.session.errMessage; - req.session.errMessage = ""; + req.session.errMessage = ""; res.render('newPlan', { user: req.user, errMessage: errMessage }); }); @@ -51,45 +51,45 @@ module.exports = (middleware, users, plans) => { return res.status(status.Unauthorized).redirect('/login'); } - const planSchema = joi.object({ - name: joi.string().min(3).max(100).required(), - retirementAge: joi.number().min(18).max(120).required(), - retirementExpenses: joi.number().min(0).required(), - retirementAssets: joi.number().min(0).required(), - retirementLiabilities: joi.number().min(0).required(), - }); - - const validationOptions = { convert: true, abortEarly: false }; - const { error, value } = planSchema.validate(req.body, validationOptions); - - if (error) { - console.error("Plan validation error:", error.details); - req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); - res.status(status.BadRequest).redirect("/newPlan"); - return; - } - const newPlan = { - userEmail: req.user.email, - name: value.name, - retirementAge: value.retirementAge, - retirementExpenses: value.retirementExpenses, - retirementAssets: value.retirementAssets, - retirementLiabilities: value.retirementLiabilities, - progress: "0%" - }; - - try{ - await plans.insertOne(newPlan); - req.session.errMessage = ""; - res.redirect('/plans'); - } - catch(err){ - console.error("Error saving plan:", err); - req.session.errMessage = "An error occurred while saving your plan. Please try again."; - res.status(status.InternalServerError).redirect("/newPlan"); - } + const planSchema = joi.object({ + name: joi.string().min(3).max(100).required(), + retirementAge: joi.number().min(18).max(120).required(), + retirementExpenses: joi.number().min(0).required(), + retirementAssets: joi.number().min(0).required(), + retirementLiabilities: joi.number().min(0).required(), }); + const validationOptions = { convert: true, abortEarly: false }; + const { error, value } = planSchema.validate(req.body, validationOptions); + + if (error) { + console.error("Plan validation error:", error.details); + req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); + res.status(status.BadRequest).redirect("/newPlan"); + return; + } + const newPlan = { + userEmail: req.user.email, + name: value.name, + retirementAge: value.retirementAge, + retirementExpenses: value.retirementExpenses, + retirementAssets: value.retirementAssets, + retirementLiabilities: value.retirementLiabilities, + progress: "0%" + }; + + try { + await plans.insertOne(newPlan); + req.session.errMessage = ""; + res.redirect('/plans'); + } + catch (err) { + console.error("Error saving plan:", err); + req.session.errMessage = "An error occurred while saving your plan. Please try again."; + res.status(status.InternalServerError).redirect("/newPlan"); + } + }); + router.get('/more', (req, res) => { res.render('more', { user: req.user }); return res.status(status.Ok); @@ -107,13 +107,13 @@ module.exports = (middleware, users, plans) => { router.get('/questionnaire', (req, res) => { const errMessage = req.session.errMessage; - req.session.errMessage = ""; + req.session.errMessage = ""; res.render('questionnaire', { user: req.user, errMessage: errMessage }); }); router.post('/questionnaire', (req, res) => { // console.log("Questionnaire POST body:", req.body); - + const questionnaireSchema = joi.object({ dob: joi.date().required(), education: joi.string().valid('primary', 'secondary', 'tertiary', 'postgraduate').required(), @@ -123,22 +123,22 @@ module.exports = (middleware, users, plans) => { assets: joi.number().min(0).required(), liabilities: joi.number().min(0).required(), }); - - const validationOptions = { convert: true, abortEarly: false }; + + const validationOptions = { convert: true, abortEarly: false }; const { error, value } = questionnaireSchema.validate(req.body, validationOptions); - - if (error) { + + if (error) { console.error("Questionnaire validation error:", error.details); - req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); - res.status(status.BadRequest).redirect("/questionnaire"); - return; + req.session.errMessage = "Invalid input: " + error.details.map(d => d.message.replace(/"/g, '')).join(', '); + res.status(status.BadRequest).redirect("/questionnaire"); + return; } - - users.updateOne( - { email: req.session.email }, - { - $set: { - financialData: true, + + users.updateOne( + { email: req.session.email }, + { + $set: { + financialData: true, dob: value.dob, education: value.education, maritalStatus: value.maritalStatus, @@ -146,26 +146,26 @@ module.exports = (middleware, users, plans) => { expenses: value.expenses, assets: value.assets, liabilities: value.liabilities, - } + } } - ).then((result) => { + ).then((result) => { if (result.matchedCount === 0) { console.log(`User not found during questionnaire update: ${req.session.email}`); - req.session.errMessage = "User session invalid. Please log in again."; - res.status(status.NotFound).redirect("/login"); + req.session.errMessage = "User session invalid. Please log in again."; + res.status(status.NotFound).redirect("/login"); return; } if (result.modifiedCount === 0 && result.matchedCount === 1) { console.log(`User questionnaire data unchanged (already up-to-date): ${req.session.email}`); } - - req.session.errMessage = ""; - res.status(status.Ok).redirect("/home"); - - }).catch(err => { + + req.session.errMessage = ""; + res.status(status.Ok).redirect("/home"); + + }).catch(err => { console.error("Error updating questionnaire in database:", err); req.session.errMessage = "An error occurred while saving your information. Please try again."; - res.status(status.InternalServerError).redirect("/questionnaire"); + res.status(status.InternalServerError).redirect("/questionnaire"); }); }); @@ -181,7 +181,7 @@ module.exports = (middleware, users, plans) => { if (valid.err) { req.session.errMessage = "Invalid input", - res.status(status.BadRequest); + res.status(status.BadRequest); return res.redirect("/profile"); } @@ -205,20 +205,20 @@ module.exports = (middleware, users, plans) => { ).then((result) => { if (result.matchedCount === 0) { console.log(`User not found during account update: ${req.session.email}`); - req.session.errMessage = "User session invalid. Please log in again."; - res.status(status.NotFound).redirect("/login"); + req.session.errMessage = "User session invalid. Please log in again."; + res.status(status.NotFound).redirect("/login"); return; } if (result.modifiedCount === 0 && result.matchedCount === 1) { console.log(`User account data unchanged (already up-to-date): ${req.session.email}`); } - - req.session.errMessage = ""; - return res.status(status.Ok).redirect("/profile"); - }).catch(err => { + + req.session.errMessage = ""; + return res.status(status.Ok).redirect("/profile"); + }).catch(err => { console.error("Error updating account in database:", err); req.session.errMessage = "An error occurred while saving your information. Please try again."; - return res.status(status.InternalServerError).redirect("/profile"); + return res.status(status.InternalServerError).redirect("/profile"); }); }); diff --git a/src/views/forgotPass.ejs b/src/views/forgotPass.ejs new file mode 100644 index 0000000..cd3a15f --- /dev/null +++ b/src/views/forgotPass.ejs @@ -0,0 +1,25 @@ +<%- include("./partials/fileHeader") %> + +
+
+
+
+

Reset Password

+
+
+ + +
+
+ +
+
+
+
+
+ + <%- include("./partials/footer") %> diff --git a/src/views/signup.ejs b/src/views/signup.ejs index 1234a2b..b3c7586 100644 --- a/src/views/signup.ejs +++ b/src/views/signup.ejs @@ -1,52 +1,48 @@ <%- include("./partials/fileHeader") %> -<%- include("./partials/headerStart") %> + <%- include("./partials/headerStart") %> -
-
-
-
-

Signup

-
-
- - -
-
- - -
-
- - -
-
- - - - -
-
<%= errMessage %>
-
- -
-
- - Login -
+
+
+ +
+

Signup

+
+
+ + +
+
+ + +
+
+ + + + +
+
+ <%= errMessage %> +
+
+ +
+
+ + Login +
+
+
- -
-
+ -<%- include("./partials/footer") %> \ No newline at end of file + <%- include("./partials/footer") %>