mirror of
https://github.com/melgarafael/DeskcommCRM.git
synced 2026-10-02 01:28:34 +08:00
A stack local entregava o comando da sessão de WhatsApp a qualquer máquina
da rede da VM, por duas portas independentes.
**A chave.** `WAHA_API_KEY=deskcomm-local-key` nascia literal em
`ubuntu-local-installer.sh` e em `scripts/local-env.sh` — isto é, publicada
neste repositório. É o mesmo argumento que já tornou a senha do dono
aleatória nesta fatia ("qualquer máquina da mesma rede alcançaria o CRM com
uma credencial que está no GitHub"), só que esta credencial comanda o
WhatsApp. Agora ela vem de `openssl rand -hex 24`.
Sem knob de propósito: nenhum terceiro precisa conhecê-la (o app manda o
plaintext, o WAHA compara o sha512 derivado dela), e um
`${WAHA_API_KEY:-...}` herdaria em silêncio a chave da nuvem de quem tiver
a variável exportada no shell — o `.env.local` deste repo é lido por 93
scripts. Em `local-env.sh` o valor é gerado UMA vez numa variável, porque o
heredoc é sem aspas e dois `$(openssl rand)` dariam chave e hash
divergentes, que rendem 401.
**A porta.** `docker-compose.local.yml` publicava `"3030:3000"` sem
endereço de bind, com `WAHA_DASHBOARD_ENABLED: "true"`. O dashboard do WAHA
tem autenticação própria, com o padrão do WAHA — a chave da API não o
protege —, então a chave aleatória sozinha não fecharia essa porta. Agora é
`"127.0.0.1:3030:3000"`.
Isto alinha o compose local com o que a casa já pratica e com o que o
próprio texto do PR já prometia: `docker-compose.prod.yml` desliga o
dashboard ("nunca expor o dashboard num template público", linha 156) e não
publica porta nenhuma para o WAHA (`networks: [internal]`); e o bloco novo
de `docs/SETUP.md` já dizia "o WAHA em `http://127.0.0.1:3030`". Nada
alcança essa porta de fora do compose — nenhum dos quatro scripts a
referencia —, e de outra máquina o túnel `ssh -L 3030:localhost:3030`
resolve, como `docs/vendaval-vps-deploy-comandos.md:64` já faz.
O app (3000) continua em 0.0.0.0: é o produto, e é o que a VM serve.
**A guarda.** Cinco verificações novas em `tests/shell/stack-local.test.sh`
(12 → 17). Três delas EXECUTAM as linhas em vez de as lerem, com um stub de
`sha512sum` (que é do GNU e não existe no macOS, onde o teste roda antes de
chegar ao Ubuntu do CI), porque o que se prova é a procedência do hash.
Sabotagem A — os três consertos desfeitos: 4 vermelhos, exatamente os
previstos, e a checagem de controle ("a sonda enxerga o bloco do WAHA")
seguiu verde, então os vermelhos são medição e não sonda morta.
Sabotagem B — chave e hash vindos de dois `openssl rand` diferentes: os
dois greps ingênuos PASSAM (`deskcomm-local-key` → 0 ocorrências; o grep de
aleatoriedade casa) e só a checagem executável fica vermelha. É o caso que
justifica ela existir.
Restaurado: `md5` dos três arquivos igual ao de antes da sabotagem, e o
conserto conferido por presença — `grep -c` devolve 1 em cada um dos quatro
pontos, e `deskcomm-local-key` devolve 0 no repositório inteiro.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RccZbdYe8URAiWCpyvZimQ
144 lines
4.6 KiB
YAML
144 lines
4.6 KiB
YAML
# Stack local completa para desenvolvimento e VM Ubuntu.
|
|
#
|
|
# O Supabase local é gerenciado pelo `supabase start` e roda em Docker com os
|
|
# serviços oficiais de Postgres, Auth, PostgREST, Storage e Realtime. Este
|
|
# arquivo sobe os serviços da aplicação na mesma máquina, usando as portas
|
|
# publicadas pelo Supabase local.
|
|
#
|
|
# Uso: bash scripts/local-stack.sh up
|
|
|
|
services:
|
|
app:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
args:
|
|
APP_VERSION: ${APP_VERSION:-local}
|
|
image: deskcomm-app:local
|
|
pull_policy: never
|
|
restart: unless-stopped
|
|
env_file: ${LOCAL_ENV_FILE:-.env.local}
|
|
environment:
|
|
WAHA_API_BASE_URL: http://waha:3000
|
|
ports:
|
|
- "${APP_PORT:-3000}:3000"
|
|
extra_hosts:
|
|
- "host.docker.internal:host-gateway"
|
|
depends_on:
|
|
waha:
|
|
condition: service_healthy
|
|
srh:
|
|
condition: service_started
|
|
healthcheck:
|
|
test: ["CMD", "node", "-e", "require('net').connect(3000,'127.0.0.1').on('connect',()=>process.exit(0)).on('error',()=>process.exit(1))"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 40s
|
|
|
|
worker:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile.worker
|
|
args:
|
|
APP_VERSION: ${APP_VERSION:-local}
|
|
image: deskcomm-worker:local
|
|
pull_policy: never
|
|
restart: unless-stopped
|
|
env_file: ${LOCAL_ENV_FILE:-.env.local}
|
|
environment:
|
|
WAHA_API_BASE_URL: http://waha:3000
|
|
extra_hosts:
|
|
- "host.docker.internal:host-gateway"
|
|
depends_on:
|
|
waha:
|
|
condition: service_healthy
|
|
ports:
|
|
- "8787:8787"
|
|
healthcheck:
|
|
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://127.0.0.1:8787/healthz"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 3
|
|
start_period: 20s
|
|
|
|
scheduler:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile.scheduler
|
|
args:
|
|
APP_VERSION: ${APP_VERSION:-local}
|
|
image: deskcomm-scheduler:local
|
|
pull_policy: never
|
|
restart: unless-stopped
|
|
env_file: ${LOCAL_ENV_FILE:-.env.local}
|
|
environment:
|
|
TZ: UTC
|
|
# Os crons autenticam com INTERNAL_CRON_SECRET quando definido; o app
|
|
# aplica a mesma prioridade. Sem este fallback, o scheduler enviava
|
|
# INTERNAL_SECRET e recebia 401 em instalações com os dois segredos.
|
|
INTERNAL_SECRET: ${INTERNAL_CRON_SECRET:-${INTERNAL_SECRET}}
|
|
depends_on:
|
|
app:
|
|
condition: service_healthy
|
|
|
|
waha:
|
|
image: devlikeapro/waha:latest-2026.7.2
|
|
restart: unless-stopped
|
|
ports:
|
|
# 127.0.0.1 e não 0.0.0.0: o dashboard do WAHA está ligado logo abaixo e
|
|
# comanda a sessão de WhatsApp — ele tem autenticação própria, com padrão
|
|
# do WAHA, que a chave da API não protege. Sem endereço de bind, qualquer
|
|
# máquina da rede da VM alcançaria esse painel. `docker-compose.prod.yml`
|
|
# resolve o mesmo problema desligando o dashboard ("nunca expor o
|
|
# dashboard num template público", linha 156) e não publicando porta
|
|
# nenhuma para o WAHA; aqui ele fica de pé para quem desenvolve, mas só
|
|
# atende de dentro da própria máquina (de fora, `ssh -L 3030:localhost:3030`).
|
|
- "127.0.0.1:3030:3000"
|
|
environment:
|
|
# O WAHA recebe o hash com o prefixo para hashear a chave plaintext
|
|
# enviada pelo app em X-Api-Key.
|
|
WAHA_API_KEY: "sha512:${WAHA_API_KEY_SHA512}"
|
|
WHATSAPP_HOOK_URL: http://host.docker.internal:${APP_PORT:-3000}/api/v1/webhooks/waha
|
|
WHATSAPP_HOOK_EVENTS: "message.any,message.ack,message.edited,message.revoked,session.status,state.change"
|
|
WHATSAPP_HOOK_HMAC: ${WAHA_HMAC_SECRET}
|
|
WHATSAPP_DEFAULT_ENGINE: NOWEB
|
|
WAHA_DASHBOARD_ENABLED: "true"
|
|
WHATSAPP_RESTART_ALL_SESSIONS: "True"
|
|
extra_hosts:
|
|
- "host.docker.internal:host-gateway"
|
|
volumes:
|
|
- waha-data:/app/.sessions
|
|
- waha-media:/app/.media
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:3000/ping"]
|
|
interval: 30s
|
|
timeout: 5s
|
|
retries: 5
|
|
start_period: 40s
|
|
|
|
redis:
|
|
image: redis:7-alpine
|
|
restart: unless-stopped
|
|
command: ["redis-server", "--save", "", "--appendonly", "no"]
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "ping"]
|
|
interval: 30s
|
|
timeout: 3s
|
|
retries: 3
|
|
|
|
srh:
|
|
image: hiett/serverless-redis-http@sha256:5b0bb9239fce53abf87b2018a7a0deb9ec7bd900c5360738fe5fbeeb426f9150
|
|
restart: unless-stopped
|
|
environment:
|
|
SRH_MODE: env
|
|
SRH_TOKEN: ${SRH_TOKEN}
|
|
SRH_CONNECTION_STRING: redis://redis:6379
|
|
depends_on:
|
|
redis:
|
|
condition: service_healthy
|
|
|
|
volumes:
|
|
waha-data:
|
|
waha-media:
|