Files
COMERCIAL-GTI/scripts/servidor.mjs
T
Gustavo d330bb5ff0 Baseline commit of the existing Maquina Comercial Pessoal project
Tracks the app that was already in place (capture, treatment, outreach,
WhatsApp integration, server, frontend, tests, docs) so future feature
work can branch and use isolated worktrees.
2026-07-31 10:54:07 -03:00

83 lines
3.0 KiB
JavaScript

import { loadDotEnv, APP_PORT, paths } from '../src/env.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);
recoverReservedCampaignQueue(db);
const jobs = createJobManager({ db });
const whatsapp = createWhatsAppManager({
authDir: paths.whatsappAuth,
log,
onMessageStatus: (event) => updateOutreachMessageStatus(db, event),
onInboundMessage: (event) => recordInboundReply(db, event)
});
if (await hasWhatsAppSession(paths.whatsappAuth)) {
whatsapp.connect().catch((error) => log(`whatsapp: reconexao inicial falhou: ${error.message}`));
}
const server = createAppServer({ db, jobs, whatsapp, log });
const stopScheduler = startOutreachScheduler({ db, whatsapp, log });
const port = APP_PORT;
server.listen(port, '127.0.0.1', () => {
log(`Maquina Comercial Pessoal rodando em http://127.0.0.1:${port}`);
log('somente este computador tem acesso (127.0.0.1). Ctrl+C para encerrar.');
});
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);