Something went wrong. Try again.
Periodic backups of ATProto repos and blobs safekeeper.dummy.cafe
atproto backup
Something went wrong. Try again.
1.8 kB · 68 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869// Background scheduler: runs `runBackupCycle` on an interval, with a simple// in-process mutex so concurrent triggers don't stack.//// The scheduler is deliberately minimal — a single `setInterval` is fine for// an app whose job is "check in periodically". If we ever want cron-style// "daily at 03:00" we can swap the trigger without changing `runBackupCycle`.
import { env } from '../env';import { runBackupCycle, type RunSummary } from './runner';
let running: Promise<RunSummary> | null = null;let timer: ReturnType<typeof setInterval> | null = null;let lastSummary: RunSummary | null = null;let lastError: Error | null = null;let lastRanAt: Date | null = null;
export async function triggerBackup(): Promise<RunSummary> { if (running) return running; running = (async () => { try { const summary = await runBackupCycle(); lastSummary = summary; lastError = null; return summary; } catch (err) { lastError = err as Error; throw err; } finally { lastRanAt = new Date(); running = null; } })(); return running;}
export function startScheduler(): void { if (timer) return; const runOnce = () => { triggerBackup().catch((err) => { console.error('[scheduler] backup cycle failed:', err); }); }; // Fire once at startup, then on the interval. The in-process mutex stops // overlap if a cycle runs longer than the interval. runOnce(); timer = setInterval(runOnce, env.BACKUP_INTERVAL_MS);}
export function stopScheduler(): void { if (timer) { clearInterval(timer); timer = null; }}
export function schedulerState(): { isRunning: boolean; lastRanAt: Date | null; lastSummary: RunSummary | null; lastError: string | null;} { return { isRunning: running !== null, lastRanAt, lastSummary, lastError: lastError?.message ?? null };}