jerkjs 2.5.6 → 2.6.1
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/CHANGELOG.md +167 -79
- package/README.md +134 -146
- package/RESULTADOS_WAF.md +63 -0
- package/doc-2.5/ADMIN_EXTENSION_COMMANDS_MANUAL.md +261 -0
- package/doc-2.5/ADMIN_EXTENSION_HOOK_EXAMPLE.md +28 -0
- package/doc-2.5/ADMIN_EXTENSION_INTEGRATION_MANUAL.md +232 -0
- package/doc-2.5/CACHE_SYSTEM_MAP.md +206 -0
- package/doc-2.5/MANUAL_MODULOS_ADMIN.md +287 -0
- package/doc-2.5/QUEUE_CLI_MODULE_MANUAL.md +289 -0
- package/doc-2.5/QUEUE_SYSTEM_MANUAL.md +320 -0
- package/doc-2.5/ROUTE_CACHE_MODULE_MANUAL.md +205 -0
- package/doc-2.5/WAF_MODULE_MANUAL.md +229 -0
- package/index.js +19 -4
- package/jerk-admin-client/README.md +69 -0
- package/jerk-admin-client/package.json +23 -0
- package/jerk-admin-client.js +257 -0
- package/lib/admin/AdminExtension.js +491 -0
- package/lib/admin/ModuleLoader.js +77 -0
- package/lib/admin/config.js +21 -0
- package/lib/admin/modules/CacheModule.js +145 -0
- package/lib/admin/modules/ControllerGeneratorModule.js +414 -0
- package/lib/admin/modules/QueueManagementModule.js +265 -0
- package/lib/admin/modules/RouteCacheModule.js +227 -0
- package/lib/admin/modules/RouteManagerModule.js +468 -0
- package/lib/admin/modules/STATS_MODULE_README.md +113 -0
- package/lib/admin/modules/StatsModule.js +140 -0
- package/lib/admin/modules/SystemModule.js +140 -0
- package/lib/admin/modules/TimeModule.js +95 -0
- package/lib/admin/modules/ViewCacheStatsModule.js +92 -0
- package/lib/admin/modules/WAFModule.js +737 -0
- package/lib/cache/CacheHooks.js +141 -0
- package/lib/core/server.js +223 -77
- package/lib/middleware/firewall.js +112 -17
- package/lib/mvc/viewEngine.js +89 -5
- package/lib/queue/GlobalQueueStorage.js +38 -0
- package/lib/queue/QueueSystem.js +451 -0
- package/lib/queue/admin_example.js +114 -0
- package/lib/queue/example.js +268 -0
- package/lib/queue/integration.js +109 -0
- package/lib/router/RouteMatcher.js +242 -54
- package/lib/utils/globalStats.js +16 -0
- package/lib/utils/globalViewCacheInfo.js +16 -0
- package/lib/utils/globalWAFStats.js +54 -0
- package/package.json +2 -2
- package/test-colors.js +46 -0
- package/test-help-alias.js +31 -0
package/lib/core/server.js
CHANGED
|
@@ -13,9 +13,12 @@ const { Logger } = require('../utils/logger');
|
|
|
13
13
|
const { ErrorHandler } = require('../utils/errorHandler');
|
|
14
14
|
const { getMimeType } = require('../utils/mimeType');
|
|
15
15
|
const RouteMatcher = require('../router/RouteMatcher');
|
|
16
|
+
const AdminExtension = require('../admin/AdminExtension');
|
|
17
|
+
const { globalStats } = require('../utils/globalStats');
|
|
16
18
|
|
|
17
19
|
// Carga anticipada de módulos comunes para evitar cargas repetidas
|
|
18
20
|
let hooksInstance = null;
|
|
21
|
+
let errorHandlerInstance = null;
|
|
19
22
|
const getHooks = () => {
|
|
20
23
|
if (!hooksInstance) {
|
|
21
24
|
hooksInstance = require('../../index.js').hooks;
|
|
@@ -23,6 +26,13 @@ const getHooks = () => {
|
|
|
23
26
|
return hooksInstance;
|
|
24
27
|
};
|
|
25
28
|
|
|
29
|
+
const getErrorHandler = () => {
|
|
30
|
+
if (!errorHandlerInstance) {
|
|
31
|
+
errorHandlerInstance = require('../utils/errorHandler');
|
|
32
|
+
}
|
|
33
|
+
return errorHandlerInstance;
|
|
34
|
+
};
|
|
35
|
+
|
|
26
36
|
class APIServer {
|
|
27
37
|
/**
|
|
28
38
|
* Constructor del servidor
|
|
@@ -59,6 +69,42 @@ class APIServer {
|
|
|
59
69
|
|
|
60
70
|
// Inicializar el componente de enrutamiento
|
|
61
71
|
this.routeMatcher = new RouteMatcher();
|
|
72
|
+
|
|
73
|
+
// Inicializar la extensión de administración
|
|
74
|
+
this.adminExtension = null;
|
|
75
|
+
|
|
76
|
+
// Inicializar el sistema de hooks si no existe
|
|
77
|
+
if (!options.hooks) {
|
|
78
|
+
const { hooks } = require('../../index.js'); // Ruta corregida
|
|
79
|
+
this.hooks = hooks;
|
|
80
|
+
} else {
|
|
81
|
+
this.hooks = options.hooks;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Conectar el sistema de hooks al routeMatcher
|
|
85
|
+
this.routeMatcher.hooks = this.hooks;
|
|
86
|
+
|
|
87
|
+
// Propiedad para almacenar el viewEngine
|
|
88
|
+
this._viewEngine = null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Setter para el viewEngine que también conecta los hooks
|
|
93
|
+
*/
|
|
94
|
+
set viewEngine(engine) {
|
|
95
|
+
this._viewEngine = engine;
|
|
96
|
+
|
|
97
|
+
// Conectar el sistema de hooks al viewEngine si está disponible
|
|
98
|
+
if (this._viewEngine && this.hooks) {
|
|
99
|
+
this._viewEngine.hooks = this.hooks;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Getter para el viewEngine
|
|
105
|
+
*/
|
|
106
|
+
get viewEngine() {
|
|
107
|
+
return this._viewEngine;
|
|
62
108
|
}
|
|
63
109
|
|
|
64
110
|
/**
|
|
@@ -76,7 +122,8 @@ class APIServer {
|
|
|
76
122
|
return async (req, res) => {
|
|
77
123
|
try {
|
|
78
124
|
// Disparar hook antes de procesar archivo estático
|
|
79
|
-
|
|
125
|
+
// Usar la instancia de hooks del servidor para evitar llamadas repetidas a getHooks()
|
|
126
|
+
const hooks = this.hooks;
|
|
80
127
|
if (hooks) {
|
|
81
128
|
const hookResult = hooks.applyFilters('pre_static_file_serve', {
|
|
82
129
|
req,
|
|
@@ -112,8 +159,8 @@ class APIServer {
|
|
|
112
159
|
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
113
160
|
res.end(JSON.stringify({ error: 'Acceso denegado' }));
|
|
114
161
|
|
|
115
|
-
if (hooks) {
|
|
116
|
-
hooks.doAction('static_file_access_denied', req, res, normalizedPath);
|
|
162
|
+
if (this.hooks) {
|
|
163
|
+
this.hooks.doAction('static_file_access_denied', req, res, normalizedPath);
|
|
117
164
|
}
|
|
118
165
|
return;
|
|
119
166
|
}
|
|
@@ -139,8 +186,8 @@ class APIServer {
|
|
|
139
186
|
try {
|
|
140
187
|
await fs.promises.access(indexPath);
|
|
141
188
|
// Disparar hook antes de servir archivo índice
|
|
142
|
-
if (hooks) {
|
|
143
|
-
hooks.doAction('serving_index_file', indexPath, req, res);
|
|
189
|
+
if (this.hooks) {
|
|
190
|
+
this.hooks.doAction('serving_index_file', indexPath, req, res);
|
|
144
191
|
}
|
|
145
192
|
|
|
146
193
|
const fileContent = await fs.promises.readFile(indexPath);
|
|
@@ -156,8 +203,8 @@ class APIServer {
|
|
|
156
203
|
res.end(fileContent);
|
|
157
204
|
|
|
158
205
|
// Disparar hook después de servir archivo
|
|
159
|
-
if (hooks) {
|
|
160
|
-
hooks.doAction('static_file_served', indexPath, req, res);
|
|
206
|
+
if (this.hooks) {
|
|
207
|
+
this.hooks.doAction('static_file_served', indexPath, req, res);
|
|
161
208
|
}
|
|
162
209
|
return;
|
|
163
210
|
} catch (e) {
|
|
@@ -169,8 +216,8 @@ class APIServer {
|
|
|
169
216
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
170
217
|
res.end(JSON.stringify({ error: 'Directorio no encontrado', path: dirPath }));
|
|
171
218
|
|
|
172
|
-
if (hooks) {
|
|
173
|
-
hooks.doAction('static_directory_no_index', dirPath, req, res);
|
|
219
|
+
if (this.hooks) {
|
|
220
|
+
this.hooks.doAction('static_directory_no_index', dirPath, req, res);
|
|
174
221
|
}
|
|
175
222
|
return;
|
|
176
223
|
} else {
|
|
@@ -178,8 +225,8 @@ class APIServer {
|
|
|
178
225
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
179
226
|
res.end(JSON.stringify({ error: 'Archivo no encontrado', path: physicalPath }));
|
|
180
227
|
|
|
181
|
-
if (hooks) {
|
|
182
|
-
hooks.doAction('static_file_not_found', physicalPath, req, res);
|
|
228
|
+
if (this.hooks) {
|
|
229
|
+
this.hooks.doAction('static_file_not_found', physicalPath, req, res);
|
|
183
230
|
}
|
|
184
231
|
return;
|
|
185
232
|
}
|
|
@@ -188,8 +235,8 @@ class APIServer {
|
|
|
188
235
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
189
236
|
res.end(JSON.stringify({ error: 'Archivo no encontrado', path: physicalPath }));
|
|
190
237
|
|
|
191
|
-
if (hooks) {
|
|
192
|
-
hooks.doAction('static_file_not_found', physicalPath, req, res);
|
|
238
|
+
if (this.hooks) {
|
|
239
|
+
this.hooks.doAction('static_file_not_found', physicalPath, req, res);
|
|
193
240
|
}
|
|
194
241
|
return;
|
|
195
242
|
}
|
|
@@ -204,8 +251,8 @@ class APIServer {
|
|
|
204
251
|
try {
|
|
205
252
|
await fs.promises.access(indexPath);
|
|
206
253
|
// Disparar hook antes de servir archivo índice
|
|
207
|
-
if (hooks) {
|
|
208
|
-
hooks.doAction('serving_index_file', indexPath, req, res);
|
|
254
|
+
if (this.hooks) {
|
|
255
|
+
this.hooks.doAction('serving_index_file', indexPath, req, res);
|
|
209
256
|
}
|
|
210
257
|
|
|
211
258
|
const fileContent = await fs.promises.readFile(indexPath);
|
|
@@ -221,8 +268,8 @@ class APIServer {
|
|
|
221
268
|
res.end(fileContent);
|
|
222
269
|
|
|
223
270
|
// Disparar hook después de servir archivo
|
|
224
|
-
if (hooks) {
|
|
225
|
-
hooks.doAction('static_file_served', indexPath, req, res);
|
|
271
|
+
if (this.hooks) {
|
|
272
|
+
this.hooks.doAction('static_file_served', indexPath, req, res);
|
|
226
273
|
}
|
|
227
274
|
return;
|
|
228
275
|
} catch (e) {
|
|
@@ -234,15 +281,15 @@ class APIServer {
|
|
|
234
281
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
235
282
|
res.end(JSON.stringify({ error: 'Directorio no encontrado' }));
|
|
236
283
|
|
|
237
|
-
if (hooks) {
|
|
238
|
-
hooks.doAction('static_directory_no_index', dirPath, req, res);
|
|
284
|
+
if (this.hooks) {
|
|
285
|
+
this.hooks.doAction('static_directory_no_index', dirPath, req, res);
|
|
239
286
|
}
|
|
240
287
|
return;
|
|
241
288
|
}
|
|
242
289
|
|
|
243
290
|
// Disparar hook antes de leer archivo
|
|
244
|
-
if (hooks) {
|
|
245
|
-
hooks.doAction('before_reading_static_file', physicalPath, req, res);
|
|
291
|
+
if (this.hooks) {
|
|
292
|
+
this.hooks.doAction('before_reading_static_file', physicalPath, req, res);
|
|
246
293
|
}
|
|
247
294
|
|
|
248
295
|
// Leer y servir el archivo
|
|
@@ -262,34 +309,31 @@ class APIServer {
|
|
|
262
309
|
res.end(fileContent);
|
|
263
310
|
|
|
264
311
|
// Disparar hook después de servir archivo
|
|
265
|
-
if (hooks) {
|
|
266
|
-
hooks.doAction('static_file_served', physicalPath, req, res);
|
|
312
|
+
if (this.hooks) {
|
|
313
|
+
this.hooks.doAction('static_file_served', physicalPath, req, res);
|
|
267
314
|
}
|
|
268
315
|
} catch (error) {
|
|
269
316
|
if (error.code === 'ENOENT') {
|
|
270
317
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
271
318
|
res.end(JSON.stringify({ error: 'Archivo no encontrado', path: error.path }));
|
|
272
319
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
hooks.doAction('static_file_not_found', error.path, req, res);
|
|
320
|
+
if (this.hooks) {
|
|
321
|
+
this.hooks.doAction('static_file_not_found', error.path, req, res);
|
|
276
322
|
}
|
|
277
323
|
} else if (error.code === 'EACCES') {
|
|
278
324
|
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
279
325
|
res.end(JSON.stringify({ error: 'Acceso denegado', path: error.path }));
|
|
280
326
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
hooks.doAction('static_file_access_denied', error.path, req, res);
|
|
327
|
+
if (this.hooks) {
|
|
328
|
+
this.hooks.doAction('static_file_access_denied', error.path, req, res);
|
|
284
329
|
}
|
|
285
330
|
} else {
|
|
286
331
|
// Usar el ErrorHandler para mostrar el stacktrace en color amarillo
|
|
287
|
-
const { ErrorHandler } =
|
|
332
|
+
const { ErrorHandler } = getErrorHandler();
|
|
288
333
|
ErrorHandler.handle(error, req, res, this.logger);
|
|
289
334
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
hooks.doAction('static_file_error', error, req, res);
|
|
335
|
+
if (this.hooks) {
|
|
336
|
+
this.hooks.doAction('static_file_error', error, req, res);
|
|
293
337
|
}
|
|
294
338
|
}
|
|
295
339
|
}
|
|
@@ -353,12 +397,18 @@ class APIServer {
|
|
|
353
397
|
// Crear handler para archivos estáticos
|
|
354
398
|
const staticHandler = this.createStaticFileHandler(routeConfig.static, routeConfig.path);
|
|
355
399
|
|
|
356
|
-
|
|
400
|
+
const route = {
|
|
357
401
|
method: routeConfig.method.toUpperCase(),
|
|
358
402
|
path: routeConfig.path,
|
|
359
403
|
handler: staticHandler,
|
|
360
404
|
isStatic: true // Marcar como ruta estática para posible procesamiento especial
|
|
361
|
-
}
|
|
405
|
+
};
|
|
406
|
+
this.routes.push(route);
|
|
407
|
+
|
|
408
|
+
// Disparar hook cuando se registra una ruta
|
|
409
|
+
if (this.hooks) {
|
|
410
|
+
this.hooks.doAction('route_registered', route);
|
|
411
|
+
}
|
|
362
412
|
|
|
363
413
|
return; // Salir después de procesar ruta estática
|
|
364
414
|
}
|
|
@@ -369,7 +419,6 @@ class APIServer {
|
|
|
369
419
|
|
|
370
420
|
// Obtener el handler final según las configuraciones
|
|
371
421
|
let finalHandler = routeConfig.handler;
|
|
372
|
-
const path = require('path');
|
|
373
422
|
|
|
374
423
|
// Si se especifica un controlador, cargarlo y obtener el handler
|
|
375
424
|
if (routeConfig.controller && routeConfig.handlerName) {
|
|
@@ -401,7 +450,6 @@ class APIServer {
|
|
|
401
450
|
if (routeConfig.auth === 'session') {
|
|
402
451
|
// Verificar si el servidor tiene sessionManager
|
|
403
452
|
if (this.sessionManager) {
|
|
404
|
-
const { sessionAuth } = require('../middleware/session');
|
|
405
453
|
const authMiddleware = sessionAuth(this.sessionManager, routeConfig.authOptions || {});
|
|
406
454
|
|
|
407
455
|
// Crear un nuevo handler que ejecute la autenticación primero
|
|
@@ -429,7 +477,7 @@ class APIServer {
|
|
|
429
477
|
}
|
|
430
478
|
} catch (error) {
|
|
431
479
|
// Usar el ErrorHandler para mostrar el stacktrace en color amarillo
|
|
432
|
-
const { ErrorHandler } =
|
|
480
|
+
const { ErrorHandler } = getErrorHandler();
|
|
433
481
|
if (!res.headersSent) {
|
|
434
482
|
ErrorHandler.handle(error, req, res, this.logger);
|
|
435
483
|
}
|
|
@@ -437,11 +485,17 @@ class APIServer {
|
|
|
437
485
|
};
|
|
438
486
|
|
|
439
487
|
// Agregar la ruta con el handler autenticado
|
|
440
|
-
|
|
488
|
+
const route = {
|
|
441
489
|
method: routeConfig.method.toUpperCase(),
|
|
442
490
|
path: routeConfig.path,
|
|
443
491
|
handler: authenticatedHandler
|
|
444
|
-
}
|
|
492
|
+
};
|
|
493
|
+
this.routes.push(route);
|
|
494
|
+
|
|
495
|
+
// Disparar hook cuando se registra una ruta
|
|
496
|
+
if (this.hooks) {
|
|
497
|
+
this.hooks.doAction('route_registered', route);
|
|
498
|
+
}
|
|
445
499
|
} else {
|
|
446
500
|
// Si no hay sessionManager en el servidor, agregar la ruta normalmente
|
|
447
501
|
this.routes.push({
|
|
@@ -480,7 +534,7 @@ class APIServer {
|
|
|
480
534
|
}
|
|
481
535
|
} catch (error) {
|
|
482
536
|
// Usar el ErrorHandler para mostrar el stacktrace en color amarillo
|
|
483
|
-
const { ErrorHandler } =
|
|
537
|
+
const { ErrorHandler } = getErrorHandler();
|
|
484
538
|
if (!res.headersSent) {
|
|
485
539
|
ErrorHandler.handle(error, req, res, this.logger);
|
|
486
540
|
}
|
|
@@ -488,27 +542,45 @@ class APIServer {
|
|
|
488
542
|
};
|
|
489
543
|
|
|
490
544
|
// Agregar la ruta con el handler autenticado
|
|
491
|
-
|
|
545
|
+
const route = {
|
|
492
546
|
method: routeConfig.method.toUpperCase(),
|
|
493
547
|
path: routeConfig.path,
|
|
494
548
|
handler: authenticatedHandler
|
|
495
|
-
}
|
|
549
|
+
};
|
|
550
|
+
this.routes.push(route);
|
|
551
|
+
|
|
552
|
+
// Disparar hook cuando se registra una ruta
|
|
553
|
+
if (this.hooks) {
|
|
554
|
+
this.hooks.doAction('route_registered', route);
|
|
555
|
+
}
|
|
496
556
|
} else {
|
|
497
557
|
// Si no hay authenticator en el servidor, agregar la ruta normalmente
|
|
498
|
-
|
|
558
|
+
const route = {
|
|
499
559
|
method: routeConfig.method.toUpperCase(),
|
|
500
560
|
path: routeConfig.path,
|
|
501
561
|
handler: finalHandler
|
|
502
|
-
}
|
|
562
|
+
};
|
|
563
|
+
this.routes.push(route);
|
|
564
|
+
|
|
565
|
+
// Disparar hook cuando se registra una ruta
|
|
566
|
+
if (this.hooks) {
|
|
567
|
+
this.hooks.doAction('route_registered', route);
|
|
568
|
+
}
|
|
503
569
|
}
|
|
504
570
|
}
|
|
505
571
|
} else {
|
|
506
572
|
// Si no hay autenticación requerida, agregar la ruta normalmente
|
|
507
|
-
|
|
573
|
+
const route = {
|
|
508
574
|
method: routeConfig.method.toUpperCase(),
|
|
509
575
|
path: routeConfig.path,
|
|
510
576
|
handler: finalHandler
|
|
511
|
-
}
|
|
577
|
+
};
|
|
578
|
+
this.routes.push(route);
|
|
579
|
+
|
|
580
|
+
// Disparar hook cuando se registra una ruta
|
|
581
|
+
if (this.hooks) {
|
|
582
|
+
this.hooks.doAction('route_registered', route);
|
|
583
|
+
}
|
|
512
584
|
}
|
|
513
585
|
}
|
|
514
586
|
|
|
@@ -535,9 +607,8 @@ class APIServer {
|
|
|
535
607
|
*/
|
|
536
608
|
start() {
|
|
537
609
|
// Disparar hook antes de iniciar el servidor
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
hooks.doAction('pre_server_start', this);
|
|
610
|
+
if (this.hooks) {
|
|
611
|
+
this.hooks.doAction('pre_server_start', this);
|
|
541
612
|
}
|
|
542
613
|
|
|
543
614
|
if (this.https && Object.keys(this.httpsOptions).length > 0) {
|
|
@@ -577,14 +648,30 @@ class APIServer {
|
|
|
577
648
|
this.logger.logMemoryUsage();
|
|
578
649
|
|
|
579
650
|
// Disparar hook después de iniciar el servidor
|
|
580
|
-
if (hooks) {
|
|
581
|
-
hooks.doAction('post_server_start', this);
|
|
651
|
+
if (this.hooks) {
|
|
652
|
+
this.hooks.doAction('post_server_start', this);
|
|
653
|
+
|
|
654
|
+
// Disparar hook para inicializar extensiones después de que el servidor esté completamente iniciado
|
|
655
|
+
this.hooks.doAction('admin_extensions_initialize', this);
|
|
582
656
|
}
|
|
583
657
|
});
|
|
584
658
|
|
|
585
659
|
return this.server;
|
|
586
660
|
}
|
|
587
661
|
|
|
662
|
+
/**
|
|
663
|
+
* Inicializa la extensión de administración
|
|
664
|
+
* @param {Object} options - Opciones para la extensión de administración
|
|
665
|
+
*/
|
|
666
|
+
initializeAdminExtension(options = {}) {
|
|
667
|
+
if (!this.adminExtension) {
|
|
668
|
+
this.adminExtension = new AdminExtension(options);
|
|
669
|
+
this.adminExtension.initialize(this);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return this.adminExtension;
|
|
673
|
+
}
|
|
674
|
+
|
|
588
675
|
/**
|
|
589
676
|
* Detiene el servidor
|
|
590
677
|
*/
|
|
@@ -594,6 +681,11 @@ class APIServer {
|
|
|
594
681
|
this.logger.info('Servidor detenido');
|
|
595
682
|
});
|
|
596
683
|
}
|
|
684
|
+
|
|
685
|
+
// Cerrar la extensión de administración si está activa
|
|
686
|
+
if (this.adminExtension) {
|
|
687
|
+
this.adminExtension.close();
|
|
688
|
+
}
|
|
597
689
|
}
|
|
598
690
|
|
|
599
691
|
/**
|
|
@@ -606,9 +698,8 @@ class APIServer {
|
|
|
606
698
|
const { pathname, query } = parsedUrl;
|
|
607
699
|
|
|
608
700
|
// Disparar hook antes de procesar la solicitud
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
hooks.doAction('request_received', req, res);
|
|
701
|
+
if (this.hooks) {
|
|
702
|
+
this.hooks.doAction('request_received', req, res);
|
|
612
703
|
}
|
|
613
704
|
|
|
614
705
|
// Agregar propiedades útiles a la solicitud
|
|
@@ -643,6 +734,9 @@ class APIServer {
|
|
|
643
734
|
// Concatenar todos los chunks una sola vez
|
|
644
735
|
req.body = Buffer.concat(bodyChunks).toString();
|
|
645
736
|
|
|
737
|
+
// Actualizar estadísticas globales de bytes recibidos
|
|
738
|
+
globalStats.requestBytes += bodySize;
|
|
739
|
+
|
|
646
740
|
// Parsear body si es JSON
|
|
647
741
|
if (req.headers['content-type'] && req.headers['content-type'].includes('application/json')) {
|
|
648
742
|
try {
|
|
@@ -666,16 +760,16 @@ class APIServer {
|
|
|
666
760
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
667
761
|
res.end(renderedHtml);
|
|
668
762
|
|
|
669
|
-
if (hooks) {
|
|
670
|
-
hooks.doAction('view_rendered', viewName, data, req, res);
|
|
763
|
+
if (this.hooks) {
|
|
764
|
+
this.hooks.doAction('view_rendered', viewName, data, req, res);
|
|
671
765
|
}
|
|
672
766
|
} catch (error) {
|
|
673
767
|
// Usar el ErrorHandler para mostrar el stacktrace en color amarillo
|
|
674
768
|
const { ErrorHandler } = require('../utils/errorHandler');
|
|
675
769
|
ErrorHandler.handle(error, req, res, this.logger);
|
|
676
770
|
|
|
677
|
-
if (hooks) {
|
|
678
|
-
hooks.doAction('view_render_error', viewName, data, req, res, error);
|
|
771
|
+
if (this.hooks) {
|
|
772
|
+
this.hooks.doAction('view_render_error', viewName, data, req, res, error);
|
|
679
773
|
}
|
|
680
774
|
}
|
|
681
775
|
};
|
|
@@ -718,8 +812,8 @@ class APIServer {
|
|
|
718
812
|
}
|
|
719
813
|
|
|
720
814
|
if (res.finished) {
|
|
721
|
-
if (hooks) {
|
|
722
|
-
hooks.doAction('middleware_response_finished', req, res);
|
|
815
|
+
if (this.hooks) {
|
|
816
|
+
this.hooks.doAction('middleware_response_finished', req, res);
|
|
723
817
|
}
|
|
724
818
|
return; // Si el middleware respondió, salir
|
|
725
819
|
}
|
|
@@ -733,8 +827,8 @@ class APIServer {
|
|
|
733
827
|
// Agregar parámetros a la solicitud
|
|
734
828
|
req.params = matchedRoute.params;
|
|
735
829
|
|
|
736
|
-
if (hooks) {
|
|
737
|
-
hooks.doAction('route_matched', matchedRoute, req, res);
|
|
830
|
+
if (this.hooks) {
|
|
831
|
+
this.hooks.doAction('route_matched', matchedRoute, req, res);
|
|
738
832
|
}
|
|
739
833
|
|
|
740
834
|
// Ejecutar handler de la ruta
|
|
@@ -748,8 +842,8 @@ class APIServer {
|
|
|
748
842
|
res.writeHead(204);
|
|
749
843
|
res.end();
|
|
750
844
|
|
|
751
|
-
if (hooks) {
|
|
752
|
-
hooks.doAction('options_response_sent', req, res);
|
|
845
|
+
if (this.hooks) {
|
|
846
|
+
this.hooks.doAction('options_response_sent', req, res);
|
|
753
847
|
}
|
|
754
848
|
}
|
|
755
849
|
}
|
|
@@ -761,8 +855,8 @@ class APIServer {
|
|
|
761
855
|
// Agregar parámetros a la solicitud
|
|
762
856
|
req.params = matchedRoute.params;
|
|
763
857
|
|
|
764
|
-
if (hooks) {
|
|
765
|
-
hooks.doAction('route_matched', matchedRoute, req, res);
|
|
858
|
+
if (this.hooks) {
|
|
859
|
+
this.hooks.doAction('route_matched', matchedRoute, req, res);
|
|
766
860
|
}
|
|
767
861
|
|
|
768
862
|
// Ejecutar middlewares (excepto para rutas estáticas)
|
|
@@ -793,8 +887,8 @@ class APIServer {
|
|
|
793
887
|
}
|
|
794
888
|
|
|
795
889
|
if (res.finished) {
|
|
796
|
-
if (hooks) {
|
|
797
|
-
hooks.doAction('middleware_response_finished', req, res);
|
|
890
|
+
if (this.hooks) {
|
|
891
|
+
this.hooks.doAction('middleware_response_finished', req, res);
|
|
798
892
|
}
|
|
799
893
|
return; // Si el middleware respondió, salir
|
|
800
894
|
}
|
|
@@ -802,30 +896,82 @@ class APIServer {
|
|
|
802
896
|
}
|
|
803
897
|
}
|
|
804
898
|
|
|
899
|
+
// Capturar el tamaño de la respuesta antes de enviarla
|
|
900
|
+
const originalWrite = res.write;
|
|
901
|
+
const originalEnd = res.end;
|
|
902
|
+
let responseData = [];
|
|
903
|
+
|
|
904
|
+
res.write = function(chunk) {
|
|
905
|
+
if (chunk) {
|
|
906
|
+
responseData.push(Buffer.from(chunk));
|
|
907
|
+
}
|
|
908
|
+
return originalWrite.apply(this, arguments);
|
|
909
|
+
};
|
|
910
|
+
|
|
911
|
+
res.end = function(chunk) {
|
|
912
|
+
if (chunk) {
|
|
913
|
+
responseData.push(Buffer.from(chunk));
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// Almacenar los datos de la respuesta para estadísticas
|
|
917
|
+
res._data = Buffer.concat(responseData);
|
|
918
|
+
|
|
919
|
+
// Actualizar estadísticas globales de bytes enviados
|
|
920
|
+
if (res._data) {
|
|
921
|
+
const responseSize = Buffer.byteLength(res._data, 'utf8');
|
|
922
|
+
globalStats.responseBytes += responseSize;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
return originalEnd.apply(this, arguments);
|
|
926
|
+
};
|
|
927
|
+
|
|
928
|
+
// Actualizar estadísticas globales de solicitudes procesadas
|
|
929
|
+
globalStats.requestsProcessed++;
|
|
930
|
+
|
|
805
931
|
// Ejecutar handler de la ruta
|
|
806
932
|
await matchedRoute.route.handler(req, res);
|
|
807
933
|
|
|
808
|
-
|
|
809
|
-
|
|
934
|
+
// Actualizar estadísticas globales de respuestas enviadas
|
|
935
|
+
globalStats.responsesSent++;
|
|
936
|
+
|
|
937
|
+
// Registrar acceso a ruta
|
|
938
|
+
if (req.originalUrl) {
|
|
939
|
+
const routeKey = `${req.method} ${req.originalUrl}`;
|
|
940
|
+
if (!globalStats.routeAccesses.has(routeKey)) {
|
|
941
|
+
globalStats.routeAccesses.set(routeKey, 0);
|
|
942
|
+
}
|
|
943
|
+
globalStats.routeAccesses.set(routeKey, globalStats.routeAccesses.get(routeKey) + 1);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
// Registrar hit al endpoint
|
|
947
|
+
const endpointKey = `${req.method} ${matchedRoute.route.path}`;
|
|
948
|
+
if (!globalStats.endpointHits.has(endpointKey)) {
|
|
949
|
+
globalStats.endpointHits.set(endpointKey, 0);
|
|
950
|
+
}
|
|
951
|
+
globalStats.endpointHits.set(endpointKey, globalStats.endpointHits.get(endpointKey) + 1);
|
|
952
|
+
|
|
953
|
+
if (this.hooks) {
|
|
954
|
+
this.hooks.doAction('route_handler_executed', matchedRoute, req, res);
|
|
810
955
|
}
|
|
811
956
|
} else {
|
|
812
957
|
// Ruta no encontrada
|
|
813
|
-
if (hooks) {
|
|
814
|
-
hooks.doAction('route_not_found', pathname, req, res);
|
|
958
|
+
if (this.hooks) {
|
|
959
|
+
this.hooks.doAction('route_not_found', pathname, req, res);
|
|
815
960
|
}
|
|
816
961
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
817
962
|
res.end(JSON.stringify({ error: 'Ruta no encontrada', path: pathname }));
|
|
818
963
|
}
|
|
819
964
|
}
|
|
820
965
|
|
|
821
|
-
if (hooks) {
|
|
822
|
-
hooks.doAction('request_processed', req, res);
|
|
966
|
+
if (this.hooks) {
|
|
967
|
+
this.hooks.doAction('request_processed', req, res);
|
|
823
968
|
}
|
|
824
969
|
} catch (error) {
|
|
825
|
-
if (hooks) {
|
|
826
|
-
hooks.doAction('request_error', req, res, error);
|
|
970
|
+
if (this.hooks) {
|
|
971
|
+
this.hooks.doAction('request_error', req, res, error);
|
|
827
972
|
}
|
|
828
973
|
|
|
974
|
+
const { ErrorHandler } = getErrorHandler();
|
|
829
975
|
ErrorHandler.handle(error, req, res, this.logger);
|
|
830
976
|
}
|
|
831
977
|
});
|