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.
- package/.vscode/extensions.json +3 -2
- package/.vscode/settings.json +2 -0
- package/CHANGELOG.md +24 -4
- package/README.md +39 -2
- package/bin/deploy.js +1205 -131
- package/bin/file.js +8 -0
- package/bin/index.js +1 -233
- package/cli.md +451 -0
- package/docker-compose.yml +1 -1
- package/jsdoc.json +1 -1
- package/manifests/calico-custom-resources.yaml +25 -0
- package/manifests/deployment/adminer/deployment.yaml +32 -0
- package/manifests/deployment/adminer/kustomization.yaml +7 -0
- package/manifests/deployment/adminer/service.yaml +13 -0
- package/manifests/mongodb-4.4/service-deployment.yaml +1 -1
- package/manifests/postgresql/configmap.yaml +9 -0
- package/manifests/postgresql/kustomization.yaml +10 -0
- package/manifests/postgresql/pv.yaml +15 -0
- package/manifests/postgresql/pvc.yaml +13 -0
- package/manifests/postgresql/service.yaml +10 -0
- package/manifests/postgresql/statefulset.yaml +37 -0
- package/manifests/valkey/statefulset.yaml +6 -4
- package/package.json +3 -9
- package/src/api/user/user.service.js +13 -10
- package/src/cli/cluster.js +113 -11
- package/src/cli/db.js +18 -8
- package/src/cli/deploy.js +157 -58
- package/src/cli/fs.js +14 -3
- package/src/cli/image.js +0 -68
- package/src/cli/index.js +312 -0
- package/src/cli/monitor.js +170 -26
- package/src/cli/repository.js +5 -2
- package/src/client/components/core/Account.js +3 -3
- package/src/client/components/core/CalendarCore.js +0 -1
- package/src/client/components/core/Css.js +0 -1
- package/src/client/components/core/CssCore.js +2 -0
- package/src/client/components/core/EventsUI.js +1 -1
- package/src/client/components/core/JoyStick.js +2 -2
- package/src/client/components/core/Modal.js +1 -0
- package/src/client/components/core/RichText.js +1 -11
- package/src/index.js +9 -8
- package/src/mailer/MailerProvider.js +3 -0
- package/src/server/client-build.js +13 -0
- package/src/server/conf.js +48 -0
- package/src/server/dns.js +47 -17
- package/src/server/json-schema.js +77 -0
- package/src/server/peer.js +2 -2
- package/src/server/proxy.js +4 -4
- package/src/server/runtime.js +24 -9
- package/src/server/start.js +122 -0
- package/src/server/valkey.js +25 -11
|
@@ -7,28 +7,18 @@ const RichText = {
|
|
|
7
7
|
Render: async function (options = { id: '', parentIdModal: '' }) {
|
|
8
8
|
const id = options?.id ? options.id : getId(this.Tokens, 'rich-text-');
|
|
9
9
|
this.Tokens[id] = {};
|
|
10
|
-
let top, height;
|
|
11
10
|
setTimeout(() => {
|
|
12
11
|
const easyMDE = new EasyMDE({
|
|
13
12
|
element: s(`.${id}`),
|
|
13
|
+
hideIcons: ['fullscreen', 'side-by-side'],
|
|
14
14
|
onToggleFullScreen: (onFs) => {
|
|
15
15
|
if (onFs) {
|
|
16
16
|
if (options.parentIdModal) {
|
|
17
|
-
s(`.btn-bar-modal-container-${options.parentIdModal}`).classList.add('hide');
|
|
18
|
-
top = newInstance(s(`.${options.parentIdModal}`).style.top);
|
|
19
|
-
height = newInstance(s(`.${options.parentIdModal}`).style.height);
|
|
20
|
-
s(`.${options.parentIdModal}`).style.top = '0px';
|
|
21
|
-
s(`.${options.parentIdModal}`).style.height = `${window.innerHeight}px`;
|
|
22
17
|
}
|
|
23
18
|
// Modal.cleanUI();
|
|
24
|
-
if (s(`.slide-menu-top-bar`)) s(`.slide-menu-top-bar`).classList.add('hide');
|
|
25
19
|
} else {
|
|
26
20
|
if (options.parentIdModal) {
|
|
27
|
-
s(`.btn-bar-modal-container-${options.parentIdModal}`).classList.remove('hide');
|
|
28
|
-
s(`.${options.parentIdModal}`).style.top = top;
|
|
29
|
-
s(`.${options.parentIdModal}`).style.height = height;
|
|
30
21
|
}
|
|
31
|
-
if (s(`.slide-menu-top-bar`)) s(`.slide-menu-top-bar`).classList.add('remove');
|
|
32
22
|
// Modal.restoreUI();
|
|
33
23
|
}
|
|
34
24
|
},
|
package/src/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import UnderpostRepository from './cli/repository.js';
|
|
|
16
16
|
import UnderpostScript from './cli/script.js';
|
|
17
17
|
import UnderpostSecret from './cli/secrets.js';
|
|
18
18
|
import UnderpostTest from './cli/test.js';
|
|
19
|
+
import UnderpostStartUp from './server/start.js';
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Underpost main module methods
|
|
@@ -29,7 +30,7 @@ class Underpost {
|
|
|
29
30
|
* @type {String}
|
|
30
31
|
* @memberof Underpost
|
|
31
32
|
*/
|
|
32
|
-
static version = 'v2.8.
|
|
33
|
+
static version = 'v2.8.67';
|
|
33
34
|
/**
|
|
34
35
|
* Repository cli API
|
|
35
36
|
* @static
|
|
@@ -51,6 +52,13 @@ class Underpost {
|
|
|
51
52
|
* @memberof Underpost
|
|
52
53
|
*/
|
|
53
54
|
static test = UnderpostTest.API;
|
|
55
|
+
/**
|
|
56
|
+
* Underpost Start Up cli API
|
|
57
|
+
* @static
|
|
58
|
+
* @type {UnderpostStartUp.API}
|
|
59
|
+
* @memberof Underpost
|
|
60
|
+
*/
|
|
61
|
+
static start = UnderpostStartUp.API;
|
|
54
62
|
/**
|
|
55
63
|
* Cluster cli API
|
|
56
64
|
* @static
|
|
@@ -93,13 +101,6 @@ class Underpost {
|
|
|
93
101
|
* @memberof Underpost
|
|
94
102
|
*/
|
|
95
103
|
static deploy = UnderpostDeploy.API;
|
|
96
|
-
/**
|
|
97
|
-
* Deployment cli NETWORK
|
|
98
|
-
* @static
|
|
99
|
-
* @type {UnderpostDeploy.NETWORK}
|
|
100
|
-
* @memberof Underpost
|
|
101
|
-
*/
|
|
102
|
-
static deployNetwork = UnderpostDeploy.NETWORK;
|
|
103
104
|
/**
|
|
104
105
|
* Cron cli API
|
|
105
106
|
* @static
|
|
@@ -32,6 +32,9 @@ const MailerProvider = {
|
|
|
32
32
|
},
|
|
33
33
|
) {
|
|
34
34
|
try {
|
|
35
|
+
options.transport.tls = {
|
|
36
|
+
rejectUnauthorized: false,
|
|
37
|
+
};
|
|
35
38
|
const { id } = options;
|
|
36
39
|
// Generate test SMTP service account from ethereal.email
|
|
37
40
|
// Only needed if you don't have a real mail account for testing
|
|
@@ -683,6 +683,19 @@ Sitemap: https://${host}${path === '/' ? '' : path}/sitemap.xml`,
|
|
|
683
683
|
root file where the route starts, such as index.js, app.js, routes.js, etc ... */
|
|
684
684
|
|
|
685
685
|
await swaggerAutoGen({ openapi: '3.0.0' })(outputFile, routes, doc);
|
|
686
|
+
|
|
687
|
+
const htmlFiles = await fs.readdir(`./public/${host}/docs/engine/${Underpost.version.replace('v', '')}`);
|
|
688
|
+
for (const htmlFile of htmlFiles) {
|
|
689
|
+
if (htmlFile.match('.html')) {
|
|
690
|
+
fs.writeFileSync(
|
|
691
|
+
`./public/${host}/docs/engine/${Underpost.version.replace('v', '')}/${htmlFile}`,
|
|
692
|
+
fs
|
|
693
|
+
.readFileSync(`./public/${host}/docs/engine/${Underpost.version.replace('v', '')}/${htmlFile}`, 'utf8')
|
|
694
|
+
.replaceAll('Tutorials', 'References'),
|
|
695
|
+
'utf8',
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
686
699
|
}
|
|
687
700
|
|
|
688
701
|
if (client) {
|
package/src/server/conf.js
CHANGED
|
@@ -923,6 +923,53 @@ const mergeFile = async (parts = [], outputFilePath) => {
|
|
|
923
923
|
});
|
|
924
924
|
};
|
|
925
925
|
|
|
926
|
+
const rebuildConfFactory = ({ deployId, valkey, mongo }) => {
|
|
927
|
+
const confServer = loadReplicas(
|
|
928
|
+
JSON.parse(fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.json`, 'utf8')),
|
|
929
|
+
);
|
|
930
|
+
const hosts = {};
|
|
931
|
+
for (const host of Object.keys(confServer)) {
|
|
932
|
+
hosts[host] = {};
|
|
933
|
+
for (const path of Object.keys(confServer[host])) {
|
|
934
|
+
if (!confServer[host][path].db) continue;
|
|
935
|
+
const { singleReplica, replicas, db } = confServer[host][path];
|
|
936
|
+
const { provider } = db;
|
|
937
|
+
if (singleReplica) {
|
|
938
|
+
for (const replica of replicas) {
|
|
939
|
+
const deployIdReplica = buildReplicaId({ replica, deployId });
|
|
940
|
+
const confServerReplica = JSON.parse(
|
|
941
|
+
fs.readFileSync(`./engine-private/replica/${deployIdReplica}/conf.server.json`, 'utf8'),
|
|
942
|
+
);
|
|
943
|
+
for (const _host of Object.keys(confServerReplica)) {
|
|
944
|
+
for (const _path of Object.keys(confServerReplica[_host])) {
|
|
945
|
+
hosts[host][_path] = { replica: { host, path } };
|
|
946
|
+
confServerReplica[_host][_path].valkey = valkey;
|
|
947
|
+
switch (provider) {
|
|
948
|
+
case 'mongoose':
|
|
949
|
+
confServerReplica[_host][_path].db.host = mongo.host;
|
|
950
|
+
break;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
fs.writeFileSync(
|
|
955
|
+
`./engine-private/replica/${deployIdReplica}/conf.server.json`,
|
|
956
|
+
JSON.stringify(confServerReplica, null, 4),
|
|
957
|
+
'utf8',
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
} else hosts[host][path] = {};
|
|
961
|
+
confServer[host][path].valkey = valkey;
|
|
962
|
+
switch (provider) {
|
|
963
|
+
case 'mongoose':
|
|
964
|
+
confServer[host][path].db.host = mongo.host;
|
|
965
|
+
break;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
fs.writeFileSync(`./engine-private/conf/${deployId}/conf.server.json`, JSON.stringify(confServer, null, 4), 'utf8');
|
|
970
|
+
return { hosts };
|
|
971
|
+
};
|
|
972
|
+
|
|
926
973
|
const getRestoreCronCmd = async (options = { host: '', path: '', conf: {}, deployId: '' }) => {
|
|
927
974
|
const { host, path, conf, deployId } = options;
|
|
928
975
|
const { runtime, db, git, directory } = conf[host][path];
|
|
@@ -1166,4 +1213,5 @@ export {
|
|
|
1166
1213
|
pathPortAssignmentFactory,
|
|
1167
1214
|
deployRangePortFactory,
|
|
1168
1215
|
awaitDeployMonitor,
|
|
1216
|
+
rebuildConfFactory,
|
|
1169
1217
|
};
|
package/src/server/dns.js
CHANGED
|
@@ -5,6 +5,9 @@ import validator from 'validator';
|
|
|
5
5
|
import { publicIp, publicIpv4, publicIpv6 } from 'public-ip';
|
|
6
6
|
import { loggerFactory } from './logger.js';
|
|
7
7
|
import UnderpostRootEnv from '../cli/env.js';
|
|
8
|
+
import dns from 'node:dns';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import { shellExec } from './process.js';
|
|
8
11
|
|
|
9
12
|
dotenv.config();
|
|
10
13
|
|
|
@@ -18,35 +21,59 @@ const ip = {
|
|
|
18
21
|
},
|
|
19
22
|
};
|
|
20
23
|
|
|
24
|
+
const isInternetConnection = (domain = 'google.com') =>
|
|
25
|
+
new Promise((resolve) => dns.lookup(domain, {}, (err) => resolve(err ? false : true)));
|
|
26
|
+
|
|
27
|
+
// export INTERFACE=$(ip route | grep default | cut -d ' ' -f 5)
|
|
28
|
+
// export IP_ADDRESS=$(ip -4 addr show dev $INTERFACE | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
|
|
29
|
+
const getLocalIPv4Address = () =>
|
|
30
|
+
os.networkInterfaces()[
|
|
31
|
+
shellExec(`ip route | grep default | cut -d ' ' -f 5`, {
|
|
32
|
+
stdout: true,
|
|
33
|
+
silent: true,
|
|
34
|
+
disableLog: true,
|
|
35
|
+
}).trim()
|
|
36
|
+
].find((i) => i.family === 'IPv4').address;
|
|
37
|
+
|
|
21
38
|
class Dns {
|
|
22
39
|
static callback = async function (deployList) {
|
|
23
40
|
// Network topology configuration:
|
|
24
41
|
// LAN -> [NAT-VPS](modem/router device) -> WAN
|
|
25
42
|
// enabled DMZ Host to proxy IP 80-443 (79-444) sometimes router block first port
|
|
26
|
-
|
|
43
|
+
|
|
44
|
+
// Enabling DHCP
|
|
45
|
+
// Navigate to Subnets > VLAN > Configure DHCP.
|
|
46
|
+
// Select the appropriate DHCP options (Managed or Relay).
|
|
47
|
+
// Save and apply changes.
|
|
48
|
+
|
|
27
49
|
// verify inet ip proxy server address
|
|
28
50
|
// DHCP (Dynamic Host Configuration Protocol) LAN reserver IP -> MAC ID
|
|
29
51
|
// LAN server or device's local servers port -> 3000-3100 (2999-3101)
|
|
30
52
|
// DNS Records: [ANAME](Address Dynamic) -> [A](ipv4) host | [AAAA](ipv6) host -> [public-ip]
|
|
31
53
|
// Forward the router's TCP/UDP ports to the LAN device's IP address
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
|
|
36
|
-
const confCronData = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
|
|
37
|
-
|
|
38
|
-
let testIp;
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
testIp = await ip.public.ipv4();
|
|
42
|
-
} catch (error) {
|
|
43
|
-
logger.error(error, { testIp, stack: error.stack });
|
|
44
|
-
}
|
|
54
|
+
const isOnline = await isInternetConnection();
|
|
55
|
+
|
|
56
|
+
if (!isOnline) return;
|
|
45
57
|
|
|
46
|
-
|
|
58
|
+
let testIp;
|
|
47
59
|
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
try {
|
|
61
|
+
testIp = await ip.public.ipv4();
|
|
62
|
+
} catch (error) {
|
|
63
|
+
logger.error(error, { testIp, stack: error.stack });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const currentIp = UnderpostRootEnv.API.get('ip');
|
|
67
|
+
|
|
68
|
+
if (validator.isIP(testIp) && currentIp !== testIp) {
|
|
69
|
+
logger.info(`new ip`, testIp);
|
|
70
|
+
UnderpostRootEnv.API.set('monitor-input', 'pause');
|
|
71
|
+
|
|
72
|
+
for (const _deployId of deployList.split(',')) {
|
|
73
|
+
const deployId = _deployId.trim();
|
|
74
|
+
const privateCronConfPath = `./engine-private/conf/${deployId}/conf.cron.json`;
|
|
75
|
+
const confCronPath = fs.existsSync(privateCronConfPath) ? privateCronConfPath : './conf/conf.cron.json';
|
|
76
|
+
const confCronData = JSON.parse(fs.readFileSync(confCronPath, 'utf8'));
|
|
50
77
|
for (const recordType of Object.keys(confCronData.records)) {
|
|
51
78
|
switch (recordType) {
|
|
52
79
|
case 'A':
|
|
@@ -68,6 +95,7 @@ class Dns {
|
|
|
68
95
|
if (verifyIp === testIp) {
|
|
69
96
|
logger.info('ip updated successfully', testIp);
|
|
70
97
|
UnderpostRootEnv.API.set('ip', testIp);
|
|
98
|
+
UnderpostRootEnv.API.delete('monitor-input');
|
|
71
99
|
} else logger.error('ip not updated', testIp);
|
|
72
100
|
} catch (error) {
|
|
73
101
|
logger.error(error, error.stack);
|
|
@@ -102,3 +130,5 @@ class Dns {
|
|
|
102
130
|
}
|
|
103
131
|
|
|
104
132
|
export default Dns;
|
|
133
|
+
|
|
134
|
+
export { Dns, ip, isInternetConnection, getLocalIPv4Address };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
function isPlainObject(obj) {
|
|
2
|
+
return obj ? typeof obj === 'object' && Object.getPrototypeOf(obj) === Object.prototype : false;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
const supportType = ['string', 'number', 'array', 'object', 'boolean', 'integer'];
|
|
6
|
+
|
|
7
|
+
function getType(type) {
|
|
8
|
+
if (!type) type = 'string';
|
|
9
|
+
if (supportType.indexOf(type) !== -1) {
|
|
10
|
+
return type;
|
|
11
|
+
}
|
|
12
|
+
return typeof type;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isSchema(object) {
|
|
16
|
+
if (supportType.indexOf(object.type) !== -1) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function handleSchema(json, schema) {
|
|
23
|
+
Object.assign(schema, json);
|
|
24
|
+
if (schema.type === 'object') {
|
|
25
|
+
delete schema.properties;
|
|
26
|
+
parse(json.properties, schema);
|
|
27
|
+
}
|
|
28
|
+
if (schema.type === 'array') {
|
|
29
|
+
delete schema.items;
|
|
30
|
+
schema.items = {};
|
|
31
|
+
parse(json.items, schema.items);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function handleArray(arr, schema) {
|
|
36
|
+
schema.type = 'array';
|
|
37
|
+
let props = (schema.items = {});
|
|
38
|
+
parse(arr[0], props);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function handleObject(json, schema) {
|
|
42
|
+
if (isSchema(json)) {
|
|
43
|
+
return handleSchema(json, schema);
|
|
44
|
+
}
|
|
45
|
+
schema.type = 'object';
|
|
46
|
+
schema.required = [];
|
|
47
|
+
let props = (schema.properties = {});
|
|
48
|
+
for (let key in json) {
|
|
49
|
+
let item = json[key];
|
|
50
|
+
let curSchema = (props[key] = {});
|
|
51
|
+
if (key[0] === '*') {
|
|
52
|
+
delete props[key];
|
|
53
|
+
key = key.substr(1);
|
|
54
|
+
schema.required.push(key);
|
|
55
|
+
curSchema = props[key] = {};
|
|
56
|
+
}
|
|
57
|
+
parse(item, curSchema);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function parse(json, schema) {
|
|
62
|
+
if (Array.isArray(json)) {
|
|
63
|
+
handleArray(json, schema);
|
|
64
|
+
} else if (isPlainObject(json)) {
|
|
65
|
+
handleObject(json, schema);
|
|
66
|
+
} else {
|
|
67
|
+
schema.type = getType(json);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function ejs(data) {
|
|
72
|
+
let JsonSchema = {};
|
|
73
|
+
parse(data, JsonSchema);
|
|
74
|
+
return JsonSchema;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { ejs };
|
package/src/server/peer.js
CHANGED
|
@@ -2,7 +2,7 @@ import { PeerServer } from 'peer';
|
|
|
2
2
|
import dotenv from 'dotenv';
|
|
3
3
|
import { loggerFactory } from './logger.js';
|
|
4
4
|
import fs from 'fs-extra';
|
|
5
|
-
import
|
|
5
|
+
import UnderpostStartUp from './start.js';
|
|
6
6
|
|
|
7
7
|
dotenv.config();
|
|
8
8
|
|
|
@@ -25,7 +25,7 @@ const createPeerServer = async ({ port, devPort, origins, host, path }) => {
|
|
|
25
25
|
// cert: fs.readFileSync(''),
|
|
26
26
|
// ca: fs.readFileSync(''),
|
|
27
27
|
};
|
|
28
|
-
const peerServer = listenServerFactory(() => PeerServer(options));
|
|
28
|
+
const peerServer = UnderpostStartUp.API.listenServerFactory(() => PeerServer(options));
|
|
29
29
|
|
|
30
30
|
return { options, peerServer, meta: import.meta };
|
|
31
31
|
};
|
package/src/server/proxy.js
CHANGED
|
@@ -5,9 +5,9 @@ import dotenv from 'dotenv';
|
|
|
5
5
|
|
|
6
6
|
import { createProxyMiddleware } from 'http-proxy-middleware';
|
|
7
7
|
import { loggerFactory, loggerMiddleware } from './logger.js';
|
|
8
|
-
import { listenPortController } from './network.js';
|
|
9
8
|
import { createSslServer, sslRedirectMiddleware } from './ssl.js';
|
|
10
9
|
import { buildPortProxyRouter, buildProxyRouter, maintenanceMiddleware } from './conf.js';
|
|
10
|
+
import UnderpostStartUp from './start.js';
|
|
11
11
|
|
|
12
12
|
dotenv.config();
|
|
13
13
|
|
|
@@ -71,11 +71,11 @@ const buildProxy = async () => {
|
|
|
71
71
|
switch (port) {
|
|
72
72
|
case 443:
|
|
73
73
|
const { ServerSSL } = await createSslServer(app, hosts);
|
|
74
|
-
await listenPortController(ServerSSL, port, runningData);
|
|
74
|
+
await UnderpostStartUp.API.listenPortController(ServerSSL, port, runningData);
|
|
75
75
|
break;
|
|
76
76
|
|
|
77
77
|
default:
|
|
78
|
-
await listenPortController(app, port, runningData);
|
|
78
|
+
await UnderpostStartUp.API.listenPortController(app, port, runningData);
|
|
79
79
|
|
|
80
80
|
break;
|
|
81
81
|
}
|
|
@@ -83,7 +83,7 @@ const buildProxy = async () => {
|
|
|
83
83
|
break;
|
|
84
84
|
|
|
85
85
|
default:
|
|
86
|
-
await listenPortController(app, port, runningData);
|
|
86
|
+
await UnderpostStartUp.API.listenPortController(app, port, runningData);
|
|
87
87
|
|
|
88
88
|
break;
|
|
89
89
|
}
|
package/src/server/runtime.js
CHANGED
|
@@ -9,7 +9,7 @@ import compression from 'compression';
|
|
|
9
9
|
|
|
10
10
|
import { createServer } from 'http';
|
|
11
11
|
import { getRootDirectory } from './process.js';
|
|
12
|
-
import
|
|
12
|
+
import UnderpostStartUp from './start.js';
|
|
13
13
|
import { loggerFactory, loggerMiddleware } from './logger.js';
|
|
14
14
|
import { getCapVariableName, newInstance } from '../client/components/core/CommonJs.js';
|
|
15
15
|
import { Xampp } from '../runtime/xampp/Xampp.js';
|
|
@@ -21,6 +21,7 @@ import { Lampp } from '../runtime/lampp/Lampp.js';
|
|
|
21
21
|
import { getDeployId } from './conf.js';
|
|
22
22
|
import { JSONweb, ssrFactory } from './client-formatted.js';
|
|
23
23
|
import Underpost from '../index.js';
|
|
24
|
+
import { createValkeyConnection } from './valkey.js';
|
|
24
25
|
|
|
25
26
|
dotenv.config();
|
|
26
27
|
|
|
@@ -67,6 +68,7 @@ const buildRuntime = async () => {
|
|
|
67
68
|
peer,
|
|
68
69
|
singleReplica,
|
|
69
70
|
replicas,
|
|
71
|
+
valkey,
|
|
70
72
|
} = confServer[host][path];
|
|
71
73
|
|
|
72
74
|
if (singleReplica && replicas && replicas.length > 0 && !singleReplicaHosts.includes(host)) {
|
|
@@ -140,6 +142,8 @@ const buildRuntime = async () => {
|
|
|
140
142
|
// if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
|
|
141
143
|
// $_SERVER['HTTPS'] = 'on';
|
|
142
144
|
// }
|
|
145
|
+
// For plugins:
|
|
146
|
+
// define( 'FS_METHOD', 'direct' );
|
|
143
147
|
|
|
144
148
|
// ErrorDocument 404 /custom_404.html
|
|
145
149
|
// ErrorDocument 500 /custom_50x.html
|
|
@@ -182,7 +186,11 @@ const buildRuntime = async () => {
|
|
|
182
186
|
// RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
|
|
183
187
|
// RewriteRule ^ https://%1%{REQUEST_URI} [L,NE,R=301]
|
|
184
188
|
|
|
185
|
-
await listenPortController(
|
|
189
|
+
await UnderpostStartUp.API.listenPortController(
|
|
190
|
+
UnderpostStartUp.API.listenServerFactory(),
|
|
191
|
+
port,
|
|
192
|
+
runningData,
|
|
193
|
+
);
|
|
186
194
|
break;
|
|
187
195
|
case 'xampp':
|
|
188
196
|
if (!Xampp.enabled()) continue;
|
|
@@ -229,7 +237,11 @@ const buildRuntime = async () => {
|
|
|
229
237
|
// if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
|
|
230
238
|
// $_SERVER['HTTPS'] = 'on';
|
|
231
239
|
// }
|
|
232
|
-
await listenPortController(
|
|
240
|
+
await UnderpostStartUp.API.listenPortController(
|
|
241
|
+
UnderpostStartUp.API.listenServerFactory(),
|
|
242
|
+
port,
|
|
243
|
+
runningData,
|
|
244
|
+
);
|
|
233
245
|
break;
|
|
234
246
|
case 'nodejs':
|
|
235
247
|
const app = express();
|
|
@@ -282,7 +294,7 @@ const buildRuntime = async () => {
|
|
|
282
294
|
currentPort += 2;
|
|
283
295
|
const staticPort = newInstance(currentPort);
|
|
284
296
|
|
|
285
|
-
await listenPortController(app, staticPort, runningData);
|
|
297
|
+
await UnderpostStartUp.API.listenPortController(app, staticPort, runningData);
|
|
286
298
|
currentPort++;
|
|
287
299
|
continue;
|
|
288
300
|
}
|
|
@@ -333,7 +345,7 @@ const buildRuntime = async () => {
|
|
|
333
345
|
// }),
|
|
334
346
|
// );
|
|
335
347
|
|
|
336
|
-
await listenPortController(app, port, runningData);
|
|
348
|
+
await UnderpostStartUp.API.listenPortController(app, port, runningData);
|
|
337
349
|
break;
|
|
338
350
|
}
|
|
339
351
|
|
|
@@ -353,6 +365,9 @@ const buildRuntime = async () => {
|
|
|
353
365
|
|
|
354
366
|
if (db && apis) await DataBaseProvider.load({ apis, host, path, db });
|
|
355
367
|
|
|
368
|
+
// valkey server
|
|
369
|
+
await createValkeyConnection({ host, path }, valkey);
|
|
370
|
+
|
|
356
371
|
if (mailer) {
|
|
357
372
|
const mailerSsrConf = confSSR[getCapVariableName(client)];
|
|
358
373
|
await MailerProvider.load({
|
|
@@ -441,7 +456,7 @@ const buildRuntime = async () => {
|
|
|
441
456
|
port,
|
|
442
457
|
origins,
|
|
443
458
|
});
|
|
444
|
-
await listenPortController(listenServerFactory(), port, {
|
|
459
|
+
await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), port, {
|
|
445
460
|
runtime: 'nodejs',
|
|
446
461
|
client: null,
|
|
447
462
|
host,
|
|
@@ -461,7 +476,7 @@ const buildRuntime = async () => {
|
|
|
461
476
|
path,
|
|
462
477
|
});
|
|
463
478
|
|
|
464
|
-
await listenPortController(peerServer, peerPort, {
|
|
479
|
+
await UnderpostStartUp.API.listenPortController(peerServer, peerPort, {
|
|
465
480
|
runtime: 'nodejs',
|
|
466
481
|
client: null,
|
|
467
482
|
host,
|
|
@@ -470,7 +485,7 @@ const buildRuntime = async () => {
|
|
|
470
485
|
});
|
|
471
486
|
}
|
|
472
487
|
|
|
473
|
-
await listenPortController(server, port, runningData);
|
|
488
|
+
await UnderpostStartUp.API.listenPortController(server, port, runningData);
|
|
474
489
|
|
|
475
490
|
break;
|
|
476
491
|
default:
|
|
@@ -483,7 +498,7 @@ const buildRuntime = async () => {
|
|
|
483
498
|
if (Xampp.enabled() && Xampp.router) Xampp.initService();
|
|
484
499
|
if (Lampp.enabled() && Lampp.router) Lampp.initService();
|
|
485
500
|
|
|
486
|
-
logRuntimeRouter();
|
|
501
|
+
UnderpostStartUp.API.logRuntimeRouter();
|
|
487
502
|
};
|
|
488
503
|
|
|
489
504
|
export { buildRuntime };
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import UnderpostDeploy from '../cli/deploy.js';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import { awaitDeployMonitor } from './conf.js';
|
|
4
|
+
import { actionInitLog, loggerFactory } from './logger.js';
|
|
5
|
+
import { shellCd, shellExec } from './process.js';
|
|
6
|
+
import UnderpostRootEnv from '../cli/env.js';
|
|
7
|
+
|
|
8
|
+
const logger = loggerFactory(import.meta);
|
|
9
|
+
|
|
10
|
+
class UnderpostStartUp {
|
|
11
|
+
static API = {
|
|
12
|
+
logRuntimeRouter: () => {
|
|
13
|
+
const displayLog = {};
|
|
14
|
+
|
|
15
|
+
for (const host of Object.keys(UnderpostDeploy.NETWORK))
|
|
16
|
+
for (const path of Object.keys(UnderpostDeploy.NETWORK[host]))
|
|
17
|
+
displayLog[UnderpostDeploy.NETWORK[host][path].publicHost] = UnderpostDeploy.NETWORK[host][path].local;
|
|
18
|
+
|
|
19
|
+
logger.info('Runtime network', displayLog);
|
|
20
|
+
},
|
|
21
|
+
listenServerFactory: (logic = async () => {}) => {
|
|
22
|
+
return {
|
|
23
|
+
listen: async (...args) => {
|
|
24
|
+
const msDelta = 1000;
|
|
25
|
+
const msMax = 30 * 24 * 60 * 60 * 1000; // ~ 1 month
|
|
26
|
+
let msCount = 0;
|
|
27
|
+
setInterval(() => {
|
|
28
|
+
msCount += msDelta;
|
|
29
|
+
if (msCount >= msMax) {
|
|
30
|
+
const message = 'Listen server factory timeout';
|
|
31
|
+
logger.error(message);
|
|
32
|
+
throw new Error(message);
|
|
33
|
+
}
|
|
34
|
+
}, msDelta);
|
|
35
|
+
return logic ? await logic(...args) : undefined, args[1]();
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
listenPortController: async (server, port, metadata) =>
|
|
40
|
+
new Promise((resolve) => {
|
|
41
|
+
try {
|
|
42
|
+
if (port === ':') {
|
|
43
|
+
server.listen(port, actionInitLog);
|
|
44
|
+
return resolve(true);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const { host, path, client, runtime, meta } = metadata;
|
|
48
|
+
const error = [];
|
|
49
|
+
if (port === undefined) error.push(`port`);
|
|
50
|
+
if (host === undefined) error.push(`host`);
|
|
51
|
+
if (path === undefined) error.push(`path`);
|
|
52
|
+
if (client === undefined) error.push(`client`);
|
|
53
|
+
if (runtime === undefined) error.push(`runtime`);
|
|
54
|
+
if (meta === undefined) error.push(`meta`);
|
|
55
|
+
if (error.length > 0) throw new Error('Listen port controller requires values: ' + error.join(', '));
|
|
56
|
+
|
|
57
|
+
server.listen(port, () => {
|
|
58
|
+
if (!UnderpostDeploy.NETWORK[host]) UnderpostDeploy.NETWORK[host] = {};
|
|
59
|
+
UnderpostDeploy.NETWORK[host][path] = {
|
|
60
|
+
meta,
|
|
61
|
+
client,
|
|
62
|
+
runtime,
|
|
63
|
+
port,
|
|
64
|
+
publicHost:
|
|
65
|
+
port === 80
|
|
66
|
+
? `http://${host}${path}`
|
|
67
|
+
: port === 443
|
|
68
|
+
? `https://${host}${path}`
|
|
69
|
+
: `http://${host}:${port}${path}`,
|
|
70
|
+
local: `http://localhost:${port}${path}`,
|
|
71
|
+
apis: metadata.apis,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return resolve(true);
|
|
75
|
+
});
|
|
76
|
+
} catch (error) {
|
|
77
|
+
logger.error(error, { metadata, port, stack: error.stack });
|
|
78
|
+
resolve(false);
|
|
79
|
+
}
|
|
80
|
+
}),
|
|
81
|
+
|
|
82
|
+
async callback(deployId = 'default', env = 'development', options = { build: false, run: false }) {
|
|
83
|
+
if (options.build === true) await UnderpostStartUp.API.build(deployId, env);
|
|
84
|
+
if (options.run === true) await UnderpostStartUp.API.run(deployId, env);
|
|
85
|
+
},
|
|
86
|
+
async build(deployId = 'default', env = 'development') {
|
|
87
|
+
const buildBasePath = `/home/dd`;
|
|
88
|
+
const repoName = `engine-${deployId.split('-')[1]}`;
|
|
89
|
+
shellExec(`cd ${buildBasePath} && underpost clone underpostnet/${repoName}`);
|
|
90
|
+
shellExec(`cd ${buildBasePath} && sudo mv ./${repoName} ./engine`);
|
|
91
|
+
shellExec(`cd ${buildBasePath}/engine && underpost clone underpostnet/${repoName}-private`);
|
|
92
|
+
shellExec(`cd ${buildBasePath}/engine && sudo mv ./${repoName}-private ./engine-private`);
|
|
93
|
+
shellCd(`${buildBasePath}/engine`);
|
|
94
|
+
shellExec(`npm install`);
|
|
95
|
+
shellExec(`node bin/deploy conf ${deployId} ${env}`);
|
|
96
|
+
if (fs.existsSync('./engine-private/itc-scripts')) {
|
|
97
|
+
const itcScripts = await fs.readdir('./engine-private/itc-scripts');
|
|
98
|
+
for (const itcScript of itcScripts)
|
|
99
|
+
if (itcScript.match(deployId)) shellExec(`node ./engine-private/itc-scripts/${itcScript}`);
|
|
100
|
+
}
|
|
101
|
+
shellExec(`node bin/deploy build-full-client ${deployId}`);
|
|
102
|
+
},
|
|
103
|
+
async run(deployId = 'default', env = 'development') {
|
|
104
|
+
const runCmd = env === 'production' ? 'run prod-img' : 'run dev-img';
|
|
105
|
+
if (fs.existsSync(`./engine-private/replica`)) {
|
|
106
|
+
const replicas = await fs.readdir(`./engine-private/replica`);
|
|
107
|
+
for (const replica of replicas) {
|
|
108
|
+
if (!replica.match(deployId)) continue;
|
|
109
|
+
shellExec(`node bin/deploy conf ${replica} ${env}`);
|
|
110
|
+
shellExec(`npm ${runCmd} deploy deploy-id:${replica}`, { async: true });
|
|
111
|
+
await awaitDeployMonitor(true);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
shellExec(`node bin/deploy conf ${deployId} ${env}`);
|
|
115
|
+
shellExec(`npm ${runCmd} deploy deploy-id:${deployId}`, { async: true });
|
|
116
|
+
await awaitDeployMonitor(true);
|
|
117
|
+
UnderpostRootEnv.API.set('container-status', `${deployId}-${env}-running-deployment`);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export default UnderpostStartUp;
|