42 lines
1.4 KiB
TypeScript
42 lines
1.4 KiB
TypeScript
import { and, eq, lt } from 'drizzle-orm'
|
|
import { db } from '../../db/client.js'
|
|
import { notifications, topics } from '../../db/schema.js'
|
|
import { resolveRetentionDefaultDays } from '../admin/system-settings.js'
|
|
|
|
const CLEANUP_INTERVAL_MS = 60 * 60 * 1000 // 1 hour
|
|
|
|
// Per-topic retentionDays overrides the global default; 0 means "forever".
|
|
// See docs/phase1/03-database-model.md, "Retention cleanup".
|
|
export async function runRetentionCleanup(): Promise<number> {
|
|
const topicRows = await db.select().from(topics)
|
|
const retentionDefaultDays = await resolveRetentionDefaultDays()
|
|
let totalDeleted = 0
|
|
|
|
for (const topic of topicRows) {
|
|
const retentionDays = topic.retentionDays ?? retentionDefaultDays
|
|
if (retentionDays === 0) continue
|
|
|
|
const cutoff = new Date(Date.now() - retentionDays * 24 * 60 * 60 * 1000)
|
|
const deleted = await db
|
|
.delete(notifications)
|
|
.where(and(eq(notifications.topicId, topic.id), lt(notifications.createdAt, cutoff)))
|
|
.returning({ id: notifications.id })
|
|
|
|
totalDeleted += deleted.length
|
|
}
|
|
|
|
return totalDeleted
|
|
}
|
|
|
|
export function startRetentionCleanupJob(
|
|
onError: (err: unknown) => void = console.error,
|
|
): NodeJS.Timeout {
|
|
const run = () => {
|
|
runRetentionCleanup().catch(onError)
|
|
}
|
|
run() // catch up on anything overdue from before this boot
|
|
const timer = setInterval(run, CLEANUP_INTERVAL_MS)
|
|
timer.unref() // never block process shutdown just to keep this alive
|
|
return timer
|
|
}
|