import { config } from "./config.js"; import { listUpcomingDeadlines } from "./contractsStore.js"; import { createLogger } from "./logger.js"; import { composeDeadlineNotification, sendDeadlineNotifications } from "./notifications.js"; import { getRuntimeSettings } from "./runtimeSettings.js"; const logger = createLogger(config.logLevel); export class DeadlineMonitor { private timer: NodeJS.Timeout | null = null; private running = false; start() { if (this.running) { return; } this.running = true; const settings = getRuntimeSettings(); logger.info( `Starting deadline monitor (interval=${settings.schedulerIntervalMinutes} min, alert window=${settings.alertDaysBefore} days)` ); this.scheduleNext(1000); } stop() { if (!this.running) { return; } this.running = false; if (this.timer) { clearTimeout(this.timer); this.timer = null; } logger.info("Deadline monitor stopped."); } private scheduleNext(delayMs?: number) { if (!this.running) { return; } const settings = getRuntimeSettings(); const intervalMs = Math.max(settings.schedulerIntervalMinutes, 5) * 60 * 1000; const wait = delayMs ?? intervalMs; this.timer = setTimeout(() => { this.runCheck() .catch((error) => { logger.error("Deadline check failed", error); }) .finally(() => { this.scheduleNext(); }); }, wait); } private async runCheck() { const settings = getRuntimeSettings(); const deadlines = listUpcomingDeadlines(settings.alertDaysBefore); if (deadlines.length === 0) { logger.debug("No upcoming deadlines within alert window."); return; } for (const item of deadlines) { logger.warn( "Upcoming deadline: %s (provider=%s, documentId=%s, terminate by %s, days=%s)", item.title, item.provider ?? "n/a", item.paperlessDocumentId ?? "n/a", item.terminationDeadline ?? "n/a", item.daysUntilDeadline ?? "n/a" ); } const notification = composeDeadlineNotification(deadlines, settings); await sendDeadlineNotifications(notification, settings); } } export const deadlineMonitor = new DeadlineMonitor();