jerkjs 2.1.6 → 2.2.0
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 +36 -0
- package/README.md +202 -5
- package/index.js +29 -4
- package/lib/core/server.js +328 -27
- package/lib/loader/routeLoader.js +148 -117
- package/lib/middleware/compressor.js +87 -18
- package/lib/mvc/GenericAdapter.js +136 -0
- package/lib/mvc/MariaDBAdapter.js +315 -0
- package/lib/mvc/MemoryAdapter.js +269 -0
- package/lib/mvc/ModelControllerExample.js +285 -0
- package/lib/mvc/controllerBase.js +60 -0
- package/lib/mvc/modelBase.js +383 -0
- package/lib/mvc/modelManager.js +284 -0
- package/lib/mvc/userModel.js +265 -0
- package/lib/mvc/viewEngine.js +32 -1
- package/lib/utils/mimeType.js +62 -0
- package/package.json +5 -3
- package/JERK_FRAMEWORK_DIAGRAM.txt +0 -492
- package/JERK_FRAMEWORK_DIAGRAM_MERMAID.mmd +0 -124
- package/JERK_FRAMEWORK_DOCUMENTATION.md +0 -527
- package/LICENSE +0 -201
- package/README_EN.md +0 -230
- package/README_PT.md +0 -230
- package/docs/ARQUITECTURA_ROUTES.md +0 -140
- package/docs/EXTENSION_MANUAL.md +0 -955
- package/docs/FIREWALL_MANUAL.md +0 -416
- package/docs/HOOK-2.0.md +0 -512
- package/docs/HOOKS_REFERENCE_IMPROVED.md +0 -596
- package/docs/MANUAL_API_SDK.md +0 -536
- package/docs/MARIADB_TOKENS_IMPLEMENTATION.md +0 -110
- package/docs/MIDDLEWARE_MANUAL.md +0 -518
- package/docs/OAUTH2_GOOGLE_MANUAL.md +0 -405
- package/docs/ROUTING_WITHOUT_JSON_GUIDE.md +0 -454
- package/docs/frontend-and-sessions.md +0 -353
- package/docs/guia_inicio_rapido_jerkjs.md +0 -113
- package/examples/examples.arj +0 -0
- package/standard/CompressionTestController.js +0 -56
- package/standard/HealthController.js +0 -16
- package/standard/HomeController.js +0 -12
- package/standard/ProductController.js +0 -18
- package/standard/README.md +0 -47
- package/standard/UserController.js +0 -23
- package/standard/package.json +0 -22
- package/standard/routes.json +0 -65
- package/standard/server.js +0 -140
- package/standardA/controllers/AuthController.js +0 -82
- package/standardA/controllers/HomeController.js +0 -19
- package/standardA/controllers/UserController.js +0 -41
- package/standardA/server.js +0 -311
- package/standardA/views/auth/dashboard.html +0 -51
- package/standardA/views/auth/login.html +0 -47
- package/standardA/views/index.html +0 -32
- package/standardA/views/users/detail.html +0 -28
- package/standardA/views/users/list.html +0 -36
package/lib/core/server.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Servidor HTTP básico para el framework JERK
|
|
3
3
|
* Implementación del componente core/server.js
|
|
4
|
-
* JERK Framework v2.1 - Con optimizaciones de rendimiento
|
|
4
|
+
* JERK Framework v2.1 - Con optimizaciones de rendimiento y soporte para archivos estáticos
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const http = require('http');
|
|
8
8
|
const https = require('https');
|
|
9
9
|
const url = require('url');
|
|
10
10
|
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
11
12
|
const { Logger } = require('../utils/logger');
|
|
12
13
|
const { ErrorHandler } = require('../utils/errorHandler');
|
|
14
|
+
const { getMimeType } = require('../utils/mimeType');
|
|
13
15
|
|
|
14
16
|
class APIServer {
|
|
15
17
|
/**
|
|
@@ -49,6 +51,198 @@ class APIServer {
|
|
|
49
51
|
this.routeRegexCache = new Map();
|
|
50
52
|
}
|
|
51
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Método para crear un handler para servir archivos estáticos
|
|
56
|
+
* @param {Object} staticConfig - Configuración de la ruta estática
|
|
57
|
+
* @returns {Function} - Handler para servir archivos estáticos
|
|
58
|
+
*/
|
|
59
|
+
createStaticFileHandler(staticConfig) {
|
|
60
|
+
return async (req, res) => {
|
|
61
|
+
try {
|
|
62
|
+
// Disparar hook antes de procesar archivo estático
|
|
63
|
+
const hooks = require('../../index.js').hooks;
|
|
64
|
+
if (hooks) {
|
|
65
|
+
const hookResult = hooks.applyFilters('pre_static_file_serve', {
|
|
66
|
+
req,
|
|
67
|
+
res,
|
|
68
|
+
staticConfig,
|
|
69
|
+
shouldContinue: true
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (!hookResult.shouldContinue) {
|
|
73
|
+
return; // Cancelar proceso si el filtro lo indica
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Actualizar configuración si el filtro la modificó
|
|
77
|
+
staticConfig = hookResult.staticConfig || staticConfig;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Extraer la subruta de la solicitud
|
|
81
|
+
// Si la ruta base es /static y la solicitud es /static/assets/hola.js
|
|
82
|
+
// entonces la subruta sería assets/hola.js
|
|
83
|
+
const parsedUrl = require('url').parse(req.url, true);
|
|
84
|
+
const pathname = parsedUrl.pathname;
|
|
85
|
+
let requestedPath = '';
|
|
86
|
+
|
|
87
|
+
// Encontrar la ruta estática que coincide con la solicitud actual
|
|
88
|
+
for (const route of this.routes) {
|
|
89
|
+
if (route.isStatic && pathname.startsWith(route.path)) {
|
|
90
|
+
requestedPath = pathname.substring(route.path.length);
|
|
91
|
+
if (requestedPath.startsWith('/')) {
|
|
92
|
+
requestedPath = requestedPath.substring(1);
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Prevenir path traversal
|
|
99
|
+
const normalizedPath = path.normalize(requestedPath);
|
|
100
|
+
if (normalizedPath.includes('..')) {
|
|
101
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
102
|
+
res.end(JSON.stringify({ error: 'Acceso denegado' }));
|
|
103
|
+
|
|
104
|
+
if (hooks) {
|
|
105
|
+
hooks.doAction('static_file_access_denied', req, res, normalizedPath);
|
|
106
|
+
}
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Asegurarse de que el directorio base sea absoluto
|
|
111
|
+
let baseDir = staticConfig.dir;
|
|
112
|
+
if (!path.isAbsolute(baseDir)) {
|
|
113
|
+
baseDir = path.join(process.cwd(), baseDir);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Construir la ruta física
|
|
117
|
+
const physicalPath = path.join(baseDir, normalizedPath);
|
|
118
|
+
|
|
119
|
+
// Verificar si el archivo existe
|
|
120
|
+
try {
|
|
121
|
+
await fs.promises.access(physicalPath);
|
|
122
|
+
} catch (accessErr) {
|
|
123
|
+
// Si no existe el archivo solicitado, verificar si es un directorio y buscar archivo índice
|
|
124
|
+
const dirPath = path.join(baseDir, normalizedPath);
|
|
125
|
+
try {
|
|
126
|
+
const stats = await fs.promises.stat(dirPath);
|
|
127
|
+
|
|
128
|
+
if (stats.isDirectory()) {
|
|
129
|
+
// Buscar archivo índice
|
|
130
|
+
for (const indexFile of staticConfig.index || ['index.html']) {
|
|
131
|
+
const indexPath = path.join(dirPath, indexFile);
|
|
132
|
+
try {
|
|
133
|
+
await fs.promises.access(indexPath);
|
|
134
|
+
// Disparar hook antes de servir archivo índice
|
|
135
|
+
if (hooks) {
|
|
136
|
+
hooks.doAction('serving_index_file', indexPath, req, res);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const fileContent = await fs.promises.readFile(indexPath);
|
|
140
|
+
const mimeType = getMimeType(indexPath);
|
|
141
|
+
|
|
142
|
+
res.setHeader('Content-Type', mimeType);
|
|
143
|
+
|
|
144
|
+
if (staticConfig.cacheControl) {
|
|
145
|
+
res.setHeader('Cache-Control', staticConfig.cacheControl);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
res.writeHead(200);
|
|
149
|
+
res.end(fileContent);
|
|
150
|
+
|
|
151
|
+
// Disparar hook después de servir archivo
|
|
152
|
+
if (hooks) {
|
|
153
|
+
hooks.doAction('static_file_served', indexPath, req, res);
|
|
154
|
+
}
|
|
155
|
+
return;
|
|
156
|
+
} catch (e) {
|
|
157
|
+
continue; // Probar siguiente archivo índice
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Si es directorio pero no hay archivo índice
|
|
162
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
163
|
+
res.end(JSON.stringify({ error: 'Directorio no encontrado' }));
|
|
164
|
+
|
|
165
|
+
if (hooks) {
|
|
166
|
+
hooks.doAction('static_directory_no_index', dirPath, req, res);
|
|
167
|
+
}
|
|
168
|
+
return;
|
|
169
|
+
} else {
|
|
170
|
+
// No es directorio ni archivo existente
|
|
171
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
172
|
+
res.end(JSON.stringify({ error: 'Archivo no encontrado' }));
|
|
173
|
+
|
|
174
|
+
if (hooks) {
|
|
175
|
+
hooks.doAction('static_file_not_found', physicalPath, req, res);
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
} catch (statErr) {
|
|
180
|
+
// No es directorio ni archivo
|
|
181
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
182
|
+
res.end(JSON.stringify({ error: 'Archivo no encontrado' }));
|
|
183
|
+
|
|
184
|
+
if (hooks) {
|
|
185
|
+
hooks.doAction('static_file_not_found', physicalPath, req, res);
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Disparar hook antes de leer archivo
|
|
192
|
+
if (hooks) {
|
|
193
|
+
hooks.doAction('before_reading_static_file', physicalPath, req, res);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Leer y servir el archivo
|
|
197
|
+
const fileContent = await fs.promises.readFile(physicalPath);
|
|
198
|
+
|
|
199
|
+
// Determinar el tipo MIME
|
|
200
|
+
const mimeType = getMimeType(physicalPath);
|
|
201
|
+
|
|
202
|
+
// Configurar headers
|
|
203
|
+
res.setHeader('Content-Type', mimeType);
|
|
204
|
+
|
|
205
|
+
if (staticConfig.cacheControl) {
|
|
206
|
+
res.setHeader('Cache-Control', staticConfig.cacheControl);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
res.writeHead(200);
|
|
210
|
+
res.end(fileContent);
|
|
211
|
+
|
|
212
|
+
// Disparar hook después de servir archivo
|
|
213
|
+
if (hooks) {
|
|
214
|
+
hooks.doAction('static_file_served', physicalPath, req, res);
|
|
215
|
+
}
|
|
216
|
+
} catch (error) {
|
|
217
|
+
if (error.code === 'ENOENT') {
|
|
218
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
219
|
+
res.end(JSON.stringify({ error: 'Archivo no encontrado' }));
|
|
220
|
+
|
|
221
|
+
const hooks = require('../../index.js').hooks;
|
|
222
|
+
if (hooks) {
|
|
223
|
+
hooks.doAction('static_file_not_found', error.path, req, res);
|
|
224
|
+
}
|
|
225
|
+
} else if (error.code === 'EACCES') {
|
|
226
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
227
|
+
res.end(JSON.stringify({ error: 'Acceso denegado' }));
|
|
228
|
+
|
|
229
|
+
const hooks = require('../../index.js').hooks;
|
|
230
|
+
if (hooks) {
|
|
231
|
+
hooks.doAction('static_file_access_denied', error.path, req, res);
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
235
|
+
res.end(JSON.stringify({ error: 'Error interno del servidor' }));
|
|
236
|
+
|
|
237
|
+
const hooks = require('../../index.js').hooks;
|
|
238
|
+
if (hooks) {
|
|
239
|
+
hooks.doAction('static_file_error', error, req, res);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
52
246
|
/**
|
|
53
247
|
* Método para agregar una ruta al servidor
|
|
54
248
|
* @param {string|Object} method - Método HTTP (GET, POST, PUT, DELETE, etc.) o un objeto de configuración de ruta
|
|
@@ -91,6 +285,31 @@ class APIServer {
|
|
|
91
285
|
throw new Error('La ruta debe tener una propiedad "method"');
|
|
92
286
|
}
|
|
93
287
|
|
|
288
|
+
// Verificar si es una ruta estática
|
|
289
|
+
if (routeConfig.static) {
|
|
290
|
+
// Validar configuración estática
|
|
291
|
+
if (!routeConfig.static.dir) {
|
|
292
|
+
throw new Error('Las rutas estáticas deben tener un directorio especificado');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// El método debe ser GET para rutas estáticas
|
|
296
|
+
if (routeConfig.method.toUpperCase() !== 'GET') {
|
|
297
|
+
throw new Error('Las rutas estáticas deben usar el método GET, no ' + routeConfig.method);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Crear handler para archivos estáticos
|
|
301
|
+
const staticHandler = this.createStaticFileHandler(routeConfig.static);
|
|
302
|
+
|
|
303
|
+
this.routes.push({
|
|
304
|
+
method: routeConfig.method.toUpperCase(),
|
|
305
|
+
path: routeConfig.path,
|
|
306
|
+
handler: staticHandler,
|
|
307
|
+
isStatic: true // Marcar como ruta estática para posible procesamiento especial
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
return; // Salir después de procesar ruta estática
|
|
311
|
+
}
|
|
312
|
+
|
|
94
313
|
if (!routeConfig.handler && !routeConfig.controller) {
|
|
95
314
|
throw new Error('La ruta debe tener un "handler" o un "controller"');
|
|
96
315
|
}
|
|
@@ -256,7 +475,7 @@ class APIServer {
|
|
|
256
475
|
*/
|
|
257
476
|
findRoute(method, pathname) {
|
|
258
477
|
// Buscar ruta exacta primero
|
|
259
|
-
const exactMatch = this.routes.find(route =>
|
|
478
|
+
const exactMatch = this.routes.find(route =>
|
|
260
479
|
route.method === method && route.path === pathname
|
|
261
480
|
);
|
|
262
481
|
|
|
@@ -267,6 +486,23 @@ class APIServer {
|
|
|
267
486
|
};
|
|
268
487
|
}
|
|
269
488
|
|
|
489
|
+
// Buscar rutas estáticas (prefijos)
|
|
490
|
+
for (const route of this.routes) {
|
|
491
|
+
if (route.method !== method) continue;
|
|
492
|
+
|
|
493
|
+
// Para rutas estáticas, verificar si la ruta solicitada comienza con el prefijo de la ruta estática
|
|
494
|
+
if (route.isStatic && pathname.startsWith(route.path)) {
|
|
495
|
+
// Verificar que sea exactamente el prefijo o que haya una barra después del prefijo
|
|
496
|
+
const remainingPath = pathname.substring(route.path.length);
|
|
497
|
+
if (route.path === '/' || remainingPath === '' || remainingPath.startsWith('/')) {
|
|
498
|
+
return {
|
|
499
|
+
route: route,
|
|
500
|
+
params: {}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
270
506
|
// Buscar rutas parametrizadas
|
|
271
507
|
for (const route of this.routes) {
|
|
272
508
|
if (route.method !== method) continue;
|
|
@@ -426,9 +662,16 @@ class APIServer {
|
|
|
426
662
|
const parsedUrl = url.parse(req.url, true);
|
|
427
663
|
const { pathname, query } = parsedUrl;
|
|
428
664
|
|
|
665
|
+
// Disparar hook antes de procesar la solicitud
|
|
666
|
+
const hooks = require('../../index.js').hooks;
|
|
667
|
+
if (hooks) {
|
|
668
|
+
hooks.doAction('request_received', req, res);
|
|
669
|
+
}
|
|
670
|
+
|
|
429
671
|
// Agregar propiedades útiles a la solicitud
|
|
430
672
|
req.query = query;
|
|
431
673
|
req.params = {};
|
|
674
|
+
req.originalUrl = req.url; // Guardar la URL original para rutas estáticas
|
|
432
675
|
|
|
433
676
|
// Inicializar array para acumular chunks del body
|
|
434
677
|
const bodyChunks = [];
|
|
@@ -443,6 +686,10 @@ class APIServer {
|
|
|
443
686
|
res.writeHead(413, { 'Content-Type': 'application/json' });
|
|
444
687
|
res.end(JSON.stringify({ error: 'Solicitud demasiado grande', details: `El cuerpo de la solicitud excede el límite permitido de ${this.maxBodySize} bytes` }));
|
|
445
688
|
req.destroy(); // Terminar la conexión
|
|
689
|
+
|
|
690
|
+
if (hooks) {
|
|
691
|
+
hooks.doAction('request_body_too_large', req, res, bodySize, this.maxBodySize);
|
|
692
|
+
}
|
|
446
693
|
return;
|
|
447
694
|
}
|
|
448
695
|
bodyChunks.push(chunk);
|
|
@@ -459,6 +706,10 @@ class APIServer {
|
|
|
459
706
|
req.body = JSON.parse(req.body);
|
|
460
707
|
} catch (e) {
|
|
461
708
|
req.body = {};
|
|
709
|
+
|
|
710
|
+
if (hooks) {
|
|
711
|
+
hooks.doAction('request_body_parse_error', req, res, e);
|
|
712
|
+
}
|
|
462
713
|
}
|
|
463
714
|
}
|
|
464
715
|
|
|
@@ -469,10 +720,18 @@ class APIServer {
|
|
|
469
720
|
const renderedHtml = this.viewEngine.render(viewName, data);
|
|
470
721
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
471
722
|
res.end(renderedHtml);
|
|
723
|
+
|
|
724
|
+
if (hooks) {
|
|
725
|
+
hooks.doAction('view_rendered', viewName, data, req, res);
|
|
726
|
+
}
|
|
472
727
|
} catch (error) {
|
|
473
728
|
console.error('Error renderizando vista:', error);
|
|
474
729
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
475
730
|
res.end(JSON.stringify({ error: 'Error interno del servidor', details: error.message }));
|
|
731
|
+
|
|
732
|
+
if (hooks) {
|
|
733
|
+
hooks.doAction('view_render_error', viewName, data, req, res, error);
|
|
734
|
+
}
|
|
476
735
|
}
|
|
477
736
|
};
|
|
478
737
|
}
|
|
@@ -506,7 +765,12 @@ class APIServer {
|
|
|
506
765
|
await middleware(req, res);
|
|
507
766
|
}
|
|
508
767
|
|
|
509
|
-
if (res.finished)
|
|
768
|
+
if (res.finished) {
|
|
769
|
+
if (hooks) {
|
|
770
|
+
hooks.doAction('middleware_response_finished', req, res);
|
|
771
|
+
}
|
|
772
|
+
return; // Si el middleware respondió, salir
|
|
773
|
+
}
|
|
510
774
|
}
|
|
511
775
|
}
|
|
512
776
|
|
|
@@ -516,6 +780,11 @@ class APIServer {
|
|
|
516
780
|
if (matchedRoute) {
|
|
517
781
|
// Agregar parámetros a la solicitud
|
|
518
782
|
req.params = matchedRoute.params;
|
|
783
|
+
|
|
784
|
+
if (hooks) {
|
|
785
|
+
hooks.doAction('route_matched', matchedRoute, req, res);
|
|
786
|
+
}
|
|
787
|
+
|
|
519
788
|
// Ejecutar handler de la ruta
|
|
520
789
|
await matchedRoute.route.handler(req, res);
|
|
521
790
|
} else {
|
|
@@ -526,6 +795,10 @@ class APIServer {
|
|
|
526
795
|
// devolver 204 para cumplir con CORS preflight
|
|
527
796
|
res.writeHead(204);
|
|
528
797
|
res.end();
|
|
798
|
+
|
|
799
|
+
if (hooks) {
|
|
800
|
+
hooks.doAction('options_response_sent', req, res);
|
|
801
|
+
}
|
|
529
802
|
}
|
|
530
803
|
}
|
|
531
804
|
} else {
|
|
@@ -536,45 +809,73 @@ class APIServer {
|
|
|
536
809
|
// Agregar parámetros a la solicitud
|
|
537
810
|
req.params = matchedRoute.params;
|
|
538
811
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
812
|
+
if (hooks) {
|
|
813
|
+
hooks.doAction('route_matched', matchedRoute, req, res);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// Ejecutar middlewares (excepto para rutas estáticas)
|
|
817
|
+
if (!matchedRoute.route.isStatic) {
|
|
818
|
+
for (const middleware of this.middlewares) {
|
|
819
|
+
// Verificar si el middleware es una función antes de ejecutarla
|
|
820
|
+
if (typeof middleware === 'function') {
|
|
821
|
+
// Verificar si el middleware tiene firma (req, res, next)
|
|
822
|
+
if (middleware.length === 3) {
|
|
823
|
+
// Middleware con next
|
|
824
|
+
await new Promise((resolve, reject) => {
|
|
825
|
+
const next = (err) => {
|
|
826
|
+
if (err) {
|
|
827
|
+
reject(err);
|
|
828
|
+
} else {
|
|
829
|
+
resolve();
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
const result = middleware(req, res, next);
|
|
833
|
+
// Si el middleware devuelve una promesa, esperarla
|
|
834
|
+
if (result && typeof result.then === 'function') {
|
|
835
|
+
result.then(resolve).catch(reject);
|
|
552
836
|
}
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
//
|
|
556
|
-
|
|
557
|
-
|
|
837
|
+
});
|
|
838
|
+
} else {
|
|
839
|
+
// Middleware sin next
|
|
840
|
+
await middleware(req, res);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
if (res.finished) {
|
|
844
|
+
if (hooks) {
|
|
845
|
+
hooks.doAction('middleware_response_finished', req, res);
|
|
558
846
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
// Middleware sin next
|
|
562
|
-
await middleware(req, res);
|
|
847
|
+
return; // Si el middleware respondió, salir
|
|
848
|
+
}
|
|
563
849
|
}
|
|
564
|
-
|
|
565
|
-
if (res.finished) return; // Si el middleware respondió, salir
|
|
566
850
|
}
|
|
567
851
|
}
|
|
568
852
|
|
|
569
853
|
// Ejecutar handler de la ruta
|
|
570
854
|
await matchedRoute.route.handler(req, res);
|
|
855
|
+
|
|
856
|
+
if (hooks) {
|
|
857
|
+
hooks.doAction('route_handler_executed', matchedRoute, req, res);
|
|
858
|
+
}
|
|
571
859
|
} else {
|
|
572
860
|
// Ruta no encontrada
|
|
861
|
+
if (hooks) {
|
|
862
|
+
hooks.doAction('route_not_found', pathname, req, res);
|
|
863
|
+
}
|
|
573
864
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
574
865
|
res.end(JSON.stringify({ error: 'Ruta no encontrada', path: pathname }));
|
|
866
|
+
|
|
867
|
+
|
|
575
868
|
}
|
|
576
869
|
}
|
|
870
|
+
|
|
871
|
+
if (hooks) {
|
|
872
|
+
hooks.doAction('request_processed', req, res);
|
|
873
|
+
}
|
|
577
874
|
} catch (error) {
|
|
875
|
+
if (hooks) {
|
|
876
|
+
hooks.doAction('request_error', req, res, error);
|
|
877
|
+
}
|
|
878
|
+
|
|
578
879
|
ErrorHandler.handle(error, req, res, this.logger);
|
|
579
880
|
}
|
|
580
881
|
});
|