underpost 3.2.8 → 3.2.10

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 (92) hide show
  1. package/.github/workflows/npmpkg.ci.yml +1 -0
  2. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  3. package/.github/workflows/release.cd.yml +1 -0
  4. package/.vscode/settings.json +10 -5
  5. package/CHANGELOG.md +223 -2
  6. package/CLI-HELP.md +36 -7
  7. package/README.md +38 -9
  8. package/bin/build.js +27 -11
  9. package/bin/deploy.js +20 -21
  10. package/bin/file.js +32 -13
  11. package/bin/index.js +2 -1
  12. package/bin/vs.js +1 -1
  13. package/bump.config.js +26 -0
  14. package/conf.js +20 -4
  15. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +2 -2
  16. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +2 -2
  17. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  18. package/manifests/deployment/dd-test-development/deployment.yaml +4 -2
  19. package/manifests/kind-config-dev.yaml +8 -0
  20. package/manifests/mongodb/pv-pvc.yaml +44 -8
  21. package/manifests/mongodb/statefulset.yaml +55 -68
  22. package/package.json +40 -25
  23. package/scripts/k3s-node-setup.sh +30 -11
  24. package/scripts/nat-iptables.sh +103 -18
  25. package/src/api/core/core.router.js +19 -14
  26. package/src/api/core/core.service.js +5 -5
  27. package/src/api/default/default.router.js +22 -18
  28. package/src/api/default/default.service.js +5 -5
  29. package/src/api/document/document.router.js +28 -23
  30. package/src/api/document/document.service.js +100 -23
  31. package/src/api/file/file.router.js +19 -13
  32. package/src/api/file/file.service.js +9 -7
  33. package/src/api/test/test.router.js +17 -12
  34. package/src/api/types.js +24 -0
  35. package/src/api/user/guest.service.js +5 -4
  36. package/src/api/user/user.router.js +297 -288
  37. package/src/api/user/user.service.js +100 -35
  38. package/src/cli/baremetal.js +20 -11
  39. package/src/cli/cluster.js +243 -55
  40. package/src/cli/db.js +106 -62
  41. package/src/cli/deploy.js +297 -154
  42. package/src/cli/fs.js +19 -3
  43. package/src/cli/index.js +37 -9
  44. package/src/cli/ipfs.js +4 -6
  45. package/src/cli/kubectl.js +4 -1
  46. package/src/cli/lxd.js +217 -135
  47. package/src/cli/release.js +289 -131
  48. package/src/cli/repository.js +91 -34
  49. package/src/cli/run.js +297 -56
  50. package/src/cli/test.js +9 -3
  51. package/src/client/Default.index.js +9 -3
  52. package/src/client/components/core/Auth.js +19 -5
  53. package/src/client/components/core/Docs.js +6 -34
  54. package/src/client/components/core/FileExplorer.js +6 -6
  55. package/src/client/components/core/Modal.js +65 -2
  56. package/src/client/components/core/PanelForm.js +56 -52
  57. package/src/client/components/core/Recover.js +4 -4
  58. package/src/client/components/core/Worker.js +170 -350
  59. package/src/client/services/default/default.management.js +20 -25
  60. package/src/client/services/user/guest.service.js +10 -3
  61. package/src/client/sw/core.sw.js +174 -112
  62. package/src/db/DataBaseProvider.js +120 -20
  63. package/src/db/mongo/MongoBootstrap.js +587 -0
  64. package/src/db/mongo/MongooseDB.js +126 -22
  65. package/src/index.js +1 -1
  66. package/src/runtime/express/Express.js +2 -2
  67. package/src/runtime/wp/Wp.js +8 -5
  68. package/src/server/auth.js +2 -2
  69. package/src/server/client-build-docs.js +1 -1
  70. package/src/server/client-build.js +94 -129
  71. package/src/server/conf.js +20 -65
  72. package/src/server/data-query.js +32 -20
  73. package/src/server/dns.js +22 -0
  74. package/src/server/process.js +180 -19
  75. package/src/server/runtime.js +1 -1
  76. package/src/server/start.js +26 -7
  77. package/src/server/valkey.js +9 -2
  78. package/src/ws/IoInterface.js +16 -16
  79. package/src/ws/core/channels/core.ws.chat.js +11 -11
  80. package/src/ws/core/channels/core.ws.mailer.js +29 -29
  81. package/src/ws/core/channels/core.ws.stream.js +19 -19
  82. package/src/ws/core/core.ws.connection.js +8 -8
  83. package/src/ws/core/core.ws.server.js +6 -5
  84. package/src/ws/default/channels/default.ws.main.js +10 -10
  85. package/src/ws/default/default.ws.connection.js +4 -4
  86. package/src/ws/default/default.ws.server.js +4 -3
  87. package/typedoc.json +10 -1
  88. package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
  89. package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
  90. /package/src/client/ssr/{offline → views}/Maintenance.js +0 -0
  91. /package/src/client/ssr/{offline → views}/NoNetworkConnection.js +0 -0
  92. /package/src/client/ssr/{pages → views}/Test.js +0 -0
package/src/cli/fs.js CHANGED
@@ -150,8 +150,14 @@ class UnderpostFileStorage {
150
150
  } else pullSkipCount++;
151
151
  }
152
152
  if (pullSkipCount > 0) logger.warn(`Pull skipped ${pullSkipCount} files that already exist`);
153
- Underpost.repo.initLocalRepo({ path });
154
- shellExec(`cd ${path} && git add . && git commit -m "Base pull state"`);
153
+ // Only run git init/commit when the caller explicitly requests git tracking (--git flag).
154
+ // For bundle pulls into ./build the git step is unwanted and would error on a non-repo path.
155
+ if (options.git === true) {
156
+ Underpost.repo.initLocalRepo({ path });
157
+ shellExec(`cd ${path} && git add . && git commit -m "Base pull state"`, {
158
+ silentOnError: true
159
+ });
160
+ }
155
161
  } else {
156
162
  const files =
157
163
  options.git === true ? Underpost.repo.getChangedFiles(path) : await fs.readdir(path, { recursive: true });
@@ -203,11 +209,13 @@ class UnderpostFileStorage {
203
209
  * @description Uploads a file to Cloudinary.
204
210
  * @param {string} path - The path to the file to upload.
205
211
  * @param {object} [options] - An object containing options for the upload.
206
- * @param {boolean} [options.force=false] - Flag to force file operations.
212
+ * @param {string} [options.deployId=''] - The identifier for the deployment (used to locate the storage config file).
213
+ * @param {boolean} [options.force=false] - Flag to force file operations (overwrites existing remote asset).
207
214
  * @param {string} [options.storageFilePath=''] - The path to the storage configuration file.
208
215
  * @returns {Promise<object>} A promise that resolves to the upload result.
209
216
  * @memberof UnderpostFileStorage
210
217
  */
218
+
211
219
  async upload(
212
220
  path,
213
221
  options = { rm: false, recursive: false, deployId: '', force: false, pull: false, storageFilePath: '' },
@@ -235,6 +243,7 @@ class UnderpostFileStorage {
235
243
  * @param {string} path - The path to the file to pull.
236
244
  * @param {object} [options] - Pull options.
237
245
  * @param {boolean} [options.omitUnzip=false] - If true, do not extract zip and keep downloaded zip file.
246
+ * @param {boolean} [options.force=false] - If true, re-download even if the local zip already exists.
238
247
  * @returns {Promise<void>} A promise that resolves when the file is pulled.
239
248
  * @memberof UnderpostFileStorage
240
249
  */
@@ -264,6 +273,13 @@ class UnderpostFileStorage {
264
273
  path = Underpost.fs.zip2File(zipPath);
265
274
  fs.removeSync(`${path}.zip`);
266
275
  },
276
+ /**
277
+ * @method delete
278
+ * @description Deletes a file from Cloudinary by its public ID.
279
+ * @param {string} path - The path (public ID) of the file to delete.
280
+ * @returns {Promise<object>} A promise that resolves to the Cloudinary delete result.
281
+ * @memberof UnderpostFileStorage
282
+ */
267
283
  async delete(path) {
268
284
  Underpost.fs.cloudinaryConfig();
269
285
  const deleteResult = await cloudinary.api
package/src/cli/index.js CHANGED
@@ -66,6 +66,10 @@ program
66
66
  .option('--underpost-quickly-install', 'Uses Underpost Quickly Install for dependency installation.')
67
67
  .option('--skip-pull-base', 'Skips cloning repositories, uses current workspace code directly.')
68
68
  .option('--skip-full-build', 'Skips the full client bundle build during deployment.')
69
+ .option(
70
+ '--pull-bundle',
71
+ 'Downloads the pre-built client bundle from Cloudinary via pull-bundle before starting. Use together with --skip-full-build to skip the local build entirely.',
72
+ )
69
73
  .action(Underpost.start.callback)
70
74
  .description('Initiates application servers, build pipelines, or other defined services based on the deployment ID.');
71
75
 
@@ -230,6 +234,7 @@ program
230
234
  .option('--ban-egress-clear', 'Clears all banned egress IP addresses.')
231
235
  .option('--ban-both-add', 'Adds IP addresses to both banned ingress and egress lists.')
232
236
  .option('--ban-both-remove', 'Removes IP addresses from both banned ingress and egress lists.')
237
+ .option('--mac', 'Prints the MAC address of the main network interface.')
233
238
  .description('Displays the current public machine IP addresses.')
234
239
  .action(Underpost.dns.ipDispatcher);
235
240
 
@@ -237,6 +242,7 @@ program
237
242
  .command('cluster')
238
243
  .argument('[pod-name]', 'Optional: Filters information by a specific pod name.')
239
244
  .option('--reset', `Deletes all clusters and prunes all related data and caches.`)
245
+ .option('--reset-mongodb', `Performs a hard cleanup of only MongoDB-related resources (StatefulSet, PVCs/PVs, Secrets, ConfigMaps, caches) without restarting the whole node.`)
240
246
  .option('--mariadb', 'Initializes the cluster with a MariaDB statefulset.')
241
247
  .option('--mysql', 'Initializes the cluster with a MySQL statefulset.')
242
248
  .option('--mongodb', 'Initializes the cluster with a MongoDB statefulset.')
@@ -333,6 +339,18 @@ program
333
339
  'Sets the local:remote port to expose when --expose is active (overrides auto-detected service port).',
334
340
  )
335
341
  .option('--cmd <cmd>', 'Custom initialization command for deployment (comma-separated commands).')
342
+ .option(
343
+ '--skip-full-build',
344
+ 'Skip client bundle rebuild; container will pull pre-built bundle via pull-bundle instead.',
345
+ )
346
+ .option(
347
+ '--pull-bundle',
348
+ 'Explicitly pull the pre-built client bundle from Cloudinary inside the container. Use together with --skip-full-build.',
349
+ )
350
+ .option(
351
+ '--image-pull-policy <policy>',
352
+ 'Override container imagePullPolicy in the generated deployment manifest (Always, IfNotPresent, Never). Defaults to Never for localhost/ images and IfNotPresent otherwise.',
353
+ )
336
354
  .description('Manages application deployments, defaulting to deploying development pods.')
337
355
  .action(Underpost.deploy.callback);
338
356
 
@@ -657,17 +675,25 @@ program
657
675
  .option(
658
676
  '--host-aliases <host-aliases>',
659
677
  'Adds entries to the Pod /etc/hosts via hostAliases. ' +
660
- 'Format: semicolon-separated entries of "ip=hostname1,hostname2" ' +
661
- '(e.g., "127.0.0.1=foo.local,bar.local;10.1.2.3=foo.remote,bar.remote").',
678
+ 'Format: semicolon-separated entries of "ip=hostname1,hostname2" ' +
679
+ '(e.g., "127.0.0.1=foo.local,bar.local;10.1.2.3=foo.remote,bar.remote").',
662
680
  )
663
681
  .option('--copy', 'Copies the runner output to the clipboard (supported by: generate-pass, template-deploy-local).')
682
+ .option(
683
+ '--skip-full-build',
684
+ 'Skip client bundle rebuild; triggers pull-bundle in container startup (supported by: sync, template-deploy).',
685
+ )
686
+ .option(
687
+ '--pull-bundle',
688
+ 'Explicitly download the pre-built client bundle from Cloudinary inside the container (supported by: sync, template-deploy). Use together with --skip-full-build.',
689
+ )
664
690
  .description('Runs specified scripts using various runners.')
665
691
  .action(Underpost.run.callback);
666
692
 
667
693
  program
668
694
  .command('lxd')
669
695
  .option('--init', 'Initializes LXD on the current machine via preseed.')
670
- .option('--reset', 'Removes the LXD snap and purges all data.')
696
+ .option('--reset', 'SAFE complete reset: cleans all VMs (proxy devices first), profiles, networks, then removes LXD snap.')
671
697
  .option('--install', 'Installs the LXD snap.')
672
698
  .option('--dev', 'Use local paths instead of the global npm installation.')
673
699
  .option('--create-virtual-network', 'Creates the lxdbr0 bridge network.')
@@ -676,7 +702,7 @@ program
676
702
  .option('--control', 'Initialize the target VM as a K3s control plane node.')
677
703
  .option('--worker', 'Initialize the target VM as a K3s worker node.')
678
704
  .option('--create-vm <vm-name>', 'Copy the LXC launch command for a new K3s VM to the clipboard.')
679
- .option('--delete-vm <vm-name>', 'Stop and delete the specified VM.')
705
+ .option('--delete-vm <vm-name>', 'SAFELY stop and delete VM (removes proxy devices first, then stops, then deletes). Safe to re-run.')
680
706
  .option('--init-vm <vm-name>', 'Run k3s-node-setup.sh on the specified VM (use with --control or --worker).')
681
707
  .option('--info-vm <vm-name>', 'Display full configuration and status for the specified VM.')
682
708
  .option('--test <vm-name>', 'Run connectivity and health checks on the specified VM.')
@@ -684,13 +710,11 @@ program
684
710
  .option(
685
711
  '--join-node <nodes>',
686
712
  'Join a K3s worker to a control plane. Standalone format: "workerName,controlName". ' +
687
- 'When used with --init-vm --worker, provide just the control node name for auto-join.',
713
+ 'When used with --init-vm --worker, provide just the control node name for auto-join.',
688
714
  )
689
715
  .option('--expose <vm-name:ports>', 'Proxy host ports to a VM (e.g., "k3s-control:80,443").')
690
716
  .option('--delete-expose <vm-name:ports>', 'Remove proxied ports from a VM (e.g., "k3s-control:80,443").')
691
- .option('--workflow-id <workflow-id>', 'Workflow ID to execute via runWorkflow.')
692
- .option('--vm-id <vm-name>', 'Target VM name for workflow execution.')
693
- .option('--deploy-id <deploy-id>', 'Deployment ID context for workflow execution.')
717
+ .option('--bootstrap-engine <vm-name>', 'Replicate /home/dd/engine source into the VM after init completes.')
694
718
  .option('--namespace <namespace>', 'Kubernetes namespace context (defaults to "default").')
695
719
  .description('Manages LXD virtual machines as K3s nodes (control plane or workers).')
696
720
  .action(Underpost.lxd.callback);
@@ -793,7 +817,7 @@ program
793
817
  .option(
794
818
  '--ci-push <deploy-id>',
795
819
  'Local equivalent of engine-*.ci.yml: builds dd-{deploy-id} and pushes to the engine-{deploy-id} repository. ' +
796
- 'Accepts the suffix (e.g., "cyberia"), "dd-cyberia", or "engine-cyberia".',
820
+ 'Accepts the suffix (e.g., "cyberia"), "dd-cyberia", or "engine-cyberia".',
797
821
  )
798
822
  .option(
799
823
  '--message <message>',
@@ -803,6 +827,10 @@ program
803
827
  '--pwa-build',
804
828
  'Runs the pwa-microservices-template update flow: always re-clones, syncs engine sources, installs, builds, and pushes.',
805
829
  )
830
+ .option(
831
+ '--dry-run',
832
+ 'For --build: previews version-bump changes (per-file substitution counts) without writing files or running downstream commands.',
833
+ )
806
834
  .description('Release orchestrator for building new versions and deploying releases of the Underpost CLI.')
807
835
  .action(async (version, options) => {
808
836
  if (options.build) return Underpost.release.build(version, options);
package/src/cli/ipfs.js CHANGED
@@ -131,12 +131,10 @@ class UnderpostIPFS {
131
131
  // Apply UDP buffer sysctl on every Kind node so QUIC (used by IPFS) can reach the
132
132
  // recommended 7.5 MB buffer size. Kind nodes are containers and do NOT inherit the
133
133
  // host sysctl values, so this must be set via docker exec on each node directly.
134
- if (!options.kubeadm && !options.k3s) {
135
- logger.info('Applying UDP buffer sysctl on Kind nodes');
136
- shellExec(
137
- `for node in $(kind get nodes); do docker exec $node sysctl -w net.core.rmem_max=7500000 net.core.wmem_max=7500000; done`,
138
- );
139
- }
134
+ shellExec(
135
+ `sudo sysctl -w net.core.rmem_max=7500000
136
+ sudo sysctl -w net.core.wmem_max=7500000`,
137
+ );
140
138
 
141
139
  shellExec(`kubectl apply -f ${underpostRoot}/manifests/ipfs/storage-class.yaml`);
142
140
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/ipfs -n ${options.namespace}`);
@@ -47,9 +47,12 @@ class UnderpostKubectl {
47
47
  * @memberof UnderpostKubectl
48
48
  */
49
49
  get(deployId, kindType = 'pods', namespace = '') {
50
+ // Existence-check style: a missing kubectl context, a non-existent
51
+ // namespace, or no pods matching the filter must return an empty
52
+ // list (not throw). silentOnError keeps the legacy contract.
50
53
  const raw = shellExec(
51
54
  `sudo kubectl get ${kindType}${namespace ? ` -n ${namespace}` : ` --all-namespaces`} -o wide`,
52
- { stdout: true, disableLog: true, silent: true },
55
+ { stdout: true, disableLog: true, silent: true, silentOnError: true },
53
56
  );
54
57
 
55
58
  const heads = raw