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.
Files changed (46) hide show
  1. package/CHANGELOG.md +167 -79
  2. package/README.md +134 -146
  3. package/RESULTADOS_WAF.md +63 -0
  4. package/doc-2.5/ADMIN_EXTENSION_COMMANDS_MANUAL.md +261 -0
  5. package/doc-2.5/ADMIN_EXTENSION_HOOK_EXAMPLE.md +28 -0
  6. package/doc-2.5/ADMIN_EXTENSION_INTEGRATION_MANUAL.md +232 -0
  7. package/doc-2.5/CACHE_SYSTEM_MAP.md +206 -0
  8. package/doc-2.5/MANUAL_MODULOS_ADMIN.md +287 -0
  9. package/doc-2.5/QUEUE_CLI_MODULE_MANUAL.md +289 -0
  10. package/doc-2.5/QUEUE_SYSTEM_MANUAL.md +320 -0
  11. package/doc-2.5/ROUTE_CACHE_MODULE_MANUAL.md +205 -0
  12. package/doc-2.5/WAF_MODULE_MANUAL.md +229 -0
  13. package/index.js +19 -4
  14. package/jerk-admin-client/README.md +69 -0
  15. package/jerk-admin-client/package.json +23 -0
  16. package/jerk-admin-client.js +257 -0
  17. package/lib/admin/AdminExtension.js +491 -0
  18. package/lib/admin/ModuleLoader.js +77 -0
  19. package/lib/admin/config.js +21 -0
  20. package/lib/admin/modules/CacheModule.js +145 -0
  21. package/lib/admin/modules/ControllerGeneratorModule.js +414 -0
  22. package/lib/admin/modules/QueueManagementModule.js +265 -0
  23. package/lib/admin/modules/RouteCacheModule.js +227 -0
  24. package/lib/admin/modules/RouteManagerModule.js +468 -0
  25. package/lib/admin/modules/STATS_MODULE_README.md +113 -0
  26. package/lib/admin/modules/StatsModule.js +140 -0
  27. package/lib/admin/modules/SystemModule.js +140 -0
  28. package/lib/admin/modules/TimeModule.js +95 -0
  29. package/lib/admin/modules/ViewCacheStatsModule.js +92 -0
  30. package/lib/admin/modules/WAFModule.js +737 -0
  31. package/lib/cache/CacheHooks.js +141 -0
  32. package/lib/core/server.js +223 -77
  33. package/lib/middleware/firewall.js +112 -17
  34. package/lib/mvc/viewEngine.js +89 -5
  35. package/lib/queue/GlobalQueueStorage.js +38 -0
  36. package/lib/queue/QueueSystem.js +451 -0
  37. package/lib/queue/admin_example.js +114 -0
  38. package/lib/queue/example.js +268 -0
  39. package/lib/queue/integration.js +109 -0
  40. package/lib/router/RouteMatcher.js +242 -54
  41. package/lib/utils/globalStats.js +16 -0
  42. package/lib/utils/globalViewCacheInfo.js +16 -0
  43. package/lib/utils/globalWAFStats.js +54 -0
  44. package/package.json +2 -2
  45. package/test-colors.js +46 -0
  46. package/test-help-alias.js +31 -0
@@ -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
- const hooks = getHooks();
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
- const hooks = getHooks();
274
- if (hooks) {
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
- const hooks = getHooks();
282
- if (hooks) {
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 } = require('../utils/errorHandler');
332
+ const { ErrorHandler } = getErrorHandler();
288
333
  ErrorHandler.handle(error, req, res, this.logger);
289
334
 
290
- const hooks = getHooks();
291
- if (hooks) {
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
- this.routes.push({
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 } = require('../utils/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
- this.routes.push({
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 } = require('../utils/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
- this.routes.push({
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
- this.routes.push({
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
- this.routes.push({
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
- const hooks = getHooks();
539
- if (hooks) {
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
- const hooks = getHooks();
610
- if (hooks) {
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
- if (hooks) {
809
- hooks.doAction('route_handler_executed', matchedRoute, req, res);
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
  });