From 4835c6e085e0a3b95d7b275b43632d4731c0aafd Mon Sep 17 00:00:00 2001 From: Gustavo Date: Mon, 3 Aug 2026 08:13:45 -0300 Subject: [PATCH] Wire campaign scheduler into server boot --- scripts/servidor.mjs | 2 ++ src/outreach/campaign-scheduler.mjs | 25 +++++++++++++++++++++++++ tests/campaign-scheduler.test.mjs | 12 ++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 src/outreach/campaign-scheduler.mjs create mode 100644 tests/campaign-scheduler.test.mjs diff --git a/scripts/servidor.mjs b/scripts/servidor.mjs index 5cdb13a..871c293 100644 --- a/scripts/servidor.mjs +++ b/scripts/servidor.mjs @@ -3,6 +3,7 @@ import { openDb, ensureSeeds } from '../src/db.mjs'; import { createJobManager, recoverOrphans } from '../src/jobs/manager.mjs'; import { createAppServer } from '../src/server/index.mjs'; import { startOutreachScheduler } from '../src/outreach/scheduler.mjs'; +import { startCampaignScheduler } from '../src/outreach/campaign-scheduler.mjs'; // Servidor local do GTI Maquina Comercial. Inicie com: // npm start (ou node scripts/servidor.mjs, ou o GTI-Comercial.exe) @@ -19,6 +20,7 @@ recoverOrphans(db, log); const jobs = createJobManager(); const server = createAppServer({ db, jobs, log }); startOutreachScheduler({ db, log }); +startCampaignScheduler({ db, log }); const port = GTI_PORT; server.listen(port, '127.0.0.1', () => { diff --git a/src/outreach/campaign-scheduler.mjs b/src/outreach/campaign-scheduler.mjs new file mode 100644 index 0000000..ed0407e --- /dev/null +++ b/src/outreach/campaign-scheduler.mjs @@ -0,0 +1,25 @@ +import { runCampaignQueue } from './campaign-worker.mjs'; +import { escalateWeeklyLimits } from './campaign-store.mjs'; + +const CICLO_MS = 20 * 60 * 1000; + +// Timer interno separado do scheduler.mjs existente (run.mjs/runOutreachBatch) +// — mesmo padrao (setInterval com .unref(), primeiro ciclo antecipado pouco +// depois do boot), rodando ao lado sem interferir um no outro. +export function startCampaignScheduler({ db, log = () => {} }) { + const ciclo = () => { + escalateWeeklyLimits(db); + runCampaignQueue({ db, log }).catch((error) => { + log(`campanhas: erro inesperado no ciclo automatico: ${error.message}`); + }); + }; + + const primeiro = setTimeout(ciclo, 45 * 1000); + primeiro.unref(); + const timer = setInterval(ciclo, CICLO_MS); + timer.unref(); + return () => { + clearTimeout(primeiro); + clearInterval(timer); + }; +} diff --git a/tests/campaign-scheduler.test.mjs b/tests/campaign-scheduler.test.mjs new file mode 100644 index 0000000..544cec3 --- /dev/null +++ b/tests/campaign-scheduler.test.mjs @@ -0,0 +1,12 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { openDb } from '../src/db.mjs'; +import { startCampaignScheduler } from '../src/outreach/campaign-scheduler.mjs'; + +test('startCampaignScheduler retorna funcao de parada que limpa os timers sem lancar erro', () => { + const db = openDb(':memory:'); + const stop = startCampaignScheduler({ db, log: () => {} }); + assert.equal(typeof stop, 'function'); + assert.doesNotThrow(() => stop()); + db.close(); +});