underpost 2.8.55 → 2.8.56

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.
@@ -43,7 +43,7 @@ jobs:
43
43
  run: |
44
44
  npm install -g underpost
45
45
  underpost config set GITHUB_TOKEN ${{ secrets.GIT_AUTH_TOKEN }}
46
- underpost install
46
+ npm install
47
47
  node ./bin/deploy rename-package @underpostnet/underpost
48
48
  node ./bin/deploy set-repo underpostnet/pwa-microservices-template-ghpkg
49
49
 
@@ -28,7 +28,7 @@ jobs:
28
28
  run: |
29
29
  npm install -g underpost
30
30
  underpost config set GITHUB_TOKEN ${{ secrets.GIT_AUTH_TOKEN }}
31
- underpost install
31
+ npm install
32
32
 
33
33
  - name: Set git credentials
34
34
  run: |
@@ -43,7 +43,7 @@ jobs:
43
43
  - name: Build the site
44
44
  run: |
45
45
  npm install -g underpost
46
- underpost install
46
+ npm install
47
47
  node bin/deploy update-default-conf ghpkg
48
48
  env-cmd -f .env.production node bin/deploy build-full-client github-pages underpostnet.github.io /pwa-microservices-template-ghpkg
49
49
  # git lfs install
package/bin/build.js CHANGED
@@ -155,6 +155,15 @@ const { DefaultConf } = await import(`../conf.${confName}.js`);
155
155
  `${basePath}/.github/workflows/engine.${confName.split('dd-')[1]}.ci.yml`,
156
156
  );
157
157
 
158
+ switch (confName) {
159
+ case 'dd-cyberia':
160
+ fs.copyFileSync(`./bin/cyberia.js`, `${basePath}/bin/cyberia.js`);
161
+ break;
162
+
163
+ default:
164
+ break;
165
+ }
166
+
158
167
  const packageJson = JSON.parse(fs.readFileSync(`${basePath}/package.json`, 'utf8'));
159
168
  packageJson.name = repoName;
160
169
  fs.writeFileSync(
package/bin/index.js CHANGED
@@ -85,12 +85,14 @@ program
85
85
  .option('--reset', `Delete all clusters and prune all data and caches`)
86
86
  .option('--mariadb', 'Init with mariadb statefulset')
87
87
  .option('--mongodb', 'Init with mongodb statefulset')
88
+ .option('--mongodb4', 'Init with mongodb 4.4 service')
88
89
  .option('--valkey', 'Init with valkey service')
89
90
  .option('--contour', 'Init with project contour base HTTPProxy and envoy')
90
91
  .option('--cert-manager', 'Init with letsencrypt-prod ClusterIssuer')
91
92
  .option('--info', 'Get all kinds objects deployed')
92
93
  .option('--full', 'Init with all statefulsets and services available')
93
94
  .option('--ns-use <ns-name>', 'Switches current context to namespace')
95
+ .option('--dev', 'init with dev cluster')
94
96
  .option('--list-pods', 'Display list pods information')
95
97
  .action(Underpost.cluster.init)
96
98
  .description('Manage cluster, for default initialization base kind cluster');
@@ -158,6 +160,8 @@ program
158
160
  .argument('<deploy-list>', 'Deploy id list, e.g. default-a,default-b')
159
161
  .option('--import', 'Import container backups from repositories')
160
162
  .option('--export', 'Export container backups to repositories')
163
+ .option('--pod-name <pod-name>', 'Optional pod context')
164
+ .option('--ns <ns-name>', 'Optional name space context')
161
165
  .description('Manage databases')
162
166
  .action(UnderpostDB.API.callback);
163
167
 
@@ -184,6 +188,18 @@ program
184
188
  .description('Cron jobs management')
185
189
  .action(Underpost.cron.callback);
186
190
 
191
+ program
192
+ .command('fs')
193
+ .argument('[path]', 'Absolute or relative directory')
194
+ .option('--rm', 'Remove file')
195
+ .option('--git', 'Current git changes')
196
+ .option('--recursive', 'Upload files recursively')
197
+ .option('--deploy-id <deploy-id>', 'Deploy configuration id')
198
+ .option('--pull', 'Download file')
199
+ .option('--force', 'Force action')
200
+ .description('File storage management, for default upload file')
201
+ .action(Underpost.fs.callback);
202
+
187
203
  program
188
204
  .command('test')
189
205
  .argument('[deploy-list]', 'Deploy id list, e.g. default-a,default-b')
@@ -193,6 +209,7 @@ program
193
209
  .option('--logs', 'Display container logs')
194
210
  .option('--pod-name <pod-name>')
195
211
  .option('--pod-status <pod-status>')
212
+ .option('--kind-type <kind-type>')
196
213
  .action(Underpost.test.callback);
197
214
 
198
215
  program.parse();
@@ -58,7 +58,7 @@ services:
58
58
  cpus: '0.25'
59
59
  memory: 20M
60
60
  labels: # labels in Compose file instead of Dockerfile
61
- engine.version: '2.8.55'
61
+ engine.version: '2.8.56'
62
62
  networks:
63
63
  - load-balancer
64
64
 
@@ -0,0 +1,12 @@
1
+ kind: Cluster
2
+ apiVersion: kind.x-k8s.io/v1alpha4
3
+ nodes:
4
+ - role: control-plane
5
+ - role: worker
6
+ # extraPortMappings:
7
+ # - containerPort: 80
8
+ # hostPort: 80
9
+ # listenAddress: '0.0.0.0'
10
+ # - containerPort: 443
11
+ # hostPort: 443
12
+ # listenAddress: '0.0.0.0'
@@ -29,10 +29,10 @@ spec:
29
29
  - name: mongo-persistent-storage
30
30
  mountPath: /data/db
31
31
 
32
- resources:
33
- requests:
34
- memory: '500Mi'
35
- cpu: '500m'
32
+ # resources:
33
+ # requests:
34
+ # memory: '500Mi'
35
+ # cpu: '500m'
36
36
  volumes:
37
37
  - name: mongo-persistent-storage
38
38
  persistentVolumeClaim:
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "module",
3
3
  "main": "src/index.js",
4
4
  "name": "underpost",
5
- "version": "2.8.55",
5
+ "version": "2.8.56",
6
6
  "description": "pwa api rest template",
7
7
  "scripts": {
8
8
  "start": "env-cmd -f .env.production node --max-old-space-size=8192 src/server",
@@ -57,9 +57,11 @@
57
57
  "ag-grid-community": "31.0.0",
58
58
  "axios": "^1.5.1",
59
59
  "chai": "^5.1.0",
60
+ "clean-jsdoc-theme": "^4.3.0",
60
61
  "cli-progress": "^3.12.0",
61
62
  "cli-spinners": "^3.0.0",
62
63
  "clipboardy": "^4.0.0",
64
+ "cloudinary": "^2.5.1",
63
65
  "color": "^4.2.3",
64
66
  "colors": "^1.4.0",
65
67
  "commander": "^12.1.0",
@@ -67,6 +69,7 @@
67
69
  "cors": "^2.8.5",
68
70
  "d3": "^7.9.0",
69
71
  "dotenv": "^16.3.1",
72
+ "easy-json-schema": "^0.0.2-beta",
70
73
  "easymde": "^2.18.0",
71
74
  "env-cmd": "^10.1.0",
72
75
  "express": "^4.18.2",
@@ -87,6 +90,7 @@
87
90
  "log-update": "^6.0.0",
88
91
  "mariadb": "^3.2.2",
89
92
  "marked": "^12.0.2",
93
+ "mocha": "^10.8.2",
90
94
  "mongoose": "^8.9.5",
91
95
  "morgan": "^1.10.0",
92
96
  "nodemailer": "^6.9.9",
@@ -95,6 +99,7 @@
95
99
  "peer": "^1.0.2",
96
100
  "peerjs": "^1.5.2",
97
101
  "pixi.js": "7.4.2",
102
+ "plantuml": "^0.0.2",
98
103
  "prom-client": "^15.1.2",
99
104
  "public-ip": "^6.0.1",
100
105
  "read": "^2.1.0",
@@ -106,17 +111,13 @@
106
111
  "socket.io": "^4.8.0",
107
112
  "sortablejs": "^1.15.0",
108
113
  "split-file": "^2.3.0",
114
+ "swagger-autogen": "^2.23.7",
109
115
  "swagger-ui-express": "^5.0.0",
110
116
  "systeminformation": "^5.23.7",
111
117
  "uglify-js": "^3.17.4",
112
118
  "validator": "^13.11.0",
113
119
  "vanilla-jsoneditor": "^2.3.2",
114
- "winston": "^3.11.0",
115
- "clean-jsdoc-theme": "^4.3.0",
116
- "easy-json-schema": "^0.0.2-beta",
117
- "mocha": "^10.8.2",
118
- "plantuml": "^0.0.2",
119
- "swagger-autogen": "^2.23.7"
120
+ "winston": "^3.11.0"
120
121
  },
121
122
  "devDependencies": {
122
123
  "clean-jsdoc-theme": "^4.3.0",
@@ -12,7 +12,8 @@ class UnderpostCluster {
12
12
  async init(
13
13
  podName,
14
14
  options = {
15
- valkey: false,
15
+ mongodb: false,
16
+ mongodb4: false,
16
17
  mariadb: false,
17
18
  valkey: false,
18
19
  full: false,
@@ -20,19 +21,20 @@ class UnderpostCluster {
20
21
  certManager: false,
21
22
  listPods: false,
22
23
  reset: false,
24
+ dev: false,
23
25
  nsUse: '',
24
26
  },
25
27
  ) {
26
28
  const npmRoot = getNpmRootPath();
27
- const underpostRoot = `${npmRoot}/underpost`;
29
+ const underpostRoot = options?.dev === true ? '.' : `${npmRoot}/underpost`;
28
30
  if (options.reset === true) return await UnderpostCluster.API.reset();
29
31
  if (options.listPods === true) return console.table(UnderpostDeploy.API.get(podName ?? undefined));
30
32
 
31
- if (options.nsUse) {
33
+ if (options.nsUse && typeof options.nsUse === 'string') {
32
34
  shellExec(`kubectl config set-context --current --namespace=${options.nsUse}`);
33
35
  return;
34
36
  }
35
- if (options.info) {
37
+ if (options.info === true) {
36
38
  shellExec(`kubectl config get-contexts`); // config env persisente for manage multiple clusters
37
39
  shellExec(`kubectl config get-clusters`);
38
40
  shellExec(`kubectl get nodes -o wide`); // set of nodes of a cluster
@@ -63,12 +65,8 @@ class UnderpostCluster {
63
65
  shellExec(`kubectl get crd --all-namespaces -o wide`);
64
66
  return;
65
67
  }
66
- const testClusterInit = shellExec(`kubectl get pods --all-namespaces -o wide`, {
67
- disableLog: true,
68
- silent: true,
69
- stdout: true,
70
- });
71
- if (!(testClusterInit.match('kube-system') && testClusterInit.match('kube-proxy'))) {
68
+
69
+ if (!UnderpostDeploy.API.get('kube-apiserver-kind-control-plane')[0]) {
72
70
  shellExec(`containerd config default > /etc/containerd/config.toml`);
73
71
  shellExec(`sed -i -e "s/SystemdCgroup = false/SystemdCgroup = true/g" /etc/containerd/config.toml`);
74
72
  // shellExec(`cp /etc/kubernetes/admin.conf ~/.kube/config`);
@@ -76,15 +74,19 @@ class UnderpostCluster {
76
74
  shellExec(`sudo service docker restart`);
77
75
  shellExec(`sudo systemctl enable --now containerd.service`);
78
76
  shellExec(`sudo systemctl restart containerd`);
79
- shellExec(`cd ${underpostRoot}/manifests && kind create cluster --config kind-config.yaml`);
77
+ shellExec(
78
+ `cd ${underpostRoot}/manifests && kind create cluster --config kind-config${
79
+ options?.dev === true ? '-dev' : ''
80
+ }.yaml`,
81
+ );
80
82
  shellExec(`sudo chown $(id -u):$(id -g) $HOME/.kube/config**`);
81
83
  } else logger.warn('Cluster already initialized');
82
84
 
83
- if (options.full || options.valkey) {
85
+ if (options.full === true || options.valkey === true) {
84
86
  shellExec(`kubectl delete statefulset service-valkey`);
85
87
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/valkey`);
86
88
  }
87
- if (options.full || options.mariadb) {
89
+ if (options.full === true || options.mariadb === true) {
88
90
  shellExec(
89
91
  `sudo kubectl create secret generic mariadb-secret --from-file=username=/home/dd/engine/engine-private/mariadb-username --from-file=password=/home/dd/engine/engine-private/mariadb-password`,
90
92
  );
@@ -94,7 +96,29 @@ class UnderpostCluster {
94
96
  shellExec(`kubectl delete statefulset mariadb-statefulset`);
95
97
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/mariadb`);
96
98
  }
97
- if (options.full || options.mongodb) {
99
+ if (options.mongodb4 === true) {
100
+ shellExec(`kubectl apply -k ${underpostRoot}/manifests/mongodb-4.4`);
101
+
102
+ const deploymentName = 'mongodb-deployment';
103
+
104
+ const successInstance = await UnderpostTest.API.statusMonitor(deploymentName);
105
+
106
+ if (successInstance) {
107
+ const mongoConfig = {
108
+ _id: 'rs0',
109
+ members: [{ _id: 0, host: '127.0.0.1:27017' }],
110
+ };
111
+
112
+ const [pod] = UnderpostDeploy.API.get(deploymentName);
113
+
114
+ shellExec(
115
+ `sudo kubectl exec -i ${pod.NAME} -- mongo --quiet \
116
+ --eval 'rs.initiate(${JSON.stringify(mongoConfig)})'`,
117
+ );
118
+ }
119
+
120
+ // await UnderpostTest.API.statusMonitor('mongodb-1');
121
+ } else if (options.full === true || options.mongodb === true) {
98
122
  shellExec(
99
123
  `sudo kubectl create secret generic mongodb-keyfile --from-file=/home/dd/engine/engine-private/mongodb-keyfile`,
100
124
  );
@@ -104,28 +128,30 @@ class UnderpostCluster {
104
128
  shellExec(`kubectl delete statefulset mongodb`);
105
129
  shellExec(`kubectl apply -k ${underpostRoot}/manifests/mongodb`);
106
130
 
107
- await UnderpostTest.API.podStatusMonitor('mongodb-1');
131
+ const successInstance = await UnderpostTest.API.statusMonitor('mongodb-1');
108
132
 
109
- const mongoConfig = {
110
- _id: 'rs0',
111
- members: [
112
- { _id: 0, host: 'mongodb-0.mongodb-service:27017', priority: 1 },
113
- { _id: 1, host: 'mongodb-1.mongodb-service:27017', priority: 1 },
114
- ],
115
- };
133
+ if (successInstance) {
134
+ const mongoConfig = {
135
+ _id: 'rs0',
136
+ members: [
137
+ { _id: 0, host: 'mongodb-0.mongodb-service:27017', priority: 1 },
138
+ { _id: 1, host: 'mongodb-1.mongodb-service:27017', priority: 1 },
139
+ ],
140
+ };
116
141
 
117
- shellExec(
118
- `sudo kubectl exec -i mongodb-0 -- mongosh --quiet --json=relaxed \
119
- --eval 'use admin' \
120
- --eval 'rs.initiate(${JSON.stringify(mongoConfig)})' \
121
- --eval 'rs.status()'`,
122
- );
142
+ shellExec(
143
+ `sudo kubectl exec -i mongodb-0 -- mongosh --quiet --json=relaxed \
144
+ --eval 'use admin' \
145
+ --eval 'rs.initiate(${JSON.stringify(mongoConfig)})' \
146
+ --eval 'rs.status()'`,
147
+ );
148
+ }
123
149
  }
124
150
 
125
- if (options.full || options.contour)
151
+ if (options.full === true || options.contour === true)
126
152
  shellExec(`kubectl apply -f https://projectcontour.io/quickstart/contour.yaml`);
127
153
 
128
- if (options.full || options.certManager) {
154
+ if (options.full === true || options.certManager === true) {
129
155
  if (!UnderpostDeploy.API.get('cert-manager').find((p) => p.STATUS === 'Running')) {
130
156
  shellExec(`helm repo add jetstack https://charts.jetstack.io --force-update`);
131
157
  shellExec(
package/src/cli/db.js CHANGED
@@ -7,9 +7,9 @@ const logger = loggerFactory(import.meta);
7
7
 
8
8
  class UnderpostDB {
9
9
  static API = {
10
- async callback(deployList = 'default', options = { import: false, export: false }) {
10
+ async callback(deployList = 'default', options = { import: false, export: false, podName: false, ns: false }) {
11
11
  const newBackupTimestamp = new Date().getTime();
12
- const nameSpace = 'default';
12
+ const nameSpace = options.ns && typeof options.ns === 'string' ? options.ns : 'default';
13
13
  for (const _deployId of deployList.split(',')) {
14
14
  const deployId = _deployId.trim();
15
15
  if (!deployId) continue;
@@ -74,7 +74,8 @@ class UnderpostDB {
74
74
 
75
75
  switch (provider) {
76
76
  case 'mariadb': {
77
- const podName = `mariadb-statefulset-0`;
77
+ const podName =
78
+ options.podName && typeof options.podName === 'string' ? options.podName : `mariadb-statefulset-0`;
78
79
  const serviceName = 'mariadb';
79
80
  if (options.import === true) {
80
81
  shellExec(`sudo kubectl cp ${_toSqlPath} ${nameSpace}/${podName}:/${dbName}.sql`);
@@ -95,7 +96,8 @@ class UnderpostDB {
95
96
 
96
97
  case 'mongoose': {
97
98
  if (options.import === true) {
98
- const podName = `mongodb-0`;
99
+ const podName =
100
+ options.podName && typeof options.podName === 'string' ? options.podName : `mongodb-0`;
99
101
  shellExec(`sudo kubectl cp ${_toBsonPath} ${nameSpace}/${podName}:/${dbName}`);
100
102
  const cmd = `mongorestore -d ${dbName} /${dbName}`;
101
103
  shellExec(`sudo kubectl exec -i ${podName} -- sh -c "${cmd}"`);
package/src/cli/fs.js ADDED
@@ -0,0 +1,134 @@
1
+ import { v2 as cloudinary } from 'cloudinary';
2
+ import { loggerFactory } from '../server/logger.js';
3
+ import dotenv from 'dotenv';
4
+ import AdmZip from 'adm-zip';
5
+ import * as dir from 'path';
6
+ import fs from 'fs-extra';
7
+ import { Downloader } from '../server/downloader.js';
8
+ import UnderpostRepository from './repository.js';
9
+ import { shellExec } from '../server/process.js';
10
+ dotenv.config();
11
+
12
+ const logger = loggerFactory(import.meta);
13
+
14
+ class UnderpostFileStorage {
15
+ static API = {
16
+ cloudinaryConfig() {
17
+ // https://console.cloudinary.com/
18
+ cloudinary.config({
19
+ cloud_name: process.env.CLOUDINARY_CLOUD_NAME,
20
+ api_key: process.env.CLOUDINARY_API_KEY,
21
+ api_secret: process.env.CLOUDINARY_API_SECRET,
22
+ });
23
+ },
24
+ getStorageConf(options) {
25
+ let storage, storageConf;
26
+ if (options.deployId && typeof options.deployId === 'string') {
27
+ storageConf = `./engine-private/conf/${options.deployId}/storage.json`;
28
+ if (!fs.existsSync(storageConf)) fs.writeFileSync(storageConf, JSON.stringify({}), 'utf8');
29
+ storage = JSON.parse(fs.readFileSync(storageConf, 'utf8'));
30
+ }
31
+ return { storage, storageConf };
32
+ },
33
+ writeStorageConf(storage, storageConf) {
34
+ if (storage) fs.writeFileSync(storageConf, JSON.stringify(storage, null, 4), 'utf8');
35
+ },
36
+ async recursiveCallback(
37
+ path,
38
+ options = { rm: false, recursive: false, deployId: '', force: false, pull: false, git: false },
39
+ ) {
40
+ const { storage, storageConf } = UnderpostFileStorage.API.getStorageConf(options);
41
+ const files =
42
+ options.git === true
43
+ ? UnderpostRepository.API.getChangedFiles(path)
44
+ : await fs.readdir(path, { recursive: true });
45
+ for (const relativePath of files) {
46
+ const _path = path + '/' + relativePath;
47
+ if (fs.statSync(_path).isDirectory()) {
48
+ if (options.pull === true && !fs.existsSync(_path)) fs.mkdirSync(_path, { recursive: true });
49
+ continue;
50
+ }
51
+ if (options.pull === true) {
52
+ await UnderpostFileStorage.API.pull(_path, options);
53
+ } else if (!(_path in storage) || options.force === true) {
54
+ await UnderpostFileStorage.API.upload(_path, options);
55
+ if (storage) storage[_path] = {};
56
+ } else logger.warn('File already exists', _path);
57
+ }
58
+ UnderpostFileStorage.API.writeStorageConf(storage, storageConf);
59
+ if (options.git === true) {
60
+ shellExec(`cd ${path} && git add .`);
61
+ shellExec(`underpost cmt ${path} feat`);
62
+ }
63
+ },
64
+ async callback(
65
+ path,
66
+ options = { rm: false, recursive: false, deployId: '', force: false, pull: false, git: false },
67
+ ) {
68
+ if (options.recursive === true || options.git === true)
69
+ return await UnderpostFileStorage.API.recursiveCallback(path, options);
70
+ if (options.pull === true) return await UnderpostFileStorage.API.pull(path, options);
71
+ if (options.rm === true) return await UnderpostFileStorage.API.delete(path, options);
72
+ return await UnderpostFileStorage.API.upload(path, options);
73
+ },
74
+ async upload(path, options = { rm: false, recursive: false, deployId: '', force: false, pull: false }) {
75
+ UnderpostFileStorage.API.cloudinaryConfig();
76
+ const { storage, storageConf } = UnderpostFileStorage.API.getStorageConf(options);
77
+ // path = UnderpostFileStorage.API.file2Zip(path);
78
+ const uploadResult = await cloudinary.uploader
79
+ .upload(path, {
80
+ public_id: path,
81
+ resource_type: 'raw',
82
+ overwrite: options.force === true ? true : false,
83
+ })
84
+ .catch((error) => {
85
+ logger.error(error, { path, stack: error.stack });
86
+ });
87
+ logger.info('upload result', uploadResult);
88
+ if (storage) storage[path] = {};
89
+ UnderpostFileStorage.API.writeStorageConf(storage, storageConf);
90
+ return uploadResult;
91
+ },
92
+ async pull(path) {
93
+ UnderpostFileStorage.API.cloudinaryConfig();
94
+ const downloadResult = await cloudinary.utils.download_archive_url({
95
+ public_ids: [path],
96
+ resource_type: 'raw',
97
+ });
98
+ logger.info('download result', downloadResult);
99
+ await Downloader(downloadResult, path + '.zip');
100
+ path = UnderpostFileStorage.API.zip2File(path + '.zip');
101
+ fs.removeSync(path + '.zip');
102
+ },
103
+ async delete(path) {
104
+ UnderpostFileStorage.API.cloudinaryConfig();
105
+ const deleteResult = await cloudinary.api
106
+ .delete_resources([path], { type: 'upload', resource_type: 'raw' })
107
+ .catch((error) => {
108
+ logger.error(error, { path, stack: error.stack });
109
+ });
110
+ logger.info('delete result', deleteResult);
111
+ return deleteResult;
112
+ },
113
+ file2Zip(path) {
114
+ const zip = new AdmZip();
115
+ zip.addLocalFile(path, '/');
116
+ path = path + '.zip';
117
+ zip.writeZip(path);
118
+ return path;
119
+ },
120
+ zip2File(path) {
121
+ const zip = new AdmZip(path);
122
+ path = path.replaceAll('.zip', '');
123
+ zip.extractEntryTo(
124
+ /*entry name*/ path.split('/').pop(),
125
+ /*target path*/ dir.dirname(path),
126
+ /*maintainEntryPath*/ false,
127
+ /*overwrite*/ true,
128
+ );
129
+ return path;
130
+ },
131
+ };
132
+ }
133
+
134
+ export default UnderpostFileStorage;
@@ -98,6 +98,19 @@ class UnderpostRepository {
98
98
  }
99
99
  });
100
100
  },
101
+
102
+ getChangedFiles(path = '.', extension = '', head = false) {
103
+ const extensionFilter = extension ? `-- '***.${extension}'` : '';
104
+ const command = `cd ${path} && git diff ${head ? 'HEAD^ HEAD ' : ''}--name-only ${extensionFilter}`;
105
+ const commandUntrack = `cd ${path} && git ls-files --others --exclude-standard`;
106
+ const diffOutput = shellExec(command, { stdout: true, silent: true });
107
+ const diffUntrackOutput = shellExec(commandUntrack, { stdout: true, silent: true });
108
+ return diffOutput
109
+ .toString()
110
+ .split('\n')
111
+ .filter(Boolean)
112
+ .concat(diffUntrackOutput.toString().split('\n').filter(Boolean));
113
+ },
101
114
  };
102
115
  }
103
116
 
package/src/cli/test.js CHANGED
@@ -36,7 +36,7 @@ class UnderpostTest {
36
36
  options.podStatus &&
37
37
  typeof options.podStatus === 'string'
38
38
  )
39
- return await UnderpostTest.API.podStatusMonitor(options.podName, options.podStatus);
39
+ return await UnderpostTest.API.statusMonitor(options.podName, options.podStatus, options.kindType);
40
40
 
41
41
  if (options.sh === true || options.logs === true) {
42
42
  const [pod] = UnderpostDeploy.API.get(deployList);
@@ -86,16 +86,27 @@ class UnderpostTest {
86
86
  }
87
87
  } else return UnderpostTest.API.run();
88
88
  },
89
- podStatusMonitor(podName, status = 'Running', deltaMs = 1000) {
89
+ statusMonitor(podName, status = 'Running', kindType = '', deltaMs = 1000, maxAttempts = 60 * 5) {
90
+ if (!(kindType && typeof kindType === 'string')) kindType = 'pods';
90
91
  return new Promise(async (resolve) => {
91
92
  let index = 0;
92
- logger.info(`Loading ${podName} instance`, { status, deltaMs });
93
+ logger.info(`Loading instance`, { podName, status, kindType, deltaMs, maxAttempts });
93
94
  const _monitor = async () => {
94
95
  await timer(deltaMs);
95
- const result = UnderpostDeploy.API.get(podName).find((p) => p.STATUS === status);
96
- logger.info(`Testing pod ${podName}... ${result ? 1 : 0}/1 - elapsed time ${deltaMs * (index + 1)}ms`);
97
- if (result) return resolve();
96
+ const pods = UnderpostDeploy.API.get(podName, kindType);
97
+ const result = pods.find((p) => p.STATUS === status);
98
+ logger.info(
99
+ `Testing pod ${podName}... ${result ? 1 : 0}/1 - elapsed time ${deltaMs * (index + 1)}s - attempt ${
100
+ index + 1
101
+ }/${maxAttempts}`,
102
+ pods[0] ? pods[0].STATUS : 'Not found kind object',
103
+ );
104
+ if (result) return resolve(true);
98
105
  index++;
106
+ if (index === maxAttempts) {
107
+ logger.error(`Failed to test pod ${podName} within ${maxAttempts} attempts`);
108
+ return resolve(false);
109
+ }
99
110
  return _monitor();
100
111
  };
101
112
  await _monitor();
package/src/index.js CHANGED
@@ -9,6 +9,7 @@ import UnderpostCron from './cli/cron.js';
9
9
  import UnderpostDB from './cli/db.js';
10
10
  import UnderpostDeploy from './cli/deploy.js';
11
11
  import UnderpostRootEnv from './cli/env.js';
12
+ import UnderpostFileStorage from './cli/fs.js';
12
13
  import UnderpostImage from './cli/image.js';
13
14
  import UnderpostRepository from './cli/repository.js';
14
15
  import UnderpostScript from './cli/script.js';
@@ -27,7 +28,7 @@ class Underpost {
27
28
  * @type {String}
28
29
  * @memberof Underpost
29
30
  */
30
- static version = 'v2.8.55';
31
+ static version = 'v2.8.56';
31
32
  /**
32
33
  * Repository cli API
33
34
  * @static
@@ -98,6 +99,13 @@ class Underpost {
98
99
  * @memberof Underpost
99
100
  */
100
101
  static cron = UnderpostCron.API;
102
+ /**
103
+ * File Storage cli API
104
+ * @static
105
+ * @type {UnderpostFileStorage.API}
106
+ * @memberof UnderpostFileStorage
107
+ */
108
+ static fs = UnderpostFileStorage.API;
101
109
  }
102
110
 
103
111
  const up = Underpost;
@@ -2,6 +2,7 @@
2
2
 
3
3
  import fs from 'fs-extra';
4
4
  import vm from 'node:vm';
5
+ import Underpost from '../index.js';
5
6
 
6
7
  const srcFormatted = (src) =>
7
8
  src
@@ -49,7 +50,7 @@ const viewFormatted = (src, dists, proxyPath, baseHost = '') => {
49
50
  };
50
51
 
51
52
  const ssrFactory = async (componentPath = `./src/client/ssr/Render.js`) => {
52
- const context = { SrrComponent: () => {}, npm_package_version: process.env.npm_package_version };
53
+ const context = { SrrComponent: () => {}, npm_package_version: Underpost.version };
53
54
  vm.createContext(context);
54
55
  vm.runInContext(await srcFormatted(fs.readFileSync(componentPath, 'utf8')), context);
55
56
  return context.SrrComponent;
package/src/server/dns.js CHANGED
@@ -12,8 +12,8 @@ const logger = loggerFactory(import.meta);
12
12
 
13
13
  class Dns {
14
14
  static callback = async function (deployList) {
15
- // NAT-VPS modem/router device configuration:
16
- // LAN --> [NAT-VPS] --> WAN
15
+ // Network topology configuration:
16
+ // LAN -> [NAT-VPS](modem/router device) -> WAN
17
17
  // enabled DMZ Host to proxy IP 80-443 (79-444) sometimes router block first port
18
18
  // disabled local red DHCP
19
19
  // verify inet ip proxy server address