Files
DeskcommCRM/hostgator-setup-kit/README.md
T
Rafael MelgaçoandClaude Opus 5 17197d718c fix(kit): a suíte travava para sempre e escrevia no crontab da máquina
O conserto do PR #140 estava certo; o portão em volta dele não. Cinco pontos.

1. A suíte travava na forma que ela mesma documenta. O dublê de `crontab` era
   `cat >/dev/null` INCONDICIONAL. O kit faz `crontab -l | grep -qF ...`: o `cat`
   ficava lendo o stdin herdado — num terminal, o tty, que nunca dá EOF — e o
   `grep` esperava um fim que não vinha. Medido no HEAD anterior, num PTY de
   verdade (`script`/`openpty`): morta com SIGKILL aos 90s; sabotando de volta só
   o dublê no código já corrigido, morta aos 240s. Com o dublê novo a suíte
   fecha em 18,4s, exit code 0, 133 OK / 0 FAIL — no PTY e com stdin fechado.
   Um dublê só consome stdin onde o comando real consumiria (`crontab -` e
   `crontab <arquivo>`), nunca em `crontab -l`.

   O dublê também grava ATOMICAMENTE (temporário + mv), como o crontab de
   verdade. Sem isso ele mentia num ponto que importa: nos dois lados de
   `crontab -l | ... | crontab -` rodando ao mesmo tempo, um `cat > arquivo`
   truncava o arquivo que o leitor ainda lia, o merge recebia um crontab vazio e
   cada gravação apagava a anterior. Medido: 1 linha capturada antes, 3 depois.

2. Os testes escreveram no crontab REAL. Agora o dublê grava num sandbox e a
   suíte compara `crontab -l` antes/depois, com controle positivo: se o sandbox
   não recebeu nenhuma linha do kit, o caso reprova por INCONCLUSIVO em vez de
   passar por não ter medido nada. Os dois braços foram provados mordendo, sem
   tocar no crontab da máquina: com um `crontab` de fachada no PATH fazendo o
   papel do real, remover o dublê do fixture derruba pela vacuidade (e vazam as
   4 linhas órfãs, a do Bearer inclusive); um dublê que grava no sandbox E vaza
   derruba pela comparação, com o diff na tela.

3. O guard de rede só existia no install.sh. `nome_do_projeto_compose` e a
   checagem/criação da bridge saíram para `_common.sh` (`garantir_rede_do_proxy`)
   e agora valem nos dois caminhos: o `dc up -d` do update.sh corre o mesmo risco
   — a bridge some num `docker network prune` ou no `down -v` que o próprio kit
   ensina — e quem roda o update.sh é o agent.sh a cada 5 minutos, sem ninguém
   lendo a tela. Entra um teste de integração que roda o update.sh inteiro contra
   dublês e cobra a ORDEM (criar depois do `up -d` não serviria de nada).

4. A eleição do Traefik em modo host era frouxa. `docker ps --filter
   network=host` elegia qualquer Traefik único como dono de 80/443 sem nenhuma
   prova: em modo host a coluna Ports sai vazia para TODO mundo, então o que
   aquela varredura responde é "há um único Traefik em modo host aqui", não "é
   ele quem está com as portas". Com um nginx nativo segurando as portas, o CRM
   subia atrás de um proxy que não atende — "instalou com sucesso" e site mudo.
   Agora a eleição vem MARCADA e o desfecho é fechado na ação, aberto na
   informação: interativo confirma dizendo o que achou; `--yes` recusa ensinando
   a saída (declarar REVERSE_PROXY=traefik no .env, que segue valendo). Quem é
   eleito pela coluna Ports não perde nada — provado pelo cenário Coolify novo,
   e pelas duas sabotagens (sempre `segue` e sempre `pergunta`) mordendo cada uma
   o seu lado.

5. `.env.hostgator.example` prometia `<pasta>_proxy` enquanto o código cria com o
   nome normalizado. Numa pasta `CRM.Host_Teste` o doc dizia `CRM.Host_Teste_proxy`
   e o instalador criava `crmhost_teste_proxy`. Era o defeito que o commit
   anterior corrigiu no código sobrevivendo na prosa. README e CLAUDE.md do kit
   também diziam "detecta isso sozinho" sem ressalva — agora não é mais verdade
   no modo host, e o texto diz isso.

Achado ao fechar o item 3: a extração levou junto o `TRAEFIK_NETWORK="${…:-traefik}"`
que estava solto no fluxo, para dentro de uma função que retorna cedo em modo
caddy. O `.env` é escrito num `{ … } > .env` e `envq TRAEFIK_NETWORK` é
incondicional, então a VPS LIMPA — a instalação mais comum de todas — morria em
`set -u` e deixava o .env pela metade, parando exatamente na linha seguinte a
REVERSE_PROXY. Com os quatro cenários de proxy externo verdes. Não havia teste de
integração nenhum no caminho do Caddy; agora há, e ele reprova contra o refactor e
passa contra o HEAD anterior — o controle que separa "regressão minha" de "defeito
que já existia". A asserção olha a última linha do bloco, não a mensagem de erro:
`set -u` fala a língua do shell de quem roda, e a primeira versão do teste passou
batido justamente por procurar "unbound variable" num shell em pt-BR.

Portões: `bash -n` nos 12 .sh do kit; a suíte num PTY de verdade (18,4s, exit 0)
e com stdin fechado (18s, exit 0), 133 OK / 0 FAIL nos dois; e o sha256 do
crontab da máquina igual no começo e no fim de tudo.

Refs #139

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HstH7nNmrTvCtZsasppeHj
2026-08-05 13:37:56 -03:00

7.9 KiB

DeskcommCRM — Kit de Instalação (HostGator)

Este kit sobe o DeskcommCRM no seu servidor VPS da HostGator. Você tem dois caminhos:

Ainda nem tem servidor? Comece por comecar.sh — ele roda no seu computador, antes de existir VPS, e responde a pergunta que trava todo mundo no início: o que eu preciso contratar? Ele nomeia o plano (VPS Turing, 2 vCPU / 4 GB — o Cartesius não dá conta do WhatsApp), abre a página se você quiser, e devolve o comando exato do seu caso. Depois que a VPS existir, o caminho é o install.sh daqui de baixo.

bash comecar.sh

Outra hospedagem? O kit é feito para a HostGator (é a parceria do projeto e o caminho testado de ponta a ponta), mas roda em qualquer VPS com Docker. Se a sua já vem com um proxy reverso próprio ocupando as portas 80/443 — caso de Hostinger, Coolify, Dokploy e CapRover —, o instalador detecta isso sozinho e publica o CRM através dele, em vez de tentar subir um Caddy que não caberia. Ver VPS que já vem com proxy próprio.

🤖 Caminho fácil: deixe o Claude Code fazer

  1. Contrate um VPS na HostGator e acesse-o por SSH.
  2. Jogue esta pasta (ou o .zip) no chat do Claude Code rodando dentro do VPS.
  3. Diga: "instala o DeskcommCRM pra mim". Ele lê o CLAUDE.md e conduz tudo — cria o banco, gera as senhas, sobe o CRM e te ajuda a conectar o WhatsApp.

⚙️ Caminho manual: um comando

Dentro do VPS:

bash install.sh

VPS sem Docker? O instalador resolve. Se não encontrar o Docker, ele pergunta antes e instala pelo get.docker.com — o instalador oficial da Docker, que roda como root, como manda a documentação deles. Com --yes ele segue sem perguntar, que é o contrato desse modo. Se preferir instalar por conta própria, responda n e rode curl -fsSL https://get.docker.com | sh antes.

O instalador pergunta o que precisa (domínio, chaves do Supabase e da Anthropic, e-mail/senha do admin), gera o resto e sobe tudo.

Modo não-interativo: copie .env.hostgator.example (do repositório) para .env, preencha, e rode bash install.sh --yes.

Criar o Supabase automaticamente (opcional)

Criar o projeto no navegador e copiar as 4 credenciais é o passo mais demorado da instalação — e o mais fácil de errar (copiar a Direct connection, que é IPv6-only e não conecta de um VPS IPv4, é a armadilha mais comum). Dá para pular tudo isso:

export SUPABASE_ACCESS_TOKEN=sbp_...        # supabase.com/dashboard/account/tokens
bash install.sh                             # cria o projeto e segue a instalação

O install.sh chama o provisionamento sozinho quando encontra o token e as credenciais ainda vazias — as 4 variáveis entram no fluxo sem copiar e colar. Para criar só o projeto, sem instalar, o script também roda sozinho:

bash supabase-provision.sh "Nome do Projeto" sa-east-1 >> .env

O script cria o projeto, espera o banco ficar ACTIVE_HEALTHY (projeto novo não nasce pronto), busca as chaves e descobre o host do pooler testando conexão real em vez de adivinhar. Imprime as 4 linhas prontas para colar no .env.

⚠️ O token é uma chave mestra — dá acesso a todos os projetos da conta. Ele é lido do ambiente e nunca gravado em disco. Instalando para terceiros, use o token DO CLIENTE, ou rode o script na sua máquina e leve só as 4 credenciais para o servidor dele.

⚠️ Plano grátis: 2 projetos por usuário, contados em todas as organizações onde ele é Owner/Admin. Não dá para hospedar vários clientes numa conta só.

O que você precisa antes

Item Onde conseguir
VPS (Docker) HostGator — VPS com Docker (n8n/OpenClaw/GatorClaw). Outras hospedagens com Docker também servem — se a sua já tiver proxy próprio nas portas 80/443, veja aqui
Domínio Registro de domínio (aponte um A-record pro IP do VPS)
Banco de dados Conta grátis no supabase.com (3 chaves + connection string)
IA Chave da Anthropic
WhatsApp Seu número — conectado por QR code no onboarding

Requisitos do VPS

  • 4 GB RAM recomendados. A imagem é pré-buildada, então o servidor não compila nada e a stack SOBE com 2 GB — mas operar é outra coisa: são 7 contêineres, e o WAHA consome ~150 MB por sessão de WhatsApp além de ~300 MB de overhead do Node. Com 2 GB você roda no limite e vai precisar de swap. Ver docs/runbooks/waha-hostgator.md.
  • Portas 80 e 443 abertas (ufw allow 80,443,22/tcp).
  • Docker + Docker Compose v2 — o install.sh instala o Docker sozinho se faltar (ver acima).

VPS que já vem com proxy próprio (Hostinger, Coolify, Dokploy…)

Algumas hospedagens entregam a VPS com um Traefik já ocupando as portas 80/443 — é ele que dá HTTPS automático ao que o painel instala. O Caddy do kit quer as mesmas portas e não sobe. O instalador detecta isso sozinho e grava REVERSE_PROXY=traefik no .env; a partir daí os scripts do kit incluem o override que desliga o Caddy e publica o app pelo Traefik da hospedagem. Rodando compose na mão nessas instalações, use os dois arquivos:

docker compose -f docker-compose.prod.yml -f docker-compose.traefik.yml up -d

Não desligue o Traefik da hospedagem para liberar as portas — isso quebra as automações do painel dela. Se o seu Traefik usa nomes diferentes de websecure/letsencrypt, ajuste TRAEFIK_ENTRYPOINT e TRAEFIK_CERTRESOLVER no .env.

Há um caso em que o instalador pergunta em vez de decidir: quando o Traefik da hospedagem roda em --network host (a Hostinger faz assim), o Docker não mostra porta publicada em contêiner nenhum, e então não dá para provar que é ele quem atende o seu domínio — poderia ser um nginx instalado direto no servidor. Como publicar o CRM atrás do proxy errado deixa o site no ar sem responder, o instalador mostra o que encontrou e pede confirmação. Em bash install.sh --yes não há a quem perguntar: ele para e pede que você declare REVERSE_PROXY=traefik no .env — aí a escolha é sua e ele segue sem perguntar.

Scripts do kit

Script Função
install.sh Instala tudo (idempotente)
update.sh Atualiza pra versão nova
backup.sh Backup do banco + sessões WhatsApp
restore.sh Restaura um backup
reset-password.sh Redefine senha de um usuário
reset-mfa.sh Remove o MFA de um usuário travado
healthcheck.sh Diagnóstico dos serviços

Automações e webhooks

O install.sh (e o update.sh, a cada atualização) já ativa sozinho um cron que roda todo minuto e "puxa" a fila de eventos pendentes (/api/v1/cron/event-log-drain) — é isso que faz uma automação disparar de verdade no seu servidor (ex.: enviar uma mensagem de WhatsApp quando um pedido muda de status). Sem esse cron, as automações ficam paradas na fila e nunca rodam — é um requisito, não um extra.

Rodar de novo o install.sh/update.sh não duplica a linha do cron (ele mesmo substitui a antiga). Na 1ª vez que o cron é ativado numa instalação que já existia há um tempo, o script também limpa eventos pendentes com mais de 7 dias (marcando como concluídos, sem apagar histórico) — assim o primeiro drain não sai disparando efeitos atrasados de semanas atrás.

Pra testar na mão, rode no próprio VPS (usa o INTERNAL_SECRET do seu .env):

source .env && curl -s -H "Authorization: Bearer ${INTERNAL_SECRET}" "${NEXT_PUBLIC_APP_URL}/api/v1/cron/event-log-drain"

Resposta esperada: {"data":{"scanned":N,...}} (N pode ser 0 se não houver eventos na fila — o importante é receber esse formato, não um erro de autenticação ou de conexão).

Suporte

Problemas comuns e como resolver estão no CLAUDE.md (seção "Quando der problema").