From a991f1fe2c4162a647841d48a9e3033884e6107f Mon Sep 17 00:00:00 2001 From: VPS 4 Builder Date: Wed, 17 Jun 2026 05:19:27 +0200 Subject: [PATCH] =?UTF-8?q?feat(version):=20vers=C3=A3o=20em=20runtime=20v?= =?UTF-8?q?ia=20/api/version=20(Passos=201/3/4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - buildInfo.ts: hook useAppVersion() consome GET /api/version (fonte única = backend), com fallback ao carimbo do bundle; deixa de depender do APP_VERSION manual. - Versão exibida em Configurações ("Sobre o sistema") + telas públicas (Login/WaitingInvite); removida do rodapé do Sidebar. - compose.prod/deploy-prod.sh: injetam APP_VERSION (=tag) e APP_ENV=PROD em runtime, desacoplando a versão semântica do build (commit/build seguem da imagem). - constants.ts marcado legado + bump V1.0.15 (transitório até o Passo 5; evita colisão de tag). - DEPLOY-PRODUCAO.md reescrito com status ✅/🧪/🎯 + nota de fase transitória. Co-Authored-By: Claude Opus 4.8 --- deploy-prod.sh | 3 +- docker-compose.prod.yml | 19 ++- documentação/DEPLOY-PRODUCAO.md | 242 ++++++++++++--------------- frontend/buildInfo.ts | 30 +++- frontend/components/Sidebar.tsx | 10 -- frontend/constants.ts | 6 +- frontend/views/ConfiguracoesView.tsx | 29 +++- frontend/views/LoginView.tsx | 7 +- frontend/views/WaitingInviteView.tsx | 5 +- 9 files changed, 187 insertions(+), 164 deletions(-) diff --git a/deploy-prod.sh b/deploy-prod.sh index 8bcdc87..4358591 100755 --- a/deploy-prod.sh +++ b/deploy-prod.sh @@ -24,8 +24,9 @@ FILES="-f docker-compose.yml -f docker-compose.prod.yml" echo "──────────────────────────────────────────────────────────────" echo " Deploy ScoreOdonto → PRODUÇÃO tag: $TAG" +echo " Versão exibida (/api/version) será injetada em runtime = $TAG (commit/build vêm da imagem)" echo "──────────────────────────────────────────────────────────────" -echo "⚠️ Recomendado: backup do banco antes (dev/prod compartilham o banco):" +echo "⚠️ Recomendado: backup do banco de PRODUÇÃO antes (migrações rodam no boot do backend):" echo " ssh deploy@10.99.0.3 'pg_dump -Fc scoreodonto -f ~/backups/scoreodonto_\$(date +%Y%m%d_%H%M).dump'" echo diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 405b020..42a2ad0 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,14 +1,25 @@ # Override de PRODUÇÃO (VPS1): roda IMAGENS VERSIONADAS do registry em vez de buildar. -# A produção não builda — só consome a imagem pronta publicada pela CI (Etapa 5). +# A produção não builda — só consome a imagem pronta publicada pela CI. # -# Uso (na VPS1), via deploy-prod.sh , que faz: -# SCOREODONTO_TAG=v1.0.13 docker compose -f docker-compose.yml -f docker-compose.prod.yml pull ... +# Uso (na VPS1), via deploy-prod.sh , que exporta SCOREODONTO_TAG e faz: +# docker compose -f docker-compose.yml -f docker-compose.prod.yml pull ... # docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-build ... -# # O --no-build garante que o build: do compose base seja ignorado (usa a imagem puxada). +# +# VERSÃO EM RUNTIME (Passo 3): a versão semântica EXIBIDA (/api/version → footer/Configurações) +# vem da TAG promovida, injetada via `environment` abaixo — NÃO do build. `commit` e `build` +# continuam vindo do ENV carimbado na imagem (imutáveis, propriedade do artefato). Assim a +# MESMA imagem pode ser promovida sob qualquer tag sem rebuild — pré-requisito do +# "build once, promote the same artifact". O frontend não precisa de versão aqui: é estático +# e lê tudo de /api/version (backend) em runtime. services: scoreodonto-backend: image: git.clube67.com/ruicesar/scoreodonto-backend:${SCOREODONTO_TAG:-latest} + # Mescla com o environment do compose base (por chave). Sobrescreve o APP_VERSION/APP_ENV + # que vieram embutidos na imagem pelo build, fixando a versão = tag e o ambiente = PROD. + environment: + - APP_VERSION=${SCOREODONTO_TAG:-dev} + - APP_ENV=PROD scoreodonto-frontend: image: git.clube67.com/ruicesar/scoreodonto-frontend:${SCOREODONTO_TAG:-latest} diff --git a/documentação/DEPLOY-PRODUCAO.md b/documentação/DEPLOY-PRODUCAO.md index 7aea8ac..58ea1fc 100644 --- a/documentação/DEPLOY-PRODUCAO.md +++ b/documentação/DEPLOY-PRODUCAO.md @@ -1,170 +1,142 @@ -# DEPLOY-PRODUCAO — Runbook operacional +# Deploy & Versionamento — Runbook ScoreOdonto -> ✅ **FLUXO CANÔNICO HOJE: deploy por TAG (CI/CD).** Para subir uma versão à produção: -> ``` -> # na VPS4, com a versão validada em dev: -> node update-version.js && git add -A && git commit -m "..." && git push origin main -> git tag v1.0.XX && git push origin v1.0.XX # ← a TAG dispara build + deploy automático na VPS1 -> ``` -> A CI builda a imagem versionada, publica no registry e promove para a VPS1 (`deploy-prod.sh`). Confirme em `https://scoreodonto.com/api/version`. -> **Rollback:** na VPS1, `./deploy-prod.sh `. Detalhes em `deploy.md` e `REGISTRY.md`. +> Auditado contra o sistema em **17/jun/2026**. Cada item carrega seu **status real** — o doc +> responde *"o que existe hoje?"*, não *"o que gostaríamos?"*. > -> ⬇️ O passo a passo manual abaixo continua válido como **fallback** (deploy sem a CI). - ---- - -> 🛑 Procedimento manual (fallback), passo a passo, sem adivinhação. -> Reflete o mecanismo de build local. A realidade da infra está em `arquitetura.md`; o fluxo por tag em `deploy.md`. +> **Legenda de status (convenção do projeto):** +> - ✅ **EM PRODUÇÃO** — está no ar em `scoreodonto.com` (VPS1), verificável agora. +> - 🧪 **EM DEV** — implementado no working tree da VPS4 / `dev.scoreodonto.com`; **não commitado, não promovido**. +> - 🎯 **ALVO** — desenhado/aprovado, **ainda não implementado**. > -> ⚠️ **Regra de ouro:** antes de qualquer build/deploy/push, confirme com o operador **DEV ou PRODUÇÃO**. -> `dev.scoreodonto.com` = VPS4 (descartável, mas **mesmo banco** da prod). `scoreodonto.com` = VPS1 (real). -> **Nunca** `git push --force` na `main`. **Dev e prod compartilham o banco** (VPS3) — o backup é a única rede de segurança. +> **Produção AGORA:** `v1.0.14 · commit 081985c · 2026-06-16` (`curl https://scoreodonto.com/api/version`). +> Todo o trabalho de versionamento abaixo marcado 🧪 está no working tree da VPS4 (não commitado). --- -## Mapa rápido (quem é quem) +## 1. O modelo -| Onde | Host | Papel | Socket Docker | -|------|------|-------|----------------| -| VPS4 | `10.99.0.4` | dev + builder (código aqui) | `/var/run/docker.sock` (root) | -| VPS1 | `10.99.0.1` | produção | `unix:///run/user/1000/docker.sock` (rootless) | -| VPS3 | `10.99.0.3` | Postgres + Gitea | — | +> **Push** na `main` produz imagem por **commit** no registry; a **tag `vX.Y.Z`** **promove** essa imagem para produção. **Produção nunca builda — só executa imagens.** -- Repo: `ssh://git@10.99.0.3/ruicesar/scoreodonto.com.git` (Gitea). -- Diretório do repo nas duas máquinas: `/home/deploy/stack/scoreodonto.com`. -- Project Compose: `scoreodontocom` (containers `scoreodontocom-*`). +``` + PUSH main (commit C) TAG vX.Y.Z (no commit C) + │ │ + ▼ ▼ + CI: build CI: deploy + publica :C, :, :latest ✅ hoje: REBUILDA + promove + deploya + ▼ 🎯 alvo (Passo 5): só re-tag :C→:vX.Y.Z, SEM build + registry ▼ + VPS1: pull + up --no-build ✅ + APP_VERSION=tag em runtime 🧪 (Passo 3) +``` --- -## FASE 0 — Pré-voo (na VPS4) +## 2. Estado por componente (verificado) + +| Componente | Status | Detalhe | +|---|---|---| +| Infra VPS1/3/4 + Docker rootless | ✅ | Mapa na §3 | +| Registry (`git.clube67.com/ruicesar/...`) | ✅ | Ver `REGISTRY.md` | +| CI: `act_runner` "vps4-builder" builda+publica em push | ✅ | `.gitea/workflows/build.yml`; runner systemd na VPS4 | +| Deploy por tag (job `deploy` SSH VPS4→VPS1) | ✅ | `deploy-prod.sh` na VPS1 | +| Produção não builda (`image:` + `pull` + `up --no-build`) | ✅ | `docker-compose.prod.yml` | +| Banco DEV isolado em `pgdev`; prod no Postgres da VPS3 | ✅ | Ver `BANCO-DEV-ESTRATEGIA.md` | +| `GET /api/version` responde (de `process.env`) | ✅ | Em prod, valores vêm do **build** da imagem | +| **Versão exibida em Configurações → "Sobre o sistema"** | 🧪 | Passo 1. Em **produção a versão está no Sidebar** | +| **Front/telas consomem `/api/version` (`useAppVersion`); app não usa `APP_VERSION`** | 🧪 | Passos 1/4. Em **prod o front usa `APP_VERSION` do bundle** | +| **Deploy injeta `APP_VERSION`/`APP_ENV` em runtime (`environment`)** | 🧪 | Passo 3 (`compose.prod`/`deploy-prod.sh` no working tree) | +| **Tag promove sem rebuild + validação de integridade** | 🎯 | Passo 5 (§7) | + +--- + +## 3. Mapa de máquinas ✅ + +| Onde | Host | Papel | Docker | +|---|---|---|---| +| VPS1 | `10.99.0.1` | **Produção** (Traefik + apps) | rootless (`unix:///run/user/1000/docker.sock`) | +| VPS3 | `10.99.0.3` | Postgres (prod) + Gitea + registry | — | +| VPS4 | `10.99.0.4` | **Dev + Builder** (código; runner CI; `pgdev`) | rootless | + +- Repo (fonte da verdade): `ssh://git@10.99.0.3/ruicesar/scoreodonto.com.git`. +- Project Compose: `scoreodontocom`. Diretório nas duas máquinas: `/home/deploy/stack/scoreodonto.com`. + +--- + +## 4. De onde vem a versão (contrato `/api/version`) + +```json +{ "name": "ScoreOdonto API", "version": "v1.0.14", "commit": "081985c", + "build": "2026-06-16 08:06 UTC", "environment": "PROD" } +``` +- **commit / build** = carimbados no **build** da imagem (imutáveis — identidade do artefato). ✅ +- **version / environment** — **hoje (✅)** também vêm do build da imagem (build arg no CI). **🧪 (Passo 3):** passam a ser injetados no **deploy** em runtime (a partir da tag), liberando a mesma imagem para qualquer tag sem rebuild. +- **Frontend** — **🧪 (Passos 1/4):** lê tudo de `/api/version` via `useAppVersion()`; deixa de embutir versão. **Em produção** ainda embute `APP_VERSION` no bundle. + +--- + +## 5. Runbook — subir um release (fluxo ATUAL ✅) + +> ⚠️ **Regra de ouro:** confirme **DEV ou PRODUÇÃO** antes de agir. `dev.scoreodonto.com` = VPS4 (banco `pgdev`, descartável). `scoreodonto.com` = VPS1 (real). **Nunca** `git push --force` na `main`. Push/tag exigem autorização explícita. ```bash +# 1) VPS4: validar em DEV (build local do container de dev) cd /home/deploy/stack/scoreodonto.com - -# 1. Sanidade do backend node --check backend/server.js +docker compose build scoreodonto-backend scoreodonto-frontend +docker compose up -d --force-recreate scoreodonto-backend scoreodonto-frontend +# conferir em https://dev.scoreodonto.com -# 2. Validar a mudança em DEV (já no ar em https://dev.scoreodonto.com) -# Conferir o bundle servido (deve mudar a cada rebuild): -curl -s https://dev.scoreodonto.com/ | grep -oE 'index-[A-Za-z0-9_-]+\.js' | head -1 -``` +# 2) ⚠️ transitório (até Passo 5): a TAG precisa casar com frontend/constants.ts +node update-version.js # bump (ex. V1.0.14 → V1.0.15) -```bash -# 3. BACKUP DO BANCO (rede de segurança única — dev/prod compartilham o banco) -# Rodar na VPS3. Usar as credenciais do ambiente/segredo, nunca em texto puro. -ssh deploy@10.99.0.3 'pg_dump -Fc scoreodonto -f /home/deploy/backups/scoreodonto_$(date +%Y%m%d_%H%M).dump' -``` - ---- - -## FASE 1 — Versão (na VPS4) ⚠️ passo manual - -O bump **não é automático** (`npm run build` é só `vite build`). É preciso incrementar **à mão**: - -```bash -cd /home/deploy/stack/scoreodonto.com -node update-version.js # incrementa o patch em frontend/constants.ts (ex.: V1.0.12 → V1.0.13) -grep APP_VERSION frontend/constants.ts -``` - -> Hoje a versão **não fica visível no navegador** (está no bundle JS). Até a Etapa 3 (versionamento visível), anote a versão que está promovendo. - ---- - -## FASE 2 — Commit & push para o Gitea (na VPS4) - -> Só execute com autorização explícita do operador (é o repo de produção). - -```bash -cd /home/deploy/stack/scoreodonto.com -git add -A -git commit -m "feat: (bump vX.Y.Z)" +# 3) commit + push → CI builda e publica : + : + :latest (NÃO deploya) +git add -A && git commit -m "feat: (vX.Y.Z)" git push origin main + +# 4) tag = "aprovado p/ produção" → CI (hoje REBUILDA ✅) promove + deploya na VPS1 +git tag v1.0.15 && git push origin v1.0.15 + +# 5) verificar SEM SSH: +curl -s https://scoreodonto.com/api/version # → version/commit/environment ``` -> ❗ Push **não** dispara deploy automático hoje (não há listener/webhook ativo). A promoção é a FASE 3. +**Backup do banco de PRODUÇÃO antes de releases sensíveis** (migrações rodam no boot; aditivas/idempotentes): +```bash +ssh deploy@10.99.0.3 'pg_dump -Fc scoreodonto -f ~/backups/scoreodonto_$(date +%Y%m%d_%H%M).dump' +``` + +> ### ⚠️ Fase transitória — por que `APP_VERSION`/`constants.ts` ainda existem +> Estamos entre o **modelo antigo** (versão escrita à mão em `constants.ts`) e o **novo** +> (identidade pelo **commit**; tags só promovem). Até o **Passo 5**, o CI ainda lê `APP_VERSION` +> para nomear a imagem → o bump manual continua **necessário para evitar colisão de tags no +> registry** (republicar `:vX.Y.Z` com outro código quebraria *"uma tag = um artefato"*). +> **Após o Passo 5:** `constants.ts` e `update-version.js` são removidos, a identidade passa a +> ser o commit, e tags apenas **promovem** artefatos já construídos. --- -## FASE 3 — Promover para produção (na VPS1) +## 6. Rollback ✅ +Reverter o **código** é seguro (migrações só adicionam). Por imagem, **sem rebuild**: ```bash ssh deploy@10.99.0.1 -export DOCKER_HOST=unix:///run/user/1000/docker.sock # docker rootless da VPS1 -cd /home/deploy/stack/scoreodonto.com - -# 1. Trazer o código aprovado -git pull origin main - -# 2. Carimbo de versão visível (lido pelos build args do compose) — em PROD use APP_ENV=PROD -export APP_VERSION=$(grep -oE 'V[0-9]+\.[0-9]+\.[0-9]+' frontend/constants.ts | head -1) -export GIT_COMMIT=$(git rev-parse --short HEAD) -export BUILD_TIME=$(date -u +'%Y-%m-%d %H:%M UTC') -export APP_ENV=PROD - -# 3. Buildar as imagens (frontend faz vite build; backend npm install) -docker compose build scoreodonto-frontend scoreodonto-backend - -# 4. Subir (troca os containers pelas imagens novas) -docker compose up -d scoreodonto-frontend scoreodonto-backend nginx +/home/deploy/stack/scoreodonto.com/deploy-prod.sh v1.0.13 # tag anterior → pull + up --no-build ``` - -> Os 4 `export` carimbam versão/commit/data/ambiente na imagem. Sem eles, o build ainda funciona mas mostra `dev`/`unknown` no rodapé. Em **DEV** (VPS4) use `APP_ENV=DEV`. - -O backend leva ~10–30s no boot (reconecta Postgres/Dragonfly e roda as migrações idempotentes). +Restaurar o dump do banco só em caso extremo (`pg_restore --clean`). --- -## FASE 4 — Verificação pós-deploy (na VPS1) +## 7. Passo 5 — "tag promove, não builda" 🎯 -```bash -export DOCKER_HOST=unix:///run/user/1000/docker.sock +Desenho **aprovado (17/jun/2026), não aplicado**. Refina o CI existente para Build Once → Promote → Deploy: +- `build.yml` → 2 jobs por ref: **build** (só push; publica `:`+`:latest`, sem `APP_VERSION`); **promote** (só tag; re-tag `:→:vX.Y.Z` via `docker buildx imagetools create`, sem rebuild) → deploy. +- **Validação de integridade antes do deploy:** existe `backend:` e `frontend:`? digests válidos? alias `:vX.Y.Z` == digest de `:`? front e back do **mesmo commit**? Só então deploy (evita deploy parcial). +- Decisões fechadas: manter **`:`** (sem prefixo); **falhar** se o commit não foi pushado (nunca buildar na tag); **`:latest`** só conveniência. +- Eliminar `update-version.js` + `frontend/constants.ts` (a versão passa a nascer da **tag git**). +- Follow-ups: `GET /api/health`; **política de retenção** do registry. -# Containers no ar -docker ps --format '{{.Names}} | {{.Status}}' | grep score - -# Backend: esperar a linha de migração + carimbo de versão -docker logs scoreodontocom-scoreodonto-backend-1 --tail 40 | grep -iE 'migration|VERSION' -# alvo: [MIGRATION] all migrations OK -# [VERSION] ScoreOdonto API vX.Y.Z · commit ... · build ... · PROD - -# Confirmar a versão no ar SEM acessar container (a forma definitiva): -curl -s https://scoreodonto.com/api/version -# {"name":"ScoreOdonto API","version":"vX.Y.Z","commit":"...","build":"...","environment":"PROD"} - -# E visualmente: a versão aparece no rodapé do menu lateral (Sidebar). -``` - -**Smoke tests mínimos:** abrir `https://scoreodonto.com`, logar, e exercitar o que mudou (ex.: lançamento financeiro, agenda, salas). Conferir ausência de erro 500 no `docker logs`. +Racional completo em `deploy.md` e na memória `project-cicd-tag-promove-nao-rebuilda`. --- -## FASE 5 — Rollback (se algo quebrar) - -As migrações são **aditivas** → reverter o **código** é seguro (colunas/tabelas extras ficam inertes). - -```bash -ssh deploy@10.99.0.1 -export DOCKER_HOST=unix:///run/user/1000/docker.sock -cd /home/deploy/stack/scoreodonto.com - -git log --oneline -5 # achar o commit bom anterior -git checkout # ou: git reset --hard -docker compose build scoreodonto-frontend scoreodonto-backend -docker compose up -d scoreodonto-frontend scoreodonto-backend -``` - -> Hoje o rollback **rebuilda** a versão anterior (as imagens são `:latest`, não há tag por versão). Quando a **Etapa 6** entregar deploy por tag + registry, o rollback vira só `docker compose pull` da tag anterior — sem rebuild. Restaurar o dump do banco (FASE 0) só em caso extremo: -> `pg_restore -d "postgresql://scoreodonto_user:@10.99.0.3:5432/scoreodonto" --clean .dump` - ---- - -## Resumo em uma tela - -``` -VPS4: validar em dev → node --check → backup do banco (VPS3) - → node update-version.js → commit → git push origin main (com OK do operador) -VPS1: git pull → docker compose build → docker compose up -d - → verificar logs ([MIGRATION] all migrations OK) + smoke test -Rollback: git checkout → build → up -d -``` +> **Sequência acordada para fechar o drift:** (1) doc honesta com status ✅/🧪/🎯 [este doc] → (2) commitar Passos 1/3/4 → (3) promover → (4) confirmar `/api/version` → (5) elevar `VERDADE-HOJE.md` a doc principal (com esta convenção e precedência) → (6) Passo 5 → (7) atualizar docs. À medida que cada item 🧪 for promovido, vira ✅ aqui. diff --git a/frontend/buildInfo.ts b/frontend/buildInfo.ts index 2c2d949..f730243 100644 --- a/frontend/buildInfo.ts +++ b/frontend/buildInfo.ts @@ -1,17 +1,35 @@ -import { APP_VERSION } from './constants.ts'; +import { useState, useEffect } from 'react'; -// Metadados de build. commit/build/ambiente são carimbados no build via -// variáveis VITE_* (ARGs do Dockerfile do frontend). Em dev sem build args, -// caem nos defaults abaixo. +// Carimbo do BUILD (bundle). É só FALLBACK enquanto o /api/version não responde. +// A FONTE OFICIAL da versão é o backend, em runtime, via useAppVersion(). +// Não depende mais de APP_VERSION manual: version cai em VITE_APP_VERSION (se o +// pipeline injetar) ou 'dev'. commit/build/ambiente seguem carimbados no build (VITE_*). const meta = (import.meta as any).env || {}; export const BUILD_INFO = { name: 'ScoreOdonto', - version: APP_VERSION, + version: meta.VITE_APP_VERSION || 'dev', commit: meta.VITE_GIT_COMMIT || 'dev', build: meta.VITE_BUILD_TIME || '', environment: String(meta.VITE_APP_ENV || 'DEV').toUpperCase(), }; -// Rótulo curto: "V1.0.13 · 7a9b3c1 · DEV" +// Rótulo curto: "v1.0.15 · 7a9b3c1 · DEV" export const versionLabel = `${BUILD_INFO.version} · ${BUILD_INFO.commit} · ${BUILD_INFO.environment}`; + +// Fonte ÚNICA da versão exibida: o backend em runtime (GET /api/version, público). +// Cache em módulo p/ não refazer o fetch a cada tela. Fallback = BUILD_INFO (bundle). +let _verCache: any = null; +export function useAppVersion() { + const [info, setInfo] = useState(_verCache || BUILD_INFO); + useEffect(() => { + if (_verCache) { setInfo(_verCache); return; } + let alive = true; + fetch('/api/version') + .then(r => (r.ok ? r.json() : null)) + .then(v => { if (alive && v && v.version) { _verCache = { ...BUILD_INFO, ...v }; setInfo(_verCache); } }) + .catch(() => {}); + return () => { alive = false; }; + }, []); + return info; +} diff --git a/frontend/components/Sidebar.tsx b/frontend/components/Sidebar.tsx index 616eed7..5b3a284 100644 --- a/frontend/components/Sidebar.tsx +++ b/frontend/components/Sidebar.tsx @@ -7,7 +7,6 @@ import { import { ToothIcon } from './ToothIcon.tsx'; import { HybridBackend } from '../services/backend.ts'; import { getActivePlugins } from '../views/plugins/pluginRegistry.ts'; -import { BUILD_INFO } from '../buildInfo.ts'; interface SidebarProps { activeTab: string; @@ -284,15 +283,6 @@ export const Sidebar: React.FC = ({ activeTab, setActiveTab }) => SAIR DO SISTEMA - -
- {BUILD_INFO.version} - {' · '}{BUILD_INFO.commit} - {' · '}{BUILD_INFO.environment} -
); diff --git a/frontend/constants.ts b/frontend/constants.ts index 5881662..d5ff483 100644 --- a/frontend/constants.ts +++ b/frontend/constants.ts @@ -1 +1,5 @@ -export const APP_VERSION = 'V1.0.14'; +// LEGADO (Passo 4): a APLICAÇÃO não usa mais este valor — a versão exibida vem do backend +// (/api/version) em runtime via useAppVersion(). Permanece apenas porque o PIPELINE ainda o lê +// (update-version.js + .gitea/workflows/build.yml). Será eliminado no Passo 5, quando a versão +// passar a nascer da TAG git. Não referenciar no código do frontend. +export const APP_VERSION = 'V1.0.15'; diff --git a/frontend/views/ConfiguracoesView.tsx b/frontend/views/ConfiguracoesView.tsx index 0834197..c48fef9 100644 --- a/frontend/views/ConfiguracoesView.tsx +++ b/frontend/views/ConfiguracoesView.tsx @@ -5,7 +5,7 @@ import { GoogleConnectButton } from '../components/GoogleConnectButton.tsx'; import { useToast } from '../contexts/ToastContext.tsx'; import { useConfirm } from '../contexts/ConfirmContext.tsx'; import { ChangePasswordModal } from '../components/ChangePasswordModal.tsx'; -import { APP_VERSION } from '../constants.ts'; +import { useAppVersion } from '../buildInfo.ts'; import { buscarCep } from '../services/viacep.ts'; const FIELD_CLS = 'w-full border border-gray-200 rounded-xl px-3 py-2.5 text-sm font-medium text-gray-800 outline-none focus:border-teal-500 focus:ring-2 focus:ring-teal-100 transition-all'; @@ -352,6 +352,8 @@ export const ConfiguracoesView: React.FC = () => { const toast = useToast(); const confirm = useConfirm(); const [showChangePw, setShowChangePw] = useState(false); + // Versão/commit/ambiente vêm do backend em RUNTIME (fonte única). Fallback: carimbo do bundle. + const ver = useAppVersion(); const authHeader = { 'Authorization': `Bearer ${localStorage.getItem('SCOREODONTO_AUTH_TOKEN')}`, 'Content-Type': 'application/json' }; @@ -417,7 +419,7 @@ export const ConfiguracoesView: React.FC = () => {

Gerencie seu perfil e integrações

- {APP_VERSION} + {ver.version} @@ -567,6 +569,29 @@ export const ConfiguracoesView: React.FC = () => { + {/* Sobre o sistema — versão/commit/ambiente vindos do backend em runtime */} +
+
+

Sobre o sistema

+
+
+
+
Versão
+
{ver.version}
+
Commit
+
{ver.commit}
+
Build
+
{ver.build || '—'}
+
Ambiente
+
+ + {ver.environment} + +
+
+
+
+ {showChangePw && setShowChangePw(false)} onDone={() => setShowChangePw(false)} />} ); diff --git a/frontend/views/LoginView.tsx b/frontend/views/LoginView.tsx index 413e896..ef37e9c 100644 --- a/frontend/views/LoginView.tsx +++ b/frontend/views/LoginView.tsx @@ -5,7 +5,7 @@ import { UserSearch, DoorOpen, ScanLine, Sparkles, MapPin, Building2, Users, FlaskConical } from 'lucide-react'; import { HybridBackend } from '../services/backend.ts'; -import { APP_VERSION } from '../constants.ts'; +import { useAppVersion } from '../buildInfo.ts'; import { OnboardingChat } from './OnboardingChat.tsx'; interface LoginViewProps { @@ -57,6 +57,7 @@ const InputCls = 'w-full pl-10 pr-4 py-3.5 bg-gray-50 border border-gray-200 rou // ── Component ───────────────────────────────────────────────────────────────── export const LoginView: React.FC = ({ onLoginSuccess }) => { + const ver = useAppVersion(); // versão oficial via /api/version (público), fallback bundle const [tab, setTab] = useState<'login' | 'cadastro'>('login'); const [authMode, setAuthMode] = useState<'login' | 'forgot' | 'reset'>('login'); @@ -215,7 +216,7 @@ export const LoginView: React.FC = ({ onLoginSuccess }) => { {/* Footer */}
- Dados criptografados · LGPD · {APP_VERSION} + Dados criptografados · LGPD · {ver.version}
@@ -335,7 +336,7 @@ export const LoginView: React.FC = ({ onLoginSuccess }) => {
- Ambiente Seguro · {APP_VERSION} + Ambiente Seguro · {ver.version}
)} diff --git a/frontend/views/WaitingInviteView.tsx b/frontend/views/WaitingInviteView.tsx index 8cd163d..0c11c90 100644 --- a/frontend/views/WaitingInviteView.tsx +++ b/frontend/views/WaitingInviteView.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { Mail, LogOut, Stethoscope, Clock, CheckCircle2, ArrowRight, MapPin, Users, Wrench, ChevronDown, ChevronUp, Loader2 } from 'lucide-react'; import { HybridBackend } from '../services/backend.ts'; -import { APP_VERSION } from '../constants.ts'; +import { useAppVersion } from '../buildInfo.ts'; const UF_LIST = ['AC','AL','AM','AP','BA','CE','DF','ES','GO','MA','MG','MS','MT','PA','PB','PE','PI','PR','RJ','RN','RO','RR','RS','SC','SE','SP','TO']; @@ -13,6 +13,7 @@ const ROLE_CONFIG: Record { + const ver = useAppVersion(); const userData = localStorage.getItem('SCOREODONTO_USER_DATA'); const user = userData ? JSON.parse(userData) : null; const role = user?.role ?? 'dentista'; @@ -229,7 +230,7 @@ export const WaitingInviteView: React.FC = () => {
- {APP_VERSION} + {ver.version}