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
|
@@ -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
|
|
|
@@ -64,6 +64,39 @@ class Compressor {
|
|
|
64
64
|
// Array para almacenar los chunks de la respuesta
|
|
65
65
|
const responseChunks = [];
|
|
66
66
|
|
|
67
|
+
// Variables para almacenar información del encabezado
|
|
68
|
+
let statusCode = 200;
|
|
69
|
+
let responseHeaders = {};
|
|
70
|
+
|
|
71
|
+
// Sobrescribir res.writeHead para capturar los encabezados
|
|
72
|
+
res.writeHead = (code, headers) => {
|
|
73
|
+
statusCode = code;
|
|
74
|
+
if (typeof headers === 'object' && headers !== null) {
|
|
75
|
+
responseHeaders = { ...headers };
|
|
76
|
+
} else if (typeof headers === 'string') {
|
|
77
|
+
// Si headers es una string, intentar parsearla
|
|
78
|
+
try {
|
|
79
|
+
// Esto es un caso especial, normalmente headers debería ser un objeto
|
|
80
|
+
responseHeaders = { 'Content-Type': headers };
|
|
81
|
+
} catch (e) {
|
|
82
|
+
responseHeaders = {};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Permitir a los hooks modificar los encabezados antes de escribirlos
|
|
87
|
+
if (this.hooks && this.hooks.applyFilters) {
|
|
88
|
+
responseHeaders = this.hooks.applyFilters('compressor_response_headers', responseHeaders, req, res);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// NO LLAMAR A ORIGINAL WRITEHEAD AÚN - ESPERAR A VER SI NECESITAMOS COMPRIMIR
|
|
92
|
+
// Marcar que writeHead ha sido llamado pero diferir el envío real
|
|
93
|
+
res.__hasCalledWriteHead = true;
|
|
94
|
+
res.__pendingStatusCode = code;
|
|
95
|
+
res.__pendingHeaders = { ...headers };
|
|
96
|
+
|
|
97
|
+
return res;
|
|
98
|
+
};
|
|
99
|
+
|
|
67
100
|
// Sobrescribir res.write para capturar los chunks
|
|
68
101
|
res.write = (chunk, encoding) => {
|
|
69
102
|
// Permitir a los hooks interceptar los chunks antes de almacenarlos
|
|
@@ -101,7 +134,7 @@ class Compressor {
|
|
|
101
134
|
const shouldCompress = this.hooks.applyFilters('compressor_should_compress', false, responseBody, req, res, this.threshold);
|
|
102
135
|
if (shouldCompress) {
|
|
103
136
|
// Si un hook determina que debería comprimirse, proseguir con la compresión
|
|
104
|
-
this._performCompression(req, res, originalEnd, originalWriteHead, responseBody, compressionMethod);
|
|
137
|
+
this._performCompression(req, res, originalEnd, originalWriteHead, responseBody, compressionMethod, statusCode, responseHeaders);
|
|
105
138
|
return;
|
|
106
139
|
}
|
|
107
140
|
}
|
|
@@ -113,8 +146,16 @@ class Compressor {
|
|
|
113
146
|
|
|
114
147
|
// Solo establecer encabezados si no se han enviado aún
|
|
115
148
|
if (!res.headersSent) {
|
|
116
|
-
|
|
117
|
-
|
|
149
|
+
// Restaurar encabezados originales y asegurar que no haya encabezado de codificación
|
|
150
|
+
const headersToSend = { ...responseHeaders };
|
|
151
|
+
delete headersToSend['content-encoding']; // Asegurar que no haya encabezado de codificación
|
|
152
|
+
|
|
153
|
+
// Si writeHead fue llamado previamente, usar los encabezados pendientes
|
|
154
|
+
if (res.__hasCalledWriteHead && res.__pendingHeaders) {
|
|
155
|
+
originalWriteHead.call(res, res.__pendingStatusCode || statusCode, headersToSend);
|
|
156
|
+
} else {
|
|
157
|
+
originalWriteHead.call(res, statusCode, headersToSend);
|
|
158
|
+
}
|
|
118
159
|
}
|
|
119
160
|
originalEnd.call(res, responseBody, encoding);
|
|
120
161
|
return;
|
|
@@ -169,17 +210,24 @@ class Compressor {
|
|
|
169
210
|
|
|
170
211
|
// Solo establecer encabezados si no se han enviado aún
|
|
171
212
|
if (!res.headersSent) {
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
213
|
+
// Combinar encabezados originales con el encabezado de compresión
|
|
214
|
+
const headersToSend = { ...responseHeaders };
|
|
215
|
+
headersToSend['Content-Encoding'] = compressionMethod;
|
|
216
|
+
delete headersToSend['content-length']; // Eliminar Content-Length original
|
|
175
217
|
|
|
176
218
|
// Permitir a los hooks modificar los encabezados
|
|
177
219
|
if (this.hooks && this.hooks.doAction) {
|
|
178
220
|
this.hooks.doAction('compressor_before_headers_sent', req, res, compressionMethod);
|
|
179
221
|
}
|
|
180
222
|
|
|
181
|
-
//
|
|
182
|
-
|
|
223
|
+
// Si writeHead fue llamado previamente, usar los encabezados pendientes
|
|
224
|
+
if (res.__hasCalledWriteHead && res.__pendingHeaders) {
|
|
225
|
+
// Combinar los encabezados pendientes con el encabezado de compresión
|
|
226
|
+
const combinedHeaders = { ...res.__pendingHeaders, ...headersToSend };
|
|
227
|
+
originalWriteHead.call(res, res.__pendingStatusCode || statusCode, combinedHeaders);
|
|
228
|
+
} else {
|
|
229
|
+
originalWriteHead.call(res, statusCode, headersToSend);
|
|
230
|
+
}
|
|
183
231
|
}
|
|
184
232
|
|
|
185
233
|
// Permitir a los hooks modificar el comportamiento antes de enviar la respuesta
|
|
@@ -204,8 +252,15 @@ class Compressor {
|
|
|
204
252
|
|
|
205
253
|
// Si ocurre un error, enviar sin comprimir
|
|
206
254
|
if (!res.headersSent) {
|
|
207
|
-
|
|
208
|
-
|
|
255
|
+
const headersToSend = { ...responseHeaders };
|
|
256
|
+
delete headersToSend['content-encoding'];
|
|
257
|
+
|
|
258
|
+
// Si writeHead fue llamado previamente, usar los encabezados pendientes
|
|
259
|
+
if (res.__hasCalledWriteHead && res.__pendingHeaders) {
|
|
260
|
+
originalWriteHead.call(res, res.__pendingStatusCode || statusCode, { ...res.__pendingHeaders, ...headersToSend });
|
|
261
|
+
} else {
|
|
262
|
+
originalWriteHead.call(res, statusCode, headersToSend);
|
|
263
|
+
}
|
|
209
264
|
}
|
|
210
265
|
originalEnd.call(res, responseBody, encoding);
|
|
211
266
|
});
|
|
@@ -390,7 +445,7 @@ class Compressor {
|
|
|
390
445
|
* Método auxiliar para realizar la compresión
|
|
391
446
|
* @private
|
|
392
447
|
*/
|
|
393
|
-
_performCompression(req, res, originalEnd, originalWriteHead, responseBody, compressionMethod) {
|
|
448
|
+
_performCompression(req, res, originalEnd, originalWriteHead, responseBody, compressionMethod, statusCode = 200, responseHeaders = {}) {
|
|
394
449
|
const zlib = require('zlib');
|
|
395
450
|
|
|
396
451
|
// Permitir a los hooks modificar el comportamiento cuando se va a comprimir
|
|
@@ -442,17 +497,24 @@ class Compressor {
|
|
|
442
497
|
|
|
443
498
|
// Solo establecer encabezados si no se han enviado aún
|
|
444
499
|
if (!res.headersSent) {
|
|
445
|
-
//
|
|
446
|
-
|
|
447
|
-
|
|
500
|
+
// Combinar encabezados originales con el encabezado de compresión
|
|
501
|
+
const headersToSend = { ...responseHeaders };
|
|
502
|
+
headersToSend['Content-Encoding'] = compressionMethod;
|
|
503
|
+
delete headersToSend['content-length']; // Eliminar Content-Length original
|
|
448
504
|
|
|
449
505
|
// Permitir a los hooks modificar los encabezados
|
|
450
506
|
if (this.hooks && this.hooks.doAction) {
|
|
451
507
|
this.hooks.doAction('compressor_before_headers_sent', req, res, compressionMethod);
|
|
452
508
|
}
|
|
453
509
|
|
|
454
|
-
//
|
|
455
|
-
|
|
510
|
+
// Si writeHead fue llamado previamente, usar los encabezados pendientes
|
|
511
|
+
if (res.__hasCalledWriteHead && res.__pendingHeaders) {
|
|
512
|
+
// Combinar los encabezados pendientes con el encabezado de compresión
|
|
513
|
+
const combinedHeaders = { ...res.__pendingHeaders, ...headersToSend };
|
|
514
|
+
originalWriteHead.call(res, res.__pendingStatusCode || statusCode, combinedHeaders);
|
|
515
|
+
} else {
|
|
516
|
+
originalWriteHead.call(res, statusCode, headersToSend);
|
|
517
|
+
}
|
|
456
518
|
}
|
|
457
519
|
|
|
458
520
|
// Permitir a los hooks modificar el comportamiento antes de enviar la respuesta
|
|
@@ -477,8 +539,15 @@ class Compressor {
|
|
|
477
539
|
|
|
478
540
|
// Si ocurre un error, enviar sin comprimir
|
|
479
541
|
if (!res.headersSent) {
|
|
480
|
-
|
|
481
|
-
|
|
542
|
+
const headersToSend = { ...responseHeaders };
|
|
543
|
+
delete headersToSend['content-encoding'];
|
|
544
|
+
|
|
545
|
+
// Si writeHead fue llamado previamente, usar los encabezados pendientes
|
|
546
|
+
if (res.__hasCalledWriteHead && res.__pendingHeaders) {
|
|
547
|
+
originalWriteHead.call(res, res.__pendingStatusCode || statusCode, { ...res.__pendingHeaders, ...headersToSend });
|
|
548
|
+
} else {
|
|
549
|
+
originalWriteHead.call(res, statusCode, headersToSend);
|
|
550
|
+
}
|
|
482
551
|
}
|
|
483
552
|
originalEnd.call(res, responseBody);
|
|
484
553
|
});
|
|
@@ -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;
|