refactor: 统一 target name/description 可空语义,前端展示 fallback 到 id
- schema: name/description 允许省略或显式 null,TypeBox Union([Null, String]) - 类型: RawTargetConfig/ResolvedTargetBase/子类型/StoredTarget/TargetStatus name 改为 string | null - checker resolve: name: t.name ?? null,不再 fallback 到 id - 语义校验: 拒绝空字符串和纯空白 name - SQLite: targets.name 列改为可空 TEXT - 前端: 新增 getTargetDisplayName(target) 展示 name ?? id - 测试: 覆盖 name/description null 全场景,查找改为按 id - 文档: 更新 README/DEVELOPMENT 和 6 个 openspec specs
This commit is contained in:
@@ -179,6 +179,10 @@ function validateConfig(config: RawProbeConfig): ConfigValidationIssue[] {
|
||||
const nameValue: unknown = raw["name"];
|
||||
const name = isString(nameValue) ? nameValue : id;
|
||||
|
||||
if (isString(nameValue) && nameValue.trim() === "") {
|
||||
issues.push(issue("invalid-value", `targets[${i}].name`, "name 不能为空白", name));
|
||||
}
|
||||
|
||||
const type: unknown = raw["type"];
|
||||
if (!isString(type)) {
|
||||
issues.push(issue("required", `targets[${i}].type`, "缺少 type 字段", name));
|
||||
|
||||
@@ -192,7 +192,7 @@ export class CommandChecker implements CheckerDefinition<ResolvedCommandTarget>
|
||||
group: target.group ?? "default",
|
||||
id: t.id,
|
||||
intervalMs: context.defaultIntervalMs,
|
||||
name: t.name ?? t.id,
|
||||
name: t.name ?? null,
|
||||
timeoutMs: context.defaultTimeoutMs,
|
||||
type: "cmd",
|
||||
} satisfies ResolvedCommandTarget;
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface ResolvedCommandTarget extends ResolvedTargetBase {
|
||||
expect?: CommandExpectConfig;
|
||||
group: string;
|
||||
intervalMs: number;
|
||||
name: string;
|
||||
name: null | string;
|
||||
timeoutMs: number;
|
||||
type: "cmd";
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ export class DbChecker implements CheckerDefinition<ResolvedDbTarget> {
|
||||
group: target.group ?? "default",
|
||||
id: t.id,
|
||||
intervalMs: context.defaultIntervalMs,
|
||||
name: t.name ?? t.id,
|
||||
name: t.name ?? null,
|
||||
timeoutMs: context.defaultTimeoutMs,
|
||||
type: "db",
|
||||
} satisfies ResolvedDbTarget;
|
||||
|
||||
@@ -21,7 +21,7 @@ export interface ResolvedDbTarget extends ResolvedTargetBase {
|
||||
expect?: DbExpectConfig;
|
||||
group: string;
|
||||
intervalMs: number;
|
||||
name: string;
|
||||
name: null | string;
|
||||
timeoutMs: number;
|
||||
type: "db";
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ export class HttpChecker implements CheckerDefinition<ResolvedHttpTarget> {
|
||||
},
|
||||
id: t.id,
|
||||
intervalMs: context.defaultIntervalMs,
|
||||
name: t.name ?? t.id,
|
||||
name: t.name ?? null,
|
||||
timeoutMs: context.defaultTimeoutMs,
|
||||
type: "http",
|
||||
} satisfies ResolvedHttpTarget;
|
||||
|
||||
@@ -51,7 +51,7 @@ export interface ResolvedHttpTarget extends ResolvedTargetBase {
|
||||
group: string;
|
||||
http: ResolvedHttpConfig;
|
||||
intervalMs: number;
|
||||
name: string;
|
||||
name: null | string;
|
||||
timeoutMs: number;
|
||||
type: "http";
|
||||
}
|
||||
|
||||
@@ -49,12 +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 })),
|
||||
description: Type.Optional(Type.Union([Type.Null(), Type.String({ maxLength: 500 })])),
|
||||
expect: Type.Optional(checker.schemas.expect),
|
||||
group: Type.Optional(Type.String()),
|
||||
id: Type.String({ maxLength: 30, minLength: 1 }),
|
||||
interval: Type.Optional(durationSchema),
|
||||
name: Type.Optional(Type.String({ maxLength: 30, minLength: 1 })),
|
||||
name: Type.Optional(Type.Union([Type.Null(), Type.String({ maxLength: 30, minLength: 1 })])),
|
||||
timeout: Type.Optional(durationSchema),
|
||||
type: Type.Literal(checker.type),
|
||||
};
|
||||
@@ -69,11 +69,11 @@ function cloneSchema(schema: TSchema): Record<string, unknown> {
|
||||
function createBaseTargetSchema(checkers: CheckerDefinition[]): TSchema {
|
||||
return Type.Object(
|
||||
{
|
||||
description: Type.Optional(Type.String({ maxLength: 500 })),
|
||||
description: Type.Optional(Type.Union([Type.Null(), Type.String({ maxLength: 500 })])),
|
||||
group: Type.Optional(Type.String()),
|
||||
id: Type.String({ maxLength: 30, minLength: 1 }),
|
||||
interval: Type.Optional(durationSchema),
|
||||
name: Type.Optional(Type.String({ maxLength: 30, minLength: 1 })),
|
||||
name: Type.Optional(Type.Union([Type.Null(), 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[]]),
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ import { checkerRegistry } from "./runner";
|
||||
const CREATE_TARGETS_TABLE = `
|
||||
CREATE TABLE IF NOT EXISTS targets (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
name TEXT,
|
||||
description TEXT,
|
||||
type TEXT NOT NULL,
|
||||
target TEXT NOT NULL,
|
||||
@@ -309,10 +309,7 @@ export class ProbeStore {
|
||||
|
||||
syncTargets(targets: ResolvedTargetBase[]): void {
|
||||
if (this.closed) return;
|
||||
const existingRows = this.db.query("SELECT id FROM targets").all() as Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
}>;
|
||||
const existingRows = this.db.query("SELECT id FROM targets").all() as Array<{ id: string }>;
|
||||
const existingIds = new Set(existingRows.map((r) => r.id));
|
||||
const configIds = new Set(targets.map((t) => t.id));
|
||||
|
||||
|
||||
@@ -41,12 +41,12 @@ export interface ProbeConfig {
|
||||
|
||||
export interface RawTargetConfig {
|
||||
[configKey: string]: unknown;
|
||||
description?: string;
|
||||
description?: null | string;
|
||||
expect?: unknown;
|
||||
group?: string;
|
||||
id: string;
|
||||
interval?: string;
|
||||
name?: string;
|
||||
name?: null | string;
|
||||
timeout?: string;
|
||||
type: string;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ export interface ResolvedTargetBase {
|
||||
group: string;
|
||||
id: string;
|
||||
intervalMs: number;
|
||||
name: string;
|
||||
name: null | string;
|
||||
timeoutMs: number;
|
||||
type: string;
|
||||
}
|
||||
@@ -86,7 +86,7 @@ export interface StoredTarget {
|
||||
grp: string;
|
||||
id: string;
|
||||
interval_ms: number;
|
||||
name: string;
|
||||
name: null | string;
|
||||
target: string;
|
||||
timeout_ms: number;
|
||||
type: string;
|
||||
|
||||
Reference in New Issue
Block a user