Files
starr-philproperties/apps/api/cron/jobs/lift_expired_bans.cron.js
T

86 lines
3.4 KiB
JavaScript

/***********************************************************************************************************************************************************************
* File Name : lift_expired_bans.cron.js
* Type : Cron Job
* Description : Automatically lifts temporary bans whose expires_at has passed.
* Clears is_banned + ban_expires_at on the user record and marks
* the ban row as lifted with a system lift_reason.
*
* Note: The auth middleware also auto-lifts expired bans inline on
* the next login attempt, so this cron is a safety net — it keeps
* the DB state clean even if a user never logs in again.
*
* Schedule : Every hour, on the hour ("0 * * * *"). Registered by
* cron/admin.cron.js.
*
* Author: Kenneth Obsequio
* Date Created: Jun. 27, 2026
***********************************************************************************************************************************************************************/
const { Op } = require('sequelize');
const sequelize = require('../../config/db.config');
const mdl_Users = require('../../models/users/users.mdl');
const mdl_UserBans = require('../../models/users/user_bans.mdl');
const { sendEmail } = require('../../services/email.service');
const { fmtDate } = require('../../utils/datetime.util');
async function run() {
try {
const expiredBans = await mdl_UserBans.findAll({
where: {
ban_type: 'temporary',
is_lifted: false,
expires_at: { [Op.lte]: new Date() },
},
});
if (!expiredBans.length) return;
const banIds = expiredBans.map((b) => b.ban_id);
const userIds = [...new Set(expiredBans.map((b) => Number(b.user_id)))];
const usersMap = await mdl_Users.findAll({
where: { user_id: userIds },
attributes: ['user_id', 'email', 'personal_info'],
}).then((rows) => Object.fromEntries(rows.map((u) => [u.user_id, u])));
await sequelize.transaction(async (t) => {
await mdl_UserBans.update(
{
is_lifted: true,
lifted_at: new Date(),
lift_reason: 'Automatically lifted — ban period expired.',
},
{ where: { ban_id: banIds }, transaction: t }
);
await mdl_Users.update(
{ is_banned: false, ban_expires_at: null },
{ where: { user_id: userIds }, transaction: t }
);
});
const dateStr = fmtDate(new Date());
userIds.forEach((uid) => {
const u = usersMap[uid];
if (!u) return;
sendEmail({
to: u.email,
type: 'BAN_LIFTED',
data: {
name: u.personal_info?.name?.full_name ?? 'User',
email: u.email,
date: dateStr,
},
}).catch((err) => console.error('[CRON][LIFT EXPIRED BANS] Email failed:', u.email, err));
});
console.log(`[CRON][LIFT EXPIRED BANS] Lifted ${expiredBans.length} ban(s) for ${userIds.length} user(s).`);
} catch (err) {
console.error('[CRON][LIFT EXPIRED BANS] Failed:', err);
}
}
module.exports = {
name: 'liftExpiredBans',
schedule: '0 * * * *',
run,
};