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.
- package/.env.example +67 -0
- package/CHANGELOG.md +167 -0
- package/README.md +1 -3
- package/config/database.json +11 -0
- package/controllers/admin/AuthController.js +2 -1
- package/core/ViewHelper.js +75 -0
- package/core/hotReload.js +1 -1
- package/data/blackcoffee_admin.db-shm +0 -0
- package/data/blackcoffee_admin.db-wal +0 -0
- package/includes/adminAuth.js +5 -3
- package/includes/sessions.js +1 -1
- package/otrack.tar.gz +0 -0
- package/package.json +4 -2
- package/programatically/initFlow.js +2 -2
- package/test-aplicacion.con-logisession/BlackCoffee.js +0 -226
- package/test-aplicacion.con-logisession/SSL_SETUP.md +0 -53
- package/test-aplicacion.con-logisession/certs/ca-certificate.pem +0 -32
- package/test-aplicacion.con-logisession/certs/ca-private-key.pem +0 -52
- package/test-aplicacion.con-logisession/certs/certificate-2048.pem +0 -22
- package/test-aplicacion.con-logisession/certs/certificate.pem +0 -32
- package/test-aplicacion.con-logisession/certs/private-key-2048.pem +0 -28
- package/test-aplicacion.con-logisession/certs/private-key.pem +0 -52
- package/test-aplicacion.con-logisession/config/iaQueueSetup.js +0 -84
- package/test-aplicacion.con-logisession/config/qwen-rules.json +0 -39
- package/test-aplicacion.con-logisession/controllers/analyticsController.js +0 -117
- package/test-aplicacion.con-logisession/controllers/auth/AdminAuthController.js +0 -142
- package/test-aplicacion.con-logisession/controllers/auth/AuthController.js +0 -439
- package/test-aplicacion.con-logisession/controllers/auth/AuthViewController.js +0 -223
- package/test-aplicacion.con-logisession/controllers/endpointController.js +0 -66
- package/test-aplicacion.con-logisession/controllers/example.js +0 -183
- package/test-aplicacion.con-logisession/controllers/iaQueueController.js +0 -367
- package/test-aplicacion.con-logisession/controllers/queueController.js +0 -206
- package/test-aplicacion.con-logisession/controllers/qwenQueueController.js +0 -197
- package/test-aplicacion.con-logisession/controllers/test.js +0 -0
- package/test-aplicacion.con-logisession/controllers/tracking/EventsNoFinishController.js +0 -78
- package/test-aplicacion.con-logisession/controllers/tracking/TrackingController.js +0 -412
- package/test-aplicacion.con-logisession/controllers/tracking/TrackingControllerWithLoadModel.js +0 -437
- package/test-aplicacion.con-logisession/hooks/admin-hooks.js +0 -20
- package/test-aplicacion.con-logisession/hooks/general-hooks.js +0 -97
- package/test-aplicacion.con-logisession/hooks/queue-hooks.js +0 -64
- package/test-aplicacion.con-logisession/hooks/route-directory-hooks.js +0 -38
- package/test-aplicacion.con-logisession/hooks/security-hooks.js +0 -24
- package/test-aplicacion.con-logisession/insitu-admin-client/README.md +0 -69
- package/test-aplicacion.con-logisession/insitu-admin-client/package.json +0 -23
- package/test-aplicacion.con-logisession/insitu-admin-client.js +0 -257
- package/test-aplicacion.con-logisession/models/ExampleModel.js +0 -88
- package/test-aplicacion.con-logisession/models/QueueJobModel.js +0 -263
- package/test-aplicacion.con-logisession/models/TokenModel.js +0 -207
- package/test-aplicacion.con-logisession/models/auth/AuthModel.js +0 -66
- package/test-aplicacion.con-logisession/models/auth/UserModel.js +0 -189
- package/test-aplicacion.con-logisession/models/tracking/CompletedCartModel.js +0 -213
- package/test-aplicacion.con-logisession/models/tracking/EventModel.js +0 -366
- package/test-aplicacion.con-logisession/models/tracking/EventsNoFinishModel.js +0 -131
- package/test-aplicacion.con-logisession/models/tracking/SessionModel.js +0 -360
- package/test-aplicacion.con-logisession/models/tracking/SiteFlowModel.js +0 -286
- package/test-aplicacion.con-logisession/models/tracking/TokenModel.js +0 -207
- package/test-aplicacion.con-logisession/package-lock.json +0 -3313
- package/test-aplicacion.con-logisession/package.json +0 -32
- package/test-aplicacion.con-logisession/public/blackcoffee-welcome/index.html +0 -1339
- package/test-aplicacion.con-logisession/public/css/style.css +0 -64
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/index.html +0 -18
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/script.js +0 -16
- package/test-aplicacion.con-logisession/public/ejemplo-estatica/styles.css +0 -43
- package/test-aplicacion.con-logisession/public/images/logo.svg +0 -7
- package/test-aplicacion.con-logisession/public/js/main.js +0 -67
- package/test-aplicacion.con-logisession/routes/analytics-routes.json +0 -8
- package/test-aplicacion.con-logisession/routes/auth-routes.json +0 -98
- package/test-aplicacion.con-logisession/routes/blackcoffee-welcome-routes.json +0 -20
- package/test-aplicacion.con-logisession/routes/duplicate-test-routes.json.disabled +0 -16
- package/test-aplicacion.con-logisession/routes/ejemplo-estatica-routes.json +0 -11
- package/test-aplicacion.con-logisession/routes/endpoints-routes.json +0 -8
- package/test-aplicacion.con-logisession/routes/ia-queue-routes.json +0 -26
- package/test-aplicacion.con-logisession/routes/product-routes.json.disabled +0 -20
- package/test-aplicacion.con-logisession/routes/queue-routes.json +0 -32
- package/test-aplicacion.con-logisession/routes/qwen-routes.json +0 -14
- package/test-aplicacion.con-logisession/routes/static-routes.json +0 -29
- package/test-aplicacion.con-logisession/routes/tracking-routes.json +0 -58
- package/test-aplicacion.con-logisession/routes/tracking-with-loadmodel-routes.json +0 -51
- package/test-aplicacion.con-logisession/utils/dbAdapter.js +0 -88
- package/test-aplicacion.con-logisession/utils/qbWrapper.js +0 -4
- package/test-aplicacion.con-logisession/utils/queueProcessor.js +0 -305
- package/test-aplicacion.con-logisession/utils/qwenRulesService.js +0 -131
- package/test-aplicacion.con-logisession/utils/tokenHelper.js +0 -22
- package/test-aplicacion.con-logisession/views/auth/dashboard.html +0 -443
- package/test-aplicacion.con-logisession/views/auth/forgot-password.html +0 -200
- package/test-aplicacion.con-logisession/views/auth/login.html +0 -213
- package/test-aplicacion.con-logisession/views/auth/register.html +0 -294
- package/test-aplicacion.con-logisession/views/contact/form.html +0 -47
- 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;
|