const express = require('express');
const mongoose = require('mongoose');
const shortid = require('shortid');
const randomstring = require("randomstring");
const { body, validationResult } = require('express-validator/check');
const router = express.Router();
const Event = mongoose.model('Event');
const Log = mongoose.model('Log');
var moment = require('moment');
const marked = require('marked');
const ical = require('ical');
const apiCredentials = require('./config/api.js');
const sgMail = require('@sendgrid/mail');
sgMail.setApiKey(apiCredentials.sendgrid);
const fileUpload = require('express-fileupload');
var Jimp = require('jimp');
router.use(fileUpload());
// LOGGING
function addToLog(process, status, message) {
let logEntry = new Log({
status: status,
process: process,
message: message,
timestamp: moment()
});
logEntry.save().catch(() => { console.log("Error saving log entry!") });
}
// SCHEDULED DELETION
const schedule = require('node-schedule');
const deleteOldEvents = schedule.scheduleJob('59 23 * * *', function(fireDate){
const too_old = moment().subtract(7, 'days').toDate();
console.log(too_old);
Event.find({ end: { $lte: too_old } }).remove().exec().then((RemoveStatus) => {
console.log("Documents removed successfully");
addToLog("deleteOldEvents", "success", "Old events deleted");
}).catch((err) => {
addToLog("deleteOldEvents", "error", "Attempt to delete old events failed with error: " + err);
});
});
// FRONTEND ROUTES
router.get('/', (req, res) => {
res.render('home');
});
router.get('/new', (req, res) => {
res.render('home');
});
//router.get('/login', (req, res) => {
// res.render('admin');
//})
//router.get('/login', (req, res) => {
// res.render('login');
//});
//
//router.get('/register', (req, res) => {
// res.render('register');
//});
router.get('/new/event', (req, res) => {
res.render('newevent');
});
router.get('/new/event/public', (req, res) => {
let isPrivate = false;
let isPublic = true;
let isOrganisation = false;
let isUnknownType = false;
// let eventType = req.params.eventType;
// if (eventType == "private"){
// isPrivate = true;
// }
// else if (eventType == "public"){
// isPublic = true;
// }
// else if (eventType == "organisation"){
// isOrganisation = true;
// }
// else {
// isUnknownType = true;
// }
res.render('newevent', {
title: 'New event',
isPrivate: isPrivate,
isPublic: isPublic,
isOrganisation: isOrganisation,
isUnknownType: isUnknownType,
eventType: 'public'
});
})
router.get('/:eventID', (req, res) => {
Event.findOne({
id: req.params.eventID
})
.then((event) => {
parsedLocation = event['location'].replace(/\s+/g, '+');
if (moment(event.end).isSame(event.start, 'day')){
// Happening during one day
displayDate = moment(event.start).format('dddd D MMMM YYYY [from] h:mm a') + moment(event.end).format(' [to] h:mm a');
}
else {
displayDate = moment(event.start).format('dddd D MMMM YYYY [at] h:mm a') + moment(event.end).format(' [–] dddd D MMMM YYYY [at] h:mm a');
}
parsedStart = moment(event.start).format('YYYYMMDD[T]HHmmss');
parsedEnd = moment(event.end).format('YYYYMMDD[T]HHmmss');
let eventHasConcluded = false;
if (moment(event.end).isBefore(moment())){
eventHasConcluded = true;
}
fromNow = moment(event.start).fromNow();
parsedDescription = marked(event.description);
eventEditToken = event.editToken;
escapedName = event.name.replace(/\s+/g, '+');
let eventHasCoverImage = false;
if( event.image ) {
eventHasCoverImage = true;
}
else {
eventHasCoverImage = false;
}
let eventHasHost = false;
if( event.hostName ) {
eventHasHost = true;
}
else {
eventHasHost = false;
}
let firstLoad = false;
if (event.firstLoad === true) {
firstLoad = true;
Event.findOneAndUpdate({id: req.params.eventID}, {firstLoad: false}, function(err, raw) {
if (err) {
res.send(err);
}
});
}
let editingEnabled = false;
if (Object.keys(req.query).length !== 0) {
if (!req.query.e) {
editingEnabled = false;
console.log("No edit token set");
}
else {
if (req.query.e == eventEditToken){
editingEnabled = true;
}
else {
editingEnabled = false;
}
}
}
res.set("X-Robots-Tag", "noindex");
res.render('event', {title: event.name, escapedName: escapedName, eventData: event, parsedLocation: parsedLocation, parsedStart: parsedStart, parsedEnd: parsedEnd, displayDate: displayDate, fromNow: fromNow, parsedDescription: parsedDescription, editingEnabled: editingEnabled, eventHasCoverImage: eventHasCoverImage, eventHasHost: eventHasHost, firstLoad: firstLoad, eventHasConcluded: eventHasConcluded })
})
.catch((err) => {
addToLog("displayEvent", "error", "Attempt to display event " + req.params.eventID + " failed with error: " + err);
console.log(err)
res.status(404);
res.render('404', { url: req.url });
return;
});
})
// BACKEND ROUTES
//router.post('/login',
// passport.authenticate('local', { successRedirect: '/admin',
// failureRedirect: '/login',
// failureFlash: true })
//);
router.post('/newevent', (req, res) => {
let eventID = shortid.generate();
let editToken = randomstring.generate();
let eventImageFilename = "";
if (req.files && Object.keys(req.files).length != 0) {
let eventImageBuffer = req.files.imageUpload.data;
Jimp.read(eventImageBuffer, (err, img) => {
if (err) addToLog("Jimp", "error", "Attempt to edit image failed with error: " + err);
img
.resize(920, Jimp.AUTO) // resize
.quality(60) // set JPEG quality
.write('./public/images/' + eventID + '.jpg'); // save
});
eventImageFilename = eventID + '.jpg';
}
const event = new Event({
id: eventID,
type: req.body.eventType,
name: req.body.eventName,
location: req.body.eventLocation,
start: req.body.eventStart,
end: req.body.eventEnd,
description: req.body.eventDescription,
image: eventImageFilename,
creatorEmail: req.body.creatorEmail,
url: req.body.eventURL,
hostName: req.body.hostName,
viewPassword: req.body.viewPassword,
editPassword: req.body.editPassword,
editToken: editToken,
usersCanAttend: req.body.joinCheckbox ? true : false,
showUsersList: req.body.guestlistCheckbox ? true : false,
usersCanComment: req.body.interactionCheckbox ? true : false,
firstLoad: true
});
event.save()
.then(() => {
addToLog("createEvent", "success", "Event " + eventID + "created");
// Send email with edit link
const msg = {
to: req.body.creatorEmail,
from: {
name: 'Gathio',
email: 'notifications@gath.io',
},
templateId: 'd-00330b8278ab463e9f88c16566487d97',
dynamic_template_data: {
subject: 'gathio: ' + req.body.eventName,
eventID: eventID,
editToken: editToken
},
};
sgMail.send(msg).then(() => {
res.writeHead(302, {
'Location': '/' + eventID + '?e=' + editToken
});
res.end();
}).catch(e => {
console.error(e.toString());
res.status(500).end();
});
})
.catch((err) => { res.send('Database error, please try again :('); addToLog("createEvent", "error", "Attempt to create event failed with error: " + err);});
});
router.post('/importevent', (req, res) => {
let eventID = shortid.generate();
let editToken = randomstring.generate();
if (req.files && Object.keys(req.files).length != 0) {
importediCalObject = ical.parseICS(req.files.icsImportControl.data.toString('utf8'));
for (var key in importediCalObject) {
importedEventData = importediCalObject[key];
}
creatorEmail = importedEventData.organizer.val.replace("MAILTO:", "")
const event = new Event({
id: eventID,
type: 'public',
name: importedEventData.summary,
location: importedEventData.location,
start: importedEventData.start,
end: importedEventData.end,
description: importedEventData.description,
image: '',
creatorEmail: creatorEmail,
url: '',
hostName: importedEventData.organizer.params.CN,
viewPassword: '',
editPassword: '',
editToken: editToken,
usersCanAttend: false,
showUsersList: false,
usersCanComment: false,
firstLoad: true
});
event.save()
.then(() => {
addToLog("createEvent", "success", "Event " + eventID + "created");
// Send email with edit link
const msg = {
to: creatorEmail,
from: {
name: 'Gathio',
email: 'notifications@gath.io',
},
templateId: 'd-00330b8278ab463e9f88c16566487d97',
dynamic_template_data: {
subject: 'gathio: ' + req.body.eventName,
eventID: eventID,
editToken: editToken
},
};
sgMail.send(msg).then(() => {
res.writeHead(302, {
'Location': '/' + eventID + '?e=' + editToken
});
res.end();
}).catch(e => {
console.error(e.toString());
res.status(500).end();
});
})
.catch((err) => { res.send('Database error, please try again :('); addToLog("createEvent", "error", "Attempt to create event failed with error: " + err);});
}
else {
console.log("Files array is empty!")
res.status(500).end();
}
});
router.post('/editevent/:eventID/:editToken', (req, res) => {
let submittedEditToken = req.params.editToken;
Event.findOne(({
id: req.params.eventID,
}))
.then((event) => {
if (event.editToken === submittedEditToken) {
// Token matches
// If there is a new image, upload that first
let eventID = req.params.eventID;
let eventImageFilename = event.image;
if (req.files && Object.keys(req.files).length != 0) {
let eventImageBuffer = req.files.imageUpload.data;
Jimp.read(eventImageBuffer, (err, img) => {
if (err) throw err;
img
.resize(920, Jimp.AUTO) // resize
.quality(60) // set JPEG quality
.write('./public/images/' + eventID + '.jpg'); // save
});
eventImageFilename = eventID + '.jpg';
}
const updatedEvent = {
name: req.body.eventName,
location: req.body.eventLocation,
start: req.body.eventStart,
end: req.body.eventEnd,
description: req.body.eventDescription,
url: req.body.eventURL,
hostName: req.body.hostName,
image: eventImageFilename,
usersCanAttend: req.body.joinCheckbox ? true : false,
showUsersList: req.body.guestlistCheckbox ? true : false,
usersCanComment: req.body.interactionCheckbox ? true : false
}
Event.findOneAndUpdate({id: req.params.eventID}, updatedEvent, function(err, raw) {
if (err) {
addToLog("editEvent", "error", "Attempt to edit event " + req.params.eventID + " failed with error: " + err);
res.send(err);
}
})
.then(() => {
addToLog("editEvent", "success", "Event " + req.params.eventID + " edited");
Event.find({id: req.params.eventID}).distinct('attendees.email', function(error, ids) {
console.log(ids)
attendeeEmails = ids;
if (!error && attendeeEmails != ""){
console.log("Sending emails to: " + attendeeEmails);
const msg = {
to: attendeeEmails,
from: {
name: 'Gathio',
email: 'notifications@gath.io',
},
templateId: 'd-e21f3ca49d82476b94ddd8892c72a162',
dynamic_template_data: {
subject: 'gathio: Event edited',
actionType: 'edited',
eventExists: true,
eventID: req.params.eventID
}
}
sgMail.sendMultiple(msg);
}
else {
console.log("Nothing to send!");
}
})
res.writeHead(302, {
'Location': '/' + req.params.eventID + '?e=' + req.params.editToken
});
res.end();
})
.catch((err) => { console.error(err); res.send('Sorry! Something went wrong!'); addToLog("editEvent", "error", "Attempt to edit event " + req.params.eventID + " failed with error: " + err);});
}
else {
// Token doesn't match
res.send('Sorry! Something went wrong');
addToLog("editEvent", "error", "Attempt to edit event " + req.params.eventID + " failed with error: token does not match");
}
})
.catch((err) => { console.error(err); res.send('Sorry! Something went wrong!'); addToLog("editEvent", "error", "Attempt to edit event " + req.params.eventID + " failed with error: " + err);});
});
router.post('/deleteevent/:eventID/:editToken', (req, res) => {
let submittedEditToken = req.params.editToken;
Event.find(({
id: req.params.eventID,
}))
.then((event) => {
if (event.editToken === submittedEditToken) {
// Token matches
// Send emails here otherwise they don't exist lol
Event.find({id: req.params.eventID}).distinct('attendees.email', function(error, ids) {
attendeeEmails = ids;
if (!error){
console.log("Sending emails to: " + attendeeEmails);
const msg = {
to: attendeeEmails,
from: {
name: 'Gathio',
email: 'notifications@gath.io',
},
templateId: 'd-e21f3ca49d82476b94ddd8892c72a162',
dynamic_template_data: {
subject: 'gathio: Event "' + event.name + '" deleted',
actionType: 'deleted',
eventExists: false,
eventID: req.params.eventID
}
}
sgMail.sendMultiple(msg);
}
else {
console.log("Nothing to send!");
}
});
Event.deleteOne({id: req.params.eventID}, function(err, raw) {
if (err) {
res.send(err);
addToLog("deleteEvent", "error", "Attempt to delete event " + req.params.eventID + " failed with error: " + err);
}
})
.then(() => {
addToLog("deleteEvent", "success", "Event " + req.params.eventID + " deleted");
res.writeHead(302, {
'Location': '/'
});
res.end();
})
.catch((err) => { res.send('Sorry! Something went wrong (error deleting): ' + err); addToLog("deleteEvent", "error", "Attempt to delete event " + req.params.eventID + " failed with error: " + err);});
}
else {
// Token doesn't match
res.send('Sorry! Something went wrong');
addToLog("deleteEvent", "error", "Attempt to delete event " + req.params.eventID + " failed with error: token does not match");
}
})
.catch((err) => { res.send('Sorry! Something went wrong: ' + err); addToLog("deleteEvent", "error", "Attempt to delete event " + req.params.eventID + " failed with error: " + err);});
});
router.post('/attendevent/:eventID', (req, res) => {
const newAttendee = {
name: req.body.attendeeName,
status: 'attending',
email: req.body.attendeeEmail
};
Event.findOne({
id: req.params.eventID,
}, function(err,event) {
event.attendees.push(newAttendee);
event.save()
.then(() => {
addToLog("addEventAttendee", "success", "Attendee added to event " + req.params.eventID);
if (req.body.attendeeEmail){
const msg = {
to: req.body.attendeeEmail,
from: {
name: 'Gathio',
email: 'notifications@gath.io',
},
templateId: 'd-977612474bba49c48b58e269f04f927c',
dynamic_template_data: {
subject: 'gathio: ' + event.name,
eventID: req.params.eventID
},
};
console.log(msg);
sgMail.send(msg);
}
res.writeHead(302, {
'Location': '/' + req.params.eventID
});
res.end();
})
.catch((err) => { res.send('Database error, please try again :('); addToLog("addEventAttendee", "error", "Attempt to add attendee to event " + req.params.eventID + " failed with error: " + err); });
});
});
router.post('/post/comment/:eventID', (req, res) => {
let commentID = shortid.generate();
const newComment = {
id: commentID,
author: req.body.commentAuthor,
content: req.body.commentContent,
timestamp: moment()
};
Event.findOne({
id: req.params.eventID,
}, function(err,event) {
event.comments.push(newComment);
event.save()
.then(() => {
addToLog("addEventComment", "success", "Comment added to event " + req.params.eventID);
Event.find({id: req.params.eventID}).distinct('attendees.email', function(error, ids) {
attendeeEmails = ids;
if (!error){
console.log("Sending emails to: " + attendeeEmails);
const msg = {
to: attendeeEmails,
from: {
name: 'Gathio',
email: 'notifications@gath.io',
},
templateId: 'd-756d078561e047aba307155f02b6686d',
dynamic_template_data: {
subject: 'gathio: New comment in ' + event.name,
commentAuthor: req.body.commentAuthor,
eventID: req.params.eventID
}
}
sgMail.sendMultiple(msg);
}
else {
console.log("Nothing to send!");
}
});
res.writeHead(302, {
'Location': '/' + req.params.eventID
});
res.end();
})
.catch((err) => { res.send('Database error, please try again :(' + err); addToLog("addEventComment", "error", "Attempt to add comment to event " + req.params.eventID + " failed with error: " + err); });
});
});
router.post('/post/reply/:eventID/:commentID', (req, res) => {
let replyID = shortid.generate();
let commentID = req.params.commentID;
const newReply = {
id: replyID,
author: req.body.replyAuthor,
content: req.body.replyContent,
timestamp: moment()
};
Event.findOne({
id: req.params.eventID,
}, function(err,event) {
var parentComment = event.comments.id(commentID);
parentComment.replies.push(newReply);
event.save()
.then(() => {
addToLog("addEventReply", "success", "Reply added to comment " + commentID + " in event " + req.params.eventID);
Event.find({id: req.params.eventID}).distinct('attendees.email', function(error, ids) {
attendeeEmails = ids;
if (!error){
console.log("Sending emails to: " + attendeeEmails);
const msg = {
to: attendeeEmails,
from: {
name: 'Gathio',
email: 'notifications@gath.io',
},
templateId: 'd-756d078561e047aba307155f02b6686d',
dynamic_template_data: {
subject: 'gathio: New comment in ' + event.name,
commentAuthor: req.body.commentAuthor,
eventID: req.params.eventID
}
}
sgMail.sendMultiple(msg);
}
else {
console.log("Nothing to send!");
}
});
res.writeHead(302, {
'Location': '/' + req.params.eventID
});
res.end();
})
.catch((err) => { res.send('Database error, please try again :('); addToLog("addEventReply", "error", "Attempt to add reply to comment " + commentID + " in event " + req.params.eventID + " failed with error: " + err); });
});
});
router.post('/deletecomment/:eventID/:commentID/:editToken', (req, res) => {
let submittedEditToken = req.params.editToken;
Event.findOne(({
id: req.params.eventID,
}))
.then((event) => {
if (event.editToken === submittedEditToken) {
// Token matches
event.comments.id(req.params.commentID).remove();
event.save()
.then(() => {
addToLog("deleteComment", "success", "Comment deleted from event " + req.params.eventID);
res.writeHead(302, {
'Location': '/' + req.params.eventID + '?e=' + req.params.editToken
});
res.end();
})
.catch((err) => { res.send('Sorry! Something went wrong (error deleting): ' + err); addToLog("deleteComment", "error", "Attempt to delete comment " + req.params.commentID + "from event " + req.params.eventID + " failed with error: " + err);});
}
else {
// Token doesn't match
res.send('Sorry! Something went wrong');
addToLog("deleteComment", "error", "Attempt to delete comment " + req.params.commentID + "from event " + req.params.eventID + " failed with error: token does not match");
}
})
.catch((err) => { res.send('Sorry! Something went wrong: ' + err); addToLog("deleteComment", "error", "Attempt to delete comment " + req.params.commentID + "from event " + req.params.eventID + " failed with error: " + err);});
});
router.use(function(req, res, next){
res.status(404);
res.render('404', { url: req.url });
return;
});
addToLog("startup", "success", "Started up successfully");
module.exports = router;