docs(wiki): Deploy V.1.0.2 - Modelo A otimizado, Wasabi, erros comuns ~30-40s por deploy

VPS 4 Deploy Agent
2026-05-30 16:37:14 +02:00
parent 246164ff03
commit 424ff4b31a
+281
@@ -0,0 +1,281 @@
# 🚀 Deploy V.1.0.2 — GitOps + Modelo A otimizado para Wasabi
> Versão **1.0.2** — RX agora usa **Modelo A (build na VPS4, imagem pronta para VPS1)** com Wasabi como storage.
> Tempo de deploy: **~30-40 segundos** (era 3-7min com Modelo B).
> Evita os erros comuns documentados aqui.
---
## 📋 Índice
1. [Arquitetura Multi-VPS](#1-arquitetura-multi-vps)
2. [Pipeline GitOps](#2-pipeline-gitops-modelo-a)
3. [Por que RX usa Modelo A (não B)](#3-por-que-rx-usa-modelo-a)
4. [O Listener do Webhook](#4-o-listener-do-webhook)
5. [Versionamento do Servidor](#5-versionamento--servidor)
6. [Versionamento do Cliente Windows](#6-versionamento--cliente-windows)
7. [Erros comuns e soluções](#7-erros-comuns-e-soluções)
8. [Troubleshooting](#8-troubleshooting)
9. [Changelog](#9-changelog)
---
## 1. Arquitetura Multi-VPS
| Servidor | IP (VPN) | Função |
|---|---|---|
| **VPS 1 — App** | `10.99.0.1` | Produção (Docker rootless) |
| **VPS 3 — Data/Git** | `10.99.0.3` | Gitea + PostgreSQL |
| **VPS 4 — Builder** | `10.99.0.4` | Build + deploy |
| **Wasabi** | S3-compatível | Storage de imagens (não no disco) |
---
## 2. Pipeline GitOps (Modelo A)
```
git push origin main
Gitea dispara webhook → VPS4:9003/webhook
listener-rx.js: valida token, dispara deploy-prod.sh
VPS4: docker compose build app (cria imagem)
VPS4: docker save → app.tar.gz (exporta imagem compilada)
VPS4: rsync app.tar.gz + docker-compose.yml → VPS1
VPS1: docker load < app.tar.gz (carrega imagem)
VPS1: docker compose up -d --no-deps app (reinicia container)
VPS4: git push chore(deploy): bump version (anti-loop)
✅ Deploy concluído (~30-40 segundos)
```
**Por que é rápido:**
- ✅ VPS4 faz o build pesado (docker compose build)
- ✅ VPS1 só recebe a imagem pronta (docker load)
- ✅ Nenhuma compilação na produção
- ✅ Usa cache do Docker na VPS4 (npm install reutilizado)
---
## 3. Por que RX usa Modelo A (não B)
**Modelo B** (build na VPS1, descontinuado para RX):
```
VPS4: rsync código-fonte → VPS1
VPS1: docker compose build app ← LENTO (3-7 min)
```
**Problema:** `COPY . .` no Dockerfile invalida cache a cada mudança de código.
Docker tem que recompile tudo mesmo que `package.json` não tenha mudado.
**RX com Wasabi:**
- Imagens estão no Wasabi (não no container)
- Container é **stateless** (sem volumes de dados)
- Modelo A é perfeito: build rápido na VPS4, imagem pronta na VPS1
**Clube67 (modelo A histórico):**
```
VPS4: npm install + npm build (front) ✓ rápido
VPS4: rsync dist/ → VPS1 ✓ rápido
VPS1: docker compose up (sem build) ✓ instantâneo
```
---
## 4. O Listener do Webhook
**Arquivo:** `/home/deploy/stack/webhook-listener/listener-rx.js` (porta 9003)
**Responsabilidades:**
- Valida token (`x-gitea-token` ou `?token=...`)
- **Trava de concorrência:** `isBuilding` flag (não permite deploys paralelos)
- **Anti-loop:** ignora commits `chore(deploy): bump version` (senão loop infinito)
- Dispara `deploy-prod.sh` em background
- Reporta status ao Gitea (pending → success/failure)
**Configuração:**
```bash
# /etc/systemd/system/webhook-rx.service
PORT=9003
WEBHOOK_TOKEN=rx_deploy_token_9003_c7f2a1b4e8d5
DEPLOY_SCRIPT=/home/deploy/stack/rx.scoreodonto.com/deploy-prod.sh
GITEA_TOKEN=<token da API do Gitea>
```
---
## 5. Versionamento — Servidor
### Fonte de verdade: `dental-server/version.txt`
```bash
# Arquivo:
cat /home/deploy/stack/rx.scoreodonto.com/dental-server/version.txt
# Output: 2.1.4
# Ou via HTTP (injetado pelo server no sidebar):
curl -s http://rx.scoreodonto.com/ | grep -o 'v[0-9]\.[0-9]\.[0-9]'
# Output: v2.1.4
```
### Como o versionamento funciona
| Arquivo | Quando muda |
|---|---|
| `version.txt` | A cada deploy (auto-bump PATCH) |
| `package.json` | **NUNCA** (preserva Docker cache) |
| `index.html` | A cada deploy (versão injetada) |
| Server em runtime | Lê `version.txt` (não package.json) |
**Importante:** `update-version.js` **NÃO toca** `package.json`:
```javascript
// ❌ NÃO faz isso (invalida cache Docker):
fs.writeFileSync(packageFile, JSON.stringify(pkg, null, 2));
// ✅ Só toca esses:
fs.writeFileSync(versionFile, next); // version.txt
fs.writeFileSync(indexFile, html); // index.html (versão injetada)
```
---
## 6. Versionamento — Cliente Windows
### Fonte de verdade: `package.json` → `latest.yml`
```bash
# Arquivo:
cat /home/deploy/stack/rx.scoreodonto.com-client/package.json | grep version
# Output: "version": "1.2.7"
# Gerado no build:
cat /home/deploy/stack/rx.scoreodonto.com/dental-server/updates/latest.yml
# Output:
# version: 1.2.7
# files:
# - url: ScoreOdonto Dental Client Setup 1.2.7.exe
```
**Fluxo de atualização nas clínicas:**
```
Clínica inicia app
electron-updater: GET /updates/latest.yml
version no yml > versão instalada?
↓ SIM
Baixa .exe + instala ao reiniciar
↓ NÃO
Nada acontece
```
**⚠️ CRÍTICO:** Se `latest.yml` não for atualizado, **clínicas não recebem update**.
---
## 7. Erros comuns e soluções
### ❌ Erro 1: `previewStyle is not defined`
**Causa:** Variável não definida no `patientCardHTML()`
**Solução:** Sempre ter `<img>` ou placeholder, nunca deixar vazio
```javascript
// ❌ ERRADO:
const preview = thumb ? `<img...>` : '';
<div class="image-preview"${previewStyle}> // previewStyle não existe
// ✅ CORRETO:
const preview = thumb
? `<img class="preview-img" ...>`
: `<div class="preview-img-placeholder"></div>`;
<div class="image-preview">
${preview}
</div>
```
### ❌ Erro 2: Deploy leva 3-7 minutos
**Causa:** Modelo B com `docker compose build` na VPS1
**Solução:** Usar Modelo A — build na VPS4, imagem para VPS1
```bash
# ❌ LENTO (Modelo B):
ssh VPS1 "docker compose build app && docker compose up"
# ✅ RÁPIDO (Modelo A):
docker compose build app # VPS4 (local, cache reutilizado)
docker save | gzip > app.tar.gz
rsync app.tar.gz → VPS1
ssh VPS1 "docker load < app.tar.gz && docker compose up"
```
### ❌ Erro 3: Docker cache sempre invalida
**Causa:** `COPY . .` no Dockerfile invalida cache quando código muda
**Solução:** Não tocar `package.json` no version bump
```dockerfile
# ❌ PROBLEMA:
FROM node:22-alpine
RUN npm install # camada 1
COPY . . # camada 2 (invalida sempre que código muda)
# ✅ SOLUÇÃO:
FROM node:22-alpine
COPY package*.json ./ # copia ANTES de install
RUN npm install # camada 2 (reutiliza cache se package.json não muda)
COPY . . # camada 3 (invalida, mas npm install já rodou)
```
### ❌ Erro 4: Imagens dos cards ficam em branco
**Causa:** Sem `thumb_filename` no banco, sem placeholder no frontend
**Solução:** SEMPRE gerar thumbnail + mostrar placeholder
```javascript
// Servidor gera thumb ao receber imagem:
const thumbBuffer = await imageProcessor.getThumbnail(imageBuffer, 500, 500);
thumbFilename = `thumb_${filename}`;
await storage.saveImage(thumbFilename, thumbBuffer, ...);
// Salva no banco com thumb_filename
// Frontend SEMPRE mostra algo:
const preview = thumb
? `<img src="${thumb}" loading="lazy">` // mostra thumb rápido
: `<div class="placeholder"></div>`; // fallback cinza enquanto carrega
```
### ❌ Erro 5: Loop infinito de CI
**Causa:** Listener não ignora commits de bump de versão
**Solução:** Regex anti-loop no listener
```javascript
const VERSION_BUMP_RE = /^chore\(deploy\):\s*bump version/i;
if (messages.every(m => VERSION_BUMP_RE.test(m))) {
// Ignora commit de bump → sem loop
return res.json({ status: 'skipped' });
}
```
---
## 8. Troubleshooting
| Problema | Diagnóstico | Solução |
|---|---|---|
| Deploy leva 3+ min | Está usando Modelo B? | Mudar para Modelo A (build na VPS4) |
| Docker cache invalida | `COPY . .` perde cache? | Não tocar `package.json` no bump |
| Webhook não dispara | `systemctl status webhook-rx` | Reiniciar listener ou verificar token |
| `429` no webhook | Build em progresso? | Aguardar concluir |
| Imagens em branco nos cards | Sem `thumb_filename` no banco? | Rodar `/api/images/backfill-thumbs` |
| Clínicas não recebem update | `latest.yml` desatualizado? | Verificar se deploy-client.sh roda completo |
| `docker load` falha | Imagem corrompida? | Refazer build: `docker compose build app` |
---
## 9. Changelog
| Versão | Data | O que mudou |
|---|---|---|
| **1.0.2** | 2026-05-30 | RX: switch para Modelo A (build VPS4), documentar erros comuns, Wasabi storage. Deploy agora ~30-40s. |
| 1.0.1 | 2026-05-30 | Consolidação inicial: deploy + versionamento + segurança. |
| 1.0.0 | 2026-05-30 | Versão inicial. |