underpost 2.8.882 → 2.8.884
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 +10 -2
- package/bin/db.js +1 -4
- package/cli.md +1 -1
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
- package/manifests/maas/device-scan.sh +1 -1
- package/package.json +1 -1
- package/src/api/user/user.service.js +3 -10
- package/src/client/components/core/Docs.js +0 -1
- package/src/client/components/core/Modal.js +14 -8
- package/src/index.js +1 -1
- package/src/runtime/express/Express.js +262 -0
- package/src/runtime/lampp/Lampp.js +272 -128
- package/src/server/auth.js +74 -16
- package/src/server/runtime.js +54 -230
- package/src/runtime/nginx/Nginx.js +0 -3
- package/src/runtime/xampp/Xampp.js +0 -83
package/src/server/runtime.js
CHANGED
|
@@ -1,32 +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 { Xampp } from '../runtime/xampp/Xampp.js';
|
|
18
|
-
import { createValkeyConnection } from './valkey.js';
|
|
19
|
-
import { applySecurity, authMiddlewareFactory } from './auth.js';
|
|
20
16
|
import { getInstanceContext } from './conf.js';
|
|
21
|
-
|
|
17
|
+
|
|
18
|
+
import ExpressService from '../runtime/express/Express.js';
|
|
22
19
|
|
|
23
20
|
dotenv.config();
|
|
24
21
|
|
|
25
22
|
const logger = loggerFactory(import.meta);
|
|
26
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
|
+
*/
|
|
27
31
|
const buildRuntime = async () => {
|
|
28
32
|
const deployId = process.env.DEPLOY_ID;
|
|
29
33
|
|
|
34
|
+
// 1. Initialize Prometheus Metrics
|
|
30
35
|
const collectDefaultMetrics = promClient.collectDefaultMetrics;
|
|
31
36
|
collectDefaultMetrics();
|
|
32
37
|
|
|
@@ -39,12 +44,17 @@ const buildRuntime = async () => {
|
|
|
39
44
|
const requestCounter = new promClient.Counter(promCounterOption);
|
|
40
45
|
const initPort = parseInt(process.env.PORT) + 1;
|
|
41
46
|
let currentPort = initPort;
|
|
47
|
+
|
|
48
|
+
// 2. Load Configuration
|
|
42
49
|
const confServer = JSON.parse(fs.readFileSync(`./conf/conf.server.json`, 'utf8'));
|
|
43
50
|
const confSSR = JSON.parse(fs.readFileSync(`./conf/conf.ssr.json`, 'utf8'));
|
|
44
51
|
const singleReplicaHosts = [];
|
|
52
|
+
|
|
53
|
+
// 3. Iterate through hosts and paths
|
|
45
54
|
for (const host of Object.keys(confServer)) {
|
|
46
55
|
if (singleReplicaHosts.length > 0)
|
|
47
56
|
currentPort += singleReplicaHosts.reduce((accumulator, currentValue) => accumulator + currentValue.replicas, 0);
|
|
57
|
+
|
|
48
58
|
const rootHostPath = `/public/${host}`;
|
|
49
59
|
for (const path of Object.keys(confServer[host])) {
|
|
50
60
|
confServer[host][path].port = newInstance(currentPort);
|
|
@@ -66,6 +76,7 @@ const buildRuntime = async () => {
|
|
|
66
76
|
apiBaseHost,
|
|
67
77
|
} = confServer[host][path];
|
|
68
78
|
|
|
79
|
+
// Calculate context data
|
|
69
80
|
const { redirectTarget, singleReplicaHost } = await getInstanceContext({
|
|
70
81
|
redirect,
|
|
71
82
|
singleReplicaHosts,
|
|
@@ -92,203 +103,37 @@ const buildRuntime = async () => {
|
|
|
92
103
|
|
|
93
104
|
switch (runtime) {
|
|
94
105
|
case 'nodejs':
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// js src compression
|
|
123
|
-
app.use(compression({ filter: shouldCompress }));
|
|
124
|
-
function shouldCompress(req, res) {
|
|
125
|
-
if (req.headers['x-no-compression']) {
|
|
126
|
-
// don't compress responses with this request header
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// fallback to standard filter function
|
|
131
|
-
return compression.filter(req, res);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// parse requests of content-type - application/json
|
|
135
|
-
app.use(express.json({ limit: '100MB' }));
|
|
136
|
-
|
|
137
|
-
// parse requests of content-type - application/x-www-form-urlencoded
|
|
138
|
-
app.use(express.urlencoded({ extended: true, limit: '20MB' }));
|
|
139
|
-
|
|
140
|
-
// file upload middleware
|
|
141
|
-
app.use(fileUpload());
|
|
142
|
-
|
|
143
|
-
// json formatted response
|
|
144
|
-
if (process.env.NODE_ENV === 'development') app.set('json spaces', 2);
|
|
145
|
-
|
|
146
|
-
// lang handling middleware
|
|
147
|
-
app.use(function (req, res, next) {
|
|
148
|
-
const lang = req.headers['accept-language'] || 'en';
|
|
149
|
-
if (typeof lang === 'string' && lang.toLowerCase().match('es')) {
|
|
150
|
-
req.lang = 'es';
|
|
151
|
-
} else req.lang = 'en';
|
|
152
|
-
return next();
|
|
106
|
+
// The devApiPort is used for development CORS origin calculation
|
|
107
|
+
// It needs to account for the current port and potential peer server increment
|
|
108
|
+
const devApiPort = currentPort + (peer ? 2 : 1);
|
|
109
|
+
|
|
110
|
+
logger.info('Build nodejs server runtime', `${host}${path}:${port}`);
|
|
111
|
+
|
|
112
|
+
const { portsUsed } = await ExpressService.createApp({
|
|
113
|
+
host,
|
|
114
|
+
path,
|
|
115
|
+
port,
|
|
116
|
+
client,
|
|
117
|
+
apis,
|
|
118
|
+
origins,
|
|
119
|
+
directory,
|
|
120
|
+
ws,
|
|
121
|
+
mailer,
|
|
122
|
+
db,
|
|
123
|
+
redirect,
|
|
124
|
+
peer,
|
|
125
|
+
valkey,
|
|
126
|
+
apiBaseHost,
|
|
127
|
+
devApiPort, // Pass the dynamically calculated dev API port
|
|
128
|
+
redirectTarget,
|
|
129
|
+
rootHostPath,
|
|
130
|
+
confSSR,
|
|
131
|
+
promRequestCounter: requestCounter,
|
|
132
|
+
promRegister: promClient.register,
|
|
153
133
|
});
|
|
154
134
|
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
if (process.argv.includes('static')) {
|
|
158
|
-
logger.info('Build static server runtime', `${host}${path}`);
|
|
159
|
-
currentPort += 2;
|
|
160
|
-
const staticPort = newInstance(currentPort);
|
|
161
|
-
await UnderpostStartUp.API.listenPortController(app, staticPort, runningData);
|
|
162
|
-
currentPort++;
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Flag swagger requests before security middleware is applied
|
|
167
|
-
const swaggerJsonPath = `./public/${host}${path === '/' ? path : `${path}/`}swagger-output.json`;
|
|
168
|
-
const swaggerPath = `${path === '/' ? `/api-docs` : `${path}/api-docs`}`;
|
|
169
|
-
if (fs.existsSync(swaggerJsonPath))
|
|
170
|
-
app.use(swaggerPath, (req, res, next) => {
|
|
171
|
-
res.locals.isSwagger = true;
|
|
172
|
-
next();
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
// security
|
|
176
|
-
applySecurity(app, {
|
|
177
|
-
origin: origins.concat(
|
|
178
|
-
apis && process.env.NODE_ENV === 'development' ? [`http://localhost:${currentPort + 2}`] : [],
|
|
179
|
-
),
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
if (redirect) {
|
|
183
|
-
app.use(function (req = express.Request, res = express.Response, next = express.NextFunction) {
|
|
184
|
-
if (process.env.NODE_ENV === 'production' && !req.url.startsWith(`/.well-known/acme-challenge`))
|
|
185
|
-
return res.status(302).redirect(redirectTarget + req.url);
|
|
186
|
-
// if (!req.url.startsWith(`/.well-known/acme-challenge`)) return res.status(302).redirect(redirect);
|
|
187
|
-
return next();
|
|
188
|
-
});
|
|
189
|
-
// app.use(
|
|
190
|
-
// '*',
|
|
191
|
-
// createProxyMiddleware({
|
|
192
|
-
// target: redirect,
|
|
193
|
-
// changeOrigin: true,
|
|
194
|
-
// }),
|
|
195
|
-
// );
|
|
196
|
-
|
|
197
|
-
await UnderpostStartUp.API.listenPortController(app, port, runningData);
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
// instance server
|
|
201
|
-
const server = createServer({}, app);
|
|
202
|
-
if (peer) currentPort++;
|
|
203
|
-
|
|
204
|
-
if (!apiBaseHost) {
|
|
205
|
-
if (fs.existsSync(swaggerJsonPath)) {
|
|
206
|
-
const swaggerInstance =
|
|
207
|
-
(swaggerDoc) =>
|
|
208
|
-
(...args) =>
|
|
209
|
-
swaggerUi.setup(swaggerDoc)(...args);
|
|
210
|
-
const swaggerDoc = JSON.parse(fs.readFileSync(swaggerJsonPath, 'utf8'));
|
|
211
|
-
const swaggerPath = `${path === '/' ? `/api-docs` : `${path}/api-docs`}`;
|
|
212
|
-
app.use(swaggerPath, swaggerUi.serve, swaggerInstance(swaggerDoc));
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (db && apis) await DataBaseProvider.load({ apis, host, path, db });
|
|
216
|
-
|
|
217
|
-
// valkey server
|
|
218
|
-
if (valkey) await createValkeyConnection({ host, path }, valkey);
|
|
219
|
-
|
|
220
|
-
if (mailer) {
|
|
221
|
-
const mailerSsrConf = confSSR[getCapVariableName(client)];
|
|
222
|
-
await MailerProvider.load({
|
|
223
|
-
id: `${host}${path}`,
|
|
224
|
-
meta: `mailer-${host}${path}`,
|
|
225
|
-
host,
|
|
226
|
-
path,
|
|
227
|
-
...mailer,
|
|
228
|
-
templates: mailerSsrConf ? mailerSsrConf.mailer : {},
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
if (apis && apis.length > 0) {
|
|
232
|
-
const authMiddleware = authMiddlewareFactory({ host, path });
|
|
233
|
-
const apiPath = `${path === '/' ? '' : path}/${process.env.BASE_API}`;
|
|
234
|
-
for (const api of apis)
|
|
235
|
-
await (async () => {
|
|
236
|
-
logger.info(`Build api server`, `${host}${apiPath}/${api}`);
|
|
237
|
-
const { ApiRouter } = await import(`../api/${api}/${api}.router.js`);
|
|
238
|
-
const router = ApiRouter({ host, path, apiPath, mailer, db, authMiddleware, origins });
|
|
239
|
-
// router.use(cors({ origin: origins }));
|
|
240
|
-
// logger.info('Load api router', { host, path: apiPath, api });
|
|
241
|
-
app.use(`${apiPath}/${api}`, router);
|
|
242
|
-
})();
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (ws)
|
|
246
|
-
await (async () => {
|
|
247
|
-
const { createIoServer } = await import(`../ws/${ws}/${ws}.ws.server.js`);
|
|
248
|
-
// logger.info('Load socket.io ws router', { host, ws });
|
|
249
|
-
// start socket.io
|
|
250
|
-
const { options, meta } = await createIoServer(server, {
|
|
251
|
-
host,
|
|
252
|
-
path,
|
|
253
|
-
db,
|
|
254
|
-
port,
|
|
255
|
-
origins,
|
|
256
|
-
});
|
|
257
|
-
await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), port, {
|
|
258
|
-
runtime: 'nodejs',
|
|
259
|
-
client: null,
|
|
260
|
-
host,
|
|
261
|
-
path: options.path,
|
|
262
|
-
meta,
|
|
263
|
-
});
|
|
264
|
-
})();
|
|
265
|
-
|
|
266
|
-
if (peer) {
|
|
267
|
-
const peerPort = newInstance(currentPort);
|
|
268
|
-
const { options, meta, peerServer } = await createPeerServer({
|
|
269
|
-
port: peerPort,
|
|
270
|
-
devPort: port,
|
|
271
|
-
origins,
|
|
272
|
-
host,
|
|
273
|
-
path,
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
await UnderpostStartUp.API.listenPortController(peerServer, peerPort, {
|
|
277
|
-
runtime: 'nodejs',
|
|
278
|
-
client: null,
|
|
279
|
-
host,
|
|
280
|
-
path: options.path,
|
|
281
|
-
meta,
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// load ssr
|
|
287
|
-
const ssr = await ssrMiddlewareFactory({ app, directory, rootHostPath, path });
|
|
288
|
-
for (const [_, ssrMiddleware] of Object.entries(ssr)) app.use(ssrMiddleware);
|
|
289
|
-
|
|
290
|
-
await UnderpostStartUp.API.listenPortController(server, port, runningData);
|
|
291
|
-
|
|
135
|
+
// Increment currentPort by any additional ports used by the service (e.g., PeerServer port)
|
|
136
|
+
currentPort += portsUsed;
|
|
292
137
|
break;
|
|
293
138
|
|
|
294
139
|
case 'lampp':
|
|
@@ -311,26 +156,6 @@ const buildRuntime = async () => {
|
|
|
311
156
|
);
|
|
312
157
|
}
|
|
313
158
|
break;
|
|
314
|
-
case 'xampp':
|
|
315
|
-
{
|
|
316
|
-
const { disabled } = await Xampp.createApp({
|
|
317
|
-
port,
|
|
318
|
-
host,
|
|
319
|
-
path,
|
|
320
|
-
directory,
|
|
321
|
-
rootHostPath,
|
|
322
|
-
redirect,
|
|
323
|
-
redirectTarget,
|
|
324
|
-
resetRouter: currentPort === initPort,
|
|
325
|
-
});
|
|
326
|
-
if (disabled) continue;
|
|
327
|
-
await UnderpostStartUp.API.listenPortController(
|
|
328
|
-
UnderpostStartUp.API.listenServerFactory(),
|
|
329
|
-
port,
|
|
330
|
-
runningData,
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
break;
|
|
334
159
|
default:
|
|
335
160
|
break;
|
|
336
161
|
}
|
|
@@ -338,7 +163,6 @@ const buildRuntime = async () => {
|
|
|
338
163
|
}
|
|
339
164
|
}
|
|
340
165
|
|
|
341
|
-
if (Xampp.enabled() && Xampp.router) Xampp.initService();
|
|
342
166
|
if (Lampp.enabled() && Lampp.router) Lampp.initService();
|
|
343
167
|
|
|
344
168
|
UnderpostStartUp.API.logRuntimeRouter();
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import { shellExec, getRootDirectory } from '../../server/process.js';
|
|
3
|
-
|
|
4
|
-
const Xampp = {
|
|
5
|
-
ports: [],
|
|
6
|
-
initService: async function (options = { daemon: false }) {
|
|
7
|
-
let cmd;
|
|
8
|
-
// windows
|
|
9
|
-
fs.writeFileSync(
|
|
10
|
-
`C:/xampp/apache/conf/httpd.conf`,
|
|
11
|
-
fs.readFileSync(`C:/xampp/apache/conf/httpd.template.conf`, 'utf8').replace(`Listen 80`, ``),
|
|
12
|
-
'utf8',
|
|
13
|
-
);
|
|
14
|
-
fs.writeFileSync(`C:/xampp/apache/conf/extra/httpd-ssl.conf`, this.router || '', 'utf8');
|
|
15
|
-
cmd = `C:/xampp/xampp_stop.exe`;
|
|
16
|
-
shellExec(cmd);
|
|
17
|
-
cmd = `C:/xampp/xampp_start.exe`;
|
|
18
|
-
if (this.router) fs.writeFileSync(`./tmp/xampp-router.conf`, this.router, 'utf-8');
|
|
19
|
-
shellExec(cmd);
|
|
20
|
-
},
|
|
21
|
-
enabled: () => fs.existsSync(`C:/xampp/apache/conf/httpd.conf`),
|
|
22
|
-
appendRouter: function (render) {
|
|
23
|
-
if (!this.router) {
|
|
24
|
-
if (fs.existsSync(`./tmp/xampp-router.conf`))
|
|
25
|
-
return (this.router = fs.readFileSync(`./tmp/xampp-router.conf`, 'utf-8')) + render;
|
|
26
|
-
return (this.router = render);
|
|
27
|
-
}
|
|
28
|
-
return (this.router += render);
|
|
29
|
-
},
|
|
30
|
-
removeRouter: function () {
|
|
31
|
-
this.router = undefined;
|
|
32
|
-
if (fs.existsSync(`./tmp/xampp-router.conf`)) fs.rmSync(`./tmp/xampp-router.conf`);
|
|
33
|
-
},
|
|
34
|
-
createApp: async ({ port, host, path, directory, rootHostPath, redirect, redirectTarget, resetRouter }) => {
|
|
35
|
-
if (!Xampp.enabled()) {
|
|
36
|
-
return { disabled: true };
|
|
37
|
-
}
|
|
38
|
-
if (!Xampp.ports.includes(port)) Xampp.ports.push(port);
|
|
39
|
-
if (resetRouter) Xampp.removeRouter();
|
|
40
|
-
Xampp.appendRouter(`
|
|
41
|
-
Listen ${port}
|
|
42
|
-
|
|
43
|
-
<VirtualHost *:${port}>
|
|
44
|
-
DocumentRoot "${directory ? directory : `${getRootDirectory()}${rootHostPath}`}"
|
|
45
|
-
ServerName ${host}:${port}
|
|
46
|
-
|
|
47
|
-
<Directory "${directory ? directory : `${getRootDirectory()}${rootHostPath}`}">
|
|
48
|
-
Options Indexes FollowSymLinks MultiViews
|
|
49
|
-
AllowOverride All
|
|
50
|
-
Require all granted
|
|
51
|
-
</Directory>
|
|
52
|
-
|
|
53
|
-
${
|
|
54
|
-
redirect
|
|
55
|
-
? `
|
|
56
|
-
RewriteEngine on
|
|
57
|
-
|
|
58
|
-
RewriteCond %{REQUEST_URI} !^/.well-known/acme-challenge
|
|
59
|
-
RewriteRule ^(.*)$ ${redirectTarget}%{REQUEST_URI} [R=302,L]
|
|
60
|
-
`
|
|
61
|
-
: ''
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
ErrorDocument 400 ${path === '/' ? '' : path}/400.html
|
|
65
|
-
ErrorDocument 404 ${path === '/' ? '' : path}/400.html
|
|
66
|
-
ErrorDocument 500 ${path === '/' ? '' : path}/500.html
|
|
67
|
-
ErrorDocument 502 ${path === '/' ? '' : path}/500.html
|
|
68
|
-
ErrorDocument 503 ${path === '/' ? '' : path}/500.html
|
|
69
|
-
ErrorDocument 504 ${path === '/' ? '' : path}/500.html
|
|
70
|
-
|
|
71
|
-
</VirtualHost>
|
|
72
|
-
|
|
73
|
-
`);
|
|
74
|
-
// ERR too many redirects:
|
|
75
|
-
// Check: SELECT * FROM database.wp_options where option_name = 'siteurl' or option_name = 'home';
|
|
76
|
-
// Check: wp-config.php
|
|
77
|
-
// if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
|
|
78
|
-
// $_SERVER['HTTPS'] = 'on';
|
|
79
|
-
// }
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
export { Xampp };
|