jerkjs 2.5.2 → 2.5.6
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/BENCHMARK_RESULTS.md +60 -0
- package/CHANGELOG.md +87 -137
- package/README.md +108 -454
- package/README_LEGACY.md +513 -0
- package/auditoria_jerkjs.md +91 -0
- package/doc-2.5/SESSION_SECURITY_FLAGS.md +174 -0
- package/doc-2.5/an/303/241lisis-completo-jerk-framework.md +213 -0
- package/doc-2.5/manual-mvc-completo.md +934 -0
- package/docs/MANUAL_CONTROLLER_VIEW_MODEL.md +310 -0
- package/docs/SERVER_OPTIMIZATION_NOTES.md +87 -0
- package/jerk2.5.webp +0 -0
- package/lib/core/server.js +64 -53
- package/lib/middleware/session.js +11 -3
- package/lib/mvc/controllerBase.js +100 -32
- package/lib/router/RouteMatcher.js +45 -10
- package/package.json +13 -5
- package/example-directory-loader.js +0 -46
- package/examples/examples.arj +0 -0
- package/qa/informe_qa_fix_enrutamiento.md +0 -93
- package/utils/find_file_path.sh +0 -36
|
@@ -207,57 +207,125 @@ class ControllerBase {
|
|
|
207
207
|
* Carga un modelo para su uso en el controlador
|
|
208
208
|
* @param {string} modelName - Nombre del modelo a cargar
|
|
209
209
|
* @param {Object} options - Opciones para la creación del modelo
|
|
210
|
+
* @param {Object} options.adapter - Adaptador de base de datos opcional
|
|
211
|
+
* @param {string} options.modelDir - Directorio personalizado donde buscar el modelo
|
|
210
212
|
* @returns {ModelBase} - Instancia del modelo cargado
|
|
211
213
|
*/
|
|
212
214
|
loadModel(modelName, options = {}) {
|
|
213
215
|
try {
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
+
// Obtener la ruta del archivo que llama a este método para calcular rutas relativas correctamente
|
|
217
|
+
const callerDir = process.cwd();
|
|
216
218
|
let ModelClass;
|
|
217
219
|
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
`../models/${modelName}.js`, // Con extensión explícita
|
|
225
|
-
`../../models/${modelName}`, // En el directorio models del directorio abuelo
|
|
226
|
-
`../../models/${modelName}.js`, // Con extensión explícita
|
|
227
|
-
`../../../models/${modelName}`, // En el directorio models del directorio bisabuelo
|
|
228
|
-
`../../../models/${modelName}.js`, // Con extensión explícita
|
|
229
|
-
`./${modelName}`, // En el directorio actual sin subdirectorio
|
|
230
|
-
`./${modelName}.js` // En el directorio actual con extensión
|
|
231
|
-
];
|
|
232
|
-
|
|
233
|
-
let lastError = null;
|
|
234
|
-
|
|
235
|
-
for (const path of possiblePaths) {
|
|
220
|
+
// Si se especifica un directorio personalizado, usarlo como prioridad
|
|
221
|
+
if (options.modelDir) {
|
|
222
|
+
const customPath = `${options.modelDir}/${modelName}`;
|
|
223
|
+
const customPathWithJs = `${options.modelDir}/${modelName}.js`;
|
|
224
|
+
|
|
225
|
+
// Intentar cargar desde el directorio personalizado
|
|
236
226
|
try {
|
|
237
|
-
|
|
238
|
-
ModelClass = require(
|
|
239
|
-
// Si la importación fue exitosa, salir del bucle
|
|
240
|
-
break;
|
|
227
|
+
const fullPath = require.resolve(customPath, { paths: [callerDir] });
|
|
228
|
+
ModelClass = require(fullPath);
|
|
241
229
|
} catch (error) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
230
|
+
try {
|
|
231
|
+
const fullPath = require.resolve(customPathWithJs, { paths: [callerDir] });
|
|
232
|
+
ModelClass = require(fullPath);
|
|
233
|
+
} catch (error2) {
|
|
234
|
+
// Si falla en el directorio personalizado, continuar con rutas estándar
|
|
235
|
+
}
|
|
245
236
|
}
|
|
246
237
|
}
|
|
247
238
|
|
|
248
|
-
// Si no se encontró el modelo en
|
|
239
|
+
// Si no se encontró el modelo en el directorio personalizado, buscar en rutas estándar
|
|
249
240
|
if (!ModelClass) {
|
|
250
|
-
|
|
241
|
+
// Array de rutas posibles para buscar el modelo
|
|
242
|
+
// Usamos rutas relativas al directorio actual de trabajo
|
|
243
|
+
const possiblePaths = [
|
|
244
|
+
// Rutas relativas al directorio actual
|
|
245
|
+
`./models/${modelName}`, // En el subdirectorio models del directorio actual
|
|
246
|
+
`./models/${modelName}.js`, // Con extensión explícita
|
|
247
|
+
`./${modelName}`, // En el directorio actual sin subdirectorio
|
|
248
|
+
`./${modelName}.js`, // En el directorio actual con extensión
|
|
249
|
+
|
|
250
|
+
// Rutas relativas a directorios superiores
|
|
251
|
+
`../models/${modelName}`, // En el directorio models del directorio padre
|
|
252
|
+
`../models/${modelName}.js`, // Con extensión explícita
|
|
253
|
+
`../../models/${modelName}`, // En el directorio models del directorio abuelo
|
|
254
|
+
`../../models/${modelName}.js`, // Con extensión explícita
|
|
255
|
+
`../../../models/${modelName}`, // En el directorio models del directorio bisabuelo
|
|
256
|
+
`../../../models/${modelName}.js`, // Con extensión explícita
|
|
257
|
+
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
let lastError = null;
|
|
261
|
+
|
|
262
|
+
// Intentar cargar el modelo desde cada ruta posible
|
|
263
|
+
for (const relativePath of possiblePaths) {
|
|
264
|
+
try {
|
|
265
|
+
// Resolver la ruta absoluta
|
|
266
|
+
const fullPath = require.resolve(relativePath, { paths: [callerDir] });
|
|
267
|
+
ModelClass = require(fullPath);
|
|
268
|
+
|
|
269
|
+
// Si la importación fue exitosa, salir del bucle
|
|
270
|
+
break;
|
|
271
|
+
} catch (error) {
|
|
272
|
+
// Solo registrar el error si es diferente al anterior para evitar spam
|
|
273
|
+
if (!lastError || lastError.message !== error.message) {
|
|
274
|
+
lastError = error;
|
|
275
|
+
}
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Si no se encontró el modelo en ninguna ubicación
|
|
281
|
+
if (!ModelClass) {
|
|
282
|
+
throw new Error(`Modelo '${modelName}' no encontrado en ubicaciones estándar. Ultimo error: ${lastError?.message || 'desconocido'}`);
|
|
283
|
+
}
|
|
251
284
|
}
|
|
252
285
|
|
|
253
|
-
//
|
|
254
|
-
|
|
286
|
+
// Si ModelClass no es una función constructora, verificar si es un objeto con el modelo
|
|
287
|
+
if (typeof ModelClass !== 'function') {
|
|
288
|
+
if (ModelClass.default && typeof ModelClass.default === 'function') {
|
|
289
|
+
ModelClass = ModelClass.default; // Soporte para ES6 modules
|
|
290
|
+
} else {
|
|
291
|
+
// Buscar la clase en el objeto exportado
|
|
292
|
+
const exportedKeys = Object.keys(ModelClass);
|
|
293
|
+
for (const key of exportedKeys) {
|
|
294
|
+
if (typeof ModelClass[key] === 'function' && ModelClass[key].prototype) {
|
|
295
|
+
ModelClass = ModelClass[key];
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
255
301
|
|
|
256
|
-
//
|
|
302
|
+
// Verificar si ya existe una instancia del modelo en este controlador
|
|
257
303
|
if (!this.models) {
|
|
258
304
|
this.models = {};
|
|
259
305
|
}
|
|
260
306
|
|
|
307
|
+
// Si ya existe una instancia del modelo, devolverla y actualizar el adaptador si es necesario
|
|
308
|
+
if (this.models[modelName]) {
|
|
309
|
+
const existingModel = this.models[modelName];
|
|
310
|
+
|
|
311
|
+
// Si se proporciona un adaptador y el modelo no lo tiene, asignarlo
|
|
312
|
+
if (!existingModel.adapter && options.adapter) {
|
|
313
|
+
existingModel.setAdapter(options.adapter);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return existingModel;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Crear nueva instancia del modelo con las opciones proporcionadas
|
|
320
|
+
const modelOptions = { ...options };
|
|
321
|
+
const modelInstance = new ModelClass(modelOptions);
|
|
322
|
+
|
|
323
|
+
// Si el modelo no tiene un adaptador y se proporcionó uno en las opciones, asignarlo
|
|
324
|
+
if (!modelInstance.adapter && options.adapter) {
|
|
325
|
+
modelInstance.setAdapter(options.adapter);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Guardar referencia del modelo en el controlador
|
|
261
329
|
this.models[modelName] = modelInstance;
|
|
262
330
|
|
|
263
331
|
return modelInstance;
|
|
@@ -33,8 +33,23 @@ class RouteMatcher {
|
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
// Buscar rutas
|
|
37
|
-
// Esto
|
|
36
|
+
// Buscar rutas estáticas que coincidan exactamente antes que rutas parametrizadas
|
|
37
|
+
// Esto permite que rutas estáticas como /css/style.css tengan prioridad sobre rutas parametrizadas
|
|
38
|
+
for (const route of routes) {
|
|
39
|
+
if (route.method !== method) continue;
|
|
40
|
+
|
|
41
|
+
// Verificar si es una ruta estática que coincide exactamente
|
|
42
|
+
if (route.isStatic && route.path === pathname) {
|
|
43
|
+
return {
|
|
44
|
+
route: route,
|
|
45
|
+
params: {}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Buscar rutas parametrizadas
|
|
51
|
+
// Pero antes de devolver una ruta parametrizada, verificar si hay una ruta estática más específica
|
|
52
|
+
const parametrizedMatches = [];
|
|
38
53
|
for (const route of routes) {
|
|
39
54
|
if (route.method !== method) continue;
|
|
40
55
|
|
|
@@ -44,14 +59,15 @@ class RouteMatcher {
|
|
|
44
59
|
|
|
45
60
|
if (match) {
|
|
46
61
|
const params = this.extractParams(route.path, pathname);
|
|
47
|
-
|
|
62
|
+
parametrizedMatches.push({
|
|
48
63
|
route: route,
|
|
49
|
-
params
|
|
50
|
-
};
|
|
64
|
+
params: params
|
|
65
|
+
});
|
|
51
66
|
}
|
|
52
67
|
}
|
|
53
68
|
|
|
54
|
-
// Buscar rutas estáticas (prefijos)
|
|
69
|
+
// Buscar rutas estáticas (prefijos)
|
|
70
|
+
const staticMatches = [];
|
|
55
71
|
for (const route of routes) {
|
|
56
72
|
if (route.method !== method) continue;
|
|
57
73
|
|
|
@@ -60,14 +76,33 @@ class RouteMatcher {
|
|
|
60
76
|
// Verificar que sea exactamente el prefijo o que haya una barra después del prefijo
|
|
61
77
|
const remainingPath = pathname.substring(route.path.length);
|
|
62
78
|
if (route.path === '/' || remainingPath === '' || remainingPath.startsWith('/')) {
|
|
63
|
-
|
|
79
|
+
staticMatches.push({
|
|
64
80
|
route: route,
|
|
65
|
-
params: {}
|
|
66
|
-
|
|
81
|
+
params: {},
|
|
82
|
+
specificity: route.path.length // Agregar medida de especificidad basada en longitud del prefijo
|
|
83
|
+
});
|
|
67
84
|
}
|
|
68
85
|
}
|
|
69
86
|
}
|
|
70
87
|
|
|
88
|
+
// Prioridad: si hay rutas estáticas que coinciden, y la solicitud parece ser para un archivo
|
|
89
|
+
// (tiene extensión), dar prioridad a la ruta estática más específica
|
|
90
|
+
const hasFileExtension = /\.[^.]+$/.test(pathname);
|
|
91
|
+
|
|
92
|
+
if (hasFileExtension && staticMatches.length > 0) {
|
|
93
|
+
// Ordenar por especificidad (longitud del prefijo, descendente) y tomar la más específica
|
|
94
|
+
staticMatches.sort((a, b) => b.specificity - a.specificity);
|
|
95
|
+
// Para solicitudes de archivos, dar prioridad a rutas estáticas más específicas
|
|
96
|
+
return staticMatches[0]; // Devolver la coincidencia estática más específica
|
|
97
|
+
} else if (parametrizedMatches.length > 0) {
|
|
98
|
+
// Para solicitudes sin extensión o rutas API, dar prioridad a rutas parametrizadas
|
|
99
|
+
return parametrizedMatches[0]; // Devolver la primera coincidencia parametrizada
|
|
100
|
+
} else if (staticMatches.length > 0) {
|
|
101
|
+
// Si no hay coincidencias parametrizadas, usar la estática más específica
|
|
102
|
+
staticMatches.sort((a, b) => b.specificity - a.specificity);
|
|
103
|
+
return staticMatches[0];
|
|
104
|
+
}
|
|
105
|
+
|
|
71
106
|
return null;
|
|
72
107
|
}
|
|
73
108
|
|
|
@@ -86,7 +121,7 @@ class RouteMatcher {
|
|
|
86
121
|
let escapedPath = '';
|
|
87
122
|
for (let i = 0; i < path.length; i++) {
|
|
88
123
|
const char = path[i];
|
|
89
|
-
if (char.match(/[.+?^${}()|[\]
|
|
124
|
+
if (char.match(/[.+?^${}()|[\]\\-]/) && !(i > 0 && path[i-1] === ':')) {
|
|
90
125
|
escapedPath += '\\' + char;
|
|
91
126
|
} else {
|
|
92
127
|
escapedPath += char;
|
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.6",
|
|
4
|
+
"description": "JERK Framework v2.5.6 - A comprehensive framework for building secure and scalable APIs with frontend support, sessions, template engine, integration with qbuilderjs, complete MVC architecture with models, enhanced route loading from directory, improved model loading system, and fixed routing issues with static and parametrized routes",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
@@ -39,11 +39,19 @@
|
|
|
39
39
|
},
|
|
40
40
|
"homepage": "https://jerk.page.gd/",
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"
|
|
43
|
-
"
|
|
42
|
+
"bcrypt": "^5.1.1",
|
|
43
|
+
"cacache": "^20.0.3",
|
|
44
44
|
"jsonwebtoken": "^9.0.3",
|
|
45
|
+
"make-fetch-happen": "^14.0.3",
|
|
45
46
|
"mariadb": "^3.4.5",
|
|
46
|
-
"
|
|
47
|
+
"qbuilderjs": "^1.0.1",
|
|
48
|
+
"sqlite3": "^5.0.2",
|
|
49
|
+
"tar": "^7.5.7"
|
|
50
|
+
},
|
|
51
|
+
"overrides": {
|
|
52
|
+
"tar": "^7.5.7",
|
|
53
|
+
"cacache": "^20.0.3",
|
|
54
|
+
"make-fetch-happen": "^14.0.3"
|
|
47
55
|
},
|
|
48
56
|
"engines": {
|
|
49
57
|
"node": ">=14.0.0"
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ejemplo de uso del componente RouteDirectoryLoader
|
|
3
|
-
* Este ejemplo demuestra cómo cargar rutas desde un directorio con múltiples archivos JSON
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { APIServer, RouteDirectoryLoader, hooks } = require('./index.js');
|
|
7
|
-
|
|
8
|
-
// Crear instancia del servidor
|
|
9
|
-
const server = new APIServer({
|
|
10
|
-
port: 3000,
|
|
11
|
-
host: 'localhost'
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
// Crear instancia del cargador de rutas desde directorio
|
|
15
|
-
const routeDirectoryLoader = new RouteDirectoryLoader();
|
|
16
|
-
|
|
17
|
-
// Registrar hooks para ver eventos
|
|
18
|
-
hooks.addAction('route_duplicate_detected', (data) => {
|
|
19
|
-
console.log('Evento: Ruta duplicada detectada');
|
|
20
|
-
console.log('Ruta sobreescrita:', data.overwrittenRoute);
|
|
21
|
-
console.log('Nueva ruta:', data.newRoute);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
hooks.addAction('route_loaded_from_directory', (data) => {
|
|
25
|
-
console.log('Evento: Ruta cargada desde directorio');
|
|
26
|
-
console.log('Ruta:', data.route.path, data.route.method);
|
|
27
|
-
console.log('Archivo:', data.sourceFile);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
// Directorio que contiene los archivos JSON de rutas
|
|
31
|
-
const routesDirectory = './routes';
|
|
32
|
-
|
|
33
|
-
// Cargar rutas desde el directorio
|
|
34
|
-
routeDirectoryLoader.loadRoutesFromDirectory(server, routesDirectory)
|
|
35
|
-
.then(routes => {
|
|
36
|
-
console.log(`${routes.length} rutas cargadas exitosamente desde el directorio`);
|
|
37
|
-
|
|
38
|
-
// Iniciar el servidor
|
|
39
|
-
server.start();
|
|
40
|
-
})
|
|
41
|
-
.catch(error => {
|
|
42
|
-
console.error('Error cargando rutas desde directorio:', error.message);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
// Opcional: Observar cambios en el directorio de rutas
|
|
46
|
-
// routeDirectoryLoader.watchRoutesDirectory(server, routesDirectory);
|
package/examples/examples.arj
DELETED
|
Binary file
|
|
@@ -1,93 +0,0 @@
|
|
|
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.
|
package/utils/find_file_path.sh
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# Script to find a file's location using 'which' and show its real path with 'ls -ln'
|
|
4
|
-
|
|
5
|
-
if [ $# -eq 0 ]; then
|
|
6
|
-
echo "Usage: $0 <filename>"
|
|
7
|
-
echo "Example: $0 python"
|
|
8
|
-
exit 1
|
|
9
|
-
fi
|
|
10
|
-
|
|
11
|
-
filename="$1"
|
|
12
|
-
|
|
13
|
-
# Find the file using which
|
|
14
|
-
echo "Finding location of '$filename' using 'which':"
|
|
15
|
-
which_result=$(which "$filename")
|
|
16
|
-
|
|
17
|
-
if [ $? -eq 0 ]; then
|
|
18
|
-
echo "Found: $which_result"
|
|
19
|
-
|
|
20
|
-
# Get the directory containing the file
|
|
21
|
-
file_dir=$(dirname "$which_result")
|
|
22
|
-
file_basename=$(basename "$which_result")
|
|
23
|
-
|
|
24
|
-
# Change to the directory and use ls -ln to show the real path
|
|
25
|
-
echo ""
|
|
26
|
-
echo "Using 'ls -ln' to show detailed info:"
|
|
27
|
-
(cd "$file_dir" && ls -ln "$file_basename")
|
|
28
|
-
|
|
29
|
-
# If the file is a symlink, show the real path using readlink
|
|
30
|
-
echo ""
|
|
31
|
-
echo "Real path (using readlink -f):"
|
|
32
|
-
readlink -f "$which_result"
|
|
33
|
-
else
|
|
34
|
-
echo "File '$filename' not found in PATH"
|
|
35
|
-
exit 1
|
|
36
|
-
fi
|