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
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Módulo de Caché para la Extensión de Administración de JERK Framework
3
+ * Submódulo personalizado para mostrar y gestionar el sistema de caché
4
+ */
5
+
6
+ class CacheModule {
7
+ /**
8
+ * Constructor del módulo de caché
9
+ * @param {Object} adminExtension - Instancia de la extensión de administración
10
+ */
11
+ constructor(adminExtension) {
12
+ this.adminExtension = adminExtension;
13
+ this.name = 'Cache Module';
14
+ this.description = 'Módulo para administrar y monitorear el sistema de caché';
15
+ this.commands = ['cache', 'cache-stats', 'cache-clear', 'cache-info'];
16
+ }
17
+
18
+ /**
19
+ * Manejador para los comandos del módulo de caché
20
+ * @param {string} command - Comando a ejecutar
21
+ * @param {Object} socket - Socket de la conexión
22
+ */
23
+ handleCommand(command, socket) {
24
+ switch (command) {
25
+ case 'cache':
26
+ case 'cache-info':
27
+ this.showCacheInfo(socket);
28
+ break;
29
+ case 'cache-stats':
30
+ this.showCacheStats(socket);
31
+ break;
32
+ case 'cache-clear':
33
+ this.clearCache(socket);
34
+ break;
35
+ default:
36
+ socket.write(`Comando desconocido para el módulo de caché: ${command}\n`);
37
+ socket.write(`Comandos disponibles: cache, cache-info, cache-stats, cache-clear\n\n`);
38
+ socket.write(`> `);
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Muestra información general del sistema de caché
44
+ * @param {Object} socket - Socket de la conexión
45
+ */
46
+ showCacheInfo(socket) {
47
+ const routeMatcher = this.getRouteMatcher();
48
+ const viewEngine = this.getViewEngine();
49
+
50
+ const routeCacheSize = routeMatcher ? routeMatcher.routeRegexCache.size : 0;
51
+ const viewCacheSize = viewEngine ? (viewEngine.viewCache ? viewEngine.viewCache.size : 0) : 0;
52
+
53
+ socket.write('\n=== Información del Sistema de Caché ===\n');
54
+ socket.write(`Caché de Expresiones Regulares: ${routeCacheSize} entradas\n`);
55
+ socket.write(`Caché de Vistas: ${viewCacheSize} entradas\n`);
56
+ socket.write(`\n> `);
57
+ }
58
+
59
+ /**
60
+ * Muestra estadísticas detalladas del sistema de caché
61
+ * @param {Object} socket - Socket de la conexión
62
+ */
63
+ showCacheStats(socket) {
64
+ const routeMatcher = this.getRouteMatcher();
65
+ const viewEngine = this.getViewEngine();
66
+
67
+ const routeCacheSize = routeMatcher ? routeMatcher.routeRegexCache.size : 0;
68
+ const viewCacheSize = viewEngine ? (viewEngine.viewCache ? viewEngine.viewCache.size : 0) : 0;
69
+
70
+ socket.write('\n=== Estadísticas del Sistema de Caché ===\n');
71
+ socket.write(`Tamaño del caché de rutas: ${routeCacheSize}\n`);
72
+ socket.write(`Tamaño del caché de vistas: ${viewCacheSize}\n`);
73
+
74
+ // Mostrar algunos elementos del caché si existen
75
+ if (routeMatcher && routeMatcher.routeRegexCache.size > 0) {
76
+ socket.write('\nEntradas de caché de rutas (primeras 5):\n');
77
+ let count = 0;
78
+ for (const [path, regex] of routeMatcher.routeRegexCache) {
79
+ if (count >= 5) break;
80
+ socket.write(` ${path}: ${regex}\n`);
81
+ count++;
82
+ }
83
+ }
84
+
85
+ if (viewEngine && viewEngine.viewCache && viewEngine.viewCache.size > 0) {
86
+ socket.write('\nEntradas de caché de vistas (primeras 5):\n');
87
+ let count = 0;
88
+ for (const [path, content] of viewEngine.viewCache) {
89
+ if (count >= 5) break;
90
+ socket.write(` ${path}: ${content.length} chars\n`);
91
+ count++;
92
+ }
93
+ }
94
+
95
+ socket.write(`\n> `);
96
+ }
97
+
98
+ /**
99
+ * Limpia todos los sistemas de caché
100
+ * @param {Object} socket - Socket de la conexión
101
+ */
102
+ clearCache(socket) {
103
+ const routeMatcher = this.getRouteMatcher();
104
+ const viewEngine = this.getViewEngine();
105
+
106
+ let clearedSystems = [];
107
+
108
+ if (routeMatcher) {
109
+ routeMatcher.clearCache(); // Usar el nuevo método que dispara hooks
110
+ clearedSystems.push('caché de rutas');
111
+ }
112
+
113
+ if (viewEngine && viewEngine.viewCache) {
114
+ viewEngine.clearCache(); // Usar el nuevo método que dispara hooks
115
+ clearedSystems.push('caché de vistas');
116
+ }
117
+
118
+ socket.write(`\nSistemas de caché limpiados: ${clearedSystems.join(', ')}\n`);
119
+ socket.write(`\n> `);
120
+ }
121
+
122
+ /**
123
+ * Obtiene la instancia de RouteMatcher si está disponible
124
+ * @returns {Object|null} - Instancia de RouteMatcher o null
125
+ */
126
+ getRouteMatcher() {
127
+ if (this.adminExtension.frameworkInstance && this.adminExtension.frameworkInstance.routeMatcher) {
128
+ return this.adminExtension.frameworkInstance.routeMatcher;
129
+ }
130
+ return null;
131
+ }
132
+
133
+ /**
134
+ * Obtiene la instancia de ViewEngine si está disponible
135
+ * @returns {Object|null} - Instancia de ViewEngine o null
136
+ */
137
+ getViewEngine() {
138
+ if (this.adminExtension.frameworkInstance && this.adminExtension.frameworkInstance.viewEngine) {
139
+ return this.adminExtension.frameworkInstance.viewEngine;
140
+ }
141
+ return null;
142
+ }
143
+ }
144
+
145
+ module.exports = CacheModule;
@@ -0,0 +1,414 @@
1
+ /**
2
+ * Módulo de Generación de Controladores para la Extensión de Administración de JERK Framework
3
+ * Módulo personalizado para crear controladores de manera interactiva
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ class ControllerGeneratorModule {
10
+ /**
11
+ * Constructor del módulo de generación de controladores
12
+ * @param {Object} adminExtension - Instancia de la extensión de administración
13
+ */
14
+ constructor(adminExtension) {
15
+ this.adminExtension = adminExtension;
16
+ this.name = 'Controller Generator Module';
17
+ this.description = 'Módulo para crear controladores de manera interactiva';
18
+ this.commands = ['generate-controller', 'gen-ctrl', 'new-ctrl'];
19
+
20
+ // Estado para el proceso interactivo
21
+ this.currentProcess = new Map(); // Almacena el estado de generación por socket
22
+ }
23
+
24
+ /**
25
+ * Manejador para los comandos del módulo de generación de controladores
26
+ * @param {string} command - Comando a ejecutar
27
+ * @param {Object} socket - Socket de la conexión
28
+ */
29
+ handleCommand(command, socket) {
30
+ switch (command) {
31
+ case 'generate-controller':
32
+ case 'gen-ctrl':
33
+ case 'new-ctrl':
34
+ this.startControllerGeneration(socket);
35
+ break;
36
+ default:
37
+ socket.write(`Comando desconocido para el módulo de generación de controladores: ${command}\n`);
38
+ socket.write(`Comandos disponibles: generate-controller, gen-ctrl, new-ctrl\n\n`);
39
+ socket.write(`> `);
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Inicia el proceso interactivo de generación de controlador
45
+ * @param {Object} socket - Socket de la conexión
46
+ */
47
+ startControllerGeneration(socket) {
48
+ const sessionId = this.getSessionId(socket);
49
+
50
+ // Inicializar el estado de la sesión
51
+ this.currentProcess.set(sessionId, {
52
+ step: 'name',
53
+ controllerData: {}
54
+ });
55
+
56
+ socket.write('\n=== Generador de Controladores ===\n');
57
+ socket.write('Por favor, introduce el nombre del controlador (sin la palabra "Controller"):\n');
58
+ socket.write('(Ej: "User", "Product", "Home")\n');
59
+ socket.write('Para cancelar, escribe "cancel"\n');
60
+ socket.write('> ');
61
+ }
62
+
63
+ /**
64
+ * Obtiene un ID único para la sesión del socket
65
+ * @param {Object} socket - Socket de la conexión
66
+ * @returns {string} - ID de sesión
67
+ */
68
+ getSessionId(socket) {
69
+ // Usar la dirección remota y puerto como identificador único
70
+ return `${socket.remoteAddress}:${socket.remotePort}`;
71
+ }
72
+
73
+ /**
74
+ * Procesa la entrada del usuario durante el proceso interactivo
75
+ * @param {Object} socket - Socket de la conexión
76
+ * @param {string} input - Entrada del usuario
77
+ */
78
+ processInput(socket, input) {
79
+ const sessionId = this.getSessionId(socket);
80
+ const processState = this.currentProcess.get(sessionId);
81
+
82
+ if (!processState) {
83
+ socket.write('No hay proceso activo. Usa "generate-controller" para iniciar.\n> ');
84
+ return;
85
+ }
86
+
87
+ const userInput = input.trim();
88
+
89
+ if (userInput.toLowerCase() === 'cancel') {
90
+ this.cancelProcess(socket, sessionId);
91
+ return;
92
+ }
93
+
94
+ switch (processState.step) {
95
+ case 'name':
96
+ this.handleControllerName(socket, sessionId, userInput);
97
+ break;
98
+ case 'methods':
99
+ this.handleMethodsSelection(socket, sessionId, userInput);
100
+ break;
101
+ case 'confirm':
102
+ this.handleConfirmation(socket, sessionId, userInput);
103
+ break;
104
+ case 'path':
105
+ this.handleControllerPath(socket, sessionId, userInput);
106
+ break;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Maneja la entrada del nombre del controlador
112
+ * @param {Object} socket - Socket de la conexión
113
+ * @param {string} sessionId - ID de la sesión
114
+ * @param {string} name - Nombre del controlador
115
+ */
116
+ handleControllerName(socket, sessionId, name) {
117
+ if (!name) {
118
+ socket.write('El nombre no puede estar vacío. Por favor, introduce un nombre válido:\n> ');
119
+ return;
120
+ }
121
+
122
+ // Validar nombre (solo letras, números y guiones bajos)
123
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
124
+ socket.write('Nombre inválido. Usa solo letras, números y guiones bajos, comenzando con una letra o guión bajo:\n> ');
125
+ return;
126
+ }
127
+
128
+ const processState = this.currentProcess.get(sessionId);
129
+ processState.controllerData.name = name;
130
+ processState.step = 'methods';
131
+
132
+ socket.write(`\nNombre del controlador: ${name}Controller\n`);
133
+ socket.write('\nSelecciona los métodos que deseas incluir (separados por comas):\n');
134
+ socket.write('Opciones disponibles:\n');
135
+ socket.write(' 1. index - Método para mostrar lista de elementos\n');
136
+ socket.write(' 2. show - Método para mostrar un elemento específico\n');
137
+ socket.write(' 3. create - Método para crear un nuevo elemento\n');
138
+ socket.write(' 4. update - Método para actualizar un elemento\n');
139
+ socket.write(' 5. delete - Método para eliminar un elemento\n');
140
+ socket.write(' 6. All - Todos los métodos anteriores\n');
141
+ socket.write('\nEjemplos: "1,2,3" o "index,show,create" o "All"\n> ');
142
+ }
143
+
144
+ /**
145
+ * Maneja la selección de métodos
146
+ * @param {Object} socket - Socket de la conexión
147
+ * @param {string} sessionId - ID de la sesión
148
+ * @param {string} selection - Selección de métodos
149
+ */
150
+ handleMethodsSelection(socket, sessionId, selection) {
151
+ const processState = this.currentProcess.get(sessionId);
152
+ const methodsMap = {
153
+ '1': 'index', '2': 'show', '3': 'create', '4': 'update', '5': 'delete'
154
+ };
155
+
156
+ let selectedMethods = [];
157
+
158
+ if (selection.toLowerCase() === 'all') {
159
+ selectedMethods = ['index', 'show', 'create', 'update', 'delete'];
160
+ } else {
161
+ // Separar por comas y procesar cada selección
162
+ const selections = selection.split(',').map(s => s.trim());
163
+
164
+ for (const sel of selections) {
165
+ if (methodsMap[sel]) {
166
+ selectedMethods.push(methodsMap[sel]);
167
+ } else if (['index', 'show', 'create', 'update', 'delete'].includes(sel)) {
168
+ selectedMethods.push(sel);
169
+ } else {
170
+ socket.write(`Selección inválida: ${sel}. Por favor, selecciona métodos válidos:\n> `);
171
+ return;
172
+ }
173
+ }
174
+ }
175
+
176
+ // Eliminar duplicados
177
+ selectedMethods = [...new Set(selectedMethods)];
178
+
179
+ processState.controllerData.methods = selectedMethods;
180
+ processState.step = 'path';
181
+
182
+ socket.write(`\nMétodos seleccionados: ${selectedMethods.join(', ')}\n`);
183
+ socket.write('\nIndica la ruta donde deseas crear el archivo del controlador:\n');
184
+ socket.write('(Ruta relativa desde la raíz del proyecto, ej: "./controllers/", "./src/controllers/")\n');
185
+ socket.write('Deja vacío para usar "./controllers/" por defecto:\n> ');
186
+ }
187
+
188
+ /**
189
+ * Maneja la selección de la ruta del controlador
190
+ * @param {Object} socket - Socket de la conexión
191
+ * @param {string} sessionId - ID de la sesión
192
+ * @param {string} pathInput - Ruta ingresada
193
+ */
194
+ handleControllerPath(socket, sessionId, pathInput) {
195
+ const processState = this.currentProcess.get(sessionId);
196
+
197
+ // Usar la ruta por defecto si no se proporciona ninguna
198
+ const controllerPath = pathInput.trim() || './controllers/';
199
+
200
+ // Asegurarse de que termine con '/'
201
+ const normalizedPath = controllerPath.endsWith('/') ? controllerPath : controllerPath + '/';
202
+
203
+ processState.controllerData.path = normalizedPath;
204
+ processState.step = 'confirm';
205
+
206
+ socket.write(`\nRuta del controlador: ${normalizedPath}\n`);
207
+ this.showControllerPreview(socket, processState.controllerData);
208
+ }
209
+
210
+ /**
211
+ * Muestra una vista previa del controlador que se va a generar
212
+ * @param {Object} socket - Socket de la conexión
213
+ * @param {Object} controllerData - Datos del controlador
214
+ */
215
+ showControllerPreview(socket, controllerData) {
216
+ socket.write('\n=== Vista Previa del Controlador ===\n');
217
+ socket.write(`Nombre del archivo: ${controllerData.name}Controller.js\n\n`);
218
+
219
+ let preview = `const ControllerBase = require('../../index.js').ControllerBase;\n\n`;
220
+ preview += `class ${controllerData.name}Controller extends ControllerBase {\n`;
221
+
222
+ for (const method of controllerData.methods) {
223
+ preview += this.generateMethodSkeleton(method, controllerData.name);
224
+ }
225
+
226
+ preview += `}\n\n`;
227
+ preview += `module.exports = new ${controllerData.name}Controller();\n`;
228
+
229
+ socket.write(preview);
230
+ socket.write('\n¿Deseas crear este controlador? (sí/no):\n> ');
231
+ }
232
+
233
+ /**
234
+ * Genera el esqueleto de un método
235
+ * @param {string} method - Nombre del método
236
+ * @param {string} controllerName - Nombre del controlador
237
+ * @returns {string} - Código del método
238
+ */
239
+ generateMethodSkeleton(method, controllerName) {
240
+ let skeleton = '';
241
+
242
+ switch (method) {
243
+ case 'index':
244
+ skeleton += ` async index(req, res) {\n`;
245
+ skeleton += ` try {\n`;
246
+ skeleton += ` // Lógica para obtener y mostrar una lista de ${controllerName.toLowerCase()}s\n`;
247
+ skeleton += ` const items = await this.loadModel('${controllerName}Model').then(model => model.find({}));\n`;
248
+ skeleton += ` \n`;
249
+ skeleton += ` res.render('${controllerName.toLowerCase()}/index', { \n`;
250
+ skeleton += ` title: '${controllerName}s',\n`;
251
+ skeleton += ` items\n`;
252
+ skeleton += ` });\n`;
253
+ skeleton += ` } catch (error) {\n`;
254
+ skeleton += ` console.error('Error en el método index:', error);\n`;
255
+ skeleton += ` res.writeHead(500, { 'Content-Type': 'application/json' });\n`;
256
+ skeleton += ` res.end(JSON.stringify({ error: 'Error interno del servidor' }));\n`;
257
+ skeleton += ` }\n`;
258
+ skeleton += ` }\n\n`;
259
+ break;
260
+
261
+ case 'show':
262
+ skeleton += ` async show(req, res) {\n`;
263
+ skeleton += ` try {\n`;
264
+ skeleton += ` const id = req.params.id;\n`;
265
+ skeleton += ` // Lógica para obtener y mostrar un ${controllerName.toLowerCase()} específico\n`;
266
+ skeleton += ` const item = await this.loadModel('${controllerName}Model').then(model => model.findOne({ id: parseInt(id) }));\n`;
267
+ skeleton += ` \n`;
268
+ skeleton += ` if (!item) {\n`;
269
+ skeleton += ` res.writeHead(404, { 'Content-Type': 'application/json' });\n`;
270
+ skeleton += ` res.end(JSON.stringify({ error: '${controllerName} no encontrado' }));\n`;
271
+ skeleton += ` return;\n`;
272
+ skeleton += ` }\n`;
273
+ skeleton += ` \n`;
274
+ skeleton += ` res.render('${controllerName.toLowerCase()}/show', { \n`;
275
+ skeleton += ` title: '${controllerName} #' + id,\n`;
276
+ skeleton += ` item\n`;
277
+ skeleton += ` });\n`;
278
+ skeleton += ` } catch (error) {\n`;
279
+ skeleton += ` console.error('Error en el método show:', error);\n`;
280
+ skeleton += ` res.writeHead(500, { 'Content-Type': 'application/json' });\n`;
281
+ skeleton += ` res.end(JSON.stringify({ error: 'Error interno del servidor' }));\n`;
282
+ skeleton += ` }\n`;
283
+ skeleton += ` }\n\n`;
284
+ break;
285
+
286
+ case 'create':
287
+ skeleton += ` async create(req, res) {\n`;
288
+ skeleton += ` try {\n`;
289
+ skeleton += ` // Lógica para crear un nuevo ${controllerName.toLowerCase()}\n`;
290
+ skeleton += ` const data = req.body;\n`;
291
+ skeleton += ` const newItem = await this.loadModel('${controllerName}Model').then(model => model.create(data));\n`;
292
+ skeleton += ` \n`;
293
+ skeleton += ` res.writeHead(201, { 'Content-Type': 'application/json' });\n`;
294
+ skeleton += ` res.end(JSON.stringify({ success: true, data: newItem }));\n`;
295
+ skeleton += ` } catch (error) {\n`;
296
+ skeleton += ` console.error('Error en el método create:', error);\n`;
297
+ skeleton += ` res.writeHead(400, { 'Content-Type': 'application/json' });\n`;
298
+ skeleton += ` res.end(JSON.stringify({ error: error.message }));\n`;
299
+ skeleton += ` }\n`;
300
+ skeleton += ` }\n\n`;
301
+ break;
302
+
303
+ case 'update':
304
+ skeleton += ` async update(req, res) {\n`;
305
+ skeleton += ` try {\n`;
306
+ skeleton += ` const id = req.params.id;\n`;
307
+ skeleton += ` const data = req.body;\n`;
308
+ skeleton += ` // Lógica para actualizar un ${controllerName.toLowerCase()} existente\n`;
309
+ skeleton += ` const result = await this.loadModel('${controllerName}Model').then(model => model.update({ id: parseInt(id) }, data));\n`;
310
+ skeleton += ` \n`;
311
+ skeleton += ` res.writeHead(200, { 'Content-Type': 'application/json' });\n`;
312
+ skeleton += ` res.end(JSON.stringify({ success: true, data: result }));\n`;
313
+ skeleton += ` } catch (error) {\n`;
314
+ skeleton += ` console.error('Error en el método update:', error);\n`;
315
+ skeleton += ` res.writeHead(400, { 'Content-Type': 'application/json' });\n`;
316
+ skeleton += ` res.end(JSON.stringify({ error: error.message }));\n`;
317
+ skeleton += ` }\n`;
318
+ skeleton += ` }\n\n`;
319
+ break;
320
+
321
+ case 'delete':
322
+ skeleton += ` async delete(req, res) {\n`;
323
+ skeleton += ` try {\n`;
324
+ skeleton += ` const id = req.params.id;\n`;
325
+ skeleton += ` // Lógica para eliminar un ${controllerName.toLowerCase()}\n`;
326
+ skeleton += ` const result = await this.loadModel('${controllerName}Model').then(model => model.delete({ id: parseInt(id) }));\n`;
327
+ skeleton += ` \n`;
328
+ skeleton += ` res.writeHead(200, { 'Content-Type': 'application/json' });\n`;
329
+ skeleton += ` res.end(JSON.stringify({ success: true, message: '${controllerName} eliminado correctamente' }));\n`;
330
+ skeleton += ` } catch (error) {\n`;
331
+ skeleton += ` console.error('Error en el método delete:', error);\n`;
332
+ skeleton += ` res.writeHead(400, { 'Content-Type': 'application/json' });\n`;
333
+ skeleton += ` res.end(JSON.stringify({ error: error.message }));\n`;
334
+ skeleton += ` }\n`;
335
+ skeleton += ` }\n\n`;
336
+ break;
337
+ }
338
+
339
+ return skeleton;
340
+ }
341
+
342
+ /**
343
+ * Maneja la confirmación de creación del controlador
344
+ * @param {Object} socket - Socket de la conexión
345
+ * @param {string} sessionId - ID de la sesión
346
+ * @param {string} confirmation - Confirmación del usuario
347
+ */
348
+ handleConfirmation(socket, sessionId, confirmation) {
349
+ const processState = this.currentProcess.get(sessionId);
350
+
351
+ if (confirmation.toLowerCase() === 'sí' || confirmation.toLowerCase() === 'si' || confirmation.toLowerCase() === 'yes') {
352
+ this.createControllerFile(socket, processState.controllerData);
353
+ } else {
354
+ socket.write('\nGeneración del controlador cancelada.\n> ');
355
+ this.currentProcess.delete(sessionId);
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Crea el archivo del controlador
361
+ * @param {Object} socket - Socket de la conexión
362
+ * @param {Object} controllerData - Datos del controlador
363
+ */
364
+ createControllerFile(socket, controllerData) {
365
+ const { name, methods, path: controllerPath } = controllerData;
366
+
367
+ // Crear el contenido del archivo
368
+ let content = `const ControllerBase = require('../../index.js').ControllerBase;\n\n`;
369
+ content += `class ${name}Controller extends ControllerBase {\n`;
370
+
371
+ for (const method of methods) {
372
+ content += this.generateMethodSkeleton(method, name);
373
+ }
374
+
375
+ content += `}\n\n`;
376
+ content += `module.exports = new ${name}Controller();\n`;
377
+
378
+ // Crear directorio si no existe
379
+ const fullPath = path.join(process.cwd(), controllerPath);
380
+ if (!fs.existsSync(fullPath)) {
381
+ fs.mkdirSync(fullPath, { recursive: true });
382
+ }
383
+
384
+ // Ruta completa del archivo
385
+ const filePath = path.join(fullPath, `${name}Controller.js`);
386
+
387
+ try {
388
+ // Escribir el archivo
389
+ fs.writeFileSync(filePath, content);
390
+
391
+ socket.write(`\n✅ Controlador "${name}Controller" creado exitosamente en:\n${filePath}\n`);
392
+ socket.write(`\nMétodos incluidos: ${methods.join(', ')}\n`);
393
+ socket.write('\n> ');
394
+ } catch (error) {
395
+ socket.write(`\n❌ Error al crear el archivo: ${error.message}\n> `);
396
+ }
397
+
398
+ // Limpiar el estado
399
+ const sessionId = this.getSessionId(socket);
400
+ this.currentProcess.delete(sessionId);
401
+ }
402
+
403
+ /**
404
+ * Cancela el proceso de generación
405
+ * @param {Object} socket - Socket de la conexión
406
+ * @param {string} sessionId - ID de la sesión
407
+ */
408
+ cancelProcess(socket, sessionId) {
409
+ this.currentProcess.delete(sessionId);
410
+ socket.write('\nProceso cancelado.\n> ');
411
+ }
412
+ }
413
+
414
+ module.exports = ControllerGeneratorModule;