feat: versionamento visível (rodapé + /api/version + carimbo no build)

- backend: endpoint GET /api/version + linha [VERSION] no boot (le APP_VERSION/GIT_COMMIT/BUILD_TIME/APP_ENV)
- frontend: buildInfo.ts le import.meta.env.VITE_* ; versão no rodapé do Sidebar
- Dockerfiles (front+back): ARG/ENV do carimbo de versão
- docker-compose.yml: build args (APP_VERSION/GIT_COMMIT/BUILD_TIME/APP_ENV) com defaults
- bump V1.0.12 -> V1.0.13

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-06-16 06:46:01 +02:00
parent 37cc818f4c
commit adf96002b6
7 changed files with 64 additions and 1 deletions
+7
View File
@@ -4,6 +4,13 @@ WORKDIR /app/frontend
COPY frontend/package*.json .
RUN npm install
COPY frontend/ .
# Carimbo de versão visível (lido por buildInfo.ts via import.meta.env.VITE_*)
ARG GIT_COMMIT=dev
ARG BUILD_TIME=
ARG APP_ENV=DEV
ENV VITE_GIT_COMMIT=$GIT_COMMIT
ENV VITE_BUILD_TIME=$BUILD_TIME
ENV VITE_APP_ENV=$APP_ENV
RUN npm run build
# Stage 2: Serve
+17
View File
@@ -0,0 +1,17 @@
import { APP_VERSION } from './constants.ts';
// 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.
const meta = (import.meta as any).env || {};
export const BUILD_INFO = {
name: 'ScoreOdonto',
version: APP_VERSION,
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"
export const versionLabel = `${BUILD_INFO.version} · ${BUILD_INFO.commit} · ${BUILD_INFO.environment}`;
+10
View File
@@ -7,6 +7,7 @@ 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;
@@ -283,6 +284,15 @@ export const Sidebar: React.FC<SidebarProps> = ({ activeTab, setActiveTab }) =>
<LogOut size={16} />
SAIR DO SISTEMA
</button>
<div
className="px-4 pt-1 text-[8px] leading-tight text-gray-300 font-mono select-text"
title={`Build: ${BUILD_INFO.build || 'n/a'}`}
>
<span className="font-bold text-gray-400">{BUILD_INFO.version}</span>
{' · '}{BUILD_INFO.commit}
{' · '}<span className={BUILD_INFO.environment === 'PRODUÇÃO' || BUILD_INFO.environment === 'PROD' ? 'text-emerald-400' : 'text-amber-400'}>{BUILD_INFO.environment}</span>
</div>
</div>
</div>
);
+1 -1
View File
@@ -1 +1 @@
export const APP_VERSION = 'V1.0.12';
export const APP_VERSION = 'V1.0.13';