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.
- package/.github/workflows/ghpkg.yml +1 -1
- package/.github/workflows/npmpkg.yml +1 -1
- package/.github/workflows/pwa-microservices-template.page.yml +1 -1
- package/bin/build.js +9 -0
- package/bin/index.js +17 -0
- package/docker-compose.yml +1 -1
- package/manifests/kind-config-dev.yaml +12 -0
- package/manifests/mongodb-4.4/service-deployment.yaml +4 -4
- package/package.json +8 -7
- package/src/cli/cluster.js +56 -30
- package/src/cli/db.js +6 -4
- package/src/cli/fs.js +134 -0
- package/src/cli/repository.js +13 -0
- package/src/cli/test.js +17 -6
- package/src/index.js +9 -1
- package/src/server/client-formatted.js +2 -1
- package/src/server/dns.js +2 -2
|
@@ -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
|
-
|
|
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
|
|
|
@@ -43,7 +43,7 @@ jobs:
|
|
|
43
43
|
- name: Build the site
|
|
44
44
|
run: |
|
|
45
45
|
npm install -g underpost
|
|
46
|
-
|
|
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();
|
package/docker-compose.yml
CHANGED
|
@@ -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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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.
|
|
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",
|
package/src/cli/cluster.js
CHANGED
|
@@ -12,7 +12,8 @@ class UnderpostCluster {
|
|
|
12
12
|
async init(
|
|
13
13
|
podName,
|
|
14
14
|
options = {
|
|
15
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
131
|
+
const successInstance = await UnderpostTest.API.statusMonitor('mongodb-1');
|
|
108
132
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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 =
|
|
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 =
|
|
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;
|
package/src/cli/repository.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
|
93
|
+
logger.info(`Loading instance`, { podName, status, kindType, deltaMs, maxAttempts });
|
|
93
94
|
const _monitor = async () => {
|
|
94
95
|
await timer(deltaMs);
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
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.
|
|
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:
|
|
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
|
-
//
|
|
16
|
-
// LAN
|
|
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
|