Table of Contents
- 🚀 Deploy V.1.0.5 — Nomes de Sistema e Cliente Congelados + Deploy Conjunto
- 📋 Índice
- 1. Arquitetura Multi-VPS
- 2. Pipeline GitOps (Modelo A)
- 3. Os Listeners de Webhook
- 4. Versionamento — Servidor
- 5. Cliente Windows — Build, Publicação e Auto-Update
- 5.1. Fonte de verdade
- 5.2. As DUAS pastas (e por que confundem)
- 5.3. Deploy automático via webhook (desde v1.3.0)
- 5.4. Fluxo de auto-update nas clínicas
- 5.5. Verificação rápida (3 pontos têm que bater)
- 5.6. Proteções contra perda de dados na atualização (v1.3.0)
- 6. 🔒 Nomes Congelados — Regra Obrigatória
- 6.1. Nome do sistema (painel web)
- 6.2. Nome do executável do cliente Windows
- 6.3. URL de download — resolução dinâmica
- 6.4. Checklist antes de refatorar
- 6.5. ⚠️ REGRA OBRIGATÓRIA: Deploy Conjunto (Client + Server)
- 7. Erros comuns e soluções
- ❌ Erro A — Cliente desatualizado nas clínicas
- ❌ Erro B — Botão download → 404
- ❌ Erro C — vite: not found
- ❌ Erro D — Imagens em branco
- ❌ Erro E — Wasabi 403
- ❌ Erro F — Listener do client não dispara
- 8. Troubleshooting
- 9. Changelog
🚀 Deploy V.1.0.5 — Nomes de Sistema e Cliente Congelados + Deploy Conjunto
Versão 1.0.5 — inclui tudo da 1.0.4 e formaliza a Regra de Deploy Conjunto: atualizações de funcionalidades que afetam tanto o servidor quanto o cliente exigem push em AMBOS os repositórios. O nome do executável e do sistema permanecem fixos.
📋 Índice
- Arquitetura Multi-VPS
- Pipeline GitOps (Servidor — Modelo A)
- Os Listeners de Webhook
- Versionamento — Servidor
- Cliente Windows — Build, Publicação e Auto-Update
- 🔒 Nomes Congelados — Regra Obrigatória
- Erros comuns e soluções
- Troubleshooting
- Changelog
1. Arquitetura Multi-VPS
| Servidor | IP (VPN) | Função |
|---|---|---|
| VPS 1 — App | 10.99.0.1 |
Produção (Docker rootless) + serve /updates/ do cliente |
| VPS 3 — Data/Git | 10.99.0.3 |
Gitea + PostgreSQL |
| VPS 4 — Builder | 10.99.0.4 |
Build do servidor e do cliente Windows (cross-compile) |
| Wasabi | S3-compatível | Storage de imagens (não no disco) |
2. Pipeline GitOps (Modelo A)
Servidor
git push origin main (rx.scoreodonto.com)
↓
Gitea dispara webhook → VPS4:9003/webhook
↓
listener-rx.js → deploy-prod.sh
↓
VPS4: build React (Vite) → docker compose build → docker save → app.tar.gz
↓
VPS4: rsync app.tar.gz + docker-compose.yml + version.txt → VPS1
↓
VPS1: docker load + docker compose up -d --no-deps app
↓
✅ Deploy concluído (~30-40 s)
Cliente Windows (automatizado desde 2026-05-31)
git push origin main (rx.scoreodonto.com-client)
↓
Gitea dispara webhook → VPS4:9004/webhook
↓
listener-rx-client.js → deploy-client.sh
↓
VPS4: npm install → electron-builder --win --x64 → dist/*.exe + latest.yml (~90s)
↓
VPS4: rsync dist/*.exe + *.yml + *.blockmap → VPS1:/dental-server/updates/
↓
✅ Client publicado em produção (auto-updater nas clínicas detecta em ~5 min)
⚠️ O build do React roda com
NODE_ENV=development npm install --include=devporque oviteé devDependency e o listener roda comNODE_ENV=production(que pula devDeps).
3. Os Listeners de Webhook
| Listener | Porta | Serviço systemd | Repo | Script |
|---|---|---|---|---|
listener-rx.js |
9003 | webhook-rx.service |
rx.scoreodonto.com |
deploy-prod.sh |
listener-rx-client.js |
9004 | webhook-rx-client.service |
rx.scoreodonto.com-client |
deploy-client.sh |
listener.js |
9000 | webhook-listener.service |
clube67 (system hook) |
deploy-prod-builder.sh |
📌 O webhook do sistema (
git.clube67.com/-/admin/hooks→10.99.0.4:9000) dispara para todos os repositórios. Os listeners das portas 9003 e 9004 são webhooks por repositório, configurados individualmente em cada repo, e rodam em paralelo.
Todos os listeners seguem o mesmo padrão:
- Validam token via
x-gitea-tokenheader ou?token=query string - Trava de concorrência (
isBuilding = true) — ignora pushes enquanto um build roda - Anti-loop — ignora commits
chore(deploy): bump version - Respondem HTTP 202 imediatamente, evitando timeout do Gitea (30s)
- Atualizam o status do commit no Gitea (
pending → success / failure)
Tokens e URLs de webhook
Servidor → http://10.99.0.4:9003/webhook?token=rx_deploy_token_9003_c7f2a1b4e8d5
Client → http://10.99.0.4:9004/webhook?token=rx_client_deploy_token_9004_b3e1f7a2c9d6
Clube67 → http://10.99.0.4:9000/webhook?token=gitea_webhook_secret_clube67_58e4a92c3d18
Verificar status dos listeners
sudo systemctl status webhook-rx.service
sudo systemctl status webhook-rx-client.service
sudo systemctl status webhook-listener.service
# Portas abertas (deve mostrar 9000, 9003, 9004):
ss -tlnp | grep -E "900[0-9]"
# Teste rápido (401 = listener respondendo):
curl -s -o /dev/null -w "%{http_code}" -X POST http://10.99.0.4:9004/webhook
Logs (todos compartilham o mesmo arquivo)
tail -50 /home/deploy/stack/webhook-listener/deploy.log
# Prefixos: [rx.scoreodonto.com] | [rx.scoreodonto.com-client] | sem prefixo (clube67)
4. Versionamento — Servidor
Fonte de verdade: dental-server/version.txt (lido em runtime, injetado no sidebar). package.json nunca é tocado no bump (preserva cache do Docker).
ssh deploy@10.99.0.1 "docker exec rx_scoreodonto_app cat /app/version.txt"
curl -s https://rx.scoreodonto.com/ | grep -o 'v[0-9]\.[0-9]\.[0-9]'
5. Cliente Windows — Build, Publicação e Auto-Update
Projeto: rx.scoreodonto.com-client (Electron + electron-updater).
5.1. Fonte de verdade
grep '"version"' /home/deploy/stack/rx.scoreodonto.com-client/package.json
# "version": "1.3.0"
5.2. As DUAS pastas (e por que confundem)
| Pasta | Onde | O que é |
|---|---|---|
rx.scoreodonto.com-client/dist/ |
VPS4 | Saída do build. Tem todos os .exe gerados. |
rx.scoreodonto.com/dental-server/updates/ |
VPS1 | O que é servido via HTTP. É o que as clínicas enxergam. |
🔑 Buildar ≠ publicar. O cliente só "existe" para as clínicas quando o
latest.ymlda VPS1 aponta para a nova versão.
5.3. Deploy automático via webhook (desde v1.3.0)
# Basta fazer push para main:
git push origin main
# → Gitea notifica VPS4:9004 → deploy-client.sh roda (~90s) → VPS1 atualizada
Deploy manual (emergência):
cd /home/deploy/stack/rx.scoreodonto.com-client
bash deploy-client.sh
5.4. Fluxo de auto-update nas clínicas
App inicia → GET https://rx.scoreodonto.com/updates/latest.yml
→ versão yml > instalada? → baixa .exe → instala ao reiniciar
5.5. Verificação rápida (3 pontos têm que bater)
# 1. Código:
grep '"version"' /home/deploy/stack/rx.scoreodonto.com-client/package.json
# 2. HTTP público (o que o auto-updater baixa):
curl -s https://rx.scoreodonto.com/updates/latest.yml | head -1
# 3. VPS1 direto:
ssh deploy@10.99.0.1 "head -1 /home/deploy/stack/rx.scoreodonto.com/dental-server/updates/latest.yml"
5.6. Proteções contra perda de dados na atualização (v1.3.0)
deleteAppDataOnUninstall: false—%APPDATA%\ScoreOdonto Dental Client\nunca é apagadoconfig.backup.json— espelho doconfig.json, restaurado automaticamente se o principal corromper (preservaclientIde credenciais — evita reconfiguração do zero)update-state.json— após 3 falhas da mesma versão, para de auto-instalar e exibe balão pedindo reinstalação manual; zerado automaticamente no próximo boot bem-sucedido
6. 🔒 Nomes Congelados — Regra Obrigatória
⛔ Agentes de IA e desenvolvedores NÃO devem alterar os nomes abaixo sem aprovação explícita do responsável pelo projeto.
6.1. Nome do sistema (painel web)
| Local | Valor fixo |
|---|---|
dental-client/index.html → <title> |
Score Client — Gerenciador de Imagens |
src/components/Sidebar.jsx → cabeçalho |
Score Client |
src/pages/LoginPage.jsx → <h1> |
Score Client |
src/pages/DownloadPage.jsx → <h2> |
Score Client — Cliente Windows |
6.2. Nome do executável do cliente Windows
{ "productName": "ScoreOdonto Dental Client" }
Gera: ScoreOdonto Dental Client Setup X.Y.Z.exe — apenas X.Y.Z muda.
Alterar o productName quebra: latest.yml, URL de download, histórico de instalações no Windows.
6.3. URL de download — resolução dinâmica
DownloadPage.jsx lê path: do latest.yml em runtime — sem hardcode. Depende do productName exato.
6.4. Checklist antes de refatorar
<title>=Score Client — Gerenciador de Imagens?- Sidebar =
Score Client? <h1>login =Score Client?productName=ScoreOdonto Dental Client?latest.ymlem produção tempath: ScoreOdonto Dental Client Setup X.Y.Z.exe?
6.5. ⚠️ REGRA OBRIGATÓRIA: Deploy Conjunto (Client + Server)
Como o sistema é dividido em dois repositórios independentes (rx.scoreodonto.com e rx.scoreodonto.com-client), o build do cliente não ocorre junto com o do servidor.
Sempre que for criada uma funcionalidade que exija comunicação entre as duas pontas (ex: Sockets, API, IPC de Auto-Update, Sincronização), é OBRIGATÓRIO (CRITICAL) confirmar se o git add . && git commit && git push origin main foi executado separadamente nos DOIS repositórios.
- Fiz o
git pushno repositóriorx.scoreodonto.com(Servidor)? - Fiz o
git pushno repositóriorx.scoreodonto.com-client(Cliente Windows)?
7. Erros comuns e soluções
❌ Erro A — Cliente desatualizado nas clínicas
Causa: build no dist/ mas rsync → VPS1 não rodou, ou webhook não disparou.
grep "rx.scoreodonto.com-client" /home/deploy/stack/webhook-listener/deploy.log | tail -10
# Solução rápida (sem rebuild):
rsync -avz dist/*.exe dist/*.yml dist/*.blockmap \
deploy@10.99.0.1:/home/deploy/stack/rx.scoreodonto.com/dental-server/updates/
❌ Erro B — Botão download → 404
path: no latest.yml ≠ arquivo real. Causa: productName alterado.
curl -s https://rx.scoreodonto.com/updates/latest.yml | grep "^path:"
Solução: restaurar productName para ScoreOdonto Dental Client e rodar deploy-client.sh.
❌ Erro C — vite: not found
deploy-prod.sh usa NODE_ENV=development npm install --include=dev.
❌ Erro D — Imagens em branco
Endpoint de thumb deve servir bytes (image/jpeg), não JSON.
❌ Erro E — Wasabi 403
Chamar storage.loadConfigFromDb() após salvar credenciais.
❌ Erro F — Listener do client não dispara
sudo systemctl status webhook-rx-client.service
curl -s -o /dev/null -w "%{http_code}" -X POST http://10.99.0.4:9004/webhook
# Esperado: 401
sudo systemctl restart webhook-rx-client.service
8. Troubleshooting
| Problema | Diagnóstico | Solução |
|---|---|---|
| Cliente desatualizado | package.json ≠ latest.yml VPS1? |
Push para main (auto) ou bash deploy-client.sh |
| Webhook client não dispara | systemctl status webhook-rx-client / porta 9004? |
sudo systemctl restart webhook-rx-client.service |
| Botão download → 404 | path: ≠ arquivo real? |
Verificar productName; rebuildar |
| Build client falha | electron-builder na VPS4 | Ver log; rodar deploy-client.sh manual |
| Loop de atualização | 3+ tentativas mesma versão? | v1.3.0 tem trava; atualizar client |
| Config sumiu após update | config.backup.json em AppData? |
v1.3.0 restaura automaticamente |
Deploy servidor vite: not found |
NODE_ENV=production no install |
--include=dev no deploy-prod.sh |
| Imagens em branco | thumb retornando JSON? | Endpoint deve servir bytes image/jpeg |
| Wasabi 403 | credenciais recarregadas? | loadConfigFromDb() após salvar |
| Deploy servidor lento (3+ min) | Modelo B? | Modelo A (build na VPS4) |
9. Changelog
| Versão | Data | O que mudou |
|---|---|---|
| 1.0.5 | 2026-06-03 | Regra de Deploy Conjunto: Adicionada a seção 6.5 exigindo a verificação obrigatória de git push em ambos os repositórios para evitar descompasso de versão entre o servidor e o cliente (como ocorreu na versão 1.3.8 do client). Atualização automática transparente via Socket.IO. |
| 1.0.4 | 2026-05-31 | Automação deploy client: listener-rx-client.js (porta 9004) + webhook-rx-client.service + hook por repositório — push para main dispara build e publicação automáticos. Client v1.3.0: anti-loop de atualização, backup automático de config, deleteAppDataOnUninstall: false. Documenta webhook do sistema vs webhooks por repositório. Nomes congelados formalizados. |
| 1.0.3 | 2026-05-31 | Ciclo completo do Cliente Windows. Armadilha "buildado mas não publicado", verificação em 3 pontos, vite: not found. |
| 1.0.2 | 2026-05-30 | Switch para Modelo A (build VPS4), erros comuns, Wasabi. Deploy ~30-40s. |
| 1.0.1 | 2026-05-30 | Consolidação inicial: deploy + versionamento + segurança. |
| 1.0.0 | 2026-05-30 | Versão inicial. |