feat(store): event_log + audit_log rotation (30d/90d TTL + 100k row cap, 6h interval)

This commit is contained in:
Mitchell R 2026-05-23 01:30:26 +02:00
parent 2d157e900d
commit 890271d4c8
No known key found for this signature in database
2 changed files with 43 additions and 11 deletions

View file

@ -175,22 +175,26 @@ export class Plugin extends BSBService<InstanceType<typeof Config>, typeof Event
registerRepo(this._repo);
const purged = this._repo.purgeOldKioskLogs(2);
if (purged > 0) {
obs.log.info("purged {count} kiosk logs older than 2h", { count: purged });
}
// Startup purge
this.runPurge(obs);
obs.log.info("store ready");
}
async run(obs: Observable): Promise<void> {
this.purgeTimer = setInterval(() => {
private runPurge(obs: Observable): void {
if (!this._repo) return;
const purged = this._repo.purgeOldKioskLogs(2);
if (purged > 0) {
obs.log.info("purged {count} kiosk logs older than 2h", { count: purged });
const r = this._repo;
const kl = r.purgeKioskLogs(14);
const el = r.purgeEventLog(30, 100_000);
const al = r.purgeAuditLog(90);
if (kl + el + al > 0) {
obs.log.info("purge: {kl} kiosk_logs, {el} event_log, {al} audit_log", { kl, el, al });
}
}, 10 * 60 * 1000);
}
async run(obs: Observable): Promise<void> {
// Purge every 6 hours.
this.purgeTimer = setInterval(() => this.runPurge(obs), 6 * 60 * 60 * 1000);
}
async dispose(): Promise<void> {

View file

@ -1722,6 +1722,34 @@ export class Repository {
this.prep("UPDATE event_log SET forwarded_to_nodered = 1 WHERE id = ?").run(eventId);
}
/**
* Delete event_log rows older than `days` AND trim to `maxRows` total.
* Returns the number of rows deleted.
*/
purgeEventLog(days: number = 30, maxRows: number = 100_000): number {
const cutoff = new Date(Date.now() - days * 86_400_000).toISOString();
const r1 = this.db.prepare("DELETE FROM event_log WHERE received_at < ?").run(cutoff);
// Trim to maxRows by deleting oldest beyond the cap.
const r2 = this.db.prepare(
`DELETE FROM event_log WHERE id NOT IN (
SELECT id FROM event_log ORDER BY received_at DESC LIMIT ?
)`,
).run(maxRows);
return Number(r1.changes) + Number(r2.changes);
}
purgeAuditLog(days: number = 90): number {
const cutoff = new Date(Date.now() - days * 86_400_000).toISOString();
const r = this.db.prepare("DELETE FROM audit_log WHERE ts < ?").run(cutoff);
return Number(r.changes);
}
purgeKioskLogs(days: number = 14): number {
const cutoff = new Date(Date.now() - days * 86_400_000).toISOString();
const r = this.db.prepare("DELETE FROM kiosk_logs WHERE received_at < ?").run(cutoff);
return Number(r.changes);
}
queryEvents(filters: EventQueryFilters): { events: EventLog[]; total: number } {
const where: string[] = [];
const params: (string | number)[] = [];