/** * Audit Cleanup Job * * Runs the GDPR IP anonymisation task on a schedule. * Must be started once from server.ts during application boot. * * Two scheduling options are shown: * 1. node-cron (recommended) — cron expression, survives DST changes * 2. setInterval (fallback) — simpler, no extra dependency * * Install node-cron if using option 1: * npm install node-cron * npm install --save-dev @types/node-cron */ import auditService from '../services/audit.service'; import logger from '../utils/logger'; // --------------------------------------------------------------------------- // Option 1 (RECOMMENDED): node-cron // // Runs every day at 02:00 local server time. // The cron expression format is: second(optional) minute hour dom month dow // // Uncomment this block and comment out the setInterval block below. // --------------------------------------------------------------------------- /* import cron from 'node-cron'; let cronJob: cron.ScheduledTask | null = null; export function startAuditCleanupJob(): void { if (cronJob) { logger.warn('Audit cleanup job already running — skipping duplicate start'); return; } // '0 2 * * *' = every day at 02:00 cronJob = cron.schedule('0 2 * * *', async () => { logger.info('Audit cleanup job: starting IP anonymisation'); await auditService.anonymizeOldIpAddresses(); logger.info('Audit cleanup job: IP anonymisation complete'); }, { scheduled: true, timezone: 'Europe/Vienna', // Feuerwehr Rems is in Austria }); logger.info('Audit cleanup job scheduled (node-cron, daily at 02:00 Europe/Vienna)'); } export function stopAuditCleanupJob(): void { if (cronJob) { cronJob.stop(); cronJob = null; logger.info('Audit cleanup job stopped'); } } */ // --------------------------------------------------------------------------- // Option 2 (FALLBACK): setInterval // // Runs every 24 hours from the moment the server starts. // Simpler — no extra dependency — but does not align to wall-clock time // and will drift if the server restarts frequently. // --------------------------------------------------------------------------- const INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours let cleanupInterval: ReturnType | null = null; export function startAuditCleanupJob(): void { if (cleanupInterval !== null) { logger.warn('Audit cleanup job already running — skipping duplicate start'); return; } // Run once on startup to catch any backlog, then repeat on schedule. // We intentionally do not await — startup must not be blocked. runAuditCleanup(); cleanupInterval = setInterval(() => { runAuditCleanup(); }, INTERVAL_MS); logger.info('Audit cleanup job scheduled (setInterval, 24h interval)'); } export function stopAuditCleanupJob(): void { if (cleanupInterval !== null) { clearInterval(cleanupInterval); cleanupInterval = null; logger.info('Audit cleanup job stopped'); } } // --------------------------------------------------------------------------- // Core cleanup function — called by both scheduler options // --------------------------------------------------------------------------- export async function runAuditCleanup(): Promise { try { logger.debug('Audit cleanup: running IP anonymisation'); await auditService.anonymizeOldIpAddresses(); } catch (error) { // anonymizeOldIpAddresses() swallows its own errors, but we add a second // layer of protection here so that a scheduler implementation mistake // cannot crash the process. logger.error('Audit cleanup job: unexpected error', { error: error instanceof Error ? error.message : String(error), }); } }