jerkjs 2.2.0 → 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.
@@ -0,0 +1,518 @@
1
+ # Manual de Creación de Middleware para API SDK Framework
2
+
3
+ ## Tabla de Contenidos
4
+
5
+ 1. [Introducción al Middleware](#introducción-al-middleware)
6
+ 2. [Patrones de Middleware](#patrones-de-middleware)
7
+ 3. [Guía de Implementación](#guía-de-implementación)
8
+ 4. [Ejemplo Completo: Middleware de Auditoría](#ejemplo-completo-middleware-de-auditoría)
9
+ 5. [Pruebas y Validación](#pruebas-y-validación)
10
+ 6. [Integración con el Framework](#integración-con-el-framework)
11
+
12
+ ## Introducción al Middleware
13
+
14
+ El middleware en el API SDK Framework es una función que se ejecuta en el pipeline de procesamiento de solicitudes HTTP. Actúa como intermediario entre la solicitud entrante y el handler final, permitiendo modificar la solicitud, la respuesta, o incluso detener el flujo de ejecución.
15
+
16
+ ### Características del Middleware
17
+
18
+ - **Secuencial**: Se ejecutan en el orden en que se registran
19
+ - **Interceptable**: Pueden modificar `req` y `res` antes de pasar al siguiente
20
+ - **Terminable**: Pueden enviar respuesta y detener el flujo
21
+ - **Flexible**: Pueden aplicarse globalmente o a rutas específicas
22
+
23
+ ### Firma del Middleware
24
+
25
+ ```javascript
26
+ function middleware(req, res, next) {
27
+ // Lógica del middleware
28
+ next(); // Continuar con el siguiente middleware
29
+ }
30
+ ```
31
+
32
+ ## Patrones de Middleware
33
+
34
+ ### 1. Middleware de Logging
35
+
36
+ Registra información sobre las solicitudes entrantes.
37
+
38
+ ### 2. Middleware de Autenticación
39
+
40
+ Verifica credenciales antes de permitir el acceso.
41
+
42
+ ### 3. Middleware de Validación
43
+
44
+ Valida datos de entrada antes de procesarlos.
45
+
46
+ ### 4. Middleware de Seguridad
47
+
48
+ Aplica medidas de seguridad como CORS, Rate Limiting, etc.
49
+
50
+ ### 5. Middleware de Transformación
51
+
52
+ Modifica la solicitud o respuesta antes de procesarla.
53
+
54
+ ## Guía de Implementación
55
+
56
+ ### Paso 1: Definir el Propósito
57
+
58
+ Antes de crear cualquier middleware, define claramente:
59
+
60
+ 1. **¿Qué problema resolverá?**
61
+ 2. **¿En qué punto del pipeline se ejecutará?**
62
+ 3. **¿Qué datos necesita procesar?**
63
+ 4. **¿Qué efectos secundarios tendrá?**
64
+
65
+ ### Paso 2: Implementar la Función Base
66
+
67
+ ```javascript
68
+ function miMiddleware(opciones = {}) {
69
+ return (req, res, next) => {
70
+ // Lógica del middleware
71
+ next();
72
+ };
73
+ }
74
+ ```
75
+
76
+ ### Paso 3: Manejar Casos Especiales
77
+
78
+ Considera casos como:
79
+ - Solicitudes que deben ser rechazadas
80
+ - Solicitudes que requieren transformación
81
+ - Solicitudes que deben ser registradas
82
+ - Errores durante el procesamiento
83
+
84
+ ### Paso 4: Asegurar la Seguridad
85
+
86
+ - Validar entradas
87
+ - Sanitizar datos
88
+ - Aplicar límites de seguridad
89
+ - Registrar actividades sospechosas
90
+
91
+ ## Ejemplo Completo: Middleware de Auditoría
92
+
93
+ Vamos a crear un middleware de auditoría que registre todas las actividades de los usuarios:
94
+
95
+ ```javascript
96
+ // lib/middleware/auditLogger.js
97
+ const fs = require('fs');
98
+ const path = require('path');
99
+
100
+ class AuditLogger {
101
+ /**
102
+ * Constructor del middleware de auditoría
103
+ * @param {Object} options - Opciones de configuración
104
+ * @param {string} options.logFile - Ruta al archivo de logs
105
+ * @param {Array} options.events - Eventos a auditar
106
+ * @param {Function} options.filter - Función para filtrar solicitudes
107
+ * @param {boolean} options.includeBody - Incluir cuerpo de la solicitud
108
+ * @param {boolean} options.includeHeaders - Incluir headers
109
+ */
110
+ constructor(options = {}) {
111
+ this.logFile = options.logFile || './audit.log';
112
+ this.events = options.events || ['request', 'response', 'error'];
113
+ this.filter = options.filter || (() => true);
114
+ this.includeBody = options.includeBody !== false;
115
+ this.includeHeaders = options.includeHeaders !== false;
116
+ this.logger = options.logger || console;
117
+ }
118
+
119
+ /**
120
+ * Middleware de auditoría
121
+ * @returns {Function} - Middleware de auditoría
122
+ */
123
+ middleware() {
124
+ return (req, res, next) => {
125
+ // Verificar si la solicitud debe ser auditada
126
+ if (!this.filter(req)) {
127
+ next();
128
+ return;
129
+ }
130
+
131
+ const startTime = Date.now();
132
+ const requestId = this.generateRequestId();
133
+
134
+ // Registrar la solicitud entrante
135
+ this.logRequest(req, requestId);
136
+
137
+ // Capturar la respuesta original para registrarla
138
+ const originalEnd = res.end;
139
+ res.end = (chunk, encoding) => {
140
+ const duration = Date.now() - startTime;
141
+
142
+ // Registrar la respuesta
143
+ this.logResponse(req, res, chunk, duration, requestId);
144
+
145
+ // Llamar al método original
146
+ originalEnd.call(res, chunk, encoding);
147
+ };
148
+
149
+ // Capturar errores para auditarlos
150
+ const originalOnError = req.connection && req.connection.onerror;
151
+ req.connection.onerror = (err) => {
152
+ this.logError(req, err, requestId);
153
+ if (originalOnError) originalOnError.call(req.connection, err);
154
+ };
155
+
156
+ next();
157
+ };
158
+ }
159
+
160
+ /**
161
+ * Genera un ID único para la solicitud
162
+ * @returns {string} - ID único de solicitud
163
+ */
164
+ generateRequestId() {
165
+ return Date.now().toString(36) + Math.random().toString(36).substr(2);
166
+ }
167
+
168
+ /**
169
+ * Registra la solicitud entrante
170
+ * @param {Object} req - Objeto de solicitud
171
+ * @param {string} requestId - ID de la solicitud
172
+ */
173
+ logRequest(req, requestId) {
174
+ if (!this.events.includes('request')) return;
175
+
176
+ const logEntry = {
177
+ timestamp: new Date().toISOString(),
178
+ event: 'request',
179
+ requestId,
180
+ method: req.method,
181
+ url: req.url,
182
+ ip: this.getClientIP(req),
183
+ userAgent: req.headers['user-agent'],
184
+ headers: this.includeHeaders ? req.headers : undefined,
185
+ body: this.includeBody ? req.body : undefined
186
+ };
187
+
188
+ this.writeLog(logEntry);
189
+ }
190
+
191
+ /**
192
+ * Registra la respuesta saliente
193
+ * @param {Object} req - Objeto de solicitud
194
+ * @param {Object} res - Objeto de respuesta
195
+ * @param {any} chunk - Cuerpo de la respuesta
196
+ * @param {number} duration - Duración de la solicitud
197
+ * @param {string} requestId - ID de la solicitud
198
+ */
199
+ logResponse(req, res, chunk, duration, requestId) {
200
+ if (!this.events.includes('response')) return;
201
+
202
+ const logEntry = {
203
+ timestamp: new Date().toISOString(),
204
+ event: 'response',
205
+ requestId,
206
+ method: req.method,
207
+ url: req.url,
208
+ statusCode: res.statusCode,
209
+ duration,
210
+ ip: this.getClientIP(req),
211
+ responseSize: chunk ? Buffer.byteLength(chunk) : 0
212
+ };
213
+
214
+ this.writeLog(logEntry);
215
+ }
216
+
217
+ /**
218
+ * Registra un error
219
+ * @param {Object} req - Objeto de solicitud
220
+ * @param {Error} error - Error ocurrido
221
+ * @param {string} requestId - ID de la solicitud
222
+ */
223
+ logError(req, error, requestId) {
224
+ if (!this.events.includes('error')) return;
225
+
226
+ const logEntry = {
227
+ timestamp: new Date().toISOString(),
228
+ event: 'error',
229
+ requestId,
230
+ method: req.method,
231
+ url: req.url,
232
+ ip: this.getClientIP(req),
233
+ errorMessage: error.message,
234
+ stack: error.stack
235
+ };
236
+
237
+ this.writeLog(logEntry);
238
+ }
239
+
240
+ /**
241
+ * Obtiene la IP del cliente
242
+ * @param {Object} req - Objeto de solicitud
243
+ * @returns {string} - IP del cliente
244
+ */
245
+ getClientIP(req) {
246
+ return req.headers['x-forwarded-for'] ||
247
+ req.connection.remoteAddress ||
248
+ req.socket.remoteAddress ||
249
+ (req.connection.socket ? req.connection.socket.remoteAddress : null);
250
+ }
251
+
252
+ /**
253
+ * Escribe una entrada de log
254
+ * @param {Object} entry - Entrada de log
255
+ */
256
+ writeLog(entry) {
257
+ const logLine = JSON.stringify(entry) + '\n';
258
+
259
+ // Escribir al archivo de log
260
+ fs.appendFileSync(this.logFile, logLine);
261
+
262
+ // También escribir al logger si está disponible
263
+ if (this.logger) {
264
+ this.logger.info(`AUDIT: ${entry.event} - ${entry.method} ${entry.url} - ${entry.ip}`);
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Limpia logs antiguos
270
+ * @param {number} days - Días a mantener
271
+ */
272
+ cleanupOldLogs(days = 30) {
273
+ // Esta es una implementación básica
274
+ // En una implementación real, usarías librerías como 'winston' con transporte de archivos
275
+ console.log(`Limpiando logs anteriores a ${days} días...`);
276
+ }
277
+ }
278
+
279
+ module.exports = AuditLogger;
280
+ ```
281
+
282
+ ### Uso del Middleware de Auditoría
283
+
284
+ ```javascript
285
+ // examples/v2/audit_middleware_example.js
286
+ const { APISDK, Logger } = require('../../index');
287
+ const AuditLogger = require('../../lib/middleware/auditLogger');
288
+
289
+ // Crear instancia del logger
290
+ const logger = new Logger({ level: 'info', timestamp: true });
291
+
292
+ logger.info('🔐 Iniciando ejemplo con middleware de auditoría');
293
+
294
+ // Crear instancia del servidor
295
+ const server = new APISDK({
296
+ port: 8084,
297
+ host: 'localhost'
298
+ });
299
+
300
+ // Crear instancia del middleware de auditoría
301
+ const auditLogger = new AuditLogger({
302
+ logFile: './audit.log',
303
+ events: ['request', 'response', 'error'],
304
+ includeBody: true,
305
+ includeHeaders: false,
306
+ filter: (req) => {
307
+ // No auditar solicitudes a /health
308
+ return req.url !== '/health';
309
+ }
310
+ });
311
+
312
+ // Aplicar middleware de auditoría
313
+ server.use(auditLogger.middleware());
314
+
315
+ // Middleware de logging normal
316
+ server.use((req, res, next) => {
317
+ logger.info(`${req.method} ${req.url} - IP: ${req.connection.remoteAddress}`);
318
+ next();
319
+ });
320
+
321
+ // Rutas de ejemplo
322
+ server.addRoute('GET', '/', (req, res) => {
323
+ res.writeHead(200, { 'Content-Type': 'application/json' });
324
+ res.end(JSON.stringify({
325
+ message: 'API con middleware de auditoría',
326
+ timestamp: new Date().toISOString()
327
+ }));
328
+ });
329
+
330
+ server.addRoute('POST', '/api/users', (req, res) => {
331
+ res.writeHead(201, { 'Content-Type': 'application/json' });
332
+ res.end(JSON.stringify({
333
+ success: true,
334
+ message: 'Usuario creado',
335
+ data: req.body
336
+ }));
337
+ });
338
+
339
+ server.addRoute('GET', '/health', (req, res) => {
340
+ res.writeHead(200, { 'Content-Type': 'application/json' });
341
+ res.end(JSON.stringify({ status: 'healthy' }));
342
+ });
343
+
344
+ logger.info('✅ Rutas configuradas con middleware de auditoría');
345
+
346
+ // Iniciar el servidor
347
+ const httpServer = server.start();
348
+
349
+ logger.info('✅ Servidor iniciado en http://localhost:8084');
350
+ logger.info('📋 Endpoints disponibles:');
351
+ logger.info(' GET / - Página principal (auditable)');
352
+ logger.info(' POST /api/users - Crear usuario (auditable)');
353
+ logger.info(' GET /health - Salud del sistema (no auditado)');
354
+
355
+ logger.info('\n🔧 Comandos de prueba con curl:');
356
+ logger.info(' # Probar endpoint principal (será auditado):');
357
+ logger.info(' curl http://localhost:8084/');
358
+ logger.info('');
359
+ logger.info(' # Crear usuario (será auditado):');
360
+ logger.info(' curl -X POST http://localhost:8084/api/users \\');
361
+ logger.info(' -H "Content-Type: application/json" \\');
362
+ logger.info(' -d \'{"name":"Test User", "email":"test@example.com"}\'');
363
+ logger.info('');
364
+ logger.info(' # Verificar salud (no será auditado):');
365
+ logger.info(' curl http://localhost:8084/health');
366
+
367
+ logger.info('\n📊 Los eventos se registrarán en ./audit.log');
368
+
369
+ // Manejo de cierre
370
+ const gracefulShutdown = () => {
371
+ logger.info('🛑 Cerrando servidor...');
372
+ httpServer.close(() => {
373
+ logger.info('🔌 Servidor detenido');
374
+ process.exit(0);
375
+ });
376
+ };
377
+
378
+ process.on('SIGTERM', gracefulShutdown);
379
+ process.on('SIGINT', gracefulShutdown);
380
+ ```
381
+
382
+ ## Pruebas y Validación
383
+
384
+ ### Prueba del Middleware de Auditoría
385
+
386
+ ```javascript
387
+ // test_audit_middleware.js
388
+ const { APISDK } = require('../index');
389
+ const AuditLogger = require('../lib/middleware/auditLogger');
390
+ const fs = require('fs');
391
+ const path = require('path');
392
+
393
+ async function testAuditMiddleware() {
394
+ console.log('🧪 Probando middleware de auditoría...\n');
395
+
396
+ // Crear servidor de prueba
397
+ const server = new APISDK({ port: 9998 });
398
+
399
+ // Crear middleware de auditoría para pruebas
400
+ const auditLogger = new AuditLogger({
401
+ logFile: './test_audit.log',
402
+ events: ['request', 'response'],
403
+ includeBody: true
404
+ });
405
+
406
+ // Aplicar middleware
407
+ server.use(auditLogger.middleware());
408
+
409
+ // Agregar ruta de prueba
410
+ server.addRoute('GET', '/test', (req, res) => {
411
+ res.writeHead(200, { 'Content-Type': 'application/json' });
412
+ res.end(JSON.stringify({ message: 'OK' }));
413
+ });
414
+
415
+ server.addRoute('POST', '/test-post', (req, res) => {
416
+ res.writeHead(200, { 'Content-Type': 'application/json' });
417
+ res.end(JSON.stringify({ received: req.body }));
418
+ });
419
+
420
+ console.log('✅ Middleware de auditoría aplicado');
421
+ console.log('✅ Rutas de prueba configuradas');
422
+
423
+ // Iniciar servidor (en una implementación real, aquí haríamos solicitudes de prueba)
424
+ const httpServer = server.start();
425
+
426
+ console.log('✅ Servidor de prueba iniciado');
427
+ console.log('✅ El middleware de auditoría está funcionando');
428
+ console.log('✅ Las solicitudes se registrarán en test_audit.log');
429
+
430
+ // Detener servidor después de un tiempo
431
+ setTimeout(() => {
432
+ httpServer.close();
433
+ console.log('\n✅ Prueba completada');
434
+ console.log('📊 Revisa el archivo test_audit.log para ver los registros');
435
+ }, 2000);
436
+ }
437
+
438
+ // Ejecutar prueba
439
+ testAuditMiddleware().catch(console.error);
440
+ ```
441
+
442
+ ## Integración con el Framework
443
+
444
+ ### 1. Registro en el Punto de Entrada
445
+
446
+ ```javascript
447
+ // Actualizar index.js para exportar el nuevo middleware
448
+ const AuditLogger = require('./lib/middleware/auditLogger');
449
+
450
+ module.exports = {
451
+ // ... otros componentes ...
452
+ AuditLogger // Exportar el nuevo middleware
453
+ };
454
+ ```
455
+
456
+ ### 2. Ejemplos de Uso en Aplicaciones Reales
457
+
458
+ ```javascript
459
+ // Ejemplo de uso en una aplicación real
460
+ const { APISDK, AuditLogger } = require('apisdk');
461
+
462
+ const server = new APISDK({ port: 3000 });
463
+
464
+ // Middleware de auditoría para eventos de seguridad
465
+ const securityAudit = new AuditLogger({
466
+ logFile: './security_audit.log',
467
+ events: ['request', 'error'],
468
+ filter: (req) => {
469
+ // Solo auditar endpoints sensibles
470
+ return req.url.startsWith('/api/admin') || req.url.startsWith('/api/users');
471
+ }
472
+ });
473
+
474
+ server.use(securityAudit.middleware());
475
+
476
+ // Middleware de auditoría general
477
+ const generalAudit = new AuditLogger({
478
+ logFile: './general_audit.log',
479
+ events: ['request', 'response']
480
+ });
481
+
482
+ server.use(generalAudit.middleware());
483
+ ```
484
+
485
+ ## Buenas Prácticas para Middleware
486
+
487
+ ### 1. Rendimiento
488
+ - Minimizar operaciones costosas
489
+ - Usar caché cuando sea posible
490
+ - Evitar operaciones de bloqueo
491
+
492
+ ### 2. Seguridad
493
+ - Validar entradas
494
+ - Sanitizar datos
495
+ - Aplicar límites de seguridad
496
+ - Registrar actividades sospechosas
497
+
498
+ ### 3. Observabilidad
499
+ - Registrar adecuadamente
500
+ - Medir tiempos de ejecución
501
+ - Detectar anomalías
502
+
503
+ ### 4. Mantenibilidad
504
+ - Código limpio y bien documentado
505
+ - Opciones configurables
506
+ - Manejo adecuado de errores
507
+
508
+ ## Conclusión
509
+
510
+ El sistema de middleware del API SDK Framework es potente y flexible, permitiendo extender la funcionalidad del servidor de múltiples maneras. El ejemplo del middleware de auditoría demuestra cómo crear middleware complejo que puede:
511
+
512
+ - Registrar eventos de solicitud, respuesta y error
513
+ - Filtrar solicitudes según criterios personalizados
514
+ - Incluir o excluir información sensible
515
+ - Integrarse completamente con el sistema de logging del framework
516
+ - Ser configurado y reutilizado en diferentes aplicaciones
517
+
518
+ Este patrón puede aplicarse para crear cualquier tipo de middleware que necesites: autenticación, autorización, validación, transformación de datos, logging, métricas, etc.