From 4b79a43e15d6982108f366c71403b8d3d3ecd8a8 Mon Sep 17 00:00:00 2001 From: William Oldham Date: Sat, 4 Nov 2023 12:30:04 +0000 Subject: [PATCH] Split user deletion job and add more job logs --- src/modules/jobs/index.ts | 4 ++ src/modules/jobs/job.ts | 5 +- src/modules/jobs/list/challengeCode.ts | 26 ++++---- src/modules/jobs/list/sessionExpiry.ts | 85 ++++++++++++++------------ src/modules/jobs/list/userDeletion.ts | 42 +++++++++++++ 5 files changed, 111 insertions(+), 51 deletions(-) create mode 100644 src/modules/jobs/list/userDeletion.ts diff --git a/src/modules/jobs/index.ts b/src/modules/jobs/index.ts index 9b1ecdf..e6e955d 100644 --- a/src/modules/jobs/index.ts +++ b/src/modules/jobs/index.ts @@ -1,5 +1,9 @@ +import { challengeCodeJob } from '@/modules/jobs/list/challengeCode'; import { sessionExpiryJob } from '@/modules/jobs/list/sessionExpiry'; +import { userDeletionJob } from '@/modules/jobs/list/userDeletion'; export async function setupJobs() { + challengeCodeJob.start(); sessionExpiryJob.start(); + userDeletionJob.start(); } diff --git a/src/modules/jobs/job.ts b/src/modules/jobs/job.ts index fbe1550..888a1fd 100644 --- a/src/modules/jobs/job.ts +++ b/src/modules/jobs/job.ts @@ -20,9 +20,11 @@ const wait = (sec: number) => * @param cron crontime in this order: (min of hour) (hour of day) (day of month) (day of week) (sec of month) */ export function job( + name: string, cron: string, cb: (ctx: { em: EntityManager; log: Logger }) => Promise, ): CronJob { + log.info(`Registering job '${name}' with cron '${cron}'`); return CronJob.from({ cronTime: cron, onTick: async () => { @@ -33,9 +35,10 @@ export function job( // actually run the job try { const em = getORM().em.fork(); + log.info(`Starting job '${name}' with cron '${cron}'`); await cb({ em, log }); } catch (err) { - log.error('Failed to run job!'); + log.error(`Failed to run '${name}' job!`); log.error(err); } }, diff --git a/src/modules/jobs/list/challengeCode.ts b/src/modules/jobs/list/challengeCode.ts index fd3d7a2..ed0c478 100644 --- a/src/modules/jobs/list/challengeCode.ts +++ b/src/modules/jobs/list/challengeCode.ts @@ -2,14 +2,18 @@ import { ChallengeCode } from '@/db/models/ChallengeCode'; import { job } from '@/modules/jobs/job'; // every day at 12:00:00 -export const sessionExpiryJob = job('0 12 * * *', async ({ em }) => { - await em - .createQueryBuilder(ChallengeCode) - .delete() - .where({ - expiresAt: { - $lt: new Date(), - }, - }) - .execute(); -}); +export const challengeCodeJob = job( + 'Challenge Code Expiry', + '0 12 * * *', + async ({ em }) => { + await em + .createQueryBuilder(ChallengeCode) + .delete() + .where({ + expiresAt: { + $lt: new Date(), + }, + }) + .execute(); + }, +); diff --git a/src/modules/jobs/list/sessionExpiry.ts b/src/modules/jobs/list/sessionExpiry.ts index 51740d5..b54313f 100644 --- a/src/modules/jobs/list/sessionExpiry.ts +++ b/src/modules/jobs/list/sessionExpiry.ts @@ -1,49 +1,56 @@ import { Session } from '@/db/models/Session'; import { User } from '@/db/models/User'; import { job } from '@/modules/jobs/job'; + // every day at 12:00:00 -export const sessionExpiryJob = job('0 12 * * *', async ({ em, log }) => { - const deletedSessions = await em - .createQueryBuilder(Session) - .delete() - .where({ - expiresAt: { - $lt: new Date(), - }, - }) - .execute<{ affectedRows: number }>('run'); +export const sessionExpiryJob = job( + 'Session Expiry', + '0 12 * * *', + async ({ em, log }) => { + const deletedSessions = await em + .createQueryBuilder(Session) + .delete() + .where({ + expiresAt: { + $lt: new Date(), + }, + }) + .execute<{ affectedRows: number }>('run'); - log.info(`Removed ${deletedSessions.affectedRows} sessions that had expired`); + log.info( + `Removed ${deletedSessions.affectedRows} sessions that had expired`, + ); - const knex = em.getKnex(); + const knex = em.getKnex(); - // Count all sessions for a user ID - const sessionCountForUser = em - .createQueryBuilder(Session, 'session') - .count() - .where({ user: knex.ref('user.id') }) - .getKnexQuery(); + // Count all sessions for a user ID + const sessionCountForUser = em + .createQueryBuilder(Session, 'session') + .count() + .where({ user: knex.ref('user.id') }) + .getKnexQuery(); - const now = new Date(); - const oneYearAgo = new Date(); - oneYearAgo.setFullYear(now.getFullYear() - 1); + const now = new Date(); + const oneYearAgo = new Date(); + oneYearAgo.setFullYear(now.getFullYear() - 1); - // Delete all users who do not have any sessions AND - // (their login date is null OR they last logged in over 1 year ago) - const deletedUsers = await em - .createQueryBuilder(User, 'user') - .delete() - .withSubQuery(sessionCountForUser, 'session.sessionCount') - .where({ - 'session.sessionCount': 0, - $or: [ - { lastLoggedIn: { $eq: undefined } }, - { lastLoggedIn: { $lt: oneYearAgo } }, - ], - }) - .execute<{ affectedRows: number }>('run'); + // Delete all users who do not have any sessions AND + // (their login date is null OR they last logged in over 1 year ago) + const deletedUsers = await em + .createQueryBuilder(User, 'user') + .delete() + .withSubQuery(sessionCountForUser, 'session.sessionCount') + .where({ + 'session.sessionCount': 0, + $or: [ + { lastLoggedIn: { $eq: undefined } }, + { lastLoggedIn: { $lt: oneYearAgo } }, + ], + }) + .execute<{ affectedRows: number }>('run'); - log.info( - `Removed ${deletedUsers.affectedRows} users older than 1 year with no sessions`, - ); -}); + log.info( + `Removed ${deletedUsers.affectedRows} users older than 1 year with no sessions`, + ); + }, +); diff --git a/src/modules/jobs/list/userDeletion.ts b/src/modules/jobs/list/userDeletion.ts new file mode 100644 index 0000000..8cad565 --- /dev/null +++ b/src/modules/jobs/list/userDeletion.ts @@ -0,0 +1,42 @@ +import { Session } from '@/db/models/Session'; +import { User } from '@/db/models/User'; +import { job } from '@/modules/jobs/job'; + +// every day at 12:00:00 +export const userDeletionJob = job( + 'User Deletion', + '0 12 * * *', + async ({ em, log }) => { + const knex = em.getKnex(); + + // Count all sessions for a user ID + const sessionCountForUser = em + .createQueryBuilder(Session, 'session') + .count() + .where({ user: knex.ref('user.id') }) + .getKnexQuery(); + + const now = new Date(); + const oneYearAgo = new Date(); + oneYearAgo.setFullYear(now.getFullYear() - 1); + + // Delete all users who do not have any sessions AND + // (their login date is null OR they last logged in over 1 year ago) + const deletedUsers = await em + .createQueryBuilder(User, 'user') + .delete() + .withSubQuery(sessionCountForUser, 'session.sessionCount') + .where({ + 'session.sessionCount': 0, + $or: [ + { lastLoggedIn: { $eq: undefined } }, + { lastLoggedIn: { $lt: oneYearAgo } }, + ], + }) + .execute<{ affectedRows: number }>('run'); + + log.info( + `Removed ${deletedUsers.affectedRows} users older than 1 year with no sessions`, + ); + }, +);