From 11eb76c1d76017f8304b4df65f90744031075594 Mon Sep 17 00:00:00 2001 From: VPS 4 Deploy Agent Date: Sun, 31 May 2026 01:22:33 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20adiciona=20Deploy-V.1.0.4=20=E2=80=94?= =?UTF-8?q?=20nomes=20de=20sistema=20e=20cliente=20congelados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Deploy-V.1.0.4.md | 319 ++++++++++++++++++++++++++++++++++++++++++++++ Home.md | 7 +- 2 files changed, 323 insertions(+), 3 deletions(-) create mode 100644 Deploy-V.1.0.4.md diff --git a/Deploy-V.1.0.4.md b/Deploy-V.1.0.4.md new file mode 100644 index 0000000..f2fd73d --- /dev/null +++ b/Deploy-V.1.0.4.md @@ -0,0 +1,319 @@ +# 🚀 Deploy V.1.0.4 — Nomes de Sistema e Cliente Congelados + +> Versão **1.0.4** — inclui tudo da 1.0.3 e formaliza a **regra de nomes congelados**: +> o nome do sistema (`Score Client`) e o nome do executável do cliente Windows +> (`ScoreOdonto Dental Client Setup X.Y.Z.exe`) são **fixos e imutáveis** — +> apenas o número de versão pode mudar. + +--- + +## 📋 Índice + +1. [Arquitetura Multi-VPS](#1-arquitetura-multi-vps) +2. [Pipeline GitOps (Servidor — Modelo A)](#2-pipeline-gitops-modelo-a) +3. [O Listener do Webhook](#3-o-listener-do-webhook) +4. [Versionamento — Servidor](#4-versionamento--servidor) +5. [Cliente Windows — Build, Publicação e Auto-Update](#5-cliente-windows--build-publicacao-e-auto-update) +6. [🔒 Nomes Congelados — Regra Obrigatória](#6-nomes-congelados--regra-obrigatória) +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) + 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) + +``` +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 app → 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) +``` + +> ⚠️ O build do React roda com `NODE_ENV=development npm install --include=dev` porque +> o `vite` é devDependency e o listener roda com `NODE_ENV=production` (que pula devDeps). + +--- + +## 3. O Listener do Webhook + +**Arquivo:** `/home/deploy/stack/webhook-listener/listener-rx.js` (porta 9003) + +- Valida token (`x-gitea-token` ou `?token=...`) +- **Trava de concorrência** (`isBuilding`) + **anti-loop** (ignora `chore(deploy): bump version`) +- Dispara `deploy-prod.sh` e reporta status ao Gitea (pending → success/failure) + +> 📌 **O cliente Windows NÃO tem webhook.** O `deploy-client.sh` é **manual** (ver seção 5). +> Essa é a causa nº 1 de defasagem de versão do cliente. + +--- + +## 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). Detalhes na 1.0.2. + +```bash +# Versão servida (dentro do container é a fonte de verdade): +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 + +Tudo parte do `package.json` do cliente: + +```bash +grep '"version"' /home/deploy/stack/rx.scoreodonto.com-client/package.json +# "version": "1.2.7" +``` + +O `electron-builder` lê esse número e gera **3 artefatos** por versão: +`ScoreOdonto Dental Client Setup X.Y.Z.exe`, `...exe.blockmap` e `latest.yml`. + +### 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` já gerados. | +| `rx.scoreodonto.com/dental-server/updates/` | **VPS1** | **O que é servido** em `https://rx.scoreodonto.com/updates/`. É o que as clínicas enxergam. | + +> 🔑 **Buildar (gerar no `dist/`) ≠ publicar (copiar para `updates/` na VPS1).** +> O cliente só "existe" para as clínicas quando o `latest.yml` da **VPS1** aponta para a nova versão. + +### 5.3. Ciclo completo — `deploy-client.sh` + +```bash +cd /home/deploy/stack/rx.scoreodonto.com-client +bash deploy-client.sh +``` + +O script faz, em ordem: +``` +1. npm install +2. npx electron-builder --win --x64 --publish never → gera dist/*.exe + dist/latest.yml (VPS4) +3. valida que dist/ tem .exe e latest.yml +4. rsync dist/*.exe dist/*.yml dist/*.blockmap → VPS1:/.../dental-server/updates/ ← PUBLICAÇÃO +``` + +**Só termina o trabalho quando o passo 4 roda.** Se você buildar manualmente +(`npm run dist`) e parar aí, o `dist/` na VPS4 fica novo, mas a VPS1 continua na versão antiga. + +### 5.4. Fluxo de auto-update nas clínicas + +``` +App do cliente inicia + ↓ +electron-updater: GET https://rx.scoreodonto.com/updates/latest.yml + ↓ +versão do yml > versão instalada? → baixa o .exe e instala ao reiniciar + → senão, nada acontece +``` + +### 5.5. Verificação rápida (3 pontos têm que bater) + +```bash +# 1. Fonte (código): +grep '"version"' /home/deploy/stack/rx.scoreodonto.com-client/package.json + +# 2. Publicado na VPS1 (o que conta): +ssh deploy@10.99.0.1 "head -1 /home/deploy/stack/rx.scoreodonto.com/dental-server/updates/latest.yml" + +# 3. O que o auto-updater realmente baixa (HTTP público): +curl -s https://rx.scoreodonto.com/updates/latest.yml | head -1 +``` +Se os três não baterem → rode `deploy-client.sh` até o fim. + +--- + +## 6. 🔒 Nomes Congelados — Regra Obrigatória + +> ⛔ **Esta seção define regras imutáveis do projeto. 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` → `` | `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` | + +**O nome `Score Client` é fixo.** Nunca substituir por variações como `RF Dental`, `RFDental`, +`ScoreOdonto`, `RX Dental` ou qualquer outro. Se houver refatoração de UI, manter exatamente +`Score Client`. + +### 6.2. Nome do executável do cliente Windows + +O `electron-builder` gera o instalador com o nome definido em `package.json` → campo `productName`: + +```json +{ + "productName": "ScoreOdonto Dental Client" +} +``` + +Isso resulta em arquivos com o padrão: + +``` +ScoreOdonto Dental Client Setup X.Y.Z.exe +ScoreOdonto Dental Client Setup X.Y.Z.exe.blockmap +latest.yml → path: ScoreOdonto Dental Client Setup X.Y.Z.exe +``` + +**Apenas `X.Y.Z` (o número de versão) pode mudar.** O prefixo `ScoreOdonto Dental Client Setup ` +é imutável. Alterar o `productName` no `package.json` quebra: +- O `latest.yml` gerado (nome do `path` muda → auto-updater não encontra o arquivo) +- A URL de download no painel web (que lê `path:` do `latest.yml` dinamicamente) +- O histórico de instalações nas clínicas (Windows trata como produto diferente) + +### 6.3. URL de download — como funciona (e por que não usar nome hardcoded) + +O `DownloadPage.jsx` lê o nome real do arquivo a partir do `latest.yml` **em tempo de execução**: + +```javascript +fetch('/updates/latest.yml') + .then(r => r.text()) + .then(t => { + const vMatch = t.match(/version:\s*([\d.]+)/); + if (vMatch) setVersion(vMatch[1]); // ex: "1.2.6" + const pMatch = t.match(/^path:\s*(.+)$/m); + if (pMatch) setExeFilename(pMatch[1].trim()); // ex: "ScoreOdonto Dental Client Setup 1.2.6.exe" + }); + +// URL final construída: +const downloadUrl = `/updates/${encodeURIComponent(exeFilename)}`; +``` + +> ✅ Isso garante que o botão de download sempre aponta para o arquivo correto da versão em produção, +> sem hardcode de nome — mas **depende de que o `productName` no `package.json` seja mantido exato**. + +### 6.4. Checklist antes de qualquer refatoração de UI ou cliente + +- [ ] O título da aba do browser ainda é `Score Client — Gerenciador de Imagens`? +- [ ] O cabeçalho da sidebar ainda exibe `Score Client`? +- [ ] O `<h1>` da tela de login ainda é `Score Client`? +- [ ] O `productName` no `package.json` do cliente ainda é `ScoreOdonto Dental Client`? +- [ ] O `latest.yml` em produção tem `path: ScoreOdonto Dental Client Setup X.Y.Z.exe`? + +Se qualquer item acima for `NÃO` → reverter antes de continuar. + +--- + +## 7. Erros comuns e soluções + +### ❌ Erro A — Cliente em uma versão, servidor servindo outra (mais antiga) +**Sintoma:** `package.json` = `1.2.7`, mas `updates/latest.yml` na VPS1 = `1.2.6`. +As clínicas não recebem o update e novas instalações baixam a versão velha. + +**Causa:** a versão foi buildada no `dist/` da VPS4 mas **o `rsync → VPS1` (passo 4 do +`deploy-client.sh`) não rodou**. Buildar não publica. + +**Diagnóstico:** +```bash +# Build existe no dist/ (VPS4)? +ls -1 /home/deploy/stack/rx.scoreodonto.com-client/dist/*.exe | sort -V | tail -1 +cat /home/deploy/stack/rx.scoreodonto.com-client/dist/latest.yml | head -1 # ex: 1.2.7 + +# Mas a VPS1 está atrás? +ssh deploy@10.99.0.1 "head -1 /home/deploy/.../dental-server/updates/latest.yml" # ex: 1.2.6 +``` +Versões diferentes = build não publicado. + +**Solução (rápida, sem rebuild — artefato já existe e é válido):** +```bash +cd /home/deploy/stack/rx.scoreodonto.com-client +rsync -avz dist/*.exe dist/*.yml dist/*.blockmap \ + deploy@10.99.0.1:/home/deploy/stack/rx.scoreodonto.com/dental-server/updates/ +``` +**Solução (canônica):** `bash deploy-client.sh` (rebuilda e publica). + +--- + +### ❌ Erro B — Botão de download diz "arquivo não disponível" +**Sintoma:** ao clicar em "Baixar Instalador" na página `/download`, o browser retorna 404. + +**Causa mais comum:** o `path:` no `latest.yml` não bate com o nome real do arquivo em `updates/`. +Isso acontece se o `productName` foi alterado no `package.json` do cliente, gerando um `.exe` +com nome diferente do que o `latest.yml` antigo listava. + +**Diagnóstico:** +```bash +# Nome que o painel web vai pedir: +ssh deploy@10.99.0.1 "grep '^path:' /home/deploy/stack/rx.scoreodonto.com/dental-server/updates/latest.yml" +# ex: path: ScoreOdonto Dental Client Setup 1.2.6.exe + +# Arquivos que existem de fato: +ssh deploy@10.99.0.1 "ls /home/deploy/stack/rx.scoreodonto.com/dental-server/updates/*.exe" +``` +Se os nomes não baterem → o `productName` foi alterado ou o `rsync` não foi executado. + +**Solução:** restaurar o `productName` para `ScoreOdonto Dental Client` e rodar `deploy-client.sh`. + +--- + +### ❌ Erro C — `vite: not found` no deploy do servidor +**Causa:** listener roda com `NODE_ENV=production` → `npm install` pula devDependencies (vite). +**Solução:** no `deploy-prod.sh`, `NODE_ENV=development npm install --include=dev` antes do `vite build`. + +### ❌ Erro D — Imagens do RX não aparecem (placeholder cinza, sem erro no DevTools) +**Causa:** endpoint de thumbnail retornava JSON `{url}` enquanto o `<img src>` apontava direto para ele. +**Solução:** o endpoint serve **bytes** (`image/jpeg`); gera a thumb on-the-fly e faz backfill no Wasabi. + +### ❌ Erro E — Wasabi 403 após trocar credenciais +**Causa:** salvar credenciais no banco **não recarregava** o cliente S3 em memória. +**Solução:** `storage-config` chama `storage.loadConfigFromDb()` após salvar. + +--- + +## 8. Troubleshooting + +| Problema | Diagnóstico | Solução | +|---|---|---| +| **Cliente desatualizado nas clínicas** | `package.json` ≠ `updates/latest.yml` na VPS1? | Rodar `deploy-client.sh` **até o passo 4** (ou rsync do `dist/`) | +| **Botão download → 404** | `path:` no `latest.yml` ≠ arquivo real em `updates/`? | Verificar `productName`; restaurar para `ScoreOdonto Dental Client` e rebuildar | +| Build do cliente falha | Wine/electron-builder na VPS4 | Ver log; `npx electron-builder --win --x64 --publish never` | +| Clínica não atualiza mesmo com yml novo | versão instalada ≥ yml? | Auto-update só sobe versão; conferir semver | +| Deploy do 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 config | +| Deploy do servidor lento (3+ min) | Modelo B? | Modelo A (build na VPS4) | + +--- + +## 9. Changelog + +| Versão | Data | O que mudou | +|---|---|---| +| **1.0.4** | 2026-05-31 | **Nomes congelados:** formaliza que `Score Client` (sistema) e `ScoreOdonto Dental Client Setup` (executável) são imutáveis. Documenta o Erro B (404 no download por nome errado), checklist de refatoração e como o `DownloadPage.jsx` resolve o nome dinamicamente via `latest.yml`. | +| 1.0.3 | 2026-05-31 | Ciclo completo do Cliente Windows (build `dist/` na VPS4 ≠ publicação `updates/` na VPS1). Documenta a armadilha "buildado mas não publicado", verificação em 3 pontos, e o `vite: not found`. | +| 1.0.2 | 2026-05-30 | RX: 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. | diff --git a/Home.md b/Home.md index 0942a92..c240432 100644 --- a/Home.md +++ b/Home.md @@ -4,12 +4,13 @@ Documentação da automação de deploy GitOps (Gitea + Multi-VPS) e versionamen ## Versão atual -- **[Deploy V.1.0.3](Deploy-V.1.0.3)** ← versão mais recente — ciclo completo do **Cliente Windows** - (build na VPS4 vs publicação na VPS1), armadilha "buildado mas não publicado", verificação em 3 pontos, - além de tudo do servidor (Modelo A, Wasabi, versionamento). +- **[Deploy V.1.0.4](Deploy-V.1.0.4)** ← versão mais recente — **nomes congelados**: `Score Client` + (sistema) e `ScoreOdonto Dental Client Setup` (executável) são imutáveis; apenas o número de versão + pode mudar. Inclui checklist de refatoração, Erro B (404 no download) e tudo da 1.0.3. ## Arquivo de versões anteriores +- [Deploy V.1.0.3](Deploy-V.1.0.3) — ciclo completo do Cliente Windows (build na VPS4 vs publicação na VPS1). - [Deploy V.1.0.2](Deploy-V.1.0.2) — Modelo A otimizado para Wasabi (deploy ~30-40s). - [Deploy V.1.0.1](Deploy-V.1.0.1) — consolidação: deploy + versionamento + segurança. - [Deploy V.1.0.0](Deploy-V.1.0.0) — versão inicial (apenas deploy, sem versionamento).