underpost 2.7.7 → 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 +115 -0
- package/.github/workflows/publish.yml +20 -3
- package/.github/workflows/pwa-microservices-template.page.yml +54 -0
- package/.github/workflows/pwa-microservices-template.test.yml +30 -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 +207 -11
- 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/Css.js +0 -222
- 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/Translate.js +6 -2
- package/src/client/components/core/WebComponent.js +44 -0
- package/src/client/components/core/Worker.js +12 -4
- 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/services/default/default.management.js +118 -120
- package/src/client/ssr/Render.js +224 -3
- package/src/client/ssr/common/Alert.js +75 -0
- package/src/client/ssr/common/SsrCore.js +91 -0
- package/src/client/ssr/common/Translate.js +26 -0
- package/src/client/ssr/common/Worker.js +28 -0
- package/src/client/ssr/{body-components → components/body}/CacheControl.js +1 -1
- package/src/client/ssr/{body-components → components/body}/DefaultSplashScreen.js +15 -4
- package/src/client/ssr/components/head/Pwa.js +146 -0
- package/src/client/ssr/pages/404.js +12 -0
- package/src/client/ssr/pages/500.js +12 -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 +13 -9
- package/src/db/DataBaseProvider.js +12 -1
- package/src/db/mongo/MongooseDB.js +0 -1
- package/src/mailer/EmailRender.js +1 -1
- package/src/server/backup.js +82 -70
- package/src/server/client-build-live.js +6 -0
- package/src/server/client-build.js +76 -73
- package/src/server/client-formatted.js +11 -1
- package/src/server/client-icons.js +1 -1
- package/src/server/conf.js +60 -12
- 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 +27 -8
- package/.github/workflows/test.yml +0 -80
- package/src/client/ssr/head-components/Microdata.js +0 -11
- package/src/cron.js +0 -30
- package/src/server/cron.js +0 -35
- /package/src/client/ssr/{email-components → components/email}/DefaultRecoverEmail.js +0 -0
- /package/src/client/ssr/{email-components → components/email}/DefaultVerifyEmail.js +0 -0
- /package/src/client/ssr/{head-components → components/head}/Css.js +0 -0
- /package/src/client/ssr/{head-components → components/head}/DefaultScripts.js +0 -0
- /package/src/client/ssr/{head-components → components/head}/Production.js +0 -0
- /package/src/client/ssr/{head-components → components/head}/PwaDefault.js +0 -0
- /package/src/client/ssr/{head-components → components/head}/Seo.js +0 -0
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')
|
|
@@ -240,14 +247,14 @@ const buildClientSrc = async (
|
|
|
240
247
|
}
|
|
241
248
|
|
|
242
249
|
fs.writeFileSync(
|
|
243
|
-
`./src/client/ssr/head
|
|
244
|
-
formattedSrc(fs.readFileSync(`./src/client/ssr/head
|
|
250
|
+
`./src/client/ssr/components/head/${toClientVariableName}Scripts.js`,
|
|
251
|
+
formattedSrc(fs.readFileSync(`./src/client/ssr/components/head/${fromClientVariableName}Scripts.js`, 'utf8')),
|
|
245
252
|
'utf8',
|
|
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
|
|
|
@@ -560,7 +567,7 @@ const validateTemplatePath = (absolutePath = '') => {
|
|
|
560
567
|
const confServer = DefaultConf.server[host][path];
|
|
561
568
|
const confClient = DefaultConf.client[client];
|
|
562
569
|
const confSsr = DefaultConf.ssr[ssr];
|
|
563
|
-
const clients = Object.keys(confClient).concat(['core', 'test']);
|
|
570
|
+
const clients = Object.keys(confClient).concat(['core', 'test', 'default']);
|
|
564
571
|
|
|
565
572
|
if (absolutePath.match('src/api') && !confServer.apis.find((p) => absolutePath.match(`src/api/${p}/`))) {
|
|
566
573
|
return false;
|
|
@@ -584,14 +591,14 @@ const validateTemplatePath = (absolutePath = '') => {
|
|
|
584
591
|
return false;
|
|
585
592
|
}
|
|
586
593
|
if (
|
|
587
|
-
absolutePath.match('src/client/ssr/body
|
|
588
|
-
!confSsr.body.find((p) => absolutePath.match(`src/client/ssr/body
|
|
594
|
+
absolutePath.match('src/client/ssr/components/body') &&
|
|
595
|
+
!confSsr.body.find((p) => absolutePath.match(`src/client/ssr/components/body/${p}.js`))
|
|
589
596
|
) {
|
|
590
597
|
return false;
|
|
591
598
|
}
|
|
592
599
|
if (
|
|
593
|
-
absolutePath.match('src/client/ssr/head
|
|
594
|
-
!confSsr.head.find((p) => absolutePath.match(`src/client/ssr/head
|
|
600
|
+
absolutePath.match('src/client/ssr/components/head') &&
|
|
601
|
+
!confSsr.head.find((p) => absolutePath.match(`src/client/ssr/components/head/${p}.js`))
|
|
595
602
|
) {
|
|
596
603
|
return false;
|
|
597
604
|
}
|
|
@@ -599,6 +606,7 @@ const validateTemplatePath = (absolutePath = '') => {
|
|
|
599
606
|
if (
|
|
600
607
|
absolutePath.match('/client') &&
|
|
601
608
|
absolutePath.match('.index.js') &&
|
|
609
|
+
!absolutePath.match('/offline') &&
|
|
602
610
|
!clients.find((p) => absolutePath.match(`src/client/${capFirst(p)}.index.js`))
|
|
603
611
|
) {
|
|
604
612
|
return false;
|
|
@@ -659,6 +667,16 @@ const getDeployGroupId = () => {
|
|
|
659
667
|
return 'dd';
|
|
660
668
|
};
|
|
661
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
|
+
|
|
662
680
|
const getCronBackUpFolder = (host = '', path = '') => {
|
|
663
681
|
return `${host}${path.replace(/\\/g, '/').replace(`/`, '-')}`;
|
|
664
682
|
};
|
|
@@ -670,7 +688,7 @@ const execDeploy = async (options = { deployId: 'default' }) => {
|
|
|
670
688
|
shellExec(Cmd.run(deployId));
|
|
671
689
|
return await new Promise(async (resolve) => {
|
|
672
690
|
const maxTime = 1000 * 60 * 5;
|
|
673
|
-
const minTime =
|
|
691
|
+
const minTime = 7 * 1000;
|
|
674
692
|
const intervalTime = 1000;
|
|
675
693
|
let currentTime = 0;
|
|
676
694
|
const attempt = () => {
|
|
@@ -695,6 +713,7 @@ const execDeploy = async (options = { deployId: 'default' }) => {
|
|
|
695
713
|
const deployRun = async (dataDeploy, reset) => {
|
|
696
714
|
if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
|
|
697
715
|
if (reset) fs.writeFileSync(`./tmp/runtime-router.json`, '{}', 'utf8');
|
|
716
|
+
await fixDependencies();
|
|
698
717
|
for (const deploy of dataDeploy) await execDeploy(deploy);
|
|
699
718
|
const { failed } = await deployTest(dataDeploy);
|
|
700
719
|
if (failed.length > 0) {
|
|
@@ -831,6 +850,10 @@ const Cmd = {
|
|
|
831
850
|
conf: (deployId, env) => `node bin/deploy conf ${deployId} ${env ? env : 'production'}`,
|
|
832
851
|
replica: (deployId, host, path) => `node bin/deploy build-single-replica ${deployId} ${host} ${path}`,
|
|
833
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
|
+
},
|
|
834
857
|
};
|
|
835
858
|
|
|
836
859
|
const fixDependencies = async () => {
|
|
@@ -850,6 +873,27 @@ const fixDependencies = async () => {
|
|
|
850
873
|
);
|
|
851
874
|
};
|
|
852
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
|
+
|
|
853
897
|
export {
|
|
854
898
|
Cmd,
|
|
855
899
|
Config,
|
|
@@ -878,4 +922,8 @@ export {
|
|
|
878
922
|
getRestoreCronCmd,
|
|
879
923
|
mergeBackUp,
|
|
880
924
|
fixDependencies,
|
|
925
|
+
getDeployId,
|
|
926
|
+
maintenancePath,
|
|
927
|
+
maintenanceMiddleware,
|
|
928
|
+
setUpProxyMaintenanceServer,
|
|
881
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
|
+
};
|
package/src/server/proxy.js
CHANGED
|
@@ -9,7 +9,8 @@ import { loggerFactory, loggerMiddleware } from './logger.js';
|
|
|
9
9
|
import { listenPortController, network } from './network.js';
|
|
10
10
|
import { orderArrayFromAttrInt } from '../client/components/core/CommonJs.js';
|
|
11
11
|
import { createSslServer, sslRedirectMiddleware } from './ssl.js';
|
|
12
|
-
import { buildProxyRouter } from './conf.js';
|
|
12
|
+
import { buildProxyRouter, maintenanceMiddleware } from './conf.js';
|
|
13
|
+
import { getRootDirectory } from './process.js';
|
|
13
14
|
|
|
14
15
|
dotenv.config();
|
|
15
16
|
|
|
@@ -25,6 +26,9 @@ const buildProxy = async () => {
|
|
|
25
26
|
for (let port of Object.keys(proxyRouter)) {
|
|
26
27
|
port = parseInt(port);
|
|
27
28
|
const hosts = proxyRouter[port];
|
|
29
|
+
const proxyPath = '/';
|
|
30
|
+
const proxyHost = 'localhost';
|
|
31
|
+
const runningData = { host: proxyHost, path: proxyPath, client: null, runtime: 'nodejs', meta: import.meta };
|
|
28
32
|
const app = express();
|
|
29
33
|
|
|
30
34
|
// set logger
|
|
@@ -47,6 +51,7 @@ const buildProxy = async () => {
|
|
|
47
51
|
onProxyReq: (proxyReq, req, res, options) => {
|
|
48
52
|
// https://wtools.io/check-http-status-code
|
|
49
53
|
// http://nexodev.org
|
|
54
|
+
maintenanceMiddleware(req, res, port, proxyRouter);
|
|
50
55
|
sslRedirectMiddleware(req, res, port, proxyRouter);
|
|
51
56
|
},
|
|
52
57
|
pathRewrite: {
|
|
@@ -54,30 +59,27 @@ const buildProxy = async () => {
|
|
|
54
59
|
// '^/target-path': '/',
|
|
55
60
|
},
|
|
56
61
|
};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// instance proxy server
|
|
79
|
-
const proxyPath = '/';
|
|
80
|
-
const proxyHost = 'localhost';
|
|
62
|
+
if (!process.argv.includes('maintenance')) {
|
|
63
|
+
// build router
|
|
64
|
+
Object.keys(hosts).map((hostKey) => {
|
|
65
|
+
let { host, path, target, proxy, peer } = hosts[hostKey];
|
|
66
|
+
if (process.env.NODE_ENV === 'development') host = `localhost`;
|
|
67
|
+
|
|
68
|
+
if (!proxy.includes(port)) return;
|
|
69
|
+
const absoluteHost = [80, 443].includes(port)
|
|
70
|
+
? `${host}${path === '/' ? '' : path}`
|
|
71
|
+
: `${host}:${port}${path === '/' ? '' : path}`;
|
|
72
|
+
|
|
73
|
+
if (!(absoluteHost in options.router)) options.router[absoluteHost] = target;
|
|
74
|
+
});
|
|
75
|
+
if (Object.keys(options.router).length === 0) continue;
|
|
76
|
+
|
|
77
|
+
// order router
|
|
78
|
+
const router = {};
|
|
79
|
+
for (const absoluteHostKey of orderArrayFromAttrInt(Object.keys(options.router), 'length'))
|
|
80
|
+
router[absoluteHostKey] = options.router[absoluteHostKey];
|
|
81
|
+
options.router = router;
|
|
82
|
+
}
|
|
81
83
|
|
|
82
84
|
const filter = false
|
|
83
85
|
? (pathname, req) => {
|
|
@@ -88,8 +90,6 @@ const buildProxy = async () => {
|
|
|
88
90
|
app.use(proxyPath, createProxyMiddleware(filter, options));
|
|
89
91
|
await network.port.portClean(port);
|
|
90
92
|
|
|
91
|
-
const runningData = { host: proxyHost, path: proxyPath, client: null, runtime: 'nodejs', meta: import.meta };
|
|
92
|
-
|
|
93
93
|
switch (process.env.NODE_ENV) {
|
|
94
94
|
case 'production':
|
|
95
95
|
switch (port) {
|