Add version display in admin sidebar footer
- Add /api/version endpoint that returns build info from env vars - Add version footer to Layout.tsx showing build version, git SHA, and image tag - Update Dockerfile to accept build args for version info (APP_BUILD_VERSION, APP_GIT_SHA, APP_BUILD_TIME, CONTAINER_IMAGE_TAG) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,18 @@ RUN npm run build
|
|||||||
# Production stage
|
# Production stage
|
||||||
FROM node:20-slim
|
FROM node:20-slim
|
||||||
|
|
||||||
|
# Build arguments for version info
|
||||||
|
ARG APP_BUILD_VERSION=dev
|
||||||
|
ARG APP_GIT_SHA=unknown
|
||||||
|
ARG APP_BUILD_TIME=unknown
|
||||||
|
ARG CONTAINER_IMAGE_TAG=local
|
||||||
|
|
||||||
|
# Set version info as environment variables
|
||||||
|
ENV APP_BUILD_VERSION=${APP_BUILD_VERSION}
|
||||||
|
ENV APP_GIT_SHA=${APP_GIT_SHA}
|
||||||
|
ENV APP_BUILD_TIME=${APP_BUILD_TIME}
|
||||||
|
ENV CONTAINER_IMAGE_TAG=${CONTAINER_IMAGE_TAG}
|
||||||
|
|
||||||
# Install Chromium dependencies
|
# Install Chromium dependencies
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
chromium \
|
chromium \
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import apiPermissionsRoutes from './routes/api-permissions';
|
|||||||
import parallelScrapeRoutes from './routes/parallel-scrape';
|
import parallelScrapeRoutes from './routes/parallel-scrape';
|
||||||
import scheduleRoutes from './routes/schedule';
|
import scheduleRoutes from './routes/schedule';
|
||||||
import crawlerSandboxRoutes from './routes/crawler-sandbox';
|
import crawlerSandboxRoutes from './routes/crawler-sandbox';
|
||||||
|
import versionRoutes from './routes/version';
|
||||||
import { trackApiUsage, checkRateLimit } from './middleware/apiTokenTracker';
|
import { trackApiUsage, checkRateLimit } from './middleware/apiTokenTracker';
|
||||||
import { startCrawlScheduler } from './services/crawl-scheduler';
|
import { startCrawlScheduler } from './services/crawl-scheduler';
|
||||||
import { validateWordPressPermissions } from './middleware/wordpressPermissions';
|
import { validateWordPressPermissions } from './middleware/wordpressPermissions';
|
||||||
@@ -80,6 +81,7 @@ app.use('/api/api-permissions', apiPermissionsRoutes);
|
|||||||
app.use('/api/parallel-scrape', parallelScrapeRoutes);
|
app.use('/api/parallel-scrape', parallelScrapeRoutes);
|
||||||
app.use('/api/schedule', scheduleRoutes);
|
app.use('/api/schedule', scheduleRoutes);
|
||||||
app.use('/api/crawler-sandbox', crawlerSandboxRoutes);
|
app.use('/api/crawler-sandbox', crawlerSandboxRoutes);
|
||||||
|
app.use('/api/version', versionRoutes);
|
||||||
|
|
||||||
async function startServer() {
|
async function startServer() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
25
backend/src/routes/version.ts
Normal file
25
backend/src/routes/version.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { Router, Request, Response } from 'express';
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /api/version
|
||||||
|
* Returns build version information for display in admin UI
|
||||||
|
*/
|
||||||
|
router.get('/', async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const versionInfo = {
|
||||||
|
build_version: process.env.APP_BUILD_VERSION || 'dev',
|
||||||
|
git_sha: process.env.APP_GIT_SHA || 'local',
|
||||||
|
build_time: process.env.APP_BUILD_TIME || new Date().toISOString(),
|
||||||
|
image_tag: process.env.CONTAINER_IMAGE_TAG || 'local',
|
||||||
|
};
|
||||||
|
|
||||||
|
res.json(versionInfo);
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('Error fetching version info:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to fetch version info' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode, useEffect, useState } from 'react';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
|
import { api } from '../lib/api';
|
||||||
import {
|
import {
|
||||||
LayoutDashboard,
|
LayoutDashboard,
|
||||||
Store,
|
Store,
|
||||||
@@ -24,6 +25,13 @@ interface LayoutProps {
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface VersionInfo {
|
||||||
|
build_version: string;
|
||||||
|
git_sha: string;
|
||||||
|
build_time: string;
|
||||||
|
image_tag: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface NavLinkProps {
|
interface NavLinkProps {
|
||||||
to: string;
|
to: string;
|
||||||
icon: ReactNode;
|
icon: ReactNode;
|
||||||
@@ -69,6 +77,19 @@ export function Layout({ children }: LayoutProps) {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { user, logout } = useAuthStore();
|
const { user, logout } = useAuthStore();
|
||||||
|
const [versionInfo, setVersionInfo] = useState<VersionInfo | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchVersion = async () => {
|
||||||
|
try {
|
||||||
|
const response = await api.get('/version');
|
||||||
|
setVersionInfo(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch version info:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchVersion();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
logout();
|
logout();
|
||||||
@@ -206,6 +227,18 @@ export function Layout({ children }: LayoutProps) {
|
|||||||
<span>Logout</span>
|
<span>Logout</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Version Footer */}
|
||||||
|
{versionInfo && (
|
||||||
|
<div className="px-3 py-2 border-t border-gray-200 bg-gray-50">
|
||||||
|
<p className="text-xs text-gray-500 text-center">
|
||||||
|
{versionInfo.build_version} ({versionInfo.git_sha.slice(0, 7)})
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-gray-400 text-center mt-0.5">
|
||||||
|
{versionInfo.image_tag}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
|
|||||||
Reference in New Issue
Block a user