import cron from 'node-cron'; import { runSync } from './sync_source_docs.js'; import { log } from '../log.js'; import { enqueue } from '../jobs/queue.js'; import { checkAll } from '../health/checker.js'; import * as statusRepo from '../db/repos/service_status.js'; import * as services from '../db/repos/monitored_services.js'; import { runDeviceScanCycle } from '../infra/scan_cycle.js'; import * as settings from '../db/repos/app_settings.js'; // Speedtest runs on a user-configurable interval (PUT /api/speedtest/config → // setSpeedtestSchedule). Held module-level so it can be stopped + rescheduled. let speedtestTask = null; function speedtestExpr(min) { if (min < 60) return `*/${min} * * * *`; if (min % 60 === 0) { const h = min / 60; return h >= 24 ? '0 2 * * *' : `0 */${h} * * *`; } return '0 * * * *'; } export function setSpeedtestSchedule(min) { const m = Math.max(5, Math.min(1440, Number(min) || 60)); if (speedtestTask) { speedtestTask.stop(); speedtestTask = null; } const expr = speedtestExpr(m); speedtestTask = cron.schedule(expr, async () => { try { await enqueue('speedtest', {}); log.info({ expr }, 'cron speedtest enqueued'); } catch (e) { log.error({ err: e }, 'cron speedtest failed'); } }); log.info({ expr, min: m }, 'speedtest schedule set'); } export function startCron() { // Daily at 03:00 local time cron.schedule('0 3 * * *', async () => { try { const n = await runSync(); log.info({ enqueued: n }, 'cron sync.source_doc complete'); } catch (e) { log.error({ err: e }, 'cron sync.source_doc failed'); } }); // Speedtest — interval from the saved config (default 60 min), reschedulable. settings.get('speedtest', {}) .then(cfg => setSpeedtestSchedule(cfg?.interval_min || 60)) .catch(e => { log.error({ err: e }, 'speedtest schedule init failed'); setSpeedtestSchedule(60); }); // Health checks every minute. NOTE: this runs checkAll() inline; the same // probe+upsert logic is also exposed on-demand via the `health.check` pg-boss // worker (lib/jobs/workers/health_check.js, triggered by POST /api/health/check). // Keep the two in sync — both rely on lib/health/checker.js as the source of truth. cron.schedule('*/1 * * * *', async () => { try { const results = await checkAll(await services.listEnabled()); for (const r of results) await statusRepo.upsert(r); log.info({ n: results.length }, 'health check complete'); } catch (e) { log.error({ err: e }, 'health check failed'); } }); // Hourly LAN device scan (staggered off the :00 speedtest) cron.schedule('7 * * * *', async () => { try { await runDeviceScanCycle(); } catch (e) { log.error({ err: e }, 'device scan cycle failed'); } }); log.info('cron started'); }