blackcoffee2 2.1.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 (170) hide show
  1. package/CHANGELOG.md +664 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +25 -0
  4. package/README.md +246 -0
  5. package/apps.zip +0 -0
  6. package/bin/adminclient +105 -0
  7. package/bin/blackcoffee +133 -0
  8. package/cli/admin-users.js +282 -0
  9. package/cli/commands/app.js +561 -0
  10. package/cli/commands/config.js +182 -0
  11. package/cli/commands/db.js +257 -0
  12. package/cli/commands/server.js +200 -0
  13. package/config/applications.json +5 -0
  14. package/config/database.json +28 -0
  15. package/config/database.json.example +23 -0
  16. package/config/server.json +32 -0
  17. package/controllers/admin/AdminController.js +529 -0
  18. package/controllers/admin/AdminViewController.js +90 -0
  19. package/controllers/admin/AuthController.js +293 -0
  20. package/controllers/admin/DatabaseAdminController.js +218 -0
  21. package/core/SQLiteAdapter.js +333 -0
  22. package/core/appLoader.js +385 -0
  23. package/core/databasePoolManager.js +431 -0
  24. package/core/hotReload.js +363 -0
  25. package/data/ADMIN-README.md +145 -0
  26. package/data/CHANGELOG.md +48 -0
  27. package/data/GTK3-NODE-PROPOSALS.md +410 -0
  28. package/data/admin-db.js +150 -0
  29. package/data/admin-gui.js +452 -0
  30. package/data/blackcoffee_admin.db-shm +0 -0
  31. package/data/blackcoffee_admin.db-wal +0 -0
  32. package/data/migrations/001_create_admin_users.sql +33 -0
  33. package/docs/APP_HOOKS_HANDLER.md +432 -0
  34. package/docs/APP_HOOKS_REQUIREMENTS.md +588 -0
  35. package/docs/ARCHITECTURE.md +435 -0
  36. package/docs/CREAR_APP_Y_USAR_POOLS.md +1595 -0
  37. package/docs/EVENTS_APP_MANUAL.md +289 -0
  38. package/docs/INSITU_BINARY_UPLOAD_PROPOSAL.md +186 -0
  39. package/docs/INSITU_FIREWALL_EXCEPTION.md +187 -0
  40. package/docs/ROADMAP.md +242 -0
  41. package/docs/ROADMAP.md.backup +243 -0
  42. package/includes/404-hooks.js +423 -0
  43. package/includes/adminAuth.js +214 -0
  44. package/includes/adminExtension.js +53 -0
  45. package/includes/appHooks.js +302 -0
  46. package/includes/initAdminDb.js +115 -0
  47. package/includes/routeLoader.js +67 -0
  48. package/includes/sessions.js +223 -0
  49. package/issues/001-duplicate-module-loading.md +92 -0
  50. package/manuales/ADMIN_EXTENSION_COMMANDS_MANUAL.md +261 -0
  51. package/manuales/ADMIN_EXTENSION_HOOK_EXAMPLE.md +28 -0
  52. package/manuales/ADMIN_EXTENSION_INTEGRATION_MANUAL.md +232 -0
  53. package/manuales/CACHE_REGEX_COMMANDS.md +136 -0
  54. package/manuales/CACHE_SYSTEM_MAP.md +206 -0
  55. package/manuales/CREACION_DE_CONTROLADORES_INSITU.md +383 -0
  56. package/manuales/QUEUE_CLI_MODULE_MANUAL.md +289 -0
  57. package/manuales/QUEUE_SYSTEM_MANUAL.md +320 -0
  58. package/manuales/ROUTE_CACHE_MODULE_MANUAL.md +205 -0
  59. package/manuales/SESSION_MANAGER_GUIDE.md +529 -0
  60. package/manuales/SESSION_SECURITY_FLAGS.md +174 -0
  61. package/manuales/WAF_MODULE_MANUAL.md +229 -0
  62. package/manuales/after_route_handler_filter_example.md +116 -0
  63. package/manuales/after_route_handler_usage.md +130 -0
  64. package/manuales/an/303/241lisis-completo-insitu-framework.md +213 -0
  65. package/manuales/async_hooks_promises_guide.md +325 -0
  66. package/manuales/before_route_handler_filter_example.md +97 -0
  67. package/manuales/before_route_handler_usage.md +122 -0
  68. package/manuales/hooks_chaining_conditions_guide.md +261 -0
  69. package/manuales/hooks_filters_documentation.md +493 -0
  70. package/manuales/hooks_filters_documentation_en.md +493 -0
  71. package/manuales/hooks_vs_middlewares_comparison.md +87 -0
  72. package/manuales/manual-mvc-completo.md +934 -0
  73. package/manuales/modulos_administracion.md +89 -0
  74. package/manuales/router_execution_points.md +74 -0
  75. package/manuales/static_file_hooks_usage.md +222 -0
  76. package/models/AdminUserModel.js +132 -0
  77. package/package.json +45 -0
  78. package/programatically/PRoutes.js +89 -0
  79. package/programatically/initFlow.js +211 -0
  80. package/public/admin/css/db-pools.css +336 -0
  81. package/public/admin/css/styles.css +310 -0
  82. package/public/admin/database.html +312 -0
  83. package/public/admin/index.html +116 -0
  84. package/public/admin/js/app.js +470 -0
  85. package/public/admin/js/db-pools.js +253 -0
  86. package/public/admin/login.html +278 -0
  87. package/public/assets/css/styles.css +477 -0
  88. package/public/assets/js/main.js +89 -0
  89. package/public/index.html +136 -0
  90. package/public/templates/404.html +158 -0
  91. package/routes/admin-views.json +20 -0
  92. package/routes/admin.json +38 -0
  93. package/routes/auth.json +32 -0
  94. package/routes/static.json +18 -0
  95. package/server.js +299 -0
  96. package/test-aplicacion.con-logisession/BlackCoffee.js +226 -0
  97. package/test-aplicacion.con-logisession/SSL_SETUP.md +53 -0
  98. package/test-aplicacion.con-logisession/certs/ca-certificate.pem +32 -0
  99. package/test-aplicacion.con-logisession/certs/ca-private-key.pem +52 -0
  100. package/test-aplicacion.con-logisession/certs/certificate-2048.pem +22 -0
  101. package/test-aplicacion.con-logisession/certs/certificate.pem +32 -0
  102. package/test-aplicacion.con-logisession/certs/private-key-2048.pem +28 -0
  103. package/test-aplicacion.con-logisession/certs/private-key.pem +52 -0
  104. package/test-aplicacion.con-logisession/config/iaQueueSetup.js +84 -0
  105. package/test-aplicacion.con-logisession/config/qwen-rules.json +39 -0
  106. package/test-aplicacion.con-logisession/controllers/analyticsController.js +117 -0
  107. package/test-aplicacion.con-logisession/controllers/auth/AdminAuthController.js +142 -0
  108. package/test-aplicacion.con-logisession/controllers/auth/AuthController.js +439 -0
  109. package/test-aplicacion.con-logisession/controllers/auth/AuthViewController.js +223 -0
  110. package/test-aplicacion.con-logisession/controllers/endpointController.js +66 -0
  111. package/test-aplicacion.con-logisession/controllers/example.js +183 -0
  112. package/test-aplicacion.con-logisession/controllers/iaQueueController.js +367 -0
  113. package/test-aplicacion.con-logisession/controllers/queueController.js +206 -0
  114. package/test-aplicacion.con-logisession/controllers/qwenQueueController.js +197 -0
  115. package/test-aplicacion.con-logisession/controllers/test.js +0 -0
  116. package/test-aplicacion.con-logisession/controllers/tracking/EventsNoFinishController.js +78 -0
  117. package/test-aplicacion.con-logisession/controllers/tracking/TrackingController.js +412 -0
  118. package/test-aplicacion.con-logisession/controllers/tracking/TrackingControllerWithLoadModel.js +437 -0
  119. package/test-aplicacion.con-logisession/hooks/admin-hooks.js +20 -0
  120. package/test-aplicacion.con-logisession/hooks/general-hooks.js +97 -0
  121. package/test-aplicacion.con-logisession/hooks/queue-hooks.js +64 -0
  122. package/test-aplicacion.con-logisession/hooks/route-directory-hooks.js +38 -0
  123. package/test-aplicacion.con-logisession/hooks/security-hooks.js +24 -0
  124. package/test-aplicacion.con-logisession/insitu-admin-client/README.md +69 -0
  125. package/test-aplicacion.con-logisession/insitu-admin-client/package.json +23 -0
  126. package/test-aplicacion.con-logisession/insitu-admin-client.js +257 -0
  127. package/test-aplicacion.con-logisession/models/ExampleModel.js +88 -0
  128. package/test-aplicacion.con-logisession/models/QueueJobModel.js +263 -0
  129. package/test-aplicacion.con-logisession/models/TokenModel.js +207 -0
  130. package/test-aplicacion.con-logisession/models/auth/AuthModel.js +66 -0
  131. package/test-aplicacion.con-logisession/models/auth/UserModel.js +189 -0
  132. package/test-aplicacion.con-logisession/models/tracking/CompletedCartModel.js +213 -0
  133. package/test-aplicacion.con-logisession/models/tracking/EventModel.js +366 -0
  134. package/test-aplicacion.con-logisession/models/tracking/EventsNoFinishModel.js +131 -0
  135. package/test-aplicacion.con-logisession/models/tracking/SessionModel.js +360 -0
  136. package/test-aplicacion.con-logisession/models/tracking/SiteFlowModel.js +286 -0
  137. package/test-aplicacion.con-logisession/models/tracking/TokenModel.js +207 -0
  138. package/test-aplicacion.con-logisession/package-lock.json +3313 -0
  139. package/test-aplicacion.con-logisession/package.json +32 -0
  140. package/test-aplicacion.con-logisession/public/blackcoffee-welcome/index.html +1339 -0
  141. package/test-aplicacion.con-logisession/public/css/style.css +64 -0
  142. package/test-aplicacion.con-logisession/public/ejemplo-estatica/index.html +18 -0
  143. package/test-aplicacion.con-logisession/public/ejemplo-estatica/script.js +16 -0
  144. package/test-aplicacion.con-logisession/public/ejemplo-estatica/styles.css +43 -0
  145. package/test-aplicacion.con-logisession/public/images/logo.svg +7 -0
  146. package/test-aplicacion.con-logisession/public/js/main.js +67 -0
  147. package/test-aplicacion.con-logisession/routes/analytics-routes.json +8 -0
  148. package/test-aplicacion.con-logisession/routes/auth-routes.json +98 -0
  149. package/test-aplicacion.con-logisession/routes/blackcoffee-welcome-routes.json +20 -0
  150. package/test-aplicacion.con-logisession/routes/duplicate-test-routes.json.disabled +16 -0
  151. package/test-aplicacion.con-logisession/routes/ejemplo-estatica-routes.json +11 -0
  152. package/test-aplicacion.con-logisession/routes/endpoints-routes.json +8 -0
  153. package/test-aplicacion.con-logisession/routes/ia-queue-routes.json +26 -0
  154. package/test-aplicacion.con-logisession/routes/product-routes.json.disabled +20 -0
  155. package/test-aplicacion.con-logisession/routes/queue-routes.json +32 -0
  156. package/test-aplicacion.con-logisession/routes/qwen-routes.json +14 -0
  157. package/test-aplicacion.con-logisession/routes/static-routes.json +29 -0
  158. package/test-aplicacion.con-logisession/routes/tracking-routes.json +58 -0
  159. package/test-aplicacion.con-logisession/routes/tracking-with-loadmodel-routes.json +51 -0
  160. package/test-aplicacion.con-logisession/utils/dbAdapter.js +88 -0
  161. package/test-aplicacion.con-logisession/utils/qbWrapper.js +4 -0
  162. package/test-aplicacion.con-logisession/utils/queueProcessor.js +305 -0
  163. package/test-aplicacion.con-logisession/utils/qwenRulesService.js +131 -0
  164. package/test-aplicacion.con-logisession/utils/tokenHelper.js +22 -0
  165. package/test-aplicacion.con-logisession/views/auth/dashboard.html +443 -0
  166. package/test-aplicacion.con-logisession/views/auth/forgot-password.html +200 -0
  167. package/test-aplicacion.con-logisession/views/auth/login.html +213 -0
  168. package/test-aplicacion.con-logisession/views/auth/register.html +294 -0
  169. package/test-aplicacion.con-logisession/views/contact/form.html +47 -0
  170. package/test-aplicacion.con-logisession/views/products/index.html +39 -0
@@ -0,0 +1,423 @@
1
+ /**
2
+ * Hooks para manejo de errores 404 en BlackCoffee
3
+ * Utiliza el sistema de hooks de Insitu Framework
4
+ *
5
+ * Maneja dos tipos de errores 404:
6
+ * 1. Ruta no encontrada (endpoints)
7
+ * 2. Archivo estático no encontrado
8
+ *
9
+ * Sirve un template HTML personalizado desde public/templates/404.html
10
+ *
11
+ * Uso:
12
+ * const hooks404 = require('./includes/404-hooks.js');
13
+ * hooks404.register(server.hooks);
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+
19
+ // Cache del template 404
20
+ let cached404Template = null;
21
+
22
+ /**
23
+ * Carga el template 404 desde el archivo
24
+ * @returns {string} - Contenido del template HTML
25
+ */
26
+ function load404Template() {
27
+ if (cached404Template) {
28
+ return cached404Template;
29
+ }
30
+
31
+ const templatePath = path.join(__dirname, '../public/templates/404.html');
32
+
33
+ try {
34
+ cached404Template = fs.readFileSync(templatePath, 'utf8');
35
+ console.log('✅ Template 404 cargado en caché');
36
+ return cached404Template;
37
+ } catch (error) {
38
+ console.warn('⚠️ No se pudo cargar el template 404:', error.message);
39
+ return null;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Registra los hooks para manejo de errores 404
45
+ * @param {Object} hooks - Sistema de hooks de Insitu
46
+ */
47
+ function register(hooks) {
48
+ if (!hooks) {
49
+ console.warn('⚠️ No se proporcionó el sistema de hooks');
50
+ return;
51
+ }
52
+
53
+ console.log('🔗 Registrando hooks para manejo 404...');
54
+
55
+ // Cargar template 404
56
+ load404Template();
57
+
58
+ // ============================================
59
+ // FILTROS (Filters) - Permiten modificar datos
60
+ // ============================================
61
+
62
+ /**
63
+ * Filtro: custom_404_message
64
+ * Permite personalizar el mensaje de error 404
65
+ * Este es el filtro principal para modificar la respuesta
66
+ */
67
+ hooks.addFilter('custom_404_message', (errorData, req, res, type) => {
68
+ if (type === 'file') {
69
+ errorData.message = 'Archivo no encontrado';
70
+ errorData.hint = 'Verifica la ruta del archivo estático';
71
+ } else {
72
+ errorData.message = 'Endpoint no encontrado';
73
+ errorData.hint = 'Verifica la URL e intenta nuevamente';
74
+ }
75
+
76
+ errorData.timestamp = new Date().toISOString();
77
+ errorData.blackcoffee = 'BlackCoffee v1.1.0';
78
+
79
+ return errorData;
80
+ }, 10);
81
+
82
+ /**
83
+ * Filtro: pre_route_not_found
84
+ * Se ejecuta antes de devolver un 404 de ruta
85
+ */
86
+ hooks.addFilter('pre_route_not_found', (data, req, res) => {
87
+ return data;
88
+ }, 10);
89
+
90
+ /**
91
+ * Filtro: customize_404_response
92
+ * Filtro PRINCIPAL para servir template HTML 404
93
+ * Si shouldSendDefaultResponse = false, evita la respuesta JSON por defecto
94
+ * y sirve el template HTML personalizado
95
+ */
96
+ hooks.addFilter('customize_404_response', (data, pathname, req, res) => {
97
+ const template = load404Template();
98
+
99
+ if (template && req && res) {
100
+ // Preparar datos del error
101
+ const errorData = {
102
+ type: 'route',
103
+ path: pathname,
104
+ method: req.method,
105
+ timestamp: new Date().toISOString()
106
+ };
107
+
108
+ // Reemplazar placeholders en el template
109
+ let html = template;
110
+ html = html.replace(
111
+ "window.errorData = {};",
112
+ `window.errorData = ${JSON.stringify(errorData)};`
113
+ );
114
+
115
+ // Enviar respuesta HTML
116
+ res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
117
+ res.end(html);
118
+
119
+ // Evitar respuesta por defecto
120
+ data.shouldSendDefaultResponse = false;
121
+ }
122
+
123
+ return data;
124
+ }, 5); // Prioridad alta para que se ejecute primero
125
+
126
+ /**
127
+ * Filtro: customize_static_404_response
128
+ * Filtro para servir template HTML 404 para archivos estáticos
129
+ * Si shouldSendDefaultResponse = false, evita la respuesta JSON por defecto
130
+ * y sirve el template HTML personalizado
131
+ */
132
+ hooks.addFilter('customize_static_404_response', (data, filePath, req, res) => {
133
+ const template = load404Template();
134
+
135
+ // Usar filePath de data si el directo es undefined
136
+ const actualFilePath = filePath || data?.filePath;
137
+ const actualReq = req || data?.req;
138
+ const actualRes = res || data?.res;
139
+
140
+ if (template && actualReq && actualRes && actualFilePath) {
141
+ // Preparar datos del error para archivo estático
142
+ const errorData = {
143
+ type: 'file',
144
+ path: actualFilePath,
145
+ method: actualReq.method || 'GET',
146
+ url: actualReq.url || actualFilePath,
147
+ timestamp: new Date().toISOString()
148
+ };
149
+
150
+ // Reemplazar placeholders en el template
151
+ let html = template;
152
+ html = html.replace(
153
+ "window.errorData = {};",
154
+ `window.errorData = ${JSON.stringify(errorData)};`
155
+ );
156
+
157
+ // Enviar respuesta HTML
158
+ actualRes.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
159
+ actualRes.end(html);
160
+
161
+ // Evitar respuesta por defecto
162
+ data.shouldSendDefaultResponse = false;
163
+ }
164
+
165
+ return data;
166
+ }, 5); // Prioridad alta para que se ejecute primero
167
+
168
+ /**
169
+ * Filtro: route_not_found_response
170
+ * Permite modificar la respuesta 404 antes de enviarla
171
+ */
172
+ hooks.addFilter('route_not_found_response', (response, req, res, path) => {
173
+ const customResponse = hooks.applyFilters(
174
+ 'custom_404_message',
175
+ response,
176
+ req,
177
+ res,
178
+ 'route'
179
+ );
180
+
181
+ return customResponse;
182
+ }, 10);
183
+
184
+ /**
185
+ * Filtro: static_file_not_found_response
186
+ * Permite modificar la respuesta 404 para archivos estáticos
187
+ * NOTA: Este hook se ejecuta DESPUÉS de sendNotFoundResponse,
188
+ * por lo que no podemos evitar la respuesta JSON.
189
+ * Se usa solo para logging/métricas.
190
+ */
191
+ hooks.addFilter('static_file_not_found_response', (response, req, res, filePath) => {
192
+ const customResponse = hooks.applyFilters(
193
+ 'custom_404_message',
194
+ response,
195
+ req,
196
+ res,
197
+ 'file'
198
+ );
199
+
200
+ customResponse.requestedFile = filePath;
201
+
202
+ return customResponse;
203
+ }, 10);
204
+
205
+ // ============================================
206
+ // FUNCIONES AUXILIARES
207
+ // ============================================
208
+
209
+ /**
210
+ * Obtiene la IP del cliente de forma segura
211
+ */
212
+ function getIP(req) {
213
+ if (!req) return 'N/A';
214
+ return req.connection?.remoteAddress ||
215
+ req.socket?.remoteAddress ||
216
+ req.headers?.['x-forwarded-for'] ||
217
+ 'N/A';
218
+ }
219
+
220
+ // ============================================
221
+ // ACCIONES (Actions) - Efectos secundarios
222
+ // ============================================
223
+
224
+ /**
225
+ * Acción: route_not_found
226
+ * Se ejecuta cuando no se encuentra una ruta (endpoint)
227
+ * Muestra mensaje por stdout
228
+ * Firma del hook: (pathname, req, res)
229
+ */
230
+ hooks.addAction('route_not_found', (pathname, req, res) => {
231
+ const ip = getIP(req);
232
+ const method = req?.method || 'N/A';
233
+ const userAgent = req?.headers?.['user-agent'] || 'unknown';
234
+
235
+ console.log(`\n❌ [404 - ROUTE] Ruta no encontrada:`);
236
+ console.log(` Método: ${method}`);
237
+ console.log(` Path: ${pathname}`);
238
+ console.log(` IP: ${ip}`);
239
+ console.log(` User-Agent: ${userAgent}`);
240
+ console.log(` Timestamp: ${new Date().toISOString()}\n`);
241
+
242
+ // Registrar en métricas
243
+ trackPath(pathname, 'route');
244
+ }, 10);
245
+
246
+ /**
247
+ * Acción: post_route_not_found
248
+ * Se ejecuta después de enviar la respuesta 404 de ruta
249
+ */
250
+ hooks.addAction('post_route_not_found', (pathname, req, res) => {
251
+ console.log(`📤 [404 - ROUTE] Respuesta enviada para: ${pathname}`);
252
+ }, 10);
253
+
254
+ /**
255
+ * Acción: static_file_not_found
256
+ * Se ejecuta cuando no se encuentra un archivo estático
257
+ * Muestra mensaje por stdout
258
+ * Firma del hook: (filePath, req, res)
259
+ */
260
+ hooks.addAction('static_file_not_found', (filePath, req, res) => {
261
+ const ip = getIP(req);
262
+ const method = req?.method || 'N/A';
263
+ const url = req?.url || 'N/A';
264
+
265
+ console.log(`\n❌ [404 - FILE] Archivo no encontrado:`);
266
+ console.log(` Método: ${method}`);
267
+ console.log(` Path solicitado: ${url}`);
268
+ console.log(` Archivo físico: ${filePath}`);
269
+ console.log(` IP: ${ip}`);
270
+ console.log(` Timestamp: ${new Date().toISOString()}\n`);
271
+
272
+ // Registrar en métricas
273
+ trackPath(filePath, 'file');
274
+ }, 10);
275
+
276
+ /**
277
+ * Acción: static_file_access_denied
278
+ * Se ejecuta cuando se deniega acceso a un archivo
279
+ * Firma del hook: (req, res, normalizedPath)
280
+ */
281
+ hooks.addAction('static_file_access_denied', (req, res, normalizedPath) => {
282
+ console.log(`\n❌ [403 - FILE] Acceso denegado:`);
283
+ console.log(` Archivo: ${normalizedPath}`);
284
+ console.log(` Razón: Path traversal detectado`);
285
+ console.log(` IP: ${req?.connection?.remoteAddress || req?.socket?.remoteAddress || 'N/A'}`);
286
+ console.log(` Timestamp: ${new Date().toISOString()}\n`);
287
+ }, 10);
288
+
289
+ /**
290
+ * Acción: post_static_file_not_found
291
+ * Se ejecuta después de enviar la respuesta 404 de archivo
292
+ */
293
+ hooks.addAction('post_static_file_not_found', (filePath, req, res) => {
294
+ console.log(`📤 [404 - FILE] Respuesta enviada para: ${filePath}`);
295
+ }, 10);
296
+
297
+ console.log('✅ Hooks 404 registrados exitosamente');
298
+ console.log(' Filtros:');
299
+ console.log(' • custom_404_message (personalizar mensaje)');
300
+ console.log(' • customize_404_response (template HTML para rutas)');
301
+ console.log(' • customize_static_404_response (template HTML para archivos)');
302
+ console.log(' • pre_route_not_found');
303
+ console.log(' • route_not_found_response');
304
+ console.log(' • static_file_not_found_response');
305
+ console.log(' Acciones:');
306
+ console.log(' • route_not_found (stdout)');
307
+ console.log(' • post_route_not_found');
308
+ console.log(' • static_file_not_found (stdout)');
309
+ console.log(' • static_file_access_denied (stdout)');
310
+ console.log(' • post_static_file_not_found\n');
311
+ }
312
+
313
+ // ============================================
314
+ // MÉTRICAS
315
+ // ============================================
316
+
317
+ /**
318
+ * Inicializa el sistema de métricas 404
319
+ */
320
+ function initMetrics() {
321
+ global.metrics404 = {
322
+ count: 0,
323
+ routes: 0,
324
+ files: 0,
325
+ lastPath: null,
326
+ lastType: null,
327
+ lastTime: null,
328
+ paths: new Map() // Contador por path
329
+ };
330
+
331
+ console.log('📊 Métricas 404 inicializadas');
332
+ }
333
+
334
+ /**
335
+ * Registra un path en las métricas
336
+ * @param {string} path - Ruta o archivo solicitado
337
+ * @param {string} type - Tipo: 'route' o 'file'
338
+ */
339
+ function trackPath(path, type = 'route') {
340
+ if (!global.metrics404) {
341
+ initMetrics();
342
+ }
343
+
344
+ global.metrics404.count++;
345
+
346
+ if (type === 'file') {
347
+ global.metrics404.files++;
348
+ } else {
349
+ global.metrics404.routes++;
350
+ }
351
+
352
+ global.metrics404.lastPath = path;
353
+ global.metrics404.lastType = type;
354
+ global.metrics404.lastTime = new Date().toISOString();
355
+
356
+ const count = global.metrics404.paths.get(path) || 0;
357
+ global.metrics404.paths.set(path, count + 1);
358
+ }
359
+
360
+ /**
361
+ * Obtiene las métricas de errores 404
362
+ * @returns {Object} - Métricas de errores 404
363
+ */
364
+ function getMetrics() {
365
+ return global.metrics404 || {
366
+ count: 0,
367
+ routes: 0,
368
+ files: 0,
369
+ lastPath: null,
370
+ lastType: null,
371
+ lastTime: null,
372
+ paths: new Map()
373
+ };
374
+ }
375
+
376
+ /**
377
+ * Limpia las métricas de errores 404
378
+ */
379
+ function clearMetrics() {
380
+ global.metrics404 = {
381
+ count: 0,
382
+ routes: 0,
383
+ files: 0,
384
+ lastPath: null,
385
+ lastType: null,
386
+ lastTime: null,
387
+ paths: new Map()
388
+ };
389
+ console.log('🧹 Métricas 404 limpiadas');
390
+ }
391
+
392
+ /**
393
+ * Imprime las métricas por stdout
394
+ */
395
+ function printMetrics() {
396
+ const metrics = getMetrics();
397
+ console.log('\n📊 === Métricas 404 ===');
398
+ console.log(` Total: ${metrics.count}`);
399
+ console.log(` Rutas: ${metrics.routes}`);
400
+ console.log(` Archivos: ${metrics.files}`);
401
+ console.log(` Último: ${metrics.lastPath} (${metrics.lastType})`);
402
+ console.log(` Hora: ${metrics.lastTime || 'N/A'}`);
403
+
404
+ if (metrics.paths.size > 0) {
405
+ console.log(' Paths más solicitados:');
406
+ const sorted = Array.from(metrics.paths.entries())
407
+ .sort((a, b) => b[1] - a[1])
408
+ .slice(0, 5);
409
+ sorted.forEach(([path, count]) => {
410
+ console.log(` - ${path}: ${count}`);
411
+ });
412
+ }
413
+ console.log('========================\n');
414
+ }
415
+
416
+ module.exports = {
417
+ register,
418
+ initMetrics,
419
+ getMetrics,
420
+ trackPath,
421
+ clearMetrics,
422
+ printMetrics
423
+ };
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Módulo de Autenticación y Rutas de Administración para BlackCoffee
3
+ * Maneja SessionManager, hooks de autenticación y rutas del dashboard
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const { SessionManager } = require('insitu-js');
9
+ const { hooks } = require('insitu-js');
10
+ const { initDatabase } = require('./initAdminDb');
11
+
12
+ /**
13
+ * Middleware de autenticación para proteger rutas admin
14
+ */
15
+ function authMiddleware() {
16
+ return (req, res, next) => {
17
+ const requestPath = req.url?.split('?')[0];
18
+
19
+ if (!requestPath) {
20
+ return next();
21
+ }
22
+
23
+ // Excepción: recursos estáticos del admin (CSS, JS) - NO requieren autenticación
24
+ if (requestPath.startsWith('/admin/css') || requestPath.startsWith('/admin/js')) {
25
+ return next();
26
+ }
27
+
28
+ // Excepción: página de login web - NO requiere autenticación
29
+ if (requestPath === '/admin/login') {
30
+ return next();
31
+ }
32
+
33
+ // Excepción: rutas de autenticación (API) - NO requieren autenticación
34
+ if (requestPath.startsWith('/api/admin/auth')) {
35
+ return next();
36
+ }
37
+
38
+ // Rutas que requieren autenticación (API y Web)
39
+ const protectedPaths = [
40
+ '/api/admin/apps',
41
+ '/api/admin/stats',
42
+ '/api/admin/config',
43
+ '/api/admin/db',
44
+ '/api/admin/database',
45
+ '/admin', // Dashboard web (ahora es dinámica)
46
+ '/admin/database' // Database Pools web (ahora es dinámica)
47
+ ];
48
+
49
+ // Verificar si la ruta está protegida
50
+ let isProtected = false;
51
+ for (const p of protectedPaths) {
52
+ if (p === requestPath) {
53
+ isProtected = true;
54
+ break;
55
+ }
56
+ if (requestPath.startsWith(p + '/')) {
57
+ isProtected = true;
58
+ break;
59
+ }
60
+ }
61
+
62
+ if (isProtected) {
63
+ // Verificar sesión
64
+ const isAuthenticated = req.session && req.session.data && req.session.data.authenticated;
65
+
66
+ if (!isAuthenticated) {
67
+ // Para peticiones API, devolver JSON
68
+ if (requestPath.startsWith('/api/')) {
69
+ res.writeHead(401, { 'Content-Type': 'application/json' });
70
+ res.end(JSON.stringify({
71
+ success: false,
72
+ error: 'No autenticado',
73
+ redirect: '/admin/login'
74
+ }));
75
+ return;
76
+ }
77
+
78
+ // Para peticiones web, redirigir al login
79
+ res.writeHead(302, { 'Location': '/admin/login' });
80
+ res.end();
81
+ return;
82
+ }
83
+
84
+ // Usuario autenticado, añadir información a la request
85
+ req.currentUser = {
86
+ userId: req.session.data.userId,
87
+ username: req.session.data.username,
88
+ role: req.session.data.role
89
+ };
90
+ }
91
+
92
+ next();
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Inicializar el sistema de autenticación y rutas admin
98
+ * @param {Object} server - Instancia del servidor APIServer
99
+ */
100
+ async function initAdminAuth(server) {
101
+ const config = {
102
+ cookieName: 'blackcoffee_admin_session',
103
+ secret: process.env.SESSION_SECRET || 'blackcoffee-session-secret-change-in-production',
104
+ timeout: 3600000, // 1 hora
105
+ secure: false, // Cambiar a true en producción con HTTPS
106
+ httpOnly: true,
107
+ sameSite: 'lax'
108
+ };
109
+
110
+ // ============================================
111
+ // Session Manager para autenticación
112
+ // ============================================
113
+ const sessionManager = new SessionManager(config);
114
+
115
+ // Registrar middleware de sesión PRIMERO
116
+ server.use(sessionManager.middleware());
117
+
118
+ server.sessionManager = sessionManager;
119
+ console.log('🔐 Session Manager inicializado\n');
120
+
121
+ // ============================================
122
+ // Rutas estáticas para CSS y JS (ANTES del middleware de auth)
123
+ // ============================================
124
+ console.log('🎨 Cargando recursos estáticos del admin...\n');
125
+
126
+ server.addRoute('GET', '/admin/css', {
127
+ static: {
128
+ dir: path.join(__dirname, '..', 'public', 'admin', 'css'),
129
+ cacheControl: 'public, max-age=31536000'
130
+ }
131
+ });
132
+
133
+ server.addRoute('GET', '/admin/js', {
134
+ static: {
135
+ dir: path.join(__dirname, '..', 'public', 'admin', 'js'),
136
+ cacheControl: 'public, max-age=31536000'
137
+ }
138
+ });
139
+
140
+ console.log(' ✅ Recursos estáticos cargados\n');
141
+
142
+ // ============================================
143
+ // Middleware de autenticación (DESPUÉS de las estáticas)
144
+ // ============================================
145
+ server.use(authMiddleware());
146
+ console.log('🔒 Middleware de autenticación registrado\n');
147
+
148
+ // ============================================
149
+ // Inicializar base de datos de administración
150
+ // ============================================
151
+ try {
152
+ await initDatabase();
153
+ console.log('🗄️ Base de datos admin inicializada\n');
154
+ } catch (error) {
155
+ console.error('❌ Error inicializando DB admin:', error.message);
156
+ }
157
+
158
+ // ============================================
159
+ // Rutas de autenticación (API)
160
+ // ============================================
161
+ const authRoutesPath = path.join(__dirname, '..', 'routes', 'auth.json');
162
+ if (fs.existsSync(authRoutesPath)) {
163
+ const RouteLoader = require('insitu-js/lib/loader/routeLoader');
164
+ const routeLoader = new RouteLoader();
165
+
166
+ try {
167
+ const authRoutes = fs.readFileSync(authRoutesPath, 'utf8');
168
+ const routes = JSON.parse(authRoutes);
169
+
170
+ for (const route of routes) {
171
+ const controllerPath = path.resolve(__dirname, '..', route.controller);
172
+ const routeCopy = { ...route, controller: controllerPath };
173
+ await routeLoader.loadSingleRoute(server, routeCopy);
174
+ console.log(` ✅ ${route.method} ${route.path}`);
175
+ }
176
+
177
+ console.log(' ✅ Rutas de autenticación cargadas\n');
178
+ } catch (error) {
179
+ console.error(` ❌ Error cargando rutas auth: ${error.message}\n`);
180
+ }
181
+ }
182
+
183
+ // ============================================
184
+ // Rutas de la UI de administración (Web) - DINÁMICAS con controlador
185
+ // ============================================
186
+ console.log('🎨 Cargando UI de administración...\n');
187
+
188
+ const adminViewsRoutesPath = path.join(__dirname, '..', 'routes', 'admin-views.json');
189
+ if (fs.existsSync(adminViewsRoutesPath)) {
190
+ const RouteLoader = require('insitu-js/lib/loader/routeLoader');
191
+ const routeLoader = new RouteLoader();
192
+
193
+ try {
194
+ const adminViewsRoutes = fs.readFileSync(adminViewsRoutesPath, 'utf8');
195
+ const routes = JSON.parse(adminViewsRoutes);
196
+
197
+ for (const route of routes) {
198
+ const controllerPath = path.resolve(__dirname, '..', route.controller);
199
+ const routeCopy = { ...route, controller: controllerPath };
200
+ await routeLoader.loadSingleRoute(server, routeCopy);
201
+ console.log(` ✅ ${route.method} ${route.path}`);
202
+ }
203
+
204
+ console.log(' ✅ Rutas de administración cargadas\n');
205
+ } catch (error) {
206
+ console.error(` ❌ Error cargando rutas admin views: ${error.message}\n`);
207
+ }
208
+ }
209
+
210
+ // Exponer sessionManager globalmente para acceso desde controladores
211
+ global.sessionManager = sessionManager;
212
+ }
213
+
214
+ module.exports = { initAdminAuth };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Extensión administrativa de BlackCoffee
3
+ * Habilita la consola TCP de administración de Insitu
4
+ *
5
+ * Uso:
6
+ * const adminExtension = require('./includes/adminExtension');
7
+ * adminExtension.init(server, config);
8
+ */
9
+
10
+ /**
11
+ * Inicializa la consola administrativa
12
+ * @param {Object} server - Instancia del servidor APIServer
13
+ * @param {Object} config - Configuración del servidor
14
+ */
15
+ function init(server, config = {}) {
16
+ const adminPort = config.adminPort || parseInt(process.env.ADMIN_PORT, 10) || 9999;
17
+ const adminHost = config.adminHost || process.env.ADMIN_HOST || '127.0.0.1';
18
+
19
+ try {
20
+ console.log(`🔧 Inicializando consola administrativa...`);
21
+
22
+ server.initializeAdminExtension({
23
+ port: adminPort,
24
+ host: adminHost
25
+ });
26
+
27
+ console.log(`✅ Consola administrativa iniciada en ${adminHost}:${adminPort}`);
28
+ console.log(` Conéctate con: telnet ${adminHost} ${adminPort}`);
29
+ console.log(` O usa: nc ${adminHost} ${adminPort}\n`);
30
+
31
+ return true;
32
+ } catch (error) {
33
+ console.error(`❌ Error inicializando consola administrativa: ${error.message}`);
34
+ return false;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Obtiene información sobre la consola administrativa
40
+ * @returns {Object} - Información de la consola
41
+ */
42
+ function getInfo() {
43
+ return {
44
+ port: parseInt(process.env.ADMIN_PORT, 10) || 9999,
45
+ host: process.env.ADMIN_HOST || '127.0.0.1',
46
+ connectCommand: `telnet ${process.env.ADMIN_HOST || '127.0.0.1'} ${process.env.ADMIN_PORT || 9999}`
47
+ };
48
+ }
49
+
50
+ module.exports = {
51
+ init,
52
+ getInfo
53
+ };