jerkjs 2.5.1 → 2.5.2
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 +11 -0
- package/lib/core/server.js +4 -113
- package/lib/loader/routeDirectoryLoader.js +14 -8
- package/lib/loader/routeLoader.js +3 -21
- package/lib/router/RouteMatcher.js +139 -0
- package/package.json +2 -2
- package/qa/informe_qa_fix_enrutamiento.md +93 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## v2.5.2 - 7 de febrero de 2026
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **Prioridad de enrutamiento corregida**: Se resolvió un problema crítico donde las rutas estáticas (prefijos) tenían prioridad sobre las rutas parametrizadas, lo que impedía que rutas como `/api/products/:id` funcionaran correctamente cuando existía una ruta estática como `/api`. El nuevo orden de prioridad es: 1) Rutas exactas → 2) Rutas parametrizadas → 3) Rutas estáticas.
|
|
7
|
+
- **Funcionamiento de rutas parametrizadas en modo directorio**: Se corrigió el problema donde las rutas parametrizadas no funcionaban correctamente cuando se utilizaba el modo de carga de rutas desde directorio (RouteDirectoryLoader), afectando especialmente el sistema de colas de Qwen.
|
|
8
|
+
- **Separación de responsabilidades en enrutamiento**: Se movió toda la lógica de enrutamiento a un componente especializado `RouteMatcher.js`, eliminando la duplicación de lógica entre `server.js` y `routeLoader.js`.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Arquitectura de enrutamiento mejorada**: Se implementó un componente especializado `RouteMatcher.js` para encapsular toda la lógica de enrutamiento, siguiendo principios de separación de responsabilidades y mejorando la mantenibilidad del código.
|
|
12
|
+
- **Compatibilidad mantenida**: Se mantuvo la compatibilidad hacia atrás con el modo archivo único, asegurando que no se introdujeran regresiones en funcionalidades existentes.
|
|
13
|
+
|
|
3
14
|
## v2.5.1 - 7 de febrero de 2026
|
|
4
15
|
|
|
5
16
|
### Fixed
|
package/lib/core/server.js
CHANGED
|
@@ -12,6 +12,7 @@ const path = require('path');
|
|
|
12
12
|
const { Logger } = require('../utils/logger');
|
|
13
13
|
const { ErrorHandler } = require('../utils/errorHandler');
|
|
14
14
|
const { getMimeType } = require('../utils/mimeType');
|
|
15
|
+
const RouteMatcher = require('../router/RouteMatcher');
|
|
15
16
|
|
|
16
17
|
class APIServer {
|
|
17
18
|
/**
|
|
@@ -47,8 +48,8 @@ class APIServer {
|
|
|
47
48
|
this.logger = new Logger();
|
|
48
49
|
this.server = null;
|
|
49
50
|
|
|
50
|
-
//
|
|
51
|
-
this.
|
|
51
|
+
// Inicializar el componente de enrutamiento
|
|
52
|
+
this.routeMatcher = new RouteMatcher();
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
/**
|
|
@@ -522,117 +523,7 @@ class APIServer {
|
|
|
522
523
|
* @returns {Object|null} - Objeto de ruta encontrado o null
|
|
523
524
|
*/
|
|
524
525
|
findRoute(method, pathname) {
|
|
525
|
-
|
|
526
|
-
const exactMatch = this.routes.find(route =>
|
|
527
|
-
route.method === method && route.path === pathname
|
|
528
|
-
);
|
|
529
|
-
|
|
530
|
-
if (exactMatch) {
|
|
531
|
-
return {
|
|
532
|
-
route: exactMatch,
|
|
533
|
-
params: {}
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
// Buscar rutas estáticas (prefijos)
|
|
538
|
-
for (const route of this.routes) {
|
|
539
|
-
if (route.method !== method) continue;
|
|
540
|
-
|
|
541
|
-
// Para rutas estáticas, verificar si la ruta solicitada comienza con el prefijo de la ruta estática
|
|
542
|
-
if (route.isStatic && pathname.startsWith(route.path)) {
|
|
543
|
-
// Verificar que sea exactamente el prefijo o que haya una barra después del prefijo
|
|
544
|
-
const remainingPath = pathname.substring(route.path.length);
|
|
545
|
-
if (route.path === '/' || remainingPath === '' || remainingPath.startsWith('/')) {
|
|
546
|
-
return {
|
|
547
|
-
route: route,
|
|
548
|
-
params: {}
|
|
549
|
-
};
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
// Buscar rutas parametrizadas
|
|
555
|
-
for (const route of this.routes) {
|
|
556
|
-
if (route.method !== method) continue;
|
|
557
|
-
|
|
558
|
-
// Convertir ruta parametrizada a expresión regular
|
|
559
|
-
const routeRegex = this.pathToRegex(route.path);
|
|
560
|
-
const match = pathname.match(routeRegex);
|
|
561
|
-
|
|
562
|
-
if (match) {
|
|
563
|
-
const params = this.extractParams(route.path, pathname);
|
|
564
|
-
return {
|
|
565
|
-
route: route,
|
|
566
|
-
params
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
return null;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
/**
|
|
575
|
-
* Convierte una ruta con parámetros a expresión regular
|
|
576
|
-
* @param {string} path - Ruta con posibles parámetros
|
|
577
|
-
* @returns {RegExp} - Expresión regular para la ruta
|
|
578
|
-
*/
|
|
579
|
-
pathToRegex(path) {
|
|
580
|
-
// Verificar si ya está en caché
|
|
581
|
-
if (this.routeRegexCache.has(path)) {
|
|
582
|
-
return this.routeRegexCache.get(path);
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// Lógica de escape de caracteres especiales
|
|
586
|
-
let escapedPath = '';
|
|
587
|
-
for (let i = 0; i < path.length; i++) {
|
|
588
|
-
const char = path[i];
|
|
589
|
-
if (char.match(/[.+?^${}()|[\]\\]/) && !(i > 0 && path[i-1] === ':')) {
|
|
590
|
-
escapedPath += '\\' + char;
|
|
591
|
-
} else {
|
|
592
|
-
escapedPath += char;
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
// Reemplazar parámetros :param con grupos de captura no greedy para evitar problemas de matching
|
|
596
|
-
const regexPath = escapedPath.replace(/:([a-zA-Z0-9_]+)/g, '([^/]+?)');
|
|
597
|
-
const regex = new RegExp(`^${regexPath}$`);
|
|
598
|
-
|
|
599
|
-
// Almacenar en caché
|
|
600
|
-
this.routeRegexCache.set(path, regex);
|
|
601
|
-
return regex;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
/**
|
|
605
|
-
* Extrae los parámetros de una ruta parametrizada
|
|
606
|
-
* @param {string} routePath - Ruta con parámetros (ej. /users/:id)
|
|
607
|
-
* @param {string} actualPath - Ruta real solicitada
|
|
608
|
-
* @returns {Object} - Objeto con los parámetros extraídos
|
|
609
|
-
*/
|
|
610
|
-
extractParams(routePath, actualPath) {
|
|
611
|
-
const params = {};
|
|
612
|
-
|
|
613
|
-
// Expresión regular para encontrar parámetros en la ruta
|
|
614
|
-
const paramNames = [];
|
|
615
|
-
const paramNameRegex = /:([a-zA-Z0-9_]+)/g;
|
|
616
|
-
let match;
|
|
617
|
-
|
|
618
|
-
while ((match = paramNameRegex.exec(routePath)) !== null) {
|
|
619
|
-
paramNames.push(match[1]);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// Crear expresión regular para extraer valores
|
|
623
|
-
const routeRegex = this.pathToRegex(routePath);
|
|
624
|
-
const values = actualPath.match(routeRegex);
|
|
625
|
-
|
|
626
|
-
if (values) {
|
|
627
|
-
// El primer elemento es la cadena completa, los demás son los valores capturados
|
|
628
|
-
for (let i = 0; i < paramNames.length; i++) {
|
|
629
|
-
if (values[i + 1]) {
|
|
630
|
-
params[paramNames[i]] = values[i + 1];
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
return params;
|
|
526
|
+
return this.routeMatcher.findRoute(this.routes, method, pathname);
|
|
636
527
|
}
|
|
637
528
|
|
|
638
529
|
/**
|
|
@@ -186,7 +186,7 @@ class RouteDirectoryLoader {
|
|
|
186
186
|
// Verificar si ya existe una ruta con el mismo método y path
|
|
187
187
|
if (this.routeMap.has(routeKey)) {
|
|
188
188
|
const existingRouteInfo = this.routeMap.get(routeKey);
|
|
189
|
-
|
|
189
|
+
|
|
190
190
|
// Disparar hook antes de sobrescribir ruta
|
|
191
191
|
const hooks = require('../../index.js').hooks;
|
|
192
192
|
if (hooks) {
|
|
@@ -196,18 +196,24 @@ class RouteDirectoryLoader {
|
|
|
196
196
|
server
|
|
197
197
|
});
|
|
198
198
|
}
|
|
199
|
-
|
|
199
|
+
|
|
200
200
|
// Mostrar mensaje de advertencia con colores
|
|
201
201
|
console.log('\x1b[31m%s\x1b[0m', `[RUTA SOBREESCRITA] Archivo: ${existingRouteInfo.sourceFile}, Ruta: ${route.method.toUpperCase()} ${route.path}`);
|
|
202
202
|
console.log('\x1b[33m%s\x1b[0m', `[RUTA ACTUAL] Archivo: ${sourceFile}, Ruta: ${route.method.toUpperCase()} ${route.path}`);
|
|
203
|
+
|
|
204
|
+
// Actualizar la ruta en el mapa con la nueva información
|
|
205
|
+
this.routeMap.set(routeKey, {
|
|
206
|
+
route: { ...route },
|
|
207
|
+
sourceFile
|
|
208
|
+
});
|
|
209
|
+
} else {
|
|
210
|
+
// Agregar la ruta al mapa con su archivo de origen
|
|
211
|
+
this.routeMap.set(routeKey, {
|
|
212
|
+
route: { ...route },
|
|
213
|
+
sourceFile
|
|
214
|
+
});
|
|
203
215
|
}
|
|
204
216
|
|
|
205
|
-
// Agregar la ruta al mapa con su archivo de origen
|
|
206
|
-
this.routeMap.set(routeKey, {
|
|
207
|
-
route: { ...route },
|
|
208
|
-
sourceFile
|
|
209
|
-
});
|
|
210
|
-
|
|
211
217
|
// Cargar la ruta en el servidor
|
|
212
218
|
const routeLoader = new RouteLoader();
|
|
213
219
|
await routeLoader.loadSingleRoute(server, route);
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const fs = require('fs');
|
|
8
8
|
const path = require('path');
|
|
9
|
+
const RouteMatcher = require('../router/RouteMatcher');
|
|
9
10
|
|
|
10
11
|
class RouteLoader {
|
|
11
12
|
/**
|
|
@@ -13,6 +14,8 @@ class RouteLoader {
|
|
|
13
14
|
*/
|
|
14
15
|
constructor() {
|
|
15
16
|
this.loadedRoutes = [];
|
|
17
|
+
// Inicializar el componente de enrutamiento para uso en el loader si es necesario
|
|
18
|
+
this.routeMatcher = new RouteMatcher();
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
/**
|
|
@@ -296,27 +299,6 @@ class RouteLoader {
|
|
|
296
299
|
}
|
|
297
300
|
}
|
|
298
301
|
|
|
299
|
-
/**
|
|
300
|
-
* Convierte una ruta con parámetros a expresión regular
|
|
301
|
-
* @param {string} path - Ruta con posibles parámetros
|
|
302
|
-
* @returns {RegExp} - Expresión regular para la ruta
|
|
303
|
-
*/
|
|
304
|
-
pathToRegex(path) {
|
|
305
|
-
// Escapar caracteres especiales de la ruta, excepto los parámetros
|
|
306
|
-
// Pero dejar : sin escapar ya que lo usaremos para identificar parámetros
|
|
307
|
-
let escapedPath = '';
|
|
308
|
-
for (let i = 0; i < path.length; i++) {
|
|
309
|
-
const char = path[i];
|
|
310
|
-
if (char.match(/[.+?^${}()|[\]\\]/) && !(i > 0 && path[i-1] === ':')) {
|
|
311
|
-
escapedPath += '\\' + char;
|
|
312
|
-
} else {
|
|
313
|
-
escapedPath += char;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
// Reemplazar parámetros :param con grupos de captura
|
|
317
|
-
const regexPath = escapedPath.replace(/:([a-zA-Z0-9_]+)/g, '([^/]+)');
|
|
318
|
-
return new RegExp(`^${regexPath}$`);
|
|
319
|
-
}
|
|
320
302
|
|
|
321
303
|
/**
|
|
322
304
|
* Método para recargar rutas desde un archivo
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Componente especializado para la lógica de enrutado
|
|
3
|
+
* Implementación del componente router/RouteMatcher.js
|
|
4
|
+
* JERK Framework v2.1 - Separación de responsabilidades para enrutamiento
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class RouteMatcher {
|
|
8
|
+
/**
|
|
9
|
+
* Constructor del matcher de rutas
|
|
10
|
+
*/
|
|
11
|
+
constructor() {
|
|
12
|
+
// Cache de expresiones regulares para rutas parametrizadas
|
|
13
|
+
this.routeRegexCache = new Map();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Método para encontrar una ruta coincidente
|
|
18
|
+
* @param {Array} routes - Array de rutas registradas
|
|
19
|
+
* @param {string} method - Método HTTP
|
|
20
|
+
* @param {string} pathname - Ruta a buscar
|
|
21
|
+
* @returns {Object|null} - Objeto de ruta encontrado o null
|
|
22
|
+
*/
|
|
23
|
+
findRoute(routes, method, pathname) {
|
|
24
|
+
// Buscar ruta exacta primero
|
|
25
|
+
const exactMatch = routes.find(route =>
|
|
26
|
+
route.method === method && route.path === pathname
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
if (exactMatch) {
|
|
30
|
+
return {
|
|
31
|
+
route: exactMatch,
|
|
32
|
+
params: {}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Buscar rutas parametrizadas antes que rutas estáticas (prefijos)
|
|
37
|
+
// Esto evita que rutas estáticas como /api capturen rutas parametrizadas como /api/users/:id
|
|
38
|
+
for (const route of routes) {
|
|
39
|
+
if (route.method !== method) continue;
|
|
40
|
+
|
|
41
|
+
// Convertir ruta parametrizada a expresión regular
|
|
42
|
+
const routeRegex = this.pathToRegex(route.path);
|
|
43
|
+
const match = pathname.match(routeRegex);
|
|
44
|
+
|
|
45
|
+
if (match) {
|
|
46
|
+
const params = this.extractParams(route.path, pathname);
|
|
47
|
+
return {
|
|
48
|
+
route: route,
|
|
49
|
+
params
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Buscar rutas estáticas (prefijos) - solo después de buscar rutas parametrizadas
|
|
55
|
+
for (const route of routes) {
|
|
56
|
+
if (route.method !== method) continue;
|
|
57
|
+
|
|
58
|
+
// Para rutas estáticas, verificar si la ruta solicitada comienza con el prefijo de la ruta estática
|
|
59
|
+
if (route.isStatic && pathname.startsWith(route.path)) {
|
|
60
|
+
// Verificar que sea exactamente el prefijo o que haya una barra después del prefijo
|
|
61
|
+
const remainingPath = pathname.substring(route.path.length);
|
|
62
|
+
if (route.path === '/' || remainingPath === '' || remainingPath.startsWith('/')) {
|
|
63
|
+
return {
|
|
64
|
+
route: route,
|
|
65
|
+
params: {}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Convierte una ruta con parámetros a expresión regular
|
|
76
|
+
* @param {string} path - Ruta con posibles parámetros
|
|
77
|
+
* @returns {RegExp} - Expresión regular para la ruta
|
|
78
|
+
*/
|
|
79
|
+
pathToRegex(path) {
|
|
80
|
+
// Verificar si ya está en caché
|
|
81
|
+
if (this.routeRegexCache.has(path)) {
|
|
82
|
+
return this.routeRegexCache.get(path);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Lógica de escape de caracteres especiales
|
|
86
|
+
let escapedPath = '';
|
|
87
|
+
for (let i = 0; i < path.length; i++) {
|
|
88
|
+
const char = path[i];
|
|
89
|
+
if (char.match(/[.+?^${}()|[\]\\]/) && !(i > 0 && path[i-1] === ':')) {
|
|
90
|
+
escapedPath += '\\' + char;
|
|
91
|
+
} else {
|
|
92
|
+
escapedPath += char;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Reemplazar parámetros :param con grupos de captura no greedy para evitar problemas de matching
|
|
96
|
+
const regexPath = escapedPath.replace(/:([a-zA-Z0-9_]+)/g, '([^/]+?)');
|
|
97
|
+
const regex = new RegExp(`^${regexPath}$`);
|
|
98
|
+
|
|
99
|
+
// Almacenar en caché
|
|
100
|
+
this.routeRegexCache.set(path, regex);
|
|
101
|
+
return regex;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Extrae los parámetros de una ruta parametrizada
|
|
106
|
+
* @param {string} routePath - Ruta con parámetros (ej. /users/:id)
|
|
107
|
+
* @param {string} actualPath - Ruta real solicitada
|
|
108
|
+
* @returns {Object} - Objeto con los parámetros extraídos
|
|
109
|
+
*/
|
|
110
|
+
extractParams(routePath, actualPath) {
|
|
111
|
+
const params = {};
|
|
112
|
+
|
|
113
|
+
// Expresión regular para encontrar parámetros en la ruta
|
|
114
|
+
const paramNames = [];
|
|
115
|
+
const paramNameRegex = /:([a-zA-Z0-9_]+)/g;
|
|
116
|
+
let match;
|
|
117
|
+
|
|
118
|
+
while ((match = paramNameRegex.exec(routePath)) !== null) {
|
|
119
|
+
paramNames.push(match[1]);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Crear expresión regular para extraer valores
|
|
123
|
+
const routeRegex = this.pathToRegex(routePath);
|
|
124
|
+
const values = actualPath.match(routeRegex);
|
|
125
|
+
|
|
126
|
+
if (values) {
|
|
127
|
+
// El primer elemento es la cadena completa, los demás son los valores capturados
|
|
128
|
+
for (let i = 0; i < paramNames.length; i++) {
|
|
129
|
+
if (values[i + 1]) {
|
|
130
|
+
params[paramNames[i]] = values[i + 1];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return params;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
module.exports = RouteMatcher;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jerkjs",
|
|
3
|
-
"version": "2.5.
|
|
4
|
-
"description": "JERK Framework v2.5.
|
|
3
|
+
"version": "2.5.2",
|
|
4
|
+
"description": "JERK Framework v2.5.2 - A comprehensive framework for building secure and scalable APIs with frontend support, sessions, template engine, integration with qbuilderjs, complete MVC architecture with models, and enhanced route loading from directory",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Informe QA: Sistema BlackCoffee con Fix de Enrutamiento
|
|
2
|
+
|
|
3
|
+
## Resumen Ejecutivo
|
|
4
|
+
|
|
5
|
+
El sistema BlackCoffee ha sido sometido a pruebas exhaustivas tras la implementación de un fix crítico para el enrutamiento de rutas parametrizadas. El problema que impedía el funcionamiento correcto de las rutas parametrizadas en modo directorio ha sido completamente resuelto.
|
|
6
|
+
|
|
7
|
+
## Estado Anterior vs Actual
|
|
8
|
+
|
|
9
|
+
### Antes del Fix
|
|
10
|
+
- **Modo directorio (predeterminado)**: ❌ Rutas parametrizadas no funcionaban
|
|
11
|
+
- **Modo archivo único**: ✅ Funcionaba correctamente
|
|
12
|
+
- **Impacto**: Sistema de colas de Qwen no operativo en modo directorio
|
|
13
|
+
|
|
14
|
+
### Después del Fix
|
|
15
|
+
- **Modo directorio**: ✅ **TOTALMENTE OPERATIVO**
|
|
16
|
+
- **Modo archivo único**: ✅ **CONTINÚA FUNCIONANDO CORRECTAMENTE**
|
|
17
|
+
- **Impacto**: Sistema de colas de Qwen completamente funcional
|
|
18
|
+
|
|
19
|
+
## Cambios Implementados
|
|
20
|
+
|
|
21
|
+
### Arquitectura de Enrutamiento
|
|
22
|
+
- Se implementó un componente especializado `RouteMatcher.js` para encapsular toda la lógica de enrutamiento
|
|
23
|
+
- Se separaron claramente las responsabilidades entre componentes
|
|
24
|
+
- Se centralizó la lógica de matching de rutas
|
|
25
|
+
|
|
26
|
+
### Prioridad de Enrutamiento (Fix Principal)
|
|
27
|
+
- **Antes**: Rutas estáticas (prefijos) tenían prioridad sobre rutas parametrizadas
|
|
28
|
+
- **Después**: Rutas parametrizadas tienen prioridad sobre rutas estáticas
|
|
29
|
+
- **Nuevo orden**: 1) Rutas exactas → 2) Rutas parametrizadas → 3) Rutas estáticas
|
|
30
|
+
|
|
31
|
+
## Resultados de las Pruebas
|
|
32
|
+
|
|
33
|
+
### Rutas Parametrizadas Funcionales
|
|
34
|
+
| Ruta | Método | Controlador | Estado |
|
|
35
|
+
|------|--------|-------------|---------|
|
|
36
|
+
| `/api/products/:id` | GET | `getProductById` | ✅ Funcional |
|
|
37
|
+
| `/api/qwen/result/:id` | GET | `getQwenResult` | ✅ Funcional |
|
|
38
|
+
| `/api/queue/status/:id` | GET | `getJobStatus` | ✅ Funcional |
|
|
39
|
+
| `/api/queue/jobs/:status` | GET | `getJobsByStatus` | ✅ Funcional |
|
|
40
|
+
|
|
41
|
+
### Sistema de Colas de Qwen
|
|
42
|
+
| Endpoint | Funcionalidad | Estado |
|
|
43
|
+
|----------|---------------|---------|
|
|
44
|
+
| `POST /api/qwen/queue` | Creación de trabajos | ✅ Funcional |
|
|
45
|
+
| `GET /api/qwen/result/:id` | Consulta de resultados | ✅ Funcional |
|
|
46
|
+
| Procesamiento completo | Trabajo de extremo a extremo | ✅ Funcional |
|
|
47
|
+
|
|
48
|
+
### Validación de Parámetros
|
|
49
|
+
- Extracción correcta de parámetros de rutas (ej: `:id`, `:status`)
|
|
50
|
+
- Valores de parámetros correctamente pasados a controladores
|
|
51
|
+
- Respuestas coherentes según valores de parámetros
|
|
52
|
+
|
|
53
|
+
## Cobertura de Pruebas
|
|
54
|
+
|
|
55
|
+
### Modo Directorio (Principal)
|
|
56
|
+
- ✅ Carga de rutas desde múltiples archivos JSON
|
|
57
|
+
- ✅ Registro correcto de todas las rutas
|
|
58
|
+
- ✅ Prioridad correcta de rutas parametrizadas
|
|
59
|
+
- ✅ Funcionamiento de rutas estáticas y dinámicas
|
|
60
|
+
- ✅ Sistema de colas de Qwen operativo
|
|
61
|
+
|
|
62
|
+
### Modo Archivo Único (Regresión)
|
|
63
|
+
- ✅ Continuidad del funcionamiento previo
|
|
64
|
+
- ✅ No regresiones introducidas
|
|
65
|
+
- ✅ Compatibilidad hacia atrás mantenida
|
|
66
|
+
|
|
67
|
+
## Métricas de Desempeño
|
|
68
|
+
|
|
69
|
+
- **Tiempo de respuesta**: Dentro de rangos normales
|
|
70
|
+
- **Consumo de memoria**: Estable
|
|
71
|
+
- **Procesamiento de rutas**: Correcto en ambos modos
|
|
72
|
+
- **Sistema de hooks**: Operativo sin interrupciones
|
|
73
|
+
|
|
74
|
+
## Validación de Endpoints Registrados
|
|
75
|
+
|
|
76
|
+
Se verificó que los 19 endpoints se registran correctamente en modo directorio, incluyendo:
|
|
77
|
+
- 4 rutas parametrizadas funcionales
|
|
78
|
+
- 10 rutas estáticas funcionales
|
|
79
|
+
- 5 rutas estáticas funcionales
|
|
80
|
+
|
|
81
|
+
## Conclusión
|
|
82
|
+
|
|
83
|
+
### ✅ ACEPTACIÓN TOTAL
|
|
84
|
+
|
|
85
|
+
El fix implementado ha resuelto completamente el problema reportado. El sistema BlackCoffee ahora:
|
|
86
|
+
|
|
87
|
+
1. **Opera correctamente en modo directorio** con todas las rutas parametrizadas funcionando
|
|
88
|
+
2. **Mantiene compatibilidad** con el modo archivo único
|
|
89
|
+
3. **Soporta completamente** el sistema de colas de Qwen
|
|
90
|
+
4. **Sigue principios de arquitectura limpia** con separación de responsabilidades
|
|
91
|
+
5. **No introduce regresiones** en funcionalidades existentes
|
|
92
|
+
|
|
93
|
+
**Recomendación**: Aprobar el despliegue del fix a producción.
|