cyberia 3.0.3 → 3.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/{.env.production → .env.example} +20 -4
  2. package/.github/workflows/engine-cyberia.cd.yml +43 -10
  3. package/.github/workflows/engine-cyberia.ci.yml +48 -26
  4. package/.github/workflows/ghpkg.ci.yml +5 -5
  5. package/.github/workflows/gitlab.ci.yml +1 -1
  6. package/.github/workflows/hardhat.ci.yml +82 -0
  7. package/.github/workflows/npmpkg.ci.yml +60 -14
  8. package/.github/workflows/publish.ci.yml +26 -7
  9. package/.github/workflows/publish.cyberia.ci.yml +5 -5
  10. package/.github/workflows/pwa-microservices-template-page.cd.yml +6 -7
  11. package/.github/workflows/pwa-microservices-template-test.ci.yml +4 -4
  12. package/.github/workflows/release.cd.yml +14 -8
  13. package/.vscode/extensions.json +9 -8
  14. package/.vscode/settings.json +3 -2
  15. package/CHANGELOG.md +643 -1
  16. package/CLI-HELP.md +132 -57
  17. package/Dockerfile +4 -2
  18. package/README.md +347 -22
  19. package/WHITE-PAPER.md +1540 -0
  20. package/bin/build.js +21 -12
  21. package/bin/cyberia.js +2640 -106
  22. package/bin/deploy.js +258 -372
  23. package/bin/file.js +5 -1
  24. package/bin/index.js +2640 -106
  25. package/bin/vs.js +3 -3
  26. package/conf.js +169 -105
  27. package/deployment.yaml +236 -20
  28. package/hardhat/.env.example +31 -0
  29. package/hardhat/README.md +531 -0
  30. package/hardhat/WHITE-PAPER.md +1540 -0
  31. package/hardhat/contracts/ObjectLayerToken.sol +391 -0
  32. package/hardhat/deployments/.gitkeep +0 -0
  33. package/hardhat/deployments/hardhat-ObjectLayerToken.json +11 -0
  34. package/hardhat/hardhat.config.js +136 -0
  35. package/hardhat/ignition/modules/ObjectLayerToken.js +21 -0
  36. package/hardhat/networks/besu-object-layer.network.json +138 -0
  37. package/hardhat/package-lock.json +4323 -0
  38. package/hardhat/package.json +36 -0
  39. package/hardhat/scripts/deployObjectLayerToken.js +98 -0
  40. package/hardhat/test/ObjectLayerToken.js +592 -0
  41. package/hardhat/types/ethers-contracts/ObjectLayerToken.ts +690 -0
  42. package/hardhat/types/ethers-contracts/common.ts +92 -0
  43. package/hardhat/types/ethers-contracts/factories/ObjectLayerToken__factory.ts +1055 -0
  44. package/hardhat/types/ethers-contracts/factories/index.ts +4 -0
  45. package/hardhat/types/ethers-contracts/hardhat.d.ts +47 -0
  46. package/hardhat/types/ethers-contracts/index.ts +6 -0
  47. package/jsdoc.dd-cyberia.json +68 -0
  48. package/jsdoc.json +65 -49
  49. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +5 -4
  50. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +5 -4
  51. package/manifests/deployment/dd-cyberia-development/deployment.yaml +562 -0
  52. package/manifests/deployment/dd-cyberia-development/proxy.yaml +297 -0
  53. package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +132 -0
  54. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  55. package/manifests/deployment/dd-test-development/deployment.yaml +88 -74
  56. package/manifests/deployment/dd-test-development/proxy.yaml +13 -4
  57. package/manifests/deployment/playwright/deployment.yaml +1 -1
  58. package/manifests/pv-pvc-dd.yaml +1 -1
  59. package/nodemon.json +1 -1
  60. package/package.json +60 -48
  61. package/proxy.yaml +118 -10
  62. package/pv-pvc.yaml +132 -0
  63. package/scripts/k3s-node-setup.sh +1 -1
  64. package/scripts/ports-ls.sh +2 -0
  65. package/scripts/rhel-grpc-setup.sh +56 -0
  66. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.controller.js +47 -1
  67. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.model.js +17 -2
  68. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +5 -0
  69. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +80 -7
  70. package/src/api/cyberia-dialogue/cyberia-dialogue.controller.js +93 -0
  71. package/src/api/cyberia-dialogue/cyberia-dialogue.model.js +36 -0
  72. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +29 -0
  73. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +51 -0
  74. package/src/api/cyberia-entity/cyberia-entity.controller.js +74 -0
  75. package/src/api/cyberia-entity/cyberia-entity.model.js +24 -0
  76. package/src/api/cyberia-entity/cyberia-entity.router.js +27 -0
  77. package/src/api/cyberia-entity/cyberia-entity.service.js +42 -0
  78. package/src/api/cyberia-instance/cyberia-fallback-world.js +368 -0
  79. package/src/api/cyberia-instance/cyberia-instance.controller.js +92 -0
  80. package/src/api/cyberia-instance/cyberia-instance.model.js +84 -0
  81. package/src/api/cyberia-instance/cyberia-instance.router.js +63 -0
  82. package/src/api/cyberia-instance/cyberia-instance.service.js +191 -0
  83. package/src/api/cyberia-instance/cyberia-portal-connector.js +486 -0
  84. package/src/api/cyberia-instance-conf/cyberia-instance-conf.controller.js +74 -0
  85. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +413 -0
  86. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +228 -0
  87. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +27 -0
  88. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +42 -0
  89. package/src/api/cyberia-map/cyberia-map.controller.js +79 -0
  90. package/src/api/cyberia-map/cyberia-map.model.js +30 -0
  91. package/src/api/cyberia-map/cyberia-map.router.js +40 -0
  92. package/src/api/cyberia-map/cyberia-map.service.js +74 -0
  93. package/src/api/document/document.service.js +1 -1
  94. package/src/api/file/file.controller.js +3 -1
  95. package/src/api/file/file.ref.json +18 -0
  96. package/src/api/file/file.service.js +28 -5
  97. package/src/api/ipfs/ipfs.controller.js +4 -25
  98. package/src/api/ipfs/ipfs.model.js +43 -34
  99. package/src/api/ipfs/ipfs.router.js +8 -13
  100. package/src/api/ipfs/ipfs.service.js +56 -104
  101. package/src/api/object-layer/README.md +347 -22
  102. package/src/api/object-layer/object-layer.controller.js +6 -2
  103. package/src/api/object-layer/object-layer.model.js +12 -8
  104. package/src/api/object-layer/object-layer.router.js +698 -42
  105. package/src/api/object-layer/object-layer.service.js +119 -37
  106. package/src/api/object-layer-render-frames/object-layer-render-frames.model.js +1 -2
  107. package/src/api/user/user.router.js +10 -5
  108. package/src/api/user/user.service.js +15 -14
  109. package/src/cli/baremetal.js +6 -10
  110. package/src/cli/cloud-init.js +0 -3
  111. package/src/cli/cluster.js +7 -7
  112. package/src/cli/db.js +723 -857
  113. package/src/cli/deploy.js +215 -105
  114. package/src/cli/env.js +34 -5
  115. package/src/cli/fs.js +5 -4
  116. package/src/cli/image.js +0 -3
  117. package/src/cli/index.js +83 -15
  118. package/src/cli/kubectl.js +211 -0
  119. package/src/cli/monitor.js +5 -6
  120. package/src/cli/release.js +284 -0
  121. package/src/cli/repository.js +708 -62
  122. package/src/cli/run.js +371 -151
  123. package/src/cli/secrets.js +73 -2
  124. package/src/cli/ssh.js +1 -1
  125. package/src/cli/test.js +3 -3
  126. package/src/client/Cryptokoyn.index.js +3 -4
  127. package/src/client/CyberiaPortal.index.js +3 -4
  128. package/src/client/Default.index.js +3 -4
  129. package/src/client/Itemledger.index.js +4 -963
  130. package/src/client/Underpost.index.js +3 -4
  131. package/src/client/components/core/AgGrid.js +20 -5
  132. package/src/client/components/core/Alert.js +2 -2
  133. package/src/client/components/core/AppStore.js +69 -0
  134. package/src/client/components/core/CalendarCore.js +2 -2
  135. package/src/client/components/core/Content.js +22 -3
  136. package/src/client/components/core/Docs.js +30 -6
  137. package/src/client/components/core/DropDown.js +137 -17
  138. package/src/client/components/core/FileExplorer.js +71 -4
  139. package/src/client/components/core/Input.js +1 -1
  140. package/src/client/components/core/Keyboard.js +2 -2
  141. package/src/client/components/core/LogIn.js +2 -2
  142. package/src/client/components/core/LogOut.js +2 -2
  143. package/src/client/components/core/Modal.js +20 -7
  144. package/src/client/components/core/Panel.js +0 -1
  145. package/src/client/components/core/PanelForm.js +19 -19
  146. package/src/client/components/core/RichText.js +1 -2
  147. package/src/client/components/core/SocketIo.js +82 -29
  148. package/src/client/components/core/SocketIoHandler.js +75 -0
  149. package/src/client/components/core/Stream.js +143 -95
  150. package/src/client/components/core/Webhook.js +40 -7
  151. package/src/client/components/cryptokoyn/AppStoreCryptokoyn.js +5 -0
  152. package/src/client/components/cryptokoyn/LogInCryptokoyn.js +3 -3
  153. package/src/client/components/cryptokoyn/LogOutCryptokoyn.js +2 -2
  154. package/src/client/components/cryptokoyn/MenuCryptokoyn.js +3 -3
  155. package/src/client/components/cryptokoyn/SocketIoCryptokoyn.js +3 -51
  156. package/src/client/components/cyberia/InstanceEngineCyberia.js +700 -0
  157. package/src/client/components/cyberia/MapEngineCyberia.js +1359 -2
  158. package/src/client/components/cyberia/ObjectLayerEngineModal.js +17 -6
  159. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +92 -54
  160. package/src/client/components/cyberia-portal/AppStoreCyberiaPortal.js +5 -0
  161. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +217 -30
  162. package/src/client/components/cyberia-portal/CssCyberiaPortal.js +44 -2
  163. package/src/client/components/cyberia-portal/LogInCyberiaPortal.js +3 -4
  164. package/src/client/components/cyberia-portal/LogOutCyberiaPortal.js +2 -2
  165. package/src/client/components/cyberia-portal/MenuCyberiaPortal.js +104 -9
  166. package/src/client/components/cyberia-portal/RoutesCyberiaPortal.js +5 -0
  167. package/src/client/components/cyberia-portal/SocketIoCyberiaPortal.js +3 -49
  168. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +4 -0
  169. package/src/client/components/default/AppStoreDefault.js +5 -0
  170. package/src/client/components/default/LogInDefault.js +3 -3
  171. package/src/client/components/default/LogOutDefault.js +2 -2
  172. package/src/client/components/default/MenuDefault.js +5 -5
  173. package/src/client/components/default/SocketIoDefault.js +3 -51
  174. package/src/client/components/itemledger/AppStoreItemledger.js +5 -0
  175. package/src/client/components/itemledger/LogInItemledger.js +3 -3
  176. package/src/client/components/itemledger/LogOutItemledger.js +2 -2
  177. package/src/client/components/itemledger/MenuItemledger.js +3 -3
  178. package/src/client/components/itemledger/SocketIoItemledger.js +3 -51
  179. package/src/client/components/underpost/AppStoreUnderpost.js +5 -0
  180. package/src/client/components/underpost/CssUnderpost.js +59 -0
  181. package/src/client/components/underpost/LogInUnderpost.js +6 -3
  182. package/src/client/components/underpost/LogOutUnderpost.js +4 -2
  183. package/src/client/components/underpost/MenuUnderpost.js +104 -18
  184. package/src/client/components/underpost/RoutesUnderpost.js +2 -0
  185. package/src/client/components/underpost/SocketIoUnderpost.js +3 -51
  186. package/src/client/public/cryptokoyn/assets/logo/base-icon.png +0 -0
  187. package/src/client/public/cryptokoyn/browserconfig.xml +12 -0
  188. package/src/client/public/cryptokoyn/microdata.json +85 -0
  189. package/src/client/public/cryptokoyn/site.webmanifest +57 -0
  190. package/src/client/public/cryptokoyn/sitemap +3 -3
  191. package/src/client/public/default/sitemap +3 -3
  192. package/src/client/public/itemledger/browserconfig.xml +2 -2
  193. package/src/client/public/itemledger/manifest.webmanifest +4 -4
  194. package/src/client/public/itemledger/microdata.json +71 -0
  195. package/src/client/public/itemledger/sitemap +3 -3
  196. package/src/client/public/itemledger/yandex-browser-manifest.json +2 -2
  197. package/src/client/public/test/sitemap +3 -3
  198. package/src/client/services/core/core.service.js +20 -8
  199. package/src/client/services/cyberia-dialogue/cyberia-dialogue.service.js +105 -0
  200. package/src/client/services/cyberia-entity/cyberia-entity.management.js +57 -0
  201. package/src/client/services/cyberia-entity/cyberia-entity.service.js +105 -0
  202. package/src/client/services/cyberia-instance/cyberia-instance.management.js +194 -0
  203. package/src/client/services/cyberia-instance/cyberia-instance.service.js +122 -0
  204. package/src/client/services/cyberia-instance-conf/cyberia-instance-conf.service.js +105 -0
  205. package/src/client/services/cyberia-map/cyberia-map.management.js +193 -0
  206. package/src/client/services/cyberia-map/cyberia-map.service.js +126 -0
  207. package/src/client/services/instance/instance.management.js +2 -2
  208. package/src/client/services/ipfs/ipfs.service.js +3 -23
  209. package/src/client/services/object-layer/object-layer.management.js +3 -3
  210. package/src/client/services/object-layer/object-layer.service.js +21 -0
  211. package/src/client/services/user/user.management.js +2 -2
  212. package/src/client/ssr/body/404.js +15 -11
  213. package/src/client/ssr/body/500.js +15 -11
  214. package/src/client/ssr/body/SwaggerDarkMode.js +285 -0
  215. package/src/client/ssr/head/PwaItemledger.js +60 -0
  216. package/src/client/ssr/offline/NoNetworkConnection.js +11 -10
  217. package/src/client/ssr/pages/CyberiaServerMetrics.js +1 -1
  218. package/src/client/ssr/pages/Test.js +11 -10
  219. package/src/client.build.js +0 -3
  220. package/src/client.dev.js +0 -3
  221. package/src/db/DataBaseProvider.js +17 -2
  222. package/src/db/mariadb/MariaDB.js +14 -9
  223. package/src/db/mongo/MongooseDB.js +17 -1
  224. package/src/grpc/cyberia/OFF_CHAIN_ECONOMY.md +305 -0
  225. package/src/grpc/cyberia/README.md +326 -0
  226. package/src/grpc/cyberia/grpc-server.js +530 -0
  227. package/src/index.js +24 -1
  228. package/src/proxy.js +0 -3
  229. package/src/runtime/express/Dockerfile +4 -0
  230. package/src/runtime/express/Express.js +33 -10
  231. package/src/runtime/lampp/Dockerfile +13 -2
  232. package/src/runtime/lampp/Lampp.js +33 -17
  233. package/src/runtime/wp/Dockerfile +68 -0
  234. package/src/runtime/wp/Wp.js +639 -0
  235. package/src/server/auth.js +36 -15
  236. package/src/server/backup.js +39 -12
  237. package/src/server/besu-genesis-generator.js +1630 -0
  238. package/src/server/client-build-docs.js +133 -17
  239. package/src/server/client-build-live.js +9 -18
  240. package/src/server/client-build.js +229 -101
  241. package/src/server/client-dev-server.js +14 -13
  242. package/src/server/client-formatted.js +109 -57
  243. package/src/server/conf.js +391 -164
  244. package/src/server/cron.js +27 -24
  245. package/src/server/dns.js +29 -12
  246. package/src/server/downloader.js +0 -2
  247. package/src/server/ipfs-client.js +24 -1
  248. package/src/server/logger.js +27 -9
  249. package/src/server/object-layer.js +217 -103
  250. package/src/server/peer.js +8 -2
  251. package/src/server/process.js +1 -50
  252. package/src/server/proxy.js +4 -8
  253. package/src/server/runtime.js +30 -9
  254. package/src/server/semantic-layer-generator-floor.js +359 -0
  255. package/src/server/semantic-layer-generator-skin.js +1294 -0
  256. package/src/server/semantic-layer-generator.js +116 -555
  257. package/src/server/ssr.js +0 -3
  258. package/src/server/start.js +19 -12
  259. package/src/server/tls.js +0 -2
  260. package/src/server.js +0 -4
  261. package/src/ws/IoInterface.js +1 -10
  262. package/src/ws/IoServer.js +14 -33
  263. package/src/ws/core/channels/core.ws.chat.js +65 -20
  264. package/src/ws/core/channels/core.ws.mailer.js +113 -32
  265. package/src/ws/core/channels/core.ws.stream.js +90 -31
  266. package/src/ws/core/core.ws.connection.js +12 -33
  267. package/src/ws/core/core.ws.emit.js +10 -26
  268. package/src/ws/core/core.ws.server.js +25 -58
  269. package/src/ws/default/channels/default.ws.main.js +53 -12
  270. package/src/ws/default/default.ws.connection.js +26 -13
  271. package/src/ws/default/default.ws.server.js +30 -12
  272. package/.env.development +0 -43
  273. package/.env.test +0 -43
  274. package/hardhat/contracts/CryptoKoyn.sol +0 -59
  275. package/hardhat/contracts/ItemLedger.sol +0 -73
  276. package/hardhat/contracts/Lock.sol +0 -34
  277. package/hardhat/hardhat.config.cjs +0 -45
  278. package/hardhat/ignition/modules/Lock.js +0 -18
  279. package/hardhat/networks/cryptokoyn-itemledger.network.json +0 -29
  280. package/hardhat/scripts/deployCryptokoyn.cjs +0 -25
  281. package/hardhat/scripts/deployItemledger.cjs +0 -25
  282. package/hardhat/test/Lock.js +0 -126
  283. package/hardhat/white-paper.md +0 -581
  284. package/src/client/components/cryptokoyn/CommonCryptokoyn.js +0 -29
  285. package/src/client/components/cryptokoyn/ElementsCryptokoyn.js +0 -38
  286. package/src/client/components/cyberia-portal/ElementsCyberiaPortal.js +0 -38
  287. package/src/client/components/default/ElementsDefault.js +0 -38
  288. package/src/client/components/itemledger/CommonItemledger.js +0 -29
  289. package/src/client/components/itemledger/ElementsItemledger.js +0 -38
  290. package/src/client/components/underpost/CommonUnderpost.js +0 -29
  291. package/src/client/components/underpost/ElementsUnderpost.js +0 -38
  292. package/src/ws/core/management/core.ws.chat.js +0 -8
  293. package/src/ws/core/management/core.ws.mailer.js +0 -16
  294. package/src/ws/core/management/core.ws.stream.js +0 -8
  295. package/src/ws/default/management/default.ws.main.js +0 -8
  296. package/white-paper.md +0 -581
@@ -0,0 +1,530 @@
1
+ /**
2
+ * gRPC server for the Cyberia Engine data pipeline.
3
+ *
4
+ * Runs alongside Express on a separate port (default 50051).
5
+ * Provides read-only RPCs for the Go game server to fetch
6
+ * ObjectLayers, Instances, and Maps from MongoDB.
7
+ *
8
+ * @module src/grpc/cyberia/grpc-server.js
9
+ */
10
+
11
+ import * as grpc from '@grpc/grpc-js';
12
+ import * as protoLoader from '@grpc/proto-loader';
13
+ import path from 'path';
14
+ import { fileURLToPath } from 'url';
15
+ import { DataBaseProvider } from '../../db/DataBaseProvider.js';
16
+ import { loggerFactory } from '../../server/logger.js';
17
+ import {
18
+ CYBERIA_INSTANCE_CONF_DEFAULTS as FALLBACK_CONFIG_DEFAULTS,
19
+ ENTITY_TYPE_DEFAULTS,
20
+ STATUS_ICONS,
21
+ } from '../../api/cyberia-instance-conf/cyberia-instance-conf.defaults.js';
22
+ import { generateFallbackWorld } from '../../api/cyberia-instance/cyberia-fallback-world.js';
23
+
24
+ const logger = loggerFactory(import.meta);
25
+
26
+ const __filename = fileURLToPath(import.meta.url);
27
+ const __dirname = path.dirname(__filename);
28
+
29
+ const PROTO_PATH = path.resolve(__dirname, '../../../cyberia-server/proto/cyberia.proto');
30
+
31
+ const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
32
+ keepCase: false,
33
+ longs: Number,
34
+ enums: String,
35
+ defaults: true,
36
+ oneofs: true,
37
+ });
38
+
39
+ const proto = grpc.loadPackageDefinition(packageDefinition).cyberia;
40
+
41
+ // ═══════════════════════════════════════════════════════════════════
42
+ // Helpers
43
+ // ═══════════════════════════════════════════════════════════════════
44
+
45
+ function getModels(dbKey) {
46
+ const bucket = DataBaseProvider.instance[dbKey];
47
+ if (!bucket || !bucket.mongoose || !bucket.mongoose.models) {
48
+ throw new Error(`DataBaseProvider not loaded for key "${dbKey}"`);
49
+ }
50
+ return bucket.mongoose.models;
51
+ }
52
+
53
+ // ── Mongoose doc → protobuf message converters ───────────────────
54
+
55
+ function toObjectLayerMsg(doc) {
56
+ const d = doc.data || {};
57
+ const item = d.item || {};
58
+ const stats = d.stats || {};
59
+ const ledger = d.ledger || {};
60
+ const render = d.render || {};
61
+ const rf = doc.objectLayerRenderFramesId;
62
+ let frameDuration = 250;
63
+ let isStateless = false;
64
+ if (rf && typeof rf === 'object') {
65
+ if (rf.frame_duration != null) frameDuration = rf.frame_duration;
66
+ if (rf.is_stateless != null) isStateless = rf.is_stateless;
67
+ }
68
+ return {
69
+ mongoId: String(doc._id),
70
+ stats: {
71
+ effect: stats.effect || 0,
72
+ resistance: stats.resistance || 0,
73
+ agility: stats.agility || 0,
74
+ range: stats.range || 0,
75
+ intelligence: stats.intelligence || 0,
76
+ utility: stats.utility || 0,
77
+ },
78
+ item: {
79
+ id: item.id || '',
80
+ type: item.type || '',
81
+ description: item.description || '',
82
+ activable: !!item.activable,
83
+ },
84
+ ledger: {
85
+ type: ledger.type || 'OFF_CHAIN',
86
+ address: ledger.address || '',
87
+ tokenId: ledger.tokenId || '',
88
+ },
89
+ render: {
90
+ cid: render.cid || '',
91
+ metadataCid: render.metadataCid || '',
92
+ },
93
+ sha256: doc.sha256 || '',
94
+ cid: doc.cid || '',
95
+ frameDuration,
96
+ isStateless,
97
+ };
98
+ }
99
+
100
+ function toEntityMsg(ent) {
101
+ return {
102
+ entityType: ent.entityType || 'floor',
103
+ initCellX: ent.initCellX || 0,
104
+ initCellY: ent.initCellY || 0,
105
+ dimX: ent.dimX || 1,
106
+ dimY: ent.dimY || 1,
107
+ color: ent.color || '',
108
+ objectLayerItemIds: ent.objectLayerItemIds || [],
109
+ spawnRadius: ent.spawnRadius || 0,
110
+ aggroRange: ent.aggroRange || 0,
111
+ maxLife: ent.maxLife || 0,
112
+ lifeRegen: ent.lifeRegen || 0,
113
+ portalSubtype: ent.portalSubtype || '',
114
+ };
115
+ }
116
+
117
+ function toMapMsg(doc) {
118
+ return {
119
+ mongoId: String(doc._id),
120
+ code: doc.code || '',
121
+ name: doc.name || '',
122
+ gridX: doc.gridX || 16,
123
+ gridY: doc.gridY || 16,
124
+ cellWidth: doc.cellWidth || 32,
125
+ cellHeight: doc.cellHeight || 32,
126
+ entities: (doc.entities || []).map(toEntityMsg),
127
+ };
128
+ }
129
+
130
+ function toInstanceMsg(doc) {
131
+ return {
132
+ mongoId: String(doc._id),
133
+ code: doc.code || '',
134
+ name: doc.name || '',
135
+ description: doc.description || '',
136
+ tags: doc.tags || [],
137
+ mapCodes: doc.cyberiaMapCodes || [],
138
+ portals: (doc.portals || []).map((p) => ({
139
+ sourceMapCode: p.sourceMapCode || '',
140
+ sourceCellX: p.sourceCellX || 0,
141
+ sourceCellY: p.sourceCellY || 0,
142
+ targetMapCode: p.targetMapCode || '',
143
+ targetCellX: p.targetCellX || 0,
144
+ targetCellY: p.targetCellY || 0,
145
+ portalMode: p.portalMode || 'inter-portal',
146
+ })),
147
+ topologyMode: doc.topologyMode || 'hybrid',
148
+ seed: doc.seed || '',
149
+ };
150
+ }
151
+
152
+ /**
153
+ * Converts a CyberiaInstanceConf Mongoose document (or plain object) into
154
+ * a complete InstanceConfig proto message.
155
+ * Any field that is null/undefined in `gc` falls back to FALLBACK_CONFIG_DEFAULTS,
156
+ * so partial DB documents always produce a fully playable config.
157
+ */
158
+ function toInstanceConfig(gc) {
159
+ const fb = FALLBACK_CONFIG_DEFAULTS;
160
+ if (!gc) return buildFallbackConfig();
161
+
162
+ const colors =
163
+ gc.colors && gc.colors.length > 0
164
+ ? gc.colors.map((c) => ({
165
+ key: c.key || '',
166
+ r: c.r ?? 0,
167
+ g: c.g ?? 0,
168
+ b: c.b ?? 0,
169
+ a: c.a ?? 255,
170
+ }))
171
+ : fb.colors.map((c) => ({ ...c }));
172
+
173
+ // Merge entity defaults: use canonical ENTITY_TYPE_DEFAULTS as base, overlay with
174
+ // any instance-specific overrides stored in gc.entityDefaults.
175
+ const gcDefaults = gc.entityDefaults && gc.entityDefaults.length > 0 ? gc.entityDefaults : [];
176
+ const gcDefaultsMap = Object.fromEntries(gcDefaults.map((d) => [d.entityType, d]));
177
+ const entityDefaults = ENTITY_TYPE_DEFAULTS.map((canonical) => {
178
+ const override = gcDefaultsMap[canonical.entityType] ?? {};
179
+ const dols = override.defaultObjectLayers ?? canonical.defaultObjectLayers ?? [];
180
+ return {
181
+ entityType: canonical.entityType,
182
+ liveItemIds: override.liveItemIds ?? canonical.liveItemIds,
183
+ deadItemIds: override.deadItemIds ?? canonical.deadItemIds,
184
+ colorKey: override.colorKey ?? canonical.colorKey,
185
+ defaultObjectLayers: dols.map((ol) => ({
186
+ itemId: ol.itemId || '',
187
+ active: !!ol.active,
188
+ quantity: ol.quantity || 0,
189
+ })),
190
+ };
191
+ });
192
+
193
+ return {
194
+ cellSize: gc.cellSize ?? fb.cellSize,
195
+ fps: gc.fps ?? fb.fps,
196
+ interpolationMs: gc.interpolationMs ?? fb.interpolationMs,
197
+ defaultObjWidth: gc.defaultObjWidth ?? fb.defaultObjWidth,
198
+ defaultObjHeight: gc.defaultObjHeight ?? fb.defaultObjHeight,
199
+ cameraSmoothing: gc.cameraSmoothing ?? fb.cameraSmoothing,
200
+ cameraZoom: gc.cameraZoom ?? fb.cameraZoom,
201
+ defaultWidthScreenFactor: gc.defaultWidthScreenFactor ?? fb.defaultWidthScreenFactor,
202
+ defaultHeightScreenFactor: gc.defaultHeightScreenFactor ?? fb.defaultHeightScreenFactor,
203
+ devUi: gc.devUi ?? fb.devUi,
204
+ colors,
205
+ aoiRadius: gc.aoiRadius ?? fb.aoiRadius,
206
+ portalHoldTimeMs: gc.portalHoldTimeMs ?? fb.portalHoldTimeMs,
207
+ portalSpawnRadius: gc.portalSpawnRadius ?? fb.portalSpawnRadius,
208
+ entityBaseSpeed: gc.entityBaseSpeed ?? fb.entityBaseSpeed,
209
+ entityBaseMaxLife: gc.entityBaseMaxLife ?? fb.entityBaseMaxLife,
210
+ entityBaseActionCooldownMs: gc.entityBaseActionCooldownMs ?? fb.entityBaseActionCooldownMs,
211
+ entityBaseMinActionCooldownMs: gc.entityBaseMinActionCooldownMs ?? fb.entityBaseMinActionCooldownMs,
212
+ botAggroRange: gc.botAggroRange ?? fb.botAggroRange,
213
+ defaultPlayerWidth: gc.defaultPlayerWidth ?? fb.defaultPlayerWidth,
214
+ defaultPlayerHeight: gc.defaultPlayerHeight ?? fb.defaultPlayerHeight,
215
+ playerBaseLifeRegenMin: gc.playerBaseLifeRegenMin ?? fb.playerBaseLifeRegenMin,
216
+ playerBaseLifeRegenMax: gc.playerBaseLifeRegenMax ?? fb.playerBaseLifeRegenMax,
217
+ sumStatsLimit: gc.sumStatsLimit ?? fb.sumStatsLimit,
218
+ maxActiveLayers: gc.maxActiveLayers ?? fb.maxActiveLayers,
219
+ initialLifeFraction: gc.initialLifeFraction ?? fb.initialLifeFraction,
220
+ respawnDurationMs: gc.respawnDurationMs ?? fb.respawnDurationMs,
221
+ collisionLifeLoss: gc.collisionLifeLoss ?? fb.collisionLifeLoss,
222
+ // Economy — Fountain & Sink (nested, mirrors EconomyRules proto message).
223
+ economyRules: {
224
+ botSpawnCoins: gc.economyRules?.botSpawnCoins ?? fb.economyRules.botSpawnCoins,
225
+ playerSpawnCoins: gc.economyRules?.playerSpawnCoins ?? fb.economyRules.playerSpawnCoins,
226
+ coinKillPercentVsBot: gc.economyRules?.coinKillPercentVsBot ?? fb.economyRules.coinKillPercentVsBot,
227
+ coinKillPercentVsPlayer: gc.economyRules?.coinKillPercentVsPlayer ?? fb.economyRules.coinKillPercentVsPlayer,
228
+ coinKillMinAmount: gc.economyRules?.coinKillMinAmount ?? fb.economyRules.coinKillMinAmount,
229
+ respawnCostPercent: gc.economyRules?.respawnCostPercent ?? fb.economyRules.respawnCostPercent,
230
+ portalFee: gc.economyRules?.portalFee ?? fb.economyRules.portalFee,
231
+ craftingFeePercent: gc.economyRules?.craftingFeePercent ?? fb.economyRules.craftingFeePercent,
232
+ },
233
+ lifeRegenChance: gc.lifeRegenChance ?? fb.lifeRegenChance,
234
+ maxChance: gc.maxChance ?? fb.maxChance,
235
+ entityDefaults,
236
+ skillConfig: (gc.skillConfig || []).map((sc) => ({
237
+ triggerItemId: sc.triggerItemId || '',
238
+ logicEventIds: sc.logicEventIds || [],
239
+ })),
240
+ skillRules: {
241
+ projectileSpawnChance: gc.skillRules?.projectileSpawnChance ?? fb.skillRules.projectileSpawnChance,
242
+ projectileLifetimeMs: gc.skillRules?.projectileLifetimeMs ?? fb.skillRules.projectileLifetimeMs,
243
+ projectileWidth: gc.skillRules?.projectileWidth ?? fb.skillRules.projectileWidth,
244
+ projectileHeight: gc.skillRules?.projectileHeight ?? fb.skillRules.projectileHeight,
245
+ projectileSpeedMultiplier: gc.skillRules?.projectileSpeedMultiplier ?? fb.skillRules.projectileSpeedMultiplier,
246
+ doppelgangerSpawnChance: gc.skillRules?.doppelgangerSpawnChance ?? fb.skillRules.doppelgangerSpawnChance,
247
+ doppelgangerLifetimeMs: gc.skillRules?.doppelgangerLifetimeMs ?? fb.skillRules.doppelgangerLifetimeMs,
248
+ doppelgangerSpawnRadius: gc.skillRules?.doppelgangerSpawnRadius ?? fb.skillRules.doppelgangerSpawnRadius,
249
+ doppelgangerInitialLifeFraction:
250
+ gc.skillRules?.doppelgangerInitialLifeFraction ?? fb.skillRules.doppelgangerInitialLifeFraction,
251
+ },
252
+ // Equipment rules — governs activation constraints (nested, mirrors EquipmentRules proto message).
253
+ equipmentRules: {
254
+ activeItemTypes: gc.equipmentRules?.activeItemTypes ?? fb.equipmentRules.activeItemTypes,
255
+ onePerType: gc.equipmentRules?.onePerType ?? fb.equipmentRules.onePerType,
256
+ requireSkin: gc.equipmentRules?.requireSkin ?? fb.equipmentRules.requireSkin,
257
+ },
258
+ // Status icon mapping — u8 ID → icon filename stem + border colour.
259
+ // Border colours come from the frozen STATUS_ICONS constant (canonical
260
+ // source of truth). DB entries may override iconId but borderColor is
261
+ // always canonical — the DB schema defaults are generic grey, not the
262
+ // actual per-status colours.
263
+ statusIcons: STATUS_ICONS.map((canon) => {
264
+ const dbEntry = (gc.statusIcons || []).find((s) => s.id === canon.id);
265
+ const bc = canon.borderColor || {};
266
+ return {
267
+ id: canon.id,
268
+ iconId: (dbEntry && dbEntry.iconId) || canon.iconId || '',
269
+ borderColorR: bc.r ?? 100,
270
+ borderColorG: bc.g ?? 100,
271
+ borderColorB: bc.b ?? 100,
272
+ borderColorA: bc.a ?? 200,
273
+ };
274
+ }),
275
+ };
276
+ }
277
+
278
+ /**
279
+ * Builds a minimal InstanceConfig with playable defaults.
280
+ * Derived from CYBERIA_INSTANCE_CONF_DEFAULTS (shared with the Mongoose model).
281
+ * Used when the requested instance does not exist in the database.
282
+ */
283
+ function buildFallbackConfig() {
284
+ return JSON.parse(JSON.stringify(FALLBACK_CONFIG_DEFAULTS));
285
+ }
286
+
287
+ // ═══════════════════════════════════════════════════════════════════
288
+ // RPC handler factory
289
+ // ═══════════════════════════════════════════════════════════════════
290
+
291
+ function buildHandlers(dbKey) {
292
+ return {
293
+ async ping(_call, callback) {
294
+ callback(null, { serverTimeMs: Date.now() });
295
+ },
296
+
297
+ // Server-streaming: streams all ObjectLayers
298
+ async getObjectLayerBatch(call) {
299
+ try {
300
+ const models = getModels(dbKey);
301
+ const filter = {};
302
+ if (call.request.itemTypeFilter) {
303
+ filter['data.item.type'] = call.request.itemTypeFilter;
304
+ }
305
+ const cursor = models.ObjectLayer.find(filter)
306
+ .populate('objectLayerRenderFramesId', { _id: 1, frame_duration: 1, is_stateless: 1 })
307
+ .lean()
308
+ .cursor();
309
+ for await (const doc of cursor) {
310
+ call.write(toObjectLayerMsg(doc));
311
+ }
312
+ call.end();
313
+ } catch (err) {
314
+ logger.error('getObjectLayerBatch:', err);
315
+ call.destroy(new Error(err.message));
316
+ }
317
+ },
318
+
319
+ async getObjectLayer(call, callback) {
320
+ try {
321
+ const models = getModels(dbKey);
322
+ const doc = await models.ObjectLayer.findOne({ 'data.item.id': call.request.itemId })
323
+ .populate('objectLayerRenderFramesId', { _id: 1, frame_duration: 1, is_stateless: 1 })
324
+ .lean();
325
+ if (!doc)
326
+ return callback({ code: grpc.status.NOT_FOUND, message: `ObjectLayer "${call.request.itemId}" not found` });
327
+ callback(null, toObjectLayerMsg(doc));
328
+ } catch (err) {
329
+ logger.error('getObjectLayer:', err);
330
+ callback({ code: grpc.status.INTERNAL, message: err.message });
331
+ }
332
+ },
333
+
334
+ async getMapData(call, callback) {
335
+ try {
336
+ const models = getModels(dbKey);
337
+ const { mapCode, instanceCode } = call.request;
338
+ const doc = await models.CyberiaMap.findOne({ code: mapCode }).lean();
339
+ if (!doc) return callback({ code: grpc.status.NOT_FOUND, message: `Map "${mapCode}" not found` });
340
+
341
+ // Track which instance is serving each mapCode in real time.
342
+ // instanceCode is provided by the Go server in every GetMapData request.
343
+ if (instanceCode) {
344
+ models.GlobalMapCodeRegistry.findOneAndUpdate(
345
+ { mapCode },
346
+ { instanceCode, status: 'active' },
347
+ { upsert: true, new: true, timestamps: true },
348
+ ).catch((err) => logger.warn('getMapData registry update failed:', err.message));
349
+ }
350
+
351
+ callback(null, { map: toMapMsg(doc) });
352
+ } catch (err) {
353
+ logger.error('getMapData:', err);
354
+ callback({ code: grpc.status.INTERNAL, message: err.message });
355
+ }
356
+ },
357
+
358
+ async getFullInstance(call, callback) {
359
+ try {
360
+ const models = getModels(dbKey);
361
+ // Normalise empty instanceCode to the canonical fallback name.
362
+ const instanceCode = call.request.instanceCode || 'default';
363
+ const inst = await models.CyberiaInstance.findOne({ code: instanceCode }).populate('conf').lean();
364
+
365
+ // ── Fallback: instance not found → return a multi-map procedural world ──
366
+ if (!inst) {
367
+ logger.info(`Instance "${instanceCode}" not found — returning fallback world.`);
368
+ const world = generateFallbackWorld();
369
+ const fallbackConf = buildFallbackConfig();
370
+
371
+ // Collect all objectLayerItemIds from the generated maps so the
372
+ // Go server can resolve atlases at startup.
373
+ const fallbackItemIds = new Set();
374
+ for (const m of world.maps) {
375
+ for (const e of m.entities || []) {
376
+ for (const id of e.objectLayerItemIds || []) fallbackItemIds.add(id);
377
+ }
378
+ }
379
+ // Also include system OL items from entity defaults.
380
+ for (const d of fallbackConf.entityDefaults || []) {
381
+ for (const id of d.liveItemIds || []) fallbackItemIds.add(id);
382
+ for (const id of d.deadItemIds || []) fallbackItemIds.add(id);
383
+ for (const ol of d.defaultObjectLayers || []) {
384
+ if (ol.itemId) fallbackItemIds.add(ol.itemId);
385
+ }
386
+ }
387
+
388
+ const fallbackOlDocs = fallbackItemIds.size
389
+ ? await models.ObjectLayer.find({ 'data.item.id': { $in: [...fallbackItemIds] } })
390
+ .populate('objectLayerRenderFramesId', { _id: 1, frame_duration: 1, is_stateless: 1 })
391
+ .lean()
392
+ : [];
393
+
394
+ callback(null, {
395
+ instance: toInstanceMsg({
396
+ _id: '',
397
+ code: 'fallback',
398
+ name: 'Fallback Instance',
399
+ description: 'Auto-generated procedural world (not persisted)',
400
+ tags: ['fallback', 'procedural'],
401
+ cyberiaMapCodes: world.instance.cyberiaMapCodes,
402
+ portals: world.portals,
403
+ topologyMode: 'procedural',
404
+ seed: instanceCode,
405
+ }),
406
+ maps: world.maps.map((m) => ({
407
+ mongoId: '',
408
+ code: m.code,
409
+ name: m.name,
410
+ gridX: m.gridX,
411
+ gridY: m.gridY,
412
+ cellWidth: m.cellWidth,
413
+ cellHeight: m.cellHeight,
414
+ entities: (m.entities || []).map(toEntityMsg),
415
+ })),
416
+ objectLayers: fallbackOlDocs.map(toObjectLayerMsg),
417
+ config: toInstanceConfig(fallbackConf),
418
+ });
419
+ return;
420
+ }
421
+
422
+ // ── Instance found — load maps + entity OLs + config default OLs ──────
423
+ const conf = inst.conf || {};
424
+ const mapCodes = inst.cyberiaMapCodes || [];
425
+ const mapDocs = mapCodes.length ? await models.CyberiaMap.find({ code: { $in: mapCodes } }).lean() : [];
426
+
427
+ // Collect all item IDs referenced by map entities.
428
+ const itemIds = new Set();
429
+ for (const m of mapDocs) {
430
+ for (const e of m.entities || []) {
431
+ for (const id of e.objectLayerItemIds || []) itemIds.add(id);
432
+ }
433
+ }
434
+
435
+ // Also include "system" item IDs from the instance config so the
436
+ // Go server has their OL data cached without extra round trips:
437
+ // Include OL item IDs from entityDefaults so the Go server has all
438
+ // default atlas data cached at startup without needing extra round trips.
439
+ for (const d of conf.entityDefaults || []) {
440
+ for (const id of d.liveItemIds || []) itemIds.add(id);
441
+ for (const id of d.deadItemIds || []) itemIds.add(id);
442
+ for (const ol of d.defaultObjectLayers || []) {
443
+ if (ol.itemId) itemIds.add(ol.itemId);
444
+ }
445
+ }
446
+
447
+ const olDocs = itemIds.size
448
+ ? await models.ObjectLayer.find({ 'data.item.id': { $in: [...itemIds] } })
449
+ .populate('objectLayerRenderFramesId', { _id: 1, frame_duration: 1, is_stateless: 1 })
450
+ .lean()
451
+ : [];
452
+
453
+ callback(null, {
454
+ instance: toInstanceMsg(inst),
455
+ maps: mapDocs.map(toMapMsg),
456
+ objectLayers: olDocs.map(toObjectLayerMsg),
457
+ config: toInstanceConfig(conf),
458
+ });
459
+ } catch (err) {
460
+ logger.error('getFullInstance:', err);
461
+ callback({ code: grpc.status.INTERNAL, message: err.message });
462
+ }
463
+ },
464
+
465
+ async getObjectLayerManifest(_call, callback) {
466
+ try {
467
+ const models = getModels(dbKey);
468
+ const docs = await models.ObjectLayer.find({}, { 'data.item.id': 1, sha256: 1 }).lean();
469
+ callback(null, {
470
+ entries: docs.map((d) => ({ itemId: d.data?.item?.id || '', sha256: d.sha256 || '' })),
471
+ });
472
+ } catch (err) {
473
+ logger.error('getObjectLayerManifest:', err);
474
+ callback({ code: grpc.status.INTERNAL, message: err.message });
475
+ }
476
+ },
477
+ };
478
+ }
479
+
480
+ // ═══════════════════════════════════════════════════════════════════
481
+ // Server lifecycle
482
+ // ═══════════════════════════════════════════════════════════════════
483
+
484
+ const GrpcServer = {
485
+ _server: null,
486
+
487
+ /**
488
+ * @param {Object} opts
489
+ * @param {string} opts.host - DataBaseProvider host key
490
+ * @param {string} opts.path - DataBaseProvider path key
491
+ * @param {number} [opts.port=50051]
492
+ */
493
+ async start({ host, path: dbPath, port = 50051 } = {}) {
494
+ const dbKey = `${host}${dbPath}`;
495
+ const server = new grpc.Server({
496
+ 'grpc.max_send_message_length': 64 * 1024 * 1024,
497
+ 'grpc.max_receive_message_length': 16 * 1024 * 1024,
498
+ });
499
+
500
+ server.addService(proto.CyberiaDataService.service, buildHandlers(dbKey));
501
+
502
+ // gRPC runs over Kubernetes internal network (ClusterIP) — always insecure
503
+ const creds = grpc.ServerCredentials.createInsecure();
504
+
505
+ return new Promise((resolve, reject) => {
506
+ server.bindAsync(`0.0.0.0:${port}`, creds, (err) => {
507
+ if (err) {
508
+ logger.error('gRPC bind failed:', err);
509
+ return reject(err);
510
+ }
511
+ GrpcServer._server = server;
512
+ logger.info(`gRPC server listening on 0.0.0.0:${port}`);
513
+ resolve(server);
514
+ });
515
+ });
516
+ },
517
+
518
+ async stop() {
519
+ if (!GrpcServer._server) return;
520
+ return new Promise((resolve) => {
521
+ GrpcServer._server.tryShutdown(() => {
522
+ GrpcServer._server = null;
523
+ logger.info('gRPC server stopped');
524
+ resolve();
525
+ });
526
+ });
527
+ },
528
+ };
529
+
530
+ export { GrpcServer };
package/src/index.js CHANGED
@@ -10,6 +10,7 @@ import UnderpostKickStart from './cli/kickstart.js';
10
10
  import UnderpostCluster from './cli/cluster.js';
11
11
  import UnderpostDB from './cli/db.js';
12
12
  import UnderpostDeploy from './cli/deploy.js';
13
+ import UnderpostKubectl from './cli/kubectl.js';
13
14
  import UnderpostRootEnv from './cli/env.js';
14
15
  import UnderpostFileStorage from './cli/fs.js';
15
16
  import UnderpostIPFS from './cli/ipfs.js';
@@ -22,6 +23,7 @@ import UnderpostSecret from './cli/secrets.js';
22
23
  import UnderpostSSH from './cli/ssh.js';
23
24
  import UnderpostStatic from './cli/static.js';
24
25
  import UnderpostTest from './cli/test.js';
26
+ import UnderpostRelease from './cli/release.js';
25
27
  import UnderpostSystemProvisionig from './cli/system.js';
26
28
 
27
29
  import UnderpostDns from './server/dns.js';
@@ -42,7 +44,7 @@ class Underpost {
42
44
  * @type {String}
43
45
  * @memberof Underpost
44
46
  */
45
- static version = 'v3.0.3';
47
+ static version = 'v3.2.5';
46
48
 
47
49
  /**
48
50
  * Required Node.js major version
@@ -126,6 +128,15 @@ class Underpost {
126
128
  static get db() {
127
129
  return UnderpostDB.API;
128
130
  }
131
+ /**
132
+ * Kubectl cli API
133
+ * @static
134
+ * @type {UnderpostKubectl.API}
135
+ * @memberof Underpost
136
+ */
137
+ static get kubectl() {
138
+ return UnderpostKubectl.API;
139
+ }
129
140
  /**
130
141
  * Deployment cli API
131
142
  * @static
@@ -280,6 +291,16 @@ class Underpost {
280
291
  static get tls() {
281
292
  return UnderpostTLS.API;
282
293
  }
294
+
295
+ /**
296
+ * Release orchestrator cli API
297
+ * @static
298
+ * @type {UnderpostRelease.API}
299
+ * @memberof Underpost
300
+ */
301
+ static get release() {
302
+ return UnderpostRelease.API;
303
+ }
283
304
  }
284
305
 
285
306
  if (!process.version || !process.version.startsWith(`${Underpost.majorNodejsVersion}.`))
@@ -300,6 +321,7 @@ export {
300
321
  UnderpostCluster,
301
322
  UnderpostDB,
302
323
  UnderpostDeploy,
324
+ UnderpostKubectl,
303
325
  UnderpostRootEnv,
304
326
  UnderpostFileStorage,
305
327
  UnderpostImage,
@@ -317,6 +339,7 @@ export {
317
339
  UnderpostBackup,
318
340
  UnderpostCron,
319
341
  UnderpostStartUp,
342
+ UnderpostRelease,
320
343
  UnderpostTLS,
321
344
  };
322
345
 
package/src/proxy.js CHANGED
@@ -3,14 +3,11 @@
3
3
  // https://nodejs.org/api
4
4
  // https://expressjs.com/en/4x/api.html
5
5
 
6
- import dotenv from 'dotenv';
7
6
  import { loggerFactory } from './server/logger.js';
8
7
  import { buildProxy } from './server/proxy.js';
9
8
  import { ProcessController } from './server/process.js';
10
9
  import { Config } from './server/conf.js';
11
10
 
12
- dotenv.config();
13
-
14
11
  await Config.build(process.argv[2], process.argv[3], process.argv[4]);
15
12
 
16
13
  const logger = loggerFactory(import.meta);
@@ -30,6 +30,10 @@ RUN dnf clean all
30
30
  RUN node --version
31
31
  RUN npm --version
32
32
 
33
+ # Create non-root user for secure container execution (cron jobs, init containers)
34
+ # Deployment containers override to root via securityContext when npm install -g is needed
35
+ RUN useradd -m -u 1000 -s /bin/bash dd
36
+
33
37
  # Set working directory
34
38
  WORKDIR /home/dd
35
39