From 64bccdb4b0cf0503a4d0363548a1649166b52bf8 Mon Sep 17 00:00:00 2001 From: Claude VM Date: Mon, 18 May 2026 23:06:35 +0300 Subject: [PATCH] feat(ops): /api/version endpoint with git SHA + build time Adds a public, no-auth endpoint at /api/version that returns: { commit, commitShort, buildTime, nodeEnv, cutover, nextVersion } Build-time injection via GIT_COMMIT + BUILD_TIME ARG/ENV propagated from compose build.args through Dockerfile builder + runner stages. Excluded from middleware auth gating. Deploy command (run on satra after git pull): GIT_COMMIT=$(git rev-parse HEAD) \ BUILD_TIME=$(date -u +%FT%TZ) \ docker compose build architools Without these env vars, falls back to "unknown" so the build never fails; only the endpoint shows reduced info. Useful for: confirming what's actually deployed after CI, cross-app deploy correlation (api.gis.ac, eterra.live, orchestrator), uptime monitors. Co-Authored-By: Claude Opus 4.7 (1M context) --- Dockerfile | 11 +++++++++++ docker-compose.yml | 4 ++++ src/app/api/version/route.ts | 31 +++++++++++++++++++++++++++++++ src/middleware.ts | 2 +- 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/app/api/version/route.ts diff --git a/Dockerfile b/Dockerfile index 1d2aa1a..7871537 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,11 +26,16 @@ ARG NEXT_PUBLIC_APP_NAME=ArchiTools ARG NEXT_PUBLIC_APP_URL=https://tools.beletage.ro ARG NEXT_PUBLIC_MARTIN_URL=https://tools.beletage.ro/tiles ARG NEXT_PUBLIC_PMTILES_URL= +# Version metadata baked at build time → /api/version +ARG GIT_COMMIT=unknown +ARG BUILD_TIME=unknown ENV NEXT_PUBLIC_STORAGE_ADAPTER=${NEXT_PUBLIC_STORAGE_ADAPTER} ENV NEXT_PUBLIC_APP_NAME=${NEXT_PUBLIC_APP_NAME} ENV NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL} ENV NEXT_PUBLIC_MARTIN_URL=${NEXT_PUBLIC_MARTIN_URL} ENV NEXT_PUBLIC_PMTILES_URL=${NEXT_PUBLIC_PMTILES_URL} +ENV GIT_COMMIT=${GIT_COMMIT} +ENV BUILD_TIME=${BUILD_TIME} # Increase memory for Next.js build if VM has limited RAM ENV NODE_OPTIONS="--max-old-space-size=2048" @@ -40,6 +45,12 @@ RUN npm run build FROM node:22-alpine AS runner WORKDIR /app +# Re-declare build-time version args so they propagate into runner ENV +ARG GIT_COMMIT=unknown +ARG BUILD_TIME=unknown +ENV GIT_COMMIT=${GIT_COMMIT} +ENV BUILD_TIME=${BUILD_TIME} + ENV NODE_ENV=production ENV TZ=Europe/Bucharest diff --git a/docker-compose.yml b/docker-compose.yml index 90ce342..ec429fa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,6 +8,10 @@ services: - NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-https://tools.beletage.ro} - NEXT_PUBLIC_MARTIN_URL=${NEXT_PUBLIC_MARTIN_URL} - NEXT_PUBLIC_PMTILES_URL=${NEXT_PUBLIC_PMTILES_URL} + # Inject git SHA + build timestamp; deploy via: + # GIT_COMMIT=$(git rev-parse HEAD) BUILD_TIME=$(date -u +%FT%TZ) docker compose build + - GIT_COMMIT=${GIT_COMMIT:-unknown} + - BUILD_TIME=${BUILD_TIME:-unknown} container_name: architools restart: unless-stopped ports: diff --git a/src/app/api/version/route.ts b/src/app/api/version/route.ts new file mode 100644 index 0000000..20000f9 --- /dev/null +++ b/src/app/api/version/route.ts @@ -0,0 +1,31 @@ +import { NextResponse } from "next/server"; + +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +/** + * Public version endpoint. Returns the git commit + build time baked at + * `docker compose build` (via GIT_COMMIT / BUILD_TIME build args) plus + * a few runtime flags useful for ops + remote verification. + * + * No auth. Excluded from middleware matcher. + * + * Deploy command on satra (after `git pull`): + * GIT_COMMIT=$(git rev-parse HEAD) \ + * BUILD_TIME=$(date -u +%FT%TZ) \ + * docker compose build architools + */ +export async function GET() { + const commit = process.env.GIT_COMMIT || "unknown"; + return NextResponse.json({ + commit, + commitShort: commit.slice(0, 7), + buildTime: process.env.BUILD_TIME || "unknown", + nodeEnv: process.env.NODE_ENV || "unknown", + cutover: { + useGisAcDefault: process.env.USE_GIS_AC === "1", + pilotUsers: (process.env.GIS_AC_PILOT_USERS || "").split(",").filter(Boolean).length, + }, + nextVersion: "16.1.6", + }); +} diff --git a/src/middleware.ts b/src/middleware.ts index 82060bb..51eaaaa 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -58,6 +58,6 @@ export const config = { * - /favicon.ico, /robots.txt, /sitemap.xml * - Files with extensions (images, fonts, etc.) */ - "/((?!api/auth|api/notifications/digest|api/eterra/auto-refresh|api/compress-pdf|api/address-book|api/projects|auth/signin|_next|favicon\\.ico|robots\\.txt|sitemap\\.xml|.*\\..*).*)", + "/((?!api/auth|api/version|api/notifications/digest|api/eterra/auto-refresh|api/compress-pdf|api/address-book|api/projects|auth/signin|_next|favicon\\.ico|robots\\.txt|sitemap\\.xml|.*\\..*).*)", ], };