chore(deploy): remove stack staging morto + alinha docs para "dev"

- Remove o stack de "staging" que nunca foi isolado (apontava para o banco de
  produção) e não tinha rota no Traefik:
  - docker-compose-staging.yml, nginx/default-staging.conf
  - deploy-prod-builder.sh, deploy-to-prod.sh (pipeline deprecado — ver DEPLOY-PRODUCAO.md)
- CHECKLIST-DEPLOY-PRODUCAO.md realinhado ao fluxo real: dev.scoreodonto.com (VPS4)
  -> build -> VPS1; dev e prod compartilham o mesmo banco (sem ambiente isolado).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-06-16 03:28:47 +02:00
parent 3e64514b08
commit 0824810c4d
5 changed files with 47 additions and 271 deletions
-57
View File
@@ -1,57 +0,0 @@
#!/bin/bash
# ─── deploy-prod-builder.sh (ScoreOdonto) ────────────────────────────────────
# Script sênior de automação de build e despacho (GitOps) de VPS 4 -> VPS 1.
# ─────────────────────────────────────────────────────────────────────────────
set -e
LOCK_FILE="/tmp/deploy-prod-scoreodonto.lock"
if [ -f "$LOCK_FILE" ]; then
echo "🚨 Deploy já está em andamento! Aguarde..."
exit 1
fi
touch "$LOCK_FILE"
# Garante que o lock file será apagado no final do script mesmo se houver erro
trap 'rm -f "$LOCK_FILE"' EXIT
LOG_FILE="/home/deploy/stack/webhook-listener/deploy.log"
log() {
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] [SCOREODONTO] $1" | tee -a "$LOG_FILE"
}
log "🚀 Iniciando ciclo de build e deploy automático para ScoreOdonto..."
# 1. Atualizar repositório Git
log "📦 Sincronizando repositório Git local na VPS 4..."
cd /home/deploy/stack/scoreodonto.com
git fetch origin main
git reset --hard origin/main
# 1.1 Incrementar versão
log "🔢 Incrementando versão..."
node update-version.js
# 2. Compilar o Backend
log "⚙️ Instalando dependências e preparando o Backend..."
cd /home/deploy/stack/scoreodonto.com/backend
npm install
# 3. Compilar o Frontend
log "⚙️ Instalando dependências e compilando o Frontend (Vite)..."
cd /home/deploy/stack/scoreodonto.com/frontend
npm install
npm run build
# 4. Reiniciar os serviços de Staging localmente na VPS 4
log "🔄 Reiniciando contêineres de STAGING na VPS 4..."
docker compose -f /home/deploy/stack/scoreodonto.com/docker-compose-staging.yml up -d --build
# 6. Salvar incremento de versão no Git
log "💾 Comitando o incremento de versão de volta para o repositório central..."
cd /home/deploy/stack/scoreodonto.com
git add frontend/constants.ts
git commit -m "chore(deploy): bump version"
git push origin main
log "✅ Deploy de ScoreOdonto concluído com absoluto sucesso!"
-53
View File
@@ -1,53 +0,0 @@
#!/bin/bash
# ─── deploy-to-prod.sh (ScoreOdonto) ──────────────────────────────────────────
# Script de Promoção Manual: Staging (VPS 4) ➔ Produção (VPS 1)
# ─────────────────────────────────────────────────────────────────────────────
set -e
LOCK_FILE="/tmp/deploy-prod-scoreodonto-release.lock"
if [ -f "$LOCK_FILE" ]; then
echo "🚨 Promoção para produção já está em andamento! Aguarde..."
exit 1
fi
touch "$LOCK_FILE"
trap 'rm -f "$LOCK_FILE"' EXIT
LOG_FILE="/home/deploy/stack/webhook-listener/deploy.log"
log() {
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] [SCOREODONTO-PROD] $1" | tee -a "$LOG_FILE"
}
log "🚀 Iniciando PROMOÇÃO MANUAL de Staging (VPS 4) para PRODUÇÃO (VPS 1)..."
# 1. Sincronizar Backend para a VPS 1
log "🚚 Despachando Backend para a VPS 1 (Produção)..."
rsync -avz --delete \
--exclude '.env' \
--exclude 'node_modules/.cache' \
/home/deploy/stack/scoreodonto.com/backend/ \
deploy@10.99.0.1:/home/deploy/stack/scoreodonto.com/backend/
# 2. Sincronizar Frontend (build compilada em Staging) para a VPS 1
log "🚚 Despachando Frontend (build) para a VPS 1 (Produção)..."
rsync -avz --delete \
/home/deploy/stack/scoreodonto.com/frontend/dist/ \
deploy@10.99.0.1:/home/deploy/stack/scoreodonto.com/frontend/dist/
# 3. Sincronizar Plugins para a VPS 1
log "🚚 Despachando Plugins..."
rsync -avz --delete \
/home/deploy/stack/scoreodonto.com/plugins/ \
deploy@10.99.0.1:/home/deploy/stack/scoreodonto.com/plugins/
# 4. Sincronizar Docker Compose de Produção e Configurações de Nginx
log "🚚 Despachando Infraestrutura (Docker/Config) para a VPS 1..."
rsync -avz \
/home/deploy/stack/scoreodonto.com/docker-compose.yml \
/home/deploy/stack/scoreodonto.com/nginx \
deploy@10.99.0.1:/home/deploy/stack/scoreodonto.com/
# 5. SSH na VPS 1 para Reiniciar Serviços de Produção
log "🔄 Reiniciando containers de PRODUÇÃO na VPS 1..."
ssh -o BatchMode=yes deploy@10.99.0.1 "cd /home/deploy/stack/scoreodonto.com && DOCKER_HOST=unix:///run/user/1000/docker.sock docker compose up -d"
log "✅ PROMOÇÃO PARA PRODUÇÃO CONCLUÍDA COM SUCESSO!"
-61
View File
@@ -1,61 +0,0 @@
name: scoreodonto-staging
services:
nginx:
image: nginx:alpine
container_name: scoreodonto-nginx-staging
restart: always
ports:
- "8082:80"
volumes:
- "./nginx/default-staging.conf:/etc/nginx/conf.d/default.conf:ro"
networks:
- staging-net
depends_on:
- scoreodonto-backend-staging
- scoreodonto-frontend-staging
scoreodonto-backend-staging:
build:
context: "./backend"
dockerfile: Dockerfile
container_name: scoreodonto-backend-staging
restart: always
env_file:
- "./backend/.env"
environment:
- BAILEYS_ENGINE=infinite
- PORT=8018
- NODE_ENV=staging
- DATABASE_URL=postgresql://scoreodonto_user:clube67_scoreodonto_pass_9903@10.99.0.3:5432/scoreodonto?schema=public
- DRAGONFLY_URL=redis://:clube67_dragonfly_pass_9903@10.99.0.3:6379/1
- NATS_URL=nats://10.99.0.3:4222
- TEMPORAL_ADDRESS=10.99.0.3:7233
- BAILEYS_SESSIONS_PATH=/app/sessions
- APP_URL=https://staging.scoreodonto.com
- GOOGLE_REDIRECT_URI=https://staging.scoreodonto.com/api/oauth2callback
volumes:
- "/home/deploy/scoreodonto-sessions-staging:/app/sessions"
- "/home/deploy/scoreodonto-media-staging:/app/media"
- "./plugins:/app/plugins"
networks:
- staging-net
scoreodonto-frontend-staging:
build:
context: "."
dockerfile: frontend/Dockerfile
container_name: scoreodonto-frontend-staging
restart: always
env_file:
- "./frontend/.env.production.local"
environment:
- PORT=3013
- NODE_ENV=staging
- NEXT_PUBLIC_API_URL=https://staging.scoreodonto.com/api
networks:
- staging-net
networks:
staging-net:
driver: bridge
@@ -1,61 +1,60 @@
# Checklist de Deploy para Produção — Pacote Financeiro + Salas (jun/2026)
> Consolida tudo entregue na rodada: **Financeiro V1 (Épicos 14)**, **V2 Comissão/Repasse**, **Glosas**, **GTO→Financeiro**, **modelo de contrato p/ convênio** e o **redesenho de Salas** (perfil Dono de Sala, sala como workspace, financeiro de locação, painel/relatório/agenda).
> Tudo foi desenvolvido e testado em **DEV (VPS4 / dev.scoreodonto.com)**. **Nada commitado/empurrado ainda.**
> Consolida a rodada: **Financeiro V1 (Épicos 14)**, **V2 Comissão/Repasse**, **Glosas**, **GTO→Financeiro**, **modelo de contrato p/ convênio** e o **redesenho de Salas** (perfil Dono de Sala, sala como workspace, financeiro de locação, painel/relatório/agenda).
> O **procedimento canônico de deploy** é o `DEPLOY-PRODUCAO.md` — este arquivo é só o checklist desta entrega.
## 0. Arquitetura / como o deploy funciona
- **DEV/builder:** VPS4. **PROD:** VPS1 (`10.99.0.1`). **Banco:** VPS3 (`10.99.0.3`, Postgres `scoreodonto`).
- **Atenção (verificar):** o `docker-compose.yml` aponta `DATABASE_URL=…@10.99.0.3:5432/scoreodonto`. Se PROD (VPS1) **usa o mesmo banco**, as migrações **já foram aplicadas** durante o desenvolvimento (rodam no boot do backend, que fala com o VPS3). Se PROD tiver `.env` próprio com outro banco, as migrações aplicam no **1º boot do backend de prod**. → **CONFIRMAR o `DATABASE_URL` efetivo na VPS1 antes de prosseguir.**
- **Scripts:** `deploy-prod-builder.sh` (GitOps: `git reset --hard origin/main` → build → despacha p/ VPS1) **ou** `deploy-to-prod.sh` (rsync de `backend/`, `frontend/dist/`, `plugins/`, `docker-compose.yml`, `nginx` → VPS1 → `docker compose up -d`).
- **Regra:** **NÃO** rodar `deploy-to-prod.sh` automaticamente — é execução manual do operador, com autorização.
## 0. Arquitetura (sem ambiente isolado)
- **VPS4** = **desenvolvimento + builder**. O ambiente de teste é **`dev.scoreodonto.com`** (servido pela VPS4 via Traefik da VPS1) e é onde se **buildam** as imagens que vão pra produção.
- **VPS1** = **produção** (`scoreodonto.com`) — só recebe a imagem pronta e sobe.
- **VPS3** = **dados** (Postgres `scoreodonto`, Dragonfly, Gitea). **Dev e prod apontam para o MESMO banco `scoreodonto`.**
- ⚠️ Consequência (confirmada): **dev e prod compartilham o banco** → as migrações desta entrega **já foram aplicadas** ao banco real durante o desenvolvimento (rodam no boot do backend de dev). Promover é, na prática, **levar o CÓDIGO** para a VPS1 — o schema já está migrado. **Não há ambiente nem banco isolado** entre dev e produção.
## 1. Pré-requisitos (antes de promover)
- [ ] **Backup do banco** VPS3 (`pg_dump scoreodonto`) — passo obrigatório.
- [ ] Código **commitado e pushado** para `origin/main` (Gitea `10.99.0.3`). O builder faz `git reset --hard origin/main`. *(push exige sua autorização; NUNCA `--force` na main.)*
- [ ] `node --check backend/server.js` OK.
- [ ] Build do frontend sem erros.
## 1. Fluxo de promoção (resumo do DEPLOY-PRODUCAO.md §2)
1. `git push origin main` (na VPS4) — já feito (commit `3e64514`).
2. Bump de versão (`update-version.js``frontend/constants.ts`).
3. **Build da imagem Docker na VPS4** (`npm install` + `vite build` dentro do Dockerfile) — peso fora da produção.
4. **Envia a imagem pronta para a VPS1** (registry privado pela WireGuard — recomendado — ou `docker save | ssh | docker load`).
5. **VPS1:** `docker compose up -d` (troca o container pela imagem nova). Backend leva ~1030s no boot (reconecta Postgres/Dragonfly + roda migrações).
- *(Os scripts `deploy-prod-builder.sh` e `deploy-to-prod.sh` estão **DEPRECADOS** — ver seção "Fluxo ANTIGO" do `DEPLOY-PRODUCAO.md`. Não usar.)*
## 2. Pré-requisitos
- [ ] **Backup do banco** VPS3 (`pg_dump scoreodonto` — custom format `-Fc`). Como dev/prod compartilham o banco, esse backup é a **única rede de segurança**.
- [ ] Código em `origin/main` (Gitea) — ✅ `3e64514`.
- [ ] `node --check backend/server.js` OK; build do frontend OK.
- [ ] Validado em `dev.scoreodonto.com`.
## 2. Mudanças de banco (migrações idempotentes no boot — `runMigrations()`)
Todas são **aditivas** (`CREATE TABLE/INDEX IF NOT EXISTS`, `ADD COLUMN IF NOT EXISTS`) — **nenhuma destrutiva**.
## 3. Mudanças de banco (migrações idempotentes no boot — todas ADITIVAS)
> Já aplicadas no banco (dev=prod). Listadas para auditoria/rollback. Nenhuma destrutiva.
- **Novas tabelas:** `procedimentos_realizados`, `procedimento_realizado_foto`, `comissao_regras`, `repasses`, `glosas`.
- **ALTER `financeiro`:** `paciente_id, profissional_id, procedimento_id, agendamento_id, contrato_id, realizado_id, origem, sem_comissao, pago_em, deleted_at, fornecedor, centro_custo, recorrente, convenio_id, valor_glosa, data_competencia, competencia`.
- **ALTER `orcamentos`:** `financeiro_gerado`.
- **ALTER `contratos`:** `cobranca_automatica, valor_recorrente, periodicidade, dia_cobranca`.
- **ALTER `gto_builders`:** `financeiro_id`. **ALTER `sala_reservas`:** `financeiro_id`.
- **CHECK constraints (DROP+ADD no boot):** `usuarios_role_check` e `vinculos_role_check` ganham **`donosala`** (lista é superset → seguro; nenhum role existente é removido).
- **Índices:** troca `uq_comissao_regra``uq_comissao_regra2` (DROP+CREATE); novos `idx_financeiro_contrato_comp`, `idx_financeiro_convenio`, `idx_glosas_clinica/_financeiro/_item`, `idx_repasses_clinica`, `idx_gto_builders_financeiro`.
- **Seeds (boot):** `seedContratoModelos()` insere `cmod_odo_14` "Atendimento a Convênios / Repasse" (ON CONFLICT DO NOTHING).
- ⚠️ Se o banco for o mesmo do dev, **tudo isso já está aplicado** — o boot do backend de prod será no-op nas migrações.
- **ALTER `orcamentos`:** `financeiro_gerado`. **ALTER `contratos`:** `cobranca_automatica, valor_recorrente, periodicidade, dia_cobranca`. **ALTER `gto_builders`:** `financeiro_id`. **ALTER `sala_reservas`:** `financeiro_id`.
- **CHECK `usuarios_role_check` / `vinculos_role_check`** ganham `donosala` (DROP+ADD no boot — superset, seguro).
- **Índices:** troca `uq_comissao_regra``uq_comissao_regra2`; novos `idx_financeiro_contrato_comp`, `idx_financeiro_convenio`, `idx_glosas_*`, `idx_gto_builders_financeiro`.
- **Seed (boot):** `cmod_odo_14` "Atendimento a Convênios / Repasse" (ON CONFLICT DO NOTHING).
## 3. Backend — env / infra
- [ ] `DATABASE_URL` de prod correto (ver §0).
- [ ] `JWT_SECRET` definido e estável — usado no login **e** em `signMedia/verifyMedia` (URLs assinadas das **fotos de procedimento** e dos **comprovantes de repasse**).
- [ ] **Volume de mídia** (`MEDIA_PATH``MEDIA_ROOT`, default `/app/media`) montado e persistente: grava em `…/procedimentos/<id>/`, `…/repasses/<id>/`.
- [ ] **Cron** `processarVigenciasContratos` (boot + 6h): marca **Atrasado**, vigência de contratos, **renovação automática** e **cobrança recorrente** — conferir nos logs após subir.
## 4. Backend — env / infra (VPS1)
- [ ] `DATABASE_URL` `…@10.99.0.3/scoreodonto`. `JWT_SECRET` estável (login + URLs assinadas de fotos/comprovantes).
- [ ] **Volume de mídia** persistente (`MEDIA_PATH`): grava em `…/procedimentos/<id>/` e `…/repasses/<id>/`.
- [ ] **Cron** `processarVigenciasContratos` (boot + 6h): Atrasado, vigência, **renovação automática**, **cobrança recorrente** — conferir nos logs.
## 4. Frontend — build
- [ ] `frontend/dist` recompilado e despachado. Views/arquivos novos/alterados:
- Novas views: `ComissoesView`, `GlosasView`, `ProcedimentosProntuario`, `SalaHomeView`.
- Alteradas: `FinanceiroView` (Receita/Despesa + baixa), `ReportsView` (relatório financeiro + convênio + glosa), `OrcamentoModal`, `ContratosView` (cobrança recorrente), `LancarGTO` (persiste + convênio), `SalasPlugin` (2 modos + receita locação + refresh workspaces), `OnboardingChat` (Dono de Sala), `Sidebar` (seletor de workspace agrupado + menus de sala), `App.tsx` (rotas/gates), `PluginsView` (locação "sempre incluído"), `pluginRegistry` (ALWAYS_ON), `services/backend.ts` (vários métodos novos).
- [ ] Conferir que **MINHAS SALAS / ALUGAR SALAS** aparecem (locação sempre ativa) e o **seletor de workspace** lista Clínicas × Salas.
## 5. Frontend
- [ ] Imagem rebuildada com as views novas (`ComissoesView`, `GlosasView`, `ProcedimentosProntuario`, `SalaHomeView`) e alterações (`FinanceiroView`, `ReportsView`, `OrcamentoModal`, `ContratosView`, `LancarGTO`, `SalasPlugin`, `OnboardingChat`, `Sidebar`, `App.tsx`, `PluginsView`, `pluginRegistry`, `services/backend.ts`).
- [ ] Conferir: locação **sempre ativa** (MINHAS/ALUGAR SALAS aparecem) e **seletor de workspace** lista Clínicas × Salas.
## 5. Smoke tests em PRODUÇÃO (pós-deploy)
- [ ] Login OK; workspaces carregam (clínicas + salas).
- [ ] **Financeiro:** criar lançamento Receita e Despesa (valida `normalizeFinanceiro` — sem ela NENHUM lançamento salvava pela UI). Relatório financeiro abre.
- [ ] **GTO:** Lançar GTO com convênio → finalizar → recebível aparece no financeiro.
- [ ] **Comissões:** definir % (padrão/procedimento/override) → relatório → fechar repasse → marcar pago (gera despesa) → anexar comprovante.
- [ ] **Glosas:** marcar glosa (item de GTO) → recorrer → recuperar.
- [ ] **Contratos:** gerar com "Atendimento a Convênios"; contrato vigente com cobrança recorrente → "Gerar cobrança do mês".
- [ ] **Orçamento:** assinar → parcelas no financeiro.
- [ ] **Salas:** cadastro **Dono de Sala**; criar sala; **seletor de workspace** troca p/ a sala; **Painel da Sala** (KPIs, agenda, recebíveis, relatório); confirmar reserva → recebível; dar baixa.
- [ ] **Isolamento:** em workspace = sala, telas clínicas somem; cross-tenant retorna 403.
## 6. Verificação pós-deploy (VPS1)
```bash
# versão no ar
curl -s https://scoreodonto.com | grep -o 'V[0-9]*\.[0-9]*\.[0-9]*' | head -1
# logs do backend (Docker ROOTLESS na VPS1) — espere "[MIGRATION] all migrations OK"
ssh deploy@10.99.0.1 "DOCKER_HOST=unix:///run/user/1000/docker.sock docker logs scoreodontocom-scoreodonto-backend-1 --tail 40"
```
Smoke tests: criar **lançamento financeiro** (valida `normalizeFinanceiro` — antes nenhum salvava pela UI); **GTO → recebível**; **comissão/repasse/comprovante**; **glosa**; **contrato de convênio**; **orçamento→parcelas**; **fluxo de salas** (Dono de Sala, reserva→recebível, painel); **isolamento cross-tenant 403**.
## 6. Rollback
- As migrações **só adicionam** → rollback de **código** é seguro (colunas/tabelas extras ficam inertes). Reverter para a imagem/artefato anterior na VPS1 e, se necessário, restaurar o dump do §1.
## 7. Rollback
- Migrações **só adicionam** → reverter o **código** (imagem anterior na VPS1) é seguro; colunas/tabelas extras ficam inertes. Restaurar o dump da §2 só se necessário (`pg_restore -d "<url>" --clean <arquivo>`).
## 7. Riscos específicos desta entrega
- **`normalizeFinanceiro` (crítico):** corrige o bug em que `enforceUppercase` mandava `PAGO`/`PIX` e violava os CHECK capitalizados (`Pago`/`Pix`) — antes a tabela `financeiro` ficava sempre vazia. Garantir que entrou no build de prod.
- **Banco compartilhado dev/prod (se confirmado no §0):** dados de teste do desenvolvimento podem aparecer; os E2E desta rodada sempre limparam, mas conferir contagens de `financeiro`, `salas`, `repasses`, `glosas` antes de liberar.
- **Convênios:** o modelo de contrato e os textos jurídicos seguem padrão de mercado — recomendar revisão por advogado.
- **Locação sempre ativa:** todos os papéis (menos superadmin) passam a ver MINHAS/ALUGAR SALAS — confirmar que é o comportamento desejado em produção.
## 8. Riscos específicos desta entrega
- **`normalizeFinanceiro` (crítico):** corrige o bug em que `enforceUppercase` mandava `PAGO`/`PIX` violando o CHECK capitalizado — antes a tabela `financeiro` ficava vazia. Garantir que entrou na imagem.
- **Banco único dev/prod:** dado de teste de desenvolvimento pode aparecer; os E2E desta rodada sempre limparam, mas conferir contagens de `financeiro`, `salas`, `repasses`, `glosas`.
- **Contratos de convênio:** textos seguem padrão de mercado — recomendar revisão por advogado.
- **Locação sempre ativa:** todos os papéis (menos superadmin) passam a ver MINHAS/ALUGAR SALAS.
-52
View File
@@ -1,52 +0,0 @@
server {
listen 80;
server_name _;
# Limite de upload para mídias do WhatsApp
client_max_body_size 100M;
# Resolver DNS interno do Docker para resolução dinâmica
resolver 127.0.0.11 valid=30s;
# Proxy para o Backend (Express API)
location /api/ {
set $backend_upstream http://scoreodonto-backend-staging:8018;
rewrite ^/api/api/(.*)$ /api/$1 break;
proxy_pass $backend_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Proxy para o Socket.io (Realtime Events)
location /socket.io/ {
set $backend_upstream http://scoreodonto-backend-staging:8018;
proxy_pass $backend_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Proxy para o Frontend (Vite React SPA)
location / {
set $frontend_upstream http://scoreodonto-frontend-staging:3013;
proxy_pass $frontend_upstream;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}