fix(version): 界面版本号改为动态读取,统一版本为 v1.0.2
界面侧栏与页脚硬编码 v1.0.0,发版后始终显示陈旧版本。 - 新增 useAppVersion hook:从后端 /version 读取并做模块级缓存 - Sidebar / MainLayout 改用该 hook - /agent/arch 的 version 字段原先硬编码 "1.0.0",改由 main 注入 router.AppVersion - Dockerfile 增加 AGENT_VERSION 构建参数,与镜像 VERSION 解耦, 避免纯前端改动也触发全量 Agent 重推 统一版本:镜像 lghost/aiops:v1.0.2,内置 Agent v1.0.2。 // Logan.Li
This commit is contained in:
1 parent
9360ebefd8
commit
7a96381dc7
7 files changed
+79
-6
No files matched your search
+4
-3
@@ -75,6 +75,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
|||||||
FROM golang:1.24-alpine AS agent-builder
|
FROM golang:1.24-alpine AS agent-builder
|
||||||
|
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
|
ARG AGENT_VERSION
|
||||||
ARG BUILD_TIME
|
ARG BUILD_TIME
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
|
||||||
@@ -92,13 +93,13 @@ COPY agent/ ./
|
|||||||
# Go 在 CGO_ENABLED=0 下可原生交叉编译,无需 QEMU,故一次性产出全部目标,
|
# Go 在 CGO_ENABLED=0 下可原生交叉编译,无需 QEMU,故一次性产出全部目标,
|
||||||
# 与 router/agent_static.go 的下载清单保持一致,避免 ARM 主机下载 404。 // Logan.Li
|
# 与 router/agent_static.go 的下载清单保持一致,避免 ARM 主机下载 404。 // Logan.Li
|
||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
|
||||||
-ldflags="-s -w -X main.version=${VERSION} -X main.buildTime=${BUILD_TIME}" \
|
-ldflags="-s -w -X main.version=${AGENT_VERSION} -X main.buildTime=${BUILD_TIME}" \
|
||||||
-o bin/aiops-agent-linux-amd64 .
|
-o bin/aiops-agent-linux-amd64 .
|
||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build \
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build \
|
||||||
-ldflags="-s -w -X main.version=${VERSION} -X main.buildTime=${BUILD_TIME}" \
|
-ldflags="-s -w -X main.version=${AGENT_VERSION} -X main.buildTime=${BUILD_TIME}" \
|
||||||
-o bin/aiops-agent-linux-arm64 .
|
-o bin/aiops-agent-linux-arm64 .
|
||||||
RUN CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build \
|
RUN CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build \
|
||||||
-ldflags="-s -w -X main.version=${VERSION} -X main.buildTime=${BUILD_TIME}" \
|
-ldflags="-s -w -X main.version=${AGENT_VERSION} -X main.buildTime=${BUILD_TIME}" \
|
||||||
-o bin/aiops-agent-windows-amd64.exe .
|
-o bin/aiops-agent-windows-amd64.exe .
|
||||||
|
|
||||||
# ==================== Stage 4: Runtime ====================
|
# ==================== Stage 4: Runtime ====================
|
||||||
|
|||||||
@@ -215,6 +215,8 @@ func main() {
|
|||||||
systask.Init(db)
|
systask.Init(db)
|
||||||
|
|
||||||
// 注册路由
|
// 注册路由
|
||||||
|
// 把编译期注入的版本号透传给 router,供 /agent/arch 返回真实版本。 // Logan.Li
|
||||||
|
router.AppVersion = Version
|
||||||
router.Register(r, db, log, cfg, cache)
|
router.Register(r, db, log, cfg, cache)
|
||||||
|
|
||||||
// 用户定时任务调度(Linux 5 段 crontab)// Logan.Li
|
// 用户定时任务调度(Linux 5 段 crontab)// Logan.Li
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ var agentBinaryFiles = map[string]string{
|
|||||||
"windows-amd64": "aiops-agent-windows-amd64.exe",
|
"windows-amd64": "aiops-agent-windows-amd64.exe",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AppVersion 由 main 在启动时注入,用于 /agent/arch 返回真实版本号;
|
||||||
|
// 此前该字段硬编码为 "1.0.0",发版后接口版本与实际不符。 // Logan.Li
|
||||||
|
var AppVersion = "dev"
|
||||||
|
|
||||||
// registerAgentDownloads 注册 Agent 安装脚本和二进制下载路由,避免维护 backend/agent 重复目录。 // Logan.Li
|
// registerAgentDownloads 注册 Agent 安装脚本和二进制下载路由,避免维护 backend/agent 重复目录。 // Logan.Li
|
||||||
func registerAgentDownloads(r *gin.Engine, log *zap.Logger) {
|
func registerAgentDownloads(r *gin.Engine, log *zap.Logger) {
|
||||||
paths := resolveAgentStaticPaths(log)
|
paths := resolveAgentStaticPaths(log)
|
||||||
@@ -34,7 +38,7 @@ func registerAgentDownloads(r *gin.Engine, log *zap.Logger) {
|
|||||||
}
|
}
|
||||||
downloads := availableAgentDownloads(paths.binDir)
|
downloads := availableAgentDownloads(paths.binDir)
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"version": "1.0.0",
|
"version": AppVersion,
|
||||||
"downloads": downloads,
|
"downloads": downloads,
|
||||||
"install": fmt.Sprintf("curl -fsSL %s/agent/install.sh | sudo bash -s -- --server %s --token <token>", base, base),
|
"install": fmt.Sprintf("curl -fsSL %s/agent/install.sh | sudo bash -s -- --server %s --token <token>", base, base),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ services:
|
|||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
args:
|
args:
|
||||||
VERSION: ${VERSION:-dev}
|
VERSION: ${VERSION:-dev}
|
||||||
|
# AGENT_VERSION 与镜像版本解耦:纯后端/前端改动不必让所有主机重推 Agent。 // Logan.Li
|
||||||
|
AGENT_VERSION: ${AGENT_VERSION:-v1.0.2}
|
||||||
BUILD_TIME: ${BUILD_TIME:-unknown}
|
BUILD_TIME: ${BUILD_TIME:-unknown}
|
||||||
GIT_COMMIT: ${GIT_COMMIT:-unknown}
|
GIT_COMMIT: ${GIT_COMMIT:-unknown}
|
||||||
container_name: aiops
|
container_name: aiops
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { authApi } from '@/api';
|
|||||||
import type { JsonData } from '@/types/common';
|
import type { JsonData } from '@/types/common';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { SessionWatermark } from '@/components/common/SessionWatermark';
|
import { SessionWatermark } from '@/components/common/SessionWatermark';
|
||||||
|
import { useAppVersion } from '@/hooks/use-app-version';
|
||||||
import { shouldProtectPage } from '@/components/common/sensitivePageWatermark';
|
import { shouldProtectPage } from '@/components/common/sensitivePageWatermark';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -16,6 +17,8 @@ import { shouldProtectPage } from '@/components/common/sensitivePageWatermark';
|
|||||||
* 侧栏 + 顶栏 + 面包屑 + 内容区,间距由 --layout-* 令牌驱动 // Logan.Li
|
* 侧栏 + 顶栏 + 面包屑 + 内容区,间距由 --layout-* 令牌驱动 // Logan.Li
|
||||||
*/
|
*/
|
||||||
export function MainLayout() {
|
export function MainLayout() {
|
||||||
|
// 页脚版本号同样取自后端 /version。 // Logan.Li
|
||||||
|
const appVersion = useAppVersion();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { sidebarCollapsed } = useThemeStore();
|
const { sidebarCollapsed } = useThemeStore();
|
||||||
const token = useUserStore((s) => s.token);
|
const token = useUserStore((s) => s.token);
|
||||||
@@ -97,7 +100,7 @@ export function MainLayout() {
|
|||||||
<footer className="dg-app-footer">
|
<footer className="dg-app-footer">
|
||||||
<div className="flex items-center justify-between text-xs dg-text-muted">
|
<div className="flex items-center justify-between text-xs dg-text-muted">
|
||||||
<span>{t('login.title')}</span>
|
<span>{t('login.title')}</span>
|
||||||
<span>v1.0.0</span>
|
<span>{appVersion || 'v1.0.0'}</span>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import {
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
|
import { useAppVersion } from '@/hooks/use-app-version';
|
||||||
|
|
||||||
interface MenuItem {
|
interface MenuItem {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -363,6 +364,8 @@ interface SidebarProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
|
export function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
|
||||||
|
// 版本号从后端 /version 读取,避免发版后界面仍显示硬编码的旧版本。 // Logan.Li
|
||||||
|
const appVersion = useAppVersion();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { sidebarCollapsed, toggleSidebar } = useThemeStore();
|
const { sidebarCollapsed, toggleSidebar } = useThemeStore();
|
||||||
const { hasPermission } = usePermission();
|
const { hasPermission } = usePermission();
|
||||||
@@ -433,7 +436,7 @@ export function Sidebar({ mobileOpen, onMobileClose }: SidebarProps) {
|
|||||||
{!isCollapsed && (
|
{!isCollapsed && (
|
||||||
<div className="px-3 py-2 border-t border-[var(--dg-border)] flex-shrink-0">
|
<div className="px-3 py-2 border-t border-[var(--dg-border)] flex-shrink-0">
|
||||||
<div className="text-[10px] text-[var(--dg-text-muted)] text-center">
|
<div className="text-[10px] text-[var(--dg-text-muted)] text-center">
|
||||||
AIOps v1.0.0
|
AIOps {appVersion || 'v1.0.0'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用版本信息。
|
||||||
|
*
|
||||||
|
* 版本号此前在后端 /version 已提供(含 build_time / git_commit),
|
||||||
|
* 但侧边栏与页脚硬编码了 v1.0.0,导致发版后界面显示陈旧。
|
||||||
|
* 这里统一从 /version 读取并做模块级缓存,避免多处重复请求。 // Logan.Li
|
||||||
|
*/
|
||||||
|
export interface AppVersion {
|
||||||
|
version: string;
|
||||||
|
build_time?: string;
|
||||||
|
git_commit?: string;
|
||||||
|
mode?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模块级缓存:整个会话只请求一次。
|
||||||
|
let cached: AppVersion | null = null;
|
||||||
|
let inflight: Promise<AppVersion | null> | null = null;
|
||||||
|
|
||||||
|
function fetchVersion(): Promise<AppVersion | null> {
|
||||||
|
if (cached) return Promise.resolve(cached);
|
||||||
|
if (inflight) return inflight;
|
||||||
|
|
||||||
|
// 注意:/version 挂在根路径,不带 /api/v1 前缀,故不能用统一 request 封装。
|
||||||
|
inflight = fetch('/version')
|
||||||
|
.then((res) => (res.ok ? res.json() : null))
|
||||||
|
.then((data) => {
|
||||||
|
if (data && typeof data.version === 'string') {
|
||||||
|
cached = data as AppVersion;
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.catch(() => null)
|
||||||
|
.finally(() => {
|
||||||
|
inflight = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
return inflight;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 读取后端版本号;失败时返回空串,调用方负责兜底显示。 */
|
||||||
|
export function useAppVersion(): string {
|
||||||
|
const [version, setVersion] = useState<string>(cached?.version ?? '');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let alive = true;
|
||||||
|
fetchVersion().then((v) => {
|
||||||
|
if (alive && v?.version) setVersion(v.version);
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
alive = false;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user