Crons e Jobs Agendados — Fonte única da verdade¶
Última revisão: 2026-04-21
Este documento lista todos os jobs automatizados da Gita e serve como guia operacional: quem, onde, quando, como desativar.
Três sistemas rodam jobs:
- Easypanel —
apiclickup(/Users/juniormaia/gita/server/) — automações de negócio, webhooks, crons node-cron. - Easypanel —
engine-gita-agents(/Users/juniormaia/projetos/gita-agents/packages/engine/) — pipeline multi-tenant de agentes WhatsApp + follow-up loop. - Launchd no Mac do Junior (
~/Library/LaunchAgents/com.juniormaia.*.plist) — briefings/relatórios locais. Ver LAUNCHD.md.
Controle visual de 1 e 2 → Admin panel em /crons (https://admin.gita.work/crons).
Arquitetura de controle¶
Cada cron tem duas camadas de ativação (ambas precisam estar on pra rodar):
| Camada | Onde mexe | Quando usar | Efeito |
|---|---|---|---|
Env flag (ENABLE_<ID>) |
Easypanel → serviço → Environment | Master switch. Desligar em incidente grave. Exige redeploy. | Se off, o cron nem é registrado no node-cron |
| Runtime toggle | Admin /crons (grava em Supabase crons_config) |
Pausar/ligar no dia-a-dia sem redeploy | Reflete em até 30s (cache). Cron registra normalmente, mas o handler pula. |
Regra de ouro¶
Pra desligar um cron: primeiro toggle no admin. Se não resolver (porque o handler ainda dispara para logs, webhooks etc), desliga a env flag no Easypanel.
Pra ligar um cron que estava fora: garante que a env flag tá on no Easypanel + toggle on no admin. Se o server foi deployed com a flag off, precisa redeploy (não basta mudar a var, o Easypanel só aplica no restart).
Servidor 1 — apiclickup (/gita/server/)¶
Registry declarativo: server/crons.js. Adicionar cron novo significa editar esse arquivo + criar o handler em automations/.
| id | Label | Schedule | Env flag | Default | Arquivo | Integrações |
|---|---|---|---|---|---|---|
scheduled-reports |
Relatório financeiro diário | 9h BRT seg–sex (0 12 * * 1-5 UTC) |
ENABLE_SCHEDULED_REPORTS |
off | automations/scheduled-reports.js | Uazapi · Google Sheets · Supabase |
vendas-reconcile |
Reconciliação Hotmart | 10h BRT diário (0 13 * * * UTC) |
ENABLE_VENDAS_RECONCILE |
on | automations/vendas-reconcile.js | Hotmart · Supabase |
diagnostico-sequencia |
Sequência 7 dias diagnóstico | a cada hora :15 (15 * * * *) |
ENABLE_DIAGNOSTICO_SEQUENCIA |
on | automations/diagnostico-sequencia.js | Anthropic · Uazapi · Supabase |
Endpoints úteis¶
GET /status— estado atual de todos os crons (env + runtime + lastRun).GET /reports/cron-status— diagnóstico só do relatório financeiro.GET /vendas/reconcile/status— diagnóstico da reconciliação.GET /diagnostico/sequencia/status— diagnóstico da sequência.POST /reports/send-daily— dispara relatório financeiro agora.POST /vendas/reconcile/run?dias=7— dispara reconciliação agora.POST /diagnostico/sequencia/processar— dispara sequência agora.GET /audit?event=cron.<id>— histórico de execuções.POST /crons/invalidate { id? }— invalida cache decrons_config(admin chama automaticamente após toggle).
Servidor 2 — engine-gita-agents (/projetos/gita-agents/packages/engine/)¶
| id | Label | Schedule | Env flag | Default | Arquivo |
|---|---|---|---|---|---|
followup-loop |
Follow-up automático de leads | a cada 60s | ENABLE_FOLLOWUP_LOOP |
on | engine/src/index.ts:337 |
Endpoints úteis¶
GET /status— lista o cron com estado env + runtime.POST /crons/invalidate { id? }— invalida cache.
Não há endpoint manual de "disparar agora" — loop dispara em 60s.
Como investigar um cron que não rodou¶
- Abrir
/cronsno admin. Ver estado efetivo (env + runtime) elastRun. - Se
env=off: flag está desligada no Easypanel → abrir console do Easypanel → Environment → ligar → redeploy. - Se
runtime=off: toggle no admin pra ligar (reflete em 30s). - Se ambos
onmaslastRunantigo: server pode estar fora do ar. Checar comcurl https://<host>/healthou ver o serviço no Easypanel. - Se
lastRunrecente masstatus=error: abrirGET /audit?event=cron.<id>pra ver o detalhe. - Se tudo "correto" mas nada acontece: confere se o commit deployado inclui seu código (
/status.deployCommitvsgit log HEAD).
Como adicionar um cron novo (apiclickup)¶
- Criar handler em
server/automations/<novo-cron>.jsseguindo o padrão dos existentes: - Exporta
(app) => { /* endpoints manuais, cron, audit */ } - Lê env flag:
const CRON_ENABLED = process.env.ENABLE_<NOVO> !== 'false' - Dentro do tick, checa
await cronsConfig.isEnabled('<novo-cron>')e pula se false - Loga em
audit-logcom eventocron.<novo-cron> - Registrar em server/crons.js com metadados (id, label, description, cronExpr, envFlag, defaultEnabled, integrations, manualEndpoint, statusEndpoint).
- Adicionar o módulo em server/automations/index.js.
- Declarar a env flag em server/.env.example.
- Se default=on, inserir seed no Supabase:
INSERT INTO crons_config (id, server, enabled) VALUES ('<novo>', 'apiclickup', true). - Testar:
npm run devno server, chamar endpoint manual, verGET /statusmostrando novo cron. - Subir e forçar redeploy no Easypanel.
Como adicionar um cron novo (engine)¶
Padrão ainda ad-hoc (só follow-up existe). Se virar recorrente, extrair registry como no apiclickup. Por enquanto, editar engine/src/index.ts direto:
- Adicionar flag
ENABLE_<NOVO>em.env.example. - Criar
setIntervalcondicional ao flag +isEnabled('<novo>')doservices/crons-config.ts. - Registrar em
GET /statusno arraycrons. - Adicionar seed em
crons_configvia migration.
Troubleshooting — Incidente de referência (21/04/2026)¶
Sintoma: Junior recebeu relatório financeiro às 9h mesmo após pedir desativação.
Causa raiz: o commit aecedbb (18/04) comentou scheduledReports(app) em automations/index.js — mas o Easypanel do apiclickup não foi redeployado. Produção continuou rodando o boot anterior.
Correção permanente (esta refatoração):
- Env flag ENABLE_SCHEDULED_REPORTS=false default.
- Runtime toggle via admin se precisar ligar/desligar sem redeploy.
- /status mostra deployCommit — dá pra ver se o deploy pegou o último commit.
- Endpoint /reports/cron-status agora retorna enabled: false quando está desligado (antes mentia).