import { and, eq, lt } from 'drizzle-orm'; import { db } from '../db/client'; import { recordings } from '../db/schema'; import { hasRequiredTables } from '../utils/db-schema'; const STALE_RECORDING_SECONDS = Number(process.env.RECORDING_STALE_SECONDS ?? 60 * 30); export const startRecordingsWorker = (): void => { const intervalMs = Number(process.env.RECORDING_WORKER_INTERVAL_MS ?? 30_000); const requiredTables = ['recordings']; void (async () => { const ready = await hasRequiredTables(requiredTables); if (!ready) { console.warn( `[recordings worker] skipped startup because required tables are missing (${requiredTables.join(', ')}). Run migrations and restart.`, ); return; } setInterval(() => { reconcileStaleRecordings().catch((error) => { console.error('recordings worker failed', error); }); }, intervalMs); })().catch((error) => { console.error('recordings worker failed to initialize', error); }); }; const reconcileStaleRecordings = async (): Promise => { const staleBefore = new Date(Date.now() - STALE_RECORDING_SECONDS * 1000); const stale = await db.query.recordings.findMany({ where: and(eq(recordings.status, 'awaiting_upload'), lt(recordings.createdAt, staleBefore)), limit: 100, }); for (const recording of stale) { await db .update(recordings) .set({ status: 'failed', error: 'recording upload timeout', updatedAt: new Date(), }) .where(eq(recordings.id, recording.id)); } };