add features

This commit is contained in:
Matthias Hochmeister
2026-02-27 19:50:14 +01:00
parent c5e8337a69
commit 620bacc6b5
46 changed files with 14095 additions and 1 deletions

View File

@@ -0,0 +1,114 @@
/**
* 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),
});
}
}