import { getStringSetting } from '../models/settingsModel.js'; import { listDueRunPlanSchedules, recordRunPlanScheduleDispatch } from '../models/runPlanScheduleModel.js'; import { executeRunPlan } from './powershellRunner.js'; import { logger } from './logger.js'; let timer = null; let running = false; export function runPlanScheduleIntervalMinutes() { const minutes = Number(getStringSetting('runplan_schedule_interval_minutes', process.env.RUNPLAN_SCHEDULE_INTERVAL_MINUTES || '1')); return Number.isFinite(minutes) ? minutes : 1; } export async function dispatchDueRunPlanSchedules({ nowIso = new Date().toISOString(), signal } = {}) { const due = listDueRunPlanSchedules(nowIso); const summary = { evaluated: due.length, dispatched: 0, skipped: 0, errors: 0 }; for (const schedule of due) { if (signal?.aborted) { summary.skipped += 1; break; } try { const job = executeRunPlan(schedule.runplanId, schedule.createdBy || schedule.ownerUserId || null); recordRunPlanScheduleDispatch(schedule, { jobId: job.id, plannedFor: schedule.nextRunAt || nowIso, status: 'queued', message: `Scheduled RunPlan ${schedule.runplanName || schedule.runplanId} queued as ${job.id}.` }); summary.dispatched += 1; } catch (error) { recordRunPlanScheduleDispatch(schedule, { plannedFor: schedule.nextRunAt || nowIso, status: 'failed', message: error.message }); summary.errors += 1; logger.error('runplan schedule dispatch failed', { scheduleId: schedule.id, error: error.message }); } } return summary; } export function startRunPlanScheduleWorker() { const minutes = runPlanScheduleIntervalMinutes(); if (minutes <= 0) return null; running = true; scheduleNext(minutes); logger.info(`runplan schedule dispatcher scheduled every ${minutes} minute(s)`); return timer; } export function stopRunPlanScheduleWorker() { running = false; if (timer) clearTimeout(timer); timer = null; } function scheduleNext(minutes) { if (!running || minutes <= 0) return; timer = setTimeout(() => { dispatchDueRunPlanSchedules() .then((summary) => logger.info('runplan schedule dispatch complete', summary)) .catch((error) => logger.error('runplan schedule dispatch failed', { error: error.message })) .finally(() => scheduleNext(runPlanScheduleIntervalMinutes())); }, minutes * 60 * 1000); timer.unref?.(); }