From 276dadab663aed4fd9375e3b9f2617a3995891e6 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 10 Jun 2024 15:54:45 -0700 Subject: [PATCH 1/7] Added recovery Request route, controller, and DB operations --- Server/controllers/authController.js | 34 +++++++++++++++++++----- Server/db/MongoDB.js | 39 +++++++++++++++++++++++----- Server/models/RecoveryToken.js | 25 ++++++++++++++++++ Server/routes/authRoute.js | 5 ++++ Server/service/jobQueue.js | 1 - 5 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 Server/models/RecoveryToken.js diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 8c242af67..dae68771f 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -13,6 +13,7 @@ const { sendEmail } = require("../utils/sendEmail"); const { registerTemplate, } = require("../utils/emailTemplates/registerTemplate"); +const RecoveryToken = require("../models/RecoveryToken"); /** * Creates and returns JWT token with an arbitrary payload @@ -97,10 +98,6 @@ const loginController = async (req, res, next) => { // Check if user exists const user = await req.db.getUserByEmail(req, res); - // If user not found, throw an error - if (!user) { - throw new Error("User not found!"); - } // Compare password const match = await user.comparePassword(req.body.password); if (!match) { @@ -145,7 +142,7 @@ const userEditController = async (req, res, next) => { try { const updatedUser = await req.db.updateUser(req, res); - res + return res .status(200) .json({ success: true, msg: "User updated", data: updatedUser }); } catch (error) { @@ -155,4 +152,29 @@ const userEditController = async (req, res, next) => { } }; -module.exports = { registerController, loginController, userEditController }; +const recoveryRequestController = async (req, res, next) => { + const email = req.body.email; + try { + const user = await req.db.getUserByEmail(req, res); + // TODO Email token to user + if (user) { + const recoveryToken = await req.db.requestRecoveryToken(req, res); + console.log(recoveryToken); + return res.status(200).json({ + success: true, + msg: "Created recovery token", + data: recoveryToken.token, + }); + } + } catch (error) { + error.service = SERVICE_NAME; + next(error); + return; + } +}; +module.exports = { + registerController, + loginController, + userEditController, + recoveryRequestController, +}; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index aabd3c384..54e6e2c0a 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -3,10 +3,8 @@ const mongoose = require("mongoose"); const UserModel = require("../models/user"); const Check = require("../models/Check"); const Alert = require("../models/Alert"); - -const verifyId = (userId, monitorId) => { - return userId.toString() === monitorId.toString(); -}; +const RecoveyToken = require("../models/RecoveryToken"); +const crypto = require("crypto"); const connect = async () => { try { @@ -44,6 +42,7 @@ const insertUser = async (req, res) => { * Get User by Email * Gets a user by Email. Not sure if we'll ever need this except for login. * If not needed except for login, we can move password comparison here + * Throws error if user not found * @async * @param {Express.Request} req * @param {Express.Response} res @@ -52,10 +51,14 @@ const insertUser = async (req, res) => { */ const getUserByEmail = async (req, res) => { try { - // Returns null if no user is found // Need the password to be able to compare, removed .select() // We can strip the hash before returing the user - return await UserModel.findOne({ email: req.body.email }); + const user = await UserModel.findOne({ email: req.body.email }); + if (user) { + return user; + } else { + throw new Error("User not found"); + } } catch (error) { throw error; } @@ -86,6 +89,29 @@ const updateUser = async (req, res) => { } }; +/** + * Request a recovery token + * @async + * @param {Express.Request} req + * @param {Express.Response} res + * @returns {Promise} + * @throws {Error} + */ +const requestRecoveryToken = async (req, res) => { + try { + // Delete any existing tokens + await RecoveyToken.deleteMany({ email: req.body.email }); + let recoveryToken = new RecoveyToken({ + email: req.body.email, + token: crypto.randomBytes(32).toString("hex"), + }); + await recoveryToken.save(); + return recoveryToken; + } catch (error) { + throw error; + } +}; + //**************************************** // Monitors //**************************************** @@ -393,6 +419,7 @@ module.exports = { insertUser, getUserByEmail, updateUser, + requestRecoveryToken, getAllMonitors, getMonitorById, getMonitorsByUserId, diff --git a/Server/models/RecoveryToken.js b/Server/models/RecoveryToken.js new file mode 100644 index 000000000..2b67a89e1 --- /dev/null +++ b/Server/models/RecoveryToken.js @@ -0,0 +1,25 @@ +const mongoose = require("mongoose"); + +const RecoveryTokenSchema = mongoose.Schema( + { + email: { + type: String, + required: true, + unique: true, + }, + token: { + type: String, + required: true, + }, + expiry: { + type: Date, + default: Date.now, + expires: 600, + }, + }, + { + timestamps: true, + } +); + +module.exports = mongoose.model("RecoveryToken", RecoveryTokenSchema); diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js index 637e4bf5e..41772c49b 100644 --- a/Server/routes/authRoute.js +++ b/Server/routes/authRoute.js @@ -5,10 +5,15 @@ const { registerController, loginController, userEditController, + recoveryRequestController, } = require("../controllers/authController"); router.post("/register", registerController); router.post("/login", loginController); router.post("/user/:userId", verifyJWT, userEditController); +//Recovery routes + +router.post("/recovery/request", recoveryRequestController); + module.exports = router; diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index 84241f443..5a9fbc6d5 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -14,7 +14,6 @@ class JobQueue { * @throws {Error} */ constructor() { - console.log(process.env.REDIS_PORT); this.queue = new Queue(QUEUE_NAME, { connection, }); From 0eda4ceb77c38a477577f3f721a0a4ffb90d809d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 10 Jun 2024 16:07:52 -0700 Subject: [PATCH 2/7] Added route for validating recovery token --- Server/controllers/authController.js | 21 +++++++++++++++++++-- Server/db/MongoDB.js | 15 +++++++++++++++ Server/routes/authRoute.js | 2 ++ 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index dae68771f..52e7b4bdc 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -153,10 +153,9 @@ const userEditController = async (req, res, next) => { }; const recoveryRequestController = async (req, res, next) => { - const email = req.body.email; try { + const email = req.body.email; const user = await req.db.getUserByEmail(req, res); - // TODO Email token to user if (user) { const recoveryToken = await req.db.requestRecoveryToken(req, res); console.log(recoveryToken); @@ -166,6 +165,23 @@ const recoveryRequestController = async (req, res, next) => { data: recoveryToken.token, }); } + // TODO Email token to user + } catch (error) { + error.service = SERVICE_NAME; + next(error); + return; + } +}; + +const validateRecoveryTokenController = async (req, res, next) => { + try { + const candidateToken = req.body.recoveryToken; + const recoveryToken = await req.db.validateRecoveryToken(req, res); + return res.status(200).json({ + success: true, + msg: "Token is valid", + data: recoveryToken, + }); } catch (error) { error.service = SERVICE_NAME; next(error); @@ -177,4 +193,5 @@ module.exports = { loginController, userEditController, recoveryRequestController, + validateRecoveryTokenController, }; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index 54e6e2c0a..781d4309a 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -112,6 +112,20 @@ const requestRecoveryToken = async (req, res) => { } }; +const validateRecoveryToken = async (req, res) => { + try { + const candidateToken = req.body.recoveryToken; + const recoveryToken = await RecoveyToken.findOne({ token: candidateToken }); + if (recoveryToken !== null) { + return recoveryToken; + } else { + throw new Error("Token not found"); + } + } catch (error) { + throw error; + } +}; + //**************************************** // Monitors //**************************************** @@ -420,6 +434,7 @@ module.exports = { getUserByEmail, updateUser, requestRecoveryToken, + validateRecoveryToken, getAllMonitors, getMonitorById, getMonitorsByUserId, diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js index 41772c49b..f300edfa9 100644 --- a/Server/routes/authRoute.js +++ b/Server/routes/authRoute.js @@ -6,6 +6,7 @@ const { loginController, userEditController, recoveryRequestController, + validateRecoveryTokenController, } = require("../controllers/authController"); router.post("/register", registerController); @@ -15,5 +16,6 @@ router.post("/user/:userId", verifyJWT, userEditController); //Recovery routes router.post("/recovery/request", recoveryRequestController); +router.post("/recovery/validate", validateRecoveryTokenController); module.exports = router; From e576514ff88f1cd0949e5e52d3e5327a01cf3856 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Jun 2024 10:36:56 -0700 Subject: [PATCH 3/7] Added reset password endpoint, updated readme --- README.md | 163 +++++++++++++++++++++++++++ Server/controllers/authController.js | 17 ++- Server/db/MongoDB.js | 34 +++++- Server/routes/authRoute.js | 4 +- 4 files changed, 209 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 979731f50..76c5f510e 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ BlueWave uptime monitoring application - POST [/api/v1/auth/register](#post-register) - POST [/api/v1/auth/login](#post-login) - POST [/api/v1/auth/user/{userId}](#post-auth-user-edit-id) + - POST [/api/v1/auth/recovery/request](#post-auth-recovery-request-id) + - POST [/api/v1/auth/recovery/validate](#post-auth-recovery-validate-id) + - POST [/api/v1/auth/recovery/reset](#post-auth-recovery-reset-id) ###### Monitors - GET [/api/v1/monitors](#get-monitors) - GET [/api/v1/monitor/{id}](#get-monitor-id) @@ -382,6 +385,166 @@ curl --request POST \ +
+POST/api/v1/auth/recovery/request + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +##### Body + +> | Name | Type | Notes | +> | ----- | -------- | ------------ | +> | email | `string` | User's email | + +###### Response Payload + +> | Type | Notes | +> | --------------- | --------------------------------------- | +> | `RecoveryToken` | Returns a recovery token if email found | + +##### Sample CURL request + +``` +curl --request POST \ + --url http://localhost:5000/api/v1/auth/recovery/request \ + --header 'Content-Type: application/json' \ + --data '{ + "email" : "ajhollid@gmail.com" +}' +``` + +###### Sample Response + +```json +{ + "success": true, + "msg": "Created recovery token", + "data": { + "email": "your_email@gmail.com", + "token": "f519da5e4a9be40cfc3c0fde97e60c0e6d17bdaa613f5ba537a45073f3865193", + "_id": "6668878263587f30748e968e", + "expiry": "2024-06-11T17:21:06.984Z", + "createdAt": "2024-06-11T17:21:06.985Z", + "updatedAt": "2024-06-11T17:21:06.985Z", + "__v": 0 + } +} +``` + +
+ +
+POST/api/v1/auth/recovery/validate + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +##### Body + +> | Name | Type | Notes | +> | ------------- | -------- | ----------------------------------- | +> | recoveryToken | `string` | Token issued in `/recovery/request` | + +###### Response Payload + +> | Type | Notes | +> | --------------- | -------------------------- | +> | `RecoveryToken` | Returns the recovery token | + +##### Sample CURL request + +``` +curl --request POST \ + --url http://localhost:5000/api/v1/auth/recovery/validate \ + --header 'Content-Type: application/json' \ + --data '{ + "recoveryToken" : "f519da5e4a9be40cfc3c0fde97e60c0e6d17bdaa613f5ba537a45073f3865193" +}' +``` + +###### Sample Response + +```json +{ + "success": true, + "msg": "Token is valid", + "data": { + "_id": "6668894263587f30748e969a", + "email": "ajhollid@gmail.com", + "token": "457d9926b24dedf613f120eeb524ef00ac45b3f0fc5c70bd25b1cc8aa83a64a0", + "expiry": "2024-06-11T17:28:34.349Z", + "createdAt": "2024-06-11T17:28:34.349Z", + "updatedAt": "2024-06-11T17:28:34.349Z", + "__v": 0 + } +} +``` + +
+ +
+POST/api/v1/auth/recovery/reset + +###### Method/Headers + +> | Method/Headers | Value | +> | -------------- | ----- | +> | Method | POST | + +##### Body + +> | Name | Type | Notes | +> | ------------- | -------- | --------------------------------------------- | +> | recoveryToken | `string` | Token issued returned by `/recovery/validate` | +> | password | `string` | User's new password` | + +###### Response Payload + +> | Type | Notes | +> | ------ | ------------------------ | +> | `User` | Returns the updated user | + +##### Sample CURL request + +``` +curl --request POST \ + --url http://localhost:5000/api/v1/auth/recovery/reset \ + --header 'Content-Type: application/json' \ + --data '{ + "recoveryToken" : "f519da5e4a9be40cfc3c0fde97e60c0e6d17bdaa613f5ba537a45073f3865193", + "password": "testtest" +}' +``` + +###### Sample Response + +```json +{ + "success": true, + "msg": "Password reset", + "data": { + "_id": "66675891cb17336d84c25d9f", + "firstname": "User First Name", + "lastname": "User Last Name", + "email": "your_email@gmail.com", + "isActive": true, + "isVerified": false, + "createdAt": "2024-06-10T19:48:33.863Z", + "updatedAt": "2024-06-11T17:21:22.289Z", + "__v": 0 + } +} +``` + +
+ --- ###### Monitors diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 52e7b4bdc..6c532e565 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -154,15 +154,13 @@ const userEditController = async (req, res, next) => { const recoveryRequestController = async (req, res, next) => { try { - const email = req.body.email; const user = await req.db.getUserByEmail(req, res); if (user) { const recoveryToken = await req.db.requestRecoveryToken(req, res); - console.log(recoveryToken); return res.status(200).json({ success: true, msg: "Created recovery token", - data: recoveryToken.token, + data: recoveryToken, }); } // TODO Email token to user @@ -175,8 +173,8 @@ const recoveryRequestController = async (req, res, next) => { const validateRecoveryTokenController = async (req, res, next) => { try { - const candidateToken = req.body.recoveryToken; const recoveryToken = await req.db.validateRecoveryToken(req, res); + // TODO Redirect user to reset password after validating token return res.status(200).json({ success: true, msg: "Token is valid", @@ -188,10 +186,21 @@ const validateRecoveryTokenController = async (req, res, next) => { return; } }; + +const resetPasswordController = async (req, res, next) => { + try { + user = await req.db.resetPassword(req, res); + res.status(200).json({ success: true, msg: "Password reset", data: user }); + } catch (error) { + error.service = SERVICE_NAME; + next(error); + } +}; module.exports = { registerController, loginController, userEditController, recoveryRequestController, validateRecoveryTokenController, + resetPasswordController, }; diff --git a/Server/db/MongoDB.js b/Server/db/MongoDB.js index 781d4309a..f64059342 100644 --- a/Server/db/MongoDB.js +++ b/Server/db/MongoDB.js @@ -3,7 +3,7 @@ const mongoose = require("mongoose"); const UserModel = require("../models/user"); const Check = require("../models/Check"); const Alert = require("../models/Alert"); -const RecoveyToken = require("../models/RecoveryToken"); +const RecoveryToken = require("../models/RecoveryToken"); const crypto = require("crypto"); const connect = async () => { @@ -100,8 +100,8 @@ const updateUser = async (req, res) => { const requestRecoveryToken = async (req, res) => { try { // Delete any existing tokens - await RecoveyToken.deleteMany({ email: req.body.email }); - let recoveryToken = new RecoveyToken({ + await RecoveryToken.deleteMany({ email: req.body.email }); + let recoveryToken = new RecoveryToken({ email: req.body.email, token: crypto.randomBytes(32).toString("hex"), }); @@ -115,7 +115,9 @@ const requestRecoveryToken = async (req, res) => { const validateRecoveryToken = async (req, res) => { try { const candidateToken = req.body.recoveryToken; - const recoveryToken = await RecoveyToken.findOne({ token: candidateToken }); + const recoveryToken = await RecoveryToken.findOne({ + token: candidateToken, + }); if (recoveryToken !== null) { return recoveryToken; } else { @@ -126,6 +128,29 @@ const validateRecoveryToken = async (req, res) => { } }; +const resetPassword = async (req, res) => { + try { + const newPassword = req.body.password; + + // Validate token again + const recoveryToken = await validateRecoveryToken(req, res); + const user = await UserModel.findOne({ email: recoveryToken.email }); + + if (user !== null) { + user.password = newPassword; + await user.save(); + await RecoveryToken.deleteMany({ email: recoveryToken.email }); + // Fetch the user again without the password + const userWithoutPassword = await UserModel.findOne({ + email: recoveryToken.email, + }).select("-password"); + return userWithoutPassword; + } else { + throw new Error("User not found"); + } + } catch (error) {} +}; + //**************************************** // Monitors //**************************************** @@ -435,6 +460,7 @@ module.exports = { updateUser, requestRecoveryToken, validateRecoveryToken, + resetPassword, getAllMonitors, getMonitorById, getMonitorsByUserId, diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js index f300edfa9..1049659fa 100644 --- a/Server/routes/authRoute.js +++ b/Server/routes/authRoute.js @@ -7,15 +7,17 @@ const { userEditController, recoveryRequestController, validateRecoveryTokenController, + resetPasswordController, } = require("../controllers/authController"); +//Auth routes router.post("/register", registerController); router.post("/login", loginController); router.post("/user/:userId", verifyJWT, userEditController); //Recovery routes - router.post("/recovery/request", recoveryRequestController); router.post("/recovery/validate", validateRecoveryTokenController); +router.post("/recovery/reset/", resetPasswordController); module.exports = router; From 30be07921a0775f6b135bdf506d8e85354377f3a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Jun 2024 11:19:50 -0700 Subject: [PATCH 4/7] Added token to route for set-new-password page --- Client/src/App.jsx | 2 +- Client/src/Pages/SetNewPassword/index.jsx | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Client/src/App.jsx b/Client/src/App.jsx index 6f64d45da..16774c60a 100644 --- a/Client/src/App.jsx +++ b/Client/src/App.jsx @@ -34,7 +34,7 @@ function App() { } /> } /> } /> - } /> + } /> } diff --git a/Client/src/Pages/SetNewPassword/index.jsx b/Client/src/Pages/SetNewPassword/index.jsx index 31e213c10..fc453cde1 100644 --- a/Client/src/Pages/SetNewPassword/index.jsx +++ b/Client/src/Pages/SetNewPassword/index.jsx @@ -6,8 +6,10 @@ import PasswordTextField from "../../Components/TextFields/Password/PasswordText import Check from "../../Components/Check/Check"; import Button from "../../Components/Button"; import LeftArrow from "../../assets/Images/arrow-left.png"; +import { useParams } from "react-router-dom"; const SetNewPassword = () => { + const { token } = useParams(); return (
From 105852aed2ad3c4aed4df4c2681c6540d6f4bc8e Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Jun 2024 11:41:57 -0700 Subject: [PATCH 5/7] Added frontend howst ENV, updated readme --- README.md | 1 + Server/controllers/authController.js | 36 ++++++++++++++++- Server/utils/sendEmail.js | 59 ++++++++++++++++------------ 3 files changed, 69 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 76c5f510e..02890e230 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ Configure the server with the following environmental variables: | ENV Variable Name | Required/Optional | Type | Description | Accepted Values | | -------------------- | ----------------- | --------- | ------------------------------------------------------------------------------------------- | ------------------- | +| CLIENT_HOST | Required | `string` | Frontend Host | | | JWT_SECRET | Required | `string` | JWT secret | | | DB_TYPE | Optional | `string` | Specify DB to use | `MongoDB \| FakeDB` | | DB_CONNECTION_STRING | Required | `string` | Specifies URL for MongoDB Database | | diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 6c532e565..8d66a36df 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -13,7 +13,6 @@ const { sendEmail } = require("../utils/sendEmail"); const { registerTemplate, } = require("../utils/emailTemplates/registerTemplate"); -const RecoveryToken = require("../models/RecoveryToken"); /** * Creates and returns JWT token with an arbitrary payload @@ -88,7 +87,6 @@ const registerController = async (req, res, next) => { * @param {Express.Request} req * @param {Express.Response} res * @returns {Promise} - * @throws {Error} */ const loginController = async (req, res, next) => { try { @@ -152,11 +150,26 @@ const userEditController = async (req, res, next) => { } }; +/** + * Returns a recovery token + * @async + * @param {Express.Request} req + * @property {Object} req.body + * @property {string} req.body.email + * @param {Express.Response} res + * @returns {Promise} + */ const recoveryRequestController = async (req, res, next) => { try { const user = await req.db.getUserByEmail(req, res); if (user) { const recoveryToken = await req.db.requestRecoveryToken(req, res); + await sendEmail( + [req.body.email], + "Uptime Monitor Password Recovery", + `Click here to reset your password`, + `Recovery token: ${recoveryToken.token}` + ); return res.status(200).json({ success: true, msg: "Created recovery token", @@ -171,6 +184,15 @@ const recoveryRequestController = async (req, res, next) => { } }; +/** + * Returns a recovery token + * @async + * @param {Express.Request} req + * @property {Object} req.body + * @property {string} req.body.token + * @param {Express.Response} res + * @returns {Promise} + */ const validateRecoveryTokenController = async (req, res, next) => { try { const recoveryToken = await req.db.validateRecoveryToken(req, res); @@ -187,6 +209,16 @@ const validateRecoveryTokenController = async (req, res, next) => { } }; +/** + * Returns an updated user + * @async + * @param {Express.Request} req + * @property {Object} req.body + * @property {string} req.body.token + * @property {string} req.body.password + * @param {Express.Response} res + * @returns {Promise} + */ const resetPasswordController = async (req, res, next) => { try { user = await req.db.resetPassword(req, res); diff --git a/Server/utils/sendEmail.js b/Server/utils/sendEmail.js index d60900383..306bec2c5 100644 --- a/Server/utils/sendEmail.js +++ b/Server/utils/sendEmail.js @@ -1,7 +1,7 @@ -const sgMail = require('@sendgrid/mail') -const logger = require('./logger') -sgMail.setApiKey(process.env.SENDGRID_API_KEY) -const SERVICE_NAME = 'Email_Service'; +const sgMail = require("@sendgrid/mail"); +const logger = require("./logger"); +sgMail.setApiKey(process.env.SENDGRID_API_KEY); +const SERVICE_NAME = "Email_Service"; /** * @async * @function @@ -12,25 +12,34 @@ const SERVICE_NAME = 'Email_Service'; * @example * await sendEmail(['veysel@bluewavelabs.ca','alex@bluewavelabs.ca'],'Testing Email Service','

BlueWaveLabs

','Testing Email Service') */ -const sendEmail = async (receivers,subject, contentHTML, contentText = null ) => { - const msg = { - to: receivers, - from: { - name: 'Uptime System', - email: process.env.SYSTEM_EMAIL_ADDRESS // must be verified email by sendgrid - }, - subject: subject, - text: contentText || contentHTML, - html: contentHTML, - } - try { - await sgMail.send(msg); - logger.info(`Emails sent to receivers:${receivers} with the subject:${subject}`,{service:SERVICE_NAME}) - } catch (error) { - logger.error(`Sending Email action failed, ERROR:${error}`, { service: SERVICE_NAME }); - } -} +const sendEmail = async ( + receivers, + subject, + contentHTML, + contentText = null +) => { + const msg = { + to: receivers, + from: { + name: "Uptime System", + email: process.env.SYSTEM_EMAIL_ADDRESS, // must be verified email by sendgrid + }, + subject: subject, + text: contentText || contentHTML, + html: contentHTML, + }; + try { + await sgMail.send(msg); + logger.info( + `Emails sent to receivers:${receivers} with the subject:${subject}`, + { service: SERVICE_NAME } + ); + } catch (error) { + logger.error(`Sending Email action failed, ERROR:${error}`, { + service: SERVICE_NAME, + }); + throw error; + } +}; - - -module.exports = {sendEmail} \ No newline at end of file +module.exports = { sendEmail }; From 324b52adb3d56a384ca8c689143aa7577ae9ab06 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Jun 2024 12:00:58 -0700 Subject: [PATCH 6/7] Removed token from recovery request response payload --- Server/controllers/authController.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 8d66a36df..8cad2ad59 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -173,7 +173,6 @@ const recoveryRequestController = async (req, res, next) => { return res.status(200).json({ success: true, msg: "Created recovery token", - data: recoveryToken, }); } // TODO Email token to user From d0ec39027be322f189ad7ece4a6db67d3e38e46a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Jun 2024 12:02:35 -0700 Subject: [PATCH 7/7] Removed recovery token from validate token response payload --- Server/controllers/authController.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 8cad2ad59..6c035aa5c 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -199,7 +199,6 @@ const validateRecoveryTokenController = async (req, res, next) => { return res.status(200).json({ success: true, msg: "Token is valid", - data: recoveryToken, }); } catch (error) { error.service = SERVICE_NAME;