/** * @ai-summary Centralized cron job scheduler * @ai-context Uses node-cron for scheduling background tasks */ import cron from 'node-cron'; import { logger } from '../logging/logger'; import { processScheduledNotifications } from '../../features/notifications/jobs/notification-processor.job'; import { processAccountPurges } from '../../features/user-profile/jobs/account-purge.job'; import { processScheduledBackups, setBackupJobPool, } from '../../features/backup/jobs/backup-scheduled.job'; import { processBackupRetention, setBackupCleanupJobPool, } from '../../features/backup/jobs/backup-cleanup.job'; import { processAuditLogCleanup, setAuditLogCleanupJobPool, } from '../../features/audit-log/jobs/cleanup.job'; import { processGracePeriodExpirations, setGracePeriodJobPool, } from '../../features/subscriptions/jobs/grace-period.job'; import { pool } from '../config/database'; let schedulerInitialized = false; export function initializeScheduler(): void { if (schedulerInitialized) { logger.warn('Scheduler already initialized, skipping'); return; } logger.info('Initializing cron scheduler'); // Initialize backup job pools setBackupJobPool(pool); setBackupCleanupJobPool(pool); // Initialize audit log cleanup job pool setAuditLogCleanupJobPool(pool); // Initialize grace period job pool setGracePeriodJobPool(pool); // Daily notification processing at 8 AM cron.schedule('0 8 * * *', async () => { logger.info('Running scheduled notification job'); try { await processScheduledNotifications(); } catch (error) { logger.error('Scheduled notification job failed', { error: error instanceof Error ? error.message : String(error) }); } }); // Daily account purge job at 2 AM (GDPR compliance - 30-day grace period) cron.schedule('0 2 * * *', async () => { logger.info('Running account purge job'); try { const result = await processAccountPurges(); logger.info('Account purge job completed successfully', { processed: result.processed, deleted: result.deleted, errors: result.errors.length, }); } catch (error) { logger.error('Account purge job failed', { error: error instanceof Error ? error.message : String(error) }); } }); // Grace period expiration check at 2:30 AM daily cron.schedule('30 2 * * *', async () => { logger.info('Running grace period expiration job'); try { const result = await processGracePeriodExpirations(); logger.info('Grace period job completed', { processed: result.processed, downgraded: result.downgraded, errors: result.errors.length, }); } catch (error) { logger.error('Grace period job failed', { error: error instanceof Error ? error.message : String(error) }); } }); // Check for scheduled backups every minute cron.schedule('* * * * *', async () => { logger.debug('Checking for scheduled backups'); try { await processScheduledBackups(); } catch (error) { logger.error('Scheduled backup check failed', { error: error instanceof Error ? error.message : String(error) }); } }); // Backup retention cleanup at 4 AM daily (after backups complete) cron.schedule('0 4 * * *', async () => { logger.info('Running backup retention cleanup job'); try { const result = await processBackupRetention(); logger.info('Backup retention cleanup completed', { processed: result.processed, totalDeleted: result.totalDeleted, totalFreedBytes: result.totalFreedBytes, errors: result.errors.length, }); } catch (error) { logger.error('Backup retention cleanup failed', { error: error instanceof Error ? error.message : String(error) }); } }); // Audit log retention cleanup at 3 AM daily (90-day retention) cron.schedule('0 3 * * *', async () => { logger.info('Running audit log cleanup job'); try { const result = await processAuditLogCleanup(); if (result.success) { logger.info('Audit log cleanup job completed', { deletedCount: result.deletedCount, retentionDays: result.retentionDays, }); } else { logger.error('Audit log cleanup job failed', { error: result.error, }); } } catch (error) { logger.error('Audit log cleanup job failed', { error: error instanceof Error ? error.message : String(error) }); } }); schedulerInitialized = true; logger.info('Cron scheduler initialized - notification (8 AM), account purge (2 AM), grace period (2:30 AM), audit log cleanup (3 AM), backup check (every min), retention cleanup (4 AM)'); } export function isSchedulerInitialized(): boolean { return schedulerInitialized; }