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.
- package/README.md +159 -188
- package/balancer.log +30 -0
- package/certs/auto/certificate.crt +22 -0
- package/certs/auto/private.key +28 -0
- package/kukuy-plugins/README.md +125 -0
- package/kukuy-plugins/cache-plugin/index.js +477 -0
- package/kukuy-plugins/cache-plugin/manifest.json +17 -0
- package/kukuy-plugins/ejemplo-plugin/index.js +41 -0
- package/kukuy-plugins/ejemplo-plugin/manifest.json +11 -0
- package/kukuy-plugins/health-checker/index.js +168 -0
- package/kukuy-plugins/health-checker/manifest.json +16 -0
- package/kukuy-plugins/health-monitor/index.js +58 -0
- package/kukuy-plugins/health-monitor/manifest.json +16 -0
- package/kukuy-plugins/redirect-plugin/index.js +172 -0
- package/kukuy-plugins/redirect-plugin/manifest.json +15 -0
- package/package.json +7 -3
- package/servers_real.json +5 -0
- package/src/core/Balancer.js +176 -39
- package/src/core/ServerPool.js +2 -2
- package/src/extensibility/ExtendedFilterChain.js +90 -0
- package/src/extensibility/ExtendedHookManager.js +87 -0
- package/src/extensibility/FilterChain.js +2 -9
- package/src/extensibility/HookManager.js +1 -0
- package/src/extensibility/PostStartupExtension.js +97 -0
- package/src/plugins/PluginManager.js +231 -0
- package/src/utils/HealthChecker.js +61 -6
- package/.ctagsd/ctagsd.json +0 -954
- package/.ctagsd/file_list.txt +0 -100
- package/.ctagsd/tags.db +0 -0
- package/CHANGELOG.md +0 -125
- package/LICENSE +0 -680
- package/README-SSL.md +0 -165
- package/captura.png +0 -0
- package/kukuu1.webp +0 -0
- package/kukuy.workspace +0 -11
- package/optimize-mariadb.sh +0 -152
- package/restart-balancer.sh +0 -10
- package/scripts/load_test.py +0 -151
- package/stress-test.js +0 -190
- package/test_optimization.js +0 -54
package/src/core/Balancer.js
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
|
|
19
19
|
const fs = require('fs');
|
|
20
20
|
const url = require('url');
|
|
21
|
-
const {
|
|
22
|
-
const {
|
|
21
|
+
const { ExtendedHookManager } = require('../extensibility/ExtendedHookManager');
|
|
22
|
+
const { ExtendedFilterChain } = require('../extensibility/ExtendedFilterChain');
|
|
23
23
|
const { ServerPool } = require('./ServerPool');
|
|
24
24
|
const { AlgorithmManager } = require('../algorithms/AlgorithmManager');
|
|
25
25
|
const { ConfigManager } = require('../config/ConfigManager');
|
|
@@ -31,13 +31,15 @@ const { HttpsBalancer } = require('../protocol/HttpsBalancer');
|
|
|
31
31
|
const { ProfessionalMetrics } = require('../utils/ProfessionalMetrics');
|
|
32
32
|
const { WebDashboard } = require('../dashboard/WebDashboard');
|
|
33
33
|
const { WebSocketServer } = require('../dashboard/WebSocketServer');
|
|
34
|
+
const { PostStartupExtension } = require('../extensibility/PostStartupExtension');
|
|
35
|
+
const { PluginManager } = require('../plugins/PluginManager');
|
|
34
36
|
|
|
35
37
|
class Balancer {
|
|
36
38
|
constructor() {
|
|
37
39
|
this.config = ConfigManager.getInstance();
|
|
38
|
-
this.hookManager = new
|
|
39
|
-
this.filterChain = new
|
|
40
|
-
this.serverPool = new ServerPool();
|
|
40
|
+
this.hookManager = new ExtendedHookManager();
|
|
41
|
+
this.filterChain = new ExtendedFilterChain();
|
|
42
|
+
this.serverPool = new ServerPool(this);
|
|
41
43
|
this.algorithmManager = new AlgorithmManager();
|
|
42
44
|
this.routeLoader = new RouteLoader();
|
|
43
45
|
this.logger = new Logger();
|
|
@@ -56,6 +58,64 @@ class Balancer {
|
|
|
56
58
|
|
|
57
59
|
// Inicializar servidor WebSocket para datos en tiempo real
|
|
58
60
|
this.webSocketServer = new WebSocketServer(this);
|
|
61
|
+
|
|
62
|
+
// Inicializar sistema de extensión post-inicio
|
|
63
|
+
this.postStartupExtension = new PostStartupExtension(this);
|
|
64
|
+
|
|
65
|
+
// Activar plugin de depuración si está habilitado
|
|
66
|
+
this.initializeDebugPlugin();
|
|
67
|
+
|
|
68
|
+
// Inicializar sistema de plugins
|
|
69
|
+
this.pluginManager = new PluginManager(this);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
initializeDebugPlugin() {
|
|
73
|
+
// Verificar si el plugin de depuración está habilitado
|
|
74
|
+
const debugEnabled = process.env.DEBUG_REQUEST_HEADERS === 'true' ||
|
|
75
|
+
process.env.KUKUY_DEBUG_HEADERS === 'true';
|
|
76
|
+
|
|
77
|
+
if (debugEnabled) {
|
|
78
|
+
// Registrar hooks para mostrar encabezados y servidor de destino
|
|
79
|
+
|
|
80
|
+
// Hook para mostrar encabezados cuando se recibe una solicitud
|
|
81
|
+
this.hookManager.addHook('onRequestReceived', ({ req, res }) => {
|
|
82
|
+
console.log('\n--- Nueva Solicitud Recibida ---');
|
|
83
|
+
console.log(`Método: ${req.method}`);
|
|
84
|
+
console.log(`URL: ${req.url}`);
|
|
85
|
+
console.log('Encabezados:');
|
|
86
|
+
|
|
87
|
+
// Mostrar todos los encabezados de la solicitud
|
|
88
|
+
for (const [header, value] of Object.entries(req.headers)) {
|
|
89
|
+
console.log(` ${header}: ${value}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log('-----------------------------\n');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Hook para mostrar el servidor de destino asignado
|
|
96
|
+
this.hookManager.addHook('onServerSelected', ({ req, res, server }) => {
|
|
97
|
+
console.log('\n--- Servidor de Destino Asignado ---');
|
|
98
|
+
console.log(`Solicitud: ${req.method} ${req.url}`);
|
|
99
|
+
console.log(`Servidor destino: ${server.url}`);
|
|
100
|
+
console.log(`ID del servidor: ${server.id}`);
|
|
101
|
+
console.log(`Protocolo: ${server.protocol}`);
|
|
102
|
+
console.log(`Host: ${server.host}`);
|
|
103
|
+
console.log(`Etiquetas: ${server.tags.join(', ') || 'ninguna'}`);
|
|
104
|
+
console.log('----------------------------------\n');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Hook para mostrar información cuando se envía la respuesta
|
|
108
|
+
this.hookManager.addHook('onResponseSent', ({ req, res, serverRes, responseTime }) => {
|
|
109
|
+
console.log('\n--- Respuesta Enviada ---');
|
|
110
|
+
console.log(`Solicitud: ${req.method} ${req.url}`);
|
|
111
|
+
console.log(`Código de estado: ${serverRes.statusCode}`);
|
|
112
|
+
console.log(`Tiempo de respuesta: ${responseTime}ms`);
|
|
113
|
+
console.log('------------------------\n');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
console.log('Plugin de depuración de solicitud activado');
|
|
117
|
+
console.log('Mostrando encabezados de solicitud y servidores de destino en stdout');
|
|
118
|
+
}
|
|
59
119
|
}
|
|
60
120
|
|
|
61
121
|
loadConfiguration() {
|
|
@@ -88,7 +148,7 @@ class Balancer {
|
|
|
88
148
|
}
|
|
89
149
|
}
|
|
90
150
|
|
|
91
|
-
start() {
|
|
151
|
+
async start() {
|
|
92
152
|
// Crear servidor HTTP usando HttpBalancer
|
|
93
153
|
if (this.config.httpPort) {
|
|
94
154
|
this.httpBalancer = new HttpBalancer(this.config.httpPort, this.handleRequest.bind(this));
|
|
@@ -112,13 +172,19 @@ class Balancer {
|
|
|
112
172
|
// Iniciar servidor WebSocket para datos en tiempo real
|
|
113
173
|
const wsPort = process.env.WEBSOCKET_PORT || 8083;
|
|
114
174
|
this.webSocketServer.start(wsPort);
|
|
175
|
+
|
|
176
|
+
// Cargar plugins después de iniciar componentes principales
|
|
177
|
+
await this.pluginManager.loadPlugins();
|
|
178
|
+
|
|
179
|
+
// Imprimir información sobre los plugins disponibles y activos
|
|
180
|
+
this.pluginManager.printPluginsInfo();
|
|
115
181
|
}
|
|
116
182
|
|
|
117
183
|
async handleRequest(clientReq, clientRes) {
|
|
118
184
|
try {
|
|
119
185
|
// Aplicar filters antes de procesar la solicitud
|
|
120
186
|
const filterData = { req: clientReq, res: clientRes, allowed: true, statusCode: 200, message: '' };
|
|
121
|
-
const filteredResult = await this.filterChain.applyFilters('
|
|
187
|
+
const filteredResult = await this.filterChain.applyFilters('request_processing', clientReq, clientRes);
|
|
122
188
|
|
|
123
189
|
if (!filteredResult || !filteredResult.allowed) {
|
|
124
190
|
const statusCode = filteredResult?.statusCode || 403;
|
|
@@ -128,27 +194,16 @@ class Balancer {
|
|
|
128
194
|
return;
|
|
129
195
|
}
|
|
130
196
|
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const targetServer = await this.getTargetServer(clientReq.url, clientReq.method, clientReq);
|
|
136
|
-
|
|
137
|
-
if (!targetServer) {
|
|
138
|
-
clientRes.writeHead(404);
|
|
139
|
-
clientRes.end('No available servers');
|
|
197
|
+
// Verificar si el filtro indicó que la respuesta se sirvió desde caché
|
|
198
|
+
if (filteredResult?.cached && filteredResult?.cacheHit) {
|
|
199
|
+
// La respuesta ya fue enviada desde el filtro de caché, terminamos aquí
|
|
200
|
+
// No debemos continuar con ningún otro procesamiento
|
|
140
201
|
return;
|
|
141
202
|
}
|
|
142
203
|
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
res: clientRes,
|
|
147
|
-
server: targetServer
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// Reenviar solicitud al servidor backend
|
|
151
|
-
this.proxyRequest(clientReq, clientRes, targetServer);
|
|
204
|
+
// Si llegamos aquí, no hubo cache hit, continuar con el flujo normal
|
|
205
|
+
// y permitir que se ejecuten los hooks y se contacte al servidor backend
|
|
206
|
+
await this.processRequestNormally(clientReq, clientRes);
|
|
152
207
|
|
|
153
208
|
} catch (error) {
|
|
154
209
|
this.logger.error(`Error manejando solicitud: ${error.message}`);
|
|
@@ -165,6 +220,36 @@ class Balancer {
|
|
|
165
220
|
}
|
|
166
221
|
}
|
|
167
222
|
|
|
223
|
+
async processRequestNormally(clientReq, clientRes) {
|
|
224
|
+
// Emitir hook cuando se recibe una solicitud
|
|
225
|
+
await this.hookManager.executeHooks('onRequestReceived', { req: clientReq, res: clientRes });
|
|
226
|
+
|
|
227
|
+
// Determinar la ruta objetivo
|
|
228
|
+
const targetServer = await this.getTargetServer(clientReq.url, clientReq.method, clientReq);
|
|
229
|
+
|
|
230
|
+
if (!targetServer) {
|
|
231
|
+
clientRes.writeHead(404);
|
|
232
|
+
clientRes.end('No available servers');
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Emitir hook después de seleccionar servidor
|
|
237
|
+
await this.hookManager.executeHooks('onServerSelected', {
|
|
238
|
+
req: clientReq,
|
|
239
|
+
res: clientRes,
|
|
240
|
+
server: targetServer
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Verificar si después de los hooks la respuesta ya fue enviada
|
|
244
|
+
// (esto cubre el caso de onServerError u otros hooks que puedan enviar respuesta desde la caché)
|
|
245
|
+
if (clientRes.headersSent) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Reenviar solicitud al servidor backend
|
|
250
|
+
this.proxyRequest(clientReq, clientRes, targetServer);
|
|
251
|
+
}
|
|
252
|
+
|
|
168
253
|
async getTargetServer(requestUrl, method, clientReq = null) {
|
|
169
254
|
// Usar RouteLoader para encontrar la ruta coincidente
|
|
170
255
|
const matchedRoute = this.routeLoader.findMatchingRoute(requestUrl, method);
|
|
@@ -220,12 +305,6 @@ class Balancer {
|
|
|
220
305
|
clientReq.pipe(proxyReq);
|
|
221
306
|
|
|
222
307
|
proxyReq.on('response', (serverRes) => {
|
|
223
|
-
// Copiar headers de la respuesta
|
|
224
|
-
clientRes.writeHead(serverRes.statusCode, serverRes.headers);
|
|
225
|
-
|
|
226
|
-
// Enviar cuerpo de la respuesta al cliente
|
|
227
|
-
serverRes.pipe(clientRes);
|
|
228
|
-
|
|
229
308
|
// Calcular tiempo de respuesta
|
|
230
309
|
const responseTime = Date.now() - startTime;
|
|
231
310
|
|
|
@@ -250,16 +329,66 @@ class Balancer {
|
|
|
250
329
|
// Actualizar estado del servidor en las métricas
|
|
251
330
|
this.metricsCollector.updateServerStatus(server.id, 'online');
|
|
252
331
|
|
|
253
|
-
//
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
server,
|
|
259
|
-
responseTime
|
|
332
|
+
// Capturar el cuerpo de la respuesta para posibles plugins
|
|
333
|
+
let responseData = [];
|
|
334
|
+
|
|
335
|
+
serverRes.on('data', (chunk) => {
|
|
336
|
+
responseData.push(chunk);
|
|
260
337
|
});
|
|
261
338
|
|
|
262
|
-
|
|
339
|
+
serverRes.on('end', () => {
|
|
340
|
+
const responseBody = Buffer.concat(responseData);
|
|
341
|
+
|
|
342
|
+
// Emitir hook antes de enviar la respuesta al cliente
|
|
343
|
+
// Esto permite a los plugins interceptar la respuesta
|
|
344
|
+
this.hookManager.executeHooks('onResponseReady', {
|
|
345
|
+
req: clientReq,
|
|
346
|
+
res: clientRes,
|
|
347
|
+
serverRes,
|
|
348
|
+
responseBody,
|
|
349
|
+
server,
|
|
350
|
+
responseTime
|
|
351
|
+
}).then(() => {
|
|
352
|
+
// Si algún plugin ha manejado la respuesta, no la enviamos aquí
|
|
353
|
+
if (!clientRes.headersSent) {
|
|
354
|
+
// Copiar headers de la respuesta
|
|
355
|
+
clientRes.writeHead(serverRes.statusCode, serverRes.headers);
|
|
356
|
+
|
|
357
|
+
// Enviar cuerpo de la respuesta al cliente
|
|
358
|
+
clientRes.end(responseBody);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Emitir hook cuando se envía la respuesta
|
|
362
|
+
this.hookManager.executeHooks('onResponseSent', {
|
|
363
|
+
req: clientReq,
|
|
364
|
+
res: clientRes,
|
|
365
|
+
serverRes,
|
|
366
|
+
server,
|
|
367
|
+
responseTime
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
resolve(); // Resolver la promesa al completar la solicitud
|
|
371
|
+
}).catch(err => {
|
|
372
|
+
console.error('Error ejecutando hook onResponseReady:', err);
|
|
373
|
+
|
|
374
|
+
// En caso de error, enviar la respuesta de todas formas
|
|
375
|
+
if (!clientRes.headersSent) {
|
|
376
|
+
clientRes.writeHead(serverRes.statusCode, serverRes.headers);
|
|
377
|
+
clientRes.end(responseBody);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Emitir hook cuando se envía la respuesta
|
|
381
|
+
this.hookManager.executeHooks('onResponseSent', {
|
|
382
|
+
req: clientReq,
|
|
383
|
+
res: clientRes,
|
|
384
|
+
serverRes,
|
|
385
|
+
server,
|
|
386
|
+
responseTime
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
resolve(); // Resolver la promesa al completar la solicitud
|
|
390
|
+
});
|
|
391
|
+
});
|
|
263
392
|
});
|
|
264
393
|
|
|
265
394
|
proxyReq.on('error', async (err) => {
|
|
@@ -350,6 +479,14 @@ class Balancer {
|
|
|
350
479
|
this.httpsBalancer.stop();
|
|
351
480
|
}
|
|
352
481
|
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Get the post-startup extension API
|
|
485
|
+
* @returns {PostStartupExtension} The extension API
|
|
486
|
+
*/
|
|
487
|
+
getPostStartupExtension() {
|
|
488
|
+
return this.postStartupExtension;
|
|
489
|
+
}
|
|
353
490
|
}
|
|
354
491
|
|
|
355
492
|
module.exports = { Balancer };
|
package/src/core/ServerPool.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
const { HealthChecker } = require('../utils/HealthChecker');
|
|
2
2
|
|
|
3
3
|
class ServerPool {
|
|
4
|
-
constructor() {
|
|
4
|
+
constructor(balancer = null) {
|
|
5
5
|
this.servers = [];
|
|
6
6
|
this.healthyServers = []; // Caché de servidores saludables
|
|
7
7
|
this.serversById = new Map(); // Acceso rápido por ID
|
|
8
8
|
this.serversByTag = new Map(); // Caché de servidores por tag
|
|
9
|
-
this.healthChecker = new HealthChecker();
|
|
9
|
+
this.healthChecker = new HealthChecker(balancer);
|
|
10
10
|
this.nextId = 1;
|
|
11
11
|
this.cacheValid = false; // Indicador de validez de caché
|
|
12
12
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const { FilterChain } = require('./FilterChain');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extended FilterChain that allows registering filters after startup
|
|
5
|
+
*/
|
|
6
|
+
class ExtendedFilterChain {
|
|
7
|
+
constructor() {
|
|
8
|
+
// Create a new instance of the original FilterChain
|
|
9
|
+
this.originalFilterChain = new FilterChain();
|
|
10
|
+
|
|
11
|
+
// Storage for post-startup filters
|
|
12
|
+
this.postStartupFilters = {
|
|
13
|
+
request_processing: [] // Using the same hook type as original
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Register a filter after startup
|
|
19
|
+
* @param {string} type - Type of filter ('request_processing' for now)
|
|
20
|
+
* @param {Function} filterFunction - The filter function to register
|
|
21
|
+
* @param {number} priority - Priority of the filter (lower numbers execute first)
|
|
22
|
+
*/
|
|
23
|
+
registerFilter(type, filterFunction, priority = 0) {
|
|
24
|
+
if (!this.postStartupFilters[type]) {
|
|
25
|
+
this.postStartupFilters[type] = [];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Insert filter according to priority
|
|
29
|
+
const filterObj = {
|
|
30
|
+
fn: filterFunction,
|
|
31
|
+
priority,
|
|
32
|
+
id: this.generateId()
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
this.postStartupFilters[type].push(filterObj);
|
|
36
|
+
this.postStartupFilters[type].sort((a, b) => a.priority - b.priority);
|
|
37
|
+
|
|
38
|
+
// Also add to the original hook system
|
|
39
|
+
this.originalFilterChain.addFilter(type, filterFunction);
|
|
40
|
+
|
|
41
|
+
console.log(`Filter registered for ${type} with priority ${priority}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Unregister a specific filter
|
|
46
|
+
* @param {string} type - Type of filter
|
|
47
|
+
* @param {string} filterId - ID of the filter to remove
|
|
48
|
+
*/
|
|
49
|
+
unregisterFilter(type, filterId) {
|
|
50
|
+
if (this.postStartupFilters[type]) {
|
|
51
|
+
this.postStartupFilters[type] = this.postStartupFilters[type].filter(f => f.id !== filterId);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Apply all filters of a specific type
|
|
57
|
+
* @param {string} type - Type of filter
|
|
58
|
+
* @param {Object} req - Request object
|
|
59
|
+
* @param {Object} res - Response object
|
|
60
|
+
* @param {Object} additionalData - Additional data to pass to filters
|
|
61
|
+
* @returns {Object} Result of filter processing
|
|
62
|
+
*/
|
|
63
|
+
async applyFilters(type, req, res, additionalData = {}) {
|
|
64
|
+
// Apply filters using the original system which now includes post-startup filters
|
|
65
|
+
return await this.originalFilterChain.applyFilters(type, req, res, additionalData);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Generate a unique ID
|
|
70
|
+
* @returns {string} Unique identifier
|
|
71
|
+
*/
|
|
72
|
+
generateId() {
|
|
73
|
+
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Proxy other methods to the original FilterChain
|
|
77
|
+
addFilter(filterName, filterFunction) {
|
|
78
|
+
this.originalFilterChain.addFilter(filterName, filterFunction);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
applySpecificFilter(filterName, data) {
|
|
82
|
+
return this.originalFilterChain.applySpecificFilter(filterName, data);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
getCacheStats() {
|
|
86
|
+
return this.originalFilterChain.getCacheStats();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = { ExtendedFilterChain };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const { HookManager } = require('./HookManager');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extended HookManager that allows registering hooks after startup
|
|
5
|
+
*/
|
|
6
|
+
class ExtendedHookManager {
|
|
7
|
+
constructor() {
|
|
8
|
+
// Create a new instance of the original HookManager
|
|
9
|
+
this.originalHookManager = new HookManager();
|
|
10
|
+
|
|
11
|
+
// Storage for post-startup hooks
|
|
12
|
+
this.postStartupHooks = {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Register a hook after startup
|
|
17
|
+
* @param {string} hookName - Name of the hook
|
|
18
|
+
* @param {Function} callback - The callback function to register
|
|
19
|
+
* @param {number} priority - Priority of the hook (lower numbers execute first)
|
|
20
|
+
*/
|
|
21
|
+
registerHook(hookName, callback, priority = 0) {
|
|
22
|
+
if (!this.postStartupHooks[hookName]) {
|
|
23
|
+
this.postStartupHooks[hookName] = [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const hookObj = {
|
|
27
|
+
callback,
|
|
28
|
+
priority,
|
|
29
|
+
id: this.generateId()
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
this.postStartupHooks[hookName].push(hookObj);
|
|
33
|
+
this.postStartupHooks[hookName].sort((a, b) => a.priority - b.priority);
|
|
34
|
+
|
|
35
|
+
// Add to the original hook system
|
|
36
|
+
this.originalHookManager.addHook(hookName, callback);
|
|
37
|
+
|
|
38
|
+
console.log(`Hook registered: ${hookName} with priority ${priority}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Unregister a specific hook
|
|
43
|
+
* @param {string} hookName - Name of the hook
|
|
44
|
+
* @param {string} hookId - ID of the hook to remove
|
|
45
|
+
*/
|
|
46
|
+
unregisterHook(hookName, hookId) {
|
|
47
|
+
if (this.postStartupHooks[hookName]) {
|
|
48
|
+
this.postStartupHooks[hookName] = this.postStartupHooks[hookName].filter(h => h.id !== hookId);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Execute all hooks for a specific event
|
|
54
|
+
* @param {string} hookName - Name of the hook
|
|
55
|
+
* @param {Object} data - Data to pass to the hooks
|
|
56
|
+
*/
|
|
57
|
+
async executeHooks(hookName, data) {
|
|
58
|
+
// Execute hooks using the original system which now includes post-startup hooks
|
|
59
|
+
return await this.originalHookManager.executeHooks(hookName, data);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate a unique ID
|
|
64
|
+
* @returns {string} Unique identifier
|
|
65
|
+
*/
|
|
66
|
+
generateId() {
|
|
67
|
+
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Proxy other methods to the original HookManager
|
|
71
|
+
addHook(hookName, callback) {
|
|
72
|
+
this.originalHookManager.addHook(hookName, callback);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
executeHooks(hookName, data) {
|
|
76
|
+
return this.originalHookManager.executeHooks(hookName, data);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getRegisteredHooks() {
|
|
80
|
+
return Object.keys(this.postStartupHooks).reduce((acc, hookName) => {
|
|
81
|
+
acc[hookName] = this.postStartupHooks[hookName].length;
|
|
82
|
+
return acc;
|
|
83
|
+
}, {});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = { ExtendedHookManager };
|
|
@@ -35,14 +35,6 @@ class FilterChain {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
async applyFilters(type, req, res, additionalData = {}) {
|
|
38
|
-
// Verificar si la solicitud puede ser atendida desde caché
|
|
39
|
-
const cacheResult = await this.cachingFilter.apply(req, res, additionalData);
|
|
40
|
-
|
|
41
|
-
// Si fue un hit de caché, retornar inmediatamente
|
|
42
|
-
if (cacheResult.cached && cacheResult.cacheHit) {
|
|
43
|
-
return cacheResult;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
38
|
// Usar el sistema de filtros de la librería para otros filtros
|
|
47
39
|
// Creamos un objeto combinado para pasar al filtro
|
|
48
40
|
const filterData = { req, res, ...additionalData, allowed: true, statusCode: 200, message: '' };
|
|
@@ -51,7 +43,8 @@ class FilterChain {
|
|
|
51
43
|
const processedData = this.hooks.applyFilters('request_processing', filterData);
|
|
52
44
|
|
|
53
45
|
// Devolver el resultado procesado
|
|
54
|
-
|
|
46
|
+
// Nota: El manejo de caché ahora se delega a los plugins registrados
|
|
47
|
+
return processedData;
|
|
55
48
|
}
|
|
56
49
|
|
|
57
50
|
// Filtro de autenticación básico
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API for registering extensions after Kukuy startup
|
|
3
|
+
*/
|
|
4
|
+
class PostStartupExtension {
|
|
5
|
+
constructor(balancer) {
|
|
6
|
+
this.balancer = balancer;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Register a filter after startup
|
|
11
|
+
* @param {string} type - Type of filter ('request_processing' for now)
|
|
12
|
+
* @param {Function} filterFunction - The filter function to register
|
|
13
|
+
* @param {number} priority - Priority of the filter (lower numbers execute first)
|
|
14
|
+
*/
|
|
15
|
+
registerFilter(type, filterFunction, priority = 0) {
|
|
16
|
+
if (this.balancer.filterChain && typeof this.balancer.filterChain.registerFilter === 'function') {
|
|
17
|
+
this.balancer.filterChain.registerFilter(type, filterFunction, priority);
|
|
18
|
+
} else {
|
|
19
|
+
console.error('FilterChain no disponible o registerFilter no es una función');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Unregister a filter
|
|
25
|
+
* @param {string} type - Type of filter
|
|
26
|
+
* @param {string} filterId - ID of the filter to remove
|
|
27
|
+
*/
|
|
28
|
+
unregisterFilter(type, filterId) {
|
|
29
|
+
if (this.balancer.filterChain && typeof this.balancer.filterChain.unregisterFilter === 'function') {
|
|
30
|
+
this.balancer.filterChain.unregisterFilter(type, filterId);
|
|
31
|
+
} else {
|
|
32
|
+
console.error('FilterChain no disponible o unregisterFilter no es una función');
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Register a hook after startup
|
|
38
|
+
* @param {string} hookName - Name of the hook
|
|
39
|
+
* @param {Function} callback - The callback function to register
|
|
40
|
+
* @param {number} priority - Priority of the hook (lower numbers execute first)
|
|
41
|
+
*/
|
|
42
|
+
registerHook(hookName, callback, priority = 0) {
|
|
43
|
+
if (this.balancer.hookManager && typeof this.balancer.hookManager.registerHook === 'function') {
|
|
44
|
+
this.balancer.hookManager.registerHook(hookName, callback, priority);
|
|
45
|
+
} else {
|
|
46
|
+
console.error('HookManager no disponible o registerHook no es una función');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Unregister a hook
|
|
52
|
+
* @param {string} hookName - Name of the hook
|
|
53
|
+
* @param {string} hookId - ID of the hook to remove
|
|
54
|
+
*/
|
|
55
|
+
unregisterHook(hookName, hookId) {
|
|
56
|
+
if (this.balancer.hookManager && typeof this.balancer.hookManager.unregisterHook === 'function') {
|
|
57
|
+
this.balancer.hookManager.unregisterHook(hookName, hookId);
|
|
58
|
+
} else {
|
|
59
|
+
console.error('HookManager no disponible o unregisterHook no es una función');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get access to system components
|
|
65
|
+
* @param {string} componentName - Name of the component to access
|
|
66
|
+
* @returns {Object} The requested component or null
|
|
67
|
+
*/
|
|
68
|
+
getComponent(componentName) {
|
|
69
|
+
switch(componentName) {
|
|
70
|
+
case 'serverPool':
|
|
71
|
+
return this.balancer.serverPool;
|
|
72
|
+
case 'algorithmManager':
|
|
73
|
+
return this.balancer.algorithmManager;
|
|
74
|
+
case 'metricsCollector':
|
|
75
|
+
return this.balancer.metricsCollector;
|
|
76
|
+
case 'config':
|
|
77
|
+
return this.balancer.config;
|
|
78
|
+
case 'routeLoader':
|
|
79
|
+
return this.balancer.routeLoader;
|
|
80
|
+
default:
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get information about registered hooks
|
|
87
|
+
* @returns {Object} Information about registered hooks
|
|
88
|
+
*/
|
|
89
|
+
getRegisteredHooks() {
|
|
90
|
+
if (this.balancer.hookManager && typeof this.balancer.hookManager.getRegisteredHooks === 'function') {
|
|
91
|
+
return this.balancer.hookManager.getRegisteredHooks();
|
|
92
|
+
}
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { PostStartupExtension };
|