blackcoffee2 2.1.0 → 2.1.2

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 (89) hide show
  1. package/.env.example +67 -0
  2. package/CHANGELOG.md +167 -0
  3. package/README.md +1 -3
  4. package/config/database.json +11 -0
  5. package/controllers/admin/AuthController.js +2 -1
  6. package/core/ViewHelper.js +75 -0
  7. package/core/hotReload.js +1 -1
  8. package/data/blackcoffee_admin.db-shm +0 -0
  9. package/data/blackcoffee_admin.db-wal +0 -0
  10. package/includes/adminAuth.js +5 -3
  11. package/includes/sessions.js +1 -1
  12. package/otrack.tar.gz +0 -0
  13. package/package.json +4 -2
  14. package/programatically/initFlow.js +2 -2
  15. package/test-aplicacion.con-logisession/BlackCoffee.js +0 -226
  16. package/test-aplicacion.con-logisession/SSL_SETUP.md +0 -53
  17. package/test-aplicacion.con-logisession/certs/ca-certificate.pem +0 -32
  18. package/test-aplicacion.con-logisession/certs/ca-private-key.pem +0 -52
  19. package/test-aplicacion.con-logisession/certs/certificate-2048.pem +0 -22
  20. package/test-aplicacion.con-logisession/certs/certificate.pem +0 -32
  21. package/test-aplicacion.con-logisession/certs/private-key-2048.pem +0 -28
  22. package/test-aplicacion.con-logisession/certs/private-key.pem +0 -52
  23. package/test-aplicacion.con-logisession/config/iaQueueSetup.js +0 -84
  24. package/test-aplicacion.con-logisession/config/qwen-rules.json +0 -39
  25. package/test-aplicacion.con-logisession/controllers/analyticsController.js +0 -117
  26. package/test-aplicacion.con-logisession/controllers/auth/AdminAuthController.js +0 -142
  27. package/test-aplicacion.con-logisession/controllers/auth/AuthController.js +0 -439
  28. package/test-aplicacion.con-logisession/controllers/auth/AuthViewController.js +0 -223
  29. package/test-aplicacion.con-logisession/controllers/endpointController.js +0 -66
  30. package/test-aplicacion.con-logisession/controllers/example.js +0 -183
  31. package/test-aplicacion.con-logisession/controllers/iaQueueController.js +0 -367
  32. package/test-aplicacion.con-logisession/controllers/queueController.js +0 -206
  33. package/test-aplicacion.con-logisession/controllers/qwenQueueController.js +0 -197
  34. package/test-aplicacion.con-logisession/controllers/test.js +0 -0
  35. package/test-aplicacion.con-logisession/controllers/tracking/EventsNoFinishController.js +0 -78
  36. package/test-aplicacion.con-logisession/controllers/tracking/TrackingController.js +0 -412
  37. package/test-aplicacion.con-logisession/controllers/tracking/TrackingControllerWithLoadModel.js +0 -437
  38. package/test-aplicacion.con-logisession/hooks/admin-hooks.js +0 -20
  39. package/test-aplicacion.con-logisession/hooks/general-hooks.js +0 -97
  40. package/test-aplicacion.con-logisession/hooks/queue-hooks.js +0 -64
  41. package/test-aplicacion.con-logisession/hooks/route-directory-hooks.js +0 -38
  42. package/test-aplicacion.con-logisession/hooks/security-hooks.js +0 -24
  43. package/test-aplicacion.con-logisession/insitu-admin-client/README.md +0 -69
  44. package/test-aplicacion.con-logisession/insitu-admin-client/package.json +0 -23
  45. package/test-aplicacion.con-logisession/insitu-admin-client.js +0 -257
  46. package/test-aplicacion.con-logisession/models/ExampleModel.js +0 -88
  47. package/test-aplicacion.con-logisession/models/QueueJobModel.js +0 -263
  48. package/test-aplicacion.con-logisession/models/TokenModel.js +0 -207
  49. package/test-aplicacion.con-logisession/models/auth/AuthModel.js +0 -66
  50. package/test-aplicacion.con-logisession/models/auth/UserModel.js +0 -189
  51. package/test-aplicacion.con-logisession/models/tracking/CompletedCartModel.js +0 -213
  52. package/test-aplicacion.con-logisession/models/tracking/EventModel.js +0 -366
  53. package/test-aplicacion.con-logisession/models/tracking/EventsNoFinishModel.js +0 -131
  54. package/test-aplicacion.con-logisession/models/tracking/SessionModel.js +0 -360
  55. package/test-aplicacion.con-logisession/models/tracking/SiteFlowModel.js +0 -286
  56. package/test-aplicacion.con-logisession/models/tracking/TokenModel.js +0 -207
  57. package/test-aplicacion.con-logisession/package-lock.json +0 -3313
  58. package/test-aplicacion.con-logisession/package.json +0 -32
  59. package/test-aplicacion.con-logisession/public/blackcoffee-welcome/index.html +0 -1339
  60. package/test-aplicacion.con-logisession/public/css/style.css +0 -64
  61. package/test-aplicacion.con-logisession/public/ejemplo-estatica/index.html +0 -18
  62. package/test-aplicacion.con-logisession/public/ejemplo-estatica/script.js +0 -16
  63. package/test-aplicacion.con-logisession/public/ejemplo-estatica/styles.css +0 -43
  64. package/test-aplicacion.con-logisession/public/images/logo.svg +0 -7
  65. package/test-aplicacion.con-logisession/public/js/main.js +0 -67
  66. package/test-aplicacion.con-logisession/routes/analytics-routes.json +0 -8
  67. package/test-aplicacion.con-logisession/routes/auth-routes.json +0 -98
  68. package/test-aplicacion.con-logisession/routes/blackcoffee-welcome-routes.json +0 -20
  69. package/test-aplicacion.con-logisession/routes/duplicate-test-routes.json.disabled +0 -16
  70. package/test-aplicacion.con-logisession/routes/ejemplo-estatica-routes.json +0 -11
  71. package/test-aplicacion.con-logisession/routes/endpoints-routes.json +0 -8
  72. package/test-aplicacion.con-logisession/routes/ia-queue-routes.json +0 -26
  73. package/test-aplicacion.con-logisession/routes/product-routes.json.disabled +0 -20
  74. package/test-aplicacion.con-logisession/routes/queue-routes.json +0 -32
  75. package/test-aplicacion.con-logisession/routes/qwen-routes.json +0 -14
  76. package/test-aplicacion.con-logisession/routes/static-routes.json +0 -29
  77. package/test-aplicacion.con-logisession/routes/tracking-routes.json +0 -58
  78. package/test-aplicacion.con-logisession/routes/tracking-with-loadmodel-routes.json +0 -51
  79. package/test-aplicacion.con-logisession/utils/dbAdapter.js +0 -88
  80. package/test-aplicacion.con-logisession/utils/qbWrapper.js +0 -4
  81. package/test-aplicacion.con-logisession/utils/queueProcessor.js +0 -305
  82. package/test-aplicacion.con-logisession/utils/qwenRulesService.js +0 -131
  83. package/test-aplicacion.con-logisession/utils/tokenHelper.js +0 -22
  84. package/test-aplicacion.con-logisession/views/auth/dashboard.html +0 -443
  85. package/test-aplicacion.con-logisession/views/auth/forgot-password.html +0 -200
  86. package/test-aplicacion.con-logisession/views/auth/login.html +0 -213
  87. package/test-aplicacion.con-logisession/views/auth/register.html +0 -294
  88. package/test-aplicacion.con-logisession/views/contact/form.html +0 -47
  89. package/test-aplicacion.con-logisession/views/products/index.html +0 -39
@@ -1,360 +0,0 @@
1
- /**
2
- * Modelo para la tabla sessions en el framework JERK
3
- * Implementación del componente MVC SessionModel.js
4
- * Utiliza MariaDB como adaptador de base de datos
5
- */
6
-
7
- const { ModelBase } = require('insitu-js');
8
- const tokenHelper = require('../../utils/tokenHelper');
9
- const { getSharedAdapter } = require('../../utils/dbAdapter');
10
- const { QueryBuilder } = require('insitu-js');
11
-
12
- class SessionModel extends ModelBase {
13
- constructor(options = {}) {
14
- // Obtener el adaptador centralizado
15
- const adapter = getSharedAdapter();
16
-
17
- super({
18
- ...options,
19
- tableName: options.tableName || 'sessions',
20
- adapter: adapter
21
- });
22
-
23
- // Inicializar QueryBuilder con el adaptador centralizado
24
- this.queryBuilder = new QueryBuilder(adapter, 'sessions');
25
-
26
- // Definir campos del modelo
27
- this.fields = {
28
- id: { type: 'integer', primaryKey: true, autoIncrement: true },
29
- session_id: { type: 'string', required: true },
30
- user_id: { type: 'string' },
31
- device: { type: 'string' },
32
- country: { type: 'string' },
33
- ip: { type: 'string' },
34
- user_label: { type: 'string' },
35
- wtf_session: { type: 'string' },
36
- site_url: { type: 'string' },
37
- created_at: { type: 'datetime', auto: 'create' },
38
- updated_at: { type: 'datetime', auto: 'update' }
39
- };
40
- }
41
-
42
- /**
43
- * Registra o actualiza una sesión
44
- * @param {Object} sessionData - Datos de la sesión
45
- * @returns {Promise<Object>} - Resultado de la operación
46
- */
47
- async registerSession(sessionData) {
48
- const { session_id, user_id, device, country, ip, user, wtf_session, site_url, token } = sessionData;
49
-
50
- // Validar token
51
- const isValidToken = await tokenHelper.validateToken(token);
52
- if (!isValidToken) {
53
- throw new Error('Token inválido');
54
- }
55
-
56
- // Validar campos requeridos
57
- if (!session_id) {
58
- throw new Error('session_id es requerido');
59
- }
60
-
61
- try {
62
- // Verificar si la sesión ya existe
63
- const existingSession = await this.queryBuilder
64
- .select('*')
65
- .where('session_id', '=', session_id)
66
- .first();
67
-
68
- if (existingSession) {
69
- // Actualizar la sesión existente
70
- const updatedSession = {
71
- user_id: user_id || existingSession.user_id,
72
- device: device || existingSession.device,
73
- country: country || existingSession.country,
74
- ip: ip || existingSession.ip,
75
- user_label: user || existingSession.user_label,
76
- wtf_session: wtf_session || existingSession.wtf_session,
77
- site_url: site_url || existingSession.site_url,
78
- updated_at: new Date()
79
- };
80
-
81
- await this.queryBuilder
82
- .where('session_id', '=', session_id)
83
- .update(updatedSession);
84
-
85
- console.log(`[SESSION] Sesión actualizada: ${session_id}`);
86
-
87
- // Convertir BigInt a string para evitar problemas de serialización
88
- const serializedResult = JSON.parse(JSON.stringify({ ...existingSession, ...updatedSession }, (key, value) =>
89
- typeof value === 'bigint' ? value.toString() : value
90
- ));
91
-
92
- return {
93
- success: true,
94
- message: 'Sesión actualizada exitosamente',
95
- session_id: session_id,
96
- data: serializedResult
97
- };
98
- } else {
99
- // Crear nueva sesión
100
- const newSession = {
101
- session_id,
102
- user_id: user_id || '0',
103
- device: device || '',
104
- country: country || '',
105
- ip: ip || '',
106
- user_label: user || 'Anon',
107
- wtf_session: wtf_session || '',
108
- site_url: site_url || ''
109
- };
110
-
111
- const result = await this.queryBuilder.insert(newSession);
112
-
113
- console.log(`[SESSION] Nueva sesión registrada: ${session_id}`);
114
-
115
- // Convertir BigInt a string para evitar problemas de serialización
116
- const serializedResult = JSON.parse(JSON.stringify({ id: result.insertId, ...newSession }, (key, value) =>
117
- typeof value === 'bigint' ? value.toString() : value
118
- ));
119
-
120
- return {
121
- success: true,
122
- message: 'Sesión registrada exitosamente',
123
- session_id: session_id,
124
- data: serializedResult
125
- };
126
- }
127
- } catch (error) {
128
- console.error('[ERROR] registerSession:', error);
129
- throw new Error('Error interno del servidor');
130
- }
131
- }
132
-
133
- /**
134
- * Consulta personalizada con paginación, filtros y búsqueda global
135
- * @param {Object} filters - Filtros para la consulta
136
- * @param {Object} pagination - Parámetros de paginación
137
- * @param {string} search - Término de búsqueda global
138
- * @returns {Promise<Object>} - Resultados con información de paginación
139
- */
140
- async queryWithFilters(filters = {}, pagination = {}, search = '') {
141
- try {
142
- // Mostrar parámetros de filtro para depuración
143
- console.log('Filtros recibidos en SessionModel queryWithFilters:', filters);
144
-
145
- // Extraer parámetros de paginación
146
- const offset = pagination.offset !== undefined ? parseInt(pagination.offset) : 0;
147
- const limit = pagination.limit ? parseInt(pagination.limit) : 10;
148
-
149
- // Construir condiciones WHERE dinámicamente
150
- let whereConditions = [];
151
- let whereParams = [];
152
-
153
- // Extraer parámetros de filtro específicos para fechas
154
- const { date_from, date_to, created_at_min, created_at_max, updated_at_min, updated_at_max,
155
- session_id, user_id, device, country, ip, user_label, wtf_session, site_url } = filters;
156
-
157
- // Filtro por fecha desde (date_from) - para created_at
158
- if (date_from) {
159
- // Si la fecha no tiene hora (es solo YYYY-MM-DD), agregar hora mínima del día
160
- let startDate = date_from;
161
- if (date_from.length === 10) { // Formato YYYY-MM-DD
162
- startDate = `${date_from} 00:00:00`;
163
- }
164
- console.log('Aplicando filtro: created_at >=', startDate);
165
- whereConditions.push('created_at >= ?');
166
- whereParams.push(startDate);
167
- }
168
-
169
- // Filtro por fecha hasta (date_to) - para created_at
170
- if (date_to) {
171
- // Si la fecha de fin no tiene hora (es solo YYYY-MM-DD), agregar hora 23:59:59 para incluir todo el día
172
- let endDate = date_to;
173
- if (date_to.length === 10) { // Formato YYYY-MM-DD
174
- endDate = `${date_to} 23:59:59`;
175
- }
176
- console.log('Aplicando filtro: created_at <=', endDate);
177
- whereConditions.push('created_at <= ?');
178
- whereParams.push(endDate);
179
- }
180
-
181
- // Mantener la funcionalidad existente para _min y _max
182
- if (created_at_min) {
183
- // Convertir formato de fecha si es necesario
184
- let processedValue = created_at_min;
185
- if (typeof created_at_min === 'string' && !created_at_min.includes(':')) {
186
- // Si no tiene hora, agregar hora mínima del día (00:00:00)
187
- processedValue = `${created_at_min} 00:00:00`;
188
- }
189
- whereConditions.push('created_at >= ?');
190
- whereParams.push(processedValue);
191
- }
192
-
193
- if (created_at_max) {
194
- // Convertir formato de fecha si es necesario
195
- let processedValue = created_at_max;
196
- if (typeof created_at_max === 'string' && !created_at_max.includes(':')) {
197
- // Si no tiene hora, agregar hora máxima del día (23:59:59)
198
- processedValue = `${created_at_max} 23:59:59`;
199
- }
200
- whereConditions.push('created_at <= ?');
201
- whereParams.push(processedValue);
202
- }
203
-
204
- if (updated_at_min) {
205
- // Convertir formato de fecha si es necesario
206
- let processedValue = updated_at_min;
207
- if (typeof updated_at_min === 'string' && !updated_at_min.includes(':')) {
208
- // Si no tiene hora, agregar hora mínima del día (00:00:00)
209
- processedValue = `${updated_at_min} 00:00:00`;
210
- }
211
- whereConditions.push('updated_at >= ?');
212
- whereParams.push(processedValue);
213
- }
214
-
215
- if (updated_at_max) {
216
- // Convertir formato de fecha si es necesario
217
- let processedValue = updated_at_max;
218
- if (typeof updated_at_max === 'string' && !updated_at_max.includes(':')) {
219
- // Si no tiene hora, agregar hora máxima del día (23:59:59)
220
- processedValue = `${updated_at_max} 23:59:59`;
221
- }
222
- whereConditions.push('updated_at <= ?');
223
- whereParams.push(processedValue);
224
- }
225
-
226
- // Filtros por campos específicos
227
- if (session_id) {
228
- whereConditions.push('session_id = ?');
229
- whereParams.push(session_id);
230
- }
231
-
232
- if (user_id) {
233
- whereConditions.push('user_id = ?');
234
- whereParams.push(user_id);
235
- }
236
-
237
- if (device) {
238
- whereConditions.push('device = ?');
239
- whereParams.push(device);
240
- }
241
-
242
- if (country) {
243
- whereConditions.push('country = ?');
244
- whereParams.push(country);
245
- }
246
-
247
- if (ip) {
248
- whereConditions.push('ip = ?');
249
- whereParams.push(ip);
250
- }
251
-
252
- if (user_label) {
253
- whereConditions.push('user_label = ?');
254
- whereParams.push(user_label);
255
- }
256
-
257
- if (wtf_session) {
258
- whereConditions.push('wtf_session = ?');
259
- whereParams.push(wtf_session);
260
- }
261
-
262
- if (site_url) {
263
- whereConditions.push('site_url = ?');
264
- whereParams.push(site_url);
265
- }
266
-
267
-
268
- // Aplicar búsqueda global si se proporciona
269
- if (search) {
270
- // Campos en los que se realiza la búsqueda global
271
- const searchFields = ['session_id', 'user_id', 'device', 'country', 'ip', 'user_label'];
272
-
273
- const searchConditions = searchFields.map(field => `${field} LIKE ?`).join(' OR ');
274
- whereConditions.push(`(${searchConditions})`);
275
-
276
- // Agregar parámetros de búsqueda
277
- searchFields.forEach(() => {
278
- whereParams.push(`%${search}%`);
279
- });
280
- }
281
-
282
- // Construir la cláusula WHERE completa
283
- const whereClause = whereConditions.length > 0
284
- ? 'WHERE ' + whereConditions.join(' AND ')
285
- : 'WHERE 1=1'; // Asegurar que siempre haya una condición
286
-
287
- // Consulta SQL para obtener los registros
288
- const query = `
289
- SELECT *
290
- FROM sessions
291
- ${whereClause}
292
- ORDER BY id DESC
293
- LIMIT ? OFFSET ?
294
- `;
295
-
296
- // Agregar parámetros de paginación
297
- const allParams = [...whereParams, limit, offset];
298
-
299
- // Usar el método complexQuery del QueryBuilder
300
- const { QueryBuilder } = require('insitu-js');
301
- const qb = new QueryBuilder(this.queryBuilder.adapter);
302
- qb.complexQuery(query, allParams);
303
-
304
- // Obtener los resultados
305
- const rows = await qb.get();
306
-
307
- // Consulta para obtener el conteo total con los mismos filtros
308
- const countQuery = `
309
- SELECT COUNT(*) as total
310
- FROM sessions
311
- ${whereClause}
312
- `;
313
-
314
- // Usar el método complexQuery del QueryBuilder para la consulta de conteo
315
- const countQb = new QueryBuilder(this.queryBuilder.adapter);
316
- countQb.complexQuery(countQuery, whereParams); // Sin parámetros de paginación
317
-
318
- const countResult = await countQb.first();
319
- const total = countResult ? countResult.total : 0;
320
-
321
- return {
322
- total: parseInt(total),
323
- rows: rows
324
- };
325
- } catch (error) {
326
- console.error(`Error en queryWithFilters para ${this.tableName}:`, error.message);
327
- throw error;
328
- }
329
- }
330
-
331
- /**
332
- * Obtiene sesiones con paginación, filtros y búsqueda
333
- * @param {Object} queryParams - Parámetros de consulta (offset, limit, search, filtros)
334
- * @returns {Promise<Object>} - Resultados con información de paginación
335
- */
336
- async getSessions(queryParams = {}) {
337
- // Obtener parámetros de paginación
338
- const offset = parseInt(queryParams.offset) || 0;
339
- const limit = parseInt(queryParams.limit) || 10;
340
- const search = queryParams.search || '';
341
-
342
- // Obtener otros parámetros de filtro excluyendo los de paginación y búsqueda
343
- const filters = {};
344
- for (const [key, value] of Object.entries(queryParams)) {
345
- if (!['offset', 'limit', 'search', 'sort', 'order'].includes(key)) {
346
- if (value !== '') { // Solo añadir filtros que no estén vacíos
347
- filters[key] = value;
348
- }
349
- }
350
- }
351
-
352
- return await this.queryWithFilters(
353
- filters,
354
- { offset, limit },
355
- search
356
- );
357
- }
358
- }
359
-
360
- module.exports = SessionModel;
@@ -1,286 +0,0 @@
1
- /**
2
- * Modelo para la tabla site_flow en el framework JERK
3
- * Implementación del componente MVC SiteFlowModel.js
4
- * Utiliza MariaDB como adaptador de base de datos
5
- */
6
-
7
- const { ModelBase } = require('insitu-js');
8
- const tokenHelper = require('../../utils/tokenHelper');
9
- const { getSharedAdapter } = require('../../utils/dbAdapter');
10
- const { QueryBuilder } = require('insitu-js');
11
-
12
- class SiteFlowModel extends ModelBase {
13
- constructor(options = {}) {
14
- // Obtener el adaptador centralizado
15
- const adapter = getSharedAdapter();
16
-
17
- super({
18
- ...options,
19
- tableName: options.tableName || 'site_flow',
20
- adapter: adapter
21
- });
22
-
23
- // Inicializar QueryBuilder con el adaptador centralizado
24
- this.queryBuilder = new QueryBuilder(adapter, 'site_flow');
25
-
26
- // Definir campos del modelo
27
- this.fields = {
28
- id: { type: 'integer', primaryKey: true, autoIncrement: true },
29
- session_header_id: { type: 'integer' },
30
- event_type: { type: 'string', required: true },
31
- qty: { type: 'integer' },
32
- product_id: { type: 'integer' },
33
- sku: { type: 'string' },
34
- product_name: { type: 'string' },
35
- url: { type: 'string' },
36
- price: { type: 'decimal' },
37
- old_qty: { type: 'integer' },
38
- new_qty: { type: 'integer' },
39
- compressed_data: { type: 'text' },
40
- data_hash: { type: 'string' },
41
- event_timestamp: { type: 'datetime' },
42
- session_id: { type: 'string' },
43
- wtf_session: { type: 'string' },
44
- site_url: { type: 'string' },
45
- created_at: { type: 'datetime', auto: 'create' },
46
- updated_at: { type: 'datetime', auto: 'update' }
47
- };
48
- }
49
-
50
- /**
51
- * Inserta un flujo de sitio
52
- * @param {Object} siteFlowData - Datos del flujo de sitio
53
- * @returns {Promise<Object>} - Resultado de la operación
54
- */
55
- async insertSiteFlow(siteFlowData) {
56
- const {
57
- session_header_id, event_type, qty, product_id, sku, product_name,
58
- url, price, old_qty, new_qty, compressed_data, data_hash,
59
- event_timestamp, session_id, wtf_session, site_url, token
60
- } = siteFlowData;
61
-
62
- // Validar token
63
- const isValidToken = await tokenHelper.validateToken(token);
64
- if (!isValidToken) {
65
- throw new Error('Token inválido');
66
- }
67
-
68
- // Validar campos requeridos
69
- if (!event_type) {
70
- throw new Error('event_type es requerido');
71
- }
72
-
73
- try {
74
- // Asegurar valores por defecto para campos que no pueden ser NULL
75
- const safeSessionHeaderId = session_header_id !== undefined && session_header_id !== null ? session_header_id : 0;
76
-
77
- // Crear el objeto de datos para insertar
78
- const newSiteFlow = {
79
- session_header_id: safeSessionHeaderId,
80
- event_type,
81
- qty: qty || null,
82
- product_id: product_id || null,
83
- sku: sku || null,
84
- product_name: product_name || null,
85
- url: url || null,
86
- price: price || null,
87
- old_qty: old_qty || null,
88
- new_qty: new_qty || null,
89
- compressed_data: compressed_data || null,
90
- data_hash: data_hash || null,
91
- event_timestamp: event_timestamp || null,
92
- session_id: session_id || null,
93
- wtf_session: wtf_session || null,
94
- site_url: site_url || null
95
- };
96
-
97
- // Insertar el flujo de sitio usando QueryBuilder
98
- const result = await this.queryBuilder
99
- .reset()
100
- .insert(newSiteFlow);
101
-
102
- console.log(`[TRACKING] Flujo de sitio registrado: ${event_type}`);
103
-
104
- // Convertir BigInt a string para evitar problemas de serialización
105
- const serializedResult = JSON.parse(JSON.stringify({ id: result.insertId, ...newSiteFlow }, (key, value) =>
106
- typeof value === 'bigint' ? value.toString() : value
107
- ));
108
-
109
- return {
110
- success: true,
111
- message: 'Flujo de sitio registrado exitosamente',
112
- event_type: event_type,
113
- data: serializedResult
114
- };
115
- } catch (error) {
116
- console.error('[ERROR] insertSiteFlow:', error);
117
- throw new Error('Error interno del servidor');
118
- }
119
- }
120
-
121
- /**
122
- * Consulta personalizada con paginación, filtros y búsqueda global
123
- * @param {Object} filters - Filtros para la consulta
124
- * @param {Object} pagination - Parámetros de paginación
125
- * @param {string} search - Término de búsqueda global
126
- * @returns {Promise<Object>} - Resultados con información de paginación
127
- */
128
- async queryWithFilters(filters = {}, pagination = {}, search = '') {
129
- try {
130
- // Mostrar parámetros de filtro para depuración
131
- console.log('Filtros recibidos en SiteFlowModel queryWithFilters:', filters);
132
-
133
- // Extraer parámetros de paginación
134
- const offset = pagination.offset !== undefined ? parseInt(pagination.offset) : 0;
135
- const limit = pagination.limit ? parseInt(pagination.limit) : 10;
136
-
137
- // Extraer parámetros de filtro
138
- const { date_from, date_to, event_type, session_id, wtf_session, url, site_url } = filters;
139
-
140
- // Construir condiciones WHERE dinámicamente
141
- let whereConditions = [];
142
- let whereParams = [];
143
-
144
- // Filtro por fecha desde (date_from) - para event_timestamp
145
- if (date_from) {
146
- // Si la fecha no tiene hora (es solo YYYY-MM-DD), agregar hora mínima del día
147
- let startDate = date_from;
148
- if (date_from.length === 10) { // Formato YYYY-MM-DD
149
- startDate = `${date_from} 00:00:00`;
150
- }
151
- console.log('Aplicando filtro: event_timestamp >=', startDate);
152
- whereConditions.push('event_timestamp >= ?');
153
- whereParams.push(startDate);
154
- }
155
-
156
- // Filtro por fecha hasta (date_to) - para event_timestamp
157
- if (date_to) {
158
- // Si la fecha de fin no tiene hora (es solo YYYY-MM-DD), agregar hora 23:59:59 para incluir todo el día
159
- let endDate = date_to;
160
- if (date_to.length === 10) { // Formato YYYY-MM-DD
161
- endDate = `${date_to} 23:59:59`;
162
- }
163
- console.log('Aplicando filtro: event_timestamp <=', endDate);
164
- whereConditions.push('event_timestamp <= ?');
165
- whereParams.push(endDate);
166
- }
167
-
168
- // Filtros por campos específicos
169
- if (event_type) {
170
- whereConditions.push('event_type = ?');
171
- whereParams.push(event_type);
172
- }
173
-
174
- if (session_id) {
175
- whereConditions.push('session_id = ?');
176
- whereParams.push(session_id);
177
- }
178
-
179
- if (wtf_session) {
180
- whereConditions.push('wtf_session = ?');
181
- whereParams.push(wtf_session);
182
- }
183
-
184
- if (url) {
185
- whereConditions.push('url LIKE ?');
186
- whereParams.push(`%${url}%`);
187
- }
188
-
189
- if (site_url) {
190
- whereConditions.push('site_url = ?');
191
- whereParams.push(site_url);
192
- }
193
-
194
- // Aplicar búsqueda global si se proporciona
195
- if (search) {
196
- // Campos en los que se realiza la búsqueda global
197
- const searchFields = ['event_type', 'product_name', 'sku', 'url', 'session_id', 'wtf_session', 'site_url'];
198
-
199
- const searchConditions = searchFields.map(field => `${field} LIKE ?`).join(' OR ');
200
- whereConditions.push(`(${searchConditions})`);
201
-
202
- // Agregar parámetros de búsqueda
203
- searchFields.forEach(() => {
204
- whereParams.push(`%${search}%`);
205
- });
206
- }
207
-
208
- // Construir la cláusula WHERE completa
209
- const whereClause = whereConditions.length > 0
210
- ? 'WHERE ' + whereConditions.join(' AND ')
211
- : 'WHERE 1=1'; // Asegurar que siempre haya una condición
212
-
213
- // Consulta SQL para obtener los registros
214
- const query = `
215
- SELECT *
216
- FROM site_flow
217
- ${whereClause}
218
- ORDER BY id DESC
219
- LIMIT ? OFFSET ?
220
- `;
221
-
222
- // Agregar parámetros de paginación
223
- const allParams = [...whereParams, limit, offset];
224
-
225
- // Usar el método complexQuery del QueryBuilder
226
- const { QueryBuilder } = require('insitu-js');
227
- const qb = new QueryBuilder(this.queryBuilder.adapter);
228
- qb.complexQuery(query, allParams);
229
-
230
- // Obtener los resultados
231
- const rows = await qb.get();
232
-
233
- // Consulta para obtener el conteo total con los mismos filtros
234
- const countQuery = `
235
- SELECT COUNT(*) as total
236
- FROM site_flow
237
- ${whereClause}
238
- `;
239
-
240
- // Usar el método complexQuery del QueryBuilder para la consulta de conteo
241
- const countQb = new QueryBuilder(this.queryBuilder.adapter);
242
- countQb.complexQuery(countQuery, whereParams); // Sin parámetros de paginación
243
-
244
- const countResult = await countQb.first();
245
- const total = countResult ? countResult.total : 0;
246
-
247
- return {
248
- total: parseInt(total),
249
- rows: rows
250
- };
251
- } catch (error) {
252
- console.error(`Error en queryWithFilters para ${this.tableName}:`, error.message);
253
- throw error;
254
- }
255
- }
256
-
257
- /**
258
- * Obtiene flujos de sitio con paginación, filtros y búsqueda
259
- * @param {Object} queryParams - Parámetros de consulta (offset, limit, search, filtros)
260
- * @returns {Promise<Object>} - Resultados con información de paginación
261
- */
262
- async getSiteFlow(queryParams = {}) {
263
- // Obtener parámetros de paginación
264
- const offset = parseInt(queryParams.offset) || 0;
265
- const limit = parseInt(queryParams.limit) || 10;
266
- const search = queryParams.search || '';
267
-
268
- // Obtener otros parámetros de filtro excluyendo los de paginación y búsqueda
269
- const filters = {};
270
- for (const [key, value] of Object.entries(queryParams)) {
271
- if (!['offset', 'limit', 'search', 'sort', 'order'].includes(key)) {
272
- if (value !== '') { // Solo añadir filtros que no estén vacíos
273
- filters[key] = value;
274
- }
275
- }
276
- }
277
-
278
- return await this.queryWithFilters(
279
- filters,
280
- { offset, limit },
281
- search
282
- );
283
- }
284
- }
285
-
286
- module.exports = SiteFlowModel;