@underpostnet/cyberia 3.2.80

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 (821) hide show
  1. package/.dockerignore +14 -0
  2. package/.env.example +144 -0
  3. package/.github/workflows/cyberia-client.cd.yml +40 -0
  4. package/.github/workflows/cyberia-server.cd.yml +40 -0
  5. package/.github/workflows/docker-image.cyberia-client.ci.yml +71 -0
  6. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +70 -0
  7. package/.github/workflows/docker-image.cyberia-server.ci.yml +71 -0
  8. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +71 -0
  9. package/.github/workflows/docker-image.engine-cyberia.ci.yml +53 -0
  10. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +53 -0
  11. package/.github/workflows/engine-cyberia.cd.yml +157 -0
  12. package/.github/workflows/engine-cyberia.ci.yml +125 -0
  13. package/.github/workflows/ghpkg.ci.yml +160 -0
  14. package/.github/workflows/gitlab.ci.yml +20 -0
  15. package/.github/workflows/hardhat.ci.yml +82 -0
  16. package/.github/workflows/npmpkg.ci.yml +126 -0
  17. package/.github/workflows/publish.ci.yml +87 -0
  18. package/.github/workflows/publish.cyberia.ci.yml +79 -0
  19. package/.github/workflows/pwa-microservices-template-page.cd.yml +65 -0
  20. package/.github/workflows/pwa-microservices-template-test.ci.yml +33 -0
  21. package/.github/workflows/release.cd.yml +46 -0
  22. package/.nycrc +9 -0
  23. package/.prettierignore +12 -0
  24. package/.prettierrc +9 -0
  25. package/.vscode/extensions.json +10 -0
  26. package/.vscode/settings.json +26 -0
  27. package/CHANGELOG.md +2516 -0
  28. package/CLI-HELP.md +1099 -0
  29. package/Dockerfile +161 -0
  30. package/Dockerfile.dev +161 -0
  31. package/Dockerfile.test +165 -0
  32. package/LICENSE +21 -0
  33. package/README.md +259 -0
  34. package/baremetal/commission-workflows.json +211 -0
  35. package/baremetal/packer-workflows.json +24 -0
  36. package/bin/build.js +261 -0
  37. package/bin/build.template.js +33 -0
  38. package/bin/cyberia.js +5627 -0
  39. package/bin/deploy.js +1547 -0
  40. package/bin/index.js +5627 -0
  41. package/bump.config.js +27 -0
  42. package/compose.env +144 -0
  43. package/conf.js +952 -0
  44. package/deployment.yaml +418 -0
  45. package/docker-compose.yml +434 -0
  46. package/examples/static-page/README.md +150 -0
  47. package/examples/static-page/ssr-components/CustomPage.js +554 -0
  48. package/examples/static-page/static-config-example.json +57 -0
  49. package/hardhat/.env.example +31 -0
  50. package/hardhat/contracts/ObjectLayerToken.sol +391 -0
  51. package/hardhat/deployments/.gitkeep +0 -0
  52. package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
  53. package/hardhat/hardhat.config.js +136 -0
  54. package/hardhat/ignition/modules/.gitkeep +0 -0
  55. package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
  56. package/hardhat/networks/besu-object-layer.network.json +138 -0
  57. package/hardhat/package-lock.json +2871 -0
  58. package/hardhat/package.json +43 -0
  59. package/hardhat/scripts/deployObjectLayerToken.js +98 -0
  60. package/hardhat/test/ObjectLayerToken.js +696 -0
  61. package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
  62. package/hardhat/types/ethers-contracts/common.ts +92 -0
  63. package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
  64. package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
  65. package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
  66. package/hardhat/types/ethers-contracts/index.ts +6 -0
  67. package/ipfs/configure-ipfs.sh +13 -0
  68. package/jsconfig.json +9 -0
  69. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +48 -0
  70. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +48 -0
  71. package/manifests/deployment/adminer/deployment.yaml +32 -0
  72. package/manifests/deployment/adminer/kustomization.yaml +7 -0
  73. package/manifests/deployment/adminer/service.yaml +13 -0
  74. package/manifests/deployment/dd-cyberia-development/deployment.yaml +418 -0
  75. package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
  76. package/manifests/deployment/dd-cyberia-development/proxy.yaml +189 -0
  77. package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +164 -0
  78. package/manifests/deployment/dd-default-development/deployment.yaml +163 -0
  79. package/manifests/deployment/dd-default-development/proxy.yaml +46 -0
  80. package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
  81. package/manifests/deployment/fastapi/backend-service.yml +19 -0
  82. package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
  83. package/manifests/deployment/fastapi/frontend-service.yml +15 -0
  84. package/manifests/deployment/fastapi/initial_data.sh +8 -0
  85. package/manifests/deployment/kafka/deployment.yaml +67 -0
  86. package/manifests/deployment/mongo-express/deployment.yaml +62 -0
  87. package/manifests/deployment/phpmyadmin/deployment.yaml +54 -0
  88. package/manifests/deployment/playwright/deployment.yaml +52 -0
  89. package/manifests/deployment/spark/spark-pi-py.yaml +20 -0
  90. package/manifests/deployment/tensorflow/tf-gpu-test.yaml +63 -0
  91. package/manifests/envoy-service-nodeport.yaml +22 -0
  92. package/manifests/grafana/deployment.yaml +64 -0
  93. package/manifests/grafana/kustomization.yaml +7 -0
  94. package/manifests/grafana/pvc.yaml +12 -0
  95. package/manifests/grafana/service.yaml +14 -0
  96. package/manifests/grafana/storage-class.yaml +9 -0
  97. package/manifests/ipfs/configmap.yaml +64 -0
  98. package/manifests/ipfs/headless-service.yaml +35 -0
  99. package/manifests/ipfs/kustomization.yaml +8 -0
  100. package/manifests/ipfs/statefulset.yaml +149 -0
  101. package/manifests/ipfs/storage-class.yaml +9 -0
  102. package/manifests/kind-config-dev.yaml +20 -0
  103. package/manifests/kind-config.yaml +12 -0
  104. package/manifests/kubeadm-calico-config.yaml +14 -0
  105. package/manifests/letsencrypt-prod.yaml +14 -0
  106. package/manifests/lxd/lxd-admin-profile.yaml +26 -0
  107. package/manifests/lxd/lxd-preseed.yaml +30 -0
  108. package/manifests/mariadb/kustomization.yaml +9 -0
  109. package/manifests/mariadb/pv.yaml +12 -0
  110. package/manifests/mariadb/pvc.yaml +10 -0
  111. package/manifests/mariadb/service.yaml +10 -0
  112. package/manifests/mariadb/statefulset.yaml +56 -0
  113. package/manifests/mariadb/storage-class.yaml +10 -0
  114. package/manifests/mongodb/headless-service.yaml +10 -0
  115. package/manifests/mongodb/kustomization.yaml +11 -0
  116. package/manifests/mongodb/pv-pvc.yaml +59 -0
  117. package/manifests/mongodb/statefulset.yaml +113 -0
  118. package/manifests/mongodb/storage-class.yaml +9 -0
  119. package/manifests/mongodb-4.4/headless-service.yaml +10 -0
  120. package/manifests/mongodb-4.4/kustomization.yaml +9 -0
  121. package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
  122. package/manifests/mongodb-4.4/pv-pvc.yaml +19 -0
  123. package/manifests/mongodb-4.4/service-deployment.yaml +61 -0
  124. package/manifests/mongodb-4.4/statefulset.yaml +79 -0
  125. package/manifests/mongodb-4.4/storage-class.yaml +9 -0
  126. package/manifests/mysql/kustomization.yaml +7 -0
  127. package/manifests/mysql/pv-pvc.yaml +27 -0
  128. package/manifests/mysql/statefulset.yaml +55 -0
  129. package/manifests/postgresql/configmap.yaml +9 -0
  130. package/manifests/postgresql/kustomization.yaml +10 -0
  131. package/manifests/postgresql/pv.yaml +15 -0
  132. package/manifests/postgresql/pvc.yaml +13 -0
  133. package/manifests/postgresql/service.yaml +10 -0
  134. package/manifests/postgresql/statefulset.yaml +37 -0
  135. package/manifests/prometheus/deployment.yaml +82 -0
  136. package/manifests/pv-pvc-dd.yaml +34 -0
  137. package/manifests/valkey/kustomization.yaml +7 -0
  138. package/manifests/valkey/service.yaml +10 -0
  139. package/manifests/valkey/statefulset.yaml +37 -0
  140. package/manifests/valkey/valkey-nodeport.yaml +17 -0
  141. package/mongodb/entrypoint.sh +76 -0
  142. package/nginx.conf +128 -0
  143. package/nodemon.json +6 -0
  144. package/package.json +101 -0
  145. package/packer/images/Rocky9Amd64/Makefile +62 -0
  146. package/packer/images/Rocky9Amd64/QUICKSTART.md +113 -0
  147. package/packer/images/Rocky9Amd64/README.md +122 -0
  148. package/packer/images/Rocky9Amd64/http/rocky9.ks.pkrtpl.hcl +114 -0
  149. package/packer/images/Rocky9Amd64/rocky9.pkr.hcl +164 -0
  150. package/packer/images/Rocky9Arm64/Makefile +69 -0
  151. package/packer/images/Rocky9Arm64/README.md +122 -0
  152. package/packer/images/Rocky9Arm64/http/rocky9.ks.pkrtpl.hcl +114 -0
  153. package/packer/images/Rocky9Arm64/rocky9.pkr.hcl +171 -0
  154. package/packer/scripts/fuse-nbd +64 -0
  155. package/packer/scripts/fuse-tar-root +63 -0
  156. package/proxy.yaml +189 -0
  157. package/pv-pvc.yaml +164 -0
  158. package/scripts/device-scan.sh +65 -0
  159. package/scripts/disk-clean.sh +182 -0
  160. package/scripts/disk-devices.sh +13 -0
  161. package/scripts/gen-fqdns.sh +14 -0
  162. package/scripts/gpu-diag.sh +19 -0
  163. package/scripts/ip-info.sh +118 -0
  164. package/scripts/ipxe-setup.sh +200 -0
  165. package/scripts/k3s-node-setup.sh +124 -0
  166. package/scripts/kubeadm-node-setup.sh +317 -0
  167. package/scripts/link-local-underpost-cli.sh +6 -0
  168. package/scripts/lxd-vm-setup.sh +208 -0
  169. package/scripts/maas-nat-firewalld.sh +145 -0
  170. package/scripts/maas-setup.sh +135 -0
  171. package/scripts/maas-upload-boot-resource.sh +183 -0
  172. package/scripts/nat-iptables.sh +111 -0
  173. package/scripts/nvim.sh +91 -0
  174. package/scripts/packer-init-vars-file.sh +40 -0
  175. package/scripts/packer-setup.sh +289 -0
  176. package/scripts/ports-ls.sh +33 -0
  177. package/scripts/quick-tftp.sh +19 -0
  178. package/scripts/rhel-grpc-setup.sh +56 -0
  179. package/scripts/rocky-kickstart.sh +986 -0
  180. package/scripts/rocky-pwa.sh +200 -0
  181. package/scripts/rocky-setup.sh +106 -0
  182. package/scripts/rpmfusion-ffmpeg-setup.sh +32 -0
  183. package/scripts/ssl.sh +170 -0
  184. package/scripts/test-monitor.sh +248 -0
  185. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +30 -0
  186. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +155 -0
  187. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +39 -0
  188. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +382 -0
  189. package/src/api/core/core.controller.js +8 -0
  190. package/src/api/core/core.model.js +11 -0
  191. package/src/api/core/core.router.js +23 -0
  192. package/src/api/core/core.service.js +30 -0
  193. package/src/api/crypto/crypto.controller.js +10 -0
  194. package/src/api/crypto/crypto.model.js +23 -0
  195. package/src/api/crypto/crypto.router.js +23 -0
  196. package/src/api/crypto/crypto.service.js +64 -0
  197. package/src/api/cyberia-action/cyberia-action.controller.js +8 -0
  198. package/src/api/cyberia-action/cyberia-action.model.js +79 -0
  199. package/src/api/cyberia-action/cyberia-action.router.js +21 -0
  200. package/src/api/cyberia-action/cyberia-action.service.js +58 -0
  201. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +27 -0
  202. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +100 -0
  203. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +70 -0
  204. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
  205. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +8 -0
  206. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
  207. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +20 -0
  208. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +57 -0
  209. package/src/api/cyberia-entity/cyberia-entity.controller.js +6 -0
  210. package/src/api/cyberia-entity/cyberia-entity.model.js +25 -0
  211. package/src/api/cyberia-entity/cyberia-entity.router.js +17 -0
  212. package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
  213. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +6 -0
  214. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
  215. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +17 -0
  216. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
  217. package/src/api/cyberia-instance/cyberia-fallback-world.js +337 -0
  218. package/src/api/cyberia-instance/cyberia-instance-boot.service.js +57 -0
  219. package/src/api/cyberia-instance/cyberia-instance-map.service.js +583 -0
  220. package/src/api/cyberia-instance/cyberia-instance.controller.js +42 -0
  221. package/src/api/cyberia-instance/cyberia-instance.model.js +117 -0
  222. package/src/api/cyberia-instance/cyberia-instance.router.js +75 -0
  223. package/src/api/cyberia-instance/cyberia-instance.service.js +190 -0
  224. package/src/api/cyberia-instance/cyberia-portal-connector.js +260 -0
  225. package/src/api/cyberia-instance/cyberia-world-generator.js +618 -0
  226. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +6 -0
  227. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +200 -0
  228. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +17 -0
  229. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +46 -0
  230. package/src/api/cyberia-map/cyberia-map.controller.js +6 -0
  231. package/src/api/cyberia-map/cyberia-map.model.js +33 -0
  232. package/src/api/cyberia-map/cyberia-map.router.js +19 -0
  233. package/src/api/cyberia-map/cyberia-map.service.js +76 -0
  234. package/src/api/cyberia-quest/cyberia-quest.controller.js +9 -0
  235. package/src/api/cyberia-quest/cyberia-quest.model.js +67 -0
  236. package/src/api/cyberia-quest/cyberia-quest.router.js +26 -0
  237. package/src/api/cyberia-quest/cyberia-quest.service.js +97 -0
  238. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +6 -0
  239. package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +49 -0
  240. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +21 -0
  241. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +42 -0
  242. package/src/api/cyberia-saga/cyberia-saga.controller.js +6 -0
  243. package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
  244. package/src/api/cyberia-saga/cyberia-saga.router.js +17 -0
  245. package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
  246. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +1076 -0
  247. package/src/api/cyberia-skill/cyberia-skill.controller.js +6 -0
  248. package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
  249. package/src/api/cyberia-skill/cyberia-skill.router.js +17 -0
  250. package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
  251. package/src/api/default/default.controller.js +6 -0
  252. package/src/api/default/default.model.js +20 -0
  253. package/src/api/default/default.router.js +21 -0
  254. package/src/api/default/default.service.js +42 -0
  255. package/src/api/document/document.controller.js +9 -0
  256. package/src/api/document/document.model.js +95 -0
  257. package/src/api/document/document.router.js +32 -0
  258. package/src/api/document/document.service.js +633 -0
  259. package/src/api/file/file.controller.js +15 -0
  260. package/src/api/file/file.model.js +127 -0
  261. package/src/api/file/file.ref.json +39 -0
  262. package/src/api/file/file.router.js +36 -0
  263. package/src/api/file/file.service.js +499 -0
  264. package/src/api/instance/instance.controller.js +8 -0
  265. package/src/api/instance/instance.model.js +34 -0
  266. package/src/api/instance/instance.router.js +27 -0
  267. package/src/api/instance/instance.service.js +75 -0
  268. package/src/api/ipfs/ipfs.controller.js +8 -0
  269. package/src/api/ipfs/ipfs.model.js +77 -0
  270. package/src/api/ipfs/ipfs.router.js +26 -0
  271. package/src/api/ipfs/ipfs.service.js +145 -0
  272. package/src/api/object-layer/object-layer.controller.js +18 -0
  273. package/src/api/object-layer/object-layer.model.js +200 -0
  274. package/src/api/object-layer/object-layer.router.js +729 -0
  275. package/src/api/object-layer/object-layer.service.js +894 -0
  276. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +6 -0
  277. package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +93 -0
  278. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +17 -0
  279. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +46 -0
  280. package/src/api/test/test.controller.js +14 -0
  281. package/src/api/test/test.model.js +14 -0
  282. package/src/api/test/test.router.js +23 -0
  283. package/src/api/test/test.service.js +35 -0
  284. package/src/api/types.js +24 -0
  285. package/src/api/user/guest.service.js +100 -0
  286. package/src/api/user/postman_collection.json +216 -0
  287. package/src/api/user/user.build.js +16 -0
  288. package/src/api/user/user.controller.js +35 -0
  289. package/src/api/user/user.model.js +142 -0
  290. package/src/api/user/user.router.js +367 -0
  291. package/src/api/user/user.service.js +582 -0
  292. package/src/api.js +23 -0
  293. package/src/cli/baremetal.js +3947 -0
  294. package/src/cli/cloud-init.js +673 -0
  295. package/src/cli/cluster.js +1395 -0
  296. package/src/cli/db.js +1533 -0
  297. package/src/cli/deploy.js +1542 -0
  298. package/src/cli/docker-compose.js +802 -0
  299. package/src/cli/env.js +177 -0
  300. package/src/cli/fs.js +380 -0
  301. package/src/cli/image.js +389 -0
  302. package/src/cli/index.js +1059 -0
  303. package/src/cli/ipfs.js +182 -0
  304. package/src/cli/kickstart.js +271 -0
  305. package/src/cli/kubectl.js +214 -0
  306. package/src/cli/lxd.js +1232 -0
  307. package/src/cli/monitor.js +659 -0
  308. package/src/cli/release.js +595 -0
  309. package/src/cli/repository.js +1969 -0
  310. package/src/cli/run.js +3326 -0
  311. package/src/cli/secrets.js +151 -0
  312. package/src/cli/ssh.js +975 -0
  313. package/src/cli/static.js +653 -0
  314. package/src/cli/system.js +332 -0
  315. package/src/cli/test.js +176 -0
  316. package/src/client/Cryptokoyn.index.js +30 -0
  317. package/src/client/CyberiaPortal.index.js +31 -0
  318. package/src/client/Default.index.js +77 -0
  319. package/src/client/Itemledger.index.js +35 -0
  320. package/src/client/Underpost.index.js +31 -0
  321. package/src/client/components/core/404.js +20 -0
  322. package/src/client/components/core/500.js +20 -0
  323. package/src/client/components/core/Account.js +630 -0
  324. package/src/client/components/core/AgGrid.js +238 -0
  325. package/src/client/components/core/Alert.js +76 -0
  326. package/src/client/components/core/AppStore.js +69 -0
  327. package/src/client/components/core/Auth.js +353 -0
  328. package/src/client/components/core/Badge.js +26 -0
  329. package/src/client/components/core/Blockchain.js +41 -0
  330. package/src/client/components/core/Blog.js +9 -0
  331. package/src/client/components/core/BtnIcon.js +109 -0
  332. package/src/client/components/core/CalendarCore.js +437 -0
  333. package/src/client/components/core/Chat.js +62 -0
  334. package/src/client/components/core/ClientEvents.js +163 -0
  335. package/src/client/components/core/ColorPaletteElement.js +309 -0
  336. package/src/client/components/core/CommonJs.js +1014 -0
  337. package/src/client/components/core/Content.js +349 -0
  338. package/src/client/components/core/Css.js +1134 -0
  339. package/src/client/components/core/CssCore.js +881 -0
  340. package/src/client/components/core/D3Chart.js +44 -0
  341. package/src/client/components/core/Docs.js +478 -0
  342. package/src/client/components/core/DropDown.js +321 -0
  343. package/src/client/components/core/EventBus.js +96 -0
  344. package/src/client/components/core/EventsUI.js +130 -0
  345. package/src/client/components/core/FileExplorer.js +1406 -0
  346. package/src/client/components/core/FullScreen.js +201 -0
  347. package/src/client/components/core/Input.js +537 -0
  348. package/src/client/components/core/Keyboard.js +80 -0
  349. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  350. package/src/client/components/core/LoadingAnimation.js +152 -0
  351. package/src/client/components/core/LogIn.js +212 -0
  352. package/src/client/components/core/LogOut.js +72 -0
  353. package/src/client/components/core/Logger.js +22 -0
  354. package/src/client/components/core/Modal.js +2956 -0
  355. package/src/client/components/core/NotificationManager.js +80 -0
  356. package/src/client/components/core/Pagination.js +252 -0
  357. package/src/client/components/core/Panel.js +1308 -0
  358. package/src/client/components/core/PanelForm.js +915 -0
  359. package/src/client/components/core/Polyhedron.js +947 -0
  360. package/src/client/components/core/PublicProfile.js +895 -0
  361. package/src/client/components/core/Recover.js +211 -0
  362. package/src/client/components/core/Responsive.js +146 -0
  363. package/src/client/components/core/RichText.js +33 -0
  364. package/src/client/components/core/Router.js +523 -0
  365. package/src/client/components/core/Scroll.js +76 -0
  366. package/src/client/components/core/SearchBox.js +1007 -0
  367. package/src/client/components/core/SignUp.js +153 -0
  368. package/src/client/components/core/SocketIo.js +163 -0
  369. package/src/client/components/core/SocketIoHandler.js +75 -0
  370. package/src/client/components/core/Stream.js +161 -0
  371. package/src/client/components/core/ToggleSwitch.js +89 -0
  372. package/src/client/components/core/ToolTip.js +78 -0
  373. package/src/client/components/core/Translate.js +694 -0
  374. package/src/client/components/core/Validator.js +140 -0
  375. package/src/client/components/core/VanillaJs.js +485 -0
  376. package/src/client/components/core/Wallet.js +95 -0
  377. package/src/client/components/core/WebComponent.js +44 -0
  378. package/src/client/components/core/Webhook.js +58 -0
  379. package/src/client/components/core/Worker.js +313 -0
  380. package/src/client/components/core/windowGetDimensions.js +269 -0
  381. package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +352 -0
  382. package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
  383. package/src/client/components/cryptokoyn/CssCryptokoyn.js +212 -0
  384. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +16 -0
  385. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +12 -0
  386. package/src/client/components/cryptokoyn/RouterCryptokoyn.js +37 -0
  387. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +16 -0
  388. package/src/client/components/cryptokoyn/SignUpCryptokoyn.js +11 -0
  389. package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +6 -0
  390. package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
  391. package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
  392. package/src/client/components/cyberia/InstanceEngineCyberia.js +1088 -0
  393. package/src/client/components/cyberia/InstanceSelectionView.js +900 -0
  394. package/src/client/components/cyberia/MapEngineCyberia.js +1976 -0
  395. package/src/client/components/cyberia/ObjectLayerEngine.js +1540 -0
  396. package/src/client/components/cyberia/ObjectLayerEngineModal.js +2420 -0
  397. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +1522 -0
  398. package/src/client/components/cyberia/SharedDefaultsCyberia.js +528 -0
  399. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +772 -0
  400. package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
  401. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +254 -0
  402. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +21 -0
  403. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +16 -0
  404. package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +518 -0
  405. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +72 -0
  406. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +16 -0
  407. package/src/client/components/cyberia-portal/SignUpCyberiaPortal.js +13 -0
  408. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +6 -0
  409. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +48 -0
  410. package/src/client/components/default/AppShellDefault.js +765 -0
  411. package/src/client/components/default/AppStoreDefault.js +5 -0
  412. package/src/client/components/default/CommonDefault.js +29 -0
  413. package/src/client/components/default/CssDefault.js +27 -0
  414. package/src/client/components/default/LogInDefault.js +16 -0
  415. package/src/client/components/default/LogOutDefault.js +12 -0
  416. package/src/client/components/default/RouterDefault.js +47 -0
  417. package/src/client/components/default/SettingsDefault.js +16 -0
  418. package/src/client/components/default/SignUpDefault.js +11 -0
  419. package/src/client/components/default/SocketIoDefault.js +6 -0
  420. package/src/client/components/default/TranslateDefault.js +7 -0
  421. package/src/client/components/itemledger/AppShellItemledger.js +353 -0
  422. package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
  423. package/src/client/components/itemledger/CssItemledger.js +212 -0
  424. package/src/client/components/itemledger/LogInItemledger.js +17 -0
  425. package/src/client/components/itemledger/LogOutItemledger.js +12 -0
  426. package/src/client/components/itemledger/RouterItemledger.js +38 -0
  427. package/src/client/components/itemledger/SettingsItemledger.js +16 -0
  428. package/src/client/components/itemledger/SignUpItemledger.js +11 -0
  429. package/src/client/components/itemledger/SocketIoItemledger.js +6 -0
  430. package/src/client/components/itemledger/TranslateItemledger.js +7 -0
  431. package/src/client/components/underpost/AppShellUnderpost.js +691 -0
  432. package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
  433. package/src/client/components/underpost/CssUnderpost.js +281 -0
  434. package/src/client/components/underpost/CyberpunkBloggerUnderpost.js +879 -0
  435. package/src/client/components/underpost/DocumentSearchProvider.js +448 -0
  436. package/src/client/components/underpost/LabGalleryUnderpost.js +79 -0
  437. package/src/client/components/underpost/LogInUnderpost.js +25 -0
  438. package/src/client/components/underpost/LogOutUnderpost.js +17 -0
  439. package/src/client/components/underpost/RouterUnderpost.js +45 -0
  440. package/src/client/components/underpost/SettingsUnderpost.js +16 -0
  441. package/src/client/components/underpost/SignUpUnderpost.js +11 -0
  442. package/src/client/components/underpost/SocketIoUnderpost.js +6 -0
  443. package/src/client/components/underpost/TranslateUnderpost.js +10 -0
  444. package/src/client/public/cryptokoyn/assets/banner/koyn-social.png +0 -0
  445. package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
  446. package/src/client/public/cryptokoyn/assets/logo/cryptokoyn.png +0 -0
  447. package/src/client/public/cryptokoyn/assets/splash/android-chrome-144x144.png +0 -0
  448. package/src/client/public/cryptokoyn/assets/splash/android-chrome-192x192.png +0 -0
  449. package/src/client/public/cryptokoyn/assets/splash/android-chrome-256x256.png +0 -0
  450. package/src/client/public/cryptokoyn/assets/splash/android-chrome-36x36.png +0 -0
  451. package/src/client/public/cryptokoyn/assets/splash/android-chrome-48x48.png +0 -0
  452. package/src/client/public/cryptokoyn/assets/splash/android-chrome-72x72.png +0 -0
  453. package/src/client/public/cryptokoyn/assets/splash/android-chrome-96x96.png +0 -0
  454. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114-precomposed.png +0 -0
  455. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-114x114.png +0 -0
  456. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120-precomposed.png +0 -0
  457. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-120x120.png +0 -0
  458. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144-precomposed.png +0 -0
  459. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-144x144.png +0 -0
  460. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152-precomposed.png +0 -0
  461. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-152x152.png +0 -0
  462. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180-precomposed.png +0 -0
  463. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-180x180.png +0 -0
  464. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57-precomposed.png +0 -0
  465. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-57x57.png +0 -0
  466. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60-precomposed.png +0 -0
  467. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-60x60.png +0 -0
  468. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72-precomposed.png +0 -0
  469. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-72x72.png +0 -0
  470. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76-precomposed.png +0 -0
  471. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-76x76.png +0 -0
  472. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon-precomposed.png +0 -0
  473. package/src/client/public/cryptokoyn/assets/splash/apple-touch-icon.png +0 -0
  474. package/src/client/public/cryptokoyn/assets/splash/favicon-16x16.png +0 -0
  475. package/src/client/public/cryptokoyn/assets/splash/favicon-32x32.png +0 -0
  476. package/src/client/public/cryptokoyn/assets/splash/mstile-144x144.png +0 -0
  477. package/src/client/public/cryptokoyn/assets/splash/mstile-150x150.png +0 -0
  478. package/src/client/public/cryptokoyn/assets/splash/mstile-310x150.png +0 -0
  479. package/src/client/public/cryptokoyn/assets/splash/mstile-310x310.png +0 -0
  480. package/src/client/public/cryptokoyn/assets/splash/mstile-70x70.png +0 -0
  481. package/src/client/public/cryptokoyn/assets/splash/safari-pinned-tab.svg +42 -0
  482. package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
  483. package/src/client/public/cryptokoyn/favicon.ico +0 -0
  484. package/src/client/public/cryptokoyn/microdata.json +85 -0
  485. package/src/client/public/cryptokoyn/site.webmanifest +57 -0
  486. package/src/client/public/cryptokoyn/sitemap +148 -0
  487. package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +307 -0
  488. package/src/client/public/cyberia-docs/ARCHITECTURE.md +309 -0
  489. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +204 -0
  490. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +341 -0
  491. package/src/client/public/cyberia-docs/CYBERIA-LORE.md +84 -0
  492. package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
  493. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +294 -0
  494. package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
  495. package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +242 -0
  496. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +300 -0
  497. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +309 -0
  498. package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +228 -0
  499. package/src/client/public/cyberia-docs/ROADMAP.md +240 -0
  500. package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
  501. package/src/client/public/cyberia-docs/WHITE-PAPER.md +740 -0
  502. package/src/client/public/default/android-chrome-144x144.png +0 -0
  503. package/src/client/public/default/android-chrome-192x192.png +0 -0
  504. package/src/client/public/default/android-chrome-256x256.png +0 -0
  505. package/src/client/public/default/android-chrome-36x36.png +0 -0
  506. package/src/client/public/default/android-chrome-384x384.png +0 -0
  507. package/src/client/public/default/android-chrome-48x48.png +0 -0
  508. package/src/client/public/default/android-chrome-512x512.png +0 -0
  509. package/src/client/public/default/android-chrome-72x72.png +0 -0
  510. package/src/client/public/default/android-chrome-96x96.png +0 -0
  511. package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
  512. package/src/client/public/default/apple-touch-icon-114x114-precomposed.png +0 -0
  513. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  514. package/src/client/public/default/apple-touch-icon-120x120-precomposed.png +0 -0
  515. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  516. package/src/client/public/default/apple-touch-icon-144x144-precomposed.png +0 -0
  517. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  518. package/src/client/public/default/apple-touch-icon-152x152-precomposed.png +0 -0
  519. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  520. package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
  521. package/src/client/public/default/apple-touch-icon-180x180-precomposed.png +0 -0
  522. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  523. package/src/client/public/default/apple-touch-icon-57x57-precomposed.png +0 -0
  524. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  525. package/src/client/public/default/apple-touch-icon-60x60-precomposed.png +0 -0
  526. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  527. package/src/client/public/default/apple-touch-icon-72x72-precomposed.png +0 -0
  528. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  529. package/src/client/public/default/apple-touch-icon-76x76-precomposed.png +0 -0
  530. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  531. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  532. package/src/client/public/default/apple-touch-icon.png +0 -0
  533. package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
  534. package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
  535. package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
  536. package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
  537. package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
  538. package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
  539. package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
  540. package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
  541. package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
  542. package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
  543. package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
  544. package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
  545. package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
  546. package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
  547. package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
  548. package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
  549. package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
  550. package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
  551. package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
  552. package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
  553. package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
  554. package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
  555. package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
  556. package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
  557. package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
  558. package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
  559. package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
  560. package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
  561. package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
  562. package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
  563. package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
  564. package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
  565. package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
  566. package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
  567. package/src/client/public/default/assets/background/dark.jpg +0 -0
  568. package/src/client/public/default/assets/background/dark.svg +557 -0
  569. package/src/client/public/default/assets/background/white.jpg +0 -0
  570. package/src/client/public/default/assets/background/white0-min.jpg +0 -0
  571. package/src/client/public/default/assets/background/white0.jpg +0 -0
  572. package/src/client/public/default/assets/logo/base-icon.png +0 -0
  573. package/src/client/public/default/assets/logo/underpost.gif +0 -0
  574. package/src/client/public/default/assets/mailer/api-user-check.png +0 -0
  575. package/src/client/public/default/assets/mailer/api-user-default-avatar.png +0 -0
  576. package/src/client/public/default/assets/mailer/api-user-invalid-token.png +0 -0
  577. package/src/client/public/default/assets/mailer/api-user-recover.png +0 -0
  578. package/src/client/public/default/browserconfig.xml +12 -0
  579. package/src/client/public/default/favicon-16x16.png +0 -0
  580. package/src/client/public/default/favicon-32x32.png +0 -0
  581. package/src/client/public/default/favicon-48x48.png +0 -0
  582. package/src/client/public/default/favicon.ico +0 -0
  583. package/src/client/public/default/manifest.webmanifest +69 -0
  584. package/src/client/public/default/mstile-144x144.png +0 -0
  585. package/src/client/public/default/mstile-150x150.png +0 -0
  586. package/src/client/public/default/mstile-310x150.png +0 -0
  587. package/src/client/public/default/mstile-310x310.png +0 -0
  588. package/src/client/public/default/mstile-70x70.png +0 -0
  589. package/src/client/public/default/safari-pinned-tab.svg +24 -0
  590. package/src/client/public/default/site.webmanifest +69 -0
  591. package/src/client/public/default/sitemap +148 -0
  592. package/src/client/public/default/yandex-browser-50x50.png +0 -0
  593. package/src/client/public/default/yandex-browser-manifest.json +9 -0
  594. package/src/client/public/doc/favicon.ico +0 -0
  595. package/src/client/public/doc/sitemap +148 -0
  596. package/src/client/public/itemledger/android-chrome-144x144.png +0 -0
  597. package/src/client/public/itemledger/android-chrome-192x192.png +0 -0
  598. package/src/client/public/itemledger/android-chrome-256x256.png +0 -0
  599. package/src/client/public/itemledger/android-chrome-36x36.png +0 -0
  600. package/src/client/public/itemledger/android-chrome-384x384.png +0 -0
  601. package/src/client/public/itemledger/android-chrome-48x48.png +0 -0
  602. package/src/client/public/itemledger/android-chrome-512x512.png +0 -0
  603. package/src/client/public/itemledger/android-chrome-72x72.png +0 -0
  604. package/src/client/public/itemledger/android-chrome-96x96.png +0 -0
  605. package/src/client/public/itemledger/apple-touch-icon-1024x1024.png +0 -0
  606. package/src/client/public/itemledger/apple-touch-icon-114x114.png +0 -0
  607. package/src/client/public/itemledger/apple-touch-icon-120x120.png +0 -0
  608. package/src/client/public/itemledger/apple-touch-icon-144x144.png +0 -0
  609. package/src/client/public/itemledger/apple-touch-icon-152x152.png +0 -0
  610. package/src/client/public/itemledger/apple-touch-icon-167x167.png +0 -0
  611. package/src/client/public/itemledger/apple-touch-icon-180x180.png +0 -0
  612. package/src/client/public/itemledger/apple-touch-icon-57x57.png +0 -0
  613. package/src/client/public/itemledger/apple-touch-icon-60x60.png +0 -0
  614. package/src/client/public/itemledger/apple-touch-icon-72x72.png +0 -0
  615. package/src/client/public/itemledger/apple-touch-icon-76x76.png +0 -0
  616. package/src/client/public/itemledger/apple-touch-icon-precomposed.png +0 -0
  617. package/src/client/public/itemledger/apple-touch-icon.png +0 -0
  618. package/src/client/public/itemledger/apple-touch-startup-image-1125x2436.png +0 -0
  619. package/src/client/public/itemledger/apple-touch-startup-image-1136x640.png +0 -0
  620. package/src/client/public/itemledger/apple-touch-startup-image-1170x2532.png +0 -0
  621. package/src/client/public/itemledger/apple-touch-startup-image-1179x2556.png +0 -0
  622. package/src/client/public/itemledger/apple-touch-startup-image-1242x2208.png +0 -0
  623. package/src/client/public/itemledger/apple-touch-startup-image-1242x2688.png +0 -0
  624. package/src/client/public/itemledger/apple-touch-startup-image-1284x2778.png +0 -0
  625. package/src/client/public/itemledger/apple-touch-startup-image-1290x2796.png +0 -0
  626. package/src/client/public/itemledger/apple-touch-startup-image-1334x750.png +0 -0
  627. package/src/client/public/itemledger/apple-touch-startup-image-1488x2266.png +0 -0
  628. package/src/client/public/itemledger/apple-touch-startup-image-1536x2048.png +0 -0
  629. package/src/client/public/itemledger/apple-touch-startup-image-1620x2160.png +0 -0
  630. package/src/client/public/itemledger/apple-touch-startup-image-1640x2160.png +0 -0
  631. package/src/client/public/itemledger/apple-touch-startup-image-1668x2224.png +0 -0
  632. package/src/client/public/itemledger/apple-touch-startup-image-1668x2388.png +0 -0
  633. package/src/client/public/itemledger/apple-touch-startup-image-1792x828.png +0 -0
  634. package/src/client/public/itemledger/apple-touch-startup-image-2048x1536.png +0 -0
  635. package/src/client/public/itemledger/apple-touch-startup-image-2048x2732.png +0 -0
  636. package/src/client/public/itemledger/apple-touch-startup-image-2160x1620.png +0 -0
  637. package/src/client/public/itemledger/apple-touch-startup-image-2160x1640.png +0 -0
  638. package/src/client/public/itemledger/apple-touch-startup-image-2208x1242.png +0 -0
  639. package/src/client/public/itemledger/apple-touch-startup-image-2224x1668.png +0 -0
  640. package/src/client/public/itemledger/apple-touch-startup-image-2266x1488.png +0 -0
  641. package/src/client/public/itemledger/apple-touch-startup-image-2388x1668.png +0 -0
  642. package/src/client/public/itemledger/apple-touch-startup-image-2436x1125.png +0 -0
  643. package/src/client/public/itemledger/apple-touch-startup-image-2532x1170.png +0 -0
  644. package/src/client/public/itemledger/apple-touch-startup-image-2556x1179.png +0 -0
  645. package/src/client/public/itemledger/apple-touch-startup-image-2688x1242.png +0 -0
  646. package/src/client/public/itemledger/apple-touch-startup-image-2732x2048.png +0 -0
  647. package/src/client/public/itemledger/apple-touch-startup-image-2778x1284.png +0 -0
  648. package/src/client/public/itemledger/apple-touch-startup-image-2796x1290.png +0 -0
  649. package/src/client/public/itemledger/apple-touch-startup-image-640x1136.png +0 -0
  650. package/src/client/public/itemledger/apple-touch-startup-image-750x1334.png +0 -0
  651. package/src/client/public/itemledger/apple-touch-startup-image-828x1792.png +0 -0
  652. package/src/client/public/itemledger/assets/logo/base-icon.png +0 -0
  653. package/src/client/public/itemledger/browserconfig.xml +12 -0
  654. package/src/client/public/itemledger/favicon-16x16.png +0 -0
  655. package/src/client/public/itemledger/favicon-32x32.png +0 -0
  656. package/src/client/public/itemledger/favicon-48x48.png +0 -0
  657. package/src/client/public/itemledger/favicon.ico +0 -0
  658. package/src/client/public/itemledger/manifest.webmanifest +69 -0
  659. package/src/client/public/itemledger/microdata.json +71 -0
  660. package/src/client/public/itemledger/mstile-144x144.png +0 -0
  661. package/src/client/public/itemledger/mstile-150x150.png +0 -0
  662. package/src/client/public/itemledger/mstile-310x150.png +0 -0
  663. package/src/client/public/itemledger/mstile-310x310.png +0 -0
  664. package/src/client/public/itemledger/mstile-70x70.png +0 -0
  665. package/src/client/public/itemledger/sitemap +148 -0
  666. package/src/client/public/itemledger/yandex-browser-50x50.png +0 -0
  667. package/src/client/public/itemledger/yandex-browser-manifest.json +9 -0
  668. package/src/client/public/test/favicon.ico +0 -0
  669. package/src/client/public/test/sitemap +148 -0
  670. package/src/client/services/atlas-sprite-sheet/atlas-sprite-sheet.service.js +137 -0
  671. package/src/client/services/core/core.service.js +309 -0
  672. package/src/client/services/crypto/crypto.service.js +68 -0
  673. package/src/client/services/cyberia-action/cyberia-action.service.js +99 -0
  674. package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
  675. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +99 -0
  676. package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
  677. package/src/client/services/cyberia-entity/cyberia-entity.service.js +99 -0
  678. package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
  679. package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
  680. package/src/client/services/cyberia-instance/cyberia-instance.service.js +137 -0
  681. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +99 -0
  682. package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
  683. package/src/client/services/cyberia-map/cyberia-map.service.js +120 -0
  684. package/src/client/services/cyberia-quest/cyberia-quest.service.js +99 -0
  685. package/src/client/services/cyberia-quest-progress/cyberia-quest-progress.service.js +99 -0
  686. package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
  687. package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
  688. package/src/client/services/default/default.management.js +1015 -0
  689. package/src/client/services/default/default.service.js +99 -0
  690. package/src/client/services/document/document.service.js +134 -0
  691. package/src/client/services/file/file.service.js +94 -0
  692. package/src/client/services/instance/instance.management.js +78 -0
  693. package/src/client/services/instance/instance.service.js +107 -0
  694. package/src/client/services/ipfs/ipfs.service.js +118 -0
  695. package/src/client/services/object-layer/object-layer.management.js +414 -0
  696. package/src/client/services/object-layer/object-layer.service.js +208 -0
  697. package/src/client/services/object-layer-render-frames/object-layer-render-frames.service.js +99 -0
  698. package/src/client/services/test/test.service.js +68 -0
  699. package/src/client/services/user/guest.service.js +86 -0
  700. package/src/client/services/user/user.management.js +51 -0
  701. package/src/client/services/user/user.service.js +106 -0
  702. package/src/client/ssr/RootDocument.js +237 -0
  703. package/src/client/ssr/body/404.js +77 -0
  704. package/src/client/ssr/body/500.js +76 -0
  705. package/src/client/ssr/body/CacheControl.js +117 -0
  706. package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +97 -0
  707. package/src/client/ssr/body/DefaultSplashScreen.js +97 -0
  708. package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
  709. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +90 -0
  710. package/src/client/ssr/head/CryptokoynScripts.js +4 -0
  711. package/src/client/ssr/head/Css.js +241 -0
  712. package/src/client/ssr/head/CyberiaPortalScripts.js +6 -0
  713. package/src/client/ssr/head/DefaultScripts.js +6 -0
  714. package/src/client/ssr/head/ItemledgerScripts.js +4 -0
  715. package/src/client/ssr/head/Microdata.js +11 -0
  716. package/src/client/ssr/head/Production.js +1 -0
  717. package/src/client/ssr/head/Pwa.js +146 -0
  718. package/src/client/ssr/head/PwaItemledger.js +197 -0
  719. package/src/client/ssr/head/Seo.js +15 -0
  720. package/src/client/ssr/head/UnderpostScripts.js +6 -0
  721. package/src/client/ssr/mailer/DefaultRecoverEmail.js +20 -0
  722. package/src/client/ssr/mailer/DefaultVerifyEmail.js +16 -0
  723. package/src/client/ssr/views/Cyberia404.js +328 -0
  724. package/src/client/ssr/views/CyberiaServerMetrics.js +1070 -0
  725. package/src/client/ssr/views/Maintenance.js +64 -0
  726. package/src/client/ssr/views/NoNetworkConnection.js +68 -0
  727. package/src/client/ssr/views/Test.js +199 -0
  728. package/src/client/sw/core.sw.js +274 -0
  729. package/src/client-builder/client-build-docs.js +493 -0
  730. package/src/client-builder/client-build-live.js +100 -0
  731. package/src/client-builder/client-build.js +1092 -0
  732. package/src/client-builder/client-dev-server.js +88 -0
  733. package/src/client-builder/client-formatted.js +170 -0
  734. package/src/client-builder/client-icons.js +108 -0
  735. package/src/client-builder/ssr.js +124 -0
  736. package/src/client.build.js +17 -0
  737. package/src/client.dev.js +21 -0
  738. package/src/db/DataBaseProvider.js +213 -0
  739. package/src/db/mariadb/MariaDB.js +72 -0
  740. package/src/db/mongo/MongoBootstrap.js +657 -0
  741. package/src/db/mongo/MongooseDB.js +199 -0
  742. package/src/grpc/cyberia/grpc-server.js +162 -0
  743. package/src/index.js +357 -0
  744. package/src/mailer/EmailRender.js +116 -0
  745. package/src/mailer/MailerProvider.js +213 -0
  746. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +413 -0
  747. package/src/projects/cyberia/besu-genesis-generator.js +1630 -0
  748. package/src/projects/cyberia/catalog-cyberia.js +90 -0
  749. package/src/projects/cyberia/gemini-client.js +175 -0
  750. package/src/projects/cyberia/generate-saga.js +2137 -0
  751. package/src/projects/cyberia/hot-reload-trigger.js +163 -0
  752. package/src/projects/cyberia/instance-data.js +719 -0
  753. package/src/projects/cyberia/ipfs-client.js +599 -0
  754. package/src/projects/cyberia/map-preview-generator.js +304 -0
  755. package/src/projects/cyberia/object-layer.js +978 -0
  756. package/src/projects/cyberia/semantic-layer-generator-floor.js +319 -0
  757. package/src/projects/cyberia/semantic-layer-generator-resource.js +259 -0
  758. package/src/projects/cyberia/semantic-layer-generator-skin.js +1164 -0
  759. package/src/projects/cyberia/semantic-layer-generator.js +753 -0
  760. package/src/projects/cyberia/shape-generator.js +1060 -0
  761. package/src/projects/underpost/catalog-underpost.js +60 -0
  762. package/src/proxy.js +19 -0
  763. package/src/runtime/cyberia-client/Dockerfile +49 -0
  764. package/src/runtime/cyberia-client/Dockerfile.dev +75 -0
  765. package/src/runtime/cyberia-server/Dockerfile +43 -0
  766. package/src/runtime/cyberia-server/Dockerfile.dev +56 -0
  767. package/src/runtime/engine-cyberia/Dockerfile +161 -0
  768. package/src/runtime/engine-cyberia/Dockerfile.dev +161 -0
  769. package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
  770. package/src/runtime/engine-cyberia/compose.env +144 -0
  771. package/src/runtime/engine-cyberia/docker-compose.yml +434 -0
  772. package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
  773. package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
  774. package/src/runtime/engine-cyberia/nginx.conf +128 -0
  775. package/src/runtime/express/Dockerfile +45 -0
  776. package/src/runtime/express/Express.js +285 -0
  777. package/src/runtime/lampp/Dockerfile +62 -0
  778. package/src/runtime/lampp/Lampp.js +359 -0
  779. package/src/runtime/nginx/Nginx.js +250 -0
  780. package/src/runtime/wp/Dockerfile +62 -0
  781. package/src/runtime/wp/Wp.js +642 -0
  782. package/src/server/auth.js +713 -0
  783. package/src/server/backup.js +102 -0
  784. package/src/server/catalog.js +72 -0
  785. package/src/server/conf.js +2316 -0
  786. package/src/server/cron.js +476 -0
  787. package/src/server/crypto.js +210 -0
  788. package/src/server/data-query.js +461 -0
  789. package/src/server/dns.js +553 -0
  790. package/src/server/downloader.js +73 -0
  791. package/src/server/logger.js +229 -0
  792. package/src/server/middlewares.js +152 -0
  793. package/src/server/peer.js +75 -0
  794. package/src/server/process.js +299 -0
  795. package/src/server/proxy.js +139 -0
  796. package/src/server/runtime-status.js +252 -0
  797. package/src/server/runtime.js +183 -0
  798. package/src/server/start.js +272 -0
  799. package/src/server/tls.js +254 -0
  800. package/src/server/valkey.js +201 -0
  801. package/src/server.js +25 -0
  802. package/src/ws/IoInterface.js +129 -0
  803. package/src/ws/IoServer.js +68 -0
  804. package/src/ws/core/channels/core.ws.chat.js +68 -0
  805. package/src/ws/core/channels/core.ws.mailer.js +116 -0
  806. package/src/ws/core/channels/core.ws.stream.js +90 -0
  807. package/src/ws/core/core.ws.connection.js +41 -0
  808. package/src/ws/core/core.ws.emit.js +37 -0
  809. package/src/ws/core/core.ws.server.js +44 -0
  810. package/src/ws/default/channels/default.ws.main.js +57 -0
  811. package/src/ws/default/default.ws.connection.js +35 -0
  812. package/src/ws/default/default.ws.emit.js +14 -0
  813. package/src/ws/default/default.ws.server.js +39 -0
  814. package/test/api.test.js +53 -0
  815. package/test/crypto.test.js +117 -0
  816. package/test/cyberia-instance-conf-defaults.test.js +140 -0
  817. package/test/deploy-monitor.test.js +267 -0
  818. package/test/shape-generator.test.js +463 -0
  819. package/tsconfig.docs.json +15 -0
  820. package/typedoc.dd-cyberia.json +31 -0
  821. package/typedoc.json +31 -0
package/src/cli/db.js ADDED
@@ -0,0 +1,1533 @@
1
+ /**
2
+ * UnderpostDB CLI module
3
+ * @module src/cli/db.js
4
+ * @namespace UnderpostDB
5
+ * @description Manages database operations, backups, and cluster metadata for Kubernetes deployments.
6
+ * Supports MariaDB and MongoDB with import/export capabilities, Git integration, and multi-pod operations.
7
+ */
8
+
9
+ import { mergeFile, splitFileFactory, loadConfServerJson, resolveConfSecrets, loadConfInstances } from '../server/conf.js';
10
+ import { loggerFactory } from '../server/logger.js';
11
+ import { shellExec } from '../server/process.js';
12
+ import fs from 'fs-extra';
13
+ import { DataBaseProviderService } from '../db/DataBaseProvider.js';
14
+ import { loadReplicas, pathPortAssignmentFactory, loadCronDeployEnv } from '../server/conf.js';
15
+ import { MongoBootstrap } from '../db/mongo/MongoBootstrap.js';
16
+ import Underpost from '../index.js';
17
+ import { timer } from '../client/components/core/CommonJs.js';
18
+ const logger = loggerFactory(import.meta);
19
+
20
+ /**
21
+ * Constants for database operations
22
+ * @constant {number} MAX_BACKUP_RETENTION - Maximum number of backups to retain
23
+ * @memberof UnderpostDB
24
+ */
25
+ const MAX_BACKUP_RETENTION = 5;
26
+
27
+ /**
28
+ * @typedef {Object} DatabaseOptions
29
+ * @memberof UnderpostDB
30
+ * @property {boolean} [import=false] - Flag to import data from a backup
31
+ * @property {boolean} [export=false] - Flag to export data to a backup
32
+ * @property {string} [podName=''] - Comma-separated list of pod names or patterns
33
+ * @property {string} [ns='default'] - Kubernetes namespace
34
+ * @property {string} [collections=''] - Comma-separated list of collections to include
35
+ * @property {string} [outPath=''] - Output path for backup files
36
+ * @property {boolean} [drop=false] - Flag to drop the database before importing
37
+ * @property {boolean} [preserveUUID=false] - Flag to preserve UUIDs during import
38
+ * @property {boolean} [git=false] - Flag to enable Git integration
39
+ * @property {string} [hosts=''] - Comma-separated list of hosts to include
40
+ * @property {string} [paths=''] - Comma-separated list of paths to include
41
+ * @property {boolean} [allPods=false] - Flag to target all matching pods
42
+ * @property {boolean} [primaryPod=false] - Flag to automatically detect and use MongoDB primary pod
43
+ * @property {boolean} [stats=false] - Flag to display collection/table statistics
44
+ * @property {boolean} [forceClone=false] - Flag to force remove and re-clone cron backup repository
45
+ */
46
+
47
+ /**
48
+ * @typedef {Object} PodInfo
49
+ * @memberof UnderpostDB
50
+ * @property {string} NAME - Pod name
51
+ * @property {string} NAMESPACE - Pod namespace
52
+ * @property {string} NODE - Node where pod is running
53
+ * @property {string} STATUS - Pod status
54
+ * @property {string} [IP] - Pod IP address
55
+ */
56
+
57
+ /**
58
+ * @typedef {Object} DatabaseConfig
59
+ * @memberof UnderpostDB
60
+ * @property {string} provider - Database provider (mariadb, mongoose)
61
+ * @property {string} name - Database name
62
+ * @property {string} user - Database user
63
+ * @property {string} password - Database password
64
+ * @property {string} hostFolder - Host folder path
65
+ * @property {string} host - Host identifier
66
+ * @property {string} path - Path identifier
67
+ * @property {number} [currentBackupTimestamp] - Timestamp of current backup
68
+ */
69
+
70
+ /**
71
+ * @class UnderpostDB
72
+ * @description Manages database operations and backups for Kubernetes-based deployments.
73
+ * Provides comprehensive database management including import/export, multi-pod targeting,
74
+ * Git integration, and cluster metadata management.
75
+ */
76
+ /**
77
+ * UnderpostDB class for managing database operations and backups.
78
+ * @class UnderpostDB
79
+ * @memberof UnderpostDB
80
+ */
81
+ class UnderpostDB {
82
+ /**
83
+ * Static API object containing all database operation methods.
84
+ * @static
85
+ * @memberof UnderpostDB
86
+ */
87
+ static API = {
88
+ /**
89
+ * Helper: Resolves the latest backup timestamp from an existing backup directory.
90
+ * Scans the directory for numeric (epoch) sub-folders and returns the most recent one.
91
+ * @method _getLatestBackupTimestamp
92
+ * @memberof UnderpostDB
93
+ * @param {string} backupDir - Path to the host-folder backup directory.
94
+ * @return {string|null} The latest timestamp string, or null if none found.
95
+ */
96
+ _getLatestBackupTimestamp(backupDir) {
97
+ if (!fs.existsSync(backupDir)) return null;
98
+ const entries = fs.readdirSync(backupDir).filter((e) => /^\d+$/.test(e));
99
+ if (entries.length === 0) return null;
100
+ return entries.sort((a, b) => parseInt(b) - parseInt(a))[0];
101
+ },
102
+
103
+ /**
104
+ * Helper: Performs MariaDB import operation.
105
+ * @method _importMariaDB
106
+ * @memberof UnderpostDB
107
+ * @param {Object} params - Import parameters.
108
+ * @param {PodInfo} params.pod - Target pod.
109
+ * @param {string} params.namespace - Namespace.
110
+ * @param {string} params.dbName - Database name.
111
+ * @param {string} params.user - Database user.
112
+ * @param {string} params.password - Database password.
113
+ * @param {string} params.sqlPath - SQL file path.
114
+ * @return {boolean} Success status.
115
+ */
116
+ _importMariaDB({ pod, namespace, dbName, user, password, sqlPath }) {
117
+ try {
118
+ const podName = pod.NAME;
119
+ const containerSqlPath = `/${dbName}.sql`;
120
+
121
+ logger.info('Importing MariaDB database', { podName, dbName });
122
+
123
+ // Always ensure the database exists first — required for WP even when no backup is available
124
+ Underpost.kubectl.run(
125
+ `kubectl exec -n ${namespace} -i ${podName} -- mariadb -p${password} -e 'CREATE DATABASE IF NOT EXISTS ${dbName};'`,
126
+ { context: `create database ${dbName}` },
127
+ );
128
+
129
+ // If no SQL file is available, the empty database is enough — return early
130
+ if (!sqlPath || !fs.existsSync(sqlPath)) {
131
+ logger.warn('No SQL backup file found — empty database ensured', { podName, dbName, sqlPath });
132
+ return true;
133
+ }
134
+
135
+ // Remove existing SQL file in container
136
+ Underpost.kubectl.exec({
137
+ podName,
138
+ namespace,
139
+ command: `rm -rf ${containerSqlPath}`,
140
+ });
141
+
142
+ // Copy SQL file to pod
143
+ if (
144
+ !Underpost.kubectl.cpTo({
145
+ sourcePath: sqlPath,
146
+ podName,
147
+ namespace,
148
+ destPath: containerSqlPath,
149
+ })
150
+ ) {
151
+ return false;
152
+ }
153
+
154
+ // Import SQL file
155
+ const importCmd = `mariadb -u ${user} -p${password} ${dbName} < ${containerSqlPath}`;
156
+ Underpost.kubectl.exec({ podName, namespace, command: importCmd });
157
+
158
+ logger.info('Successfully imported MariaDB database', { podName, dbName });
159
+ return true;
160
+ } catch (error) {
161
+ logger.error('MariaDB import failed', { podName: pod.NAME, dbName, error: error.message });
162
+ return false;
163
+ }
164
+ },
165
+
166
+ /**
167
+ * Helper: Performs MariaDB export operation.
168
+ * @method _exportMariaDB
169
+ * @memberof UnderpostDB
170
+ * @param {Object} params - Export parameters.
171
+ * @param {PodInfo} params.pod - Source pod.
172
+ * @param {string} params.namespace - Namespace.
173
+ * @param {string} params.dbName - Database name.
174
+ * @param {string} params.user - Database user.
175
+ * @param {string} params.password - Database password.
176
+ * @param {string} params.outputPath - Output file path.
177
+ * @return {Promise<boolean>} A promise that resolves with success status.
178
+ */
179
+ async _exportMariaDB({ pod, namespace, dbName, user, password, outputPath }) {
180
+ try {
181
+ const podName = pod.NAME;
182
+ const containerSqlPath = `/home/${dbName}.sql`;
183
+
184
+ logger.info('Exporting MariaDB database', { podName, dbName });
185
+
186
+ // Remove existing SQL file in container
187
+ Underpost.kubectl.exec({
188
+ podName,
189
+ namespace,
190
+ command: `rm -rf ${containerSqlPath}`,
191
+ });
192
+
193
+ // Dump database
194
+ const dumpCmd = `mariadb-dump --user=${user} --password=${password} --lock-tables ${dbName} > ${containerSqlPath}`;
195
+ Underpost.kubectl.exec({ podName, namespace, command: dumpCmd });
196
+
197
+ // Copy SQL file from pod
198
+ if (
199
+ !Underpost.kubectl.cpFrom({
200
+ podName,
201
+ namespace,
202
+ sourcePath: containerSqlPath,
203
+ destPath: outputPath,
204
+ })
205
+ ) {
206
+ return false;
207
+ }
208
+
209
+ // Split file if it exists
210
+ if (fs.existsSync(outputPath)) {
211
+ await splitFileFactory(dbName, outputPath);
212
+ }
213
+
214
+ logger.info('Successfully exported MariaDB database', { podName, dbName, outputPath });
215
+ return true;
216
+ } catch (error) {
217
+ logger.error('MariaDB export failed', { podName: pod.NAME, dbName, error: error.message });
218
+ return false;
219
+ }
220
+ },
221
+
222
+ /**
223
+ * Helper: Performs MongoDB import operation.
224
+ * @method _importMongoDB
225
+ * @memberof UnderpostDB
226
+ * @param {Object} params - Import parameters.
227
+ * @param {PodInfo} params.pod - Target pod.
228
+ * @param {string} params.namespace - Namespace.
229
+ * @param {string} params.dbName - Database name.
230
+ * @param {string} params.bsonPath - BSON directory path.
231
+ * @param {boolean} params.drop - Whether to drop existing database.
232
+ * @param {boolean} params.preserveUUID - Whether to preserve UUIDs.
233
+ * @param {string} [params.user=''] - MongoDB username for authenticated restore.
234
+ * @param {string} [params.password=''] - MongoDB password for authenticated restore.
235
+ * @param {string} [params.authDatabase='admin'] - Auth database for restore command.
236
+ * @return {boolean} Success status.
237
+ */
238
+ _importMongoDB({ pod, namespace, dbName, bsonPath, drop, preserveUUID, user = '', password = '', authDatabase = 'admin' }) {
239
+ try {
240
+ const podName = pod.NAME;
241
+ const containerBsonPath = `/${dbName}`;
242
+
243
+ logger.info('Importing MongoDB database', { podName, dbName });
244
+
245
+ // If no BSON directory is available, skip — MongoDB creates the DB on first write
246
+ if (!bsonPath || !fs.existsSync(bsonPath)) {
247
+ logger.warn('No BSON backup directory found — database will be created on first write', {
248
+ podName,
249
+ dbName,
250
+ bsonPath,
251
+ });
252
+ return true;
253
+ }
254
+
255
+ // Remove existing BSON directory in container
256
+ Underpost.kubectl.exec({
257
+ podName,
258
+ namespace,
259
+ command: `rm -rf ${containerBsonPath}`,
260
+ });
261
+
262
+ // Copy BSON directory to pod
263
+ if (
264
+ !Underpost.kubectl.cpTo({
265
+ sourcePath: bsonPath,
266
+ podName,
267
+ namespace,
268
+ destPath: containerBsonPath,
269
+ })
270
+ ) {
271
+ return false;
272
+ }
273
+
274
+ // Restore database
275
+ const authFlags = user && password
276
+ ? ` --username ${JSON.stringify(user)} --password ${JSON.stringify(password)} --authenticationDatabase ${JSON.stringify(authDatabase)}`
277
+ : '';
278
+ const restoreCmd = `mongorestore -d ${dbName} ${containerBsonPath}${drop ? ' --drop' : ''}${preserveUUID ? ' --preserveUUID' : ''}${authFlags}`;
279
+ Underpost.kubectl.exec({ podName, namespace, command: restoreCmd });
280
+
281
+ logger.info('Successfully imported MongoDB database', { podName, dbName });
282
+ return true;
283
+ } catch (error) {
284
+ logger.error('MongoDB import failed', { podName: pod.NAME, dbName, error: error.message });
285
+ return false;
286
+ }
287
+ },
288
+
289
+ /**
290
+ * Helper: Performs MongoDB export operation.
291
+ * @method _exportMongoDB
292
+ * @memberof UnderpostDB
293
+ * @param {Object} params - Export parameters.
294
+ * @param {PodInfo} params.pod - Source pod.
295
+ * @param {string} params.namespace - Namespace.
296
+ * @param {string} params.dbName - Database name.
297
+ * @param {string} params.outputPath - Output directory path.
298
+ * @param {string} [params.collections=''] - Comma-separated collection list.
299
+ * @param {string} [params.user=''] - MongoDB username for authenticated dump.
300
+ * @param {string} [params.password=''] - MongoDB password for authenticated dump.
301
+ * @param {string} [params.authDatabase='admin'] - Auth database for dump command.
302
+ * @return {boolean} Success status.
303
+ */
304
+ _exportMongoDB({ pod, namespace, dbName, outputPath, collections = '', user = '', password = '', authDatabase = 'admin' }) {
305
+ try {
306
+ const podName = pod.NAME;
307
+ const containerBsonPath = `/${dbName}`;
308
+
309
+ logger.info('Exporting MongoDB database', { podName, dbName, collections });
310
+
311
+ // Remove existing BSON directory in container
312
+ Underpost.kubectl.exec({
313
+ podName,
314
+ namespace,
315
+ command: `rm -rf ${containerBsonPath}`,
316
+ });
317
+
318
+ // Dump database or specific collections
319
+ const authFlags = user && password
320
+ ? ` --username ${JSON.stringify(user)} --password ${JSON.stringify(password)} --authenticationDatabase ${JSON.stringify(authDatabase)}`
321
+ : '';
322
+
323
+ if (collections) {
324
+ const collectionList = collections.split(',').map((c) => c.trim());
325
+ for (const collection of collectionList) {
326
+ const dumpCmd = `mongodump -d ${dbName} --collection ${collection} -o /${authFlags}`;
327
+ Underpost.kubectl.exec({ podName, namespace, command: dumpCmd });
328
+ }
329
+ } else {
330
+ const dumpCmd = `mongodump -d ${dbName} -o /${authFlags}`;
331
+ Underpost.kubectl.exec({ podName, namespace, command: dumpCmd });
332
+ }
333
+
334
+ // Copy BSON directory from pod
335
+ if (
336
+ !Underpost.kubectl.cpFrom({
337
+ podName,
338
+ namespace,
339
+ sourcePath: containerBsonPath,
340
+ destPath: outputPath,
341
+ })
342
+ ) {
343
+ return false;
344
+ }
345
+
346
+ logger.info('Successfully exported MongoDB database', { podName, dbName, outputPath });
347
+ return true;
348
+ } catch (error) {
349
+ logger.error('MongoDB export failed', { podName: pod.NAME, dbName, error: error.message });
350
+ return false;
351
+ }
352
+ },
353
+
354
+ /**
355
+ * Helper: Gets MongoDB collection statistics.
356
+ * @method _getMongoStats
357
+ * @memberof UnderpostDB
358
+ * @param {Object} params - Parameters.
359
+ * @param {string} params.podName - Pod name.
360
+ * @param {string} params.namespace - Namespace.
361
+ * @param {string} params.dbName - Database name.
362
+ * @param {string} [params.user=''] - MongoDB username for authenticated stats query.
363
+ * @param {string} [params.password=''] - MongoDB password for authenticated stats query.
364
+ * @param {string} [params.authDatabase='admin'] - Auth database for stats query.
365
+ * @return {Object|null} Collection statistics or null on error.
366
+ */
367
+ _getMongoStats({ podName, namespace, dbName, user = '', password = '', authDatabase = 'admin' }) {
368
+ try {
369
+ logger.info('Getting MongoDB collection statistics', { podName, dbName });
370
+
371
+ // Use db.getSiblingDB() instead of 'use' command
372
+ const script = `db.getSiblingDB('${dbName}').getCollectionNames().map(function(c) { return { collection: c, count: db.getSiblingDB('${dbName}')[c].countDocuments() }; })`;
373
+
374
+ // Execute the script
375
+ const authFlags = user && password
376
+ ? ` --authenticationDatabase ${JSON.stringify(authDatabase)} -u ${JSON.stringify(user)} -p ${JSON.stringify(password)}`
377
+ : '';
378
+ const command = `sudo kubectl exec -n ${namespace} -i ${podName} -- mongosh --quiet${authFlags} --eval "${script}"`;
379
+ const output = shellExec(command, { stdout: true, silent: true, silentOnError: true });
380
+
381
+ if (!output || output.trim() === '') {
382
+ logger.warn('No collections found or empty output');
383
+ return null;
384
+ }
385
+
386
+ // Clean the output: remove newlines, handle EJSON format, replace single quotes with double quotes
387
+ let cleanedOutput = output
388
+ .trim()
389
+ .replace(/\n/g, '')
390
+ .replace(/\s+/g, ' ')
391
+ .replace(/NumberLong\("(\d+)"\)/g, '$1')
392
+ .replace(/NumberLong\((\d+)\)/g, '$1')
393
+ .replace(/NumberInt\("(\d+)"\)/g, '$1')
394
+ .replace(/NumberInt\((\d+)\)/g, '$1')
395
+ .replace(/ISODate\("([^"]+)"\)/g, '"$1"')
396
+ .replace(/'/g, '"')
397
+ .replace(/(\w+):/g, '"$1":');
398
+
399
+ try {
400
+ const stats = JSON.parse(cleanedOutput);
401
+ logger.info('MongoDB statistics retrieved', { dbName, collections: stats.length });
402
+ return stats;
403
+ } catch (parseError) {
404
+ logger.error('Failed to parse MongoDB output', {
405
+ podName,
406
+ dbName,
407
+ error: parseError.message,
408
+ rawOutput: output.substring(0, 200),
409
+ cleanedOutput: cleanedOutput.substring(0, 200),
410
+ });
411
+ return null;
412
+ }
413
+ } catch (error) {
414
+ logger.error('Failed to get MongoDB statistics', { podName, dbName, error: error.message });
415
+ return null;
416
+ }
417
+ },
418
+
419
+ /**
420
+ * Helper: Gets MariaDB table statistics.
421
+ * @method _getMariaDBStats
422
+ * @memberof UnderpostDB
423
+ * @param {Object} params - Parameters.
424
+ * @param {string} params.podName - Pod name.
425
+ * @param {string} params.namespace - Namespace.
426
+ * @param {string} params.dbName - Database name.
427
+ * @param {string} params.user - Database user.
428
+ * @param {string} params.password - Database password.
429
+ * @return {Object|null} Table statistics or null on error.
430
+ */
431
+ _getMariaDBStats({ podName, namespace, dbName, user, password }) {
432
+ try {
433
+ logger.info('Getting MariaDB table statistics', { podName, dbName });
434
+
435
+ const command = `sudo kubectl exec -n ${namespace} -i ${podName} -- mariadb -u ${user} -p${password} ${dbName} -e "SELECT TABLE_NAME as 'table', TABLE_ROWS as 'count' FROM information_schema.TABLES WHERE TABLE_SCHEMA = '${dbName}' ORDER BY TABLE_NAME;" --skip-column-names --batch`;
436
+ const output = shellExec(command, { stdout: true, silent: true, disableLog: true, silentOnError: true });
437
+
438
+ if (!output || output.trim() === '') {
439
+ logger.warn('No tables found or empty output');
440
+ return null;
441
+ }
442
+
443
+ // Parse the output (tab-separated values)
444
+ const lines = output.trim().split('\n');
445
+ const stats = lines.map((line) => {
446
+ const [table, count] = line.split('\t');
447
+ return { table, count: parseInt(count) || 0 };
448
+ });
449
+
450
+ logger.info('MariaDB statistics retrieved', { dbName, tables: stats.length });
451
+ return stats;
452
+ } catch (error) {
453
+ logger.error('Failed to get MariaDB statistics', { podName, dbName, error: error.message });
454
+ return null;
455
+ }
456
+ },
457
+
458
+ /**
459
+ * Helper: Displays database statistics in table format.
460
+ * @method _displayStats
461
+ * @memberof UnderpostDB
462
+ * @param {Object} params - Parameters.
463
+ * @param {string} params.provider - Database provider.
464
+ * @param {string} params.dbName - Database name.
465
+ * @param {Array<Object>} params.stats - Statistics array.
466
+ * @return {void}
467
+ */
468
+ _displayStats({ provider, dbName, stats }) {
469
+ if (!stats || stats.length === 0) {
470
+ logger.warn('No statistics to display', { provider, dbName });
471
+ return;
472
+ }
473
+
474
+ const title = provider === 'mongoose' ? 'Collections' : 'Tables';
475
+ const itemKey = provider === 'mongoose' ? 'collection' : 'table';
476
+
477
+ console.log('\n' + '='.repeat(70));
478
+ console.log(`DATABASE: ${dbName} (${provider.toUpperCase()})`);
479
+ console.log('='.repeat(70));
480
+ console.log(`${title.padEnd(50)} ${'Documents/Rows'.padStart(18)}`);
481
+ console.log('-'.repeat(70));
482
+
483
+ let totalCount = 0;
484
+ stats.forEach((item) => {
485
+ const name = item[itemKey] || 'Unknown';
486
+ const count = item.count || 0;
487
+ totalCount += count;
488
+ console.log(`${name.padEnd(50)} ${count.toString().padStart(18)}`);
489
+ });
490
+
491
+ console.log('-'.repeat(70));
492
+ console.log(`${'TOTAL'.padEnd(50)} ${totalCount.toString().padStart(18)}`);
493
+ console.log('='.repeat(70) + '\n');
494
+ },
495
+
496
+
497
+
498
+ /**
499
+ * Main callback: Initiates database backup workflow.
500
+ * Orchestrates the backup process for multiple deployments, handling
501
+ * database connections, backup storage, and optional Git integration for version control.
502
+ * Supports targeting multiple specific pods, nodes, and namespaces with advanced filtering.
503
+ * @method callback
504
+ * @memberof UnderpostDB
505
+ * @param {string} [deployList='default'] - Comma-separated list of deployment IDs.
506
+ * @param {Object} [options={}] - Backup options.
507
+ * @param {boolean} [options.import=false] - Whether to perform import operation.
508
+ * @param {boolean} [options.export=false] - Whether to perform export operation.
509
+ * @param {string} [options.podName=''] - Comma-separated pod name patterns to target.
510
+ * @param {string} [options.ns='default'] - Kubernetes namespace.
511
+ * @param {string} [options.collections=''] - Comma-separated MongoDB collections for export.
512
+ * @param {string} [options.outPath=''] - Output path for backups.
513
+ * @param {boolean} [options.drop=false] - Whether to drop existing database on import.
514
+ * @param {boolean} [options.preserveUUID=false] - Whether to preserve UUIDs on MongoDB import.
515
+ * @param {boolean} [options.git=false] - Whether to use Git for backup versioning.
516
+ * @param {string} [options.hosts=''] - Comma-separated list of hosts to filter databases.
517
+ * @param {string} [options.paths=''] - Comma-separated list of paths to filter databases.
518
+ * @param {boolean} [options.allPods=false] - Whether to target all pods in deployment.
519
+ * @param {boolean} [options.primaryPod=false] - Whether to target MongoDB primary pod only.
520
+ * @param {string} [options.primaryPodEnsure=''] - Pod name to ensure MongoDB primary pod is running.
521
+ * @param {boolean} [options.stats=false] - Whether to display database statistics.
522
+ * @param {number} [options.macroRollbackExport=1] - Number of commits to rollback in macro export.
523
+ * @param {boolean} [options.forceClone=false] - Whether to force re-clone Git repository.
524
+ * @param {boolean} [options.cleanFsCollection=false] - Clean orphaned File documents flag.
525
+ * @param {boolean} [options.cleanFsDryRun=false] - Dry run mode flag (use with cleanFsCollection).
526
+ * @param {boolean} [options.dev=false] - Development mode flag.
527
+ * @param {boolean} [options.k3s=false] - k3s cluster flag.
528
+ * @param {boolean} [options.kubeadm=false] - kubeadm cluster flag.
529
+ * @param {boolean} [options.kind=false] - kind cluster flag.
530
+ * @param {boolean} [options.repoBackup=false] - Backs up repositories (git commit+push) inside deployment pods via kubectl exec.
531
+ * @return {Promise<void>} Resolves when operation is complete.
532
+ */
533
+ async callback(
534
+ deployList = 'default',
535
+ options = {
536
+ import: false,
537
+ export: false,
538
+ podName: '',
539
+ ns: 'default',
540
+ collections: '',
541
+ outPath: '',
542
+ drop: false,
543
+ preserveUUID: false,
544
+ git: false,
545
+ hosts: '',
546
+ paths: '',
547
+ allPods: false,
548
+ primaryPod: false,
549
+ primaryPodEnsure: '',
550
+ stats: false,
551
+ macroRollbackExport: 1,
552
+ forceClone: false,
553
+ cleanFsCollection: false,
554
+ cleanFsDryRun: false,
555
+ dev: false,
556
+ k3s: false,
557
+ kubeadm: false,
558
+ kind: false,
559
+ repoBackup: false,
560
+ },
561
+ ) {
562
+ // Ensure engine-private is available (clone if inside a deployment
563
+ // container where globalSecretClean has already removed it).
564
+ const firstDeployId = deployList !== 'dd' ? deployList.split(',')[0].trim() : '';
565
+ try {
566
+ loadCronDeployEnv();
567
+ const newBackupTimestamp = new Date().getTime();
568
+ const namespace = options.ns && typeof options.ns === 'string' ? options.ns : 'default';
569
+
570
+ if (deployList === 'dd') deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
571
+
572
+ // Handle repository backup (git commit+push inside deployment pod)
573
+ if (options.repoBackup) {
574
+ const namespace = options.ns && typeof options.ns === 'string' ? options.ns : 'default';
575
+ for (const _deployId of deployList.split(',')) {
576
+ const deployId = _deployId.trim();
577
+ if (!deployId) continue;
578
+ logger.info('Starting pod repository backup', { deployId, namespace });
579
+ Underpost.repo.backupPodRepositories({
580
+ deployId,
581
+ namespace,
582
+ env: options.dev ? 'development' : 'production',
583
+ });
584
+ }
585
+ return;
586
+ }
587
+
588
+ // Handle clean-fs-collection operation
589
+ if (options.cleanFsCollection || options.cleanFsDryRun) {
590
+ logger.info('Starting File collection cleanup operation', { deployList });
591
+ await Underpost.db.cleanFsCollection(deployList, {
592
+ hosts: options.hosts,
593
+ paths: options.paths,
594
+ dryRun: options.cleanFsDryRun,
595
+ });
596
+ return;
597
+ }
598
+
599
+ logger.info('Starting database operation', {
600
+ deployList,
601
+ namespace,
602
+ import: options.import,
603
+ export: options.export,
604
+ });
605
+
606
+ if (options.primaryPodEnsure) {
607
+ const primaryPodName = MongoBootstrap.getPrimaryPodName({
608
+ namespace,
609
+ podName: options.primaryPodEnsure,
610
+ username: process.env.MONGODB_USERNAME || process.env.DB_USER || '',
611
+ password: process.env.MONGODB_PASSWORD || process.env.DB_PASSWORD || '',
612
+ authDatabase: process.env.MONGODB_AUTH_DB || 'admin',
613
+ });
614
+ if (!primaryPodName) {
615
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
616
+ const baseClusterCommand = options.dev ? ' --dev' : '';
617
+ let clusterFlag = options.k3s ? ' --k3s' : options.kubeadm ? ' --kubeadm' : '';
618
+ shellExec(`${baseCommand} cluster${baseClusterCommand}${clusterFlag} --mongodb`);
619
+ }
620
+ return;
621
+ }
622
+
623
+ // Track processed repositories to avoid duplicate Git operations
624
+ const processedRepos = new Set();
625
+ // Track processed host+path combinations to avoid duplicates
626
+ const processedHostPaths = new Set();
627
+
628
+ for (const _deployId of deployList.split(',')) {
629
+ const deployId = _deployId.trim();
630
+ if (!deployId) continue;
631
+
632
+ logger.info('Processing deployment', { deployId });
633
+
634
+ /** @type {Object.<string, Object.<string, DatabaseConfig>>} */
635
+ const dbs = {};
636
+ const repoName = `engine-${deployId.includes('dd-') ? deployId.split('dd-')[1] : deployId}-cron-backups`;
637
+
638
+ // Load server configuration
639
+ const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
640
+ if (!fs.existsSync(confServerPath)) {
641
+ logger.error('Configuration file not found', { path: confServerPath });
642
+ continue;
643
+ }
644
+
645
+ const confServer = loadConfServerJson(confServerPath, { resolve: true });
646
+
647
+ // Build database configuration map
648
+ for (const host of Object.keys(confServer)) {
649
+ for (const path of Object.keys(confServer[host])) {
650
+ const { db } = confServer[host][path];
651
+ if (db) {
652
+ const { provider, name, user, password } = db;
653
+ if (!dbs[provider]) dbs[provider] = {};
654
+
655
+ if (!(name in dbs[provider])) {
656
+ dbs[provider][name] = {
657
+ user,
658
+ password,
659
+ hostFolder: host + path.replaceAll('/', '-'),
660
+ host,
661
+ path,
662
+ };
663
+ }
664
+ }
665
+ }
666
+ }
667
+
668
+ // Handle Git operations - execute only once per repository
669
+ if (!processedRepos.has(repoName)) {
670
+ logger.info('Processing Git operations for repository', { repoName, deployId });
671
+ if (options.git === true) {
672
+ Underpost.repo.manageBackupRepo({ repoName, operation: 'clone', forceClone: options.forceClone });
673
+ Underpost.repo.manageBackupRepo({ repoName, operation: 'pull' });
674
+ }
675
+
676
+ if (options.macroRollbackExport) {
677
+ // Only clone if not already done by git option above
678
+ if (options.git !== true) {
679
+ Underpost.repo.manageBackupRepo({ repoName, operation: 'clone', forceClone: options.forceClone });
680
+ Underpost.repo.manageBackupRepo({ repoName, operation: 'pull' });
681
+ }
682
+
683
+ const nCommits = parseInt(options.macroRollbackExport);
684
+ const repoPath = `../${repoName}`;
685
+ const username = process.env.GITHUB_USERNAME;
686
+
687
+ if (fs.existsSync(repoPath) && username) {
688
+ logger.info('Executing macro rollback export', { repoName, nCommits });
689
+ shellExec(`cd ${repoPath} && underpost cmt . reset ${nCommits}`);
690
+ shellExec(`cd ${repoPath} && git reset`);
691
+ shellExec(`cd ${repoPath} && git checkout .`);
692
+ shellExec(`cd ${repoPath} && git clean -f -d`);
693
+ shellExec(`cd ${repoPath} && underpost push . ${username}/${repoName} -f`);
694
+ } else {
695
+ if (!username) logger.error('GITHUB_USERNAME environment variable not set');
696
+ logger.warn('Repository not found for macro rollback', { repoPath });
697
+ }
698
+ }
699
+
700
+ processedRepos.add(repoName);
701
+ logger.info('Repository marked as processed', { repoName });
702
+ } else {
703
+ logger.info('Skipping Git operations for already processed repository', { repoName, deployId });
704
+ }
705
+
706
+ // Process each database provider
707
+ for (const provider of Object.keys(dbs)) {
708
+ for (const dbName of Object.keys(dbs[provider])) {
709
+ const { hostFolder, user, password, host, path } = dbs[provider][dbName];
710
+
711
+ // Create unique identifier for host+path combination
712
+ const hostPathKey = `${deployId}:${host}:${path}`;
713
+
714
+ // Skip if this host+path combination was already processed
715
+ if (processedHostPaths.has(hostPathKey)) {
716
+ logger.info('Skipping already processed host/path', { dbName, host, path, deployId });
717
+ continue;
718
+ }
719
+
720
+ // Filter by hosts and paths if specified
721
+ if (
722
+ (options.hosts &&
723
+ !options.hosts
724
+ .split(',')
725
+ .map((h) => h.trim())
726
+ .includes(host)) ||
727
+ (options.paths &&
728
+ !options.paths
729
+ .split(',')
730
+ .map((p) => p.trim())
731
+ .includes(path))
732
+ ) {
733
+ logger.info('Skipping database due to host/path filter', { dbName, host, path });
734
+ continue;
735
+ }
736
+
737
+ if (!hostFolder) {
738
+ logger.warn('No hostFolder defined for database', { dbName, provider });
739
+ continue;
740
+ }
741
+
742
+ logger.info('Processing database', { hostFolder, provider, dbName, deployId });
743
+
744
+ const latestBackupTimestamp = Underpost.db._getLatestBackupTimestamp(`../${repoName}/${hostFolder}`);
745
+
746
+ dbs[provider][dbName].currentBackupTimestamp = latestBackupTimestamp;
747
+
748
+ const currentTimestamp = latestBackupTimestamp || newBackupTimestamp;
749
+ const sqlContainerPath = `/home/${dbName}.sql`;
750
+ const fromPartsPath = `../${repoName}/${hostFolder}/${currentTimestamp}/${dbName}-parths.json`;
751
+ const toSqlPath = `../${repoName}/${hostFolder}/${currentTimestamp}/${dbName}.sql`;
752
+ const toNewSqlPath = `../${repoName}/${hostFolder}/${newBackupTimestamp}/${dbName}.sql`;
753
+ const toBsonPath = `../${repoName}/${hostFolder}/${currentTimestamp}/${dbName}`;
754
+ const toNewBsonPath = `../${repoName}/${hostFolder}/${newBackupTimestamp}/${dbName}`;
755
+
756
+ // Merge split SQL files if needed for import
757
+ if (options.import === true && fs.existsSync(fromPartsPath) && !fs.existsSync(toSqlPath)) {
758
+ const names = JSON.parse(fs.readFileSync(fromPartsPath, 'utf8')).map((_path) => {
759
+ return `../${repoName}/${hostFolder}/${currentTimestamp}/${_path.split('/').pop()}`;
760
+ });
761
+ logger.info('Merging backup parts', { fromPartsPath, toSqlPath, parts: names.length });
762
+ await mergeFile(names, toSqlPath);
763
+ }
764
+
765
+ // Get target pods based on provider and options
766
+ let targetPods = [];
767
+ const podCriteria = {
768
+ podNames: options.podName,
769
+ namespace,
770
+ deployId: provider === 'mariadb' ? 'mariadb' : 'mongo',
771
+ };
772
+
773
+ targetPods = Underpost.kubectl.getFilteredPods(podCriteria);
774
+
775
+ // Fallback to default if no custom pods specified
776
+ if (targetPods.length === 0 && !options.podName) {
777
+ const defaultPods = Underpost.kubectl.get(
778
+ provider === 'mariadb' ? 'mariadb' : 'mongo',
779
+ 'pods',
780
+ namespace,
781
+ );
782
+ console.log('defaultPods', defaultPods);
783
+ targetPods = defaultPods;
784
+ }
785
+
786
+ if (targetPods.length === 0) {
787
+ logger.warn('No pods found matching criteria', { provider, criteria: podCriteria });
788
+ continue;
789
+ }
790
+
791
+ // Handle primary pod detection for MongoDB
792
+ let podsToProcess = [];
793
+ if (provider === 'mongoose' && !options.allPods) {
794
+ // For MongoDB, always use primary pod unless allPods is true
795
+ if (!targetPods || targetPods.length === 0) {
796
+ logger.warn('No MongoDB pods available to check for primary');
797
+ podsToProcess = [];
798
+ } else {
799
+ const firstPod = targetPods[0].NAME;
800
+ const primaryPodName = MongoBootstrap.getPrimaryPodName({
801
+ namespace,
802
+ podName: firstPod,
803
+ username: user,
804
+ password,
805
+ authDatabase: 'admin',
806
+ });
807
+
808
+ if (primaryPodName) {
809
+ const primaryPod = targetPods.find((p) => p.NAME === primaryPodName);
810
+ if (primaryPod) {
811
+ podsToProcess = [primaryPod];
812
+ logger.info('Using MongoDB primary pod', { primaryPod: primaryPodName });
813
+ } else {
814
+ logger.warn('Primary pod not in filtered list, using first pod', { primaryPodName });
815
+ podsToProcess = [targetPods[0]];
816
+ }
817
+ } else {
818
+ logger.warn('Could not detect primary pod, using first pod');
819
+ podsToProcess = [targetPods[0]];
820
+ }
821
+ }
822
+ } else {
823
+ // For MariaDB or when allPods is true, limit to first pod unless allPods is true
824
+ podsToProcess = options.allPods === true ? targetPods : [targetPods[0]];
825
+ }
826
+
827
+ logger.info(`Processing ${podsToProcess.length} pod(s) for ${provider}`, {
828
+ dbName,
829
+ pods: podsToProcess.map((p) => p.NAME),
830
+ });
831
+
832
+ let exportSucceeded = false;
833
+
834
+ // Process each pod
835
+ for (const pod of podsToProcess) {
836
+ logger.info('Processing pod', { podName: pod.NAME, node: pod.NODE, status: pod.STATUS });
837
+
838
+ switch (provider) {
839
+ case 'mariadb': {
840
+ if (options.stats === true) {
841
+ const stats = Underpost.db._getMariaDBStats({
842
+ podName: pod.NAME,
843
+ namespace,
844
+ dbName,
845
+ user,
846
+ password,
847
+ });
848
+ if (stats) {
849
+ Underpost.db._displayStats({ provider, dbName, stats });
850
+ }
851
+ }
852
+
853
+ if (options.import === true) {
854
+ Underpost.db._importMariaDB({
855
+ pod,
856
+ namespace,
857
+ dbName,
858
+ user,
859
+ password,
860
+ sqlPath: toSqlPath,
861
+ });
862
+ }
863
+
864
+ if (options.export === true) {
865
+ const outputPath = options.outPath || toNewSqlPath;
866
+ const success = await Underpost.db._exportMariaDB({
867
+ pod,
868
+ namespace,
869
+ dbName,
870
+ user,
871
+ password,
872
+ outputPath,
873
+ });
874
+ exportSucceeded = exportSucceeded || success;
875
+ }
876
+ break;
877
+ }
878
+
879
+ case 'mongoose': {
880
+ if (options.stats === true) {
881
+ const stats = Underpost.db._getMongoStats({
882
+ podName: pod.NAME,
883
+ namespace,
884
+ dbName,
885
+ user,
886
+ password,
887
+ authDatabase: 'admin',
888
+ });
889
+ if (stats) {
890
+ Underpost.db._displayStats({ provider, dbName, stats });
891
+ }
892
+ }
893
+
894
+ if (options.import === true) {
895
+ const bsonPath = options.outPath || toBsonPath;
896
+ Underpost.db._importMongoDB({
897
+ pod,
898
+ namespace,
899
+ dbName,
900
+ bsonPath,
901
+ drop: options.drop,
902
+ preserveUUID: options.preserveUUID,
903
+ user,
904
+ password,
905
+ authDatabase: 'admin',
906
+ });
907
+ }
908
+
909
+ if (options.export === true) {
910
+ const outputPath = options.outPath || toNewBsonPath;
911
+ const success = Underpost.db._exportMongoDB({
912
+ pod,
913
+ namespace,
914
+ dbName,
915
+ outputPath,
916
+ collections: options.collections,
917
+ user,
918
+ password,
919
+ authDatabase: 'admin',
920
+ });
921
+ exportSucceeded = exportSucceeded || success;
922
+ }
923
+ break;
924
+ }
925
+
926
+ default:
927
+ logger.warn('Unsupported database provider', { provider });
928
+ break;
929
+ }
930
+ }
931
+
932
+ if (options.export === true && exportSucceeded === true) {
933
+ Underpost.db._enforceBackupRetention(`../${repoName}/${hostFolder}`);
934
+ }
935
+
936
+ // Mark this host+path combination as processed
937
+ processedHostPaths.add(hostPathKey);
938
+ }
939
+ }
940
+
941
+ // Commit and push to Git if enabled - execute only once per repository
942
+ if (options.export === true && options.git === true && !processedRepos.has(`${repoName}-committed`)) {
943
+ const commitMessage = `${new Date(newBackupTimestamp).toLocaleDateString()} ${new Date(
944
+ newBackupTimestamp,
945
+ ).toLocaleTimeString()}`;
946
+ Underpost.repo.manageBackupRepo({ repoName, operation: 'commit', message: commitMessage });
947
+ Underpost.repo.manageBackupRepo({ repoName, operation: 'push' });
948
+ processedRepos.add(`${repoName}-committed`);
949
+ }
950
+ }
951
+
952
+ logger.info('Database operation completed successfully');
953
+ } catch (error) {
954
+ logger.error('Database operation failed', { error: error.message });
955
+ throw error;
956
+ }
957
+ },
958
+ /**
959
+ * Helper: Removes old timestamp backup folders and keeps only the newest ones.
960
+ * @method _enforceBackupRetention
961
+ * @memberof UnderpostDB
962
+ * @param {string} backupDir - Path to host-folder backup directory.
963
+ * @param {number} [maxRetention=MAX_BACKUP_RETENTION] - Maximum folders to keep.
964
+ * @return {number} Number of removed backup folders.
965
+ */
966
+ _enforceBackupRetention(backupDir, maxRetention = MAX_BACKUP_RETENTION) {
967
+ try {
968
+ if (!fs.existsSync(backupDir)) return 0;
969
+
970
+ const timestamps = fs
971
+ .readdirSync(backupDir)
972
+ .filter((entry) => /^\d+$/.test(entry))
973
+ .sort((a, b) => parseInt(b, 10) - parseInt(a, 10));
974
+
975
+ if (timestamps.length <= maxRetention) return 0;
976
+
977
+ const staleTimestamps = timestamps.slice(maxRetention);
978
+ staleTimestamps.forEach((timestamp) => {
979
+ fs.removeSync(`${backupDir}/${timestamp}`);
980
+ });
981
+
982
+ logger.info('Pruned old backup timestamp folders', {
983
+ backupDir,
984
+ kept: maxRetention,
985
+ removed: staleTimestamps.length,
986
+ removedTimestamps: staleTimestamps,
987
+ });
988
+
989
+ return staleTimestamps.length;
990
+ } catch (error) {
991
+ logger.error('Failed to enforce backup retention', { backupDir, maxRetention, error: error.message });
992
+ return 0;
993
+ }
994
+ },
995
+
996
+ /**
997
+ * Creates cluster metadata for the specified deployment.
998
+ * Loads database configuration and initializes cluster metadata including
999
+ * instances and cron jobs. This method populates the database with deployment information.
1000
+ * @method clusterMetadataFactory
1001
+ * @memberof UnderpostDB
1002
+ * @param {string} [deployId=process.env.DEFAULT_DEPLOY_ID] - The deployment ID.
1003
+ * @param {string} [host=process.env.DEFAULT_DEPLOY_HOST] - The host identifier.
1004
+ * @param {string} [path=process.env.DEFAULT_DEPLOY_PATH] - The path identifier.
1005
+ * @param {object} [options] - Options.
1006
+ * @param {boolean} [options.dev=false] - Development mode flag.
1007
+ * @return {Promise<void>} Resolves when metadata creation is complete.
1008
+ * @throws {Error} If database configuration is invalid or connection fails.
1009
+ */
1010
+ async clusterMetadataFactory(
1011
+ deployId = process.env.DEFAULT_DEPLOY_ID,
1012
+ host = process.env.DEFAULT_DEPLOY_HOST,
1013
+ path = process.env.DEFAULT_DEPLOY_PATH,
1014
+ options = { dev: false },
1015
+ ) {
1016
+ try {
1017
+ loadCronDeployEnv();
1018
+ deployId = deployId ? deployId : process.env.DEFAULT_DEPLOY_ID;
1019
+ host = host ? host : process.env.DEFAULT_DEPLOY_HOST;
1020
+ path = path ? path : process.env.DEFAULT_DEPLOY_PATH;
1021
+
1022
+ logger.info('Creating cluster metadata', { deployId, host, path });
1023
+
1024
+ const env = 'production';
1025
+ const deployListPath = './engine-private/deploy/dd.router';
1026
+
1027
+ if (!fs.existsSync(deployListPath)) {
1028
+ logger.error('Deploy router file not found', { path: deployListPath });
1029
+ throw new Error(`Deploy router file not found: ${deployListPath}`);
1030
+ }
1031
+
1032
+ const deployList = fs.readFileSync(deployListPath, 'utf8').split(',');
1033
+
1034
+ const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
1035
+ if (!fs.existsSync(confServerPath)) {
1036
+ logger.error('Server configuration not found', { path: confServerPath });
1037
+ throw new Error(`Server configuration not found: ${confServerPath}`);
1038
+ }
1039
+
1040
+ const { db } = loadConfServerJson(confServerPath, { resolve: true })[host][path];
1041
+
1042
+ const maxRetries = 5;
1043
+ const retryDelay = 3000;
1044
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
1045
+ try {
1046
+ await DataBaseProviderService.load({ apis: ['instance', 'cron'], host, path, db });
1047
+ break;
1048
+ } catch (err) {
1049
+ if (attempt === maxRetries) {
1050
+ logger.error('Failed to connect to database after retries', { attempts: maxRetries, error: err.message });
1051
+ throw err;
1052
+ }
1053
+ logger.warn('Database connection failed, retrying...', { attempt, maxRetries, error: err.message });
1054
+ await timer(retryDelay);
1055
+ }
1056
+ }
1057
+
1058
+ try {
1059
+ /** @type {import('../api/instance/instance.model.js').InstanceModel} */
1060
+ const Instance = DataBaseProviderService.getModel('instance', { host, path });
1061
+
1062
+ await Instance.deleteMany();
1063
+ logger.info('Cleared existing instance metadata');
1064
+
1065
+ for (const _deployId of deployList) {
1066
+ const deployId = _deployId.trim();
1067
+ if (!deployId) continue;
1068
+
1069
+ logger.info('Processing deployment for metadata', { deployId });
1070
+
1071
+ const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
1072
+ if (!fs.existsSync(confServerPath)) {
1073
+ logger.warn('Configuration not found for deployment', { deployId, path: confServerPath });
1074
+ continue;
1075
+ }
1076
+
1077
+ const confServer = loadReplicas(deployId, loadConfServerJson(confServerPath, { resolve: true }));
1078
+ const router = await Underpost.deploy.routerFactory(deployId, env);
1079
+ const pathPortAssignmentData = await pathPortAssignmentFactory(deployId, router, confServer);
1080
+
1081
+ for (const host of Object.keys(confServer)) {
1082
+ for (const { path, port } of pathPortAssignmentData[host]) {
1083
+ if (!confServer[host][path]) continue;
1084
+
1085
+ const { client, runtime, apis, peer } = confServer[host][path];
1086
+
1087
+ // Save main instance
1088
+ {
1089
+ const body = {
1090
+ deployId,
1091
+ host,
1092
+ path,
1093
+ port,
1094
+ client,
1095
+ runtime,
1096
+ apis,
1097
+ };
1098
+
1099
+ logger.info('Saving instance metadata', body);
1100
+ await new Instance(body).save();
1101
+ }
1102
+
1103
+ // Save peer instance if exists
1104
+ if (peer) {
1105
+ const body = {
1106
+ deployId,
1107
+ host,
1108
+ path: path === '/' ? '/peer' : `${path}/peer`,
1109
+ port: port + 1,
1110
+ runtime: 'nodejs',
1111
+ };
1112
+
1113
+ logger.info('Saving peer instance metadata', body);
1114
+ await new Instance(body).save();
1115
+ }
1116
+ }
1117
+ }
1118
+
1119
+ // Process additional instances
1120
+ const confInstancesPath = `./engine-private/conf/${deployId}/conf.instances.json`;
1121
+ if (fs.existsSync(confInstancesPath)) {
1122
+ // Expands multiInstance variants so each deployed instance is
1123
+ // registered (handles both the bare-array and { instances } shapes).
1124
+ const confInstances = loadConfInstances(deployId);
1125
+ for (const instance of confInstances) {
1126
+ const { id, host, path, fromPort, metadata } = instance;
1127
+ const { runtime } = metadata;
1128
+ const body = {
1129
+ deployId,
1130
+ host,
1131
+ path,
1132
+ port: fromPort,
1133
+ client: id,
1134
+ runtime,
1135
+ };
1136
+ logger.info('Saving additional instance metadata', body);
1137
+ await new Instance(body).save();
1138
+ }
1139
+ }
1140
+ }
1141
+ } catch (error) {
1142
+ logger.error('Failed to create instance metadata', { error: error.message });
1143
+ throw error;
1144
+ }
1145
+
1146
+ try {
1147
+ const cronDeployPath = './engine-private/deploy/dd.cron';
1148
+ if (!fs.existsSync(cronDeployPath)) {
1149
+ logger.warn('Cron deploy file not found', { path: cronDeployPath });
1150
+ return;
1151
+ }
1152
+
1153
+ const cronDeployId = fs.readFileSync(cronDeployPath, 'utf8').trim();
1154
+ const confCronPath = `./engine-private/conf/${cronDeployId}/conf.cron.json`;
1155
+
1156
+ if (!fs.existsSync(confCronPath)) {
1157
+ logger.warn('Cron configuration not found', { path: confCronPath });
1158
+ return;
1159
+ }
1160
+
1161
+ const confCron = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
1162
+
1163
+ await DataBaseProviderService.load({ apis: ['cron'], host, path, db });
1164
+
1165
+ /** @type {import('../api/cron/cron.model.js').CronModel} */
1166
+ const Cron = DataBaseProviderService.getModel('cron', { host, path });
1167
+
1168
+ await Cron.deleteMany();
1169
+ logger.info('Cleared existing cron metadata');
1170
+
1171
+ for (const jobId of Object.keys(confCron.jobs)) {
1172
+ const body = {
1173
+ jobId,
1174
+ deployId: Underpost.cron.getRelatedDeployIdList(jobId),
1175
+ expression: confCron.jobs[jobId].expression,
1176
+ enabled: confCron.jobs[jobId].enabled,
1177
+ };
1178
+ logger.info('Saving cron metadata', body);
1179
+ await new Cron(body).save();
1180
+ }
1181
+ } catch (error) {
1182
+ logger.error('Failed to create cron metadata', { error: error.message });
1183
+ }
1184
+
1185
+ await DataBaseProviderService.getProvider({ host, path }, 'mongoose').close();
1186
+ logger.info('Cluster metadata creation completed');
1187
+ } catch (error) {
1188
+ logger.error('Cluster metadata creation failed', { error: error.message });
1189
+ throw error;
1190
+ }
1191
+ },
1192
+
1193
+ /**
1194
+ * Cleans orphaned File references from database collections.
1195
+ * Iterates over all deploy-ids and checks if File documents are actually referenced
1196
+ * by other collections. Removes File documents that are not referenced anywhere.
1197
+ * @method cleanFsCollection
1198
+ * @memberof UnderpostDB
1199
+ * @param {string} [deployList='dd'] - Comma-separated list of deployment IDs.
1200
+ * @param {Object} [options={}] - Clean operation options.
1201
+ * @param {string} [options.hosts=''] - Comma-separated list of hosts to filter.
1202
+ * @param {string} [options.paths=''] - Comma-separated list of paths to filter.
1203
+ * @param {boolean} [options.dryRun=false] - If true, only reports what would be deleted.
1204
+ * @param {boolean} [options.dev=false] - Development mode flag.
1205
+ * @return {Promise<void>} Resolves when clean operation is complete.
1206
+ */
1207
+ async cleanFsCollection(
1208
+ deployList = 'dd',
1209
+ options = {
1210
+ hosts: '',
1211
+ paths: '',
1212
+ dryRun: false,
1213
+ dev: false,
1214
+ },
1215
+ ) {
1216
+ const firstDeployId = deployList !== 'dd' ? deployList.split(',')[0].trim() : '';
1217
+ try {
1218
+ loadCronDeployEnv();
1219
+ if (deployList === 'dd') deployList = fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8');
1220
+
1221
+ logger.info('Starting File collection cleanup', { deployList, options });
1222
+
1223
+ // Load file.ref.json to know which models reference File
1224
+ const fileRefPath = './src/api/file/file.ref.json';
1225
+ if (!fs.existsSync(fileRefPath)) {
1226
+ logger.error('file.ref.json not found', { path: fileRefPath });
1227
+ return;
1228
+ }
1229
+
1230
+ const fileRefData = JSON.parse(fs.readFileSync(fileRefPath, 'utf8'));
1231
+ logger.info('Loaded file reference configuration', { apis: fileRefData.length });
1232
+
1233
+ // Filter hosts and paths if specified
1234
+ const filterHosts = options.hosts ? options.hosts.split(',').map((h) => h.trim()) : [];
1235
+ const filterPaths = options.paths ? options.paths.split(',').map((p) => p.trim()) : [];
1236
+
1237
+ // Track all connections to close them at the end
1238
+ const connectionsToClose = [];
1239
+
1240
+ for (const _deployId of deployList.split(',')) {
1241
+ const deployId = _deployId.trim();
1242
+ if (!deployId) continue;
1243
+
1244
+ logger.info('Processing deployment for File cleanup', { deployId });
1245
+
1246
+ // Load server configuration
1247
+ const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
1248
+ if (!fs.existsSync(confServerPath)) {
1249
+ logger.error('Configuration file not found', { path: confServerPath });
1250
+ continue;
1251
+ }
1252
+
1253
+ const confServer = loadConfServerJson(confServerPath, { resolve: true });
1254
+
1255
+ // Process each host+path combination
1256
+ for (const host of Object.keys(confServer)) {
1257
+ if (filterHosts.length > 0 && !filterHosts.includes(host)) continue;
1258
+
1259
+ for (const path of Object.keys(confServer[host])) {
1260
+ if (filterPaths.length > 0 && !filterPaths.includes(path)) continue;
1261
+
1262
+ const { db, apis } = confServer[host][path];
1263
+ if (!db || !apis) continue;
1264
+
1265
+ // Check if 'file' api is in the apis list
1266
+ if (!apis.includes('file')) {
1267
+ logger.info('Skipping - no file api in configuration', { host, path });
1268
+ continue;
1269
+ }
1270
+
1271
+ // logger.info('Processing host+path with file api', { host, path, db: db.name });
1272
+
1273
+ try {
1274
+ // Connect to database with retry
1275
+ let dbProvider;
1276
+ for (let attempt = 1; attempt <= 3; attempt++) {
1277
+ try {
1278
+ dbProvider = await DataBaseProviderService.load({ apis, host, path, db });
1279
+ break;
1280
+ } catch (err) {
1281
+ if (attempt === 3) throw err;
1282
+ logger.warn('Database connection failed, retrying...', { attempt, host, path, error: err.message });
1283
+ await timer(3000);
1284
+ }
1285
+ }
1286
+ if (!dbProvider || !dbProvider.models) {
1287
+ logger.error('Failed to load database provider', { host, path });
1288
+ continue;
1289
+ }
1290
+
1291
+ const { models } = dbProvider;
1292
+
1293
+ // Track this connection for cleanup
1294
+ connectionsToClose.push({ host, path, dbProvider });
1295
+
1296
+ // Check if File model exists
1297
+ if (!models.File) {
1298
+ logger.warn('File model not loaded', { host, path });
1299
+ continue;
1300
+ }
1301
+
1302
+ // Get all File documents
1303
+ const allFiles = await models.File.find({}, '_id').lean();
1304
+ logger.info('Found File documents', { count: allFiles.length, host, path });
1305
+
1306
+ if (allFiles.length === 0) continue;
1307
+
1308
+ // Track which File IDs are referenced
1309
+ const referencedFileIds = new Set();
1310
+
1311
+ // Check each API from file.ref.json
1312
+ for (const refConfig of fileRefData) {
1313
+ const { api, model: modelFields } = refConfig;
1314
+
1315
+ // Check if this API is loaded in current context
1316
+ const modelName = api
1317
+ .split('-')
1318
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
1319
+ .join('');
1320
+ const Model = models[modelName];
1321
+
1322
+ if (!Model) {
1323
+ logger.debug('Model not loaded in current context', { api, modelName, host, path });
1324
+ continue;
1325
+ }
1326
+
1327
+ logger.info('Checking references in model', { api, modelName });
1328
+
1329
+ // Helper function to recursively check field references
1330
+ const checkFieldReferences = async (fieldPath, fieldConfig) => {
1331
+ for (const [fieldName, fieldValue] of Object.entries(fieldConfig)) {
1332
+ const currentPath = fieldPath ? `${fieldPath}.${fieldName}` : fieldName;
1333
+
1334
+ if (fieldValue === true) {
1335
+ // This is a File reference field
1336
+ const query = {};
1337
+ query[currentPath] = { $exists: true, $ne: null };
1338
+
1339
+ const docs = await Model.find(query, currentPath).lean();
1340
+
1341
+ for (const doc of docs) {
1342
+ // Navigate to the nested field
1343
+ const parts = currentPath.split('.');
1344
+ let value = doc;
1345
+ for (const part of parts) {
1346
+ value = value?.[part];
1347
+ }
1348
+
1349
+ if (value) {
1350
+ if (Array.isArray(value)) {
1351
+ value.forEach((id) => id && referencedFileIds.add(id.toString()));
1352
+ } else {
1353
+ referencedFileIds.add(value.toString());
1354
+ }
1355
+ }
1356
+ }
1357
+
1358
+ logger.info('Found references', {
1359
+ model: modelName,
1360
+ field: currentPath,
1361
+ count: docs.length,
1362
+ });
1363
+ } else if (typeof fieldValue === 'object') {
1364
+ // Nested object, recurse
1365
+ await checkFieldReferences(currentPath, fieldValue);
1366
+ }
1367
+ }
1368
+ };
1369
+
1370
+ await checkFieldReferences('', modelFields);
1371
+ }
1372
+
1373
+ logger.info('Total referenced File IDs', { count: referencedFileIds.size, host, path });
1374
+
1375
+ // Find orphaned files
1376
+ const orphanedFiles = allFiles.filter((file) => !referencedFileIds.has(file._id.toString()));
1377
+
1378
+ if (orphanedFiles.length === 0) {
1379
+ logger.info('No orphaned files found', { host, path });
1380
+ } else {
1381
+ logger.info('Found orphaned files', { count: orphanedFiles.length, host, path });
1382
+
1383
+ if (options.dryRun) {
1384
+ logger.info('Dry run - would delete files', {
1385
+ count: orphanedFiles.length,
1386
+ ids: orphanedFiles.map((f) => f._id.toString()),
1387
+ });
1388
+ } else {
1389
+ const orphanedIds = orphanedFiles.map((f) => f._id);
1390
+ const deleteResult = await models.File.deleteMany({ _id: { $in: orphanedIds } });
1391
+ logger.info('Deleted orphaned files', {
1392
+ deletedCount: deleteResult.deletedCount,
1393
+ host,
1394
+ path,
1395
+ });
1396
+ }
1397
+ }
1398
+ } catch (error) {
1399
+ logger.error('Error processing host+path', {
1400
+ host,
1401
+ path,
1402
+ error: error.message,
1403
+ });
1404
+ }
1405
+ }
1406
+ }
1407
+ }
1408
+
1409
+ // Close all connections
1410
+ logger.info('Closing all database connections', { count: connectionsToClose.length });
1411
+ for (const { host, path, dbProvider } of connectionsToClose) {
1412
+ try {
1413
+ if (dbProvider && dbProvider.close) {
1414
+ await dbProvider.close();
1415
+ logger.info('Connection closed', { host, path });
1416
+ }
1417
+ } catch (error) {
1418
+ logger.error('Error closing connection', { host, path, error: error.message });
1419
+ }
1420
+ }
1421
+
1422
+ logger.info('File collection cleanup completed');
1423
+ } catch (error) {
1424
+ logger.error('File collection cleanup failed', { error: error.message });
1425
+ throw error;
1426
+ }
1427
+ },
1428
+
1429
+ /**
1430
+ * Handles backup of cluster metadata.
1431
+ * Orchestrates backup and restore operations for cluster metadata including
1432
+ * instances and cron jobs. Supports import/export and metadata generation.
1433
+ * @method clusterMetadataBackupCallback
1434
+ * @memberof UnderpostDB
1435
+ * @param {string} [deployId=process.env.DEFAULT_DEPLOY_ID] - The deployment ID.
1436
+ * @param {string} [host=process.env.DEFAULT_DEPLOY_HOST] - The host identifier.
1437
+ * @param {string} [path=process.env.DEFAULT_DEPLOY_PATH] - The path identifier.
1438
+ * @param {Object} [options={}] - Backup operation options.
1439
+ * @param {boolean} [options.generate=false] - Generate cluster metadata.
1440
+ * @param {boolean} [options.itc=false] - Execute in container context.
1441
+ * @param {boolean} [options.import=false] - Import metadata from backup.
1442
+ * @param {boolean} [options.export=false] - Export metadata to backup.
1443
+ * @param {boolean} [options.instances=false] - Process instances collection.
1444
+ * @param {boolean} [options.crons=false] - Process crons collection.
1445
+ * @param {boolean} [options.dev=false] - Development mode flag.
1446
+ * @return {Promise<void>} Resolves when backup operation is complete.
1447
+ */
1448
+ async clusterMetadataBackupCallback(
1449
+ deployId = process.env.DEFAULT_DEPLOY_ID,
1450
+ host = process.env.DEFAULT_DEPLOY_HOST,
1451
+ path = process.env.DEFAULT_DEPLOY_PATH,
1452
+ options = {
1453
+ generate: false,
1454
+ itc: false,
1455
+ import: false,
1456
+ export: false,
1457
+ instances: false,
1458
+ crons: false,
1459
+ dev: false,
1460
+ },
1461
+ ) {
1462
+ try {
1463
+ loadCronDeployEnv();
1464
+ deployId = deployId ? deployId : process.env.DEFAULT_DEPLOY_ID;
1465
+ host = host ? host : process.env.DEFAULT_DEPLOY_HOST;
1466
+ path = path ? path : process.env.DEFAULT_DEPLOY_PATH;
1467
+
1468
+ logger.info('Starting cluster metadata backup operation', {
1469
+ deployId,
1470
+ host,
1471
+ path,
1472
+ options,
1473
+ });
1474
+
1475
+ if (options.generate === true) {
1476
+ logger.info('Generating cluster metadata');
1477
+ await Underpost.db.clusterMetadataFactory(deployId, host, path);
1478
+ }
1479
+
1480
+ if (options.instances === true) {
1481
+ const outputPath = './engine-private/instances';
1482
+ if (!fs.existsSync(outputPath)) {
1483
+ fs.mkdirSync(outputPath, { recursive: true });
1484
+ }
1485
+ const collection = 'instances';
1486
+
1487
+ if (options.export === true) {
1488
+ logger.info('Exporting instances collection', { outputPath });
1489
+ shellExec(
1490
+ `node bin db --export --primary-pod --collections ${collection} --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
1491
+ );
1492
+ }
1493
+
1494
+ if (options.import === true) {
1495
+ logger.info('Importing instances collection', { outputPath });
1496
+ shellExec(
1497
+ `node bin db --import --primary-pod --drop --preserveUUID --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
1498
+ );
1499
+ }
1500
+ }
1501
+
1502
+ if (options.crons === true) {
1503
+ const outputPath = './engine-private/crons';
1504
+ if (!fs.existsSync(outputPath)) {
1505
+ fs.mkdirSync(outputPath, { recursive: true });
1506
+ }
1507
+ const collection = 'crons';
1508
+
1509
+ if (options.export === true) {
1510
+ logger.info('Exporting crons collection', { outputPath });
1511
+ shellExec(
1512
+ `node bin db --export --primary-pod --collections ${collection} --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
1513
+ );
1514
+ }
1515
+
1516
+ if (options.import === true) {
1517
+ logger.info('Importing crons collection', { outputPath });
1518
+ shellExec(
1519
+ `node bin db --import --primary-pod --drop --preserveUUID --out-path ${outputPath} --hosts ${host} --paths '${path}' ${deployId}`,
1520
+ );
1521
+ }
1522
+ }
1523
+
1524
+ logger.info('Cluster metadata backup operation completed');
1525
+ } catch (error) {
1526
+ logger.error('Cluster metadata backup operation failed', { error: error.message });
1527
+ throw error;
1528
+ }
1529
+ },
1530
+ };
1531
+ }
1532
+
1533
+ export default UnderpostDB;