jerkjs 2.5.6 → 2.6.1

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 (46) hide show
  1. package/CHANGELOG.md +167 -79
  2. package/README.md +134 -146
  3. package/RESULTADOS_WAF.md +63 -0
  4. package/doc-2.5/ADMIN_EXTENSION_COMMANDS_MANUAL.md +261 -0
  5. package/doc-2.5/ADMIN_EXTENSION_HOOK_EXAMPLE.md +28 -0
  6. package/doc-2.5/ADMIN_EXTENSION_INTEGRATION_MANUAL.md +232 -0
  7. package/doc-2.5/CACHE_SYSTEM_MAP.md +206 -0
  8. package/doc-2.5/MANUAL_MODULOS_ADMIN.md +287 -0
  9. package/doc-2.5/QUEUE_CLI_MODULE_MANUAL.md +289 -0
  10. package/doc-2.5/QUEUE_SYSTEM_MANUAL.md +320 -0
  11. package/doc-2.5/ROUTE_CACHE_MODULE_MANUAL.md +205 -0
  12. package/doc-2.5/WAF_MODULE_MANUAL.md +229 -0
  13. package/index.js +19 -4
  14. package/jerk-admin-client/README.md +69 -0
  15. package/jerk-admin-client/package.json +23 -0
  16. package/jerk-admin-client.js +257 -0
  17. package/lib/admin/AdminExtension.js +491 -0
  18. package/lib/admin/ModuleLoader.js +77 -0
  19. package/lib/admin/config.js +21 -0
  20. package/lib/admin/modules/CacheModule.js +145 -0
  21. package/lib/admin/modules/ControllerGeneratorModule.js +414 -0
  22. package/lib/admin/modules/QueueManagementModule.js +265 -0
  23. package/lib/admin/modules/RouteCacheModule.js +227 -0
  24. package/lib/admin/modules/RouteManagerModule.js +468 -0
  25. package/lib/admin/modules/STATS_MODULE_README.md +113 -0
  26. package/lib/admin/modules/StatsModule.js +140 -0
  27. package/lib/admin/modules/SystemModule.js +140 -0
  28. package/lib/admin/modules/TimeModule.js +95 -0
  29. package/lib/admin/modules/ViewCacheStatsModule.js +92 -0
  30. package/lib/admin/modules/WAFModule.js +737 -0
  31. package/lib/cache/CacheHooks.js +141 -0
  32. package/lib/core/server.js +223 -77
  33. package/lib/middleware/firewall.js +112 -17
  34. package/lib/mvc/viewEngine.js +89 -5
  35. package/lib/queue/GlobalQueueStorage.js +38 -0
  36. package/lib/queue/QueueSystem.js +451 -0
  37. package/lib/queue/admin_example.js +114 -0
  38. package/lib/queue/example.js +268 -0
  39. package/lib/queue/integration.js +109 -0
  40. package/lib/router/RouteMatcher.js +242 -54
  41. package/lib/utils/globalStats.js +16 -0
  42. package/lib/utils/globalViewCacheInfo.js +16 -0
  43. package/lib/utils/globalWAFStats.js +54 -0
  44. package/package.json +2 -2
  45. package/test-colors.js +46 -0
  46. package/test-help-alias.js +31 -0
@@ -1,31 +1,79 @@
1
1
  /**
2
- * Componente especializado para la lógica de enrutado
2
+ * Componente especializado para la lógica de enrutado optimizado
3
3
  * Implementación del componente router/RouteMatcher.js
4
- * JERK Framework v2.1 - Separación de responsabilidades para enrutamiento
4
+ * JERK Framework v2.5.7 - Optimización de rendimiento con índices y pre-filtrado
5
5
  */
6
6
 
7
7
  class RouteMatcher {
8
8
  /**
9
- * Constructor del matcher de rutas
9
+ * Constructor del matcher de rutas optimizado
10
10
  */
11
11
  constructor() {
12
12
  // Cache de expresiones regulares para rutas parametrizadas
13
13
  this.routeRegexCache = new Map();
14
+
15
+ // Índices para optimización
16
+ this.exactRoutes = new Map(); // Rutas exactas indexadas por método y path
17
+ this.routeBuckets = {}; // Rutas organizadas por método, número de segmentos y primer segmento
18
+ this.indexesValid = false;
19
+
20
+ // Referencia al sistema de hooks (se establecerá externamente)
21
+ this.hooks = null;
22
+ }
23
+
24
+ /**
25
+ * Actualiza los índices basados en el conjunto actual de rutas
26
+ * @param {Array} routes - Array de rutas registradas
27
+ */
28
+ updateIndexes(routes) {
29
+ // Limpiar índices existentes
30
+ this.exactRoutes.clear();
31
+ this.routeBuckets = {};
32
+
33
+ // Reconstruir índices
34
+ for (const route of routes) {
35
+ // Indexar rutas exactas
36
+ const methodPathKey = `${route.method}:${route.path}`;
37
+ this.exactRoutes.set(methodPathKey, route);
38
+
39
+ // Organizar rutas en buckets por método, número de segmentos y primer segmento
40
+ const segments = route.path.split('/').filter(s => s !== '');
41
+ const segmentCount = segments.length;
42
+ const firstSegment = segments.length > 0 ? segments[0] : '';
43
+
44
+ // Inicializar estructura de buckets si no existe
45
+ if (!this.routeBuckets[route.method]) {
46
+ this.routeBuckets[route.method] = {};
47
+ }
48
+ if (!this.routeBuckets[route.method][segmentCount]) {
49
+ this.routeBuckets[route.method][segmentCount] = {};
50
+ }
51
+ if (!this.routeBuckets[route.method][segmentCount][firstSegment]) {
52
+ this.routeBuckets[route.method][segmentCount][firstSegment] = [];
53
+ }
54
+
55
+ // Agregar ruta al bucket correspondiente
56
+ this.routeBuckets[route.method][segmentCount][firstSegment].push(route);
57
+ }
58
+
59
+ this.indexesValid = true;
14
60
  }
15
61
 
16
62
  /**
17
- * Método para encontrar una ruta coincidente
63
+ * Método para encontrar una ruta coincidente con optimizaciones
18
64
  * @param {Array} routes - Array de rutas registradas
19
65
  * @param {string} method - Método HTTP
20
66
  * @param {string} pathname - Ruta a buscar
21
67
  * @returns {Object|null} - Objeto de ruta encontrado o null
22
68
  */
23
69
  findRoute(routes, method, pathname) {
24
- // Buscar ruta exacta primero
25
- const exactMatch = routes.find(route =>
26
- route.method === method && route.path === pathname
27
- );
70
+ // Actualizar índices si es necesario
71
+ if (!this.indexesValid || routes.length !== this.getStoredRoutesCount()) {
72
+ this.updateIndexes(routes);
73
+ }
28
74
 
75
+ // Búsqueda por ruta exacta (más rápida)
76
+ const exactMatch = this.findExactMatch(method, pathname);
29
77
  if (exactMatch) {
30
78
  return {
31
79
  route: exactMatch,
@@ -33,45 +81,143 @@ class RouteMatcher {
33
81
  };
34
82
  }
35
83
 
36
- // Buscar rutas estáticas que coincidan exactamente antes que rutas parametrizadas
37
- // Esto permite que rutas estáticas como /css/style.css tengan prioridad sobre rutas parametrizadas
38
- for (const route of routes) {
39
- if (route.method !== method) continue;
40
-
41
- // Verificar si es una ruta estática que coincide exactamente
84
+ // Pre-filtrar rutas candidatas usando índices
85
+ const pathnameSegments = pathname.split('/').filter(s => s !== '');
86
+ const segmentCount = pathnameSegments.length;
87
+ const firstSegment = pathnameSegments.length > 0 ? pathnameSegments[0] : '';
88
+
89
+ // Obtener rutas candidatas basadas en número de segmentos y primer segmento
90
+ const candidateRoutes = this.getCandidateRoutes(method, segmentCount, firstSegment, pathname);
91
+
92
+ // Buscar rutas estáticas que coincidan exactamente
93
+ const staticExactMatch = this.findStaticExactMatch(candidateRoutes, pathname);
94
+ if (staticExactMatch) {
95
+ return {
96
+ route: staticExactMatch,
97
+ params: {}
98
+ };
99
+ }
100
+
101
+ // Buscar rutas parametrizadas entre los candidatos
102
+ const parametrizedMatch = this.findParametrizedMatch(candidateRoutes, pathname);
103
+ if (parametrizedMatch) {
104
+ return parametrizedMatch;
105
+ }
106
+
107
+ // Buscar rutas estáticas (prefijos) entre los candidatos
108
+ const staticPrefixMatch = this.findStaticPrefixMatch(candidateRoutes, pathname);
109
+ if (staticPrefixMatch) {
110
+ return staticPrefixMatch;
111
+ }
112
+
113
+ return null;
114
+ }
115
+
116
+ /**
117
+ * Busca rutas exactas usando el índice
118
+ * @param {string} method - Método HTTP
119
+ * @param {string} pathname - Ruta a buscar
120
+ * @returns {Object|null} - Ruta exacta encontrada o null
121
+ */
122
+ findExactMatch(method, pathname) {
123
+ const methodPathKey = `${method}:${pathname}`;
124
+ return this.exactRoutes.get(methodPathKey) || null;
125
+ }
126
+
127
+ /**
128
+ * Obtiene rutas candidatas basadas en número de segmentos y primer segmento
129
+ * @param {string} method - Método HTTP
130
+ * @param {number} segmentCount - Número de segmentos de la ruta buscada
131
+ * @param {string} firstSegment - Primer segmento de la ruta buscada
132
+ * @param {string} pathname - Ruta completa buscada
133
+ * @returns {Array} - Array de rutas candidatas
134
+ */
135
+ getCandidateRoutes(method, segmentCount, firstSegment, pathname) {
136
+ const candidates = [];
137
+
138
+ // Si existe un bucket para este método, número de segmentos y primer segmento
139
+ if (this.routeBuckets[method] &&
140
+ this.routeBuckets[method][segmentCount] &&
141
+ this.routeBuckets[method][segmentCount][firstSegment]) {
142
+ candidates.push(...this.routeBuckets[method][segmentCount][firstSegment]);
143
+ }
144
+
145
+ // También agregar rutas de otros primeros segmentos para este método y número de segmentos
146
+ // (por si hay rutas parametrizadas que también podrían coincidir)
147
+ if (this.routeBuckets[method] && this.routeBuckets[method][segmentCount]) {
148
+ for (const segment in this.routeBuckets[method][segmentCount]) {
149
+ if (segment !== firstSegment) {
150
+ candidates.push(...this.routeBuckets[method][segmentCount][segment]);
151
+ }
152
+ }
153
+ }
154
+
155
+ // Agregar rutas estáticas que podrían coincidir por prefijo
156
+ if (this.routeBuckets[method]) {
157
+ for (const count in this.routeBuckets[method]) {
158
+ for (const segment in this.routeBuckets[method][count]) {
159
+ const routes = this.routeBuckets[method][count][segment];
160
+ for (const route of routes) {
161
+ if (route.isStatic && pathname.startsWith(route.path)) {
162
+ candidates.push(route);
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+
169
+ return candidates;
170
+ }
171
+
172
+ /**
173
+ * Busca rutas estáticas con coincidencia exacta entre candidatos
174
+ * @param {Array} candidateRoutes - Rutas candidatas
175
+ * @param {string} pathname - Ruta a buscar
176
+ * @returns {Object|null} - Ruta estática exacta encontrada o null
177
+ */
178
+ findStaticExactMatch(candidateRoutes, pathname) {
179
+ for (const route of candidateRoutes) {
42
180
  if (route.isStatic && route.path === pathname) {
43
- return {
44
- route: route,
45
- params: {}
46
- };
181
+ return route;
47
182
  }
48
183
  }
184
+ return null;
185
+ }
49
186
 
50
- // Buscar rutas parametrizadas
51
- // Pero antes de devolver una ruta parametrizada, verificar si hay una ruta estática más específica
52
- const parametrizedMatches = [];
53
- for (const route of routes) {
54
- if (route.method !== method) continue;
55
-
56
- // Convertir ruta parametrizada a expresión regular
57
- const routeRegex = this.pathToRegex(route.path);
58
- const match = pathname.match(routeRegex);
59
-
60
- if (match) {
61
- const params = this.extractParams(route.path, pathname);
62
- parametrizedMatches.push({
63
- route: route,
64
- params: params
65
- });
187
+ /**
188
+ * Busca rutas parametrizadas entre candidatos
189
+ * @param {Array} candidateRoutes - Rutas candidatas
190
+ * @param {string} pathname - Ruta a buscar
191
+ * @returns {Object|null} - Ruta parametrizada encontrada o null
192
+ */
193
+ findParametrizedMatch(candidateRoutes, pathname) {
194
+ for (const route of candidateRoutes) {
195
+ if (this.isParametrizedRoute(route.path)) {
196
+ // Convertir ruta parametrizada a expresión regular
197
+ const routeRegex = this.pathToRegex(route.path);
198
+ const match = pathname.match(routeRegex);
199
+
200
+ if (match) {
201
+ const params = this.extractParams(route.path, pathname);
202
+ return {
203
+ route: route,
204
+ params: params
205
+ };
206
+ }
66
207
  }
67
208
  }
209
+ return null;
210
+ }
68
211
 
69
- // Buscar rutas estáticas (prefijos)
212
+ /**
213
+ * Busca rutas estáticas por prefijo entre candidatos
214
+ * @param {Array} candidateRoutes - Rutas candidatas
215
+ * @param {string} pathname - Ruta a buscar
216
+ * @returns {Object|null} - Ruta estática por prefijo encontrada o null
217
+ */
218
+ findStaticPrefixMatch(candidateRoutes, pathname) {
70
219
  const staticMatches = [];
71
- for (const route of routes) {
72
- if (route.method !== method) continue;
73
-
74
- // Para rutas estáticas, verificar si la ruta solicitada comienza con el prefijo de la ruta estática
220
+ for (const route of candidateRoutes) {
75
221
  if (route.isStatic && pathname.startsWith(route.path)) {
76
222
  // Verificar que sea exactamente el prefijo o que haya una barra después del prefijo
77
223
  const remainingPath = pathname.substring(route.path.length);
@@ -85,27 +231,40 @@ class RouteMatcher {
85
231
  }
86
232
  }
87
233
 
88
- // Prioridad: si hay rutas estáticas que coinciden, y la solicitud parece ser para un archivo
89
- // (tiene extensión), dar prioridad a la ruta estática más específica
90
- const hasFileExtension = /\.[^.]+$/.test(pathname);
91
-
92
- if (hasFileExtension && staticMatches.length > 0) {
234
+ if (staticMatches.length > 0) {
93
235
  // Ordenar por especificidad (longitud del prefijo, descendente) y tomar la más específica
94
236
  staticMatches.sort((a, b) => b.specificity - a.specificity);
95
- // Para solicitudes de archivos, dar prioridad a rutas estáticas más específicas
96
- return staticMatches[0]; // Devolver la coincidencia estática más específica
97
- } else if (parametrizedMatches.length > 0) {
98
- // Para solicitudes sin extensión o rutas API, dar prioridad a rutas parametrizadas
99
- return parametrizedMatches[0]; // Devolver la primera coincidencia parametrizada
100
- } else if (staticMatches.length > 0) {
101
- // Si no hay coincidencias parametrizadas, usar la estática más específica
102
- staticMatches.sort((a, b) => b.specificity - a.specificity);
103
237
  return staticMatches[0];
104
238
  }
105
239
 
106
240
  return null;
107
241
  }
108
242
 
243
+ /**
244
+ * Determina si una ruta es parametrizada
245
+ * @param {string} path - Ruta a verificar
246
+ * @returns {boolean} - True si la ruta contiene parámetros
247
+ */
248
+ isParametrizedRoute(path) {
249
+ return path.includes(':');
250
+ }
251
+
252
+ /**
253
+ * Obtiene el conteo total de rutas almacenadas en índices
254
+ * @returns {number} - Número total de rutas indexadas
255
+ */
256
+ getStoredRoutesCount() {
257
+ let count = 0;
258
+ for (const method in this.routeBuckets) {
259
+ for (const segmentCount in this.routeBuckets[method]) {
260
+ for (const firstSegment in this.routeBuckets[method][segmentCount]) {
261
+ count += this.routeBuckets[method][segmentCount][firstSegment].length;
262
+ }
263
+ }
264
+ }
265
+ return count;
266
+ }
267
+
109
268
  /**
110
269
  * Convierte una ruta con parámetros a expresión regular
111
270
  * @param {string} path - Ruta con posibles parámetros
@@ -131,8 +290,20 @@ class RouteMatcher {
131
290
  const regexPath = escapedPath.replace(/:([a-zA-Z0-9_]+)/g, '([^/]+?)');
132
291
  const regex = new RegExp(`^${regexPath}$`);
133
292
 
134
- // Almacenar en caché
135
- this.routeRegexCache.set(path, regex);
293
+ // Verificar con hook si se debe cachear esta expresión regular
294
+ const shouldCache = this.hooks ?
295
+ this.hooks.applyFilters('should_cache_route_regex', true, path, regex) : true;
296
+
297
+ if (shouldCache) {
298
+ // Almacenar en caché
299
+ this.routeRegexCache.set(path, regex);
300
+
301
+ // Disparar hook después de cachear
302
+ if (this.hooks) {
303
+ this.hooks.doAction('route_regex_cached', path, regex);
304
+ }
305
+ }
306
+
136
307
  return regex;
137
308
  }
138
309
 
@@ -169,6 +340,23 @@ class RouteMatcher {
169
340
 
170
341
  return params;
171
342
  }
343
+
344
+ /**
345
+ * Limpia el caché de expresiones regulares
346
+ */
347
+ clearCache() {
348
+ // Disparar hook antes de limpiar el caché
349
+ if (this.hooks) {
350
+ this.hooks.doAction('before_route_regex_cache_clear', this);
351
+ }
352
+
353
+ this.routeRegexCache.clear();
354
+
355
+ // Disparar hook después de limpiar el caché
356
+ if (this.hooks) {
357
+ this.hooks.doAction('route_regex_cache_cleared');
358
+ }
359
+ }
172
360
  }
173
361
 
174
362
  module.exports = RouteMatcher;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Almacenamiento global de estadísticas para el framework JERK
3
+ */
4
+
5
+ // Crear un objeto global para almacenar estadísticas
6
+ const globalStats = {
7
+ requestsProcessed: 0,
8
+ responsesSent: 0,
9
+ requestBytes: 0,
10
+ responseBytes: 0,
11
+ routeAccesses: new Map(), // Contador de accesos por ruta
12
+ endpointHits: new Map(), // Contador de hits por endpoint
13
+ lastRequests: [] // Últimas solicitudes para análisis
14
+ };
15
+
16
+ module.exports = { globalStats };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Almacenamiento global de información del caché de vistas para el framework JERK
3
+ */
4
+
5
+ // Crear un objeto global para almacenar información del caché de vistas
6
+ const globalViewCacheInfo = {
7
+ viewCache: new Map(), // Cache de vistas compiladas
8
+ cacheStats: {
9
+ hits: 0,
10
+ misses: 0,
11
+ totalViews: 0
12
+ },
13
+ viewDependencies: new Map() // Registro de dependencias entre vistas (inclusiones)
14
+ };
15
+
16
+ module.exports = { globalViewCacheInfo };
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Sistema de Almacenamiento Global de Estadísticas del WAF (Web Application Firewall)
3
+ * Almacenamiento centralizado de datos del firewall para acceso eficiente
4
+ */
5
+
6
+ // Objeto global para almacenar estadísticas del WAF
7
+ const globalWAFStats = {
8
+ // Contadores de solicitudes
9
+ requestsProcessed: 0,
10
+ responsesSent: 0,
11
+
12
+ // Contadores de bytes
13
+ requestBytes: 0,
14
+ responseBytes: 0,
15
+
16
+ // Registro de accesos a rutas
17
+ routeAccesses: new Map(), // Mapa de rutas y número de accesos
18
+
19
+ // Registro de hits a endpoints
20
+ endpointHits: new Map(), // Mapa de endpoints y número de hits
21
+
22
+ // IPs bloqueadas temporalmente
23
+ blockedIPs: new Map(), // Mapa de IPs bloqueadas con información de bloqueo
24
+
25
+ // IPs en lista blanca (nunca bloqueadas)
26
+ whitelist: new Set(),
27
+
28
+ // IPs en lista negra (siempre bloqueadas)
29
+ blacklist: new Set(),
30
+
31
+ // Reglas de seguridad activas
32
+ securityRules: new Map(), // Mapa de reglas de seguridad
33
+
34
+ // Reglas de headers X-
35
+ xHeaderRules: new Map(), // Mapa de reglas de headers X-
36
+
37
+ // Tipos de ataques detectados
38
+ attackTypes: new Set(), // Conjunto de tipos de ataques detectados
39
+
40
+ // Contador de intentos de ataque
41
+ attackAttempts: 0,
42
+
43
+ // Registros de seguridad
44
+ securityLogs: [],
45
+
46
+ // Estadísticas de rendimiento
47
+ performanceStats: {
48
+ startTime: new Date().toISOString(),
49
+ requestProcessingTime: 0,
50
+ averageResponseTime: 0
51
+ }
52
+ };
53
+
54
+ module.exports = { globalWAFStats };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jerkjs",
3
- "version": "2.5.6",
4
- "description": "JERK Framework v2.5.6 - A comprehensive framework for building secure and scalable APIs with frontend support, sessions, template engine, integration with qbuilderjs, complete MVC architecture with models, enhanced route loading from directory, improved model loading system, and fixed routing issues with static and parametrized routes",
3
+ "version": "2.6.1",
4
+ "description": "JERK Framework v2.6.1 - A comprehensive framework for building secure and scalable APIs with frontend support, sessions, template engine, integration with qbuilderjs, complete MVC architecture with models, enhanced route loading from directory, improved model loading system, administration extension, queue management system, route cache management module, and fixed routing issues with static and parametrized routes",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
7
  "test": "echo \"Error: no test specified\" && exit 1",
package/test-colors.js ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Script de prueba para verificar que los colores funcionan en el módulo de cache de rutas
5
+ * Este script simula la interacción con el módulo para verificar la visualización de comandos en color
6
+ */
7
+
8
+ // Simulación de socket para probar la funcionalidad de color
9
+ const mockSocket = {
10
+ data: '',
11
+ write: function(text) {
12
+ this.data += text;
13
+ // En un entorno real, esto escribiría en el socket
14
+ process.stdout.write(text);
15
+ }
16
+ };
17
+
18
+ // Importar el módulo
19
+ const RouteCacheModule = require('./lib/admin/modules/RouteCacheModule');
20
+
21
+ // Crear una instancia simulada de adminExtension
22
+ const mockAdminExtension = {
23
+ frameworkInstance: {
24
+ routeMatcher: {
25
+ routeRegexCache: new Map([
26
+ ['/users/:id', /^\/users\/([^\/]+?)$/],
27
+ ['/posts/:slug', /^\/posts\/([^\/]+?)$/]
28
+ ]),
29
+ exactRoutes: new Map([
30
+ ['GET:/api/users', { handler: { name: 'getUserHandler' }, isStatic: false }],
31
+ ['POST:/api/users', { handler: { name: 'createUserHandler' }, isStatic: false }]
32
+ ]),
33
+ indexesValid: true
34
+ }
35
+ }
36
+ };
37
+
38
+ // Crear instancia del módulo
39
+ const routeCacheModule = new RouteCacheModule(mockAdminExtension);
40
+
41
+ console.log('Probando el módulo de cache de rutas con colores...\n');
42
+
43
+ // Probar el manejo de un comando desconocido para ver el mensaje con comandos en verde
44
+ routeCacheModule.handleCommand('comando-invalido', mockSocket);
45
+
46
+ console.log('\nFin de la prueba.');
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Script de prueba para verificar que el alias '?' funciona en el sistema de administración
5
+ */
6
+
7
+ const { APIServer } = require('./index.js');
8
+
9
+ // Crear instancia del servidor
10
+ const server = new APIServer({ port: 3001 }); // Usar un puerto diferente para evitar conflictos
11
+
12
+ // Agregar una ruta de ejemplo
13
+ server.addRoute('GET', '/', (req, res) => {
14
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
15
+ res.end('Hola Mundo!');
16
+ });
17
+
18
+ // Inicializar la extensión de administración
19
+ server.initializeAdminExtension({
20
+ port: 9998, // Usar un puerto diferente para evitar conflictos
21
+ host: '127.0.0.1'
22
+ });
23
+
24
+ console.log('Servidor de prueba iniciado en http://localhost:3001');
25
+ console.log('Servidor de administración en tcp://localhost:9998');
26
+ console.log('\nPara probar el alias "?":');
27
+ console.log('- Conéctate al servidor de administración con: telnet localhost 9998 o nc localhost 9998');
28
+ console.log('- Escribe "?" y presiona Enter para ver la ayuda');
29
+
30
+ // Iniciar el servidor
31
+ server.start();