feat: add version display to frontend and /api/version endpoint (v1.2.5)
All checks were successful
Release / build-and-push (push) Successful in 40s
CI / lint-and-test (push) Successful in 22s

- Add GET /api/version endpoint that reads VERSION file
- Frontend fetches version on init and displays it as a badge in the header
- Add version-badge CSS styling
- Update docker-compose.yml comment to v1.2.5
This commit is contained in:
2026-04-20 17:09:02 +02:00
parent a255be93fe
commit 0c3e5ec57b
5 changed files with 35 additions and 3 deletions

View File

@@ -1 +1 @@
1.2.2 1.2.5

View File

@@ -12,7 +12,7 @@
<div class="page" x-data="aocApp()" x-init="initApp()"> <div class="page" x-data="aocApp()" x-init="initApp()">
<header class="hero"> <header class="hero">
<div> <div>
<p class="eyebrow">Admin Operations Center</p> <p class="eyebrow">Admin Operations Center <span class="version-badge" x-text="appVersion"></span></p>
<h1>Directory Audit Explorer</h1> <h1>Directory Audit Explorer</h1>
<p class="lede">Filter Microsoft Entra audit events by user, app, time, action, and action type.</p> <p class="lede">Filter Microsoft Entra audit events by user, app, time, action, and action type.</p>
</div> </div>
@@ -243,6 +243,7 @@
actor: '', selectedServices: [], search: '', operation: '', result: '', start: '', end: '', limit: 100, includeTags: '', excludeTags: '', actor: '', selectedServices: [], search: '', operation: '', result: '', start: '', end: '', limit: 100, includeTags: '', excludeTags: '',
}, },
options: { actors: [], services: [], operations: [], results: [] }, options: { actors: [], services: [], operations: [], results: [] },
appVersion: '',
askQuestionText: '', askQuestionText: '',
askLoading: false, askLoading: false,
askAnswer: '', askAnswer: '',
@@ -252,6 +253,7 @@
askLlmError: '', askLlmError: '',
async initApp() { async initApp() {
await this.loadVersion();
await this.initAuth(); await this.initAuth();
if (!this.authConfig?.auth_enabled || this.accessToken) { if (!this.authConfig?.auth_enabled || this.accessToken) {
await this.loadFilterOptions(); await this.loadFilterOptions();
@@ -260,6 +262,16 @@
} }
}, },
async loadVersion() {
try {
const res = await fetch('/api/version');
if (res.ok) {
const body = await res.json();
this.appVersion = body.version || '';
}
} catch {}
},
authHeader() { authHeader() {
return this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}; return this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {};
}, },

View File

@@ -433,6 +433,20 @@ input {
color: var(--muted); color: var(--muted);
} }
.version-badge {
display: inline-block;
margin-left: 8px;
padding: 2px 8px;
border-radius: 999px;
background: rgba(125, 211, 252, 0.15);
border: 1px solid rgba(125, 211, 252, 0.3);
color: var(--accent-strong);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.05em;
vertical-align: middle;
}
.ask-events { .ask-events {
margin-bottom: 14px; margin-bottom: 14px;
} }

View File

@@ -134,6 +134,12 @@ async def metrics():
return Response(content=prometheus_metrics(), media_type="text/plain") return Response(content=prometheus_metrics(), media_type="text/plain")
@app.get("/api/version")
async def version():
version_file = Path(__file__).parent.parent / "VERSION"
return {"version": version_file.read_text().strip() if version_file.exists() else "unknown"}
frontend_dir = Path(__file__).parent / "frontend" frontend_dir = Path(__file__).parent / "frontend"
app.mount("/", StaticFiles(directory=frontend_dir, html=True), name="frontend") app.mount("/", StaticFiles(directory=frontend_dir, html=True), name="frontend")

View File

@@ -14,7 +14,7 @@ services:
backend: backend:
build: ./backend build: ./backend
# For production, use the pre-built image instead: # For production, use the pre-built image instead:
# image: git.cqre.net/cqrenet/aoc-backend:v1.1.0 # image: git.cqre.net/cqrenet/aoc-backend:v1.2.5
container_name: aoc-backend container_name: aoc-backend
restart: always restart: always
env_file: env_file: