kukuy 1.5.0 → 1.9.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.
Files changed (40) hide show
  1. package/README.md +159 -188
  2. package/balancer.log +30 -0
  3. package/certs/auto/certificate.crt +22 -0
  4. package/certs/auto/private.key +28 -0
  5. package/kukuy-plugins/README.md +125 -0
  6. package/kukuy-plugins/cache-plugin/index.js +477 -0
  7. package/kukuy-plugins/cache-plugin/manifest.json +17 -0
  8. package/kukuy-plugins/ejemplo-plugin/index.js +41 -0
  9. package/kukuy-plugins/ejemplo-plugin/manifest.json +11 -0
  10. package/kukuy-plugins/health-checker/index.js +168 -0
  11. package/kukuy-plugins/health-checker/manifest.json +16 -0
  12. package/kukuy-plugins/health-monitor/index.js +58 -0
  13. package/kukuy-plugins/health-monitor/manifest.json +16 -0
  14. package/kukuy-plugins/redirect-plugin/index.js +172 -0
  15. package/kukuy-plugins/redirect-plugin/manifest.json +15 -0
  16. package/package.json +7 -3
  17. package/servers_real.json +5 -0
  18. package/src/core/Balancer.js +176 -39
  19. package/src/core/ServerPool.js +2 -2
  20. package/src/extensibility/ExtendedFilterChain.js +90 -0
  21. package/src/extensibility/ExtendedHookManager.js +87 -0
  22. package/src/extensibility/FilterChain.js +2 -9
  23. package/src/extensibility/HookManager.js +1 -0
  24. package/src/extensibility/PostStartupExtension.js +97 -0
  25. package/src/plugins/PluginManager.js +231 -0
  26. package/src/utils/HealthChecker.js +61 -6
  27. package/.ctagsd/ctagsd.json +0 -954
  28. package/.ctagsd/file_list.txt +0 -100
  29. package/.ctagsd/tags.db +0 -0
  30. package/CHANGELOG.md +0 -125
  31. package/LICENSE +0 -680
  32. package/README-SSL.md +0 -165
  33. package/captura.png +0 -0
  34. package/kukuu1.webp +0 -0
  35. package/kukuy.workspace +0 -11
  36. package/optimize-mariadb.sh +0 -152
  37. package/restart-balancer.sh +0 -10
  38. package/scripts/load_test.py +0 -151
  39. package/stress-test.js +0 -190
  40. package/test_optimization.js +0 -54
@@ -0,0 +1,477 @@
1
+ const crypto = require('crypto');
2
+
3
+ // Almacenamiento de la caché en memoria
4
+ let cacheStorage = new Map();
5
+ let cacheMetadata = new Map(); // Almacenar metadatos como TTL, tamaño, etc.
6
+
7
+ // Configuración del plugin
8
+ let pluginConfig = {
9
+ maxCacheSize: 100, // Número máximo de entradas
10
+ defaultTTL: 300000, // Tiempo de vida por defecto (5 minutos en ms)
11
+ enableCompression: false,
12
+ cacheableMethods: ['GET'],
13
+ cacheableStatusCodes: [200, 201, 204]
14
+ };
15
+
16
+ // Variable para almacenar temporalmente las solicitudes que están siendo procesadas
17
+ let activeRequests = new Map();
18
+
19
+ /**
20
+ * Inicializa el plugin de cache
21
+ * @param {Object} balancer - Instancia del balanceador
22
+ */
23
+ async function init(balancer) {
24
+ console.log('Inicializando plugin de cache robusto...');
25
+
26
+ const extension = balancer.getPostStartupExtension();
27
+
28
+ // Registrar filtro para procesar solicitudes con la máxima prioridad
29
+ extension.registerFilter('request_processing', requestProcessingFilter, 0);
30
+
31
+ // Registrar hook para cuando se recibe una solicitud
32
+ extension.registerHook('onRequestReceived', onRequestReceivedHook, 0);
33
+
34
+ // Registrar hook para cuando se selecciona un servidor
35
+ extension.registerHook('onServerSelected', onServerSelectedHook, 0);
36
+
37
+ // Registrar hook para cuando la respuesta está lista para ser enviada
38
+ extension.registerHook('onResponseReady', onResponseReadyHook, 0);
39
+
40
+ // Registrar hook para cuando se envía la respuesta
41
+ extension.registerHook('onResponseSent', onResponseSentHook, 0);
42
+
43
+ // Registrar hook para cuando ocurre un error con el servidor
44
+ extension.registerHook('onServerError', onServerErrorHook, 0);
45
+
46
+ // Configurar el plugin con valores del entorno
47
+ configurePluginFromEnv();
48
+
49
+ // Iniciar tarea de limpieza periódica de entradas expiradas
50
+ startCacheCleanupTask();
51
+
52
+ console.log('Plugin de cache robusto inicializado correctamente');
53
+ }
54
+
55
+ /**
56
+ * Configura el plugin con valores del entorno
57
+ */
58
+ function configurePluginFromEnv() {
59
+ if (process.env.CACHE_MAX_SIZE) {
60
+ pluginConfig.maxCacheSize = parseInt(process.env.CACHE_MAX_SIZE) || pluginConfig.maxCacheSize;
61
+ }
62
+
63
+ if (process.env.CACHE_DEFAULT_TTL) {
64
+ pluginConfig.defaultTTL = parseInt(process.env.CACHE_DEFAULT_TTL) || pluginConfig.defaultTTL;
65
+ }
66
+
67
+ if (process.env.CACHE_ENABLE_COMPRESSION) {
68
+ pluginConfig.enableCompression = process.env.CACHE_ENABLE_COMPRESSION === 'true';
69
+ }
70
+
71
+ if (process.env.CACHEABLE_METHODS) {
72
+ pluginConfig.cacheableMethods = process.env.CACHEABLE_METHODS.split(',');
73
+ }
74
+
75
+ if (process.env.CACHEABLE_STATUS_CODES) {
76
+ pluginConfig.cacheableStatusCodes = process.env.CACHEABLE_STATUS_CODES.split(',').map(Number);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Filtro para procesar solicitudes
82
+ * @param {Object} data - Datos de la solicitud
83
+ * @returns {Object} - Resultado del procesamiento
84
+ */
85
+ async function requestProcessingFilter(data) {
86
+ const { req, res } = data;
87
+
88
+ // Verificar si la solicitud es cacheable
89
+ if (!isRequestCacheable(req)) {
90
+ return { allowed: true, cached: false };
91
+ }
92
+
93
+ // Generar clave de caché
94
+ const cacheKey = generateCacheKey(req);
95
+
96
+ // Verificar si está en caché
97
+ if (cacheStorage.has(cacheKey)) {
98
+ const cachedEntry = cacheStorage.get(cacheKey);
99
+ const metadata = cacheMetadata.get(cacheKey);
100
+
101
+ // Verificar si no ha expirado
102
+ if (Date.now() < metadata.expiryTime) {
103
+ // Enviar respuesta desde caché
104
+ res.writeHead(cachedEntry.statusCode, cachedEntry.headers);
105
+ res.end(cachedEntry.body);
106
+
107
+ console.log(`\x1b[32m[CACHE-HIT]\x1b[0m Solicitud ${req.method} ${req.url} servida desde caché`);
108
+ console.log(`\x1b[33m \x1b[0m → Status: ${cachedEntry.statusCode}, Size: ${cachedEntry.body.length} bytes, TTL restante: ${Math.floor((metadata.expiryTime - Date.now())/1000)}s`);
109
+ console.log(`\x1b[33m \x1b[0m → NO SE CONTACTÓ AL SERVIDOR BACKEND`);
110
+
111
+ return {
112
+ allowed: true,
113
+ cached: true,
114
+ cacheHit: true,
115
+ cacheKey
116
+ };
117
+ } else {
118
+ // Eliminar entrada expirada
119
+ cacheStorage.delete(cacheKey);
120
+ cacheMetadata.delete(cacheKey);
121
+ console.log(`\x1b[31m[CACHE-EXPIRED]\x1b[0m Entrada expirada eliminada para ${cacheKey}`);
122
+ }
123
+ }
124
+
125
+ // No está en caché, permitir que continúe
126
+ console.log(`\x1b[36m[CACHE-MISS]\x1b[0m Solicitud ${req.method} ${req.url} no está en caché`);
127
+ console.log(`\x1b[33m \x1b[0m → SE PROCESARÁ NORMALMENTE Y SE CONTACTARÁ AL SERVIDOR BACKEND`);
128
+
129
+ // Guardar la clave de caché en la solicitud para usarla más tarde
130
+ if (req) {
131
+ req.cacheKey = cacheKey;
132
+ }
133
+
134
+ return {
135
+ allowed: true,
136
+ cached: false,
137
+ cacheKey
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Hook para cuando se recibe una solicitud
143
+ * @param {Object} params - Parámetros del hook
144
+ */
145
+ async function onRequestReceivedHook({ req, res }) {
146
+ console.log(`[CACHE-PLUGIN] Solicitud recibida: ${req.method} ${req.url}`);
147
+
148
+ // Podríamos hacer alguna lógica aquí si es necesario
149
+ }
150
+
151
+ /**
152
+ * Hook para cuando se selecciona un servidor
153
+ * @param {Object} params - Parámetros del hook
154
+ */
155
+ async function onServerSelectedHook({ req, res, server }) {
156
+ console.log(`[CACHE-PLUGIN] Servidor seleccionado: ${server.url} para ${req.method} ${req.url}`);
157
+
158
+ // Podríamos hacer alguna lógica aquí si es necesario
159
+ }
160
+
161
+ /**
162
+ * Hook para cuando la respuesta está lista para ser enviada
163
+ * @param {Object} params - Parámetros del hook
164
+ */
165
+ async function onResponseReadyHook({ req, res, serverRes, responseBody, server, responseTime }) {
166
+ console.log(`\x1b[35m[CACHE-STORE-PREPARING]\x1b[0m Respuesta lista para ${req.url}, tamaño: ${responseBody.length} bytes, código: ${serverRes.statusCode}`);
167
+ console.log(`\x1b[33m \x1b[0m → Proveniente del servidor: ${server.url}`);
168
+
169
+ // Verificar si la solicitud original tenía una clave de caché
170
+ if (req && req.cacheKey) {
171
+ // Verificar si la respuesta es cacheable
172
+ if (isResponseCacheable(req, serverRes)) {
173
+ // Crear entrada de caché
174
+ const cacheEntry = {
175
+ statusCode: serverRes.statusCode,
176
+ headers: { ...serverRes.headers },
177
+ body: responseBody
178
+ };
179
+
180
+ // Determinar TTL desde headers o usar valor por defecto
181
+ let ttl = pluginConfig.defaultTTL;
182
+ if (serverRes.headers['cache-control']) {
183
+ const cc = serverRes.headers['cache-control'];
184
+ const maxAgeMatch = cc.match(/max-age=(\d+)/);
185
+ if (maxAgeMatch) {
186
+ ttl = parseInt(maxAgeMatch[1]) * 1000; // Convertir a ms
187
+ }
188
+ }
189
+
190
+ // Crear metadatos
191
+ const metadata = {
192
+ expiryTime: Date.now() + ttl,
193
+ size: responseBody.length,
194
+ createdAt: Date.now(),
195
+ ttl: ttl
196
+ };
197
+
198
+ // Verificar límites de caché
199
+ if (cacheStorage.size >= pluginConfig.maxCacheSize) {
200
+ // Implementar LRU (eliminar la entrada menos recientemente usada)
201
+ evictLRUCacheEntry();
202
+ }
203
+
204
+ // Almacenar en caché
205
+ cacheStorage.set(req.cacheKey, cacheEntry);
206
+ cacheMetadata.set(req.cacheKey, metadata);
207
+
208
+ console.log(`\x1b[32m[CACHE-STORED]\x1b[0m Almacenada respuesta para ${req.method} ${req.url}`);
209
+ console.log(`\x1b[33m \x1b[0m → TTL: ${ttl}ms, tamaño: ${responseBody.length} bytes`);
210
+ console.log(`\x1b[33m \x1b[0m → Ahora esta respuesta se servirá desde caché`);
211
+ } else {
212
+ console.log(`\x1b[31m[CACHE-SKIPPED]\x1b[0m La respuesta no es cacheable, omitiendo almacenamiento`);
213
+ }
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Hook para cuando se envía la respuesta
219
+ * @param {Object} params - Parámetros del hook
220
+ */
221
+ async function onResponseSentHook({ req, res, serverRes, responseTime, server }) {
222
+ console.log(`[CACHE-PLUGIN] Respuesta enviada para ${req.url}, tiempo: ${responseTime}ms, código: ${serverRes.statusCode}`);
223
+
224
+ // Verificar si la solicitud original tenía una clave de caché
225
+ if (req && req.cacheKey) {
226
+ // Verificar si la respuesta es cacheable
227
+ if (isResponseCacheable(req, serverRes)) {
228
+ // Almacenar la respuesta en caché
229
+ storeResponseInCache(req, serverRes, req.cacheKey);
230
+ }
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Hook para cuando ocurre un error con el servidor
236
+ * @param {Object} params - Parámetros del hook
237
+ */
238
+ async function onServerErrorHook(params) {
239
+ // Extraer parámetros con valores por defecto para evitar errores
240
+ const { req, res, server, error, responseTime } = params || {};
241
+
242
+ // Verificar que req exista antes de continuar
243
+ if (!req) {
244
+ console.log(`[CACHE-PLUGIN] Error recibido sin solicitud válida`);
245
+ return;
246
+ }
247
+
248
+ const serverUrl = server ? server.url : 'desconocido';
249
+ const errorMessage = error ? error.message || error : 'desconocido';
250
+
251
+ console.log(`[CACHE-PLUGIN] Error con servidor ${serverUrl} para ${req.url}: ${errorMessage}`);
252
+
253
+ // Solo intentar servir desde caché si no se han enviado headers aún
254
+ if (res && !res.headersSent) {
255
+ const cacheKey = generateCacheKey(req);
256
+
257
+ if (cacheStorage.has(cacheKey)) {
258
+ const cachedEntry = cacheStorage.get(cacheKey);
259
+ const metadata = cacheMetadata.get(cacheKey);
260
+
261
+ // Verificar si no ha expirado o si está dentro de un periodo de grace period
262
+ // (por ejemplo, permitir servir contenido ligeramente expirado mientras se intenta reconectar)
263
+ const gracePeriod = 300000; // 5 minutos extra como grace period
264
+ if (Date.now() < metadata.expiryTime + gracePeriod) {
265
+ // Enviar respuesta desde caché como fallback
266
+ res.writeHead(cachedEntry.statusCode, cachedEntry.headers);
267
+ res.end(cachedEntry.body);
268
+
269
+ console.log(`\x1b[33m[CACHE-FALLBACK]\x1b[0m Solicitud ${req.method} ${req.url} servida desde caché como fallback`);
270
+ console.log(`\x1b[33m \x1b[0m → Servidor backend no disponible, usando versión en caché`);
271
+
272
+ return; // No continuar con el manejo de error estándar
273
+ }
274
+ }
275
+ }
276
+
277
+ console.log(`\x1b[31m[CACHE-NONE-AVAILABLE]\x1b[0m No hay versión en caché disponible para ${req.url}, servidor no disponible`);
278
+ }
279
+
280
+ /**
281
+ * Verifica si una solicitud es cacheable
282
+ * @param {Object} req - Objeto de solicitud
283
+ * @returns {boolean} - Verdadero si es cacheable
284
+ */
285
+ function isRequestCacheable(req) {
286
+ // Verificar método HTTP
287
+ if (!pluginConfig.cacheableMethods.includes(req.method)) {
288
+ return false;
289
+ }
290
+
291
+ // Verificar si tiene encabezados que indican no cachear
292
+ if (req.headers && req.headers['cache-control'] && req.headers['cache-control'].includes('no-cache')) {
293
+ return false;
294
+ }
295
+
296
+ if (req.headers && req.headers['pragma'] && req.headers['pragma'].includes('no-cache')) {
297
+ return false;
298
+ }
299
+
300
+ return true;
301
+ }
302
+
303
+ /**
304
+ * Verifica si una respuesta es cacheable
305
+ * @param {Object} req - Objeto de solicitud
306
+ * @param {Object} res - Objeto de respuesta del servidor backend
307
+ * @returns {boolean} - Verdadero si es cacheable
308
+ */
309
+ function isResponseCacheable(req, res) {
310
+ // Verificar si la solicitud era cacheable
311
+ if (!isRequestCacheable(req)) {
312
+ return false;
313
+ }
314
+
315
+ // Verificar código de estado
316
+ if (!pluginConfig.cacheableStatusCodes.includes(res.statusCode)) {
317
+ console.log(`[CACHE-DEBUG] Código de estado ${res.statusCode} no es cacheable`);
318
+ return false;
319
+ }
320
+
321
+ // Verificar encabezados de respuesta
322
+ if (res.headers && res.headers['cache-control'] && res.headers['cache-control'].includes('no-cache')) {
323
+ console.log('[CACHE-DEBUG] Header cache-control indica no-cache');
324
+ return false;
325
+ }
326
+
327
+ if (res.headers && res.headers['pragma'] && res.headers['pragma'].includes('no-cache')) {
328
+ console.log('[CACHE-DEBUG] Header pragma indica no-cache');
329
+ return false;
330
+ }
331
+
332
+ console.log(`[CACHE-DEBUG] Respuesta es cacheable: statusCode=${res.statusCode}`);
333
+ return true;
334
+ }
335
+
336
+ /**
337
+ * Genera una clave única para la caché basada en la solicitud
338
+ * @param {Object} req - Objeto de solicitud
339
+ * @returns {string} - Clave de caché
340
+ */
341
+ function generateCacheKey(req) {
342
+ // Crear una representación única de la solicitud
343
+ const keyData = {
344
+ method: req.method,
345
+ url: req.url,
346
+ headers: {
347
+ accept: req.headers.accept,
348
+ 'accept-encoding': req.headers['accept-encoding'],
349
+ 'user-agent': req.headers['user-agent']
350
+ }
351
+ };
352
+
353
+ // Convertir a string y generar hash
354
+ const keyString = JSON.stringify(keyData);
355
+ return crypto.createHash('md5').update(keyString).digest('hex');
356
+ }
357
+
358
+ /**
359
+ * Almacena una respuesta en caché
360
+ * @param {Object} req - Objeto de solicitud
361
+ * @param {Object} serverRes - Objeto de respuesta del servidor backend
362
+ * @param {string} cacheKey - Clave de caché
363
+ */
364
+ function storeResponseInCache(req, serverRes, cacheKey) {
365
+ // Debido a que no podemos interceptar directamente la respuesta en este punto,
366
+ // necesitamos una estrategia diferente. Vamos a usar el sistema de hooks
367
+ // del balanceador para interceptar la respuesta antes de que se envíe al cliente.
368
+ // Pero como no tenemos un hook específico para eso, vamos a implementar
369
+ // una solución alternativa registrando un middleware en el servidor proxy.
370
+
371
+ // Esta implementación requiere una modificación en el sistema del balanceador
372
+ // para permitir la interceptación de la respuesta, lo cual no es posible
373
+ // directamente desde un plugin sin modificar el código base.
374
+
375
+ // Por lo tanto, implementaremos una solución parcial que registra la intención
376
+ // de cachear la respuesta, y dejamos que el sistema principal maneje
377
+ // la captura de la respuesta.
378
+
379
+ console.log(`[CACHE-DEFERRED] Se detectó que la respuesta para ${req.method} ${req.url} debería ser cacheada`);
380
+ console.log(`[CACHE-DEFERRED] Clave de caché: ${cacheKey}`);
381
+
382
+ // En una implementación completa, aquí conectaríamos con un sistema
383
+ // que permite interceptar la respuesta del servidor backend antes
384
+ // de enviarla al cliente, pero eso requiere cambios en el código base.
385
+ }
386
+
387
+ /**
388
+ * Elimina la entrada menos recientemente usada de la caché
389
+ */
390
+ function evictLRUCacheEntry() {
391
+ let oldestTime = Infinity;
392
+ let oldestKey = null;
393
+
394
+ for (const [key, metadata] of cacheMetadata.entries()) {
395
+ if (metadata.createdAt < oldestTime) {
396
+ oldestTime = metadata.createdAt;
397
+ oldestKey = key;
398
+ }
399
+ }
400
+
401
+ if (oldestKey) {
402
+ cacheStorage.delete(oldestKey);
403
+ cacheMetadata.delete(oldestKey);
404
+ console.log(`[CACHE-EVICTION] Entrada LRU eliminada: ${oldestKey}`);
405
+ }
406
+ }
407
+
408
+ /**
409
+ * Inicia la tarea de limpieza periódica de entradas expiradas
410
+ */
411
+ function startCacheCleanupTask() {
412
+ // Limpiar entradas expiradas cada 5 minutos
413
+ setInterval(() => {
414
+ const initialSize = cacheStorage.size;
415
+ const now = Date.now();
416
+
417
+ for (const [key, metadata] of cacheMetadata.entries()) {
418
+ if (now >= metadata.expiryTime) {
419
+ cacheStorage.delete(key);
420
+ cacheMetadata.delete(key);
421
+ }
422
+ }
423
+
424
+ const removedCount = initialSize - cacheStorage.size;
425
+ if (removedCount > 0) {
426
+ console.log(`[CACHE-CLEANUP] Eliminadas ${removedCount} entradas expiradas. Tamaño actual: ${cacheStorage.size}`);
427
+ }
428
+ }, 300000); // 5 minutos
429
+ }
430
+
431
+ /**
432
+ * Obtiene estadísticas de la caché
433
+ * @returns {Object} - Estadísticas de la caché
434
+ */
435
+ function getCacheStats() {
436
+ return {
437
+ size: cacheStorage.size,
438
+ maxSize: pluginConfig.maxCacheSize,
439
+ utilization: (cacheStorage.size / pluginConfig.maxCacheSize) * 100,
440
+ entries: Array.from(cacheStorage.keys())
441
+ };
442
+ }
443
+
444
+ /**
445
+ * Limpia toda la caché
446
+ */
447
+ function clearCache() {
448
+ cacheStorage.clear();
449
+ cacheMetadata.clear();
450
+ console.log('[CACHE-CLEAR] Caché completamente limpiada');
451
+ }
452
+
453
+ /**
454
+ * Desinicializa el plugin
455
+ * @param {Object} balancer - Instancia del balanceador
456
+ */
457
+ async function deinit(balancer) {
458
+ console.log('Desactivando plugin de cache robusto...');
459
+
460
+ // Limpiar recursos si es necesario
461
+ cacheStorage.clear();
462
+ cacheMetadata.clear();
463
+
464
+ console.log('Plugin de cache robusto desactivado correctamente');
465
+ }
466
+
467
+ // Exportar funciones
468
+ module.exports = {
469
+ init,
470
+ deinit,
471
+ getCacheStats,
472
+ clearCache,
473
+ // Exportar también funciones útiles para otros módulos
474
+ isRequestCacheable,
475
+ isResponseCacheable,
476
+ generateCacheKey
477
+ };
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "Plugin de Cache Robusto",
3
+ "version": "1.0.0",
4
+ "description": "Plugin para implementar un sistema de cache robusto con hooks y filtros",
5
+ "author": "Sistema Kukuy",
6
+ "main": "index.js",
7
+ "kukuyVersion": "^1.6.0",
8
+ "hooks": [
9
+ "onRequestReceived",
10
+ "onResponseSent",
11
+ "onServerSelected"
12
+ ],
13
+ "filters": [
14
+ "request_processing"
15
+ ],
16
+ "enabled": true
17
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Plugin de ejemplo para Kukuy
3
+ * Demuestra cómo crear un plugin que se carga automáticamente
4
+ */
5
+
6
+ async function init(balancer) {
7
+ console.log('Inicializando plugin de ejemplo...');
8
+
9
+ const extension = balancer.getPostStartupExtension();
10
+
11
+ // Registrar un filtro de ejemplo
12
+ extension.registerFilter('request_processing', async (data) => {
13
+ const { req, res } = data;
14
+ if (req && res) {
15
+ console.log(`Plugin: Procesando solicitud ${req.method} ${req.url}`);
16
+
17
+ // Agregar un header personalizado
18
+ req.headers['x-plugin-example'] = 'activated';
19
+ }
20
+
21
+ return { allowed: true };
22
+ }, 5);
23
+
24
+ // Registrar un hook para cuando se recibe una solicitud
25
+ extension.registerHook('onRequestReceived', async ({ req, res }) => {
26
+ console.log(`Plugin: Solicitud recibida - ${req.method} ${req.url}`);
27
+ }, 5);
28
+
29
+ // Registrar un hook para cuando se envía la respuesta
30
+ extension.registerHook('onResponseSent', async ({ req, res, serverRes, responseTime }) => {
31
+ console.log(`Plugin: Respuesta enviada para ${req.url} en ${responseTime}ms`);
32
+ }, 5);
33
+
34
+ console.log('Plugin de ejemplo inicializado correctamente');
35
+ }
36
+
37
+ async function deinit(balancer) {
38
+ console.log('Desactivando plugin de ejemplo...');
39
+ }
40
+
41
+ module.exports = { init, deinit };
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "Plugin de Ejemplo",
3
+ "version": "1.0.0",
4
+ "description": "Plugin de ejemplo para demostrar la arquitectura de plugins",
5
+ "author": "kukuy",
6
+ "main": "index.js",
7
+ "kukuyVersion": "^1.6.0",
8
+ "hooks": ["onRequestReceived", "onResponseSent"],
9
+ "filters": ["request_processing"],
10
+ "enabled": false
11
+ }