1
0

feat: 新增 target description 字段,收紧 id/name 长度,调整延迟列和名称列

This commit is contained in:
2026-05-17 18:42:46 +08:00
parent 7926514986
commit f7193e98ff
36 changed files with 385 additions and 58 deletions

View File

@@ -144,6 +144,7 @@ function resolveTarget(
result.intervalMs = intervalMs;
result.timeoutMs = timeoutMs;
result.description = target.description ?? null;
return result;
}

View File

@@ -187,6 +187,7 @@ export class CommandChecker implements CheckerDefinition<ResolvedCommandTarget>
exec: t.cmd.exec,
maxOutputBytes,
},
description: null,
expect: target.expect as CommandExpectConfig | undefined,
group: target.group ?? "default",
id: t.id,

View File

@@ -180,6 +180,7 @@ export class DbChecker implements CheckerDefinition<ResolvedDbTarget> {
query: t.db.query,
url: t.db.url,
},
description: null,
expect: target.expect as DbExpectConfig | undefined,
group: target.group ?? "default",
id: t.id,

View File

@@ -112,6 +112,7 @@ export class HttpChecker implements CheckerDefinition<ResolvedHttpTarget> {
const maxBodyBytes = parseSize(t.http.maxBodyBytes ?? httpDefaults?.maxBodyBytes ?? "100MB");
return {
description: null,
expect: target.expect as HttpExpectConfig | undefined,
group: target.group ?? "default",
http: {

View File

@@ -49,11 +49,12 @@ export function createProbeConfigSchema(checkers: CheckerDefinition[], external
export function createTargetSchema(checker: CheckerDefinition): TSchema {
const properties: Record<string, TSchema> = {
description: Type.Optional(Type.String({ maxLength: 500 })),
expect: Type.Optional(checker.schemas.expect),
group: Type.Optional(Type.String()),
id: Type.String({ minLength: 1 }),
id: Type.String({ maxLength: 30, minLength: 1 }),
interval: Type.Optional(durationSchema),
name: Type.Optional(Type.String({ minLength: 1 })),
name: Type.Optional(Type.String({ maxLength: 30, minLength: 1 })),
timeout: Type.Optional(durationSchema),
type: Type.Literal(checker.type),
};
@@ -68,10 +69,11 @@ function cloneSchema(schema: TSchema): Record<string, unknown> {
function createBaseTargetSchema(checkers: CheckerDefinition[]): TSchema {
return Type.Object(
{
description: Type.Optional(Type.String({ maxLength: 500 })),
group: Type.Optional(Type.String()),
id: Type.String({ minLength: 1 }),
id: Type.String({ maxLength: 30, minLength: 1 }),
interval: Type.Optional(durationSchema),
name: Type.Optional(Type.String({ minLength: 1 })),
name: Type.Optional(Type.String({ maxLength: 30, minLength: 1 })),
timeout: Type.Optional(durationSchema),
type: Type.Union(checkers.map((checker) => Type.Literal(checker.type)) as unknown as [TSchema, ...TSchema[]]),
},

View File

@@ -10,6 +10,7 @@ const CREATE_TARGETS_TABLE = `
CREATE TABLE IF NOT EXISTS targets (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT,
type TEXT NOT NULL,
target TEXT NOT NULL,
config TEXT NOT NULL DEFAULT '{}',
@@ -316,10 +317,10 @@ export class ProbeStore {
const configIds = new Set(targets.map((t) => t.id));
const insertStmt = this.db.prepare(
"INSERT INTO targets (id, name, type, target, config, interval_ms, timeout_ms, expect, grp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
"INSERT INTO targets (id, name, description, type, target, config, interval_ms, timeout_ms, expect, grp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
);
const updateStmt = this.db.prepare(
"UPDATE targets SET name = ?, type = ?, target = ?, config = ?, interval_ms = ?, timeout_ms = ?, expect = ?, grp = ? WHERE id = ?",
"UPDATE targets SET name = ?, description = ?, type = ?, target = ?, config = ?, interval_ms = ?, timeout_ms = ?, expect = ?, grp = ? WHERE id = ?",
);
const deleteStmt = this.db.prepare("DELETE FROM targets WHERE id = ?");
@@ -332,9 +333,9 @@ export class ProbeStore {
const expect = t.expect ? JSON.stringify(t.expect) : null;
if (existingIds.has(t.id)) {
updateStmt.run(t.name, type, target, config, t.intervalMs, t.timeoutMs, expect, t.group, t.id);
updateStmt.run(t.name, t.description, type, target, config, t.intervalMs, t.timeoutMs, expect, t.group, t.id);
} else {
insertStmt.run(t.id, t.name, type, target, config, t.intervalMs, t.timeoutMs, expect, t.group);
insertStmt.run(t.id, t.name, t.description, type, target, config, t.intervalMs, t.timeoutMs, expect, t.group);
}
}

View File

@@ -41,6 +41,7 @@ export interface ProbeConfig {
export interface RawTargetConfig {
[configKey: string]: unknown;
description?: string;
expect?: unknown;
group?: string;
id: string;
@@ -52,6 +53,7 @@ export interface RawTargetConfig {
export interface ResolvedTargetBase {
[key: string]: unknown;
description: null | string;
expect?: unknown;
group: string;
id: string;
@@ -79,6 +81,7 @@ export interface StoredCheckResult {
export interface StoredTarget {
config: string;
description: null | string;
expect: null | string;
grp: string;
id: string;

View File

@@ -56,6 +56,7 @@ export function handleDashboard(url: URL, store: ProbeStore, mode: RuntimeMode):
return {
currentStreak,
description: target.description,
group: target.grp,
id: target.id,
interval: formatDuration(target.interval_ms),

View File

@@ -99,6 +99,7 @@ export interface TargetStats {
export interface TargetStatus {
currentStreak: CurrentStreak | null;
description: null | string;
group: string;
id: string;
interval: string;

View File

@@ -38,6 +38,7 @@ export function OverviewTab({ metricsData, metricsLoading, target }: OverviewTab
label: "最新检查时间",
},
{ content: target.latestCheck?.statusDetail ?? "-", label: "状态详情" },
{ content: target.description ?? "", label: "描述", span: 2 },
]}
/>

View File

@@ -8,7 +8,7 @@ import { StatusBar } from "../components/StatusBar";
import { StatusDot } from "../components/StatusDot";
import { getAvailabilityProgressColor } from "./color-threshold";
import { statusFilter } from "./target-table-filters";
import { availabilitySorter, latencySorter, nameSorter } from "./target-table-sorters";
import { availabilitySorter, latencySorter } from "./target-table-sorters";
export function createTargetTableColumns(checkerTypes: string[]): Array<PrimaryTableCol<TargetStatus>> {
return [
@@ -24,8 +24,6 @@ export function createTargetTableColumns(checkerTypes: string[]): Array<PrimaryT
{
colKey: "name",
ellipsis: true,
sorter: nameSorter,
sortType: "all",
title: "名称",
},
{
@@ -88,13 +86,13 @@ export function createTargetTableColumns(checkerTypes: string[]): Array<PrimaryT
const ms = row.latestCheck?.durationMs;
if (ms === null || ms === undefined) return <span className="text-disabled">-</span>;
const colorClass = ms <= 100 ? "latency-ok" : ms <= 500 ? "latency-warn" : "latency-error";
const latencyText = ms > 9999 ? "9999+ms" : `${Math.round(ms)}ms`;
const latencyText = ms > 9999 ? "9999+" : `${Math.round(ms)}`;
return <span className={`${colorClass} latency-value tabular-nums`}>{latencyText}</span>;
},
colKey: "latestCheck.durationMs",
sorter: latencySorter,
sortType: "all",
title: "延迟",
title: "延迟(ms)",
width: 75,
},
];

View File

@@ -13,10 +13,6 @@ export function latencySorter(a: TargetStatus, b: TargetStatus): number {
return (a.latestCheck?.durationMs ?? Infinity) - (b.latestCheck?.durationMs ?? Infinity);
}
export function nameSorter(a: TargetStatus, b: TargetStatus): number {
return a.name.localeCompare(b.name, "zh-CN");
}
export function statusSorter(a: TargetStatus, b: TargetStatus): number {
return getStatusRank(a) - getStatusRank(b);
}