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.
Files changed (46) hide show
  1. package/README.md +4 -116
  2. package/bin/deploy.js +9 -10
  3. package/bin/file.js +4 -6
  4. package/cli.md +15 -11
  5. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  6. package/manifests/deployment/dd-test-development/deployment.yaml +2 -2
  7. package/package.json +1 -1
  8. package/src/api/user/user.service.js +3 -10
  9. package/src/cli/cluster.js +21 -0
  10. package/src/cli/cron.js +8 -0
  11. package/src/cli/db.js +63 -1
  12. package/src/cli/deploy.js +156 -3
  13. package/src/cli/env.js +43 -0
  14. package/src/cli/fs.js +94 -0
  15. package/src/cli/image.js +8 -0
  16. package/src/cli/index.js +17 -4
  17. package/src/cli/monitor.js +0 -1
  18. package/src/cli/repository.js +95 -2
  19. package/src/client/components/core/Css.js +16 -0
  20. package/src/client/components/core/Docs.js +5 -13
  21. package/src/client/components/core/Modal.js +57 -39
  22. package/src/client/components/core/Router.js +6 -3
  23. package/src/client/components/core/Worker.js +205 -118
  24. package/src/client/components/default/MenuDefault.js +1 -0
  25. package/src/client.dev.js +6 -3
  26. package/src/db/DataBaseProvider.js +65 -12
  27. package/src/db/mariadb/MariaDB.js +39 -6
  28. package/src/db/mongo/MongooseDB.js +51 -133
  29. package/src/index.js +1 -1
  30. package/src/mailer/EmailRender.js +58 -9
  31. package/src/mailer/MailerProvider.js +98 -25
  32. package/src/runtime/express/Express.js +248 -0
  33. package/src/runtime/lampp/Lampp.js +27 -8
  34. package/src/server/auth.js +82 -43
  35. package/src/server/client-build-live.js +14 -5
  36. package/src/server/client-dev-server.js +21 -8
  37. package/src/server/conf.js +78 -25
  38. package/src/server/peer.js +2 -2
  39. package/src/server/runtime.js +49 -208
  40. package/src/server/start.js +39 -0
  41. package/src/ws/IoInterface.js +132 -39
  42. package/src/ws/IoServer.js +79 -31
  43. package/src/ws/core/core.ws.connection.js +50 -16
  44. package/src/ws/core/core.ws.emit.js +47 -8
  45. package/src/ws/core/core.ws.server.js +62 -10
  46. 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
- // process.argv.slice(2).join(' ')
10
- shellExec(`env-cmd -f .env.development node bin/deploy build-full-client ${process.argv.slice(2).join(' ')}`);
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.development node src/api ${process.argv[2]}${process.argv[5] ? ` ${process.argv[5]}` : ''}${
13
- process.argv.includes('static') ? ' static' : ''
14
- }`,
15
- { async: true },
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 = { script: './src/client.build', args: process.argv.slice(2), watch: 'src/client' };
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) {
@@ -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-')) deployContext = process.argv[2];
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
- return loadConf(deployContext, process.env.NODE_ENV, subConf);
33
- else if (deployContext.startsWith('dd-')) return loadConf(deployContext, process.env.NODE_ENV, subConf);
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 (deployContext = 'dd-default', deployList, subConf) {
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, deployContext, subConf))) {
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', envInput, subConf) => {
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 = envInput ?? '.';
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 = envInput || process.env.NODE_ENV;
177
+ const NODE_ENV = process.env.NODE_ENV;
178
178
  if (NODE_ENV) {
179
- fs.writeFileSync(`./.env`, fs.readFileSync(`${folder}/.env.${NODE_ENV}`, 'utf8'), 'utf8');
180
- const env = dotenv.parse(fs.readFileSync(`${folder}/.env.${NODE_ENV}`, 'utf8'));
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, deployContext, subConf) => {
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
- confServer[host][replicaPath] = newInstance(confServer[host][path]);
208
- delete confServer[host][replicaPath].replicas;
209
- delete confServer[host][replicaPath].singleReplica;
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
  };
@@ -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
  };
@@ -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 { createServer } from 'http';
11
- import { loggerFactory, loggerMiddleware } from './logger.js';
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
- import { ssrMiddlewareFactory } from './ssr.js';
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
- const app = express();
95
-
96
- app.use((req, res, next) => {
97
- // const info = `${req.headers.host}${req.url}`;
98
- return next();
99
- });
100
-
101
- if (process.env.NODE_ENV === 'production') app.set('trust proxy', true);
102
-
103
- app.use((req, res, next) => {
104
- requestCounter.inc({
105
- instance: `${host}:${port}${path}`,
106
- method: req.method,
107
- status_code: res.statusCode,
108
- });
109
- // decodeURIComponent(req.url)
110
- return next();
111
- });
112
-
113
- app.get(`${path === '/' ? '' : path}/metrics`, async (req, res) => {
114
- res.set('Content-Type', promClient.register.contentType);
115
- return res.end(await promClient.register.metrics());
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
- // instance public static
155
- app.use('/', express.static(directory ? directory : `.${rootHostPath}`));
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':
@@ -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);