# rx.scoreodonto.com (server) — Plano de deploy (padronização Build Once → Promote → Deploy) > Delta deste projeto. Modelo universal em `../../deploy/`. Aqui o que **difere**: é um app web > monolítico (Express serve o React `dist/` + `/api` + `/updates/` do auto-update dos clients). > O **client.exe** é outro projeto (ver `../rx.scoreodonto.com-client/`). ## O que torna este projeto diferente - **App único** (`dental-server`): Express serve o front React (Vite, buildado no `dist/`), a API e o diretório **`/updates/`** (auto-update dos clients — volume `dental-server/updates`, **não pode quebrar**). - **Banco**: Postgres `dental_images` + Redis em 10.99.0.3. Migrações no boot. - Frontend **não tem imagem própria** (como no scoreodonto): o React é buildado como **step do CI** antes do `docker build` (o `Dockerfile` atual espera o `dist/` pronto — mantido p/ menor risco). ## Estado ANTES (modelo antigo) - **webhook 9003** (VPS4) → `deploy-prod.sh`: build na VPS4 → `docker save`/`gzip`/`rsync` → `docker load`+`up` na VPS1. - Imagem sempre `:latest`; **bump automático** de PATCH (`update-version.js`). Sem registry, sem build-once, sem promote. ## Plano (modelo da stack) | Fase | O que | Status | |---|---|---| | 1 | `/api/health` + `/api/version` + **badge** no canto (lê `/api/health`) | ✅ (em dev) | | 2 | `/api/health` usa **APP_VERSION** (tag) em runtime, fallback `version.txt` | ✅ (em dev) | | 3 | **CI** `.gitea/workflows/build.yml`: push→build `rx-scoreodonto:` no registry; tag→promote (digest) + deploy | ✅ validado (CI verde) | | 4 | `docker-compose.prod.yml` (`image:` do registry, `up --no-build`, `APP_VERSION`/`GIT_COMMIT` em runtime) | ✅ | | 5 | `deploy-prod.sh` novo (VPS1 pull + up --no-build); antigo → `deploy-prod-legado-webhook.sh` | ✅ | | 6 | **Corte**: webhook 9003 desligado → push → tag **v2.2.0** → promote+deploy prod → validado | ✅ **EM PROD (18/jun/2026)** | ## Imagem / registry - `git.clube67.com/ruicesar/rx-scoreodonto:` (build-once por commit) → promovida a `:vX.Y.Z` (sem rebuild). - Versão semântica nasce da **tag**, injetada em runtime (`APP_VERSION`) — não entra no build. - **Imagem enxuta: 1.62GB → 340MB.** Lições (camadas grandes davam PUT 500/499 no registry): - `sharp` usa **binário prebuilt** (libvips embarcado) — NÃO instalar `vips`/`vips-dev` (faz compilar do fonte → camada ~400MB). - `.dockerignore` deve excluir `updates/` (196MB de `.exe`, servido por volume), `data/`, `bkp/`, `**/node_modules`. - toolchain (`python3/make/g++`) entra **virtual** e sai na mesma `RUN`. ## Pré-requisitos de PROD (VPS1) — uma vez - **`.secrets` deve existir** em `/home/deploy/stack/rx.scoreodonto.com/.secrets` (env_file do compose). Não vai pelo git/scp do CI — copiar manualmente da VPS4 (`scp .secrets deploy@10.99.0.1:...`, chmod 600). **Sem ele o `up` falha** (`env file .secrets not found`). - `docker login git.clube67.com` na VPS1 (para o `pull` do registry). ## Estado APÓS corte (18/jun/2026) - Prod roda `rx-scoreodonto:v2.2.0` (recriado via `up --no-build`). `/api/health` = `v2.2.0`/`PROD`/db ok. - `/updates/latest.yml` = **1.3.9 intacto** (auto-update dos clients preservado). - Backup pré-corte: `/home/deploy/backups/dental_images_20260618_0552.dump` (VPS4). - **Follow-up**: `/api/health` reporta `cache: down` (Redis conecta nos logs mas o `ping()` do health falha) — **pré-existente** (dev.rx já mostrava), app fica `degraded` mas funcional. Investigar o método `redis.getClient().ping()`. ## Rollback - `deploy-prod.sh ` (quando houver tag anterior no novo modelo) **ou** reativar `webhook-rx.service` (modelo antigo) + push. - Banco: `pg_restore` do dump acima.