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
package/src/cli/deploy.js CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  Config,
12
12
  deployRangePortFactory,
13
13
  getDataDeploy,
14
+ loadConfServerJson,
14
15
  loadReplicas,
15
16
  pathPortAssignmentFactory,
16
17
  } from '../server/conf.js';
@@ -131,22 +132,16 @@ class UnderpostDeploy {
131
132
  cmd = [
132
133
  `npm install -g npm@11.2.0`,
133
134
  `npm install -g underpost`,
134
- `underpost secret underpost --create-from-file /etc/config/.env.${env}`,
135
- `underpost start --build --run --underpost-quickly-install ${deployId} ${env}`,
135
+ `underpost secret underpost --create-from-env`,
136
+ `underpost start --build --run ${deployId} ${env}`,
136
137
  ];
137
138
  const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
138
- if (!volumes)
139
- volumes = [
140
- {
141
- volumeMountPath: '/etc/config',
142
- volumeName: 'config-volume',
143
- configMap: 'underpost-config',
144
- },
145
- ];
139
+ if (!volumes) volumes = [];
146
140
  const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
147
141
  ? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.volume.json`, 'utf8'))
148
142
  : [];
149
143
  volumes = volumes.concat(confVolume);
144
+ const containerImage = image ? image : `localhost/rockylinux9-underpost:v${packageJson.version}`;
150
145
  return `apiVersion: apps/v1
151
146
  kind: Deployment
152
147
  metadata:
@@ -154,6 +149,7 @@ metadata:
154
149
  namespace: ${namespace ? namespace : 'default'}
155
150
  labels:
156
151
  app: ${deployId}-${env}-${suffix}
152
+ deploy-id: ${deployId}-${env}
157
153
  spec:
158
154
  replicas: ${replicas}
159
155
  selector:
@@ -163,10 +159,14 @@ spec:
163
159
  metadata:
164
160
  labels:
165
161
  app: ${deployId}-${env}-${suffix}
162
+ deploy-id: ${deployId}-${env}
166
163
  spec:
167
164
  containers:
168
165
  - name: ${deployId}-${env}-${suffix}
169
- image: ${image ? image : `localhost/rockylinux9-underpost:v${packageJson.version}`}
166
+ image: ${containerImage}
167
+ envFrom:
168
+ - secretRef:
169
+ name: underpost-config
170
170
  ${
171
171
  resources
172
172
  ? ` resources:
@@ -182,13 +182,17 @@ ${
182
182
  - /bin/sh
183
183
  - -c
184
184
  - >
185
- ${cmd.join(` && `)}
185
+ ${cmd.join(' &&\n ')}
186
186
 
187
- ${Underpost.deploy
188
- .volumeFactory(volumes.map((v) => ((v.version = `${deployId}-${env}-${suffix}`), v)))
189
- .render.split(`\n`)
190
- .map((l) => ' ' + l)
191
- .join(`\n`)}
187
+ ${
188
+ volumes.length > 0
189
+ ? Underpost.deploy
190
+ .volumeFactory(volumes.map((v) => ((v.version = `${deployId}-${env}-${suffix}`), v)))
191
+ .render.split(`\n`)
192
+ .map((l) => ' ' + l)
193
+ .join(`\n`)
194
+ : ''
195
+ }
192
196
  ---
193
197
  apiVersion: v1
194
198
  kind: Service
@@ -230,7 +234,7 @@ spec:
230
234
  if (!deployId) continue;
231
235
  const confServer = loadReplicas(
232
236
  deployId,
233
- JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
237
+ loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`),
234
238
  );
235
239
  const router = await Underpost.deploy.routerFactory(deployId, env);
236
240
  const pathPortAssignmentData = await pathPortAssignmentFactory(deployId, router, confServer);
@@ -259,6 +263,35 @@ ${Underpost.deploy
259
263
  }
260
264
  fs.writeFileSync(`./engine-private/conf/${deployId}/build/${env}/deployment.yaml`, deploymentYamlParts, 'utf8');
261
265
 
266
+ Underpost.deploy.buildGrpcServiceManifest({
267
+ deployId,
268
+ env,
269
+ confServer,
270
+ namespace: options.namespace,
271
+ traffic: options.traffic && typeof options.traffic === 'string' ? options.traffic.split(',') : ['blue'],
272
+ });
273
+
274
+ const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
275
+ ? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.volume.json`, 'utf8'))
276
+ : [];
277
+ if (confVolume.length > 0) {
278
+ let volumeYaml = '';
279
+ for (const deploymentVersion of deploymentVersions) {
280
+ for (const volume of confVolume) {
281
+ if (!volume.claimName) continue;
282
+ const pvcId = `${volume.claimName}-${deployId}-${env}-${deploymentVersion}`;
283
+ const pvId = pvcId.replace(/^pvc-/, 'pv-');
284
+ const hostPath = `/home/dd/engine/volume/${pvId}`;
285
+ volumeYaml += `---\n${Underpost.deploy.persistentVolumeFactory({
286
+ pvcId,
287
+ namespace: options.namespace,
288
+ hostPath,
289
+ })}\n`;
290
+ }
291
+ }
292
+ fs.writeFileSync(`./engine-private/conf/${deployId}/build/${env}/pv-pvc.yaml`, volumeYaml, 'utf8');
293
+ }
294
+
262
295
  let proxyYaml = '';
263
296
  let secretYaml = '';
264
297
  const customServices = fs.existsSync(`./engine-private/conf/${deployId}/conf.services.json`)
@@ -335,7 +368,7 @@ ${Underpost.deploy
335
368
  const yamlPath = `./engine-private/conf/${deployId}/build/${env}/secret.yaml`;
336
369
  fs.writeFileSync(yamlPath, secretYaml, 'utf8');
337
370
  } else {
338
- const deploymentsFiles = ['Dockerfile', 'proxy.yaml', 'deployment.yaml'];
371
+ const deploymentsFiles = ['Dockerfile', 'proxy.yaml', 'deployment.yaml', 'pv-pvc.yaml'];
339
372
  for (const file of deploymentsFiles) {
340
373
  if (fs.existsSync(`./engine-private/conf/${deployId}/build/${env}/${file}`)) {
341
374
  fs.copyFileSync(
@@ -347,6 +380,67 @@ ${Underpost.deploy
347
380
  }
348
381
  }
349
382
  },
383
+ /**
384
+ * Builds and writes a gRPC ClusterIP service YAML for a deployment.
385
+ * Scans conf.server.json for gRPC ports and emits grpc-service.yaml under
386
+ * `engine-private/conf/<deployId>/build/<env>/`. The selector always uses the
387
+ * explicit `app: <deployId>-<env>-<traffic>` label to target only the active
388
+ * colour (blue or green).
389
+ * @param {string} deployId - Deployment ID.
390
+ * @param {string} env - Environment ('development' or 'production').
391
+ * @param {object} confServer - Parsed conf.server.json content.
392
+ * @param {string} [namespace='default'] - Kubernetes namespace.
393
+ * @param {string[]} [traffic=['blue']] - Active traffic colour(s) ('blue', 'green', or both).
394
+ * @param {string|null} [host=null] - Specific host to scan for gRPC ports. If null, all hosts are scanned.
395
+ * @returns {string|null} - Path to the written YAML file, or null if no gRPC ports found.
396
+ * @memberof UnderpostDeploy
397
+ */
398
+ buildGrpcServiceManifest({ deployId, env, confServer, namespace = 'default', traffic = ['blue'], host = null }) {
399
+ const grpcPorts = new Set();
400
+ const hostsToScan = host ? [host] : Object.keys(confServer);
401
+ for (const h of hostsToScan) {
402
+ if (!confServer[h]) continue;
403
+ for (const path of Object.keys(confServer[h])) {
404
+ const grpc = confServer[h][path].grpc;
405
+ if (grpc && grpc.port) grpcPorts.add(parseInt(grpc.port));
406
+ }
407
+ }
408
+ if (grpcPorts.size === 0) return null;
409
+ const grpcPortsList = [...grpcPorts]
410
+ .map(
411
+ (port) => ` - name: grpc-${port}
412
+ protocol: TCP
413
+ port: ${port}
414
+ targetPort: ${port}`,
415
+ )
416
+ .join('\n');
417
+ let grpcServiceYaml = '';
418
+ for (const color of traffic) {
419
+ const grpcServiceName = `${deployId}-grpc-service-${env}-${color}`;
420
+ const selectorYaml = `app: ${deployId}-${env}-${color}`;
421
+ grpcServiceYaml += `---
422
+ apiVersion: v1
423
+ kind: Service
424
+ metadata:
425
+ name: ${grpcServiceName}
426
+ namespace: ${namespace}
427
+ labels:
428
+ app: ${grpcServiceName}
429
+ spec:
430
+ type: ClusterIP
431
+ selector:
432
+ ${selectorYaml}
433
+ ports:
434
+ ${grpcPortsList}
435
+ `;
436
+ logger.info(
437
+ `gRPC ClusterIP service YAML written: ${grpcServiceName} (selector: ${selectorYaml}, ports: ${[...grpcPorts].join(', ')})`,
438
+ );
439
+ }
440
+ const yamlPath = `./engine-private/conf/${deployId}/build/${env}/grpc-service.yaml`;
441
+ fs.writeFileSync(yamlPath, grpcServiceYaml, 'utf8');
442
+ return yamlPath;
443
+ },
350
444
  /**
351
445
  * Builds a Certificate resource for a host using cert-manager.
352
446
  * @param {string} host - Hostname for which the certificate is being built.
@@ -385,7 +479,7 @@ spec:
385
479
  // kubectl get deploy,sts,svc,configmap,secret -n default -o yaml --export > default.yaml
386
480
  const hostTest = options?.hostTest
387
481
  ? options.hostTest
388
- : Object.keys(JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')))[0];
482
+ : Object.keys(loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`))[0];
389
483
  const info = shellExec(`sudo kubectl get HTTPProxy/${hostTest} -n ${options.namespace} -o yaml`, {
390
484
  silent: true,
391
485
  stdout: true,
@@ -456,6 +550,10 @@ spec:
456
550
  * @param {string} [options.kindType] - Type of Kubernetes resource to retrieve information for.
457
551
  * @param {number} [options.port] - Port number for exposing the deployment.
458
552
  * @param {string} [options.cmd] - Custom initialization command for deploymentYamlPartsFactory (comma-separated commands).
553
+ * @param {boolean} [options.k3s] - Whether to use k3s cluster context.
554
+ * @param {boolean} [options.kubeadm] - Whether to use kubeadm cluster context.
555
+ * @param {boolean} [options.kind] - Whether to use kind cluster context.
556
+ * @param {boolean} [options.gitClean] - Whether to run git clean on volume mount paths before copying.
459
557
  * @returns {Promise<void>} - Promise that resolves when the deployment process is complete.
460
558
  * @memberof UnderpostDeploy
461
559
  */
@@ -493,6 +591,10 @@ spec:
493
591
  port: 0,
494
592
  exposePort: 0,
495
593
  cmd: '',
594
+ k3s: false,
595
+ kubeadm: false,
596
+ kind: false,
597
+ gitClean: false,
496
598
  },
497
599
  ) {
498
600
  const namespace = options.namespace ? options.namespace : 'default';
@@ -531,7 +633,7 @@ EOF`);
531
633
  env,
532
634
  traffic: Underpost.deploy.getCurrentTraffic(deployId, { namespace }),
533
635
  router: await Underpost.deploy.routerFactory(deployId, env),
534
- pods: await Underpost.deploy.get(deployId),
636
+ pods: await Underpost.kubectl.get(deployId),
535
637
  instances,
536
638
  });
537
639
  }
@@ -550,7 +652,7 @@ EOF`);
550
652
  if (!(options.versions && typeof options.versions === 'string')) options.versions = 'blue,green';
551
653
  if (!options.replicas) options.replicas = 1;
552
654
  if (options.sync)
553
- getDataDeploy({
655
+ await getDataDeploy({
554
656
  buildSingleReplica: true,
555
657
  });
556
658
  if (options.buildManifest === true) await Underpost.deploy.buildManifest(deployList, env, options);
@@ -573,7 +675,7 @@ EOF`);
573
675
  if (!deployId) continue;
574
676
  if (options.expose === true) {
575
677
  const kindType = options.kindType ? options.kindType : 'svc';
576
- const svc = Underpost.deploy.get(deployId, kindType)[0];
678
+ const svc = Underpost.kubectl.get(deployId, kindType)[0];
577
679
  const port = options.exposePort
578
680
  ? parseInt(options.exposePort)
579
681
  : options.port
@@ -591,7 +693,7 @@ EOF`);
591
693
  continue;
592
694
  }
593
695
 
594
- const confServer = JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'));
696
+ const confServer = loadConfServerJson(`./engine-private/conf/${deployId}/conf.server.json`);
595
697
  const confVolume = fs.existsSync(`./engine-private/conf/${deployId}/conf.volume.json`)
596
698
  ? JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.volume.json`, 'utf8'))
597
699
  : [];
@@ -612,6 +714,8 @@ EOF`);
612
714
  version,
613
715
  namespace,
614
716
  nodeName: options.node ? options.node : env === 'development' ? 'kind-worker' : os.hostname(),
717
+ clusterContext: options.k3s ? 'k3s' : options.kubeadm ? 'kubeadm' : 'kind',
718
+ gitClean: options.gitClean || false,
615
719
  });
616
720
  }
617
721
 
@@ -630,8 +734,11 @@ EOF`);
630
734
  : `manifests/deployment/${deployId}-${env}`;
631
735
 
632
736
  if (!options.remove) {
633
- if (!options.disableUpdateDeployment)
737
+ if (!options.disableUpdateDeployment) {
634
738
  shellExec(`sudo kubectl apply -f ./${manifestsPath}/deployment.yaml -n ${namespace}`);
739
+ const grpcServicePath = `./${manifestsPath}/grpc-service.yaml`;
740
+ if (fs.existsSync(grpcServicePath)) shellExec(`sudo kubectl apply -f ${grpcServicePath} -n ${namespace}`);
741
+ }
635
742
  if (!options.disableUpdateProxy)
636
743
  shellExec(`sudo kubectl apply -f ./${manifestsPath}/proxy.yaml -n ${namespace}`);
637
744
 
@@ -649,65 +756,6 @@ EOF`);
649
756
  ` + renderHosts,
650
757
  );
651
758
  },
652
- /**
653
- * Retrieves information about a deployment.
654
- * @param {string} deployId - Deployment ID for which information is being retrieved.
655
- * @param {string} kindType - Type of Kubernetes resource to retrieve information for (e.g. 'pods').
656
- * @param {string} namespace - Kubernetes namespace to retrieve information from.
657
- * @returns {Array<object>} - Array of objects containing information about the deployment.
658
- * @memberof UnderpostDeploy
659
- */
660
- get(deployId, kindType = 'pods', namespace = '') {
661
- const raw = shellExec(
662
- `sudo kubectl get ${kindType}${namespace ? ` -n ${namespace}` : ` --all-namespaces`} -o wide`,
663
- {
664
- stdout: true,
665
- disableLog: true,
666
- silent: true,
667
- },
668
- );
669
-
670
- const heads = raw
671
- .split(`\n`)[0]
672
- .split(' ')
673
- .filter((_r) => _r.trim());
674
-
675
- const pods = raw
676
- .split(`\n`)
677
- .filter((r) => (deployId ? r.match(deployId) : r.trim() && !r.match('NAME')))
678
- .map((r) => r.split(' ').filter((_r) => _r.trim()));
679
-
680
- const result = [];
681
-
682
- for (const row of pods) {
683
- const pod = {};
684
- let index = -1;
685
- for (const head of heads) {
686
- index++;
687
- pod[head] = row[index];
688
- }
689
- result.push(pod);
690
- }
691
-
692
- return result;
693
- },
694
-
695
- /**
696
- * Checks if a container file exists in a pod.
697
- * @param {object} options - Options for the check.
698
- * @param {string} options.podName - Name of the pod to check.
699
- * @param {string} options.path - Path to the container file to check.
700
- * @returns {boolean} - True if the container file exists, false otherwise.
701
- * @memberof UnderpostDeploy
702
- */
703
- existsContainerFile({ podName, path }) {
704
- const result = shellExec(`kubectl exec ${podName} -- test -f ${path} && echo "true" || echo "false"`, {
705
- stdout: true,
706
- disableLog: true,
707
- silent: true,
708
- }).trim();
709
- return result === 'true';
710
- },
711
759
  /**
712
760
  * Checks the status of a deployment.
713
761
  * @param {string} deployId - Deployment ID for which the status is being checked.
@@ -720,7 +768,7 @@ EOF`);
720
768
  */
721
769
  async checkDeploymentReadyStatus(deployId, env, traffic, ignoresNames = [], namespace = 'default') {
722
770
  const cmd = `underpost config get container-status`;
723
- const pods = Underpost.deploy.get(`${deployId}-${env}-${traffic}`, 'pods', namespace);
771
+ const pods = Underpost.kubectl.get(`${deployId}-${env}-${traffic}`, 'pods', namespace);
724
772
  const readyPods = [];
725
773
  const notReadyPods = [];
726
774
  for (const pod of pods) {
@@ -746,15 +794,16 @@ EOF`);
746
794
  };
747
795
  },
748
796
  /**
749
- * Creates a configmap for a deployment.
750
- * @param {string} env - Environment for which the configmap is being created.
751
- * @param {string} [namespace='default'] - Kubernetes namespace for the configmap.
797
+ * Creates a Kubernetes Secret for a deployment (replaces configMap for secret data).
798
+ * Secrets are mounted as tmpfs (never written to node disk) and support RBAC restrictions.
799
+ * @param {string} env - Environment for which the secret is being created.
800
+ * @param {string} [namespace='default'] - Kubernetes namespace for the secret.
752
801
  * @memberof UnderpostDeploy
753
802
  */
754
803
  configMap(env, namespace = 'default') {
755
- shellExec(`kubectl delete configmap underpost-config -n ${namespace} --ignore-not-found`);
804
+ shellExec(`kubectl delete secret underpost-config -n ${namespace} --ignore-not-found`);
756
805
  shellExec(
757
- `kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env} --dry-run=client -o yaml | kubectl apply -f - -n ${namespace}`,
806
+ `kubectl create secret generic underpost-config --from-env-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env} --dry-run=client -o yaml | kubectl apply -f - -n ${namespace}`,
758
807
  );
759
808
  },
760
809
  /**
@@ -792,6 +841,9 @@ EOF`);
792
841
 
793
842
  shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml -n ${namespace}`);
794
843
 
844
+ const grpcServicePath = `./engine-private/conf/${deployId}/build/${env}/grpc-service.yaml`;
845
+ if (fs.existsSync(grpcServicePath)) shellExec(`kubectl apply -f ${grpcServicePath} -n ${namespace}`);
846
+
795
847
  Underpost.env.set(`${deployId}-${env}-traffic`, targetTraffic);
796
848
  },
797
849
 
@@ -807,6 +859,8 @@ EOF`);
807
859
  * @param {string} options.version - Version of the deployment.
808
860
  * @param {string} options.namespace - Kubernetes namespace for the deployment.
809
861
  * @param {string} options.nodeName - Node name for the deployment.
862
+ * @param {string} [options.clusterContext='kind'] - Cluster context type ('kind', 'kubeadm', or 'k3s').
863
+ * @param {boolean} [options.gitClean=false] - Whether to run git clean on volumeMountPath before copying.
810
864
  * @memberof UnderpostDeploy
811
865
  */
812
866
  deployVolume(
@@ -817,6 +871,8 @@ EOF`);
817
871
  version: '',
818
872
  namespace: '',
819
873
  nodeName: '',
874
+ clusterContext: 'kind',
875
+ gitClean: false,
820
876
  },
821
877
  ) {
822
878
  if (!volume.claimName) {
@@ -824,19 +880,26 @@ EOF`);
824
880
  return;
825
881
  }
826
882
  const { deployId, env, version, namespace } = options;
883
+ const clusterContext = options.clusterContext || 'kind';
827
884
  const pvcId = `${volume.claimName}-${deployId}-${env}-${version}`;
828
885
  const pvId = `${volume.claimName.replace('pvc-', 'pv-')}-${deployId}-${env}-${version}`;
829
886
  const rootVolumeHostPath = `/home/dd/engine/volume/${pvId}`;
887
+ if (options.gitClean && volume.volumeMountPath) {
888
+ Underpost.repo.clean({ paths: [volume.volumeMountPath] });
889
+ }
830
890
  if (options.nodeName) {
831
891
  if (!fs.existsSync(rootVolumeHostPath)) fs.mkdirSync(rootVolumeHostPath, { recursive: true });
832
892
  fs.copySync(volume.volumeMountPath, rootVolumeHostPath);
833
- } else {
893
+ } else if (clusterContext === 'kind') {
834
894
  shellExec(`docker exec -i kind-worker bash -c "mkdir -p ${rootVolumeHostPath}"`);
835
895
  // shellExec(`docker cp ${volume.volumeMountPath} kind-worker:${rootVolumeHostPath}`);
836
896
  shellExec(`tar -C ${volume.volumeMountPath} -c . | docker cp - kind-worker:${rootVolumeHostPath}`);
837
897
  shellExec(
838
898
  `docker exec -i kind-worker bash -c "chown -R 1000:1000 ${rootVolumeHostPath}; chmod -R 755 ${rootVolumeHostPath}"`,
839
899
  );
900
+ } else {
901
+ if (!fs.existsSync(rootVolumeHostPath)) fs.mkdirSync(rootVolumeHostPath, { recursive: true });
902
+ fs.copySync(volume.volumeMountPath, rootVolumeHostPath);
840
903
  }
841
904
  shellExec(`kubectl delete pvc ${pvcId} -n ${namespace} --ignore-not-found`);
842
905
  shellExec(`kubectl delete pv ${pvId} --ignore-not-found`);
@@ -844,6 +907,7 @@ EOF`);
844
907
  ${Underpost.deploy.persistentVolumeFactory({
845
908
  hostPath: rootVolumeHostPath,
846
909
  pvcId,
910
+ namespace,
847
911
  })}
848
912
  EOF
849
913
  `);
@@ -858,6 +922,8 @@ EOF
858
922
  * @param {string} volume.volumeType - Type of the volume (e.g. 'Directory').
859
923
  * @param {string|null} volume.claimName - Name of the persistent volume claim (if applicable).
860
924
  * @param {string|null} volume.configMap - Name of the config map (if applicable).
925
+ * @param {string|null} volume.secret - Name of the Kubernetes Secret (if applicable). Mounts as readOnly.
926
+ * @param {boolean} [volume.emptyDir=false] - If true, uses an emptyDir volume (writable tmpfs).
861
927
  * @returns {object} - Object containing the rendered volume mounts and volumes.
862
928
  * @memberof UnderpostDeploy
863
929
  */
@@ -879,7 +945,17 @@ EOF
879
945
  let _volumes = `
880
946
  volumes:`;
881
947
  volumes.map((volumeData) => {
882
- let { volumeName, volumeMountPath, volumeHostPath, volumeType, claimName, configMap, version } = volumeData;
948
+ let {
949
+ volumeName,
950
+ volumeMountPath,
951
+ volumeHostPath,
952
+ volumeType,
953
+ claimName,
954
+ configMap,
955
+ secret,
956
+ emptyDir,
957
+ version,
958
+ } = volumeData;
883
959
  if (version) {
884
960
  volumeName = `${volumeName}-${version}`;
885
961
  claimName = claimName ? `${claimName}-${version}` : null;
@@ -887,18 +963,23 @@ EOF
887
963
  _volumeMounts += `
888
964
  - name: ${volumeName}
889
965
  mountPath: ${volumeMountPath}
890
- `;
966
+ ${secret ? ` readOnly: true\n` : ''}`;
891
967
 
892
968
  _volumes += `
893
969
  - name: ${volumeName}
894
970
  ${
895
- configMap
896
- ? ` configMap:
971
+ emptyDir
972
+ ? ` emptyDir: {}`
973
+ : secret
974
+ ? ` secret:
975
+ secretName: ${secret}`
976
+ : configMap
977
+ ? ` configMap:
897
978
  name: ${configMap}`
898
- : claimName
899
- ? ` persistentVolumeClaim:
979
+ : claimName
980
+ ? ` persistentVolumeClaim:
900
981
  claimName: ${claimName}`
901
- : ` hostPath:
982
+ : ` hostPath:
902
983
  path: ${volumeHostPath}
903
984
  type: ${volumeType}
904
985
  `
@@ -914,15 +995,44 @@ EOF
914
995
  * @param {object} options - Options for the persistent volume and claim creation.
915
996
  * @param {string} options.hostPath - Host path for the persistent volume.
916
997
  * @param {string} options.pvcId - Persistent volume claim ID.
998
+ * @param {string} [options.namespace='default'] - Kubernetes namespace for the PVC claimRef.
917
999
  * @returns {string} - YAML configuration for the persistent volume and claim.
918
1000
  * @memberof UnderpostDeploy
919
1001
  */
920
- persistentVolumeFactory({ hostPath, pvcId }) {
921
- return fs
922
- .readFileSync(`./manifests/pv-pvc-dd.yaml`, 'utf8')
923
- .replace('/home/dd', hostPath)
924
- .replace('pv-dd', pvcId.replace('pvc-', 'pv-'))
925
- .replace('pvc-dd', pvcId);
1002
+ persistentVolumeFactory({ hostPath, pvcId, namespace = 'default' }) {
1003
+ const pvId = pvcId.replace(/^pvc-/, 'pv-');
1004
+ return `apiVersion: v1
1005
+ kind: PersistentVolume
1006
+ metadata:
1007
+ name: ${pvId}
1008
+ spec:
1009
+ capacity:
1010
+ storage: 5Gi
1011
+ accessModes:
1012
+ - ReadWriteOnce
1013
+ persistentVolumeReclaimPolicy: Retain
1014
+ storageClassName: manual
1015
+ claimRef:
1016
+ apiVersion: v1
1017
+ kind: PersistentVolumeClaim
1018
+ name: ${pvcId}
1019
+ namespace: ${namespace}
1020
+ hostPath:
1021
+ path: ${hostPath}
1022
+ type: DirectoryOrCreate
1023
+ ---
1024
+ apiVersion: v1
1025
+ kind: PersistentVolumeClaim
1026
+ metadata:
1027
+ name: ${pvcId}
1028
+ spec:
1029
+ accessModes:
1030
+ - ReadWriteOnce
1031
+ storageClassName: manual
1032
+ volumeName: ${pvId}
1033
+ resources:
1034
+ requests:
1035
+ storage: 5Gi`;
926
1036
  },
927
1037
 
928
1038
  /**
@@ -989,7 +1099,7 @@ ${renderHosts}`,
989
1099
  async monitorReadyRunner(deployId, env, targetTraffic, ignorePods = [], namespace = 'default', outLogType = '') {
990
1100
  let checkStatusIteration = 0;
991
1101
  const checkStatusIterationMsDelay = 1000;
992
- const maxIterations = 500;
1102
+ const maxIterations = 3000;
993
1103
  const deploymentId = `${deployId}-${env}-${targetTraffic}`;
994
1104
  const iteratorTag = `[${deploymentId}]`;
995
1105
  logger.info('Deployment init', { deployId, env, targetTraffic, checkStatusIterationMsDelay, namespace });
package/src/cli/env.js CHANGED
@@ -10,12 +10,23 @@ import { loggerFactory } from '../server/logger.js';
10
10
  import dotenv from 'dotenv';
11
11
  import { pbcopy } from '../server/process.js';
12
12
 
13
- dotenv.config();
14
-
15
13
  const logger = loggerFactory(import.meta);
16
14
 
17
15
  /**
18
- * @class UnderpostEnv
16
+ * Guards an env file path against stale directory artifacts.
17
+ * Removes the path if it exists as a directory (e.g. `.env/` created by a previous EISDIR bug).
18
+ * @param {string} envPath - The path to the environment file.
19
+ * @memberof UnderpostEnv
20
+ */
21
+ const guardEnvPath = (envPath) => {
22
+ if (fs.existsSync(envPath) && !fs.statSync(envPath).isFile()) {
23
+ logger.warn(`Removing stale directory at env path: ${envPath}`);
24
+ fs.removeSync(envPath);
25
+ }
26
+ };
27
+
28
+ /**
29
+ * @class UnderpostRootEnv
19
30
  * @description Manages the environment variables of the underpost root.
20
31
  * @memberof UnderpostEnv
21
32
  */
@@ -33,6 +44,7 @@ class UnderpostRootEnv {
33
44
  */
34
45
  set(key, value, options = { deployId: '', build: false }) {
35
46
  const _set = (envPath, key, value) => {
47
+ guardEnvPath(envPath);
36
48
  let env = {};
37
49
  if (fs.existsSync(envPath)) env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
38
50
  env[key] = value;
@@ -41,13 +53,16 @@ class UnderpostRootEnv {
41
53
  if (options.build) {
42
54
  const deployIdList = options.deployId
43
55
  ? [options.deployId]
44
- : fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',');
56
+ : fs.existsSync(`./engine-private/deploy/dd.router`)
57
+ ? fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(',')
58
+ : [DEFAULT_DEPLOY_ID];
45
59
  for (const deployId of deployIdList)
46
60
  for (const envFile of ['test', 'development', 'production'])
47
61
  _set(`./engine-private/conf/${deployId}/.env.${envFile}`, key, value);
48
62
  return;
49
63
  }
50
64
  const exeRootPath = `${getNpmRootPath()}/underpost`;
65
+ fs.ensureDirSync(exeRootPath);
51
66
  const envPath = `${exeRootPath}/.env`;
52
67
  _set(envPath, key, value);
53
68
  },
@@ -60,6 +75,7 @@ class UnderpostRootEnv {
60
75
  delete(key) {
61
76
  const exeRootPath = `${getNpmRootPath()}/underpost`;
62
77
  const envPath = `${exeRootPath}/.env`;
78
+ guardEnvPath(envPath);
63
79
  let env = {};
64
80
  if (fs.existsSync(envPath)) env = dotenv.parse(fs.readFileSync(envPath, 'utf8'));
65
81
  delete env[key];
@@ -79,7 +95,7 @@ class UnderpostRootEnv {
79
95
  get(key, value, options = { plain: false, disableLog: false, copy: false }) {
80
96
  const exeRootPath = `${getNpmRootPath()}/underpost`;
81
97
  const envPath = `${exeRootPath}/.env`;
82
- if (!fs.existsSync(envPath)) {
98
+ if (!fs.existsSync(envPath) || !fs.statSync(envPath).isFile()) {
83
99
  logger.warn(`Empty environment variables`);
84
100
  return undefined;
85
101
  }
@@ -101,6 +117,7 @@ class UnderpostRootEnv {
101
117
  list(key, value, options = {}) {
102
118
  const exeRootPath = `${getNpmRootPath()}/underpost`;
103
119
  const envPath = `${exeRootPath}/.env`;
120
+ guardEnvPath(envPath);
104
121
  if (!fs.existsSync(envPath)) {
105
122
  logger.warn(`Empty environment variables`);
106
123
  return {};
@@ -136,7 +153,19 @@ class UnderpostRootEnv {
136
153
  const envPath = `${exeRootPath}/.env`;
137
154
  fs.removeSync(envPath);
138
155
  },
156
+ /**
157
+ * @method isInsideContainer
158
+ * @description Detects whether the current process is running inside a container.
159
+ * Checks for Kubernetes service injection or Docker's .dockerenv marker.
160
+ * @returns {boolean} True if running inside a container.
161
+ * @memberof UnderpostEnv
162
+ */
163
+ isInsideContainer() {
164
+ return !!process.env.KUBERNETES_SERVICE_HOST || fs.existsSync('/.dockerenv');
165
+ },
139
166
  };
140
167
  }
141
168
 
142
169
  export default UnderpostRootEnv;
170
+
171
+ export { guardEnvPath };