@kumori/aurora-backend-handler 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.
@@ -0,0 +1,1161 @@
1
+ import { eventHelper } from "../backend-handler";
2
+ import {
3
+ initializeGlobalWebSocketClient,
4
+ makeGlobalWebSocketRequest,
5
+ } from "../websocket-manager.ts";
6
+ import { environment } from "../environment.ts";
7
+ import { Account, Container, Environment, Instance, MarketplaceItem, Notification, Organization, Platform, Registry, Resource, Service, Tenant, User, UserData } from "@hestekumori/aurora-interfaces";
8
+
9
+ type Security = string;
10
+
11
+ export const loadUser = async (token: string, previousData: UserData) => {
12
+ try {
13
+ await initializeGlobalWebSocketClient(token, "", "", previousData);
14
+ } catch (error) {
15
+ console.error("Error in loadUser:", {
16
+ error,
17
+ errorMessage: error instanceof Error ? error.message : "Unknown error",
18
+ stack: error instanceof Error ? error.stack : undefined,
19
+ });
20
+ throw error;
21
+ }
22
+ };
23
+
24
+ export const isUserLogged = async (security: Security) => {
25
+ let userLogged = false;
26
+ try {
27
+ await initializeGlobalWebSocketClient(security);
28
+ await makeGlobalWebSocketRequest(
29
+ "user:user_info",
30
+ {},
31
+ 30000,
32
+ "GET",
33
+ "USER_INFO"
34
+ );
35
+ userLogged = true;
36
+ } catch (error) {
37
+ console.error("User not logged in:", error);
38
+ userLogged = false;
39
+ }
40
+ return userLogged;
41
+ };
42
+
43
+ export const createUser = async (user: User) => {
44
+ const userBody = {
45
+ name: user.name,
46
+ surname: user.surname,
47
+ discoveryMethod: "work",
48
+ companyName: user.companyName,
49
+ companyRole: user.rol,
50
+ };
51
+
52
+ const response = await fetch(
53
+ `${environment.apiServer.baseUrl}/api/${environment.apiServer.apiVersion}/user`,
54
+ {
55
+ method: "POST",
56
+ body: JSON.stringify(userBody),
57
+ }
58
+ );
59
+
60
+ if (!response.ok) {
61
+ const error = await response.json();
62
+ throw new Error(
63
+ `HTTP Error ${response.status}: ${error.message || "Unknown error"}`
64
+ );
65
+ }
66
+ };
67
+ export const loadUserData = async () => {
68
+ try {
69
+ const response = await makeGlobalWebSocketRequest("user:user_info", {});
70
+ const userResponse = response.data;
71
+ const provider = Object.keys(userResponse.profiles)[0];
72
+ const email = userResponse.profiles[provider]?.spec?.claims?.email;
73
+
74
+ const userData: UserData = {
75
+ id: userResponse.userRecord.id.name,
76
+ name: userResponse.userRecord.spec.userinfo.name,
77
+ surname: userResponse.userRecord.spec.userinfo.surname,
78
+ provider: [
79
+ {
80
+ name: provider,
81
+ email: email || "",
82
+ password: "",
83
+ },
84
+ ],
85
+ notificationsEnabled: "true",
86
+ organizations: [],
87
+ clusterTokens: [],
88
+ tokens: [],
89
+ tenants: [],
90
+ axebowPlan: "freemium",
91
+ companyName: userResponse.userRecord.spec.userinfo.companyName,
92
+ rol: userResponse.userRecord.spec.userinfo.companyRole,
93
+ };
94
+
95
+ try {
96
+ const tenantsResponse = await makeGlobalWebSocketRequest(
97
+ "tenant:list_tenants",
98
+ { strict: true },
99
+ 30000,
100
+ "GET",
101
+ "ALL_TENANTS"
102
+ );
103
+ const tenantsData = tenantsResponse.data;
104
+ const userTenants: Tenant[] = [];
105
+ const userOrganizations: Organization[] = [];
106
+ for (const [_, value] of Object.entries(tenantsData)) {
107
+ const parts = (value as any).toString().split(",");
108
+ userTenants.push({
109
+ id: parts[0],
110
+ name: parts[0],
111
+ organizationsIds: [],
112
+ services: [],
113
+ accounts: [],
114
+ environments: [],
115
+ marketplaceItems: [],
116
+ resources: [],
117
+ role: parts[1],
118
+ status: "healthy",
119
+ users: [],
120
+ registry: [
121
+ {
122
+ name: "",
123
+ public: false,
124
+ credentials: "",
125
+ domain: "",
126
+ },
127
+ ],
128
+ });
129
+ }
130
+ for (const tenant of userTenants) {
131
+ try {
132
+ const tenantResponse = await makeGlobalWebSocketRequest(
133
+ "tenant:tenant_info",
134
+ {
135
+ tenant: tenant.name,
136
+ },
137
+ 30000,
138
+ "GET",
139
+ tenant.name
140
+ );
141
+ const tenantData = tenantResponse.data;
142
+ const registries = tenantData?.registries?.registries;
143
+
144
+ if (registries && Object.keys(registries).length > 0) {
145
+ const tenantRegistry = Object.keys(registries)[0];
146
+ const tenantOptions = registries[tenantRegistry]?.options;
147
+ let registry: Registry = {
148
+ name: "",
149
+ domain: "",
150
+ public: false,
151
+ credentials: "",
152
+ description: "",
153
+ extra: {},
154
+ password: "",
155
+ };
156
+ if (tenantOptions?.endpoint) {
157
+ registry.domain = tenantOptions.endpoint;
158
+ }
159
+ if (tenantOptions?.credentials) {
160
+ registry.credentials = tenantOptions.credentials;
161
+ }
162
+ if (typeof tenantOptions?.public === "boolean") {
163
+ registry.public = tenantOptions.public;
164
+ }
165
+ if (tenantData?.domains && Object.keys(tenantData.domains)[0]) {
166
+ registry.name = Object.keys(tenantData.domains)[0];
167
+ }
168
+ tenant.registry.push(registry);
169
+ } else {
170
+ console.warn(
171
+ `No se encontró información de registry para el tenant ${tenant.name}`
172
+ );
173
+ }
174
+
175
+ const tenantAccounts: Account[] = [];
176
+ const tenantEnvironments: Environment[] = [];
177
+ const userServices: Service[] = [];
178
+
179
+ if (tenantData && Array.isArray(tenantData.accounts)) {
180
+ for (const account of tenantData.accounts) {
181
+ const environmentsIds: string[] = [];
182
+
183
+ if (account.environments && Array.isArray(account.environments)) {
184
+ for (const env of account.environments) {
185
+ const envId = `${env.id.name}`;
186
+ environmentsIds.push(envId);
187
+ const newEnvironment: Environment = {
188
+ id: envId,
189
+ name: env.id.name,
190
+ account: `${account.account.id.parent.name}-${account.account.id.name}`,
191
+ tenant: account.account.id.parent.name.split("tenant/")[1],
192
+ logo: `${
193
+ env.logo ||
194
+ "https://kumori.systems/wp-content/uploads/2023/07/cropped-favicon-kumori.png"
195
+ }`,
196
+ services: [],
197
+ domains: [],
198
+ status: {
199
+ code: env.status.state,
200
+ message: env.status.message,
201
+ timestamp: env.status.timestamp,
202
+ },
203
+ usage: {
204
+ current: {
205
+ cpu: env.status.marks.vcpu.current / 1000,
206
+ memory: env.status.marks.memory.current / 1000,
207
+ storage: env.status.marks.storage.current / 1000,
208
+ volatileStorage:
209
+ env.status.marks.vstorage.current / 1000,
210
+ nonReplicatedStorage:
211
+ env.status.marks.nrstorage.current / 1000,
212
+ persistentStorage:
213
+ env.status.marks.rstorage.current / 1000,
214
+ },
215
+ limit: {
216
+ cpu: {
217
+ max: env.spec.marks.vcpu.highmark / 1000,
218
+ min: env.spec.marks.vcpu.lowmark / 1000,
219
+ },
220
+ memory: {
221
+ max: env.spec.marks.memory.highmark / 1000,
222
+ min: env.spec.marks.memory.lowmark / 1000,
223
+ },
224
+ storage: {
225
+ max: env.spec.marks.storage.highmark / 1000,
226
+ min: env.spec.marks.storage.lowmark / 1000,
227
+ },
228
+ volatileStorage: {
229
+ max: env.spec.marks.vstorage.highmark / 1000,
230
+ min: env.spec.marks.vstorage.lowmark / 1000,
231
+ },
232
+ nonReplicatedStorage: {
233
+ max: env.spec.marks.nrstorage.highmark / 1000,
234
+ min: env.spec.marks.nrstorage.lowmark / 1000,
235
+ },
236
+ persistentStorage: {
237
+ max: env.spec.marks.rstorage.highmark / 1000,
238
+ min: env.spec.marks.rstorage.lowmark / 1000,
239
+ },
240
+ },
241
+ cost: env.status.marks.cost.current,
242
+ },
243
+ organization: "",
244
+ cloudProvider: "",
245
+ labels: [],
246
+ type: "",
247
+ cluster: { name: "" },
248
+ };
249
+ tenantEnvironments.push(newEnvironment);
250
+ }
251
+ }
252
+
253
+ const cloudProviderInfo = extractCloudProviderInfo(
254
+ account.account.spec
255
+ );
256
+ const newAccount: Account = {
257
+ id: `${account.account.id.parent.name}-${account.account.id.name}`,
258
+ name: account.account.id.name,
259
+ tenant: account.account.id.parent.name.split("tenant/")[1],
260
+ cloudProvider: {
261
+ name: account.account.spec.api,
262
+ ...cloudProviderInfo,
263
+ },
264
+ logo: `${
265
+ account.account.spec.api ||
266
+ "https://kumori.systems/wp-content/uploads/2023/07/cropped-favicon-kumori.png"
267
+ }`,
268
+ environments: environmentsIds,
269
+ services: [],
270
+ domains: [],
271
+ status: account.account.status.validCredentials
272
+ ? account.account.status.validCredentials?.status ===
273
+ "InvalidCredentials"
274
+ ? "InvalidCredentials"
275
+ : !account.account.status.validCredentials?.status &&
276
+ account.account.id !== "kumori"
277
+ ? "pending"
278
+ : "healthy"
279
+ : "",
280
+ usage: {
281
+ current: {
282
+ cpu: account.account.status.marks.vcpu.current / 1000,
283
+ memory: account.account.status.marks.memory.current / 1000,
284
+ storage:
285
+ account.account.status.marks.storage.current / 1000,
286
+ volatileStorage:
287
+ account.account.status.marks.vstorage.current / 1000,
288
+ nonReplicatedStorage:
289
+ account.account.status.marks.nrstorage.current / 1000,
290
+ persistentStorage:
291
+ account.account.status.marks.rstorage.current / 1000,
292
+ },
293
+ limit: {
294
+ cpu: {
295
+ max: account.account.spec.marks.vcpu.highmark / 1000,
296
+ min: account.account.spec.marks.vcpu.lowmark / 1000,
297
+ },
298
+ memory: {
299
+ max: account.account.spec.marks.memory.highmark / 1000,
300
+ min: account.account.spec.marks.memory.lowmark / 1000,
301
+ },
302
+ storage: {
303
+ max: account.account.spec.marks.storage.highmark / 1000,
304
+ min: account.account.spec.marks.storage.lowmark / 1000,
305
+ },
306
+ volatileStorage: {
307
+ max: account.account.spec.marks.vstorage.highmark / 1000,
308
+ min: account.account.spec.marks.vstorage.lowmark / 1000,
309
+ },
310
+ nonReplicatedStorage: {
311
+ max: account.account.spec.marks.nrstorage.highmark / 1000,
312
+ min: account.account.spec.marks.nrstorage.lowmark / 1000,
313
+ },
314
+ persistentStorage: {
315
+ max: account.account.spec.marks.rstorage.highmark / 1000,
316
+ min: account.account.spec.marks.rstorage.lowmark / 1000,
317
+ },
318
+ },
319
+ cost: account.account.status.marks.cost.current,
320
+ },
321
+ flavors: {
322
+ small: account.account.spec.iaasconfig.smallVMFlavor,
323
+ medium: account.account.spec.iaasconfig.mediumVMFlavor,
324
+ large: account.account.spec.iaasconfig.largeVMFlavor,
325
+ volatile: account.account.spec.iaasconfig.volatile,
326
+ nonReplicated: account.account.spec.iaasconfig.nonreplicated,
327
+ persistent: account.account.spec.iaasconfig.persistent,
328
+ },
329
+ organization: account.organization,
330
+ };
331
+
332
+ tenantAccounts.push(newAccount);
333
+ }
334
+ const kinds = [
335
+ "ca",
336
+ "certificate",
337
+ "domain",
338
+ "port",
339
+ "secret",
340
+ "volume",
341
+ ];
342
+
343
+ for (const kind of kinds) {
344
+ try {
345
+ const resourcesResponse = await makeGlobalWebSocketRequest(
346
+ "resource:list_resources",
347
+ {
348
+ tenant: tenant.name,
349
+ kind: kind,
350
+ },
351
+ 30000,
352
+ "GET",
353
+ "ALL_RESOURCES"
354
+ );
355
+ const resourcesData = resourcesResponse.data;
356
+ if (
357
+ !resourcesData[kind] ||
358
+ !Array.isArray(resourcesData[kind])
359
+ ) {
360
+ console.warn(
361
+ `No se encontraron recursos de tipo ${kind} o no es un array`
362
+ );
363
+ continue;
364
+ }
365
+
366
+ for (const resource of resourcesData[kind]) {
367
+ try {
368
+ const resourceName = resource.id.name;
369
+ const resourceResponse = await makeGlobalWebSocketRequest(
370
+ "resource:resource_info",
371
+ {
372
+ tenant: tenant.name,
373
+ kind: kind,
374
+ resource: resourceName,
375
+ },
376
+ 30000,
377
+ "GET",
378
+ resourceName
379
+ );
380
+ const resourceDataJson = resourceResponse.data;
381
+ const resourceType = resourceDataJson.id.kind;
382
+ let resourceStatus: "used" | "available" | "error" =
383
+ "available";
384
+
385
+ if (
386
+ resourceDataJson.status &&
387
+ resourceDataJson.status.requested
388
+ ) {
389
+ const requestedKeys = Object.keys(
390
+ resourceDataJson.status.requested
391
+ );
392
+ if (requestedKeys.length > 0) {
393
+ resourceStatus = "used";
394
+ }
395
+ }
396
+
397
+ if (resourceType === "volume") {
398
+ tenant.resources.push({
399
+ type: kind,
400
+ name: resourceName,
401
+ value: resourceDataJson.spec[resourceType].size,
402
+ kind: resourceDataJson.spec[resourceType].kind,
403
+ maxItems: resourceDataJson.spec[resourceType].maxitems,
404
+ status: resourceStatus,
405
+ } as Resource);
406
+ } else if (resourceType === "certificate") {
407
+ tenant.resources.push({
408
+ type: kind,
409
+ name: resourceName,
410
+ value: resourceDataJson.spec[resourceType].cert,
411
+ key: resourceDataJson.spec[resourceType].key,
412
+ domain: resourceDataJson.spec[resourceType].domain,
413
+ status: resourceStatus,
414
+ } as Resource);
415
+ } else if (
416
+ resourceType === "domain" ||
417
+ resourceType === "port" ||
418
+ resourceType === "ca" ||
419
+ resourceType === "secret"
420
+ ) {
421
+ tenant.resources.push({
422
+ type: kind,
423
+ name: resourceName,
424
+ value: resourceDataJson.spec[resourceType],
425
+ status: resourceStatus,
426
+ } as Resource);
427
+ }
428
+ } catch (resourceError) {
429
+ console.error(
430
+ `Error procesando recurso individual de tipo ${kind}:`,
431
+ resourceError
432
+ );
433
+ }
434
+ }
435
+ } catch (error) {
436
+ console.error(
437
+ `Error procesando recursos de tipo ${kind}:`,
438
+ error
439
+ );
440
+ }
441
+ }
442
+ }
443
+ const servicesResponse = await makeGlobalWebSocketRequest(
444
+ "service:list_services",
445
+ {
446
+ tenant: tenant.name,
447
+ },
448
+ 30000,
449
+ "GET",
450
+ "ALL_SERVICES"
451
+ );
452
+ const servicesData = servicesResponse.data;
453
+ const serviceEntries = Object.entries(servicesData);
454
+
455
+ for (const [, serviceValue] of serviceEntries) {
456
+ try {
457
+ let serviceStatus = {
458
+ code: "",
459
+ message: "",
460
+ timestamp: "",
461
+ args: [],
462
+ };
463
+ const serviceResponse = await makeGlobalWebSocketRequest(
464
+ "service:describe_service",
465
+ {
466
+ tenant: tenant.name,
467
+ service: (serviceValue as any).name,
468
+ summary: false,
469
+ },
470
+ 30000,
471
+ "GET",
472
+ (serviceValue as any).name
473
+ );
474
+ const serviceData = serviceResponse.data;
475
+ let revisionData;
476
+ try {
477
+ const revisionResponse = await makeGlobalWebSocketRequest(
478
+ "revision:revision_info",
479
+ {
480
+ tenant: tenant.name,
481
+ service: (serviceValue as any).name,
482
+ revision: "latest",
483
+ },
484
+ 30000,
485
+ "GET_REVISION",
486
+ (serviceValue as any).name
487
+ );
488
+ revisionData = revisionResponse.data;
489
+ } catch (revisionError) {
490
+ serviceStatus = {
491
+ code: "PENDING",
492
+ message: "",
493
+ timestamp: "",
494
+ args: [],
495
+ },
496
+ console.warn(
497
+ `No se pudo obtener la revisión para el servicio ${
498
+ (serviceValue as any).name
499
+ }:`,
500
+ revisionError
501
+ );
502
+ continue;
503
+ }
504
+
505
+ const serviceName = revisionData.solution.top;
506
+ let revisionsData;
507
+ try {
508
+ const revisionsResponse = await makeGlobalWebSocketRequest(
509
+ "service:report_history_service",
510
+ {
511
+ tenant: tenant.name,
512
+ service: serviceName,
513
+ },
514
+ 30000,
515
+ "GET_HISTORY",
516
+ serviceName
517
+ );
518
+ revisionsData = revisionsResponse.data;
519
+ } catch (historyError) {
520
+ serviceStatus = {
521
+ code: "PENDING",
522
+ message: "",
523
+ timestamp: "",
524
+ args: [],
525
+ },
526
+ console.warn(
527
+ `No se pudo obtener el historial para el servicio ${serviceName}:`,
528
+ historyError
529
+ );
530
+ continue;
531
+ }
532
+
533
+ const revisions: string[] = [];
534
+ for (const revision of revisionsData) {
535
+ revisions.push(revision.revision);
536
+ }
537
+
538
+ const serviceRoles: { name: string; instances: Instance[] }[] =
539
+ [];
540
+ let serviceUsedVCPU = 0;
541
+ let serviceUsedRAM = 0;
542
+ const roles =
543
+ revisionData.solution.deployments[serviceName].artifact
544
+ .description.role;
545
+
546
+ for (const roleName of Object.keys(roles)) {
547
+ try {
548
+ const instancesResponse = await makeGlobalWebSocketRequest(
549
+ "service:describe_role_service",
550
+ {
551
+ tenant: tenant.name,
552
+ service: serviceName,
553
+ role: roleName,
554
+ },
555
+ 30000,
556
+ "GET_ROLE",
557
+ serviceName + "_" + roleName
558
+ );
559
+ const instancesData = instancesResponse.data;
560
+ const instances: Instance[] = [];
561
+
562
+ for (const [instanceName, instanceValue] of Object.entries(
563
+ instancesData.instances
564
+ )) {
565
+ const containers: Container[] = [];
566
+ for (const [
567
+ containerName,
568
+ containerValue,
569
+ ] of Object.entries((instanceValue as any).containers)) {
570
+ containers.push({
571
+ name: containerName,
572
+ ready: (containerValue as any).ready,
573
+ reestartCount: (containerValue as any).restarts,
574
+ metrics: {
575
+ cpu: (containerValue as any).metrics.usage.cpu,
576
+ memory: (containerValue as any).metrics.usage.memory,
577
+ },
578
+ states: (containerValue as any).states,
579
+ });
580
+ serviceUsedVCPU += (containerValue as any).metrics.usage
581
+ .cpu;
582
+ serviceUsedRAM += (containerValue as any).metrics.usage
583
+ .memory;
584
+ }
585
+ instances.push({
586
+ id: instanceName,
587
+ name: instanceName,
588
+ status: (instanceValue as any).status,
589
+ usage: {
590
+ current: {
591
+ cpu: (instanceValue as any).metrics.usage.cpu,
592
+ memory: (instanceValue as any).metrics.usage.memory,
593
+ storage: 0,
594
+ volatileStorage: 0,
595
+ nonReplicatedStorage: 0,
596
+ persistentStorage: 0,
597
+ },
598
+ limit: {
599
+ cpu: {
600
+ max:
601
+ serviceData.artifact.description.role[roleName]
602
+ .artifact.description.code[roleName]?.size?.cpu
603
+ ?.min / 1000,
604
+ min:
605
+ serviceData.artifact.description.role[roleName]
606
+ .artifact.description.code[roleName]?.size?.cpu
607
+ ?.min / 1000,
608
+ },
609
+ memory: {
610
+ max:
611
+ serviceData.artifact.description.role[roleName]
612
+ .artifact.description.code[roleName]?.size
613
+ ?.memory?.size / 1000,
614
+ min:
615
+ serviceData.artifact.description.role[roleName]
616
+ .artifact.description.code[roleName]?.size
617
+ ?.memory?.size / 1000,
618
+ },
619
+ storage: {
620
+ max: 0,
621
+ min: 0,
622
+ },
623
+ volatileStorage: {
624
+ max: 0,
625
+ min: 0,
626
+ },
627
+ nonReplicatedStorage: {
628
+ max: 0,
629
+ min: 0,
630
+ },
631
+ persistentStorage: {
632
+ max: 0,
633
+ min: 0,
634
+ },
635
+ },
636
+ cost: 0,
637
+ },
638
+ logs: [],
639
+ containers: containers,
640
+ });
641
+ }
642
+ serviceRoles.push({ name: roleName, instances });
643
+ } catch (instanceError) {
644
+ serviceStatus = {
645
+ code: "PENDING",
646
+ message: "",
647
+ timestamp: "",
648
+ args: [],
649
+ },
650
+ console.warn(
651
+ `Error obteniendo instancias para el rol ${roleName}:`,
652
+ instanceError
653
+ );
654
+ }
655
+ }
656
+
657
+ let containerName = "";
658
+ const firstRoleName = Object.keys(roles)[0];
659
+ const code = roles[firstRoleName].artifact.description.code;
660
+ for (const key of Object.keys(code)) {
661
+ if (!key.includes("INIT")) {
662
+ containerName = key;
663
+ break;
664
+ }
665
+ }
666
+
667
+ const newService: Service = {
668
+ id: (serviceValue as any).name,
669
+ tenant: tenant.name,
670
+ account: (serviceValue as any).environment.split("/")[4],
671
+ environment: (serviceValue as any).environment.split("/")[6],
672
+ name: (serviceValue as any).name,
673
+ logo: "",
674
+ description: "",
675
+ revisions: revisions,
676
+ status: serviceStatus,
677
+ role: serviceRoles,
678
+ links: [],
679
+ resources: [],
680
+ parameters: [],
681
+ usage: {
682
+ current: {
683
+ cpu: serviceUsedVCPU,
684
+ memory: serviceUsedRAM / 1000000000,
685
+ storage: 0,
686
+ volatileStorage: 0,
687
+ nonReplicatedStorage: 0,
688
+ persistentStorage: 0,
689
+ },
690
+ limit: {
691
+ cpu: {
692
+ max: revisionData.revision.spec.intensives.vcpu / 1000,
693
+ min: revisionData.revision.spec.intensives.vcpu / 1000,
694
+ },
695
+ memory: {
696
+ max: revisionData.revision.spec.intensives.ram / 1000,
697
+ min: revisionData.revision.spec.intensives.ram / 1000,
698
+ },
699
+ storage: {
700
+ max: revisionData.revision.spec.intensives.shared_disk,
701
+ min: revisionData.revision.spec.intensives.shared_disk,
702
+ },
703
+ volatileStorage: {
704
+ max: revisionData.revision.spec.intensives.volatile_disk,
705
+ min: revisionData.revision.spec.intensives.volatile_disk,
706
+ },
707
+ nonReplicatedStorage: {
708
+ max: revisionData.revision.spec.intensives
709
+ .nrpersistent_disk,
710
+ min: revisionData.revision.spec.intensives
711
+ .nrpersistent_disk,
712
+ },
713
+ persistentStorage: {
714
+ max: revisionData.revision.spec.intensives
715
+ .persistent_disk,
716
+ min: revisionData.revision.spec.intensives
717
+ .persistent_disk,
718
+ },
719
+ },
720
+ cost: 0,
721
+ },
722
+ minReplicas: 0,
723
+ maxReplicas: 0,
724
+ lastDeployed: `${revisionData.revision.spec.tstamp}`,
725
+ project: "",
726
+ registry:
727
+ roles[firstRoleName].artifact.description.code[containerName]
728
+ .image.hub.name,
729
+ imageName:
730
+ roles[firstRoleName].artifact.description.code[containerName]
731
+ .image.tag,
732
+ entrypoint:
733
+ roles[firstRoleName].artifact.description.code[containerName]
734
+ .entrypoint,
735
+ cmd: roles[firstRoleName].artifact.description.code[
736
+ containerName
737
+ ].cmd,
738
+ serverChannels: [],
739
+ clientChannels: [],
740
+ duplexChannels: [],
741
+ cloudProvider: "",
742
+ currentRevision: revisionData.revision.id.name,
743
+ };
744
+
745
+ userServices.push(newService);
746
+ } catch (error) {
747
+ console.error("Error al obtener los datos del servicio:", error);
748
+ }
749
+ }
750
+ for (const account of tenantAccounts) {
751
+ account.services = userServices
752
+ .filter((service) => service.account === account.name)
753
+ .map((service) => service.name);
754
+ }
755
+ for (const environment of tenantEnvironments) {
756
+ environment.services = userServices
757
+ .filter((service) => service.environment === environment.name)
758
+ .map((service) => service.name);
759
+ }
760
+
761
+ try {
762
+ const marketplaceResponse = await makeGlobalWebSocketRequest(
763
+ "marketplace:search_marketplace",
764
+ {
765
+ tenant: tenant.name,
766
+ name: "",
767
+ },
768
+ 30000,
769
+ "GET",
770
+ "MARKETPLACE"
771
+ );
772
+ const marketplaceData = marketplaceResponse.data;
773
+ const tenantItems: MarketplaceItem[] = [];
774
+ Object.entries(marketplaceData.items).forEach(([_, value]) => {
775
+ let cpu = 0;
776
+ let memory = 0;
777
+ (value as any).tags.forEach((tag: string) => {
778
+ if (tag.includes("cpu")) {
779
+ cpu = Number(tag.split("::")[1].split("vCPU")[0]);
780
+ } else if (tag.includes("memory")) {
781
+ memory = Number(tag.split("::")[1].split("MB")[0]);
782
+ }
783
+ });
784
+ const item: MarketplaceItem = {
785
+ tenant: tenant.toString(),
786
+ name: (value as any).name,
787
+ logo: "https://gitlab.com/uploads/-/system/group/avatar/86481133/Axebow-Logo-2.jpeg?width=96",
788
+ description: (value as any).name,
789
+ version: (value as any).version,
790
+ requirements: {
791
+ cpu: cpu,
792
+ memory: memory,
793
+ },
794
+ status: "",
795
+ instances: [],
796
+ links: [],
797
+ resources: [],
798
+ domain: (value as any).domain,
799
+ type: (value as any).artifactTypes[0],
800
+ };
801
+ tenantItems.push(item);
802
+ });
803
+ tenant.marketplaceItems = tenantItems;
804
+ } catch (marketplaceError) {
805
+ console.error(
806
+ `Error obteniendo marketplace para tenant ${tenant.name}:`,
807
+ marketplaceError
808
+ );
809
+ tenant.marketplaceItems = [];
810
+ }
811
+
812
+ tenant.accounts = tenantAccounts;
813
+ tenant.environments = tenantEnvironments;
814
+ tenant.services = userServices;
815
+ } catch (tenantError) {
816
+ console.error(`Error procesando tenant ${tenant.name}:`, tenantError);
817
+ }
818
+ }
819
+
820
+ try {
821
+ const organizationsResponse = await makeGlobalWebSocketRequest(
822
+ "organization:list_organizations",
823
+ {
824
+ strict: true,
825
+ },
826
+ 30000,
827
+ "GET",
828
+ "ORGANIZATIONS"
829
+ );
830
+ const organizationsData = organizationsResponse.data;
831
+ const organizationsEntries = Object.entries(organizationsData);
832
+ for (const [, organizationValue] of organizationsEntries) {
833
+ const newOrg = {
834
+ id: (organizationValue as any).spec.companyName,
835
+ name: (organizationValue as any).id.name,
836
+ usersIds: Object.keys((organizationValue as any).spec.users),
837
+ tenantsIds: Object.keys((organizationValue as any).spec.tenants),
838
+ billingInformation: {
839
+ legalName: (organizationValue as any).spec.billing.companyname,
840
+ CIFNIF: (organizationValue as any).id.name,
841
+ email: (organizationValue as any).spec.billing.email,
842
+ language: (organizationValue as any).spec.billing.language,
843
+ country: (organizationValue as any).spec.billing.country,
844
+ region: (organizationValue as any).spec.billing.region,
845
+ city: (organizationValue as any).spec.billing.city,
846
+ address: (organizationValue as any).spec.billing.address,
847
+ zipcode: (organizationValue as any).spec.billing.zip,
848
+ },
849
+ invoices: [],
850
+ };
851
+ userOrganizations.push(newOrg);
852
+ }
853
+ } catch (organizationError) {
854
+ console.error("Error obteniendo organizaciones:", organizationError);
855
+ }
856
+
857
+ try {
858
+ const platformResponse = await makeGlobalWebSocketRequest(
859
+ "misc:info",
860
+ {},
861
+ 30000,
862
+ "GET",
863
+ "PLATFORM"
864
+ );
865
+
866
+ const platformData: Platform = {
867
+ instanceName: String(platformResponse.instanceName),
868
+ platformVersion: String(platformResponse.platformVersion),
869
+ referenceDomain: String(platformResponse.referenceDomain),
870
+ apiVersions: Array.isArray(platformResponse.apiVersions)
871
+ ? platformResponse.apiVersions.map(String)
872
+ : [],
873
+ oidProviders: Array.isArray(platformResponse.oidProviders)
874
+ ? platformResponse.oidProviders.map(String)
875
+ : [],
876
+ iaasProviders:
877
+ typeof platformResponse.iaasProviders === "object" &&
878
+ platformResponse.iaasProviders !== null
879
+ ? platformResponse.iaasProviders
880
+ : {},
881
+ freemiumAdvancedLimits: {
882
+ vcpu: {
883
+ lowmark: Number(
884
+ platformResponse.freemiumAdvancedLimits.vcpu.lowmark
885
+ ),
886
+ highmark: Number(
887
+ platformResponse.freemiumAdvancedLimits.vcpu.highmark
888
+ ),
889
+ unit: String(platformResponse.freemiumAdvancedLimits.vcpu.unit),
890
+ },
891
+ memory: {
892
+ lowmark: Number(
893
+ platformResponse.freemiumAdvancedLimits.memory.lowmark
894
+ ),
895
+ highmark: Number(
896
+ platformResponse.freemiumAdvancedLimits.memory.highmark
897
+ ),
898
+ unit: String(platformResponse.freemiumAdvancedLimits.memory.unit),
899
+ },
900
+ vstorage: {
901
+ lowmark: Number(
902
+ platformResponse.freemiumAdvancedLimits.vstorage.lowmark
903
+ ),
904
+ highmark: Number(
905
+ platformResponse.freemiumAdvancedLimits.vstorage.highmark
906
+ ),
907
+ unit: String(
908
+ platformResponse.freemiumAdvancedLimits.vstorage.unit
909
+ ),
910
+ },
911
+ nrstorage: {
912
+ lowmark: Number(
913
+ platformResponse.freemiumAdvancedLimits.nrstorage.lowmark
914
+ ),
915
+ highmark: Number(
916
+ platformResponse.freemiumAdvancedLimits.nrstorage.highmark
917
+ ),
918
+ unit: String(
919
+ platformResponse.freemiumAdvancedLimits.nrstorage.unit
920
+ ),
921
+ },
922
+ rstorage: {
923
+ lowmark: Number(
924
+ platformResponse.freemiumAdvancedLimits.rstorage.lowmark
925
+ ),
926
+ highmark: Number(
927
+ platformResponse.freemiumAdvancedLimits.rstorage.highmark
928
+ ),
929
+ unit: String(
930
+ platformResponse.freemiumAdvancedLimits.rstorage.unit
931
+ ),
932
+ },
933
+ storage: {
934
+ lowmark: Number(
935
+ platformResponse.freemiumAdvancedLimits.storage.lowmark
936
+ ),
937
+ highmark: Number(
938
+ platformResponse.freemiumAdvancedLimits.storage.highmark
939
+ ),
940
+ unit: String(
941
+ platformResponse.freemiumAdvancedLimits.storage.unit
942
+ ),
943
+ },
944
+ cost: {
945
+ lowmark: Number(
946
+ platformResponse.freemiumAdvancedLimits.cost.lowmark
947
+ ),
948
+ highmark: Number(
949
+ platformResponse.freemiumAdvancedLimits.cost.highmark
950
+ ),
951
+ unit: String(platformResponse.freemiumAdvancedLimits.cost.unit),
952
+ },
953
+ },
954
+ freemiumLimits: {
955
+ vcpu: {
956
+ lowmark: Number(platformResponse.freemiumLimits.vcpu.lowmark),
957
+ highmark: Number(platformResponse.freemiumLimits.vcpu.highmark),
958
+ unit: String(platformResponse.freemiumLimits.vcpu.unit),
959
+ },
960
+ memory: {
961
+ lowmark: Number(platformResponse.freemiumLimits.memory.lowmark),
962
+ highmark: Number(platformResponse.freemiumLimits.memory.highmark),
963
+ unit: String(platformResponse.freemiumLimits.memory.unit),
964
+ },
965
+ vstorage: {
966
+ lowmark: Number(platformResponse.freemiumLimits.vstorage.lowmark),
967
+ highmark: Number(
968
+ platformResponse.freemiumLimits.vstorage.highmark
969
+ ),
970
+ unit: String(platformResponse.freemiumLimits.vstorage.unit),
971
+ },
972
+ nrstorage: {
973
+ lowmark: Number(
974
+ platformResponse.freemiumLimits.nrstorage.lowmark
975
+ ),
976
+ highmark: Number(
977
+ platformResponse.freemiumLimits.nrstorage.highmark
978
+ ),
979
+ unit: String(platformResponse.freemiumLimits.nrstorage.unit),
980
+ },
981
+ rstorage: {
982
+ lowmark: Number(platformResponse.freemiumLimits.rstorage.lowmark),
983
+ highmark: Number(
984
+ platformResponse.freemiumLimits.rstorage.highmark
985
+ ),
986
+ unit: String(platformResponse.freemiumLimits.rstorage.unit),
987
+ },
988
+ storage: {
989
+ lowmark: Number(platformResponse.freemiumLimits.storage.lowmark),
990
+ highmark: Number(
991
+ platformResponse.freemiumLimits.storage.highmark
992
+ ),
993
+ unit: String(platformResponse.freemiumLimits.storage.unit),
994
+ },
995
+ cost: {
996
+ lowmark: Number(platformResponse.freemiumLimits.cost.lowmark),
997
+ highmark: Number(platformResponse.freemiumLimits.cost.highmark),
998
+ unit: String(platformResponse.freemiumLimits.cost.unit),
999
+ },
1000
+ },
1001
+ portrange: Array.isArray(platformResponse.portrange)
1002
+ ? platformResponse.portrange.map(Number)
1003
+ : [],
1004
+ };
1005
+ if (eventHelper.user) {
1006
+ eventHelper.user.publish.loaded(
1007
+ new User(userData, userTenants, userOrganizations, platformData)
1008
+ );
1009
+ } else {
1010
+ }
1011
+ } catch (error) {
1012
+ console.error("Error al obtener los datos del tenant:", error);
1013
+ throw error;
1014
+ }
1015
+ } catch (error) {
1016
+ console.error("Error al obtener los datos del usuario:", error);
1017
+ }
1018
+ } catch (err) {
1019
+ console.error(err);
1020
+ }
1021
+ };
1022
+
1023
+ const extractCloudProviderInfo = (accountSpec: any) => {
1024
+ const credentials = accountSpec.credentials;
1025
+ if (!credentials) {
1026
+ return {
1027
+ region: "",
1028
+ interface: "",
1029
+ apiVersion: "",
1030
+ authType: "",
1031
+ authUrl: "",
1032
+ credentialId: "",
1033
+ credentialSecret: "",
1034
+ };
1035
+ }
1036
+ if (credentials.openstack) {
1037
+ return {
1038
+ region: credentials.openstack.region_name ?? "",
1039
+ interface: credentials.openstack.interface ?? "",
1040
+ apiVersion: credentials.openstack.identity_api_version ?? "",
1041
+ authType: credentials.openstack.auth_type ?? "",
1042
+ authUrl: credentials.openstack.auth?.auth_url ?? "",
1043
+ credentialId:
1044
+ credentials.openstack.auth?.credential_id ??
1045
+ credentials.openstack.auth?.application_credential_id ??
1046
+ "",
1047
+ credentialSecret:
1048
+ credentials.openstack.auth?.credential_secret ??
1049
+ credentials.openstack.auth?.application_credential_secret ??
1050
+ "",
1051
+ };
1052
+ }
1053
+ return {
1054
+ region: "",
1055
+ interface: "",
1056
+ apiVersion: "",
1057
+ authType: credentials.method ?? "",
1058
+ authUrl: credentials.apiuri ?? "",
1059
+ credentialId: "",
1060
+ credentialSecret: "",
1061
+ };
1062
+ };
1063
+ export const updateUser = async (user: User) => {
1064
+ try {
1065
+ const userBody = {
1066
+ spec: {
1067
+ name: user.name,
1068
+ surname: user.surname,
1069
+ discoveryMethod: "work",
1070
+ companyName: user.companyName,
1071
+ companyRole: user.rol,
1072
+ },
1073
+ };
1074
+ const response = await makeGlobalWebSocketRequest(
1075
+ "user:modify_user_info",
1076
+ userBody,
1077
+ 30000,
1078
+ "PUT",
1079
+ "UPDATE_USER"
1080
+ );
1081
+ eventHelper.user.publish.updated(user);
1082
+ const userUpdatedNotification: Notification = {
1083
+ type: "success",
1084
+ subtype: "user-updated",
1085
+ date: Date.now().toString(),
1086
+ status: "unread",
1087
+ callToAction: false,
1088
+ data: {
1089
+ user: user.name
1090
+ }
1091
+ };
1092
+ eventHelper.notification.publish.creation(userUpdatedNotification);
1093
+ } catch (error) {
1094
+ console.error("Error updating user:", {
1095
+ error,
1096
+ errorMessage: error instanceof Error ? error.message : "Unknown error",
1097
+ stack: error instanceof Error ? error.stack : undefined,
1098
+ });
1099
+ eventHelper.user.publish.updateError(user);
1100
+ const userUpdatedNotificationError: Notification = {
1101
+ type: "error",
1102
+ subtype: "tenant-update-error",
1103
+ date: Date.now().toString(),
1104
+ status: "unread",
1105
+ callToAction: false,
1106
+ info_content:{
1107
+ code: (error as any).error.code,
1108
+ message: (error as any).error.content
1109
+ },
1110
+ data: {
1111
+ user: user.name
1112
+ }
1113
+ };
1114
+ eventHelper.notification.publish.creation(userUpdatedNotificationError);
1115
+ throw error;
1116
+ }
1117
+ };
1118
+ export const deleteUSer = async (user: User, security: string) => {
1119
+ try {
1120
+ await initializeGlobalWebSocketClient(security);
1121
+ const response = await makeGlobalWebSocketRequest(
1122
+ "user:delete_user",
1123
+ {},
1124
+ 30000,
1125
+ "DELETE",
1126
+ "DELETE_USER",
1127
+ "user"
1128
+ );
1129
+
1130
+ const userDeletedNotification: Notification = {
1131
+ type: "success",
1132
+ subtype: "user-deleted",
1133
+ date: Date.now().toString(),
1134
+ status: "unread",
1135
+ callToAction: false,
1136
+ data: {
1137
+ user: user.name
1138
+ }
1139
+ };
1140
+ eventHelper.notification.publish.creation(userDeletedNotification);
1141
+ return response;
1142
+ } catch (error) {
1143
+ console.error("Error deleting user:", error);
1144
+ const userDeletedNotificationError: Notification = {
1145
+ type: "error",
1146
+ subtype: "user-delete-error",
1147
+ date: Date.now().toString(),
1148
+ status: "unread",
1149
+ callToAction: false,
1150
+ info_content:{
1151
+ code: (error as any).error.code,
1152
+ message: (error as any).error.content
1153
+ },
1154
+ data: {
1155
+ user: user.name
1156
+ }
1157
+ };
1158
+ eventHelper.notification.publish.creation(userDeletedNotificationError);
1159
+ throw error;
1160
+ }
1161
+ };