/*********************************************************************************************************************************************************************** * 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, };