underpost 2.8.883 → 2.8.885
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/README.md +4 -116
- package/bin/deploy.js +9 -10
- package/bin/file.js +4 -6
- package/cli.md +15 -11
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/package.json +1 -1
- package/src/api/user/user.service.js +3 -10
- package/src/cli/cluster.js +21 -0
- package/src/cli/cron.js +8 -0
- package/src/cli/db.js +63 -1
- package/src/cli/deploy.js +156 -3
- package/src/cli/env.js +43 -0
- package/src/cli/fs.js +94 -0
- package/src/cli/image.js +8 -0
- package/src/cli/index.js +17 -4
- package/src/cli/monitor.js +0 -1
- package/src/cli/repository.js +95 -2
- package/src/client/components/core/Css.js +16 -0
- package/src/client/components/core/Docs.js +5 -13
- package/src/client/components/core/Modal.js +57 -39
- package/src/client/components/core/Router.js +6 -3
- package/src/client/components/core/Worker.js +205 -118
- package/src/client/components/default/MenuDefault.js +1 -0
- package/src/client.dev.js +6 -3
- package/src/db/DataBaseProvider.js +65 -12
- package/src/db/mariadb/MariaDB.js +39 -6
- package/src/db/mongo/MongooseDB.js +51 -133
- package/src/index.js +1 -1
- package/src/mailer/EmailRender.js +58 -9
- package/src/mailer/MailerProvider.js +98 -25
- package/src/runtime/express/Express.js +248 -0
- package/src/runtime/lampp/Lampp.js +27 -8
- package/src/server/auth.js +82 -43
- package/src/server/client-build-live.js +14 -5
- package/src/server/client-dev-server.js +21 -8
- package/src/server/conf.js +78 -25
- package/src/server/peer.js +2 -2
- package/src/server/runtime.js +49 -208
- package/src/server/start.js +39 -0
- package/src/ws/IoInterface.js +132 -39
- package/src/ws/IoServer.js +79 -31
- package/src/ws/core/core.ws.connection.js +50 -16
- package/src/ws/core/core.ws.emit.js +47 -8
- package/src/ws/core/core.ws.server.js +62 -10
- package/src/runtime/nginx/Nginx.js +0 -3
|
@@ -2,17 +2,26 @@ import fs from 'fs-extra';
|
|
|
2
2
|
import nodemon from 'nodemon';
|
|
3
3
|
import { shellExec } from './process.js';
|
|
4
4
|
import { loggerFactory } from './logger.js';
|
|
5
|
+
import { writeEnv } from './conf.js';
|
|
6
|
+
import dotenv from 'dotenv';
|
|
5
7
|
|
|
6
8
|
const logger = loggerFactory(import.meta);
|
|
7
9
|
|
|
8
|
-
const createClientDevServer = (
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
const createClientDevServer = (
|
|
11
|
+
deployId = process.argv[2] || 'dd-default',
|
|
12
|
+
subConf = process.argv[3] || '',
|
|
13
|
+
host = process.argv[4] || 'default.net',
|
|
14
|
+
path = process.argv[5] || '/',
|
|
15
|
+
) => {
|
|
11
16
|
shellExec(
|
|
12
|
-
`env-cmd -f .env.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
`env-cmd -f ./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-client node bin/deploy build-full-client ${deployId} ${subConf}-dev-client ${host} ${path}`.trim(),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
shellExec(
|
|
21
|
+
`env-cmd -f ./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-client node src/server ${deployId} ${subConf}-dev-client`.trim(),
|
|
22
|
+
{
|
|
23
|
+
async: true,
|
|
24
|
+
},
|
|
16
25
|
);
|
|
17
26
|
|
|
18
27
|
// https://github.com/remy/nodemon/blob/main/doc/events.md
|
|
@@ -28,7 +37,11 @@ const createClientDevServer = () => {
|
|
|
28
37
|
|
|
29
38
|
let buildPathScope = [];
|
|
30
39
|
|
|
31
|
-
const nodemonOptions = {
|
|
40
|
+
const nodemonOptions = {
|
|
41
|
+
script: './src/client.build',
|
|
42
|
+
args: [`${deployId}`, `${subConf}-dev-client`, `${host}`, `${path}`],
|
|
43
|
+
watch: 'src/client',
|
|
44
|
+
};
|
|
32
45
|
logger.info('nodemon option', { nodemonOptions });
|
|
33
46
|
nodemon(nodemonOptions)
|
|
34
47
|
.on('start', function (...args) {
|
package/src/server/conf.js
CHANGED
|
@@ -25,13 +25,14 @@ const logger = loggerFactory(import.meta);
|
|
|
25
25
|
const Config = {
|
|
26
26
|
default: DefaultConf,
|
|
27
27
|
build: async function (deployContext = 'dd-default', deployList, subConf) {
|
|
28
|
-
if (typeof process.argv[2] === 'string' && process.argv[2].startsWith('dd-'))
|
|
28
|
+
if (process.argv[2] && typeof process.argv[2] === 'string' && process.argv[2].startsWith('dd-'))
|
|
29
|
+
deployContext = process.argv[2];
|
|
30
|
+
if (!subConf && process.argv[3] && typeof process.argv[3] === 'string') subConf = process.argv[3];
|
|
29
31
|
if (!fs.existsSync(`./tmp`)) fs.mkdirSync(`./tmp`, { recursive: true });
|
|
30
32
|
UnderpostRootEnv.API.set('await-deploy', new Date().toISOString());
|
|
31
|
-
if (fs.existsSync(`./engine-private/replica/${deployContext}`))
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (deployContext === 'proxy') Config.buildProxy(deployContext, deployList, subConf);
|
|
33
|
+
if (fs.existsSync(`./engine-private/replica/${deployContext}`)) return loadConf(deployContext, subConf);
|
|
34
|
+
else if (deployContext.startsWith('dd-')) return loadConf(deployContext, subConf);
|
|
35
|
+
if (deployContext === 'proxy') Config.buildProxy(deployList, subConf);
|
|
35
36
|
},
|
|
36
37
|
deployIdFactory: function (deployId = 'dd-default', options = { cluster: false }) {
|
|
37
38
|
if (!deployId.startsWith('dd-')) deployId = `dd-${deployId}`;
|
|
@@ -105,7 +106,7 @@ const Config = {
|
|
|
105
106
|
for (const confType of Object.keys(this.default))
|
|
106
107
|
fs.writeFileSync(`${folder}/conf.${confType}.json`, JSON.stringify(this.default[confType], null, 4), 'utf8');
|
|
107
108
|
},
|
|
108
|
-
buildProxy: function (
|
|
109
|
+
buildProxy: function (deployList = 'dd-default', subConf = '') {
|
|
109
110
|
if (!deployList) deployList = process.argv[3];
|
|
110
111
|
if (!subConf) subConf = process.argv[4];
|
|
111
112
|
this.default.server = {};
|
|
@@ -121,7 +122,7 @@ const Config = {
|
|
|
121
122
|
if (process.env.NODE_ENV === 'development' && fs.existsSync(confDevPath)) confPath = confDevPath;
|
|
122
123
|
const serverConf = JSON.parse(fs.readFileSync(confPath, 'utf8'));
|
|
123
124
|
|
|
124
|
-
for (const host of Object.keys(loadReplicas(serverConf
|
|
125
|
+
for (const host of Object.keys(loadReplicas(serverConf))) {
|
|
125
126
|
if (serverConf[host]['/'])
|
|
126
127
|
this.default.server[host] = {
|
|
127
128
|
...this.default.server[host],
|
|
@@ -138,13 +139,13 @@ const Config = {
|
|
|
138
139
|
},
|
|
139
140
|
};
|
|
140
141
|
|
|
141
|
-
const loadConf = (deployId = 'dd-default',
|
|
142
|
+
const loadConf = (deployId = 'dd-default', subConf) => {
|
|
142
143
|
if (deployId === 'current') {
|
|
143
144
|
console.log(process.env.DEPLOY_ID);
|
|
144
145
|
return;
|
|
145
146
|
}
|
|
146
147
|
if (deployId === 'clean') {
|
|
147
|
-
const path =
|
|
148
|
+
const path = '.';
|
|
148
149
|
fs.removeSync(`${path}/.env`);
|
|
149
150
|
shellExec(`git checkout ${path}/.env.production`);
|
|
150
151
|
shellExec(`git checkout ${path}/.env.development`);
|
|
@@ -164,7 +165,6 @@ const loadConf = (deployId = 'dd-default', envInput, subConf) => {
|
|
|
164
165
|
for (const typeConf of Object.keys(Config.default)) {
|
|
165
166
|
let srcConf = fs.readFileSync(`${folder}/conf.${typeConf}.json`, 'utf8');
|
|
166
167
|
if (process.env.NODE_ENV === 'development' && typeConf === 'server') {
|
|
167
|
-
if (!subConf) subConf = process.argv[3];
|
|
168
168
|
const devConfPath = `${folder}/conf.${typeConf}.dev${subConf ? `.${subConf}` : ''}.json`;
|
|
169
169
|
if (fs.existsSync(devConfPath)) srcConf = fs.readFileSync(devConfPath, 'utf8');
|
|
170
170
|
}
|
|
@@ -174,10 +174,13 @@ const loadConf = (deployId = 'dd-default', envInput, subConf) => {
|
|
|
174
174
|
fs.writeFileSync(`./.env.production`, fs.readFileSync(`${folder}/.env.production`, 'utf8'), 'utf8');
|
|
175
175
|
fs.writeFileSync(`./.env.development`, fs.readFileSync(`${folder}/.env.development`, 'utf8'), 'utf8');
|
|
176
176
|
fs.writeFileSync(`./.env.test`, fs.readFileSync(`${folder}/.env.test`, 'utf8'), 'utf8');
|
|
177
|
-
const NODE_ENV =
|
|
177
|
+
const NODE_ENV = process.env.NODE_ENV;
|
|
178
178
|
if (NODE_ENV) {
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
const subPathEnv = fs.existsSync(`${folder}/.env.${NODE_ENV}.${subConf}`)
|
|
180
|
+
? `${folder}/.env.${NODE_ENV}.${subConf}`
|
|
181
|
+
: `${folder}/.env.${NODE_ENV}`;
|
|
182
|
+
fs.writeFileSync(`./.env`, fs.readFileSync(subPathEnv, 'utf8'), 'utf8');
|
|
183
|
+
const env = dotenv.parse(fs.readFileSync(subPathEnv, 'utf8'));
|
|
181
184
|
process.env = {
|
|
182
185
|
...process.env,
|
|
183
186
|
...env,
|
|
@@ -191,22 +194,16 @@ const loadConf = (deployId = 'dd-default', envInput, subConf) => {
|
|
|
191
194
|
return { folder, deployId };
|
|
192
195
|
};
|
|
193
196
|
|
|
194
|
-
const loadReplicas = (confServer
|
|
195
|
-
if (!deployContext) deployContext = process.argv[2];
|
|
196
|
-
if (!subConf) subConf = process.argv[3];
|
|
197
|
+
const loadReplicas = (confServer) => {
|
|
197
198
|
for (const host of Object.keys(confServer)) {
|
|
198
199
|
for (const path of Object.keys(confServer[host])) {
|
|
199
200
|
const { replicas, singleReplica } = confServer[host][path];
|
|
200
|
-
if (
|
|
201
|
-
replicas &&
|
|
202
|
-
(deployContext === 'proxy' ||
|
|
203
|
-
!singleReplica ||
|
|
204
|
-
(singleReplica && process.env.NODE_ENV === 'development' && !subConf))
|
|
205
|
-
)
|
|
201
|
+
if (replicas && !singleReplica)
|
|
206
202
|
for (const replicaPath of replicas) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
203
|
+
{
|
|
204
|
+
confServer[host][replicaPath] = newInstance(confServer[host][path]);
|
|
205
|
+
delete confServer[host][replicaPath].replicas;
|
|
206
|
+
}
|
|
210
207
|
}
|
|
211
208
|
}
|
|
212
209
|
}
|
|
@@ -982,6 +979,60 @@ const getInstanceContext = async (options = { singleReplica, replicas, redirect:
|
|
|
982
979
|
return { redirectTarget };
|
|
983
980
|
};
|
|
984
981
|
|
|
982
|
+
const buildApiConf = async (options = { deployId: '', subConf: '', host: '', path: '', origin: '' }) => {
|
|
983
|
+
let { deployId, subConf, host, path, origin } = options;
|
|
984
|
+
if (!deployId) deployId = process.argv[2].trim();
|
|
985
|
+
if (!subConf) subConf = process.argv[3].trim();
|
|
986
|
+
if (process.argv[4]) host = process.argv[4].trim();
|
|
987
|
+
if (process.argv[5]) path = process.argv[5].trim();
|
|
988
|
+
if (process.argv[6])
|
|
989
|
+
origin = `${process.env.NODE_ENV === 'production' ? 'https' : 'http'}://${process.argv[6].trim()}`;
|
|
990
|
+
|
|
991
|
+
if (!origin) return;
|
|
992
|
+
const confServer = JSON.parse(
|
|
993
|
+
fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.dev.${subConf}.json`, 'utf8'),
|
|
994
|
+
);
|
|
995
|
+
const envObj = dotenv.parse(
|
|
996
|
+
fs.readFileSync(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}`, 'utf8'),
|
|
997
|
+
);
|
|
998
|
+
if (host && path) {
|
|
999
|
+
confServer[host][path].origins = [origin];
|
|
1000
|
+
logger.info('Build api conf', { host, path, origin });
|
|
1001
|
+
} else return;
|
|
1002
|
+
writeEnv(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-api`, envObj);
|
|
1003
|
+
fs.writeFileSync(
|
|
1004
|
+
`./engine-private/conf/${deployId}/conf.server.dev.${subConf}-dev-api.json`,
|
|
1005
|
+
JSON.stringify(confServer, null, 4),
|
|
1006
|
+
'utf8',
|
|
1007
|
+
);
|
|
1008
|
+
};
|
|
1009
|
+
|
|
1010
|
+
const buildClientStaticConf = async (options = { deployId: '', subConf: '', apiBaseHost: '', host: '', path: '' }) => {
|
|
1011
|
+
let { deployId, subConf, host, path } = options;
|
|
1012
|
+
if (!deployId) deployId = process.argv[2].trim();
|
|
1013
|
+
if (!subConf) subConf = process.argv[3].trim();
|
|
1014
|
+
if (!host) host = process.argv[4].trim();
|
|
1015
|
+
if (!path) path = process.argv[5].trim();
|
|
1016
|
+
const confServer = JSON.parse(
|
|
1017
|
+
fs.readFileSync(`./engine-private/conf/${deployId}/conf.server.dev.${subConf}-dev-api.json`, 'utf8'),
|
|
1018
|
+
);
|
|
1019
|
+
const envObj = dotenv.parse(
|
|
1020
|
+
fs.readFileSync(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-api`, 'utf8'),
|
|
1021
|
+
);
|
|
1022
|
+
envObj.PORT = parseInt(envObj.PORT);
|
|
1023
|
+
const apiBaseHost = options?.apiBaseHost ? options.apiBaseHost : `localhost:${envObj.PORT + 1}`;
|
|
1024
|
+
confServer[host][path].apiBaseHost = apiBaseHost;
|
|
1025
|
+
confServer[host][path].apiBaseProxyPath = path;
|
|
1026
|
+
logger.info('Build client static conf', { host, path, apiBaseHost });
|
|
1027
|
+
envObj.PORT = parseInt(confServer[host][path].origins[0].split(':')[2]) - 1;
|
|
1028
|
+
writeEnv(`./engine-private/conf/${deployId}/.env.${process.env.NODE_ENV}.${subConf}-dev-client`, envObj);
|
|
1029
|
+
fs.writeFileSync(
|
|
1030
|
+
`./engine-private/conf/${deployId}/conf.server.dev.${subConf}-dev-client.json`,
|
|
1031
|
+
JSON.stringify(confServer, null, 4),
|
|
1032
|
+
'utf8',
|
|
1033
|
+
);
|
|
1034
|
+
};
|
|
1035
|
+
|
|
985
1036
|
export {
|
|
986
1037
|
Cmd,
|
|
987
1038
|
Config,
|
|
@@ -1015,4 +1066,6 @@ export {
|
|
|
1015
1066
|
rebuildConfFactory,
|
|
1016
1067
|
buildCliDoc,
|
|
1017
1068
|
getInstanceContext,
|
|
1069
|
+
buildApiConf,
|
|
1070
|
+
buildClientStaticConf,
|
|
1018
1071
|
};
|
package/src/server/peer.js
CHANGED
|
@@ -48,7 +48,7 @@ const logger = loggerFactory(import.meta);
|
|
|
48
48
|
*/
|
|
49
49
|
const createPeerServer = async ({ port, devPort, origins, host, path }) => {
|
|
50
50
|
if (process.env.NODE_ENV === 'development' && devPort) {
|
|
51
|
-
logger.warn(`Adding development origin: http://localhost:${devPort}`);
|
|
51
|
+
// logger.warn(`Adding development origin: http://localhost:${devPort}`);
|
|
52
52
|
origins.push(`http://localhost:${devPort}`);
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -67,7 +67,7 @@ const createPeerServer = async ({ port, devPort, origins, host, path }) => {
|
|
|
67
67
|
};
|
|
68
68
|
|
|
69
69
|
// Use the framework's factory to listen on the server, ensuring graceful startup/shutdown
|
|
70
|
-
const peerServer = UnderpostStartUp.API.listenServerFactory(() => PeerServer(options));
|
|
70
|
+
const peerServer = UnderpostStartUp.API.listenServerFactory(async () => PeerServer(options));
|
|
71
71
|
|
|
72
72
|
return { options, peerServer, meta: import.meta };
|
|
73
73
|
};
|
package/src/server/runtime.js
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @namespace Runtime
|
|
3
|
+
* @description The main runtime orchestrator responsible for reading configuration,
|
|
4
|
+
* initializing services (Prometheus, Ports, DB, Mailer), and building the
|
|
5
|
+
* specific server runtime for each host/path (e.g., nodejs, lampp).
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
import fs from 'fs-extra';
|
|
2
|
-
import express from 'express';
|
|
3
9
|
import dotenv from 'dotenv';
|
|
4
|
-
import fileUpload from 'express-fileupload';
|
|
5
|
-
import swaggerUi from 'swagger-ui-express';
|
|
6
10
|
import * as promClient from 'prom-client';
|
|
7
|
-
import compression from 'compression';
|
|
8
11
|
|
|
9
12
|
import UnderpostStartUp from './start.js';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { getCapVariableName, newInstance } from '../client/components/core/CommonJs.js';
|
|
13
|
-
import { MailerProvider } from '../mailer/MailerProvider.js';
|
|
14
|
-
import { DataBaseProvider } from '../db/DataBaseProvider.js';
|
|
15
|
-
import { createPeerServer } from './peer.js';
|
|
13
|
+
import { loggerFactory } from './logger.js';
|
|
14
|
+
import { newInstance } from '../client/components/core/CommonJs.js';
|
|
16
15
|
import { Lampp } from '../runtime/lampp/Lampp.js';
|
|
17
|
-
import { createValkeyConnection } from './valkey.js';
|
|
18
|
-
import { applySecurity, authMiddlewareFactory } from './auth.js';
|
|
19
16
|
import { getInstanceContext } from './conf.js';
|
|
20
|
-
|
|
17
|
+
|
|
18
|
+
import ExpressService from '../runtime/express/Express.js';
|
|
21
19
|
|
|
22
20
|
dotenv.config();
|
|
23
21
|
|
|
24
22
|
const logger = loggerFactory(import.meta);
|
|
25
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Reads server configurations, sets up Prometheus metrics, and iterates through
|
|
26
|
+
* all defined hosts and paths to build and start the corresponding runtime instances.
|
|
27
|
+
*
|
|
28
|
+
* @memberof Runtime
|
|
29
|
+
* @returns {Promise<void>}
|
|
30
|
+
*/
|
|
26
31
|
const buildRuntime = async () => {
|
|
27
32
|
const deployId = process.env.DEPLOY_ID;
|
|
28
33
|
|
|
34
|
+
// 1. Initialize Prometheus Metrics
|
|
29
35
|
const collectDefaultMetrics = promClient.collectDefaultMetrics;
|
|
30
36
|
collectDefaultMetrics();
|
|
31
37
|
|
|
@@ -38,12 +44,17 @@ const buildRuntime = async () => {
|
|
|
38
44
|
const requestCounter = new promClient.Counter(promCounterOption);
|
|
39
45
|
const initPort = parseInt(process.env.PORT) + 1;
|
|
40
46
|
let currentPort = initPort;
|
|
47
|
+
|
|
48
|
+
// 2. Load Configuration
|
|
41
49
|
const confServer = JSON.parse(fs.readFileSync(`./conf/conf.server.json`, 'utf8'));
|
|
42
50
|
const confSSR = JSON.parse(fs.readFileSync(`./conf/conf.ssr.json`, 'utf8'));
|
|
43
51
|
const singleReplicaHosts = [];
|
|
52
|
+
|
|
53
|
+
// 3. Iterate through hosts and paths
|
|
44
54
|
for (const host of Object.keys(confServer)) {
|
|
45
55
|
if (singleReplicaHosts.length > 0)
|
|
46
56
|
currentPort += singleReplicaHosts.reduce((accumulator, currentValue) => accumulator + currentValue.replicas, 0);
|
|
57
|
+
|
|
47
58
|
const rootHostPath = `/public/${host}`;
|
|
48
59
|
for (const path of Object.keys(confServer[host])) {
|
|
49
60
|
confServer[host][path].port = newInstance(currentPort);
|
|
@@ -65,6 +76,7 @@ const buildRuntime = async () => {
|
|
|
65
76
|
apiBaseHost,
|
|
66
77
|
} = confServer[host][path];
|
|
67
78
|
|
|
79
|
+
// Calculate context data
|
|
68
80
|
const { redirectTarget, singleReplicaHost } = await getInstanceContext({
|
|
69
81
|
redirect,
|
|
70
82
|
singleReplicaHosts,
|
|
@@ -91,203 +103,32 @@ const buildRuntime = async () => {
|
|
|
91
103
|
|
|
92
104
|
switch (runtime) {
|
|
93
105
|
case 'nodejs':
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// set logger
|
|
119
|
-
app.use(loggerMiddleware(import.meta));
|
|
120
|
-
|
|
121
|
-
// js src compression
|
|
122
|
-
app.use(compression({ filter: shouldCompress }));
|
|
123
|
-
function shouldCompress(req, res) {
|
|
124
|
-
if (req.headers['x-no-compression']) {
|
|
125
|
-
// don't compress responses with this request header
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// fallback to standard filter function
|
|
130
|
-
return compression.filter(req, res);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// parse requests of content-type - application/json
|
|
134
|
-
app.use(express.json({ limit: '100MB' }));
|
|
135
|
-
|
|
136
|
-
// parse requests of content-type - application/x-www-form-urlencoded
|
|
137
|
-
app.use(express.urlencoded({ extended: true, limit: '20MB' }));
|
|
138
|
-
|
|
139
|
-
// file upload middleware
|
|
140
|
-
app.use(fileUpload());
|
|
141
|
-
|
|
142
|
-
// json formatted response
|
|
143
|
-
if (process.env.NODE_ENV === 'development') app.set('json spaces', 2);
|
|
144
|
-
|
|
145
|
-
// lang handling middleware
|
|
146
|
-
app.use(function (req, res, next) {
|
|
147
|
-
const lang = req.headers['accept-language'] || 'en';
|
|
148
|
-
if (typeof lang === 'string' && lang.toLowerCase().match('es')) {
|
|
149
|
-
req.lang = 'es';
|
|
150
|
-
} else req.lang = 'en';
|
|
151
|
-
return next();
|
|
106
|
+
logger.info('Build nodejs server runtime', `${host}${path}:${port}`);
|
|
107
|
+
|
|
108
|
+
const { portsUsed } = await ExpressService.createApp({
|
|
109
|
+
host,
|
|
110
|
+
path,
|
|
111
|
+
port,
|
|
112
|
+
client,
|
|
113
|
+
apis,
|
|
114
|
+
origins,
|
|
115
|
+
directory,
|
|
116
|
+
ws,
|
|
117
|
+
mailer,
|
|
118
|
+
db,
|
|
119
|
+
redirect,
|
|
120
|
+
peer,
|
|
121
|
+
valkey,
|
|
122
|
+
apiBaseHost,
|
|
123
|
+
redirectTarget,
|
|
124
|
+
rootHostPath,
|
|
125
|
+
confSSR,
|
|
126
|
+
promRequestCounter: requestCounter,
|
|
127
|
+
promRegister: promClient.register,
|
|
152
128
|
});
|
|
153
129
|
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
if (process.argv.includes('static')) {
|
|
157
|
-
logger.info('Build static server runtime', `${host}${path}`);
|
|
158
|
-
currentPort += 2;
|
|
159
|
-
const staticPort = newInstance(currentPort);
|
|
160
|
-
await UnderpostStartUp.API.listenPortController(app, staticPort, runningData);
|
|
161
|
-
currentPort++;
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Flag swagger requests before security middleware is applied
|
|
166
|
-
const swaggerJsonPath = `./public/${host}${path === '/' ? path : `${path}/`}swagger-output.json`;
|
|
167
|
-
const swaggerPath = `${path === '/' ? `/api-docs` : `${path}/api-docs`}`;
|
|
168
|
-
if (fs.existsSync(swaggerJsonPath))
|
|
169
|
-
app.use(swaggerPath, (req, res, next) => {
|
|
170
|
-
res.locals.isSwagger = true;
|
|
171
|
-
next();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// security
|
|
175
|
-
applySecurity(app, {
|
|
176
|
-
origin: origins.concat(
|
|
177
|
-
apis && process.env.NODE_ENV === 'development' ? [`http://localhost:${currentPort + 2}`] : [],
|
|
178
|
-
),
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
if (redirect) {
|
|
182
|
-
app.use(function (req = express.Request, res = express.Response, next = express.NextFunction) {
|
|
183
|
-
if (process.env.NODE_ENV === 'production' && !req.url.startsWith(`/.well-known/acme-challenge`))
|
|
184
|
-
return res.status(302).redirect(redirectTarget + req.url);
|
|
185
|
-
// if (!req.url.startsWith(`/.well-known/acme-challenge`)) return res.status(302).redirect(redirect);
|
|
186
|
-
return next();
|
|
187
|
-
});
|
|
188
|
-
// app.use(
|
|
189
|
-
// '*',
|
|
190
|
-
// createProxyMiddleware({
|
|
191
|
-
// target: redirect,
|
|
192
|
-
// changeOrigin: true,
|
|
193
|
-
// }),
|
|
194
|
-
// );
|
|
195
|
-
|
|
196
|
-
await UnderpostStartUp.API.listenPortController(app, port, runningData);
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
199
|
-
// instance server
|
|
200
|
-
const server = createServer({}, app);
|
|
201
|
-
if (peer) currentPort++;
|
|
202
|
-
|
|
203
|
-
if (!apiBaseHost) {
|
|
204
|
-
if (fs.existsSync(swaggerJsonPath)) {
|
|
205
|
-
const swaggerInstance =
|
|
206
|
-
(swaggerDoc) =>
|
|
207
|
-
(...args) =>
|
|
208
|
-
swaggerUi.setup(swaggerDoc)(...args);
|
|
209
|
-
const swaggerDoc = JSON.parse(fs.readFileSync(swaggerJsonPath, 'utf8'));
|
|
210
|
-
const swaggerPath = `${path === '/' ? `/api-docs` : `${path}/api-docs`}`;
|
|
211
|
-
app.use(swaggerPath, swaggerUi.serve, swaggerInstance(swaggerDoc));
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (db && apis) await DataBaseProvider.load({ apis, host, path, db });
|
|
215
|
-
|
|
216
|
-
// valkey server
|
|
217
|
-
if (valkey) await createValkeyConnection({ host, path }, valkey);
|
|
218
|
-
|
|
219
|
-
if (mailer) {
|
|
220
|
-
const mailerSsrConf = confSSR[getCapVariableName(client)];
|
|
221
|
-
await MailerProvider.load({
|
|
222
|
-
id: `${host}${path}`,
|
|
223
|
-
meta: `mailer-${host}${path}`,
|
|
224
|
-
host,
|
|
225
|
-
path,
|
|
226
|
-
...mailer,
|
|
227
|
-
templates: mailerSsrConf ? mailerSsrConf.mailer : {},
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
if (apis && apis.length > 0) {
|
|
231
|
-
const authMiddleware = authMiddlewareFactory({ host, path });
|
|
232
|
-
const apiPath = `${path === '/' ? '' : path}/${process.env.BASE_API}`;
|
|
233
|
-
for (const api of apis)
|
|
234
|
-
await (async () => {
|
|
235
|
-
logger.info(`Build api server`, `${host}${apiPath}/${api}`);
|
|
236
|
-
const { ApiRouter } = await import(`../api/${api}/${api}.router.js`);
|
|
237
|
-
const router = ApiRouter({ host, path, apiPath, mailer, db, authMiddleware, origins });
|
|
238
|
-
// router.use(cors({ origin: origins }));
|
|
239
|
-
// logger.info('Load api router', { host, path: apiPath, api });
|
|
240
|
-
app.use(`${apiPath}/${api}`, router);
|
|
241
|
-
})();
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
if (ws)
|
|
245
|
-
await (async () => {
|
|
246
|
-
const { createIoServer } = await import(`../ws/${ws}/${ws}.ws.server.js`);
|
|
247
|
-
// logger.info('Load socket.io ws router', { host, ws });
|
|
248
|
-
// start socket.io
|
|
249
|
-
const { options, meta } = await createIoServer(server, {
|
|
250
|
-
host,
|
|
251
|
-
path,
|
|
252
|
-
db,
|
|
253
|
-
port,
|
|
254
|
-
origins,
|
|
255
|
-
});
|
|
256
|
-
await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), port, {
|
|
257
|
-
runtime: 'nodejs',
|
|
258
|
-
client: null,
|
|
259
|
-
host,
|
|
260
|
-
path: options.path,
|
|
261
|
-
meta,
|
|
262
|
-
});
|
|
263
|
-
})();
|
|
264
|
-
|
|
265
|
-
if (peer) {
|
|
266
|
-
const peerPort = newInstance(currentPort);
|
|
267
|
-
const { options, meta, peerServer } = await createPeerServer({
|
|
268
|
-
port: peerPort,
|
|
269
|
-
devPort: port,
|
|
270
|
-
origins,
|
|
271
|
-
host,
|
|
272
|
-
path,
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
await UnderpostStartUp.API.listenPortController(peerServer, peerPort, {
|
|
276
|
-
runtime: 'nodejs',
|
|
277
|
-
client: null,
|
|
278
|
-
host,
|
|
279
|
-
path: options.path,
|
|
280
|
-
meta,
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// load ssr
|
|
286
|
-
const ssr = await ssrMiddlewareFactory({ app, directory, rootHostPath, path });
|
|
287
|
-
for (const [_, ssrMiddleware] of Object.entries(ssr)) app.use(ssrMiddleware);
|
|
288
|
-
|
|
289
|
-
await UnderpostStartUp.API.listenPortController(server, port, runningData);
|
|
290
|
-
|
|
130
|
+
// Increment currentPort by any additional ports used by the service (e.g., PeerServer port)
|
|
131
|
+
currentPort += portsUsed;
|
|
291
132
|
break;
|
|
292
133
|
|
|
293
134
|
case 'lampp':
|
package/src/server/start.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages the startup and runtime configuration of Underpost applications.
|
|
3
|
+
* @module src/server/start.js
|
|
4
|
+
* @namespace UnderpostStartUp
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
import UnderpostDeploy from '../cli/deploy.js';
|
|
2
8
|
import fs from 'fs-extra';
|
|
3
9
|
import { awaitDeployMonitor } from './conf.js';
|
|
@@ -7,8 +13,17 @@ import UnderpostRootEnv from '../cli/env.js';
|
|
|
7
13
|
|
|
8
14
|
const logger = loggerFactory(import.meta);
|
|
9
15
|
|
|
16
|
+
/**
|
|
17
|
+
* @class UnderpostStartUp
|
|
18
|
+
* @description Manages the startup and runtime configuration of Underpost applications.
|
|
19
|
+
* @memberof UnderpostStartUp
|
|
20
|
+
*/
|
|
10
21
|
class UnderpostStartUp {
|
|
11
22
|
static API = {
|
|
23
|
+
/**
|
|
24
|
+
* Logs the runtime network configuration.
|
|
25
|
+
* @memberof UnderpostStartUp
|
|
26
|
+
*/
|
|
12
27
|
logRuntimeRouter: () => {
|
|
13
28
|
const displayLog = {};
|
|
14
29
|
|
|
@@ -18,6 +33,12 @@ class UnderpostStartUp {
|
|
|
18
33
|
|
|
19
34
|
logger.info('Runtime network', displayLog);
|
|
20
35
|
},
|
|
36
|
+
/**
|
|
37
|
+
* Creates a server factory.
|
|
38
|
+
* @memberof UnderpostStartUp
|
|
39
|
+
* @param {Function} logic - The logic to execute when the server is listening.
|
|
40
|
+
* @returns {Object} An object with a listen method.
|
|
41
|
+
*/
|
|
21
42
|
listenServerFactory: (logic = async () => {}) => {
|
|
22
43
|
return {
|
|
23
44
|
listen: async (...args) => {
|
|
@@ -36,6 +57,15 @@ class UnderpostStartUp {
|
|
|
36
57
|
},
|
|
37
58
|
};
|
|
38
59
|
},
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Controls the listening port for a server.
|
|
63
|
+
* @memberof UnderpostStartUp
|
|
64
|
+
* @param {Object} server - The server to listen on.
|
|
65
|
+
* @param {number|string} port - The port number or colon for all ports.
|
|
66
|
+
* @param {Object} metadata - Metadata for the server.
|
|
67
|
+
* @returns {Promise<boolean>} A promise that resolves to true if the server is listening, false otherwise.
|
|
68
|
+
*/
|
|
39
69
|
listenPortController: async (server, port, metadata) =>
|
|
40
70
|
new Promise((resolve) => {
|
|
41
71
|
try {
|
|
@@ -79,6 +109,15 @@ class UnderpostStartUp {
|
|
|
79
109
|
}
|
|
80
110
|
}),
|
|
81
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Starts a deployment.
|
|
114
|
+
* @memberof UnderpostStartUp
|
|
115
|
+
* @param {string} deployId - The ID of the deployment.
|
|
116
|
+
* @param {string} env - The environment of the deployment.
|
|
117
|
+
* @param {Object} options - Options for the deployment.
|
|
118
|
+
* @param {boolean} options.build - Whether to build the deployment.
|
|
119
|
+
* @param {boolean} options.run - Whether to run the deployment.
|
|
120
|
+
*/
|
|
82
121
|
async callback(deployId = 'dd-default', env = 'development', options = { build: false, run: false }) {
|
|
83
122
|
if (options.build === true) await UnderpostStartUp.API.build(deployId, env);
|
|
84
123
|
if (options.run === true) await UnderpostStartUp.API.run(deployId, env);
|