underpost 2.7.83 → 2.7.91

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 (60) hide show
  1. package/.github/workflows/ghpkg.yml +41 -1
  2. package/.github/workflows/pwa-microservices-template.page.yml +54 -0
  3. package/.vscode/settings.json +7 -0
  4. package/CHANGELOG.md +64 -16
  5. package/bin/cron.js +47 -0
  6. package/bin/db.js +60 -7
  7. package/bin/deploy.js +345 -15
  8. package/bin/file.js +17 -1
  9. package/bin/index.js +1 -1
  10. package/bin/util.js +31 -1
  11. package/conf.js +18 -4
  12. package/docker-compose.yml +1 -1
  13. package/package.json +3 -3
  14. package/src/api/core/core.router.js +9 -9
  15. package/src/api/core/core.service.js +12 -4
  16. package/src/api/default/default.service.js +4 -4
  17. package/src/api/file/file.service.js +3 -3
  18. package/src/api/user/user.service.js +10 -8
  19. package/src/client/components/core/CommonJs.js +3 -0
  20. package/src/client/components/core/CssCore.js +30 -3
  21. package/src/client/components/core/Docs.js +110 -10
  22. package/src/client/components/core/LoadingAnimation.js +4 -2
  23. package/src/client/components/core/Modal.js +224 -22
  24. package/src/client/components/core/Panel.js +1 -1
  25. package/src/client/components/core/PanelForm.js +2 -1
  26. package/src/client/components/core/Responsive.js +34 -5
  27. package/src/client/components/core/RichText.js +4 -2
  28. package/src/client/components/core/WebComponent.js +44 -0
  29. package/src/client/components/core/Worker.js +13 -15
  30. package/src/client/public/default/plantuml/client-conf.svg +1 -1
  31. package/src/client/public/default/plantuml/client-schema.svg +1 -1
  32. package/src/client/public/default/plantuml/cron-conf.svg +1 -1
  33. package/src/client/public/default/plantuml/cron-schema.svg +1 -1
  34. package/src/client/public/default/plantuml/server-conf.svg +1 -1
  35. package/src/client/public/default/plantuml/server-schema.svg +1 -1
  36. package/src/client/public/default/plantuml/ssr-conf.svg +1 -1
  37. package/src/client/public/default/plantuml/ssr-schema.svg +1 -1
  38. package/src/client/public/default/site.webmanifest +69 -0
  39. package/src/client/ssr/components/body/CacheControl.js +1 -1
  40. package/src/client/ssr/components/head/Production.js +1 -0
  41. package/src/client/ssr/components/head/Pwa.js +146 -0
  42. package/src/client/ssr/components/head/Seo.js +14 -0
  43. package/src/client/ssr/pages/maintenance.js +14 -0
  44. package/src/client/ssr/pages/offline.js +21 -0
  45. package/src/client/sw/default.sw.js +4 -2
  46. package/src/db/DataBaseProvider.js +12 -1
  47. package/src/db/mongo/MongooseDB.js +0 -1
  48. package/src/runtime/lampp/Lampp.js +9 -9
  49. package/src/server/backup.js +82 -70
  50. package/src/server/client-build.js +46 -94
  51. package/src/server/conf.js +82 -18
  52. package/src/server/crypto.js +91 -0
  53. package/src/server/dns.js +48 -16
  54. package/src/server/network.js +94 -7
  55. package/src/server/proxy.js +27 -27
  56. package/src/server/runtime.js +4 -2
  57. package/src/server/ssl.js +2 -2
  58. package/src/client/ssr/offline/default.index.js +0 -31
  59. package/src/cron.js +0 -30
  60. package/src/server/cron.js +0 -35
@@ -9,100 +9,112 @@ dotenv.config();
9
9
  const logger = loggerFactory(import.meta);
10
10
 
11
11
  const BackUpManagement = {
12
- repoUrl: `https://${process.env.GITHUB_BACKUP_TOKEN}@github.com/${process.env.GITHUB_BACKUP_USERNAME}/${process.env.GITHUB_BACKUP_REPO}.git`,
13
- Init: async function () {
14
- await this.Callback();
15
- return this.Callback;
16
- },
17
- Callback: async function () {
18
- const privateCronConfPath = `./engine-private/conf/${process.argv[2]}/conf.cron.json`;
12
+ repoUrl: `https://${process.env.GITHUB_TOKEN}@github.com/${process.env.GITHUB_USERNAME}/${process.env.GITHUB_BACKUP_REPO}.git`,
13
+ Init: async function ({ deployId }) {
14
+ const Callback = async function () {
15
+ const privateCronConfPath = `./engine-private/conf/${deployId}/conf.cron.json`;
19
16
 
20
- const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
17
+ const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
21
18
 
22
- const { backups } = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
19
+ const { backups } = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
23
20
 
24
- if (!backups) return;
21
+ if (!backups) return;
25
22
 
26
- const currentDate = new Date().getTime();
23
+ logger.info('init backups callback');
24
+ await logger.setUpInfo();
27
25
 
28
- if (!fs.existsSync('./engine-private/cron-backups'))
29
- fs.mkdirSync('./engine-private/cron-backups', { recursive: true });
26
+ const currentDate = new Date().getTime();
30
27
 
31
- for (const deployGroupData of backups) {
32
- const { deployGroupId } = deployGroupData;
33
- const dataDeploy = getDataDeploy({ deployGroupId });
28
+ if (!fs.existsSync('./engine-private/cron-backups'))
29
+ fs.mkdirSync('./engine-private/cron-backups', { recursive: true });
34
30
 
35
- for (const deployObj of dataDeploy) {
36
- const { deployId, replicaHost } = deployObj;
31
+ for (const deployGroupData of backups) {
32
+ const { deployGroupId } = deployGroupData;
33
+ const dataDeploy = getDataDeploy({ deployGroupId });
37
34
 
38
- if (replicaHost) continue;
35
+ for (const deployObj of dataDeploy) {
36
+ const { deployId, replicaHost } = deployObj;
39
37
 
40
- const confServer = JSON.parse(
41
- fs.existsSync(`./engine-private/replica/${deployId}/conf.server.json`)
42
- ? fs.readFileSync(`./engine-private/replica/${deployId}/conf.server.json`, 'utf8')
43
- : fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'),
44
- );
38
+ if (replicaHost) continue;
45
39
 
46
- for (const host of Object.keys(confServer))
47
- for (const path of Object.keys(confServer[host])) {
48
- // retention policy
49
- let { db, backupFrequency, maxBackupRetention, singleReplica, wp, git, directory } = confServer[host][path];
40
+ const confServer = JSON.parse(
41
+ fs.existsSync(`./engine-private/replica/${deployId}/conf.server.json`)
42
+ ? fs.readFileSync(`./engine-private/replica/${deployId}/conf.server.json`, 'utf8')
43
+ : fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8'),
44
+ );
50
45
 
51
- if (!db || singleReplica) continue;
46
+ for (const host of Object.keys(confServer))
47
+ for (const path of Object.keys(confServer[host])) {
48
+ // retention policy
49
+ let { db, backupFrequency, maxBackupRetention, singleReplica, wp, git, directory } =
50
+ confServer[host][path];
52
51
 
53
- if (!backupFrequency) backupFrequency = 'daily';
54
- if (!maxBackupRetention) maxBackupRetention = 5;
52
+ if (!db || singleReplica) continue;
55
53
 
56
- const backUpPath = `${process.cwd()}/engine-private/cron-backups/${getCronBackUpFolder(host, path)}`;
57
- if (!fs.existsSync(backUpPath)) fs.mkdirSync(`${backUpPath}`, { recursive: true });
58
- // .isDirectory()
59
- const files = await fs.readdir(backUpPath, { withFileTypes: true });
54
+ if (!backupFrequency) backupFrequency = 'daily';
55
+ if (!maxBackupRetention) maxBackupRetention = 5;
60
56
 
61
- const currentBackupsDirs = files
62
- .map((fileObj) => parseInt(fileObj.name))
63
- .sort((a, b) => a - b)
64
- .reverse();
57
+ const backUpPath = `${process.cwd()}/engine-private/cron-backups/${getCronBackUpFolder(host, path)}`;
58
+ if (!fs.existsSync(backUpPath)) fs.mkdirSync(`${backUpPath}`, { recursive: true });
59
+ // .isDirectory()
60
+ const files = await fs.readdir(backUpPath, { withFileTypes: true });
65
61
 
66
- switch (backupFrequency) {
67
- case 'daily':
62
+ const currentBackupsDirs = files
63
+ .map((fileObj) => parseInt(fileObj.name))
64
+ .sort((a, b) => a - b)
65
+ .reverse();
68
66
 
69
- default:
70
- // if (currentBackupsDirs[0] && currentDate - currentBackupsDirs[0] < 1000 * 60 * 60 * 24) continue;
71
- break;
72
- }
67
+ switch (backupFrequency) {
68
+ case 'daily':
73
69
 
74
- for (const retentionPath of currentBackupsDirs.filter((t, i) => i >= maxBackupRetention - 1)) {
75
- const removePathRetention = `${backUpPath}/${retentionPath}`;
76
- logger.info('Remove backup folder', removePathRetention);
77
- fs.removeSync(removePathRetention);
78
- }
70
+ default:
71
+ // if (currentBackupsDirs[0] && currentDate - currentBackupsDirs[0] < 1000 * 60 * 60 * 24) continue;
72
+ break;
73
+ }
74
+
75
+ for (const retentionPath of currentBackupsDirs.filter((t, i) => i >= maxBackupRetention - 1)) {
76
+ const removePathRetention = `${backUpPath}/${retentionPath}`;
77
+ logger.info('Remove backup folder', removePathRetention);
78
+ fs.removeSync(removePathRetention);
79
+ }
79
80
 
80
- fs.mkdirSync(`${backUpPath}/${currentDate}`, { recursive: true });
81
+ fs.mkdirSync(`${backUpPath}/${currentDate}`, { recursive: true });
81
82
 
82
- shellExec(`node bin/db ${host}${path} export ${deployId} ${backUpPath}/${currentDate}`);
83
+ shellExec(`node bin/db ${host}${path} export ${deployId} ${backUpPath}/${currentDate}`);
83
84
 
84
- if (wp) {
85
- const repoUrl = `https://${process.env.GITHUB_BACKUP_TOKEN}@github.com/${
86
- process.env.GITHUB_BACKUP_USERNAME
87
- }/${git.split('/').pop()}.git`;
85
+ if (wp) {
86
+ const repoUrl = `https://${process.env.GITHUB_TOKEN}@github.com/${process.env.GITHUB_USERNAME}/${git
87
+ .split('/')
88
+ .pop()}.git`;
88
89
 
89
- shellExec(
90
- `cd ${directory}` +
91
- ` && git pull ${repoUrl}` +
92
- ` && git add . && git commit -m "backup ${new Date().toLocaleDateString()}"` +
93
- ` && git push ${repoUrl}`,
94
- );
90
+ shellExec(
91
+ `cd ${directory}` +
92
+ ` && git pull ${repoUrl}` +
93
+ ` && git add . && git commit -m "backup ${new Date().toLocaleDateString()}"` +
94
+ ` && git push ${repoUrl}`,
95
+ {
96
+ disableLog: true,
97
+ },
98
+ );
99
+ }
95
100
  }
96
- }
101
+ }
97
102
  }
98
- }
99
- shellExec(
100
- `cd ./engine-private/cron-backups` +
101
- ` && git pull ${BackUpManagement.repoUrl}` +
102
- ` && git add . && git commit -m "backup ${new Date().toLocaleDateString()}"` +
103
- ` && git push ${BackUpManagement.repoUrl}`,
104
- );
103
+ shellExec(
104
+ `cd ./engine-private/cron-backups` +
105
+ ` && git pull ${BackUpManagement.repoUrl}` +
106
+ ` && git add . && git commit -m "backup ${new Date().toLocaleDateString()}"` +
107
+ ` && git push ${BackUpManagement.repoUrl}`,
108
+ {
109
+ disableLog: true,
110
+ },
111
+ );
112
+ };
113
+ await Callback();
114
+ BackUpManagement.Callback = Callback;
115
+ return Callback;
105
116
  },
117
+ Callback: async function (params) {},
106
118
  };
107
119
 
108
120
  export { BackUpManagement };
@@ -40,10 +40,22 @@ const fullBuild = async ({
40
40
  }) => {
41
41
  logger.warn('Full build', rootClientPath);
42
42
 
43
- fs.removeSync(rootClientPath);
44
-
45
43
  buildAcmeChallengePath(acmeChallengeFullPath);
46
44
 
45
+ if (publicClientId && publicClientId.startsWith('html-website-templates')) {
46
+ if (!fs.existsSync(`/dd/html-website-templates/`))
47
+ shellExec(`cd /dd && git clone https://github.com/designmodo/html-website-templates.git`);
48
+ if (!fs.existsSync(`${rootClientPath}/index.php`)) {
49
+ fs.copySync(`/dd/html-website-templates/${publicClientId.split('-publicClientId-')[1]}`, rootClientPath);
50
+ shellExec(`cd ${rootClientPath} && git init && git add . && git commit -m "Base template implementation"`);
51
+ // git remote add origin git@github.com:<username>/<repo>.git
52
+ fs.writeFileSync(`${rootClientPath}/.git/.htaccess`, `Deny from all`, 'utf8');
53
+ }
54
+ return;
55
+ }
56
+
57
+ fs.removeSync(rootClientPath);
58
+
47
59
  if (fs.existsSync(`./src/client/public/${publicClientId}`)) {
48
60
  if (iconsBuild) {
49
61
  const defaultBaseIconFolderPath = `src/client/public/${publicClientId}/assets/logo`;
@@ -52,7 +64,7 @@ const fullBuild = async ({
52
64
  if (!fs.existsSync(defaultBaseIconPath))
53
65
  await buildTextImg(metadata.title, { debugFilename: defaultBaseIconPath });
54
66
 
55
- if (path === '/' && !fs.existsSync(`./src/client/public/${publicClientId}/site.webmanifest`))
67
+ if (!fs.existsSync(`./src/client/public/${publicClientId}/site.webmanifest`))
56
68
  await buildIcons({ publicClientId, metadata });
57
69
  }
58
70
  fs.copySync(
@@ -89,8 +101,15 @@ const fullBuild = async ({
89
101
  if (dists)
90
102
  for (const dist of dists) {
91
103
  if ('folder' in dist) {
92
- fs.mkdirSync(`${rootClientPath}${dist.public_folder}`, { recursive: true });
93
- fs.copySync(dist.folder, `${rootClientPath}${dist.public_folder}`);
104
+ if (fs.statSync(dist.folder).isDirectory()) {
105
+ fs.mkdirSync(`${rootClientPath}${dist.public_folder}`, { recursive: true });
106
+ fs.copySync(dist.folder, `${rootClientPath}${dist.public_folder}`);
107
+ } else {
108
+ const folder = dist.public_folder.split('/');
109
+ folder.pop();
110
+ fs.mkdirSync(`${rootClientPath}${folder.join('/')}`, { recursive: true });
111
+ fs.copyFileSync(dist.folder, `${rootClientPath}${dist.public_folder}`);
112
+ }
94
113
  }
95
114
  if ('styles' in dist) {
96
115
  fs.mkdirSync(`${rootClientPath}${dist.public_styles_folder}`, { recursive: true });
@@ -188,7 +207,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
188
207
  iconsBuild,
189
208
  metadata,
190
209
  });
191
- if (apis)
210
+ if (apis && false)
192
211
  for (const apiBuildScript of apis) {
193
212
  const scriptPath = `src/api/${apiBuildScript}/${apiBuildScript}.build.js`;
194
213
  if (fs.existsSync(`./${scriptPath}`)) {
@@ -248,8 +267,13 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
248
267
  'const getBaseHost = () => location.host;',
249
268
  `const getBaseHost = () => '${apiBaseHost}';`,
250
269
  );
251
- if (apiBaseProxyPath)
270
+ if (apiBaseProxyPath) {
252
271
  jsSrc = jsSrc.replace('${getProxyPath()}api/', `${apiBaseProxyPath}${process.env.BASE_API}/`);
272
+ jsSrc = jsSrc.replace(
273
+ "const getWsBasePath = () => (getProxyPath() !== '/' ? `${getProxyPath()}socket.io/` : undefined);",
274
+ `const getWsBasePath = () => '${apiBaseProxyPath}socket.io/';`,
275
+ );
276
+ }
253
277
  }
254
278
  fs.writeFileSync(
255
279
  jsPublicPath,
@@ -288,11 +312,11 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
288
312
  eval(await srcFormatted(fs.readFileSync(`./src/client/ssr/Render.js`, 'utf8')));
289
313
 
290
314
  if (views) {
291
- const buildJsSrcPage = async (jsSrcPath, jsPublicPath) => {
315
+ const buildJsSrcPage = async (jsSrcPath, jsPublicPath, filter) => {
292
316
  if (!(enableLiveRebuild && !options.liveClientBuildPaths.find((p) => p.srcBuildPath === jsSrcPath))) {
293
317
  let jsSrc = viewFormatted(await srcFormatted(fs.readFileSync(jsSrcPath, 'utf8')), dists, path, baseHost);
294
318
  if (jsSrc.split('/*imports*/')[1]) jsSrc = jsSrc.split('/*imports*/')[1];
295
-
319
+ if (filter) jsSrc = await filter(jsSrc);
296
320
  fs.writeFileSync(
297
321
  jsPublicPath,
298
322
  minifyBuild || process.env.NODE_ENV === 'production' ? UglifyJS.minify(jsSrc).code : jsSrc,
@@ -301,47 +325,17 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
301
325
  }
302
326
  };
303
327
 
304
- if (path === '/') {
305
- // service woker
306
- await buildJsSrcPage(
307
- fs.existsSync(`./src/client/sw/${publicClientId}.sw.js`)
308
- ? `./src/client/sw/${publicClientId}.sw.js`
309
- : `./src/client/sw/default.sw.js`,
310
- `${rootClientPath}/sw.js`,
311
- );
312
- }
313
-
314
- // offline html
315
- {
316
- await buildJsSrcPage(
317
- fs.existsSync(`./src/client/ssr/offline/${publicClientId}.index.js`)
318
- ? `./src/client/ssr/offline/${publicClientId}.index.js`
319
- : `./src/client/ssr/offline/default.index.js`,
320
- `${rootClientPath}/offline.js`,
321
- );
322
-
323
- const htmlSrc = Render({
324
- title: metadata?.title ? metadata.title : cap(client),
325
- ssrPath: '/',
326
- ssrHeadComponents: '',
327
- ssrBodyComponents: '',
328
- baseSsrLib: jsSsrCommonComponents + fs.readFileSync(`${rootClientPath}/offline.js`, 'utf8'),
329
- });
328
+ // service woker
329
+ await buildJsSrcPage(
330
+ fs.existsSync(`./src/client/sw/${publicClientId}.sw.js`)
331
+ ? `./src/client/sw/${publicClientId}.sw.js`
332
+ : `./src/client/sw/default.sw.js`,
333
+ `${rootClientPath}/sw.js`,
334
+ path !== '/'
335
+ ? (jsSrc) => jsSrc.replaceAll(`const PROXY_PATH = '/';`, `const PROXY_PATH = '${path}/';`)
336
+ : undefined,
337
+ );
330
338
 
331
- fs.writeFileSync(
332
- `${rootClientPath}offline.html`,
333
- minifyBuild || process.env.NODE_ENV === 'production'
334
- ? await minify(htmlSrc, {
335
- minifyCSS: true,
336
- minifyJS: true,
337
- collapseBooleanAttributes: true,
338
- collapseInlineTagWhitespace: true,
339
- collapseWhitespace: true,
340
- })
341
- : htmlSrc,
342
- 'utf8',
343
- );
344
- }
345
339
  // ssr pages
346
340
  for (const page of await fs.readdir('./src/client/ssr/pages')) {
347
341
  await buildJsSrcPage(`./src/client/ssr/pages/${page}`, `${rootClientPath}/${page}`);
@@ -355,7 +349,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
355
349
  });
356
350
 
357
351
  fs.writeFileSync(
358
- `${rootClientPath}${page.slice(0, -3)}.html`,
352
+ `${rootClientPath}/${page.slice(0, -3)}.html`,
359
353
  minifyBuild || process.env.NODE_ENV === 'production'
360
354
  ? await minify(htmlSrc, {
361
355
  minifyCSS: true,
@@ -432,7 +426,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
432
426
  fs.existsSync(`./src/client/public/${publicClientId}/browserconfig.xml`) &&
433
427
  fs.existsSync(`./src/client/public/${publicClientId}/site.webmanifest`);
434
428
 
435
- if (view.path === '/' && validPwaBuild) {
429
+ if (validPwaBuild) {
436
430
  // build webmanifest
437
431
  const webmanifestJson = JSON.parse(
438
432
  fs.readFileSync(`./src/client/public/${publicClientId}/site.webmanifest`, 'utf8'),
@@ -540,48 +534,6 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
540
534
  host,
541
535
  path,
542
536
  ttiLoadTimeLimit,
543
- storage: {
544
- // 'space-background': fs.readFileSync('./src/client/public/cyberia/space-background', 'utf8'),
545
- lore0: `data:image/jpeg;base64,${fs
546
- .readFileSync('./src/client/public/cyberia/assets/lore/lore0.jpeg')
547
- .toString('base64')}`,
548
- lore1: `data:image/jpeg;base64,${fs
549
- .readFileSync('./src/client/public/cyberia/assets/lore/lore1.jpeg')
550
- .toString('base64')}`,
551
- lore2: `data:image/jpeg;base64,${fs
552
- .readFileSync('./src/client/public/cyberia/assets/lore/lore2.jpeg')
553
- .toString('base64')}`,
554
- lore3: `data:image/jpeg;base64,${fs
555
- .readFileSync('./src/client/public/cyberia/assets/lore/lore3.jpeg')
556
- .toString('base64')}`,
557
- lore4: `data:image/jpeg;base64,${fs
558
- .readFileSync('./src/client/public/cyberia/assets/lore/lore4.jpeg')
559
- .toString('base64')}`,
560
- lore5: `data:image/jpeg;base64,${fs
561
- .readFileSync('./src/client/public/cyberia/assets/lore/lore5.jpeg')
562
- .toString('base64')}`,
563
- lore6: `data:image/jpeg;base64,${fs
564
- .readFileSync('./src/client/public/cyberia/assets/lore/lore6.jpeg')
565
- .toString('base64')}`,
566
- lore7: `data:image/jpeg;base64,${fs
567
- .readFileSync('./src/client/public/cyberia/assets/lore/lore7.jpeg')
568
- .toString('base64')}`,
569
- lore8: `data:image/jpeg;base64,${fs
570
- .readFileSync('./src/client/public/cyberia/assets/lore/lore8.jpeg')
571
- .toString('base64')}`,
572
- ['arrow-left']: `data:image/png;base64,${fs
573
- .readFileSync('./src/client/public/cyberia/assets/ui-icons/arrow-left.png')
574
- .toString('base64')}`,
575
- ['arrow-right']: `data:image/png;base64,${fs
576
- .readFileSync('./src/client/public/cyberia/assets/ui-icons/arrow-right.png')
577
- .toString('base64')}`,
578
- ['fullscreen']: `data:image/png;base64,${fs
579
- .readFileSync('./src/client/public/cyberia/assets/ui-icons/fullscreen.png')
580
- .toString('base64')}`,
581
- ['cyberia-logo']: `data:image/png;base64,${fs
582
- .readFileSync('./src/client/public/cyberia/assets/util/cyberia-retro-banner.png')
583
- .toString('base64')}`,
584
- },
585
537
  });
586
538
  break;
587
539
  }
@@ -686,7 +638,7 @@ Sitemap: https://${host}${path === '/' ? '' : path}/sitemap.xml`,
686
638
  fs.copySync(`./coverage`, coverageBuildPath);
687
639
 
688
640
  // uml
689
- shellExec(`node bin/deploy uml ${host} ${path}`);
641
+ // shellExec(`node bin/deploy uml ${host} ${path}`);
690
642
 
691
643
  // https://swagger-autogen.github.io/docs/
692
644
 
@@ -7,7 +7,7 @@ import cliSpinners from 'cli-spinners';
7
7
  import logUpdate from 'log-update';
8
8
  import colors from 'colors';
9
9
  import { loggerFactory } from './logger.js';
10
- import { shellExec, shellCd } from './process.js';
10
+ import { shellExec, shellCd, getRootDirectory } from './process.js';
11
11
  import { DefaultConf } from '../../conf.js';
12
12
  import ncp from 'copy-paste';
13
13
  import read from 'read';
@@ -44,7 +44,9 @@ const Config = {
44
44
  this.default.server = {};
45
45
  for (const deployId of process.argv[3].split(',')) {
46
46
  let confPath = `./engine-private/conf/${deployId}/conf.server.json`;
47
- const privateConfDevPath = `./engine-private/conf/${deployId}/conf.server.dev.${process.argv[4]}.json`;
47
+ const privateConfDevPath = fs.existsSync(`./engine-private/replica/${deployId}/conf.server.json`)
48
+ ? `./engine-private/replica/${deployId}/conf.server.json`
49
+ : `./engine-private/conf/${deployId}/conf.server.dev.${process.argv[4]}.json`;
48
50
  const confDevPath = fs.existsSync(privateConfDevPath)
49
51
  ? privateConfDevPath
50
52
  : `./engine-private/conf/${deployId}/conf.server.dev.json`;
@@ -89,6 +91,11 @@ const loadConf = (deployId) => {
89
91
  if (!fs.existsSync(`./conf`)) fs.mkdirSync(`./conf`);
90
92
  if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
91
93
  const isValidDeployId = fs.existsSync(`${folder}`);
94
+ if (!isValidDeployId) {
95
+ logger.info(`Save new deploy conf: '${deployId}'`);
96
+ shellExec(`node bin/deploy save ${deployId}`);
97
+ return loadConf(deployId);
98
+ }
92
99
  for (const typeConf of Object.keys(Config.default)) {
93
100
  let srcConf = isValidDeployId
94
101
  ? fs.readFileSync(`${folder}/conf.${typeConf}.json`, 'utf8')
@@ -100,7 +107,6 @@ const loadConf = (deployId) => {
100
107
  if (typeConf === 'server') srcConf = JSON.stringify(loadReplicas(JSON.parse(srcConf)), null, 4);
101
108
  fs.writeFileSync(`./conf/conf.${typeConf}.json`, srcConf, 'utf8');
102
109
  }
103
- if (!isValidDeployId) return {};
104
110
  fs.writeFileSync(`./.env.production`, fs.readFileSync(`${folder}/.env.production`, 'utf8'), 'utf8');
105
111
  fs.writeFileSync(`./.env.development`, fs.readFileSync(`${folder}/.env.development`, 'utf8'), 'utf8');
106
112
  fs.writeFileSync(`./.env.test`, fs.readFileSync(`${folder}/.env.test`, 'utf8'), 'utf8');
@@ -120,10 +126,16 @@ const loadReplicas = (confServer) => {
120
126
  for (const host of Object.keys(confServer)) {
121
127
  for (const path of Object.keys(confServer[host])) {
122
128
  const { replicas, singleReplica } = confServer[host][path];
123
- if (replicas && (process.argv[2] === 'proxy' || !singleReplica))
129
+ if (
130
+ replicas &&
131
+ (process.argv[2] === 'proxy' ||
132
+ !singleReplica ||
133
+ (singleReplica && process.env.NODE_ENV === 'development' && !process.argv[3]))
134
+ )
124
135
  for (const replicaPath of replicas) {
125
136
  confServer[host][replicaPath] = newInstance(confServer[host][path]);
126
137
  delete confServer[host][replicaPath].replicas;
138
+ delete confServer[host][replicaPath].singleReplica;
127
139
  }
128
140
  }
129
141
  }
@@ -246,8 +258,8 @@ const buildClientSrc = async (
246
258
  );
247
259
 
248
260
  fs.writeFileSync(
249
- `./src/client/${toClientVariableName}.js`,
250
- formattedSrc(fs.readFileSync(`./src/client/${fromClientVariableName}.js`, 'utf8')),
261
+ `./src/client/${toClientVariableName}.index.js`,
262
+ formattedSrc(fs.readFileSync(`./src/client/${fromClientVariableName}.index.js`, 'utf8')),
251
263
  'utf8',
252
264
  );
253
265
 
@@ -624,7 +636,7 @@ const deployTest = async (dataDeploy) => {
624
636
  if (singleReplica) continue;
625
637
  const urlTest = `https://${host}${path}`;
626
638
  try {
627
- const result = await axios.get(urlTest);
639
+ const result = await axios.get(urlTest, { timeout: 10000 });
628
640
  const test = result.data.split('<title>');
629
641
  if (test[1])
630
642
  logger.info('Success deploy', {
@@ -660,19 +672,29 @@ const getDeployGroupId = () => {
660
672
  return 'dd';
661
673
  };
662
674
 
675
+ const getDeployId = () => {
676
+ const deployIndexArg = process.argv.findIndex((a) => a.match(`deploy-id:`));
677
+ if (deployIndexArg > -1) return process.argv[deployIndexArg].split(':')[1].trim();
678
+ for (const deployId of process.argv) {
679
+ if (fs.existsSync(`./engine-private/conf/${deployId}`)) return deployId;
680
+ else if (fs.existsSync(`./engine-private/replica/${deployId}`)) return deployId;
681
+ }
682
+ return 'default';
683
+ };
684
+
663
685
  const getCronBackUpFolder = (host = '', path = '') => {
664
686
  return `${host}${path.replace(/\\/g, '/').replace(`/`, '-')}`;
665
687
  };
666
688
 
667
- const execDeploy = async (options = { deployId: 'default' }) => {
689
+ const execDeploy = async (options = { deployId: 'default' }, currentAttempt = 1) => {
668
690
  const { deployId } = options;
669
691
  shellExec(Cmd.delete(deployId));
670
692
  shellExec(Cmd.conf(deployId));
671
693
  shellExec(Cmd.run(deployId));
694
+ const maxTime = 1000 * 60;
695
+ const minTime = 20 * 1000;
696
+ const intervalTime = 1000;
672
697
  return await new Promise(async (resolve) => {
673
- const maxTime = 1000 * 60 * 5;
674
- const minTime = 10000 * 2;
675
- const intervalTime = 1000;
676
698
  let currentTime = 0;
677
699
  const attempt = () => {
678
700
  if (currentTime >= minTime && !fs.existsSync(`./tmp/await-deploy`)) {
@@ -682,27 +704,40 @@ const execDeploy = async (options = { deployId: 'default' }) => {
682
704
  cliSpinner(
683
705
  intervalTime,
684
706
  `[deploy.js] `,
685
- ` Load instance | elapsed time ${currentTime / 1000}s / ${maxTime / 1000}s`,
707
+ ` Load instance | attempt:${currentAttempt} | elapsed time ${currentTime / 1000}s / ${maxTime / 1000}s`,
686
708
  'yellow',
687
709
  'material',
688
710
  );
689
711
  currentTime += intervalTime;
690
- if (currentTime >= maxTime) return resolve(false);
712
+ if (currentTime >= maxTime) {
713
+ clearInterval(processMonitor);
714
+ return resolve(false);
715
+ }
691
716
  };
692
717
  const processMonitor = setInterval(attempt, intervalTime);
693
718
  });
694
719
  };
695
720
 
696
- const deployRun = async (dataDeploy, reset) => {
721
+ const deployRun = async (dataDeploy, currentAttempt = 1) => {
697
722
  if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
698
- if (reset) fs.writeFileSync(`./tmp/runtime-router.json`, '{}', 'utf8');
699
723
  await fixDependencies();
700
- for (const deploy of dataDeploy) await execDeploy(deploy);
724
+ const maxAttempts = 3;
725
+ for (const deploy of dataDeploy) {
726
+ let currentAttempt = 1;
727
+ const attempt = async () => {
728
+ const success = await execDeploy(deploy, currentAttempt);
729
+ currentAttempt++;
730
+ if (!success && currentAttempt <= maxAttempts) await attempt();
731
+ };
732
+ await attempt();
733
+ }
701
734
  const { failed } = await deployTest(dataDeploy);
702
735
  if (failed.length > 0) {
703
736
  for (const deploy of failed) logger.error(deploy.deployId, Cmd.run(deploy.deployId));
704
- await read({ prompt: 'Press enter to retry failed processes\n' });
705
- await deployRun(failed);
737
+ if (currentAttempt === maxAttempts) return logger.error(`max deploy attempts exceeded`);
738
+ if (process.argv.includes('manual')) await read({ prompt: 'Press enter to retry failed processes\n' });
739
+ currentAttempt++;
740
+ await deployRun(failed, currentAttempt);
706
741
  } else logger.info(`Deploy process successfully`);
707
742
  };
708
743
 
@@ -833,6 +868,10 @@ const Cmd = {
833
868
  conf: (deployId, env) => `node bin/deploy conf ${deployId} ${env ? env : 'production'}`,
834
869
  replica: (deployId, host, path) => `node bin/deploy build-single-replica ${deployId} ${host} ${path}`,
835
870
  syncPorts: (deployGroupId) => `node bin/deploy sync-env-port ${deployGroupId}`,
871
+ cron: (deployId, job, expression) => {
872
+ shellExec(Cmd.delete(`${deployId}-${job}`));
873
+ return `env-cmd -f .env.production pm2 start bin/cron.js --no-autorestart --instances 1 --cron "${expression}" --name ${deployId}-${job} -- ${job} ${deployId}`;
874
+ },
836
875
  };
837
876
 
838
877
  const fixDependencies = async () => {
@@ -852,6 +891,27 @@ const fixDependencies = async () => {
852
891
  );
853
892
  };
854
893
 
894
+ const maintenancePath = `${getRootDirectory()}/public/${process.env.DEFAULT_DEPLOY_HOST}${
895
+ process.env.DEFAULT_DEPLOY_PATH
896
+ }/maintenance.html`;
897
+
898
+ const maintenanceMiddleware = (req, res, port, proxyRouter) => {
899
+ if (process.argv.includes('maintenance') && fs.existsSync(maintenancePath)) {
900
+ if (req.method.toUpperCase() === 'GET') return res.status(503).sendFile(maintenancePath);
901
+ return res.status(503).json({
902
+ status: 'error',
903
+ message: 'Server is under maintenance',
904
+ });
905
+ }
906
+ };
907
+
908
+ const setUpProxyMaintenanceServer = ({ deployGroupId }) => {
909
+ shellExec(`pm2 kill`);
910
+ const proxyDeployId = fs.readFileSync(`./engine-private/deploy/${deployGroupId}.proxy`, 'utf8').trim();
911
+ shellExec(`node bin/deploy conf ${proxyDeployId} production`);
912
+ shellExec(`node bin/deploy run ${proxyDeployId} maintenance`);
913
+ };
914
+
855
915
  export {
856
916
  Cmd,
857
917
  Config,
@@ -880,4 +940,8 @@ export {
880
940
  getRestoreCronCmd,
881
941
  mergeBackUp,
882
942
  fixDependencies,
943
+ getDeployId,
944
+ maintenancePath,
945
+ maintenanceMiddleware,
946
+ setUpProxyMaintenanceServer,
883
947
  };