@topdatasec/probe 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +38 -0
- package/lib/style.css +1 -0
- package/lib/t-probe-ui.es.js +1198 -0
- package/lib/t-probe-ui.umd.js +1 -0
- package/package.json +67 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Topdatasec
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @topdatasec/probe
|
|
2
|
+
|
|
3
|
+
探针部署管理组件库
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @topdatasec/probe
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @topdatasec/probe
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 使用
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { createApp } from 'vue'
|
|
17
|
+
import TProbeUI from '@topdatasec/probe'
|
|
18
|
+
import App from './App.vue'
|
|
19
|
+
|
|
20
|
+
const app = createApp(App)
|
|
21
|
+
app.use(TProbeUI, {
|
|
22
|
+
envURL: 'your-api-url',
|
|
23
|
+
prodType: 'your-prod-type'
|
|
24
|
+
})
|
|
25
|
+
app.mount('#app')
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 按需引入
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { TCProbe } from '@topdatasec/probe'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 功能
|
|
35
|
+
|
|
36
|
+
- **安装包管理**: 上传和管理探针安装包
|
|
37
|
+
- **探针部署**: 配置和部署探针到服务器
|
|
38
|
+
- **部署历史**: 查看部署历史记录
|
package/lib/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.ip-port-item[data-v-16fc9c64]{border-style:dashed}.el-tab-pane[data-v-7172e806]{height:100%}
|
|
@@ -0,0 +1,1198 @@
|
|
|
1
|
+
import { defineComponent as H, useModel as ne, resolveComponent as p, openBlock as y, createElementBlock as x, Fragment as A, renderList as K, createVNode as e, withCtx as E, unref as M, createCommentVNode as O, ref as w, reactive as j, createTextVNode as q, createElementVNode as v, toDisplayString as C, mergeProps as X, createBlock as W } from "vue";
|
|
2
|
+
import { CircleClose as re, UploadFilled as ae } from "@element-plus/icons-vue";
|
|
3
|
+
import { TdsTable as se, TdsForm as Y } from "topdatasec-ui";
|
|
4
|
+
import { ElMessage as Q } from "element-plus";
|
|
5
|
+
const ue = {
|
|
6
|
+
key: 0,
|
|
7
|
+
class: "absolute right--13px text-#f56c6c hover:cursor-pointer"
|
|
8
|
+
}, ie = /* @__PURE__ */ H({
|
|
9
|
+
__name: "IpPortList",
|
|
10
|
+
props: {
|
|
11
|
+
modelValue: { default: () => [{ ip: "", port: "" }] },
|
|
12
|
+
modelModifiers: {}
|
|
13
|
+
},
|
|
14
|
+
emits: ["update:modelValue"],
|
|
15
|
+
setup(t) {
|
|
16
|
+
const l = ne(t, "modelValue"), { $t: o } = window;
|
|
17
|
+
function s() {
|
|
18
|
+
l.value = [...l.value, { ip: "", port: "" }];
|
|
19
|
+
}
|
|
20
|
+
function c(m) {
|
|
21
|
+
const r = [...l.value];
|
|
22
|
+
r.splice(m, 1), l.value = r;
|
|
23
|
+
}
|
|
24
|
+
return (m, r) => {
|
|
25
|
+
const _ = p("el-input"), T = p("el-form-item"), b = p("el-icon"), a = p("el-button");
|
|
26
|
+
return y(), x("div", null, [
|
|
27
|
+
(y(!0), x(A, null, K(l.value, (u, S) => (y(), x("div", {
|
|
28
|
+
key: S,
|
|
29
|
+
class: "flex form-m0 w-100% mb-5px"
|
|
30
|
+
}, [
|
|
31
|
+
e(T, { class: "w-70%" }, {
|
|
32
|
+
default: E(() => [
|
|
33
|
+
e(_, {
|
|
34
|
+
modelValue: u.ip,
|
|
35
|
+
"onUpdate:modelValue": (L) => u.ip = L,
|
|
36
|
+
class: "min-w-300px w-100%",
|
|
37
|
+
placeholder: "IP"
|
|
38
|
+
}, null, 8, ["modelValue", "onUpdate:modelValue"])
|
|
39
|
+
]),
|
|
40
|
+
_: 2
|
|
41
|
+
}, 1024),
|
|
42
|
+
e(T, { class: "ml-auto w-30%" }, {
|
|
43
|
+
default: E(() => [
|
|
44
|
+
e(_, {
|
|
45
|
+
modelValue: u.port,
|
|
46
|
+
"onUpdate:modelValue": (L) => u.port = L,
|
|
47
|
+
class: "ml-10",
|
|
48
|
+
placeholder: M(o)("global.port")
|
|
49
|
+
}, null, 8, ["modelValue", "onUpdate:modelValue", "placeholder"])
|
|
50
|
+
]),
|
|
51
|
+
_: 2
|
|
52
|
+
}, 1024),
|
|
53
|
+
l.value.length > 1 ? (y(), x("div", ue, [
|
|
54
|
+
e(b, {
|
|
55
|
+
onClick: (L) => c(S)
|
|
56
|
+
}, {
|
|
57
|
+
default: E(() => [
|
|
58
|
+
e(M(re))
|
|
59
|
+
]),
|
|
60
|
+
_: 1
|
|
61
|
+
}, 8, ["onClick"])
|
|
62
|
+
])) : O("", !0)
|
|
63
|
+
]))), 128)),
|
|
64
|
+
e(a, {
|
|
65
|
+
class: "w-100% mt-10px border-dashed!",
|
|
66
|
+
icon: "Plus",
|
|
67
|
+
onClick: s
|
|
68
|
+
})
|
|
69
|
+
]);
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
const ee = (t, l) => {
|
|
74
|
+
const o = t.__vccOpts || t;
|
|
75
|
+
for (const [s, c] of l)
|
|
76
|
+
o[s] = c;
|
|
77
|
+
return o;
|
|
78
|
+
}, V = /* @__PURE__ */ ee(ie, [["__scopeId", "data-v-16fc9c64"]]), I = 5 * 1024 * 1024;
|
|
79
|
+
function de(t, l) {
|
|
80
|
+
return new Promise((o, s) => {
|
|
81
|
+
const c = new (void 0)(), m = new FileReader(), r = Math.ceil(t.size / I);
|
|
82
|
+
let _ = 0;
|
|
83
|
+
m.onload = (b) => {
|
|
84
|
+
var a;
|
|
85
|
+
if (c.append((a = b.target) == null ? void 0 : a.result), _++, l) {
|
|
86
|
+
const u = Math.floor(_ / r * 100);
|
|
87
|
+
l(u);
|
|
88
|
+
}
|
|
89
|
+
if (_ < r)
|
|
90
|
+
T();
|
|
91
|
+
else {
|
|
92
|
+
const u = c.end();
|
|
93
|
+
o(u);
|
|
94
|
+
}
|
|
95
|
+
}, m.onerror = () => {
|
|
96
|
+
s(new Error("MD5计算失败"));
|
|
97
|
+
};
|
|
98
|
+
function T() {
|
|
99
|
+
const b = _ * I, a = Math.min(b + I, t.size);
|
|
100
|
+
m.readAsArrayBuffer(t.slice(b, a));
|
|
101
|
+
}
|
|
102
|
+
T();
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async function pe(t, l, o, s, c, m = "/api/upload/chunk") {
|
|
106
|
+
const r = l * I, _ = Math.min(r + I, t.size), T = t.slice(r, _), b = _ - r, a = new FormData();
|
|
107
|
+
a.append("file", T, t.name), a.append("chunkIndex", String(l + 1)), a.append("totalChunkNum", String(o)), a.append("fileMd5", s), a.append("tds_token", c), a.append("fileName", t.name), a.append("fileSize", String(t.size)), a.append("fileExt", t.name.substring(t.name.lastIndexOf(".") + 1)), a.append("chunkSize", String(I)), a.append("currentChunkSize", String(b));
|
|
108
|
+
try {
|
|
109
|
+
const u = await fetch(m, {
|
|
110
|
+
method: "POST",
|
|
111
|
+
body: a
|
|
112
|
+
});
|
|
113
|
+
if (console.log(u, "upload response"), !u.ok)
|
|
114
|
+
throw new Error(`分片 ${l + 1} 上传失败,HTTP状态码: ${u.status}`);
|
|
115
|
+
const S = await u.json();
|
|
116
|
+
if (console.log(S, "upload result"), S.recode !== 0)
|
|
117
|
+
throw new Error(`上传失败: ${S.remsg || "未知错误"}`);
|
|
118
|
+
return !0;
|
|
119
|
+
} catch (u) {
|
|
120
|
+
throw console.error(`上传分片 ${l + 1} 时出错:`, u), u;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function ce(t, l = "/api/upload/merge") {
|
|
124
|
+
try {
|
|
125
|
+
if (!(await fetch(l, {
|
|
126
|
+
method: "POST",
|
|
127
|
+
headers: {
|
|
128
|
+
"Content-Type": "application/json"
|
|
129
|
+
},
|
|
130
|
+
body: JSON.stringify(t)
|
|
131
|
+
})).ok)
|
|
132
|
+
throw new Error("文件合并失败");
|
|
133
|
+
return !0;
|
|
134
|
+
} catch (o) {
|
|
135
|
+
return console.error("合并分片时出错:", o), !1;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function me(t) {
|
|
139
|
+
const { token: l, file: o, fileDesc: s = "", uploadUrl: c = "/api/upload/chunk", mergeUrl: m = "/api/upload/merge", onProgress: r, onSuccess: _, onError: T } = t;
|
|
140
|
+
try {
|
|
141
|
+
r == null || r({
|
|
142
|
+
progress: 0,
|
|
143
|
+
statusText: "正在计算文件MD5...",
|
|
144
|
+
status: "preparing"
|
|
145
|
+
});
|
|
146
|
+
const a = await de(o, (g) => {
|
|
147
|
+
r == null || r({
|
|
148
|
+
progress: Math.floor(g * 0.1),
|
|
149
|
+
// MD5计算占10%进度
|
|
150
|
+
statusText: `正在计算文件MD5... ${g}%`,
|
|
151
|
+
status: "preparing"
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
r == null || r({
|
|
155
|
+
progress: 10,
|
|
156
|
+
statusText: "MD5计算完成",
|
|
157
|
+
status: "preparing",
|
|
158
|
+
fileMd5: a
|
|
159
|
+
});
|
|
160
|
+
const u = Math.ceil(o.size / I);
|
|
161
|
+
r == null || r({
|
|
162
|
+
progress: 10,
|
|
163
|
+
statusText: `开始上传 (共${u}个分片)`,
|
|
164
|
+
status: "uploading",
|
|
165
|
+
fileMd5: a
|
|
166
|
+
});
|
|
167
|
+
for (let g = 0; g < u; g++) {
|
|
168
|
+
const P = Math.floor(g / u * 80);
|
|
169
|
+
r == null || r({
|
|
170
|
+
progress: 10 + P,
|
|
171
|
+
statusText: `正在上传分片 ${g + 1}/${u}`,
|
|
172
|
+
status: "uploading",
|
|
173
|
+
fileMd5: a
|
|
174
|
+
}), await pe(o, g, u, a, l, c);
|
|
175
|
+
}
|
|
176
|
+
r == null || r({
|
|
177
|
+
progress: 90,
|
|
178
|
+
statusText: "正在合并文件...",
|
|
179
|
+
status: "merging",
|
|
180
|
+
fileMd5: a
|
|
181
|
+
});
|
|
182
|
+
const S = o.name.substring(o.name.lastIndexOf(".") + 1), L = {
|
|
183
|
+
fileDesc: s,
|
|
184
|
+
fileExt: S,
|
|
185
|
+
fileMd5: a,
|
|
186
|
+
fileName: o.name,
|
|
187
|
+
fileSize: String(o.size),
|
|
188
|
+
tds_token: l,
|
|
189
|
+
totalChunkNum: String(u)
|
|
190
|
+
};
|
|
191
|
+
if (!await ce(L, m))
|
|
192
|
+
throw new Error("文件合并失败");
|
|
193
|
+
r == null || r({
|
|
194
|
+
progress: 100,
|
|
195
|
+
statusText: "上传成功",
|
|
196
|
+
status: "success",
|
|
197
|
+
fileMd5: a
|
|
198
|
+
}), Q.success(`${o.name} 上传成功`), _ == null || _(a);
|
|
199
|
+
} catch (b) {
|
|
200
|
+
r == null || r({
|
|
201
|
+
progress: 0,
|
|
202
|
+
statusText: b.message || "上传失败",
|
|
203
|
+
status: "error"
|
|
204
|
+
}), Q.error(`${o.name} 上传失败: ${b.message}`), T == null || T(b);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function _e(t) {
|
|
208
|
+
if (t === 0)
|
|
209
|
+
return "0 B";
|
|
210
|
+
const l = 1024, o = ["B", "KB", "MB", "GB", "TB"], s = Math.floor(Math.log(t) / Math.log(l));
|
|
211
|
+
return Math.round(t / Math.pow(l, s) * 100) / 100 + " " + o[s];
|
|
212
|
+
}
|
|
213
|
+
const U = {
|
|
214
|
+
get deployURL() {
|
|
215
|
+
return window.$probeURL || "";
|
|
216
|
+
}
|
|
217
|
+
}, te = (t = {}) => window.$http.request({
|
|
218
|
+
url: `${U.deployURL}/service_deploy/deployment/servers/get`,
|
|
219
|
+
method: "get",
|
|
220
|
+
params: t,
|
|
221
|
+
isError: !1
|
|
222
|
+
}), fe = (t) => window.$http.request({
|
|
223
|
+
url: `${U.deployURL}/service_deploy/deployment/servers/add`,
|
|
224
|
+
method: "post",
|
|
225
|
+
data: t
|
|
226
|
+
}), ve = (t) => window.$http.request({
|
|
227
|
+
url: `${U.deployURL}/service_deploy/deployment/servers/delete`,
|
|
228
|
+
method: "delete",
|
|
229
|
+
data: t
|
|
230
|
+
}), le = (t = {}) => window.$http.request({
|
|
231
|
+
url: `${U.deployURL}/service_deploy/deployment/packages/get`,
|
|
232
|
+
method: "get",
|
|
233
|
+
params: t,
|
|
234
|
+
isError: !1
|
|
235
|
+
}), ye = (t) => window.$http.request({
|
|
236
|
+
url: `${U.deployURL}/service_deploy/deployment/packages/delete`,
|
|
237
|
+
method: "delete",
|
|
238
|
+
data: t
|
|
239
|
+
}), ge = () => window.$http.request({
|
|
240
|
+
url: `${U.deployURL}/service_deploy/deployment/servers/arch_type`,
|
|
241
|
+
method: "get",
|
|
242
|
+
isError: !1
|
|
243
|
+
}), oe = (t) => window.$http.request({
|
|
244
|
+
url: `${U.deployURL}/service_deploy/deployment/deploy`,
|
|
245
|
+
method: "post",
|
|
246
|
+
data: t
|
|
247
|
+
}), he = (t) => window.$http.request({
|
|
248
|
+
url: `${U.deployURL}/service_deploy/deployment/servers/test_connectivity`,
|
|
249
|
+
method: "post",
|
|
250
|
+
data: t
|
|
251
|
+
}), be = (t) => window.$http.request({
|
|
252
|
+
url: `${U.deployURL}/service_deploy/deployment/servers/check_env`,
|
|
253
|
+
method: "post",
|
|
254
|
+
data: t
|
|
255
|
+
}), we = (t) => window.$http.request({
|
|
256
|
+
url: `${U.deployURL}/service_deploy/deployment/servers/install_env`,
|
|
257
|
+
method: "post",
|
|
258
|
+
data: t
|
|
259
|
+
}), xe = (t = {}) => window.$http.request({
|
|
260
|
+
url: `${U.deployURL}/service_deploy/deployment/history/get`,
|
|
261
|
+
method: "get",
|
|
262
|
+
params: t,
|
|
263
|
+
isError: !1
|
|
264
|
+
}), ke = {
|
|
265
|
+
class: "h-100%"
|
|
266
|
+
}, Te = {
|
|
267
|
+
key: 0,
|
|
268
|
+
class: "upload-progress-list"
|
|
269
|
+
}, $e = {
|
|
270
|
+
class: "file-info"
|
|
271
|
+
}, Ee = {
|
|
272
|
+
class: "file-name"
|
|
273
|
+
}, Se = {
|
|
274
|
+
class: "file-size"
|
|
275
|
+
}, Ue = {
|
|
276
|
+
class: "progress-text"
|
|
277
|
+
}, Le = {
|
|
278
|
+
class: "table-box h-[calc(100%-220px)]"
|
|
279
|
+
}, Ce = {
|
|
280
|
+
key: 0
|
|
281
|
+
}, Me = {
|
|
282
|
+
class: "flex flex-wrap gap-12px p-12px"
|
|
283
|
+
}, qe = {
|
|
284
|
+
class: "font-bold text-14px mb-8px text-[var(--el-text-color-primary)]"
|
|
285
|
+
}, Ae = {
|
|
286
|
+
class: "text-12px text-[var(--el-text-color-regular)] space-y-4px"
|
|
287
|
+
}, Fe = {
|
|
288
|
+
key: 1,
|
|
289
|
+
class: "font-bold text-center text-gray-500 py-20"
|
|
290
|
+
}, Be = /* @__PURE__ */ H({
|
|
291
|
+
__name: "index",
|
|
292
|
+
props: {
|
|
293
|
+
token: {
|
|
294
|
+
type: String,
|
|
295
|
+
default: ""
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
setup(t) {
|
|
299
|
+
const l = w(), o = w(), s = w("快速部署"), c = w({}), {
|
|
300
|
+
$t: m,
|
|
301
|
+
$messageBox: r
|
|
302
|
+
} = window;
|
|
303
|
+
function _(i) {
|
|
304
|
+
c.value = {
|
|
305
|
+
id: i.id
|
|
306
|
+
}, o.value.open();
|
|
307
|
+
}
|
|
308
|
+
const T = w([{
|
|
309
|
+
ip: "",
|
|
310
|
+
port: ""
|
|
311
|
+
}]), b = w([{
|
|
312
|
+
ip: "",
|
|
313
|
+
port: ""
|
|
314
|
+
}]), a = w([{
|
|
315
|
+
ip: "",
|
|
316
|
+
port: ""
|
|
317
|
+
}]), u = w([{
|
|
318
|
+
ip: "",
|
|
319
|
+
port: ""
|
|
320
|
+
}]);
|
|
321
|
+
async function S(i) {
|
|
322
|
+
const k = (n, d) => {
|
|
323
|
+
const f = Math.max(n.length, d.length);
|
|
324
|
+
return Array.from({
|
|
325
|
+
length: f
|
|
326
|
+
}, (h, $) => {
|
|
327
|
+
var F, N, z, R;
|
|
328
|
+
return {
|
|
329
|
+
src_ip: ((F = n[$]) == null ? void 0 : F.ip) || "",
|
|
330
|
+
src_port: Number((N = n[$]) == null ? void 0 : N.port) || 0,
|
|
331
|
+
dst_ip: ((z = d[$]) == null ? void 0 : z.ip) || "",
|
|
332
|
+
dst_port: Number((R = d[$]) == null ? void 0 : R.port) || 0
|
|
333
|
+
};
|
|
334
|
+
});
|
|
335
|
+
}, B = {
|
|
336
|
+
packages_id: [c.value.id],
|
|
337
|
+
server_id: i.server_id,
|
|
338
|
+
service_type: window.$prodType,
|
|
339
|
+
deploy_params: {
|
|
340
|
+
probe_name: i.probe_name || "",
|
|
341
|
+
collection_mode: i.collection_mode,
|
|
342
|
+
interface: i.interface || "",
|
|
343
|
+
engine_ip: i.engine_ip || "",
|
|
344
|
+
engine_port: i.engine_port || "",
|
|
345
|
+
filterWhitelist: k(T.value, b.value),
|
|
346
|
+
filterBlacklist: k(a.value, u.value)
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
await oe({
|
|
350
|
+
data: B
|
|
351
|
+
}), l.value.getTableList(), o.value.close();
|
|
352
|
+
}
|
|
353
|
+
async function L(i) {
|
|
354
|
+
r(i, "file_name", m("button.delete"), "error").then(async () => {
|
|
355
|
+
await ye({
|
|
356
|
+
data: [i.id]
|
|
357
|
+
}), l.value.getTableList();
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
const D = j([
|
|
361
|
+
// { type: 'selection', align: 'center', field: 'id', hide: true },
|
|
362
|
+
{
|
|
363
|
+
field: "server_id",
|
|
364
|
+
name: "选择服务器",
|
|
365
|
+
enum: te,
|
|
366
|
+
fieldNames: {
|
|
367
|
+
value: "id",
|
|
368
|
+
label: "server_name"
|
|
369
|
+
},
|
|
370
|
+
show: {
|
|
371
|
+
type: "select",
|
|
372
|
+
prop: {
|
|
373
|
+
multiple: !0
|
|
374
|
+
},
|
|
375
|
+
dataType: "any"
|
|
376
|
+
},
|
|
377
|
+
hide: !0,
|
|
378
|
+
rules: [{
|
|
379
|
+
required: !0
|
|
380
|
+
}]
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
field: "file_name",
|
|
384
|
+
name: "文件名称"
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
field: "version",
|
|
388
|
+
name: "版本"
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
field: "file_size",
|
|
392
|
+
name: "大小"
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
field: "upload_time",
|
|
396
|
+
name: "上传时间"
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
field: "deployment_server",
|
|
400
|
+
name: "已部署服务器数",
|
|
401
|
+
render: (i) => {
|
|
402
|
+
var k;
|
|
403
|
+
return ((k = i.row.deployment_server) == null ? void 0 : k.length) || 0;
|
|
404
|
+
}
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
field: "upload_status",
|
|
408
|
+
name: "状态",
|
|
409
|
+
tag: !0,
|
|
410
|
+
enum: [{
|
|
411
|
+
label: "上传失败",
|
|
412
|
+
value: 0,
|
|
413
|
+
tagType: "danger"
|
|
414
|
+
}, {
|
|
415
|
+
label: "上传中",
|
|
416
|
+
value: 1,
|
|
417
|
+
tagType: "warning"
|
|
418
|
+
}, {
|
|
419
|
+
label: "上传成功",
|
|
420
|
+
value: 2,
|
|
421
|
+
tagType: "success"
|
|
422
|
+
}]
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
field: "probe_name",
|
|
426
|
+
name: "探针名称",
|
|
427
|
+
show: {
|
|
428
|
+
type: "input"
|
|
429
|
+
},
|
|
430
|
+
hide: !0,
|
|
431
|
+
rules: [{
|
|
432
|
+
required: !0
|
|
433
|
+
}]
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
field: "collection_mode",
|
|
437
|
+
name: "采集模式",
|
|
438
|
+
show: {
|
|
439
|
+
type: "input",
|
|
440
|
+
value: "DPDK",
|
|
441
|
+
disabled: !0
|
|
442
|
+
},
|
|
443
|
+
hide: !0,
|
|
444
|
+
rules: [{
|
|
445
|
+
required: !0
|
|
446
|
+
}]
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
field: "interface",
|
|
450
|
+
name: "采集网卡",
|
|
451
|
+
show: {
|
|
452
|
+
type: "input"
|
|
453
|
+
},
|
|
454
|
+
hide: !0,
|
|
455
|
+
rules: [{
|
|
456
|
+
required: !0
|
|
457
|
+
}]
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
field: "engine_ip",
|
|
461
|
+
name: "流量接收引擎IP",
|
|
462
|
+
show: {
|
|
463
|
+
type: "input"
|
|
464
|
+
},
|
|
465
|
+
hide: !0
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
field: "engine_port",
|
|
469
|
+
name: "流量接收引擎端口",
|
|
470
|
+
show: {
|
|
471
|
+
type: "input",
|
|
472
|
+
dataType: "number"
|
|
473
|
+
},
|
|
474
|
+
hide: !0,
|
|
475
|
+
rules: [{
|
|
476
|
+
required: !0,
|
|
477
|
+
type: "any"
|
|
478
|
+
}]
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
field: "whitelist",
|
|
482
|
+
name: "探针过滤白名单",
|
|
483
|
+
show: {
|
|
484
|
+
type: "input",
|
|
485
|
+
tooltip: "白名单配置后,仅采集白名单内相关流量,如不填写,则采集所有流量",
|
|
486
|
+
render: () => e(A, null, [e("p", {
|
|
487
|
+
class: "color-[var(--el-text-color-regular)]"
|
|
488
|
+
}, [q("源ip端口:")]), e(V, {
|
|
489
|
+
modelValue: T.value,
|
|
490
|
+
"onUpdate:modelValue": (i) => T.value = i
|
|
491
|
+
}, null), e("p", {
|
|
492
|
+
class: "color-[var(--el-text-color-regular)]"
|
|
493
|
+
}, [q("目的ip端口:")]), e(V, {
|
|
494
|
+
modelValue: b.value,
|
|
495
|
+
"onUpdate:modelValue": (i) => b.value = i
|
|
496
|
+
}, null)])
|
|
497
|
+
},
|
|
498
|
+
hide: !0
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
field: "blacklist",
|
|
502
|
+
name: "探针过滤黑名单",
|
|
503
|
+
show: {
|
|
504
|
+
type: "input",
|
|
505
|
+
tooltip: "黑名单配置后,将不再采集黑名单内相关流量",
|
|
506
|
+
render: () => e(A, null, [e("p", {
|
|
507
|
+
class: "color-[var(--el-text-color-regular)]"
|
|
508
|
+
}, [q("源ip端口:")]), e(V, {
|
|
509
|
+
modelValue: a.value,
|
|
510
|
+
"onUpdate:modelValue": (i) => a.value = i
|
|
511
|
+
}, null), e("p", {
|
|
512
|
+
class: "color-[var(--el-text-color-regular)]"
|
|
513
|
+
}, [q("目的ip端口:")]), e(V, {
|
|
514
|
+
modelValue: u.value,
|
|
515
|
+
"onUpdate:modelValue": (i) => u.value = i
|
|
516
|
+
}, null)])
|
|
517
|
+
},
|
|
518
|
+
hide: !0
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
field: "operation",
|
|
522
|
+
name: window.$t("global.operate"),
|
|
523
|
+
width: 140,
|
|
524
|
+
fixed: "right",
|
|
525
|
+
render: (i) => e("div", null, [e(p("el-button"), {
|
|
526
|
+
link: !0,
|
|
527
|
+
type: "primary",
|
|
528
|
+
class: "custom-btn",
|
|
529
|
+
onClick: () => _(i.row)
|
|
530
|
+
}, {
|
|
531
|
+
icon: () => e(p("tds-svg-icon"), {
|
|
532
|
+
name: "deployed",
|
|
533
|
+
tip: "部署"
|
|
534
|
+
}, null)
|
|
535
|
+
}), e(p("el-button"), {
|
|
536
|
+
link: !0,
|
|
537
|
+
type: "danger",
|
|
538
|
+
icon: "Delete",
|
|
539
|
+
title: m("button.delete"),
|
|
540
|
+
onClick: () => L([i.row])
|
|
541
|
+
}, null)])
|
|
542
|
+
}
|
|
543
|
+
]), g = w([]);
|
|
544
|
+
function P(i) {
|
|
545
|
+
const k = i.raw;
|
|
546
|
+
if (!k)
|
|
547
|
+
return;
|
|
548
|
+
const B = {
|
|
549
|
+
fileName: k.name,
|
|
550
|
+
fileSize: k.size,
|
|
551
|
+
progress: 0,
|
|
552
|
+
progressStatus: "",
|
|
553
|
+
statusText: "准备上传..."
|
|
554
|
+
};
|
|
555
|
+
g.value.push(B);
|
|
556
|
+
const n = g.value.length - 1;
|
|
557
|
+
me({
|
|
558
|
+
file: k,
|
|
559
|
+
fileDesc: "探针安装包",
|
|
560
|
+
// 替换为你实际的接口地址
|
|
561
|
+
uploadUrl: U.deployURL + "/service_deploy/deployment/packages/upload",
|
|
562
|
+
mergeUrl: U.deployURL + "/service_deploy/deployment/packages/upload_merge",
|
|
563
|
+
// 进度回调
|
|
564
|
+
onProgress: (d) => {
|
|
565
|
+
const f = g.value[n];
|
|
566
|
+
f && (f.progress = d.progress, f.statusText = d.statusText, d.fileMd5 && (f.fileMd5 = d.fileMd5), d.status === "error" ? f.progressStatus = "exception" : d.status === "success" ? f.progressStatus = "success" : f.progressStatus = "");
|
|
567
|
+
},
|
|
568
|
+
// 成功回调
|
|
569
|
+
onSuccess: () => {
|
|
570
|
+
var f;
|
|
571
|
+
const d = g.value[n];
|
|
572
|
+
d && (d.progress = 100, d.progressStatus = "success", d.statusText = "上传成功"), (f = l.value) == null || f.getTableList(), setTimeout(() => {
|
|
573
|
+
const h = g.value.findIndex(($) => $.fileName === k.name);
|
|
574
|
+
h !== -1 && g.value.splice(h, 1);
|
|
575
|
+
}, 3e3);
|
|
576
|
+
},
|
|
577
|
+
// 失败回调
|
|
578
|
+
onError: (d) => {
|
|
579
|
+
console.error("文件上传失败:", d);
|
|
580
|
+
const f = g.value[n];
|
|
581
|
+
f && (f.progressStatus = "exception", f.statusText = d.message || "上传失败");
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
return (i, k) => {
|
|
586
|
+
const B = p("el-icon"), n = p("el-upload"), d = p("el-progress"), f = p("el-table-column");
|
|
587
|
+
return y(), x("div", ke, [e(n, {
|
|
588
|
+
class: "upload-demo mt-10px",
|
|
589
|
+
drag: "",
|
|
590
|
+
action: "",
|
|
591
|
+
"auto-upload": !1,
|
|
592
|
+
accept: ".tar.gz,.tgz",
|
|
593
|
+
"on-change": P,
|
|
594
|
+
"show-file-list": !1,
|
|
595
|
+
multiple: ""
|
|
596
|
+
}, {
|
|
597
|
+
default: E(() => [e(B, {
|
|
598
|
+
class: "el-icon--upload"
|
|
599
|
+
}, {
|
|
600
|
+
default: E(() => [e(M(ae))]),
|
|
601
|
+
_: 1
|
|
602
|
+
}), k[0] || (k[0] = v("div", {
|
|
603
|
+
class: "el-upload__text"
|
|
604
|
+
}, [q(" 点击或拖拽文件到此处上传 "), v("br"), v("em", null, "支持的格式: tar.gz, tgz")], -1))]),
|
|
605
|
+
_: 1
|
|
606
|
+
}), g.value.length > 0 ? (y(), x("div", Te, [(y(!0), x(A, null, K(g.value, (h) => (y(), x("div", {
|
|
607
|
+
key: h.fileName,
|
|
608
|
+
class: "upload-progress-item"
|
|
609
|
+
}, [v("div", $e, [v("span", Ee, C(h.fileName), 1), v("span", Se, C(M(_e)(h.fileSize)), 1)]), e(d, {
|
|
610
|
+
percentage: h.progress,
|
|
611
|
+
status: h.progressStatus
|
|
612
|
+
}, null, 8, ["percentage", "status"]), v("div", Ue, C(h.statusText), 1)]))), 128))])) : O("", !0), v("div", Le, [e(M(se), {
|
|
613
|
+
ref_key: "refTdsTable",
|
|
614
|
+
ref: l,
|
|
615
|
+
columns: D,
|
|
616
|
+
"request-api": M(le),
|
|
617
|
+
options: i.$tableOptions()
|
|
618
|
+
}, {
|
|
619
|
+
default: E(() => [e(f, {
|
|
620
|
+
type: "expand"
|
|
621
|
+
}, {
|
|
622
|
+
default: E((h) => [h.row.deployment_server && h.row.deployment_server.length > 0 ? (y(), x("div", Ce, [k[1] || (k[1] = v("div", {
|
|
623
|
+
class: "font-bold text-14px mt-12px"
|
|
624
|
+
}, "已部署的服务器", -1)), v("div", Me, [(y(!0), x(A, null, K(h.row.deployment_server, ($) => (y(), x("div", {
|
|
625
|
+
key: $.id,
|
|
626
|
+
class: "w-30% px-20px py-10px rounded-8px bg-[var(--el-fill-color-light)] border border-solid border-[var(--el-border-color-lighter)] hover:shadow-md transition-shadow"
|
|
627
|
+
}, [v("div", qe, C($.server_name), 1), v("div", Ae, [v("p", null, "IP: " + C($.ip_address), 1), v("p", null, "部署时间: " + C($.deployment_time), 1)])]))), 128))])])) : (y(), x("div", Fe, "暂无部署服务器记录"))]),
|
|
628
|
+
_: 1
|
|
629
|
+
})]),
|
|
630
|
+
_: 1
|
|
631
|
+
}, 8, ["columns", "request-api", "options"])]), e(M(Y), X({
|
|
632
|
+
ref_key: "refTdsForm",
|
|
633
|
+
ref: o
|
|
634
|
+
}, i.$formBindProps({
|
|
635
|
+
title: s.value,
|
|
636
|
+
form: c.value,
|
|
637
|
+
columns: D
|
|
638
|
+
}), {
|
|
639
|
+
onSubmit: S
|
|
640
|
+
}), null, 16)]);
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
}), Ne = {
|
|
644
|
+
class: "table-box"
|
|
645
|
+
}, Ve = {
|
|
646
|
+
class: "flex items-center"
|
|
647
|
+
}, De = {
|
|
648
|
+
key: 0
|
|
649
|
+
}, ze = {
|
|
650
|
+
class: "flex flex-wrap gap-12px p-12px"
|
|
651
|
+
}, Ie = {
|
|
652
|
+
class: "font-bold text-14px mb-8px text-[var(--el-text-color-primary)]"
|
|
653
|
+
}, Pe = {
|
|
654
|
+
class: "text-12px text-[var(--el-text-color-regular)] space-y-4px"
|
|
655
|
+
}, Re = {
|
|
656
|
+
key: 1,
|
|
657
|
+
class: "font-bold text-center text-gray-500 py-20"
|
|
658
|
+
}, Oe = /* @__PURE__ */ H({
|
|
659
|
+
__name: "index",
|
|
660
|
+
setup(t) {
|
|
661
|
+
const {
|
|
662
|
+
$t: l,
|
|
663
|
+
$messageBox: o
|
|
664
|
+
} = window, s = w(), c = w(), m = w(""), r = w({}), _ = w("show");
|
|
665
|
+
function T() {
|
|
666
|
+
r.value = {}, _.value = "show", m.value = "添加服务器", c.value.open();
|
|
667
|
+
}
|
|
668
|
+
const b = (n) => {
|
|
669
|
+
o(n, "server_name", l("button.delete"), "error").then(async () => {
|
|
670
|
+
await ve({
|
|
671
|
+
data: n.map((d) => d.id)
|
|
672
|
+
}), s.value.getTableList();
|
|
673
|
+
});
|
|
674
|
+
};
|
|
675
|
+
function a(n) {
|
|
676
|
+
r.value = {
|
|
677
|
+
id: n.id
|
|
678
|
+
}, _.value = "deploy", m.value = "快速部署", c.value.open();
|
|
679
|
+
}
|
|
680
|
+
const u = w([{
|
|
681
|
+
ip: "",
|
|
682
|
+
port: ""
|
|
683
|
+
}]), S = w([{
|
|
684
|
+
ip: "",
|
|
685
|
+
port: ""
|
|
686
|
+
}]), L = w([{
|
|
687
|
+
ip: "",
|
|
688
|
+
port: ""
|
|
689
|
+
}]), D = w([{
|
|
690
|
+
ip: "",
|
|
691
|
+
port: ""
|
|
692
|
+
}]);
|
|
693
|
+
async function g(n) {
|
|
694
|
+
if (_.value === "show") {
|
|
695
|
+
await fe({
|
|
696
|
+
data: n
|
|
697
|
+
}), s.value.getTableList(), c.value.close();
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
const d = (h, $) => {
|
|
701
|
+
const F = Math.max(h.length, $.length);
|
|
702
|
+
return Array.from({
|
|
703
|
+
length: F
|
|
704
|
+
}, (N, z) => {
|
|
705
|
+
var R, G, J, Z;
|
|
706
|
+
return {
|
|
707
|
+
src_ip: ((R = h[z]) == null ? void 0 : R.ip) || "",
|
|
708
|
+
src_port: Number((G = h[z]) == null ? void 0 : G.port) || 0,
|
|
709
|
+
dst_ip: ((J = $[z]) == null ? void 0 : J.ip) || "",
|
|
710
|
+
dst_port: Number((Z = $[z]) == null ? void 0 : Z.port) || 0
|
|
711
|
+
};
|
|
712
|
+
});
|
|
713
|
+
}, f = {
|
|
714
|
+
packages_id: [n.packages_id],
|
|
715
|
+
server_id: [r.value.id],
|
|
716
|
+
service_type: window.$prodType,
|
|
717
|
+
deploy_params: {
|
|
718
|
+
probe_name: n.probe_name || "",
|
|
719
|
+
collection_mode: n.collection_mode,
|
|
720
|
+
interface: n.interface || "",
|
|
721
|
+
engine_ip: n.engine_ip || "",
|
|
722
|
+
engine_port: n.engine_port || "",
|
|
723
|
+
filterWhitelist: d(u.value, S.value),
|
|
724
|
+
filterBlacklist: d(L.value, D.value)
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
await oe({
|
|
728
|
+
data: f
|
|
729
|
+
}), s.value.getTableList(), c.value.close();
|
|
730
|
+
}
|
|
731
|
+
function P(n) {
|
|
732
|
+
he({
|
|
733
|
+
data: [n.id]
|
|
734
|
+
}), s.value.getTableList();
|
|
735
|
+
}
|
|
736
|
+
function i(n) {
|
|
737
|
+
be({
|
|
738
|
+
data: [n.id]
|
|
739
|
+
}), s.value.getTableList();
|
|
740
|
+
}
|
|
741
|
+
function k(n) {
|
|
742
|
+
we({
|
|
743
|
+
data: [n.id]
|
|
744
|
+
}), s.value.getTableList();
|
|
745
|
+
}
|
|
746
|
+
const B = j([{
|
|
747
|
+
field: "server_name",
|
|
748
|
+
name: "服务器名称",
|
|
749
|
+
show: {
|
|
750
|
+
type: "input"
|
|
751
|
+
},
|
|
752
|
+
rules: [{
|
|
753
|
+
required: !0
|
|
754
|
+
}]
|
|
755
|
+
}, {
|
|
756
|
+
field: "ip_address",
|
|
757
|
+
name: "IP地址",
|
|
758
|
+
show: {
|
|
759
|
+
type: "input"
|
|
760
|
+
},
|
|
761
|
+
rules: [{
|
|
762
|
+
required: !0
|
|
763
|
+
}]
|
|
764
|
+
}, {
|
|
765
|
+
field: "ssh_port",
|
|
766
|
+
name: "端口",
|
|
767
|
+
show: {
|
|
768
|
+
type: "input",
|
|
769
|
+
dataType: "number"
|
|
770
|
+
},
|
|
771
|
+
rules: [{
|
|
772
|
+
required: !0,
|
|
773
|
+
type: "any"
|
|
774
|
+
}]
|
|
775
|
+
}, {
|
|
776
|
+
field: "ssh_username",
|
|
777
|
+
name: "SSH用户",
|
|
778
|
+
show: {
|
|
779
|
+
type: "input"
|
|
780
|
+
},
|
|
781
|
+
rules: [{
|
|
782
|
+
required: !0
|
|
783
|
+
}]
|
|
784
|
+
}, {
|
|
785
|
+
field: "ssh_password",
|
|
786
|
+
name: "SSH登录密码",
|
|
787
|
+
hide: !0,
|
|
788
|
+
show: {
|
|
789
|
+
type: "input",
|
|
790
|
+
prop: {
|
|
791
|
+
type: "password",
|
|
792
|
+
showPassword: !0
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
rules: [{
|
|
796
|
+
required: !0
|
|
797
|
+
}]
|
|
798
|
+
}, {
|
|
799
|
+
field: "arch",
|
|
800
|
+
name: "架构",
|
|
801
|
+
enum: ge,
|
|
802
|
+
show: {
|
|
803
|
+
type: "select"
|
|
804
|
+
},
|
|
805
|
+
rules: [{
|
|
806
|
+
required: !0
|
|
807
|
+
}]
|
|
808
|
+
}, {
|
|
809
|
+
field: "deployed_count",
|
|
810
|
+
name: "已部署探针数量",
|
|
811
|
+
render: (n) => e("span", null, [n.row.deployment_packages.length])
|
|
812
|
+
}, {
|
|
813
|
+
field: "status",
|
|
814
|
+
name: "状态",
|
|
815
|
+
tag: !0,
|
|
816
|
+
enum: [{
|
|
817
|
+
label: "在线",
|
|
818
|
+
value: 1,
|
|
819
|
+
tagType: "success"
|
|
820
|
+
}, {
|
|
821
|
+
label: "不在线",
|
|
822
|
+
value: 0,
|
|
823
|
+
tagType: "danger"
|
|
824
|
+
}]
|
|
825
|
+
}, {
|
|
826
|
+
field: "ssh_status",
|
|
827
|
+
name: "连通性检查",
|
|
828
|
+
tag: !0,
|
|
829
|
+
enum: [{
|
|
830
|
+
label: "成功",
|
|
831
|
+
value: 1,
|
|
832
|
+
tagType: "success"
|
|
833
|
+
}, {
|
|
834
|
+
label: "失败",
|
|
835
|
+
value: 0,
|
|
836
|
+
tagType: "danger"
|
|
837
|
+
}]
|
|
838
|
+
}, {
|
|
839
|
+
field: "check_env_ok",
|
|
840
|
+
name: "安装环境",
|
|
841
|
+
tag: !0,
|
|
842
|
+
enum: [{
|
|
843
|
+
label: "具备",
|
|
844
|
+
value: 1,
|
|
845
|
+
tagType: "success"
|
|
846
|
+
}, {
|
|
847
|
+
label: "不具备",
|
|
848
|
+
value: 0,
|
|
849
|
+
tagType: "danger"
|
|
850
|
+
}]
|
|
851
|
+
}, {
|
|
852
|
+
field: "packages_id",
|
|
853
|
+
name: "安装包",
|
|
854
|
+
enum: le,
|
|
855
|
+
fieldNames: {
|
|
856
|
+
value: "id",
|
|
857
|
+
label: "file_name"
|
|
858
|
+
},
|
|
859
|
+
deploy: {
|
|
860
|
+
type: "select"
|
|
861
|
+
},
|
|
862
|
+
hide: !0,
|
|
863
|
+
rules: [{
|
|
864
|
+
required: !0
|
|
865
|
+
}]
|
|
866
|
+
}, {
|
|
867
|
+
field: "probe_name",
|
|
868
|
+
name: "探针名称",
|
|
869
|
+
deploy: {
|
|
870
|
+
type: "input"
|
|
871
|
+
},
|
|
872
|
+
hide: !0,
|
|
873
|
+
rules: [{
|
|
874
|
+
required: !0
|
|
875
|
+
}]
|
|
876
|
+
}, {
|
|
877
|
+
field: "collection_mode",
|
|
878
|
+
name: "采集模式",
|
|
879
|
+
deploy: {
|
|
880
|
+
type: "input",
|
|
881
|
+
value: "DPDK",
|
|
882
|
+
disabled: !0
|
|
883
|
+
},
|
|
884
|
+
hide: !0,
|
|
885
|
+
rules: [{
|
|
886
|
+
required: !0
|
|
887
|
+
}]
|
|
888
|
+
}, {
|
|
889
|
+
field: "interface",
|
|
890
|
+
name: "采集网卡",
|
|
891
|
+
deploy: {
|
|
892
|
+
type: "input"
|
|
893
|
+
},
|
|
894
|
+
hide: !0,
|
|
895
|
+
rules: [{
|
|
896
|
+
required: !0
|
|
897
|
+
}]
|
|
898
|
+
}, {
|
|
899
|
+
field: "engine_ip",
|
|
900
|
+
name: "流量接收引擎IP",
|
|
901
|
+
deploy: {
|
|
902
|
+
type: "input"
|
|
903
|
+
},
|
|
904
|
+
rules: [{
|
|
905
|
+
required: !0,
|
|
906
|
+
type: "any"
|
|
907
|
+
}],
|
|
908
|
+
hide: !0
|
|
909
|
+
}, {
|
|
910
|
+
field: "engine_port",
|
|
911
|
+
name: "流量接收引擎端口",
|
|
912
|
+
deploy: {
|
|
913
|
+
type: "input",
|
|
914
|
+
dataType: "number"
|
|
915
|
+
},
|
|
916
|
+
hide: !0,
|
|
917
|
+
rules: [{
|
|
918
|
+
required: !0,
|
|
919
|
+
type: "any"
|
|
920
|
+
}]
|
|
921
|
+
}, {
|
|
922
|
+
field: "whitelist",
|
|
923
|
+
name: "探针过滤白名单",
|
|
924
|
+
deploy: {
|
|
925
|
+
type: "input",
|
|
926
|
+
tooltip: "白名单配置后,仅采集白名单内相关流量,如不填写,则采集所有流量",
|
|
927
|
+
render: () => e(A, null, [e("p", {
|
|
928
|
+
class: "color-[var(--el-text-color-regular)]"
|
|
929
|
+
}, [q("源ip端口:")]), e(V, {
|
|
930
|
+
modelValue: u.value,
|
|
931
|
+
"onUpdate:modelValue": (n) => u.value = n
|
|
932
|
+
}, null), e("p", {
|
|
933
|
+
class: "color-[var(--el-text-color-regular)]"
|
|
934
|
+
}, [q("目的ip端口:")]), e(V, {
|
|
935
|
+
modelValue: S.value,
|
|
936
|
+
"onUpdate:modelValue": (n) => S.value = n
|
|
937
|
+
}, null)])
|
|
938
|
+
},
|
|
939
|
+
hide: !0
|
|
940
|
+
}, {
|
|
941
|
+
field: "blacklist",
|
|
942
|
+
name: "探针过滤黑名单",
|
|
943
|
+
deploy: {
|
|
944
|
+
type: "input",
|
|
945
|
+
tooltip: "黑名单配置后,将不再采集黑名单内相关流量",
|
|
946
|
+
render: () => e(A, null, [e("p", {
|
|
947
|
+
class: "color-[var(--el-text-color-regular)]"
|
|
948
|
+
}, [q("源ip端口:")]), e(V, {
|
|
949
|
+
modelValue: L.value,
|
|
950
|
+
"onUpdate:modelValue": (n) => L.value = n
|
|
951
|
+
}, null), e("p", {
|
|
952
|
+
class: "color-[var(--el-text-color-regular)]"
|
|
953
|
+
}, [q("目的ip端口:")]), e(V, {
|
|
954
|
+
modelValue: D.value,
|
|
955
|
+
"onUpdate:modelValue": (n) => D.value = n
|
|
956
|
+
}, null)])
|
|
957
|
+
},
|
|
958
|
+
hide: !0
|
|
959
|
+
}, {
|
|
960
|
+
field: "operation",
|
|
961
|
+
name: window.$t("global.operate"),
|
|
962
|
+
width: 180,
|
|
963
|
+
fixed: "right",
|
|
964
|
+
render: (n) => e("div", null, [e(p("el-button"), {
|
|
965
|
+
link: !0,
|
|
966
|
+
type: "primary",
|
|
967
|
+
icon: "Connection",
|
|
968
|
+
title: "连通性检查",
|
|
969
|
+
class: "custom-btn",
|
|
970
|
+
onClick: () => P(n.row)
|
|
971
|
+
}, null), e(p("el-button"), {
|
|
972
|
+
link: !0,
|
|
973
|
+
type: "primary",
|
|
974
|
+
class: "custom-btn",
|
|
975
|
+
onClick: () => i(n.row)
|
|
976
|
+
}, {
|
|
977
|
+
icon: () => e(p("tds-svg-icon"), {
|
|
978
|
+
name: "install_env",
|
|
979
|
+
tip: "测试安装环境"
|
|
980
|
+
}, null)
|
|
981
|
+
}), e(p("el-button"), {
|
|
982
|
+
link: !0,
|
|
983
|
+
type: "primary",
|
|
984
|
+
class: "custom-btn",
|
|
985
|
+
onClick: () => k(n.row)
|
|
986
|
+
}, {
|
|
987
|
+
icon: () => e(p("tds-svg-icon"), {
|
|
988
|
+
name: "docker",
|
|
989
|
+
tip: "自动安装docker环境"
|
|
990
|
+
}, null)
|
|
991
|
+
}), e(p("el-button"), {
|
|
992
|
+
link: !0,
|
|
993
|
+
type: "primary",
|
|
994
|
+
class: "custom-btn",
|
|
995
|
+
onClick: () => a(n.row)
|
|
996
|
+
}, {
|
|
997
|
+
icon: () => e(p("tds-svg-icon"), {
|
|
998
|
+
name: "deployed",
|
|
999
|
+
tip: "部署"
|
|
1000
|
+
}, null)
|
|
1001
|
+
}), e(p("el-button"), {
|
|
1002
|
+
link: !0,
|
|
1003
|
+
type: "danger",
|
|
1004
|
+
icon: "Delete",
|
|
1005
|
+
title: l("button.delete"),
|
|
1006
|
+
onClick: () => b([n.row])
|
|
1007
|
+
}, null)])
|
|
1008
|
+
}]);
|
|
1009
|
+
return (n, d) => {
|
|
1010
|
+
const f = p("el-button"), h = p("el-table-column"), $ = p("TdsTable");
|
|
1011
|
+
return y(), x("div", Ne, [e($, {
|
|
1012
|
+
ref_key: "refTdsTable",
|
|
1013
|
+
ref: s,
|
|
1014
|
+
columns: B,
|
|
1015
|
+
"request-api": M(te),
|
|
1016
|
+
"search-col": 4,
|
|
1017
|
+
options: n.$tableOptions(),
|
|
1018
|
+
"reserve-selection": ""
|
|
1019
|
+
}, {
|
|
1020
|
+
tableHeader: E(() => [v("div", Ve, [e(f, {
|
|
1021
|
+
class: "custom-btn",
|
|
1022
|
+
type: "primary",
|
|
1023
|
+
icon: "Plus",
|
|
1024
|
+
plain: "",
|
|
1025
|
+
onClick: d[0] || (d[0] = (F) => T())
|
|
1026
|
+
}, {
|
|
1027
|
+
default: E(() => [q(C(M(l)("button.add")), 1)]),
|
|
1028
|
+
_: 1
|
|
1029
|
+
})])]),
|
|
1030
|
+
default: E(() => [e(h, {
|
|
1031
|
+
type: "expand"
|
|
1032
|
+
}, {
|
|
1033
|
+
default: E((F) => [F.row.deployment_packages && F.row.deployment_packages.length > 0 ? (y(), x("div", De, [d[1] || (d[1] = v("div", {
|
|
1034
|
+
class: "font-bold text-14px mt-12px"
|
|
1035
|
+
}, "已部署的探针包", -1)), v("div", ze, [(y(!0), x(A, null, K(F.row.deployment_packages, (N) => (y(), x("div", {
|
|
1036
|
+
key: N.id,
|
|
1037
|
+
class: "w-30% px-20px py-10px rounded-8px bg-[var(--el-fill-color-light)] border border-solid border-[var(--el-border-color-lighter)] hover:shadow-md transition-shadow"
|
|
1038
|
+
}, [v("div", Ie, C(N.file_name), 1), v("div", Pe, [v("p", null, "版本: " + C(N.version), 1), v("p", null, "部署时间: " + C(N.deployment_time), 1)])]))), 128))])])) : (y(), x("div", Re, "暂无部署服务器记录"))]),
|
|
1039
|
+
_: 1
|
|
1040
|
+
})]),
|
|
1041
|
+
_: 1
|
|
1042
|
+
}, 8, ["columns", "request-api", "options"]), e(M(Y), X({
|
|
1043
|
+
ref_key: "refTdsForm",
|
|
1044
|
+
ref: c
|
|
1045
|
+
}, n.$formBindProps({
|
|
1046
|
+
title: m.value,
|
|
1047
|
+
form: r.value,
|
|
1048
|
+
columns: B,
|
|
1049
|
+
formKey: _.value
|
|
1050
|
+
}), {
|
|
1051
|
+
onSubmit: g
|
|
1052
|
+
}), null, 16)]);
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
}), He = {
|
|
1056
|
+
class: "table-box"
|
|
1057
|
+
}, Ke = {
|
|
1058
|
+
style: {
|
|
1059
|
+
padding: "0 50px"
|
|
1060
|
+
}
|
|
1061
|
+
}, We = /* @__PURE__ */ H({
|
|
1062
|
+
__name: "index",
|
|
1063
|
+
setup(t) {
|
|
1064
|
+
const l = j([{
|
|
1065
|
+
field: "packages_name",
|
|
1066
|
+
name: "探针包",
|
|
1067
|
+
render: (o) => e(A, null, [e("p", {
|
|
1068
|
+
class: "font-bold"
|
|
1069
|
+
}, [o.row.packages_name]), e("p", {
|
|
1070
|
+
class: "text-gray-400"
|
|
1071
|
+
}, [o.row.packages_version])])
|
|
1072
|
+
}, {
|
|
1073
|
+
field: "server_name",
|
|
1074
|
+
name: "服务器",
|
|
1075
|
+
render: (o) => e(A, null, [e("p", {
|
|
1076
|
+
class: "font-bold"
|
|
1077
|
+
}, [o.row.server_name]), e("p", {
|
|
1078
|
+
class: "text-gray-400"
|
|
1079
|
+
}, [o.row.ip_address])])
|
|
1080
|
+
}, {
|
|
1081
|
+
field: "deployment_time",
|
|
1082
|
+
name: "部署时间"
|
|
1083
|
+
}, {
|
|
1084
|
+
field: "deployment_status",
|
|
1085
|
+
name: "状态",
|
|
1086
|
+
tag: !0,
|
|
1087
|
+
enum: [{
|
|
1088
|
+
label: "部署失败",
|
|
1089
|
+
value: 0,
|
|
1090
|
+
tagType: "danger"
|
|
1091
|
+
}, {
|
|
1092
|
+
label: "部署中",
|
|
1093
|
+
value: 1,
|
|
1094
|
+
tagType: "warning"
|
|
1095
|
+
}, {
|
|
1096
|
+
label: "部署成功",
|
|
1097
|
+
value: 2,
|
|
1098
|
+
tagType: "success"
|
|
1099
|
+
}]
|
|
1100
|
+
}]);
|
|
1101
|
+
return (o, s) => {
|
|
1102
|
+
const c = p("el-table-column"), m = p("TdsTable");
|
|
1103
|
+
return y(), x("div", He, [e(m, {
|
|
1104
|
+
ref: "refTdsTable",
|
|
1105
|
+
columns: l,
|
|
1106
|
+
"search-col": 4,
|
|
1107
|
+
"request-api": M(xe),
|
|
1108
|
+
options: o.$tableOptions(),
|
|
1109
|
+
"reserve-selection": ""
|
|
1110
|
+
}, {
|
|
1111
|
+
default: E(() => [e(c, {
|
|
1112
|
+
type: "expand"
|
|
1113
|
+
}, {
|
|
1114
|
+
default: E((r) => [v("div", Ke, C(r.row.deployment_log), 1)]),
|
|
1115
|
+
_: 1
|
|
1116
|
+
})]),
|
|
1117
|
+
_: 1
|
|
1118
|
+
}, 8, ["columns", "request-api", "options"])]);
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
}), je = { class: "main-wrapper" }, Ge = /* @__PURE__ */ H({
|
|
1122
|
+
name: "TCProbe",
|
|
1123
|
+
__name: "index",
|
|
1124
|
+
props: {
|
|
1125
|
+
token: {
|
|
1126
|
+
type: String,
|
|
1127
|
+
default: ""
|
|
1128
|
+
}
|
|
1129
|
+
},
|
|
1130
|
+
setup(t) {
|
|
1131
|
+
const l = t, o = w("package");
|
|
1132
|
+
return (s, c) => {
|
|
1133
|
+
const m = p("el-tab-pane"), r = p("el-tabs");
|
|
1134
|
+
return y(), x("div", je, [
|
|
1135
|
+
e(r, {
|
|
1136
|
+
modelValue: o.value,
|
|
1137
|
+
"onUpdate:modelValue": c[0] || (c[0] = (_) => o.value = _),
|
|
1138
|
+
class: "demo-tabs"
|
|
1139
|
+
}, {
|
|
1140
|
+
default: E(() => [
|
|
1141
|
+
e(m, {
|
|
1142
|
+
label: "安装包管理",
|
|
1143
|
+
name: "package"
|
|
1144
|
+
}, {
|
|
1145
|
+
default: E(() => [
|
|
1146
|
+
o.value === "package" ? (y(), W(Be, {
|
|
1147
|
+
key: 0,
|
|
1148
|
+
token: l.token
|
|
1149
|
+
}, null, 8, ["token"])) : O("", !0)
|
|
1150
|
+
]),
|
|
1151
|
+
_: 1
|
|
1152
|
+
}),
|
|
1153
|
+
e(m, {
|
|
1154
|
+
label: "探针部署",
|
|
1155
|
+
name: "deploy"
|
|
1156
|
+
}, {
|
|
1157
|
+
default: E(() => [
|
|
1158
|
+
o.value === "deploy" ? (y(), W(Oe, { key: 0 })) : O("", !0)
|
|
1159
|
+
]),
|
|
1160
|
+
_: 1
|
|
1161
|
+
}),
|
|
1162
|
+
e(m, {
|
|
1163
|
+
label: "部署历史",
|
|
1164
|
+
name: "history"
|
|
1165
|
+
}, {
|
|
1166
|
+
default: E(() => [
|
|
1167
|
+
o.value === "history" ? (y(), W(We, { key: 0 })) : O("", !0)
|
|
1168
|
+
]),
|
|
1169
|
+
_: 1
|
|
1170
|
+
})
|
|
1171
|
+
]),
|
|
1172
|
+
_: 1
|
|
1173
|
+
}, 8, ["modelValue"])
|
|
1174
|
+
]);
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
const Je = /* @__PURE__ */ ee(Ge, [["__scopeId", "data-v-7172e806"]]), Ze = (t, l) => {
|
|
1179
|
+
if (t.install = (o) => {
|
|
1180
|
+
for (const s of [t, ...Object.values(l ?? {})])
|
|
1181
|
+
o.component(s.name, s);
|
|
1182
|
+
}, l)
|
|
1183
|
+
for (const [o, s] of Object.entries(l))
|
|
1184
|
+
t[o] = s;
|
|
1185
|
+
return t;
|
|
1186
|
+
}, Qe = Ze(Je), Xe = {
|
|
1187
|
+
TCProbe: Qe
|
|
1188
|
+
}, ot = {
|
|
1189
|
+
install(t, l) {
|
|
1190
|
+
Object.entries(Xe).forEach(([o, s]) => {
|
|
1191
|
+
t.component(o, s);
|
|
1192
|
+
}), l != null && l.prodType && (window.$prodType = l.prodType), l != null && l.envURL && (window.$probeURL = l.envURL);
|
|
1193
|
+
}
|
|
1194
|
+
};
|
|
1195
|
+
export {
|
|
1196
|
+
Qe as TCProbe,
|
|
1197
|
+
ot as default
|
|
1198
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(N,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue"),require("@element-plus/icons-vue"),require("topdatasec-ui"),require("element-plus")):typeof define=="function"&&define.amd?define(["exports","vue","@element-plus/icons-vue","topdatasec-ui","element-plus"],e):(N=typeof globalThis<"u"?globalThis:N||self,e(N.TProbeUI={},N.Vue,N.IconsVue,N.TopdatasecUI,N.ElementPlus))})(this,function(N,e,U,F,D){"use strict";const K={key:0,class:"absolute right--13px text-#f56c6c hover:cursor-pointer"},W=e.defineComponent({__name:"IpPortList",props:{modelValue:{default:()=>[{ip:"",port:""}]},modelModifiers:{}},emits:["update:modelValue"],setup(t){const o=e.useModel(t,"modelValue"),{$t:l}=window;function s(){o.value=[...o.value,{ip:"",port:""}]}function p(u){const n=[...o.value];n.splice(u,1),o.value=n}return(u,n)=>{const m=e.resolveComponent("el-input"),w=e.resolveComponent("el-form-item"),g=e.resolveComponent("el-icon"),a=e.resolveComponent("el-button");return e.openBlock(),e.createElementBlock("div",null,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(o.value,(d,k)=>(e.openBlock(),e.createElementBlock("div",{key:k,class:"flex form-m0 w-100% mb-5px"},[e.createVNode(w,{class:"w-70%"},{default:e.withCtx(()=>[e.createVNode(m,{modelValue:d.ip,"onUpdate:modelValue":V=>d.ip=V,class:"min-w-300px w-100%",placeholder:"IP"},null,8,["modelValue","onUpdate:modelValue"])]),_:2},1024),e.createVNode(w,{class:"ml-auto w-30%"},{default:e.withCtx(()=>[e.createVNode(m,{modelValue:d.port,"onUpdate:modelValue":V=>d.port=V,class:"ml-10",placeholder:e.unref(l)("global.port")},null,8,["modelValue","onUpdate:modelValue","placeholder"])]),_:2},1024),o.value.length>1?(e.openBlock(),e.createElementBlock("div",K,[e.createVNode(g,{onClick:V=>p(k)},{default:e.withCtx(()=>[e.createVNode(e.unref(U.CircleClose))]),_:1},8,["onClick"])])):e.createCommentVNode("",!0)]))),128)),e.createVNode(a,{class:"w-100% mt-10px border-dashed!",icon:"Plus",onClick:s})])}}}),Fe="",A=(t,o)=>{const l=t.__vccOpts||t;for(const[s,p]of o)l[s]=p;return l},E=A(W,[["__scopeId","data-v-16fc9c64"]]),B=5*1024*1024;function G(t,o){return new Promise((l,s)=>{const p=new(void 0),u=new FileReader,n=Math.ceil(t.size/B);let m=0;u.onload=g=>{var a;if(p.append((a=g.target)==null?void 0:a.result),m++,o){const d=Math.floor(m/n*100);o(d)}if(m<n)w();else{const d=p.end();l(d)}},u.onerror=()=>{s(new Error("MD5计算失败"))};function w(){const g=m*B,a=Math.min(g+B,t.size);u.readAsArrayBuffer(t.slice(g,a))}w()})}async function J(t,o,l,s,p,u="/api/upload/chunk"){const n=o*B,m=Math.min(n+B,t.size),w=t.slice(n,m),g=m-n,a=new FormData;a.append("file",w,t.name),a.append("chunkIndex",String(o+1)),a.append("totalChunkNum",String(l)),a.append("fileMd5",s),a.append("tds_token",p),a.append("fileName",t.name),a.append("fileSize",String(t.size)),a.append("fileExt",t.name.substring(t.name.lastIndexOf(".")+1)),a.append("chunkSize",String(B)),a.append("currentChunkSize",String(g));try{const d=await fetch(u,{method:"POST",body:a});if(console.log(d,"upload response"),!d.ok)throw new Error(`分片 ${o+1} 上传失败,HTTP状态码: ${d.status}`);const k=await d.json();if(console.log(k,"upload result"),k.recode!==0)throw new Error(`上传失败: ${k.remsg||"未知错误"}`);return!0}catch(d){throw console.error(`上传分片 ${o+1} 时出错:`,d),d}}async function Z(t,o="/api/upload/merge"){try{if(!(await fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})).ok)throw new Error("文件合并失败");return!0}catch(l){return console.error("合并分片时出错:",l),!1}}async function Q(t){const{token:o,file:l,fileDesc:s="",uploadUrl:p="/api/upload/chunk",mergeUrl:u="/api/upload/merge",onProgress:n,onSuccess:m,onError:w}=t;try{n==null||n({progress:0,statusText:"正在计算文件MD5...",status:"preparing"});const a=await G(l,f=>{n==null||n({progress:Math.floor(f*.1),statusText:`正在计算文件MD5... ${f}%`,status:"preparing"})});n==null||n({progress:10,statusText:"MD5计算完成",status:"preparing",fileMd5:a});const d=Math.ceil(l.size/B);n==null||n({progress:10,statusText:`开始上传 (共${d}个分片)`,status:"uploading",fileMd5:a});for(let f=0;f<d;f++){const q=Math.floor(f/d*80);n==null||n({progress:10+q,statusText:`正在上传分片 ${f+1}/${d}`,status:"uploading",fileMd5:a}),await J(l,f,d,a,o,p)}n==null||n({progress:90,statusText:"正在合并文件...",status:"merging",fileMd5:a});const k=l.name.substring(l.name.lastIndexOf(".")+1),V={fileDesc:s,fileExt:k,fileMd5:a,fileName:l.name,fileSize:String(l.size),tds_token:o,totalChunkNum:String(d)};if(!await Z(V,u))throw new Error("文件合并失败");n==null||n({progress:100,statusText:"上传成功",status:"success",fileMd5:a}),D.ElMessage.success(`${l.name} 上传成功`),m==null||m(a)}catch(g){n==null||n({progress:0,statusText:g.message||"上传失败",status:"error"}),D.ElMessage.error(`${l.name} 上传失败: ${g.message}`),w==null||w(g)}}function X(t){if(t===0)return"0 B";const o=1024,l=["B","KB","MB","GB","TB"],s=Math.floor(Math.log(t)/Math.log(o));return Math.round(t/Math.pow(o,s)*100)/100+" "+l[s]}const x={get deployURL(){return window.$probeURL||""}},I=(t={})=>window.$http.request({url:`${x.deployURL}/service_deploy/deployment/servers/get`,method:"get",params:t,isError:!1}),Y=t=>window.$http.request({url:`${x.deployURL}/service_deploy/deployment/servers/add`,method:"post",data:t}),v=t=>window.$http.request({url:`${x.deployURL}/service_deploy/deployment/servers/delete`,method:"delete",data:t}),P=(t={})=>window.$http.request({url:`${x.deployURL}/service_deploy/deployment/packages/get`,method:"get",params:t,isError:!1}),ee=t=>window.$http.request({url:`${x.deployURL}/service_deploy/deployment/packages/delete`,method:"delete",data:t}),te=()=>window.$http.request({url:`${x.deployURL}/service_deploy/deployment/servers/arch_type`,method:"get",isError:!1}),z=t=>window.$http.request({url:`${x.deployURL}/service_deploy/deployment/deploy`,method:"post",data:t}),oe=t=>window.$http.request({url:`${x.deployURL}/service_deploy/deployment/servers/test_connectivity`,method:"post",data:t}),le=t=>window.$http.request({url:`${x.deployURL}/service_deploy/deployment/servers/check_env`,method:"post",data:t}),re=t=>window.$http.request({url:`${x.deployURL}/service_deploy/deployment/servers/install_env`,method:"post",data:t}),ne=(t={})=>window.$http.request({url:`${x.deployURL}/service_deploy/deployment/history/get`,method:"get",params:t,isError:!1}),ae={class:"h-100%"},se={key:0,class:"upload-progress-list"},de={class:"file-info"},ie={class:"file-name"},ce={class:"file-size"},pe={class:"progress-text"},ue={class:"table-box h-[calc(100%-220px)]"},me={key:0},_e={class:"flex flex-wrap gap-12px p-12px"},fe={class:"font-bold text-14px mb-8px text-[var(--el-text-color-primary)]"},ye={class:"text-12px text-[var(--el-text-color-regular)] space-y-4px"},ge={key:1,class:"font-bold text-center text-gray-500 py-20"},he=e.defineComponent({__name:"index",props:{token:{type:String,default:""}},setup(t){const o=e.ref(),l=e.ref(),s=e.ref("快速部署"),p=e.ref({}),{$t:u,$messageBox:n}=window;function m(i){p.value={id:i.id},l.value.open()}const w=e.ref([{ip:"",port:""}]),g=e.ref([{ip:"",port:""}]),a=e.ref([{ip:"",port:""}]),d=e.ref([{ip:"",port:""}]);async function k(i){const h=(r,c)=>{const _=Math.max(r.length,c.length);return Array.from({length:_},(y,b)=>{var T,$,L,M;return{src_ip:((T=r[b])==null?void 0:T.ip)||"",src_port:Number(($=r[b])==null?void 0:$.port)||0,dst_ip:((L=c[b])==null?void 0:L.ip)||"",dst_port:Number((M=c[b])==null?void 0:M.port)||0}})},C={packages_id:[p.value.id],server_id:i.server_id,service_type:window.$prodType,deploy_params:{probe_name:i.probe_name||"",collection_mode:i.collection_mode,interface:i.interface||"",engine_ip:i.engine_ip||"",engine_port:i.engine_port||"",filterWhitelist:h(w.value,g.value),filterBlacklist:h(a.value,d.value)}};await z({data:C}),o.value.getTableList(),l.value.close()}async function V(i){n(i,"file_name",u("button.delete"),"error").then(async()=>{await ee({data:[i.id]}),o.value.getTableList()})}const S=e.reactive([{field:"server_id",name:"选择服务器",enum:I,fieldNames:{value:"id",label:"server_name"},show:{type:"select",prop:{multiple:!0},dataType:"any"},hide:!0,rules:[{required:!0}]},{field:"file_name",name:"文件名称"},{field:"version",name:"版本"},{field:"file_size",name:"大小"},{field:"upload_time",name:"上传时间"},{field:"deployment_server",name:"已部署服务器数",render:i=>{var h;return((h=i.row.deployment_server)==null?void 0:h.length)||0}},{field:"upload_status",name:"状态",tag:!0,enum:[{label:"上传失败",value:0,tagType:"danger"},{label:"上传中",value:1,tagType:"warning"},{label:"上传成功",value:2,tagType:"success"}]},{field:"probe_name",name:"探针名称",show:{type:"input"},hide:!0,rules:[{required:!0}]},{field:"collection_mode",name:"采集模式",show:{type:"input",value:"DPDK",disabled:!0},hide:!0,rules:[{required:!0}]},{field:"interface",name:"采集网卡",show:{type:"input"},hide:!0,rules:[{required:!0}]},{field:"engine_ip",name:"流量接收引擎IP",show:{type:"input"},hide:!0},{field:"engine_port",name:"流量接收引擎端口",show:{type:"input",dataType:"number"},hide:!0,rules:[{required:!0,type:"any"}]},{field:"whitelist",name:"探针过滤白名单",show:{type:"input",tooltip:"白名单配置后,仅采集白名单内相关流量,如不填写,则采集所有流量",render:()=>e.createVNode(e.Fragment,null,[e.createVNode("p",{class:"color-[var(--el-text-color-regular)]"},[e.createTextVNode("源ip端口:")]),e.createVNode(E,{modelValue:w.value,"onUpdate:modelValue":i=>w.value=i},null),e.createVNode("p",{class:"color-[var(--el-text-color-regular)]"},[e.createTextVNode("目的ip端口:")]),e.createVNode(E,{modelValue:g.value,"onUpdate:modelValue":i=>g.value=i},null)])},hide:!0},{field:"blacklist",name:"探针过滤黑名单",show:{type:"input",tooltip:"黑名单配置后,将不再采集黑名单内相关流量",render:()=>e.createVNode(e.Fragment,null,[e.createVNode("p",{class:"color-[var(--el-text-color-regular)]"},[e.createTextVNode("源ip端口:")]),e.createVNode(E,{modelValue:a.value,"onUpdate:modelValue":i=>a.value=i},null),e.createVNode("p",{class:"color-[var(--el-text-color-regular)]"},[e.createTextVNode("目的ip端口:")]),e.createVNode(E,{modelValue:d.value,"onUpdate:modelValue":i=>d.value=i},null)])},hide:!0},{field:"operation",name:window.$t("global.operate"),width:140,fixed:"right",render:i=>e.createVNode("div",null,[e.createVNode(e.resolveComponent("el-button"),{link:!0,type:"primary",class:"custom-btn",onClick:()=>m(i.row)},{icon:()=>e.createVNode(e.resolveComponent("tds-svg-icon"),{name:"deployed",tip:"部署"},null)}),e.createVNode(e.resolveComponent("el-button"),{link:!0,type:"danger",icon:"Delete",title:u("button.delete"),onClick:()=>V([i.row])},null)])}]),f=e.ref([]);function q(i){const h=i.raw;if(!h)return;const C={fileName:h.name,fileSize:h.size,progress:0,progressStatus:"",statusText:"准备上传..."};f.value.push(C);const r=f.value.length-1;Q({file:h,fileDesc:"探针安装包",uploadUrl:x.deployURL+"/service_deploy/deployment/packages/upload",mergeUrl:x.deployURL+"/service_deploy/deployment/packages/upload_merge",onProgress:c=>{const _=f.value[r];_&&(_.progress=c.progress,_.statusText=c.statusText,c.fileMd5&&(_.fileMd5=c.fileMd5),c.status==="error"?_.progressStatus="exception":c.status==="success"?_.progressStatus="success":_.progressStatus="")},onSuccess:()=>{var _;const c=f.value[r];c&&(c.progress=100,c.progressStatus="success",c.statusText="上传成功"),(_=o.value)==null||_.getTableList(),setTimeout(()=>{const y=f.value.findIndex(b=>b.fileName===h.name);y!==-1&&f.value.splice(y,1)},3e3)},onError:c=>{console.error("文件上传失败:",c);const _=f.value[r];_&&(_.progressStatus="exception",_.statusText=c.message||"上传失败")}})}return(i,h)=>{const C=e.resolveComponent("el-icon"),r=e.resolveComponent("el-upload"),c=e.resolveComponent("el-progress"),_=e.resolveComponent("el-table-column");return e.openBlock(),e.createElementBlock("div",ae,[e.createVNode(r,{class:"upload-demo mt-10px",drag:"",action:"","auto-upload":!1,accept:".tar.gz,.tgz","on-change":q,"show-file-list":!1,multiple:""},{default:e.withCtx(()=>[e.createVNode(C,{class:"el-icon--upload"},{default:e.withCtx(()=>[e.createVNode(e.unref(U.UploadFilled))]),_:1}),h[0]||(h[0]=e.createElementVNode("div",{class:"el-upload__text"},[e.createTextVNode(" 点击或拖拽文件到此处上传 "),e.createElementVNode("br"),e.createElementVNode("em",null,"支持的格式: tar.gz, tgz")],-1))]),_:1}),f.value.length>0?(e.openBlock(),e.createElementBlock("div",se,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(f.value,y=>(e.openBlock(),e.createElementBlock("div",{key:y.fileName,class:"upload-progress-item"},[e.createElementVNode("div",de,[e.createElementVNode("span",ie,e.toDisplayString(y.fileName),1),e.createElementVNode("span",ce,e.toDisplayString(e.unref(X)(y.fileSize)),1)]),e.createVNode(c,{percentage:y.progress,status:y.progressStatus},null,8,["percentage","status"]),e.createElementVNode("div",pe,e.toDisplayString(y.statusText),1)]))),128))])):e.createCommentVNode("",!0),e.createElementVNode("div",ue,[e.createVNode(e.unref(F.TdsTable),{ref_key:"refTdsTable",ref:o,columns:S,"request-api":e.unref(P),options:i.$tableOptions()},{default:e.withCtx(()=>[e.createVNode(_,{type:"expand"},{default:e.withCtx(y=>[y.row.deployment_server&&y.row.deployment_server.length>0?(e.openBlock(),e.createElementBlock("div",me,[h[1]||(h[1]=e.createElementVNode("div",{class:"font-bold text-14px mt-12px"},"已部署的服务器",-1)),e.createElementVNode("div",_e,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(y.row.deployment_server,b=>(e.openBlock(),e.createElementBlock("div",{key:b.id,class:"w-30% px-20px py-10px rounded-8px bg-[var(--el-fill-color-light)] border border-solid border-[var(--el-border-color-lighter)] hover:shadow-md transition-shadow"},[e.createElementVNode("div",fe,e.toDisplayString(b.server_name),1),e.createElementVNode("div",ye,[e.createElementVNode("p",null,"IP: "+e.toDisplayString(b.ip_address),1),e.createElementVNode("p",null,"部署时间: "+e.toDisplayString(b.deployment_time),1)])]))),128))])])):(e.openBlock(),e.createElementBlock("div",ge,"暂无部署服务器记录"))]),_:1})]),_:1},8,["columns","request-api","options"])]),e.createVNode(e.unref(F.TdsForm),e.mergeProps({ref_key:"refTdsForm",ref:l},i.$formBindProps({title:s.value,form:p.value,columns:S}),{onSubmit:k}),null,16)])}}}),we={class:"table-box"},be={class:"flex items-center"},ke={key:0},xe={class:"flex flex-wrap gap-12px p-12px"},Ve={class:"font-bold text-14px mb-8px text-[var(--el-text-color-primary)]"},Ne={class:"text-12px text-[var(--el-text-color-regular)] space-y-4px"},Te={key:1,class:"font-bold text-center text-gray-500 py-20"},Ee=e.defineComponent({__name:"index",setup(t){const{$t:o,$messageBox:l}=window,s=e.ref(),p=e.ref(),u=e.ref(""),n=e.ref({}),m=e.ref("show");function w(){n.value={},m.value="show",u.value="添加服务器",p.value.open()}const g=r=>{l(r,"server_name",o("button.delete"),"error").then(async()=>{await v({data:r.map(c=>c.id)}),s.value.getTableList()})};function a(r){n.value={id:r.id},m.value="deploy",u.value="快速部署",p.value.open()}const d=e.ref([{ip:"",port:""}]),k=e.ref([{ip:"",port:""}]),V=e.ref([{ip:"",port:""}]),S=e.ref([{ip:"",port:""}]);async function f(r){if(m.value==="show"){await Y({data:r}),s.value.getTableList(),p.value.close();return}const c=(y,b)=>{const T=Math.max(y.length,b.length);return Array.from({length:T},($,L)=>{var M,O,j,H;return{src_ip:((M=y[L])==null?void 0:M.ip)||"",src_port:Number((O=y[L])==null?void 0:O.port)||0,dst_ip:((j=b[L])==null?void 0:j.ip)||"",dst_port:Number((H=b[L])==null?void 0:H.port)||0}})},_={packages_id:[r.packages_id],server_id:[n.value.id],service_type:window.$prodType,deploy_params:{probe_name:r.probe_name||"",collection_mode:r.collection_mode,interface:r.interface||"",engine_ip:r.engine_ip||"",engine_port:r.engine_port||"",filterWhitelist:c(d.value,k.value),filterBlacklist:c(V.value,S.value)}};await z({data:_}),s.value.getTableList(),p.value.close()}function q(r){oe({data:[r.id]}),s.value.getTableList()}function i(r){le({data:[r.id]}),s.value.getTableList()}function h(r){re({data:[r.id]}),s.value.getTableList()}const C=e.reactive([{field:"server_name",name:"服务器名称",show:{type:"input"},rules:[{required:!0}]},{field:"ip_address",name:"IP地址",show:{type:"input"},rules:[{required:!0}]},{field:"ssh_port",name:"端口",show:{type:"input",dataType:"number"},rules:[{required:!0,type:"any"}]},{field:"ssh_username",name:"SSH用户",show:{type:"input"},rules:[{required:!0}]},{field:"ssh_password",name:"SSH登录密码",hide:!0,show:{type:"input",prop:{type:"password",showPassword:!0}},rules:[{required:!0}]},{field:"arch",name:"架构",enum:te,show:{type:"select"},rules:[{required:!0}]},{field:"deployed_count",name:"已部署探针数量",render:r=>e.createVNode("span",null,[r.row.deployment_packages.length])},{field:"status",name:"状态",tag:!0,enum:[{label:"在线",value:1,tagType:"success"},{label:"不在线",value:0,tagType:"danger"}]},{field:"ssh_status",name:"连通性检查",tag:!0,enum:[{label:"成功",value:1,tagType:"success"},{label:"失败",value:0,tagType:"danger"}]},{field:"check_env_ok",name:"安装环境",tag:!0,enum:[{label:"具备",value:1,tagType:"success"},{label:"不具备",value:0,tagType:"danger"}]},{field:"packages_id",name:"安装包",enum:P,fieldNames:{value:"id",label:"file_name"},deploy:{type:"select"},hide:!0,rules:[{required:!0}]},{field:"probe_name",name:"探针名称",deploy:{type:"input"},hide:!0,rules:[{required:!0}]},{field:"collection_mode",name:"采集模式",deploy:{type:"input",value:"DPDK",disabled:!0},hide:!0,rules:[{required:!0}]},{field:"interface",name:"采集网卡",deploy:{type:"input"},hide:!0,rules:[{required:!0}]},{field:"engine_ip",name:"流量接收引擎IP",deploy:{type:"input"},rules:[{required:!0,type:"any"}],hide:!0},{field:"engine_port",name:"流量接收引擎端口",deploy:{type:"input",dataType:"number"},hide:!0,rules:[{required:!0,type:"any"}]},{field:"whitelist",name:"探针过滤白名单",deploy:{type:"input",tooltip:"白名单配置后,仅采集白名单内相关流量,如不填写,则采集所有流量",render:()=>e.createVNode(e.Fragment,null,[e.createVNode("p",{class:"color-[var(--el-text-color-regular)]"},[e.createTextVNode("源ip端口:")]),e.createVNode(E,{modelValue:d.value,"onUpdate:modelValue":r=>d.value=r},null),e.createVNode("p",{class:"color-[var(--el-text-color-regular)]"},[e.createTextVNode("目的ip端口:")]),e.createVNode(E,{modelValue:k.value,"onUpdate:modelValue":r=>k.value=r},null)])},hide:!0},{field:"blacklist",name:"探针过滤黑名单",deploy:{type:"input",tooltip:"黑名单配置后,将不再采集黑名单内相关流量",render:()=>e.createVNode(e.Fragment,null,[e.createVNode("p",{class:"color-[var(--el-text-color-regular)]"},[e.createTextVNode("源ip端口:")]),e.createVNode(E,{modelValue:V.value,"onUpdate:modelValue":r=>V.value=r},null),e.createVNode("p",{class:"color-[var(--el-text-color-regular)]"},[e.createTextVNode("目的ip端口:")]),e.createVNode(E,{modelValue:S.value,"onUpdate:modelValue":r=>S.value=r},null)])},hide:!0},{field:"operation",name:window.$t("global.operate"),width:180,fixed:"right",render:r=>e.createVNode("div",null,[e.createVNode(e.resolveComponent("el-button"),{link:!0,type:"primary",icon:"Connection",title:"连通性检查",class:"custom-btn",onClick:()=>q(r.row)},null),e.createVNode(e.resolveComponent("el-button"),{link:!0,type:"primary",class:"custom-btn",onClick:()=>i(r.row)},{icon:()=>e.createVNode(e.resolveComponent("tds-svg-icon"),{name:"install_env",tip:"测试安装环境"},null)}),e.createVNode(e.resolveComponent("el-button"),{link:!0,type:"primary",class:"custom-btn",onClick:()=>h(r.row)},{icon:()=>e.createVNode(e.resolveComponent("tds-svg-icon"),{name:"docker",tip:"自动安装docker环境"},null)}),e.createVNode(e.resolveComponent("el-button"),{link:!0,type:"primary",class:"custom-btn",onClick:()=>a(r.row)},{icon:()=>e.createVNode(e.resolveComponent("tds-svg-icon"),{name:"deployed",tip:"部署"},null)}),e.createVNode(e.resolveComponent("el-button"),{link:!0,type:"danger",icon:"Delete",title:o("button.delete"),onClick:()=>g([r.row])},null)])}]);return(r,c)=>{const _=e.resolveComponent("el-button"),y=e.resolveComponent("el-table-column"),b=e.resolveComponent("TdsTable");return e.openBlock(),e.createElementBlock("div",we,[e.createVNode(b,{ref_key:"refTdsTable",ref:s,columns:C,"request-api":e.unref(I),"search-col":4,options:r.$tableOptions(),"reserve-selection":""},{tableHeader:e.withCtx(()=>[e.createElementVNode("div",be,[e.createVNode(_,{class:"custom-btn",type:"primary",icon:"Plus",plain:"",onClick:c[0]||(c[0]=T=>w())},{default:e.withCtx(()=>[e.createTextVNode(e.toDisplayString(e.unref(o)("button.add")),1)]),_:1})])]),default:e.withCtx(()=>[e.createVNode(y,{type:"expand"},{default:e.withCtx(T=>[T.row.deployment_packages&&T.row.deployment_packages.length>0?(e.openBlock(),e.createElementBlock("div",ke,[c[1]||(c[1]=e.createElementVNode("div",{class:"font-bold text-14px mt-12px"},"已部署的探针包",-1)),e.createElementVNode("div",xe,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(T.row.deployment_packages,$=>(e.openBlock(),e.createElementBlock("div",{key:$.id,class:"w-30% px-20px py-10px rounded-8px bg-[var(--el-fill-color-light)] border border-solid border-[var(--el-border-color-lighter)] hover:shadow-md transition-shadow"},[e.createElementVNode("div",Ve,e.toDisplayString($.file_name),1),e.createElementVNode("div",Ne,[e.createElementVNode("p",null,"版本: "+e.toDisplayString($.version),1),e.createElementVNode("p",null,"部署时间: "+e.toDisplayString($.deployment_time),1)])]))),128))])])):(e.openBlock(),e.createElementBlock("div",Te,"暂无部署服务器记录"))]),_:1})]),_:1},8,["columns","request-api","options"]),e.createVNode(e.unref(F.TdsForm),e.mergeProps({ref_key:"refTdsForm",ref:p},r.$formBindProps({title:u.value,form:n.value,columns:C,formKey:m.value}),{onSubmit:f}),null,16)])}}}),Ce={class:"table-box"},$e={style:{padding:"0 50px"}},Be=e.defineComponent({__name:"index",setup(t){const o=e.reactive([{field:"packages_name",name:"探针包",render:l=>e.createVNode(e.Fragment,null,[e.createVNode("p",{class:"font-bold"},[l.row.packages_name]),e.createVNode("p",{class:"text-gray-400"},[l.row.packages_version])])},{field:"server_name",name:"服务器",render:l=>e.createVNode(e.Fragment,null,[e.createVNode("p",{class:"font-bold"},[l.row.server_name]),e.createVNode("p",{class:"text-gray-400"},[l.row.ip_address])])},{field:"deployment_time",name:"部署时间"},{field:"deployment_status",name:"状态",tag:!0,enum:[{label:"部署失败",value:0,tagType:"danger"},{label:"部署中",value:1,tagType:"warning"},{label:"部署成功",value:2,tagType:"success"}]}]);return(l,s)=>{const p=e.resolveComponent("el-table-column"),u=e.resolveComponent("TdsTable");return e.openBlock(),e.createElementBlock("div",Ce,[e.createVNode(u,{ref:"refTdsTable",columns:o,"search-col":4,"request-api":e.unref(ne),options:l.$tableOptions(),"reserve-selection":""},{default:e.withCtx(()=>[e.createVNode(p,{type:"expand"},{default:e.withCtx(n=>[e.createElementVNode("div",$e,e.toDisplayString(n.row.deployment_log),1)]),_:1})]),_:1},8,["columns","request-api","options"])])}}}),Se={class:"main-wrapper"},Le=e.defineComponent({name:"TCProbe",__name:"index",props:{token:{type:String,default:""}},setup(t){const o=t,l=e.ref("package");return(s,p)=>{const u=e.resolveComponent("el-tab-pane"),n=e.resolveComponent("el-tabs");return e.openBlock(),e.createElementBlock("div",Se,[e.createVNode(n,{modelValue:l.value,"onUpdate:modelValue":p[0]||(p[0]=m=>l.value=m),class:"demo-tabs"},{default:e.withCtx(()=>[e.createVNode(u,{label:"安装包管理",name:"package"},{default:e.withCtx(()=>[l.value==="package"?(e.openBlock(),e.createBlock(he,{key:0,token:o.token},null,8,["token"])):e.createCommentVNode("",!0)]),_:1}),e.createVNode(u,{label:"探针部署",name:"deploy"},{default:e.withCtx(()=>[l.value==="deploy"?(e.openBlock(),e.createBlock(Ee,{key:0})):e.createCommentVNode("",!0)]),_:1}),e.createVNode(u,{label:"部署历史",name:"history"},{default:e.withCtx(()=>[l.value==="history"?(e.openBlock(),e.createBlock(Be,{key:0})):e.createCommentVNode("",!0)]),_:1})]),_:1},8,["modelValue"])])}}}),Ue="",R=((t,o)=>{if(t.install=l=>{for(const s of[t,...Object.values(o??{})])l.component(s.name,s)},o)for(const[l,s]of Object.entries(o))t[l]=s;return t})(A(Le,[["__scopeId","data-v-7172e806"]])),qe={TCProbe:R},Me={install(t,o){Object.entries(qe).forEach(([l,s])=>{t.component(l,s)}),o!=null&&o.prodType&&(window.$prodType=o.prodType),o!=null&&o.envURL&&(window.$probeURL=o.envURL)}};N.TCProbe=R,N.default=Me,Object.defineProperties(N,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@topdatasec/probe",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "探针部署管理组件",
|
|
5
|
+
"author": "wangguiwang",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"private": false,
|
|
8
|
+
"main": "lib/t-probe-ui.es.js",
|
|
9
|
+
"module": "lib/t-probe-ui.es.js",
|
|
10
|
+
"types": "lib/index.d.ts",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"files": [
|
|
13
|
+
"package.json",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"lib"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"lib": "node -e \"const fs=require('fs');fs.rmSync('lib',{recursive:true,force:true})\" && vite build",
|
|
20
|
+
"pub": "npm publish --access public --registry=https://registry.npmjs.org/",
|
|
21
|
+
"prettier": "prettier --write \"packages/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\""
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@rollup/plugin-commonjs": "^28.0.3",
|
|
25
|
+
"@topdatasec/eslint-rc": "^1.0.4",
|
|
26
|
+
"@typescript-eslint/eslint-plugin": "^6.7.0",
|
|
27
|
+
"@typescript-eslint/parser": "^6.7.0",
|
|
28
|
+
"@vitejs/plugin-vue": "^5.0.4",
|
|
29
|
+
"@vitejs/plugin-vue-jsx": "^3.0.2",
|
|
30
|
+
"eslint": "^8.49.0",
|
|
31
|
+
"eslint-config-prettier": "^9.0.0",
|
|
32
|
+
"eslint-plugin-prettier": "^5.0.0",
|
|
33
|
+
"eslint-plugin-vue": "^9.17.0",
|
|
34
|
+
"lint-staged": "^14.0.1",
|
|
35
|
+
"postcss": "^8.4.29",
|
|
36
|
+
"postcss-html": "^1.5.0",
|
|
37
|
+
"prettier": "^3.0.3",
|
|
38
|
+
"rollup-plugin-visualizer": "^5.9.2",
|
|
39
|
+
"sass": "~1.77.8",
|
|
40
|
+
"standard-version": "^9.5.0",
|
|
41
|
+
"typescript": "^5.2.2",
|
|
42
|
+
"unplugin-auto-import": "^0.16.6",
|
|
43
|
+
"unplugin-vue-components": "^0.25.2",
|
|
44
|
+
"unplugin-vue-setup-extend-plus": "^1.0.0",
|
|
45
|
+
"vite": "^4.4.9",
|
|
46
|
+
"vite-plugin-dts": "^4.5.3",
|
|
47
|
+
"vite-plugin-eslint": "^1.8.1",
|
|
48
|
+
"vue-tsc": "~1.8.27",
|
|
49
|
+
"spark-md5": "^3.0.2"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"@element-plus/icons-vue": "^2.3.1",
|
|
53
|
+
"element-plus": "^2.9.0",
|
|
54
|
+
"pinia": "^2.1.6",
|
|
55
|
+
"topdatasec-ui": "^1.6.1",
|
|
56
|
+
"vue": "^3.4.35"
|
|
57
|
+
},
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git"
|
|
60
|
+
},
|
|
61
|
+
"keywords": [
|
|
62
|
+
"vue",
|
|
63
|
+
"vue3",
|
|
64
|
+
"probe",
|
|
65
|
+
"components"
|
|
66
|
+
]
|
|
67
|
+
}
|