1
Deploy V.1.0.1
VPS 4 Deploy Agent edited this page 2026-05-30 15:09:26 +02:00

🚀 Deploy V.1.0.1 — Guia Completo (Deploy + Versionamento)

Versão 1.0.1 — consolida tudo num único documento: pipeline de deploy automático, versionamento do servidor e do cliente Windows, convenções e regras de ouro. Um agente ou desenvolvedor deve conseguir executar qualquer tarefa lendo apenas esta página.


📋 Índice

  1. Arquitetura Multi-VPS
  2. Pipeline de Deploy Automático (GitOps)
  3. O Listener do Webhook
  4. Dois Modelos de Deploy
  5. Configurar um Novo Projeto
  6. Versionamento — Servidor rx.scoreodonto.com
  7. Versionamento — Cliente Windows
  8. Convenção de Numeração (Semver)
  9. Relação entre Versão do Servidor e do Cliente
  10. Segurança
  11. Troubleshooting
  12. Changelog desta Wiki

1. Arquitetura Multi-VPS

Servidor IP (VPN WireGuard) Função
VPS 1 — App 10.99.0.1 Produção — roda os containers Docker (rootless)
VPS 3 — Data/Git 10.99.0.3 Gitea + PostgreSQL
VPS 4 — Builder 10.99.0.4 Desenvolvimento, build e disparo do deploy
Dev (VPS4) ──git push──► Gitea (VPS3) ──webhook──► Listener (VPS4:porta)
                                                          │
                                                   deploy-*.sh (VPS4)
                                                          │
                                    ┌─────────────────────┴──────────────────────┐
                                    ▼                                             ▼
                       Modelo A: build na VPS4                    Modelo B: rsync do fonte
                       rsync artefatos → VPS1                     build na VPS1
                       docker compose up -d                       docker compose up --build

Princípios:

  • A fonte de verdade é sempre o Gitea — o deploy faz git reset --hard origin/main antes de qualquer coisa.
  • Produção (VPS1) nunca recebe edição manual — o rsync --delete sobrescreve tudo no próximo deploy.
  • Docker rootless na VPS1 — sempre usar DOCKER_HOST=unix:///run/user/1000/docker.sock.
  • Toda comunicação VPS↔VPS é pela VPN (10.99.0.x) via chave SSH do usuário deploy.

2. Pipeline de Deploy Automático (GitOps)

git push origin main
    └─► Gitea dispara POST http://10.99.0.4:<porta>/webhook?token=<TOKEN>
            └─► listener valida token + trava de concorrência
                    └─► responde 202 imediatamente (não trava o Gitea)
                    └─► executa deploy-*.sh em background
                            └─► git reset --hard
                            └─► build / version bump
                            └─► rsync → VPS1
                            └─► ssh VPS1: docker restart/rebuild
                            └─► git push do bump ("chore(deploy): bump version")
                                    └─► listener IGNORA este commit (anti-loop)
                    └─► reporta status ao Gitea: pending → success / failure (✔ ✘ no commit)

Um listener por projeto, cada um em uma porta:

Projeto Porta Script Service systemd
clube67 / newwhats 9000 clube67/deploy-prod-builder.sh webhook-listener.service ativo
scoreodonto.com 9002 deploy-prod-scoreodonto.sh webhook-scoreodonto.service ativo
rx.scoreodonto.com rx.scoreodonto.com/deploy-prod.sh nenhum — deploy manual
rx.scoreodonto.com-client rx.scoreodonto.com-client/deploy-client.sh nenhum — deploy manual

3. O Listener do Webhook

Código de referência: webhook-listener/listener.js neste repositório.

Lógica:

  1. Valida token (x-gitea-token header ou ?token= query string).
  2. Trava isBuilding — retorna 429 se já há build rodando (sem deploys concorrentes).
  3. Anti-loop de CI — ignora commits cuja mensagem case com ^chore\(deploy\): bump version.
  4. Responde 202 imediatamente; roda o script em background.
  5. Reporta status via API do Gitea: pendingsuccess / failure.

Variáveis de ambiente (configurar em <projeto>.env, nunca commitar):

PORT=<porta única por projeto>
WEBHOOK_TOKEN=<token forte>
DEPLOY_SCRIPT=/home/deploy/stack/<projeto>/deploy-prod-builder.sh
LOG_FILE=/home/deploy/stack/webhook-listener/deploy.log
GITEA_API_URL=http://10.99.0.3:3000/api/v1
GITEA_TOKEN=<token da API do Gitea com escopo repo:status>

Instalar como serviço:

sudo cp webhook-listener/webhook-listener.service /etc/systemd/system/webhook-<projeto>.service
sudo systemctl daemon-reload && sudo systemctl enable --now webhook-<projeto>

4. Dois Modelos de Deploy

🅰️ Builder-to-Host — build na VPS4, VPS1 só reinicia

Usado pelo clube67 / newwhats. Indicado para frontends pesados (Next.js / Vite). Script: scripts/deploy-prod-builder.sh

git reset --hard ► npm install + build ► rsync dist/ + node_modules/ → VPS1 ► docker compose up -d
  • VPS1 nunca roda npm install ou npm build.
  • Ao final: git commit "chore(deploy): bump version" && git push (anti-loop).

🅱️ Build no Host — rsync do fonte, VPS1 builda a imagem Docker

Usado pelo scoreodonto.com e rx.scoreodonto.com. Indicado para apps leves (Node servindo estáticos). Script: scripts/deploy-prod-onhost.sh

git reset --hard ► rsync do código-fonte → VPS1 ► ssh VPS1: docker compose build app + up -d --no-deps app
  • Swap do container com quase zero downtime (--no-deps).
  • VPS1 builda a imagem com Dockerfile local (no caso do rx: node:22-alpine).

Promoção manual Staging → Produção

Script: scripts/deploy-to-prod.sh — usado quando há staging na VPS4 e se quer promover a build validada para produção sem rebuild.


5. Configurar um Novo Projeto

# 1. Criar o .env do listener (nunca commitar)
cp webhook-listener/config.example.env /home/deploy/stack/webhook-listener/<projeto>.env
# editar: PORT, WEBHOOK_TOKEN, DEPLOY_SCRIPT, GITEA_TOKEN

# 2. Instalar o service systemd
sudo cp webhook-listener/webhook-listener.service /etc/systemd/system/webhook-<projeto>.service
# editar: Description, SyslogIdentifier, EnvironmentFile
sudo systemctl daemon-reload && sudo systemctl enable --now webhook-<projeto>

# 3. Criar webhook no Gitea
# Repo → Settings → Webhooks → Add Webhook → Gitea
# URL: http://10.99.0.4:<PORT>/webhook?token=<WEBHOOK_TOKEN>
# Método: POST | Evento: Push | Branch: main

# 4. Criar GITEA_TOKEN de status
# Gitea → Settings → Applications → Generate Token (escopo: repo:status / write:repository)

# 5. Testar
git commit --allow-empty -m "test: dispara webhook" && git push origin main
tail -f /home/deploy/stack/webhook-listener/deploy.log

Pré-requisitos:

  • Chave SSH do deploy (VPS4) com acesso sem senha à VPS1: ssh deploy@10.99.0.1 echo ok
  • Chave SSH do deploy com permissão de push no Gitea.
  • Porta do listener liberada apenas na VPN (10.99.0.0/24).

6. Versionamento — Servidor rx.scoreodonto.com

Onde a versão existe (3 arquivos, hoje dessincronizados)

Arquivo Valor atual Quem usa
dental-server/version.txt 2.1.0 Lido pelo servidor em runtime
dental-server/package.json"version" 2.0.5 Metadado Node (não exibido ao usuário)
dental-server/public/index.html linha 33 v2.1.2 Exibido na sidebar do painel web

A versão que o usuário vê é o index.html — hardcoded diretamente no HTML:

<h2>RF Dental <span>v2.1.2</span></h2>

Não existe script que sincronize os três automaticamente. Qualquer um pode ficar para trás.

Consultar versão em produção

# O que o usuário vê no painel:
ssh deploy@10.99.0.1 "grep -o 'v[0-9]\+\.[0-9]\+\.[0-9]\+' /home/deploy/stack/rx.scoreodonto.com/dental-server/public/index.html | head -1"

# O que o servidor lê em runtime:
ssh deploy@10.99.0.1 "cat /home/deploy/stack/rx.scoreodonto.com/dental-server/version.txt"

Como bumpar (processo atual — manual)

# 1. Editar os 3 arquivos para o mesmo número
nano /home/deploy/stack/rx.scoreodonto.com/dental-server/version.txt
nano /home/deploy/stack/rx.scoreodonto.com/dental-server/package.json
nano /home/deploy/stack/rx.scoreodonto.com/dental-server/public/index.html  # linha 33

# 2. Commitar e deploy
cd /home/deploy/stack/rx.scoreodonto.com
git add -p && git commit -m "chore(version): bump para vX.Y.Z"
git push origin main
./deploy-prod.sh

# 3. Verificar no painel web

7. Versionamento — Cliente Windows rx.scoreodonto.com-client

Fonte única de verdade: package.json

{
  "version": "1.2.7"
}

O electron-builder lê esse valor e:

  • Nomeia o instalador: ScoreOdonto Dental Client Setup 1.2.7.exe
  • Gera o latest.yml que vai para dental-server/updates/:
version: 1.2.6            ← versão atual nas clínicas
files:
  - url: ScoreOdonto Dental Client Setup 1.2.6.exe
    sha512: zEYuIRBU...
    size: 102459655

Como o auto-update funciona nas clínicas

Clínica (Windows) ao iniciar o app:
  └─► GET https://rx.scoreodonto.com/updates/latest.yml
          │
          ├─ version no yml > version instalada? SIM → baixa .exe em background → instala ao reiniciar
          └─                                      NÃO → nada acontece, app continua normalmente

⚠️ Regra de ouro: a versão que as clínicas têm é determinada exclusivamente pelo latest.yml em produção. O .exe na VPS4 não vale nada enquanto o latest.yml não for atualizado.

Estado atual (detectado em 28/05/2026)

Local Versão
package.json (VPS4) 1.2.7
.exe em dist/ (VPS4) 1.2.7 — buildado
latest.yml em updates/ (VPS1) 1.2.6 — as clínicas não recebem o 1.2.7

Como bumpar (processo correto)

# 1. Subir a versão no package.json
nano /home/deploy/stack/rx.scoreodonto.com-client/package.json
# "version": "1.2.7" → "1.2.8"

# 2. Commitar
cd /home/deploy/stack/rx.scoreodonto.com-client
git add package.json && git commit -m "chore(version): bump client para 1.2.8"
git push origin main

# 3. Build + deploy (gera .exe e envia latest.yml para VPS1)
./deploy-client.sh

# 4. Confirmar que o latest.yml foi atualizado
curl -s https://rx.scoreodonto.com/updates/latest.yml
# → version: 1.2.8

Histórico de versões (dist/ VPS4)

Versão Build Status
1.2.0 28/05/2026 02:59 publicado
1.2.1 28/05/2026 03:09 publicado
1.2.2 28/05/2026 03:18 publicado
1.2.3 28/05/2026 03:32 publicado
1.2.4 28/05/2026 03:56 publicado
1.2.5 28/05/2026 05:34 publicado
1.2.6 28/05/2026 05:45 em produção (VPS1 / clínicas)
1.2.7 28/05/2026 20:38 ⚠️ buildado — não publicado

8. Convenção de Numeração (Semver)

Ambos os projetos seguem MAJOR.MINOR.PATCH. As regras são independentes — servidor e cliente têm ciclos de versão separados.

Servidor (rx.scoreodonto.com) — versão atual: v2.1.2

Parte Quando subir Exemplos
PATCH v2.1.X Qualquer bugfix, ajuste visual, melhoria pequena, correção de CSS/JS v2.1.2 → v2.1.3
MINOR v2.X.0 Nova funcionalidade no painel, nova rota de API, novo modal/fluxo v2.1.x → v2.2.0
MAJOR vX.0.0 Rewrite de arquitetura, mudança de banco, quebra de compatibilidade v2.x.x → v3.0.0

Cliente Windows (rx.scoreodonto.com-client) — versão atual: 1.2.7

Parte Quando subir Exemplos
PATCH 1.2.X Bugfix no monitoramento, ajuste de config, correção de conexão 1.2.7 → 1.2.8
MINOR 1.X.0 Nova funcionalidade no cliente (nova tela, novo comportamento, novo evento socket) 1.2.x → 1.3.0
MAJOR X.0.0 Mudança de protocolo (Socket.IO incompatível), rewrite completo 1.x.x → 2.0.0

Regra prática: na dúvida, sobe o PATCH. MINOR e MAJOR são decisões conscientes.


9. Relação entre Versão do Servidor e do Cliente

Servidor e cliente são projetos independentes com versões independentes. Não há numeração sincronizada entre v2.1.2 (servidor) e 1.2.7 (cliente).

O que importa é a compatibilidade de protocolo:

Ponto de integração Onde está definido Risco de quebra
URL do servidor Socket.IO index.js do cliente → https://rx.scoreodonto.com Baixo — só muda se o domínio mudar
Evento send-image (socket) client-monitor.js + server.js Alto — se mudar o payload, servidor e cliente devem ser atualizados juntos
Autenticação via machine_token JWT routes/client-auth.js + index.js Alto — mudança de algoritmo exige deploy sincronizado
URL de auto-update package.jsonpublish.url = https://rx.scoreodonto.com/updates/ Médio — se o path mudar no servidor, clientes antigos param de atualizar

Quando fazer deploy sincronizado (servidor + cliente no mesmo ciclo): qualquer mudança no contrato do Socket.IO (eventos, payload) ou na autenticação JWT exige que servidor e cliente sejam publicados juntos, ou na ordem: servidor primeiro, cliente depois.


10. Segurança

  • Sem segredos no Git — tokens, senhas, JWT em .env + EnvironmentFile= no systemd. .gitignore bloqueia *.env.
  • Docker rootless na VPS1DOCKER_HOST=unix:///run/user/1000/docker.sock em todos os comandos remotos. Daemon root desabilitado de propósito; se reativado junto com deploy no grupo docker, cria containers root paralelos invisíveis ao Traefik.
  • SSH — chave do deploy sem passphrase para automação, BatchMode=yes nos scripts, acesso só pela VPN.
  • Commite antes de buildar — o deploy faz git reset --hard e descarta mudanças locais não commitadas.
  • Nunca editar na VPS1 — o rsync --delete sobrescreve tudo no próximo deploy.
  • Tokens distintos por projeto (WEBHOOK_TOKEN e GITEA_TOKEN). Rotacionar periodicamente.

11. Troubleshooting

Problema Diagnóstico Solução
Webhook não dispara systemctl status webhook-<projeto> Reiniciar o listener
429 no webhook Build concorrente em andamento Aguardar o build terminar
rsync / ssh falha ssh deploy@10.99.0.1 echo ok Checar chave SSH e VPN WireGuard
Build falha na VPS1 docker logs <container> Ver erro de npm/Dockerfile
Deploy em loop Commit de bump re-dispara Confirmar regex anti-loop no listener
Versão não atualiza no painel index.html não foi editado Editar os 3 arquivos do servidor sincronizados
Clínicas não recebem update curl https://rx.scoreodonto.com/updates/latest.yml Verificar se latest.yml tem a versão nova; se não, re-rodar deploy-client.sh
Port 8020 ocupada na VPS1 sudo ss -tlnp | grep 8020 docker-proxy root zumbi — ver seção de Docker rootless
docker.service root ativo na VPS1 sudo systemctl status docker sudo systemctl disable --now docker

12. Changelog desta Wiki

Versão Data O que mudou
1.0.1 2026-05-30 Consolidação total: deploy + versionamento servidor + versionamento cliente + convenções semver + relação entre versões. Tudo numa página só.
1.0.0 2026-05-30 Versão inicial: listener de webhook, modelos A/B de deploy, configuração no Gitea, segurança.