diff --git a/crabfit-backend/cron.yaml b/crabfit-backend/cron.yaml index a999bc7..5f7911d 100644 --- a/crabfit-backend/cron.yaml +++ b/crabfit-backend/cron.yaml @@ -7,3 +7,7 @@ cron: url: /tasks/legacyCleanup schedule: every tuesday 09:00 target: api + - description: "remove people with an event id that no longer exists" + url: /tasks/removeOrphans + schedule: 1st wednesday of month 09:00 + target: api diff --git a/crabfit-backend/index.js b/crabfit-backend/index.js index 7dc0b21..47f3dab 100644 --- a/crabfit-backend/index.js +++ b/crabfit-backend/index.js @@ -16,6 +16,7 @@ const updatePerson = require('./routes/updatePerson'); const taskCleanup = require('./routes/taskCleanup'); const taskLegacyCleanup = require('./routes/taskLegacyCleanup'); +const taskRemoveOrphans = require('./routes/taskRemoveOrphans'); const app = express(); const port = 8080; @@ -53,6 +54,7 @@ app.patch('/event/:eventId/people/:personName', updatePerson); // Tasks app.get('/tasks/cleanup', taskCleanup); app.get('/tasks/legacyCleanup', taskLegacyCleanup); +app.get('/tasks/removeOrphans', taskRemoveOrphans); app.listen(port, () => { console.log(`Crabfit API listening at http://localhost:${port} in ${process.env.NODE_ENV === 'production' ? 'prod' : 'dev'} mode`) diff --git a/crabfit-backend/routes/taskCleanup.js b/crabfit-backend/routes/taskCleanup.js index 28182e5..fa87ae9 100644 --- a/crabfit-backend/routes/taskCleanup.js +++ b/crabfit-backend/routes/taskCleanup.js @@ -1,6 +1,10 @@ const dayjs = require('dayjs'); module.exports = async (req, res) => { + if (req.header('X-Appengine-Cron') === undefined) { + return res.status(400).send('This task can only be run from a cron job'); + } + const threeMonthsAgo = dayjs().subtract(3, 'month').unix(); console.log(`Running cleanup task at ${dayjs().format('h:mma D MMM YYYY')}`); diff --git a/crabfit-backend/routes/taskLegacyCleanup.js b/crabfit-backend/routes/taskLegacyCleanup.js index cbb3e6e..1d7a64d 100644 --- a/crabfit-backend/routes/taskLegacyCleanup.js +++ b/crabfit-backend/routes/taskLegacyCleanup.js @@ -1,6 +1,10 @@ const dayjs = require('dayjs'); module.exports = async (req, res) => { + if (req.header('X-Appengine-Cron') === undefined) { + return res.status(400).send('This task can only be run from a cron job'); + } + const threeMonthsAgo = dayjs().subtract(3, 'month').unix(); console.log(`Running LEGACY cleanup task at ${dayjs().format('h:mma D MMM YYYY')}`); diff --git a/crabfit-backend/routes/taskRemoveOrphans.js b/crabfit-backend/routes/taskRemoveOrphans.js new file mode 100644 index 0000000..320d276 --- /dev/null +++ b/crabfit-backend/routes/taskRemoveOrphans.js @@ -0,0 +1,46 @@ +const dayjs = require('dayjs'); + +module.exports = async (req, res) => { + if (req.header('X-Appengine-Cron') === undefined) { + return res.status(400).send('This task can only be run from a cron job'); + } + + const threeMonthsAgo = dayjs().subtract(3, 'month').unix(); + + console.log(`Running orphan removal task at ${dayjs().format('h:mma D MMM YYYY')}`); + + try { + // Fetch people that are older than 3 months + const peopleQuery = req.datastore.createQuery(req.types.person).filter('created', '<', threeMonthsAgo); + let oldPeople = (await req.datastore.runQuery(peopleQuery))[0]; + + if (oldPeople && oldPeople.length > 0) { + console.log(`Found ${oldPeople.length} people older than 3 months, checking for events`); + + // Fetch events linked to the people discovered + let peopleWithoutEvents = 0; + await Promise.all(oldPeople.map(async (person) => { + let event = (await req.datastore.get(req.datastore.key([req.types.event, person.eventId])))[0]; + + if (!event) { + peopleWithoutEvents++; + await req.datastore.delete(person[req.datastore.KEY]); + } + })); + + if (peopleWithoutEvents > 0) { + console.log(`Orphan removal successful: ${oldEventIds.length} events and ${peopleDiscovered} people removed`); + res.sendStatus(200); + } else { + console.log(`Found 0 people without events, ending orphan removal`); + res.sendStatus(404); + } + } else { + console.log(`Found 0 people older than 3 months, ending orphan removal`); + res.sendStatus(404); + } + } catch (e) { + console.error(e); + res.sendStatus(404); + } +}; diff --git a/crabfit-backend/swagger.yaml b/crabfit-backend/swagger.yaml index 7e15592..ad68a66 100644 --- a/crabfit-backend/swagger.yaml +++ b/crabfit-backend/swagger.yaml @@ -243,3 +243,16 @@ paths: description: "Not found" 400: description: "Not called from a cron job" + "/tasks/removeOrphans": + get: + summary: "Deletes people if the event they were created under no longer exists" + operationId: "taskRemoveOrphans" + tags: + - tasks + responses: + 200: + description: "OK" + 404: + description: "Not found" + 400: + description: "Not called from a cron job" diff --git a/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx b/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx index aec8520..579a074 100644 --- a/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx +++ b/crabfit-frontend/src/components/AvailabilityViewer/AvailabilityViewer.tsx @@ -7,7 +7,7 @@ import relativeTime from 'dayjs/plugin/relativeTime'; import { useSettingsStore, useLocaleUpdateStore } from 'stores'; -import { Legend, Center } from 'components'; +import { Legend } from 'components'; import { Wrapper, ScrollWrapper, diff --git a/crabfit-frontend/src/pages/Event/Event.tsx b/crabfit-frontend/src/pages/Event/Event.tsx index 5c4f346..1a63e58 100644 --- a/crabfit-frontend/src/pages/Event/Event.tsx +++ b/crabfit-frontend/src/pages/Event/Event.tsx @@ -96,7 +96,7 @@ const Event = (props) => { }; fetchEvent(); - }, [id, addRecent]); + }, [id, addRecent, removeRecent]); useEffect(() => { const fetchPeople = async () => {