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,366 +0,0 @@
1
- /**
2
- * Modelo para la tabla de eventos en el framework JERK
3
- * Implementación del componente MVC EventModel.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 EventModel extends ModelBase {
13
- constructor(options = {}) {
14
- // Obtener el adaptador centralizado
15
- const adapter = getSharedAdapter();
16
-
17
- super({
18
- ...options,
19
- tableName: options.tableName || 'events_no_finish', // Tabla por defecto
20
- adapter: adapter
21
- });
22
-
23
- // Inicializar QueryBuilder con el adaptador centralizado
24
- this.queryBuilder = new QueryBuilder(adapter, 'events_no_finish');
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
- * Registra un evento
52
- * @param {Object} eventData - Datos del evento
53
- * @returns {Promise<Object>} - Resultado de la operación
54
- */
55
- async registerEvent(eventData) {
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
- } = eventData;
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
- // Determinar la tabla destino según el tipo de evento
75
- let tableName = 'events_no_finish'; // Por defecto
76
-
77
- if (event_type === 'checkout_completed') {
78
- tableName = 'completed_carts';
79
- } else if (event_type === 'page_view') {
80
- tableName = 'site_flow';
81
- }
82
- // Otros eventos van a events_no_finish por defecto
83
-
84
- // Asegurar valores por defecto para campos que no pueden ser NULL
85
- const safeSessionHeaderId = session_header_id !== undefined && session_header_id !== null ? session_header_id : 0;
86
-
87
- // Crear el objeto de datos para insertar
88
- const newEvent = {
89
- session_header_id: safeSessionHeaderId,
90
- event_type,
91
- qty: qty || null,
92
- product_id: product_id || null,
93
- sku: sku || null,
94
- product_name: product_name || null,
95
- url: url || null,
96
- price: price || null,
97
- old_qty: old_qty || null,
98
- new_qty: new_qty || null,
99
- compressed_data: compressed_data || null,
100
- data_hash: data_hash || null,
101
- event_timestamp: event_timestamp || null,
102
- session_id: session_id || null,
103
- wtf_session: wtf_session || null,
104
- site_url: site_url || null
105
- };
106
-
107
- // Usar QueryBuilder con la tabla específica
108
- // Creamos un nuevo QueryBuilder temporal para la tabla específica
109
- const { QueryBuilder: QB } = require('insitu-js');
110
- const tempQueryBuilder = new QB(this.queryBuilder.adapter, tableName);
111
-
112
- // Insertar el evento usando QueryBuilder
113
- const result = await tempQueryBuilder.insert(newEvent);
114
-
115
- console.log(`[TRACKING] Evento registrado: ${event_type} en ${tableName}`);
116
-
117
- // Si el evento es checkout_completed, mover los eventos relacionados de events_no_finish a completed_carts
118
- if (event_type === 'checkout_completed') {
119
- try {
120
- // Obtener todos los session_id únicos de los registros cuyo event_type sea checkout_completed en completed_carts
121
- const checkoutCompletedQuery = new QB(this.queryBuilder.adapter, 'completed_carts');
122
- const checkoutCompletedSessions = await checkoutCompletedQuery
123
- .select('DISTINCT session_id')
124
- .where('event_type', '=', 'checkout_completed')
125
- .get();
126
-
127
- // Mover todos los registros con los session_ids obtenidos hacia la tabla completed_carts
128
- for (const row of checkoutCompletedSessions) {
129
- const sessionId = row.session_id;
130
-
131
- // Consultar eventos relacionados en events_no_finish para este session_id
132
- const relatedEventsQuery = new QB(this.queryBuilder.adapter, 'events_no_finish');
133
- const relatedEvents = await relatedEventsQuery
134
- .where('session_id', '=', sessionId)
135
- .orderBy('id', 'ASC')
136
- .get();
137
-
138
- if (relatedEvents.length > 0) {
139
- // Insertar los eventos relacionados en completed_carts
140
- const completedCartsQuery = new QB(this.queryBuilder.adapter, 'completed_carts');
141
-
142
- for (const event of relatedEvents) {
143
- // Excluir el campo id ya que es autoincremental
144
- const { id, ...eventData } = event;
145
- await completedCartsQuery.insert(eventData);
146
- }
147
-
148
- // Eliminar los eventos movidos de events_no_finish
149
- const deleteQuery = new QB(this.queryBuilder.adapter, 'events_no_finish');
150
- await deleteQuery.where('session_id', '=', sessionId).delete();
151
-
152
- console.log(`[TRACKING] Movidos ${relatedEvents.length} eventos de la sesión ${sessionId} de events_no_finish a completed_carts`);
153
- }
154
- }
155
- } catch (moveError) {
156
- console.error('[ERROR] Al mover eventos relacionados:', moveError);
157
- // No lanzamos el error para que no afecte el registro principal
158
- }
159
- }
160
-
161
- // Convertir BigInt a string para evitar problemas de serialización
162
- const serializedResult = JSON.parse(JSON.stringify({ id: result.insertId, ...newEvent }, (key, value) =>
163
- typeof value === 'bigint' ? value.toString() : value
164
- ));
165
-
166
- return {
167
- success: true,
168
- message: 'Evento registrado exitosamente',
169
- event_type: event_type,
170
- table: tableName,
171
- data: serializedResult
172
- };
173
- } catch (error) {
174
- console.error('[ERROR] registerEvent:', error);
175
- throw new Error('Error interno del servidor');
176
- }
177
- }
178
-
179
- /**
180
- * Consulta personalizada con paginación, filtros y búsqueda global
181
- * @param {Object} filters - Filtros para la consulta
182
- * @param {Object} pagination - Parámetros de paginación
183
- * @param {string} search - Término de búsqueda global
184
- * @returns {Promise<Object>} - Resultados con información de paginación
185
- */
186
- async queryWithFilters(filters = {}, pagination = {}, search = '') {
187
- try {
188
- // Mostrar parámetros de filtro para depuración
189
- console.log('Filtros recibidos en queryWithFilters:', filters);
190
-
191
- // Extraer parámetros de paginación
192
- const offset = pagination.offset !== undefined ? parseInt(pagination.offset) : 0;
193
- const limit = pagination.limit ? parseInt(pagination.limit) : 10;
194
-
195
- // Extraer parámetros de filtro
196
- const { created_at_min, created_at_max, date_from, date_to, event_type, session_id, wtf_session, url, site_url } = filters;
197
-
198
- // Construir condiciones WHERE dinámicamente
199
- let whereConditions = [];
200
- let whereParams = [];
201
-
202
- // Filtro por fecha desde (created_at_min)
203
- if (created_at_min) {
204
- // Si la fecha no tiene hora (es solo YYYY-MM-DD), agregar hora mínima del día
205
- let startDate = created_at_min;
206
- if (created_at_min.length === 10) { // Formato YYYY-MM-DD
207
- startDate = `${created_at_min} 00:00:00`;
208
- }
209
- whereConditions.push('created_at >= ?');
210
- whereParams.push(startDate);
211
- }
212
-
213
- // Filtro por fecha hasta (created_at_max)
214
- if (created_at_max) {
215
- // Si la fecha de fin no tiene hora (es solo YYYY-MM-DD), agregar hora 23:59:59 para incluir todo el día
216
- let endDate = created_at_max;
217
- if (created_at_max.length === 10) { // Formato YYYY-MM-DD
218
- endDate = `${created_at_max} 23:59:59`;
219
- }
220
- whereConditions.push('created_at <= ?');
221
- whereParams.push(endDate);
222
- }
223
-
224
- // Filtro por fecha desde (date_from) - para event_timestamp
225
- if (date_from) {
226
- // Si la fecha no tiene hora (es solo YYYY-MM-DD), agregar hora mínima del día
227
- let startDate = date_from;
228
- if (date_from.length === 10) { // Formato YYYY-MM-DD
229
- startDate = `${date_from} 00:00:00`;
230
- }
231
- console.log('Aplicando filtro: event_timestamp >=', startDate);
232
- whereConditions.push('event_timestamp >= ?');
233
- whereParams.push(startDate);
234
- }
235
-
236
- // Filtro por fecha hasta (date_to) - para event_timestamp
237
- if (date_to) {
238
- // Si la fecha de fin no tiene hora (es solo YYYY-MM-DD), agregar hora 23:59:59 para incluir todo el día
239
- let endDate = date_to;
240
- if (date_to.length === 10) { // Formato YYYY-MM-DD
241
- endDate = `${date_to} 23:59:59`;
242
- }
243
- console.log('Aplicando filtro: event_timestamp <=', endDate);
244
- whereConditions.push('event_timestamp <= ?');
245
- whereParams.push(endDate);
246
- }
247
-
248
- // Filtros por campos específicos
249
- if (event_type) {
250
- whereConditions.push('event_type = ?');
251
- whereParams.push(event_type);
252
- }
253
-
254
- if (session_id) {
255
- whereConditions.push('session_id = ?');
256
- whereParams.push(session_id);
257
- }
258
-
259
- if (wtf_session) {
260
- whereConditions.push('wtf_session = ?');
261
- whereParams.push(wtf_session);
262
- }
263
-
264
- if (url) {
265
- whereConditions.push('url LIKE ?');
266
- whereParams.push(`%${url}%`);
267
- }
268
-
269
- if (site_url) {
270
- whereConditions.push('site_url = ?');
271
- whereParams.push(site_url);
272
- }
273
-
274
- // Aplicar búsqueda global si se proporciona
275
- if (search) {
276
- // Campos en los que se realiza la búsqueda global
277
- const searchFields = ['event_type', 'product_name', 'sku', 'url'];
278
-
279
- const searchConditions = searchFields.map(field => `${field} LIKE ?`).join(' OR ');
280
- whereConditions.push(`(${searchConditions})`);
281
-
282
- // Agregar parámetros de búsqueda
283
- searchFields.forEach(() => {
284
- whereParams.push(`%${search}%`);
285
- });
286
- }
287
-
288
- // Construir la cláusula WHERE completa
289
- const whereClause = whereConditions.length > 0
290
- ? 'WHERE ' + whereConditions.join(' AND ')
291
- : 'WHERE 1=1'; // Asegurar que siempre haya una condición
292
-
293
- // Consulta SQL para obtener los registros
294
- const query = `
295
- SELECT *
296
- FROM events_no_finish
297
- ${whereClause}
298
- ORDER BY id DESC
299
- LIMIT ? OFFSET ?
300
- `;
301
-
302
- // Agregar parámetros de paginación
303
- const allParams = [...whereParams, limit, offset];
304
-
305
- // Usar el método complexQuery del QueryBuilder
306
- const { QueryBuilder } = require('insitu-js');
307
- const qb = new QueryBuilder(this.queryBuilder.adapter);
308
- qb.complexQuery(query, allParams);
309
-
310
- // Obtener los resultados
311
- const rows = await qb.get();
312
-
313
- // Consulta para obtener el conteo total con los mismos filtros
314
- const countQuery = `
315
- SELECT COUNT(*) as total
316
- FROM events_no_finish
317
- ${whereClause}
318
- `;
319
-
320
- // Usar el método complexQuery del QueryBuilder para la consulta de conteo
321
- const countQb = new QueryBuilder(this.queryBuilder.adapter);
322
- countQb.complexQuery(countQuery, allParams.slice(0, -2)); // Sin parámetros de paginación
323
-
324
- const countResult = await countQb.first();
325
- const total = countResult ? countResult.total : 0;
326
-
327
- return {
328
- total: parseInt(total),
329
- rows: rows
330
- };
331
- } catch (error) {
332
- console.error(`Error en queryWithFilters para ${this.tableName}:`, error.message);
333
- throw error;
334
- }
335
- }
336
-
337
- /**
338
- * Obtiene eventos con paginación, filtros y búsqueda
339
- * @param {Object} queryParams - Parámetros de consulta (offset, limit, search, filtros)
340
- * @returns {Promise<Object>} - Resultados con información de paginación
341
- */
342
- async getEvents(queryParams = {}) {
343
- // Obtener parámetros de paginación
344
- const offset = parseInt(queryParams.offset) || 0;
345
- const limit = parseInt(queryParams.limit) || 10;
346
- const search = queryParams.search || '';
347
-
348
- // Obtener otros parámetros de filtro excluyendo los de paginación y búsqueda
349
- const filters = {};
350
- for (const [key, value] of Object.entries(queryParams)) {
351
- if (!['offset', 'limit', 'search', 'sort', 'order'].includes(key)) {
352
- if (value !== '') { // Solo añadir filtros que no estén vacíos
353
- filters[key] = value;
354
- }
355
- }
356
- }
357
-
358
- return await this.queryWithFilters(
359
- filters,
360
- { offset, limit },
361
- search
362
- );
363
- }
364
- }
365
-
366
- module.exports = EventModel;
@@ -1,131 +0,0 @@
1
- /**
2
- * Modelo para obtener sesiones únicas de eventos no finalizados
3
- * EventsNoFinishModel.js
4
- */
5
-
6
- const { ModelBase } = require('insitu-js');
7
- const { getSharedAdapter } = require('../../utils/dbAdapter');
8
- const { QueryBuilder } = require('insitu-js');
9
-
10
- class EventsNoFinishModel extends ModelBase {
11
- constructor(options = {}) {
12
- // Obtener el adaptador centralizado
13
- const adapter = getSharedAdapter();
14
-
15
- super({
16
- ...options,
17
- tableName: options.tableName || 'events_no_finish',
18
- adapter: adapter
19
- });
20
-
21
- // Inicializar QueryBuilder con el adaptador centralizado
22
- this.queryBuilder = new QueryBuilder(adapter, 'events_no_finish');
23
- }
24
-
25
- /**
26
- * Obtiene sesiones únicas de eventos no finalizados con paginación y filtros
27
- * @param {Object} filters - Filtros para la consulta
28
- * @param {Object} pagination - Parámetros de paginación
29
- * @returns {Promise<Object>} - Resultados con información de paginación
30
- */
31
- async getUniqueSessions(filters = {}, pagination = {}) {
32
- try {
33
- // Extraer parámetros de paginación
34
- const offset = pagination.offset !== undefined ? parseInt(pagination.offset) : 0;
35
- const limit = pagination.limit ? parseInt(pagination.limit) : 10;
36
-
37
- // Definir los tipos de eventos que consideramos como "no finalizados"
38
- // Esto excluye los eventos de checkout_completed
39
- const incompleteEventTypes = ['view_product', 'add_to_cart', 'checkout_initialized'];
40
-
41
- // Construir condiciones WHERE dinámicamente
42
- let whereConditions = ['event_type IN (?) AND product_id IS NOT NULL'];
43
- let whereParams = [incompleteEventTypes];
44
-
45
- // Filtros adicionales
46
- const { session_id, wtf_session, site_url, event_type, date_from, date_to } = filters;
47
-
48
- if (session_id) {
49
- whereConditions.push('session_id = ?');
50
- whereParams.push(session_id);
51
- }
52
-
53
- if (wtf_session) {
54
- whereConditions.push('wtf_session = ?');
55
- whereParams.push(wtf_session);
56
- }
57
-
58
- if (site_url) {
59
- whereConditions.push('site_url = ?');
60
- whereParams.push(site_url);
61
- }
62
-
63
- if (event_type) {
64
- whereConditions.push('event_type = ?');
65
- whereParams.push(event_type);
66
- }
67
-
68
- if (date_from) {
69
- let startDate = date_from;
70
- if (date_from.length === 10) { // Formato YYYY-MM-DD
71
- startDate = `${date_from} 00:00:00`;
72
- }
73
- whereConditions.push('created_at >= ?');
74
- whereParams.push(startDate);
75
- }
76
-
77
- if (date_to) {
78
- let endDate = date_to;
79
- if (date_to.length === 10) { // Formato YYYY-MM-DD
80
- endDate = `${date_to} 23:59:59`;
81
- }
82
- whereConditions.push('created_at <= ?');
83
- whereParams.push(endDate);
84
- }
85
-
86
- // Consulta SQL para obtener las sesiones únicas
87
- const query = `
88
- SELECT DISTINCT session_id, wtf_session, site_url, product_id, product_name, MAX(created_at) as last_event_date
89
- FROM events_no_finish
90
- WHERE ${whereConditions.join(' AND ')}
91
- GROUP BY session_id, wtf_session, site_url, product_id, product_name
92
- ORDER BY last_event_date DESC
93
- LIMIT ? OFFSET ?
94
- `;
95
-
96
- // Agregar parámetros de paginación
97
- const allParams = [...whereParams, limit, offset];
98
-
99
- // Ejecutar la consulta
100
- const { QueryBuilder } = require('insitu-js');
101
- const qb = new QueryBuilder(this.queryBuilder.adapter);
102
- qb.complexQuery(query, allParams);
103
-
104
- // Obtener los resultados
105
- const rows = await qb.get();
106
-
107
- // Consulta para obtener el conteo total de sesiones únicas
108
- const countQuery = `
109
- SELECT COUNT(DISTINCT CONCAT(session_id, '-', wtf_session, '-', site_url, '-', product_id, '-', product_name)) as total
110
- FROM events_no_finish
111
- WHERE ${whereConditions.join(' AND ')}
112
- `;
113
-
114
- // Ejecutar la consulta de conteo
115
- const countQb = new QueryBuilder(this.queryBuilder.adapter);
116
- countQb.complexQuery(countQuery, whereParams);
117
- const countResult = await countQb.first();
118
- const total = countResult ? countResult.total : 0;
119
-
120
- return {
121
- total: parseInt(total),
122
- rows: rows
123
- };
124
- } catch (error) {
125
- console.error(`Error en getUniqueSessions:`, error.message);
126
- throw error;
127
- }
128
- }
129
- }
130
-
131
- module.exports = EventsNoFinishModel;