diff --git a/cmd/artex/main.go b/cmd/artex/main.go index a2b6611..30440d4 100644 --- a/cmd/artex/main.go +++ b/cmd/artex/main.go @@ -48,6 +48,10 @@ func main() { ) flag.Parse() + // hand the build version to the server package so GET /api/health can report it + // to the frontend top bar. + server.BuildVersion = version + printBanner(*addr) // capture backend logs into the in-memory sink (still to stderr) so the /logs diff --git a/server/server.go b/server/server.go index c89a5cd..2fff186 100644 --- a/server/server.go +++ b/server/server.go @@ -27,6 +27,11 @@ import ( "github.com/Autumn-27/norma/transcript" ) +// BuildVersion is the backend application version, injected from cmd/artex at +// startup (which in turn gets it from -ldflags "-X main.version="). +// Defaults to "dev" for local builds. Exposed to the frontend via GET /api/health. +var BuildVersion = "dev" + // Server exposes the ARTEX backend over a JSON HTTP API for the shadcn/ui // frontend. type Server struct { @@ -741,7 +746,7 @@ func (s *Server) Handler() http.Handler { // --- handlers --- func (s *Server) health(w http.ResponseWriter, r *http.Request) { - writeJSON(w, 200, map[string]any{"ok": true, "service": "artex"}) + writeJSON(w, 200, map[string]any{"ok": true, "service": "artex", "version": BuildVersion}) } func (s *Server) stats(w http.ResponseWriter, r *http.Request) { diff --git a/web/src/app/(main)/_components/main-content.tsx b/web/src/app/(main)/_components/main-content.tsx index 7524969..8c08bab 100644 --- a/web/src/app/(main)/_components/main-content.tsx +++ b/web/src/app/(main)/_components/main-content.tsx @@ -1,11 +1,12 @@ "use client"; -import type { ReactNode } from "react"; +import { type ReactNode, useEffect, useState } from "react"; import { usePathname } from "next/navigation"; import { Separator } from "@/components/ui/separator"; import { SidebarTrigger } from "@/components/ui/sidebar"; +import { api } from "@/lib/api"; import { cn } from "@/lib/utils"; import { useCurrentUser } from "@/hooks/use-current-user"; @@ -29,6 +30,14 @@ function isFullBleed(pathname: string) { export function MainContent({ children }: { children: ReactNode }) { const currentUser = useCurrentUser(); const pathname = usePathname(); + const [version, setVersion] = useState(""); + + useEffect(() => { + api + .health() + .then((h) => setVersion(h.version ?? "")) + .catch(() => setVersion("")); + }, []); if (isFullBleed(pathname)) { return <>{children}; @@ -52,6 +61,9 @@ export function MainContent({ children }: { children: ReactNode }) {
+ {version && ( + 版本·v{version} + )} diff --git a/web/src/lib/api.ts b/web/src/lib/api.ts index c67cb45..92f04ca 100644 --- a/web/src/lib/api.ts +++ b/web/src/lib/api.ts @@ -99,6 +99,9 @@ const arr = (x: T[] | null | undefined): T[] => x ?? []; const tq = (task?: string, sep: "?" | "&" = "?") => (task ? `${sep}task=${encodeURIComponent(task)}` : ""); export const api = { + // 后端应用版本号(release 时由 ldflags 注入,默认 "dev")。 + health: () => get<{ ok: boolean; service: string; version: string }>("/health"), + // ---- auth ---- authStatus: () => get<{ initialized: boolean }>("/auth/status"), login: (username: string, password: string) =>