underpost 2.8.64 → 2.8.67

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 (51) hide show
  1. package/.vscode/extensions.json +3 -2
  2. package/.vscode/settings.json +2 -0
  3. package/CHANGELOG.md +24 -4
  4. package/README.md +39 -2
  5. package/bin/deploy.js +1205 -131
  6. package/bin/file.js +8 -0
  7. package/bin/index.js +1 -233
  8. package/cli.md +451 -0
  9. package/docker-compose.yml +1 -1
  10. package/jsdoc.json +1 -1
  11. package/manifests/calico-custom-resources.yaml +25 -0
  12. package/manifests/deployment/adminer/deployment.yaml +32 -0
  13. package/manifests/deployment/adminer/kustomization.yaml +7 -0
  14. package/manifests/deployment/adminer/service.yaml +13 -0
  15. package/manifests/mongodb-4.4/service-deployment.yaml +1 -1
  16. package/manifests/postgresql/configmap.yaml +9 -0
  17. package/manifests/postgresql/kustomization.yaml +10 -0
  18. package/manifests/postgresql/pv.yaml +15 -0
  19. package/manifests/postgresql/pvc.yaml +13 -0
  20. package/manifests/postgresql/service.yaml +10 -0
  21. package/manifests/postgresql/statefulset.yaml +37 -0
  22. package/manifests/valkey/statefulset.yaml +6 -4
  23. package/package.json +3 -9
  24. package/src/api/user/user.service.js +13 -10
  25. package/src/cli/cluster.js +113 -11
  26. package/src/cli/db.js +18 -8
  27. package/src/cli/deploy.js +157 -58
  28. package/src/cli/fs.js +14 -3
  29. package/src/cli/image.js +0 -68
  30. package/src/cli/index.js +312 -0
  31. package/src/cli/monitor.js +170 -26
  32. package/src/cli/repository.js +5 -2
  33. package/src/client/components/core/Account.js +3 -3
  34. package/src/client/components/core/CalendarCore.js +0 -1
  35. package/src/client/components/core/Css.js +0 -1
  36. package/src/client/components/core/CssCore.js +2 -0
  37. package/src/client/components/core/EventsUI.js +1 -1
  38. package/src/client/components/core/JoyStick.js +2 -2
  39. package/src/client/components/core/Modal.js +1 -0
  40. package/src/client/components/core/RichText.js +1 -11
  41. package/src/index.js +9 -8
  42. package/src/mailer/MailerProvider.js +3 -0
  43. package/src/server/client-build.js +13 -0
  44. package/src/server/conf.js +48 -0
  45. package/src/server/dns.js +47 -17
  46. package/src/server/json-schema.js +77 -0
  47. package/src/server/peer.js +2 -2
  48. package/src/server/proxy.js +4 -4
  49. package/src/server/runtime.js +24 -9
  50. package/src/server/start.js +122 -0
  51. package/src/server/valkey.js +25 -11
@@ -0,0 +1,312 @@
1
+ import dotenv from 'dotenv';
2
+ import { Command } from 'commander';
3
+ import Underpost from '../index.js';
4
+ import { getUnderpostRootPath, loadConf } from '../server/conf.js';
5
+ import fs from 'fs-extra';
6
+ import { commitData } from '../client/components/core/CommonJs.js';
7
+ import { shellExec } from '../server/process.js';
8
+
9
+ const underpostRootPath = getUnderpostRootPath();
10
+ fs.existsSync(`${underpostRootPath}/.env`)
11
+ ? dotenv.config({ path: `${underpostRootPath}/.env`, override: true })
12
+ : dotenv.config();
13
+
14
+ const program = new Command();
15
+
16
+ program.name('underpost').description(`underpost ci/cd cli ${Underpost.version}`).version(Underpost.version);
17
+
18
+ program
19
+ .command('new')
20
+ .argument('<app-name>', 'Application name')
21
+ .description('Create a new project')
22
+ .action(Underpost.repo.new);
23
+
24
+ program
25
+ .command('start')
26
+ .argument('<deploy-id>', 'Deploy configuration id')
27
+ .argument('[env]', 'Optional environment, for default is development')
28
+ .option('--run', 'Run app servers and monitor health server')
29
+ .option('--build', 'Build app client')
30
+ .action(Underpost.start.callback)
31
+ .description('Start up server, build pipelines, or services');
32
+
33
+ program
34
+ .command('clone')
35
+ .argument(`<uri>`, 'e.g. username/repository')
36
+ .option('--bare', 'Clone only .git files')
37
+ .description('Clone github repository')
38
+ .action(Underpost.repo.clone);
39
+
40
+ program
41
+ .command('pull')
42
+ .argument('<path>', 'Absolute or relative directory')
43
+ .argument(`<uri>`, 'e.g. username/repository')
44
+ .description('Pull github repository')
45
+ .action(Underpost.repo.pull);
46
+
47
+ program
48
+ .command('cmt')
49
+ .argument('<path>', 'Absolute or relative directory')
50
+ .argument(`<commit-type>`, `Options: ${Object.keys(commitData)}`)
51
+ .argument(`[module-tag]`, 'Optional set module tag')
52
+ .argument(`[message]`, 'Optional set additional message')
53
+ .option('--empty', 'Allow empty files')
54
+ .option('--copy', 'Copy to clipboard message')
55
+ .option('--info', 'Info commit types')
56
+ .description('Commit github repository')
57
+ .action(Underpost.repo.commit);
58
+
59
+ program
60
+ .command('push')
61
+ .argument('<path>', 'Absolute or relative directory')
62
+ .argument(`<uri>`, 'e.g. username/repository')
63
+ .option('-f', 'Force push overwriting repository')
64
+ .description('Push github repository')
65
+ .action(Underpost.repo.push);
66
+
67
+ program
68
+ .command('env')
69
+ .argument('<deploy-id>', `deploy configuration id, if 'clean' restore default`)
70
+ .argument('[env]', 'Optional environment, for default is production')
71
+ .description('Set environment variables files and conf related to <deploy-id>')
72
+ .action(loadConf);
73
+
74
+ program
75
+ .command('config')
76
+ .argument('operator', `Options: ${Object.keys(Underpost.env)}`)
77
+ .argument('[key]', 'Config key')
78
+ .argument('[value]', 'Config value')
79
+ .description(`Manage configuration, operators`)
80
+ .action((...args) => Underpost.env[args[0]](args[1], args[2]));
81
+
82
+ program
83
+ .command('root')
84
+ .description('Get npm root path')
85
+ .action(() => console.log(getNpmRootPath()));
86
+
87
+ program
88
+ .command('cluster')
89
+ .argument('[pod-name]', 'Optional pod name filter')
90
+ .option('--reset', `Delete all clusters and prune all data and caches`)
91
+ .option('--mariadb', 'Init with mariadb statefulset')
92
+ .option('--mongodb', 'Init with mongodb statefulset')
93
+ .option('--postgresql', 'Init with postgresql statefulset')
94
+ .option('--mongodb4', 'Init with mongodb 4.4 service')
95
+ .option('--istio', 'Init base istio cluster')
96
+ .option('--valkey', 'Init with valkey service')
97
+ .option('--contour', 'Init with project contour base HTTPProxy and envoy')
98
+ .option('--cert-manager', 'Init with letsencrypt-prod ClusterIssuer')
99
+ .option('--info', 'Get all kinds objects deployed')
100
+ .option('--full', 'Init with all statefulsets and services available')
101
+ .option('--ns-use <ns-name>', 'Switches current context to namespace')
102
+ .option('--dev', 'init with dev cluster')
103
+ .option('--list-pods', 'Display list pods information')
104
+ .option('--info-capacity', 'display current total machine capacity info')
105
+ .option('--info-capacity-pod', 'display current machine capacity pod info')
106
+ .option('--pull-image', 'Set optional pull associated image')
107
+ .action(Underpost.cluster.init)
108
+ .description('Manage cluster, for default initialization base kind cluster');
109
+
110
+ program
111
+ .command('deploy')
112
+ .argument('<deploy-list>', 'Deploy id list, e.g. default-a,default-b')
113
+ .argument('[env]', 'Optional environment, for default is development')
114
+ .option('--remove', 'Delete deployments and services')
115
+ .option('--sync', 'Sync deployments env, ports, and replicas')
116
+ .option('--info-router', 'Display router structure')
117
+ .option('--expose', 'Expose service match deploy-list')
118
+ .option('--info-util', 'Display kubectl util management commands')
119
+ .option('--cert', 'Reset tls/ssl certificate secrets')
120
+ .option('--build-manifest', 'Build kind yaml manifests: deployments, services, proxy and secrets')
121
+ .option('--dashboard-update', 'Update dashboard instance data with current router config')
122
+ .option('--replicas <replicas>', 'Set custom number of replicas')
123
+ .option('--versions <deployment-versions>', 'Comma separated custom deployment versions')
124
+ .option('--traffic <traffic-versions>', 'Comma separated custom deployment traffic')
125
+ .option('--disable-update-deployment', 'Disable update deployments')
126
+ .option('--info-traffic', 'get traffic conf form current resources deployments')
127
+ .option(
128
+ '--rebuild-clients-bundle',
129
+ 'Inside container, rebuild clients bundle, only static public or storage client files',
130
+ )
131
+ .description('Manage deployment, for default deploy development pods')
132
+ .action(Underpost.deploy.callback);
133
+
134
+ program
135
+ .command('secret')
136
+ .argument('<platform>', `Options: ${Object.keys(Underpost.secret)}`)
137
+ .option('--init', 'Init secrets platform environment')
138
+ .option('--create-from-file <path-env-file>', 'Create secret from env file')
139
+ .option('--list', 'Lists secrets')
140
+ // .option('--delete [secret-key]', 'Delete key secret, if not set, are default delete all')
141
+ // .option('--create [secret-key] [secret-value]', 'Create secret key, with secret value')
142
+ .description(`Manage secrets`)
143
+ .action((...args) => {
144
+ if (args[1].createFromFile) return Underpost.secret[args[0]].createFromEnvFile(args[1].createFromFile);
145
+ if (args[1].list) return Underpost.secret[args[0]].list();
146
+ if (args[1].init) return Underpost.secret[args[0]].init();
147
+ });
148
+
149
+ program
150
+ .command('dockerfile-image-build')
151
+ .option('--path [path]', 'Dockerfile path')
152
+ .option('--image-name [image-name]', 'Set image name')
153
+ .option('--image-path [image-path]', 'Set tar image path')
154
+ .option('--dockerfile-name [dockerfile-name]', 'set Dockerfile name')
155
+ .option('--podman-save', 'Export tar file from podman')
156
+ .option('--kind-load', 'Import tar image to Kind cluster')
157
+ .option('--secrets', 'Dockerfile env secrets')
158
+ .option('--secrets-path [secrets-path]', 'Dockerfile custom path env secrets')
159
+ .option('--no-cache', 'Build without using cache')
160
+ .description('Build image from Dockerfile')
161
+ .action(Underpost.image.dockerfile.build);
162
+
163
+ program
164
+ .command('dockerfile-pull-base-images')
165
+ .description('Pull underpost dockerfile images requirements')
166
+ .action(Underpost.image.dockerfile.pullBaseImages);
167
+
168
+ program
169
+ .command('install')
170
+ .description('Fast import underpost npm dependencies')
171
+ .action(() => {
172
+ fs.copySync(`${underpostRootPath}/node_modules`, './node_modules');
173
+ });
174
+
175
+ program
176
+ .command('db')
177
+ .argument('<deploy-list>', 'Deploy id list, e.g. default-a,default-b')
178
+ .option('--import', 'Import container backups from repositories')
179
+ .option('--export', 'Export container backups to repositories')
180
+ .option('--pod-name <pod-name>', 'Optional pod context')
181
+ .option('--collections <collections>', 'Comma separated collections')
182
+ .option('--out-path <out-path>', 'Custom out path backup')
183
+ .option('--drop', 'Drop databases')
184
+ .option('--preserveUUID', 'Preserve Ids')
185
+ .option('--git', 'Upload to github')
186
+ .option('--hosts <hosts>', 'Comma separated hosts')
187
+ .option('--paths <paths>', 'Comma separated paths')
188
+ .option('--ns <ns-name>', 'Optional name space context')
189
+ .description('Manage databases')
190
+ .action(Underpost.db.callback);
191
+
192
+ program
193
+ .command('script')
194
+ .argument('operator', `Options: ${Object.keys(Underpost.script)}`)
195
+ .argument('<script-name>', 'Script name')
196
+ .argument('[script-value]', 'Literal command, or path')
197
+ .option('--itc', 'Inside container execution context')
198
+ .option('--itc-path', 'Inside container path options')
199
+ .option('--ns <ns-name>', 'Options name space context')
200
+ .option('--pod-name <pod-name>')
201
+ .description(
202
+ 'Supports a number of built-in underpost global scripts and their preset life cycle events as well as arbitrary scripts',
203
+ )
204
+ .action((...args) => Underpost.script[args[0]](args[1], args[2], args[3]));
205
+
206
+ program
207
+ .command('cron')
208
+ .argument('[deploy-list]', 'Deploy id list, e.g. default-a,default-b')
209
+ .argument('[job-list]', `Deploy id list, e.g. ${Object.keys(Underpost.cron)}, for default all available jobs`)
210
+ .option('--itc', 'Inside container execution context')
211
+ .option('--init', 'Init cron jobs for cron job default deploy id')
212
+ .option('--git', 'Upload to github')
213
+ .option('--dashboard-update', 'Update dashboard cron data with current jobs config')
214
+ .description('Cron jobs management')
215
+ .action(Underpost.cron.callback);
216
+
217
+ program
218
+ .command('fs')
219
+ .argument('[path]', 'Absolute or relative directory')
220
+ .option('--rm', 'Remove file')
221
+ .option('--git', 'Current git changes')
222
+ .option('--recursive', 'Upload files recursively')
223
+ .option('--deploy-id <deploy-id>', 'Deploy configuration id')
224
+ .option('--pull', 'Download file')
225
+ .option('--force', 'Force action')
226
+ .option('--storage-file-path <storage-file-path>', 'custom file storage path')
227
+ .description('File storage management, for default upload file')
228
+ .action(Underpost.fs.callback);
229
+
230
+ program
231
+ .command('test')
232
+ .argument('[deploy-list]', 'Deploy id list, e.g. default-a,default-b')
233
+ .description('Manage Test, for default run current underpost default test')
234
+ .option('--itc', 'Inside container execution context')
235
+ .option('--sh', 'Copy to clipboard, container entrypoint shell command')
236
+ .option('--logs', 'Display container logs')
237
+ .option('--pod-name <pod-name>')
238
+ .option('--pod-status <pod-status>')
239
+ .option('--kind-type <kind-type>')
240
+ .action(Underpost.test.callback);
241
+
242
+ program
243
+ .command('monitor')
244
+ .argument('<deploy-id>', 'Deploy configuration id')
245
+ .argument('[env]', 'Optional environment, for default is development')
246
+ .option('--ms-interval <ms-interval>', 'Custom ms interval delta time')
247
+ .option('--now', 'Exec immediately monitor script')
248
+ .option('--single', 'Disable recurrence')
249
+ .option('--replicas <replicas>', 'Set custom number of replicas')
250
+ .option('--type <type>', 'Set custom monitor type')
251
+ .option('--sync', 'Sync with current proxy deployments proxy traffic')
252
+ .description('Monitor health server management')
253
+ .action(Underpost.monitor.callback);
254
+
255
+ const buildCliDoc = () => {
256
+ let md = shellExec(`node bin help`, { silent: true, stdout: true }).split('Options:');
257
+ const baseOptions =
258
+ `## ${md[0].split(`\n`)[2]}
259
+
260
+ ### Usage: ` +
261
+ '`' +
262
+ md[0].split(`\n`)[0].split('Usage: ')[1] +
263
+ '`' +
264
+ `
265
+ ` +
266
+ '```\n Options:' +
267
+ md[1] +
268
+ ' \n```';
269
+ md =
270
+ baseOptions +
271
+ `
272
+
273
+ ## Commands:
274
+ `;
275
+ program.commands.map((o) => {
276
+ md +=
277
+ `
278
+
279
+ ` +
280
+ '### `' +
281
+ o._name +
282
+ '` :' +
283
+ `
284
+ ` +
285
+ '```\n ' +
286
+ shellExec(`node bin help ${o._name}`, { silent: true, stdout: true }) +
287
+ ' \n```' +
288
+ `
289
+ `;
290
+ });
291
+ fs.writeFileSync(`./src/client/public/nexodev/docs/references/Command Line Interface.md`, md, 'utf8');
292
+ fs.writeFileSync(`./cli.md`, md, 'utf8');
293
+ const readmeSplit = `pwa-microservices-template</a>`;
294
+ const readme = fs.readFileSync(`./README.md`, 'utf8').split(readmeSplit);
295
+ fs.writeFileSync(
296
+ './README.md',
297
+ readme[0] +
298
+ readmeSplit +
299
+ `
300
+
301
+ ` +
302
+ baseOptions +
303
+ `
304
+
305
+ <a target="_top" href="https://github.com/underpostnet/pwa-microservices-template/blob/master/cli.md">See complete CLI Docs here.</a>
306
+
307
+ `,
308
+ 'utf8',
309
+ );
310
+ };
311
+
312
+ export { program, buildCliDoc };
@@ -4,13 +4,33 @@ import UnderpostDeploy from './deploy.js';
4
4
  import axios from 'axios';
5
5
  import UnderpostRootEnv from './env.js';
6
6
  import fs from 'fs-extra';
7
+ import { shellExec } from '../server/process.js';
8
+ import { isInternetConnection } from '../server/dns.js';
7
9
 
8
10
  const logger = loggerFactory(import.meta);
9
11
 
10
12
  class UnderpostMonitor {
11
13
  static API = {
12
- async callback(deployId, env = 'development', options = { now: false, single: false, msInterval: '' }) {
13
- const router = await UnderpostDeploy.API.routerFactory(deployId, env);
14
+ async callback(
15
+ deployId,
16
+ env = 'development',
17
+ options = { now: false, single: false, msInterval: '', type: '', replicas: '', sync: false },
18
+ commanderOptions,
19
+ auxRouter,
20
+ ) {
21
+ if (deployId === 'dd' && fs.existsSync(`./engine-private/deploy/dd.router`)) {
22
+ for (const _deployId of fs.readFileSync(`./engine-private/deploy/dd.router`, 'utf8').split(','))
23
+ UnderpostMonitor.API.callback(
24
+ _deployId.trim(),
25
+ env,
26
+ options,
27
+ commanderOptions,
28
+ await UnderpostDeploy.API.routerFactory(_deployId, env),
29
+ );
30
+ return;
31
+ }
32
+
33
+ const router = auxRouter ?? (await UnderpostDeploy.API.routerFactory(deployId, env));
14
34
 
15
35
  const confServer = loadReplicas(
16
36
  JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
@@ -19,20 +39,58 @@ class UnderpostMonitor {
19
39
 
20
40
  const pathPortAssignmentData = pathPortAssignmentFactory(router, confServer);
21
41
 
22
- logger.info('', pathPortAssignmentData);
42
+ let errorPayloads = [];
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
+ });
23
61
 
24
- const errorPayloads = [];
25
- const maxAttempts = Object.keys(pathPortAssignmentData)
26
- .map((host) => pathPortAssignmentData[host].length)
27
- .reduce((accumulator, value) => accumulator + value, 0);
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
+ };
28
73
 
29
74
  const monitor = async (reject) => {
30
- logger.info('Check server health');
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);
79
+ logger.info(`[${deployId}-${env}] Check server health`);
31
80
  for (const host of Object.keys(pathPortAssignmentData)) {
32
81
  for (const instance of pathPortAssignmentData[host]) {
33
82
  const { port, path } = instance;
34
83
  if (path.match('peer') || path.match('socket')) continue;
35
- const urlTest = `http://localhost:${port}${path}`;
84
+ let urlTest = `http://localhost:${port}${path}`;
85
+ switch (options.type) {
86
+ case 'remote':
87
+ case 'blue-green':
88
+ urlTest = `https://${host}${path}`;
89
+ break;
90
+
91
+ default:
92
+ break;
93
+ }
36
94
  // logger.info('Test instance', urlTest);
37
95
  await axios.get(urlTest, { timeout: 10000 }).catch((error) => {
38
96
  // console.log(error);
@@ -45,15 +103,48 @@ class UnderpostMonitor {
45
103
  status: error.status,
46
104
  code: error.code,
47
105
  errors: error.errors,
106
+ timestamp: new Date().getTime(),
48
107
  };
49
108
  if (errorPayload.status !== 404) {
50
109
  errorPayloads.push(errorPayload);
51
110
  if (errorPayloads.length >= maxAttempts) {
52
111
  const message = JSON.stringify(errorPayloads, null, 4);
53
- if (reject) reject(message);
54
- else throw new Error(message);
112
+ logger.error(
113
+ `Deployment ${deployId} ${env} has been reached max attempts error payloads`,
114
+ errorPayloads,
115
+ );
116
+ switch (options.type) {
117
+ case 'blue-green':
118
+ {
119
+ const confServer = JSON.parse(
120
+ fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'),
121
+ );
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
+
128
+ for (const host of Object.keys(confServer)) {
129
+ shellExec(`sudo kubectl delete HTTPProxy ${host}`);
130
+ }
131
+ shellExec(`sudo kubectl rollout restart deployment/${deployId}-${env}-${traffic}`);
132
+
133
+ switchTraffic();
134
+ }
135
+
136
+ break;
137
+
138
+ case 'remote':
139
+ break;
140
+
141
+ default:
142
+ if (reject) reject(message);
143
+ else throw new Error(message);
144
+ }
145
+ errorPayloads = [];
55
146
  }
56
- logger.error('Error accumulator', errorPayloads.length);
147
+ logger.error(`Error accumulator ${deployId}-${env}-${traffic}`, errorPayloads.length);
57
148
  }
58
149
  });
59
150
  }
@@ -62,29 +153,82 @@ class UnderpostMonitor {
62
153
  if (options.now === true) await monitor();
63
154
  if (options.single === true) return;
64
155
  let optionsMsTimeout = parseInt(options.msInterval);
65
- if (isNaN(optionsMsTimeout)) optionsMsTimeout = 30000;
156
+ if (isNaN(optionsMsTimeout)) optionsMsTimeout = 60250; // 60.25 seconds
157
+ let monitorTrafficName;
158
+ let monitorPodName;
66
159
  const monitorCallBack = (resolve, reject) => {
67
- const envMsTimeout = UnderpostRootEnv.API.get('monitor-ms');
160
+ const envMsTimeout = UnderpostRootEnv.API.get(`${deployId}-${env}-monitor-ms`);
68
161
  setTimeout(
69
162
  async () => {
70
- switch (UnderpostRootEnv.API.get('monitor-input')) {
71
- case 'pause':
72
- monitorCallBack(resolve, reject);
73
- return;
74
- case 'restart':
75
- return reject();
76
- case 'stop':
77
- return resolve();
163
+ const isOnline = await isInternetConnection();
164
+ if (!isOnline) {
165
+ logger.warn('No internet connection');
166
+ monitorCallBack(resolve, reject);
167
+ return;
168
+ }
169
+ switch (options.type) {
170
+ case 'blue-green':
171
+ {
172
+ if (monitorTrafficName !== traffic) {
173
+ monitorTrafficName = undefined;
174
+ monitorPodName = undefined;
175
+ }
176
+ const cmd = `underpost config get container-status`;
177
+ const checkDeploymentReadyStatus = () => {
178
+ const pods = UnderpostDeploy.API.get(`${deployId}-${env}-${traffic}`);
179
+ if (pods && pods[0]) {
180
+ const { NAME } = pods[0];
181
+ if (
182
+ shellExec(`sudo kubectl exec -i ${NAME} -- sh -c "${cmd}"`, { stdout: true }).match(
183
+ `${deployId}-${env}-running-deployment`,
184
+ )
185
+ ) {
186
+ monitorPodName = NAME;
187
+ monitorTrafficName = `${traffic}`;
188
+ }
189
+ }
190
+ };
191
+ if (!monitorPodName) {
192
+ checkDeploymentReadyStatus();
193
+ monitorCallBack(resolve, reject);
194
+ return;
195
+ }
196
+ }
197
+
198
+ break;
199
+
78
200
  default:
79
- await monitor(reject);
80
- monitorCallBack(resolve, reject);
81
- return;
201
+ break;
82
202
  }
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;
83
227
  },
84
228
  !isNaN(envMsTimeout) ? envMsTimeout : optionsMsTimeout,
85
229
  );
86
230
  };
87
- return await new Promise((...args) => monitorCallBack(...args));
231
+ return new Promise((...args) => monitorCallBack(...args));
88
232
  },
89
233
  };
90
234
  }
@@ -4,7 +4,7 @@ import { pbcopy, shellExec } from '../server/process.js';
4
4
  import { actionInitLog, loggerFactory } from '../server/logger.js';
5
5
  import fs from 'fs-extra';
6
6
  import { getNpmRootPath } from '../server/conf.js';
7
- import { listenPortController, listenServerFactory } from '../server/network.js';
7
+ import UnderpostStartUp from '../server/start.js';
8
8
 
9
9
  dotenv.config();
10
10
 
@@ -78,7 +78,10 @@ class UnderpostRepository {
78
78
  return new Promise(async (resolve, reject) => {
79
79
  try {
80
80
  await logger.setUpInfo();
81
- if (repositoryName === 'service') return resolve(await listenPortController(listenServerFactory(), ':'));
81
+ if (repositoryName === 'service')
82
+ return resolve(
83
+ await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), ':'),
84
+ );
82
85
  else actionInitLog();
83
86
  const exeRootPath = `${getNpmRootPath()}/underpost`;
84
87
  const destFolder = `./${repositoryName}`;
@@ -204,7 +204,7 @@ const Account = {
204
204
  disabled: false,
205
205
  extension: async () =>
206
206
  html`${await BtnIcon.Render({
207
- class: `wfa btn-input-extension btn-account-update-username`,
207
+ class: `in wfa btn-input-extension btn-account-update-username`,
208
208
  type: 'button',
209
209
  style: 'text-align: left',
210
210
  label: html`${Translate.Render(`update`)}`,
@@ -223,7 +223,7 @@ const Account = {
223
223
  extension: !(options && options.disabled && options.disabled.includes('emailConfirm'))
224
224
  ? async () => html`<div class="in verify-email-status"></div>
225
225
  ${await BtnIcon.Render({
226
- class: `wfa btn-input-extension btn-confirm-email`,
226
+ class: `in wfa btn-input-extension btn-confirm-email`,
227
227
  type: 'button',
228
228
  style: 'text-align: left',
229
229
  label: html`<div class="in">
@@ -246,7 +246,7 @@ const Account = {
246
246
  disabledEye: true,
247
247
  extension: async () =>
248
248
  html`${await BtnIcon.Render({
249
- class: `wfa btn-input-extension btn-account-change-password`,
249
+ class: `in wfa btn-input-extension btn-account-change-password`,
250
250
  type: 'button',
251
251
  style: 'text-align: left',
252
252
  label: html`${Translate.Render(`change-password`)}`,
@@ -446,7 +446,6 @@ const CalendarCore = {
446
446
  .calendar-buttons-container {
447
447
  padding-bottom: 15px;
448
448
  height: 60px;
449
- z-index: 4;
450
449
  }
451
450
  </style>
452
451
  <div class="in calendar-container hide">
@@ -761,7 +761,6 @@ const renderWave = ({ id }) => {
761
761
  const cssTokensEffect = {};
762
762
  const cssTokensContainer = {};
763
763
  const cssEffect = async (containerSelector, event) => {
764
- return;
765
764
  // Array.from(event.target.classList)
766
765
  let offsetX, offsetY;
767
766
  if (Array.from(event.srcElement.classList).includes('ripple') && cssTokensContainer[containerSelector]) {
@@ -524,6 +524,7 @@ const CssCoreDark = {
524
524
  margin: 5px 0 0 0;
525
525
  padding: 5px;
526
526
  font-size: 16px;
527
+ min-height: 40px;
527
528
  }
528
529
  .btn-input-extension:hover {
529
530
  }
@@ -843,6 +844,7 @@ const CssCoreLight = {
843
844
  margin: 5px 0 0 0;
844
845
  padding: 5px;
845
846
  font-size: 16px;
847
+ min-height: 40px;
846
848
  }
847
849
  .btn-input-extension:hover {
848
850
  }
@@ -12,7 +12,7 @@ const EventsUI = {
12
12
  if (!s(id)) return;
13
13
  let complete = true;
14
14
  s(id)[type] = async function (e) {
15
- cssEffect(id, e);
15
+ if (options.clickEffect) cssEffect(id, e);
16
16
  if (complete) {
17
17
  complete = false;
18
18
  await LoadingAnimation.spinner.play(loadingContainer ? loadingContainer : id);
@@ -19,8 +19,8 @@ const JoyStick = {
19
19
  /* border: 2px solid red; */
20
20
  left: 5px;
21
21
  bottom: 5px;
22
- height: 200px;
23
- width: 200px;
22
+ height: 175px;
23
+ width: 175px;
24
24
  z-index: 3;
25
25
  }
26
26
  .joy-img-background-${id} {
@@ -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';