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 CHANGED
@@ -64,6 +64,10 @@
64
64
 
65
65
 
66
66
 
67
+
68
+
69
+
70
+
67
71
 
68
72
 
69
73
 
@@ -72,7 +76,7 @@
72
76
  <!-- badges -->
73
77
 
74
78
 
75
- [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/2.8.882)](https://socket.dev/npm/package/underpost/overview/2.8.882) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
79
+ [![Node.js CI](https://github.com/underpostnet/engine/actions/workflows/docker-image.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/docker-image.yml) [![Test](https://github.com/underpostnet/engine/actions/workflows/coverall.ci.yml/badge.svg?branch=master)](https://github.com/underpostnet/engine/actions/workflows/coverall.yml) [![Downloads](https://img.shields.io/npm/dm/underpost.svg)](https://www.npmjs.com/package/underpost) [![Socket Badge](https://socket.dev/api/badge/npm/package/underpost/2.8.884)](https://socket.dev/npm/package/underpost/overview/2.8.884) [![Coverage Status](https://coveralls.io/repos/github/underpostnet/engine/badge.svg?branch=master)](https://coveralls.io/github/underpostnet/engine?branch=master) [![Version](https://img.shields.io/npm/v/underpost.svg)](https://www.npmjs.org/package/underpost) [![License](https://img.shields.io/npm/l/underpost.svg)](https://www.npmjs.com/package/underpost)
76
80
 
77
81
 
78
82
  <!-- end-badges -->
@@ -125,6 +129,10 @@
125
129
 
126
130
 
127
131
 
132
+
133
+
134
+
135
+
128
136
 
129
137
 
130
138
 
@@ -174,7 +182,7 @@ Run dev client server
174
182
  npm run dev
175
183
  ```
176
184
  <!-- -->
177
- ## underpost ci/cd cli v2.8.882
185
+ ## underpost ci/cd cli v2.8.884
178
186
 
179
187
  ### Usage: `underpost [options] [command]`
180
188
  ```
package/bin/db.js CHANGED
@@ -2,7 +2,6 @@ import fs from 'fs-extra';
2
2
  import { shellExec } from '../src/server/process.js';
3
3
  import { loggerFactory } from '../src/server/logger.js';
4
4
  import { MariaDB } from '../src/db/mariadb/MariaDB.js';
5
- import { Xampp } from '../src/runtime/xampp/Xampp.js';
6
5
  import { Lampp } from '../src/runtime/lampp/Lampp.js';
7
6
  import { getCapVariableName, loadConf, splitFileFactory } from '../src/server/conf.js';
8
7
  import { DataBaseProvider } from '../src/db/DataBaseProvider.js';
@@ -105,9 +104,7 @@ try {
105
104
  break;
106
105
  case 'import':
107
106
  break;
108
- case 'init-xampp-service':
109
- await Xampp.initService();
110
- break;
107
+
111
108
  case 'init-lampp-service':
112
109
  await Lampp.initService();
113
110
  break;
package/cli.md CHANGED
@@ -1,4 +1,4 @@
1
- ## underpost ci/cd cli v2.8.882
1
+ ## underpost ci/cd cli v2.8.884
2
2
 
3
3
  ### Usage: `underpost [options] [command]`
4
4
  ```
@@ -17,7 +17,7 @@ spec:
17
17
  spec:
18
18
  containers:
19
19
  - name: dd-default-development-blue
20
- image: localhost/rockylinux9-underpost:v2.8.882
20
+ image: localhost/rockylinux9-underpost:v2.8.884
21
21
  # resources:
22
22
  # requests:
23
23
  # memory: "124Ki"
@@ -100,7 +100,7 @@ spec:
100
100
  spec:
101
101
  containers:
102
102
  - name: dd-default-development-green
103
- image: localhost/rockylinux9-underpost:v2.8.882
103
+ image: localhost/rockylinux9-underpost:v2.8.884
104
104
  # resources:
105
105
  # requests:
106
106
  # memory: "124Ki"
@@ -17,7 +17,7 @@ spec:
17
17
  spec:
18
18
  containers:
19
19
  - name: dd-test-development-blue
20
- image: localhost/rockylinux9-underpost:v2.8.882
20
+ image: localhost/rockylinux9-underpost:v2.8.884
21
21
  # resources:
22
22
  # requests:
23
23
  # memory: "96294Ki"
@@ -104,7 +104,7 @@ spec:
104
104
  spec:
105
105
  containers:
106
106
  - name: dd-test-development-green
107
- image: localhost/rockylinux9-underpost:v2.8.882
107
+ image: localhost/rockylinux9-underpost:v2.8.884
108
108
  # resources:
109
109
  # requests:
110
110
  # memory: "96294Ki"
@@ -20,7 +20,7 @@ for iface_path in /sys/class/net/*; do
20
20
  if [ -f "$pci_dev/vendor" ] && [ -f "$pci_dev/device" ]; then
21
21
  vendor_id=$(< "$pci_dev/vendor")
22
22
  device_id=$(< "$pci_dev/device")
23
- # pasamos de 0x8086 a 8086, etc.
23
+ # parse 0x8086 to 8086, etc.
24
24
  vendor_id=${vendor_id#0x}
25
25
  device_id=${device_id#0x}
26
26
  pci="${vendor_id}:${device_id}"
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "type": "module",
3
3
  "main": "src/index.js",
4
4
  "name": "underpost",
5
- "version": "2.8.882",
5
+ "version": "2.8.884",
6
6
  "description": "pwa api rest template",
7
7
  "scripts": {
8
8
  "start": "env-cmd -f .env.production node --max-old-space-size=8192 src/server",
@@ -6,7 +6,7 @@ import {
6
6
  createSessionAndUserToken,
7
7
  createUserAndSession,
8
8
  refreshSessionAndToken,
9
- hashToken,
9
+ logoutSession,
10
10
  jwtSign,
11
11
  getBearerToken,
12
12
  validatePasswordMiddleware,
@@ -382,15 +382,8 @@ const UserService = {
382
382
  const User = DataBaseProvider.instance[`${options.host}${options.path}`].mongoose.models.User;
383
383
 
384
384
  if (req.params.id === 'logout') {
385
- const refreshToken = req.cookies?.refreshToken;
386
- if (refreshToken) {
387
- const hashedToken = hashToken(refreshToken);
388
- await User.updateOne(
389
- { 'activeSessions.tokenHash': hashedToken },
390
- { $pull: { activeSessions: { tokenHash: hashedToken } } },
391
- );
392
- }
393
- res.clearCookie('refreshToken');
385
+ const result = await logoutSession(User, req, res);
386
+ if (!result) throw new Error('Logout failed');
394
387
  return { message: 'Logged out successfully' };
395
388
  }
396
389
 
@@ -156,7 +156,6 @@ const Docs = {
156
156
  id: options.idModal,
157
157
  routeId: 'docs',
158
158
  event: (path) => {
159
- Modal.subMenuBtnClass['docs'].open = !Modal.subMenuBtnClass['docs'].open;
160
159
  if (s(`.btn-docs-${path}`)) s(`.btn-docs-${path}`).click();
161
160
  if (Modal.mobileModal()) {
162
161
  setTimeout(() => {
@@ -2191,14 +2191,13 @@ const Modal = {
2191
2191
  : { ...Modal.subMenuBtnClass, _: { btnSelector, labelSelector } };
2192
2192
 
2193
2193
  for (const keyDataBtn of Object.keys(_data)) {
2194
- const { btnSelector, labelSelector, open, top } = _data[keyDataBtn];
2194
+ const { labelSelector, top } = _data[keyDataBtn];
2195
2195
  if (top)
2196
2196
  setTimeout(() => {
2197
2197
  top();
2198
2198
  });
2199
- if (open) continue;
2200
2199
  sa(labelSelector).forEach((el) => {
2201
- el.classList.add('hide');
2200
+ if (!el.classList.contains('hide')) el.classList.add('hide');
2202
2201
  el.style.transition = null;
2203
2202
  });
2204
2203
 
@@ -2413,6 +2412,10 @@ const buildBadgeToolTipMenuOption = (id, sideKey = 'left') => {
2413
2412
  return option;
2414
2413
  };
2415
2414
 
2415
+ const isSubMenuOpen = (subMenuId) => {
2416
+ return s(`.down-arrow-submenu-${subMenuId}`).style.rotate === '180deg';
2417
+ };
2418
+
2416
2419
  const subMenuRender = async (subMenuId) => {
2417
2420
  const _hBtn = 51;
2418
2421
  const menuBtn = s(`.main-btn-${subMenuId}`);
@@ -2435,8 +2438,7 @@ const subMenuRender = async (subMenuId) => {
2435
2438
  menuBtn.style.transition = '.3s';
2436
2439
  arrow.style.transition = '.3s';
2437
2440
 
2438
- if (Modal.subMenuBtnClass[subMenuId].open) {
2439
- Modal.subMenuBtnClass[subMenuId].open = false;
2441
+ if (isSubMenuOpen(subMenuId)) {
2440
2442
  // Close animation
2441
2443
  menuContainer.style.overflow = 'hidden';
2442
2444
  menuContainer.style.height = '0px';
@@ -2446,8 +2448,12 @@ const subMenuRender = async (subMenuId) => {
2446
2448
  arrow.style.rotate = '0deg';
2447
2449
  });
2448
2450
  } else {
2449
- Modal.menuTextLabelAnimation('modal-menu', subMenuId);
2450
- Modal.subMenuBtnClass[subMenuId].open = true;
2451
+ sa(`.menu-label-text-${subMenuId}`).forEach((el) => {
2452
+ if (!el.classList.contains('hide')) el.classList.add('hide');
2453
+ });
2454
+ setTimeout(() => {
2455
+ Modal.menuTextLabelAnimation('modal-menu', subMenuId);
2456
+ });
2451
2457
  // Open animation
2452
2458
  setTimeout(top, 360);
2453
2459
  menuContainer.style.width = '320px';
@@ -2466,4 +2472,4 @@ const subMenuRender = async (subMenuId) => {
2466
2472
  }, 500);
2467
2473
  };
2468
2474
 
2469
- export { Modal, renderMenuLabel, renderViewTitle, buildBadgeToolTipMenuOption, subMenuRender };
2475
+ export { Modal, renderMenuLabel, renderViewTitle, buildBadgeToolTipMenuOption, subMenuRender, isSubMenuOpen };
package/src/index.js CHANGED
@@ -35,7 +35,7 @@ class Underpost {
35
35
  * @type {String}
36
36
  * @memberof Underpost
37
37
  */
38
- static version = 'v2.8.882';
38
+ static version = 'v2.8.884';
39
39
  /**
40
40
  * Repository cli API
41
41
  * @static
@@ -0,0 +1,262 @@
1
+ /**
2
+ * A service dedicated to creating and configuring an Express.js application
3
+ * instance based on server configuration data.
4
+ * @module src/runtime/express/Express.js
5
+ * @namespace ExpressService
6
+ */
7
+
8
+ import fs from 'fs-extra';
9
+ import express from 'express';
10
+ import fileUpload from 'express-fileupload';
11
+ import swaggerUi from 'swagger-ui-express';
12
+ import compression from 'compression';
13
+ import { createServer } from 'http';
14
+
15
+ import UnderpostStartUp from '../../server/start.js';
16
+ import { loggerFactory, loggerMiddleware } from '../../server/logger.js';
17
+ import { getCapVariableName, newInstance } from '../../client/components/core/CommonJs.js';
18
+ import { MailerProvider } from '../../mailer/MailerProvider.js';
19
+ import { DataBaseProvider } from '../../db/DataBaseProvider.js';
20
+ import { createPeerServer } from '../../server/peer.js';
21
+ import { createValkeyConnection } from '../../server/valkey.js';
22
+ import { applySecurity, authMiddlewareFactory } from '../../server/auth.js';
23
+ import { ssrMiddlewareFactory } from '../../server/ssr.js';
24
+
25
+ const logger = loggerFactory(import.meta);
26
+
27
+ /**
28
+ * @class ExpressService
29
+ * @description A service dedicated to creating and configuring an Express.js application
30
+ * instance based on server configuration data.
31
+ * @memberof ExpressService
32
+ */
33
+ class ExpressService {
34
+ /**
35
+ * Creates and configures a complete Express application instance for a specific host/path configuration.
36
+ *
37
+ * @method createApp
38
+ * @memberof ExpressService
39
+ * @param {string} config.host - The host name for the instance (e.g., 'www.example.com').
40
+ * @param {string} config.path - The URL path for the instance (e.g., '/', '/api/v1').
41
+ * @param {number} config.port - The primary listening port for the instance.
42
+ * @param {string} config.client - The client associated with the instance (used for SSR/Mailer configuration lookup).
43
+ * @param {string[]} [config.apis] - A list of API names to load and attach routers for.
44
+ * @param {string[]} config.origins - Allowed origins for CORS.
45
+ * @param {string} [config.directory] - The directory for static files (if overriding default).
46
+ * @param {string} [config.ws] - The WebSocket server name to use.
47
+ * @param {object} [config.mailer] - Mailer configuration.
48
+ * @param {object} [config.db] - Database configuration.
49
+ * @param {string} [config.redirect] - URL or flag to indicate an HTTP redirect should be configured.
50
+ * @param {boolean} [config.peer] - Whether to enable the peer server.
51
+ * @param {object} [config.valkey] - Valkey connection configuration.
52
+ * @param {string} [config.apiBaseHost] - Base host for the API (if running separate API).
53
+ * @param {number} [config.devApiPort] - The dynamically calculated development API port used for CORS in dev mode.
54
+ * @param {string} config.redirectTarget - The full target URL for redirection (used if `redirect` is true).
55
+ * @param {string} config.rootHostPath - The root path for public host assets (e.g., `/public/hostname`).
56
+ * @param {object} config.confSSR - The SSR configuration object, used to look up Mailer templates.
57
+ * @param {import('prom-client').Counter<string>} config.promRequestCounter - Prometheus request counter instance.
58
+ * @param {import('prom-client').Registry} config.promRegister - Prometheus register instance for metrics.
59
+ * @returns {Promise<{portsUsed: number}>} An object indicating how many additional ports were used (e.g., for PeerServer).
60
+ */
61
+ async createApp({
62
+ host,
63
+ path,
64
+ port,
65
+ client,
66
+ apis,
67
+ origins,
68
+ directory,
69
+ ws,
70
+ mailer,
71
+ db,
72
+ redirect,
73
+ peer,
74
+ valkey,
75
+ apiBaseHost,
76
+ devApiPort, // New parameter for dev environment CORS
77
+ redirectTarget,
78
+ rootHostPath,
79
+ confSSR,
80
+ promRequestCounter,
81
+ promRegister,
82
+ }) {
83
+ let portsUsed = 0;
84
+ const runningData = {
85
+ host,
86
+ path,
87
+ runtime: 'nodejs',
88
+ client,
89
+ meta: import.meta,
90
+ apis,
91
+ };
92
+
93
+ const app = express();
94
+
95
+ if (process.env.NODE_ENV === 'production') app.set('trust proxy', true);
96
+
97
+ app.use((req, res, next) => {
98
+ res.on('finish', () => {
99
+ promRequestCounter.inc({
100
+ instance: `${host}:${port}${path}`,
101
+ method: req.method,
102
+ status_code: res.statusCode,
103
+ });
104
+ });
105
+ return next();
106
+ });
107
+
108
+ // Metrics endpoint
109
+ app.get(`${path === '/' ? '' : path}/metrics`, async (req, res) => {
110
+ res.set('Content-Type', promRegister.contentType);
111
+ return res.end(await promRegister.metrics());
112
+ });
113
+
114
+ // Logging, Compression, and Body Parsers
115
+ app.use(loggerMiddleware(import.meta));
116
+ // Compression filter logic is correctly inlined here
117
+ app.use(compression({ filter: (req, res) => !req.headers['x-no-compression'] && compression.filter(req, res) }));
118
+ app.use(express.json({ limit: '100MB' }));
119
+ app.use(express.urlencoded({ extended: true, limit: '20MB' }));
120
+ app.use(fileUpload());
121
+
122
+ if (process.env.NODE_ENV === 'development') app.set('json spaces', 2);
123
+
124
+ // Language handling middleware
125
+ app.use((req, res, next) => {
126
+ const lang = req.headers['accept-language'] || 'en';
127
+ req.lang = typeof lang === 'string' && lang.toLowerCase().match('es') ? 'es' : 'en';
128
+ return next();
129
+ });
130
+
131
+ // Static file serving
132
+ app.use('/', express.static(directory ? directory : `.${rootHostPath}`));
133
+
134
+ // Swagger path definition
135
+ const swaggerJsonPath = `./public/${host}${path === '/' ? path : `${path}/`}swagger-output.json`;
136
+ const swaggerPath = `${path === '/' ? `/api-docs` : `${path}/api-docs`}`;
137
+
138
+ // Flag swagger requests before security middleware
139
+ if (fs.existsSync(swaggerJsonPath)) {
140
+ app.use(swaggerPath, (req, res, next) => {
141
+ res.locals.isSwagger = true;
142
+ next();
143
+ });
144
+ }
145
+
146
+ // Security and CORS
147
+ applySecurity(app, {
148
+ origin: (origin, callback) => {
149
+ // Use devApiPort if provided to calculate the allowed development CORS origin
150
+ const devOrigin =
151
+ apis && process.env.NODE_ENV === 'development' && devApiPort ? [`http://localhost:${devApiPort}`] : [];
152
+
153
+ const allowedOrigins = origins.concat(devOrigin);
154
+
155
+ if (!origin || allowedOrigins.includes(origin)) {
156
+ callback(null, true);
157
+ } else {
158
+ callback(new Error('Not allowed by CORS'));
159
+ }
160
+ },
161
+ });
162
+
163
+ // Handle redirection-only instances
164
+ if (redirect) {
165
+ app.use((req, res, next) => {
166
+ if (process.env.NODE_ENV === 'production' && !req.url.startsWith(`/.well-known/acme-challenge`)) {
167
+ return res.status(302).redirect(redirectTarget + req.url);
168
+ }
169
+ return next();
170
+ });
171
+ await UnderpostStartUp.API.listenPortController(app, port, runningData);
172
+ return { portsUsed };
173
+ }
174
+
175
+ // Create HTTP server for regular instances (required for WebSockets)
176
+ const server = createServer({}, app);
177
+ if (peer) portsUsed++; // Peer server uses one additional port
178
+
179
+ if (!apiBaseHost) {
180
+ // Swagger UI setup
181
+ if (fs.existsSync(swaggerJsonPath)) {
182
+ const swaggerDoc = JSON.parse(fs.readFileSync(swaggerJsonPath, 'utf8'));
183
+ // Reusing swaggerPath defined outside, removing unnecessary redeclaration
184
+ app.use(swaggerPath, swaggerUi.serve, swaggerUi.setup(swaggerDoc));
185
+ }
186
+
187
+ // Database and Valkey connections
188
+ if (db && apis) await DataBaseProvider.load({ apis, host, path, db });
189
+ if (valkey) await createValkeyConnection({ host, path }, valkey);
190
+
191
+ // Mailer setup
192
+ if (mailer) {
193
+ const mailerSsrConf = confSSR[getCapVariableName(client)];
194
+ await MailerProvider.load({
195
+ id: `${host}${path}`,
196
+ meta: `mailer-${host}${path}`,
197
+ host,
198
+ path,
199
+ ...mailer,
200
+ templates: mailerSsrConf ? mailerSsrConf.mailer : {},
201
+ });
202
+ }
203
+
204
+ // API router loading
205
+ if (apis && apis.length > 0) {
206
+ const authMiddleware = authMiddlewareFactory({ host, path });
207
+ const apiPath = `${path === '/' ? '' : path}/${process.env.BASE_API}`;
208
+ for (const api of apis) {
209
+ logger.info(`Build api server`, `${host}${apiPath}/${api}`);
210
+ const { ApiRouter } = await import(`../../api/${api}/${api}.router.js`);
211
+ const router = ApiRouter({ host, path, apiPath, mailer, db, authMiddleware, origins });
212
+ app.use(`${apiPath}/${api}`, router);
213
+ }
214
+ }
215
+
216
+ // WebSocket server setup
217
+ if (ws) {
218
+ const { createIoServer } = await import(`../../ws/${ws}/${ws}.ws.server.js`);
219
+ const { options, meta } = await createIoServer(server, { host, path, db, port, origins });
220
+
221
+ // Listen on the main port for the WS server
222
+ await UnderpostStartUp.API.listenPortController(UnderpostStartUp.API.listenServerFactory(), port, {
223
+ runtime: 'nodejs',
224
+ client: null,
225
+ host,
226
+ path: options.path,
227
+ meta,
228
+ });
229
+ }
230
+
231
+ // Peer server setup
232
+ if (peer) {
233
+ const peerPort = newInstance(port + portsUsed); // portsUsed is 1 here
234
+ const { options, meta, peerServer } = await createPeerServer({
235
+ port: peerPort,
236
+ devPort: port,
237
+ origins,
238
+ host,
239
+ path,
240
+ });
241
+ await UnderpostStartUp.API.listenPortController(peerServer, peerPort, {
242
+ runtime: 'nodejs',
243
+ client: null,
244
+ host,
245
+ path: options.path,
246
+ meta,
247
+ });
248
+ }
249
+ }
250
+
251
+ // SSR middleware loading
252
+ const ssr = await ssrMiddlewareFactory({ app, directory, rootHostPath, path });
253
+ for (const [_, ssrMiddleware] of Object.entries(ssr)) app.use(ssrMiddleware);
254
+
255
+ // Start listening on the main port
256
+ await UnderpostStartUp.API.listenPortController(server, port, runningData);
257
+
258
+ return { portsUsed };
259
+ }
260
+ }
261
+
262
+ export default new ExpressService();