115 lines
3.7 KiB
TypeScript
115 lines
3.7 KiB
TypeScript
/**
|
|
* 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<typeof setInterval> | 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<void> {
|
|
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),
|
|
});
|
|
}
|
|
}
|