kukuy 1.4.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 (41) hide show
  1. package/.ctagsd/ctagsd.json +954 -0
  2. package/.ctagsd/file_list.txt +100 -0
  3. package/.ctagsd/tags.db +0 -0
  4. package/CHANGELOG.md +101 -0
  5. package/LICENSE +680 -0
  6. package/README.md +251 -0
  7. package/captura.png +0 -0
  8. package/kukuy.js +23 -0
  9. package/kukuy.workspace +11 -0
  10. package/package.json +26 -0
  11. package/restart-balancer.sh +10 -0
  12. package/routes.json +14 -0
  13. package/scripts/load_test.py +151 -0
  14. package/servers.json +19 -0
  15. package/servers_real.json +19 -0
  16. package/src/algorithms/AlgorithmManager.js +85 -0
  17. package/src/algorithms/IPHashAlgorithm.js +131 -0
  18. package/src/algorithms/LoadBalancingAlgorithm.js +23 -0
  19. package/src/algorithms/RoundRobinAlgorithm.js +67 -0
  20. package/src/config/ConfigManager.js +37 -0
  21. package/src/config/RouteLoader.js +36 -0
  22. package/src/core/Balancer.js +353 -0
  23. package/src/core/RoundRobinAlgorithm.js +60 -0
  24. package/src/core/ServerPool.js +77 -0
  25. package/src/dashboard/WebDashboard.js +150 -0
  26. package/src/dashboard/WebSocketServer.js +114 -0
  27. package/src/extensibility/CachingFilter.js +134 -0
  28. package/src/extensibility/FilterChain.js +93 -0
  29. package/src/extensibility/HookManager.js +48 -0
  30. package/src/protocol/HttpBalancer.js +37 -0
  31. package/src/protocol/HttpsBalancer.js +47 -0
  32. package/src/utils/BalancerLogger.js +102 -0
  33. package/src/utils/HealthChecker.js +51 -0
  34. package/src/utils/Logger.js +39 -0
  35. package/src/utils/MetricsCollector.js +82 -0
  36. package/src/utils/ProfessionalMetrics.js +501 -0
  37. package/start-iphash.sh +5 -0
  38. package/start-roundrobin.sh +5 -0
  39. package/stress-test.js +190 -0
  40. package/webpage/README.md +17 -0
  41. package/webpage/index.html +549 -0
@@ -0,0 +1,60 @@
1
+ class RoundRobinAlgorithm {
2
+ constructor(serverPool) {
3
+ this.serverPool = serverPool;
4
+ this.currentIndex = 0;
5
+ }
6
+
7
+ selectServer(servers = null) {
8
+ // Obtener servidores disponibles (si se pasan servers específicos, usarlos; sino, usar todos)
9
+ let availableServers = servers || this.serverPool.getHealthyServers();
10
+
11
+ // Filtrar servidores activos
12
+ availableServers = availableServers.filter(server => server.active !== false);
13
+
14
+ if (availableServers.length === 0) {
15
+ return null;
16
+ }
17
+
18
+ // Intentar encontrar un servidor disponible usando round robin
19
+ // Si el servidor actual no está disponible, buscar el siguiente disponible
20
+ for (let i = 0; i < availableServers.length; i++) {
21
+ const candidateIndex = (this.currentIndex + i) % availableServers.length;
22
+ const candidateServer = availableServers[candidateIndex];
23
+
24
+ // Verificar si el servidor candidato está realmente disponible
25
+ if (this.isServerAvailable(candidateServer)) {
26
+ this.currentIndex = candidateIndex + 1; // Preparar para la próxima selección
27
+ return candidateServer;
28
+ }
29
+ }
30
+
31
+ // Si ningún servidor está disponible, devolver null
32
+ return null;
33
+ }
34
+
35
+ // Método para verificar si un servidor está disponible
36
+ isServerAvailable(server) {
37
+ // Un servidor está disponible si:
38
+ // 1. Está marcado como healthy
39
+ // 2. Está activo
40
+ // 3. No ha superado el número máximo de intentos fallidos
41
+ return server.healthy && server.active && server.failedAttempts < 5;
42
+ }
43
+
44
+ // Método para seleccionar un servidor con reintento automático
45
+ async selectServerWithRetry(servers = null, maxRetries = 3) {
46
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
47
+ const server = this.selectServer(servers);
48
+ if (server) {
49
+ return server;
50
+ }
51
+ // Si no hay servidores disponibles, esperar un poco antes de reintentar
52
+ if (attempt < maxRetries - 1) {
53
+ await new Promise(resolve => setTimeout(resolve, 100)); // Esperar 100ms
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+ }
59
+
60
+ module.exports = { RoundRobinAlgorithm };
@@ -0,0 +1,77 @@
1
+ const { HealthChecker } = require('../utils/HealthChecker');
2
+
3
+ class ServerPool {
4
+ constructor() {
5
+ this.servers = [];
6
+ this.healthChecker = new HealthChecker();
7
+ this.nextId = 1;
8
+ }
9
+
10
+ addServer(serverConfig) {
11
+ const server = {
12
+ id: this.nextId++,
13
+ url: serverConfig.url,
14
+ protocol: serverConfig.url.startsWith('https') ? 'https:' : 'http:',
15
+ host: new URL(serverConfig.url).host,
16
+ weight: serverConfig.weight || 1,
17
+ active: serverConfig.active !== false, // Por defecto activo
18
+ tags: serverConfig.tags || [],
19
+ failedAttempts: 0,
20
+ lastChecked: Date.now(),
21
+ healthy: true
22
+ };
23
+
24
+ this.servers.push(server);
25
+ return server;
26
+ }
27
+
28
+ addServers(serversConfig) {
29
+ serversConfig.forEach(serverConfig => {
30
+ this.addServer(serverConfig);
31
+ });
32
+ }
33
+
34
+ getServers() {
35
+ return this.servers;
36
+ }
37
+
38
+ getHealthyServers() {
39
+ return this.servers.filter(server => server.healthy && server.active);
40
+ }
41
+
42
+ getServersByTag(tag) {
43
+ return this.servers.filter(server => server.tags.includes(tag));
44
+ }
45
+
46
+ markServerAsFailed(serverId) {
47
+ const server = this.servers.find(s => s.id === serverId);
48
+ if (server) {
49
+ server.failedAttempts++;
50
+ server.healthy = false;
51
+
52
+ // Programar verificación de salud después de un tiempo
53
+ setTimeout(() => {
54
+ this.healthChecker.checkServerHealth(server)
55
+ .then(isHealthy => {
56
+ if (isHealthy) {
57
+ server.healthy = true;
58
+ server.failedAttempts = 0;
59
+ server.lastChecked = Date.now();
60
+ } else {
61
+ server.lastChecked = Date.now();
62
+ // Si ha fallado demasiadas veces, mantenerlo inactivo
63
+ if (server.failedAttempts > 5) {
64
+ server.active = false;
65
+ }
66
+ }
67
+ });
68
+ }, 30000); // Reintento cada 30 segundos
69
+ }
70
+ }
71
+
72
+ getServerById(serverId) {
73
+ return this.servers.find(server => server.id === serverId);
74
+ }
75
+ }
76
+
77
+ module.exports = { ServerPool };
@@ -0,0 +1,150 @@
1
+ const http = require('http');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const url = require('url');
5
+
6
+ class WebDashboard {
7
+ constructor(balancerInstance) {
8
+ this.balancer = balancerInstance;
9
+ this.port = process.env.DASHBOARD_PORT || 8082;
10
+ }
11
+
12
+ start() {
13
+ const server = http.createServer((req, res) => {
14
+ const parsedUrl = url.parse(req.url, true);
15
+ const pathname = parsedUrl.pathname;
16
+
17
+ // Rutas de la API
18
+ if (pathname === '/api/status') {
19
+ this.handleStatusRequest(res);
20
+ } else if (pathname === '/api/metrics') {
21
+ this.handleMetricsRequest(res);
22
+ } else if (pathname === '/api/servers') {
23
+ this.handleServersRequest(res);
24
+ } else if (pathname === '/api/server-stats') {
25
+ this.handleServerStatsRequest(res);
26
+ } else if (pathname === '/api/config') {
27
+ this.handleConfigRequest(res);
28
+ } else {
29
+ // Servir archivos estáticos
30
+ this.serveStaticFile(req, res, pathname);
31
+ }
32
+ });
33
+
34
+ server.listen(this.port, () => {
35
+ console.log(`Panel web del balanceador escuchando en puerto ${this.port}`);
36
+ });
37
+ }
38
+
39
+ handleStatusRequest(res) {
40
+ const status = {
41
+ running: true,
42
+ algorithm: this.balancer.algorithmManager.getCurrentAlgorithm()?.getName() || 'unknown',
43
+ uptime: Date.now() - this.balancer.startTime,
44
+ timestamp: new Date().toISOString()
45
+ };
46
+
47
+ res.writeHead(200, { 'Content-Type': 'application/json' });
48
+ res.end(JSON.stringify(status));
49
+ }
50
+
51
+ handleMetricsRequest(res) {
52
+ const metrics = this.balancer.metricsCollector.getMetrics();
53
+
54
+ res.writeHead(200, { 'Content-Type': 'application/json' });
55
+ res.end(JSON.stringify(metrics));
56
+ }
57
+
58
+ handleServersRequest(res) {
59
+ const servers = this.balancer.serverPool.getServers().map(server => ({
60
+ id: server.id,
61
+ url: server.url,
62
+ protocol: server.protocol,
63
+ host: server.host,
64
+ weight: server.weight,
65
+ active: server.active,
66
+ tags: server.tags,
67
+ failedAttempts: server.failedAttempts,
68
+ lastChecked: server.lastChecked,
69
+ healthy: server.healthy,
70
+ status: server.healthy ? 'online' : 'offline'
71
+ }));
72
+
73
+ res.writeHead(200, { 'Content-Type': 'application/json' });
74
+ res.end(JSON.stringify({ servers }));
75
+ }
76
+
77
+ handleServerStatsRequest(res) {
78
+ // Obtener estadísticas detalladas por servidor
79
+ const serverStats = this.balancer.metricsCollector.getServerStats();
80
+
81
+ res.writeHead(200, { 'Content-Type': 'application/json' });
82
+ res.end(JSON.stringify({ serverStats }));
83
+ }
84
+
85
+ handleConfigRequest(res) {
86
+ // Devolver la configuración necesaria para el cliente web
87
+ const config = {
88
+ websocketPort: process.env.WEBSOCKET_PORT || 8083, // Puerto WebSocket por defecto
89
+ algorithm: this.balancer.algorithmManager.getCurrentAlgorithmName ?
90
+ this.balancer.algorithmManager.getCurrentAlgorithmName() :
91
+ 'roundrobin',
92
+ httpPort: process.env.BALANCER_HTTP_PORT || 8081,
93
+ dashboardPort: process.env.DASHBOARD_PORT || 8082
94
+ };
95
+
96
+ res.writeHead(200, { 'Content-Type': 'application/json' });
97
+ res.end(JSON.stringify(config));
98
+ }
99
+
100
+ serveStaticFile(req, res, pathname) {
101
+ let filePath = path.join(__dirname, '../../webpage', pathname);
102
+
103
+ // Si la ruta es '/', servir index.html
104
+ if (pathname === '/') {
105
+ filePath = path.join(__dirname, '../../webpage', 'index.html');
106
+ }
107
+
108
+ // Mapeo de extensiones a tipos MIME
109
+ const mimeTypes = {
110
+ '.html': 'text/html',
111
+ '.css': 'text/css',
112
+ '.js': 'application/javascript',
113
+ '.json': 'application/json',
114
+ '.png': 'image/png',
115
+ '.jpg': 'image/jpg',
116
+ '.gif': 'image/gif',
117
+ '.svg': 'image/svg+xml',
118
+ '.wav': 'audio/wav',
119
+ '.mp4': 'video/mp4',
120
+ '.woff': 'application/font-woff',
121
+ '.ttf': 'application/font-ttf',
122
+ '.eot': 'application/vnd.ms-fontobject',
123
+ '.otf': 'application/font-otf',
124
+ '.wasm': 'application/wasm'
125
+ };
126
+
127
+ const extname = String(path.extname(filePath)).toLowerCase();
128
+ const contentType = mimeTypes[extname] || 'application/octet-stream';
129
+
130
+ fs.readFile(filePath, (err, content) => {
131
+ if (err) {
132
+ if (err.code === 'ENOENT') {
133
+ // Archivo no encontrado
134
+ res.writeHead(404);
135
+ res.end('404 Not Found');
136
+ } else {
137
+ // Otro error
138
+ res.writeHead(500);
139
+ res.end(`Error interno del servidor: ${err.code}`);
140
+ }
141
+ } else {
142
+ // Archivo encontrado
143
+ res.writeHead(200, { 'Content-Type': contentType });
144
+ res.end(content, 'utf-8');
145
+ }
146
+ });
147
+ }
148
+ }
149
+
150
+ module.exports = { WebDashboard };
@@ -0,0 +1,114 @@
1
+ const WebSocket = require('ws');
2
+ const { ProfessionalMetrics } = require('../utils/ProfessionalMetrics');
3
+
4
+ class WebSocketServer {
5
+ constructor(balancerInstance) {
6
+ this.balancer = balancerInstance;
7
+ this.wss = null;
8
+ this.clients = new Set();
9
+ }
10
+
11
+ start(port = 8083) {
12
+ this.wss = new WebSocket.Server({ port });
13
+
14
+ this.wss.on('connection', (ws) => {
15
+ // Agregar nuevo cliente
16
+ this.clients.add(ws);
17
+ console.log(`Cliente WebSocket conectado. Total: ${this.clients.size}`);
18
+
19
+ // Enviar datos inmediatamente al conectar
20
+ this.sendCurrentMetrics(ws);
21
+
22
+ // Manejar desconexión
23
+ ws.on('close', () => {
24
+ this.clients.delete(ws);
25
+ console.log(`Cliente WebSocket desconectado. Total: ${this.clients.size}`);
26
+ });
27
+
28
+ // Manejar errores
29
+ ws.on('error', (error) => {
30
+ console.error('Error en WebSocket:', error);
31
+ this.clients.delete(ws);
32
+ });
33
+ });
34
+
35
+ console.log(`Servidor WebSocket iniciado en puerto ${port}`);
36
+
37
+ // Iniciar envío periódico de datos
38
+ this.startPeriodicBroadcast();
39
+ }
40
+
41
+ sendCurrentMetrics(ws) {
42
+ try {
43
+ const metrics = this.balancer.metricsCollector.getMetrics();
44
+ const serverStats = this.balancer.metricsCollector.getServerStats();
45
+ const status = {
46
+ running: true,
47
+ algorithm: this.balancer.algorithmManager.getCurrentAlgorithm()?.getName() || 'unknown',
48
+ uptime: Date.now() - this.balancer.startTime,
49
+ timestamp: new Date().toISOString()
50
+ };
51
+
52
+ const data = {
53
+ type: 'metrics_update',
54
+ timestamp: Date.now(),
55
+ metrics,
56
+ serverStats,
57
+ status
58
+ };
59
+
60
+ ws.send(JSON.stringify(data));
61
+ } catch (error) {
62
+ console.error('Error enviando métricas:', error);
63
+ }
64
+ }
65
+
66
+ broadcastMetrics() {
67
+ if (this.clients.size === 0) return;
68
+
69
+ try {
70
+ const metrics = this.balancer.metricsCollector.getMetrics();
71
+ const serverStats = this.balancer.metricsCollector.getServerStats();
72
+ const status = {
73
+ running: true,
74
+ algorithm: this.balancer.algorithmManager.getCurrentAlgorithm()?.getName() || 'unknown',
75
+ uptime: Date.now() - this.balancer.startTime,
76
+ timestamp: new Date().toISOString()
77
+ };
78
+
79
+ const data = {
80
+ type: 'metrics_update',
81
+ timestamp: Date.now(),
82
+ metrics,
83
+ serverStats,
84
+ status
85
+ };
86
+
87
+ const jsonData = JSON.stringify(data);
88
+
89
+ // Enviar a todos los clientes conectados
90
+ for (const client of this.clients) {
91
+ if (client.readyState === WebSocket.OPEN) {
92
+ client.send(jsonData);
93
+ }
94
+ }
95
+ } catch (error) {
96
+ console.error('Error en broadcast de métricas:', error);
97
+ }
98
+ }
99
+
100
+ startPeriodicBroadcast(interval = 2000) { // Enviar cada 2 segundos
101
+ setInterval(() => {
102
+ this.broadcastMetrics();
103
+ }, interval);
104
+ }
105
+
106
+ close() {
107
+ if (this.wss) {
108
+ this.wss.close();
109
+ this.clients.clear();
110
+ }
111
+ }
112
+ }
113
+
114
+ module.exports = { WebSocketServer };
@@ -0,0 +1,134 @@
1
+ class CachingFilter {
2
+ constructor(maxCacheSize = 100) {
3
+ this.cache = new Map();
4
+ this.maxCacheSize = maxCacheSize;
5
+ this.accessLog = new Map(); // Para implementar LRU
6
+ }
7
+
8
+ // Generar clave única para la caché basada en la solicitud
9
+ generateCacheKey(req) {
10
+ return `${req.method}:${req.url}:${JSON.stringify(req.headers)}`;
11
+ }
12
+
13
+ // Verificar si una solicitud es cacheable
14
+ isCacheable(req) {
15
+ // Solo GET requests son cacheables por defecto
16
+ return req.method === 'GET';
17
+ }
18
+
19
+ // Aplicar el filtro de caché
20
+ async apply(req, res, additionalData = {}) {
21
+ if (!this.isCacheable(req)) {
22
+ // Si no es cacheable, continuar con la cadena
23
+ return { allowed: true, cached: false };
24
+ }
25
+
26
+ const cacheKey = this.generateCacheKey(req);
27
+ const now = Date.now();
28
+
29
+ // Verificar si está en caché y no ha expirado
30
+ if (this.cache.has(cacheKey)) {
31
+ const cachedData = this.cache.get(cacheKey);
32
+
33
+ // Verificar si no ha expirado (por ejemplo, 5 minutos)
34
+ if (now - cachedData.timestamp < 300000) { // 5 minutos en milisegundos
35
+ // Enviar respuesta desde caché
36
+ res.writeHead(cachedData.statusCode, cachedData.headers);
37
+ res.end(cachedData.body);
38
+
39
+ return {
40
+ allowed: true,
41
+ cached: true,
42
+ cacheHit: true,
43
+ cacheKey
44
+ };
45
+ } else {
46
+ // Eliminar entrada expirada
47
+ this.cache.delete(cacheKey);
48
+ this.accessLog.delete(cacheKey);
49
+ }
50
+ }
51
+
52
+ // Si no está en caché, permitir que la solicitud continúe
53
+ // y almacenar la respuesta para futuras solicitudes
54
+ const originalWriteHead = res.writeHead;
55
+ const originalEnd = res.end;
56
+ const filter = this;
57
+
58
+ // Interceptamos la respuesta para almacenarla
59
+ res.writeHead = function(statusCode, headers) {
60
+ res.cachedStatusCode = statusCode;
61
+ res.cachedHeaders = headers;
62
+ return originalWriteHead.call(this, statusCode, headers);
63
+ };
64
+
65
+ res.end = function(chunk) {
66
+ if (chunk && !res.finished) {
67
+ // Almacenar en caché antes de enviar la respuesta
68
+ filter.storeInCache(cacheKey, {
69
+ statusCode: res.cachedStatusCode || 200,
70
+ headers: res.cachedHeaders || {},
71
+ body: chunk,
72
+ timestamp: now
73
+ });
74
+ }
75
+ return originalEnd.call(this, chunk);
76
+ };
77
+
78
+ return {
79
+ allowed: true,
80
+ cached: false,
81
+ cacheKey
82
+ };
83
+ }
84
+
85
+ storeInCache(cacheKey, data) {
86
+ // Si excede el tamaño máximo, eliminar el menos recientemente usado
87
+ if (this.cache.size >= this.maxCacheSize) {
88
+ const oldestKey = this.getLeastRecentlyUsedKey();
89
+ if (oldestKey) {
90
+ this.cache.delete(oldestKey);
91
+ this.accessLog.delete(oldestKey);
92
+ }
93
+ }
94
+
95
+ this.cache.set(cacheKey, data);
96
+ this.accessLog.set(cacheKey, Date.now());
97
+ }
98
+
99
+ getLeastRecentlyUsedKey() {
100
+ let oldestTime = Infinity;
101
+ let oldestKey = null;
102
+
103
+ for (const [key, time] of this.accessLog.entries()) {
104
+ if (time < oldestTime) {
105
+ oldestTime = time;
106
+ oldestKey = key;
107
+ }
108
+ }
109
+
110
+ return oldestKey;
111
+ }
112
+
113
+ // Limpiar entradas expiradas
114
+ clearExpiredEntries() {
115
+ const now = Date.now();
116
+ for (const [key, data] of this.cache.entries()) {
117
+ if (now - data.timestamp >= 300000) { // 5 minutos
118
+ this.cache.delete(key);
119
+ this.accessLog.delete(key);
120
+ }
121
+ }
122
+ }
123
+
124
+ // Obtener estadísticas de la caché
125
+ getStats() {
126
+ return {
127
+ size: this.cache.size,
128
+ maxSize: this.maxCacheSize,
129
+ utilization: (this.cache.size / this.maxCacheSize) * 100
130
+ };
131
+ }
132
+ }
133
+
134
+ module.exports = { CachingFilter };
@@ -0,0 +1,93 @@
1
+ /*
2
+ * KUKUY
3
+ * Copyright (C) 2026 Desarrollador
4
+ *
5
+ * This program is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * This program is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+
19
+ const HookSystem = require('jerk-hooked-lib');
20
+ const { CachingFilter } = require('./CachingFilter');
21
+
22
+ class FilterChain {
23
+ constructor() {
24
+ this.hooks = new HookSystem();
25
+ this.cachingFilter = new CachingFilter();
26
+ this.setupDefaultFilters();
27
+ }
28
+
29
+ setupDefaultFilters() {
30
+ // Registrar filtros predeterminados
31
+ this.hooks.addFilter('request_processing', this.authenticationFilter.bind(this));
32
+ this.hooks.addFilter('request_processing', this.rateLimitFilter.bind(this));
33
+ this.hooks.addFilter('request_processing', this.loggingFilter.bind(this));
34
+ // El caching filter se aplica de manera especial, no en la cadena estándar
35
+ }
36
+
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
+ // Usar el sistema de filtros de la librería para otros filtros
47
+ // Creamos un objeto combinado para pasar al filtro
48
+ const filterData = { req, res, ...additionalData, allowed: true, statusCode: 200, message: '' };
49
+
50
+ // Aplicar filtros según el tipo
51
+ const processedData = this.hooks.applyFilters('request_processing', filterData);
52
+
53
+ // Devolver el resultado procesado
54
+ return { ...processedData, ...cacheResult };
55
+ }
56
+
57
+ // Filtro de autenticación básico
58
+ authenticationFilter(data) {
59
+ // Aquí iría la lógica de autenticación
60
+ // Por ahora, permitimos todas las solicitudes
61
+ return data; // Devolvemos el mismo objeto con posibles modificaciones
62
+ }
63
+
64
+ // Filtro de límite de tasa básico
65
+ rateLimitFilter(data) {
66
+ // Aquí iría la lógica de límite de tasa
67
+ // Por ahora, permitimos todas las solicitudes
68
+ return data;
69
+ }
70
+
71
+ // Filtro de logging básico
72
+ loggingFilter(data) {
73
+ console.log(`${new Date().toISOString()} - ${data.req.method} ${data.req.url}`);
74
+ return data;
75
+ }
76
+
77
+ // Método para añadir nuevos filtros
78
+ addFilter(filterName, filterFunction) {
79
+ this.hooks.addFilter(filterName, filterFunction);
80
+ }
81
+
82
+ // Método para aplicar un filtro específico
83
+ applySpecificFilter(filterName, data) {
84
+ return this.hooks.applyFilters(filterName, data);
85
+ }
86
+
87
+ // Obtener estadísticas de la caché
88
+ getCacheStats() {
89
+ return this.cachingFilter.getStats();
90
+ }
91
+ }
92
+
93
+ module.exports = { FilterChain };
@@ -0,0 +1,48 @@
1
+ const HookSystem = require('jerk-hooked-lib');
2
+
3
+ class HookManager {
4
+ constructor() {
5
+ this.hooks = new HookSystem();
6
+ this.registerDefaultHooks();
7
+ }
8
+
9
+ registerDefaultHooks() {
10
+ // Definir hooks predeterminados
11
+ const hookNames = [
12
+ 'onRequestReceived',
13
+ 'onServerSelected',
14
+ 'onResponseSent',
15
+ 'onServerError'
16
+ ];
17
+
18
+ // Los hooks se registran implícitamente al usar addAction
19
+ // No es necesario registro explícito en esta librería
20
+ }
21
+
22
+ async executeHooks(hookName, data) {
23
+ // En esta librería, usamos doAction para ejecutar hooks
24
+ // Pasamos el objeto data como argumento
25
+ this.hooks.doAction(hookName, data);
26
+ }
27
+
28
+ addHook(hookName, callback) {
29
+ // En esta librería, usamos addAction para añadir hooks
30
+ this.hooks.addAction(hookName, callback);
31
+ }
32
+
33
+ removeHook(hookName, callback) {
34
+ // Para remover hooks, necesitamos un ID único
35
+ // Esta operación puede no estar disponible directamente en esta librería
36
+ console.warn(`Remoción de hook no implementada directamente para: ${hookName}`);
37
+ }
38
+
39
+ hasHook(hookName) {
40
+ return this.hooks.hasAction(hookName);
41
+ }
42
+
43
+ getHookCount(hookName) {
44
+ return this.hooks.actionsCount(hookName);
45
+ }
46
+ }
47
+
48
+ module.exports = { HookManager };