jerkjs 2.1.7 → 2.3.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 +56 -0
- package/README.md +204 -4
- package/README_EN.md +1 -1
- package/README_PT.md +1 -1
- package/docs/ARQUITECTURA_ROUTES.md +84 -38
- package/{JERK_FRAMEWORK_DOCUMENTATION.md → docs/JERK_FRAMEWORK_DOCUMENTATION.md} +28 -2
- package/docs/JERK_MODELOS_HOWTO.md +566 -0
- package/index.js +41 -5
- package/jerk-qbuilder/CHANGELOG.md +71 -0
- package/jerk-qbuilder/HOWTO.md +325 -0
- package/jerk-qbuilder/README.md +52 -0
- package/lib/core/server.js +328 -27
- package/lib/loader/routeLoader.js +148 -117
- 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 +77 -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/query/MariaDBAdapter.js +78 -0
- package/lib/query/consoleAdapter.js +184 -0
- package/lib/query/queryBuilder.js +953 -0
- package/lib/query/queryBuilderHooks.js +455 -0
- package/lib/query/queryBuilderMiddleware.js +332 -0
- package/lib/utils/mimeType.js +62 -0
- package/package.json +5 -3
- package/utils/find_file_path.sh +36 -0
- package/BUG_REPORTE_COMPRESION.txt +0 -72
- 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/{JERK_FRAMEWORK_DIAGRAM.txt → docs/JERK_FRAMEWORK_DIAGRAM.txt} +0 -0
- /package/{JERK_FRAMEWORK_DIAGRAM_MERMAID.mmd → docs/JERK_FRAMEWORK_DIAGRAM_MERMAID.mmd} +0 -0
|
@@ -83,12 +83,29 @@ class RouteLoader {
|
|
|
83
83
|
throw new Error(`La ruta en la posición ${i} no tiene propiedad 'method'`);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
// Verificar si es una ruta estática
|
|
87
|
+
if (route.static) {
|
|
88
|
+
if (typeof route.static !== 'object' || route.static === null) {
|
|
89
|
+
throw new Error(`La ruta en la posición ${i} tiene una propiedad 'static' inválida`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!route.static.dir) {
|
|
93
|
+
throw new Error(`La ruta en la posición ${i} tiene una configuración 'static' sin directorio 'dir'`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// El método debe ser GET para rutas estáticas
|
|
97
|
+
if (route.method.toUpperCase() !== 'GET') {
|
|
98
|
+
throw new Error(`Las rutas estáticas deben usar el método GET, no ${route.method}`);
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
// Validación normal para rutas dinámicas
|
|
102
|
+
if (!route.controller) {
|
|
103
|
+
throw new Error(`La ruta en la posición ${i} no tiene propiedad 'controller'`);
|
|
104
|
+
}
|
|
89
105
|
|
|
90
|
-
|
|
91
|
-
|
|
106
|
+
if (!route.handler) {
|
|
107
|
+
throw new Error(`La ruta en la posición ${i} no tiene propiedad 'handler'`);
|
|
108
|
+
}
|
|
92
109
|
}
|
|
93
110
|
|
|
94
111
|
// Validar que el content-type sea un string si está presente
|
|
@@ -104,134 +121,148 @@ class RouteLoader {
|
|
|
104
121
|
* @param {Object} route - Objeto de ruta a cargar
|
|
105
122
|
*/
|
|
106
123
|
async loadSingleRoute(server, route) {
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
// Verificar si es una ruta estática
|
|
125
|
+
if (route.static) {
|
|
126
|
+
// Para rutas estáticas, simplemente llamar a addRoute con la configuración estática
|
|
127
|
+
server.addRoute({
|
|
128
|
+
method: route.method,
|
|
129
|
+
path: route.path,
|
|
130
|
+
static: route.static,
|
|
131
|
+
contentType: route.contentType,
|
|
132
|
+
auth: route.auth,
|
|
133
|
+
authOptions: route.authOptions
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
// Lógica existente para rutas dinámicas
|
|
137
|
+
// Obtener el controlador
|
|
138
|
+
const controllerPath = path.resolve(process.cwd(), route.controller);
|
|
139
|
+
let controllerModule;
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
controllerModule = require(controllerPath);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
throw new Error(`No se pudo cargar el controlador: ${route.controller}. Error: ${error.message}`);
|
|
145
|
+
}
|
|
116
146
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
147
|
+
// Obtener el handler del controlador
|
|
148
|
+
const handler = controllerModule[route.handler];
|
|
149
|
+
if (typeof handler !== 'function') {
|
|
150
|
+
throw new Error(`El handler '${route.handler}' no es una función en el controlador: ${route.controller}`);
|
|
151
|
+
}
|
|
122
152
|
|
|
123
|
-
|
|
124
|
-
|
|
153
|
+
// Crear un handler que establezca el content-type si está especificado
|
|
154
|
+
let finalHandler = handler;
|
|
125
155
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
156
|
+
if (route.contentType) {
|
|
157
|
+
finalHandler = async (req, res) => {
|
|
158
|
+
// Establecer el content-type antes de ejecutar el handler original
|
|
159
|
+
res.setHeader('Content-Type', route.contentType);
|
|
130
160
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
161
|
+
// Si el handler es asíncrono, esperarlo
|
|
162
|
+
if (handler.constructor.name === 'AsyncFunction') {
|
|
163
|
+
await handler(req, res);
|
|
164
|
+
} else {
|
|
165
|
+
handler(req, res);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
}
|
|
139
169
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
170
|
+
// Aplicar autenticación si está especificada
|
|
171
|
+
if (route.auth && route.auth !== 'none') {
|
|
172
|
+
// Verificar si es autenticación de sesión
|
|
173
|
+
if (route.auth === 'session') {
|
|
174
|
+
// Verificar si el servidor tiene sessionManager
|
|
175
|
+
if (server.sessionManager) {
|
|
176
|
+
// Importar el middleware de autenticación de sesión
|
|
177
|
+
const { sessionAuth } = require('../middleware/session');
|
|
178
|
+
const authMiddleware = sessionAuth(server.sessionManager, route.authOptions || {});
|
|
179
|
+
|
|
180
|
+
// Crear un nuevo handler que ejecute la autenticación primero
|
|
181
|
+
const authenticatedHandler = async (req, res) => {
|
|
182
|
+
try {
|
|
183
|
+
// Ejecutar el middleware de autenticación y esperar a que se resuelva
|
|
184
|
+
await new Promise((resolve, reject) => {
|
|
185
|
+
const next = () => resolve();
|
|
186
|
+
const result = authMiddleware(req, res, next);
|
|
187
|
+
|
|
188
|
+
// Si authMiddleware devuelve una promesa, esperarla
|
|
189
|
+
if (result && typeof result.then === 'function') {
|
|
190
|
+
result.then(resolve).catch(reject);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Si la autenticación fue exitosa (no se envió respuesta aún), ejecutar el handler original
|
|
195
|
+
if (!res.headersSent) {
|
|
196
|
+
// Si el handler es asíncrono, esperarlo también
|
|
197
|
+
if (finalHandler.constructor.name === 'AsyncFunction') {
|
|
198
|
+
await finalHandler(req, res);
|
|
199
|
+
} else {
|
|
200
|
+
finalHandler(req, res);
|
|
201
|
+
}
|
|
161
202
|
}
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (finalHandler.constructor.name === 'AsyncFunction') {
|
|
168
|
-
await finalHandler(req, res);
|
|
169
|
-
} else {
|
|
170
|
-
finalHandler(req, res);
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.error('Error en el manejo de autenticación de sesión:', error);
|
|
205
|
+
if (!res.headersSent) {
|
|
206
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
207
|
+
res.end(JSON.stringify({ error: 'Error interno del servidor' }));
|
|
171
208
|
}
|
|
172
209
|
}
|
|
173
|
-
}
|
|
174
|
-
console.error('Error en el manejo de autenticación de sesión:', error);
|
|
175
|
-
if (!res.headersSent) {
|
|
176
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
177
|
-
res.end(JSON.stringify({ error: 'Error interno del servidor' }));
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
};
|
|
210
|
+
};
|
|
181
211
|
|
|
182
|
-
|
|
183
|
-
|
|
212
|
+
// Agregar la ruta con el handler autenticado
|
|
213
|
+
server.addRoute(route.method, route.path, authenticatedHandler);
|
|
214
|
+
} else {
|
|
215
|
+
// Si no hay sessionManager en el servidor, agregar la ruta normalmente
|
|
216
|
+
server.addRoute(route.method, route.path, finalHandler);
|
|
217
|
+
}
|
|
184
218
|
} else {
|
|
185
|
-
//
|
|
186
|
-
server.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
219
|
+
// Usar el authenticator del servidor si está disponible para otros tipos de autenticación
|
|
220
|
+
if (server.authenticator) {
|
|
221
|
+
const authMiddleware = server.authenticator.authenticate(route.auth, route.authOptions || {});
|
|
222
|
+
|
|
223
|
+
// Crear un nuevo handler que ejecute la autenticación primero
|
|
224
|
+
const authenticatedHandler = async (req, res) => {
|
|
225
|
+
try {
|
|
226
|
+
// Ejecutar el middleware de autenticación y esperar a que se resuelva
|
|
227
|
+
await new Promise((resolve, reject) => {
|
|
228
|
+
const next = () => resolve();
|
|
229
|
+
const result = authMiddleware(req, res, next);
|
|
230
|
+
|
|
231
|
+
// Si authMiddleware devuelve una promesa, esperarla
|
|
232
|
+
if (result && typeof result.then === 'function') {
|
|
233
|
+
result.then(resolve).catch(reject);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Si la autenticación fue exitosa (no se envió respuesta aún), ejecutar el handler original
|
|
238
|
+
if (!res.headersSent) {
|
|
239
|
+
// Si el handler es asíncrono, esperarlo también
|
|
240
|
+
if (finalHandler.constructor.name === 'AsyncFunction') {
|
|
241
|
+
await finalHandler(req, res);
|
|
242
|
+
} else {
|
|
243
|
+
finalHandler(req, res);
|
|
244
|
+
}
|
|
204
245
|
}
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
if (finalHandler.constructor.name === 'AsyncFunction') {
|
|
211
|
-
await finalHandler(req, res);
|
|
212
|
-
} else {
|
|
213
|
-
finalHandler(req, res);
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error('Error en el manejo de autenticación:', error);
|
|
248
|
+
if (!res.headersSent) {
|
|
249
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
250
|
+
res.end(JSON.stringify({ error: 'Error interno del servidor' }));
|
|
214
251
|
}
|
|
215
252
|
}
|
|
216
|
-
}
|
|
217
|
-
console.error('Error en el manejo de autenticación:', error);
|
|
218
|
-
if (!res.headersSent) {
|
|
219
|
-
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
220
|
-
res.end(JSON.stringify({ error: 'Error interno del servidor' }));
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
};
|
|
253
|
+
};
|
|
224
254
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
255
|
+
// Agregar la ruta con el handler autenticado
|
|
256
|
+
server.addRoute(route.method, route.path, authenticatedHandler);
|
|
257
|
+
} else {
|
|
258
|
+
// Si no hay authenticator en el servidor, agregar la ruta normalmente
|
|
259
|
+
server.addRoute(route.method, route.path, finalHandler);
|
|
260
|
+
}
|
|
230
261
|
}
|
|
262
|
+
} else {
|
|
263
|
+
// Si no hay autenticación requerida, agregar la ruta normalmente
|
|
264
|
+
server.addRoute(route.method, route.path, finalHandler);
|
|
231
265
|
}
|
|
232
|
-
} else {
|
|
233
|
-
// Si no hay autenticación requerida, agregar la ruta normalmente
|
|
234
|
-
server.addRoute(route.method, route.path, finalHandler);
|
|
235
266
|
}
|
|
236
267
|
}
|
|
237
268
|
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptador genérico para modelos en el framework JERK
|
|
3
|
+
* Implementación del componente MVC GenericAdapter.js
|
|
4
|
+
* Proporciona una interfaz común para diferentes tipos de almacenamiento
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class GenericAdapter {
|
|
8
|
+
/**
|
|
9
|
+
* Constructor del adaptador genérico
|
|
10
|
+
* @param {Object} options - Opciones de configuración
|
|
11
|
+
*/
|
|
12
|
+
constructor(options = {}) {
|
|
13
|
+
this.options = options;
|
|
14
|
+
this.type = options.type || 'generic';
|
|
15
|
+
this.logger = options.logger || console;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Método para crear un registro
|
|
20
|
+
* @param {string} tableName - Nombre de la tabla
|
|
21
|
+
* @param {Object} data - Datos a crear
|
|
22
|
+
* @returns {Promise<Object>} - Promesa con el resultado
|
|
23
|
+
*/
|
|
24
|
+
async create(tableName, data) {
|
|
25
|
+
throw new Error('Método create no implementado en el adaptador genérico');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Método para encontrar registros
|
|
30
|
+
* @param {string} tableName - Nombre de la tabla
|
|
31
|
+
* @param {Object} conditions - Condiciones de búsqueda
|
|
32
|
+
* @param {Object} options - Opciones adicionales
|
|
33
|
+
* @returns {Promise<Array>} - Promesa con los resultados
|
|
34
|
+
*/
|
|
35
|
+
async find(tableName, conditions, options) {
|
|
36
|
+
throw new Error('Método find no implementado en el adaptador genérico');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Método para encontrar un solo registro
|
|
41
|
+
* @param {string} tableName - Nombre de la tabla
|
|
42
|
+
* @param {Object} conditions - Condiciones de búsqueda
|
|
43
|
+
* @param {Object} options - Opciones adicionales
|
|
44
|
+
* @returns {Promise<Object|null>} - Promesa con el resultado o null
|
|
45
|
+
*/
|
|
46
|
+
async findOne(tableName, conditions, options) {
|
|
47
|
+
throw new Error('Método findOne no implementado en el adaptador genérico');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Método para actualizar registros
|
|
52
|
+
* @param {string} tableName - Nombre de la tabla
|
|
53
|
+
* @param {Object} conditions - Condiciones para seleccionar registros
|
|
54
|
+
* @param {Object} data - Datos a actualizar
|
|
55
|
+
* @returns {Promise<number>} - Promesa con el número de registros afectados
|
|
56
|
+
*/
|
|
57
|
+
async update(tableName, conditions, data) {
|
|
58
|
+
throw new Error('Método update no implementado en el adaptador genérico');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Método para eliminar registros
|
|
63
|
+
* @param {string} tableName - Nombre de la tabla
|
|
64
|
+
* @param {Object} conditions - Condiciones para seleccionar registros
|
|
65
|
+
* @returns {Promise<number>} - Promesa con el número de registros eliminados
|
|
66
|
+
*/
|
|
67
|
+
async delete(tableName, conditions) {
|
|
68
|
+
throw new Error('Método delete no implementado en el adaptador genérico');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Método para contar registros
|
|
73
|
+
* @param {string} tableName - Nombre de la tabla
|
|
74
|
+
* @param {Object} conditions - Condiciones de conteo
|
|
75
|
+
* @returns {Promise<number>} - Promesa con el número de registros
|
|
76
|
+
*/
|
|
77
|
+
async count(tableName, conditions) {
|
|
78
|
+
throw new Error('Método count no implementado en el adaptador genérico');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Método para ejecutar consultas personalizadas
|
|
83
|
+
* @param {string} query - Consulta personalizada
|
|
84
|
+
* @param {Array} params - Parámetros de la consulta
|
|
85
|
+
* @returns {Promise<any>} - Promesa con el resultado
|
|
86
|
+
*/
|
|
87
|
+
async query(query, params = []) {
|
|
88
|
+
throw new Error('Método query no implementado en el adaptador genérico');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Método para inicializar el adaptador
|
|
93
|
+
* @returns {Promise<void>}
|
|
94
|
+
*/
|
|
95
|
+
async initialize() {
|
|
96
|
+
// Implementación por defecto
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Método para cerrar la conexión
|
|
101
|
+
* @returns {Promise<void>}
|
|
102
|
+
*/
|
|
103
|
+
async close() {
|
|
104
|
+
// Implementación por defecto
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Método para verificar si el adaptador está conectado
|
|
109
|
+
* @returns {boolean} - Verdadero si está conectado
|
|
110
|
+
*/
|
|
111
|
+
isConnected() {
|
|
112
|
+
return true; // Por defecto, asumimos que está conectado
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Método para migrar una tabla
|
|
117
|
+
* @param {string} tableName - Nombre de la tabla a migrar
|
|
118
|
+
* @returns {Promise<void>}
|
|
119
|
+
*/
|
|
120
|
+
async migrate(tableName) {
|
|
121
|
+
throw new Error('Método migrate no implementado en el adaptador genérico');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Serializa el adaptador a JSON
|
|
126
|
+
* @returns {Object} - Representación JSON del adaptador
|
|
127
|
+
*/
|
|
128
|
+
toJSON() {
|
|
129
|
+
return {
|
|
130
|
+
type: this.type,
|
|
131
|
+
initialized: true
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = GenericAdapter;
|