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,452 @@
1
+ #!/usr/bin/env node
2
+ // admin-gui.js - Interfaz gráfica GTK3 para administrar usuarios BlackCoffee
3
+
4
+ const {
5
+ Button,
6
+ Label,
7
+ Box,
8
+ Scroll,
9
+ Window,
10
+ init,
11
+ run,
12
+ Entry,
13
+ TextView
14
+ } = require('gtk3-node');
15
+
16
+ const AdminDatabase = require('./admin-db.js');
17
+ const path = require('path');
18
+
19
+ // ============================================
20
+ // ARGUMENTOS DE LÍNEA DE COMANDOS
21
+ // ============================================
22
+
23
+ function parseArgs() {
24
+ const args = process.argv.slice(2);
25
+ const options = {
26
+ db: null,
27
+ action: null,
28
+ help: false
29
+ };
30
+
31
+ for (let i = 0; i < args.length; i++) {
32
+ if (args[i] === '--db' && args[i + 1]) {
33
+ options.db = args[i + 1];
34
+ i++;
35
+ } else if (args[i] === '-d' && args[i + 1]) {
36
+ options.db = args[i + 1];
37
+ i++;
38
+ } else if (args[i] === '--edit' && args[i + 1]) {
39
+ options.action = { type: 'edit', id: parseInt(args[i + 1]) };
40
+ i++;
41
+ } else if (args[i] === '--delete' && args[i + 1]) {
42
+ options.action = { type: 'delete', id: parseInt(args[i + 1]) };
43
+ i++;
44
+ } else if (args[i] === '--toggle' && args[i + 1]) {
45
+ options.action = { type: 'toggle', id: parseInt(args[i + 1]) };
46
+ i++;
47
+ } else if (args[i] === '--help' || args[i] === '-h') {
48
+ options.help = true;
49
+ }
50
+ }
51
+
52
+ return options;
53
+ }
54
+
55
+ function showHelp() {
56
+ console.log(`
57
+ ╔═══════════════════════════════════════════════════════════╗
58
+ ║ BlackCoffee Admin Panel - Gestión de Usuarios ║
59
+ ╠═══════════════════════════════════════════════════════════╣
60
+ ║ Uso: node admin-gui.js [opciones] ║
61
+ ╠═══════════════════════════════════════════════════════════╣
62
+ ║ Opciones: ║
63
+ ║ --db, -d <ruta> Ruta a la base de datos SQLite ║
64
+ ║ --edit <id> Editar usuario por ID ║
65
+ ║ --delete <id> Eliminar usuario por ID ║
66
+ ║ --toggle <id> Activar/Desactivar usuario ║
67
+ ║ --help, -h Mostrar esta ayuda ║
68
+ ╠═══════════════════════════════════════════════════════════╣
69
+ ║ Ejemplos: ║
70
+ ║ node admin-gui.js ║
71
+ ║ node admin-gui.js --db ./blackcoffee_admin.db ║
72
+ ║ node admin-gui.js -d /ruta/a/mibase.db ║
73
+ ║ node admin-gui.js --edit 1 ║
74
+ ╚═══════════════════════════════════════════════════════════╝
75
+ `);
76
+ process.exit(0);
77
+ }
78
+
79
+ // ============================================
80
+ // INICIALIZACIÓN
81
+ // ============================================
82
+
83
+ const options = parseArgs();
84
+
85
+ if (options.help) {
86
+ showHelp();
87
+ }
88
+
89
+ // Inicializar GTK
90
+ init();
91
+
92
+ // Instanciar base de datos
93
+ const dbPath = options.db ? path.resolve(options.db) : null;
94
+ const db = new AdminDatabase(dbPath);
95
+
96
+ // ============================================
97
+ // ACCIONES POR LÍNEA DE COMANDOS
98
+ // ============================================
99
+
100
+ if (options.action) {
101
+ const { type, id } = options.action;
102
+ const user = db.getUserById(id);
103
+
104
+ if (!user) {
105
+ console.log(`❌ Usuario #${id} no encontrado`);
106
+ process.exit(1);
107
+ }
108
+
109
+ if (type === 'edit') {
110
+ console.log(`✏️ Editar usuario #${id}: ${user.username}`);
111
+ console.log(' Email actual:', user.email);
112
+ console.log(' Rol actual:', user.role);
113
+ console.log(' Estado:', user.active ? 'Activo' : 'Inactivo');
114
+ console.log('\nUse la interfaz gráfica para editar.');
115
+ } else if (type === 'delete') {
116
+ console.log(`🗑️ Eliminar usuario #${id}: ${user.username}`);
117
+ db.deleteUser(id);
118
+ console.log('✅ Usuario eliminado');
119
+ process.exit(0);
120
+ } else if (type === 'toggle') {
121
+ console.log(`🔄 Toggle usuario #${id}: ${user.username}`);
122
+ db.toggleUserActive(id);
123
+ console.log('✅ Estado actualizado');
124
+ process.exit(0);
125
+ }
126
+ }
127
+
128
+ // ============================================
129
+ // ESTADO GLOBAL
130
+ // ============================================
131
+
132
+ let usersList = [];
133
+ let editingUserId = null;
134
+
135
+ // ============================================
136
+ // FUNCIONES DE DATOS
137
+ // ============================================
138
+
139
+ function loadUsers() {
140
+ try {
141
+ return db.getAllUsers();
142
+ } catch (error) {
143
+ console.error('Error cargando usuarios:', error.message);
144
+ return [];
145
+ }
146
+ }
147
+
148
+ function refreshAndRestart() {
149
+ // Pequeño hack: recargar la aplicación
150
+ setTimeout(() => {
151
+ db.close();
152
+ const { spawn } = require('child_process');
153
+ spawn(process.argv[0], process.argv.slice(1), { detached: true, stdio: 'ignore' });
154
+ process.exit(0);
155
+ }, 300);
156
+ }
157
+
158
+ // ============================================
159
+ // VENTANAS DE DIÁLOGO (Modales simulados)
160
+ // ============================================
161
+
162
+ function createAddUserWindow() {
163
+ const win = new Window('Nuevo Usuario', 400, 280);
164
+ const formBox = new Box('vertical');
165
+
166
+ formBox.add(new Label(''));
167
+ formBox.add(new Label('Nombre de usuario:'));
168
+ const usernameEntry = new Entry('');
169
+ formBox.add(usernameEntry);
170
+
171
+ formBox.add(new Label('Email:'));
172
+ const emailEntry = new Entry('');
173
+ formBox.add(emailEntry);
174
+
175
+ formBox.add(new Label('Rol:'));
176
+ const roleEntry = new Entry('admin');
177
+ formBox.add(roleEntry);
178
+
179
+ formBox.add(new Label(''));
180
+
181
+ const buttonBox = new Box('horizontal');
182
+
183
+ const saveBtn = new Button('💾 Guardar');
184
+ saveBtn.onClick(() => {
185
+ try {
186
+ db.createUser(usernameEntry.text, emailEntry.text, roleEntry.text);
187
+ console.log('✅ Usuario creado:', usernameEntry.text);
188
+ refreshAndRestart();
189
+ } catch (error) {
190
+ console.error('❌ Error:', error.message);
191
+ }
192
+ });
193
+
194
+ const cancelBtn = new Button('Cancelar');
195
+ cancelBtn.onClick(() => {
196
+ console.log('Operación cancelada');
197
+ refreshAndRestart();
198
+ });
199
+
200
+ buttonBox.add(saveBtn);
201
+ buttonBox.add(cancelBtn);
202
+ formBox.add(buttonBox);
203
+
204
+ win.add(formBox);
205
+ win.show();
206
+
207
+ return win;
208
+ }
209
+
210
+ function createEditUserWindow(user) {
211
+ const win = new Window(`Editar: ${user.username}`, 400, 320);
212
+ const formBox = new Box('vertical');
213
+
214
+ formBox.add(new Label(''));
215
+ formBox.add(new Label('ID: ' + user.id));
216
+
217
+ formBox.add(new Label('Nombre de usuario:'));
218
+ const usernameEntry = new Entry(user.username);
219
+ formBox.add(usernameEntry);
220
+
221
+ formBox.add(new Label('Email:'));
222
+ const emailEntry = new Entry(user.email || '');
223
+ formBox.add(emailEntry);
224
+
225
+ formBox.add(new Label('Rol:'));
226
+ const roleEntry = new Entry(user.role);
227
+ formBox.add(roleEntry);
228
+
229
+ formBox.add(new Label(''));
230
+ formBox.add(new Label(`Estado: ${user.active ? '🟢 Activo' : '🔴 Inactivo'}`));
231
+ formBox.add(new Label(''));
232
+
233
+ const buttonBox = new Box('horizontal');
234
+
235
+ const saveBtn = new Button('💾 Guardar Cambios');
236
+ saveBtn.onClick(() => {
237
+ try {
238
+ db.updateUser(user.id, {
239
+ username: usernameEntry.text,
240
+ email: emailEntry.text,
241
+ role: roleEntry.text
242
+ });
243
+ console.log('✅ Usuario actualizado:', user.id);
244
+ refreshAndRestart();
245
+ } catch (error) {
246
+ console.error('❌ Error:', error.message);
247
+ }
248
+ });
249
+
250
+ const cancelBtn = new Button('Cancelar');
251
+ cancelBtn.onClick(() => {
252
+ console.log('Operación cancelada');
253
+ refreshAndRestart();
254
+ });
255
+
256
+ buttonBox.add(saveBtn);
257
+ buttonBox.add(cancelBtn);
258
+ formBox.add(buttonBox);
259
+
260
+ win.add(formBox);
261
+ win.show();
262
+
263
+ return win;
264
+ }
265
+
266
+ function createConfirmWindow(title, message, onConfirm) {
267
+ const win = new Window(title, 350, 150);
268
+ const contentBox = new Box('vertical');
269
+
270
+ contentBox.add(new Label(''));
271
+ contentBox.add(new Label(message));
272
+ contentBox.add(new Label(''));
273
+
274
+ const buttonBox = new Box('horizontal');
275
+
276
+ const confirmBtn = new Button('Confirmar');
277
+ confirmBtn.onClick(() => {
278
+ onConfirm();
279
+ });
280
+
281
+ const cancelBtn = new Button('Cancelar');
282
+ cancelBtn.onClick(() => {
283
+ console.log('Operación cancelada');
284
+ refreshAndRestart();
285
+ });
286
+
287
+ buttonBox.add(confirmBtn);
288
+ buttonBox.add(cancelBtn);
289
+ contentBox.add(buttonBox);
290
+
291
+ win.add(contentBox);
292
+ win.show();
293
+
294
+ return win;
295
+ }
296
+
297
+ // ============================================
298
+ // VISTA PRINCIPAL
299
+ // ============================================
300
+
301
+ function createMainView() {
302
+ const mainLayout = new Box('vertical');
303
+
304
+ // Header con stats
305
+ const stats = db.getStats();
306
+ const headerLabel = new Label(`👥 BlackCoffee Admin - Total: ${stats.total} | 🟢 Activos: ${stats.active} | 🔴 Inactivos: ${stats.inactive}`);
307
+ mainLayout.add(headerLabel);
308
+
309
+ // Toolbar
310
+ const toolbarBox = new Box('horizontal');
311
+
312
+ const addBtn = new Button('➕ Nuevo');
313
+ addBtn.onClick(() => {
314
+ createAddUserWindow();
315
+ });
316
+
317
+ const refreshBtn = new Button('🔄 Refrescar');
318
+ refreshBtn.onClick(() => {
319
+ refreshAndRestart();
320
+ });
321
+
322
+ const closeBtn = new Button('❌ Salir');
323
+ closeBtn.onClick(() => {
324
+ db.close();
325
+ Window.quit();
326
+ process.exit(0);
327
+ });
328
+
329
+ toolbarBox.add(addBtn);
330
+ toolbarBox.add(refreshBtn);
331
+ toolbarBox.add(closeBtn);
332
+ mainLayout.add(toolbarBox);
333
+
334
+ // Separador
335
+ mainLayout.add(new Label(''));
336
+ mainLayout.add(new Label('═══════════════════════════════════════════════════════════'));
337
+
338
+ // Encabezados de columna
339
+ const headerBox = new Box('horizontal');
340
+ headerBox.add(new Label(' ID '));
341
+ headerBox.add(new Label(' Usuario '));
342
+ headerBox.add(new Label(' Email '));
343
+ headerBox.add(new Label(' Rol '));
344
+ headerBox.add(new Label(' Estado '));
345
+ headerBox.add(new Label(' Acciones'));
346
+ mainLayout.add(headerBox);
347
+
348
+ mainLayout.add(new Label('─────────────────────────────────────────────────────────────'));
349
+
350
+ // Lista de usuarios
351
+ usersList = loadUsers();
352
+
353
+ if (usersList.length === 0) {
354
+ mainLayout.add(new Label(' No hay usuarios registrados.'));
355
+ mainLayout.add(new Label(' Use "➕ Nuevo" para crear uno.'));
356
+ } else {
357
+ usersList.forEach(user => {
358
+ const rowBox = new Box('horizontal');
359
+
360
+ const statusIcon = user.active ? '🟢' : '🔴';
361
+ const roleIcon = user.role === 'superadmin' ? '👑' : '🔹';
362
+ const statusText = user.active ? 'Activo ' : 'Inactivo ';
363
+
364
+ let emailDisplay = user.email || 'sin@email.com';
365
+ if (emailDisplay.length > 28) {
366
+ emailDisplay = emailDisplay.substring(0, 25) + '...';
367
+ }
368
+
369
+ const idStr = user.id.toString().padStart(4, ' ');
370
+ const usernameStr = user.username.padEnd(18, ' ');
371
+ const emailStr = emailDisplay.padEnd(28, ' ');
372
+ const roleStr = user.role.padEnd(10, ' ');
373
+
374
+ rowBox.add(new Label(` ${idStr} `));
375
+ rowBox.add(new Label(` ${statusIcon} ${usernameStr} `));
376
+ rowBox.add(new Label(` ${emailStr} `));
377
+ rowBox.add(new Label(` ${roleIcon} ${roleStr} `));
378
+ rowBox.add(new Label(` ${statusText} `));
379
+
380
+ // Botón Editar
381
+ const editBtn = new Button('✏️');
382
+ editBtn.onClick(() => {
383
+ createEditUserWindow(user);
384
+ });
385
+
386
+ // Botón Toggle
387
+ const toggleBtn = new Button(user.active ? '🔓' : '🔒');
388
+ toggleBtn.onClick(() => {
389
+ const action = user.active ? 'desactivar' : 'activar';
390
+ createConfirmWindow(
391
+ `Confirmar ${action}`,
392
+ `¿${action.charAt(0).toUpperCase() + action.slice(1)} a "${user.username}"?`,
393
+ () => {
394
+ db.toggleUserActive(user.id);
395
+ console.log(`✅ Usuario ${action}do:`, user.id);
396
+ refreshAndRestart();
397
+ }
398
+ );
399
+ });
400
+
401
+ // Botón Eliminar
402
+ const deleteBtn = new Button('🗑️');
403
+ deleteBtn.onClick(() => {
404
+ createConfirmWindow(
405
+ 'Confirmar Eliminación',
406
+ `¿Eliminar usuario "${user.username}"?\nEsta acción no se puede deshacer.`,
407
+ () => {
408
+ db.deleteUser(user.id);
409
+ console.log('✅ Usuario eliminado:', user.id);
410
+ refreshAndRestart();
411
+ }
412
+ );
413
+ });
414
+
415
+ rowBox.add(editBtn);
416
+ rowBox.add(toggleBtn);
417
+ rowBox.add(deleteBtn);
418
+
419
+ mainLayout.add(rowBox);
420
+ });
421
+ }
422
+
423
+ // Separador inferior
424
+ mainLayout.add(new Label('─────────────────────────────────────────────────────────────'));
425
+ mainLayout.add(new Label(` Total: ${usersList.length} usuario(s)`));
426
+
427
+ return mainLayout;
428
+ }
429
+
430
+ // ============================================
431
+ // VENTANA PRINCIPAL
432
+ // ============================================
433
+
434
+ const mainWindow = new Window('BlackCoffee Admin Panel', 950, 500);
435
+
436
+ const scroll = new Scroll();
437
+ const mainView = createMainView();
438
+ scroll.add(mainView);
439
+
440
+ mainWindow.add(scroll);
441
+ mainWindow.show();
442
+
443
+ console.log('╔═══════════════════════════════════════════════════════════╗');
444
+ console.log('║ BlackCoffee Admin Panel - Gestión de Usuarios ║');
445
+ console.log('╠═══════════════════════════════════════════════════════════╣');
446
+ console.log(`║ Usuarios cargados: ${usersList.length.toString().padEnd(35)} ║`);
447
+ console.log('║ ║');
448
+ console.log('║ 🟢 Activo | 🔴 Inactivo | ✏️ Editar | 🗑️ Eliminar ║');
449
+ console.log('╚═══════════════════════════════════════════════════════════╝');
450
+
451
+ // Iniciar loop de eventos GTK
452
+ run();
Binary file
File without changes
@@ -0,0 +1,33 @@
1
+ -- Migración: Crear tabla de usuarios administradores
2
+ -- BlackCoffee Admin Users Table
3
+
4
+ CREATE TABLE IF NOT EXISTS admin_users (
5
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6
+ username TEXT UNIQUE NOT NULL,
7
+ password_hash TEXT NOT NULL,
8
+ email TEXT UNIQUE,
9
+ role TEXT DEFAULT 'admin',
10
+ active INTEGER DEFAULT 1,
11
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
12
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
13
+ last_login DATETIME
14
+ );
15
+
16
+ -- Índice para búsquedas por username
17
+ CREATE INDEX IF NOT EXISTS idx_admin_users_username ON admin_users(username);
18
+
19
+ -- Índice para estado activo
20
+ CREATE INDEX IF NOT EXISTS idx_admin_users_active ON admin_users(active);
21
+
22
+ -- Insertar usuario admin por defecto (password: bl4ckc0ff33)
23
+ -- El hash se genera con: salt + ':' + pbkdf2(password, salt, 1000, 64, sha512)
24
+ -- NOTA: Este usuario debe ser cambiado en producción
25
+
26
+ INSERT OR IGNORE INTO admin_users (username, password_hash, email, role, active)
27
+ VALUES (
28
+ 'admin',
29
+ 'a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6:8f4e5d6c7b8a9f0e1d2c3b4a5f6e7d8c9b0a1f2e3d4c5b6a7f8e9d0c1b2a3f4e5d6c7b8a9f0e1d2c3b4a5f6e7d8c9b0a1f2e3d4c5b6a7f8e9d0c1b2a3f4',
30
+ 'admin@blackcoffee.local',
31
+ 'superadmin',
32
+ 1
33
+ );