diff --git a/config.js b/config.js new file mode 100644 index 0000000..5c53dcc --- /dev/null +++ b/config.js @@ -0,0 +1,16 @@ +var config = {}; + +// Mailer settings +config.mail = {}; +config.mail.host = "localhost"; +config.mail.port = "465"; +config.mail.secure = false; +config.mail.user = "username"; +config.mail.pass = "pass"; +config.mail.from = "" + +// Session purge settings +config.maxSessionLength = 30; + + +module.exports = config; diff --git a/index.js b/index.js index dc722bb..cf1620e 100644 --- a/index.js +++ b/index.js @@ -1,21 +1,27 @@ +require('log-timestamp'); const express = require('express'); const app = express(); const PORT = 4000; const cors = require('cors'); -var bodyParser = require('body-parser') const mongoose = require('mongoose'); +const utils = require ('./utils'); +const CronJob = require('cron').CronJob; const config = require('./DB.js'); - const userRoutes = require('./user.route'); +console.log("Starting Kno-Logic Backend Server"); + // Handle MongoDB connection mongoose.Promise = global.Promise; mongoose.connect(config.DB, { useNewUrlParser: true, useUnifiedTopology: true }).then( () => { console.log('Connected to dabase'); + utils.loadDefaultTemplates(); + utils.purgeSessions(); }, err => { - console.log('Could not connect to database: ' + err); + console.log('Could not connect to database: '); + console.log(err); } ); @@ -30,3 +36,7 @@ app.use('/users', userRoutes); app.listen(PORT, () => { console.log('Express server running on port:', PORT); }); + +// Cron jobs +var purge = new CronJob('*/5 * * * *', utils.purgeSessions); +purge.start(); diff --git a/package-lock.json b/package-lock.json index 440c56b..1522cf4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -461,6 +461,14 @@ "vary": "^1" } }, + "cron": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz", + "integrity": "sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg==", + "requires": { + "moment-timezone": "^0.5.x" + } + }, "crypto-random-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", @@ -1008,6 +1016,19 @@ "package-json": "^6.3.0" } }, + "log-prefix": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/log-prefix/-/log-prefix-0.1.1.tgz", + "integrity": "sha512-aP1Lst8OCdZKATqzXDN0JBissNVZuiKLyo6hOXDBxaQ1jHDsaxh2J1i5Pp0zMy6ayTKDWfUlLMXyLaQe1PJ48g==" + }, + "log-timestamp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/log-timestamp/-/log-timestamp-0.3.0.tgz", + "integrity": "sha512-luRz6soxijd1aJh0GkLXFjKABihxthvTfWTzu3XhCgg5EivG2bsTpSd63QFbUgS+/KmFtL+0RfSpeaD2QvOV8Q==", + "requires": { + "log-prefix": "0.1.1" + } + }, "lowercase-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", @@ -1118,6 +1139,19 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + }, + "moment-timezone": { + "version": "0.5.33", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz", + "integrity": "sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==", + "requires": { + "moment": ">= 2.9.0" + } + }, "mongodb": { "version": "3.6.6", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.6.tgz", @@ -1214,6 +1248,11 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, + "nodemailer": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.6.0.tgz", + "integrity": "sha512-ikSMDU1nZqpo2WUPE0wTTw/NGGImTkwpJKDIFPZT+YvvR9Sj+ze5wzu95JHkBMglQLoG2ITxU21WukCC/XsFkg==" + }, "nodemon": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz", diff --git a/package.json b/package.json index 3db4f8b..e48ca55 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,11 @@ "bcrypt": "^5.0.1", "body-parser": "^1.19.0", "cors": "^2.8.5", + "cron": "^1.8.2", "express": "^4.17.1", - "mongoose": "^5.12.7" + "log-timestamp": "^0.3.0", + "mongoose": "^5.12.7", + "nodemailer": "^6.6.0" }, "devDependencies": { "nodemon": "^2.0.7" diff --git a/user.route.js b/user.route.js index 2bac100..ab41007 100644 --- a/user.route.js +++ b/user.route.js @@ -16,6 +16,16 @@ let User = require('./user.model'); 409 - Account already exists */ userRoutes.route('/create').post((req, res) => { + if (!req.body) { + res.status(401).send("Missing body"); + return; + } else if (!req.body.email || !req.body.password || !req.body.name) { + res.status(401).send("Missing body"); + return; + } else if (req.body.email == "" || req.body.password == "" || req.body.name == "") { + 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 @@ -56,22 +66,32 @@ userRoutes.route('/create').post((req, res) => { 401 - Incorrect */ userRoutes.route('/login').post((req, res) => { + if (!req.body) { + res.status(401).send("Missing body"); + return; + } else if (!req.body.email || !req.body.password) { + res.status(401).send("Missing body"); + return; + } else if (req.body.email == "" || req.body.password == "") { + res.status(401).send("Empty fields"); + return; + } User.findOne({ email: req.body.email }, (err, u) => { if (err) { console.log(err); - res.status(500); + res.status(500).send("Error logging in user"); return; } if (!u) { - res.status(401); + res.status(401).send("No user exists with that email"); return; } bcrypt.compare(req.body.password, u.password, (err, result) => { if (err) { console.log(err); - res.status(500); + res.status(500).send("Error logging in user"); return; } @@ -83,13 +103,13 @@ userRoutes.route('/login').post((req, res) => { s.save() .then(() => { - res.json(u); + res.json(s); }) .catch(() => { - res.status(500); + res.status(500).send("Error logging in user"); }); } else { - res.status(401); + res.status(401).send("Incorrect password"); } }); @@ -103,15 +123,15 @@ userRoutes.route('/login').post((req, res) => { 400 - No session exists */ userRoutes.route('/logout').post((req, res) => { - Session.findOne({ userId: req.body._id }, (err, sess) => { + Session.findOne({sessionId: req.body.sessionId}, (err, sess) => { if (err) { console.log(err); - res.status(500); + res.status(500).send("Error logging out"); return; } if (!sess) { - res.status(400); + res.status(400).send("No session found"); return; } @@ -120,7 +140,7 @@ userRoutes.route('/logout').post((req, res) => { res.status(201).send("Success deleting session"); }) .catch(() => { - res.status(500); + res.status(500).send("Error logging out"); }); }); diff --git a/utils.js b/utils.js index 5bdc5c0..d15abad 100644 --- a/utils.js +++ b/utils.js @@ -1,20 +1,100 @@ +const fs = require('fs'); +const nodemailer = require("nodemailer"); +var config = require('./config'); let User = require('./user.model'); let Session = require('./session.model'); let Message = require('./message.model'); +var maxSessionLength = config.maxSessionLength; +// purgeSessions() purge sessions that have existed for longer than maxSessionLength const purgeSessions = () => { - // TODO: Write a function to purge all old session IDs automatically, use cron to schedule + console.log("Purging old sessions..."); + Session.find({}, (err, arr) => { + for (let i = 0; i < arr.length; i++) { + let timeDifference = new Date().getTime() - arr[i].date; + let dayDifference = timeDifference / (1000 * 3600 * 24); + if (dayDifference > maxSessionLength) { + arr[i].delete().catch(e => { + console.log(e); + }); + } + } + }); } +// loadDefaultTemplates() load the default email templates into the database +// if no template exists for that name const loadDefaultTemplates = () => { - // TODO: Write a function that loads default mailing templates into the database + console.log("Loading default email templates...") + fs.readdir('./templates', (err, files) => { + files.forEach(file => { + fs.readFile('./templates/' + file, 'utf8', function (err, data) { + if (err) { + console.log(err); + } + let name = file.substring(0, file.indexOf('.')); + Message.findOne({ name: name }, (err, msg) => { + if (err) { + console.log(err); + return; + } + if (msg) { + + } else { + let newMsg = Message(); + newMsg.name = name; + newMsg.subject = "Email From Kno-Logic" //TODO: Should load these from config.js + newMsg.body = data; + newMsg.save() + .then(() => { + console.log("Loaded " + name + " message"); + }) + .catch(() => { + console.log("Error loading messages from file."); + }); + } + }); + }); + }); + }); } -const sendMail = (user, message) => { - // TODO: Write a function that can take in a message and a user and send the email using nodemailer -} +// sendMail(user, message, replacements) send an email with message to the user, making +// replacements in the message +// replacements is an array of data {from, to} +const sendMail = async (user, message, replacements) => { + console.log("Sending mail..."); + let transporter = nodemailer.createTransport({ + host: config.mail.host, + port: config.mail.port, + secure: config.mail.secure, + auth: { + user: config.mail.user, + pass: config.mail.pass, + }, + }); + + let msgBody = message.body; + + for (let i = 0; i < replacements.length; i++) { + msgBody = msgBody.replace(replacements[i].from, replacements[i].to); + } + + 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); + } +} module.exports.purgeSessions = purgeSessions; module.exports.loadDefaultTemplates = loadDefaultTemplates; -module.exports.sendMail = sendMail; \ No newline at end of file +module.exports.sendMail = sendMail;