underpost 2.8.652 → 2.8.782
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/.vscode/extensions.json +37 -2
- package/.vscode/settings.json +2 -0
- package/CHANGELOG.md +24 -4
- package/README.md +5 -4
- package/bin/deploy.js +1455 -144
- package/cli.md +56 -14
- package/docker-compose.yml +1 -1
- package/manifests/deployment/adminer/deployment.yaml +32 -0
- package/manifests/deployment/adminer/kustomization.yaml +7 -0
- package/manifests/deployment/adminer/service.yaml +13 -0
- package/manifests/deployment/fastapi/backend-deployment.yml +120 -0
- package/manifests/deployment/fastapi/backend-service.yml +19 -0
- package/manifests/deployment/fastapi/frontend-deployment.yml +54 -0
- package/manifests/deployment/fastapi/frontend-service.yml +15 -0
- package/manifests/deployment/fastapi/initial_data.sh +56 -0
- package/manifests/deployment/kafka/deployment.yaml +69 -0
- package/manifests/deployment/spark/spark-pi-py.yaml +21 -0
- package/manifests/envoy-service-nodeport.yaml +23 -0
- package/manifests/kubeadm-calico-config.yaml +119 -0
- package/manifests/kubelet-config.yaml +65 -0
- package/manifests/mongodb/kustomization.yaml +1 -1
- package/manifests/mongodb/statefulset.yaml +12 -11
- package/manifests/mongodb/storage-class.yaml +9 -0
- package/manifests/mongodb-4.4/service-deployment.yaml +1 -1
- package/manifests/mysql/kustomization.yaml +7 -0
- package/manifests/mysql/pv-pvc.yaml +27 -0
- package/manifests/mysql/statefulset.yaml +55 -0
- package/manifests/postgresql/configmap.yaml +9 -0
- package/manifests/postgresql/kustomization.yaml +10 -0
- package/manifests/postgresql/pv.yaml +15 -0
- package/manifests/postgresql/pvc.yaml +13 -0
- package/manifests/postgresql/service.yaml +10 -0
- package/manifests/postgresql/statefulset.yaml +37 -0
- package/manifests/valkey/statefulset.yaml +4 -3
- package/package.json +2 -1
- package/src/cli/cluster.js +281 -27
- package/src/cli/deploy.js +81 -15
- package/src/cli/fs.js +14 -3
- package/src/cli/image.js +34 -7
- package/src/cli/index.js +37 -2
- package/src/cli/lxd.js +19 -0
- package/src/cli/monitor.js +75 -30
- package/src/cli/repository.js +9 -6
- package/src/client/components/core/JoyStick.js +2 -2
- package/src/client/components/core/Modal.js +1 -0
- package/src/index.js +1 -1
- package/src/runtime/lampp/Dockerfile +1 -1
- package/src/server/conf.js +5 -1
- package/src/server/dns.js +47 -17
- package/src/server/runtime.js +2 -0
- package/src/server/start.js +0 -1
package/src/cli/image.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
|
-
import { shellCd, shellExec } from '../server/process.js';
|
|
3
2
|
import dotenv from 'dotenv';
|
|
4
|
-
import { awaitDeployMonitor, getNpmRootPath } from '../server/conf.js';
|
|
5
3
|
import { loggerFactory } from '../server/logger.js';
|
|
6
|
-
import
|
|
4
|
+
import Underpost from '../index.js';
|
|
5
|
+
import { getUnderpostRootPath } from '../server/conf.js';
|
|
6
|
+
import { shellExec } from '../server/process.js';
|
|
7
7
|
|
|
8
8
|
dotenv.config();
|
|
9
9
|
|
|
@@ -12,8 +12,23 @@ const logger = loggerFactory(import.meta);
|
|
|
12
12
|
class UnderpostImage {
|
|
13
13
|
static API = {
|
|
14
14
|
dockerfile: {
|
|
15
|
-
pullBaseImages(
|
|
15
|
+
pullBaseImages(
|
|
16
|
+
options = {
|
|
17
|
+
kindLoad: false,
|
|
18
|
+
kubeadmLoad: false,
|
|
19
|
+
path: false,
|
|
20
|
+
version: '',
|
|
21
|
+
},
|
|
22
|
+
) {
|
|
16
23
|
shellExec(`sudo podman pull docker.io/library/debian:buster`);
|
|
24
|
+
const IMAGE_NAME = `debian-underpost`;
|
|
25
|
+
const IMAGE_NAME_FULL = `${IMAGE_NAME}:${options.version ?? Underpost.version}`;
|
|
26
|
+
const LOAD_TYPE = options.kindLoad === true ? `--kin-load` : `--kubeadm-load`;
|
|
27
|
+
shellExec(
|
|
28
|
+
`underpost dockerfile-image-build --podman-save --no-cache --image-path=. --path ${
|
|
29
|
+
options.path ?? getUnderpostRootPath()
|
|
30
|
+
} --image-name=${IMAGE_NAME_FULL} ${LOAD_TYPE}`,
|
|
31
|
+
);
|
|
17
32
|
},
|
|
18
33
|
build(
|
|
19
34
|
options = {
|
|
@@ -23,13 +38,24 @@ class UnderpostImage {
|
|
|
23
38
|
dockerfileName: '',
|
|
24
39
|
podmanSave: false,
|
|
25
40
|
kindLoad: false,
|
|
41
|
+
kubeadmLoad: false,
|
|
26
42
|
secrets: false,
|
|
27
43
|
secretsPath: '',
|
|
28
44
|
noCache: false,
|
|
29
45
|
},
|
|
30
46
|
) {
|
|
31
|
-
const {
|
|
32
|
-
|
|
47
|
+
const {
|
|
48
|
+
path,
|
|
49
|
+
imageName,
|
|
50
|
+
imagePath,
|
|
51
|
+
dockerfileName,
|
|
52
|
+
podmanSave,
|
|
53
|
+
secrets,
|
|
54
|
+
secretsPath,
|
|
55
|
+
kindLoad,
|
|
56
|
+
noCache,
|
|
57
|
+
kubeadmLoad,
|
|
58
|
+
} = options;
|
|
33
59
|
const podManImg = `localhost/${imageName}`;
|
|
34
60
|
if (imagePath && typeof imagePath === 'string' && !fs.existsSync(imagePath))
|
|
35
61
|
fs.mkdirSync(imagePath, { recursive: true });
|
|
@@ -54,11 +80,12 @@ class UnderpostImage {
|
|
|
54
80
|
shellExec(
|
|
55
81
|
`cd ${path}${secretsInput}&& sudo podman build -f ./${
|
|
56
82
|
dockerfileName && typeof dockerfileName === 'string' ? dockerfileName : 'Dockerfile'
|
|
57
|
-
} -t ${imageName} --pull=never --cap-add=CAP_AUDIT_WRITE${cache}${secretDockerInput}`,
|
|
83
|
+
} -t ${imageName} --pull=never --cap-add=CAP_AUDIT_WRITE${cache}${secretDockerInput} --network host`,
|
|
58
84
|
);
|
|
59
85
|
|
|
60
86
|
if (podmanSave === true) shellExec(`podman save -o ${tarFile} ${podManImg}`);
|
|
61
87
|
if (kindLoad === true) shellExec(`sudo kind load image-archive ${tarFile}`);
|
|
88
|
+
if (kubeadmLoad === true) shellExec(`sudo ctr -n k8s.io images import ${tarFile}`);
|
|
62
89
|
},
|
|
63
90
|
},
|
|
64
91
|
};
|
package/src/cli/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import dotenv from 'dotenv';
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import Underpost from '../index.js';
|
|
4
|
-
import { getUnderpostRootPath, loadConf } from '../server/conf.js';
|
|
4
|
+
import { getNpmRootPath, getUnderpostRootPath, loadConf } from '../server/conf.js';
|
|
5
5
|
import fs from 'fs-extra';
|
|
6
6
|
import { commitData } from '../client/components/core/CommonJs.js';
|
|
7
7
|
import { shellExec } from '../server/process.js';
|
|
8
|
+
import UnderpostLxd from './lxd.js';
|
|
8
9
|
|
|
9
10
|
const underpostRootPath = getUnderpostRootPath();
|
|
10
11
|
fs.existsSync(`${underpostRootPath}/.env`)
|
|
@@ -34,6 +35,7 @@ program
|
|
|
34
35
|
.command('clone')
|
|
35
36
|
.argument(`<uri>`, 'e.g. username/repository')
|
|
36
37
|
.option('--bare', 'Clone only .git files')
|
|
38
|
+
.option('-g8', 'Use g8 repo extension')
|
|
37
39
|
.description('Clone github repository')
|
|
38
40
|
.action(Underpost.repo.clone);
|
|
39
41
|
|
|
@@ -42,6 +44,7 @@ program
|
|
|
42
44
|
.argument('<path>', 'Absolute or relative directory')
|
|
43
45
|
.argument(`<uri>`, 'e.g. username/repository')
|
|
44
46
|
.description('Pull github repository')
|
|
47
|
+
.option('-g8', 'Use g8 repo extension')
|
|
45
48
|
.action(Underpost.repo.pull);
|
|
46
49
|
|
|
47
50
|
program
|
|
@@ -61,6 +64,7 @@ program
|
|
|
61
64
|
.argument('<path>', 'Absolute or relative directory')
|
|
62
65
|
.argument(`<uri>`, 'e.g. username/repository')
|
|
63
66
|
.option('-f', 'Force push overwriting repository')
|
|
67
|
+
.option('-g8', 'Use g8 repo extension')
|
|
64
68
|
.description('Push github repository')
|
|
65
69
|
.action(Underpost.repo.push);
|
|
66
70
|
|
|
@@ -89,24 +93,31 @@ program
|
|
|
89
93
|
.argument('[pod-name]', 'Optional pod name filter')
|
|
90
94
|
.option('--reset', `Delete all clusters and prune all data and caches`)
|
|
91
95
|
.option('--mariadb', 'Init with mariadb statefulset')
|
|
96
|
+
.option('--mysql', 'Init with mysql statefulset')
|
|
92
97
|
.option('--mongodb', 'Init with mongodb statefulset')
|
|
98
|
+
.option('--postgresql', 'Init with postgresql statefulset')
|
|
93
99
|
.option('--mongodb4', 'Init with mongodb 4.4 service')
|
|
100
|
+
// .option('--istio', 'Init base istio service mesh')
|
|
94
101
|
.option('--valkey', 'Init with valkey service')
|
|
95
102
|
.option('--contour', 'Init with project contour base HTTPProxy and envoy')
|
|
96
103
|
.option('--cert-manager', 'Init with letsencrypt-prod ClusterIssuer')
|
|
104
|
+
.option('--dedicated-gpu', 'Init with dedicated gpu base resources env')
|
|
97
105
|
.option('--info', 'Get all kinds objects deployed')
|
|
98
106
|
.option('--full', 'Init with all statefulsets and services available')
|
|
99
107
|
.option('--ns-use <ns-name>', 'Switches current context to namespace')
|
|
108
|
+
.option('--kubeadm', 'Init with kubeadm controlplane management')
|
|
100
109
|
.option('--dev', 'init with dev cluster')
|
|
101
110
|
.option('--list-pods', 'Display list pods information')
|
|
102
111
|
.option('--info-capacity', 'display current total machine capacity info')
|
|
103
112
|
.option('--info-capacity-pod', 'display current machine capacity pod info')
|
|
113
|
+
.option('--pull-image', 'Set optional pull associated image')
|
|
114
|
+
.option('--init-host', 'Install k8s node necessary cli env: kind, kubeadm, docker, podman, helm')
|
|
104
115
|
.action(Underpost.cluster.init)
|
|
105
116
|
.description('Manage cluster, for default initialization base kind cluster');
|
|
106
117
|
|
|
107
118
|
program
|
|
108
119
|
.command('deploy')
|
|
109
|
-
.argument('
|
|
120
|
+
.argument('[deploy-list]', 'Deploy id list, e.g. default-a,default-b')
|
|
110
121
|
.argument('[env]', 'Optional environment, for default is development')
|
|
111
122
|
.option('--remove', 'Delete deployments and services')
|
|
112
123
|
.option('--sync', 'Sync deployments env, ports, and replicas')
|
|
@@ -119,6 +130,14 @@ program
|
|
|
119
130
|
.option('--replicas <replicas>', 'Set custom number of replicas')
|
|
120
131
|
.option('--versions <deployment-versions>', 'Comma separated custom deployment versions')
|
|
121
132
|
.option('--traffic <traffic-versions>', 'Comma separated custom deployment traffic')
|
|
133
|
+
.option('--disable-update-deployment', 'Disable update deployments')
|
|
134
|
+
.option('--info-traffic', 'get traffic conf form current resources deployments')
|
|
135
|
+
.option('--kubeadm', 'Enable kubeadm context')
|
|
136
|
+
.option('--restore-hosts', 'Restore defautl etc hosts')
|
|
137
|
+
.option(
|
|
138
|
+
'--rebuild-clients-bundle',
|
|
139
|
+
'Inside container, rebuild clients bundle, only static public or storage client files',
|
|
140
|
+
)
|
|
122
141
|
.description('Manage deployment, for default deploy development pods')
|
|
123
142
|
.action(Underpost.deploy.callback);
|
|
124
143
|
|
|
@@ -145,6 +164,7 @@ program
|
|
|
145
164
|
.option('--dockerfile-name [dockerfile-name]', 'set Dockerfile name')
|
|
146
165
|
.option('--podman-save', 'Export tar file from podman')
|
|
147
166
|
.option('--kind-load', 'Import tar image to Kind cluster')
|
|
167
|
+
.option('--kubeadm-load', 'Import tar image to Kubeadm cluster')
|
|
148
168
|
.option('--secrets', 'Dockerfile env secrets')
|
|
149
169
|
.option('--secrets-path [secrets-path]', 'Dockerfile custom path env secrets')
|
|
150
170
|
.option('--no-cache', 'Build without using cache')
|
|
@@ -153,6 +173,10 @@ program
|
|
|
153
173
|
|
|
154
174
|
program
|
|
155
175
|
.command('dockerfile-pull-base-images')
|
|
176
|
+
.option('--path [path]', 'Dockerfile path')
|
|
177
|
+
.option('--kind-load', 'Import tar image to Kind cluster')
|
|
178
|
+
.option('--kubeadm-load', 'Import tar image to Kubeadm cluster')
|
|
179
|
+
.option('--version', 'Set custom version')
|
|
156
180
|
.description('Pull underpost dockerfile images requirements')
|
|
157
181
|
.action(Underpost.image.dockerfile.pullBaseImages);
|
|
158
182
|
|
|
@@ -214,6 +238,7 @@ program
|
|
|
214
238
|
.option('--deploy-id <deploy-id>', 'Deploy configuration id')
|
|
215
239
|
.option('--pull', 'Download file')
|
|
216
240
|
.option('--force', 'Force action')
|
|
241
|
+
.option('--storage-file-path <storage-file-path>', 'custom file storage path')
|
|
217
242
|
.description('File storage management, for default upload file')
|
|
218
243
|
.action(Underpost.fs.callback);
|
|
219
244
|
|
|
@@ -236,10 +261,20 @@ program
|
|
|
236
261
|
.option('--ms-interval <ms-interval>', 'Custom ms interval delta time')
|
|
237
262
|
.option('--now', 'Exec immediately monitor script')
|
|
238
263
|
.option('--single', 'Disable recurrence')
|
|
264
|
+
.option('--replicas <replicas>', 'Set custom number of replicas')
|
|
239
265
|
.option('--type <type>', 'Set custom monitor type')
|
|
266
|
+
.option('--sync', 'Sync with current proxy deployments proxy traffic')
|
|
240
267
|
.description('Monitor health server management')
|
|
241
268
|
.action(Underpost.monitor.callback);
|
|
242
269
|
|
|
270
|
+
program
|
|
271
|
+
.command('lxd')
|
|
272
|
+
.option('--init', 'Init lxd')
|
|
273
|
+
.option('--reset', 'Reset lxd on current machine')
|
|
274
|
+
.option('--install', 'Install lxd on current machine')
|
|
275
|
+
.description('Lxd management')
|
|
276
|
+
.action(UnderpostLxd.API.callback);
|
|
277
|
+
|
|
243
278
|
const buildCliDoc = () => {
|
|
244
279
|
let md = shellExec(`node bin help`, { silent: true, stdout: true }).split('Options:');
|
|
245
280
|
const baseOptions =
|
package/src/cli/lxd.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { shellExec } from '../server/process.js';
|
|
2
|
+
|
|
3
|
+
class UnderpostLxd {
|
|
4
|
+
static API = {
|
|
5
|
+
async callback(options = { init: false, reset: false, install: false }) {
|
|
6
|
+
if (options.reset === true) {
|
|
7
|
+
shellExec(`sudo systemctl stop snap.lxd.daemon`);
|
|
8
|
+
shellExec(`sudo snap remove lxd --purge`);
|
|
9
|
+
}
|
|
10
|
+
if (options.install === true) shellExec(`sudo snap install lxd`);
|
|
11
|
+
if (options.init === true) {
|
|
12
|
+
shellExec(`sudo systemctl start snap.lxd.daemon`);
|
|
13
|
+
shellExec(`sudo systemctl status snap.lxd.daemon`);
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default UnderpostLxd;
|
package/src/cli/monitor.js
CHANGED
|
@@ -5,6 +5,7 @@ import axios from 'axios';
|
|
|
5
5
|
import UnderpostRootEnv from './env.js';
|
|
6
6
|
import fs from 'fs-extra';
|
|
7
7
|
import { shellExec } from '../server/process.js';
|
|
8
|
+
import { isInternetConnection } from '../server/dns.js';
|
|
8
9
|
|
|
9
10
|
const logger = loggerFactory(import.meta);
|
|
10
11
|
|
|
@@ -13,7 +14,7 @@ class UnderpostMonitor {
|
|
|
13
14
|
async callback(
|
|
14
15
|
deployId,
|
|
15
16
|
env = 'development',
|
|
16
|
-
options = { now: false, single: false, msInterval: '', type: '' },
|
|
17
|
+
options = { now: false, single: false, msInterval: '', type: '', replicas: '', sync: false },
|
|
17
18
|
commanderOptions,
|
|
18
19
|
auxRouter,
|
|
19
20
|
) {
|
|
@@ -38,15 +39,43 @@ class UnderpostMonitor {
|
|
|
38
39
|
|
|
39
40
|
const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
|
|
40
41
|
|
|
41
|
-
logger.info(`${deployId} ${env}`, pathPortAssignmentData);
|
|
42
|
-
|
|
43
42
|
let errorPayloads = [];
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
if (options.sync === true) {
|
|
44
|
+
const currentTraffic = UnderpostDeploy.API.getCurrentTraffic(deployId);
|
|
45
|
+
if (currentTraffic) UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, currentTraffic);
|
|
46
|
+
}
|
|
47
|
+
let traffic = UnderpostRootEnv.API.get(`${deployId}-${env}-traffic`) ?? 'blue';
|
|
48
|
+
const maxAttempts = parseInt(
|
|
49
|
+
Object.keys(pathPortAssignmentData)
|
|
50
|
+
.map((host) => pathPortAssignmentData[host].length)
|
|
51
|
+
.reduce((accumulator, value) => accumulator + value, 0) * 2.5,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
logger.info(`Init deploy monitor`, {
|
|
55
|
+
pathPortAssignmentData,
|
|
56
|
+
maxAttempts,
|
|
57
|
+
deployId,
|
|
58
|
+
env,
|
|
59
|
+
traffic,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const switchTraffic = () => {
|
|
63
|
+
if (traffic === 'blue') traffic = 'green';
|
|
64
|
+
else traffic = 'blue';
|
|
65
|
+
UnderpostRootEnv.API.set(`${deployId}-${env}-traffic`, traffic);
|
|
66
|
+
shellExec(
|
|
67
|
+
`node bin deploy --info-router --build-manifest --traffic ${traffic} --replicas ${
|
|
68
|
+
options.replicas ? options.replicas : 1
|
|
69
|
+
} ${deployId} ${env}`,
|
|
70
|
+
);
|
|
71
|
+
shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
|
|
72
|
+
};
|
|
48
73
|
|
|
49
74
|
const monitor = async (reject) => {
|
|
75
|
+
if (UnderpostRootEnv.API.get(`monitor-init-callback-script`))
|
|
76
|
+
shellExec(UnderpostRootEnv.API.get(`monitor-init-callback-script`));
|
|
77
|
+
const currentTimestamp = new Date().getTime();
|
|
78
|
+
errorPayloads = errorPayloads.filter((e) => currentTimestamp - e.timestamp < 60 * 1000 * 5);
|
|
50
79
|
logger.info(`[${deployId}-${env}] Check server health`);
|
|
51
80
|
for (const host of Object.keys(pathPortAssignmentData)) {
|
|
52
81
|
for (const instance of pathPortAssignmentData[host]) {
|
|
@@ -74,6 +103,7 @@ class UnderpostMonitor {
|
|
|
74
103
|
status: error.status,
|
|
75
104
|
code: error.code,
|
|
76
105
|
errors: error.errors,
|
|
106
|
+
timestamp: new Date().getTime(),
|
|
77
107
|
};
|
|
78
108
|
if (errorPayload.status !== 404) {
|
|
79
109
|
errorPayloads.push(errorPayload);
|
|
@@ -90,19 +120,17 @@ class UnderpostMonitor {
|
|
|
90
120
|
fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'),
|
|
91
121
|
);
|
|
92
122
|
|
|
123
|
+
shellExec(`kubectl delete configmap underpost-config`);
|
|
124
|
+
shellExec(
|
|
125
|
+
`kubectl create configmap underpost-config --from-file=/home/dd/engine/engine-private/conf/dd-cron/.env.${env}`,
|
|
126
|
+
);
|
|
127
|
+
|
|
93
128
|
for (const host of Object.keys(confServer)) {
|
|
94
129
|
shellExec(`sudo kubectl delete HTTPProxy ${host}`);
|
|
95
130
|
}
|
|
96
131
|
shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${traffic}`);
|
|
97
132
|
|
|
98
|
-
|
|
99
|
-
else traffic = 'blue';
|
|
100
|
-
|
|
101
|
-
shellExec(
|
|
102
|
-
`node bin deploy --info-router --build-manifest --traffic ${traffic} ${deployId} ${env}`,
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
shellExec(`sudo kubectl apply -f ./engine-private/conf/${deployId}/build/${env}/proxy.yaml`);
|
|
133
|
+
switchTraffic();
|
|
106
134
|
}
|
|
107
135
|
|
|
108
136
|
break;
|
|
@@ -116,7 +144,7 @@ class UnderpostMonitor {
|
|
|
116
144
|
}
|
|
117
145
|
errorPayloads = [];
|
|
118
146
|
}
|
|
119
|
-
logger.error(
|
|
147
|
+
logger.error(`Error accumulator ${deployId}-${env}-${traffic}`, errorPayloads.length);
|
|
120
148
|
}
|
|
121
149
|
});
|
|
122
150
|
}
|
|
@@ -125,13 +153,19 @@ class UnderpostMonitor {
|
|
|
125
153
|
if (options.now === true) await monitor();
|
|
126
154
|
if (options.single === true) return;
|
|
127
155
|
let optionsMsTimeout = parseInt(options.msInterval);
|
|
128
|
-
if (isNaN(optionsMsTimeout)) optionsMsTimeout =
|
|
156
|
+
if (isNaN(optionsMsTimeout)) optionsMsTimeout = 60250; // 60.25 seconds
|
|
129
157
|
let monitorTrafficName;
|
|
130
158
|
let monitorPodName;
|
|
131
159
|
const monitorCallBack = (resolve, reject) => {
|
|
132
160
|
const envMsTimeout = UnderpostRootEnv.API.get(`${deployId}-${env}-monitor-ms`);
|
|
133
161
|
setTimeout(
|
|
134
162
|
async () => {
|
|
163
|
+
const isOnline = await isInternetConnection();
|
|
164
|
+
if (!isOnline) {
|
|
165
|
+
logger.warn('No internet connection');
|
|
166
|
+
monitorCallBack(resolve, reject);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
135
169
|
switch (options.type) {
|
|
136
170
|
case 'blue-green':
|
|
137
171
|
{
|
|
@@ -166,19 +200,30 @@ class UnderpostMonitor {
|
|
|
166
200
|
default:
|
|
167
201
|
break;
|
|
168
202
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
203
|
+
for (const monitorStatus of [
|
|
204
|
+
{ key: `monitor-input`, value: UnderpostRootEnv.API.get(`monitor-input`) },
|
|
205
|
+
{
|
|
206
|
+
key: `${deployId}-${env}-monitor-input`,
|
|
207
|
+
value: UnderpostRootEnv.API.get(`${deployId}-${env}-monitor-input`),
|
|
208
|
+
},
|
|
209
|
+
])
|
|
210
|
+
switch (monitorStatus.value) {
|
|
211
|
+
case 'pause':
|
|
212
|
+
monitorCallBack(resolve, reject);
|
|
213
|
+
return;
|
|
214
|
+
case 'restart':
|
|
215
|
+
UnderpostRootEnv.API.delete(monitorStatus.key);
|
|
216
|
+
return reject();
|
|
217
|
+
case 'stop':
|
|
218
|
+
UnderpostRootEnv.API.delete(monitorStatus.key);
|
|
219
|
+
return resolve();
|
|
220
|
+
case 'blue-green-switch':
|
|
221
|
+
UnderpostRootEnv.API.delete(monitorStatus.key);
|
|
222
|
+
switchTraffic();
|
|
223
|
+
}
|
|
224
|
+
await monitor(reject);
|
|
225
|
+
monitorCallBack(resolve, reject);
|
|
226
|
+
return;
|
|
182
227
|
},
|
|
183
228
|
!isNaN(envMsTimeout) ? envMsTimeout : optionsMsTimeout,
|
|
184
229
|
);
|
package/src/cli/repository.js
CHANGED
|
@@ -12,23 +12,25 @@ const logger = loggerFactory(import.meta);
|
|
|
12
12
|
|
|
13
13
|
class UnderpostRepository {
|
|
14
14
|
static API = {
|
|
15
|
-
clone(gitUri = 'underpostnet/pwa-microservices-template', options = { bare: false }) {
|
|
15
|
+
clone(gitUri = 'underpostnet/pwa-microservices-template', options = { bare: false, g8: false }) {
|
|
16
|
+
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
16
17
|
const repoName = gitUri.split('/').pop();
|
|
17
18
|
if (fs.existsSync(`./${repoName}`)) fs.removeSync(`./${repoName}`);
|
|
18
19
|
shellExec(
|
|
19
20
|
`git clone ${options?.bare === true ? ` --bare ` : ''}https://${
|
|
20
21
|
process.env.GITHUB_TOKEN ? `${process.env.GITHUB_TOKEN}@` : ''
|
|
21
|
-
}github.com/${gitUri}
|
|
22
|
+
}github.com/${gitUri}${gExtension}`,
|
|
22
23
|
{
|
|
23
24
|
disableLog: true,
|
|
24
25
|
},
|
|
25
26
|
);
|
|
26
27
|
},
|
|
27
|
-
pull(repoPath = './', gitUri = 'underpostnet/pwa-microservices-template') {
|
|
28
|
+
pull(repoPath = './', gitUri = 'underpostnet/pwa-microservices-template', options = { g8: false }) {
|
|
29
|
+
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
28
30
|
shellExec(
|
|
29
31
|
`cd ${repoPath} && git pull https://${
|
|
30
32
|
process.env.GITHUB_TOKEN ? `${process.env.GITHUB_TOKEN}@` : ''
|
|
31
|
-
}github.com/${gitUri}
|
|
33
|
+
}github.com/${gitUri}${gExtension}`,
|
|
32
34
|
{
|
|
33
35
|
disableLog: true,
|
|
34
36
|
},
|
|
@@ -57,9 +59,10 @@ class UnderpostRepository {
|
|
|
57
59
|
shellExec(`cd ${repoPath} && git commit ${options?.empty ? `--allow-empty ` : ''}-m "${_message}"`);
|
|
58
60
|
},
|
|
59
61
|
|
|
60
|
-
push(repoPath = './', gitUri = 'underpostnet/pwa-microservices-template', options = { f: false }) {
|
|
62
|
+
push(repoPath = './', gitUri = 'underpostnet/pwa-microservices-template', options = { f: false, g8: false }) {
|
|
63
|
+
const gExtension = options.g8 === true ? '.g8' : '.git';
|
|
61
64
|
shellExec(
|
|
62
|
-
`cd ${repoPath} && git push https://${process.env.GITHUB_TOKEN}@github.com/${gitUri}
|
|
65
|
+
`cd ${repoPath} && git push https://${process.env.GITHUB_TOKEN}@github.com/${gitUri}${gExtension}${
|
|
63
66
|
options?.f === true ? ' --force' : ''
|
|
64
67
|
}`,
|
|
65
68
|
{
|
|
@@ -1629,6 +1629,7 @@ const Modal = {
|
|
|
1629
1629
|
currentTopModalId: '',
|
|
1630
1630
|
zIndexSync: function ({ idModal }) {
|
|
1631
1631
|
setTimeout(() => {
|
|
1632
|
+
if (!this.Data[idModal]) return;
|
|
1632
1633
|
const cleanTopModal = () => {
|
|
1633
1634
|
Object.keys(this.Data).map((_idModal) => {
|
|
1634
1635
|
if (this.Data[_idModal].options.zIndexSync && s(`.${_idModal}`)) s(`.${_idModal}`).style.zIndex = '3';
|
package/src/index.js
CHANGED
package/src/server/conf.js
CHANGED
|
@@ -927,7 +927,9 @@ const rebuildConfFactory = ({ deployId, valkey, mongo }) => {
|
|
|
927
927
|
const confServer = loadReplicas(
|
|
928
928
|
JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
|
|
929
929
|
);
|
|
930
|
+
const hosts = {};
|
|
930
931
|
for (const host of Object.keys(confServer)) {
|
|
932
|
+
hosts[host] = {};
|
|
931
933
|
for (const path of Object.keys(confServer[host])) {
|
|
932
934
|
if (!confServer[host][path].db) continue;
|
|
933
935
|
const { singleReplica, replicas, db } = confServer[host][path];
|
|
@@ -940,6 +942,7 @@ const rebuildConfFactory = ({ deployId, valkey, mongo }) => {
|
|
|
940
942
|
);
|
|
941
943
|
for (const _host of Object.keys(confServerReplica)) {
|
|
942
944
|
for (const _path of Object.keys(confServerReplica[_host])) {
|
|
945
|
+
hosts[host][_path] = { replica: { host, path } };
|
|
943
946
|
confServerReplica[_host][_path].valkey = valkey;
|
|
944
947
|
switch (provider) {
|
|
945
948
|
case 'mongoose':
|
|
@@ -954,7 +957,7 @@ const rebuildConfFactory = ({ deployId, valkey, mongo }) => {
|
|
|
954
957
|
'utf8',
|
|
955
958
|
);
|
|
956
959
|
}
|
|
957
|
-
}
|
|
960
|
+
} else hosts[host][path] = {};
|
|
958
961
|
confServer[host][path].valkey = valkey;
|
|
959
962
|
switch (provider) {
|
|
960
963
|
case 'mongoose':
|
|
@@ -964,6 +967,7 @@ const rebuildConfFactory = ({ deployId, valkey, mongo }) => {
|
|
|
964
967
|
}
|
|
965
968
|
}
|
|
966
969
|
fs.writeFileSync(`./engine-private/conf/${deployId}/conf.server.json`, JSON.stringify(confServer, null, 4), 'utf8');
|
|
970
|
+
return { hosts };
|
|
967
971
|
};
|
|
968
972
|
|
|
969
973
|
const getRestoreCronCmd = async (options = { host: '', path: '', conf: {}, deployId: '' }) => {
|
package/src/server/dns.js
CHANGED
|
@@ -5,6 +5,9 @@ import validator from 'validator';
|
|
|
5
5
|
import { publicIp, publicIpv4, publicIpv6 } from 'public-ip';
|
|
6
6
|
import { loggerFactory } from './logger.js';
|
|
7
7
|
import UnderpostRootEnv from '../cli/env.js';
|
|
8
|
+
import dns from 'node:dns';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import { shellExec } from './process.js';
|
|
8
11
|
|
|
9
12
|
dotenv.config();
|
|
10
13
|
|
|
@@ -18,35 +21,59 @@ const ip = {
|
|
|
18
21
|
},
|
|
19
22
|
};
|
|
20
23
|
|
|
24
|
+
const isInternetConnection = (domain = 'google.com') =>
|
|
25
|
+
new Promise((resolve) => dns.lookup(domain, {}, (err) => resolve(err ? false : true)));
|
|
26
|
+
|
|
27
|
+
// export INTERFACE=$(ip route | grep default | cut -d ' ' -f 5)
|
|
28
|
+
// export IP_ADDRESS=$(ip -4 addr show dev $INTERFACE | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
|
|
29
|
+
const getLocalIPv4Address = () =>
|
|
30
|
+
os.networkInterfaces()[
|
|
31
|
+
shellExec(`ip route | grep default | cut -d ' ' -f 5`, {
|
|
32
|
+
stdout: true,
|
|
33
|
+
silent: true,
|
|
34
|
+
disableLog: true,
|
|
35
|
+
}).trim()
|
|
36
|
+
].find((i) => i.family === 'IPv4').address;
|
|
37
|
+
|
|
21
38
|
class Dns {
|
|
22
39
|
static callback = async function (deployList) {
|
|
23
40
|
// Network topology configuration:
|
|
24
41
|
// LAN -> [NAT-VPS](modem/router device) -> WAN
|
|
25
42
|
// enabled DMZ Host to proxy IP 80-443 (79-444) sometimes router block first port
|
|
26
|
-
|
|
43
|
+
|
|
44
|
+
// Enabling DHCP
|
|
45
|
+
// Navigate to Subnets > VLAN > Configure DHCP.
|
|
46
|
+
// Select the appropriate DHCP options (Managed or Relay).
|
|
47
|
+
// Save and apply changes.
|
|
48
|
+
|
|
27
49
|
// verify inet ip proxy server address
|
|
28
50
|
// DHCP (Dynamic Host Configuration Protocol) LAN reserver IP -> MAC ID
|
|
29
51
|
// LAN server or device's local servers port -> 3000-3100 (2999-3101)
|
|
30
52
|
// DNS Records: [ANAME](Address Dynamic) -> [A](ipv4) host | [AAAA](ipv6) host -> [public-ip]
|
|
31
53
|
// Forward the router's TCP/UDP ports to the LAN device's IP address
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
|
|
36
|
-
const confCronData = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
|
|
37
|
-
|
|
38
|
-
let testIp;
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
testIp = await ip.public.ipv4();
|
|
42
|
-
} catch (error) {
|
|
43
|
-
logger.error(error, { testIp, stack: error.stack });
|
|
44
|
-
}
|
|
54
|
+
const isOnline = await isInternetConnection();
|
|
55
|
+
|
|
56
|
+
if (!isOnline) return;
|
|
45
57
|
|
|
46
|
-
|
|
58
|
+
let testIp;
|
|
47
59
|
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
try {
|
|
61
|
+
testIp = await ip.public.ipv4();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
logger.error(error, { testIp, stack: error.stack });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const currentIp = UnderpostRootEnv.API.get('ip');
|
|
67
|
+
|
|
68
|
+
if (validator.isIP(testIp) && currentIp !== testIp) {
|
|
69
|
+
logger.info(`new ip`, testIp);
|
|
70
|
+
UnderpostRootEnv.API.set('monitor-input', 'pause');
|
|
71
|
+
|
|
72
|
+
for (const _deployId of deployList.split(',')) {
|
|
73
|
+
const deployId = _deployId.trim();
|
|
74
|
+
const privateCronConfPath = `./engine-private/conf/${deployId}/conf.cron.json`;
|
|
75
|
+
const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
|
|
76
|
+
const confCronData = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
|
|
50
77
|
for (const recordType of Object.keys(confCronData.records)) {
|
|
51
78
|
switch (recordType) {
|
|
52
79
|
case 'A':
|
|
@@ -68,6 +95,7 @@ class Dns {
|
|
|
68
95
|
if (verifyIp === testIp) {
|
|
69
96
|
logger.info('ip updated successfully', testIp);
|
|
70
97
|
UnderpostRootEnv.API.set('ip', testIp);
|
|
98
|
+
UnderpostRootEnv.API.delete('monitor-input');
|
|
71
99
|
} else logger.error('ip not updated', testIp);
|
|
72
100
|
} catch (error) {
|
|
73
101
|
logger.error(error, error.stack);
|
|
@@ -102,3 +130,5 @@ class Dns {
|
|
|
102
130
|
}
|
|
103
131
|
|
|
104
132
|
export default Dns;
|
|
133
|
+
|
|
134
|
+
export { Dns, ip, isInternetConnection, getLocalIPv4Address };
|
package/src/server/runtime.js
CHANGED
|
@@ -142,6 +142,8 @@ const buildRuntime = async () => {
|
|
|
142
142
|
// if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
|
|
143
143
|
// $_SERVER['HTTPS'] = 'on';
|
|
144
144
|
// }
|
|
145
|
+
// For plugins:
|
|
146
|
+
// define( 'FS_METHOD', 'direct' );
|
|
145
147
|
|
|
146
148
|
// ErrorDocument 404 /custom_404.html
|
|
147
149
|
// ErrorDocument 500 /custom_50x.html
|
package/src/server/start.js
CHANGED