Repository Public
1/**2 * Daily retention (see the comment at the bottom of migrations/001_init.sql):3 * terminal_events: settings.sessionRetentionDays (default 90) · node_metrics: 14 d ·4 * login_attempts: 30 d · audit_events: 400 d · notifications: 90 d ·5 * auth_sessions: 7 d after expiry/revocation · preauth_challenges: once expired6 */7import type { FastifyBaseLogger } from 'fastify';8import type { Db } from './db.js';910export async function runJanitor(db: Db): Promise<Record<string, number>> {11 const del = async (sql: string): Promise<number> => (await db.q(`WITH d AS (${sql} RETURNING 1) SELECT count(*)::int AS n FROM d`))[0]?.n as number;12 return {13 terminal_events: await del(14 `DELETE FROM terminal_events e USING terminal_sessions s15 WHERE s.id = e.session_id AND e.at < now() - make_interval(days => coalesce(16 (SELECT least(greatest((a.settings->>'sessionRetentionDays')::int, 1), 3650) FROM application_settings a17 WHERE a.user_id = s.owner_id AND a.settings->>'sessionRetentionDays' ~ '^[0-9]{1,4}$'), 90))`,18 ),19 node_metrics: await del("DELETE FROM node_metrics WHERE at < now() - interval '14 days'"),20 login_attempts: await del("DELETE FROM login_attempts WHERE at < now() - interval '30 days'"),21 audit_events: await del("DELETE FROM audit_events WHERE at < now() - interval '400 days'"),22 notifications: await del("DELETE FROM notifications WHERE created_at < now() - interval '90 days'"),23 auth_sessions: await del(24 "DELETE FROM auth_sessions WHERE expires_at < now() - interval '7 days' OR revoked_at < now() - interval '7 days'",25 ),26 preauth_challenges: await del('DELETE FROM preauth_challenges WHERE expires_at < now()'),27 };28}2930export function startJanitor(db: Db, log: FastifyBaseLogger): () => void {31 const run = (): void => {32 runJanitor(db).then(33 (deleted) => log.info({ deleted }, 'janitor pass'),34 (err: unknown) => log.error({ err }, 'janitor failed'),35 );36 };37 const first = setTimeout(run, 60_000);38 const timer = setInterval(run, 24 * 3600 * 1000);39 first.unref();40 timer.unref();41 return () => {42 clearTimeout(first);43 clearInterval(timer);44 };45}46