Skip to content
13 changes: 8 additions & 5 deletions backend/app.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@

// app.js - Entry point for our application

// Load in all of our node modules. Their uses are explained below as they are called.
const express = require('express');
const morgan = require('morgan');
const cron = require('node-cron');
const fetch = require('node-fetch');
const morgan = require('morgan');
const cookieParser = require('cookie-parser');

const customRequestHeaderName = 'x-customrequired-header';
Expand Down Expand Up @@ -52,12 +53,14 @@ app.use(cookieParser());
app.use(morgan('dev'));

// WORKERS
const runOpenCheckinWorker = require('./workers/openCheckins')(cron, fetch);
const runCloseCheckinWorker = require('./workers/closeCheckins')(cron, fetch);
const runOpenCheckinWorker = require('./workers/openCheckins');
runOpenCheckinWorker(cron, fetch);

const { createRecurringEvents } = require('./workers/createRecurringEvents');
const runCreateRecurringEventsWorker = createRecurringEvents(cron, fetch);
const runCloseCheckinWorker = require('./workers/closeCheckins');
runCloseCheckinWorker(cron, fetch);

const { createRecurringEvents } = require('./workers/createRecurringEvents');
createRecurringEvents(cron, fetch);
// const runSlackBot = require("./workers/slackbot")(fetch);

// MIDDLEWARE
Expand Down
32 changes: 27 additions & 5 deletions backend/controllers/event.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ EventController.event_list = async function (req, res) {
const events = await Event.find(query).populate('project');
return res.status(200).send(events);
} catch (err) {
console.error('[event.controller]', err);
return res.sendStatus(400);
}
};
Expand All @@ -20,6 +21,7 @@ EventController.event_by_id = async function (req, res) {
const events = await Event.findById(EventId).populate('project');
return res.status(200).send(events);
} catch (err) {
console.error('[event.controller]', err);
return res.sendStatus(400);
}
};
Expand All @@ -28,9 +30,15 @@ EventController.create = async function (req, res) {
const { body } = req;

try {
const event = await Event.create(body);
return res.status(201).send(event);
if (Array.isArray(body)) {
const events = await Event.insertMany(body);
return res.status(201).send(events);
} else {
const event = await Event.create(body);
return res.status(201).send(event);
}
} catch (err) {
console.error('[event.controller]', err);
return res.sendStatus(400);
}
};
Expand All @@ -42,17 +50,31 @@ EventController.destroy = async function (req, res) {
const event = await Event.findByIdAndDelete(EventId);
return res.status(200).send(event);
} catch (err) {
console.error('[event.controller]', err);
return res.sendStatus(400);
}
};

EventController.update = async function (req, res) {
const { EventId } = req.params;
const { body } = req;

try {
const event = await Event.findByIdAndUpdate(EventId, req.body);
return res.status(200).send(event);
if (Array.isArray(body)) {
const ops = body.map((e) => ({
updateOne: {
filter: { _id: e._id },
update: { $set: { checkInReady: e.checkInReady } },
},
}));
const events = await Event.bulkWrite(ops);
return res.status(200).send(events);
} else {
const { EventId } = req.params;
const event = await Event.findByIdAndUpdate(EventId, req.body);
return res.status(200).send(event);
}
} catch (err) {
console.error('[event.controller]', err);
return res.sendStatus(400);
}
};
Expand Down
8 changes: 5 additions & 3 deletions backend/routers/events.router.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const express = require("express");
const express = require('express');
const router = express.Router();

const { Event } = require('../models/event.model');
Expand All @@ -13,12 +13,14 @@ router.get('/:EventId', EventController.event_by_id);

router.delete('/:EventId', EventController.destroy);

router.patch('/batchUpdate', EventController.update);

router.patch('/:EventId', EventController.update);

// TODO: Refactor and remove
router.get("/nexteventbyproject/:id", (req, res) => {
router.get('/nexteventbyproject/:id', (req, res) => {
Event.find({ project: req.params.id })
.populate("project")
.populate('project')
.then((events) => {
res.status(200).json(events[events.length - 1]);
})
Expand Down
177 changes: 95 additions & 82 deletions backend/workers/closeCheckins.js
Original file line number Diff line number Diff line change
@@ -1,86 +1,99 @@
module.exports = (cron, fetch) => {
// Check to see if any events are about to start,
// and if so, open their respective check-ins

const url =
process.env.NODE_ENV === 'prod'
? 'https://www.vrms.io'
: `http://localhost:${process.env.BACKEND_PORT}`;
const headerToSend = process.env.CUSTOM_REQUEST_HEADER;

async function fetchEvents() {
try {
const res = await fetch(`${url}/api/events`, {
headers: {
'x-customrequired-header': headerToSend,
},
});
const resJson = await res.json();

return resJson;
} catch (error) {
console.log(error);
}
}

async function updateEvents(eventsToUpdate) {
try {
const res = await fetch(`${url}/api/events/batchUpdate`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'x-customrequired-header': headerToSend,
},
body: JSON.stringify(eventsToUpdate),
});
if (!res.ok) throw new Error('Failed to update event');
return await res.json();
} catch (error) {
console.error('Error updating event:', error);
return null;
}
}
async function sortAndFilterEvents() {
const events = await fetchEvents();

// Get current time and set to date variable
const now = Date.now();

// Filter events if event date is after now but before thirty minutes from now
if (events && events.length > 0) {
const sortedEvents = events.filter((event) => {
if (!event.date) {
// handle if event date is null/undefined
// false meaning don't include in sortedEvents
return false;
}
// Calculate three hours from now
const threeHoursFromStartTime = new Date(event.date).getTime() + 10800000;
if (Number.isNaN(threeHoursFromStartTime)) return false;
return now >= threeHoursFromStartTime && event.checkInReady === true;
});

// console.log('Sorted events: ', sortedEvents);
return sortedEvents;
}
}

async function closeCheckins(events) {
if (events && events.length > 0) {
console.log('Closing check-ins');
// console.log('Closing event: ', event);
const batchEventsToUpdate = events.map((e) => ({
_id: e._id,
checkInReady: false,
}));
const updatedEvents = await updateEvents(batchEventsToUpdate);
if (updatedEvents) console.log('Updated events:', updatedEvents);
console.log('Check-ins closed');
} else {
console.log('No open events to close');
}
}

async function runTask() {
const eventsToClose = await sortAndFilterEvents().catch((err) => {
console.log(err);
});

// Check to see if any events are about to start,
// and if so, open their respective check-ins

const url = process.env.NODE_ENV === 'prod' ? 'https://www.vrms.io' : `http://localhost:${process.env.BACKEND_PORT}`;
const headerToSend = process.env.CUSTOM_REQUEST_HEADER;

async function fetchEvents() {
try {
const res = await fetch(`${url}/api/events`, {
headers: {
"x-customrequired-header": headerToSend
}
});
const resJson = await res.json();

return resJson;
} catch(error) {
console.log(error);
};
};

async function sortAndFilterEvents() {
const events = await fetchEvents();

// Filter events if event date is after now but before thirty minutes from now
if (events && events.length > 0) {

const sortedEvents = events.filter(event => {
if (!event.date) {
// handle if event date is null/undefined
// false meaning don't include in sortedEvents
return false
}

const currentTimeISO = new Date().toISOString();
const threeHoursFromStartTime = new Date(event.date).getTime() + 10800000;
const threeHoursISO = new Date(threeHoursFromStartTime).toISOString();

return (currentTimeISO > threeHoursISO) && (event.checkInReady === true);
});

// console.log('Sorted events: ', sortedEvents);
return sortedEvents;
};
};

async function closeCheckins(events) {
if(events && events.length > 0) {
events.forEach(async event => {
// console.log('Closing event: ', event);

await fetch(`${url}/api/events/${event._id}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
"x-customrequired-header": headerToSend
},
body: JSON.stringify({ checkInReady: false })
})
.catch(err => {
console.log(err);
});
});
};
};

async function runTask() {
console.log("Closing check-ins");

const eventsToClose = await sortAndFilterEvents()
.catch(err => {console.log(err)});

await closeCheckins(eventsToClose)
.catch(err => {console.log(err)});

console.log("Check-ins closed");
};

const scheduledTask = cron.schedule('*/30 * * * *', () => {
runTask();
await closeCheckins(eventsToClose).catch((err) => {
console.log(err);
});
}

const scheduledTask = cron.schedule('*/30 * * * *', () => {
runTask();
});

return scheduledTask;
};
return scheduledTask;
};
Loading
Loading