Added session purge, email templates, email sending, and fixed user bugs
This commit is contained in:
16
config.js
Normal file
16
config.js
Normal file
@@ -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 = "<noreply@knologic.com>"
|
||||||
|
|
||||||
|
// Session purge settings
|
||||||
|
config.maxSessionLength = 30;
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = config;
|
||||||
16
index.js
16
index.js
@@ -1,21 +1,27 @@
|
|||||||
|
require('log-timestamp');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = 4000;
|
const PORT = 4000;
|
||||||
const cors = require('cors');
|
const cors = require('cors');
|
||||||
var bodyParser = require('body-parser')
|
|
||||||
const mongoose = require('mongoose');
|
const mongoose = require('mongoose');
|
||||||
|
const utils = require ('./utils');
|
||||||
|
const CronJob = require('cron').CronJob;
|
||||||
const config = require('./DB.js');
|
const config = require('./DB.js');
|
||||||
|
|
||||||
const userRoutes = require('./user.route');
|
const userRoutes = require('./user.route');
|
||||||
|
|
||||||
|
console.log("Starting Kno-Logic Backend Server");
|
||||||
|
|
||||||
// Handle MongoDB connection
|
// Handle MongoDB connection
|
||||||
mongoose.Promise = global.Promise;
|
mongoose.Promise = global.Promise;
|
||||||
mongoose.connect(config.DB, { useNewUrlParser: true, useUnifiedTopology: true }).then(
|
mongoose.connect(config.DB, { useNewUrlParser: true, useUnifiedTopology: true }).then(
|
||||||
() => {
|
() => {
|
||||||
console.log('Connected to dabase');
|
console.log('Connected to dabase');
|
||||||
|
utils.loadDefaultTemplates();
|
||||||
|
utils.purgeSessions();
|
||||||
},
|
},
|
||||||
err => {
|
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, () => {
|
app.listen(PORT, () => {
|
||||||
console.log('Express server running on port:', PORT);
|
console.log('Express server running on port:', PORT);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Cron jobs
|
||||||
|
var purge = new CronJob('*/5 * * * *', utils.purgeSessions);
|
||||||
|
purge.start();
|
||||||
|
|||||||
39
package-lock.json
generated
39
package-lock.json
generated
@@ -461,6 +461,14 @@
|
|||||||
"vary": "^1"
|
"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": {
|
"crypto-random-string": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
|
||||||
@@ -1008,6 +1016,19 @@
|
|||||||
"package-json": "^6.3.0"
|
"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": {
|
"lowercase-keys": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
"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": {
|
"mongodb": {
|
||||||
"version": "3.6.6",
|
"version": "3.6.6",
|
||||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.6.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
||||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
|
"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": {
|
"nodemon": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.7.tgz",
|
||||||
|
|||||||
@@ -20,8 +20,11 @@
|
|||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"cron": "^1.8.2",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"mongoose": "^5.12.7"
|
"log-timestamp": "^0.3.0",
|
||||||
|
"mongoose": "^5.12.7",
|
||||||
|
"nodemailer": "^6.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^2.0.7"
|
"nodemon": "^2.0.7"
|
||||||
|
|||||||
@@ -16,6 +16,16 @@ let User = require('./user.model');
|
|||||||
409 - Account already exists
|
409 - Account already exists
|
||||||
*/
|
*/
|
||||||
userRoutes.route('/create').post((req, res) => {
|
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);
|
console.log(req.body);
|
||||||
let u = new User(req.body);
|
let u = new User(req.body);
|
||||||
// TODO: Look for a different encryption method that can scale more easily
|
// TODO: Look for a different encryption method that can scale more easily
|
||||||
@@ -56,22 +66,32 @@ userRoutes.route('/create').post((req, res) => {
|
|||||||
401 - Incorrect
|
401 - Incorrect
|
||||||
*/
|
*/
|
||||||
userRoutes.route('/login').post((req, res) => {
|
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) => {
|
User.findOne({ email: req.body.email }, (err, u) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
res.status(500);
|
res.status(500).send("Error logging in user");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!u) {
|
if (!u) {
|
||||||
res.status(401);
|
res.status(401).send("No user exists with that email");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bcrypt.compare(req.body.password, u.password, (err, result) => {
|
bcrypt.compare(req.body.password, u.password, (err, result) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
res.status(500);
|
res.status(500).send("Error logging in user");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,13 +103,13 @@ userRoutes.route('/login').post((req, res) => {
|
|||||||
|
|
||||||
s.save()
|
s.save()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
res.json(u);
|
res.json(s);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
res.status(500);
|
res.status(500).send("Error logging in user");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(401);
|
res.status(401).send("Incorrect password");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -103,15 +123,15 @@ userRoutes.route('/login').post((req, res) => {
|
|||||||
400 - No session exists
|
400 - No session exists
|
||||||
*/
|
*/
|
||||||
userRoutes.route('/logout').post((req, res) => {
|
userRoutes.route('/logout').post((req, res) => {
|
||||||
Session.findOne({ userId: req.body._id }, (err, sess) => {
|
Session.findOne({sessionId: req.body.sessionId}, (err, sess) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
res.status(500);
|
res.status(500).send("Error logging out");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sess) {
|
if (!sess) {
|
||||||
res.status(400);
|
res.status(400).send("No session found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +140,7 @@ userRoutes.route('/logout').post((req, res) => {
|
|||||||
res.status(201).send("Success deleting session");
|
res.status(201).send("Success deleting session");
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
res.status(500);
|
res.status(500).send("Error logging out");
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
90
utils.js
90
utils.js
@@ -1,19 +1,99 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
const nodemailer = require("nodemailer");
|
||||||
|
var config = require('./config');
|
||||||
let User = require('./user.model');
|
let User = require('./user.model');
|
||||||
let Session = require('./session.model');
|
let Session = require('./session.model');
|
||||||
let Message = require('./message.model');
|
let Message = require('./message.model');
|
||||||
|
var maxSessionLength = config.maxSessionLength;
|
||||||
|
|
||||||
|
// purgeSessions() purge sessions that have existed for longer than maxSessionLength
|
||||||
const purgeSessions = () => {
|
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 = () => {
|
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) => {
|
// sendMail(user, message, replacements) send an email with message to the user, making
|
||||||
// TODO: Write a function that can take in a message and a user and send the email using nodemailer
|
// 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.purgeSessions = purgeSessions;
|
||||||
module.exports.loadDefaultTemplates = loadDefaultTemplates;
|
module.exports.loadDefaultTemplates = loadDefaultTemplates;
|
||||||
|
|||||||
Reference in New Issue
Block a user