@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
@@ -0,0 +1,1395 @@
1
+ /**
2
+ * Cluster module for managing Kubernetes cluster initialization, configuration, and component deployment.
3
+ * @module src/cli/cluster.js
4
+ * @namespace UnderpostCluster
5
+ */
6
+
7
+ import { getNpmRootPath } from '../server/conf.js';
8
+ import { loggerFactory } from '../server/logger.js';
9
+ import { shellExec } from '../server/process.js';
10
+ import { MONGODB_DEFAULT_REPLICA_COUNT } from '../db/mongo/MongooseDB.js';
11
+ import { MongoBootstrap } from '../db/mongo/MongoBootstrap.js';
12
+ import os from 'os';
13
+ import fs from 'fs-extra';
14
+ import Underpost from '../index.js';
15
+
16
+ const logger = loggerFactory(import.meta);
17
+
18
+ /**
19
+ * @class UnderpostCluster
20
+ * @description Manages Kubernetes cluster initialization, configuration, and component deployment.
21
+ * This class provides a set of static methods to handle cluster initialization, configuration,
22
+ * and optional component deployments.
23
+ * @memberof UnderpostCluster
24
+ */
25
+ class UnderpostCluster {
26
+ static API = {
27
+ /**
28
+ * @method init
29
+ * @description Initializes and configures the Kubernetes cluster based on provided options.
30
+ * This method handles host prerequisites, cluster initialization (Kind, Kubeadm, or K3s),
31
+ * and optional component deployments.
32
+ * @param {string} [podName] - Optional name of a pod for specific operations (e.g., listing).
33
+ * @param {object} [options] - Configuration options for cluster initialization.
34
+ * @param {boolean} [options.mongodb=false] - Deploy MongoDB.
35
+ * @param {boolean} [options.mongodb4=false] - Deploy MongoDB 4.4.
36
+ * @param {string} [options.serviceHost=''] - Set a custom host/IP for exposed MongoDB and Valkey clients.
37
+ * @param {boolean} [options.mariadb=false] - Deploy MariaDB.
38
+ * @param {boolean} [options.mysql=false] - Deploy MySQL.
39
+ * @param {boolean} [options.postgresql=false] - Deploy PostgreSQL.
40
+ * @param {boolean} [options.valkey=false] - Deploy Valkey.
41
+ * @param {boolean} [options.ipfs=false] - Deploy ipfs-cluster statefulset.
42
+ * @param {boolean} [options.info=false] - Display extensive Kubernetes cluster information.
43
+ * @param {boolean} [options.certManager=false] - Deploy Cert-Manager for certificate management.
44
+ * @param {boolean} [options.listPods=false] - List Kubernetes pods.
45
+ * @param {boolean} [options.reset=false] - Perform a comprehensive reset of Kubernetes and container environments.
46
+ * @param {boolean} [options.resetMongodb=false] - Perform a targeted reset of MongoDB components without restarting the entire cluster.
47
+ * @param {boolean} [options.dev=false] - Run in development mode (adjusts paths).
48
+ * @param {string} [options.nsUse=''] - Set the current kubectl namespace (creates namespace if it doesn't exist).
49
+ * @param {string} [options.namespace='default'] - Kubernetes namespace for cluster operations.
50
+ * @param {boolean} [options.infoCapacity=false] - Display resource capacity information for the cluster.
51
+ * @param {boolean} [options.infoCapacityPod=false] - Display resource capacity information for pods.
52
+ * @param {boolean} [options.pullImage=false] - Pull necessary Docker images before deployment.
53
+ * @param {boolean} [options.dedicatedGpu=false] - Configure for dedicated GPU usage (e.g., NVIDIA GPU Operator).
54
+ * @param {boolean} [options.kubeadm=false] - Initialize the cluster using Kubeadm.
55
+ * @param {string} [options.podNetworkCidr='192.168.0.0/16'] - Custom pod network CIDR for Kubeadm cluster initialization. Defaults to '192.168.0.0/16'.
56
+ * @param {string} [options.controlPlaneEndpoint=''] - Custom control plane endpoint for Kubeadm cluster initialization. Defaults to '${os.hostname()}:6443'.
57
+ * @param {boolean} [options.k3s=false] - Initialize the cluster using K3s.
58
+ * @param {boolean} [options.initHost=false] - Perform initial host setup (install Docker, Podman, Kind, Kubeadm, Helm).
59
+ * @param {boolean} [options.grafana=false] - Initialize the cluster with a Grafana deployment.
60
+ * @param {string} [options.prom=''] - Initialize the cluster with a Prometheus Operator deployment and monitor scrap for specified hosts.
61
+ * @param {boolean} [options.uninstallHost=false] - Uninstall all host components.
62
+ * @param {boolean} [options.config=false] - Apply general host configuration (SELinux, containerd, sysctl, firewalld).
63
+ * @param {boolean} [options.chown=false] - Set up kubectl configuration for the current user.
64
+ * @param {boolean} [options.removeVolumeHostPaths=false] - Remove data from host paths used by Persistent Volumes.
65
+ * @param {string} [options.hosts] - Set custom hosts entries.
66
+ * @param {string} [options.replicas] - Set the number of replicas for certain deployments.
67
+ * @param {boolean} [options.nodePort=false] - Expose enabled ready services (e.g. MongoDB 4.4, Valkey)
68
+ * to the host/public network via their NodePort Service manifest. The node port value lives directly
69
+ * in each manifest (mongodb-4.4/mongodb-nodeport.yaml, valkey/valkey-nodeport.yaml).
70
+ * @param {string} [options.nodeSelector=''] - Pin the just-deployed StatefulSet (MongoDB 4.4 / Valkey)
71
+ * to a specific Kubernetes node by name (e.g. 'localhost.localdomain'). Applied via a
72
+ * `kubernetes.io/hostname` nodeSelector patch once the workload reports Ready.
73
+ * @memberof UnderpostCluster
74
+ */
75
+ async init(
76
+ podName,
77
+ options = {
78
+ mongodb: false,
79
+ mongodb4: false,
80
+ serviceHost: '',
81
+ mariadb: false,
82
+ mysql: false,
83
+ postgresql: false,
84
+ valkey: false,
85
+ ipfs: false,
86
+ info: false,
87
+ certManager: false,
88
+ listPods: false,
89
+ reset: false,
90
+ resetMongodb: false,
91
+ dev: false,
92
+ nsUse: '',
93
+ namespace: 'default',
94
+ infoCapacity: false,
95
+ infoCapacityPod: false,
96
+ pullImage: false,
97
+ dedicatedGpu: false,
98
+ kubeadm: false,
99
+ podNetworkCidr: '192.168.0.0/16',
100
+ controlPlaneEndpoint: '',
101
+ k3s: false,
102
+ initHost: false,
103
+ grafana: false,
104
+ prom: '',
105
+ uninstallHost: false,
106
+ config: false,
107
+ chown: false,
108
+ removeVolumeHostPaths: false,
109
+ hosts: '',
110
+ replicas: '',
111
+ nodePort: false,
112
+ nodeSelector: '',
113
+ },
114
+ ) {
115
+ if (options.initHost) return Underpost.cluster.initHost();
116
+
117
+ if (options.uninstallHost) return Underpost.cluster.uninstallHost();
118
+
119
+ if (options.config) return options.k3s ? Underpost.cluster.configMinimalK3s() : Underpost.cluster.config();
120
+
121
+ if (options.chown) return Underpost.cluster.chown(options.k3s ? 'k3s' : options.kubeadm ? 'kubeadm' : 'kind');
122
+
123
+ const npmRoot = getNpmRootPath();
124
+ const underpostRoot = options.dev ? '.' : `${npmRoot}/underpost`;
125
+ const serviceHostInput = `${options.serviceHost || ''}`.trim();
126
+ const serviceHost = Underpost.cluster.resolveServiceHost(options);
127
+
128
+ if (options.listPods) return console.table(Underpost.kubectl.get(podName ?? undefined));
129
+ // Set default namespace if not specified
130
+ if (!options.namespace) options.namespace = 'default';
131
+
132
+ if (options.nsUse) {
133
+ // Verify if namespace exists, create if not
134
+ const namespaceExists = shellExec(`kubectl get namespace ${options.nsUse} --ignore-not-found -o name`, {
135
+ stdout: true,
136
+ silent: true,
137
+ silentOnError: true,
138
+ }).trim();
139
+
140
+ if (!namespaceExists) {
141
+ logger.info(`Namespace '${options.nsUse}' does not exist. Creating it...`);
142
+ shellExec(`kubectl create namespace ${options.nsUse}`);
143
+ logger.info(`Namespace '${options.nsUse}' created successfully.`);
144
+ } else {
145
+ logger.info(`Namespace '${options.nsUse}' already exists.`);
146
+ }
147
+
148
+ shellExec(`kubectl config set-context --current --namespace=${options.nsUse}`);
149
+ logger.info(`Context switched to namespace: ${options.nsUse}`);
150
+ return;
151
+ }
152
+
153
+ // Route reset to the per-type method. Each only touches what its runtime owns.
154
+ if (options.reset) {
155
+ if (options.k3s)
156
+ return await Underpost.cluster.safeResetK3s({ underpostRoot, resetMode: options.resetMode || 'full' });
157
+ if (options.kubeadm)
158
+ return await Underpost.cluster.safeResetKubeadm({
159
+ underpostRoot,
160
+ removeVolumeHostPaths: options.removeVolumeHostPaths,
161
+ });
162
+ return await Underpost.cluster.safeResetKind({
163
+ underpostRoot,
164
+ removeVolumeHostPaths: options.removeVolumeHostPaths,
165
+ });
166
+ }
167
+
168
+ // Targeted MongoDB-only reset (does not restart the whole node)
169
+ if (options.resetMongodb) {
170
+ const clusterType = options.k3s ? 'k3s' : options.kubeadm ? 'kubeadm' : 'kind';
171
+ return await MongoBootstrap.reset({
172
+ namespace: options.namespace,
173
+ clusterType,
174
+ underpostRoot,
175
+ });
176
+ }
177
+
178
+ // Check if a cluster (Kind, Kubeadm, or K3s) is already initialized by
179
+ // inspecting nodes rather than probing add-on pods. See detectClusterRuntime.
180
+ const runtime = Underpost.cluster.detectClusterRuntime();
181
+ const alreadyCluster = runtime.type !== null;
182
+ if (alreadyCluster) {
183
+ logger.info(
184
+ `Detected existing ${runtime.type} cluster (${runtime.ready ? 'Ready' : 'NotReady'}); skipping initialization.`,
185
+ );
186
+ }
187
+
188
+ // --- Kubeadm/Kind/K3s Cluster Initialization ---
189
+ // Host config is applied per cluster type (not shared) so the K3s path can
190
+ // stay minimal and avoid the Docker/containerd/kubelet setup it does not need.
191
+ if (!alreadyCluster) {
192
+ if (options.k3s) {
193
+ // K3s is self-contained (bundles containerd, kubelet, CNI, CoreDNS).
194
+ // Apply ONLY minimal host config — see configMinimalK3s.
195
+ Underpost.cluster.configMinimalK3s();
196
+ logger.info('Initializing K3s control plane...');
197
+ // Install K3s
198
+ logger.info('Installing K3s...');
199
+ // Disable the bundled traefik ingress and servicelb (Klipper) load
200
+ // balancer. The platform exposes services explicitly via Project
201
+ // Contour / Envoy and NodePort services (see --node-port); leaving the
202
+ // K3s built-ins enabled would bind the same host ports and conflict.
203
+ shellExec(`curl -sfL https://get.k3s.io | sh -s - --disable=traefik --disable=servicelb`);
204
+ logger.info('K3s installation completed.');
205
+
206
+ Underpost.cluster.chown('k3s');
207
+
208
+ logger.info('Waiting for K3s to be ready...');
209
+ shellExec(`sudo systemctl is-active --wait k3s || sudo systemctl wait --for=active k3s.service`);
210
+ logger.info('K3s service is active.');
211
+ // K3s manages its own CNI (flannel) and iptables rules. nat-iptables.sh
212
+ // is neither needed nor desirable for a single K3s node in a VM.
213
+ } else if (options.kubeadm) {
214
+ Underpost.cluster.config();
215
+ Underpost.cluster.natSetup({ underpostRoot });
216
+ logger.info('Initializing Kubeadm control plane...');
217
+ // Set default values if not provided
218
+ const podNetworkCidr = options.podNetworkCidr || '192.168.0.0/16';
219
+ const controlPlaneEndpoint = options.controlPlaneEndpoint || `${os.hostname()}:6443`;
220
+
221
+ // Initialize kubeadm control plane.
222
+ // Use CRI-O socket when available, otherwise fall back to containerd.
223
+ const crioSocket = 'unix:///var/run/crio/crio.sock';
224
+ const containerdSocket = 'unix:///run/containerd/containerd.sock';
225
+ const criSocket =
226
+ shellExec(`test -S /var/run/crio/crio.sock && echo crio || echo containerd`, {
227
+ stdout: true,
228
+ silent: true,
229
+ }).trim() === 'crio'
230
+ ? crioSocket
231
+ : containerdSocket;
232
+ shellExec(
233
+ `sudo kubeadm init --pod-network-cidr=${podNetworkCidr} --control-plane-endpoint="${controlPlaneEndpoint}" --cri-socket=${criSocket}`,
234
+ );
235
+ // Configure kubectl for the current user
236
+ Underpost.cluster.chown('kubeadm'); // Pass 'kubeadm' to chown
237
+
238
+ // Install Calico CNI
239
+ logger.info('Installing Calico CNI...');
240
+ shellExec(
241
+ `sudo kubectl create -f https://cdn.jsdelivr.net/gh/projectcalico/calico@v3.29.3/manifests/tigera-operator.yaml`,
242
+ );
243
+ shellExec(
244
+ `kubectl create -f https://cdn.jsdelivr.net/gh/projectcalico/calico@v3.29.3/manifests/custom-resources.yaml`,
245
+ );
246
+ shellExec(`sudo kubectl apply -f ${underpostRoot}/manifests/kubeadm-calico-config.yaml`);
247
+
248
+ // Untaint control plane node to allow scheduling pods
249
+ const nodeName = os.hostname();
250
+ shellExec(`kubectl taint nodes ${nodeName} node-role.kubernetes.io/control-plane:NoSchedule-`);
251
+ // Install local-path-provisioner for dynamic PVCs
252
+ logger.info('Installing local-path-provisioner...');
253
+ shellExec(
254
+ `kubectl apply -f https://cdn.jsdelivr.net/gh/rancher/local-path-provisioner@master/deploy/local-path-storage.yaml`,
255
+ );
256
+ } else {
257
+ Underpost.cluster.config();
258
+ Underpost.cluster.natSetup({ underpostRoot });
259
+ // Kind cluster initialization (default for development)
260
+ logger.info('Initializing Kind cluster...');
261
+ const devReplicaCount = Math.max(Number(options.replicas) || MONGODB_DEFAULT_REPLICA_COUNT, 3);
262
+ shellExec(`sudo mkdir -p /data/mongodb`);
263
+ for (let index = 0; index < devReplicaCount; index++) {
264
+ shellExec(`sudo mkdir -p /data/mongodb/v${index}`);
265
+ }
266
+ const kindCreateCmd = `cd ${underpostRoot}/manifests && kind create cluster --config kind-config-dev.yaml`;
267
+ try {
268
+ shellExec(kindCreateCmd);
269
+ } catch (error) {
270
+ const kindCreateErrText = `${error?.message || ''}\n${error?.stderr || ''}`;
271
+ if (kindCreateErrText.includes('all predefined address pools have been fully subnetted')) {
272
+ logger.warn(
273
+ 'Docker address pool exhausted while creating Kind cluster. Running cleanup and retrying once...',
274
+ );
275
+ Underpost.cluster.recoverKindDockerNetworks();
276
+ try {
277
+ shellExec(kindCreateCmd);
278
+ } catch (retryError) {
279
+ const retryErrText = `${retryError?.message || ''}\n${retryError?.stderr || ''}`;
280
+ if (retryErrText.includes('all predefined address pools have been fully subnetted')) {
281
+ logger.warn(
282
+ 'Kind retry still failed from pool exhaustion. Applying Docker daemon address-pool config and retrying once more...',
283
+ );
284
+ Underpost.cluster.ensureDockerDefaultAddressPools();
285
+ shellExec(kindCreateCmd);
286
+ } else {
287
+ throw retryError;
288
+ }
289
+ }
290
+ } else {
291
+ throw error;
292
+ }
293
+ }
294
+ Underpost.cluster.chown('kind'); // Pass 'kind' to chown
295
+ }
296
+ }
297
+
298
+ // --- Optional Component Deployments (Databases, Ingress, Cert-Manager) ---
299
+ // These deployments happen after the base cluster is up.
300
+
301
+ if (options.dedicatedGpu) {
302
+ shellExec(`node ${underpostRoot}/bin/deploy nvidia-gpu-operator`);
303
+ shellExec(`node ${underpostRoot}/bin/deploy kubeflow-spark-operator${options.kubeadm ? ' kubeadm' : ''}`);
304
+ }
305
+
306
+ if (options.grafana) {
307
+ shellExec(`kubectl delete deployment grafana -n ${options.namespace} --ignore-not-found`);
308
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/grafana -n ${options.namespace}`);
309
+ const yaml = `${fs
310
+ .readFileSync(`${underpostRoot}/manifests/grafana/deployment.yaml`, 'utf8')
311
+ .replace('{{GF_SERVER_ROOT_URL}}', options.hosts.split(',')[0])}`;
312
+ console.log(yaml);
313
+ shellExec(`kubectl apply -f - -n ${options.namespace} <<EOF
314
+ ${yaml}
315
+ EOF
316
+ `);
317
+ }
318
+
319
+ if (options.prom) {
320
+ shellExec(`kubectl delete deployment prometheus --ignore-not-found`);
321
+ shellExec(`kubectl delete configmap prometheus-config --ignore-not-found`);
322
+ shellExec(`kubectl delete service prometheus --ignore-not-found`);
323
+ // Prometheus server host: http://<prometheus-cluster-ip>:9090
324
+ const yaml = `${fs.readFileSync(`${underpostRoot}/manifests/prometheus/deployment.yaml`, 'utf8').replace(
325
+ '- targets: []',
326
+ `- targets: [${options.prom
327
+ .split(',')
328
+ .map((host) => `'${host}'`)
329
+ .join(',')}]`,
330
+ )}`;
331
+ console.log(yaml);
332
+ shellExec(`kubectl apply -f - -n ${options.namespace} <<EOF
333
+ ${yaml}
334
+ EOF
335
+ `);
336
+ }
337
+
338
+ if (options.valkey) {
339
+ if (options.pullImage) Underpost.cluster.pullImage('valkey/valkey:latest', options);
340
+ shellExec(`kubectl delete statefulset valkey-service -n ${options.namespace} --ignore-not-found`);
341
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/valkey -n ${options.namespace}`);
342
+ const valkeyReady = await Underpost.test.statusMonitor('valkey-service', 'Running', 'pods', 1000, 60 * 10);
343
+ // Expose valkey to the host/public network only once the pod is ready.
344
+ // The node port (32079) is set directly in the manifest.
345
+ if (valkeyReady && options.nodePort)
346
+ shellExec(`kubectl apply -f ${underpostRoot}/manifests/valkey/valkey-nodeport.yaml -n ${options.namespace}`);
347
+ if (valkeyReady && options.nodeSelector)
348
+ Underpost.cluster.pinToNode({
349
+ name: 'valkey-service',
350
+ namespace: options.namespace,
351
+ node: options.nodeSelector,
352
+ });
353
+ if (valkeyReady && serviceHost)
354
+ Underpost.cluster.syncServiceConnectionEnv({
355
+ serviceHost,
356
+ valkey: true,
357
+ options,
358
+ });
359
+ }
360
+ if (options.ipfs) {
361
+ await Underpost.ipfs.deploy(options, underpostRoot);
362
+ }
363
+ if (options.mariadb) {
364
+ shellExec(
365
+ `sudo kubectl create secret generic mariadb-secret --from-file=username=/home/dd/engine/engine-private/mariadb-username --from-file=password=/home/dd/engine/engine-private/mariadb-password --dry-run=client -o yaml | kubectl apply -f - -n ${options.namespace}`,
366
+ );
367
+ shellExec(`kubectl delete statefulset mariadb-statefulset -n ${options.namespace} --ignore-not-found`);
368
+
369
+ if (options.pullImage) Underpost.cluster.pullImage('mariadb:latest', options);
370
+ shellExec(`kubectl apply -f ${underpostRoot}/manifests/mariadb/storage-class.yaml -n ${options.namespace}`);
371
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/mariadb -n ${options.namespace}`);
372
+ }
373
+ if (options.mysql) {
374
+ shellExec(
375
+ `sudo kubectl create secret generic mysql-secret --from-file=username=/home/dd/engine/engine-private/mysql-username --from-file=password=/home/dd/engine/engine-private/mysql-password --dry-run=client -o yaml | kubectl apply -f - -n ${options.namespace}`,
376
+ );
377
+ shellExec(`sudo mkdir -p /mnt/data`);
378
+ shellExec(`sudo chmod 777 /mnt/data`);
379
+ shellExec(`sudo chown -R $(whoami):$(whoami) /mnt/data`);
380
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/mysql -n ${options.namespace}`);
381
+ }
382
+ if (options.postgresql) {
383
+ if (options.pullImage) Underpost.cluster.pullImage('postgres:latest', options);
384
+ shellExec(
385
+ `sudo kubectl create secret generic postgres-secret --from-file=password=/home/dd/engine/engine-private/postgresql-password --dry-run=client -o yaml | kubectl apply -f - -n ${options.namespace}`,
386
+ );
387
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/postgresql -n ${options.namespace}`);
388
+ }
389
+ if (options.mongodb4) {
390
+ if (options.pullImage) Underpost.cluster.pullImage('mongo:4.4', options);
391
+ shellExec(`kubectl delete statefulset mongodb -n ${options.namespace} --ignore-not-found`);
392
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/mongodb-4.4 -n ${options.namespace}`);
393
+
394
+ const statefulSetName = 'mongodb';
395
+ const podName = 'mongodb-0';
396
+
397
+ const successInstance = await Underpost.test.statusMonitor(podName);
398
+
399
+ if (successInstance) {
400
+ // mongod only accepts a member host it can recognize as itself (the isSelf check):
401
+ // it must match a local interface IP or be reachable back at that address. A pod-external
402
+ // LAN IP / NodePort address is neither bound in the pod netns nor routable back from it,
403
+ // so reconfiguring to it fails with NodeNotFound ("...maps to this node") even under
404
+ // force. Bootstrap on localhost; only advertise a non-localhost host when the node can
405
+ // self-verify it, and tolerate the failure otherwise so bootstrap stays idempotent.
406
+ // Clients reaching the set through an external IP/NodePort must use directConnection=true,
407
+ // which ignores the advertised member host (see MongooseDB.buildUri).
408
+ const rsHost = serviceHostInput || (options.dev ? '127.0.0.1' : 'mongodb-0.mongodb-service');
409
+ const memberHost = `${rsHost}:27017`;
410
+ const initEval = [
411
+ `try { rs.initiate({ _id: "rs0", members: [{ _id: 0, host: "localhost:27017" }] }); }`,
412
+ `catch (e) { if (!String(e).includes("already initialized") && !String(e).includes("AlreadyInitialized")) throw e; }`,
413
+ `for (var i = 0; i < 30; i++) { if (db.isMaster().ismaster) break; sleep(1000); }`,
414
+ memberHost === 'localhost:27017'
415
+ ? ``
416
+ : `try { var c = rs.conf(); c.members[0].host = "${memberHost}"; rs.reconfig(c, { force: true }); print("RS_HOST_SET ${memberHost}"); } catch (e) { if (String(e).includes("NodeNotFound") || String(e).includes("maps to this node")) { print("RS_HOST_SKIPPED ${memberHost} (not self-reachable from pod; clients must use directConnection=true)"); } else { throw e; } }`,
417
+ ]
418
+ .filter(Boolean)
419
+ .join(' ');
420
+
421
+ shellExec(`sudo kubectl exec -i ${podName} -n ${options.namespace} -- mongo --quiet --eval '${initEval}'`);
422
+
423
+ // Only expose mongos to the host/public network once the instance is
424
+ // confirmed ready and the replica set is initiated. The node port (32017)
425
+ // is set directly in the manifest.
426
+ if (options.nodePort)
427
+ shellExec(
428
+ `kubectl apply -f ${underpostRoot}/manifests/mongodb-4.4/mongodb-nodeport.yaml -n ${options.namespace}`,
429
+ );
430
+ if (options.nodeSelector)
431
+ Underpost.cluster.pinToNode({
432
+ name: statefulSetName,
433
+ namespace: options.namespace,
434
+ node: options.nodeSelector,
435
+ });
436
+ if (serviceHost)
437
+ Underpost.cluster.syncServiceConnectionEnv({
438
+ serviceHost,
439
+ mongodb: true,
440
+ options,
441
+ });
442
+ }
443
+ } else if (options.mongodb) {
444
+ const clusterType = options.k3s ? 'k3s' : options.kubeadm ? 'kubeadm' : 'kind';
445
+ await MongoBootstrap.initReplicaSet({
446
+ namespace: options.namespace,
447
+ replicaCount: Number(options.replicas) || MONGODB_DEFAULT_REPLICA_COUNT,
448
+ hostList: serviceHostInput,
449
+ pullImage: options.pullImage,
450
+ reset: options.reset,
451
+ clusterType,
452
+ underpostRoot,
453
+ });
454
+ if (serviceHost)
455
+ Underpost.cluster.syncServiceConnectionEnv({
456
+ serviceHost,
457
+ mongodb: true,
458
+ options,
459
+ });
460
+ }
461
+
462
+ if (options.contour) {
463
+ shellExec(
464
+ `kubectl apply -f https://cdn.jsdelivr.net/gh/projectcontour/contour@release-1.33/examples/render/contour.yaml`,
465
+ );
466
+ if (options.kubeadm) {
467
+ // Envoy service might need NodePort for kubeadm
468
+ shellExec(
469
+ `sudo kubectl apply -f ${underpostRoot}/manifests/envoy-service-nodeport.yaml -n ${options.namespace}`,
470
+ );
471
+ }
472
+ // K3s has a built-in LoadBalancer (Klipper-lb) that can expose services,
473
+ // so a specific NodePort service might not be needed or can be configured differently.
474
+ }
475
+
476
+ if (options.certManager) {
477
+ if (!Underpost.kubectl.get('cert-manager').find((p) => p.STATUS === 'Running')) {
478
+ shellExec(`helm repo add jetstack https://charts.jetstack.io --force-update`);
479
+ shellExec(
480
+ `helm install cert-manager jetstack/cert-manager \
481
+ --namespace cert-manager \
482
+ --create-namespace \
483
+ --version v1.17.0 \
484
+ --set crds.enabled=true`,
485
+ );
486
+ }
487
+
488
+ const letsEncName = 'letsencrypt-prod';
489
+ shellExec(`sudo kubectl delete ClusterIssuer ${letsEncName} --ignore-not-found`);
490
+ shellExec(`sudo kubectl apply -f ${underpostRoot}/manifests/${letsEncName}.yaml -n ${options.namespace}`);
491
+ }
492
+ },
493
+
494
+ /**
495
+ * @method detectClusterRuntime
496
+ * @description Detects an already-initialized cluster by inspecting Kubernetes
497
+ * nodes, and classifies its runtime. Nodes are authoritative and stable, unlike
498
+ * add-on pods: the previous check keyed off pod names (`calico-kube-controllers`,
499
+ * `kube-apiserver-kind-control-plane`, `svclb-traefik`), whose presence and
500
+ * readiness are timing- and config-dependent. Disabling servicelb removes the
501
+ * `svclb-traefik` pods entirely, and CNI/controller pods report NotReady for a
502
+ * window right after init — both of which made re-runs misdetect the cluster
503
+ * state. Classification relies on stable node attributes:
504
+ * - k3s: the node VERSION carries a `+k3s` build suffix (e.g. v1.30.5+k3s1).
505
+ * - kind: kind names every node `<cluster>-control-plane` / `<cluster>-worker`.
506
+ * - kubeadm: a real control-plane node that is neither of the above.
507
+ * @returns {{ type: ('k3s'|'kubeadm'|'kind'|null), ready: boolean, nodes: Array<object> }}
508
+ * `type` is the detected runtime (null when no cluster exists); `ready` is true
509
+ * when at least one node reports STATUS=Ready.
510
+ * @memberof UnderpostCluster
511
+ */
512
+ detectClusterRuntime() {
513
+ const nodes = Underpost.kubectl.get('', 'nodes');
514
+ if (!nodes.length) return { type: null, ready: false, nodes: [] };
515
+
516
+ // STATUS can be a comma-joined list (e.g. "Ready,SchedulingDisabled").
517
+ const ready = nodes.some((n) => `${n.STATUS || ''}`.split(',').includes('Ready'));
518
+
519
+ let type;
520
+ if (nodes.some((n) => `${n.VERSION || ''}`.includes('+k3s'))) type = 'k3s';
521
+ else if (nodes.some((n) => `${n.NAME || ''}`.includes('-control-plane') || `${n.NAME || ''}`.includes('kind')))
522
+ type = 'kind';
523
+ else type = 'kubeadm';
524
+
525
+ return { type, ready, nodes };
526
+ },
527
+
528
+ /**
529
+ * @method pinToNode
530
+ * @description Pins a workload to a specific Kubernetes node by patching its
531
+ * pod template with a `kubernetes.io/hostname` nodeSelector. General-purpose;
532
+ * currently used to place the MongoDB 4.4 / Valkey StatefulSets on a chosen
533
+ * node (`--node-selector`). The patch triggers a rolling reschedule onto the
534
+ * target node.
535
+ * @param {object} params
536
+ * @param {string} [params.kind='statefulset'] - Workload kind to patch.
537
+ * @param {string} params.name - Workload name.
538
+ * @param {string} params.namespace - Target namespace.
539
+ * @param {string} params.node - Target node name (matches `kubernetes.io/hostname`).
540
+ * @memberof UnderpostCluster
541
+ */
542
+ pinToNode({ kind = 'statefulset', name, namespace, node }) {
543
+ logger.info(`Pinning ${kind}/${name} to node '${node}' (namespace: ${namespace}).`);
544
+ const patch = JSON.stringify({
545
+ spec: { template: { spec: { nodeSelector: { 'kubernetes.io/hostname': node } } } },
546
+ });
547
+ shellExec(`kubectl patch ${kind} ${name} -n ${namespace} --type merge -p '${patch}'`);
548
+ },
549
+
550
+ /**
551
+ * @method resolveServiceHost
552
+ * @description Resolves a shared single-host override used by exposed service clients.
553
+ * @param {object} [options={}] - Cluster options.
554
+ * @returns {string} A single host override, or an empty string when unset / not reusable.
555
+ * @memberof UnderpostCluster
556
+ */
557
+ resolveServiceHost(options = {}) {
558
+ const candidate = `${options.serviceHost || ''}`.trim();
559
+ return candidate && !candidate.includes(',') ? candidate : '';
560
+ },
561
+
562
+ /**
563
+ * @method upsertEnvVar
564
+ * @description Replaces or appends one env var assignment in raw env file text.
565
+ * @param {string} envText - Existing env file contents.
566
+ * @param {string} key - Env var name.
567
+ * @param {string} value - Env var value.
568
+ * @returns {string} Updated env file contents.
569
+ * @memberof UnderpostCluster
570
+ */
571
+ upsertEnvVar(envText, key, value) {
572
+ const nextEntry = `${key}=${value}`;
573
+ const envKeyPattern = new RegExp(`^${key}=.*$`, 'm');
574
+ if (envKeyPattern.test(envText)) return envText.replace(envKeyPattern, nextEntry);
575
+
576
+ const trimmedEnvText = envText.replace(/\s*$/, '');
577
+ return `${trimmedEnvText}${trimmedEnvText ? '\n' : ''}${nextEntry}\n`;
578
+ },
579
+
580
+ /**
581
+ * @method syncServiceConnectionEnv
582
+ * @description Persists exposed service connection hosts to the active deploy env files.
583
+ * Currently applies only to MongoDB (`DB_HOST`) and Valkey (`VALKEY_HOST`).
584
+ * @param {object} params
585
+ * @param {string} params.serviceHost - Shared exposed host/IP.
586
+ * @param {boolean} [params.mongodb=false] - Update MongoDB runtime host.
587
+ * @param {boolean} [params.valkey=false] - Update Valkey runtime host.
588
+ * @param {object} [params.options={}] - Cluster options used to infer the active env.
589
+ * @memberof UnderpostCluster
590
+ */
591
+ syncServiceConnectionEnv({ serviceHost, mongodb = false, valkey = false, options = {} }) {
592
+ if (!serviceHost) return;
593
+
594
+ const updates = {};
595
+ if (mongodb) updates.DB_HOST = `mongodb://${serviceHost}:27017`;
596
+ if (valkey) updates.VALKEY_HOST = serviceHost;
597
+ if (Object.keys(updates).length === 0) return;
598
+
599
+ const deployId = process.env.DEPLOY_ID || process.env.DEFAULT_DEPLOY_ID || 'dd-default';
600
+ const envName = process.env.NODE_ENV || (options.dev ? 'development' : 'production');
601
+ const envPaths = [`./engine-private/conf/${deployId}/.env.${envName}`, `./.env.${envName}`, `./.env`].filter(
602
+ (envPath, index, paths) => fs.existsSync(envPath) && paths.indexOf(envPath) === index,
603
+ );
604
+
605
+ if (envPaths.length === 0) {
606
+ logger.warn(`No env files found to persist service host override for deploy '${deployId}' (${envName}).`);
607
+ return;
608
+ }
609
+
610
+ for (const envPath of envPaths) {
611
+ let envText = fs.readFileSync(envPath, 'utf8');
612
+ for (const [key, value] of Object.entries(updates))
613
+ envText = Underpost.cluster.upsertEnvVar(envText, key, value);
614
+ fs.writeFileSync(envPath, envText, 'utf8');
615
+ }
616
+
617
+ logger.info(`Persisted service host override for ${Object.keys(updates).join(', ')} to ${envPaths.join(', ')}`);
618
+ },
619
+
620
+ /**
621
+ * @method pullImage
622
+ * @description Pulls a container image using the appropriate runtime based on the cluster type.
623
+ * - For Kind clusters: pulls via Docker and loads the image into the Kind cluster.
624
+ * - For Kubeadm/K3s clusters: pulls via crictl (containerd).
625
+ * @param {string} image - The fully-qualified container image reference (e.g. 'mongo:latest').
626
+ * @param {object} options - The cluster options object from `init`.
627
+ * @param {boolean} [options.kubeadm=false] - Whether the cluster is Kubeadm-based.
628
+ * @param {boolean} [options.k3s=false] - Whether the cluster is K3s-based.
629
+ * @memberof UnderpostCluster
630
+ */
631
+ pullImage(image, options = { kubeadm: false, k3s: false }) {
632
+ if (!options.kubeadm && !options.k3s) {
633
+ const tarPath = `/tmp/kind-image-${image.replace(/[\/:]/g, '-')}.tar`;
634
+ shellExec(`docker pull ${image}`);
635
+ shellExec(`docker save ${image} -o ${tarPath}`);
636
+ shellExec(
637
+ `for node in $(kind get nodes); do cat ${tarPath} | docker exec -i $node ctr --namespace=k8s.io images import -; done`,
638
+ );
639
+ shellExec(`rm -f ${tarPath}`);
640
+ } else if (options.k3s) {
641
+ // K3s uses its own embedded containerd socket, not the host-level one
642
+ // used by kubeadm/containerd installations.
643
+ shellExec(
644
+ `sudo env PATH="$PATH:/usr/local/bin:/usr/bin" crictl --runtime-endpoint unix:///run/k3s/containerd/containerd.sock pull ${image}`,
645
+ );
646
+ } else if (options.kubeadm) {
647
+ // Kubeadm / K3s: use crictl to pull directly into the active CRI runtime.
648
+ // crictl is not in sudo's secure_path; pass full PATH through env.
649
+ // Point crictl at CRI-O when the socket exists, otherwise fall back to containerd.
650
+ const criSock =
651
+ shellExec(`test -S /var/run/crio/crio.sock && echo crio || echo containerd`, {
652
+ stdout: true,
653
+ silent: true,
654
+ }).trim() === 'crio'
655
+ ? 'unix:///var/run/crio/crio.sock'
656
+ : 'unix:///run/containerd/containerd.sock';
657
+ shellExec(`sudo env PATH="$PATH:/usr/local/bin:/usr/bin" crictl --runtime-endpoint ${criSock} pull ${image}`);
658
+ }
659
+ },
660
+
661
+ /**
662
+ * @method config
663
+ * @description Configures host-level settings required for Kubernetes.
664
+ * This method ensures proper SELinux, Docker, Containerd, and Sysctl settings
665
+ * are applied for a healthy Kubernetes environment. It explicitly avoids
666
+ * iptables flushing commands to prevent conflicts with Kubernetes' own network management.
667
+ * @param {object} [options] - Configuration options for host setup.
668
+ * @param {string} [options.underpostRoot] - The root path of the underpost project, used for locating scripts if needed.
669
+ * @memberof UnderpostCluster
670
+ */
671
+ config(options = { underpostRoot: '.' }) {
672
+ const { underpostRoot } = options;
673
+ console.log('Applying host configuration: SELinux, Docker, Containerd, and Sysctl settings.');
674
+ // Disable SELinux (permissive mode)
675
+ shellExec(`sudo setenforce 0`, {
676
+ silentOnError: true,
677
+ });
678
+ shellExec(`sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config`, {
679
+ silentOnError: true,
680
+ });
681
+
682
+ // Enable and start Docker and Kubelet services
683
+ shellExec(`sudo systemctl enable --now docker`); // Docker might not be needed for K3s
684
+ shellExec(`sudo systemctl enable --now kubelet`); // Kubelet might not be needed for K3s (K3s uses its own agent)
685
+
686
+ // Configure containerd for SystemdCgroup and explicitly disable SELinux
687
+ // This is crucial for kubelet/k3s to interact correctly with containerd
688
+ shellExec(`containerd config default | sudo tee /etc/containerd/config.toml > /dev/null`);
689
+ shellExec(`sudo sed -i -e "s/SystemdCgroup = false/SystemdCgroup = true/g" /etc/containerd/config.toml`);
690
+ // Add a new line to disable SELinux for the runc runtime
691
+ // shellExec(
692
+ // `sudo sed -i '/SystemdCgroup = true/a selinux_disabled = true' /etc/containerd/config.toml`,
693
+ // );
694
+ // Restart docker after containerd config changes. Rocky 9 uses systemctl,
695
+ // not the legacy service command.
696
+ shellExec(`sudo systemctl restart docker || sudo service docker restart || true`);
697
+ shellExec(`sudo systemctl enable --now containerd.service`);
698
+ shellExec(`sudo systemctl restart containerd`); // Restart containerd to apply changes
699
+
700
+ // Disable swap (required by Kubernetes)
701
+ shellExec(`sudo swapoff -a; sudo sed -i '/swap/d' /etc/fstab`);
702
+
703
+ // Reload systemd daemon to pick up new unit files/changes
704
+ shellExec(`sudo systemctl daemon-reload`);
705
+
706
+ // Increase inotify limits
707
+ shellExec(`sudo sysctl -w fs.inotify.max_user_watches=2099999999`);
708
+ shellExec(`sudo sysctl -w fs.inotify.max_user_instances=2099999999`);
709
+ shellExec(`sudo sysctl -w fs.inotify.max_queued_events=2099999999`);
710
+ },
711
+
712
+ /**
713
+ * @method configMinimalK3s
714
+ * @description Minimal host configuration for a K3s node. K3s is fully
715
+ * self-contained — it ships its own containerd, kubelet, CNI (flannel),
716
+ * CoreDNS and traefik. It therefore needs NONE of the Docker /
717
+ * standalone-containerd / standalone-kubelet setup that `config()` applies
718
+ * for kind and kubeadm. In a fresh LXD VM those packages do not exist, so
719
+ * `config()` there is both redundant and a source of errors.
720
+ *
721
+ * This applies only what K3s genuinely requires, and every step is guarded
722
+ * so it is a no-op when the relevant tooling is absent (e.g. minimal images
723
+ * without SELinux userspace):
724
+ * - SELinux → permissive (only if SELinux tooling is present).
725
+ * - swap off (Kubernetes best practice).
726
+ * - br_netfilter + bridge/forward sysctls (pod networking).
727
+ * - inotify limits.
728
+ * @memberof UnderpostCluster
729
+ */
730
+ configMinimalK3s() {
731
+ console.log('Applying minimal K3s host configuration (firewalld, SELinux, swap, sysctl).');
732
+
733
+ // Disable firewalld. K3s manages its own iptables rules; firewalld
734
+ // closes 6443/tcp (supervisor + API) and 8472/udp (flannel VXLAN) by
735
+ // default on RHEL/Rocky, which makes k3s-agent hang on `systemctl start`
736
+ // forever (the upstream unit ships TimeoutStartSec=0).
737
+ shellExec(`if systemctl is-active --quiet firewalld; then sudo systemctl disable --now firewalld; fi`);
738
+
739
+ // SELinux → permissive, but only when the tooling exists. Rocky has it;
740
+ // minimal LXD images may not. K3s also installs k3s-selinux for enforcing
741
+ // mode, so this is a best-effort dev convenience, not a hard requirement.
742
+ shellExec(`if command -v setenforce >/dev/null 2>&1; then sudo setenforce 0; fi`, {
743
+ silentOnError: true,
744
+ });
745
+ shellExec(
746
+ `if [ -f /etc/selinux/config ]; then sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config; fi`,
747
+ { silentOnError: true },
748
+ );
749
+
750
+ // Disable swap. `swapoff -a` is a no-op without swap; the sed only edits
751
+ // fstab when a swap line is present.
752
+ shellExec(`sudo swapoff -a`);
753
+ shellExec(`sudo sed -i '/swap/d' /etc/fstab`);
754
+
755
+ // Pod networking: ensure br_netfilter is loaded and the bridge/forward
756
+ // sysctls are set. K3s + flannel depend on these.
757
+ shellExec(
758
+ `if command -v lsmod >/dev/null 2>&1 && command -v modprobe >/dev/null 2>&1; then if ! lsmod | grep -q '^br_netfilter'; then sudo modprobe br_netfilter || true; fi; fi`,
759
+ );
760
+ shellExec(
761
+ `echo 'net.bridge.bridge-nf-call-iptables = 1
762
+ net.bridge.bridge-nf-call-ip6tables = 1
763
+ net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-k3s.conf > /dev/null`,
764
+ );
765
+ shellExec(`sudo sysctl --system`);
766
+
767
+ // inotify limits — many pods/watchers. Conservative, sane values.
768
+ shellExec(`sudo sysctl -w fs.inotify.max_user_instances=1024`);
769
+ shellExec(`sudo sysctl -w fs.inotify.max_user_watches=1048576`);
770
+ },
771
+
772
+ /**
773
+ * @method natSetup
774
+ * @description Configures NAT and iptables settings for Kubernetes networking.
775
+ * This method enables necessary sysctl settings for bridge networking and applies iptables rules
776
+ * required for Kubernetes cluster communication. It is designed to work with kubeadm and k3s clusters, ensuring that
777
+ * traffic through Linux bridges is processed by iptables, which is crucial for CNI plugins to function correctly.
778
+ * The method also applies NAT iptables rules and configures firewalld for Kubernetes, which is required for multi-machine kubeadm inter-node communication.
779
+ * Note: This method must be called before kubeadm init / kind create so that br_netfilter is loaded and kernel networking is ready when the control plane starts.
780
+ * @param {object} [options] - Configuration options for NAT setup.
781
+ * @param {string} [options.underpostRoot] - The root path of the underpost project, used to locate the nat-iptables.sh script.
782
+ * @memberof UnderpostCluster
783
+ */
784
+ natSetup(options = { underpostRoot: '.' }) {
785
+ const { underpostRoot } = options;
786
+ // Loads br_netfilter, applies bridge/forward sysctls, opens firewall ports, enables masquerade.
787
+ // Must run before kubeadm init / kind create so kernel networking is ready.
788
+ shellExec(`${underpostRoot}/scripts/nat-iptables.sh`);
789
+ },
790
+
791
+ /**
792
+ * @method chown
793
+ * @description Sets up kubectl configuration for the current user based on the cluster type.
794
+ * @param {string} clusterType - The type of Kubernetes cluster ('kubeadm', 'k3s', or 'kind').
795
+ * @memberof UnderpostCluster
796
+ */
797
+ chown(clusterType) {
798
+ console.log(`Setting up kubectl configuration for ${clusterType} cluster...`);
799
+ shellExec(`mkdir -p ~/.kube`);
800
+
801
+ let kubeconfigPath;
802
+ if (clusterType === 'k3s') {
803
+ kubeconfigPath = '/etc/rancher/k3s/k3s.yaml';
804
+ } else if (clusterType === 'kubeadm') {
805
+ kubeconfigPath = '/etc/kubernetes/admin.conf';
806
+ } else {
807
+ // Default to kind if not specified or unknown
808
+ kubeconfigPath = ''; // Kind's kubeconfig is usually managed by kind itself, or merged
809
+ }
810
+
811
+ if (kubeconfigPath) {
812
+ shellExec(`sudo -E cp -i -f ${kubeconfigPath} ~/.kube/config`);
813
+ shellExec(`sudo -E chown $(id -u):$(id -g) ~/.kube/config`);
814
+ } else if (clusterType === 'kind') {
815
+ // For Kind, the kubeconfig is usually merged automatically or can be explicitly exported
816
+ // This command ensures it's merged into the default kubeconfig
817
+ shellExec(`kind get kubeconfig > ~/.kube/config`);
818
+ shellExec(`sudo -E chown $(id -u):$(id -g) ~/.kube/config`);
819
+ } else {
820
+ logger.warn('No specific kubeconfig path defined for this cluster type, or it is managed automatically.');
821
+ }
822
+ console.log('kubectl config set up successfully.');
823
+ },
824
+
825
+ // Shared reset helpers (internal — used by safeResetKind / safeResetKubeadm).
826
+
827
+ /**
828
+ * @method _truncateLargeLogs
829
+ * @description Removes files >1 GiB under /var/log. Best-effort.
830
+ * @private
831
+ */
832
+ _truncateLargeLogs() {
833
+ try {
834
+ const cleanPath = `/var/log/`;
835
+ const largeLogsFiles = shellExec(
836
+ `sudo du -sh ${cleanPath}* | awk '{if ($1 ~ /G$/ && ($1+0) > 1) print}' | sort -rh`,
837
+ { stdout: true },
838
+ );
839
+ for (const pathLog of largeLogsFiles
840
+ .split('\n')
841
+ .map((p) => p.split(cleanPath)[1])
842
+ .filter((p) => p)) {
843
+ shellExec(`sudo rm -rf ${cleanPath}${pathLog}`);
844
+ }
845
+ } catch (err) {
846
+ logger.warn(` -> Skipped log truncation: ${err.message}`);
847
+ }
848
+ },
849
+
850
+ /**
851
+ * @method _cleanHostPathPvs
852
+ * @description Wipes contents of every hostPath PV. Destroys live data —
853
+ * only call when `--remove-volume-host-paths` is set.
854
+ * @private
855
+ */
856
+ _cleanHostPathPvs() {
857
+ try {
858
+ const pvListJson = shellExec(`kubectl get pv -o json || echo '{"items":[]}'`, {
859
+ stdout: true,
860
+ silent: true,
861
+ });
862
+ const pvList = JSON.parse(pvListJson);
863
+ if (!pvList.items || pvList.items.length === 0) {
864
+ logger.info(' -> No PersistentVolumes with hostPath found.');
865
+ return;
866
+ }
867
+ for (const pv of pvList.items) {
868
+ if (pv.spec.hostPath && pv.spec.hostPath.path) {
869
+ const hostPath = pv.spec.hostPath.path;
870
+ logger.info(` -> Removing PV '${pv.metadata.name}' hostPath: ${hostPath}`);
871
+ shellExec(`sudo rm -rf ${hostPath}/*`);
872
+ }
873
+ }
874
+ } catch (error) {
875
+ logger.error(` -> Failed cleaning hostPath PVs: ${error.message}`);
876
+ }
877
+ },
878
+
879
+ /**
880
+ * @method _pruneContainerCaches
881
+ * @description Reclaims container-runtime disk left behind after a cluster
882
+ * teardown: stopped containers, unused images, build cache and anonymous
883
+ * volumes. Best-effort across every runtime present on the host (docker,
884
+ * podman, and optionally the CRI runtime via crictl). Each block is guarded
885
+ * by a command-existence check, so it is safe to call when a runtime is
886
+ * absent. This is what turns a "cluster deleted" into "disk actually freed":
887
+ * `kind delete` / `kubeadm reset` remove the cluster but leave gigabytes of
888
+ * images and overlay layers under /var/lib/{docker,containers}.
889
+ * @param {object} [options]
890
+ * @param {boolean} [options.all=true] - Remove all unused images, not just dangling ones.
891
+ * @param {boolean} [options.crictl=false] - Also prune the CRI runtime via crictl.
892
+ * @param {string} [options.criSocket] - Optional crictl --runtime-endpoint override.
893
+ * @private
894
+ */
895
+ _pruneContainerCaches(options = {}) {
896
+ const all = options.all !== false;
897
+ const a = all ? '-a ' : '';
898
+ logger.info(` -> Pruning container-runtime caches (all=${all})...`);
899
+ // Docker (also matches the podman-docker shim when docker is symlinked to podman).
900
+ shellExec(
901
+ `if command -v docker >/dev/null 2>&1; then sudo docker system prune ${a}--volumes -f; sudo docker builder prune ${a}-f; fi`,
902
+ { silentOnError: true },
903
+ );
904
+ // Podman native — on this host images/overlays live under /var/lib/containers/storage.
905
+ shellExec(`if command -v podman >/dev/null 2>&1; then sudo podman system prune ${a}--volumes -f; fi`, {
906
+ silentOnError: true,
907
+ });
908
+ if (options.crictl) {
909
+ const ep = options.criSocket ? `--runtime-endpoint ${options.criSocket} ` : '';
910
+ shellExec(
911
+ `if command -v crictl >/dev/null 2>&1; then sudo env PATH="$PATH:/usr/local/bin:/usr/bin" crictl ${ep}rmi --prune; fi`,
912
+ { silentOnError: true },
913
+ );
914
+ }
915
+ Underpost.cluster._unmountOrphanContainerOverlays();
916
+ },
917
+
918
+ /**
919
+ * @method _unmountOrphanContainerOverlays
920
+ * @description Unmounts leaked container overlay 'merged' mounts under
921
+ * /var/lib/{containers/storage,docker}/overlay. Pruning images/containers
922
+ * frees the backing data but leaves these mountpoints attached, so they
923
+ * keep showing up as identical `overlay ... /merged` rows in `df -h` long
924
+ * after the containers are gone. Only overlays NOT backing a still-existing
925
+ * container are unmounted, so running workloads are never disturbed.
926
+ * Best-effort; safe to call when no runtime is present.
927
+ * @private
928
+ */
929
+ _unmountOrphanContainerOverlays() {
930
+ logger.info(' -> Unmounting orphaned container overlay mounts...');
931
+ shellExec(`if command -v podman >/dev/null 2>&1; then sudo podman umount --all >/dev/null 2>&1 || true; fi`, {
932
+ silentOnError: true,
933
+ });
934
+ shellExec(
935
+ `active="$(sudo podman ps -aq 2>/dev/null | xargs -r -I{} sudo podman inspect --format '{{.GraphDriver.Data.MergedDir}}' {} 2>/dev/null || true)"
936
+ findmnt -rn -o TARGET 2>/dev/null | grep -E '/var/lib/(containers/storage|docker)/overlay.*/merged' | sort -r | while IFS= read -r m; do
937
+ if ! printf '%s\\n' "$active" | grep -qxF "$m"; then
938
+ echo "Unmounting orphaned overlay: $m"
939
+ sudo umount -l "$m" 2>/dev/null || true
940
+ fi
941
+ done`,
942
+ { silentOnError: true },
943
+ );
944
+ },
945
+
946
+ /**
947
+ * @method _lazyUmountKubeletMounts
948
+ * @description Lazy-unmounts every mount under /var/lib/kubelet so a
949
+ * subsequent `rm -rf` does not hit 'Device or resource busy'. Best-effort.
950
+ * @private
951
+ */
952
+ _lazyUmountKubeletMounts() {
953
+ shellExec(
954
+ `sudo sh -c 'findmnt --raw --noheadings -o TARGET | grep /var/lib/kubelet | sort -r | xargs -r umount -l'`,
955
+ { silentOnError: true },
956
+ );
957
+ },
958
+
959
+ // Per-type reset methods. Each only touches what its own runtime owns.
960
+
961
+ /**
962
+ * @method safeResetKind
963
+ * @description Kind (Kubernetes in Docker) reset — Docker-scoped only.
964
+ * Does not touch host kubelet / containerd / iptables / SELinux.
965
+ * @param {object} [options]
966
+ * @param {string} [options.underpostRoot='.']
967
+ * @param {boolean} [options.removeVolumeHostPaths=false]
968
+ * @memberof UnderpostCluster
969
+ */
970
+ async safeResetKind(options = { underpostRoot: '.', removeVolumeHostPaths: false }) {
971
+ logger.info('=== KIND SAFE RESET (development) ===');
972
+
973
+ logger.info('Phase 1/5: Cleaning Kind node-local MongoDB hostPath directories...');
974
+ Underpost.cluster.cleanKindMongoHostPaths({ basePath: '/data/mongodb', replicaCount: 3 });
975
+
976
+ logger.info('Phase 2/5: PersistentVolume hostPath cleanup...');
977
+ if (options.removeVolumeHostPaths) Underpost.cluster._cleanHostPathPvs();
978
+ else logger.info(' -> Skipping (pass --remove-volume-host-paths to enable).');
979
+
980
+ logger.info('Phase 3/6: Deleting all Kind clusters...');
981
+ shellExec(`clusters=$(kind get clusters)
982
+ if [ -n "$clusters" ]; then
983
+ for c in $clusters; do
984
+ echo "Deleting cluster: $c"
985
+ kind delete cluster --name "$c"
986
+ done
987
+ fi`);
988
+
989
+ logger.info('Phase 4/6: Cleaning kubeconfig and Kind Docker networks...');
990
+ shellExec(`rm -rf "$HOME/.kube"`);
991
+ Underpost.cluster.recoverKindDockerNetworks();
992
+
993
+ logger.info('Phase 5/6: Pruning container-runtime caches (kindest/node images, build cache, volumes)...');
994
+ Underpost.cluster._pruneContainerCaches({ all: true });
995
+
996
+ logger.info('Phase 6/6: Re-applying host configuration (Docker, containerd, sysctl).');
997
+ Underpost.cluster.config();
998
+
999
+ logger.info('=== KIND SAFE RESET COMPLETE ===');
1000
+ },
1001
+
1002
+ /**
1003
+ * @method safeResetKubeadm
1004
+ * @description Kubeadm reset on the host: stop kubelet + runtime, kill
1005
+ * control-plane ports (6443 / 2379 / 2380 / 10257 / 10259), run
1006
+ * `kubeadm reset --force`, wipe kubeadm-managed FS + network state.
1007
+ * Does not touch K3s or Kind state.
1008
+ * @param {object} [options]
1009
+ * @param {string} [options.underpostRoot='.']
1010
+ * @param {boolean} [options.removeVolumeHostPaths=false]
1011
+ * @memberof UnderpostCluster
1012
+ */
1013
+ async safeResetKubeadm(options = { underpostRoot: '.', removeVolumeHostPaths: false }) {
1014
+ logger.info('=== KUBEADM SAFE RESET ===');
1015
+
1016
+ logger.info('Phase 0/7: Truncating large /var/log files...');
1017
+ Underpost.cluster._truncateLargeLogs();
1018
+
1019
+ logger.info('Phase 1/7: PersistentVolume hostPath cleanup...');
1020
+ if (options.removeVolumeHostPaths) Underpost.cluster._cleanHostPathPvs();
1021
+ else logger.info(' -> Skipping (pass --remove-volume-host-paths to enable).');
1022
+
1023
+ logger.info('Phase 2/7: SELinux permissive + restore contexts (when present)...');
1024
+ shellExec(`if command -v setenforce >/dev/null 2>&1; then sudo setenforce 0; fi`);
1025
+ shellExec(
1026
+ `if [ -f /etc/selinux/config ]; then sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config; fi`,
1027
+ );
1028
+ shellExec(
1029
+ `if command -v restorecon >/dev/null 2>&1 && [ -d /var/lib/kubelet ]; then sudo restorecon -Rv /var/lib/kubelet; fi`,
1030
+ );
1031
+ shellExec(
1032
+ `if command -v restorecon >/dev/null 2>&1 && [ -d /var/lib/containerd ]; then sudo restorecon -Rv /var/lib/containerd; fi`,
1033
+ );
1034
+
1035
+ logger.info('Phase 3/7: Stopping host kubelet and container runtimes (kubeadm-scope only)...');
1036
+ shellExec(`if systemctl is-active --quiet kubelet; then sudo systemctl stop kubelet; fi`);
1037
+ shellExec(`if systemctl is-active --quiet docker; then sudo systemctl stop docker; fi`);
1038
+ shellExec(`if systemctl is-active --quiet crio; then sudo systemctl stop crio; fi`);
1039
+ Underpost.cluster._lazyUmountKubeletMounts();
1040
+
1041
+ logger.info('Phase 4/7: Killing control-plane processes and running kubeadm reset...');
1042
+ shellExec(`if command -v crictl >/dev/null 2>&1; then sudo crictl rm -a -f; fi`, { silentOnError: true });
1043
+ // Remove CNI config before stopping sandboxes so Calico's CNI delete hook is
1044
+ // not invoked (the API server is already down and the hook would fail).
1045
+ shellExec(`sudo rm -rf /etc/cni/net.d/*`);
1046
+ shellExec(`if command -v crictl >/dev/null 2>&1; then sudo crictl rmp -a -f; fi`, { silentOnError: true });
1047
+ shellExec(`if systemctl is-active --quiet etcd; then sudo systemctl stop etcd; fi`);
1048
+ for (const port of [6443, 10259, 10257, 2379, 2380]) {
1049
+ shellExec(`if sudo fuser ${port}/tcp >/dev/null 2>&1; then sudo fuser -k ${port}/tcp; fi`);
1050
+ }
1051
+ shellExec(`if command -v kubeadm >/dev/null 2>&1; then sudo kubeadm reset --force; fi`);
1052
+
1053
+ logger.info('Phase 5/7: Filesystem cleanup (kubeadm-managed paths only)...');
1054
+ shellExec(`sudo rm -rf /etc/kubernetes/*`);
1055
+ Underpost.cluster._lazyUmountKubeletMounts();
1056
+ shellExec(`sudo rm -rf /var/lib/kubelet/*`);
1057
+ shellExec(`sudo rm -rf /var/lib/etcd`);
1058
+ shellExec(`sudo rm -rf /var/lib/cni/*`);
1059
+ shellExec(`sudo rm -rf /var/lib/containerd/*`);
1060
+ shellExec(`rm -rf "$HOME/.kube"`);
1061
+
1062
+ logger.info('Phase 6/7: Network cleanup (Calico interfaces + host iptables)...');
1063
+ shellExec(`if ip link show cni0 >/dev/null 2>&1; then sudo ip link del cni0; fi`);
1064
+ shellExec(`if ip link show vxlan.calico >/dev/null 2>&1; then sudo ip link del vxlan.calico; fi`);
1065
+ shellExec(`if ip link show tunl0 >/dev/null 2>&1; then sudo ip link del tunl0; fi`);
1066
+ shellExec(`sudo iptables -F`);
1067
+ shellExec(`sudo iptables -t nat -F`);
1068
+ Underpost.cluster._pruneContainerCaches({ all: true, crictl: true });
1069
+
1070
+ logger.info('Phase 7/7: Re-applying host configuration (Docker, containerd, sysctl).');
1071
+ Underpost.cluster.config();
1072
+
1073
+ logger.info('=== KUBEADM SAFE RESET COMPLETE ===');
1074
+ },
1075
+
1076
+ /**
1077
+ * @method safeResetK3s
1078
+ * @description Centralized K3s teardown. Runs the same way on a physical
1079
+ * host (`node bin cluster --dev --reset --k3s`) or inside an LXD VM via
1080
+ * `lxc exec` (driven by `_resetK3sInVm` in src/cli/lxd.js).
1081
+ * @param {object} [options]
1082
+ * @param {string} [options.underpostRoot='.']
1083
+ * @param {'drain'|'full'} [options.resetMode='full'] - `drain` stops
1084
+ * services + runs `k3s-killall.sh` (K3s persists, returns on next boot).
1085
+ * `full` also runs `k3s-uninstall.sh` and cleans residual state.
1086
+ * @memberof UnderpostCluster
1087
+ */
1088
+ async safeResetK3s(options = { underpostRoot: '.', resetMode: 'full' }) {
1089
+ const resetMode = options.resetMode === 'drain' ? 'drain' : 'full';
1090
+ logger.info(`=== K3s SAFE RESET (resetMode=${resetMode}) ===`);
1091
+
1092
+ logger.info('Phase 1/5: Stopping K3s systemd units...');
1093
+ shellExec(`if systemctl list-unit-files | grep -q '^k3s\\.service'; then sudo systemctl stop k3s; fi`);
1094
+ shellExec(
1095
+ `if systemctl list-unit-files | grep -q '^k3s-agent\\.service'; then sudo systemctl stop k3s-agent; fi`,
1096
+ );
1097
+
1098
+ logger.info('Phase 2/5: Running k3s-killall.sh (unmount pod overlays, tear down CNI)...');
1099
+ shellExec(`if [ -x /usr/local/bin/k3s-killall.sh ]; then sudo /usr/local/bin/k3s-killall.sh; fi`);
1100
+
1101
+ if (resetMode === 'drain') {
1102
+ logger.info('=== K3s DRAIN COMPLETE (K3s remains installed; will start on next boot) ===');
1103
+ return;
1104
+ }
1105
+
1106
+ logger.info('Phase 3/5: Running k3s-uninstall.sh...');
1107
+ shellExec(`if [ -x /usr/local/bin/k3s-uninstall.sh ]; then sudo /usr/local/bin/k3s-uninstall.sh; fi`);
1108
+ shellExec(`if [ -x /usr/local/bin/k3s-agent-uninstall.sh ]; then sudo /usr/local/bin/k3s-agent-uninstall.sh; fi`);
1109
+
1110
+ logger.info('Phase 4/5: Removing residual K3s state...');
1111
+ shellExec(`rm -rf "$HOME/.kube"`);
1112
+ shellExec(`if [ -d /etc/rancher/k3s ]; then sudo rm -rf /etc/rancher/k3s; fi`);
1113
+ shellExec(`if ip link show flannel.1 >/dev/null 2>&1; then sudo ip link del flannel.1; fi`);
1114
+ shellExec(`if ip link show cni0 >/dev/null 2>&1; then sudo ip link del cni0; fi`);
1115
+ // k3s-uninstall.sh removes /var/lib/rancher/k3s; still prune any host docker/podman leftovers.
1116
+ Underpost.cluster._pruneContainerCaches({ all: true });
1117
+
1118
+ logger.info('Phase 5/5: Re-applying minimal K3s host config.');
1119
+ Underpost.cluster.configMinimalK3s();
1120
+
1121
+ logger.info('=== K3s SAFE RESET COMPLETE (full) ===');
1122
+ },
1123
+
1124
+ /**
1125
+ * @method getResourcesCapacity
1126
+ * @description Retrieves the capacity of resources (CPU and memory) for a specific node in the cluster.
1127
+ * @param {string} [node=os.hostname()] - The node to query. Defaults to the current host.
1128
+ * @returns {object} An object containing the CPU and memory capacity of the node.
1129
+ * @memberof UnderpostCluster
1130
+ */
1131
+ getResourcesCapacity(node) {
1132
+ const resources = {};
1133
+ const nodeName = node
1134
+ ? node
1135
+ : Underpost.kubectl.get('kind-control-plane', 'node').length > 0
1136
+ ? 'kind-control-plane'
1137
+ : os.hostname();
1138
+ const info = shellExec(`kubectl describe node ${nodeName} | grep -E '(Allocatable:|Capacity:)' -A 6`, {
1139
+ stdout: true,
1140
+ silent: true,
1141
+ });
1142
+ info
1143
+ .split('Allocatable:')[1]
1144
+ .split('\n')
1145
+ .filter((row) => row.match('cpu') || row.match('memory'))
1146
+ .map((row) => {
1147
+ if (row.match('cpu'))
1148
+ resources.cpu = {
1149
+ value: parseInt(row.split(':')[1].trim()) * 1000,
1150
+ unit: 'm',
1151
+ };
1152
+ if (row.match('memory'))
1153
+ resources.memory = {
1154
+ value: parseInt(row.split(':')[1].split('Ki')[0].trim()),
1155
+ unit: 'Ki',
1156
+ };
1157
+ });
1158
+
1159
+ return resources;
1160
+ },
1161
+
1162
+ /**
1163
+ * @method initHost
1164
+ * @description Installs essential host-level prerequisites for Kubernetes (Docker, Podman, Kind, Kubeadm, Helm).
1165
+ * @memberof UnderpostCluster
1166
+ */
1167
+ initHost() {
1168
+ const archData = Underpost.baremetal.getHostArch();
1169
+ logger.info('Installing essential host-level prerequisites for Kubernetes...', archData);
1170
+
1171
+ // Install base rocky setup and updates
1172
+ shellExec(`node bin run host-update`);
1173
+
1174
+ // Install Docker and its dependencies
1175
+ shellExec(`sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo`);
1176
+ shellExec(`sudo dnf -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin`);
1177
+
1178
+ // Install Podman
1179
+ shellExec(`sudo dnf -y install podman`);
1180
+
1181
+ // Install CRI-O (required for kubeadm with CRI-O socket)
1182
+ shellExec(`node bin run install-crio`);
1183
+
1184
+ // Install Kind (Kubernetes in Docker)
1185
+ shellExec(`[ $(uname -m) = ${archData.name} ] && curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.29.0/kind-linux-${archData.alias}
1186
+ chmod +x ./kind
1187
+ sudo mv ./kind /bin/kind`);
1188
+ // Install Kubernetes tools: Kubeadm, Kubelet, and Kubectl
1189
+ shellExec(`cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
1190
+ [kubernetes]
1191
+ name=Kubernetes
1192
+ baseurl=https://pkgs.k8s.io/core:/stable:/v1.33/rpm/
1193
+ enabled=1
1194
+ gpgcheck=1
1195
+ gpgkey=https://pkgs.k8s.io/core:/stable:/v1.33/rpm/repodata/repomd.xml.key
1196
+ exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
1197
+ EOF`);
1198
+ shellExec(`sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes`);
1199
+
1200
+ // Install Helm — check if already installed and linked to /bin/helm first
1201
+ const helmBin = shellExec(`test -x /bin/helm && echo exists || echo missing`, {
1202
+ stdout: true,
1203
+ silent: true,
1204
+ }).trim();
1205
+ const helmLocalBin = shellExec(`test -x /usr/local/bin/helm && echo exists || echo missing`, {
1206
+ stdout: true,
1207
+ silent: true,
1208
+ }).trim();
1209
+
1210
+ if (helmBin === 'exists') {
1211
+ logger.info('Helm is already installed and linked to /bin/helm; skipping.');
1212
+ } else if (helmLocalBin === 'exists') {
1213
+ // Helm binary exists at /usr/local/bin but not linked to /bin/helm
1214
+ logger.info('Helm found at /usr/local/bin; linking to /bin/helm...');
1215
+ shellExec(`sudo ln -sf /usr/local/bin/helm /bin/helm`);
1216
+ } else {
1217
+ // Helm not installed — download and install
1218
+ shellExec(`curl -fsSL -o get_helm.sh https://cdn.jsdelivr.net/gh/helm/helm@main/scripts/get-helm-3`);
1219
+ shellExec(`chmod 700 get_helm.sh`);
1220
+ // Run get_helm.sh but ignore its exit code — it may fail on PATH check
1221
+ // even when installation succeeds (binary placed at /usr/local/bin).
1222
+ shellExec(`bash ./get_helm.sh || true`);
1223
+ // Ensure the binary is executable and linked to /bin/helm
1224
+ shellExec(`test -x /usr/local/bin/helm && sudo chmod +x /usr/local/bin/helm || true`);
1225
+ shellExec(`test -x /usr/local/bin/helm && sudo ln -sf /usr/local/bin/helm /bin/helm || true`);
1226
+ shellExec(`sudo rm -rf get_helm.sh`);
1227
+ }
1228
+
1229
+ // Install snap
1230
+ shellExec(`sudo yum install -y snapd`);
1231
+ shellExec(`sudo systemctl enable --now snapd.socket`);
1232
+
1233
+ console.log('Host prerequisites installed successfully.');
1234
+ },
1235
+
1236
+ /**
1237
+ * @method uninstallHost
1238
+ * @description Uninstalls all host components installed by initHost.
1239
+ * This includes Docker, Podman, Kind, Kubeadm, Kubelet, Kubectl, and Helm.
1240
+ * @memberof UnderpostCluster
1241
+ */
1242
+ uninstallHost() {
1243
+ console.log('Uninstalling host components: Docker, Podman, Kind, Kubeadm, Kubelet, Kubectl, Helm.');
1244
+
1245
+ // Remove Kind
1246
+ console.log('Removing Kind...');
1247
+ shellExec(`sudo rm -f /bin/kind`);
1248
+
1249
+ // Remove Helm
1250
+ console.log('Removing Helm...');
1251
+ shellExec(`sudo rm -f /usr/local/bin/helm`);
1252
+ shellExec(`sudo rm -f /usr/local/bin/helm.sh`); // clean up the install script if it exists
1253
+
1254
+ // Remove Docker and its dependencies
1255
+ console.log('Removing Docker, containerd, and related packages...');
1256
+ shellExec(`sudo dnf -y remove docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin`);
1257
+
1258
+ // Remove Podman
1259
+ console.log('Removing Podman...');
1260
+ shellExec(`sudo dnf -y remove podman`);
1261
+
1262
+ // Remove CRI-O
1263
+ console.log('Removing CRI-O...');
1264
+ shellExec('sudo systemctl stop crio', { silentOnError: true });
1265
+ shellExec('sudo systemctl disable crio', { silentOnError: true });
1266
+ shellExec(`sudo dnf -y remove cri-o`);
1267
+ shellExec(`sudo rm -f /etc/yum.repos.d/cri-o.repo`);
1268
+ shellExec(`sudo rm -f /etc/crictl.yaml`);
1269
+
1270
+ // Remove Kubeadm, Kubelet, and Kubectl
1271
+ console.log('Removing Kubernetes tools...');
1272
+ shellExec(`sudo yum remove -y kubelet kubeadm kubectl`);
1273
+
1274
+ // Remove Kubernetes repo file
1275
+ console.log('Removing Kubernetes repository configuration...');
1276
+ shellExec(`sudo rm -f /etc/yum.repos.d/kubernetes.repo`);
1277
+
1278
+ // Clean up Kubeadm config and data directories
1279
+ console.log('Cleaning up Kubernetes configuration directories...');
1280
+ shellExec(`sudo rm -rf /etc/kubernetes/pki`);
1281
+ shellExec(`sudo rm -rf ~/.kube`);
1282
+
1283
+ // Stop and disable services
1284
+ console.log('Stopping and disabling services...');
1285
+ shellExec(`sudo systemctl stop docker.service`);
1286
+ shellExec(`sudo systemctl disable docker.service`);
1287
+ shellExec(`sudo systemctl stop containerd.service`);
1288
+ shellExec(`sudo systemctl disable containerd.service`);
1289
+ shellExec(`sudo systemctl stop kubelet.service`);
1290
+ shellExec(`sudo systemctl disable kubelet.service`);
1291
+
1292
+ // Clean up config files
1293
+ console.log('Removing host configuration files...');
1294
+ shellExec(`sudo rm -f /etc/containerd/config.toml`);
1295
+ shellExec(`sudo rm -f /etc/sysctl.d/k8s.conf`);
1296
+ shellExec(`sudo rm -f /etc/sysctl.d/99-k8s-ipforward.conf`);
1297
+ shellExec(`sudo rm -f /etc/sysctl.d/99-k8s.conf`);
1298
+
1299
+ // Restore SELinux to enforcing
1300
+ console.log('Restoring SELinux to enforcing mode...');
1301
+ // shellExec(`sudo setenforce 1`);
1302
+ // shellExec(`sudo sed -i 's/^SELINUX=permissive$/SELINUX=enforcing/' /etc/selinux/config`);
1303
+
1304
+ console.log('Uninstall process completed.');
1305
+ },
1306
+
1307
+ /**
1308
+ * @method cleanKindMongoHostPaths
1309
+ * @description Best-effort cleanup of MongoDB hostPath directories inside Kind node containers.
1310
+ * This prevents stale replica/auth state when hostPath data lives in node-local container filesystems.
1311
+ * @param {object} [options]
1312
+ * @param {string} [options.basePath='/data/mongodb'] - Node-internal base path for MongoDB data.
1313
+ * @param {number} [options.replicaCount=3] - Number of replica ordinal directories (v0..vN-1).
1314
+ * @memberof UnderpostCluster
1315
+ */
1316
+ cleanKindMongoHostPaths(options = { basePath: '/data/mongodb', replicaCount: 3 }) {
1317
+ const basePath = options.basePath || '/data/mongodb';
1318
+ const replicaCount = Math.max(Number(options.replicaCount) || 3, 1);
1319
+ const nodesRaw = shellExec('kind get nodes', {
1320
+ stdout: true,
1321
+ silent: true,
1322
+ silentOnError: true,
1323
+ });
1324
+ const nodes = nodesRaw
1325
+ .split('\n')
1326
+ .map((node) => node.trim())
1327
+ .filter((node) => !!node);
1328
+
1329
+ if (nodes.length === 0) {
1330
+ logger.info('No Kind nodes detected for node-local MongoDB hostPath cleanup.');
1331
+ return;
1332
+ }
1333
+
1334
+ for (const node of nodes) {
1335
+ logger.info(
1336
+ `Cleaning Kind node-local MongoDB paths '${basePath}/v0..v${replicaCount - 1}' on node '${node}'...`,
1337
+ );
1338
+ const prepareReplicaDirsCmd = Array.from(
1339
+ { length: replicaCount },
1340
+ (_, index) => `mkdir -p ${basePath}/v${index}; rm -rf ${basePath}/v${index}/*;`,
1341
+ ).join(' ');
1342
+ const verifyReplicaDirsCmd = Array.from(
1343
+ { length: replicaCount },
1344
+ (_, index) => `test -d ${basePath}/v${index};`,
1345
+ ).join(' ');
1346
+ shellExec(`sudo docker exec ${node} sh -lc 'mkdir -p ${basePath}; ${prepareReplicaDirsCmd}'`, {
1347
+ silentOnError: true,
1348
+ });
1349
+ shellExec(`sudo docker exec ${node} sh -lc '${verifyReplicaDirsCmd}'`);
1350
+ }
1351
+ },
1352
+
1353
+ /**
1354
+ * @method recoverKindDockerNetworks
1355
+ * @description Best-effort cleanup of stale Kind Docker resources when Docker bridge
1356
+ * address pools are exhausted and new networks cannot be allocated.
1357
+ * @memberof UnderpostCluster
1358
+ */
1359
+ recoverKindDockerNetworks() {
1360
+ logger.warn('Attempting Docker network recovery for Kind (address pool exhaustion detected)...');
1361
+ shellExec(`sudo docker ps -aq --filter label=io.x-k8s.kind.cluster | xargs -r sudo docker rm -f`, {
1362
+ silentOnError: true,
1363
+ });
1364
+ shellExec(`sudo docker network ls -q --filter label=io.x-k8s.kind.cluster | xargs -r sudo docker network rm`, {
1365
+ silentOnError: true,
1366
+ });
1367
+ shellExec(`sudo docker network rm kind`, { silentOnError: true });
1368
+ shellExec(`sudo docker network prune -f`, { silentOnError: true });
1369
+ },
1370
+
1371
+ /**
1372
+ * @method ensureDockerDefaultAddressPools
1373
+ * @description Writes a sane Docker default-address-pools config to reduce
1374
+ * Kind network allocation failures on hosts with exhausted predefined pools.
1375
+ * @memberof UnderpostCluster
1376
+ */
1377
+ ensureDockerDefaultAddressPools() {
1378
+ logger.warn('Applying Docker default-address-pools workaround for Kind network creation...');
1379
+ shellExec(`cat <<'EOF' | sudo tee /etc/docker/daemon.json
1380
+ {
1381
+ "default-address-pools": [
1382
+ {"base": "172.17.0.0/16", "size": 24},
1383
+ {"base": "172.18.0.0/16", "size": 24},
1384
+ {"base": "172.19.0.0/16", "size": 24},
1385
+ {"base": "172.20.0.0/14", "size": 24},
1386
+ {"base": "172.24.0.0/14", "size": 24}
1387
+ ]
1388
+ }
1389
+ EOF`);
1390
+ shellExec('sudo systemctl restart docker');
1391
+ shellExec('sudo docker network prune -f', { silentOnError: true });
1392
+ },
1393
+ };
1394
+ }
1395
+ export default UnderpostCluster;