# DeskcommCRM — override para VPS que JÁ TEM um proxy reverso Traefik. # # docker compose -f docker-compose.prod.yml -f docker-compose.traefik.yml up -d # # QUANDO USAR # ----------- # O stack padrão sobe um Caddy que publica as portas 80 e 443. Isso é o certo # num VPS "cru". Mas várias hospedagens entregam a VPS com um Traefik próprio # já ocupando essas portas — Hostinger, Coolify, Dokploy, CapRover e afins. É # esse Traefik que dá HTTPS automático a tudo que o painel instala; desligá-lo # quebraria as automações da hospedagem, presentes e futuras. # # Dois processos não podem ouvir a mesma porta, então o `up -d` padrão falha no # bind e a instalação morre no meio. Com este override: # # 1. o serviço `caddy` ganha um profile que nunca é ativado — o compose # simplesmente não o cria (o Traefik da hospedagem faz o papel dele); # 2. o serviço `app` ganha labels do Traefik que reproduzem, uma a uma, as # regras do Caddyfile. # # O comportamento padrão (sem este arquivo) fica EXATAMENTE como antes: quem # não passa o `-f docker-compose.traefik.yml` não é afetado por nada aqui. # # EQUIVALÊNCIA COM O Caddyfile # ---------------------------- # - `reverse_proxy app:3000` → router `deskcomm` + loadbalancer.server.port # - `encode gzip` → middleware `deskcomm-compress` # - `respond @waha_global 403`→ router `deskcomm-waha-block` (ver abaixo) # # PRÉ-REQUISITOS NO TRAEFIK DA HOSPEDAGEM # --------------------------------------- # Espera-se um entrypoint `websecure` (443) e um certresolver `letsencrypt` — # os nomes padrão dessas instalações. Se a sua usa outros nomes, ajuste # TRAEFIK_ENTRYPOINT e TRAEFIK_CERTRESOLVER no .env. # # A REDE onde o Traefik encontra o app é TRAEFIK_NETWORK, e ela varia por # instalação. O install.sh a descobre e grava no .env — são dois cenários com # respostas OPOSTAS, ambos medidos (o porquê de cada um está na função # `rede_do_traefik`, em hostgator-setup-kit/install.sh): # # Traefik numa bridge PRÓPRIA (Coolify, Dokploy) → a rede DELE, que já existe. # Traefik em `--network host` (Hostinger) → uma bridge "_proxy" # que o install.sh CRIA. Em modo host o proxy não está em rede nenhuma do # Docker, e alcança qualquer bridge pela stack do host. # # O default abaixo ("traefik") cobre a instalação manual mais comum. services: app: # Só o `app` entra na rede do proxy. O inverso — juntar o Traefik à rede # `internal` — colocaria um contêiner que serve OUTROS tenants da VPS dentro # de uma rede onde o redis roda sem senha (docker-compose.prod.yml: `redis-server # --save '' --appendonly no`, sem requirepass). waha, redis e srh ficam de fora. networks: - internal - proxy labels: traefik.enable: "true" # Este label diz ao Traefik em qual rede pegar o IP do contêiner. Com o # Traefik numa bridge própria, é a rede do PROXY — apontando para a # `internal` ele mira um IP que não alcança e a requisição pendura até dar # timeout, mesmo com o contêiner já conectado nas duas redes. Medido com # Traefik v3.3 real: # app só na internal, label=internal -> HTTP 000 (timeout) # app nas duas redes, label=internal -> HTTP 000 (ainda o IP errado) # app nas duas redes, label=proxy -> HTTP 200 # Com o Traefik em modo host a conclusão não se inverte, ela se generaliza: # continua sendo a rede `proxy` — só que lá ela é a bridge que o install.sh # cria para este projeto, porque um proxy em modo host não tem rede própria # (e alcança qualquer bridge pela stack do host). traefik.docker.network: "${TRAEFIK_NETWORK:-traefik}" # Rota principal — equivale ao `reverse_proxy app:3000` do Caddyfile. traefik.http.routers.deskcomm.rule: "Host(`${DOMAIN}`)" traefik.http.routers.deskcomm.entrypoints: "${TRAEFIK_ENTRYPOINT:-websecure}" traefik.http.routers.deskcomm.tls.certresolver: "${TRAEFIK_CERTRESOLVER:-letsencrypt}" traefik.http.routers.deskcomm.middlewares: "deskcomm-compress" traefik.http.services.deskcomm.loadbalancer.server.port: "3000" # `encode gzip` do Caddyfile. traefik.http.middlewares.deskcomm-compress.compress: "true" # `respond @waha_global 403` do Caddyfile. O webhook GLOBAL do WAHA não # pode ser alcançável da internet: quem soubesse o endereço injetaria # mensagem falsa no CRM e faria o WhatsApp do dono responder a um número # arbitrário. Aqui dentro o WAHA fala com o app pela rede do Docker, então # esta rota nunca precisou ser pública. # # O Traefik não tem um "responda 403" direto. O equivalente é um # ipallowlist cuja única faixa permitida é 192.0.2.1/32 — TEST-NET-1 # (RFC 5737), um endereço reservado para documentação que nenhum cliente # real tem. Toda requisição àquele caminho cai fora da lista e recebe 403, # que é exatamente o comportamento desejado. A prioridade alta é o que faz # esta rota vencer a principal, que casaria o mesmo Host. traefik.http.routers.deskcomm-waha-block.rule: "Host(`${DOMAIN}`) && Path(`/api/v1/webhooks/waha`)" traefik.http.routers.deskcomm-waha-block.entrypoints: "${TRAEFIK_ENTRYPOINT:-websecure}" traefik.http.routers.deskcomm-waha-block.tls.certresolver: "${TRAEFIK_CERTRESOLVER:-letsencrypt}" traefik.http.routers.deskcomm-waha-block.priority: "1000" traefik.http.routers.deskcomm-waha-block.service: "deskcomm" traefik.http.routers.deskcomm-waha-block.middlewares: "deskcomm-deny" traefik.http.middlewares.deskcomm-deny.ipallowlist.sourcerange: "192.0.2.1/32" # HTTP -> HTTPS. O Caddy fazia isto automaticamente; no Traefik precisa ser # dito. Sem este router, quem digitar http:// não recebe resposta nenhuma. traefik.http.routers.deskcomm-http.rule: "Host(`${DOMAIN}`)" traefik.http.routers.deskcomm-http.entrypoints: "${TRAEFIK_ENTRYPOINT_HTTP:-web}" traefik.http.routers.deskcomm-http.middlewares: "deskcomm-https" traefik.http.routers.deskcomm-http.service: "deskcomm" traefik.http.middlewares.deskcomm-https.redirectscheme.scheme: "https" traefik.http.middlewares.deskcomm-https.redirectscheme.permanent: "true" # O Caddy não é removido do compose (isso exigiria editar o arquivo base e # quebraria quem usa o padrão) — ele só é posto num profile que nunca é # ligado. Sem profile ativo, o compose não cria o contêiner e as portas 80/443 # seguem com o Traefik da hospedagem. # # ATENÇÃO a um detalhe do Compose: nomear o serviço explicitamente # (`docker compose up -d caddy`) ATIVA o profile dele automaticamente e o # contêiner sobe assim mesmo, indo bater de frente com o Traefik. Por isso o # update.sh checa REVERSE_PROXY antes de recriar o proxy. caddy: profiles: ["caddy-nao-usado-com-proxy-externo"] # `external: true` = quem cria esta rede não é o compose. Nos dois cenários ela # já existe quando o `up -d` roda: ou é a rede do proxy da hospedagem (coolify, # dokploy-network, traefik...), ou é a bridge que o install.sh criou para este # projeto quando o Traefik está em modo host. # # Rede externa AUSENTE é recusada ANTES de o compose criar qualquer coisa — # medido com o compose v2.38.2 contra este mesmo arquivo: "network X declared as # external, but could not be found", sem dizer de onde saiu o nome. Vale # inclusive quando X é a `_internal` que o próprio compose criaria neste # mesmo `up` (a checagem de rede externa vem antes de qualquer criação); por isso # a bridge do modo host é uma rede separada, e o install.sh a cria antes de subir # a stack. networks: proxy: external: true name: "${TRAEFIK_NETWORK:-traefik}"