// ── Team forum upload sweep ──────────────────────────────────────────────── // // TEAMS.md §5.5.4's lifecycle half: soft-deleted uploads lose their bytes after a // retention window, and files uploaded into a composer that was never submitted // lose theirs after a grace period. The existing admin upload path never deletes // anything, which is fine at admin volume and is not fine once a community can // upload. // // Same in-process shape as utils/teamActivityPrune — setInterval + unref + stop(), // wired into server.js start/shutdown. There is no cron in this stack. // // **It runs whether or not `teams_forum_images` is `uploads`, and that is the // point.** An operator who turns uploads off after a problem has files already on // disk; a sweep that switched itself off with the setting would strand exactly the // bytes they were trying to be rid of. The admin help text says the same thing in // the other direction — disabling uploads stops new files, it does not delete old // ones — and this is the only thing that eventually does. const uploads = require('../model/teams/teamForumUploads.model') const log = require('./logger')('teams') const INTERVAL_MS = Number(process.env.TEAM_FORUM_SWEEP_MS) || 24 * 60 * 60 * 1000 // Later than the activity prune's five minutes, so two table-walking jobs do not // land on the same boot at the same moment. const FIRST_RUN_MS = Number(process.env.TEAM_FORUM_SWEEP_DELAY_MS) || 10 * 60 * 1000 let timer = null let firstRun = null /** One sweep. Never throws — it runs on a timer with nobody to catch it. */ async function tick() { try { const result = await uploads.sweep() if (result.swept) log.info('team forum upload sweep', result) return result } catch (err) { log.error('team forum upload sweep failed', { message: err.message }) return null } } function start() { if (timer || firstRun) return timer firstRun = setTimeout(() => { firstRun = null tick() timer = setInterval(() => { tick() }, INTERVAL_MS) if (timer.unref) timer.unref() }, FIRST_RUN_MS) if (firstRun.unref) firstRun.unref() log.info('team forum upload sweep started', { intervalMs: INTERVAL_MS, firstRunMs: FIRST_RUN_MS }) return timer } function stop() { if (firstRun) { clearTimeout(firstRun) firstRun = null } if (timer) { clearInterval(timer) timer = null } } module.exports = { start, stop, tick, INTERVAL_MS, FIRST_RUN_MS }