underpost 2.7.8 → 2.7.9
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 +41 -1
- package/.github/workflows/publish.yml +17 -18
- package/.github/workflows/pwa-microservices-template.page.yml +54 -0
- package/.vscode/settings.json +6 -0
- package/CHANGELOG.md +64 -16
- package/bin/cron.js +47 -0
- package/bin/db.js +9 -1
- package/bin/deploy.js +194 -9
- package/bin/file.js +17 -1
- package/bin/index.js +1 -1
- package/bin/util.js +22 -0
- package/conf.js +18 -4
- package/docker-compose.yml +1 -1
- package/package.json +3 -3
- package/src/api/core/core.router.js +9 -9
- package/src/api/core/core.service.js +6 -4
- package/src/api/default/default.service.js +4 -4
- package/src/api/file/file.service.js +3 -3
- package/src/api/user/user.service.js +7 -7
- package/src/client/components/core/CssCore.js +30 -3
- package/src/client/components/core/Docs.js +110 -10
- package/src/client/components/core/Modal.js +224 -22
- package/src/client/components/core/Panel.js +1 -1
- package/src/client/components/core/PanelForm.js +2 -1
- package/src/client/components/core/Responsive.js +15 -0
- package/src/client/components/core/RichText.js +4 -2
- package/src/client/components/core/WebComponent.js +44 -0
- package/src/client/components/core/Worker.js +10 -12
- package/src/client/public/default/plantuml/client-conf.svg +1 -1
- package/src/client/public/default/plantuml/client-schema.svg +1 -1
- package/src/client/public/default/plantuml/cron-conf.svg +1 -1
- package/src/client/public/default/plantuml/cron-schema.svg +1 -1
- package/src/client/public/default/plantuml/server-conf.svg +1 -1
- package/src/client/public/default/plantuml/server-schema.svg +1 -1
- package/src/client/public/default/plantuml/ssr-conf.svg +1 -1
- package/src/client/public/default/plantuml/ssr-schema.svg +1 -1
- package/src/client/public/default/site.webmanifest +69 -0
- package/src/client/ssr/components/body/CacheControl.js +1 -1
- package/src/client/ssr/components/head/Production.js +1 -0
- package/src/client/ssr/components/head/Pwa.js +146 -0
- package/src/client/ssr/components/head/Seo.js +14 -0
- package/src/client/ssr/pages/maintenance.js +14 -0
- package/src/client/ssr/pages/offline.js +21 -0
- package/src/client/sw/default.sw.js +4 -2
- package/src/db/DataBaseProvider.js +12 -1
- package/src/db/mongo/MongooseDB.js +0 -1
- package/src/server/backup.js +82 -70
- package/src/server/client-build.js +23 -90
- package/src/server/conf.js +51 -5
- package/src/server/crypto.js +91 -0
- package/src/server/dns.js +42 -13
- package/src/server/network.js +94 -7
- package/src/server/proxy.js +27 -27
- package/src/server/runtime.js +3 -1
- package/src/client/ssr/offline/default.index.js +0 -31
- package/src/cron.js +0 -30
- package/src/server/cron.js +0 -35
|
@@ -52,7 +52,7 @@ const fullBuild = async ({
|
|
|
52
52
|
if (!fs.existsSync(defaultBaseIconPath))
|
|
53
53
|
await buildTextImg(metadata.title, { debugFilename: defaultBaseIconPath });
|
|
54
54
|
|
|
55
|
-
if (
|
|
55
|
+
if (!fs.existsSync(`./src/client/public/${publicClientId}/site.webmanifest`))
|
|
56
56
|
await buildIcons({ publicClientId, metadata });
|
|
57
57
|
}
|
|
58
58
|
fs.copySync(
|
|
@@ -188,7 +188,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
188
188
|
iconsBuild,
|
|
189
189
|
metadata,
|
|
190
190
|
});
|
|
191
|
-
if (apis)
|
|
191
|
+
if (apis && false)
|
|
192
192
|
for (const apiBuildScript of apis) {
|
|
193
193
|
const scriptPath = `src/api/${apiBuildScript}/${apiBuildScript}.build.js`;
|
|
194
194
|
if (fs.existsSync(`./${scriptPath}`)) {
|
|
@@ -248,8 +248,13 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
248
248
|
'const getBaseHost = () => location.host;',
|
|
249
249
|
`const getBaseHost = () => '${apiBaseHost}';`,
|
|
250
250
|
);
|
|
251
|
-
if (apiBaseProxyPath)
|
|
251
|
+
if (apiBaseProxyPath) {
|
|
252
252
|
jsSrc = jsSrc.replace('${getProxyPath()}api/', `${apiBaseProxyPath}${process.env.BASE_API}/`);
|
|
253
|
+
jsSrc = jsSrc.replace(
|
|
254
|
+
"const getWsBasePath = () => (getProxyPath() !== '/' ? `${getProxyPath()}socket.io/` : undefined);",
|
|
255
|
+
`const getWsBasePath = () => '${apiBaseProxyPath}socket.io/';`,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
253
258
|
}
|
|
254
259
|
fs.writeFileSync(
|
|
255
260
|
jsPublicPath,
|
|
@@ -288,11 +293,11 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
288
293
|
eval(await srcFormatted(fs.readFileSync(`./src/client/ssr/Render.js`, 'utf8')));
|
|
289
294
|
|
|
290
295
|
if (views) {
|
|
291
|
-
const buildJsSrcPage = async (jsSrcPath, jsPublicPath) => {
|
|
296
|
+
const buildJsSrcPage = async (jsSrcPath, jsPublicPath, filter) => {
|
|
292
297
|
if (!(enableLiveRebuild && !options.liveClientBuildPaths.find((p) => p.srcBuildPath === jsSrcPath))) {
|
|
293
298
|
let jsSrc = viewFormatted(await srcFormatted(fs.readFileSync(jsSrcPath, 'utf8')), dists, path, baseHost);
|
|
294
299
|
if (jsSrc.split('/*imports*/')[1]) jsSrc = jsSrc.split('/*imports*/')[1];
|
|
295
|
-
|
|
300
|
+
if (filter) jsSrc = await filter(jsSrc);
|
|
296
301
|
fs.writeFileSync(
|
|
297
302
|
jsPublicPath,
|
|
298
303
|
minifyBuild || process.env.NODE_ENV === 'production' ? UglifyJS.minify(jsSrc).code : jsSrc,
|
|
@@ -301,47 +306,17 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
301
306
|
}
|
|
302
307
|
};
|
|
303
308
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
});
|
|
309
|
+
// service woker
|
|
310
|
+
await buildJsSrcPage(
|
|
311
|
+
fs.existsSync(`./src/client/sw/${publicClientId}.sw.js`)
|
|
312
|
+
? `./src/client/sw/${publicClientId}.sw.js`
|
|
313
|
+
: `./src/client/sw/default.sw.js`,
|
|
314
|
+
`${rootClientPath}/sw.js`,
|
|
315
|
+
path !== '/'
|
|
316
|
+
? (jsSrc) => jsSrc.replaceAll(`const PROXY_PATH = '/';`, `const PROXY_PATH = '${path}/';`)
|
|
317
|
+
: undefined,
|
|
318
|
+
);
|
|
330
319
|
|
|
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
320
|
// ssr pages
|
|
346
321
|
for (const page of await fs.readdir('./src/client/ssr/pages')) {
|
|
347
322
|
await buildJsSrcPage(`./src/client/ssr/pages/${page}`, `${rootClientPath}/${page}`);
|
|
@@ -355,7 +330,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
355
330
|
});
|
|
356
331
|
|
|
357
332
|
fs.writeFileSync(
|
|
358
|
-
`${rootClientPath}
|
|
333
|
+
`${rootClientPath}/${page.slice(0, -3)}.html`,
|
|
359
334
|
minifyBuild || process.env.NODE_ENV === 'production'
|
|
360
335
|
? await minify(htmlSrc, {
|
|
361
336
|
minifyCSS: true,
|
|
@@ -432,7 +407,7 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
432
407
|
fs.existsSync(`./src/client/public/${publicClientId}/browserconfig.xml`) &&
|
|
433
408
|
fs.existsSync(`./src/client/public/${publicClientId}/site.webmanifest`);
|
|
434
409
|
|
|
435
|
-
if (
|
|
410
|
+
if (validPwaBuild) {
|
|
436
411
|
// build webmanifest
|
|
437
412
|
const webmanifestJson = JSON.parse(
|
|
438
413
|
fs.readFileSync(`./src/client/public/${publicClientId}/site.webmanifest`, 'utf8'),
|
|
@@ -540,48 +515,6 @@ const buildClient = async (options = { liveClientBuildPaths: [], instances: [] }
|
|
|
540
515
|
host,
|
|
541
516
|
path,
|
|
542
517
|
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
518
|
});
|
|
586
519
|
break;
|
|
587
520
|
}
|
|
@@ -686,7 +619,7 @@ Sitemap: https://${host}${path === '/' ? '' : path}/sitemap.xml`,
|
|
|
686
619
|
fs.copySync(`./coverage`, coverageBuildPath);
|
|
687
620
|
|
|
688
621
|
// uml
|
|
689
|
-
shellExec(`node bin/deploy uml ${host} ${path}`);
|
|
622
|
+
// shellExec(`node bin/deploy uml ${host} ${path}`);
|
|
690
623
|
|
|
691
624
|
// https://swagger-autogen.github.io/docs/
|
|
692
625
|
|
package/src/server/conf.js
CHANGED
|
@@ -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/
|
|
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')
|
|
@@ -246,8 +253,8 @@ const buildClientSrc = async (
|
|
|
246
253
|
);
|
|
247
254
|
|
|
248
255
|
fs.writeFileSync(
|
|
249
|
-
`./src/client/${toClientVariableName}.js`,
|
|
250
|
-
formattedSrc(fs.readFileSync(`./src/client/${fromClientVariableName}.js`, 'utf8')),
|
|
256
|
+
`./src/client/${toClientVariableName}.index.js`,
|
|
257
|
+
formattedSrc(fs.readFileSync(`./src/client/${fromClientVariableName}.index.js`, 'utf8')),
|
|
251
258
|
'utf8',
|
|
252
259
|
);
|
|
253
260
|
|
|
@@ -660,6 +667,16 @@ const getDeployGroupId = () => {
|
|
|
660
667
|
return 'dd';
|
|
661
668
|
};
|
|
662
669
|
|
|
670
|
+
const getDeployId = () => {
|
|
671
|
+
const deployIndexArg = process.argv.findIndex((a) => a.match(`deploy-id:`));
|
|
672
|
+
if (deployIndexArg > -1) return process.argv[deployIndexArg].split(':')[1].trim();
|
|
673
|
+
for (const deployId of process.argv) {
|
|
674
|
+
if (fs.existsSync(`./engine-private/conf/${deployId}`)) return deployId;
|
|
675
|
+
else if (fs.existsSync(`./engine-private/replica/${deployId}`)) return deployId;
|
|
676
|
+
}
|
|
677
|
+
return 'default';
|
|
678
|
+
};
|
|
679
|
+
|
|
663
680
|
const getCronBackUpFolder = (host = '', path = '') => {
|
|
664
681
|
return `${host}${path.replace(/\\/g, '/').replace(`/`, '-')}`;
|
|
665
682
|
};
|
|
@@ -671,7 +688,7 @@ const execDeploy = async (options = { deployId: 'default' }) => {
|
|
|
671
688
|
shellExec(Cmd.run(deployId));
|
|
672
689
|
return await new Promise(async (resolve) => {
|
|
673
690
|
const maxTime = 1000 * 60 * 5;
|
|
674
|
-
const minTime =
|
|
691
|
+
const minTime = 7 * 1000;
|
|
675
692
|
const intervalTime = 1000;
|
|
676
693
|
let currentTime = 0;
|
|
677
694
|
const attempt = () => {
|
|
@@ -833,6 +850,10 @@ const Cmd = {
|
|
|
833
850
|
conf: (deployId, env) => `node bin/deploy conf ${deployId} ${env ? env : 'production'}`,
|
|
834
851
|
replica: (deployId, host, path) => `node bin/deploy build-single-replica ${deployId} ${host} ${path}`,
|
|
835
852
|
syncPorts: (deployGroupId) => `node bin/deploy sync-env-port ${deployGroupId}`,
|
|
853
|
+
cron: (deployId, job, expression) => {
|
|
854
|
+
shellExec(Cmd.delete(`${deployId}-${job}`));
|
|
855
|
+
return `env-cmd -f .env.production pm2 start bin/cron.js --no-autorestart --instances 1 --cron "${expression}" --name ${deployId}-${job} -- ${job} ${deployId}`;
|
|
856
|
+
},
|
|
836
857
|
};
|
|
837
858
|
|
|
838
859
|
const fixDependencies = async () => {
|
|
@@ -852,6 +873,27 @@ const fixDependencies = async () => {
|
|
|
852
873
|
);
|
|
853
874
|
};
|
|
854
875
|
|
|
876
|
+
const maintenancePath = `${getRootDirectory()}/public/${process.env.DEFAULT_DEPLOY_HOST}${
|
|
877
|
+
process.env.DEFAULT_DEPLOY_PATH
|
|
878
|
+
}/maintenance.html`;
|
|
879
|
+
|
|
880
|
+
const maintenanceMiddleware = (req, res, port, proxyRouter) => {
|
|
881
|
+
if (process.argv.includes('maintenance') && fs.existsSync(maintenancePath)) {
|
|
882
|
+
if (req.method.toUpperCase() === 'GET') return res.status(503).sendFile(maintenancePath);
|
|
883
|
+
return res.status(503).json({
|
|
884
|
+
status: 'error',
|
|
885
|
+
message: 'Server is under maintenance',
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
const setUpProxyMaintenanceServer = ({ deployGroupId }) => {
|
|
891
|
+
shellExec(`pm2 kill`);
|
|
892
|
+
const proxyDeployId = fs.readFileSync(`./engine-private/deploy/${deployGroupId}.proxy`, 'utf8').trim();
|
|
893
|
+
shellExec(`node bin/deploy conf ${proxyDeployId} production`);
|
|
894
|
+
shellExec(`node bin/deploy run ${proxyDeployId} maintenance`);
|
|
895
|
+
};
|
|
896
|
+
|
|
855
897
|
export {
|
|
856
898
|
Cmd,
|
|
857
899
|
Config,
|
|
@@ -880,4 +922,8 @@ export {
|
|
|
880
922
|
getRestoreCronCmd,
|
|
881
923
|
mergeBackUp,
|
|
882
924
|
fixDependencies,
|
|
925
|
+
getDeployId,
|
|
926
|
+
maintenancePath,
|
|
927
|
+
maintenanceMiddleware,
|
|
928
|
+
setUpProxyMaintenanceServer,
|
|
883
929
|
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
|
|
4
|
+
const CryptoBuilder = {
|
|
5
|
+
symmetric: {
|
|
6
|
+
instance: function (options = { iv: '', encryptionKey: '' }) {
|
|
7
|
+
// Generate a random 32-byte encryption key
|
|
8
|
+
const encryptionKey = option?.encryptionKey ? options.encryptionKey : crypto.randomBytes(32);
|
|
9
|
+
const iv = option?.iv ? options.iv : crypto.randomBytes(16); // Generate a new Initialization Vector (IV) for each encryption
|
|
10
|
+
|
|
11
|
+
// Function to encrypt data
|
|
12
|
+
function encryptData(plaintext = '') {
|
|
13
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', encryptionKey, iv);
|
|
14
|
+
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
|
|
15
|
+
encrypted += cipher.final('hex');
|
|
16
|
+
return `${iv.toString('hex')}:${encrypted}`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Function to decrypt data
|
|
20
|
+
function decryptData(ciphertext = '') {
|
|
21
|
+
const [ivHex, encrypted] = ciphertext.split(':');
|
|
22
|
+
const _iv = Buffer.from(ivHex, 'hex');
|
|
23
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', encryptionKey, _iv);
|
|
24
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
|
25
|
+
decrypted += decipher.final('utf8');
|
|
26
|
+
return decrypted;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
encryptionKey,
|
|
31
|
+
iv,
|
|
32
|
+
encryptData,
|
|
33
|
+
decryptData,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
asymmetric: {
|
|
38
|
+
instance: function (
|
|
39
|
+
options = {
|
|
40
|
+
publicKey: '', // fs.readFileSync('./key.pem', 'utf8')
|
|
41
|
+
privateKey: '', // fs.readFileSync('./key.pem', 'utf8')
|
|
42
|
+
},
|
|
43
|
+
) {
|
|
44
|
+
// Generate a new key pair
|
|
45
|
+
const { privateKey, publicKey } = options
|
|
46
|
+
? options
|
|
47
|
+
: crypto.generateKeyPairSync('rsa', {
|
|
48
|
+
modulusLength: 2048, // Key size in bits
|
|
49
|
+
publicKeyEncoding: {
|
|
50
|
+
type: 'spki',
|
|
51
|
+
format: 'pem',
|
|
52
|
+
},
|
|
53
|
+
privateKeyEncoding: {
|
|
54
|
+
type: 'pkcs8',
|
|
55
|
+
format: 'pem',
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Function to encrypt data
|
|
60
|
+
function encryptData(plaintext) {
|
|
61
|
+
const buffer = Buffer.from(plaintext, 'utf8');
|
|
62
|
+
const encrypted = crypto.publicEncrypt(publicKey, buffer);
|
|
63
|
+
return encrypted.toString('hex');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Function to decrypt data
|
|
67
|
+
function decryptData(ciphertext) {
|
|
68
|
+
const buffer = Buffer.from(ciphertext, 'hex');
|
|
69
|
+
const decrypted = crypto.privateDecrypt(privateKey, buffer);
|
|
70
|
+
return decrypted.toString('utf8');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fs.writeFileSync('./public.pem', publicKey);
|
|
74
|
+
fs.writeFileSync('./private.pem', privateKey);
|
|
75
|
+
|
|
76
|
+
const result = {
|
|
77
|
+
privateKey: fs.readFileSync('./public.pem', 'utf8'),
|
|
78
|
+
publicKey: fs.readFileSync('./private.pem', 'utf8'),
|
|
79
|
+
encryptData,
|
|
80
|
+
decryptData,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
fs.removeSync('./public.pem');
|
|
84
|
+
fs.removeSync('./private.pem');
|
|
85
|
+
|
|
86
|
+
return result;
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export { CryptoBuilder };
|
package/src/server/dns.js
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import dotenv from 'dotenv';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
-
import
|
|
4
|
+
import https from 'https';
|
|
5
5
|
|
|
6
6
|
import { ip } from './network.js';
|
|
7
7
|
import { loggerFactory } from './logger.js';
|
|
8
8
|
import { isIPv4 } from 'is-ip';
|
|
9
|
+
import { shellExec } from './process.js';
|
|
10
|
+
|
|
11
|
+
const httpsAgent = new https.Agent({
|
|
12
|
+
rejectUnauthorized: false,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
axios.defaults.httpsAgent = httpsAgent;
|
|
9
16
|
|
|
10
17
|
dotenv.config();
|
|
11
18
|
|
|
12
19
|
const logger = loggerFactory(import.meta);
|
|
13
20
|
|
|
14
21
|
const Dns = {
|
|
15
|
-
|
|
16
|
-
ipDaemon: null,
|
|
22
|
+
repoUrl: `https://${process.env.GITHUB_TOKEN}@github.com/${process.env.GITHUB_USERNAME}/${process.env.GITHUB_DNS_REPO}.git`,
|
|
17
23
|
callback: () => null,
|
|
18
|
-
InitIpDaemon: async function () {
|
|
24
|
+
InitIpDaemon: async function ({ deployId }) {
|
|
19
25
|
// WAN | NAT-VPS | LAN
|
|
20
26
|
// enabled DMZ Host to proxy IP 80-443 (79-444) sometimes router block first port
|
|
21
27
|
// LAN server or device's local servers port -> 3000-3100 (2999-3101)
|
|
@@ -23,16 +29,16 @@ const Dns = {
|
|
|
23
29
|
// DHCP (Dynamic Host Configuration Protocol) LAN reserver IP -> MAC ID
|
|
24
30
|
// Forward the router's TCP/UDP ports to the LAN device's IP address
|
|
25
31
|
|
|
26
|
-
const privateCronConfPath = `./engine-private/conf/${
|
|
32
|
+
const privateCronConfPath = `./engine-private/conf/${deployId}/conf.cron.json`;
|
|
27
33
|
|
|
28
34
|
const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
|
|
29
|
-
|
|
30
35
|
let confCronData = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
|
|
31
36
|
if (confCronData.ipDaemon.disabled) return;
|
|
32
37
|
Dns.ip = confCronData.ipDaemon.ip;
|
|
33
38
|
logger.info(`Current ip`, Dns.ip);
|
|
34
|
-
if (Dns.ipDaemon) clearInterval(Dns.ipDaemon);
|
|
35
39
|
const callback = async () => {
|
|
40
|
+
logger.info('init dns ip callback');
|
|
41
|
+
await logger.setUpInfo();
|
|
36
42
|
let testIp;
|
|
37
43
|
try {
|
|
38
44
|
testIp = await ip.public.ipv4();
|
|
@@ -41,15 +47,12 @@ const Dns = {
|
|
|
41
47
|
}
|
|
42
48
|
if (testIp && typeof testIp === 'string' && isIPv4(testIp) && Dns.ip !== testIp) {
|
|
43
49
|
logger.info(`New ip`, testIp);
|
|
44
|
-
Dns.ip = testIp;
|
|
45
|
-
confCronData.ipDaemon.ip = Dns.ip;
|
|
46
|
-
fs.writeFileSync(confCronPath, JSON.stringify(confCronData, null, 4), 'utf8');
|
|
47
50
|
for (const recordType of Object.keys(confCronData.records)) {
|
|
48
51
|
switch (recordType) {
|
|
49
52
|
case 'A':
|
|
50
53
|
for (const dnsProvider of confCronData.records[recordType]) {
|
|
51
54
|
if (typeof Dns.services.updateIp[dnsProvider.dns] === 'function')
|
|
52
|
-
await Dns.services.updateIp[dnsProvider.dns](dnsProvider);
|
|
55
|
+
await Dns.services.updateIp[dnsProvider.dns]({ ...dnsProvider, ip: testIp });
|
|
53
56
|
}
|
|
54
57
|
break;
|
|
55
58
|
|
|
@@ -57,16 +60,28 @@ const Dns = {
|
|
|
57
60
|
break;
|
|
58
61
|
}
|
|
59
62
|
}
|
|
63
|
+
try {
|
|
64
|
+
const ipUrlTest = `https://${process.env.DEFAULT_DEPLOY_HOST}`;
|
|
65
|
+
const response = await axios.get(ipUrlTest);
|
|
66
|
+
const verifyIp = response.request.socket.remoteAddress;
|
|
67
|
+
logger.info(ipUrlTest + ' IP', verifyIp);
|
|
68
|
+
if (verifyIp === testIp) {
|
|
69
|
+
await this.saveIp(confCronPath, confCronData, testIp);
|
|
70
|
+
} else logger.error('ip not updated');
|
|
71
|
+
} catch (error) {
|
|
72
|
+
logger.error(error), 'ip not updated';
|
|
73
|
+
}
|
|
60
74
|
}
|
|
61
75
|
};
|
|
76
|
+
await callback();
|
|
62
77
|
this.callback = callback;
|
|
63
78
|
return callback;
|
|
64
79
|
},
|
|
65
80
|
services: {
|
|
66
81
|
updateIp: {
|
|
67
82
|
dondominio: (options) => {
|
|
68
|
-
const { user, api_key, host, dns } = options;
|
|
69
|
-
const url = `https://dondns.dondominio.com/json/?user=${user}&password=${api_key}&host=${host}&ip=${
|
|
83
|
+
const { user, api_key, host, dns, ip } = options;
|
|
84
|
+
const url = `https://dondns.dondominio.com/json/?user=${user}&password=${api_key}&host=${host}&ip=${ip}`;
|
|
70
85
|
logger.info(`${dns} update ip url`, url);
|
|
71
86
|
if (process.env.NODE_ENV !== 'production') return false;
|
|
72
87
|
return new Promise((resolve) => {
|
|
@@ -84,6 +99,20 @@ const Dns = {
|
|
|
84
99
|
},
|
|
85
100
|
},
|
|
86
101
|
},
|
|
102
|
+
saveIp: async (confCronPath, confCronData, ip) => {
|
|
103
|
+
Dns.ip = ip;
|
|
104
|
+
confCronData.ipDaemon.ip = ip;
|
|
105
|
+
fs.writeFileSync(confCronPath, JSON.stringify(confCronData, null, 4), 'utf8');
|
|
106
|
+
shellExec(
|
|
107
|
+
`cd ./engine-private` +
|
|
108
|
+
` && git pull ${Dns.repoUrl}` +
|
|
109
|
+
` && git add . && git commit -m "update ip ${new Date().toLocaleDateString()}"` +
|
|
110
|
+
` && git push ${Dns.repoUrl}`,
|
|
111
|
+
{
|
|
112
|
+
disableLog: true,
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
},
|
|
87
116
|
};
|
|
88
117
|
|
|
89
118
|
export { Dns };
|
package/src/server/network.js
CHANGED
|
@@ -5,6 +5,8 @@ import { publicIp, publicIpv4, publicIpv6 } from 'public-ip';
|
|
|
5
5
|
import { killPortProcess } from 'kill-port-process';
|
|
6
6
|
import { loggerFactory } from './logger.js';
|
|
7
7
|
import { orderArrayFromAttrInt } from '../client/components/core/CommonJs.js';
|
|
8
|
+
import { DataBaseProvider } from '../db/DataBaseProvider.js';
|
|
9
|
+
import { getDeployId } from './conf.js';
|
|
8
10
|
|
|
9
11
|
// Network Address Translation Management
|
|
10
12
|
|
|
@@ -66,12 +68,86 @@ const logRuntimeRouter = () => {
|
|
|
66
68
|
logger.info('Runtime network', displayLog);
|
|
67
69
|
};
|
|
68
70
|
|
|
69
|
-
const saveRuntimeRouter = () =>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
const saveRuntimeRouter = async () => {
|
|
72
|
+
try {
|
|
73
|
+
const deployId = process.env.DEFAULT_DEPLOY_ID;
|
|
74
|
+
const host = process.env.DEFAULT_DEPLOY_HOST;
|
|
75
|
+
const path = process.env.DEFAULT_DEPLOY_PATH;
|
|
76
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
77
|
+
const confServer = JSON.parse(fs.readFileSync(confServerPath, 'utf8'));
|
|
78
|
+
const { db } = confServer[host][path];
|
|
79
|
+
|
|
80
|
+
let closeConn;
|
|
81
|
+
if (!DataBaseProvider.instance[`${host}${path}`]) {
|
|
82
|
+
await DataBaseProvider.load({ apis: ['instance'], host, path, db });
|
|
83
|
+
closeConn = true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** @type {import('../api/instance/instance.model.js').InstanceModel} */
|
|
87
|
+
const Instance = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Instance;
|
|
88
|
+
|
|
89
|
+
for (const _host of Object.keys(networkRouter)) {
|
|
90
|
+
for (const _path of Object.keys(networkRouter[_host])) {
|
|
91
|
+
const body = {
|
|
92
|
+
host: _host,
|
|
93
|
+
path: _path,
|
|
94
|
+
deployId: getDeployId(),
|
|
95
|
+
client: networkRouter[_host][_path].client,
|
|
96
|
+
runtime: networkRouter[_host][_path].runtime,
|
|
97
|
+
port: networkRouter[_host][_path].port,
|
|
98
|
+
apis: networkRouter[_host][_path].apis,
|
|
99
|
+
};
|
|
100
|
+
const instance = await Instance.findOne({ deployId: body.deployId, port: body.port });
|
|
101
|
+
if (instance) {
|
|
102
|
+
await Instance.findByIdAndUpdate(instance._id, body);
|
|
103
|
+
} else {
|
|
104
|
+
await new Instance(body).save();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (closeConn) await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
|
|
110
|
+
} catch (error) {
|
|
111
|
+
logger.error(error);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const netWorkCron = [];
|
|
116
|
+
|
|
117
|
+
const saveRuntimeCron = async () => {
|
|
118
|
+
try {
|
|
119
|
+
const deployId = process.env.DEFAULT_DEPLOY_ID;
|
|
120
|
+
const host = process.env.DEFAULT_DEPLOY_HOST;
|
|
121
|
+
const path = process.env.DEFAULT_DEPLOY_PATH;
|
|
122
|
+
const confServerPath = `./engine-private/conf/${deployId}/conf.server.json`;
|
|
123
|
+
const confServer = JSON.parse(fs.readFileSync(confServerPath, 'utf8'));
|
|
124
|
+
const { db } = confServer[host][path];
|
|
125
|
+
|
|
126
|
+
let closeConn;
|
|
127
|
+
if (!DataBaseProvider.instance[`${host}${path}`]) {
|
|
128
|
+
await DataBaseProvider.load({ apis: ['cron'], host, path, db });
|
|
129
|
+
closeConn = true;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** @type {import('../api/cron/cron.model.js').CronModel} */
|
|
133
|
+
const Cron = DataBaseProvider.instance[`${host}${path}`].mongoose.models.Cron;
|
|
134
|
+
|
|
135
|
+
// await Cron.insertMany(netWorkCron);
|
|
136
|
+
|
|
137
|
+
for (const cronInstance of netWorkCron) {
|
|
138
|
+
const cron = await Cron.findOne({ deployId: cronInstance.deployId, jobId: cronInstance.jobId });
|
|
139
|
+
if (cron) {
|
|
140
|
+
await Cron.findByIdAndUpdate(cron._id, cronInstance);
|
|
141
|
+
} else {
|
|
142
|
+
await new Cron(cronInstance).save();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (closeConn) await DataBaseProvider.instance[`${host}${path}`].mongoose.close();
|
|
147
|
+
} catch (error) {
|
|
148
|
+
logger.error(error);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
75
151
|
|
|
76
152
|
const listenServerFactory = (logic = async () => {}) => {
|
|
77
153
|
return {
|
|
@@ -109,6 +185,7 @@ const listenPortController = async (server, port, metadata) =>
|
|
|
109
185
|
? `https://${host}${path}`
|
|
110
186
|
: `http://${host}:${port}${path}`,
|
|
111
187
|
local: `http://localhost:${port}${path}`,
|
|
188
|
+
apis: metadata.apis,
|
|
112
189
|
};
|
|
113
190
|
|
|
114
191
|
return resolve(true);
|
|
@@ -119,4 +196,14 @@ const listenPortController = async (server, port, metadata) =>
|
|
|
119
196
|
}
|
|
120
197
|
});
|
|
121
198
|
|
|
122
|
-
export {
|
|
199
|
+
export {
|
|
200
|
+
ip,
|
|
201
|
+
network,
|
|
202
|
+
listenPortController,
|
|
203
|
+
networkRouter,
|
|
204
|
+
netWorkCron,
|
|
205
|
+
saveRuntimeRouter,
|
|
206
|
+
logRuntimeRouter,
|
|
207
|
+
listenServerFactory,
|
|
208
|
+
saveRuntimeCron,
|
|
209
|
+
};
|