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.
83 lines
3.0 KiB
JavaScript
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);
|