Add version numbering (v1.0.0)

- package.json: bump to 1.0.0
- server/config.js: export APP_VERSION (from package.json) and GIT_COMMIT
  (from COMMIT_SHA env var set by CI, or git rev-parse --short HEAD fallback)
- GET /api/version: new public endpoint returning { version, commit }
- GET /api/admin-auth/status: includes version and commit in response
- AdminPage sidebar: displays version string (e.g. "v1.0.0 (1d43875)")
  below the Log Out button, styled as muted metadata text

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
nmemmert
2026-07-16 07:55:52 -04:00
parent 1d43875e5a
commit 172e4a9358
6 changed files with 53 additions and 2 deletions
+28
View File
@@ -1,9 +1,37 @@
import path from 'node:path'
import { readFileSync } from 'node:fs'
import { execSync } from 'node:child_process'
import { validateAdminPasswordSetup } from './auth.js'
import { ROOT_DIR, DATA_DIR } from './paths.js'
export { ROOT_DIR, DATA_DIR }
function readPackageVersion() {
try {
const pkg = JSON.parse(readFileSync(path.join(ROOT_DIR, 'package.json'), 'utf8'))
return typeof pkg.version === 'string' && pkg.version.trim() ? pkg.version.trim() : 'unknown'
} catch {
return 'unknown'
}
}
function readGitCommit() {
// CI injects the full SHA as COMMIT_SHA; fall back to running git locally.
if (typeof process.env.COMMIT_SHA === 'string' && process.env.COMMIT_SHA.trim()) {
return process.env.COMMIT_SHA.trim().slice(0, 7)
}
try {
return execSync('git rev-parse --short HEAD', { cwd: ROOT_DIR, timeout: 2000 }).toString().trim()
} catch {
return 'unknown'
}
}
// APP_VERSION is sourced from package.json.
// GIT_COMMIT is the 7-char short SHA, from COMMIT_SHA env var (set by CI) or git at startup.
export const APP_VERSION = readPackageVersion()
export const GIT_COMMIT = readGitCommit()
export const DATA_FILE = path.join(DATA_DIR, 'admin-content.json')
export const DRAFT_DATA_FILE = path.join(DATA_DIR, 'admin-content-draft.json')
export const HIT_STATS_FILE = path.join(DATA_DIR, 'hit-stats.json')
+3
View File
@@ -1,6 +1,7 @@
import rateLimit from 'express-rate-limit'
import qrcode from 'qrcode'
import { parseCookies } from '../helpers.js'
import { APP_VERSION, GIT_COMMIT } from '../config.js'
import {
isAdminPasswordConfigured,
isValidAdminSession,
@@ -40,6 +41,8 @@ export function register(app) {
authenticated: isValidAdminSession(req),
configured: isAdminPasswordConfigured(),
totpEnabled: await isTotpEnabled(),
version: APP_VERSION,
commit: GIT_COMMIT,
})
})
+6
View File
@@ -9,6 +9,8 @@ import {
UPLOADS_DIR,
INDEX_FILE,
DEFAULT_SEO,
APP_VERSION,
GIT_COMMIT,
} from '../config.js'
import { state } from '../state.js'
import { loadSiteContentFile } from '../data.js'
@@ -18,6 +20,10 @@ import { sanitizeRedirectRules } from '../study-helpers.js'
export function register(app) {
const SALVATION_INDEX_FILE = path.join(DIST_DIR, 'salvation', 'index.html')
app.get('/api/version', (_req, res) => {
res.json({ version: APP_VERSION, commit: GIT_COMMIT })
})
app.get('/robots.txt', async (_req, res) => {
let content = state.cachedSiteContent
if (!content) {