cyberia 3.0.1 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.github/workflows/engine-cyberia.cd.yml +1 -0
  2. package/CHANGELOG.md +56 -1
  3. package/CLI-HELP.md +2 -4
  4. package/README.md +139 -0
  5. package/bin/build.js +5 -0
  6. package/bin/cyberia.js +385 -71
  7. package/bin/deploy.js +18 -26
  8. package/bin/file.js +3 -0
  9. package/bin/index.js +385 -71
  10. package/conf.js +32 -3
  11. package/deployment.yaml +2 -2
  12. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  13. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  14. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  15. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  16. package/manifests/ipfs/configmap.yaml +7 -0
  17. package/package.json +8 -8
  18. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +2 -0
  19. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +7 -0
  20. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +93 -2
  21. package/src/api/file/file.controller.js +3 -13
  22. package/src/api/file/file.ref.json +0 -21
  23. package/src/api/ipfs/ipfs.controller.js +104 -0
  24. package/src/api/ipfs/ipfs.model.js +71 -0
  25. package/src/api/ipfs/ipfs.router.js +31 -0
  26. package/src/api/ipfs/ipfs.service.js +193 -0
  27. package/src/api/object-layer/README.md +139 -0
  28. package/src/api/object-layer/object-layer.controller.js +3 -0
  29. package/src/api/object-layer/object-layer.model.js +15 -1
  30. package/src/api/object-layer/object-layer.router.js +6 -10
  31. package/src/api/object-layer/object-layer.service.js +311 -182
  32. package/src/cli/cluster.js +30 -38
  33. package/src/cli/index.js +0 -1
  34. package/src/cli/run.js +14 -0
  35. package/src/client/components/core/LoadingAnimation.js +2 -3
  36. package/src/client/components/core/Modal.js +1 -1
  37. package/src/client/components/cyberia/ObjectLayerEngineModal.js +4 -5
  38. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +280 -29
  39. package/src/client/services/ipfs/ipfs.service.js +144 -0
  40. package/src/client/services/object-layer/object-layer.management.js +161 -8
  41. package/src/index.js +1 -1
  42. package/src/runtime/express/Express.js +1 -1
  43. package/src/server/auth.js +18 -18
  44. package/src/server/ipfs-client.js +433 -0
  45. package/src/server/object-layer.js +649 -18
  46. package/src/server/semantic-layer-generator.js +1083 -0
  47. package/src/server/shape-generator.js +952 -0
  48. package/test/shape-generator.test.js +457 -0
  49. package/bin/ssl.js +0 -63
@@ -10,15 +10,16 @@ import {
10
10
  import { ObjectLayerService } from '../../services/object-layer/object-layer.service.js';
11
11
  import { AtlasSpriteSheetService } from '../../services/atlas-sprite-sheet/atlas-sprite-sheet.service.js';
12
12
  import { NotificationManager } from '../core/NotificationManager.js';
13
- import { htmls, s } from '../core/VanillaJs.js';
13
+ import { append, htmls, s } from '../core/VanillaJs.js';
14
14
 
15
- import { darkTheme, ThemeEvents } from '../core/Css.js';
15
+ import { darkTheme, ThemeEvents, subThemeManager, lightenHex, darkenHex } from '../core/Css.js';
16
16
  import { ObjectLayerManagement } from '../../services/object-layer/object-layer.management.js';
17
17
  import { ObjectLayerEngineModal } from './ObjectLayerEngineModal.js';
18
18
  import { Modal } from '../core/Modal.js';
19
19
  import { DefaultManagement } from '../../services/default/default.management.js';
20
20
  import { AgGrid } from '../core/AgGrid.js';
21
21
  import { EventsUI } from '../core/EventsUI.js';
22
+ import { createJSONEditor } from 'vanilla-jsoneditor';
22
23
 
23
24
  const logger = loggerFactory(import.meta);
24
25
 
@@ -30,10 +31,11 @@ const ObjectLayerEngineViewer = {
30
31
  currentMode: 'idle',
31
32
  webp: null,
32
33
  isGenerating: false,
33
- currentCid: undefined, // Track current loaded cid to prevent unnecessary reloads
34
+ currentObjectId: undefined, // Track current loaded object layer id to prevent unnecessary reloads
34
35
  atlasSpriteSheet: null,
35
36
  isGeneratingAtlas: false,
36
37
  webpMetadata: null,
38
+ metadataJsonEditor: null,
37
39
  },
38
40
 
39
41
  // Map user-friendly direction/mode to numeric direction codes
@@ -55,8 +57,8 @@ const ObjectLayerEngineViewer = {
55
57
  Render: async function ({ Elements }) {
56
58
  const id = 'object-layer-engine-viewer';
57
59
 
58
- // Reset currentCid when modal is rendered to ensure Reload triggers properly
59
- this.Data.currentCid = undefined;
60
+ // Reset currentObjectId when modal is rendered to ensure Reload triggers properly
61
+ this.Data.currentObjectId = undefined;
60
62
 
61
63
  Modal.Data[`modal-${id}`].onReloadModalListener[id] = async () => {
62
64
  ObjectLayerEngineViewer.Reload({ Elements });
@@ -66,15 +68,15 @@ const ObjectLayerEngineViewer = {
66
68
  listenQueryParamsChange({
67
69
  id: `${id}-query-listener`,
68
70
  event: async (queryParams) => {
69
- const cid = queryParams.cid || null;
71
+ const objectId = queryParams.id || null;
70
72
 
71
73
  if (!s(`.modal-${id}`) || !s(`#${id}`)) {
72
74
  logger.warn('ObjectLayerEngineViewer DOM not ready for query param change');
73
75
  return;
74
76
  }
75
77
 
76
- // Only reload if cid actually changed (normalize undefined to null for comparison)
77
- if (cid !== this.Data.currentCid) {
78
+ // Only reload if object id actually changed (normalize undefined to null for comparison)
79
+ if (objectId !== this.Data.currentObjectId) {
78
80
  await this.Reload({ Elements });
79
81
  }
80
82
  },
@@ -101,8 +103,8 @@ const ObjectLayerEngineViewer = {
101
103
  return;
102
104
  }
103
105
 
104
- // Clear current cid when rendering empty state
105
- this.Data.currentCid = null;
106
+ // Clear current object id when rendering empty state
107
+ this.Data.currentObjectId = null;
106
108
 
107
109
  // Check if the management table grid already exists AND its DOM is still present
108
110
  // If it does, don't re-render (just let DefaultManagement's RouterEvents handle URL changes)
@@ -569,6 +571,50 @@ const ObjectLayerEngineViewer = {
569
571
  color: ${darkTheme ? '#666' : '#999'};
570
572
  padding: 20px;
571
573
  }
574
+ .ipfs-cid-label {
575
+ font-size: 14px;
576
+ color: ${darkTheme ? '#b0b8c8' : '#555'};
577
+ word-break: break-all;
578
+ padding: 10px 12px;
579
+ border: 1px solid
580
+ ${(() => {
581
+ const tc = darkTheme ? subThemeManager.darkColor : subThemeManager.lightColor;
582
+ return tc ? (darkTheme ? darkenHex(tc, 0.7) : lightenHex(tc, 0.7)) : darkTheme ? '#3a3f4b' : '#d0d5dd';
583
+ })()};
584
+ border-radius: 6px;
585
+ background: ${(() => {
586
+ const tc = darkTheme ? subThemeManager.darkColor : subThemeManager.lightColor;
587
+ return tc ? (darkTheme ? darkenHex(tc, 0.85) : lightenHex(tc, 0.85)) : darkTheme ? '#1a1f2e' : '#f4f6f9';
588
+ })()};
589
+ margin-top: 8px;
590
+ display: flex;
591
+ align-items: baseline;
592
+ gap: 6px;
593
+ line-height: 1.5;
594
+ }
595
+ .ipfs-cid-label i {
596
+ color: ${(() => {
597
+ const tc = darkTheme ? subThemeManager.darkColor : subThemeManager.lightColor;
598
+ return tc ? (darkTheme ? lightenHex(tc, 0.5) : darkenHex(tc, 0.3)) : darkTheme ? '#4a9eff' : '#2196F3';
599
+ })()};
600
+ font-size: 14px;
601
+ flex-shrink: 0;
602
+ }
603
+ .ipfs-cid-label strong {
604
+ color: ${darkTheme ? '#cdd4e0' : '#333'};
605
+ white-space: nowrap;
606
+ font-size: 14px;
607
+ }
608
+ .ipfs-cid-label .ipfs-cid-value {
609
+ user-select: all;
610
+ cursor: text;
611
+ color: ${(() => {
612
+ const tc = darkTheme ? subThemeManager.darkColor : subThemeManager.lightColor;
613
+ return tc ? (darkTheme ? lightenHex(tc, 0.6) : darkenHex(tc, 0.3)) : darkTheme ? '#8ecfff' : '#1565c0';
614
+ })()};
615
+ font-family: monospace;
616
+ font-size: 13px;
617
+ }
572
618
 
573
619
  @media (max-width: 850px) {
574
620
  .object-layer-viewer-container {
@@ -619,6 +665,38 @@ const ObjectLayerEngineViewer = {
619
665
  <span style="font-weight: 600;">${itemActivable ? 'Yes' : 'No'}</span>
620
666
  </div>
621
667
  </div>
668
+ ${objectLayer.cid
669
+ ? html`<div class="ipfs-cid-label">
670
+ <i class="fa-solid fa-cube"></i>
671
+ <strong>IPFS CID:</strong>
672
+ <span class="ipfs-cid-value">${objectLayer.cid}</span>
673
+ </div>`
674
+ : ''}
675
+ ${objectLayer.data.atlasSpriteSheetCid
676
+ ? html`<div class="ipfs-cid-label">
677
+ <i class="fa-solid fa-image"></i>
678
+ <strong>Atlas IPFS CID:</strong>
679
+ <span class="ipfs-cid-value">${objectLayer.data.atlasSpriteSheetCid}</span>
680
+ </div>`
681
+ : ''}
682
+ ${objectLayer.sha256
683
+ ? html`<div class="ipfs-cid-label">
684
+ <i class="fa-solid fa-fingerprint"></i>
685
+ <strong>SHA-256:</strong>
686
+ <span class="ipfs-cid-value">${objectLayer.sha256}</span>
687
+ </div>`
688
+ : ''}
689
+ </div>
690
+
691
+ <!-- Metadata JSON Section -->
692
+ <div class="control-group" style="margin-bottom: 20px;">
693
+ <h4><i class="fa-solid fa-code"></i> Metadata JSON</h4>
694
+ <div
695
+ id="metadata-json-editor-container"
696
+ style="height: 400px; border-radius: 6px; overflow: hidden; border: 1px solid ${darkTheme
697
+ ? '#444'
698
+ : '#ddd'};"
699
+ ></div>
622
700
  </div>
623
701
 
624
702
  <!-- Stats Data Section -->
@@ -777,6 +855,16 @@ const ObjectLayerEngineViewer = {
777
855
  <p style="padding: 2px"><strong class="item-data-key-label">ID:</strong></p>
778
856
  <p style="padding: 2px" font-size: 12px;">${this.Data.atlasSpriteSheet._id}</p>
779
857
  </div>
858
+ ${
859
+ this.Data.atlasSpriteSheet.cid
860
+ ? html`<div style="grid-column: 1 / -1;">
861
+ <p style="padding: 2px"><strong class="item-data-key-label">IPFS CID:</strong></p>
862
+ <p class="ipfs-cid-value" style="padding: 2px;">
863
+ ${this.Data.atlasSpriteSheet.cid}
864
+ </p>
865
+ </div>`
866
+ : ''
867
+ }
780
868
  <div>
781
869
  <p style="padding: 2px"><strong class="item-data-key-label">Dimensions:</strong></p>
782
870
  <p style="padding: 2px">
@@ -837,6 +925,10 @@ const ObjectLayerEngineViewer = {
837
925
  <i class="fa-solid fa-edit"></i>
838
926
  <span>Edit</span>
839
927
  </button>
928
+ <button class="default-viewer-btn" id="delete-object-layer-btn" style="background: #dc3545;">
929
+ <i class="fa-solid fa-trash"></i>
930
+ <span>Delete</span>
931
+ </button>
840
932
  </div>
841
933
  `}
842
934
  </div>
@@ -850,6 +942,9 @@ const ObjectLayerEngineViewer = {
850
942
  if (this.Data.webp) {
851
943
  this.displayWebp();
852
944
  }
945
+
946
+ // Initialize metadata JSON editor
947
+ this.initMetadataJsonEditor();
853
948
  },
854
949
 
855
950
  displayWebp: async function () {
@@ -894,6 +989,128 @@ const ObjectLayerEngineViewer = {
894
989
  }
895
990
  },
896
991
 
992
+ initMetadataJsonEditor: async function () {
993
+ const container = s('#metadata-json-editor-container');
994
+ if (!container) return;
995
+
996
+ // Ensure vanilla-jsoneditor dark theme CSS is loaded
997
+ if (!s('.jse-dark-theme-link')) {
998
+ append(
999
+ 'head',
1000
+ html`<link
1001
+ class="jse-dark-theme-link"
1002
+ rel="stylesheet"
1003
+ type="text/css"
1004
+ href="${getProxyPath()}styles/vanilla-jsoneditor/jse-theme-dark.css"
1005
+ />`,
1006
+ );
1007
+ }
1008
+
1009
+ // Destroy previous instance if any
1010
+ if (this.Data.metadataJsonEditor) {
1011
+ this.Data.metadataJsonEditor.destroy();
1012
+ this.Data.metadataJsonEditor = null;
1013
+ }
1014
+
1015
+ const objectLayerId = this.Data.objectLayer?._id;
1016
+ if (!objectLayerId) return;
1017
+
1018
+ try {
1019
+ const response = await ObjectLayerService.getMetadata({ id: objectLayerId });
1020
+ const metadataContent = response.status === 'success' && response.data ? response.data : response;
1021
+
1022
+ this.Data.metadataJsonEditor = createJSONEditor({
1023
+ target: container,
1024
+ props: {
1025
+ content: { json: metadataContent },
1026
+ readOnly: true,
1027
+ mainMenuBar: true,
1028
+ navigationBar: true,
1029
+ statusBar: true,
1030
+ mode: 'tree',
1031
+ },
1032
+ });
1033
+
1034
+ // Apply dark theme class based on current theme
1035
+ this._applyJsonEditorTheme();
1036
+
1037
+ // Register theme event to toggle dark/light on the JSON editor
1038
+ ThemeEvents['metadata-json-editor-theme'] = () => {
1039
+ this._applyJsonEditorTheme();
1040
+ };
1041
+ } catch (err) {
1042
+ logger.warn('Failed to initialize metadata JSON editor:', err);
1043
+ container.innerHTML = html`<div style="padding: 20px; color: #999; text-align: center;">
1044
+ Failed to load metadata JSON
1045
+ </div>`;
1046
+ }
1047
+ },
1048
+
1049
+ _applyJsonEditorTheme: function () {
1050
+ const container = s('#metadata-json-editor-container');
1051
+ if (!container) return;
1052
+ if (darkTheme) {
1053
+ container.classList.add('jse-theme-dark');
1054
+ } else {
1055
+ container.classList.remove('jse-theme-dark');
1056
+ }
1057
+ },
1058
+
1059
+ deleteObjectLayer: async function ({ Elements } = {}) {
1060
+ const objectLayerId = this.Data.objectLayer?._id;
1061
+ if (!objectLayerId) return;
1062
+
1063
+ const itemId = this.Data.objectLayer?.data?.item?.id || objectLayerId;
1064
+
1065
+ const confirmResult = await Modal.RenderConfirm({
1066
+ id: 'delete-object-layer-confirm',
1067
+ html: async () => html`
1068
+ <div class="in section-mp" style="text-align: center">
1069
+ <p>Are you sure you want to permanently delete object layer <strong>"${itemId}"</strong>?</p>
1070
+ <p style="color: #dc3545; font-size: 13px; margin-top: 8px;">
1071
+ This will remove all associated data including render frames, atlas sprite sheet, IPFS pins, and static
1072
+ asset files.
1073
+ </p>
1074
+ </div>
1075
+ `,
1076
+ });
1077
+
1078
+ if (confirmResult.status !== 'confirm') return;
1079
+
1080
+ try {
1081
+ const result = await ObjectLayerService.delete({ id: objectLayerId });
1082
+ if (result.status === 'success') {
1083
+ NotificationManager.Push({
1084
+ html: `Object layer "${itemId}" deleted successfully`,
1085
+ status: 'success',
1086
+ });
1087
+
1088
+ // Clean up JSON editor and its theme event
1089
+ if (this.Data.metadataJsonEditor) {
1090
+ this.Data.metadataJsonEditor.destroy();
1091
+ this.Data.metadataJsonEditor = null;
1092
+ }
1093
+ delete ThemeEvents['metadata-json-editor-theme'];
1094
+
1095
+ // Navigate back to list
1096
+ this.Data.currentObjectId = undefined;
1097
+ this.Data.objectLayer = null;
1098
+ this.Data.webp = null;
1099
+ this.Data.webpMetadata = null;
1100
+ this.Data.atlasSpriteSheet = null;
1101
+ setQueryParams({ id: null }, { replace: false });
1102
+ } else {
1103
+ throw new Error(result.message || 'Failed to delete object layer');
1104
+ }
1105
+ } catch (error) {
1106
+ logger.error('Error deleting object layer:', error);
1107
+ NotificationManager.Push({
1108
+ html: `Failed to delete object layer: ${error.message}`,
1109
+ status: 'error',
1110
+ });
1111
+ }
1112
+ },
1113
+
897
1114
  attachEventListeners: function ({ Elements }) {
898
1115
  // Direction buttons
899
1116
  const directionButtons = document.querySelectorAll('[data-direction]');
@@ -904,7 +1121,7 @@ const ObjectLayerEngineViewer = {
904
1121
  if (direction !== this.Data.currentDirection) {
905
1122
  this.Data.currentDirection = direction;
906
1123
  await this.renderViewer({ Elements });
907
- await this.attachEventListeners({ Elements });
1124
+ // attachEventListeners is already called inside renderViewer
908
1125
  await this.generateWebp();
909
1126
  }
910
1127
  });
@@ -919,7 +1136,7 @@ const ObjectLayerEngineViewer = {
919
1136
  if (mode !== this.Data.currentMode) {
920
1137
  this.Data.currentMode = mode;
921
1138
  await this.renderViewer({ Elements });
922
- await this.attachEventListeners({ Elements });
1139
+ // attachEventListeners is already called inside renderViewer
923
1140
  await this.generateWebp();
924
1141
  }
925
1142
  });
@@ -936,10 +1153,26 @@ const ObjectLayerEngineViewer = {
936
1153
  // Return to list button
937
1154
  const listBtn = s('#return-to-list-btn');
938
1155
  if (listBtn) {
939
- listBtn.addEventListener('click', () => {
940
- // Clear the cid parameter to return to list view
941
- setQueryParams({ cid: null }, { replace: false });
942
- // The listener will detect the change and call renderEmpty()
1156
+ listBtn.addEventListener('click', async () => {
1157
+ // Clear object data and reset state
1158
+ this.Data.webp = null;
1159
+ this.Data.webpMetadata = null;
1160
+ this.Data.objectLayer = null;
1161
+ this.Data.frameCounts = null;
1162
+
1163
+ // Set currentObjectId to null BEFORE setQueryParams so the
1164
+ // listenQueryParamsChange listener sees the id already matches
1165
+ // and skips calling Reload (avoids double-render race condition)
1166
+ this.Data.currentObjectId = null;
1167
+
1168
+ // Update the URL to remove the id parameter
1169
+ setQueryParams({ id: null }, { replace: false });
1170
+
1171
+ // Directly render the list view instead of relying on the
1172
+ // listener → Reload → renderEmpty chain which can silently
1173
+ // fail when the URL was already clean or currentObjectId
1174
+ // was already null
1175
+ await this.renderEmpty({ Elements });
943
1176
  });
944
1177
  }
945
1178
 
@@ -951,6 +1184,14 @@ const ObjectLayerEngineViewer = {
951
1184
  });
952
1185
  }
953
1186
 
1187
+ // Delete button
1188
+ const deleteBtn = s('#delete-object-layer-btn');
1189
+ if (deleteBtn) {
1190
+ deleteBtn.addEventListener('click', async () => {
1191
+ await this.deleteObjectLayer({ Elements });
1192
+ });
1193
+ }
1194
+
954
1195
  // Atlas buttons
955
1196
  if (s('#generate-atlas-btn')) {
956
1197
  EventsUI.onClick('#generate-atlas-btn', async () => {
@@ -1020,7 +1261,10 @@ const ObjectLayerEngineViewer = {
1020
1261
  html: 'Atlas sprite sheet generated successfully',
1021
1262
  status: 'success',
1022
1263
  });
1264
+ // Reset generating flag before reload so renderViewer shows updated content
1265
+ this.Data.isGeneratingAtlas = false;
1023
1266
  await this.Reload({ Elements, force: true, skipWebp: true });
1267
+ return;
1024
1268
  } else {
1025
1269
  throw new Error(message || 'Failed to generate atlas');
1026
1270
  }
@@ -1031,8 +1275,10 @@ const ObjectLayerEngineViewer = {
1031
1275
  status: 'error',
1032
1276
  });
1033
1277
  } finally {
1034
- this.Data.isGeneratingAtlas = false;
1035
- await this.renderViewer({ Elements });
1278
+ if (this.Data.isGeneratingAtlas) {
1279
+ this.Data.isGeneratingAtlas = false;
1280
+ await this.renderViewer({ Elements });
1281
+ }
1036
1282
  }
1037
1283
  },
1038
1284
 
@@ -1062,7 +1308,10 @@ const ObjectLayerEngineViewer = {
1062
1308
  html: 'Atlas sprite sheet removed successfully',
1063
1309
  status: 'success',
1064
1310
  });
1311
+ // Reset generating flag before reload so renderViewer shows updated content
1312
+ this.Data.isGeneratingAtlas = false;
1065
1313
  await this.Reload({ Elements, force: true, skipWebp: true });
1314
+ return;
1066
1315
  } else {
1067
1316
  throw new Error(message || 'Failed to remove atlas');
1068
1317
  }
@@ -1073,8 +1322,10 @@ const ObjectLayerEngineViewer = {
1073
1322
  status: 'error',
1074
1323
  });
1075
1324
  } finally {
1076
- this.Data.isGeneratingAtlas = false;
1077
- await this.renderViewer({ Elements });
1325
+ if (this.Data.isGeneratingAtlas) {
1326
+ this.Data.isGeneratingAtlas = false;
1327
+ await this.renderViewer({ Elements });
1328
+ }
1078
1329
  }
1079
1330
  },
1080
1331
 
@@ -1209,7 +1460,7 @@ const ObjectLayerEngineViewer = {
1209
1460
  // Navigate to editor route first
1210
1461
  setPath(`${getProxyPath()}object-layer-engine`);
1211
1462
  // Then add query param without replacing history
1212
- setQueryParams({ cid: objectLayer._id }, { replace: true });
1463
+ setQueryParams({ id: objectLayer._id }, { replace: true });
1213
1464
 
1214
1465
  if (s(`.modal-object-layer-engine`)) {
1215
1466
  ObjectLayerEngineModal.Reload();
@@ -1221,22 +1472,22 @@ const ObjectLayerEngineViewer = {
1221
1472
  Reload: async function (options = {}) {
1222
1473
  const { Elements, force = false, skipWebp = false } = options;
1223
1474
  const queryParams = getQueryParams();
1224
- const cid = queryParams.cid || null;
1475
+ const objectId = queryParams.id || null;
1225
1476
 
1226
- // Only reload if cid actually changed (same logic as listener) or forced
1227
- if (cid !== this.Data.currentCid || force) {
1228
- if (cid !== this.Data.currentCid && !skipWebp) {
1477
+ // Only reload if object id actually changed (same logic as listener) or forced
1478
+ if (objectId !== this.Data.currentObjectId || force) {
1479
+ if (objectId !== this.Data.currentObjectId && !skipWebp) {
1229
1480
  this.Data.webp = null;
1230
1481
  this.Data.webpMetadata = null;
1231
1482
  }
1232
- this.Data.currentCid = cid;
1483
+ this.Data.currentObjectId = objectId;
1233
1484
 
1234
- if (cid) {
1235
- await this.loadObjectLayer(cid, Elements, { skipWebp });
1485
+ if (objectId) {
1486
+ await this.loadObjectLayer(objectId, Elements, { skipWebp });
1236
1487
  } else {
1237
1488
  await this.renderEmpty({ Elements });
1238
1489
  }
1239
- } else if (!cid && (this.Data.currentCid === null || force)) {
1490
+ } else if (!objectId && (this.Data.currentObjectId === null || force)) {
1240
1491
  // Special case: if we're already in empty state but DOM might have been reset
1241
1492
  // (e.g., modal reopened), force render the table if DOM is missing
1242
1493
  const id = 'object-layer-engine-viewer';
@@ -0,0 +1,144 @@
1
+ import { Auth } from '../../components/core/Auth.js';
2
+ import { loggerFactory } from '../../components/core/Logger.js';
3
+ import { getApiBaseUrl, headersFactory, payloadFactory, buildQueryUrl } from '../core/core.service.js';
4
+
5
+ const logger = loggerFactory(import.meta);
6
+
7
+ logger.info('Load service');
8
+
9
+ const endpoint = 'ipfs';
10
+
11
+ const IpfsService = {
12
+ post: (options = { id: '', body: {} }) =>
13
+ new Promise((resolve, reject) =>
14
+ fetch(getApiBaseUrl({ id: options.id, endpoint }), {
15
+ method: 'POST',
16
+ headers: headersFactory(),
17
+ credentials: 'include',
18
+ body: payloadFactory(options.body),
19
+ })
20
+ .then(async (res) => {
21
+ return await res.json();
22
+ })
23
+ .then((res) => {
24
+ logger.info(res);
25
+ return resolve(res);
26
+ })
27
+ .catch((error) => {
28
+ logger.error(error);
29
+ return reject(error);
30
+ }),
31
+ ),
32
+ put: (options = { id: '', body: {} }) =>
33
+ new Promise((resolve, reject) =>
34
+ fetch(getApiBaseUrl({ id: options.id, endpoint }), {
35
+ method: 'PUT',
36
+ headers: headersFactory(),
37
+ credentials: 'include',
38
+ body: payloadFactory(options.body),
39
+ })
40
+ .then(async (res) => {
41
+ return await res.json();
42
+ })
43
+ .then((res) => {
44
+ logger.info(res);
45
+ return resolve(res);
46
+ })
47
+ .catch((error) => {
48
+ logger.error(error);
49
+ return reject(error);
50
+ }),
51
+ ),
52
+ get: (options = {}) => {
53
+ const { id, page, limit, filterModel, sortModel, sort, asc, order } = options;
54
+ const url = buildQueryUrl(getApiBaseUrl({ id, endpoint }), {
55
+ page,
56
+ limit,
57
+ filterModel,
58
+ sortModel,
59
+ sort,
60
+ asc,
61
+ order,
62
+ });
63
+
64
+ return new Promise((resolve, reject) =>
65
+ fetch(url.toString(), {
66
+ method: 'GET',
67
+ headers: headersFactory(),
68
+ credentials: 'include',
69
+ })
70
+ .then(async (res) => {
71
+ return await res.json();
72
+ })
73
+ .then((res) => {
74
+ logger.info(res);
75
+ return resolve(res);
76
+ })
77
+ .catch((error) => {
78
+ logger.error(error);
79
+ return reject(error);
80
+ }),
81
+ );
82
+ },
83
+ delete: (options = { id: '', body: {} }) =>
84
+ new Promise((resolve, reject) =>
85
+ fetch(getApiBaseUrl({ id: options.id, endpoint }), {
86
+ method: 'DELETE',
87
+ headers: headersFactory(),
88
+ credentials: 'include',
89
+ body: payloadFactory(options.body),
90
+ })
91
+ .then(async (res) => {
92
+ return await res.json();
93
+ })
94
+ .then((res) => {
95
+ logger.info(res);
96
+ return resolve(res);
97
+ })
98
+ .catch((error) => {
99
+ logger.error(error);
100
+ return reject(error);
101
+ }),
102
+ ),
103
+ pin: (options = { body: {} }) =>
104
+ new Promise((resolve, reject) =>
105
+ fetch(`${getApiBaseUrl({ endpoint })}/pin`, {
106
+ method: 'POST',
107
+ headers: headersFactory(),
108
+ credentials: 'include',
109
+ body: payloadFactory(options.body),
110
+ })
111
+ .then(async (res) => {
112
+ return await res.json();
113
+ })
114
+ .then((res) => {
115
+ logger.info(res);
116
+ return resolve(res);
117
+ })
118
+ .catch((error) => {
119
+ logger.error(error);
120
+ return reject(error);
121
+ }),
122
+ ),
123
+ unpin: (options = { cid: '' }) =>
124
+ new Promise((resolve, reject) =>
125
+ fetch(`${getApiBaseUrl({ endpoint })}/pin/${options.cid}`, {
126
+ method: 'DELETE',
127
+ headers: headersFactory(),
128
+ credentials: 'include',
129
+ })
130
+ .then(async (res) => {
131
+ return await res.json();
132
+ })
133
+ .then((res) => {
134
+ logger.info(res);
135
+ return resolve(res);
136
+ })
137
+ .catch((error) => {
138
+ logger.error(error);
139
+ return reject(error);
140
+ }),
141
+ ),
142
+ };
143
+
144
+ export { IpfsService };