@underpostnet/cyberia 3.3.73 → 3.3.77

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 (269) hide show
  1. package/.env.example +17 -5
  2. package/.github/workflows/coverall.cyberia.ci.yml +10 -0
  3. package/.github/workflows/docker-image.cyberia-client.ci.yml +1 -1
  4. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +1 -1
  5. package/.github/workflows/docker-image.cyberia-server.ci.yml +1 -1
  6. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +1 -1
  7. package/.github/workflows/docker-image.engine-cyberia.ci.yml +1 -1
  8. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +1 -1
  9. package/.github/workflows/engine-cyberia.ci.yml +1 -1
  10. package/.github/workflows/publish.cyberia.ci.yml +44 -0
  11. package/AGENTS.md +61 -12
  12. package/CHANGELOG.md +159 -161
  13. package/CLI-HELP.md +20 -13
  14. package/Dockerfile +1 -1
  15. package/Dockerfile.dev +1 -1
  16. package/Dockerfile.test +2 -2
  17. package/README.md +28 -28
  18. package/bin/build.js +96 -21
  19. package/bin/cyberia.js +1247 -1373
  20. package/bin/index.js +1247 -1373
  21. package/compose.env +17 -5
  22. package/conf.js +7 -4
  23. package/deploy/cyberia-client/deploy.sh +10 -11
  24. package/deploy/cyberia-client/package.sh +0 -1
  25. package/deploy/cyberia-client/state.sh +3 -2
  26. package/deploy/cyberia-server/deploy.sh +10 -11
  27. package/deploy/cyberia-server/package.sh +0 -1
  28. package/deploy/cyberia-server/state.sh +3 -2
  29. package/deploy/dd-cyberia/deploy.sh +5 -5
  30. package/deploy/dd-cyberia/init.sh +15 -14
  31. package/deploy/dd-cyberia/package.sh +0 -1
  32. package/deploy/dd-cyberia/state.sh +2 -2
  33. package/deploy/dd-cyberia/sync-deploy.sh +139 -75
  34. package/deploy/lib/config.sh +29 -0
  35. package/deploy/lib/github-actions-logging.sh +92 -76
  36. package/deploy/lib/host.sh +28 -19
  37. package/deploy/lib/state.sh +4 -1
  38. package/deploy/pwa-microservices-template/deploy.sh +10 -8
  39. package/deploy/release/deploy.sh +0 -2
  40. package/deployment.yaml +1 -1
  41. package/docker-compose.yml +23 -17
  42. package/docs/coverage/cyberia/api/cyberia-instance/cyberia-fallback-capture.js.html +1591 -0
  43. package/docs/coverage/cyberia/api/cyberia-instance/index.html +116 -0
  44. package/docs/coverage/cyberia/api/cyberia-server-defaults/cyberia-server-defaults.js.html +4138 -0
  45. package/docs/coverage/cyberia/api/cyberia-server-defaults/index.html +116 -0
  46. package/docs/coverage/cyberia/api/object-layer/index.html +116 -0
  47. package/docs/coverage/cyberia/api/object-layer/object-layer.model.js.html +1393 -0
  48. package/docs/coverage/cyberia/base.css +362 -0
  49. package/docs/coverage/cyberia/block-navigation.js +82 -0
  50. package/docs/coverage/cyberia/favicon.png +0 -0
  51. package/docs/coverage/cyberia/index.html +161 -0
  52. package/docs/coverage/cyberia/prettify.css +101 -0
  53. package/docs/coverage/cyberia/prettify.js +937 -0
  54. package/docs/coverage/cyberia/projects/cyberia/atlas-sprite-sheet-generator.js.html +1315 -0
  55. package/docs/coverage/cyberia/projects/cyberia/index.html +176 -0
  56. package/docs/coverage/cyberia/projects/cyberia/instance-backup.js.html +715 -0
  57. package/docs/coverage/cyberia/projects/cyberia/instance-data.js.html +2182 -0
  58. package/docs/coverage/cyberia/projects/cyberia/shape-generator.js.html +3265 -0
  59. package/docs/coverage/cyberia/projects/cyberia/stat-balance.js.html +328 -0
  60. package/docs/coverage/cyberia/sort-arrow-sprite.png +0 -0
  61. package/docs/coverage/cyberia/sorter.js +205 -0
  62. package/hardhat/package-lock.json +2 -2
  63. package/hardhat/package.json +1 -1
  64. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  65. package/manifests/cronjobs/dd-cron/dd-cron-vultr.yaml +1 -1
  66. package/manifests/deployment/dd-cyberia-development/deployment.yaml +1 -1
  67. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  68. package/manifests/deployment/mongo-express/deployment.yaml +46 -27
  69. package/manifests/deployment/mongo-express/kustomization.yaml +9 -0
  70. package/manifests/deployment/mongo-express/mongo-express-nodeport.yaml +18 -0
  71. package/manifests/deployment/mongo-express/service.yaml +16 -0
  72. package/manifests/deployment/mongo-express-no-auth/disable-mongodb-auth.yaml +16 -0
  73. package/manifests/deployment/mongo-express-no-auth/kustomization.yaml +16 -0
  74. package/package.json +19 -17
  75. package/scripts/test-monitor.sh +3 -3
  76. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +14 -9
  77. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +36 -5
  78. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  79. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +88 -184
  80. package/src/api/cyberia-action/cyberia-action.router.js +2 -2
  81. package/src/api/cyberia-action/cyberia-action.service.js +3 -4
  82. package/src/api/cyberia-audio/cyberia-audio.controller.js +6 -0
  83. package/src/api/cyberia-audio/cyberia-audio.model.js +61 -0
  84. package/src/api/cyberia-audio/cyberia-audio.router.js +21 -0
  85. package/src/api/cyberia-audio/cyberia-audio.service.js +220 -0
  86. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +1 -3
  87. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +10 -19
  88. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +12 -34
  89. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +15 -32
  90. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +1 -1
  91. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +1 -1
  92. package/src/api/cyberia-entity/cyberia-entity.model.js +2 -0
  93. package/src/api/cyberia-entity/cyberia-entity.service.js +1 -1
  94. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +8 -2
  95. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +31 -20
  96. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +16 -1
  97. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +466 -3
  98. package/src/api/cyberia-instance/cyberia-fallback-capture.js +87 -51
  99. package/src/api/cyberia-instance/cyberia-fallback-world.js +21 -40
  100. package/src/api/cyberia-instance/cyberia-instance-boot.service.js +5 -5
  101. package/src/api/cyberia-instance/cyberia-instance-items.js +98 -0
  102. package/src/api/cyberia-instance/cyberia-instance-map.service.js +34 -141
  103. package/src/api/cyberia-instance/cyberia-instance.controller.js +1 -4
  104. package/src/api/cyberia-instance/cyberia-instance.model.js +0 -14
  105. package/src/api/cyberia-instance/cyberia-instance.router.js +3 -16
  106. package/src/api/cyberia-instance/cyberia-instance.service.js +3 -43
  107. package/src/api/cyberia-instance/cyberia-world-generator.js +8 -13
  108. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +49 -75
  109. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +74 -1
  110. package/src/api/cyberia-map/cyberia-map.service.js +6 -2
  111. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.controller.js +31 -0
  112. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.model.js +56 -0
  113. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.router.js +33 -0
  114. package/src/api/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +189 -0
  115. package/src/api/cyberia-quest/cyberia-quest.router.js +3 -3
  116. package/src/api/cyberia-quest/cyberia-quest.service.js +3 -3
  117. package/src/api/cyberia-quest-progress/cyberia-quest-progress.model.js +1 -1
  118. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +403 -188
  119. package/src/api/cyberia-skill/cyberia-skill.model.js +6 -6
  120. package/src/api/file/file.ref.js +153 -0
  121. package/src/api/file/file.ref.json +11 -2
  122. package/src/api/object-layer/object-layer.model.js +47 -12
  123. package/src/api/object-layer/object-layer.router.js +10 -3
  124. package/src/api/object-layer/object-layer.service.js +113 -120
  125. package/src/cli/app.js +12 -1
  126. package/src/cli/client.js +2 -2
  127. package/src/cli/cluster.js +80 -18
  128. package/src/cli/deploy.js +215 -34
  129. package/src/cli/docker-compose.js +1 -1
  130. package/src/cli/fs-selection.js +115 -0
  131. package/src/cli/fs.js +158 -342
  132. package/src/cli/index.js +22 -10
  133. package/src/cli/release.js +3 -1
  134. package/src/cli/repository.js +35 -9
  135. package/src/cli/run.js +80 -199
  136. package/src/cli/test.js +4 -0
  137. package/src/cli/wireguard.js +32 -31
  138. package/src/client/components/core/Badge.js +0 -1
  139. package/src/client/components/core/CssCore.js +8 -1
  140. package/src/client/components/core/Docs.js +77 -100
  141. package/src/client/components/core/Modal.js +77 -23
  142. package/src/client/components/core/ToolTip.js +15 -0
  143. package/src/client/components/cryptokoyn/AppShellCryptokoyn.js +0 -2
  144. package/src/client/components/cyberia/ActionEngineCyberia.js +12 -36
  145. package/src/client/components/cyberia/EntityEngineCyberia.js +492 -121
  146. package/src/client/components/cyberia/InstanceEngineCyberia.js +129 -160
  147. package/src/client/components/cyberia/InstanceSelectionView.js +7 -1
  148. package/src/client/components/cyberia/MapEngineCyberia.js +175 -69
  149. package/src/client/components/cyberia/ObjectLayerEngine.js +1011 -97
  150. package/src/client/components/cyberia/ObjectLayerEngineModal.js +81 -155
  151. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +113 -98
  152. package/src/client/components/cyberia/SharedDefaultsCyberia.js +215 -56
  153. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +0 -34
  154. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +1 -1
  155. package/src/client/components/cyberia-portal/MainBodyCyberiaPortal.js +712 -76
  156. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +0 -4
  157. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +0 -4
  158. package/src/client/components/default/AppShellDefault.js +1 -4
  159. package/src/client/components/itemledger/AppShellItemledger.js +0 -2
  160. package/src/client/components/underpost/AppShellUnderpost.js +1 -1
  161. package/src/client/public/cyberia-docs/ARCHITECTURE.md +15 -15
  162. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +280 -24
  163. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +51 -29
  164. package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +52 -53
  165. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +16 -11
  166. package/src/client/public/cyberia-docs/CYBERIA.md +28 -28
  167. package/src/client/public/cyberia-docs/ENTITY-PROFILE.md +11 -5
  168. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  169. package/src/client/public/cyberia-docs/STATS-PROGRESSION.md +217 -0
  170. package/src/client/public/cyberia-docs/WHITE-PAPER.md +20 -12
  171. package/src/client/services/cyberia-audio/cyberia-audio.service.js +99 -0
  172. package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +38 -0
  173. package/src/client/services/cyberia-instance/cyberia-instance.management.js +5 -4
  174. package/src/client/services/cyberia-instance/cyberia-instance.service.js +0 -40
  175. package/src/client/services/cyberia-map/cyberia-map.management.js +5 -4
  176. package/src/client/services/cyberia-map-audio-conf/cyberia-map-audio-conf.service.js +138 -0
  177. package/src/client/services/object-layer/object-layer.management.js +6 -10
  178. package/src/client/services/object-layer/object-layer.service.js +3 -1
  179. package/src/client/ssr/views/Cyberia404.js +3 -3
  180. package/src/client-builder/client-build-docs.js +52 -52
  181. package/src/client-builder/client-build.js +67 -44
  182. package/src/client-builder/client-bundle.js +347 -0
  183. package/src/db/mongo/MongoExpress.js +187 -0
  184. package/src/grpc/cyberia/grpc-server.js +5 -7
  185. package/src/index.js +1 -1
  186. package/src/projects/cyberia/atlas-sprite-sheet-generator.js +143 -146
  187. package/src/projects/cyberia/atlas-sprite-sheet-store.js +391 -0
  188. package/src/projects/cyberia/besu-genesis-generator.js +5 -14
  189. package/src/projects/cyberia/boot-contract-fixtures.js +32 -0
  190. package/src/projects/cyberia/catalog-cyberia.js +3 -2
  191. package/src/projects/cyberia/gemini-client.js +3 -3
  192. package/src/projects/cyberia/generate-saga.js +4 -11
  193. package/src/projects/cyberia/hot-reload-trigger.js +17 -28
  194. package/src/projects/cyberia/instance-backup.js +210 -0
  195. package/src/projects/cyberia/instance-data.js +177 -257
  196. package/src/projects/cyberia/map-preview-generator.js +32 -68
  197. package/src/projects/cyberia/object-layer.js +152 -319
  198. package/src/projects/cyberia/seed-audio.js +139 -0
  199. package/src/projects/cyberia/semantic-layer-generator-skin.js +2 -8
  200. package/src/projects/cyberia/semantic-layer-generator.js +2 -18
  201. package/src/projects/cyberia/stat-balance.js +81 -0
  202. package/src/projects/cyberia/stat-commands.js +23 -0
  203. package/src/projects/cyberia/stat-contract-generator.js +101 -0
  204. package/src/runtime/cyberia-client/Dockerfile +8 -4
  205. package/src/runtime/cyberia-client/Dockerfile.dev +5 -2
  206. package/src/runtime/cyberia-server/Dockerfile +9 -2
  207. package/src/runtime/cyberia-server/Dockerfile.dev +8 -1
  208. package/src/runtime/engine-cyberia/Dockerfile +1 -1
  209. package/src/runtime/engine-cyberia/Dockerfile.dev +1 -1
  210. package/src/runtime/engine-cyberia/Dockerfile.test +2 -2
  211. package/src/runtime/engine-cyberia/compose.env +17 -5
  212. package/src/runtime/engine-cyberia/docker-compose.yml +23 -17
  213. package/src/server/build/coverage.js +113 -42
  214. package/src/server/build/package.js +55 -12
  215. package/src/server/build/testing.js +59 -9
  216. package/src/server/network/middlewares.js +8 -2
  217. package/src/server/network/underpost-gateway.js +113 -14
  218. package/src/server/ops/logger.js +70 -35
  219. package/src/server/runtime/conf.js +40 -9
  220. package/src/server/runtime/start.js +11 -2
  221. package/src/server/storage/data-query.js +16 -0
  222. package/src/server/storage/downloader.js +62 -14
  223. package/src/server/storage/zip.js +153 -0
  224. package/test/integration/app/cyberia/atlas-sprite-sheet-store.test.js +402 -0
  225. package/test/integration/app/cyberia/atlas-sprite-sheet.test.js +86 -0
  226. package/test/integration/app/cyberia/cyberia-cli-plain-reads.test.js +34 -0
  227. package/test/integration/app/cyberia/cyberia-entity-type-default.test.js +842 -0
  228. package/test/integration/app/cyberia/cyberia-instance-conf-coerce.test.js +53 -0
  229. package/test/integration/app/cyberia/cyberia-instance-conf-defaults.test.js +16 -22
  230. package/test/integration/app/cyberia/cyberia-instance-items.test.js +90 -0
  231. package/test/integration/app/cyberia/cyberia-load.test.js +6 -7
  232. package/test/integration/app/cyberia/cyberia-map-audio-conf.test.js +256 -0
  233. package/test/integration/app/cyberia/cyberia-stats.test.js +150 -0
  234. package/test/integration/app/cyberia/fallback-world-capture.test.js +87 -2
  235. package/test/integration/app/cyberia/object-layer-item-selection.test.js +56 -0
  236. package/test/integration/app/cyberia/object-layer-natural-key.test.js +32 -0
  237. package/test/integration/app/cyberia/seed-audio.test.js +275 -0
  238. package/test/integration/infra/2-network/wireguard-cli.test.js +33 -9
  239. package/test/integration/infra/2-network/wireguard-edge.test.js +58 -18
  240. package/test/integration/infra/3-cluster/mongo-express-deploy.test.js +156 -0
  241. package/test/integration/infra/4-ingress/api-cross-origin.test.js +79 -0
  242. package/test/integration/infra/4-ingress/underpost-ingress.test.js +1 -1
  243. package/test/unit/catalog.test.js +13 -2
  244. package/test/unit/client-build-docs.test.js +97 -70
  245. package/test/unit/client-bundle.test.js +313 -0
  246. package/test/unit/conf-resolution.test.js +58 -7
  247. package/test/unit/coverage-artifact.test.js +120 -29
  248. package/test/unit/cyberia/instance-backup.test.js +183 -0
  249. package/test/unit/cyberia/instance-data.test.js +314 -0
  250. package/test/unit/cyberia/instance-object-layer-items.test.js +46 -0
  251. package/test/unit/cyberia/publish-workflow.test.js +22 -0
  252. package/test/unit/cyberia/stat-balance.test.js +69 -0
  253. package/test/unit/deploy-legacy-gateway-sweep.test.js +63 -0
  254. package/test/unit/deploy-log-table.test.js +76 -5
  255. package/test/unit/downloader-integrity.test.js +98 -0
  256. package/test/unit/file-reference-registry.test.js +141 -0
  257. package/test/unit/fs-storage-paths.test.js +596 -0
  258. package/test/unit/gateway-pod-fetch.test.js +95 -0
  259. package/test/unit/letsencrypt-issuer.test.js +101 -0
  260. package/test/unit/logger-redaction.test.js +29 -0
  261. package/test/unit/package.test.js +70 -16
  262. package/test/unit/prepare-host.test.js +84 -1
  263. package/test/unit/release-bump.test.js +3 -1
  264. package/test/unit/start-options.test.js +12 -0
  265. package/test/unit/test-tiers.test.js +27 -0
  266. package/test/unit/zip-archive.test.js +142 -0
  267. package/vitest.config.js +11 -3
  268. package/src/api/cyberia-instance/cyberia-fallback-default-items.js +0 -63
  269. package/src/client/components/cyberia/FallbackWorldEngineCyberia.js +0 -368
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Client bundle transport: enumerates the zip artifacts a deployment's routes build, uploads
3
+ * them to file storage, and restores them into the served public directory.
4
+ *
5
+ * Push and pull are two directions of one contract, so both resolve their routes, bundle ids,
6
+ * storage keys and part files through the helpers here rather than re-deriving them. Route
7
+ * selection mirrors {@link buildClient}: a route that the build skips produces no artifact for
8
+ * either direction to carry.
9
+ * @module src/client-builder/client-bundle.js
10
+ * @namespace clientBundle
11
+ */
12
+
13
+ import fs from 'fs-extra';
14
+ import UnderpostFileStorage from '../cli/fs.js';
15
+ import * as dir from 'path';
16
+ import { cli } from '../server/build/execution.js';
17
+ import { loadConfServerJson, loadReplicas } from '../server/runtime/conf.js';
18
+ import { loggerFactory } from '../server/ops/logger.js';
19
+ import { shellArgumentFactory, shellExec } from '../server/runtime/process.js';
20
+ import { clientBundleIdFactory, getZipPartPaths, mergeClientBuildZip, unzipClientBuild } from './client-build.js';
21
+
22
+ const logger = loggerFactory(import.meta);
23
+
24
+ /** @type {string} Directory `buildClient --build-zip` writes bundles to, and the only place push and pull look. */
25
+ const CLIENT_BUNDLE_DIRECTORY = './build';
26
+
27
+ /** @type {number} Part size in MB applied when the caller names none. */
28
+ const DEFAULT_SPLIT_MB = 8;
29
+
30
+ /** @type {string} `--split` value that turns splitting off. */
31
+ const SPLIT_DISABLED = 'none';
32
+
33
+ const CLIENT_BUNDLE_STORAGE_ID = 'bundle';
34
+
35
+ /**
36
+ * Resolves the conf.server path a deployment's routes are read from.
37
+ * @function clientBundleConfServerPath
38
+ * @param {string} deployId - The deployment identifier.
39
+ * @returns {string} Path to the deployment's `conf.server.json`.
40
+ * @memberof clientBundle
41
+ */
42
+ const clientBundleConfServerPath = (deployId) => `./engine-private/conf/${deployId}/conf.server.json`;
43
+
44
+ /**
45
+ * Resolves the storage manifest that tracks a deployment's uploaded bundle parts. Push writes
46
+ * it and pull reads it, so both must name the same file.
47
+ * @function clientBundleStorageFilePath
48
+ * @param {string} deployId - The deployment identifier.
49
+ * @returns {string} Path to the deployment's bundle storage manifest.
50
+ * @memberof clientBundle
51
+ */
52
+ const clientBundleStorageFilePath = (deployId) =>
53
+ UnderpostFileStorage.API.resolveManifest({ deployId, storageId: CLIENT_BUNDLE_STORAGE_ID });
54
+
55
+ /**
56
+ * Parses the comma-separated host filter both runners accept as their positional argument.
57
+ * @function clientBundleHostFilter
58
+ * @param {string} [hosts] - Comma-separated host names, or empty for every host.
59
+ * @returns {string[]} The requested hosts, empty when no filter was given.
60
+ * @memberof clientBundle
61
+ */
62
+ const clientBundleHostFilter = (hosts = '') =>
63
+ `${hosts ?? ''}`
64
+ .split(',')
65
+ .map((host) => host.trim())
66
+ .filter(Boolean);
67
+
68
+ /**
69
+ * Resolves the `--split` option into the flag `underpost client` expects.
70
+ * @function clientBundleSplitFlag
71
+ * @param {string|number} [split] - Part size in MB, or `none` to disable splitting.
72
+ * @returns {string} The flag to append, empty when splitting is disabled.
73
+ * @memberof clientBundle
74
+ */
75
+ const clientBundleSplitFlag = (split) => {
76
+ const requested = `${split ?? ''}`.trim();
77
+ if (!requested) return `--split ${DEFAULT_SPLIT_MB}`;
78
+ if (requested === SPLIT_DISABLED) return '';
79
+
80
+ const splitMb = Number(requested);
81
+ if (Number.isFinite(splitMb) && splitMb > 0) return `--split ${splitMb}`;
82
+
83
+ logger.warn('invalid split option, falling back to the default', { split: requested, default: DEFAULT_SPLIT_MB });
84
+ return `--split ${DEFAULT_SPLIT_MB}`;
85
+ };
86
+
87
+ /**
88
+ * Enumerates the bundles a deployment's routes build to.
89
+ *
90
+ * A route is skipped exactly where {@link buildClient} skips it — redirects and
91
+ * rebuild-disabled routes emit nothing, and a `singleReplica` route's replicas are built under
92
+ * their own deploy ids, so they carry their own manifests rather than the parent's.
93
+ * @function clientBundleEntriesFactory
94
+ * @param {object} options
95
+ * @param {string} options.deployId - The deployment identifier.
96
+ * @param {string[]} [options.hosts] - Optional host filter; empty selects every configured host.
97
+ * @returns {Array<{host: string, routePath: string, buildId: string, zipPath: string, publicPath: string}>}
98
+ * One entry per bundle, in configuration order.
99
+ * @memberof clientBundle
100
+ */
101
+ const clientBundleEntriesFactory = ({ deployId, hosts = [] }) => {
102
+ const confServerPath = clientBundleConfServerPath(deployId);
103
+ if (!fs.existsSync(confServerPath)) {
104
+ logger.warn('conf.server not found', { deployId, confServerPath });
105
+ return [];
106
+ }
107
+
108
+ const confServer = loadReplicas(deployId, loadConfServerJson(confServerPath));
109
+ const selectedHosts = hosts.length > 0 ? hosts : Object.keys(confServer);
110
+
111
+ return selectedHosts.flatMap((host) => {
112
+ if (!confServer[host]) {
113
+ logger.warn('host not present in conf.server', { deployId, host });
114
+ return [];
115
+ }
116
+ return Object.keys(confServer[host])
117
+ .filter((routePath) => {
118
+ const { redirect, disabledRebuild, singleReplica } = confServer[host][routePath] ?? {};
119
+ return !redirect && !disabledRebuild && !singleReplica;
120
+ })
121
+ .map((routePath) => {
122
+ const buildId = clientBundleIdFactory(host, routePath);
123
+ return {
124
+ host,
125
+ routePath,
126
+ buildId,
127
+ zipPath: `${CLIENT_BUNDLE_DIRECTORY}/${buildId}.zip`,
128
+ publicPath: routePath === '/' ? `public/${host}` : `public/${host}${routePath}`,
129
+ };
130
+ });
131
+ });
132
+ };
133
+
134
+ /**
135
+ * Reads the storage manifest as a plain lookup, for the recorded size of each uploaded artifact.
136
+ * Read-only and tolerant: a manifest that is absent or unreadable disables verification rather
137
+ * than failing the pull, since the artifacts themselves are still what the bundle is made of.
138
+ * @function clientBundleManifestFactory
139
+ * @param {string} deployId - The deployment identifier.
140
+ * @returns {Object<string, {bytes?: number}>} The tracked entries, empty when unavailable.
141
+ * @memberof clientBundle
142
+ */
143
+ const clientBundleManifestFactory = (deployId) => {
144
+ const manifestPath = clientBundleStorageFilePath(deployId);
145
+ try {
146
+ return UnderpostFileStorage.API.readManifest({ deployId, storageId: CLIENT_BUNDLE_STORAGE_ID }).storage;
147
+ } catch (error) {
148
+ logger.warn('bundle manifest unreadable, size verification skipped', { manifestPath, error: error?.message });
149
+ return {};
150
+ }
151
+ };
152
+
153
+ /**
154
+ * The tracked keys that belong to one bundle.
155
+ *
156
+ * A bundle owns its zip and every split part of it, and nothing else: the id already carries the
157
+ * route, so `underpost.net-` and `underpost.net-peer` cannot claim each other's parts.
158
+ * @function clientBundleTrackedKeys
159
+ * @param {object} manifest - The bundle storage manifest.
160
+ * @param {string} buildId - The bundle's build id.
161
+ * @returns {string[]} The manifest keys for that bundle.
162
+ * @memberof clientBundle
163
+ */
164
+ const clientBundleTrackedKeys = (manifest, buildId) =>
165
+ Object.keys(manifest).filter((key) =>
166
+ key.replace(/^\.\//, '').startsWith(`${CLIENT_BUNDLE_DIRECTORY.replace(/^\.\//, '')}/${buildId}.zip`),
167
+ );
168
+
169
+ /**
170
+ * Lists the local artifacts that make up one bundle: its split parts when the build split it,
171
+ * otherwise the single zip.
172
+ * @function clientBundleArtifactPaths
173
+ * @param {string} zipPath - The bundle's zip path.
174
+ * @returns {{zipPath: string, partPaths: string[], artifactPaths: string[]}} The resolved artifacts.
175
+ * @memberof clientBundle
176
+ */
177
+ const clientBundleArtifactPaths = (zipPath) => {
178
+ const partPaths = fs.existsSync(dir.dirname(zipPath)) ? getZipPartPaths(zipPath) : [];
179
+ const artifactPaths = partPaths.length > 0 ? partPaths : fs.existsSync(zipPath) ? [zipPath] : [];
180
+ return { zipPath, partPaths, artifactPaths };
181
+ };
182
+
183
+ /**
184
+ * Builds a deployment's client bundles and uploads them to file storage.
185
+ *
186
+ * The build and the uploads run as child processes because `underpost app load` writes the
187
+ * `.env` this deployment's credentials live in, and only a freshly started process reads it.
188
+ * @function pushClientBundle
189
+ * @param {object} options
190
+ * @param {string} options.deployId - The deployment identifier.
191
+ * @param {string[]} [options.hosts] - Optional host filter; empty selects every configured host.
192
+ * @param {string|number} [options.split] - Part size in MB, or `none` to upload one zip.
193
+ * @param {boolean} [options.dev] - Build for development; defaults to production.
194
+ * @returns {{pushed: number, skipped: number}} How many bundles were uploaded and skipped.
195
+ * @memberof clientBundle
196
+ */
197
+ const pushClientBundle = ({ deployId, hosts = [], split, dev = false }) => {
198
+ const baseCommand = cli('underpost', { local: true });
199
+ const env = dev ? 'development' : 'production';
200
+ const splitFlag = clientBundleSplitFlag(split);
201
+
202
+ shellExec(`${baseCommand} app load --env ${env} --args deploy-id=${deployId}`);
203
+ shellExec(`${baseCommand} client ${deployId} --env ${env} --build-zip${splitFlag ? ` ${splitFlag}` : ''}`);
204
+
205
+ const entries = clientBundleEntriesFactory({ deployId, hosts });
206
+ if (entries.length === 0) logger.warn('no bundle routes resolved', { deployId, hosts });
207
+
208
+ let pushed = 0;
209
+ let skipped = 0;
210
+ for (const { host, routePath, buildId, zipPath } of entries) {
211
+ const { artifactPaths } = clientBundleArtifactPaths(zipPath);
212
+ if (artifactPaths.length === 0) {
213
+ logger.warn('no bundle artifacts found', { host, routePath, buildId });
214
+ skipped++;
215
+ continue;
216
+ }
217
+ for (const artifactPath of artifactPaths) {
218
+ shellExec(
219
+ `${baseCommand} fs ${artifactPath} --deploy-id ${deployId} --storage-id ${CLIENT_BUNDLE_STORAGE_ID} --force`,
220
+ );
221
+ }
222
+
223
+ // Remove obsolete manifest entries only after all current artifacts upload.
224
+ const current = new Set(artifactPaths.map((artifactPath) => artifactPath.replace(/^\.\//, '')));
225
+ const stale = clientBundleTrackedKeys(clientBundleManifestFactory(deployId), buildId).filter(
226
+ (key) => !current.has(key.replace(/^\.\//, '')),
227
+ );
228
+ for (const key of stale) {
229
+ shellExec(
230
+ `${baseCommand} fs --tracked --key ${shellArgumentFactory(key)} ` +
231
+ `--deploy-id ${deployId} --storage-id ${CLIENT_BUNDLE_STORAGE_ID} --rm`,
232
+ );
233
+ }
234
+ if (stale.length > 0) logger.warn('removed bundle parts a smaller build no longer produces', { buildId, stale });
235
+
236
+ logger.info('pushed bundle', { host, routePath, artifacts: artifactPaths.length });
237
+ pushed++;
238
+ }
239
+ return { pushed, skipped };
240
+ };
241
+
242
+ /**
243
+ * Downloads a deployment's client bundles from file storage and installs them into the served
244
+ * public directory.
245
+ *
246
+ * Merging and extraction run in-process through the build module; only the download needs a
247
+ * child process, for the same `.env` reason {@link pushClientBundle} documents.
248
+ * @function pullClientBundle
249
+ * @param {object} options
250
+ * @param {string} options.deployId - The deployment identifier.
251
+ * @param {string[]} [options.hosts] - Optional host filter; empty selects every configured host.
252
+ * @param {boolean} [options.dev] - Pull the development environment; defaults to production.
253
+ * @returns {Promise<{pulled: number, skipped: number}>} How many bundles were installed and skipped.
254
+ * @memberof clientBundle
255
+ */
256
+ const expectedBundleBytes = ({ manifest, partPaths }) => {
257
+ const sizes = partPaths.map((partPath) => manifest[clientBundleManifestKey(partPath)]?.bytes);
258
+ return sizes.every((bytes) => Number.isFinite(bytes)) ? sizes.reduce((total, bytes) => total + bytes, 0) : undefined;
259
+ };
260
+
261
+ /**
262
+ * The manifest key a locally pulled part corresponds to. A pull writes the downloaded archive as
263
+ * `<key>.zip`, so the wrapper suffix is what has to come off to address the entry that produced it.
264
+ * @function clientBundleManifestKey
265
+ * @param {string} partPath - Local path of a pulled part.
266
+ * @returns {string} The tracked storage key.
267
+ * @memberof clientBundle
268
+ */
269
+ const clientBundleManifestKey = (partPath) => partPath.replace(/^\.\//, '').replace(/\.zip$/i, '');
270
+
271
+ const pullClientBundle = async ({ deployId, hosts = [], dev = false }) => {
272
+ const baseCommand = cli('underpost', { local: true });
273
+ const env = dev ? 'development' : 'production';
274
+
275
+ const entries = clientBundleEntriesFactory({ deployId, hosts });
276
+ if (entries.length === 0) {
277
+ logger.error('no bundle routes resolved', { deployId, hosts });
278
+ return { pulled: 0, skipped: 0 };
279
+ }
280
+
281
+ shellExec(`${baseCommand} app load --env ${env} --args deploy-id=${deployId}`);
282
+ fs.mkdirSync(CLIENT_BUNDLE_DIRECTORY, { recursive: true });
283
+ shellExec(
284
+ `${baseCommand} fs ${CLIENT_BUNDLE_DIRECTORY.replace(/^\.\//, '')} --tracked --deploy-id ${deployId} ` +
285
+ `--storage-id ${CLIENT_BUNDLE_STORAGE_ID} --pull --omit-unzip`,
286
+ );
287
+
288
+ const manifest = clientBundleManifestFactory(deployId);
289
+
290
+ let pulled = 0;
291
+ let skipped = 0;
292
+ for (const { host, routePath, buildId, zipPath, publicPath } of entries) {
293
+ const { partPaths, artifactPaths } = clientBundleArtifactPaths(zipPath);
294
+ if (artifactPaths.length === 0) {
295
+ logger.warn('bundle not found, skipping', { host, routePath, buildId, zipPath });
296
+ skipped++;
297
+ continue;
298
+ }
299
+
300
+ if (partPaths.length > 0) {
301
+ const { mergedBytes } = await mergeClientBuildZip({ buildPrefix: zipPath, logger });
302
+ // The parts are reassembled blind, so a single stale or short one is only visible as a
303
+ // corrupt archive far downstream. The pushed sizes are what identify it here instead.
304
+ const expectedBytes = expectedBundleBytes({ manifest, partPaths });
305
+ if (expectedBytes !== undefined && expectedBytes !== mergedBytes)
306
+ throw new Error(
307
+ `Bundle ${buildId} reassembled to ${mergedBytes} bytes, expected ${expectedBytes} from ${partPaths.length} pushed parts`,
308
+ );
309
+ }
310
+ const { outputPath } = await unzipClientBuild({ buildPrefix: zipPath, logger });
311
+
312
+ fs.removeSync(zipPath);
313
+ for (const partPath of partPaths) fs.removeSync(partPath);
314
+
315
+ if (!fs.existsSync(outputPath)) {
316
+ logger.warn('extracted bundle directory not found, skipping', { host, routePath, outputPath });
317
+ skipped++;
318
+ continue;
319
+ }
320
+
321
+ // The previous deployment's document root can be owned by the web server user, so the
322
+ // replace goes through sudo; the extracted tree this process just wrote does not.
323
+ shellExec(`sudo rm -rf ${publicPath}`);
324
+ if (routePath !== '/') shellExec(`sudo mkdir -p public/${host}`);
325
+ fs.copySync(outputPath, publicPath);
326
+
327
+ logger.info('pulled bundle', { host, routePath, publicPath });
328
+ pulled++;
329
+ }
330
+ return { pulled, skipped };
331
+ };
332
+
333
+ export {
334
+ CLIENT_BUNDLE_DIRECTORY,
335
+ clientBundleTrackedKeys,
336
+ clientBundleManifestFactory,
337
+ clientBundleManifestKey,
338
+ DEFAULT_SPLIT_MB,
339
+ clientBundleArtifactPaths,
340
+ clientBundleConfServerPath,
341
+ clientBundleEntriesFactory,
342
+ clientBundleHostFilter,
343
+ clientBundleSplitFlag,
344
+ clientBundleStorageFilePath,
345
+ pullClientBundle,
346
+ pushClientBundle,
347
+ };
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Mongo Express Deployment Module
3
+ * @module src/db/mongo/MongoExpress
4
+ * @namespace MongoExpress
5
+ * @description Deploys the mongo-express web client used to inspect the cluster's MongoDB
6
+ * StatefulSet collections. The workload is a plain Deployment plus a ClusterIP Service, so the
7
+ * same manifests converge identically under Kind, Kubeadm and K3s; the only runtime-dependent
8
+ * decision is whether the deployed mongod enforces authentication, which selects the overlay.
9
+ */
10
+
11
+ import { loggerFactory } from '../../server/ops/logger.js';
12
+ import { shellExec } from '../../server/runtime/process.js';
13
+ // Cyclic by construction (index -> cluster -> MongoExpress -> index), same as MongoBootstrap.js.
14
+ // Safe because the binding is only dereferenced inside method bodies, never at module scope.
15
+ import Underpost from '../../index.js';
16
+ import { MONGODB_DEFAULT_PORT, MONGODB_SERVICE_NAME, MONGODB_STATEFULSET_NAME } from './MongooseDB.js';
17
+
18
+ const logger = loggerFactory(import.meta);
19
+
20
+ const MONGO_EXPRESS_NAME = 'mongo-express';
21
+ const MONGO_EXPRESS_SERVICE_NAME = 'mongo-express-service';
22
+ const MONGO_EXPRESS_IMAGE = 'mongo-express:1.0.2';
23
+ const MONGO_EXPRESS_PORT = 8081;
24
+ const MONGO_EXPRESS_NODE_PORT = 32081;
25
+ const MONGO_EXPRESS_BASE_URL = '/mongo/';
26
+ // The UI reads the root credentials and gates its own session with the same pair, so it consumes
27
+ // the StatefulSet's secret rather than owning a second credential of its own.
28
+ const MONGO_EXPRESS_SECRET_NAME = 'mongodb-secret';
29
+ const MONGO_EXPRESS_MANIFEST_DIR = 'manifests/deployment/mongo-express';
30
+ const MONGO_EXPRESS_NO_AUTH_MANIFEST_DIR = 'manifests/deployment/mongo-express-no-auth';
31
+
32
+ // Literal and unescaped: kubectl's jsonpath quotes carry escape sequences of their own, and no
33
+ // mongod argument contains a pipe.
34
+ const MONGOD_ARGS_FIELD_SEPARATOR = '|';
35
+
36
+ /**
37
+ * Parses the mongod argument vector reported by `kubectl -o jsonpath` for the StatefulSet.
38
+ * @param {string} raw - Raw jsonpath stdout: the StatefulSet name, then one field per argument.
39
+ * @returns {Array<string>|null} Argument vector, or null when no StatefulSet was reported.
40
+ * @memberof MongoExpress
41
+ */
42
+ const parseMongodArgs = (raw) => {
43
+ const value = `${raw || ''}`;
44
+ if (!value.trim()) return null;
45
+ const [, ...args] = value.split(MONGOD_ARGS_FIELD_SEPARATOR);
46
+ return args.map((arg) => arg.trim()).filter(Boolean);
47
+ };
48
+
49
+ /**
50
+ * Decides whether mongo-express must authenticate against the deployed mongod.
51
+ *
52
+ * The deployed StatefulSet is the authority: `manifests/mongodb` starts mongod with `--auth` and
53
+ * `manifests/mongodb-4.4` does not, and either can be the one running regardless of which flag
54
+ * this invocation carries. The flags only answer for a cluster with no StatefulSet deployed yet,
55
+ * where `--mongodb` (the auth-enabled manifest set) is the default the platform installs.
56
+ * @param {object} [params={}] - Resolution inputs.
57
+ * @param {Array<string>|null} [params.args=null] - mongod argument vector, or null when undeployed.
58
+ * @param {object} [params.options={}] - Cluster options (`mongodb`, `mongodb4`).
59
+ * @returns {boolean} True when the admin credential pair must be projected.
60
+ * @memberof MongoExpress
61
+ */
62
+ const mongoAuthEnabledFactory = ({ args = null, options = {} } = {}) => {
63
+ if (Array.isArray(args)) return args.includes('--auth');
64
+ return options.mongodb4 !== true;
65
+ };
66
+
67
+ /**
68
+ * Resolves the kustomization root the auth mode selects.
69
+ * @param {object} [params={}] - Path inputs.
70
+ * @param {string} [params.underpostRoot='.'] - Repository root holding `manifests/`.
71
+ * @param {boolean} [params.authEnabled=true] - Whether mongod enforces authentication.
72
+ * @returns {string} Kustomization directory path.
73
+ * @memberof MongoExpress
74
+ */
75
+ const mongoExpressManifestPathFactory = ({ underpostRoot = '.', authEnabled = true } = {}) =>
76
+ `${underpostRoot}/${authEnabled ? MONGO_EXPRESS_MANIFEST_DIR : MONGO_EXPRESS_NO_AUTH_MANIFEST_DIR}`;
77
+
78
+ /**
79
+ * Resolves the opt-in NodePort Service manifest path. It lives in the base directory and is left
80
+ * out of both kustomizations: exposing an admin UI on the node network is a caller's decision.
81
+ * @param {object} [params={}] - Path inputs.
82
+ * @param {string} [params.underpostRoot='.'] - Repository root holding `manifests/`.
83
+ * @returns {string} NodePort manifest path.
84
+ * @memberof MongoExpress
85
+ */
86
+ const mongoExpressNodePortManifestFactory = ({ underpostRoot = '.' } = {}) =>
87
+ `${underpostRoot}/${MONGO_EXPRESS_MANIFEST_DIR}/mongo-express-nodeport.yaml`;
88
+
89
+ /**
90
+ * @class MongoExpress
91
+ * @description Static deployment surface for the mongo-express admin client.
92
+ * @memberof MongoExpress
93
+ */
94
+ class MongoExpress {
95
+ /**
96
+ * Reads the mongod argument vector from the deployed MongoDB StatefulSet.
97
+ *
98
+ * The projection leads with the StatefulSet name so a deployed server that declares no arguments
99
+ * stays distinguishable from no server at all — one is mongod on its defaults, the other is a
100
+ * cluster with no data tier yet, and both would otherwise reduce to empty output.
101
+ * @param {string} [namespace='default'] - Namespace holding the StatefulSet.
102
+ * @returns {Array<string>|null} Argument vector, or null when no StatefulSet is deployed.
103
+ * @memberof MongoExpress
104
+ */
105
+ static readMongodArgs(namespace = 'default') {
106
+ const raw = shellExec(
107
+ `kubectl get statefulset ${MONGODB_STATEFULSET_NAME} -n ${namespace} --ignore-not-found -o jsonpath=` +
108
+ `'{.metadata.name}{"${MONGOD_ARGS_FIELD_SEPARATOR}"}` +
109
+ `{range .spec.template.spec.containers[?(@.name=="${MONGODB_STATEFULSET_NAME}")].args[*]}` +
110
+ `{@}{"${MONGOD_ARGS_FIELD_SEPARATOR}"}{end}'`,
111
+ { stdout: true, silent: true, silentOnError: true },
112
+ );
113
+ return parseMongodArgs(raw);
114
+ }
115
+
116
+ /**
117
+ * Deploys mongo-express and, once it is Ready, applies the exposure and placement the caller
118
+ * asked for. Cluster-type agnostic by construction: only the image pull differs between Kind
119
+ * (loaded into the node's image store) and Kubeadm/K3s (pulled through the CRI).
120
+ * @param {object} [params={}] - Deployment parameters.
121
+ * @param {string} [params.namespace='default'] - Target namespace.
122
+ * @param {string} [params.underpostRoot='.'] - Repository root holding `manifests/`.
123
+ * @param {object} [params.options={}] - Cluster options (`pullImage`, `nodePort`, `nodeName`, `mongodb4`).
124
+ * @returns {Promise<boolean>} Whether the deployment reported Ready.
125
+ * @memberof MongoExpress
126
+ */
127
+ static async deploy({ namespace = 'default', underpostRoot = '.', options = {} } = {}) {
128
+ // Secret before workload: the Deployment's secretKeyRef must resolve at pod admission, and
129
+ // the encrypted store stays the source of truth wherever a manifest exists.
130
+ if (!Underpost.secret.applyIfPresent(MONGO_EXPRESS_SECRET_NAME, namespace))
131
+ Underpost.secret.applyFromOriginSeed(MONGO_EXPRESS_SECRET_NAME, namespace);
132
+
133
+ if (options.pullImage) Underpost.cluster.pullImage(MONGO_EXPRESS_IMAGE, options);
134
+
135
+ const args = MongoExpress.readMongodArgs(namespace);
136
+ const authEnabled = mongoAuthEnabledFactory({ args, options });
137
+ const manifestPath = mongoExpressManifestPathFactory({ underpostRoot, authEnabled });
138
+ logger.info('Deploying mongo-express', {
139
+ namespace,
140
+ authEnabled,
141
+ manifestPath,
142
+ mongodb: args ? 'deployed' : 'not deployed',
143
+ });
144
+ shellExec(`kubectl apply -k ${manifestPath} -n ${namespace}`);
145
+
146
+ const ready = await Underpost.test.statusMonitor(MONGO_EXPRESS_NAME, 'Running', 'pods', 1000, 60 * 10);
147
+ if (!ready) {
148
+ logger.error('mongo-express did not report Running', { namespace });
149
+ return false;
150
+ }
151
+
152
+ if (options.nodePort)
153
+ shellExec(`kubectl apply -f ${mongoExpressNodePortManifestFactory({ underpostRoot })} -n ${namespace}`);
154
+ if (options.nodeName)
155
+ Underpost.cluster.pinToNode({
156
+ kind: 'deployment',
157
+ name: MONGO_EXPRESS_NAME,
158
+ namespace,
159
+ node: options.nodeName,
160
+ });
161
+
162
+ logger.info('mongo-express ready', {
163
+ server: `${MONGODB_STATEFULSET_NAME}-0.${MONGODB_SERVICE_NAME}:${MONGODB_DEFAULT_PORT}`,
164
+ basicAuth: `secret/${MONGO_EXPRESS_SECRET_NAME}`,
165
+ clusterIp: `${MONGO_EXPRESS_SERVICE_NAME}:${MONGO_EXPRESS_PORT}${MONGO_EXPRESS_BASE_URL}`,
166
+ nodePort: options.nodePort ? `<node>:${MONGO_EXPRESS_NODE_PORT}${MONGO_EXPRESS_BASE_URL}` : null,
167
+ portForward: `kubectl port-forward -n ${namespace} svc/${MONGO_EXPRESS_SERVICE_NAME} ${MONGO_EXPRESS_PORT}:${MONGO_EXPRESS_PORT}`,
168
+ });
169
+ return true;
170
+ }
171
+ }
172
+
173
+ export {
174
+ MONGO_EXPRESS_BASE_URL,
175
+ MONGO_EXPRESS_IMAGE,
176
+ MONGO_EXPRESS_NAME,
177
+ MONGO_EXPRESS_NODE_PORT,
178
+ MONGO_EXPRESS_PORT,
179
+ MONGO_EXPRESS_SECRET_NAME,
180
+ MONGO_EXPRESS_SERVICE_NAME,
181
+ MongoExpress,
182
+ mongoAuthEnabledFactory,
183
+ mongoExpressManifestPathFactory,
184
+ mongoExpressNodePortManifestFactory,
185
+ parseMongodArgs,
186
+ };
187
+ export default MongoExpress;
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * gRPC transport for the Cyberia Engine data pipeline.
3
3
  *
4
- * Runs alongside Express on a separate port (default 50051).
5
- * Thin adapter over src/projects/cyberia/instance-data.js the shared
6
- * world-load/boot assembly also served by the REST fallback at
7
- * /api/cyberia-instance/boot/* — so both transports stay equivalent.
4
+ * Runs beside Express on its own port (default 50051). Thin adapter over
5
+ * src/projects/cyberia/instance-data.js, the same assembly the REST fallback
6
+ * at /api/cyberia-instance/boot/* serves, so both transports stay equivalent.
8
7
  *
9
8
  * @module src/grpc/cyberia/grpc-server.js
10
9
  */
@@ -16,7 +15,6 @@ import { fileURLToPath } from 'url';
16
15
  import { loggerFactory } from '../../server/ops/logger.js';
17
16
  import {
18
17
  buildFallbackConfig,
19
- buildFallbackInstanceConfig,
20
18
  fetchFullInstance,
21
19
  fetchMapData,
22
20
  fetchObjectLayer,
@@ -131,7 +129,7 @@ class GrpcServer {
131
129
 
132
130
  server.addService(proto.CyberiaDataService.service, buildHandlers(dbKey));
133
131
 
134
- // gRPC runs over Kubernetes internal network (ClusterIP) always insecure
132
+ // gRPC stays on the cluster-internal network, so credentials are insecure.
135
133
  const creds = grpc.ServerCredentials.createInsecure();
136
134
 
137
135
  return new Promise((resolve, reject) => {
@@ -159,4 +157,4 @@ class GrpcServer {
159
157
  }
160
158
  }
161
159
 
162
- export { GrpcServer, buildFallbackConfig, buildFallbackInstanceConfig };
160
+ export { GrpcServer, buildFallbackConfig };
package/src/index.js CHANGED
@@ -52,7 +52,7 @@ class Underpost {
52
52
  * @type {String}
53
53
  * @memberof Underpost
54
54
  */
55
- static version = 'v3.3.73';
55
+ static version = 'v3.3.77';
56
56
 
57
57
  /**
58
58
  * Required Node.js major version