@underpostnet/cyberia 3.3.0 → 3.3.73

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 (471) hide show
  1. package/.dockerignore +1 -0
  2. package/.github/workflows/coverall.cyberia.ci.yml +34 -14
  3. package/.github/workflows/cyberia-client.cd.yml +5 -0
  4. package/.github/workflows/cyberia-server.cd.yml +5 -0
  5. package/.github/workflows/docker-image.cyberia-client.ci.yml +8 -5
  6. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +8 -3
  7. package/.github/workflows/docker-image.cyberia-server.ci.yml +8 -5
  8. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +8 -5
  9. package/.github/workflows/docker-image.engine-cyberia.ci.yml +15 -1
  10. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +15 -1
  11. package/.github/workflows/engine-cyberia.cd.yml +3 -0
  12. package/.github/workflows/engine-cyberia.ci.yml +4 -5
  13. package/.github/workflows/ghpkg.ci.yml +35 -20
  14. package/.github/workflows/hardhat.ci.yml +12 -11
  15. package/.github/workflows/npmpkg.ci.yml +17 -15
  16. package/.github/workflows/pwa-microservices-template-test.ci.yml +2 -2
  17. package/.github/workflows/release.cd.yml +1 -0
  18. package/.prettierignore +1 -0
  19. package/AGENTS.md +47 -0
  20. package/CHANGELOG.md +565 -2510
  21. package/CLI-HELP.md +306 -147
  22. package/Dockerfile +25 -9
  23. package/Dockerfile.dev +25 -9
  24. package/Dockerfile.test +4 -4
  25. package/README.md +21 -1
  26. package/bin/build.js +66 -41
  27. package/bin/build.template.js +8 -3
  28. package/bin/cyberia.js +308 -91
  29. package/bin/deploy.js +18 -104
  30. package/bin/index.js +308 -91
  31. package/conf.js +91 -4
  32. package/deploy/cyberia-client/deploy.sh +47 -0
  33. package/deploy/cyberia-client/package.sh +18 -0
  34. package/deploy/cyberia-client/state.sh +14 -0
  35. package/deploy/cyberia-server/deploy.sh +47 -0
  36. package/deploy/cyberia-server/package.sh +18 -0
  37. package/deploy/cyberia-server/state.sh +14 -0
  38. package/deploy/dd-cyberia/deploy.sh +6 -26
  39. package/deploy/dd-cyberia/init.sh +35 -43
  40. package/deploy/dd-cyberia/package.sh +17 -0
  41. package/deploy/dd-cyberia/state.sh +14 -0
  42. package/deploy/dd-cyberia/sync-deploy.sh +92 -170
  43. package/deploy/lib/github-actions-logging.sh +594 -0
  44. package/deploy/lib/host.sh +185 -0
  45. package/deploy/lib/state.sh +62 -0
  46. package/deploy/pwa-microservices-template/deploy.sh +12 -39
  47. package/deploy/release/deploy.sh +9 -39
  48. package/deployment.yaml +7 -2
  49. package/docker-compose.yml +7 -7
  50. package/examples/static-page/README.md +19 -19
  51. package/examples/static-page/ssr-components/CustomPage.js +2 -2
  52. package/hardhat/hardhat.config.js +1 -6
  53. package/hardhat/package-lock.json +101 -40
  54. package/hardhat/package.json +2 -2
  55. package/manifests/alertmanager/deployment.yaml +81 -0
  56. package/manifests/blackbox-exporter/deployment.yaml +75 -0
  57. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +32 -7
  58. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +32 -7
  59. package/manifests/deployment/dd-cyberia-development/deployment.yaml +7 -2
  60. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  61. package/manifests/grafana/deployment.yaml +26 -7
  62. package/manifests/grafana/kustomization.yaml +1 -0
  63. package/manifests/grafana/service.yaml +6 -1
  64. package/manifests/mariadb/pv.yaml +1 -0
  65. package/manifests/mongodb/statefulset.yaml +9 -2
  66. package/manifests/mysql/pv-pvc.yaml +14 -1
  67. package/manifests/postgresql/pv.yaml +12 -1
  68. package/manifests/postgresql/pvc.yaml +1 -0
  69. package/manifests/prometheus/deployment.yaml +18 -22
  70. package/package.json +83 -10
  71. package/scripts/event-monitor.sh +56 -0
  72. package/scripts/k3s-node-setup.sh +27 -14
  73. package/scripts/kubeadm-node-setup.sh +31 -11
  74. package/scripts/maas-setup.sh +8 -8
  75. package/scripts/selinux-normalize.sh +907 -0
  76. package/scripts/shutdown-machine.sh +843 -0
  77. package/scripts/test-monitor.sh +3 -3
  78. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +1 -1
  79. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +2 -2
  80. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +2 -2
  81. package/src/api/core/core.controller.js +1 -1
  82. package/src/api/core/core.router.js +2 -2
  83. package/src/api/core/core.service.js +2 -2
  84. package/src/api/crypto/crypto.controller.js +1 -1
  85. package/src/api/crypto/crypto.service.js +1 -1
  86. package/src/api/cyberia-action/cyberia-action.controller.js +1 -1
  87. package/src/api/cyberia-action/cyberia-action.router.js +1 -1
  88. package/src/api/cyberia-action/cyberia-action.service.js +2 -2
  89. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -1
  90. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +1 -1
  91. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +2 -2
  92. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +1 -1
  93. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
  94. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +2 -2
  95. package/src/api/cyberia-entity/cyberia-entity.controller.js +1 -1
  96. package/src/api/cyberia-entity/cyberia-entity.router.js +1 -1
  97. package/src/api/cyberia-entity/cyberia-entity.service.js +2 -2
  98. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +1 -1
  99. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +1 -1
  100. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +2 -2
  101. package/src/api/cyberia-instance/cyberia-fallback-capture.js +466 -0
  102. package/src/api/cyberia-instance/cyberia-fallback-world.js +8 -0
  103. package/src/api/cyberia-instance/cyberia-instance-map.service.js +1 -1
  104. package/src/api/cyberia-instance/cyberia-instance.controller.js +1 -1
  105. package/src/api/cyberia-instance/cyberia-instance.router.js +2 -2
  106. package/src/api/cyberia-instance/cyberia-instance.service.js +2 -2
  107. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +1 -1
  108. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +1 -1
  109. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +2 -2
  110. package/src/api/cyberia-map/cyberia-map.controller.js +1 -1
  111. package/src/api/cyberia-map/cyberia-map.router.js +1 -1
  112. package/src/api/cyberia-map/cyberia-map.service.js +2 -2
  113. package/src/api/cyberia-quest/cyberia-quest.controller.js +1 -1
  114. package/src/api/cyberia-quest/cyberia-quest.router.js +1 -1
  115. package/src/api/cyberia-quest/cyberia-quest.service.js +2 -2
  116. package/src/api/cyberia-quest-progress/cyberia-quest-progress.controller.js +1 -1
  117. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +1 -1
  118. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +2 -2
  119. package/src/api/cyberia-saga/cyberia-saga.controller.js +1 -1
  120. package/src/api/cyberia-saga/cyberia-saga.router.js +1 -1
  121. package/src/api/cyberia-saga/cyberia-saga.service.js +2 -2
  122. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +0 -26
  123. package/src/api/cyberia-skill/cyberia-skill.controller.js +1 -1
  124. package/src/api/cyberia-skill/cyberia-skill.router.js +1 -1
  125. package/src/api/cyberia-skill/cyberia-skill.service.js +2 -2
  126. package/src/api/default/default.controller.js +1 -1
  127. package/src/api/default/default.router.js +1 -1
  128. package/src/api/default/default.service.js +2 -2
  129. package/src/api/document/document.controller.js +1 -1
  130. package/src/api/document/document.router.js +1 -1
  131. package/src/api/document/document.service.js +2 -2
  132. package/src/api/file/file.controller.js +1 -1
  133. package/src/api/file/file.router.js +1 -1
  134. package/src/api/file/file.service.js +2 -2
  135. package/src/api/instance/instance.controller.js +1 -1
  136. package/src/api/instance/instance.router.js +1 -1
  137. package/src/api/instance/instance.service.js +2 -2
  138. package/src/api/ipfs/ipfs.controller.js +1 -1
  139. package/src/api/ipfs/ipfs.router.js +2 -2
  140. package/src/api/ipfs/ipfs.service.js +2 -2
  141. package/src/api/object-layer/object-layer.controller.js +1 -1
  142. package/src/api/object-layer/object-layer.router.js +2 -2
  143. package/src/api/object-layer/object-layer.service.js +3 -3
  144. package/src/api/object-layer-render-frames/object-layer-render-frames.controller.js +1 -1
  145. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +1 -1
  146. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +2 -2
  147. package/src/api/test/test.controller.js +1 -1
  148. package/src/api/test/test.service.js +1 -1
  149. package/src/api/user/guest.service.js +3 -3
  150. package/src/api/user/user.controller.js +1 -1
  151. package/src/api/user/user.router.js +2 -2
  152. package/src/api/user/user.service.js +4 -4
  153. package/src/api.js +5 -9
  154. package/src/cli/app.js +391 -0
  155. package/src/cli/baremetal.js +30 -35
  156. package/src/cli/client.js +306 -0
  157. package/src/cli/cloud-init.js +3 -3
  158. package/src/cli/cluster.js +184 -82
  159. package/src/cli/db.js +120 -68
  160. package/src/cli/deploy.js +153 -71
  161. package/src/cli/docker-compose.js +5 -5
  162. package/src/cli/domains.js +184 -0
  163. package/src/cli/dotenv-store.js +143 -0
  164. package/src/cli/event.js +2281 -0
  165. package/src/cli/fs.js +3 -3
  166. package/src/cli/host.js +672 -0
  167. package/src/cli/image.js +4 -6
  168. package/src/cli/index.js +322 -142
  169. package/src/cli/ipfs.js +34 -30
  170. package/src/cli/kickstart.js +1 -1
  171. package/src/cli/kubectl.js +218 -41
  172. package/src/cli/lxd.js +3 -3
  173. package/src/cli/monitor.js +1359 -26
  174. package/src/cli/package.js +90 -0
  175. package/src/cli/release.js +16 -12
  176. package/src/cli/repository.js +558 -363
  177. package/src/cli/run.js +331 -650
  178. package/src/cli/secrets.js +2158 -964
  179. package/src/cli/ssh.js +478 -279
  180. package/src/cli/state.js +394 -0
  181. package/src/cli/static.js +2 -2
  182. package/src/cli/system.js +1 -1
  183. package/src/cli/test.js +199 -125
  184. package/src/cli/vultr.js +61 -21
  185. package/src/cli/wireguard.js +1111 -306
  186. package/src/client/components/core/CalendarCore.js +53 -96
  187. package/src/client/components/core/Docs.js +258 -211
  188. package/src/client/components/core/FileExplorer.js +0 -21
  189. package/src/client/components/core/PanelForm.js +4 -4
  190. package/src/client/components/core/Repository.js +56 -0
  191. package/src/client/components/core/Translate.js +7 -0
  192. package/src/client/components/core/{FullScreen.js → ViewModeController.js} +42 -39
  193. package/src/client/components/core/Wallet.js +0 -11
  194. package/src/client/components/core/Worker.js +7 -1
  195. package/src/client/components/cryptokoyn/SettingsCryptokoyn.js +2 -2
  196. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +2 -1
  197. package/src/client/components/cyberia-portal/SettingsCyberiaPortal.js +2 -2
  198. package/src/client/components/default/AppShellDefault.js +1 -1
  199. package/src/client/components/default/SettingsDefault.js +2 -2
  200. package/src/client/components/itemledger/SettingsItemledger.js +2 -2
  201. package/src/client/components/underpost/AppShellUnderpost.js +3 -2
  202. package/src/client/components/underpost/SettingsUnderpost.js +2 -2
  203. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +32 -5
  204. package/src/client/public/cyberia-docs/CYBERIA.md +21 -1
  205. package/src/client/public/cyberia-docs/HARDHAT-MODULE.md +23 -6
  206. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  207. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  208. package/src/client/public/default/android-chrome-144x144.png +0 -0
  209. package/src/client/public/default/android-chrome-192x192.png +0 -0
  210. package/src/client/public/default/android-chrome-256x256.png +0 -0
  211. package/src/client/public/default/android-chrome-36x36.png +0 -0
  212. package/src/client/public/default/android-chrome-384x384.png +0 -0
  213. package/src/client/public/default/android-chrome-48x48.png +0 -0
  214. package/src/client/public/default/android-chrome-512x512.png +0 -0
  215. package/src/client/public/default/android-chrome-72x72.png +0 -0
  216. package/src/client/public/default/android-chrome-96x96.png +0 -0
  217. package/src/client/public/default/apple-touch-icon-1024x1024.png +0 -0
  218. package/src/client/public/default/apple-touch-icon-114x114.png +0 -0
  219. package/src/client/public/default/apple-touch-icon-120x120.png +0 -0
  220. package/src/client/public/default/apple-touch-icon-144x144.png +0 -0
  221. package/src/client/public/default/apple-touch-icon-152x152.png +0 -0
  222. package/src/client/public/default/apple-touch-icon-167x167.png +0 -0
  223. package/src/client/public/default/apple-touch-icon-180x180.png +0 -0
  224. package/src/client/public/default/apple-touch-icon-57x57.png +0 -0
  225. package/src/client/public/default/apple-touch-icon-60x60.png +0 -0
  226. package/src/client/public/default/apple-touch-icon-72x72.png +0 -0
  227. package/src/client/public/default/apple-touch-icon-76x76.png +0 -0
  228. package/src/client/public/default/apple-touch-icon-precomposed.png +0 -0
  229. package/src/client/public/default/apple-touch-icon.png +0 -0
  230. package/src/client/public/default/apple-touch-startup-image-1125x2436.png +0 -0
  231. package/src/client/public/default/apple-touch-startup-image-1136x640.png +0 -0
  232. package/src/client/public/default/apple-touch-startup-image-1170x2532.png +0 -0
  233. package/src/client/public/default/apple-touch-startup-image-1179x2556.png +0 -0
  234. package/src/client/public/default/apple-touch-startup-image-1242x2208.png +0 -0
  235. package/src/client/public/default/apple-touch-startup-image-1242x2688.png +0 -0
  236. package/src/client/public/default/apple-touch-startup-image-1284x2778.png +0 -0
  237. package/src/client/public/default/apple-touch-startup-image-1290x2796.png +0 -0
  238. package/src/client/public/default/apple-touch-startup-image-1334x750.png +0 -0
  239. package/src/client/public/default/apple-touch-startup-image-1488x2266.png +0 -0
  240. package/src/client/public/default/apple-touch-startup-image-1536x2048.png +0 -0
  241. package/src/client/public/default/apple-touch-startup-image-1620x2160.png +0 -0
  242. package/src/client/public/default/apple-touch-startup-image-1640x2160.png +0 -0
  243. package/src/client/public/default/apple-touch-startup-image-1668x2224.png +0 -0
  244. package/src/client/public/default/apple-touch-startup-image-1668x2388.png +0 -0
  245. package/src/client/public/default/apple-touch-startup-image-1792x828.png +0 -0
  246. package/src/client/public/default/apple-touch-startup-image-2048x1536.png +0 -0
  247. package/src/client/public/default/apple-touch-startup-image-2048x2732.png +0 -0
  248. package/src/client/public/default/apple-touch-startup-image-2160x1620.png +0 -0
  249. package/src/client/public/default/apple-touch-startup-image-2160x1640.png +0 -0
  250. package/src/client/public/default/apple-touch-startup-image-2208x1242.png +0 -0
  251. package/src/client/public/default/apple-touch-startup-image-2224x1668.png +0 -0
  252. package/src/client/public/default/apple-touch-startup-image-2266x1488.png +0 -0
  253. package/src/client/public/default/apple-touch-startup-image-2388x1668.png +0 -0
  254. package/src/client/public/default/apple-touch-startup-image-2436x1125.png +0 -0
  255. package/src/client/public/default/apple-touch-startup-image-2532x1170.png +0 -0
  256. package/src/client/public/default/apple-touch-startup-image-2556x1179.png +0 -0
  257. package/src/client/public/default/apple-touch-startup-image-2688x1242.png +0 -0
  258. package/src/client/public/default/apple-touch-startup-image-2732x2048.png +0 -0
  259. package/src/client/public/default/apple-touch-startup-image-2778x1284.png +0 -0
  260. package/src/client/public/default/apple-touch-startup-image-2796x1290.png +0 -0
  261. package/src/client/public/default/apple-touch-startup-image-640x1136.png +0 -0
  262. package/src/client/public/default/apple-touch-startup-image-750x1334.png +0 -0
  263. package/src/client/public/default/apple-touch-startup-image-828x1792.png +0 -0
  264. package/src/client/public/default/assets/background/white.jpg +0 -0
  265. package/src/client/public/default/browserconfig.xml +1 -1
  266. package/src/client/public/default/favicon-16x16.png +0 -0
  267. package/src/client/public/default/favicon-32x32.png +0 -0
  268. package/src/client/public/default/favicon-48x48.png +0 -0
  269. package/src/client/public/default/favicon.ico +0 -0
  270. package/src/client/public/default/manifest.webmanifest +5 -5
  271. package/src/client/public/default/mstile-144x144.png +0 -0
  272. package/src/client/public/default/mstile-150x150.png +0 -0
  273. package/src/client/public/default/mstile-310x150.png +0 -0
  274. package/src/client/public/default/mstile-310x310.png +0 -0
  275. package/src/client/public/default/mstile-70x70.png +0 -0
  276. package/src/client/public/default/yandex-browser-50x50.png +0 -0
  277. package/src/client/public/default/yandex-browser-manifest.json +1 -1
  278. package/src/client/ssr/RootDocument.js +1 -1
  279. package/src/client/ssr/body/404.js +1 -1
  280. package/src/client/ssr/body/500.js +1 -1
  281. package/src/client/ssr/body/CacheControl.js +1 -1
  282. package/src/client/ssr/body/CyberiaDefaultSplashScreen.js +1 -1
  283. package/src/client/ssr/body/DefaultSplashScreen.js +1 -1
  284. package/src/client/ssr/body/SwaggerDarkMode.js +1 -1
  285. package/src/client/ssr/body/UnderpostDefaultSplashScreen.js +1 -1
  286. package/src/client/ssr/head/CryptokoynScripts.js +1 -1
  287. package/src/client/ssr/head/Css.js +1 -1
  288. package/src/client/ssr/head/CyberiaPortalScripts.js +1 -1
  289. package/src/client/ssr/head/DefaultScripts.js +1 -1
  290. package/src/client/ssr/head/ItemledgerScripts.js +1 -1
  291. package/src/client/ssr/head/Microdata.js +1 -1
  292. package/src/client/ssr/head/Production.js +1 -1
  293. package/src/client/ssr/head/Pwa.js +1 -1
  294. package/src/client/ssr/head/PwaItemledger.js +1 -1
  295. package/src/client/ssr/head/Seo.js +1 -1
  296. package/src/client/ssr/head/UnderpostScripts.js +1 -1
  297. package/src/client/ssr/mailer/DefaultRecoverEmail.js +1 -1
  298. package/src/client/ssr/mailer/DefaultVerifyEmail.js +1 -1
  299. package/src/client/ssr/views/Cyberia404.js +7 -5
  300. package/src/client/ssr/views/CyberiaServerMetrics.js +1 -1
  301. package/src/client/ssr/views/Maintenance.js +1 -1
  302. package/src/client/ssr/views/NoNetworkConnection.js +1 -1
  303. package/src/client/ssr/views/Test.js +1 -1
  304. package/src/client/sw/core.sw.js +20 -14
  305. package/src/client-builder/client-build-docs.js +61 -46
  306. package/src/client-builder/client-build-live.js +2 -2
  307. package/src/client-builder/client-build.js +38 -30
  308. package/src/client-builder/client-dev-server.js +3 -3
  309. package/src/client-builder/client-icons.js +2 -2
  310. package/src/client-builder/ssr.js +3 -3
  311. package/src/client.build.js +3 -6
  312. package/src/client.dev.js +3 -6
  313. package/src/db/DataBaseProvider.js +186 -3
  314. package/src/db/mariadb/MariaDB.js +3 -2
  315. package/src/db/mongo/MongoBootstrap.js +273 -101
  316. package/src/db/mongo/MongooseDB.js +7 -8
  317. package/src/{server/valkey.js → db/valkey/Valkey.js} +5 -4
  318. package/src/grpc/cyberia/grpc-server.js +1 -1
  319. package/src/index.js +79 -23
  320. package/src/mailer/EmailRender.js +2 -2
  321. package/src/mailer/MailerInterceptor.js +87 -0
  322. package/src/mailer/MailerProvider.js +50 -6
  323. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +1 -1
  324. package/src/projects/cyberia/besu-genesis-generator.js +3 -3
  325. package/src/projects/cyberia/catalog-cyberia.js +53 -10
  326. package/src/projects/cyberia/gemini-client.js +1 -1
  327. package/src/projects/cyberia/generate-saga.js +1 -1
  328. package/src/projects/cyberia/hot-reload-trigger.js +1 -1
  329. package/src/projects/cyberia/instance-data.js +1 -1
  330. package/src/projects/cyberia/ipfs-client.js +4 -4
  331. package/src/projects/cyberia/map-preview-generator.js +1 -1
  332. package/src/projects/cyberia/object-layer.js +1 -1
  333. package/src/projects/cyberia/semantic-layer-generator.js +1 -1
  334. package/src/projects/underpost/catalog-underpost.js +9 -1
  335. package/src/proxy.js +4 -7
  336. package/src/runtime/cyberia-client/Dockerfile +9 -4
  337. package/src/runtime/cyberia-client/Dockerfile.dev +8 -2
  338. package/src/runtime/cyberia-server/Dockerfile +9 -4
  339. package/src/runtime/cyberia-server/Dockerfile.dev +8 -2
  340. package/src/runtime/engine-cyberia/Dockerfile +25 -9
  341. package/src/runtime/engine-cyberia/Dockerfile.dev +25 -9
  342. package/src/runtime/engine-cyberia/Dockerfile.test +4 -4
  343. package/src/runtime/engine-cyberia/docker-compose.yml +7 -7
  344. package/src/runtime/express/Express.js +32 -27
  345. package/src/runtime/lampp/Lampp.js +22 -70
  346. package/src/runtime/nginx/Nginx.js +1 -1
  347. package/src/runtime/wp/Wp.js +16 -48
  348. package/src/server/{catalog.js → build/catalog.js} +15 -5
  349. package/src/server/build/coverage.js +117 -0
  350. package/src/server/build/execution.js +402 -0
  351. package/src/server/build/package.js +465 -0
  352. package/src/server/build/testing.js +528 -0
  353. package/src/server/{dns.js → network/dns.js} +144 -11
  354. package/src/server/{forward-proxy.js → network/forward-proxy.js} +10 -18
  355. package/src/server/{middlewares.js → network/middlewares.js} +3 -3
  356. package/src/server/network/node-capability.js +98 -0
  357. package/src/server/{peer.js → network/peer.js} +3 -3
  358. package/src/server/{proxy.js → network/proxy.js} +7 -13
  359. package/src/server/network/router.js +356 -0
  360. package/src/server/{tls.js → network/tls.js} +2 -2
  361. package/src/server/{underpost-compression.js → network/underpost-compression.js} +1 -1
  362. package/src/server/{underpost-gateway.js → network/underpost-gateway.js} +7 -7
  363. package/src/server/{underpost-ingress.js → network/underpost-ingress.js} +2 -2
  364. package/src/server/{cri.js → ops/cri.js} +2 -2
  365. package/src/server/{cron.js → ops/cron.js} +349 -74
  366. package/src/server/ops/event-notification.js +284 -0
  367. package/src/server/{logger.js → ops/logger.js} +108 -23
  368. package/src/server/ops/monitoring.js +1724 -0
  369. package/src/server/{systemd.js → ops/systemd.js} +55 -1
  370. package/src/server/{conf.js → runtime/conf.js} +513 -422
  371. package/src/server/runtime/config-scope.js +215 -0
  372. package/src/server/runtime/environment.js +149 -0
  373. package/src/server/{process.js → runtime/process.js} +153 -12
  374. package/src/server/{runtime-status.js → runtime/runtime-status.js} +155 -16
  375. package/src/server/{runtime.js → runtime/runtime.js} +12 -8
  376. package/src/server/{start.js → runtime/start.js} +128 -39
  377. package/src/server/{auth.js → security/auth.js} +7 -8
  378. package/src/server/security/container-storage.js +382 -0
  379. package/src/server/{crypto.js → security/crypto.js} +1 -1
  380. package/src/server/{selinux.js → security/selinux.js} +1 -1
  381. package/src/server/{backup.js → storage/backup.js} +17 -8
  382. package/src/server/{data-query.js → storage/data-query.js} +1 -1
  383. package/src/server/{downloader.js → storage/downloader.js} +2 -2
  384. package/src/server/storage/repository.js +68 -0
  385. package/src/server.js +4 -7
  386. package/src/ws/IoInterface.js +1 -1
  387. package/src/ws/IoServer.js +1 -1
  388. package/src/ws/core/core.ws.connection.js +1 -1
  389. package/src/ws/core/core.ws.emit.js +1 -1
  390. package/src/ws/core/core.ws.server.js +1 -1
  391. package/src/ws/default/default.ws.connection.js +1 -1
  392. package/src/ws/default/default.ws.emit.js +1 -1
  393. package/src/ws/default/default.ws.server.js +1 -1
  394. package/test/e2e/event-e2e-public-ingress-down.js +45 -0
  395. package/test/e2e/event-e2e-wireguard-server-down.js +38 -0
  396. package/test/e2e/event-e2e-wireguard-spoke-down.js +40 -0
  397. package/test/{api.test.js → integration/app/api.test.js} +33 -14
  398. package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +30 -0
  399. package/test/{cyberia-instance-conf-defaults.test.js → integration/app/cyberia/cyberia-instance-conf-defaults.test.js} +1 -1
  400. package/test/integration/app/cyberia/cyberia-load.test.js +467 -0
  401. package/test/integration/app/cyberia/fallback-world-capture.test.js +253 -0
  402. package/test/integration/app/cyberia/object-layer-item-id.test.js +327 -0
  403. package/test/{shape-generator.test.js → integration/app/cyberia/shape-generator.test.js} +1 -1
  404. package/test/integration/infra/1-security/config-scope.test.js +305 -0
  405. package/test/integration/infra/1-security/container-storage.test.js +356 -0
  406. package/test/integration/infra/1-security/in-pod-cli-surface.test.js +86 -0
  407. package/test/integration/infra/1-security/secret-onboarding.test.js +930 -0
  408. package/test/{selinux.test.js → integration/infra/1-security/selinux.test.js} +2 -4
  409. package/test/{sops-secret-store.test.js → integration/infra/1-security/sops-secret-store.test.js} +415 -61
  410. package/test/integration/infra/1-security/systemd-service.test.js +70 -0
  411. package/test/integration/infra/1-security/underpost-config-secret.test.js +367 -0
  412. package/test/integration/infra/2-network/dns-firewall.test.js +566 -0
  413. package/test/integration/infra/2-network/node-capability.test.js +182 -0
  414. package/test/integration/infra/2-network/wireguard-cli.test.js +1163 -0
  415. package/test/{wireguard-edge.test.js → integration/infra/2-network/wireguard-edge.test.js} +609 -55
  416. package/test/{cluster-instances.test.js → integration/infra/3-cluster/cluster-instances.test.js} +11 -15
  417. package/test/{deploy-node-placement.test.js → integration/infra/3-cluster/deploy-node-placement.test.js} +3 -3
  418. package/test/integration/infra/3-cluster/docker-compose-stack.test.js +313 -0
  419. package/test/integration/infra/4-ingress/deploy-routes.test.js +379 -0
  420. package/test/integration/infra/4-ingress/gateway-static-assets.test.js +453 -0
  421. package/test/{instance-traffic-plan.test.js → integration/infra/4-ingress/instance-traffic-plan.test.js} +10 -6
  422. package/test/{underpost-gateway.test.js → integration/infra/4-ingress/underpost-gateway.test.js} +6 -6
  423. package/test/{underpost-ingress.test.js → integration/infra/4-ingress/underpost-ingress.test.js} +4 -4
  424. package/test/integration/infra/5-observability/cron-jobs.test.js +652 -0
  425. package/test/{deploy-monitor.test.js → integration/infra/5-observability/deploy-monitor.test.js} +29 -31
  426. package/test/integration/infra/5-observability/event-notification.test.js +197 -0
  427. package/test/integration/infra/5-observability/event-remediation.test.js +502 -0
  428. package/test/integration/infra/5-observability/event-targets.test.js +1174 -0
  429. package/test/integration/infra/5-observability/monitoring-stack.test.js +902 -0
  430. package/test/support/shell-harness.js +73 -0
  431. package/test/unit/build-template.test.js +97 -0
  432. package/test/unit/build-workflow-offline.test.js +132 -0
  433. package/test/unit/catalog.test.js +71 -0
  434. package/test/unit/client-build-docs.test.js +334 -0
  435. package/test/unit/conf-loading.test.js +303 -0
  436. package/test/unit/conf-resolution.test.js +979 -0
  437. package/test/unit/coverage-artifact.test.js +130 -0
  438. package/test/{crypto.test.js → unit/crypto.test.js} +1 -1
  439. package/test/unit/deploy-env-resolution.test.js +38 -0
  440. package/test/unit/deploy-log-table.test.js +447 -0
  441. package/test/unit/execution-profiles.test.js +219 -0
  442. package/test/unit/fleet-sync-source.test.js +149 -0
  443. package/test/unit/logger-redaction.test.js +112 -0
  444. package/test/unit/package.test.js +403 -0
  445. package/test/unit/prepare-host.test.js +299 -0
  446. package/test/unit/process-environment.test.js +18 -0
  447. package/test/unit/propagation-message.test.js +78 -0
  448. package/test/unit/redeploy-plan.test.js +24 -0
  449. package/test/unit/release-bump.test.js +474 -0
  450. package/test/unit/ssh-output-redaction.test.js +28 -0
  451. package/test/unit/start-options.test.js +46 -0
  452. package/test/unit/test-tiers.test.js +193 -0
  453. package/vitest.config.js +58 -0
  454. package/.nycrc +0 -9
  455. package/deploy/lib/logging.sh +0 -96
  456. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +0 -48
  457. package/manifests/mongodb/pv-pvc.yaml +0 -59
  458. package/scripts/coverall-test-cyberia.sh +0 -24
  459. package/scripts/coverall-test.sh +0 -24
  460. package/scripts/link-local-underpost-cli.sh +0 -6
  461. package/src/api/user/user.build.js +0 -16
  462. package/src/cli/env.js +0 -177
  463. package/src/client/components/core/Blockchain.js +0 -41
  464. package/src/client/components/core/Blog.js +0 -9
  465. package/src/client/components/core/KeyboardAvoidance.js +0 -145
  466. package/src/client/public/default/assets/background/dark.jpg +0 -0
  467. package/src/client/public/default/assets/background/white0-min.jpg +0 -0
  468. package/src/client/public/default/assets/background/white0.jpg +0 -0
  469. package/src/client/public/doc/favicon.ico +0 -0
  470. package/src/client/public/doc/sitemap +0 -148
  471. package/src/server/environment.js +0 -98
package/src/cli/test.js CHANGED
@@ -1,174 +1,248 @@
1
1
  /**
2
- * Test module for running tests on the application.
2
+ * Runs the test tiers, wherever they have to run: on this host, inside a
3
+ * deployment's containers, or as a Job on the cluster.
4
+ *
5
+ * One entrypoint, one runner. Selecting suites, ordering tiers and rendering
6
+ * the reporting surfaces belong to `src/server/build/testing.js`; this module resolves
7
+ * where the run happens and drives it.
8
+ *
3
9
  * @module src/cli/test.js
4
10
  * @namespace UnderpostTest
5
11
  */
6
12
 
7
13
  import fs from 'fs-extra';
14
+ import nodePath from 'node:path';
8
15
  import { timer } from '../client/components/core/CommonJs.js';
9
- import { MariaDB } from '../db/mariadb/MariaDB.js';
10
- import { getNpmRootPath } from '../server/environment.js';
11
- import { actionInitLog, loggerFactory, setUpInfo } from '../server/logger.js';
12
- import { pbcopy, shellExec } from '../server/process.js';
16
+ import { getUnderpostRootPath } from '../server/runtime/environment.js';
17
+ import { actionInitLog, loggerFactory, setUpInfo } from '../server/ops/logger.js';
18
+ import { shellExec } from '../server/runtime/process.js';
19
+ import {
20
+ UNDERPOST_TESTING,
21
+ allureManifestsFactory,
22
+ resolveTestSelection,
23
+ testJobManifestFactory,
24
+ vitestArgsFactory,
25
+ } from '../server/build/testing.js';
13
26
  import Underpost from '../index.js';
27
+
14
28
  const logger = loggerFactory(import.meta);
15
29
 
16
30
  /**
17
31
  * @class UnderpostTest
18
- * @description Manages the test of the application.
32
+ * @description Manages test execution and its cluster-side reporting.
19
33
  * @memberof UnderpostTest
20
34
  */
21
35
  class UnderpostTest {
22
36
  static API = {
23
37
  /**
24
- * Logs information about the current process environment to the console.
25
- *
26
- * This function is used to log details about
27
- * the execution context, such as command-line arguments,
28
- * environment variables, the process's administrative privileges,
29
- * and the maximum available heap space size.
30
- *
31
- * @static
32
38
  * @method setUpInfo
39
+ * @description Logs the execution context a failing run has to be read against:
40
+ * arguments, environment, privileges and heap ceiling.
33
41
  * @returns {Promise<void>}
34
42
  * @memberof UnderpostTest
35
43
  */
36
44
  async setUpInfo() {
37
45
  return await setUpInfo(logger);
38
46
  },
47
+
39
48
  /**
40
49
  * @method run
41
- * @description Runs the test of the application.
50
+ * @description Runs the selected tiers on this host.
51
+ *
52
+ * Resolved against the globally installed engine when the current directory
53
+ * is not one, so `underpost test` from anywhere still runs the shipped suites
54
+ * rather than failing on a missing config.
55
+ * @param {object} [params]
56
+ * @param {string} [params.suite] - Suite or tier selector.
57
+ * @param {string} [params.grep] - Substring filter on test names.
58
+ * @param {boolean} [params.watch] - Keep the runner open and re-run on change.
59
+ * @param {boolean} [params.coverage] - Emit the coverage reporters.
60
+ * @param {boolean} [params.allure] - Also write Allure results for the dashboard.
61
+ * @returns {void}
62
+ * @throws {Error} When no engine tree with a runner configuration can be found.
42
63
  * @memberof UnderpostTest
43
64
  */
44
- run() {
65
+ run({ suite = '', grep = '', watch = false, coverage = true, allure = false } = {}) {
45
66
  actionInitLog();
46
- const underpostTestPath = `${getNpmRootPath()}/underpost`;
47
- if (fs.existsSync(underpostTestPath)) {
48
- shellExec(`cd ${underpostTestPath} && npm run test`);
49
- } else {
50
- logger.warn(`Global underpost not found at ${underpostTestPath}, running local npm test instead`);
51
- shellExec('npm test');
67
+ const root = [process.cwd(), getUnderpostRootPath()].find(
68
+ (candidate) => candidate && fs.existsSync(`${candidate}/vitest.config.js`),
69
+ );
70
+ if (!root) throw new Error('[test] no vitest.config.js in the current directory or the global underpost install');
71
+
72
+ const { projects, runVitest, delegated } = resolveTestSelection(suite);
73
+ const allureResultsDirectory =
74
+ allure &&
75
+ nodePath.resolve(
76
+ root,
77
+ process.env[UNDERPOST_TESTING.allureResultsEnvKey] || UNDERPOST_TESTING.allureResultsDirectory,
78
+ );
79
+
80
+ if (runVitest) {
81
+ const env = [
82
+ 'NODE_ENV=test',
83
+ ...(allureResultsDirectory ? [`${UNDERPOST_TESTING.allureResultsEnvKey}=${allureResultsDirectory}`] : []),
84
+ ];
85
+ shellExec(
86
+ `cd ${root} && ${env.join(' ')} npx vitest ${vitestArgsFactory({ projects, grep, watch, coverage }).join(' ')}`,
87
+ );
88
+ }
89
+
90
+ for (const { name, directory, delegate } of delegated) {
91
+ const tierRoot = `${root}/${directory}`;
92
+ // A product build strips the tiers it does not own, so an absent
93
+ // directory is a tier this tree does not ship, not a failure.
94
+ if (!fs.existsSync(tierRoot)) {
95
+ logger.warn(`Skipping tier not present in this tree`, { tier: name, directory });
96
+ continue;
97
+ }
98
+ if (allureResultsDirectory) fs.mkdirSync(allureResultsDirectory, { recursive: true });
99
+ const resultsPath = allureResultsDirectory ? `${allureResultsDirectory}/TEST-${name}.xml` : '';
100
+ shellExec(`cd ${tierRoot} && ${delegate({ resultsPath, grep })}`);
52
101
  }
53
102
  },
103
+
54
104
  /**
55
- * @method callback
56
- * @description Manages the test of the application.
57
- * @param {string} deployList - The list of deployments to test.
58
- * @param {object} options - The options for the test.
59
- * @param {boolean} options.itc - If true, tests the inside container.
60
- * @param {boolean} options.sh - If true, tests the shell.
61
- * @param {boolean} options.logs - If true, tests the logs.
62
- * @param {string} options.podName - The name of the pod to test.
63
- * @param {string} options.podStatus - The status of the pod to test.
64
- * @param {string} options.kindType - The type of the kind to test.
65
- * @param {number} options.deltaMs - The delta time in milliseconds.
66
- * @param {number} options.maxAttempts - The maximum number of attempts.
105
+ * @method dashboard
106
+ * @description Applies the Allure dashboard: the results claim every run
107
+ * writes to, the report server, and the ways in.
108
+ * @param {object} params
109
+ * @param {string} [params.namespace='default'] - Target namespace.
110
+ * @param {string} [params.host] - Hostname to route the dashboard sub-path on.
111
+ * @param {boolean} [params.dryRun] - Print the manifests instead of applying them.
112
+ * @returns {string} The rendered manifests.
67
113
  * @memberof UnderpostTest
68
114
  */
69
- async callback(
70
- deployList = '',
71
- options = {
72
- itc: false,
73
- sh: false,
74
- logs: false,
75
- podName: '',
76
- podStatus: '',
77
- kindType: '',
78
- deltaMs: 1000,
79
- maxAttempts: 60 * 5,
80
- },
81
- ) {
82
- if (
83
- options.podName &&
84
- typeof options.podName === 'string' &&
85
- options.podStatus &&
86
- typeof options.podStatus === 'string'
87
- )
88
- return await Underpost.test.statusMonitor(options.podName, options.podStatus, options.kindType);
115
+ dashboard({ namespace = 'default', host = '', dryRun = false } = {}) {
116
+ const manifests = allureManifestsFactory({ namespace, host });
117
+ if (dryRun) {
118
+ console.log(manifests);
119
+ return manifests;
120
+ }
121
+ shellExec(`sudo kubectl apply -f - <<'EOF'\n${manifests}\nEOF`);
122
+ const { allure } = UNDERPOST_TESTING;
123
+ logger.info('Allure dashboard applied', {
124
+ namespace,
125
+ nodePort: allure.nodePort,
126
+ url: host ? `https://${host}${allure.subPath}` : '',
127
+ });
128
+ return manifests;
129
+ },
89
130
 
90
- if (options.sh === true || options.logs === true) {
91
- const [pod] = Underpost.kubectl.get(deployList);
92
- if (pod) {
93
- if (options.sh) return pbcopy(`sudo kubectl exec -it ${pod.NAME} -- sh`);
94
- if (options.logs) return shellExec(`sudo kubectl logs -f ${pod.NAME}`);
95
- }
96
- return logger.warn(`Couldn't find pods in deployment`, deployList);
131
+ /**
132
+ * @method job
133
+ * @description Runs one suite on the cluster as a Job.
134
+ *
135
+ * The Job writes into the same claim the dashboard reads, so its results are
136
+ * published by finishing — there is no upload step to fail separately from
137
+ * the tests it would have reported.
138
+ * @param {object} params
139
+ * @param {string} params.image - Image carrying the engine and its dependencies.
140
+ * @param {string} [params.suite] - Suite or tier selector.
141
+ * @param {string} [params.namespace='default'] - Target namespace.
142
+ * @param {string} [params.nodeName] - Pins the pod to one node.
143
+ * @param {boolean} [params.dryRun] - Print the manifest instead of applying it.
144
+ * @returns {Promise<boolean>} Whether the Job's pod reached completion.
145
+ * @throws {Error} When no image is given.
146
+ * @memberof UnderpostTest
147
+ */
148
+ async job({ image = '', suite = '', namespace = 'default', nodeName = '', dryRun = false } = {}) {
149
+ if (!image) throw new Error('[test] --job needs --image: the Job has no engine tree of its own');
150
+ const name = `${UNDERPOST_TESTING.job.namePrefix}-${(suite || 'all').replace(/[^a-z0-9]+/g, '-')}-${Date.now()}`;
151
+ const manifest = testJobManifestFactory({ name, namespace, image, suite, nodeName });
152
+ if (dryRun) {
153
+ console.log(manifest);
154
+ return true;
97
155
  }
98
- if (deployList) {
99
- for (const _deployId of deployList.split(',')) {
100
- const deployId = _deployId.trim();
101
- if (!deployId) continue;
102
- if (options.itc === true)
103
- switch (deployId) {
104
- case 'dd-lampp':
105
- {
106
- const { MARIADB_HOST, MARIADB_USER, MARIADB_PASSWORD, DD_LAMPP_TEST_DB_0 } = process.env;
156
+ shellExec(`sudo kubectl apply -f - <<'EOF'\n${manifest}\nEOF`);
157
+ return await Underpost.test.statusMonitor(name, 'Completed', 'pods');
158
+ },
107
159
 
108
- await MariaDB.query({
109
- host: MARIADB_HOST,
110
- user: MARIADB_USER,
111
- password: MARIADB_PASSWORD,
112
- query: `SHOW TABLES FROM ${DD_LAMPP_TEST_DB_0}`,
113
- });
114
- }
115
- break;
160
+ /**
161
+ * @method callback
162
+ * @description `underpost test` entrypoint.
163
+ * @param {string} [suite] - Suite or tier selector.
164
+ * @param {object} [options]
165
+ * @param {boolean} [options.itc] - Run here rather than dispatching into pods.
166
+ * @param {string} [options.deployList] - Comma separated deploy ids to run inside.
167
+ * @param {boolean} [options.dashboard] - Apply the Allure dashboard and exit.
168
+ * @param {boolean} [options.job] - Run the suite as a cluster Job.
169
+ * @param {boolean} [options.allure] - Write Allure results alongside the run.
170
+ * @param {string} [options.grep] - Substring filter on test names.
171
+ * @param {boolean} [options.watch] - Keep the runner open and re-run on change.
172
+ * @param {boolean} [options.coverage] - Emit the coverage reporters.
173
+ * @param {string} [options.namespace] - Namespace for the cluster-side actions.
174
+ * @param {string} [options.image] - Image for `--job`.
175
+ * @param {string} [options.nodeName] - Node pin for `--job`.
176
+ * @param {string} [options.host] - Hostname for the dashboard route.
177
+ * @param {boolean} [options.dryRun] - Render manifests without applying them.
178
+ * @param {string} [options.podName] - Wait for this object instead of running tests.
179
+ * @param {string} [options.podStatus] - Status `--pod-name` waits for.
180
+ * @param {string} [options.kindType] - Kind `--pod-name` queries.
181
+ * @returns {Promise<void>}
182
+ * @memberof UnderpostTest
183
+ */
184
+ async callback(suite = '', options = {}) {
185
+ const { itc, deployList, dashboard, job, podName, podStatus, kindType, namespace = 'default' } = options;
116
186
 
117
- default:
118
- {
119
- shellExec('npm run test');
120
- }
187
+ if (podName) return void (await Underpost.test.statusMonitor(podName, podStatus || 'Running', kindType));
188
+ if (dashboard) return void Underpost.test.dashboard(options);
189
+ if (job) return void (await Underpost.test.job({ ...options, suite }));
121
190
 
122
- break;
123
- }
124
- else {
125
- const pods = Underpost.kubectl.get(deployId);
126
- if (pods.length > 0)
127
- for (const deployData of pods) {
128
- const { NAME } = deployData;
129
- shellExec(
130
- `sudo kubectl exec -i ${NAME} -- sh -c "cd /home/dd/engine && underpost test ${deployId} --inside-container"`,
131
- );
132
- }
133
- else logger.warn(`Couldn't find pods in deployment`, { deployId });
191
+ if (deployList && !itc) {
192
+ for (const deployId of deployList
193
+ .split(',')
194
+ .map((value) => value.trim())
195
+ .filter(Boolean)) {
196
+ const pods = Underpost.kubectl.get(deployId, 'pods', namespace);
197
+ if (pods.length === 0) {
198
+ logger.warn(`Couldn't find pods in deployment`, { deployId });
199
+ continue;
134
200
  }
201
+ for (const { NAME } of pods)
202
+ Underpost.kubectl.exec({
203
+ podName: NAME,
204
+ namespace,
205
+ command: `cd ${UNDERPOST_TESTING.job.workingDirectory} && underpost test ${suite} --itc`,
206
+ });
135
207
  }
136
- } else return Underpost.test.run();
208
+ return;
209
+ }
210
+
211
+ return void Underpost.test.run({ ...options, suite });
137
212
  },
213
+
138
214
  /**
139
215
  * @method statusMonitor
140
- * @description Monitors the status of a pod.
141
- * @param {string} podName - The name of the pod to monitor.
142
- * @param {string} status - The status of the pod to monitor.
143
- * @param {string} kindType - The type of the kind to monitor.
144
- * @param {number} deltaMs - The delta time in milliseconds.
145
- * @param {number} maxAttempts - The maximum number of attempts.
216
+ * @description Waits for a cluster object to reach a status.
217
+ *
218
+ * `Completed` is accepted wherever `Running` was asked for: a Job's pod and a
219
+ * one-shot container both satisfy "it came up" by having finished, and a
220
+ * caller polling for readiness would otherwise time out on a success.
221
+ * @param {string} podName - Name, or name substring, of the object to watch.
222
+ * @param {string} [status='Running'] - Status to wait for.
223
+ * @param {string} [kindType='pods'] - Kind to query.
224
+ * @param {number} [deltaMs=1000] - Delay between attempts.
225
+ * @param {number} [maxAttempts=300] - Attempts before giving up.
226
+ * @returns {Promise<boolean>} Whether the status was reached.
146
227
  * @memberof UnderpostTest
147
228
  */
148
- statusMonitor(podName, status = 'Running', kindType = '', deltaMs = 1000, maxAttempts = 60 * 5) {
149
- if (!(kindType && typeof kindType === 'string')) kindType = 'pods';
150
- return new Promise(async (resolve) => {
151
- let index = 0;
152
- logger.info(`Loading instance`, { podName, status, kindType, deltaMs, maxAttempts });
153
- const _monitor = async () => {
154
- await timer(deltaMs);
155
- const pods = Underpost.kubectl.get(podName, kindType);
156
- let result = pods.find((p) => p.STATUS === status || (status === 'Running' && p.STATUS === 'Completed'));
157
- logger.info(
158
- `Testing pod ${podName}... ${result ? 1 : 0}/1 - elapsed time ${deltaMs * (index + 1)}s - attempt ${index + 1
159
- }/${maxAttempts}`,
160
- pods[0] ? pods[0].STATUS : 'Not found kind object',
161
- );
162
- if (result) return resolve(true);
163
- index++;
164
- if (index === maxAttempts) {
165
- logger.error(`Failed to test pod ${podName} within ${maxAttempts} attempts`);
166
- return resolve(false);
167
- }
168
- return _monitor();
169
- };
170
- await _monitor();
171
- });
229
+ async statusMonitor(podName, status = 'Running', kindType = 'pods', deltaMs = 1000, maxAttempts = 60 * 5) {
230
+ if (!kindType) kindType = 'pods';
231
+ logger.info(`Loading instance`, { podName, status, kindType, deltaMs, maxAttempts });
232
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
233
+ await timer(deltaMs);
234
+ const pods = Underpost.kubectl.get(podName, kindType);
235
+ const reached = pods.find(
236
+ (pod) => pod.STATUS === status || (status === 'Running' && pod.STATUS === 'Completed'),
237
+ );
238
+ logger.info(
239
+ `Testing pod ${podName}... ${reached ? 1 : 0}/1 - elapsed time ${deltaMs * attempt}ms - attempt ${attempt}/${maxAttempts}`,
240
+ pods[0] ? pods[0].STATUS : 'Not found kind object',
241
+ );
242
+ if (reached) return true;
243
+ }
244
+ logger.error(`Failed to test pod ${podName} within ${maxAttempts} attempts`);
245
+ return false;
172
246
  },
173
247
  };
174
248
  }
package/src/cli/vultr.js CHANGED
@@ -10,7 +10,7 @@
10
10
  * This module is that meter. It reads the instance's consumption from the Vultr
11
11
  * API, compares it against the plan's quota, and — once a configured fraction of
12
12
  * it is gone — reaches the VPS over SSH and drops its egress with
13
- * {@link module:src/server/dns.js}'s `blockAllEgress`.
13
+ * {@link module:src/server/network/dns.js}'s `blockAllEgress`.
14
14
  *
15
15
  * That last step is deliberately blunt: it takes every hostname behind the hub
16
16
  * offline. It is the cheaper failure. An overage accrues silently and without a
@@ -19,7 +19,7 @@
19
19
  * inbound SSH session still completes its handshake and the host stays
20
20
  * reachable to undo it.
21
21
  *
22
- * The enforcement is latched in the root env rather than re-applied every run,
22
+ * The enforcement is latched in the host store rather than re-applied every run,
23
23
  * so a cron firing every ten minutes does not re-open an SSH session to a host
24
24
  * that is already blocked.
25
25
  *
@@ -28,9 +28,12 @@
28
28
  */
29
29
 
30
30
  import axios from 'axios';
31
- import { environmentValueFactory } from '../server/environment.js';
32
- import { FORWARD_PROXY, fetchViaForwardProxy } from '../server/forward-proxy.js';
33
- import { loggerFactory } from '../server/logger.js';
31
+ import { environmentValueFactory } from '../server/runtime/environment.js';
32
+ import { FORWARD_PROXY, fetchViaForwardProxy } from '../server/network/forward-proxy.js';
33
+ import fs from 'fs-extra';
34
+ import nodePath from 'node:path';
35
+ import { loggerFactory } from '../server/ops/logger.js';
36
+ import { UNDERPOST_MONITORING } from '../server/ops/monitoring.js';
34
37
  import Underpost from '../index.js';
35
38
 
36
39
  const logger = loggerFactory(import.meta);
@@ -55,9 +58,8 @@ const UNDERPOST_VULTR = {
55
58
  requestTimeoutMs: 20000,
56
59
  defaultSshUser: 'root',
57
60
  defaultSshPort: 22,
58
- defaultSshKeyPath: './engine-private/deploy/id_rsa',
59
61
  remoteEnginePath: '/home/dd/engine',
60
- // Latched in the root env, which the CronJob mounts from the host, so the
62
+ // latched in the host store, which the CronJob mounts from the host, so the
61
63
  // decision survives the container that made it.
62
64
  latchKey: 'VULTR_EGRESS_BLOCKED_AT',
63
65
  env: {
@@ -80,13 +82,13 @@ const UNDERPOST_VULTR = {
80
82
  * @method envFactory
81
83
  * @description First non-empty value among a list of keys.
82
84
  *
83
- * Resolution is {@link module:src/server/environment.js.environmentValueFactory}'s — the process
84
- * environment, then the deploy env `underpost env <deploy-id> <environment>`
85
- * selects into `./.env`, then the underpost root env — rather than a second
85
+ * Resolution is {@link module:src/server/runtime/environment.js.environmentValueFactory}'s — the process
86
+ * environment, then the deploy env `underpost app load --env <environment>`
87
+ * selects into `./.env`, then the host store — rather than a second
86
88
  * implementation of it, because the three callers differ: a CronJob container has
87
89
  * its deploy env loaded into `process.env` by `loadCronDeployEnv`, an operator
88
- * preparing a manual run has `./.env`, and an operator who ran `underpost env
89
- * set` has the root env. Never logged — one of the keys this resolves is an API
90
+ * preparing a manual run has `./.env`, and an operator who ran `underpost host
91
+ * set` has the host store. Never logged — one of the keys this resolves is an API
90
92
  * key.
91
93
  * @param {string|Array<string>} keys - Environment variable name, or names in precedence order.
92
94
  * @returns {string} The resolved value, or an empty string.
@@ -294,7 +296,7 @@ const vultrRequest = async ({ apiKey, path, params = {}, proxy = null }) => {
294
296
  const code = error?.code || error?.cause?.code;
295
297
  const proxyHint =
296
298
  proxy?.apiKey && ['EHOSTUNREACH', 'ENETUNREACH', 'ECONNREFUSED', 'ETIMEDOUT'].includes(code)
297
- ? '; verify wg0 on the spoke; if only pods fail, re-run --wireguard-setup --client and restart wg0; if the host also fails, re-run --forward-proxy-server on the hub'
299
+ ? '; verify wg0 on the node; if only pods fail, re-run --wireguard-setup and restart wg0; if the host also fails, re-run --forward-proxy-server on the hub'
298
300
  : '';
299
301
  throw new Error(
300
302
  `[vultr] GET /v2${path} failed${responseStatus ? ` (${responseStatus})` : ''}: ${detail}${proxyHint}`,
@@ -353,7 +355,7 @@ class UnderpostVultr {
353
355
  * {@link UnderpostCron} job does, so `underpost cron default vultr` dispatches
354
356
  * to it unchanged. The deploy list is not used to select an instance — the
355
357
  * edge hub is one machine for the whole cluster, exactly as its WireGuard
356
- * peer registry is — but it is logged so a run is attributable.
358
+ * WireGuard topology is — but it is logged so a run is attributable.
357
359
  * @param {string} [deployList] - Comma-separated deploy ids, from the cron dispatcher.
358
360
  * @param {object} [options] - CLI flags.
359
361
  * @returns {Promise<object>} Result from {@link UnderpostVultr.checkBandwidth}.
@@ -363,6 +365,43 @@ class UnderpostVultr {
363
365
  return await UnderpostVultr.API.checkBandwidth({ ...options, deployList });
364
366
  },
365
367
 
368
+ /**
369
+ * @method publishBandwidthMetrics
370
+ * @description Publishes the measured quota so the cluster can read it
371
+ * without calling Vultr.
372
+ *
373
+ * The API is rate limited and needs a credential, neither of which belongs
374
+ * on an alert evaluation path, so the guard — which already holds the
375
+ * numbers — writes them where the host collector picks them up, and to the
376
+ * host configuration store for anything that reads configuration rather than metrics.
377
+ * The file is renamed into place because the collector may read it mid-write.
378
+ * @param {{consumedBytes: number, maxBytes: number}} state - Measured quota state.
379
+ * @returns {boolean} True when the metrics file was written.
380
+ * @memberof UnderpostVultr
381
+ */
382
+ publishBandwidthMetrics({ consumedBytes = 0, maxBytes = 0 } = {}) {
383
+ Underpost.host.store.set('VULTR_BANDWIDTH_USAGE_BYTES', `${consumedBytes}`);
384
+ Underpost.host.store.set('VULTR_BANDWIDTH_LIMIT_BYTES', `${maxBytes}`);
385
+
386
+ const target = `${UNDERPOST_MONITORING.nodeExporter.textfileDirectory}/vultr_bandwidth.prom`;
387
+ const content =
388
+ '# HELP vultr_bandwidth_used_bytes Monthly bandwidth consumed by the edge hub.\n' +
389
+ '# TYPE vultr_bandwidth_used_bytes gauge\n' +
390
+ `vultr_bandwidth_used_bytes ${consumedBytes}\n` +
391
+ '# HELP vultr_bandwidth_limit_bytes Monthly bandwidth quota of the edge hub plan.\n' +
392
+ '# TYPE vultr_bandwidth_limit_bytes gauge\n' +
393
+ `vultr_bandwidth_limit_bytes ${maxBytes}\n`;
394
+ try {
395
+ fs.mkdirpSync(nodePath.dirname(target));
396
+ fs.writeFileSync(`${target}.tmp`, content, 'utf8');
397
+ fs.renameSync(`${target}.tmp`, target);
398
+ return true;
399
+ } catch (error) {
400
+ logger.warn('Bandwidth metrics not published to the host collector', { target, error: `${error.message}` });
401
+ return false;
402
+ }
403
+ },
404
+
366
405
  /**
367
406
  * @method resolveConfig
368
407
  * @description Every input the guard needs, resolved once.
@@ -382,10 +421,9 @@ class UnderpostVultr {
382
421
  threshold: thresholdFactory(options.threshold || envFactory(UNDERPOST_VULTR.env.threshold)),
383
422
  host: `${options.host || ''}`.trim() || envFactory(UNDERPOST_VULTR.env.host),
384
423
  user: `${options.user || ''}`.trim() || envFactory(UNDERPOST_VULTR.env.user) || UNDERPOST_VULTR.defaultSshUser,
385
- keyPath:
386
- `${options.keyPath || ''}`.trim() ||
387
- envFactory(UNDERPOST_VULTR.env.keyPath) ||
388
- UNDERPOST_VULTR.defaultSshKeyPath,
424
+ keyPath: Underpost.ssh.keyPathFactory(
425
+ `${options.keyPath || ''}`.trim() || envFactory(UNDERPOST_VULTR.env.keyPath),
426
+ ),
389
427
  port: Number(options.port || envFactory(UNDERPOST_VULTR.env.port)) || UNDERPOST_VULTR.defaultSshPort,
390
428
  // `total` is the conservative reading and trips first; `outgoing` counts
391
429
  // egress alone, which is what a plan billing outbound-only meters.
@@ -441,7 +479,9 @@ class UnderpostVultr {
441
479
  const totals = bandwidthTotalsFactory({ bandwidth, month: config.month });
442
480
  const consumedBytes = config.metric === 'outgoing' ? totals.outgoingBytes : totals.totalBytes;
443
481
  const state = quotaStateFactory({ consumedBytes, planBandwidthGB, threshold: config.threshold });
444
- const latchedAt = `${Underpost.env.get(UNDERPOST_VULTR.latchKey, undefined, { disableLog: true }) ?? ''}`.trim();
482
+ UnderpostVultr.API.publishBandwidthMetrics(state);
483
+ const latchedAt =
484
+ `${Underpost.host.store.get(UNDERPOST_VULTR.latchKey, undefined, { disableLog: true }) ?? ''}`.trim();
445
485
 
446
486
  const summary = {
447
487
  instanceId: config.instanceId,
@@ -472,7 +512,7 @@ class UnderpostVultr {
472
512
  // real trigger, so it is cleared as soon as usage is back under the
473
513
  // threshold — but the host stays blocked until someone says otherwise.
474
514
  if (latchedAt) {
475
- if (!config.dryRun) Underpost.env.delete(UNDERPOST_VULTR.latchKey);
515
+ if (!config.dryRun) Underpost.host.store.delete(UNDERPOST_VULTR.latchKey);
476
516
  if (config.autoUnblock) await UnderpostVultr.API.setEdgeEgress({ config, blocked: false });
477
517
  else
478
518
  logger.warn('Edge egress is still blocked from a previous cycle; unblock it when you are ready', {
@@ -496,7 +536,7 @@ class UnderpostVultr {
496
536
  }
497
537
 
498
538
  const enforced = await UnderpostVultr.API.setEdgeEgress({ config, blocked: true });
499
- if (enforced && !config.dryRun) Underpost.env.set(UNDERPOST_VULTR.latchKey, new Date().toISOString());
539
+ if (enforced && !config.dryRun) Underpost.host.store.set(UNDERPOST_VULTR.latchKey, new Date().toISOString());
500
540
  return { ...state, ...summary, enforced, latched: enforced };
501
541
  },
502
542