web: unify Agent management and add a draggable link board

Replace Probe pages with directed Agent links, shared deployment controls and unified display data. Add node dragging, canvas pan and zoom, saved layouts, and per-direction link controls.
This commit is contained in:
justme committed 2026-10-21 11:55:50 +08:00
1 parent 016dbc2bbf
commit 15f0023614
32 files changed
+937 -1093

No files matched your search

+128 -405
View File
@@ -164,7 +164,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** Latest host resource usage of every visible agent and probe (no hardware model, no addresses) */
/** Latest host resource usage of every visible node (no hardware model, no addresses) */
get: operations["hosts_list"];
put?: never;
post?: never;
@@ -181,7 +181,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** Host resource usage history of one agent or probe (columnar, averaged per step) */
/** Host resource usage history of one node (columnar, averaged per step) */
get: operations["hosts_history"];
put?: never;
post?: never;
@@ -198,7 +198,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** Display info of the visible agents and probes: system, architecture, country and ISP (never an address) */
/** Display info of the visible nodes: system, architecture, country and ISP (never an address) */
get: operations["nodes_list"];
put?: never;
post?: never;
@@ -472,7 +472,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** All agents including pending and disabled ones */
/** All nodes (agents) including pending and disabled ones; each says whether it listens, which dialers are connected to it and which listeners it dials */
get: operations["agents_list"];
put?: never;
post?: never;
@@ -497,7 +497,7 @@ export interface paths {
delete: operations["agents_delete"];
options?: never;
head?: never;
/** Update name, labels, public host or visibility (merge-patch, If-Match supported) */
/** Update name, labels, public host, ISP or visibility (merge-patch, If-Match supported) */
patch: operations["agents_update"];
trace?: never;
};
@@ -508,7 +508,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** Recent Agent or Probe runtime logs */
/** Recent runtime logs of a node */
get: operations["node_runtime_logs"];
put?: never;
post?: never;
@@ -596,7 +596,7 @@ export interface paths {
/** Join tokens */
get: operations["join_tokens_list"];
put?: never;
/** Create a join token for an agent or a probe (role); the plaintext is returned only once */
/** Create a join token for an agent; the plaintext is returned only once */
post: operations["join_tokens_create"];
delete?: never;
options?: never;
@@ -621,43 +621,7 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/admin/probes": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** All probes, with the agents each one is connected to */
get: operations["probes_list"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/admin/probes/{id}": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** One probe */
get: operations["probes_get"];
put?: never;
post?: never;
/** Delete a probe and its lines; stored data expires with the retention policy */
delete: operations["probes_delete"];
options?: never;
head?: never;
/** Rename a probe */
patch: operations["probes_update"];
trace?: never;
};
"/api/admin/probes/{id}/revoke": {
"/api/admin/agents/{id}/dialers/{dialer_id}/disconnect": {
parameters: {
query?: never;
header?: never;
@@ -666,25 +630,8 @@ export interface paths {
};
get?: never;
put?: never;
/** Revoke a probe: every agent drops its session and refuses it; history stays until it expires */
post: operations["probes_revoke"];
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/admin/agents/{id}/probes/{probe_id}/disconnect": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
get?: never;
put?: never;
/** Make an agent drop one probe's session (the probe reconnects unless it is revoked) */
post: operations["agents_probe_disconnect"];
/** Make a listening node drop one dialer's session (the dialer reconnects unless it is disabled) */
post: operations["agents_dialer_disconnect"];
delete?: never;
options?: never;
head?: never;
@@ -978,7 +925,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** Version, tag, commit, build time and update state of the backend and every agent and probe */
/** Version, tag, commit, build time and update state of the backend and every node */
get: operations["build_info_get"];
put?: never;
post?: never;
@@ -1014,7 +961,7 @@ export interface paths {
};
get?: never;
put?: never;
/** Ask an online agent or probe to install the release it has been offered */
/** Ask an online node to install the release it has been offered */
post: operations["nodes_update"];
delete?: never;
options?: never;
@@ -1083,7 +1030,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** Every probe/agent pair: whether it is linked, the manual override and whether it is connected */
/** Every possible dialer/listener pair: whether it is linked, the manual override and whether it is connected; plus the nodes and which of them listen */
get: operations["links_list"];
put?: never;
post?: never;
@@ -1093,7 +1040,7 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/admin/links/{probe_id}/{agent_id}": {
"/api/admin/links/{dialer_id}/{listener_id}": {
parameters: {
query?: never;
header?: never;
@@ -1101,7 +1048,7 @@ export interface paths {
cookie?: never;
};
get?: never;
/** Force a link on or off, or give it back to the automatic setting (enabled: null) */
/** Connect a node to a listening node (enabled: true), force the link off (false), or give it back to the automatic setting (null) */
put: operations["links_set"];
post?: never;
delete?: never;
@@ -1110,7 +1057,7 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/admin/links/{probe_id}/{agent_id}/retry": {
"/api/admin/links/{dialer_id}/{listener_id}/retry": {
parameters: {
query?: never;
header?: never;
@@ -1119,7 +1066,7 @@ export interface paths {
};
get?: never;
put?: never;
/** Enable and immediately retry the Probe-to-Agent connection */
/** Enable and immediately retry the dialer-to-listener connection */
post: operations["links_retry"];
delete?: never;
options?: never;
@@ -1168,24 +1115,8 @@ export interface paths {
path?: never;
cookie?: never;
};
get: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
/** The static native OpenWrt installer and updater (Agent) */
get: operations["install_openwrt"];
put?: never;
post?: never;
delete?: never;
@@ -1218,7 +1149,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** The signed release manifest of the component an install code is for (exact signed bytes) */
/** The signed release manifest an install code may fetch (exact signed bytes) */
get: operations["install_manifest"];
put?: never;
post?: never;
@@ -1252,7 +1183,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** The static compose file of agent, probe or backend (agent.yaml, probe.yaml, backend.yaml; same bytes for everyone) */
/** The static compose file of the agent or the backend (agent.yaml, backend.yaml; same bytes for everyone) */
get: operations["install_compose"];
put?: never;
post?: never;
@@ -1269,7 +1200,7 @@ export interface paths {
path?: never;
cookie?: never;
};
/** The static Podman Quadlet unit of agent or probe (agent.container, probe.container; same bytes for everyone) */
/** The static Podman Quadlet unit of the agent (agent.container; same bytes for everyone) */
get: operations["install_quadlet"];
put?: never;
post?: never;
@@ -1360,14 +1291,15 @@ export interface components {
name: string;
status: string;
online: boolean;
probe_connected: boolean;
probes: components["schemas"]["ConnectedProbeDto"][];
listens: boolean;
dialer_connected: boolean;
dialers: components["schemas"]["ConnectedDialerDto"][];
outgoing: components["schemas"]["OutgoingConnectionDto"][];
host: null | components["schemas"]["HostDto"];
public_host: string;
/** Format: int32 */
probe_tcp_port: number | string;
probe_udp_ports: (number | string)[];
probe_quic_ports: (number | string)[];
tcp_port: number | string;
quic_ports: (number | string)[];
labels: string[];
public_visible: boolean;
kinds: string[];
@@ -1411,6 +1343,7 @@ export interface components {
ipv6_host?: null | string;
public_host_auto?: null | boolean;
ipv6_host_auto?: null | boolean;
isp?: null | string;
};
ApiTokenDto: {
id: string;
@@ -1522,8 +1455,8 @@ export interface components {
/** Format: int64 */
seq: number | string;
};
ConnectedProbeDto: {
probe_id: string;
ConnectedDialerDto: {
dialer_id: string;
version: null | string;
remote: null | string;
/** Format: date-time */
@@ -1680,7 +1613,6 @@ export interface components {
};
HostItemDto: {
id: string;
kind: string;
name: string;
online: boolean;
stale: boolean;
@@ -1734,8 +1666,6 @@ export interface components {
revoked_at: unknown;
/** Format: date-time */
created_at: unknown;
/** @default agent */
role: string;
created_by?: null | string;
};
JsonElement: unknown;
@@ -1751,12 +1681,12 @@ export interface components {
LineDto: {
id: string;
agent_id: string;
probe_id: string;
probe_name: string;
dialer_id: string;
dialer_name: string;
name: string;
labels: string[];
online: boolean;
probe_connected: boolean;
dialer_connected: boolean;
/** Format: date-time */
data_since: unknown;
kinds: string[];
@@ -1768,16 +1698,16 @@ export interface components {
quic_health?: null | components["schemas"]["QuicLineHealthDto"];
};
LinkDto: {
probe_id: string;
probe_name: string;
agent_id: string;
agent_name: string;
dialer_id: string;
dialer_name: string;
listener_id: string;
listener_name: string;
linked: boolean;
override: null | boolean;
connected: boolean;
probe_runtime_status: components["schemas"]["NodeRuntimeStatus"];
agent_runtime_status: components["schemas"]["NodeRuntimeStatus"];
/** @default connecting */
dialer_runtime_status: components["schemas"]["NodeRuntimeStatus"];
listener_runtime_status: components["schemas"]["NodeRuntimeStatus"];
/** @default control_connecting */
connection_state: string;
connection_reason?: null | string;
last_error?: null | string;
@@ -1791,8 +1721,16 @@ export interface components {
};
LinkMatrixDto: {
auto_link: boolean;
nodes: components["schemas"]["LinkNodeDto"][];
items: components["schemas"]["LinkDto"][];
};
LinkNodeDto: {
id: string;
name: string;
listens: boolean;
online: boolean;
runtime_status: components["schemas"]["NodeRuntimeStatus"];
};
LinkRetryDto: {
queued: boolean;
link: components["schemas"]["LinkDto"];
@@ -1852,7 +1790,6 @@ export interface components {
};
NodeBuildDto: {
id: string;
kind: string;
os: null | string;
os_name: null | string;
arch: null | string;
@@ -1865,7 +1802,6 @@ export interface components {
};
NodeInfoDto: {
id: string;
kind: string;
os: null | string;
os_name: null | string;
arch: null | string;
@@ -1899,7 +1835,7 @@ export interface components {
dropped: number | string;
};
/** @enum {unknown} */
NodeRuntimeStatus: "initializing" | "running" | "probe_paused" | "maintenance" | "stopping" | "offline" | "updating";
NodeRuntimeStatus: "initializing" | "running" | "idle" | "maintenance" | "stopping" | "offline" | "updating";
NodeUpdateDto: {
state: string;
current_digest: null | string;
@@ -1909,6 +1845,17 @@ export interface components {
/** Format: date-time */
at: unknown;
};
OutgoingConnectionDto: {
listener_id: string;
listener_name: string;
listener_online: boolean;
version: null | string;
remote: null | string;
/** Format: date-time */
connected_since: unknown;
/** @default ipv4 */
ip_version: string;
};
OverviewDto: {
range: string;
/** Format: date-time */
@@ -1923,12 +1870,12 @@ export interface components {
OverviewLineDto: {
id: string;
agent_id: string;
probe_id: string;
probe_name: string;
dialer_id: string;
dialer_name: string;
name: string;
labels: string[];
online: boolean;
probe_connected: boolean;
dialer_connected: boolean;
/** Format: int32 */
endpoint_changes: number | string;
summary: components["schemas"]["SummaryDto"];
@@ -1993,10 +1940,6 @@ export interface components {
items: components["schemas"]["PresetDto"][];
next_cursor: null | string;
};
PageDtoOfProbeDto: {
items: components["schemas"]["ProbeDto"][];
next_cursor: null | string;
};
PageDtoOfRawChunkMetaDto: {
items: components["schemas"]["RawChunkMetaDto"][];
next_cursor: null | string;
@@ -2066,10 +2009,6 @@ export interface components {
timeout_ms: number | string;
allow_credentials?: null | string[];
};
PatchProbeRequest: {
name: null | string;
isp?: null | string;
};
PatchTaskRequest: {
params: null | Record<string, never>;
schedule: null | string;
@@ -2093,31 +2032,6 @@ export interface components {
label: string;
metrics: string[];
};
ProbeConnectionDto: {
agent_id: string;
agent_name: string;
agent_online: boolean;
version: null | string;
remote: null | string;
/** Format: date-time */
connected_since: unknown;
/** @default ipv4 */
ip_version: string;
};
ProbeDto: {
id: string;
name: string;
/** Format: date-time */
created_at: unknown;
/** Format: date-time */
revoked_at: unknown;
revoked: boolean;
online: boolean;
connections: components["schemas"]["ProbeConnectionDto"][];
runtime_status: components["schemas"]["NodeRuntimeStatus"];
ipv6?: null | components["schemas"]["Ipv6DiagnosticDto"];
added_by?: null | string;
};
ProblemDto: {
type: string;
title: string;
@@ -2187,6 +2101,8 @@ export interface components {
retry_attempt: number | string;
/** Format: int64 */
retry_in_ms?: null | number | string;
/** @default dialing */
control_state: string;
};
RawChunkMetaDto: {
chunk_id: string;
@@ -2260,7 +2176,7 @@ export interface components {
SeriesDto: {
line_id: string;
agent_id: string;
probe_id: string;
dialer_id: string;
/** Format: date-time */
from: unknown;
/** Format: date-time */
@@ -2346,7 +2262,7 @@ export interface components {
TestDto: {
id: string;
agent_id: string;
probe_id: string;
dialer_id: string;
plan: string;
phases: string[];
status: string;
@@ -4307,260 +4223,13 @@ export interface operations {
};
};
};
probes_list: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["PageDtoOfProbeDto"];
};
};
/** @description Unauthorized */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Forbidden */
403: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
};
};
probes_get: {
agents_dialer_disconnect: {
parameters: {
query?: never;
header?: never;
path: {
id: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ProbeDto"];
};
};
/** @description Unauthorized */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Forbidden */
403: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Not Found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
};
};
probes_delete: {
parameters: {
query?: never;
header?: never;
path: {
id: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
/** @description No Content */
204: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": unknown;
};
};
/** @description Unauthorized */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Forbidden */
403: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
};
};
probes_update: {
parameters: {
query?: never;
header?: never;
path: {
id: string;
};
cookie?: never;
};
requestBody: {
content: {
"application/json": components["schemas"]["PatchProbeRequest"];
};
};
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ProbeDto"];
};
};
/** @description Bad Request */
400: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Unauthorized */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Forbidden */
403: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Not Found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Conflict */
409: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
};
};
probes_revoke: {
parameters: {
query?: never;
header?: never;
path: {
id: string;
};
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["ProbeDto"];
};
};
/** @description Unauthorized */
401: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Forbidden */
403: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Not Found */
404: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
};
};
agents_probe_disconnect: {
parameters: {
query?: never;
header?: never;
path: {
id: string;
probe_id: string;
dialer_id: string;
};
cookie?: never;
};
@@ -6227,8 +5896,8 @@ export interface operations {
query?: never;
header?: never;
path: {
probe_id: string;
agent_id: string;
dialer_id: string;
listener_id: string;
};
cookie?: never;
};
@@ -6283,6 +5952,24 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Conflict */
409: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Unprocessable Entity */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
};
};
links_retry: {
@@ -6290,8 +5977,8 @@ export interface operations {
query?: never;
header?: never;
path: {
probe_id: string;
agent_id: string;
dialer_id: string;
listener_id: string;
};
cookie?: never;
};
@@ -6333,6 +6020,24 @@ export interface operations {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Conflict */
409: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
/** @description Unprocessable Entity */
422: {
headers: {
[name: string]: unknown;
};
content: {
"application/problem+json": components["schemas"]["ProblemDto"];
};
};
};
};
health_get: {
@@ -6400,6 +6105,24 @@ export interface operations {
};
};
};
install_openwrt: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description OK */
200: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
install_sh: {
parameters: {
query?: never;
+2 -3
View File
@@ -6,13 +6,12 @@ type S = components['schemas']
type NarrowValue<V> = unknown extends V ? string : number extends V ? Exclude<V, string> : V extends readonly (infer U)[] ? NarrowValue<U>[] : V extends object ? Narrow<V> : V
type Narrow<T> = { [K in keyof T]: NarrowValue<T[K]> }
export type HostDto = Narrow<S['HostDto']>
export type HostItemDto = Narrow<S['HostItemDto']>
export type AgentDto = Narrow<S['AgentDto']>
export type NodeRuntimeLogsDto = Narrow<S['NodeRuntimeLogsDto']>
export type NodeRuntimeStatus = S['NodeRuntimeStatus']
export type AgentIpv6Dto = Narrow<S['AgentIpv6Dto']>
export type ConnectedProbeDto = Narrow<S['ConnectedProbeDto']>
export type ProbeDto = Narrow<S['ProbeDto']>
export type ProbeConnectionDto = Narrow<S['ProbeConnectionDto']>
export type LineDto = Narrow<S['LineDto']>
export type NodeInfoDto = Narrow<S['NodeInfoDto']>
export type AboutDto = Narrow<S['AboutDto']>
+7 -15
View File
@@ -12,9 +12,7 @@ const CODES: Record<string, string> = {
site_private: '站点未公开:请管理员登录,或在管理端把站点设为公开',
not_found: '没有找到',
agent_not_found: '没有找到这个 Agent',
probe_not_found: '没有找到这个 Probe',
probe_revoked: '这个 Probe 已经被吊销,不能再换密钥,请新建一个 Probe',
probe_required: '有多个 Probe,请指定具体的线路',
dialer_required: '请指定拨号 Agent 或具体线路',
task_not_found: '没有找到这个任务',
test_not_found: '没有找到这次测试',
chunk_not_found: '没有找到这个原始数据块',
@@ -22,7 +20,10 @@ const CODES: Record<string, string> = {
unknown_kind: '不认识这种采集方法',
unknown_plan: '不认识这个测试方案',
name_conflict: '名称已经被使用,换一个',
probe_offline: '这个 Probe 现在没有连着这个 Agent',
dialer_offline: '拨号 Agent 当前没有接入这个 Agent',
node_revoked: '节点已被吊销',
not_listening: '目标 Agent 没有开启监听',
self_link: 'Agent 不能连接自身',
test_busy: '这条线路上已经有测试在运行',
agent_not_active: '这个 Agent 还没有启用',
agent_offline: 'Agent 不在线',
@@ -107,20 +108,11 @@ const MESSAGES: [RegExp, (m: RegExpMatchArray) => string][] = [
[/^once tasks are created through the tests API$/, () => '一次性测试请在「按需测试」里发起'],
[/^unknown kind (.+)$/, (m) => `不认识采集方法 ${m[1]}`],
[/^unknown plan (.+)$/, (m) => `不认识测试方案 ${m[1]}`],
[/^a probe named (.+) already exists$/, (m) => `已经有一个叫 ${m[1]} 的 Probe`],
[/^probe (\S+) has no session with this agent$/, () => '这个 Probe 现在没有连着这个 Agent'],
[/^probe (\S+) is not connected to this agent$/, () => '这个 Probe 现在没有连着这个 Agent'],
[/^this probe has no session with this agent$/, () => '这个 Probe 现在没有连着这个 Agent'],
[/^the probe has no session with this agent$/, () => '这个 Probe 现在没有连着这个 Agent'],
[/^another test is already running on this line$/, () => '这条线路上已经有测试在运行'],
[/^agent is offline$/, () => 'Agent 不在线'],
[/^agent is not active$/, () => 'Agent 还没有启用'],
[/^agent not found$/, () => '没有找到这个 Agent'],
[/^probe not found$/, () => '没有找到这个 Probe'],
[/^line not found$/, () => '没有找到这条线路'],
[/^there is no active probe$/, () => '现在没有可用的 Probe'],
[/^several probes exist.*$/, () => '有多个 Probe,请指定具体的线路'],
[/^a revoked probe cannot get a new key.*$/, () => '已吊销的 Probe 不能换密钥,请新建一个 Probe'],
[/^this agent does not support (\S+), which the (\S+) plan needs$/, (m) => `这个 Agent 不支持 ${m[1]},而 ${m[2]} 方案需要它`],
[/^the agent did not accept the test$/, () => 'Agent 没有接受这次测试'],
[/^a valid X-CSRF-Token header is required$/, () => '登录状态已经过期,请刷新页面重新登录'],
@@ -143,8 +135,8 @@ export function translateCode(code: string): string | undefined {
const SETTINGS: Record<string, string> = {
'site.public': '匿名访客能不能读展示页的数据(关闭时只有登录的管理员能看)',
'fleet.auto_link': '所有 Probe 自动连接所有 Agent(关闭后只连在「Probe → 线路」里手动选中的)',
'site.show_build': '展示页底部和节点卡片上显示后端、前端、Agent、Probe 的版本、tag 和构建时间',
'fleet.auto_link': 'Agent 默认相互探测;关闭后只连接在线路矩阵中手动开启的方向',
'site.show_build': '展示页底部和节点卡片上显示后端、前端、Agent 的版本、tag 和构建时间',
'retention.raw_days': '持续任务的原始数据块保留多少天',
'retention.test_raw_days': '按需测试的原始数据块保留多少天',
'retention.metric_10s_hours': '10 秒精度的指标保留多少小时',
+2 -2
View File
@@ -9,7 +9,7 @@ import telecomIcon from './assets/isp/telecom.svg?url'
import unicomIcon from './assets/isp/unicom.svg?url'
/**
* 节点(Agent、Probe)的展示信息:国旗、运营商、系统和架构。
* 节点(Agent)的展示信息:国旗、运营商、系统和架构。
* 后端只给国家代码、运营商名、系统名和架构——没有 IP,所以这里也画不出 IP。
*/
@@ -107,7 +107,7 @@ export function NodeTag({ info, geoOnly }: TagProps) {
const RUNTIME_STATUS: Record<NodeRuntimeStatus, { label: string; tone: 'ok' | 'warn' | 'bad' | 'accent' | '' }> = {
initializing: { label: '初始化中', tone: 'warn' },
running: { label: '运行中', tone: 'ok' },
probe_paused: { label: '暂停探测', tone: 'warn' },
idle: { label: '空闲', tone: 'warn' },
maintenance: { label: '维护中', tone: 'warn' },
stopping: { label: '停止中', tone: 'warn' },
offline: { label: '失联', tone: 'bad' },
+1 -1
View File
@@ -508,7 +508,7 @@ export function SourceButton({ variant = 'icon' }: { variant?: 'icon' | 'status'
)
}
/** 下载文本文件(probe.json 只给一次)。 */
/** 下载文本文件。 */
export function downloadText(filename: string, text: string) {
const url = URL.createObjectURL(new Blob([text], { type: 'application/json' }))
const a = document.createElement('a')