@yuku123/z-graph-component 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1344 +0,0 @@
1
- import Me, { useState as m, useCallback as ne, useEffect as Re, useMemo as te, useRef as ze } from "react";
2
- import { useSearchParams as Le, useNavigate as De, Routes as Ge, Route as le, Navigate as He } from "react-router-dom";
3
- import { Alert as L, Card as C, Descriptions as be, Space as k, Select as Ne, Button as J, Row as pe, Col as F, Table as W, Empty as ve, Modal as Ue, Tag as x, Input as Te, Statistic as xe, Tooltip as ye } from "antd";
4
- import { ReloadOutlined as ke, TableOutlined as Fe, NodeIndexOutlined as qe, ProfileOutlined as Be, PlayCircleOutlined as We, ThunderboltOutlined as Je, BranchesOutlined as _e, HistoryOutlined as Ye } from "@ant-design/icons";
5
- import N from "@yuku123/z-frontend-common";
6
- import { PageHeader as ce, EmptyState as X } from "@yuku123/z-frontend-common/ui";
7
- const Ve = { BASE_URL: "/", DEV: !1, MODE: "production", PROD: !0, SSR: !1 };
8
- let Se = typeof window < "u" && window.__Z_GRAPH_API__ || Ve && void 0 || "/api";
9
- function Xe(e, n) {
10
- return !e || e === "/api" ? "/api" + n : e.replace(/\/$/, "") + n;
11
- }
12
- async function me(e, n = {}) {
13
- const s = Xe(Se, e), o = await fetch(s, {
14
- headers: { Accept: "application/json", ...n.headers || {} },
15
- ...n
16
- });
17
- if (!o.ok) {
18
- let d = "";
19
- try {
20
- d = await o.text();
21
- } catch {
22
- }
23
- throw new Error(`HTTP ${o.status} ${o.statusText}: ${d || s}`);
24
- }
25
- const i = await o.text(), p = o.headers.get("X-Response-Time");
26
- if (!i) return { data: null, elapsed: p };
27
- try {
28
- return { data: JSON.parse(i), elapsed: p };
29
- } catch {
30
- return { data: i, elapsed: p };
31
- }
32
- }
33
- async function V(e, n = {}) {
34
- return (await me(e, n)).data;
35
- }
36
- const Ke = {
37
- baseUrl: () => Se,
38
- setBaseUrl: (e) => {
39
- Se = e, typeof window < "u" && (window.__Z_GRAPH_API__ = e);
40
- },
41
- // 健康检查
42
- health: () => V("/health"),
43
- // 元数据
44
- branches: () => V("/meta/branches"),
45
- commits: () => V("/meta/commits"),
46
- schema: (e = "main") => V("/meta/schema?branch=" + encodeURIComponent(e)),
47
- stats: (e = "main") => V("/meta/stats?branch=" + encodeURIComponent(e)),
48
- metrics: () => V("/meta/metrics"),
49
- // 请求日志
50
- logs: (e = {}) => {
51
- const n = new URLSearchParams();
52
- return e.limit && n.set("limit", e.limit), e.offset && n.set("offset", e.offset), e.method && n.set("method", e.method), e.status && n.set("status", e.status), e.path && n.set("path", e.path), V("/meta/logs" + (n.toString() ? "?" + n.toString() : ""));
53
- },
54
- // 导出 / 导入
55
- exportData: (e = "main") => me("/meta/export?branch=" + encodeURIComponent(e)),
56
- // 批量查询
57
- batch: async (e) => me("/query/batch", {
58
- method: "POST",
59
- headers: { "Content-Type": "application/json" },
60
- body: JSON.stringify({ statements: e })
61
- }),
62
- // 查询 — 直接返回数据数组(后端 /query 响应本身就是数组)
63
- // 注意:后端会把 commit 字段当成字符串处理,"null" 字符串无法区分真正的 null,
64
- // 所以 commit 为空时直接不发送该字段,避免触发"Unknown commit: null" 错误。
65
- query: (e, n = "main", s = null) => me("/query", {
66
- method: "POST",
67
- headers: { "Content-Type": "application/json" },
68
- body: JSON.stringify(s ? { cypher: e, branch: n, commit: s } : { cypher: e, branch: n })
69
- }).then((i) => i.data)
70
- };
71
- function St(e) {
72
- if (!e) return "";
73
- try {
74
- return new Date(Number(e)).toLocaleString();
75
- } catch {
76
- return String(e);
77
- }
78
- }
79
- function wt(e, n = 8) {
80
- return e ? e.length > n ? e.substring(0, n) : e : "";
81
- }
82
- const $ = {
83
- instance: () => N.get("/__instance").then((e) => e.data),
84
- health: () => N.get("/health").then((e) => e.data),
85
- branches: () => N.get("/meta/branches").then((e) => e.data),
86
- commits: () => N.get("/meta/commits").then((e) => e.data),
87
- schema: (e) => N.get("/meta/schema", { params: e ? { branch: e } : {} }).then((n) => n.data),
88
- stats: (e) => N.get("/meta/stats", { params: e ? { branch: e } : {} }).then((n) => n.data),
89
- metrics: () => N.get("/meta/metrics").then((e) => e.data),
90
- logs: (e) => N.get("/meta/logs", { params: e }).then((n) => n.data),
91
- /**
92
- * 查询走 GET 而不是 POST:`handleQuery` 的 POST 分支用的是手搓的
93
- * `parseJsonStringMap`(按引号外逗号切 token、再按第一个冒号切 kv),
94
- * 而 GET 分支用的是 `URLDecoder.decode` —— 对含 `:`、`,`、`{}` 的 Cypher
95
- * 只有前者会被解析器啃掉。所以这里统一走 GET。
96
- */
97
- query: (e, n, s) => N.get("/query", { params: s ? { cypher: e, branch: n, commit: s } : { cypher: e, branch: n } }).then((o) => o.data),
98
- explain: (e, n) => N.post("/query/explain", { cypher: e, branch: n }).then((s) => s.data)
99
- };
100
- function Rt(e = "/api") {
101
- N.defaults.baseURL = e, Ke.setBaseUrl(e);
102
- }
103
- const Ze = /^\s*(CREATE|MERGE|DELETE|DROP|ALTER|SET|REMOVE|REPLACE)\b/i;
104
- function Qe(e) {
105
- if (!e) return !1;
106
- const n = e.toUpperCase();
107
- return !!(Ze.test(e) || n.startsWith("MATCH") && (n.includes(" SET ") || n.includes(" DELETE ") || n.includes("DETACH DELETE")));
108
- }
109
- function se(e) {
110
- var s;
111
- const n = (s = e == null ? void 0 : e.response) == null ? void 0 : s.data;
112
- if (n && typeof n == "object") {
113
- const o = n.message || n.error, i = n.source ? ` (${n.source})` : "";
114
- if (o) return `${o}${i}${n.hint ? ` — ${n.hint}` : ""}`;
115
- }
116
- return typeof n == "string" && n.trim() ? n.slice(0, 300) : (e == null ? void 0 : e.message) || String(e);
117
- }
118
- function we(e) {
119
- var n, s, o;
120
- return ((n = e == null ? void 0 : e.response) == null ? void 0 : n.status) === 503 || /not bound in this JVM/.test(String(((o = (s = e == null ? void 0 : e.response) == null ? void 0 : s.data) == null ? void 0 : o.message) || ""));
121
- }
122
- const re = "main";
123
- var he = { exports: {} }, ie = {};
124
- /**
125
- * @license React
126
- * react-jsx-runtime.production.js
127
- *
128
- * Copyright (c) Meta Platforms, Inc. and affiliates.
129
- *
130
- * This source code is licensed under the MIT license found in the
131
- * LICENSE file in the root directory of this source tree.
132
- */
133
- var Ie;
134
- function et() {
135
- if (Ie) return ie;
136
- Ie = 1;
137
- var e = Symbol.for("react.transitional.element"), n = Symbol.for("react.fragment");
138
- function s(o, i, p) {
139
- var d = null;
140
- if (p !== void 0 && (d = "" + p), i.key !== void 0 && (d = "" + i.key), "key" in i) {
141
- p = {};
142
- for (var h in i)
143
- h !== "key" && (p[h] = i[h]);
144
- } else p = i;
145
- return i = p.ref, {
146
- $$typeof: e,
147
- type: o,
148
- key: d,
149
- ref: i !== void 0 ? i : null,
150
- props: p
151
- };
152
- }
153
- return ie.Fragment = n, ie.jsx = s, ie.jsxs = s, ie;
154
- }
155
- var oe = {};
156
- /**
157
- * @license React
158
- * react-jsx-runtime.development.js
159
- *
160
- * Copyright (c) Meta Platforms, Inc. and affiliates.
161
- *
162
- * This source code is licensed under the MIT license found in the
163
- * LICENSE file in the root directory of this source tree.
164
- */
165
- var Ae;
166
- function tt() {
167
- return Ae || (Ae = 1, process.env.NODE_ENV !== "production" && (function() {
168
- function e(r) {
169
- if (r == null) return null;
170
- if (typeof r == "function")
171
- return r.$$typeof === Q ? null : r.displayName || r.name || null;
172
- if (typeof r == "string") return r;
173
- switch (r) {
174
- case l:
175
- return "Fragment";
176
- case M:
177
- return "Profiler";
178
- case R:
179
- return "StrictMode";
180
- case O:
181
- return "Suspense";
182
- case K:
183
- return "SuspenseList";
184
- case w:
185
- return "Activity";
186
- case P:
187
- return "ViewTransition";
188
- }
189
- if (typeof r == "object")
190
- switch (typeof r.tag == "number" && console.error(
191
- "Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue."
192
- ), r.$$typeof) {
193
- case a:
194
- return "Portal";
195
- case A:
196
- return r.displayName || "Context";
197
- case D:
198
- return (r._context.displayName || "Context") + ".Consumer";
199
- case Y:
200
- var c = r.render;
201
- return r = r.displayName, r || (r = c.displayName || c.name || "", r = r !== "" ? "ForwardRef(" + r + ")" : "ForwardRef"), r;
202
- case Z:
203
- return c = r.displayName || null, c !== null ? c : e(r.type) || "Memo";
204
- case f:
205
- c = r._payload, r = r._init;
206
- try {
207
- return e(r(c));
208
- } catch {
209
- }
210
- }
211
- return null;
212
- }
213
- function n(r) {
214
- return "" + r;
215
- }
216
- function s(r) {
217
- try {
218
- n(r);
219
- var c = !1;
220
- } catch {
221
- c = !0;
222
- }
223
- if (c) {
224
- c = console;
225
- var j = c.error, b = typeof Symbol == "function" && Symbol.toStringTag && r[Symbol.toStringTag] || r.constructor.name || "Object";
226
- return j.call(
227
- c,
228
- "The provided key is an unsupported type %s. This value must be coerced to a string before using it here.",
229
- b
230
- ), n(r);
231
- }
232
- }
233
- function o(r) {
234
- if (r === l) return "<>";
235
- if (typeof r == "object" && r !== null && r.$$typeof === f)
236
- return "<...>";
237
- try {
238
- var c = e(r);
239
- return c ? "<" + c + ">" : "<...>";
240
- } catch {
241
- return "<...>";
242
- }
243
- }
244
- function i() {
245
- var r = z.A;
246
- return r === null ? null : r.getOwner();
247
- }
248
- function p() {
249
- return Error("react-stack-top-frame");
250
- }
251
- function d(r) {
252
- if (ae.call(r, "key")) {
253
- var c = Object.getOwnPropertyDescriptor(r, "key").get;
254
- if (c && c.isReactWarning) return !1;
255
- }
256
- return r.key !== void 0;
257
- }
258
- function h(r, c) {
259
- function j() {
260
- G || (G = !0, console.error(
261
- "%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)",
262
- c
263
- ));
264
- }
265
- j.isReactWarning = !0, Object.defineProperty(r, "key", {
266
- get: j,
267
- configurable: !0
268
- });
269
- }
270
- function E() {
271
- var r = e(this.type);
272
- return H[r] || (H[r] = !0, console.error(
273
- "Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release."
274
- )), r = this.props.ref, r !== void 0 ? r : null;
275
- }
276
- function g(r, c, j, b, B, U) {
277
- var T = j.ref;
278
- return r = {
279
- $$typeof: I,
280
- type: r,
281
- key: c,
282
- props: j,
283
- _owner: b
284
- }, (T !== void 0 ? T : null) !== null ? Object.defineProperty(r, "ref", {
285
- enumerable: !1,
286
- get: E
287
- }) : Object.defineProperty(r, "ref", { enumerable: !1, value: null }), r._store = {}, Object.defineProperty(r._store, "validated", {
288
- configurable: !1,
289
- enumerable: !1,
290
- writable: !0,
291
- value: 0
292
- }), Object.defineProperty(r, "_debugInfo", {
293
- configurable: !1,
294
- enumerable: !1,
295
- writable: !0,
296
- value: null
297
- }), Object.defineProperty(r, "_debugStack", {
298
- configurable: !1,
299
- enumerable: !1,
300
- writable: !0,
301
- value: B
302
- }), Object.defineProperty(r, "_debugTask", {
303
- configurable: !1,
304
- enumerable: !1,
305
- writable: !0,
306
- value: U
307
- }), Object.freeze && (Object.freeze(r.props), Object.freeze(r)), r;
308
- }
309
- function S(r, c, j, b, B, U) {
310
- var T = c.children;
311
- if (T !== void 0)
312
- if (b)
313
- if (de(T)) {
314
- for (b = 0; b < T.length; b++)
315
- v(T[b]);
316
- Object.freeze && Object.freeze(T);
317
- } else
318
- console.error(
319
- "React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead."
320
- );
321
- else v(T);
322
- if (ae.call(c, "key")) {
323
- T = e(r);
324
- var ee = Object.keys(c).filter(function($e) {
325
- return $e !== "key";
326
- });
327
- b = 0 < ee.length ? "{key: someKey, " + ee.join(": ..., ") + ": ...}" : "{key: someKey}", Ce[T + b] || (ee = 0 < ee.length ? "{" + ee.join(": ..., ") + ": ...}" : "{}", console.error(
328
- `A props object containing a "key" prop is being spread into JSX:
329
- let props = %s;
330
- <%s {...props} />
331
- React keys must be passed directly to JSX without using spread:
332
- let props = %s;
333
- <%s key={someKey} {...props} />`,
334
- b,
335
- T,
336
- ee,
337
- T
338
- ), Ce[T + b] = !0);
339
- }
340
- if (T = null, j !== void 0 && (s(j), T = "" + j), d(c) && (s(c.key), T = "" + c.key), "key" in c) {
341
- j = {};
342
- for (var fe in c)
343
- fe !== "key" && (j[fe] = c[fe]);
344
- } else j = c;
345
- return T && h(
346
- j,
347
- typeof r == "function" ? r.displayName || r.name || "Unknown" : r
348
- ), g(
349
- r,
350
- T,
351
- j,
352
- i(),
353
- B,
354
- U
355
- );
356
- }
357
- function v(r) {
358
- y(r) ? r._store && (r._store.validated = 1) : typeof r == "object" && r !== null && r.$$typeof === f && (r._payload.status === "fulfilled" ? y(r._payload.value) && r._payload.value._store && (r._payload.value._store.validated = 1) : r._store && (r._store.validated = 1));
359
- }
360
- function y(r) {
361
- return typeof r == "object" && r !== null && r.$$typeof === I;
362
- }
363
- var _ = Me, I = Symbol.for("react.transitional.element"), a = Symbol.for("react.portal"), l = Symbol.for("react.fragment"), R = Symbol.for("react.strict_mode"), M = Symbol.for("react.profiler"), D = Symbol.for("react.consumer"), A = Symbol.for("react.context"), Y = Symbol.for("react.forward_ref"), O = Symbol.for("react.suspense"), K = Symbol.for("react.suspense_list"), Z = Symbol.for("react.memo"), f = Symbol.for("react.lazy"), w = Symbol.for("react.activity"), P = Symbol.for("react.view_transition"), Q = Symbol.for("react.client.reference"), z = _.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE, ae = Object.prototype.hasOwnProperty, de = Array.isArray, u = console.createTask ? console.createTask : function() {
364
- return null;
365
- };
366
- _ = {
367
- react_stack_bottom_frame: function(r) {
368
- return r();
369
- }
370
- };
371
- var G, H = {}, q = _.react_stack_bottom_frame.bind(
372
- _,
373
- p
374
- )(), ue = u(o(p)), Ce = {};
375
- oe.Fragment = l, oe.jsx = function(r, c, j) {
376
- var b = 1e4 > z.recentlyCreatedOwnerStacks++;
377
- if (b) {
378
- var B = Error.stackTraceLimit;
379
- Error.stackTraceLimit = 10;
380
- var U = Error("react-stack-top-frame");
381
- Error.stackTraceLimit = B;
382
- } else U = q;
383
- return S(
384
- r,
385
- c,
386
- j,
387
- !1,
388
- U,
389
- b ? u(o(r)) : ue
390
- );
391
- }, oe.jsxs = function(r, c, j) {
392
- var b = 1e4 > z.recentlyCreatedOwnerStacks++;
393
- if (b) {
394
- var B = Error.stackTraceLimit;
395
- Error.stackTraceLimit = 10;
396
- var U = Error("react-stack-top-frame");
397
- Error.stackTraceLimit = B;
398
- } else U = q;
399
- return S(
400
- r,
401
- c,
402
- j,
403
- !0,
404
- U,
405
- b ? u(o(r)) : ue
406
- );
407
- };
408
- })()), oe;
409
- }
410
- var Pe;
411
- function rt() {
412
- return Pe || (Pe = 1, process.env.NODE_ENV === "production" ? he.exports = et() : he.exports = tt()), he.exports;
413
- }
414
- var t = rt();
415
- const nt = [
416
- { title: "点类型 (Tag)", dataIndex: "Name", key: "Name", render: (e) => /* @__PURE__ */ t.jsx(x, { color: "blue", children: e }) },
417
- {
418
- title: "操作",
419
- key: "action",
420
- width: 110,
421
- render: (e, n) => /* @__PURE__ */ t.jsx(J, { type: "link", size: "small", onClick: () => n.onFields(n.Name), children: "字段" })
422
- }
423
- ], st = [
424
- { title: "边类型 (EdgeType)", dataIndex: "Name", key: "Name", render: (e) => /* @__PURE__ */ t.jsx(x, { color: "purple", children: e }) },
425
- {
426
- title: "操作",
427
- key: "action",
428
- width: 110,
429
- render: (e, n) => /* @__PURE__ */ t.jsx(J, { type: "link", size: "small", onClick: () => n.onFields(n.Name), children: "字段" })
430
- }
431
- ], at = [
432
- { title: "索引名", dataIndex: "Name", key: "Name", render: (e) => /* @__PURE__ */ t.jsx("code", { children: e }) },
433
- {
434
- title: "作用对象类型",
435
- dataIndex: "Kind",
436
- key: "Kind",
437
- width: 130,
438
- render: (e) => e ? /* @__PURE__ */ t.jsx(x, { color: e === "EDGE" ? "purple" : "blue", children: e }) : "-"
439
- },
440
- { title: "挂在", dataIndex: "On", key: "On", width: 160 },
441
- { title: "属性", dataIndex: "Property", key: "Property", width: 160 }
442
- ], lt = [
443
- { title: "字段", dataIndex: "Field", key: "Field" },
444
- { title: "类型", dataIndex: "Type", key: "Type", width: 140, render: (e) => /* @__PURE__ */ t.jsx(x, { children: e }) },
445
- {
446
- title: "可空",
447
- dataIndex: "Null",
448
- key: "Null",
449
- width: 100,
450
- render: (e) => /* @__PURE__ */ t.jsx(x, { color: e === "NO" ? "warning" : "default", children: e })
451
- }
452
- ];
453
- function it() {
454
- const [e, n] = m([re]), [s, o] = m(re), [i, p] = m(null), [d, h] = m(null), [E, g] = m(!1), [S, v] = m(null), [y, _] = m(null), [I, a] = m(null), [l, R] = m(!1), M = ne(async (f) => {
455
- g(!0);
456
- try {
457
- const w = f ?? s;
458
- $.branches().then((z) => {
459
- Array.isArray(z) && z.length > 0 && n(z);
460
- }).catch(() => {
461
- });
462
- const [P, Q] = await Promise.all([
463
- $.schema(w),
464
- $.stats(w).catch(() => null)
465
- ]);
466
- p(P), h(Q), v(null);
467
- } catch (w) {
468
- p(null), h(null), v(w);
469
- } finally {
470
- g(!1);
471
- }
472
- }, [s]);
473
- Re(() => {
474
- M(re);
475
- }, []);
476
- const D = ne(async (f, w) => {
477
- _({ kind: f, name: w, rows: null }), a(null), R(!0);
478
- try {
479
- const P = await $.query(`SHOW ${f} ${w}`, s);
480
- if (!Array.isArray(P))
481
- throw new Error(`SHOW ${f} 返回的不是行数组:${JSON.stringify(P)}`);
482
- _({ kind: f, name: w, rows: P });
483
- } catch (P) {
484
- a(P);
485
- } finally {
486
- R(!1);
487
- }
488
- }, [s]), A = te(() => {
489
- const f = d == null ? void 0 : d.stats;
490
- return Array.isArray(f) && f.length > 0 ? f[0] : null;
491
- }, [d]), Y = we(S), O = ((i == null ? void 0 : i.tags) ?? []).map((f) => ({ ...f, onFields: (w) => D("TAG", w) })), K = ((i == null ? void 0 : i.edges) ?? []).map((f) => ({ ...f, onFields: (w) => D("EDGE", w) })), Z = (i == null ? void 0 : i.indexes) ?? [];
492
- return /* @__PURE__ */ t.jsxs("div", { children: [
493
- /* @__PURE__ */ t.jsx(
494
- ce,
495
- {
496
- title: "图 Schema",
497
- subtitle: "z-graph 点类型 / 边类型 / 属性索引(数据源 GET /api/graph/meta/schema,由 GraphProxyController 转发到本 JVM 内嵌的 GraphControlServer)"
498
- }
499
- ),
500
- S && /* @__PURE__ */ t.jsx(
501
- L,
502
- {
503
- style: { marginBottom: 16 },
504
- type: Y ? "error" : "warning",
505
- showIcon: !0,
506
- message: `Schema 读取失败:${se(S)}`,
507
- description: Y ? "503 = 本 JVM 的内嵌 GraphControlServer 没 bind 成功(不是「图里没有 schema」)。先去「实例与端口」页看 boundPort 与 foreignListenerSuspected。" : "上游 GraphControlServer 的 handleSchema 内部会跑 SHOW TAGS/EDGES/INDEXES 三次查询,任一抛错就整体 500,错误原文在上面的 message 里。"
508
- }
509
- ),
510
- /* @__PURE__ */ t.jsx(
511
- C,
512
- {
513
- loading: E,
514
- title: "当前分支概览",
515
- extra: /* @__PURE__ */ t.jsxs(k, { children: [
516
- /* @__PURE__ */ t.jsx("span", { style: { fontSize: 12, color: "#64748b" }, children: "分支" }),
517
- /* @__PURE__ */ t.jsx(
518
- Ne,
519
- {
520
- size: "small",
521
- style: { width: 160 },
522
- value: s,
523
- options: e.map((f) => ({ value: f, label: f })),
524
- onChange: (f) => {
525
- o(f), M(f);
526
- }
527
- }
528
- ),
529
- /* @__PURE__ */ t.jsx(J, { icon: /* @__PURE__ */ t.jsx(ke, {}), loading: E, onClick: () => M(), children: "刷新" })
530
- ] }),
531
- children: /* @__PURE__ */ t.jsx(
532
- be,
533
- {
534
- bordered: !0,
535
- size: "small",
536
- column: 4,
537
- items: [
538
- { key: "branch", label: "分支", children: (i == null ? void 0 : i.branch) ?? "-" },
539
- {
540
- key: "head",
541
- label: "head commit",
542
- children: i && (d != null && d.head) ? /* @__PURE__ */ t.jsx("a", { title: d.head, children: /* @__PURE__ */ t.jsxs("code", { children: [
543
- d.head.slice(0, 12),
544
- "…"
545
- ] }) }) : "-"
546
- },
547
- { key: "nodes", label: "点数", children: (d == null ? void 0 : d.nodeCount) ?? "-" },
548
- { key: "edges", label: "边数", children: (d == null ? void 0 : d.edgeCount) ?? "-" },
549
- { key: "tags", label: "点类型数", children: i ? O.length : "-" },
550
- { key: "edgeTypes", label: "边类型数", children: i ? K.length : "-" },
551
- { key: "indexes", label: "索引数", children: i ? Z.length : "-" },
552
- {
553
- key: "labelCount",
554
- label: "引擎自报统计 (/meta/stats)",
555
- children: A ? /* @__PURE__ */ t.jsx("span", { title: JSON.stringify(A), children: `标签 ${A.labelCount} / 边类型 ${A.edgeTypeCount} / 属性索引 ${A.propertyIndexCount}` }) : "-"
556
- }
557
- ]
558
- }
559
- )
560
- }
561
- ),
562
- /* @__PURE__ */ t.jsxs(pe, { gutter: 16, style: { marginTop: 16 }, children: [
563
- /* @__PURE__ */ t.jsx(F, { span: 12, children: /* @__PURE__ */ t.jsx(C, { title: /* @__PURE__ */ t.jsxs(k, { children: [
564
- /* @__PURE__ */ t.jsx(Fe, {}),
565
- "点类型 (TAG)"
566
- ] }), loading: E, children: /* @__PURE__ */ t.jsx(
567
- W,
568
- {
569
- size: "small",
570
- rowKey: "Name",
571
- dataSource: O,
572
- columns: nt,
573
- pagination: !1,
574
- loading: E,
575
- locale: { emptyText: ge(!!i, "没有点类型") }
576
- }
577
- ) }) }),
578
- /* @__PURE__ */ t.jsx(F, { span: 12, children: /* @__PURE__ */ t.jsx(C, { title: /* @__PURE__ */ t.jsxs(k, { children: [
579
- /* @__PURE__ */ t.jsx(qe, {}),
580
- "边类型 (EDGE TYPE)"
581
- ] }), loading: E, children: /* @__PURE__ */ t.jsx(
582
- W,
583
- {
584
- size: "small",
585
- rowKey: "Name",
586
- dataSource: K,
587
- columns: st,
588
- pagination: !1,
589
- loading: E,
590
- locale: { emptyText: ge(!!i, "没有边类型") }
591
- }
592
- ) }) })
593
- ] }),
594
- /* @__PURE__ */ t.jsx(C, { style: { marginTop: 16 }, title: /* @__PURE__ */ t.jsxs(k, { children: [
595
- /* @__PURE__ */ t.jsx(Be, {}),
596
- "属性索引"
597
- ] }), loading: E, children: /* @__PURE__ */ t.jsx(
598
- W,
599
- {
600
- size: "small",
601
- rowKey: "Name",
602
- dataSource: Z,
603
- columns: at,
604
- pagination: !1,
605
- loading: E,
606
- locale: { emptyText: ge(!!i, "没有属性索引") }
607
- }
608
- ) }),
609
- /* @__PURE__ */ t.jsx(C, { style: { marginTop: 16 }, title: "这页为什么可以什么都没有", children: /* @__PURE__ */ t.jsx(ve, { description: /* @__PURE__ */ t.jsxs("span", { style: { fontSize: 12, color: "#64748b", lineHeight: 1.9 }, children: [
610
- "z-graph 的 schema 由 Cypher 的 DDL 建立,",
611
- /* @__PURE__ */ t.jsx("b", { children: "没有独立的建表接口" }),
612
- ":",
613
- /* @__PURE__ */ t.jsx("code", { children: " CREATE TAG name (f TYPE, …)" }),
614
- " ·",
615
- /* @__PURE__ */ t.jsx("code", { children: " CREATE EDGE TYPE name (f TYPE, …)" }),
616
- " ·",
617
- /* @__PURE__ */ t.jsx("code", { children: " CREATE INDEX ON tag(prop)" }),
618
- "; 删除对应 ",
619
- /* @__PURE__ */ t.jsx("code", { children: "DROP TAG / DROP EDGE TYPE / DROP INDEX" }),
620
- "(取值与语法以 CypherEngine 的实现为准)。",
621
- /* @__PURE__ */ t.jsx("br", {}),
622
- "这些语句会",
623
- /* @__PURE__ */ t.jsx("b", { children: "真的改图" }),
624
- "(每个写语句在 GraphQueryService 上开一个事务并提交一个新 commit), 所以本页不提供执行入口 —— 要建 schema 请去「图查询」页,那里对写语句有二次确认。"
625
- ] }) }) }),
626
- /* @__PURE__ */ t.jsx(
627
- Ue,
628
- {
629
- open: !!y,
630
- width: 640,
631
- title: y ? `字段明细 · SHOW ${y.kind} ${y.name}` : "",
632
- footer: null,
633
- onCancel: () => _(null),
634
- children: I ? /* @__PURE__ */ t.jsx(L, { type: "error", showIcon: !0, message: `读取失败:${se(I)}` }) : /* @__PURE__ */ t.jsx(
635
- W,
636
- {
637
- size: "small",
638
- rowKey: "Field",
639
- loading: l,
640
- dataSource: (y == null ? void 0 : y.rows) ?? [],
641
- columns: lt,
642
- pagination: !1,
643
- locale: { emptyText: /* @__PURE__ */ t.jsx(ve, { description: "该类型没有字段(schema 里有名字但属性列表为空)" }) }
644
- }
645
- )
646
- }
647
- )
648
- ] });
649
- }
650
- function ge(e, n) {
651
- return e ? /* @__PURE__ */ t.jsx(
652
- X,
653
- {
654
- title: `${n}`,
655
- description: "GET /api/graph/meta/schema 是通的、这一类确实是空数组。z-graph 的内存图刚启动时只有 Initial graph 一个提交,schema 需要显式建。"
656
- }
657
- ) : /* @__PURE__ */ t.jsx(
658
- X,
659
- {
660
- title: "没有拿到 schema 数据",
661
- description: "schema 请求本身失败了,见页面顶部的错误条;这里不是「空」,是「没读通」。"
662
- }
663
- );
664
- }
665
- const { TextArea: ot } = Te, ct = [
666
- { label: "全量取样:MATCH (n) RETURN n LIMIT 20", value: "MATCH (n) RETURN n LIMIT 20" },
667
- { label: "Schema:SHOW TAGS", value: "SHOW TAGS" },
668
- { label: "Schema:SHOW EDGES", value: "SHOW EDGES" },
669
- { label: "Schema:SHOW INDEXES", value: "SHOW INDEXES" },
670
- { label: "统计:CALL db.stats()", value: "CALL db.stats()" },
671
- { label: "版本:SHOW BRANCHES", value: "SHOW BRANCHES" },
672
- { label: "定长跳:MATCH (a)-[r*1..2]->(b) RETURN a,r,b LIMIT 20", value: "MATCH (a)-[r*1..2]->(b) RETURN a,r,b LIMIT 20" }
673
- ];
674
- function dt(e) {
675
- return e === null || ["string", "number", "boolean"].includes(typeof e);
676
- }
677
- function ut(e) {
678
- return e == null ? /* @__PURE__ */ t.jsx("span", { style: { color: "#94a3b8" }, children: "null" }) : dt(e) ? String(e) : /* @__PURE__ */ t.jsx("code", { style: { fontSize: 12, wordBreak: "break-all" }, children: JSON.stringify(e) });
679
- }
680
- function ht() {
681
- var P, Q, z, ae, de;
682
- const [e] = Le(), [n, s] = m(e.get("cypher") || "MATCH (n) RETURN n LIMIT 20"), [o, i] = m(e.get("branch") || re), [p, d] = m(e.get("commit") || ""), [h, E] = m(null), [g, S] = m(null), [v, y] = m(null), [_, I] = m(!1), [a, l] = m(!1), [R, M] = m(null), [D, A] = m(!1), Y = ze(0), O = te(() => Qe(n), [n]), K = ne(async () => {
683
- I(!0), Y.current = performance.now();
684
- try {
685
- const u = await $.query(n, o, p.trim() || void 0);
686
- if (!Array.isArray(u))
687
- throw new Error(`期望行数组,拿到 ${JSON.stringify(u)}`);
688
- E(u), S(null), y(null), M(Math.round(performance.now() - Y.current)), A(!1);
689
- } catch (u) {
690
- E(null), y(u), M(null);
691
- } finally {
692
- I(!1);
693
- }
694
- }, [n, o, p]), Z = () => {
695
- if (O && !D) {
696
- A(!0);
697
- return;
698
- }
699
- K();
700
- }, f = ne(async () => {
701
- l(!0);
702
- try {
703
- const u = await $.explain(n, o);
704
- S(u), y(null);
705
- } catch (u) {
706
- y(u), S(null);
707
- } finally {
708
- l(!1);
709
- }
710
- }, [n, o]), w = te(() => {
711
- if (!Array.isArray(h)) return [];
712
- const u = [], G = /* @__PURE__ */ new Set();
713
- return h.forEach((H) => Object.keys(H || {}).forEach((q) => {
714
- G.has(q) || (G.add(q), u.push(q));
715
- })), u.map((H, q) => ({
716
- title: H,
717
- dataIndex: H,
718
- key: H,
719
- ellipsis: !1,
720
- width: u.length > 4 ? void 0 : 240,
721
- render: (ue) => ut(ue),
722
- // 上游行对象允许同名 key 冲突吗:不会,Map 的 key 唯一;这里用序号兜底防止空 key
723
- ...H === "" ? { title: `(空列名 #${q})` } : {}
724
- }));
725
- }, [h]);
726
- return /* @__PURE__ */ t.jsxs("div", { children: [
727
- /* @__PURE__ */ t.jsx(
728
- ce,
729
- {
730
- title: "图查询",
731
- subtitle: "Cypher 执行与执行计划(数据源 GET /api/graph/query、POST /api/graph/query/explain)"
732
- }
733
- ),
734
- /* @__PURE__ */ t.jsxs(pe, { gutter: 16, children: [
735
- /* @__PURE__ */ t.jsx(F, { span: 16, children: /* @__PURE__ */ t.jsx(C, { title: "查询语句", children: /* @__PURE__ */ t.jsxs(k, { direction: "vertical", style: { width: "100%" }, size: 12, children: [
736
- /* @__PURE__ */ t.jsxs(k, { wrap: !0, children: [
737
- /* @__PURE__ */ t.jsx("span", { style: { fontSize: 12, color: "#64748b" }, children: "插入示例" }),
738
- /* @__PURE__ */ t.jsx(
739
- Ne,
740
- {
741
- size: "small",
742
- style: { width: 340 },
743
- placeholder: "常用只读查询",
744
- showSearch: !0,
745
- optionFilterProp: "label",
746
- options: ct.map((u) => ({ value: u.value, label: u.label })),
747
- onChange: (u) => {
748
- s(u), A(!1);
749
- }
750
- }
751
- )
752
- ] }),
753
- /* @__PURE__ */ t.jsx(
754
- ot,
755
- {
756
- value: n,
757
- rows: 4,
758
- spellCheck: !1,
759
- style: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" },
760
- onChange: (u) => {
761
- s(u.target.value), A(!1);
762
- },
763
- placeholder: "MATCH (n:Person)-[r:KNOWS]->(m) RETURN n.name, r.since, m.name LIMIT 50"
764
- }
765
- ),
766
- /* @__PURE__ */ t.jsxs(k, { wrap: !0, children: [
767
- /* @__PURE__ */ t.jsx("span", { style: { fontSize: 12, color: "#64748b" }, children: "分支" }),
768
- /* @__PURE__ */ t.jsx(
769
- Te,
770
- {
771
- size: "small",
772
- style: { width: 140 },
773
- value: o,
774
- onChange: (u) => i(u.target.value || re)
775
- }
776
- ),
777
- /* @__PURE__ */ t.jsx("span", { style: { fontSize: 12, color: "#64748b" }, children: "或按 commit 时间旅行(留空=按分支 head)" }),
778
- /* @__PURE__ */ t.jsx(
779
- Te,
780
- {
781
- size: "small",
782
- style: { width: 260 },
783
- value: p,
784
- disabled: O,
785
- placeholder: O ? "写语句不能带 commit" : "40 位 commit id",
786
- onChange: (u) => d(u.target.value.trim())
787
- }
788
- )
789
- ] }),
790
- O && /* @__PURE__ */ t.jsx(
791
- L,
792
- {
793
- type: "warning",
794
- showIcon: !0,
795
- message: "这条语句会被上游判定为写操作",
796
- description: D ? "已确认。再点一次「执行」就会真的改图:GraphControlServer 对写语句开一个事务并提交,产生一个新的不可撤回 commit。" : "上游按前缀/关键字判定写(CREATE / MERGE / MATCH…SET|DELETE / DETACH DELETE),执行时会开事务并落一个新 commit。第一次点击只出这条确认,不会执行。"
797
- }
798
- ),
799
- /* @__PURE__ */ t.jsxs(k, { children: [
800
- /* @__PURE__ */ t.jsx(
801
- J,
802
- {
803
- type: "primary",
804
- icon: /* @__PURE__ */ t.jsx(We, {}),
805
- loading: _,
806
- danger: O && D,
807
- onClick: Z,
808
- children: O && D ? "确认执行(会改图)" : "执行"
809
- }
810
- ),
811
- /* @__PURE__ */ t.jsx(J, { icon: /* @__PURE__ */ t.jsx(Je, {}), loading: a, onClick: f, children: "执行计划" })
812
- ] })
813
- ] }) }) }),
814
- /* @__PURE__ */ t.jsxs(F, { span: 8, children: [
815
- /* @__PURE__ */ t.jsxs(C, { title: "本次结果", children: [
816
- /* @__PURE__ */ t.jsxs(pe, { gutter: 16, children: [
817
- /* @__PURE__ */ t.jsx(F, { span: 8, children: /* @__PURE__ */ t.jsx(xe, { title: "返回行数", value: h ? h.length : "-" }) }),
818
- /* @__PURE__ */ t.jsx(F, { span: 8, children: /* @__PURE__ */ t.jsx(
819
- xe,
820
- {
821
- title: "往返耗时",
822
- suffix: "ms",
823
- value: R ?? "-"
824
- }
825
- ) }),
826
- /* @__PURE__ */ t.jsx(F, { span: 8, children: /* @__PURE__ */ t.jsx(xe, { title: "列数", value: w.length || "-" }) })
827
- ] }),
828
- /* @__PURE__ */ t.jsxs("div", { style: { marginTop: 12, fontSize: 12, color: "#64748b" }, children: [
829
- "耗时是前端 axios 往返(含 8888 的代理一跳),不是上游 ",
830
- /* @__PURE__ */ t.jsx("code", { children: "X-Response-Time" }),
831
- "; 后者是跨域响应头,axios 读不到,不拿它当自己的数。"
832
- ] })
833
- ] }),
834
- g && /* @__PURE__ */ t.jsxs(
835
- C,
836
- {
837
- style: { marginTop: 16 },
838
- title: "执行计划 (POST /query/explain)",
839
- extra: /* @__PURE__ */ t.jsxs(k, { size: 4, children: [
840
- /* @__PURE__ */ t.jsx(x, { children: ((P = g.plan) == null ? void 0 : P.queryType) ?? "-" }),
841
- /* @__PURE__ */ t.jsx(x, { color: ((Q = g.plan) == null ? void 0 : Q.estimatedComplexity) === "HIGH" ? "error" : ((z = g.plan) == null ? void 0 : z.estimatedComplexity) === "MEDIUM" ? "warning" : "success", children: ((ae = g.plan) == null ? void 0 : ae.estimatedComplexity) ?? "-" })
842
- ] }),
843
- children: [
844
- /* @__PURE__ */ t.jsx(
845
- W,
846
- {
847
- size: "small",
848
- rowKey: (u, G) => `${u.operation}-${G}`,
849
- dataSource: ((de = g.plan) == null ? void 0 : de.steps) ?? [],
850
- pagination: !1,
851
- columns: [
852
- { title: "算子", dataIndex: "operation", key: "operation", width: 140 },
853
- { title: "说明", dataIndex: "description", key: "description" }
854
- ],
855
- locale: { emptyText: /* @__PURE__ */ t.jsx(X, { title: "上游没有给出算子" }) }
856
- }
857
- ),
858
- /* @__PURE__ */ t.jsxs("div", { style: { marginTop: 8, fontSize: 12, color: "#64748b" }, children: [
859
- "这份计划是 ",
860
- /* @__PURE__ */ t.jsx("code", { children: "handleExplain" }),
861
- " 按字符串前缀 + 关键字命中拼出来的 (MATCH→Scan、含 WHERE→Filter…),",
862
- /* @__PURE__ */ t.jsx("b", { children: "不是代价估算器" }),
863
- "; 复杂度只有 HIGH/MEDIUM/LOW 三档。别按真实执行成本解读。"
864
- ] })
865
- ]
866
- }
867
- )
868
- ] })
869
- ] }),
870
- /* @__PURE__ */ t.jsx(C, { style: { marginTop: 16 }, title: h ? `结果集(${h.length} 行)` : "结果集", children: v ? /* @__PURE__ */ t.jsx(
871
- L,
872
- {
873
- type: "error",
874
- showIcon: !0,
875
- message: `查询失败:${se(v)}`,
876
- description: "上游所有 catch 分支都只回 {error: msg}:Cypher 语法错、Tag 不存在、head 冲突(409 Stale head)都在这里。503 则是内嵌实例没 bind。"
877
- }
878
- ) : /* @__PURE__ */ t.jsx(
879
- W,
880
- {
881
- size: "small",
882
- rowKey: (u, G) => G,
883
- loading: _,
884
- dataSource: h ?? [],
885
- columns: w,
886
- scroll: { x: "max-content" },
887
- pagination: { size: "small", pageSize: 20, showSizeChanger: !0 },
888
- locale: {
889
- emptyText: h === null ? /* @__PURE__ */ t.jsx(X, { title: "还没有执行过查询", description: "点「执行」或「执行计划」。这里没有预置示例数据。" }) : /* @__PURE__ */ t.jsx(X, { title: "返回 0 行", description: "查询成功执行、上游确实给了空数组(空图或未命中)。" })
890
- }
891
- }
892
- ) }),
893
- h && h.length > 0 && /* @__PURE__ */ t.jsx(C, { style: { marginTop: 16 }, title: "原始响应(第一行,逐字段照上游 JSON 渲染)", children: /* @__PURE__ */ t.jsx("pre", { style: { margin: 0, fontSize: 12, overflow: "auto", maxHeight: 260 }, children: JSON.stringify(h[0], null, 2) }) })
894
- ] });
895
- }
896
- function je(e) {
897
- if (e == null) return "-";
898
- const n = new Date(e);
899
- return Number.isNaN(n.getTime()) ? String(e) : n.toLocaleString("zh-CN", { hour12: !1 });
900
- }
901
- function mt() {
902
- const e = De(), [n, s] = m([]), [o, i] = m([]), [p, d] = m(!1), [h, E] = m(null), [g, S] = m(null), v = ne(async () => {
903
- d(!0);
904
- try {
905
- const [a, l] = await Promise.all([$.branches(), $.commits()]);
906
- if (!Array.isArray(a)) throw new Error(`branches 应为字符串数组,拿到 ${JSON.stringify(a)}`);
907
- if (!Array.isArray(l)) throw new Error(`commits 应为对象数组,拿到 ${JSON.stringify(l)}`);
908
- s(a), i(l), E(null);
909
- } catch (a) {
910
- s([]), i([]), E(a);
911
- } finally {
912
- d(!1);
913
- }
914
- }, []);
915
- Re(() => {
916
- v();
917
- }, [v]);
918
- const y = te(() => {
919
- const a = /* @__PURE__ */ new Map();
920
- return o.forEach((l) => a.set(l.id, l)), a;
921
- }, [o]), _ = te(() => {
922
- const a = /* @__PURE__ */ new Map();
923
- return o.forEach((l) => {
924
- const R = a.get(l.branch);
925
- (!R || (l.timestamp ?? 0) >= (R.timestamp ?? 0)) && a.set(l.branch, l);
926
- }), a;
927
- }, [o]), I = te(() => [...g ? o.filter((l) => l.branch === g) : o].sort((l, R) => (R.timestamp ?? 0) - (l.timestamp ?? 0)), [o, g]);
928
- return h ? /* @__PURE__ */ t.jsxs("div", { children: [
929
- /* @__PURE__ */ t.jsx(ce, { title: "分支与提交", subtitle: "GET /api/graph/meta/branches + /api/graph/meta/commits" }),
930
- /* @__PURE__ */ t.jsx(
931
- L,
932
- {
933
- type: we(h) ? "error" : "warning",
934
- showIcon: !0,
935
- message: `提交历史读取失败:${se(h)}`,
936
- description: we(h) ? "503 = 本 JVM 的内嵌 GraphControlServer 没 bind 成功(不是「图里没有提交」)。去「实例与端口」页确认 boundPort 与 foreignListenerSuspected。" : void 0
937
- }
938
- )
939
- ] }) : /* @__PURE__ */ t.jsxs("div", { children: [
940
- /* @__PURE__ */ t.jsx(
941
- ce,
942
- {
943
- title: "分支与提交",
944
- subtitle: "z-graph 的 Git 式版本面:分支 head 与全量 commit 图(数据源 /api/graph/meta/branches、/meta/commits)"
945
- }
946
- ),
947
- /* @__PURE__ */ t.jsx(
948
- C,
949
- {
950
- title: /* @__PURE__ */ t.jsxs(k, { children: [
951
- /* @__PURE__ */ t.jsx(_e, {}),
952
- "分支(",
953
- n.length,
954
- ")"
955
- ] }),
956
- extra: /* @__PURE__ */ t.jsx(J, { icon: /* @__PURE__ */ t.jsx(ke, {}), loading: p, onClick: v, children: "刷新" }),
957
- children: /* @__PURE__ */ t.jsxs(pe, { gutter: [16, 16], children: [
958
- n.length === 0 && !p && /* @__PURE__ */ t.jsx(F, { span: 24, children: /* @__PURE__ */ t.jsx(
959
- X,
960
- {
961
- title: "一个分支都没有",
962
- description: "GET /api/graph/meta/branches 是通的、返回了空数组。内嵌实例若刚启动,GraphVersionStore 构造函数会先落一个 Initial graph 根提交并把 main 指过去,所以真为空通常说明连的不是你以为的那个实例。"
963
- }
964
- ) }),
965
- n.map((a) => {
966
- const l = _.get(a), R = o.filter((M) => M.branch === a).length;
967
- return /* @__PURE__ */ t.jsx(F, { span: 8, children: /* @__PURE__ */ t.jsx(
968
- C,
969
- {
970
- size: "small",
971
- hoverable: !0,
972
- onClick: () => S(g === a ? null : a),
973
- title: /* @__PURE__ */ t.jsxs(k, { children: [
974
- /* @__PURE__ */ t.jsx(_e, {}),
975
- /* @__PURE__ */ t.jsx("code", { children: a })
976
- ] }),
977
- extra: g === a ? /* @__PURE__ */ t.jsx(x, { color: "processing", children: "已筛选" }) : null,
978
- children: /* @__PURE__ */ t.jsxs(k, { direction: "vertical", size: 2, style: { width: "100%" }, children: [
979
- /* @__PURE__ */ t.jsxs("span", { style: { fontSize: 12 }, children: [
980
- "head:",
981
- /* @__PURE__ */ t.jsx("code", { children: l ? `${l.id.slice(0, 12)}…` : "未知" })
982
- ] }),
983
- /* @__PURE__ */ t.jsxs("span", { style: { fontSize: 12, color: "#64748b" }, children: [
984
- "本分支提交 ",
985
- R,
986
- " · 点 ",
987
- (l == null ? void 0 : l.nodeCount) ?? "-",
988
- " · 边 ",
989
- (l == null ? void 0 : l.edgeCount) ?? "-"
990
- ] }),
991
- /* @__PURE__ */ t.jsxs("span", { style: { fontSize: 12, color: "#64748b" }, children: [
992
- "最近提交:",
993
- l ? je(l.timestamp) : "-"
994
- ] })
995
- ] })
996
- }
997
- ) }, a);
998
- })
999
- ] })
1000
- }
1001
- ),
1002
- /* @__PURE__ */ t.jsx(
1003
- C,
1004
- {
1005
- style: { marginTop: 16 },
1006
- title: /* @__PURE__ */ t.jsxs(k, { children: [
1007
- /* @__PURE__ */ t.jsx(Ye, {}),
1008
- "提交历史(全量 commit 图,",
1009
- I.length,
1010
- " 条)"
1011
- ] }),
1012
- extra: g ? /* @__PURE__ */ t.jsx(k, { children: /* @__PURE__ */ t.jsxs(x, { color: "processing", closable: !0, onClose: () => S(null), children: [
1013
- "branch = ",
1014
- g
1015
- ] }) }) : /* @__PURE__ */ t.jsx("span", { style: { fontSize: 12, color: "#64748b" }, children: "点分支卡片可筛选" }),
1016
- children: /* @__PURE__ */ t.jsx(
1017
- W,
1018
- {
1019
- size: "small",
1020
- rowKey: "id",
1021
- loading: p,
1022
- dataSource: I,
1023
- pagination: { size: "small", pageSize: 20, showSizeChanger: !0 },
1024
- columns: [
1025
- {
1026
- title: "commit",
1027
- dataIndex: "id",
1028
- key: "id",
1029
- width: 170,
1030
- render: (a) => /* @__PURE__ */ t.jsx(ye, { title: a, children: /* @__PURE__ */ t.jsx("code", { children: `${String(a).slice(0, 12)}…` }) })
1031
- },
1032
- {
1033
- title: "分支",
1034
- dataIndex: "branch",
1035
- key: "branch",
1036
- width: 110,
1037
- render: (a) => /* @__PURE__ */ t.jsx(x, { color: a === re ? "blue" : "geekblue", children: a })
1038
- },
1039
- { title: "作者", dataIndex: "author", key: "author", width: 110 },
1040
- {
1041
- title: "说明",
1042
- dataIndex: "message",
1043
- key: "message",
1044
- ellipsis: { showTitle: !1 },
1045
- render: (a) => /* @__PURE__ */ t.jsx(ye, { title: a, placement: "topLeft", children: /* @__PURE__ */ t.jsx("span", { children: a || "-" }) })
1046
- },
1047
- { title: "时间", dataIndex: "timestamp", key: "timestamp", width: 190, render: je },
1048
- { title: "点", dataIndex: "nodeCount", key: "nodeCount", width: 70 },
1049
- { title: "边", dataIndex: "edgeCount", key: "edgeCount", width: 70 },
1050
- {
1051
- title: "父提交",
1052
- dataIndex: "parents",
1053
- key: "parents",
1054
- width: 150,
1055
- render: (a) => !Array.isArray(a) || a.length === 0 ? /* @__PURE__ */ t.jsx(x, { children: "根提交" }) : /* @__PURE__ */ t.jsx(k, { direction: "vertical", size: 0, children: a.map((l) => /* @__PURE__ */ t.jsx(
1056
- ye,
1057
- {
1058
- title: y.has(l) ? `${y.get(l).message} · ${je(y.get(l).timestamp)}` : l,
1059
- children: /* @__PURE__ */ t.jsx("code", { style: { fontSize: 12 }, children: `${String(l).slice(0, 8)}…` })
1060
- },
1061
- l
1062
- )) })
1063
- },
1064
- {
1065
- title: "操作",
1066
- key: "action",
1067
- width: 130,
1068
- render: (a, l) => /* @__PURE__ */ t.jsx(
1069
- J,
1070
- {
1071
- type: "link",
1072
- size: "small",
1073
- onClick: () => e(`/graph/query?branch=${encodeURIComponent(l.branch)}&commit=${encodeURIComponent(l.id)}`),
1074
- children: "按此版本查"
1075
- }
1076
- )
1077
- }
1078
- ],
1079
- locale: {
1080
- emptyText: /* @__PURE__ */ t.jsx(
1081
- X,
1082
- {
1083
- title: "没有任何提交",
1084
- description: "GET /api/graph/meta/commits 通了但返回空数组 —— 上游 GraphVersionStore 构造时至少会有一个 Initial graph 根提交,所以这一格为空要去「实例与端口」页确认应答者。"
1085
- }
1086
- )
1087
- }
1088
- }
1089
- )
1090
- }
1091
- )
1092
- ] });
1093
- }
1094
- const pt = [
1095
- { title: "方法", dataIndex: "method", key: "method", width: 90, render: (e) => /* @__PURE__ */ t.jsx(x, { children: e }) },
1096
- { title: "路径", dataIndex: "path", key: "path", ellipsis: !0 },
1097
- { title: "来源 IP", dataIndex: "clientIp", key: "clientIp", width: 150 },
1098
- {
1099
- title: "状态",
1100
- dataIndex: "status",
1101
- key: "status",
1102
- width: 90,
1103
- render: (e) => /* @__PURE__ */ t.jsx(x, { color: e >= 500 ? "error" : e >= 400 ? "warning" : "success", children: e })
1104
- },
1105
- { title: "耗时", dataIndex: "elapsedMs", key: "elapsedMs", width: 90, render: (e) => `${e} ms` },
1106
- { title: "请求 ID", dataIndex: "requestId", key: "requestId", width: 170, render: (e) => /* @__PURE__ */ t.jsx("code", { children: e }) },
1107
- { title: "线程", dataIndex: "thread", key: "thread", width: 160, render: (e) => /* @__PURE__ */ t.jsx("code", { children: e }) }
1108
- ];
1109
- function ft(e) {
1110
- if (e == null) return "-";
1111
- const n = Math.floor(e / 86400), s = Math.floor(e % 86400 / 3600), o = Math.floor(e % 3600 / 60);
1112
- return n > 0 ? `${n} 天 ${s} 时 ${o} 分` : s > 0 ? `${s} 时 ${o} 分 ${e % 60} 秒` : `${o} 分 ${e % 60} 秒`;
1113
- }
1114
- function Ee(e) {
1115
- return e == null ? "-" : `${(e / 1024 / 1024).toFixed(1)} MiB`;
1116
- }
1117
- function Oe(e, n = "是", s = "否", o = "error") {
1118
- return e == null ? "-" : e ? /* @__PURE__ */ t.jsx(x, { color: "success", children: n }) : /* @__PURE__ */ t.jsx(x, { color: o, children: s });
1119
- }
1120
- function xt() {
1121
- const [e, n] = m(null), [s, o] = m(null), [i, p] = m(null), [d, h] = m(null), [E, g] = m(!1), [S, v] = m(null), y = ne(async () => {
1122
- g(!0);
1123
- try {
1124
- const a = await $.instance();
1125
- n(a), v(null);
1126
- try {
1127
- const [l, R] = await Promise.all([$.metrics(), $.logs({ limit: 20 })]);
1128
- o(l), p(null), h(R);
1129
- } catch (l) {
1130
- o(null), h(null), p(l);
1131
- }
1132
- } catch (a) {
1133
- n(null), v(a);
1134
- } finally {
1135
- g(!1);
1136
- }
1137
- }, []);
1138
- Re(() => {
1139
- y();
1140
- }, [y]);
1141
- const _ = !S && e && !e.embeddedRunning, I = !S && e && e.foreignListenerSuspected;
1142
- return /* @__PURE__ */ t.jsxs("div", { children: [
1143
- /* @__PURE__ */ t.jsx(
1144
- ce,
1145
- {
1146
- title: "实例与端口",
1147
- subtitle: "GraphProxyController 自省接口 GET /api/graph/__instance + 控制面自报的 /meta/metrics"
1148
- }
1149
- ),
1150
- /* @__PURE__ */ t.jsx(C, { extra: /* @__PURE__ */ t.jsx(k, { children: /* @__PURE__ */ t.jsx(J, { icon: /* @__PURE__ */ t.jsx(ke, {}), loading: E, onClick: y, children: "刷新" }) }), children: S ? /* @__PURE__ */ t.jsx(
1151
- L,
1152
- {
1153
- type: "error",
1154
- showIcon: !0,
1155
- message: `自省接口调用失败:${se(S)}`,
1156
- description: "/api/graph/__instance 由 z-opc 自己的 controller 直接应答,不经过内嵌服务;它都不通说明是 z-opc 侧的路由/认证问题(302 到登录页 = 没带会话 Cookie)。"
1157
- }
1158
- ) : /* @__PURE__ */ t.jsxs(t.Fragment, { children: [
1159
- I && /* @__PURE__ */ t.jsx(
1160
- L,
1161
- {
1162
- type: "error",
1163
- showIcon: !0,
1164
- style: { marginBottom: 16 },
1165
- message: "配置端口上有监听者,但它不是本 JVM —— 这一路连的是外部进程,页面数据不能算孵化结果",
1166
- description: `boundPort=0(本 JVM 的 GraphControlServer 没 bind 上)而 ${e.configuredPort} 端口 TCP 可连。用 lsof -nP -iTCP:${e.configuredPort} -sTCP:LISTEN 取属主 PID,与本页 jvm 字段(${e.jvm})比对;对不上就是假孵化。处理方式只有两种:让出该端口,或给 zgraph.server.port 换一个端口——两者都需要定夺,代理层不会替你去占端口。`
1167
- }
1168
- ),
1169
- _ && !I && /* @__PURE__ */ t.jsx(
1170
- L,
1171
- {
1172
- type: "warning",
1173
- showIcon: !0,
1174
- style: { marginBottom: 16 },
1175
- message: "内嵌 GraphControlServer 没有 bind 成功,其余三页的空数组是「没起」而不是「图里没数据」",
1176
- description: `bindError = ${e.bindError ?? "null"}。proxy 只在 boundPort>0 时才转发,所以现在所有 /api/graph/meta* 与 /api/graph/query 都会拿到 503。`
1177
- }
1178
- ),
1179
- /* @__PURE__ */ t.jsx(
1180
- be,
1181
- {
1182
- bordered: !0,
1183
- size: "small",
1184
- column: 2,
1185
- items: [
1186
- {
1187
- key: "jvm",
1188
- label: "应答的 JVM (pid@host)",
1189
- children: /* @__PURE__ */ t.jsx("code", { children: (e == null ? void 0 : e.jvm) ?? "-" })
1190
- },
1191
- {
1192
- key: "lifecycleBean",
1193
- label: "内嵌配置已装载",
1194
- children: e ? e.lifecycleBean ? /* @__PURE__ */ t.jsx(x, { color: "success", children: "是" }) : /* @__PURE__ */ t.jsx(x, { color: "error", children: "否(zgraph.enabled≠true)" }) : "-"
1195
- },
1196
- {
1197
- key: "configuredPort",
1198
- label: "配置端口",
1199
- children: (e == null ? void 0 : e.configuredPort) ?? "-"
1200
- },
1201
- {
1202
- key: "boundPort",
1203
- label: "内嵌实例实际 bind 端口",
1204
- children: e ? e.boundPort > 0 ? /* @__PURE__ */ t.jsx(x, { color: "success", children: e.boundPort }) : /* @__PURE__ */ t.jsx(x, { color: "error", children: "0(没 bind 上)" }) : "-"
1205
- },
1206
- {
1207
- key: "embeddedRunning",
1208
- label: "内嵌控制面在跑",
1209
- children: Oe(e == null ? void 0 : e.embeddedRunning)
1210
- },
1211
- {
1212
- key: "acceptingNow",
1213
- label: "本实例端口此刻可连",
1214
- children: Oe(e == null ? void 0 : e.acceptingNow, "是", "否", "warning")
1215
- },
1216
- {
1217
- key: "foreign",
1218
- label: "外来监听者嫌疑",
1219
- children: e ? e.foreignListenerSuspected ? /* @__PURE__ */ t.jsx(x, { color: "error", children: "有(应答者不是本 JVM)" }) : /* @__PURE__ */ t.jsx(x, { color: "success", children: "无" }) : "-"
1220
- },
1221
- {
1222
- key: "token",
1223
- label: "上游 API Token (环境变量)",
1224
- children: e ? e.apiTokenConfigured ? /* @__PURE__ */ t.jsx(x, { color: "success", children: "已设置" }) : /* @__PURE__ */ t.jsx(x, { color: "warning", children: "未设置 = 通配端口不鉴权" }) : "-"
1225
- },
1226
- {
1227
- key: "bindError",
1228
- label: "bind 失败原因",
1229
- children: e != null && e.bindError ? /* @__PURE__ */ t.jsx("code", { children: e.bindError }) : "-"
1230
- },
1231
- {
1232
- key: "path",
1233
- label: "转发前缀",
1234
- children: /* @__PURE__ */ t.jsx("code", { children: "/api/graph/** → 127.0.0.1:<boundPort>" })
1235
- }
1236
- ]
1237
- }
1238
- ),
1239
- /* @__PURE__ */ t.jsxs("div", { style: { marginTop: 12, color: "#64748b", fontSize: 12 }, children: [
1240
- "代理是逐字节透传,不在 z-opc 侧重拼 JSON。上游注册的端点:",
1241
- ((e == null ? void 0 : e.upstreamEndpoints) ?? []).map((a) => /* @__PURE__ */ t.jsx(
1242
- "code",
1243
- {
1244
- style: { marginRight: 6 },
1245
- children: a
1246
- },
1247
- a
1248
- ))
1249
- ] })
1250
- ] }) }),
1251
- /* @__PURE__ */ t.jsx(
1252
- C,
1253
- {
1254
- title: "控制面自报指标 (GET /api/graph/meta/metrics)",
1255
- style: { marginTop: 16 },
1256
- extra: /* @__PURE__ */ t.jsx(x, { color: "blue", children: "走上游转发,内嵌没起时这里必然报错" }),
1257
- children: i ? /* @__PURE__ */ t.jsx(
1258
- L,
1259
- {
1260
- type: "error",
1261
- showIcon: !0,
1262
- message: `读不到 /meta/metrics:${se(i)}`
1263
- }
1264
- ) : /* @__PURE__ */ t.jsx(
1265
- be,
1266
- {
1267
- bordered: !0,
1268
- size: "small",
1269
- column: 3,
1270
- items: [
1271
- {
1272
- key: "uptime",
1273
- label: "控制面存活时长",
1274
- children: `${ft(s == null ? void 0 : s.uptimeSeconds)}(${(s == null ? void 0 : s.uptimeFormatted) ?? "-"})`
1275
- },
1276
- { key: "totalRequests", label: "累计请求", children: (s == null ? void 0 : s.totalRequests) ?? "-" },
1277
- { key: "errorResponses", label: "错误应答", children: (s == null ? void 0 : s.errorResponses) ?? "-" },
1278
- { key: "errorRate", label: "错误率", children: (s == null ? void 0 : s.errorRate) ?? "-" },
1279
- { key: "authFailures", label: "鉴权失败", children: (s == null ? void 0 : s.authFailures) ?? "-" },
1280
- { key: "rateLimited", label: "被限流", children: (s == null ? void 0 : s.rateLimitedRequests) ?? "-" },
1281
- {
1282
- key: "heap",
1283
- label: "JVM 堆 (已用/已申请/上限)",
1284
- children: s != null && s.jvmMemory ? `${Ee(s.jvmMemory.usedBytes)} / ${Ee(s.jvmMemory.totalBytes)} / ${Ee(s.jvmMemory.maxBytes)}` : "-"
1285
- },
1286
- { key: "cpu", label: "可用核数", children: (s == null ? void 0 : s.availableProcessors) ?? "-" },
1287
- {
1288
- key: "rate",
1289
- label: "限流阈值/活跃桶",
1290
- children: `${(s == null ? void 0 : s.rateLimitPerMinute) ?? "-"} / ${(s == null ? void 0 : s.activeRateBuckets) ?? "-"}`
1291
- }
1292
- ]
1293
- }
1294
- )
1295
- }
1296
- ),
1297
- /* @__PURE__ */ t.jsx(C, { title: "最近请求日志 (GET /api/graph/meta/logs?limit=20)", style: { marginTop: 16 }, children: d ? /* @__PURE__ */ t.jsx(
1298
- W,
1299
- {
1300
- size: "small",
1301
- rowKey: (a) => `${a.timestamp}-${a.requestId}`,
1302
- loading: E,
1303
- dataSource: d.entries ?? [],
1304
- columns: pt,
1305
- pagination: {
1306
- size: "small",
1307
- total: d.total ?? 0,
1308
- pageSize: 20,
1309
- showTotal: (a) => `环形缓冲 ${a} 条 / 容量 ${d.bufferSize ?? "-"}`
1310
- }
1311
- }
1312
- ) : /* @__PURE__ */ t.jsx(
1313
- L,
1314
- {
1315
- type: "info",
1316
- showIcon: !0,
1317
- message: i ? "读不到请求日志(同上)" : "日志缓冲为空 —— 控制面刚起或还没打过请求"
1318
- }
1319
- ) })
1320
- ] });
1321
- }
1322
- function kt() {
1323
- return /* @__PURE__ */ t.jsxs(Ge, { children: [
1324
- /* @__PURE__ */ t.jsx(le, { index: !0, element: /* @__PURE__ */ t.jsx(He, { to: "schema", replace: !0 }) }),
1325
- /* @__PURE__ */ t.jsx(le, { path: "schema", element: /* @__PURE__ */ t.jsx(it, {}) }),
1326
- /* @__PURE__ */ t.jsx(le, { path: "query", element: /* @__PURE__ */ t.jsx(ht, {}) }),
1327
- /* @__PURE__ */ t.jsx(le, { path: "branches", element: /* @__PURE__ */ t.jsx(mt, {}) }),
1328
- /* @__PURE__ */ t.jsx(le, { path: "instance", element: /* @__PURE__ */ t.jsx(xt, {}) })
1329
- ] });
1330
- }
1331
- export {
1332
- re as D,
1333
- kt as G,
1334
- xt as I,
1335
- Ke as a,
1336
- se as b,
1337
- Rt as c,
1338
- we as d,
1339
- St as f,
1340
- $ as g,
1341
- Qe as i,
1342
- t as j,
1343
- wt as s
1344
- };