feat: 版本管理,package.json 唯一版本源、/api/meta 返回版本、Dashboard Header 展示版本号
This commit is contained in:
@@ -28,6 +28,7 @@ export interface BootstrapOptions {
|
||||
configPath: string;
|
||||
mode: RuntimeMode;
|
||||
staticAssets?: StaticAssets;
|
||||
version: string;
|
||||
}
|
||||
|
||||
type BootstrapEngine = Pick<ProbeEngine, "start" | "stop">;
|
||||
@@ -73,6 +74,7 @@ export async function bootstrap(options: BootstrapOptions, dependencies: Bootstr
|
||||
mode: options.mode,
|
||||
staticAssets: options.staticAssets,
|
||||
store,
|
||||
version: options.version,
|
||||
});
|
||||
} catch (error) {
|
||||
engine?.stop();
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { bootstrap } from "./bootstrap";
|
||||
import { readRuntimeConfig } from "./config";
|
||||
import { readAppVersion } from "./version";
|
||||
|
||||
async function main() {
|
||||
const { configPath } = readRuntimeConfig();
|
||||
await bootstrap({ configPath, mode: "development" });
|
||||
const version = await readAppVersion();
|
||||
await bootstrap({ configPath, mode: "development", version });
|
||||
}
|
||||
|
||||
void main().catch((error) => {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { bootstrap } from "./bootstrap";
|
||||
import { readRuntimeConfig } from "./config";
|
||||
import { readAppVersion } from "./version";
|
||||
|
||||
async function main() {
|
||||
const { configPath } = readRuntimeConfig();
|
||||
await bootstrap({ configPath, mode: "production" });
|
||||
const version = await readAppVersion();
|
||||
await bootstrap({ configPath, mode: "production", version });
|
||||
}
|
||||
|
||||
void main().catch((error) => {
|
||||
|
||||
@@ -3,9 +3,10 @@ import type { MetaResponse, RuntimeMode } from "../../shared/api";
|
||||
import { checkerRegistry } from "../checker/runner";
|
||||
import { jsonResponse } from "../helpers";
|
||||
|
||||
export function handleMeta(mode: RuntimeMode): Response {
|
||||
export function handleMeta(mode: RuntimeMode, version: string): Response {
|
||||
const response: MetaResponse = {
|
||||
checkerTypes: checkerRegistry.supportedTypes,
|
||||
version,
|
||||
};
|
||||
|
||||
return jsonResponse(response, { mode });
|
||||
|
||||
@@ -16,10 +16,11 @@ export interface StartServerOptions {
|
||||
mode: RuntimeMode;
|
||||
staticAssets?: StaticAssets;
|
||||
store: ProbeStore;
|
||||
version: string;
|
||||
}
|
||||
|
||||
export function startServer(options: StartServerOptions) {
|
||||
const { config, mode, staticAssets, store } = options;
|
||||
const { config, mode, staticAssets, store, version } = options;
|
||||
|
||||
const server = Bun.serve({
|
||||
fetch(req) {
|
||||
@@ -36,7 +37,7 @@ export function startServer(options: StartServerOptions) {
|
||||
GET: (req) => handleDashboard(new URL(req.url), store, mode),
|
||||
},
|
||||
"/api/meta": {
|
||||
GET: () => handleMeta(mode),
|
||||
GET: () => handleMeta(mode, version),
|
||||
},
|
||||
"/api/targets/:id/history": {
|
||||
GET: (req) => handleHistory(req.params.id, new URL(req.url), store, mode),
|
||||
|
||||
17
src/server/version.ts
Normal file
17
src/server/version.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { resolve } from "node:path";
|
||||
|
||||
import { validateVersion } from "../../scripts/bump-version-logic";
|
||||
|
||||
const PACKAGE_JSON_PATH = resolve(import.meta.dir, "..", "..", "package.json");
|
||||
|
||||
export async function readAppVersion(): Promise<string> {
|
||||
const packageJson = (await Bun.file(PACKAGE_JSON_PATH).json()) as { version: string };
|
||||
const version = packageJson.version;
|
||||
|
||||
if (typeof version !== "string") {
|
||||
throw new Error("package.json does not have a valid version field");
|
||||
}
|
||||
|
||||
validateVersion(version);
|
||||
return version;
|
||||
}
|
||||
@@ -58,6 +58,7 @@ export interface HistoryResponse {
|
||||
|
||||
export interface MetaResponse {
|
||||
checkerTypes: string[];
|
||||
version: string;
|
||||
}
|
||||
|
||||
export interface RecentSample {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Alert, Layout, Menu, RadioGroup, Skeleton } from "tdesign-react";
|
||||
import { RefreshCountdown } from "./components/RefreshCountdown";
|
||||
import { SummaryCards } from "./components/SummaryCards";
|
||||
import { TargetBoard } from "./components/TargetBoard";
|
||||
import { useDashboard } from "./hooks/use-queries";
|
||||
import { useDashboard, useMeta } from "./hooks/use-queries";
|
||||
import { useTargetDetail } from "./hooks/use-target-detail";
|
||||
import { type ThemePreference, useThemePreference } from "./hooks/use-theme-preference";
|
||||
|
||||
@@ -46,6 +46,7 @@ export function App() {
|
||||
isLoading: dashboardLoading,
|
||||
refetch: refetchDashboard,
|
||||
} = useDashboard(dashboardRefetchInterval);
|
||||
const { data: meta } = useMeta();
|
||||
const {
|
||||
activeTab,
|
||||
closeDrawer,
|
||||
@@ -62,6 +63,7 @@ export function App() {
|
||||
timeTo,
|
||||
} = useTargetDetail();
|
||||
const isManualRefresh = refreshInterval === 0;
|
||||
const versionDisplay = meta?.version ? `v${meta.version}` : null;
|
||||
|
||||
const handleIntervalChange = (value: number) => {
|
||||
void refetchDashboard();
|
||||
@@ -80,6 +82,7 @@ export function App() {
|
||||
<span className="dashboard-brand">
|
||||
<span className="dashboard-logo">DiAL</span>
|
||||
<span className="dashboard-subtitle">统一拨测平台</span>
|
||||
{versionDisplay && <span className="dashboard-version">{versionDisplay}</span>}
|
||||
</span>
|
||||
}
|
||||
operations={
|
||||
|
||||
@@ -46,6 +46,12 @@
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.dashboard-version {
|
||||
color: var(--td-text-color-placeholder);
|
||||
font-size: var(--td-font-size-body-small);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.dashboard-header-controls {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user