// controllers/admin/notificationSettings.controller.js const CronNotificationSetting = require('../../models/notifications/cron_notification_setting.mdl'); const R = require('../../utils/response.util'); const logActivity = require('../../utils/logActivity.util'); const { CRON_PRESETS, CRON_PRESET_BY_EXPRESSION } = require('../../data/cronPresets.data'); const { rescheduleJob, getOrCreateSetting } = require('../../cron/cronRegistry.util'); // ─── Job registry — which cron scope owns each job (for defaults + labels) ──── const JOBS = { taskOverdue: { schedule: '0 * * * *', label: 'Task Overdue Alerts (Admin)', description: 'Notifies admins when tasks flip to overdue.' }, userNotifications: { schedule: '5 * * * *', label: 'Task Overdue Alerts (Users)', description: 'Notifies affected users when their tasks are marked overdue.' }, issueCertificates: { schedule: '0 * * * *', label: 'Certificate Issued', description: 'Notifies users when a course certificate is ready.' }, expireUserTiers: { schedule: '* * * * *', label: 'Tier Expired', description: 'Notifies users when their subscription tier expires.' }, }; // ─── GET ────────────────────────────────────────────────────────────────────── exports.getSettings = async (req, res) => { try { const rows = []; for (const [job_name, meta] of Object.entries(JOBS)) { const row = await getOrCreateSetting(job_name, meta.schedule); rows.push({ job_name, enabled: row.enabled, schedule: row.schedule, preset: CRON_PRESET_BY_EXPRESSION[row.schedule] ?? null, label: meta.label, description: meta.description, updatedAt: row.updatedAt, }); } return R.success(res, 'Notification settings retrieved.', rows); } catch (err) { console.error('[NOTIFICATION SETTINGS][GET]', err); return R.error(res, 'Could not retrieve notification settings.', 500); } }; // ─── UPDATE ─────────────────────────────────────────────────────────────────── exports.updateSetting = async (req, res) => { try { const { jobName } = req.params; const { enabled, preset, updatedBy } = req.body; if (!JOBS[jobName]) return R.error(res, `Unknown job "${jobName}".`, 404); const row = await CronNotificationSetting.findOne({ where: { job_name: jobName } }); if (!row) return R.error(res, 'Setting not found.', 404); if (enabled !== undefined) row.enabled = enabled === true || enabled === 'true'; if (preset !== undefined) { const schedule = CRON_PRESETS[preset]; if (!schedule) return R.error(res, `Invalid preset. Must be one of: ${Object.keys(CRON_PRESETS).join(', ')}`, 400); row.schedule = schedule; try { rescheduleJob(jobName, schedule); } catch (err) { console.error('[NOTIFICATION SETTINGS][RESCHEDULE]', err); return R.error(res, `Saved, but failed to reschedule the live job: ${err.message}`, 500); } } row.updatedBy = updatedBy ?? null; await row.save(); logActivity(req.user?.user_id, 'update_notification_setting', { entityType: 'cron_notification_setting', entityId: jobName, details: { enabled: row.enabled, schedule: row.schedule } }); return R.success(res, 'Notification setting updated.', { data: row }); } catch (err) { console.error('[NOTIFICATION SETTINGS][UPDATE]', err); return R.error(res, 'Internal server error.', 500); } };