underpost 3.2.4 → 3.2.8

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 (141) hide show
  1. package/.github/workflows/release.cd.yml +1 -2
  2. package/CHANGELOG.md +268 -1
  3. package/CLI-HELP.md +26 -13
  4. package/Dockerfile +0 -4
  5. package/README.md +3 -3
  6. package/bin/build.js +13 -3
  7. package/bin/deploy.js +570 -1
  8. package/bin/file.js +5 -0
  9. package/conf.js +11 -2
  10. package/jsconfig.json +1 -1
  11. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -3
  12. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -3
  13. package/manifests/deployment/dd-default-development/deployment.yaml +2 -6
  14. package/manifests/deployment/dd-test-development/deployment.yaml +136 -66
  15. package/manifests/deployment/dd-test-development/proxy.yaml +41 -5
  16. package/package.json +20 -11
  17. package/src/api/core/core.controller.js +10 -10
  18. package/src/api/core/core.service.js +10 -10
  19. package/src/api/default/default.controller.js +10 -10
  20. package/src/api/default/default.service.js +10 -10
  21. package/src/api/document/document.controller.js +12 -12
  22. package/src/api/document/document.model.js +10 -16
  23. package/src/api/file/file.controller.js +8 -8
  24. package/src/api/file/file.model.js +10 -10
  25. package/src/api/file/file.service.js +36 -36
  26. package/src/api/test/test.controller.js +8 -8
  27. package/src/api/test/test.service.js +8 -8
  28. package/src/api/user/guest.service.js +99 -0
  29. package/src/api/user/user.controller.js +6 -6
  30. package/src/api/user/user.model.js +8 -13
  31. package/src/api/user/user.service.js +3 -20
  32. package/src/cli/deploy.js +33 -30
  33. package/src/cli/fs.js +62 -5
  34. package/src/cli/image.js +43 -1
  35. package/src/cli/index.js +5 -1
  36. package/src/cli/release.js +58 -2
  37. package/src/cli/repository.js +35 -3
  38. package/src/cli/run.js +304 -38
  39. package/src/cli/ssh.js +1 -1
  40. package/src/cli/static.js +43 -115
  41. package/src/client/Default.index.js +21 -33
  42. package/src/client/components/core/404.js +4 -4
  43. package/src/client/components/core/500.js +4 -4
  44. package/src/client/components/core/Account.js +73 -60
  45. package/src/client/components/core/AgGrid.js +23 -33
  46. package/src/client/components/core/Alert.js +12 -13
  47. package/src/client/components/core/AppStore.js +1 -1
  48. package/src/client/components/core/Auth.js +20 -32
  49. package/src/client/components/core/Badge.js +7 -13
  50. package/src/client/components/core/BtnIcon.js +15 -17
  51. package/src/client/components/core/CalendarCore.js +42 -63
  52. package/src/client/components/core/Chat.js +13 -15
  53. package/src/client/components/core/ClientEvents.js +87 -0
  54. package/src/client/components/core/ColorPaletteElement.js +309 -0
  55. package/src/client/components/core/Content.js +17 -14
  56. package/src/client/components/core/Css.js +15 -71
  57. package/src/client/components/core/CssCore.js +12 -16
  58. package/src/client/components/core/D3Chart.js +4 -4
  59. package/src/client/components/core/Docs.js +60 -59
  60. package/src/client/components/core/DropDown.js +69 -91
  61. package/src/client/components/core/EventBus.js +92 -0
  62. package/src/client/components/core/EventsUI.js +14 -17
  63. package/src/client/components/core/FileExplorer.js +102 -234
  64. package/src/client/components/core/FullScreen.js +47 -75
  65. package/src/client/components/core/Input.js +24 -69
  66. package/src/client/components/core/Keyboard.js +25 -18
  67. package/src/client/components/core/KeyboardAvoidance.js +145 -0
  68. package/src/client/components/core/LoadingAnimation.js +25 -31
  69. package/src/client/components/core/LogIn.js +41 -41
  70. package/src/client/components/core/LogOut.js +23 -14
  71. package/src/client/components/core/Modal.js +397 -176
  72. package/src/client/components/core/NotificationManager.js +14 -18
  73. package/src/client/components/core/Panel.js +54 -50
  74. package/src/client/components/core/PanelForm.js +25 -125
  75. package/src/client/components/core/Polyhedron.js +110 -214
  76. package/src/client/components/core/PublicProfile.js +39 -32
  77. package/src/client/components/core/Recover.js +52 -48
  78. package/src/client/components/core/Responsive.js +88 -32
  79. package/src/client/components/core/RichText.js +9 -18
  80. package/src/client/components/core/Router.js +24 -3
  81. package/src/client/components/core/SearchBox.js +37 -37
  82. package/src/client/components/core/SignUp.js +39 -30
  83. package/src/client/components/core/SocketIo.js +31 -2
  84. package/src/client/components/core/SocketIoHandler.js +6 -6
  85. package/src/client/components/core/ToggleSwitch.js +8 -20
  86. package/src/client/components/core/ToolTip.js +5 -17
  87. package/src/client/components/core/Translate.js +56 -59
  88. package/src/client/components/core/Validator.js +26 -16
  89. package/src/client/components/core/Wallet.js +15 -26
  90. package/src/client/components/core/Worker.js +140 -25
  91. package/src/client/components/core/windowGetDimensions.js +7 -7
  92. package/src/client/components/default/{MenuDefault.js → AppShellDefault.js} +87 -87
  93. package/src/client/components/default/CssDefault.js +12 -12
  94. package/src/client/components/default/LogInDefault.js +6 -4
  95. package/src/client/components/default/LogOutDefault.js +6 -4
  96. package/src/client/components/default/RouterDefault.js +47 -0
  97. package/src/client/components/default/SettingsDefault.js +4 -4
  98. package/src/client/components/default/SignUpDefault.js +6 -4
  99. package/src/client/components/default/TranslateDefault.js +3 -3
  100. package/src/client/services/core/core.service.js +17 -49
  101. package/src/client/services/default/default.management.js +139 -242
  102. package/src/client/services/default/default.service.js +10 -16
  103. package/src/client/services/document/document.service.js +14 -19
  104. package/src/client/services/file/file.service.js +8 -13
  105. package/src/client/services/test/test.service.js +8 -13
  106. package/src/client/services/user/guest.service.js +79 -0
  107. package/src/client/services/user/user.management.js +5 -5
  108. package/src/client/services/user/user.service.js +14 -20
  109. package/src/client/ssr/body/404.js +3 -3
  110. package/src/client/ssr/body/500.js +3 -3
  111. package/src/client/ssr/body/CacheControl.js +5 -2
  112. package/src/client/ssr/body/DefaultSplashScreen.js +19 -12
  113. package/src/client/ssr/mailer/DefaultRecoverEmail.js +19 -20
  114. package/src/client/ssr/mailer/DefaultVerifyEmail.js +15 -16
  115. package/src/client/ssr/offline/Maintenance.js +12 -11
  116. package/src/client/ssr/offline/NoNetworkConnection.js +3 -3
  117. package/src/client/ssr/pages/Test.js +2 -2
  118. package/src/client/sw/core.sw.js +212 -0
  119. package/src/index.js +1 -1
  120. package/src/runtime/express/Dockerfile +4 -4
  121. package/src/runtime/lampp/Dockerfile +8 -7
  122. package/src/runtime/wp/Dockerfile +11 -17
  123. package/src/server/backup.js +1 -2
  124. package/src/server/client-build-docs.js +45 -46
  125. package/src/server/client-build.js +334 -60
  126. package/src/server/client-formatted.js +47 -16
  127. package/src/server/conf.js +29 -13
  128. package/src/server/cron.js +6 -8
  129. package/src/server/dns.js +2 -1
  130. package/src/server/ipfs-client.js +232 -91
  131. package/src/server/process.js +13 -27
  132. package/src/server/start.js +6 -3
  133. package/src/server/valkey.js +134 -235
  134. package/tsconfig.docs.json +15 -0
  135. package/typedoc.json +20 -0
  136. package/jsdoc.json +0 -52
  137. package/src/client/components/core/ColorPalette.js +0 -5267
  138. package/src/client/components/core/JoyStick.js +0 -80
  139. package/src/client/components/default/RoutesDefault.js +0 -49
  140. package/src/client/sw/default.sw.js +0 -127
  141. package/src/client/sw/template.sw.js +0 -84
package/src/cli/run.js CHANGED
@@ -93,7 +93,6 @@ const logger = loggerFactory(import.meta);
93
93
  * @property {boolean} kubeadm - Whether to run in kubeadm mode.
94
94
  * @property {boolean} kind - Whether to run in kind mode.
95
95
  * @property {boolean} k3s - Whether to run in k3s mode.
96
- * @property {string} logType - The type of log to generate.
97
96
  * @property {string} hosts - The hosts to use.
98
97
  * @property {string} deployId - The deployment ID.
99
98
  * @property {string} instanceId - The instance ID.
@@ -158,7 +157,6 @@ const DEFAULT_OPTION = {
158
157
  kubeadm: false,
159
158
  kind: false,
160
159
  k3s: false,
161
- logType: '',
162
160
  hosts: '',
163
161
  deployId: '',
164
162
  instanceId: '',
@@ -490,21 +488,6 @@ class UnderpostRun {
490
488
  : await Underpost.release.pwa(sanitizedMessage, options);
491
489
  pbcopy(triggerCmd + ' && cd /home/dd/engine');
492
490
  },
493
- /**
494
- * @method template-deploy-image
495
- * @description Dispatches the Docker image CI workflow for the `engine` repository.
496
- * @param {string} path - The input value, identifier, or path for the operation.
497
- * @param {Object} options - The default underpost runner options for customizing workflow
498
- * @memberof UnderpostRun
499
- */
500
- 'template-deploy-image': (path, options = DEFAULT_OPTION) => {
501
- Underpost.repo.dispatchWorkflow({
502
- repo: `${process.env.GITHUB_USERNAME}/engine`,
503
- workflowFile: 'docker-image.ci.yml',
504
- ref: 'master',
505
- inputs: {},
506
- });
507
- },
508
491
  /**
509
492
  * @method docker-image
510
493
  * @description Dispatches the Docker image CI workflow (`docker-image.ci.yml`) for the `engine` repository via `workflow_dispatch`.
@@ -515,7 +498,7 @@ class UnderpostRun {
515
498
  'docker-image': (path, options = DEFAULT_OPTION) => {
516
499
  Underpost.repo.dispatchWorkflow({
517
500
  repo: `${process.env.GITHUB_USERNAME}/engine`,
518
- workflowFile: 'docker-image.ci.yml',
501
+ workflowFile: `docker-image${path ? `.${path}` : ''}.ci.yml`,
519
502
  ref: 'master',
520
503
  inputs: {},
521
504
  });
@@ -652,8 +635,9 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
652
635
  sync: async (path, options = DEFAULT_OPTION) => {
653
636
  // Dev usage: node bin run --dev --build sync dd-default
654
637
  const env = options.dev ? 'development' : 'production';
655
- const baseCommand = options.dev ? 'node bin' : 'underpost';
638
+ const baseCommand = 'node bin'; // options.dev ? 'node bin' : 'underpost';
656
639
  const baseClusterCommand = options.dev ? ' --dev' : '';
640
+ const clusterFlag = options.k3s ? ' --k3s' : options.kind ? ' --kind' : ' --kubeadm';
657
641
  const defaultPath = [
658
642
  'dd-default',
659
643
  options.replicas,
@@ -668,13 +652,23 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
668
652
  image = image ? image : defaultPath[3];
669
653
  node = node ? node : defaultPath[4];
670
654
  shellExec(`${baseCommand} cluster --ns-use ${options.namespace}`);
655
+
656
+ if (image && !image.startsWith('localhost'))
657
+ Underpost.image.pullDockerHubImage({
658
+ dockerhubImage: image,
659
+ kind: options.kind || (!options.nodeName && !options.kubeadm && !options.k3s),
660
+ kubeadm: options.nodeName || options.kubeadm,
661
+ k3s: options.k3s,
662
+ });
663
+
671
664
  if (isDeployRunnerContext(path, options)) {
672
665
  if (!options.disablePrivateConfUpdate) {
673
666
  const { validVersion } = Underpost.repo.privateConfUpdate(deployId);
674
667
  if (!validVersion) throw new Error('Version mismatch');
675
668
  }
676
669
  if (options.timezone !== 'none') shellExec(`${baseCommand} run${baseClusterCommand} tz`);
677
- if (options.deployIdCronJobs !== 'none') shellExec(`node bin cron --dev --setup-start --apply`);
670
+ if (options.deployIdCronJobs !== 'none')
671
+ shellExec(`node bin cron${baseClusterCommand}${clusterFlag} --setup-start --git --apply`);
678
672
  }
679
673
 
680
674
  const currentTraffic = isDeployRunnerContext(path, options)
@@ -687,7 +681,6 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
687
681
  const cmdString = options.cmd
688
682
  ? ' --cmd ' + (options.cmd.find((c) => c.match('"')) ? '"' + options.cmd + '"' : "'" + options.cmd + "'")
689
683
  : '';
690
- const clusterFlag = options.k3s ? ' --k3s' : options.kind ? ' --kind' : ' --kubeadm';
691
684
  const gitCleanFlag = options.gitClean ? ' --git-clean' : '';
692
685
 
693
686
  shellExec(
@@ -701,7 +694,7 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
701
694
  if (isDeployRunnerContext(path, options)) {
702
695
  // Backup app/services repositories with repo-backup configured
703
696
  shellExec(
704
- `${baseCommand} db ${deployId} ${clusterFlag} --repo-backup --dev --primary-pod --git --force-clone --preserveUUID ${options.namespace ? ` --ns ${options.namespace}` : ''}`,
697
+ `${baseCommand} db ${deployId} ${clusterFlag}${baseClusterCommand} --repo-backup --primary-pod --git --force-clone --preserveUUID ${options.namespace ? ` --ns ${options.namespace}` : ''}`,
705
698
  );
706
699
  shellExec(
707
700
  `${baseCommand} deploy${clusterFlag}${cmdString} --replicas ${replicas} --disable-update-proxy ${deployId} ${env} --versions ${versions}${
@@ -927,12 +920,17 @@ echo -e "[code]\nname=Visual Studio Code\nbaseurl=https://packages.microsoft.com
927
920
  image: _image,
928
921
  fromPort: _fromPort,
929
922
  toPort: _toPort,
923
+ fromDebugPort: _fromDebugPort,
924
+ toDebugPort: _toDebugPort,
930
925
  cmd: _cmd,
931
926
  volumes: _volumes,
932
927
  metadata: _metadata,
933
928
  } = instance;
934
929
  if (id !== _id) continue;
935
930
  const _deployId = `${deployId}-${_id}`;
931
+ // Use debug ports in development when defined, fall back to production ports.
932
+ if (env === 'development' && _fromDebugPort) _fromPort = _fromDebugPort;
933
+ if (env === 'development' && _toDebugPort) _toPort = _toDebugPort;
936
934
  const currentTraffic = Underpost.deploy.getCurrentTraffic(_deployId, {
937
935
  hostTest: _host,
938
936
  namespace: options.namespace,
@@ -1002,12 +1000,17 @@ EOF
1002
1000
  image: _image,
1003
1001
  fromPort: _fromPort,
1004
1002
  toPort: _toPort,
1003
+ fromDebugPort: _fromDebugPort,
1004
+ toDebugPort: _toDebugPort,
1005
1005
  cmd: _cmd,
1006
1006
  volumes: _volumes,
1007
1007
  metadata: _metadata,
1008
1008
  } = instance;
1009
1009
  if (id !== _id) continue;
1010
1010
  const _deployId = `${deployId}-${_id}`;
1011
+ // Use debug ports in development when defined, fall back to production ports.
1012
+ if (env === 'development' && _fromDebugPort) _fromPort = _fromDebugPort;
1013
+ if (env === 'development' && _toDebugPort) _toPort = _toDebugPort;
1011
1014
  etcHosts.push(_host);
1012
1015
  if (options.expose) continue;
1013
1016
  // Examples images:
@@ -1015,12 +1018,13 @@ EOF
1015
1018
  // `localhost/rockylinux9-underpost:${Underpost.version}`
1016
1019
  if (!_image) _image = `underpost/underpost-engine:${Underpost.version}`;
1017
1020
 
1018
- Underpost.image.pullDockerHubImage({
1019
- dockerhubImage: _image,
1020
- kind: options.kind || (!options.nodeName && !options.kubeadm && !options.k3s),
1021
- kubeadm: options.nodeName || options.kubeadm,
1022
- k3s: options.k3s,
1023
- });
1021
+ if (_image && !_image.startsWith('localhost'))
1022
+ Underpost.image.pullDockerHubImage({
1023
+ dockerhubImage: _image,
1024
+ kind: options.kind || (!options.nodeName && !options.kubeadm && !options.k3s),
1025
+ kubeadm: options.nodeName || options.kubeadm,
1026
+ k3s: options.k3s,
1027
+ });
1024
1028
 
1025
1029
  const currentTraffic = Underpost.deploy.getCurrentTraffic(_deployId, {
1026
1030
  hostTest: _host,
@@ -1094,7 +1098,6 @@ EOF
1094
1098
  targetTraffic,
1095
1099
  ignorePods,
1096
1100
  options.namespace,
1097
- options.logType,
1098
1101
  );
1099
1102
 
1100
1103
  if (!ready) {
@@ -1114,6 +1117,153 @@ EOF
1114
1117
  }
1115
1118
  },
1116
1119
 
1120
+ /**
1121
+ * @method instance-build-manifest
1122
+ * @description Builds a Kubernetes Deployment + Service manifest for a specific instance entry
1123
+ * from `conf.instances.json` and writes it to a file.
1124
+ * Traffic colour is automatically chosen as the opposite of the current live colour (blue/green),
1125
+ * defaulting to `blue` when no deployment is running yet.
1126
+ *
1127
+ * If `--build` is supplied the image is built from the project Dockerfile and loaded into the
1128
+ * cluster before the manifest is written (kind by default; `--kubeadm` / `--k3s` override).
1129
+ *
1130
+ * @param {string} path - Comma-separated: `deployId,instanceId[,projectPath]`.
1131
+ * `projectPath` is the root directory that contains the `Dockerfile` (e.g. `./cyberia-client`).
1132
+ * Artifacts are written to `<projectPath>/manifests/<env>/Dockerfile` and
1133
+ * `<projectPath>/manifests/<env>/deployment.yaml`.
1134
+ * In production, files are also copied to `<projectPath>/Dockerfile` and
1135
+ * `<projectPath>/deployment.yaml`.
1136
+ * @param {Object} options - The default underpost runner options for customizing workflow
1137
+ * @memberof UnderpostRun
1138
+ */
1139
+ 'instance-build-manifest': (path, options = DEFAULT_OPTION) => {
1140
+ const env = options.dev ? 'development' : 'production';
1141
+ let [deployId, id, projectPath] = path.split(',');
1142
+ const rootPath = projectPath ? projectPath : '.';
1143
+ const envManifestPath = `${rootPath}/manifests/deployments/${id}-${env}`;
1144
+ const outputPath = `${envManifestPath}/deployment.yaml`;
1145
+ const dockerfileManifestPath = `${envManifestPath}/Dockerfile`;
1146
+
1147
+ fs.mkdirpSync(envManifestPath);
1148
+
1149
+ const confInstances = JSON.parse(
1150
+ fs.readFileSync(`./engine-private/conf/${deployId}/conf.instances.json`, 'utf8'),
1151
+ );
1152
+
1153
+ const instance = confInstances.find((i) => i.id === id);
1154
+ if (!instance) {
1155
+ logger.error(`Instance with id '${id}' not found in conf.instances.json for deployId '${deployId}'`);
1156
+ return;
1157
+ }
1158
+
1159
+ let {
1160
+ id: _id,
1161
+ host: _host,
1162
+ path: _path,
1163
+ image: _image,
1164
+ fromPort: _fromPort,
1165
+ toPort: _toPort,
1166
+ fromDebugPort: _fromDebugPort,
1167
+ toDebugPort: _toDebugPort,
1168
+ cmd: _cmd,
1169
+ volumes: _volumes,
1170
+ metadata: _metadata,
1171
+ runtime: _runtime,
1172
+ } = instance;
1173
+
1174
+ // Resolve Dockerfile source: use runtime-specific path when instance defines a runtime.
1175
+ const dockerfileSourcePath = _runtime ? `src/runtime/${_runtime}/Dockerfile` : `${rootPath}/Dockerfile`;
1176
+ if (fs.existsSync(dockerfileSourcePath)) {
1177
+ fs.copyFileSync(dockerfileSourcePath, dockerfileManifestPath);
1178
+ } else {
1179
+ logger.warn(`[instance-build-manifest] Dockerfile not found at ${dockerfileSourcePath}`);
1180
+ }
1181
+
1182
+ const _deployId = `${deployId}-${_id}`;
1183
+ if (!_image) _image = `underpost/underpost-engine:${Underpost.version}`;
1184
+ // Use debug ports in development when defined, fall back to production ports.
1185
+ if (env === 'development' && _fromDebugPort) _fromPort = _fromDebugPort;
1186
+ if (env === 'development' && _toDebugPort) _toPort = _toDebugPort;
1187
+
1188
+ // Build image from projectPath Dockerfile and load into cluster when --build is set.
1189
+ if (options.build && projectPath) {
1190
+ const isKind = !options.kubeadm && !options.k3s;
1191
+ Underpost.image.build({
1192
+ path: projectPath,
1193
+ imageName: _image,
1194
+ podmanSave: true,
1195
+ imagePath: projectPath,
1196
+ kind: isKind,
1197
+ kubeadm: !!options.kubeadm,
1198
+ k3s: !!options.k3s,
1199
+ reset: !!options.reset,
1200
+ dev: options.dev,
1201
+ });
1202
+ logger.info(`[instance-build-manifest] Image built and loaded`, {
1203
+ image: _image,
1204
+ cluster: isKind ? 'kind' : options.kubeadm ? 'kubeadm' : 'k3s',
1205
+ });
1206
+ }
1207
+
1208
+ // Determine target traffic: opposite of current, or 'blue' if nothing is running yet.
1209
+ const currentTraffic = Underpost.deploy.getCurrentTraffic(_deployId, {
1210
+ hostTest: _host,
1211
+ namespace: options.namespace,
1212
+ });
1213
+ const targetTraffic = currentTraffic ? (currentTraffic === 'blue' ? 'green' : 'blue') : 'blue';
1214
+
1215
+ // Resolve {{grpc-service-dns}} using the parent deploy's current (or default) traffic.
1216
+ const parentTraffic = Underpost.deploy.getCurrentTraffic(deployId, { namespace: options.namespace }) || 'blue';
1217
+ const resolvedCmd = _cmd[env].map((c) =>
1218
+ c.replaceAll(
1219
+ '{{grpc-service-dns}}',
1220
+ `${deployId}-grpc-service-${env}-${parentTraffic}.${options.namespace || 'default'}.svc.cluster.local:50051`,
1221
+ ),
1222
+ );
1223
+
1224
+ const deploymentYaml =
1225
+ `---\n` +
1226
+ Underpost.deploy
1227
+ .deploymentYamlPartsFactory({
1228
+ deployId: _deployId,
1229
+ env,
1230
+ suffix: targetTraffic,
1231
+ resources: Underpost.deploy.resourcesFactory(options),
1232
+ replicas: options.replicas,
1233
+ image: _image,
1234
+ namespace: options.namespace,
1235
+ volumes: _volumes,
1236
+ cmd: resolvedCmd,
1237
+ })
1238
+ .replace('{{ports}}', buildKindPorts(_fromPort, _toPort));
1239
+
1240
+ fs.writeFileSync(outputPath, deploymentYaml, 'utf8');
1241
+ logger.info(`[instance-build-manifest] Manifest written to ${outputPath}`, {
1242
+ deployId: _deployId,
1243
+ env,
1244
+ traffic: targetTraffic,
1245
+ image: _image,
1246
+ });
1247
+
1248
+ if (env === 'production') {
1249
+ if (fs.existsSync(dockerfileManifestPath)) {
1250
+ fs.copyFileSync(dockerfileManifestPath, `${rootPath}/Dockerfile`);
1251
+ }
1252
+ fs.copyFileSync(outputPath, `${rootPath}/deployment.yaml`);
1253
+ logger.info('[instance-build-manifest] Production artifacts copied to project root', {
1254
+ rootPath,
1255
+ dockerfile: `${rootPath}/Dockerfile`,
1256
+ deployment: `${rootPath}/deployment.yaml`,
1257
+ });
1258
+ const ciSrc = `./.github/workflows/docker-image.${_runtime}.ci.yml`;
1259
+ if (fs.existsSync(ciSrc)) {
1260
+ if (!fs.existsSync(`${rootPath}/.github/workflows`)) fs.mkdirpSync(`${rootPath}/.github/workflows`);
1261
+ fs.copyFileSync(ciSrc, `${rootPath}/.github/workflows/docker-image.${_runtime}.ci.yml`);
1262
+ logger.info(`[instance-build-manifest] CI workflow copied`, { src: ciSrc });
1263
+ }
1264
+ }
1265
+ },
1266
+
1117
1267
  /**
1118
1268
  * @method ls-deployments
1119
1269
  * @description Retrieves and logs a table of Kubernetes deployments using `Underpost.deploy.get`.
@@ -1919,6 +2069,16 @@ EOF
1919
2069
 
1920
2070
  shellCd('/home/dd/engine');
1921
2071
  },
2072
+ /**
2073
+ * @method pull-rocky-image
2074
+ * @description Pulls the base `rockylinux:9` image from Docker Hub via Podman.
2075
+ * @param {string} path - The input value, identifier, or path for the operation.
2076
+ * @param {Object} options - The default underpost runner options for customizing workflow
2077
+ * @memberof UnderpostRun
2078
+ */
2079
+ 'pull-rocky-image': (path, options = DEFAULT_OPTION) => {
2080
+ shellExec(`sudo podman pull docker.io/library/rockylinux:9`);
2081
+ },
1922
2082
  /**
1923
2083
  * @method rmi
1924
2084
  * @description Forces the removal of all local Podman images (`podman rmi $(podman images -qa) --force`).
@@ -1948,13 +2108,6 @@ EOF
1948
2108
  } else shellExec(`sudo kill -9 $(lsof -t -i:${_path})`);
1949
2109
  }
1950
2110
  },
1951
- /**
1952
- * @method secret
1953
- * @description Creates an Underpost secret named 'underpost' from a file, defaulting to `/home/dd/engine/engine-private/conf/dd-cron/.env.production` if no path is provided.
1954
- * @param {string} path - The input value, identifier, or path for the operation (used as the optional path to the secret file).
1955
- * @param {Object} options - The default underpost runner options for customizing workflow
1956
- * @memberof UnderpostRun
1957
- */
1958
2111
  /**
1959
2112
  * @method generate-pass
1960
2113
  * @description Generates a cryptographically secure random password that satisfies all validatePassword
@@ -1991,10 +2144,16 @@ EOF
1991
2144
  if (options.copy) pbcopy(password);
1992
2145
  else console.log(password);
1993
2146
  },
1994
-
2147
+ /**
2148
+ * @method secret
2149
+ * @description Creates an Underpost secret named 'underpost' from a file, defaulting to `/home/dd/engine/engine-private/conf/dd-cron/.env.production` if no path is provided.
2150
+ * @param {string} path - The input value, identifier, or path for the operation (used as the optional path to the secret file).
2151
+ * @param {Object} options - The default underpost runner options for customizing workflow
2152
+ * @memberof UnderpostRun
2153
+ */
1995
2154
  secret: (path, options = DEFAULT_OPTION) => {
1996
2155
  const secretPath = path ? path : `/home/dd/engine/engine-private/conf/dd-cron/.env.production`;
1997
- const command = `node bin secret underpost --create-from-file ${secretPath}`;
2156
+ const command = `${options.dev ? 'node bin' : 'underpost'} secret underpost --create-from-file ${secretPath}`;
1998
2157
  shellExec(command);
1999
2158
  },
2000
2159
  /**
@@ -2165,6 +2324,113 @@ EOF`;
2165
2324
  if (options.logs) shellExec(`kubectl logs -f ${podName} -n ${namespace}`, { async: true });
2166
2325
  }
2167
2326
  },
2327
+
2328
+ /**
2329
+ * @method push-bundle
2330
+ * @description Builds the client zip for the specified deployment, splits it into parts, and uploads to file storage.
2331
+ * Steps: set env, build+split zip, switch to cron env, upload parts to storage.
2332
+ * @param {string} path - Optional `fsPath.splitOption` string.
2333
+ * Examples: `build` (default split 8), `build.16` (split 16 MB), `build.none-split` (no split flag).
2334
+ * @param {Object} options - The default underpost runner options for customizing workflow.
2335
+ * @param {string} [options.deployId] - Override deploy ID.
2336
+ * @param {boolean} [options.dev] - Use development environment; defaults to production.
2337
+ * @memberof UnderpostRun
2338
+ */
2339
+ 'push-bundle': (path = '', options = DEFAULT_OPTION) => {
2340
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
2341
+ const env = options.dev ? 'development' : 'production';
2342
+ const deployId = options.deployId || 'dd-default';
2343
+ const pathParts = (path || '').split('.');
2344
+ const fsPath = (pathParts[0] || '').trim() || 'build';
2345
+ const splitOption = (pathParts[1] || '').trim();
2346
+
2347
+ let splitFlag = '--split 8';
2348
+ if (splitOption) {
2349
+ if (splitOption === 'none-split') {
2350
+ splitFlag = '';
2351
+ } else {
2352
+ const splitMb = Number(splitOption);
2353
+ if (Number.isFinite(splitMb) && splitMb > 0) {
2354
+ splitFlag = `--split ${splitMb}`;
2355
+ } else {
2356
+ logger.warn('push-bundle: invalid split option, using default split 8', {
2357
+ path,
2358
+ splitOption,
2359
+ });
2360
+ }
2361
+ }
2362
+ }
2363
+
2364
+ shellExec(`${baseCommand} env ${deployId} ${env}`);
2365
+ shellExec(`${baseCommand} client ${deployId} --build-zip${splitFlag ? ` ${splitFlag}` : ''}`);
2366
+ shellExec(
2367
+ `${baseCommand} fs ${fsPath} --recursive --deploy-id ${deployId} --storage-file-path engine-private/conf/${deployId}/storage.bundle.json --force`,
2368
+ );
2369
+ },
2370
+
2371
+ /**
2372
+ * @method pull-bundle
2373
+ * @description Downloads split zip parts from file storage, merges and extracts them, and moves the result into the public directory.
2374
+ * Steps: set cron env, download parts (omit-unzip), merge zip, unzip, remove zip, move to public/<host>.
2375
+ * @param {string} path - Optional host name(s) used to locate zip(s) and as public destination(s) (e.g. 'underpost.net' or 'a.com,b.com').
2376
+ * If omitted, hosts are loaded from `engine-private/conf/<deployId>/conf.server.json`.
2377
+ * @param {Object} options - The default underpost runner options for customizing workflow.
2378
+ * @param {string} [options.deployId] - Deploy ID for storage lookup (defaults to 'dd-default').
2379
+ * @param {boolean} [options.dev] - Use development environment; defaults to production.
2380
+ * @memberof UnderpostRun
2381
+ */
2382
+ 'pull-bundle': (path = '', options = DEFAULT_OPTION) => {
2383
+ const baseCommand = options.dev ? 'node bin' : 'underpost';
2384
+ const env = options.dev ? 'development' : 'production';
2385
+ const deployId = options.deployId || 'dd-default';
2386
+ const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
2387
+ const hosts = path
2388
+ ? path
2389
+ .split(',')
2390
+ .map((h) => h.trim())
2391
+ .filter(Boolean)
2392
+ : fs.existsSync(confServerPath)
2393
+ ? Object.keys(loadConfServerJson(confServerPath))
2394
+ : [];
2395
+
2396
+ if (hosts.length === 0) {
2397
+ logger.error('pull-bundle: no hosts resolved', {
2398
+ deployId,
2399
+ path,
2400
+ confServerPath,
2401
+ });
2402
+ return;
2403
+ }
2404
+
2405
+ shellExec(`${baseCommand} env ${deployId} ${env}`);
2406
+ if (!fs.existsSync('./build')) fs.mkdirSync('./build', { recursive: true });
2407
+ shellExec(
2408
+ `${baseCommand} fs build --recursive --deploy-id ${deployId} --storage-file-path engine-private/conf/${deployId}/storage.bundle.json --pull --omit-unzip`,
2409
+ );
2410
+ for (const host of hosts) {
2411
+ const zipPath = `build/${host}-.zip`;
2412
+ const hasZip = fs.existsSync(zipPath);
2413
+ const hasParts =
2414
+ fs.existsSync('./build') &&
2415
+ fs
2416
+ .readdirSync('./build')
2417
+ .some((name) => name.startsWith(`${host}-.zip.part`) || name.startsWith(`${host}-.zip-part`));
2418
+
2419
+ if (!hasZip && !hasParts) {
2420
+ logger.warn(`Bundle not found for host '${host}'. Skipping host.`, {
2421
+ zipPath,
2422
+ deployId,
2423
+ });
2424
+ continue;
2425
+ }
2426
+
2427
+ if (hasParts) shellExec(`${baseCommand} client --merge-zip ${zipPath}`);
2428
+ shellExec(`${baseCommand} client --unzip ${zipPath}`);
2429
+ shellExec(`sudo rm -rf ${zipPath}`);
2430
+ if (fs.existsSync(`public/${host}`)) shellExec(`sudo rm -rf public/${host}`);
2431
+ shellExec(`sudo mv build/${host} public/${host}`);
2432
+ }
2433
+ },
2168
2434
  };
2169
2435
 
2170
2436
  static API = {
package/src/cli/ssh.js CHANGED
@@ -315,7 +315,7 @@ EOF`);
315
315
  console.log(`group_name : password_x : GID(Internal Group ID) : user_list`.blue);
316
316
  console.log(filter ? groupsOut.replaceAll(filter, filter.red) : groupsOut);
317
317
  console.log('Users'.bold.blue);
318
- console.log(`usuario : x : UID : GID : GECOS : home_dir : shell`.blue);
318
+ console.log(`user : x : UID : GID : GECOS : home_dir : shell`.blue);
319
319
  console.log(filter ? usersOut.replaceAll(filter, filter.red) : usersOut);
320
320
  }
321
321