cyberia 3.0.3 → 3.2.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.
Files changed (296) hide show
  1. package/{.env.production → .env.example} +20 -4
  2. package/.github/workflows/engine-cyberia.cd.yml +43 -10
  3. package/.github/workflows/engine-cyberia.ci.yml +48 -26
  4. package/.github/workflows/ghpkg.ci.yml +5 -5
  5. package/.github/workflows/gitlab.ci.yml +1 -1
  6. package/.github/workflows/hardhat.ci.yml +82 -0
  7. package/.github/workflows/npmpkg.ci.yml +60 -14
  8. package/.github/workflows/publish.ci.yml +26 -7
  9. package/.github/workflows/publish.cyberia.ci.yml +5 -5
  10. package/.github/workflows/pwa-microservices-template-page.cd.yml +6 -7
  11. package/.github/workflows/pwa-microservices-template-test.ci.yml +4 -4
  12. package/.github/workflows/release.cd.yml +14 -8
  13. package/.vscode/extensions.json +9 -8
  14. package/.vscode/settings.json +3 -2
  15. package/CHANGELOG.md +643 -1
  16. package/CLI-HELP.md +132 -57
  17. package/Dockerfile +4 -2
  18. package/README.md +347 -22
  19. package/WHITE-PAPER.md +1540 -0
  20. package/bin/build.js +21 -12
  21. package/bin/cyberia.js +2640 -106
  22. package/bin/deploy.js +258 -372
  23. package/bin/file.js +5 -1
  24. package/bin/index.js +2640 -106
  25. package/bin/vs.js +3 -3
  26. package/conf.js +169 -105
  27. package/deployment.yaml +236 -20
  28. package/hardhat/.env.example +31 -0
  29. package/hardhat/README.md +531 -0
  30. package/hardhat/WHITE-PAPER.md +1540 -0
  31. package/hardhat/contracts/ObjectLayerToken.sol +391 -0
  32. package/hardhat/deployments/.gitkeep +0 -0
  33. package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
  34. package/hardhat/hardhat.config.js +136 -0
  35. package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
  36. package/hardhat/networks/besu-object-layer.network.json +138 -0
  37. package/hardhat/package-lock.json +4323 -0
  38. package/hardhat/package.json +36 -0
  39. package/hardhat/scripts/deployObjectLayerToken.js +98 -0
  40. package/hardhat/test/ObjectLayerToken.js +592 -0
  41. package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
  42. package/hardhat/types/ethers-contracts/common.ts +92 -0
  43. package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
  44. package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
  45. package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
  46. package/hardhat/types/ethers-contracts/index.ts +6 -0
  47. package/jsdoc.dd-cyberia.json +68 -0
  48. package/jsdoc.json +65 -49
  49. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -4
  50. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +5 -4
  51. package/manifests/deployment/dd-cyberia-development/deployment.yaml +562 -0
  52. package/manifests/deployment/dd-cyberia-development/proxy.yaml +297 -0
  53. package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +132 -0
  54. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  55. package/manifests/deployment/dd-test-development/deployment.yaml +88 -74
  56. package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
  57. package/manifests/deployment/playwright/deployment.yaml +1 -1
  58. package/manifests/pv-pvc-dd.yaml +1 -1
  59. package/nodemon.json +1 -1
  60. package/package.json +60 -48
  61. package/proxy.yaml +118 -10
  62. package/pv-pvc.yaml +132 -0
  63. package/scripts/k3s-node-setup.sh +1 -1
  64. package/scripts/ports-ls.sh +2 -0
  65. package/scripts/rhel-grpc-setup.sh +56 -0
  66. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +47 -1
  67. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +17 -2
  68. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  69. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +80 -7
  70. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
  71. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
  72. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
  73. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
  74. package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
  75. package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
  76. package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
  77. package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
  78. package/src/api/cyberia-instance/cyberia-fallback-world.js +368 -0
  79. package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
  80. package/src/api/cyberia-instance/cyberia-instance.model.js +84 -0
  81. package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
  82. package/src/api/cyberia-instance/cyberia-instance.service.js +191 -0
  83. package/src/api/cyberia-instance/cyberia-portal-connector.js +486 -0
  84. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
  85. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +413 -0
  86. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +228 -0
  87. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
  88. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +42 -0
  89. package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
  90. package/src/api/cyberia-map/cyberia-map.model.js +30 -0
  91. package/src/api/cyberia-map/cyberia-map.router.js +40 -0
  92. package/src/api/cyberia-map/cyberia-map.service.js +74 -0
  93. package/src/api/document/document.service.js +1 -1
  94. package/src/api/file/file.controller.js +3 -1
  95. package/src/api/file/file.ref.json +18 -0
  96. package/src/api/file/file.service.js +28 -5
  97. package/src/api/ipfs/ipfs.controller.js +4 -25
  98. package/src/api/ipfs/ipfs.model.js +43 -34
  99. package/src/api/ipfs/ipfs.router.js +8 -13
  100. package/src/api/ipfs/ipfs.service.js +56 -104
  101. package/src/api/object-layer/README.md +347 -22
  102. package/src/api/object-layer/object-layer.controller.js +6 -2
  103. package/src/api/object-layer/object-layer.model.js +12 -8
  104. package/src/api/object-layer/object-layer.router.js +698 -42
  105. package/src/api/object-layer/object-layer.service.js +119 -37
  106. package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +1 -2
  107. package/src/api/user/user.router.js +10 -5
  108. package/src/api/user/user.service.js +15 -14
  109. package/src/cli/baremetal.js +6 -10
  110. package/src/cli/cloud-init.js +0 -3
  111. package/src/cli/cluster.js +7 -7
  112. package/src/cli/db.js +723 -857
  113. package/src/cli/deploy.js +215 -105
  114. package/src/cli/env.js +34 -5
  115. package/src/cli/fs.js +5 -4
  116. package/src/cli/image.js +0 -3
  117. package/src/cli/index.js +83 -15
  118. package/src/cli/kubectl.js +211 -0
  119. package/src/cli/monitor.js +5 -6
  120. package/src/cli/release.js +284 -0
  121. package/src/cli/repository.js +708 -62
  122. package/src/cli/run.js +371 -151
  123. package/src/cli/secrets.js +73 -2
  124. package/src/cli/ssh.js +1 -1
  125. package/src/cli/test.js +3 -3
  126. package/src/client/Cryptokoyn.index.js +3 -4
  127. package/src/client/CyberiaPortal.index.js +3 -4
  128. package/src/client/Default.index.js +3 -4
  129. package/src/client/Itemledger.index.js +4 -963
  130. package/src/client/Underpost.index.js +3 -4
  131. package/src/client/components/core/AgGrid.js +20 -5
  132. package/src/client/components/core/Alert.js +2 -2
  133. package/src/client/components/core/AppStore.js +69 -0
  134. package/src/client/components/core/CalendarCore.js +2 -2
  135. package/src/client/components/core/Content.js +22 -3
  136. package/src/client/components/core/Docs.js +30 -6
  137. package/src/client/components/core/DropDown.js +137 -17
  138. package/src/client/components/core/FileExplorer.js +71 -4
  139. package/src/client/components/core/Input.js +1 -1
  140. package/src/client/components/core/Keyboard.js +2 -2
  141. package/src/client/components/core/LogIn.js +2 -2
  142. package/src/client/components/core/LogOut.js +2 -2
  143. package/src/client/components/core/Modal.js +20 -7
  144. package/src/client/components/core/Panel.js +0 -1
  145. package/src/client/components/core/PanelForm.js +19 -19
  146. package/src/client/components/core/RichText.js +1 -2
  147. package/src/client/components/core/SocketIo.js +82 -29
  148. package/src/client/components/core/SocketIoHandler.js +75 -0
  149. package/src/client/components/core/Stream.js +143 -95
  150. package/src/client/components/core/Webhook.js +40 -7
  151. package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
  152. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +3 -3
  153. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +2 -2
  154. package/src/client/components/cryptokoyn/MenuCryptokoyn.js +3 -3
  155. package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
  156. package/src/client/components/cyberia/InstanceEngineCyberia.js +700 -0
  157. package/src/client/components/cyberia/MapEngineCyberia.js +1359 -2
  158. package/src/client/components/cyberia/ObjectLayerEngineModal.js +17 -6
  159. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +92 -54
  160. package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
  161. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +217 -30
  162. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +44 -2
  163. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +3 -4
  164. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +2 -2
  165. package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +104 -9
  166. package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +5 -0
  167. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
  168. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
  169. package/src/client/components/default/AppStoreDefault.js +5 -0
  170. package/src/client/components/default/LogInDefault.js +3 -3
  171. package/src/client/components/default/LogOutDefault.js +2 -2
  172. package/src/client/components/default/MenuDefault.js +5 -5
  173. package/src/client/components/default/SocketIoDefault.js +3 -51
  174. package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
  175. package/src/client/components/itemledger/LogInItemledger.js +3 -3
  176. package/src/client/components/itemledger/LogOutItemledger.js +2 -2
  177. package/src/client/components/itemledger/MenuItemledger.js +3 -3
  178. package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
  179. package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
  180. package/src/client/components/underpost/CssUnderpost.js +59 -0
  181. package/src/client/components/underpost/LogInUnderpost.js +6 -3
  182. package/src/client/components/underpost/LogOutUnderpost.js +4 -2
  183. package/src/client/components/underpost/MenuUnderpost.js +104 -18
  184. package/src/client/components/underpost/RoutesUnderpost.js +2 -0
  185. package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
  186. package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
  187. package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
  188. package/src/client/public/cryptokoyn/microdata.json +85 -0
  189. package/src/client/public/cryptokoyn/site.webmanifest +57 -0
  190. package/src/client/public/cryptokoyn/sitemap +3 -3
  191. package/src/client/public/default/sitemap +3 -3
  192. package/src/client/public/itemledger/browserconfig.xml +2 -2
  193. package/src/client/public/itemledger/manifest.webmanifest +4 -4
  194. package/src/client/public/itemledger/microdata.json +71 -0
  195. package/src/client/public/itemledger/sitemap +3 -3
  196. package/src/client/public/itemledger/yandex-browser-manifest.json +2 -2
  197. package/src/client/public/test/sitemap +3 -3
  198. package/src/client/services/core/core.service.js +20 -8
  199. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +105 -0
  200. package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
  201. package/src/client/services/cyberia-entity/cyberia-entity.service.js +105 -0
  202. package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
  203. package/src/client/services/cyberia-instance/cyberia-instance.service.js +122 -0
  204. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +105 -0
  205. package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
  206. package/src/client/services/cyberia-map/cyberia-map.service.js +126 -0
  207. package/src/client/services/instance/instance.management.js +2 -2
  208. package/src/client/services/ipfs/ipfs.service.js +3 -23
  209. package/src/client/services/object-layer/object-layer.management.js +3 -3
  210. package/src/client/services/object-layer/object-layer.service.js +21 -0
  211. package/src/client/services/user/user.management.js +2 -2
  212. package/src/client/ssr/body/404.js +15 -11
  213. package/src/client/ssr/body/500.js +15 -11
  214. package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
  215. package/src/client/ssr/head/PwaItemledger.js +60 -0
  216. package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
  217. package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
  218. package/src/client/ssr/pages/Test.js +11 -10
  219. package/src/client.build.js +0 -3
  220. package/src/client.dev.js +0 -3
  221. package/src/db/DataBaseProvider.js +17 -2
  222. package/src/db/mariadb/MariaDB.js +14 -9
  223. package/src/db/mongo/MongooseDB.js +17 -1
  224. package/src/grpc/cyberia/OFF_CHAIN_ECONOMY.md +305 -0
  225. package/src/grpc/cyberia/README.md +326 -0
  226. package/src/grpc/cyberia/grpc-server.js +530 -0
  227. package/src/index.js +24 -1
  228. package/src/proxy.js +0 -3
  229. package/src/runtime/express/Dockerfile +4 -0
  230. package/src/runtime/express/Express.js +33 -10
  231. package/src/runtime/lampp/Dockerfile +13 -2
  232. package/src/runtime/lampp/Lampp.js +33 -17
  233. package/src/runtime/wp/Dockerfile +68 -0
  234. package/src/runtime/wp/Wp.js +639 -0
  235. package/src/server/auth.js +36 -15
  236. package/src/server/backup.js +39 -12
  237. package/src/server/besu-genesis-generator.js +1630 -0
  238. package/src/server/client-build-docs.js +133 -17
  239. package/src/server/client-build-live.js +9 -18
  240. package/src/server/client-build.js +229 -101
  241. package/src/server/client-dev-server.js +14 -13
  242. package/src/server/client-formatted.js +109 -57
  243. package/src/server/conf.js +391 -164
  244. package/src/server/cron.js +27 -24
  245. package/src/server/dns.js +29 -12
  246. package/src/server/downloader.js +0 -2
  247. package/src/server/ipfs-client.js +24 -1
  248. package/src/server/logger.js +27 -9
  249. package/src/server/object-layer.js +217 -103
  250. package/src/server/peer.js +8 -2
  251. package/src/server/process.js +1 -50
  252. package/src/server/proxy.js +4 -8
  253. package/src/server/runtime.js +30 -9
  254. package/src/server/semantic-layer-generator-floor.js +359 -0
  255. package/src/server/semantic-layer-generator-skin.js +1294 -0
  256. package/src/server/semantic-layer-generator.js +116 -555
  257. package/src/server/ssr.js +0 -3
  258. package/src/server/start.js +19 -12
  259. package/src/server/tls.js +0 -2
  260. package/src/server.js +0 -4
  261. package/src/ws/IoInterface.js +1 -10
  262. package/src/ws/IoServer.js +14 -33
  263. package/src/ws/core/channels/core.ws.chat.js +65 -20
  264. package/src/ws/core/channels/core.ws.mailer.js +113 -32
  265. package/src/ws/core/channels/core.ws.stream.js +90 -31
  266. package/src/ws/core/core.ws.connection.js +12 -33
  267. package/src/ws/core/core.ws.emit.js +10 -26
  268. package/src/ws/core/core.ws.server.js +25 -58
  269. package/src/ws/default/channels/default.ws.main.js +53 -12
  270. package/src/ws/default/default.ws.connection.js +26 -13
  271. package/src/ws/default/default.ws.server.js +30 -12
  272. package/.env.development +0 -43
  273. package/.env.test +0 -43
  274. package/hardhat/contracts/CryptoKoyn.sol +0 -59
  275. package/hardhat/contracts/ItemLedger.sol +0 -73
  276. package/hardhat/contracts/Lock.sol +0 -34
  277. package/hardhat/hardhat.config.cjs +0 -45
  278. package/hardhat/ignition/modules/Lock.js +0 -18
  279. package/hardhat/networks/cryptokoyn-itemledger.network.json +0 -29
  280. package/hardhat/scripts/deployCryptokoyn.cjs +0 -25
  281. package/hardhat/scripts/deployItemledger.cjs +0 -25
  282. package/hardhat/test/Lock.js +0 -126
  283. package/hardhat/white-paper.md +0 -581
  284. package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
  285. package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
  286. package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
  287. package/src/client/components/default/ElementsDefault.js +0 -38
  288. package/src/client/components/itemledger/CommonItemledger.js +0 -29
  289. package/src/client/components/itemledger/ElementsItemledger.js +0 -38
  290. package/src/client/components/underpost/CommonUnderpost.js +0 -29
  291. package/src/client/components/underpost/ElementsUnderpost.js +0 -38
  292. package/src/ws/core/management/core.ws.chat.js +0 -8
  293. package/src/ws/core/management/core.ws.mailer.js +0 -16
  294. package/src/ws/core/management/core.ws.stream.js +0 -8
  295. package/src/ws/default/management/default.ws.main.js +0 -8
  296. package/white-paper.md +0 -581
@@ -1,6 +1,22 @@
1
1
  /**
2
- * Mongoose model for IPFS API – a general-purpose pin record that relates
3
- * a user to a CID.
2
+ * Mongoose model for the IPFS CID Registry.
3
+ *
4
+ * Purpose: tracks every CID that the Engine has pinned to IPFS so that:
5
+ * 1. Deletions can cleanly unpin the right CID from the IPFS node/cluster.
6
+ * 2. Admins can run a health-check that verifies every DB-registered CID is
7
+ * still actually pinned (GET /ipfs/verify).
8
+ * 3. Garbage-collection jobs can discover orphaned CIDs (pinned on the node
9
+ * but not referenced by any DB document).
10
+ *
11
+ * Fields:
12
+ * cid - IPFS Content Identifier (CIDv0 or CIDv1).
13
+ * resourceType - What kind of asset this CID belongs to:
14
+ * 'object-layer-data' - JSON payload of an ObjectLayer document.
15
+ * 'atlas-sprite-sheet' - PNG sprite-sheet of an ObjectLayer.
16
+ * 'atlas-metadata' - JSON metadata of an AtlasSpriteSheet.
17
+ * mfsPath - MFS (Mutable File System) path used when the CID was added,
18
+ * e.g. /object-layer/sword/sword_data.json.
19
+ * Enables targeted removal via files/rm without knowing the CID.
4
20
  *
5
21
  * @module src/api/ipfs/ipfs.model.js
6
22
  * @namespace IpfsModel
@@ -8,14 +24,6 @@
8
24
 
9
25
  import { Schema, model } from 'mongoose';
10
26
 
11
- /**
12
- * @typedef {Object} Ipfs
13
- * @property {string} cid – IPFS Content Identifier (CIDv0 or CIDv1).
14
- * @property {Types.ObjectId} userId – Reference to the User who owns / requested the pin.
15
- * @property {Date} createdAt – Auto-managed by Mongoose.
16
- * @property {Date} updatedAt – Auto-managed by Mongoose.
17
- * @memberof IpfsModel
18
- */
19
27
  const IpfsSchema = new Schema(
20
28
  {
21
29
  cid: {
@@ -23,10 +31,19 @@ const IpfsSchema = new Schema(
23
31
  required: true,
24
32
  trim: true,
25
33
  },
26
- userId: {
27
- type: Schema.Types.ObjectId,
28
- ref: 'User',
34
+ // Asset category - determines which service owns this CID.
35
+ resourceType: {
36
+ type: String,
29
37
  required: true,
38
+ trim: true,
39
+ enum: ['object-layer-data', 'atlas-sprite-sheet', 'atlas-metadata'],
40
+ },
41
+ // MFS path used when the content was added to IPFS.
42
+ // Empty string when the content was added without an MFS copy.
43
+ mfsPath: {
44
+ type: String,
45
+ default: '',
46
+ trim: true,
30
47
  },
31
48
  },
32
49
  {
@@ -34,14 +51,14 @@ const IpfsSchema = new Schema(
34
51
  },
35
52
  );
36
53
 
37
- // Compound index: one pin record per user + CID pair.
38
- IpfsSchema.index({ cid: 1, userId: 1 }, { unique: true });
54
+ // One DB record per (CID, resourceType) pair.
55
+ IpfsSchema.index({ cid: 1, resourceType: 1 }, { unique: true });
39
56
 
40
- // Fast look-ups by user.
41
- IpfsSchema.index({ userId: 1 });
57
+ // Fast look-ups for health-check and garbage-collection by type.
58
+ IpfsSchema.index({ resourceType: 1 });
42
59
 
43
- // Fast look-ups by CID.
44
- IpfsSchema.index({ cid: 1 });
60
+ // Fast look-ups for targeted MFS cleanup.
61
+ IpfsSchema.index({ mfsPath: 1 });
45
62
 
46
63
  const IpfsModel = model('Ipfs', IpfsSchema);
47
64
 
@@ -49,21 +66,13 @@ const ProviderSchema = IpfsSchema;
49
66
 
50
67
  const IpfsDto = {
51
68
  select: {
52
- get: () => {
53
- return {
54
- _id: 1,
55
- cid: 1,
56
- userId: 1,
57
- createdAt: 1,
58
- updatedAt: 1,
59
- };
60
- },
61
- },
62
- populate: {
63
- user: () => ({
64
- path: 'userId',
65
- model: 'User',
66
- select: '_id username email role',
69
+ get: () => ({
70
+ _id: 1,
71
+ cid: 1,
72
+ resourceType: 1,
73
+ mfsPath: 1,
74
+ createdAt: 1,
75
+ updatedAt: 1,
67
76
  }),
68
77
  },
69
78
  };
@@ -8,19 +8,14 @@ const logger = loggerFactory(import.meta);
8
8
  const IpfsRouter = (options) => {
9
9
  const router = express.Router();
10
10
  const authMiddleware = options.authMiddleware;
11
- router.post(`/pin`, authMiddleware, adminGuard, async (req, res) => await IpfsController.pin(req, res, options));
12
- router.delete(
13
- `/pin/:cid`,
14
- authMiddleware,
15
- adminGuard,
16
- async (req, res) => await IpfsController.unpin(req, res, options),
17
- );
18
- router.post(`/:id`, authMiddleware, async (req, res) => await IpfsController.post(req, res, options));
19
- router.post(`/`, authMiddleware, async (req, res) => await IpfsController.post(req, res, options));
20
- router.get(`/:id`, authMiddleware, async (req, res) => await IpfsController.get(req, res, options));
21
- router.get(`/`, authMiddleware, async (req, res) => await IpfsController.get(req, res, options));
22
- router.put(`/:id`, authMiddleware, async (req, res) => await IpfsController.put(req, res, options));
23
- router.put(`/`, authMiddleware, async (req, res) => await IpfsController.put(req, res, options));
11
+ // Health / audit must come before /:id to avoid matching conflicts.
12
+ router.get(`/verify`, authMiddleware, adminGuard, async (req, res) => await IpfsController.verify(req, res, options));
13
+ router.post(`/:id`, authMiddleware, adminGuard, async (req, res) => await IpfsController.post(req, res, options));
14
+ router.post(`/`, authMiddleware, adminGuard, async (req, res) => await IpfsController.post(req, res, options));
15
+ router.get(`/:id`, authMiddleware, adminGuard, async (req, res) => await IpfsController.get(req, res, options));
16
+ router.get(`/`, authMiddleware, adminGuard, async (req, res) => await IpfsController.get(req, res, options));
17
+ router.put(`/:id`, authMiddleware, adminGuard, async (req, res) => await IpfsController.put(req, res, options));
18
+ router.put(`/`, authMiddleware, adminGuard, async (req, res) => await IpfsController.put(req, res, options));
24
19
  router.delete(`/:id`, authMiddleware, adminGuard, async (req, res) => await IpfsController.delete(req, res, options));
25
20
  router.delete(`/`, authMiddleware, adminGuard, async (req, res) => await IpfsController.delete(req, res, options));
26
21
  return router;
@@ -7,47 +7,40 @@ import { IpfsDto } from './ipfs.model.js';
7
7
  const logger = loggerFactory(import.meta);
8
8
 
9
9
  /**
10
- * Create (or upsert) an IPFS pin record for a given user + CID pair.
11
- * This is a helper consumed by other services (ObjectLayer, AtlasSpriteSheet, …)
12
- * so they don't need to know about the Ipfs model directly.
10
+ * Upsert a CID registry entry.
11
+ * Called by asset services (ObjectLayer, AtlasSpriteSheet) after a successful
12
+ * IPFS pin so the registry always reflects what is actually pinned.
13
13
  *
14
- * @param {object} opts
15
- * @param {string} opts.cid IPFS Content Identifier.
16
- * @param {string} opts.userId – Mongoose ObjectId string of the owning user.
17
- * @param {object} opts.options – Router options ({ host, path }) for DB lookup.
14
+ * @param {object} opts
15
+ * @param {string} opts.cid - IPFS Content Identifier.
16
+ * @param {string} opts.resourceType - Asset category ('object-layer-data' | 'atlas-sprite-sheet' | 'atlas-metadata').
17
+ * @param {string} [opts.mfsPath] - MFS path used when adding the content.
18
+ * @param {object} opts.options - Router options ({ host, path }) for DB lookup.
18
19
  * @returns {Promise<import('mongoose').Document>}
19
20
  */
20
- const createPinRecord = async ({ cid, userId, options }) => {
21
+ const createPinRecord = async ({ cid, resourceType, mfsPath = '', options }) => {
21
22
  const Ipfs = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.Ipfs;
22
-
23
- // Upsert: if a record for this user + CID already exists, just touch it.
24
23
  const record = await Ipfs.findOneAndUpdate(
25
- { cid, userId },
26
- { cid, userId },
27
- { upsert: true, new: true, setDefaultsOnInsert: true },
24
+ { cid, resourceType },
25
+ { cid, resourceType, mfsPath },
26
+ { upsert: true, returnDocument: 'after', setDefaultsOnInsert: true },
28
27
  );
29
-
30
- logger.info(`IPFS pin record upserted – CID: ${cid}, userId: ${userId}`);
28
+ logger.info(`IPFS registry upserted – CID: ${cid}, type: ${resourceType}, mfsPath: ${mfsPath}`);
31
29
  return record;
32
30
  };
33
31
 
34
32
  /**
35
- * Remove all DB pin records for a CID, then best-effort unpin from IPFS node/cluster.
36
- * Always deletes the DB records first so that even if the IPFS node is unreachable
37
- * the database stays clean.
33
+ * Remove all DB registry entries for a CID, then best-effort unpin from the IPFS node.
34
+ * Always deletes DB records first so the registry stays clean even if the node is down.
38
35
  *
39
- * @param {string} cid IPFS Content Identifier to clean up.
40
- * @param {object} options Router options ({ host, path }) for DB lookup.
36
+ * @param {string} cid - IPFS Content Identifier to remove.
37
+ * @param {object} options - Router options ({ host, path }) for DB lookup.
41
38
  * @returns {Promise<void>}
42
39
  */
43
40
  const removePinRecordsAndUnpin = async (cid, options) => {
44
41
  const Ipfs = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.Ipfs;
45
-
46
- // 1. Remove all DB pin records for this CID first
47
42
  await Ipfs.deleteMany({ cid });
48
- logger.info(`Removed all IPFS pin records for CID: ${cid}`);
49
-
50
- // 2. Best-effort unpin from IPFS node/cluster (ignore "not pinned" errors)
43
+ logger.info(`Removed IPFS registry entries for CID: ${cid}`);
51
44
  try {
52
45
  await IpfsClient.unpinCid(cid);
53
46
  } catch (err) {
@@ -65,128 +58,87 @@ const IpfsService = {
65
58
  // ──────────────────────────────────────────────
66
59
 
67
60
  post: async (req, res, options) => {
68
- /** @type {import('./ipfs.model.js').IpfsModel} */
69
61
  const Ipfs = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.Ipfs;
70
-
71
- // Accept { cid, userId? } in body.
72
- // If userId is omitted, fall back to the authenticated user.
73
- const body = { ...req.body };
74
- if (!body.userId && req.auth && req.auth.user) {
75
- body.userId = req.auth.user._id;
76
- }
77
- // Strip pinType if sent by legacy clients
78
- delete body.pinType;
79
-
80
- return await new Ipfs(body).save();
62
+ return await new Ipfs(req.body).save();
81
63
  },
82
64
 
83
65
  get: async (req, res, options) => {
84
- /** @type {import('./ipfs.model.js').IpfsModel} */
85
66
  const Ipfs = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.Ipfs;
86
-
87
67
  if (req.params.id) {
88
- return await Ipfs.findById(req.params.id).select(IpfsDto.select.get()).populate(IpfsDto.populate.user());
68
+ return await Ipfs.findById(req.params.id).select(IpfsDto.select.get());
89
69
  }
90
-
91
70
  const { query, sort, skip, limit, page } = DataQuery.parse(req.query);
92
-
93
71
  const [data, total] = await Promise.all([
94
- Ipfs.find(query)
95
- .select(IpfsDto.select.get())
96
- .sort(sort)
97
- .limit(limit)
98
- .skip(skip)
99
- .populate(IpfsDto.populate.user()),
72
+ Ipfs.find(query).select(IpfsDto.select.get()).sort(sort).limit(limit).skip(skip),
100
73
  Ipfs.countDocuments(query),
101
74
  ]);
102
-
103
75
  const totalPages = Math.ceil(total / limit);
104
76
  return { data, total, page, totalPages };
105
77
  },
106
78
 
107
79
  put: async (req, res, options) => {
108
- /** @type {import('./ipfs.model.js').IpfsModel} */
109
80
  const Ipfs = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.Ipfs;
110
- return await Ipfs.findByIdAndUpdate(req.params.id, req.body, { new: true });
81
+ return await Ipfs.findByIdAndUpdate(req.params.id, req.body, { returnDocument: 'after' });
111
82
  },
112
83
 
113
84
  delete: async (req, res, options) => {
114
- /** @type {import('./ipfs.model.js').IpfsModel} */
115
85
  const Ipfs = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.Ipfs;
116
-
117
86
  if (req.params.id) {
118
87
  const record = await Ipfs.findById(req.params.id);
119
88
  if (record) {
120
- // Remove DB record first, then best-effort unpin
121
89
  await Ipfs.findByIdAndDelete(req.params.id);
122
- try {
123
- // Only unpin from IPFS if no other records reference this CID
124
- const remaining = await Ipfs.countDocuments({ cid: record.cid });
125
- if (remaining === 0) {
90
+ // Only unpin from the node when no other registry entries reference this CID.
91
+ const remaining = await Ipfs.countDocuments({ cid: record.cid });
92
+ if (remaining === 0) {
93
+ try {
126
94
  await IpfsClient.unpinCid(record.cid);
95
+ } catch (err) {
96
+ logger.warn(`Failed to unpin CID ${record.cid}: ${err.message}`);
127
97
  }
128
- } catch (err) {
129
- logger.warn(`Failed to unpin CID ${record.cid} from IPFS node: ${err.message}`);
130
98
  }
131
99
  return record;
132
100
  }
133
101
  return null;
134
102
  }
135
-
136
103
  return await Ipfs.deleteMany();
137
104
  },
138
105
 
139
106
  // ──────────────────────────────────────────────
140
- // Pin / Unpin helpers (called via controller)
107
+ // Health / audit
141
108
  // ──────────────────────────────────────────────
142
109
 
143
110
  /**
144
- * POST /ipfs/pin – add content to IPFS, pin it, and create a DB record.
145
- * Body: { data: <string|object>, userId? }
111
+ * GET /ipfs/verify
112
+ *
113
+ * Iterates every CID in the registry and checks whether it is actually pinned
114
+ * on the connected IPFS node. Returns a summary suitable for admin dashboards
115
+ * and automated alerting.
116
+ *
117
+ * Response shape:
118
+ * { total, pinned, unpinned, errors, entries: [{ cid, resourceType, mfsPath, pinned, error? }] }
146
119
  */
147
- pin: async (req, res, options) => {
148
- const userId = req.body.userId || (req.auth && req.auth.user ? req.auth.user._id : undefined);
149
- if (!userId) throw new Error('userId is required to create a pin record');
150
-
151
- const content = typeof req.body.data === 'string' ? req.body.data : JSON.stringify(req.body.data);
152
- const result = await IpfsClient.addToIpfs(Buffer.from(content, 'utf-8'), req.body.filename || 'data');
153
-
154
- if (!result) throw new Error('IPFS node is unreachable – content was not pinned');
155
-
156
- const record = await createPinRecord({
157
- cid: result.cid,
158
- userId,
159
- options,
160
- });
161
-
162
- return { cid: result.cid, size: result.size, record };
163
- },
164
-
165
- /**
166
- * DELETE /ipfs/pin/:cid – unpin a CID and remove the DB record for the current user.
167
- */
168
- unpin: async (req, res, options) => {
120
+ verify: async (req, res, options) => {
169
121
  const Ipfs = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.Ipfs;
170
- const userId = req.auth && req.auth.user ? req.auth.user._id : undefined;
171
- const cid = req.params.cid || req.params.id;
122
+ const records = await Ipfs.find({}).select(IpfsDto.select.get()).lean();
172
123
 
173
- const record = await Ipfs.findOne({ cid, ...(userId ? { userId } : {}) });
174
- if (!record) throw new Error(`No pin record found for CID ${cid}`);
175
-
176
- // Remove DB record first
177
- await Ipfs.findByIdAndDelete(record._id);
178
-
179
- // Only unpin from the IPFS node when nobody else has a record for this CID
180
- const remaining = await Ipfs.countDocuments({ cid });
181
- if (remaining === 0) {
182
- try {
183
- await IpfsClient.unpinCid(cid);
184
- } catch (err) {
185
- logger.warn(`Best-effort IPFS unpin failed for CID ${cid}: ${err.message}`);
186
- }
187
- }
124
+ let pinned = 0;
125
+ let unpinned = 0;
126
+ let errors = 0;
127
+ const entries = await Promise.all(
128
+ records.map(async (r) => {
129
+ try {
130
+ const isPinned = await IpfsClient.isCidPinned(r.cid);
131
+ if (isPinned) pinned++;
132
+ else unpinned++;
133
+ return { cid: r.cid, resourceType: r.resourceType, mfsPath: r.mfsPath, pinned: isPinned };
134
+ } catch (err) {
135
+ errors++;
136
+ return { cid: r.cid, resourceType: r.resourceType, mfsPath: r.mfsPath, pinned: false, error: err.message };
137
+ }
138
+ }),
139
+ );
188
140
 
189
- return { success: true, cid };
141
+ return { total: records.length, pinned, unpinned, errors, entries };
190
142
  },
191
143
  };
192
144