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,89 @@
1
+ # Documentación de Módulos del Sistema de Administración
2
+
3
+ Este documento describe los módulos disponibles en el servidor de administración del framework Insitu.
4
+
5
+ ## Módulos Disponibles
6
+
7
+ ### 1. Routes Module (`routes`)
8
+ - **Descripción**: Módulo para ver rutas registradas y activas
9
+ - **Comandos**: `routes`, `active`
10
+
11
+ ### 2. System Module (`system`)
12
+ - **Descripción**: Módulo para información del sistema
13
+ - **Comandos**: `version`, `status`, `help`
14
+
15
+ ### 3. Cache Module (`cachemodule`)
16
+ - **Descripción**: Módulo para administrar y monitorear el sistema de caché
17
+ - **Comandos**: `cache`, `cache-stats`, `cache-clear`, `cache-info`
18
+
19
+ ### 4. Queue Management Module (`queuemanagementmodule`)
20
+ - **Descripción**: Módulo para gestionar las colas del sistema
21
+ - **Comandos**: `queues`, `queue-info`, `queue-pause`, `queue-resume`, `queue-clear`, `queue-retry-failed`
22
+
23
+ ### 5. Route Cache Module (`routecachemodule`)
24
+ - **Descripción**: Módulo para gestionar y visualizar el cache de rutas dinámicas y estáticas
25
+ - **Comandos**: `route-cache`, `cache-stats`, `cache-view`, `cache-clear`, `cache-dynamic`, `cache-static`
26
+
27
+ ### 6. Route Manager Module (`routemanagermodule`)
28
+ - **Descripción**: Módulo para crear y deshabilitar rutas de manera interactiva
29
+ - **Comandos**: `manage-routes`, `route-manager`, `route-mgmt`
30
+
31
+ ### 7. Stats Module (`statsmodule`)
32
+ - **Descripción**: Módulo para mostrar estadísticas del servidor
33
+ - **Comandos**: `stats`, `statistics`, `requests`, `endpoints`
34
+
35
+ ### 8. System Module (`systemmodule`)
36
+ - **Descripción**: Módulo para mostrar información del sistema
37
+ - **Comandos**: `sysinfo`, `system`, `resources`
38
+
39
+ ### 9. Time Module (`timemodule`)
40
+ - **Descripción**: Módulo para mostrar hora y fecha
41
+ - **Comandos**: `time`, `date`
42
+
43
+ ### 10. View Cache Stats Module (`viewcachestatsmodule`)
44
+ - **Descripción**: Módulo para mostrar estadísticas del caché de vistas
45
+ - **Comandos**: `view-cache`
46
+
47
+ ### 11. WAF Module (`wafmodule`)
48
+ - **Descripción**: Módulo para mostrar estadísticas del Web Application Firewall
49
+ - **Comandos**: `waf-status`, `waf-stats`, `waf-blocked`, `waf-security`, `waf-block-ip`, `waf-unblock-ip`, `waf-whitelist`, `waf-blacklist`, `waf-x-headers`, `create-x-rule`, `remove-x-rule`, `save-rules`, `load-rules`, `waf-logs`, `waf-monitored`
50
+
51
+ ## Conexión al Servidor de Administración
52
+
53
+ Existen dos formas principales de conectarte al servidor de administración:
54
+
55
+ ### 1. Usando Netcat (nc)
56
+ Para conectarte al servidor de administración, usa el siguiente comando:
57
+
58
+ ```bash
59
+ nc localhost 9999
60
+ ```
61
+
62
+ Una vez conectado, puedes usar cualquiera de los comandos mencionados arriba para interactuar con los diferentes módulos.
63
+
64
+ ### 2. Usando el Cliente Oficial de Administración
65
+ El framework también incluye un cliente CLI especializado para interactuar con el servidor de administración:
66
+
67
+ ```bash
68
+ node icca.js
69
+ ```
70
+
71
+ Este cliente ofrece características adicionales como:
72
+ - Historial de comandos
73
+ - Autocompletado de comandos
74
+ - Reutilización de comandos anteriores
75
+ - Comandos especiales del cliente:
76
+ - `history`: Muestra el historial de comandos
77
+ - `clear`: Limpia la pantalla
78
+ - `re <número>`: Reutiliza el comando número n del historial
79
+ - `re <texto>`: Reutiliza el último comando que contiene el texto
80
+ - `help-cli`: Muestra ayuda del cliente
81
+
82
+ Opciones de conexión:
83
+ - `-h, --host HOST`: Especifica el host del servidor (por defecto: localhost)
84
+ - `-p, --port PORT`: Especifica el puerto del servidor (por defecto: 9999)
85
+
86
+ Ejemplo:
87
+ ```bash
88
+ node icca.js --host localhost --port 9999
89
+ ```
@@ -0,0 +1,74 @@
1
+ # Puntos de ejecución en el framework Insitu
2
+
3
+ ## 1. Ejecución del método indicado por el router
4
+
5
+ El punto exacto donde se ejecuta el handler (método) asociado a una ruta se encuentra en:
6
+ `/home/bds/insitu-js/lib/core/server.js`
7
+
8
+ Líneas específicas:
9
+ - Línea 906: `await matchedRoute.route.handler(req, res);` (solo para solicitudes OPTIONS)
10
+ - Línea 958: `await matchedRoute.route.handler(req, res);` (para otros métodos HTTP)
11
+
12
+ Esto ocurre después de que el sistema encuentra una ruta coincidente (`matchedRoute`) y antes de disparar el hook correspondiente (`handleRouteHandlerExecuted`).
13
+
14
+ ### Hooks y filtros disponibles
15
+
16
+ Después de las modificaciones realizadas, ahora existen hooks y filtros disponibles para la ejecución de handlers en la línea 958 (solicitudes que no sean OPTIONS):
17
+
18
+ - **Antes de la ejecución del handler** (línea ~965):
19
+ - Hook de acción: `before_route_handler`
20
+ - Filtro: `before_route_handler` (permite modificar req/res)
21
+
22
+ - **Después de la ejecución del handler** (línea ~975):
23
+ - Hook de acción: `after_route_handler`
24
+ - Filtro: `after_route_handler` (permite modificar req/res)
25
+
26
+ Estos hooks y filtros **no se aplican** a las solicitudes OPTIONS (línea 906).
27
+
28
+ ### Explicación de la duplicación
29
+
30
+ La duplicación del código `await matchedRoute.route.handler(req, res);` en las líneas 906 y 958 se debe a que hay dos caminos de ejecución diferentes en la función `handleRequest`:
31
+
32
+ 1. **Línea 906**: Se ejecuta en el caso de solicitudes **OPTIONS** que tienen un handler específico registrado para esa ruta. Esta parte del código está dentro de un bloque condicional que maneja específicamente el método HTTP OPTIONS.
33
+
34
+ 2. **Línea 958**: Se ejecuta para otros métodos HTTP (GET, POST, PUT, etc.) que no sean OPTIONS, en el flujo normal de procesamiento de rutas.
35
+
36
+ El código está estructurado de esta manera para manejar de forma diferente las solicitudes OPTIONS (que son parte del mecanismo CORS) respecto a otros tipos de solicitudes. Las solicitudes OPTIONS pueden requerir un tratamiento especial en el contexto de CORS, por lo que se separaron en diferentes ramas de lógica.
37
+
38
+ Aunque la instrucción es idéntica en ambos lugares, están en contextos ligeramente diferentes para permitir un manejo específico de solicitudes OPTIONS versus otros métodos HTTP.
39
+
40
+ ## 2. Carga de archivos estáticos
41
+
42
+ El punto exacto donde se carga un archivo estático se encuentra en el método `createStaticFileHandler` en:
43
+ `/home/bds/insitu-js/lib/core/server.js`
44
+
45
+ Bloque de código clave para archivos índice (líneas aproximadas 400-415):
46
+ ```javascript
47
+ const fileContent = await fs.promises.readFile(indexPath);
48
+ const mimeType = getMimeType(indexPath);
49
+
50
+ this.setStaticFileHeaders(res, mimeType, staticConfig);
51
+
52
+ res.writeHead(200);
53
+ res.end(fileContent);
54
+
55
+ // Disparar hook después de servir archivo
56
+ StaticFileHooksHandler.handleStaticFileServed(this, indexPath, req, res);
57
+ return;
58
+ ```
59
+
60
+ Y también para archivos normales (líneas aproximadas 490-495):
61
+ ```javascript
62
+ const fileContent = await fs.promises.readFile(physicalPath);
63
+ const mimeType = getMimeType(physicalPath);
64
+
65
+ this.setStaticFileHeaders(res, mimeType, staticConfig);
66
+
67
+ res.writeHead(200);
68
+ res.end(fileContent);
69
+
70
+ // Disparar hook después de servir archivo
71
+ StaticFileHooksHandler.handleStaticFileServed(this, physicalPath, req, res);
72
+ ```
73
+
74
+ Estos bloques de código se ejecutan cuando una ruta estática coincide con la solicitud y se debe servir un archivo físico desde el sistema de archivos.
@@ -0,0 +1,222 @@
1
+ # Cómo usar los hooks `before_static_file_load` y `after_static_file_load`
2
+
3
+ Los hooks `before_static_file_load` y `after_static_file_load` permiten interceptar y manipular la carga de archivos estáticos en el framework Insitu. Estos hooks se ejecutan en ambos puntos donde se cargan archivos estáticos: tanto para archivos normales como para archivos índice (como `index.html`).
4
+
5
+ ## Hook `before_static_file_load`
6
+
7
+ Este hook se ejecuta justo antes de que se lea un archivo estático del sistema de archivos.
8
+
9
+ ### Ejemplo 1: Uso como acción para registrar accesos
10
+
11
+ ```javascript
12
+ // Registrar cada vez que se va a cargar un archivo estático
13
+ hooks.addAction('before_static_file_load', (req, res, filePath, staticConfig) => {
14
+ console.log(`Cargando archivo estático: ${filePath}`);
15
+ console.log(`URL solicitada: ${req.url}`);
16
+ }, 10, 4);
17
+ ```
18
+
19
+ ### Ejemplo 2: Uso como filtro para modificar la respuesta
20
+
21
+ ```javascript
22
+ // Añadir headers personalizados antes de cargar el archivo
23
+ hooks.addFilter('before_static_file_load', (data, req, res, filePath, staticConfig) => {
24
+ // Añadir header para indicar que es un archivo estático
25
+ res.setHeader('X-Static-File', 'true');
26
+
27
+ // Añadir header con la ruta del archivo
28
+ res.setHeader('X-File-Path', filePath);
29
+
30
+ // Devolver el objeto modificado
31
+ return {
32
+ ...data,
33
+ res: res
34
+ };
35
+ }, 10, 4);
36
+ ```
37
+
38
+ ### Ejemplo 3: Uso como filtro para implementar autenticación en archivos estáticos
39
+
40
+ ```javascript
41
+ // Verificar permisos antes de servir archivos estáticos
42
+ hooks.addFilter('before_static_file_load', (data, req, res, filePath, staticConfig) => {
43
+ // Verificar si el archivo está protegido
44
+ if (isProtectedFile(filePath)) {
45
+ // Aquí podrías implementar tu lógica de autenticación
46
+ const isAuthenticated = checkAuthentication(req);
47
+
48
+ if (!isAuthenticated) {
49
+ // Si no está autenticado, enviar respuesta de error
50
+ if (!res.headersSent) {
51
+ res.writeHead(403, { 'Content-Type': 'text/plain' });
52
+ res.end('Acceso prohibido');
53
+ }
54
+ return data; // Retorna sin cargar el archivo
55
+ }
56
+ }
57
+
58
+ // Si está autorizado, continuar normalmente
59
+ return data;
60
+ }, 5, 4);
61
+ ```
62
+
63
+ ### Ejemplo 4: Uso como filtro para modificar la configuración estática
64
+
65
+ ```javascript
66
+ // Modificar la configuración estática basada en la solicitud
67
+ hooks.addFilter('before_static_file_load', (data, req, res, filePath, staticConfig) => {
68
+ // Modificar la configuración basada en el tipo de usuario
69
+ if (req.userRole === 'admin') {
70
+ // Permitir acceso a archivos adicionales para administradores
71
+ const modifiedConfig = {
72
+ ...staticConfig,
73
+ // Añadir configuraciones específicas para admins
74
+ };
75
+
76
+ return {
77
+ ...data,
78
+ staticConfig: modifiedConfig
79
+ };
80
+ }
81
+
82
+ return data;
83
+ }, 15, 4);
84
+ ```
85
+
86
+ ## Hook `after_static_file_load`
87
+
88
+ Este hook se ejecuta después de que se ha leído el archivo del sistema de archivos pero antes de enviarlo al cliente.
89
+
90
+ ### Ejemplo 1: Uso como acción para registrar métricas
91
+
92
+ ```javascript
93
+ // Registrar cada vez que se completa la carga de un archivo estático
94
+ hooks.addAction('after_static_file_load', (req, res, filePath, staticConfig) => {
95
+ console.log(`Archivo estático cargado: ${filePath}`);
96
+ console.log(`Tamaño: ${res.getHeader('Content-Length')} bytes`);
97
+ }, 10, 4);
98
+ ```
99
+
100
+ ### Ejemplo 2: Uso como filtro para modificar el contenido del archivo
101
+
102
+ ```javascript
103
+ // Modificar el contenido del archivo antes de enviarlo (solo para ciertos tipos de archivo)
104
+ hooks.addFilter('after_static_file_load', (data, req, res, filePath, staticConfig, fileContent) => {
105
+ // Solo para archivos HTML
106
+ if (filePath.endsWith('.html') && !res.headersSent) {
107
+ // Acceder al contenido del archivo
108
+ let content = data.fileContent.toString();
109
+
110
+ // Modificar el contenido (por ejemplo, inyectar un script)
111
+ if (content.includes('</body>')) {
112
+ content = content.replace('</body>', '<script>console.log("Archivo modificado por el hook");</script></body>');
113
+ }
114
+
115
+ // Devolver el objeto con el contenido modificado
116
+ return {
117
+ ...data,
118
+ fileContent: Buffer.from(content)
119
+ };
120
+ }
121
+
122
+ return data;
123
+ }, 10, 5);
124
+ ```
125
+
126
+ ### Ejemplo 2b: Uso con Cheerio para manipulación HTML más avanzada
127
+
128
+ ```javascript
129
+ const cheerio = require('cheerio'); // Necesitas instalar cheerio: npm install cheerio
130
+
131
+ // Manipular HTML usando Cheerio para inyección de scripts o estilos
132
+ hooks.addFilter('after_static_file_load', (data, req, res, filePath, staticConfig, fileContent) => {
133
+ // Solo para archivos HTML
134
+ if (filePath.endsWith('.html') && !res.headersSent) {
135
+ try {
136
+ // Convertir el buffer a string y cargarlo en Cheerio
137
+ const htmlString = data.fileContent.toString();
138
+ const $ = cheerio.load(htmlString);
139
+
140
+ // Añadir un script al head
141
+ $('head').append('<script src="/assets/custom-script.js"></script>');
142
+
143
+ // Añadir un estilo personalizado
144
+ $('body').prepend('<style>.custom-class { color: blue; }</style>');
145
+
146
+ // Devolver el HTML modificado
147
+ const modifiedHtml = $.html();
148
+ return {
149
+ ...data,
150
+ fileContent: Buffer.from(modifiedHtml)
151
+ };
152
+ } catch (error) {
153
+ console.error('Error al manipular el archivo HTML:', error);
154
+ // Devolver el contenido original si hay un error
155
+ return data;
156
+ }
157
+ }
158
+
159
+ return data;
160
+ }, 10, 5);
161
+ ```
162
+
163
+ ### Ejemplo 3: Uso como filtro para auditoría post-carga
164
+
165
+ ```javascript
166
+ // Registrar información de auditoría después de cargar el archivo
167
+ hooks.addFilter('after_static_file_load', (data, req, res, filePath, staticConfig) => {
168
+ const auditLog = {
169
+ timestamp: new Date().toISOString(),
170
+ userId: req.userId || 'anonymous',
171
+ filePath: filePath,
172
+ url: req.url,
173
+ method: req.method,
174
+ statusCode: res.statusCode || 200
175
+ };
176
+
177
+ // Guardar registro de auditoría
178
+ console.log('STATIC_FILE_AUDIT:', JSON.stringify(auditLog));
179
+
180
+ return data;
181
+ }, 5, 4);
182
+ ```
183
+
184
+ ### Ejemplo 4: Uso como filtro para compresión condicional
185
+
186
+ ```javascript
187
+ // Aplicar compresión condicional después de cargar el archivo
188
+ hooks.addFilter('after_static_file_load', (data, req, res, filePath, staticConfig) => {
189
+ // Verificar si el cliente admite compresión
190
+ const acceptEncoding = req.headers['accept-encoding'];
191
+ if (acceptEncoding && acceptEncoding.includes('gzip')) {
192
+ // Aquí podrías aplicar compresión gzip si el archivo es grande
193
+ // y el tipo de archivo es apropiado para compresión
194
+ if (shouldCompressFile(filePath)) {
195
+ res.setHeader('Content-Encoding', 'gzip');
196
+ }
197
+ }
198
+
199
+ return data;
200
+ }, 15, 4);
201
+ ```
202
+
203
+ ## Parámetros importantes:
204
+
205
+ - El hook `before_static_file_load` recibe: `(data, req, res, filePath, staticConfig)` para los filtros
206
+ - El hook `after_static_file_load` recibe: `(data, req, res, filePath, staticConfig, fileContent)` para los filtros
207
+ - El objeto `data` incluye: `{ req, res, filePath, staticConfig, fileContent }`
208
+ - Las acciones para `before_static_file_load` reciben: `(req, res, filePath, staticConfig)`
209
+ - Las acciones para `after_static_file_load` reciben: `(req, res, filePath, staticConfig, fileContent)`
210
+ - El parámetro `acceptedArgs` define cuántos argumentos acepta tu callback (normalmente 4 para before y 5 para after)
211
+ - El parámetro `priority` define el orden de ejecución (valores más bajos se ejecutan primero)
212
+ - El hook se aplica tanto a archivos normales como a archivos índice (como `index.html`)
213
+
214
+ ## Consideraciones importantes:
215
+
216
+ 1. **Estado de la respuesta**: En `after_static_file_load`, el archivo ya ha sido leído pero aún no enviado, por lo que puedes modificar encabezados pero no el contenido sin una implementación adicional.
217
+
218
+ 2. **Rendimiento**: Ten cuidado al implementar lógica pesada en estos hooks ya que afectan el rendimiento de carga de archivos estáticos.
219
+
220
+ 3. **Seguridad**: Estos hooks son ideales para implementar controles de acceso, autenticación y auditoría en archivos estáticos.
221
+
222
+ Estos hooks proporcionan una potente capacidad de personalización del comportamiento del framework al servir archivos estáticos, permitiendo implementar lógica de autenticación, auditoría, modificación de headers y más.
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Modelo de Usuario Administrador para BlackCoffee
3
+ * Gestiona la tabla admin_users en SQLite
4
+ */
5
+
6
+ const path = require('path');
7
+ const { ModelBase } = require('insitu-js');
8
+ const crypto = require('crypto');
9
+
10
+ class AdminUserModel extends ModelBase {
11
+ constructor(options = {}) {
12
+ super({
13
+ tableName: 'admin_users',
14
+ adapter: options.adapter || 'sqlite-pool',
15
+ fields: ['id', 'username', 'password_hash', 'email', 'role', 'active', 'created_at', 'updated_at', 'last_login']
16
+ });
17
+ }
18
+
19
+ /**
20
+ * Hash de contraseña usando crypto
21
+ */
22
+ static hashPassword(password) {
23
+ const salt = crypto.randomBytes(16).toString('hex');
24
+ const hash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');
25
+ return `${salt}:${hash}`;
26
+ }
27
+
28
+ /**
29
+ * Verificar contraseña contra el hash almacenado
30
+ */
31
+ static verifyPassword(password, storedHash) {
32
+ try {
33
+ const [salt, hash] = storedHash.split(':');
34
+ const verifyHash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');
35
+ return hash === verifyHash;
36
+ } catch (error) {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Buscar usuario por username
43
+ */
44
+ async findByUsername(username) {
45
+ if (!this.adapter) {
46
+ return null;
47
+ }
48
+
49
+ const selectSQL = `SELECT * FROM ${this.tableName} WHERE username = ?`;
50
+ const results = await this.adapter.query(selectSQL, [username]);
51
+ return results[0] || null;
52
+ }
53
+
54
+ /**
55
+ * Buscar usuario por username y verificar password
56
+ */
57
+ async findByCredentials(username, password) {
58
+ const user = await this.findByUsername(username);
59
+
60
+ if (!user || !user.active) {
61
+ return null;
62
+ }
63
+
64
+ if (AdminUserModel.verifyPassword(password, user.password_hash)) {
65
+ // Actualizar último login usando query directa
66
+ try {
67
+ const updateSQL = `UPDATE ${this.tableName} SET last_login = ?, updated_at = ? WHERE id = ?`;
68
+ await this.adapter.query(updateSQL, [new Date().toISOString(), new Date().toISOString(), user.id]);
69
+ } catch (err) {
70
+ console.error('Error actualizando last_login:', err.message);
71
+ }
72
+
73
+ // Devolver usuario sin el hash
74
+ const { password_hash, ...userWithoutPassword } = user;
75
+ return userWithoutPassword;
76
+ }
77
+
78
+ return null;
79
+ }
80
+
81
+ /**
82
+ * Buscar por ID
83
+ */
84
+ async findById(id) {
85
+ if (!this.adapter) {
86
+ return null;
87
+ }
88
+
89
+ const selectSQL = `SELECT * FROM ${this.tableName} WHERE id = ?`;
90
+ const results = await this.adapter.query(selectSQL, [id]);
91
+ return results[0] || null;
92
+ }
93
+
94
+ /**
95
+ * Crear usuario con password hasheado
96
+ */
97
+ async createWithPassword(userData) {
98
+ const { password, ...restData } = userData;
99
+
100
+ const userWithHash = {
101
+ ...restData,
102
+ password_hash: AdminUserModel.hashPassword(password),
103
+ active: userData.active !== undefined ? userData.active : 1,
104
+ role: userData.role || 'admin',
105
+ created_at: new Date().toISOString(),
106
+ updated_at: new Date().toISOString()
107
+ };
108
+
109
+ const id = await this.create(userWithHash);
110
+ return { id, ...userWithHash };
111
+ }
112
+
113
+ /**
114
+ * Cambiar contraseña
115
+ */
116
+ async changePassword(userId, newPassword) {
117
+ const updateSQL = `UPDATE ${this.tableName} SET password_hash = ?, updated_at = ? WHERE id = ?`;
118
+ const hash = AdminUserModel.hashPassword(newPassword);
119
+ await this.adapter.query(updateSQL, [hash, new Date().toISOString(), userId]);
120
+ return true;
121
+ }
122
+
123
+ /**
124
+ * Verificar si existe el usuario
125
+ */
126
+ async usernameExists(username) {
127
+ const user = await this.findByUsername(username);
128
+ return !!user;
129
+ }
130
+ }
131
+
132
+ module.exports = AdminUserModel;
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "blackcoffee2",
3
+ "version": "2.1.0",
4
+ "description": "A app server built with Insitu Framework",
5
+ "main": "server.js",
6
+ "author": "Benjamin Sanchez Cardenas <bytedogssyndicate@gmail.com>",
7
+ "license": "Apache-2.0",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://gitlab.com/bytedogssyndicate1/bc2"
11
+ },
12
+ "scripts": {
13
+ "start": "node server.js",
14
+ "admin": "node bin/adminclient",
15
+ "admin:connect": "node bin/adminclient",
16
+ "dev": "node --watch server.js",
17
+ "cli": "node bin/blackcoffee"
18
+ },
19
+ "bin": {
20
+ "blackcoffee": "./bin/blackcoffee"
21
+ },
22
+ "dependencies": {
23
+ "better-sqlite3": "^12.6.2",
24
+ "chokidar": "^5.0.0",
25
+ "gtk3-node": "^1.3.0",
26
+ "insitu-js": "^1.3.0"
27
+ },
28
+ "engines": {
29
+ "node": ">=22.0.0"
30
+ },
31
+ "keywords": [
32
+ "blackcoffee",
33
+ "insitu",
34
+ "application-server",
35
+ "app-server",
36
+ "https-server",
37
+ "rest-api",
38
+ "nodejs",
39
+ "web-server",
40
+ "backend",
41
+ "framework",
42
+ "microservices",
43
+ "api-server"
44
+ ]
45
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Rutas registradas programáticamente en BlackCoffee v2.0.0
3
+ *
4
+ * Este archivo está reservado para rutas programáticas del núcleo.
5
+ * Los desarrolladores NO deben modificar este archivo.
6
+ *
7
+ * Para agregar rutas, usar:
8
+ * - routes/*.json para rutas declarativas
9
+ * - controllers/ para controladores
10
+ * - apps/ para aplicaciones desplegadas
11
+ */
12
+
13
+ const path = require('path');
14
+
15
+ /**
16
+ * Registra las rutas programáticas del núcleo
17
+ * @param {Object} server - Instancia del servidor APIServer
18
+ */
19
+ function PRoutes(server) {
20
+ console.log('📋 Registrando rutas del núcleo...\n');
21
+
22
+ // ============================================
23
+ // Rutas de Administración de Base de Datos
24
+ // ============================================
25
+ const DatabaseAdminController = require('../controllers/admin/DatabaseAdminController');
26
+
27
+ // API: Listar pools
28
+ server.addRoute('GET', '/api/admin/database/pools', (req, res) => {
29
+ DatabaseAdminController.getPools(req, res);
30
+ });
31
+
32
+ // API: Crear pool
33
+ server.addRoute('POST', '/api/admin/database/pools', (req, res) => {
34
+ DatabaseAdminController.createPool(req, res);
35
+ });
36
+
37
+ // API: Eliminar pool
38
+ server.addRoute('DELETE', '/api/admin/database/pools/:name', (req, res) => {
39
+ DatabaseAdminController.removePool(req, res);
40
+ });
41
+
42
+ // API: Conectar pool
43
+ server.addRoute('POST', '/api/admin/database/pools/:name/connect', (req, res) => {
44
+ DatabaseAdminController.connectPool(req, res);
45
+ });
46
+
47
+ // API: Desconectar pool
48
+ server.addRoute('POST', '/api/admin/database/pools/:name/disconnect', (req, res) => {
49
+ DatabaseAdminController.disconnectPool(req, res);
50
+ });
51
+
52
+ // UI: Página de Database Pools
53
+ server.addRoute('GET', '/admin/database', (req, res) => {
54
+ const fs = require('fs');
55
+ const dbPagePath = path.join(__dirname, '..', 'public', 'admin', 'database.html');
56
+
57
+ if (fs.existsSync(dbPagePath)) {
58
+ const html = fs.readFileSync(dbPagePath, 'utf8');
59
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
60
+ res.end(html);
61
+ } else {
62
+ res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
63
+ res.end('<h1>404 - Database Manager Page not found</h1>');
64
+ }
65
+ });
66
+
67
+ // UI: CSS para Database Pools
68
+ server.addRoute('GET', '-/admin/css/db-pools.css', {
69
+ static: {
70
+ dir: path.join(__dirname, '..', 'public', 'admin', 'css'),
71
+ cacheControl: 'public, max-age=31536000'
72
+ }
73
+ });
74
+
75
+ // UI: JS para Database Pools
76
+ server.addRoute('GET', '/admin/js/db-pools.js', {
77
+ static: {
78
+ dir: path.join(__dirname, '..', 'public', 'admin', 'js'),
79
+ cacheControl: 'public, max-age=31536000'
80
+ }
81
+ });
82
+
83
+ console.log(' ✅ Rutas de Database Admin registradas');
84
+ console.log(' ✅ PRoutes: listo para rutas del núcleo\n');
85
+ }
86
+
87
+ module.exports = {
88
+ PRoutes
89
+ };