import { loadDotEnv, APP_HOST, APP_PORT, paths, telegramConfig } from '../src/env.mjs'; import { createTelegramAlerter } from '../src/alerts/telegram.mjs'; import { hasCampaignsPausedByRestart, notifyStartupState, startInitialWhatsAppReconnect } from '../src/alerts/runtime-alerts.mjs'; import { openDb, ensureSeeds, recoverPendingOutreach, updateOutreachMessageStatus } 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 { pauseCampaignsAfterRestart, recoverReservedCampaignQueue } from '../src/outreach/campaign-worker.mjs'; import { recordInboundReply } from '../src/outreach/inbound.mjs'; import { createWhatsAppManager, hasWhatsAppSession } from '../src/whatsapp/manager.mjs'; // Servidor local da Maquina Comercial Pessoal. await loadDotEnv(); const log = (message) => console.log(`[servidor] ${message}`); const db = openDb(); await ensureSeeds(db, { log }); recoverOrphans(db, log); recoverPendingOutreach(db); pauseCampaignsAfterRestart(db); const campaignsPausedByRestart = hasCampaignsPausedByRestart(db); recoverReservedCampaignQueue(db); const tg = telegramConfig(); const alerter = createTelegramAlerter({ enabled: tg.enabled, token: tg.botToken, chatId: tg.chatId, log }); const jobs = createJobManager({ db }); const whatsapp = createWhatsAppManager({ authDir: paths.whatsappAuth, log, onMessageStatus: (event) => updateOutreachMessageStatus(db, event), onInboundMessage: (event) => recordInboundReply(db, event) }); void notifyStartupState({ alerter, whatsappStatus: whatsapp.getStatus().state, campaignsPausedByRestart, log }); await startInitialWhatsAppReconnect({ hasSession: () => hasWhatsAppSession(paths.whatsappAuth), whatsapp, alerter, log }); const server = createAppServer({ db, jobs, whatsapp, log }); const stopScheduler = startOutreachScheduler({ db, whatsapp, log }); const port = APP_PORT; server.listen(port, APP_HOST, () => { log(`Maquina Comercial Pessoal rodando em http://${APP_HOST}:${port}`); if (APP_HOST === '127.0.0.1') log('somente este computador tem acesso (127.0.0.1). Ctrl+C para encerrar.'); else log('modo container/rede: proteja o acesso com proxy HTTPS e senha forte.'); }); let shuttingDown = false; async function shutdown() { if (shuttingDown) return; shuttingDown = true; log('encerrando...'); const watchdog = setTimeout(() => process.exit(1), 30_000); watchdog.unref(); const shutdownTasks = [ stopScheduler().catch((error) => log(`outreach: falha ao encerrar scheduler: ${error.message}`)), jobs.close().catch((error) => log(`jobs: falha ao encerrar: ${error.message}`)), whatsapp.close().catch((error) => log(`whatsapp: falha ao encerrar: ${error.message}`)) ]; let resourceTimer; const resourcesTimedOut = await Promise.race([ Promise.allSettled(shutdownTasks).then(() => false), new Promise((resolve) => { resourceTimer = setTimeout(() => resolve(true), 20_000); resourceTimer.unref(); }) ]); clearTimeout(resourceTimer); if (resourcesTimedOut) log('encerramento dos recursos excedeu o prazo; prosseguindo'); recoverOrphans(db, log); let httpTimer; const httpTimedOut = await Promise.race([ new Promise((resolve) => server.close(resolve)).then(() => false), new Promise((resolve) => { httpTimer = setTimeout(() => resolve(true), 5_000); httpTimer.unref(); }) ]); clearTimeout(httpTimer); if (httpTimedOut) log('encerramento HTTP excedeu o prazo; prosseguindo'); db.close(); process.exit(0); } process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown);