From ac404a0ac5d91bf4d2cd846364023672dabba89e Mon Sep 17 00:00:00 2001 From: Johnathon Slightham <31053827+jslightham@users.noreply.github.com> Date: Wed, 19 May 2021 15:53:22 -0400 Subject: [PATCH] Added email subjects, and favourites --- README.md | 2 + category.route.js | 6 +- config.js | 8 +-- index.js | 4 ++ package-lock.json | 5 ++ package.json | 1 + templates/forgotPassword.txt | 1 + templates/newUser.txt | 3 +- user.model.js | 3 + user.route.js | 111 +++++++++++++++++++++++++++++++---- utils.js | 62 +++++++++++-------- 11 files changed, 161 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 795a1a1..9f3d7fc 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ Emails are stored in the [`templates`](https://github.com/jslightham/kno-logic-a Any portion of the message can be replaced when sending the email. The convention used in all default email templates is `%replace_string%`. The replace string does not matter since when calling the sendMail function, replacements is an array of data with the form `{from: "%replace_string%", to: "Username"}`. +Email subjects are the first line of the file, which are removed from the email body. + ## Configuration Configuration for the application is done in the [`config.js`](https://github.com/jslightham/kno-logic-api/blob/main/config.js) file. diff --git a/category.route.js b/category.route.js index a21f76a..5508ff4 100644 --- a/category.route.js +++ b/category.route.js @@ -27,7 +27,7 @@ categoryRoutes.route('/create').post((req, res) => { res.json(c); }) .catch((e) => { - console.log(e); + console.error(e); res.status(500).send("Error creating category"); }); } else { @@ -46,7 +46,7 @@ categoryRoutes.route('/create').post((req, res) => { categoryRoutes.route('/all').get((req, res) => { Category.find({}, (err, cArr) => { if (err) { - console.log(err); + console.error(err); res.status(500).send("Error getting categories"); return; } @@ -62,7 +62,7 @@ categoryRoutes.route('/all').get((req, res) => { categoryRoutes.route('/posts').get((req, res) => { Post.find({}, (err, postArr) => { if (err) { - console.log(err); + console.error(err); res.status(500).send("Error getting posts"); return; } diff --git a/config.js b/config.js index 5c53dcc..6df67f6 100644 --- a/config.js +++ b/config.js @@ -3,11 +3,11 @@ var config = {}; // Mailer settings config.mail = {}; config.mail.host = "localhost"; -config.mail.port = "465"; +config.mail.port = "587"; config.mail.secure = false; -config.mail.user = "username"; -config.mail.pass = "pass"; -config.mail.from = "" +config.mail.user = "email"; +config.mail.pass = "password"; +config.mail.from = "name" // Session purge settings config.maxSessionLength = 30; diff --git a/index.js b/index.js index e45073c..e5bcc41 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,7 @@ const config = require('./DB.js'); const userRoutes = require('./user.route'); const postRoutes = require('./post.route'); const categoryRoutes = require('./category.route'); +const mongoSanitize = require('express-mongo-sanitize'); console.log("Starting Kno-Logic Backend Server"); @@ -32,6 +33,9 @@ app.use(cors()); app.use(express.urlencoded({ extended: true })) app.use(express.json()); +// Sanitize data to prevent NoSQL injections +app.use(mongoSanitize()); + // Express routes app.use('/user', userRoutes); app.use('/post', postRoutes); diff --git a/package-lock.json b/package-lock.json index 1522cf4..b19cea3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -622,6 +622,11 @@ "vary": "~1.1.2" } }, + "express-mongo-sanitize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/express-mongo-sanitize/-/express-mongo-sanitize-2.1.0.tgz", + "integrity": "sha512-ELGeH/Tx+kJGn3klCzSmOewfN1ezJQrkqzq83dl/K3xhd5PUbvLtiD5CiuYRmQfoZPL4rUEVjANf/YjE2BpTWQ==" + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", diff --git a/package.json b/package.json index e48ca55..a9b1e40 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "cors": "^2.8.5", "cron": "^1.8.2", "express": "^4.17.1", + "express-mongo-sanitize": "^2.1.0", "log-timestamp": "^0.3.0", "mongoose": "^5.12.7", "nodemailer": "^6.6.0" diff --git a/templates/forgotPassword.txt b/templates/forgotPassword.txt index 1ef7baa..90bd3ce 100644 --- a/templates/forgotPassword.txt +++ b/templates/forgotPassword.txt @@ -1,3 +1,4 @@ +Kno-Logic Password Reset Hello %name%, Someone has requested a password reset for the account connected to your email. diff --git a/templates/newUser.txt b/templates/newUser.txt index 746b1ec..fb93138 100644 --- a/templates/newUser.txt +++ b/templates/newUser.txt @@ -1,3 +1,4 @@ +Welcome to Kno-Logic! Hello %name%, -Welcome to kno-logic! \ No newline at end of file +Welcome to the kno-logic app. Thank you for... \ No newline at end of file diff --git a/user.model.js b/user.model.js index 33c5352..7635c1f 100644 --- a/user.model.js +++ b/user.model.js @@ -14,6 +14,9 @@ let User = new Schema({ }, permission: { type: Number + }, + favorites: { + type: Array } }, { collection: 'users' diff --git a/user.route.js b/user.route.js index 6eb2feb..877d275 100644 --- a/user.route.js +++ b/user.route.js @@ -1,4 +1,4 @@ -const utils = require ('./utils'); +const utils = require('./utils'); const express = require('express'); const bcrypt = require('bcrypt'); const userRoutes = express.Router(); @@ -8,6 +8,7 @@ const sessionLength = 25; let Session = require('./session.model'); let User = require('./user.model'); +let Post = require('./post.model'); /* POST - /user/create @@ -26,19 +27,16 @@ userRoutes.route('/create').post((req, res) => { res.status(401).send("Empty fields"); return; } - console.log(req.body); let u = new User(req.body); - // TODO: Look for a different encryption method that can scale more easily bcrypt.hash(u.password, saltRounds, (err, hash) => { if (err) { - console.log(err); + console.error(err); res.status(500).send("Error creating user");; } else { - console.log(hash); u.password = hash; User.find({ email: u.email }, (err, arr) => { if (err) { - console.log(err); + console.error(err); res.status(500).send("Error creating user"); } // Account already exists @@ -67,7 +65,6 @@ userRoutes.route('/create').post((req, res) => { 401 - Incorrect */ userRoutes.route('/login').post((req, res) => { - console.log(req.body); if (!req.body) { res.status(401).send("Missing body"); return; @@ -80,7 +77,7 @@ userRoutes.route('/login').post((req, res) => { } User.findOne({ email: req.body.email }, (err, u) => { if (err) { - console.log(err); + console.error(err); res.status(500).send("Error logging in user"); return; } @@ -92,7 +89,7 @@ userRoutes.route('/login').post((req, res) => { bcrypt.compare(req.body.password, u.password, (err, result) => { if (err) { - console.log(err); + console.error(err); res.status(500).send("Error logging in user"); return; } @@ -125,9 +122,9 @@ userRoutes.route('/login').post((req, res) => { 400 - No session exists */ userRoutes.route('/logout').post((req, res) => { - Session.findOne({sessionId: req.body.sessionId}, (err, sess) => { + Session.findOne({ sessionId: req.body.sessionId }, (err, sess) => { if (err) { - console.log(err); + console.error(err); res.status(500).send("Error logging out"); return; } @@ -141,7 +138,8 @@ userRoutes.route('/logout').post((req, res) => { .then(() => { res.status(201).send("Success deleting session"); }) - .catch(() => { + .catch((e) => { + console.error(e); res.status(500).send("Error logging out"); }); @@ -150,6 +148,95 @@ userRoutes.route('/logout').post((req, res) => { // TODO: Add forgotten password route +/* + POST - /user/favorite/add + Add a favorite article + Response: 200 - OK + 401 - Unauthorized +*/ +userRoutes.route('/favorite/add').post((req, res) => { + utils.checkSession(req.body.userId, req.body.sessionId, valid => { + if (valid) { + User.findById(req.body.userId, (err, user) => { + if (err) { + console.error(err); + res.status(500).send("Error adding article"); + return; + } + user.favorites.push(req.body.postId); + user.save() + .then(() => { + res.status(201).send("Success saving article"); + }) + .catch((e) => { + console.error(e); + res.status(500).send("Error saving article"); + }); + }) + } else { + res.status(401).send("Unauthorized"); + } + }) +}) + +/* + POST - /user/favorite/remove + Remove a favorite article + Response: 200 - OK + 401 - Unauthorized +*/ +userRoutes.route('/favorite/remove').post((req, res) => { + utils.checkSession(req.body.userId, req.body.sessionId, valid => { + if (valid) { + User.findById(req.body.userId, (err, user) => { + if (err) { + console.error(err); + res.status(500).send("Error removing article"); + return; + } + user.favorites = utils.removeValue(user.favorites, req.body.articleId); + user.save() + .then(() => { + res.status(201).send("Success removing article"); + }) + .catch((e) => { + console.error(e); + res.status(500).send("Error removing article"); + }); + }) + } else { + res.status(401).send("Unauthorized"); + } + }) +}) + +/* + POST - /user/favorite/get + Get all favorite articles + Response: 200 - OK + 401 - Unauthorized +*/ +userRoutes.route('/favorite/get').post((req, res) => { + utils.checkSession(req.body.userId, req.body.sessionId, valid => { + if (valid) { + User.findById(req.body.userId, (err, user) => { + if (err) { + console.error(err); + res.status(500).send("Error removing article"); + return; + } + console.log(user.favorites); + Post.find({ '_id': { $in: user.favorites } }, (err, postArray) => { + res.json(postArray); + }) + }) + } else { + res.status(401).send("Unauthorized"); + } + }) +}) + + module.exports = userRoutes; function generateSession() { diff --git a/utils.js b/utils.js index 736631c..53181f3 100644 --- a/utils.js +++ b/utils.js @@ -43,8 +43,8 @@ const loadDefaultTemplates = () => { } else { let newMsg = Message(); newMsg.name = name; - newMsg.subject = "Email From Kno-Logic" //TODO: Should load these from config.js - newMsg.body = data; + newMsg.subject = data.substring(0, data.indexOf("\n")); + newMsg.body = data.substring(data.indexOf("\n") + 1); newMsg.save() .then(() => { console.log("Loaded " + name + " message"); @@ -68,37 +68,40 @@ const sendMail = async (user, message, replacements) => { let transporter = nodemailer.createTransport({ host: config.mail.host, port: config.mail.port, - secure: config.mail.secure, + secure: config.mail.secure, auth: { - user: config.mail.user, - pass: config.mail.pass, + user: config.mail.user, + pass: config.mail.pass, }, }); + Message.findOne({ name: message }, (err, message) => { + let msgBody = message.body; - let msgBody = message.body; + for (let i = 0; i < replacements.length; i++) { + msgBody = msgBody.replace(replacements[i].from, replacements[i].to); + } - for (let i = 0; i < replacements.length; i++) { - msgBody = msgBody.replace(replacements[i].from, replacements[i].to); - } + console.log(message); - try { - await transporter.sendMail({ - from: config.mail.from, - to: user.email, - subject: message.subject, - text: msgBody, - }); - - } catch (error) { - console.log("Error sending mail: ") - console.error(error); - } + try { + transporter.sendMail({ + from: config.mail.from, + to: user.email, + subject: message.subject, + text: msgBody, + }); + + } catch (error) { + console.log("Error sending mail: ") + console.error(error); + } + }) } // checkSession(userId, sessionId) checks if the sessionId is valid for the user const checkSession = (userId, sessionId, f) => { - Session.find({userId: userId, sessionId: sessionId }, (err, res) => { + Session.find({ userId: userId, sessionId: sessionId }, (err, res) => { if (res) { f(true); return; @@ -110,7 +113,7 @@ const checkSession = (userId, sessionId, f) => { // isAdmin(userId) checks if the user with userId is an administrator const isAdmin = (userId, f) => { User.findById(userId, (err, res) => { - if(res.permission == 1) { + if (res.permission == 1) { f(true); return; } @@ -124,11 +127,19 @@ function dateToEpoch(d) { if (d) { // When comparing js dates, the timezone does not matter // ex. May 17 EDT == May 17 GMT, May 17 EDT != May 18 GMT - return d.setHours(0,0,0,0); + return d.setHours(0, 0, 0, 0); } else { return null; } - } +} + +// removeValue(array, item) remove item from the array +function removeValue(array, item) { + var index = array.indexOf(item); + if (index !== -1) { + array.splice(index, 1); + } +} module.exports.purgeSessions = purgeSessions; module.exports.loadDefaultTemplates = loadDefaultTemplates; @@ -136,3 +147,4 @@ module.exports.sendMail = sendMail; module.exports.checkSession = checkSession; module.exports.isAdmin = isAdmin; module.exports.dateToEpoch = dateToEpoch; +module.exports.removeValue = removeValue;