@jfvilas/plugin-kwirth-fileman 0.13.5

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.
@@ -0,0 +1,585 @@
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import useAsync from 'react-use/esm/useAsync';
3
+ import { Progress, WarningPanel } from '@backstage/core-components';
4
+ import { useApi, alertApiRef } from '@backstage/core-plugin-api';
5
+ import { isKwirthAvailable, ANNOTATION_BACKSTAGE_KUBERNETES_LABELID, ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR, getPodList, getContainerList } from '@jfvilas/plugin-kwirth-common';
6
+ import { useEntity, MissingAnnotationEmptyState } from '@backstage/plugin-catalog-react';
7
+ import { kwirthFilemanApiRef } from '../api/types.esm.js';
8
+ import { SignalMessageLevelEnum, accessKeySerialize, InstanceMessageTypeEnum, InstanceMessageActionEnum, InstanceMessageFlowEnum, InstanceConfigViewEnum, InstanceConfigObjectEnum, SignalMessageEventEnum } from '@jfvilas/kwirth-common';
9
+ import { ComponentNotFound, ErrorType, ClusterList, KwirthNews, StatusLog } from '@jfvilas/plugin-kwirth-frontend';
10
+ import { Box, Grid, Card, CardHeader, IconButton, Typography } from '@material-ui/core';
11
+ import PlayIcon from '@material-ui/icons/PlayArrow';
12
+ import PauseIcon from '@material-ui/icons/Pause';
13
+ import StopIcon from '@material-ui/icons/Stop';
14
+ import InfoIcon from '@material-ui/icons/Info';
15
+ import WarningIcon from '@material-ui/icons/Warning';
16
+ import ErrorIcon from '@material-ui/icons/Error';
17
+ import KwirthFilemanLogo from '../plugins/plugin-kwirth-fileman/src/assets/kwirthfileman-logo.svg';
18
+ import { v4 } from 'uuid';
19
+ import { FileManager } from '@jfvilas/react-file-manager';
20
+ import '@jfvilas/react-file-manager/dist/style.css';
21
+ import styles from './custom-fm.module.css.esm.js';
22
+ import { VERSION } from '../version.esm.js';
23
+
24
+ const EntityKwirthFilemanContent = (props) => {
25
+ const { entity } = useEntity();
26
+ const kwirthFilemanApi = useApi(kwirthFilemanApiRef);
27
+ const alertApi = useApi(alertApiRef);
28
+ const [validClusters, setResources] = useState([]);
29
+ const [selectedClusterName, setSelectedClusterName] = useState("");
30
+ const [selectedNamespaces, setSelectedNamespaces] = useState([]);
31
+ const [selectedPodNames, setSelectedPodNames] = useState([]);
32
+ const [selectedContainerNames, setSelectedContainerNames] = useState([]);
33
+ const [started, setStarted] = useState(false);
34
+ const [stopped, setStopped] = useState(true);
35
+ const paused = useRef(false);
36
+ const [statusMessages, setStatusMessages] = useState([]);
37
+ const [webSocket, setWebSocket] = useState();
38
+ const [showStatusDialog, setShowStatusDialog] = useState(false);
39
+ const [statusLevel, setStatusLevel] = useState(SignalMessageLevelEnum.INFO);
40
+ const [backendVersion, setBackendVersion] = useState("");
41
+ const [backendInfo, setBackendInfo] = useState();
42
+ const instance = useRef();
43
+ const [stateFiles, setStateFiles] = useState([]);
44
+ const files = useRef([]);
45
+ const [currentPath, setCurrentPath] = useState("");
46
+ const { loading, error } = useAsync(async () => {
47
+ if (backendVersion === "") setBackendVersion(await kwirthFilemanApi.getVersion());
48
+ if (!backendInfo) setBackendInfo(await kwirthFilemanApi.getInfo());
49
+ let reqScopes = ["fileman$read"];
50
+ let data = await kwirthFilemanApi.requestAccess(entity, "fileman", reqScopes);
51
+ setResources(data);
52
+ });
53
+ const filemanBoxRef = useRef(null);
54
+ const [filemanBoxTop, setFilemanBoxTop] = useState(0);
55
+ let permissions = {
56
+ create: true,
57
+ delete: true,
58
+ download: true,
59
+ copy: true,
60
+ move: true,
61
+ rename: true,
62
+ upload: true
63
+ };
64
+ let fileUploadConfig = {
65
+ url: ""
66
+ };
67
+ let cluster = validClusters.find((cluster2) => cluster2.name === selectedClusterName);
68
+ if (cluster) {
69
+ let accessKey = cluster.accessKeys.get("fileman$read");
70
+ if (accessKey) {
71
+ fileUploadConfig = {
72
+ url: `${cluster.url}/channel/fileman/upload?key=${instance.current}`,
73
+ method: "POST",
74
+ headers: {
75
+ "Authorization": "Bearer " + accessKeySerialize(accessKey)
76
+ }
77
+ };
78
+ }
79
+ }
80
+ let level = currentPath.split("/").length - 1;
81
+ if (level < 3) {
82
+ permissions = {
83
+ create: false,
84
+ delete: false,
85
+ download: false,
86
+ copy: false,
87
+ move: false,
88
+ rename: false,
89
+ upload: false
90
+ };
91
+ }
92
+ useEffect(() => {
93
+ if (filemanBoxRef.current) setFilemanBoxTop(filemanBoxRef.current.getBoundingClientRect().top);
94
+ });
95
+ const clickStart = () => {
96
+ if (!paused.current) {
97
+ setStarted(true);
98
+ paused.current = false;
99
+ setStopped(false);
100
+ startFilemanViewer();
101
+ } else {
102
+ paused.current = false;
103
+ setStarted(true);
104
+ }
105
+ };
106
+ const onClickPause = () => {
107
+ setStarted(false);
108
+ paused.current = true;
109
+ };
110
+ const onClickStop = () => {
111
+ setStarted(false);
112
+ setStopped(true);
113
+ paused.current = false;
114
+ stopFilemanViewer();
115
+ };
116
+ const onSelectCluster = (clusterName) => {
117
+ if (started) onClickStop();
118
+ if (clusterName) {
119
+ setSelectedClusterName(clusterName);
120
+ setSelectedPodNames([]);
121
+ setSelectedContainerNames([]);
122
+ setStatusMessages([]);
123
+ let cluster2 = validClusters.find((cluster3) => cluster3.name === clusterName);
124
+ if (cluster2 && cluster2.pods) {
125
+ let validNamespaces = Array.from(new Set(cluster2.pods.map((pod) => pod.namespace)));
126
+ if (validNamespaces.length === 1) {
127
+ setSelectedNamespaces(validNamespaces);
128
+ let podList = getPodList(cluster2.pods, validNamespaces);
129
+ setSelectedPodNames(podList.map((pod) => pod.name));
130
+ setSelectedContainerNames(getContainerList(cluster2.pods, validNamespaces, podList.map((pod) => pod.name), props.excludeContainers || []));
131
+ } else {
132
+ setSelectedNamespaces([]);
133
+ }
134
+ }
135
+ }
136
+ };
137
+ let FilemanCommandEnum;
138
+ ((FilemanCommandEnum2) => {
139
+ FilemanCommandEnum2["HOME"] = "home";
140
+ FilemanCommandEnum2["DIR"] = "dir";
141
+ FilemanCommandEnum2["CREATE"] = "create";
142
+ FilemanCommandEnum2["RENAME"] = "rename";
143
+ FilemanCommandEnum2["DELETE"] = "delete";
144
+ FilemanCommandEnum2["MOVE"] = "move";
145
+ FilemanCommandEnum2["COPY"] = "copy";
146
+ FilemanCommandEnum2["UPLOAD"] = "upload";
147
+ FilemanCommandEnum2["DOWNLOAD"] = "download";
148
+ })(FilemanCommandEnum || (FilemanCommandEnum = {}));
149
+ const processFilemanMessage = (wsEvent) => {
150
+ let msg = JSON.parse(wsEvent.data);
151
+ switch (msg.type) {
152
+ case InstanceMessageTypeEnum.DATA: {
153
+ let response = JSON.parse(wsEvent.data);
154
+ switch (response.action) {
155
+ case InstanceMessageActionEnum.COMMAND:
156
+ {
157
+ switch (response.command) {
158
+ case "home" /* HOME */:
159
+ let data = response.data;
160
+ let nss = Array.from(new Set(data.map((n) => n.split("/")[0])));
161
+ nss.map((ns) => {
162
+ if (!files.current.some((f) => f.path === "/" + ns)) {
163
+ files.current.push({ name: ns, isDirectory: true, path: "/" + ns, class: "namespace" });
164
+ }
165
+ let podNames = Array.from(new Set(data.filter((a) => a.split("/")[0] === ns).map((o) => o.split("/")[1])));
166
+ podNames.map((p) => {
167
+ if (!files.current.some((f) => f.path === "/" + ns + "/" + p)) {
168
+ files.current.push({ name: p, isDirectory: true, path: "/" + ns + "/" + p, class: "pod" });
169
+ }
170
+ let conts = Array.from(new Set(data.filter((a) => a.split("/")[0] === ns && a.split("/")[1] === p).map((o) => o.split("/")[2])));
171
+ conts.map((c) => {
172
+ if (!files.current.some((f) => f.path === "/" + ns + "/" + p + "/" + c)) {
173
+ files.current.push({ name: c, isDirectory: true, path: "/" + ns + "/" + p + "/" + c, class: "container" });
174
+ }
175
+ });
176
+ });
177
+ });
178
+ setStateFiles([...files.current]);
179
+ break;
180
+ case "dir" /* DIR */:
181
+ let content = JSON.parse(response.data);
182
+ if (content.status === "Success") {
183
+ for (let o of content.metadata.object) {
184
+ let name = o.name.split("/")[o.name.split("/").length - 1];
185
+ let e = {
186
+ name,
187
+ isDirectory: o.type === 1,
188
+ path: o.name,
189
+ updatedAt: (/* @__PURE__ */ new Date(+o.time)).toISOString(),
190
+ size: +o.size,
191
+ ...o.type === 0 ? { class: "file" } : {}
192
+ };
193
+ let i = files.current.findIndex((f) => f.path === e.path);
194
+ if (i >= 0)
195
+ files.current[i] = e;
196
+ else
197
+ files.current.push(e);
198
+ }
199
+ setStateFiles([...files.current]);
200
+ } else {
201
+ addMessage(SignalMessageLevelEnum.ERROR, content.text || content.message);
202
+ }
203
+ break;
204
+ case "rename" /* RENAME */:
205
+ {
206
+ let content2 = JSON.parse(response.data);
207
+ if (content2.status !== "Success") addMessage(SignalMessageLevelEnum.ERROR, content2.text || content2.message);
208
+ }
209
+ break;
210
+ case "delete" /* DELETE */: {
211
+ let content2 = JSON.parse(response.data);
212
+ if (content2.status === "Success") {
213
+ let fname = content2.metadata.object;
214
+ files.current = files.current.filter((f) => f.path !== fname);
215
+ files.current = files.current.filter((f) => !f.path.startsWith(fname + "/"));
216
+ setStateFiles([...files.current]);
217
+ } else {
218
+ addMessage(SignalMessageLevelEnum.ERROR, content2.text || content2.message);
219
+ }
220
+ break;
221
+ }
222
+ case "move" /* MOVE */:
223
+ case "copy" /* COPY */:
224
+ case "create" /* CREATE */: {
225
+ let content2 = JSON.parse(response.data);
226
+ if (content2.status === "Success") {
227
+ let f = {
228
+ name: content2.metadata.object.split("/").slice(-1)[0],
229
+ isDirectory: content2.metadata.type === 1,
230
+ path: content2.metadata.object,
231
+ updatedAt: (/* @__PURE__ */ new Date(+content2.metadata.time)).toISOString(),
232
+ size: +content2.metadata.size,
233
+ ...content2.metadata.type.type === 0 ? { class: "file" } : {}
234
+ };
235
+ files.current.push(f);
236
+ setStateFiles([...files.current]);
237
+ } else {
238
+ addMessage(SignalMessageLevelEnum.ERROR, content2.text || content2.message);
239
+ }
240
+ break;
241
+ }
242
+ }
243
+ }
244
+ break;
245
+ }
246
+ break;
247
+ }
248
+ case InstanceMessageTypeEnum.SIGNAL:
249
+ let signalMessage = JSON.parse(wsEvent.data);
250
+ if (signalMessage.flow === InstanceMessageFlowEnum.RESPONSE) {
251
+ if (signalMessage.action === InstanceMessageActionEnum.START) {
252
+ if (signalMessage.text) addMessage(SignalMessageLevelEnum.INFO, signalMessage.text);
253
+ instance.current = signalMessage.instance;
254
+ } else {
255
+ addMessage(SignalMessageLevelEnum.ERROR, wsEvent.data);
256
+ }
257
+ } else if (signalMessage.flow === InstanceMessageFlowEnum.UNSOLICITED) {
258
+ let cluster2 = validClusters.find((cluster3) => cluster3.name === selectedClusterName);
259
+ if (cluster2) {
260
+ let accessKey = cluster2.accessKeys.get("fileman$read");
261
+ if (accessKey && instance?.current) {
262
+ if (signalMessage.event === SignalMessageEventEnum.ADD) {
263
+ let filemanMessage = {
264
+ flow: InstanceMessageFlowEnum.REQUEST,
265
+ action: InstanceMessageActionEnum.COMMAND,
266
+ channel: "fileman",
267
+ type: InstanceMessageTypeEnum.DATA,
268
+ accessKey: accessKeySerialize(accessKey),
269
+ instance: instance.current,
270
+ id: v4(),
271
+ command: "home" /* HOME */,
272
+ namespace: signalMessage.namespace,
273
+ group: "",
274
+ pod: signalMessage.pod,
275
+ container: signalMessage.container,
276
+ params: [],
277
+ msgtype: "filemanmessage"
278
+ };
279
+ let payload = JSON.stringify(filemanMessage);
280
+ wsEvent.target.send(payload);
281
+ if (signalMessage.text) addMessage(SignalMessageLevelEnum.INFO, signalMessage.text);
282
+ }
283
+ } else {
284
+ addMessage(SignalMessageLevelEnum.INFO, "Have no instance/accessKey");
285
+ }
286
+ } else {
287
+ addMessage(SignalMessageLevelEnum.INFO, "Have no cluster");
288
+ }
289
+ }
290
+ break;
291
+ default:
292
+ console.log(`Invalid message type ${msg.type}`);
293
+ break;
294
+ }
295
+ };
296
+ const addMessage = (level2, text) => {
297
+ alertApi.post({ message: text, severity: level2, display: "transient" });
298
+ setStatusMessages((prev) => [...prev, {
299
+ level: level2,
300
+ text,
301
+ type: InstanceMessageTypeEnum.SIGNAL
302
+ }]);
303
+ };
304
+ const websocketOnMessage = (wsEvent) => {
305
+ let instanceMessage;
306
+ try {
307
+ instanceMessage = JSON.parse(wsEvent.data);
308
+ } catch (err) {
309
+ console.log(err);
310
+ console.log(wsEvent.data);
311
+ return;
312
+ }
313
+ switch (instanceMessage.channel) {
314
+ case "fileman":
315
+ processFilemanMessage(wsEvent);
316
+ break;
317
+ default:
318
+ addMessage(SignalMessageLevelEnum.ERROR, "Invalid channel in message: " + instanceMessage.channel);
319
+ addMessage(SignalMessageLevelEnum.ERROR, "Invalid message: " + JSON.stringify(instanceMessage));
320
+ break;
321
+ }
322
+ };
323
+ const websocketOnOpen = (ws) => {
324
+ setWebSocket(ws);
325
+ let cluster2 = validClusters.find((cluster3) => cluster3.name === selectedClusterName);
326
+ if (!cluster2) {
327
+ addMessage(SignalMessageLevelEnum.ERROR, "No cluster selected");
328
+ return;
329
+ }
330
+ let pods = cluster2.pods.filter((p2) => selectedNamespaces.includes(p2.namespace));
331
+ if (!pods) {
332
+ addMessage(SignalMessageLevelEnum.ERROR, "No pods found");
333
+ return;
334
+ }
335
+ console.log(`WS connected`);
336
+ let accessKey = cluster2.accessKeys.get("fileman$read");
337
+ if (accessKey) {
338
+ let containers = [];
339
+ if (selectedContainerNames.length > 0) {
340
+ for (var p of selectedPodNames) {
341
+ for (var c of selectedContainerNames) {
342
+ containers.push(p + "+" + c);
343
+ }
344
+ }
345
+ }
346
+ let iConfig = {
347
+ channel: "fileman",
348
+ objects: InstanceConfigObjectEnum.PODS,
349
+ action: InstanceMessageActionEnum.START,
350
+ flow: InstanceMessageFlowEnum.REQUEST,
351
+ instance: "",
352
+ accessKey: accessKeySerialize(accessKey),
353
+ scope: "fileman$read",
354
+ view: selectedContainerNames.length > 0 ? InstanceConfigViewEnum.CONTAINER : InstanceConfigViewEnum.POD,
355
+ namespace: selectedNamespaces.join(","),
356
+ group: "",
357
+ pod: selectedPodNames.map((p2) => p2).join(","),
358
+ container: containers.join(","),
359
+ data: {},
360
+ type: InstanceMessageTypeEnum.SIGNAL
361
+ };
362
+ ws.send(JSON.stringify(iConfig));
363
+ } else {
364
+ addMessage(SignalMessageLevelEnum.ERROR, "No accessKey for starting fileman streaming");
365
+ return;
366
+ }
367
+ };
368
+ const startFilemanViewer = () => {
369
+ let cluster2 = validClusters.find((cluster3) => cluster3.name === selectedClusterName);
370
+ if (!cluster2) {
371
+ addMessage(SignalMessageLevelEnum.ERROR, "No cluster selected");
372
+ return;
373
+ }
374
+ try {
375
+ let ws = new WebSocket(cluster2.url);
376
+ ws.onopen = () => websocketOnOpen(ws);
377
+ ws.onmessage = (event) => websocketOnMessage(event);
378
+ ws.onclose = (event) => websocketOnClose(event);
379
+ setWebSocket(ws);
380
+ } catch (err) {
381
+ }
382
+ };
383
+ const websocketOnClose = (_event) => {
384
+ console.log(`WS disconnected`);
385
+ setStarted(false);
386
+ paused.current = false;
387
+ setStopped(true);
388
+ };
389
+ const stopFilemanViewer = () => {
390
+ webSocket?.close();
391
+ };
392
+ const actionButtons = () => {
393
+ let hasKey = false;
394
+ let cluster2 = validClusters.find((cluster3) => cluster3.name === selectedClusterName);
395
+ if (cluster2) {
396
+ hasKey = Boolean(cluster2.accessKeys.get("fileman$read"));
397
+ }
398
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(IconButton, { onClick: () => clickStart(), title: "Play", disabled: started || !paused || selectedPodNames.length === 0 || !hasKey }, /* @__PURE__ */ React.createElement(PlayIcon, null)), /* @__PURE__ */ React.createElement(IconButton, { onClick: onClickPause, title: "Pause", disabled: !(started && !paused.current && selectedPodNames.length > 0) }, /* @__PURE__ */ React.createElement(PauseIcon, null)), /* @__PURE__ */ React.createElement(IconButton, { onClick: onClickStop, title: "Stop", disabled: stopped || selectedPodNames.length === 0 }, /* @__PURE__ */ React.createElement(StopIcon, null)));
399
+ };
400
+ const statusButtons = (title) => {
401
+ const show = (level2) => {
402
+ setShowStatusDialog(true);
403
+ setStatusLevel(level2);
404
+ };
405
+ const prepareText = (txt) => {
406
+ return txt ? txt.length > 25 ? txt.substring(0, 25) + "..." : txt : "N/A";
407
+ };
408
+ return /* @__PURE__ */ React.createElement(Grid, { container: true, direction: "row" }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "h5" }, prepareText(title))), /* @__PURE__ */ React.createElement(Grid, { item: true, style: { marginTop: "-8px" } }, /* @__PURE__ */ React.createElement(IconButton, { title: "info", disabled: !statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.INFO), onClick: () => show(SignalMessageLevelEnum.INFO) }, /* @__PURE__ */ React.createElement(InfoIcon, { style: { color: statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.INFO) ? "blue" : "#BDBDBD" } })), /* @__PURE__ */ React.createElement(IconButton, { title: "warning", disabled: !statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.WARNING), onClick: () => show(SignalMessageLevelEnum.WARNING), style: { marginLeft: "-16px" } }, /* @__PURE__ */ React.createElement(WarningIcon, { style: { color: statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.WARNING) ? "orange" : "#BDBDBD" } })), /* @__PURE__ */ React.createElement(IconButton, { title: "error", disabled: !statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.ERROR), onClick: () => show(SignalMessageLevelEnum.ERROR), style: { marginLeft: "-16px" } }, /* @__PURE__ */ React.createElement(ErrorIcon, { style: { color: statusMessages.some((m) => m.type === InstanceMessageTypeEnum.SIGNAL && m.level === SignalMessageLevelEnum.ERROR) ? "red" : "#BDBDBD" } }))));
409
+ };
410
+ const statusClear = (level2) => {
411
+ setStatusMessages(statusMessages.filter((m) => m.level !== level2));
412
+ setShowStatusDialog(false);
413
+ };
414
+ const onError = (error2, _file) => {
415
+ addMessage(SignalMessageLevelEnum.ERROR, error2.message);
416
+ };
417
+ const onRename = (file, newName) => {
418
+ let [namespace, pod, container] = file.path.split("/").slice(1);
419
+ files.current = files.current.filter((f) => f.path !== file.path);
420
+ setStateFiles([...files.current]);
421
+ sendCommand("rename" /* RENAME */, namespace, pod, container, [file.path, newName]);
422
+ };
423
+ const onRefresh = () => {
424
+ if (level >= 3) {
425
+ files.current = files.current.filter((f) => !f.path.startsWith(currentPath + "/"));
426
+ getLocalDir(currentPath + "/");
427
+ } else {
428
+ sendCommand("home" /* HOME */, "", "", "", []);
429
+ }
430
+ };
431
+ const getLocalDir = (folder) => {
432
+ let cluster2 = validClusters.find((cluster3) => cluster3.name === selectedClusterName);
433
+ if (cluster2) {
434
+ let accessKey = cluster2.accessKeys.get("fileman$read");
435
+ if (accessKey && instance?.current && webSocket) {
436
+ let [namespace, pod, container] = folder.split("/").slice(1);
437
+ let filemanMessage = {
438
+ flow: InstanceMessageFlowEnum.REQUEST,
439
+ action: InstanceMessageActionEnum.COMMAND,
440
+ channel: "fileman",
441
+ type: InstanceMessageTypeEnum.DATA,
442
+ accessKey: accessKeySerialize(accessKey),
443
+ instance: instance.current,
444
+ id: v4(),
445
+ command: "dir" /* DIR */,
446
+ namespace,
447
+ group: "",
448
+ pod,
449
+ container,
450
+ params: [folder],
451
+ msgtype: "filemanmessage"
452
+ };
453
+ let payload = JSON.stringify(filemanMessage);
454
+ webSocket.send(payload);
455
+ }
456
+ }
457
+ };
458
+ const sendCommand = (command, namespace, pod, container, params) => {
459
+ let cluster2 = validClusters.find((cluster3) => cluster3.name === selectedClusterName);
460
+ if (cluster2) {
461
+ let accessKey = cluster2.accessKeys.get("fileman$read");
462
+ if (accessKey && instance?.current && webSocket) {
463
+ let filemanMessage = {
464
+ flow: InstanceMessageFlowEnum.REQUEST,
465
+ action: InstanceMessageActionEnum.COMMAND,
466
+ channel: "fileman",
467
+ type: InstanceMessageTypeEnum.DATA,
468
+ accessKey: accessKeySerialize(accessKey),
469
+ instance: instance.current,
470
+ id: v4(),
471
+ command,
472
+ namespace,
473
+ group: "",
474
+ pod,
475
+ container,
476
+ params,
477
+ msgtype: "filemanmessage"
478
+ };
479
+ let payload = JSON.stringify(filemanMessage);
480
+ webSocket.send(payload);
481
+ } else {
482
+ addMessage(SignalMessageLevelEnum.ERROR, "Have no instance/accessKey");
483
+ }
484
+ } else {
485
+ addMessage(SignalMessageLevelEnum.ERROR, "Have no cluster");
486
+ }
487
+ };
488
+ const onDelete = async (filesToDelete) => {
489
+ for (let file of filesToDelete) {
490
+ let [namespace, pod, container] = file.path.split("/").slice(1);
491
+ sendCommand("delete" /* DELETE */, namespace, pod, container, [file.path]);
492
+ }
493
+ };
494
+ const onCreateFolder = async (name, parentFolder) => {
495
+ let [namespace, pod, container] = parentFolder.path.split("/").slice(1);
496
+ sendCommand("create" /* CREATE */, namespace, pod, container, [parentFolder.path + "/" + name]);
497
+ };
498
+ const onDownload = async (filesToDownload) => {
499
+ let cluster2 = validClusters.find((cluster3) => cluster3.name === selectedClusterName);
500
+ if (cluster2) {
501
+ let accessKey = cluster2.accessKeys.get("fileman$read");
502
+ if (accessKey) {
503
+ for (let file of filesToDownload) {
504
+ const url = `${cluster2.url}/channel/fileman/download?key=${instance.current}&filename=${file.path}`;
505
+ try {
506
+ const response = await fetch(url, { headers: { "Authorization": "Bearer " + accessKeySerialize(accessKey) } });
507
+ if (response.ok) {
508
+ const blob = await response.blob();
509
+ const link = document.createElement("a");
510
+ link.href = URL.createObjectURL(blob);
511
+ link.download = file.path.split("/").slice(-1)[0];
512
+ if (file.isDirectory) link.download += ".tar.gz";
513
+ document.body.appendChild(link);
514
+ link.click();
515
+ document.body.removeChild(link);
516
+ URL.revokeObjectURL(link.href);
517
+ } else {
518
+ console.error(`Error downloading file: ${file.path}`);
519
+ addMessage(SignalMessageLevelEnum.ERROR, `Error downloading file ${file.path}: (${response.status}) ${await response.text()}`);
520
+ }
521
+ } catch (error2) {
522
+ console.error(`Error downloading file: ${file.path}`, error2);
523
+ addMessage(SignalMessageLevelEnum.ERROR, `Error downloading file ${file.path}: ${error2}`);
524
+ }
525
+ }
526
+ } else {
527
+ addMessage(SignalMessageLevelEnum.ERROR, "Have no instance/accessKey");
528
+ }
529
+ } else {
530
+ addMessage(SignalMessageLevelEnum.ERROR, "Have no cluster");
531
+ }
532
+ };
533
+ const onPaste = (filesToPaste, destFolder, operation) => {
534
+ let command = operation === "move" ? "move" /* MOVE */ : "copy" /* COPY */;
535
+ for (let file of filesToPaste) {
536
+ let [namespace, pod, container] = file.path.split("/").slice(1);
537
+ sendCommand(command, namespace, pod, container, [file.path, destFolder.path]);
538
+ }
539
+ };
540
+ const onFolderChange = (folder) => {
541
+ setCurrentPath(folder);
542
+ folder += "/";
543
+ let level2 = folder.split("/").length - 1;
544
+ if (level2 > 3) getLocalDir(folder);
545
+ };
546
+ const onFileUploading = (file, _parentFolder) => {
547
+ return { filename: currentPath + "/" + file.name };
548
+ };
549
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, loading && /* @__PURE__ */ React.createElement(Progress, null), !isKwirthAvailable(entity) && !loading && error && /* @__PURE__ */ React.createElement(WarningPanel, { title: "An error has ocurred while obtaining data from kuebernetes clusters.", message: error?.message }), !isKwirthAvailable(entity) && !loading && /* @__PURE__ */ React.createElement(MissingAnnotationEmptyState, { readMoreUrl: "https://github.com/jfvilas/plugin-kwirth-fileman", annotation: [ANNOTATION_BACKSTAGE_KUBERNETES_LABELID, ANNOTATION_BACKSTAGE_KUBERNETES_LABELSELECTOR] }), isKwirthAvailable(entity) && !loading && validClusters && validClusters.length === 0 && /* @__PURE__ */ React.createElement(ComponentNotFound, { error: ErrorType.NO_CLUSTERS, entity }), isKwirthAvailable(entity) && !loading && validClusters && validClusters.length > 0 && validClusters.reduce((sum, cluster2) => sum + cluster2.pods.length, 0) === 0 && /* @__PURE__ */ React.createElement(ComponentNotFound, { error: ErrorType.NO_PODS, entity }), isKwirthAvailable(entity) && !loading && validClusters && validClusters.length > 0 && validClusters.reduce((sum, cluster2) => sum + cluster2.pods.length, 0) > 0 && /* @__PURE__ */ React.createElement(Box, { sx: { display: "flex", height: "100%" } }, /* @__PURE__ */ React.createElement(Box, { sx: { width: "200px", maxWidth: "200px" } }, /* @__PURE__ */ React.createElement(Grid, { container: true, direction: "column" }, /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(ClusterList, { resources: validClusters, selectedClusterName, onSelect: onSelectCluster }))), !props.hideVersion && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(KwirthNews, { latestVersions: backendInfo, backendVersion, ownVersion: VERSION }))))), /* @__PURE__ */ React.createElement(Box, { sx: { flexGrow: 1, flex: 1, overflow: "hidden", p: 1, marginLeft: "8px" } }, !selectedClusterName && /* @__PURE__ */ React.createElement("img", { src: KwirthFilemanLogo, alt: "No cluster selected", style: { left: "40%", marginTop: "10%", width: "20%", position: "relative" } }), selectedClusterName && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Card, { style: { marginTop: -8, marginBottom: "8px" } }, /* @__PURE__ */ React.createElement(
550
+ CardHeader,
551
+ {
552
+ title: statusButtons(selectedClusterName),
553
+ style: { marginTop: -4, marginBottom: 4, flexShrink: 0 },
554
+ action: actionButtons()
555
+ }
556
+ )), started && /* @__PURE__ */ React.createElement(Grid, { ref: filemanBoxRef, style: { height: `calc(100vh - ${filemanBoxTop}px - 35px)` } }, /* @__PURE__ */ React.createElement(
557
+ FileManager,
558
+ {
559
+ className: styles.customFm,
560
+ files: stateFiles,
561
+ filePreviewPath: "http://avoid-console-error",
562
+ primaryColor: "#1976d2",
563
+ fontFamily: '"Helvetica Neue", Helvetica, Roboto, Arial, sans-serif',
564
+ height: "100%",
565
+ actions: /* @__PURE__ */ new Map(),
566
+ icons: /* @__PURE__ */ new Map(),
567
+ fileUploadConfig,
568
+ onCreateFolder,
569
+ onError,
570
+ onRename,
571
+ onPaste,
572
+ onDelete,
573
+ onFolderChange,
574
+ onRefresh,
575
+ onFileUploading,
576
+ onDownload,
577
+ enableFilePreview: false,
578
+ initialPath: "",
579
+ permissions
580
+ }
581
+ ))))), showStatusDialog && /* @__PURE__ */ React.createElement(StatusLog, { level: statusLevel, onClose: () => setShowStatusDialog(false), statusMessages, onClear: statusClear }));
582
+ };
583
+
584
+ export { EntityKwirthFilemanContent };
585
+ //# sourceMappingURL=EntityKwirthFilemanContent.esm.js.map