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,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Modelo para la tabla tokens en el framework JERK
|
|
3
|
-
* Implementación del componente MVC TokenModel.js
|
|
4
|
-
* Utiliza MariaDB como adaptador de base de datos
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { ModelBase, hooks } = require('insitu-js');
|
|
8
|
-
const { getSharedAdapter } = require('../utils/dbAdapter');
|
|
9
|
-
const { QueryBuilder } = require('insitu-js');
|
|
10
|
-
|
|
11
|
-
class TokenModel extends ModelBase {
|
|
12
|
-
constructor(options = {}) {
|
|
13
|
-
// Obtener el adaptador centralizado
|
|
14
|
-
const adapter = getSharedAdapter();
|
|
15
|
-
|
|
16
|
-
super({
|
|
17
|
-
...options,
|
|
18
|
-
tableName: options.tableName || 'tokens',
|
|
19
|
-
adapter: adapter
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
// Inicializar QueryBuilder con el adaptador centralizado
|
|
23
|
-
this.queryBuilder = new QueryBuilder(adapter, 'tokens');
|
|
24
|
-
|
|
25
|
-
// Definir campos del modelo
|
|
26
|
-
this.fields = {
|
|
27
|
-
id: { type: 'integer', primaryKey: true, autoIncrement: true },
|
|
28
|
-
token: { type: 'string', required: true },
|
|
29
|
-
created_at: { type: 'datetime', auto: 'create' },
|
|
30
|
-
store_id: { type: 'integer' }
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Valida un token contra la base de datos
|
|
36
|
-
* @param {string} token - Token a validar
|
|
37
|
-
* @returns {Promise<boolean>} - True si el token es válido y no revocado, false en caso contrario
|
|
38
|
-
*/
|
|
39
|
-
async validateToken(token) {
|
|
40
|
-
if (!token) {
|
|
41
|
-
// Disparar hook de validación de token fallida
|
|
42
|
-
hooks.doAction('token_validation_failed', { token: token, reason: 'Token vacío o nulo' });
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Disparar hook de inicio de validación de token
|
|
47
|
-
hooks.doAction('token_validation_start', { token: token });
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
// Buscar el token en la base de datos usando QueryBuilder
|
|
51
|
-
const tokenRecord = await this.queryBuilder
|
|
52
|
-
.reset()
|
|
53
|
-
.select('*')
|
|
54
|
-
.where('token', token)
|
|
55
|
-
.first();
|
|
56
|
-
|
|
57
|
-
if (tokenRecord) {
|
|
58
|
-
// Verificar si el token está revocado
|
|
59
|
-
if (tokenRecord.revoked === 1) {
|
|
60
|
-
// Disparar hook de validación de token fallida por revocación
|
|
61
|
-
hooks.doAction('token_validation_failed', { token: token, reason: 'Token revocado' });
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Disparar hook de validación de token exitosa
|
|
66
|
-
hooks.doAction('token_validation_success', { token: token, tokenRecord: tokenRecord });
|
|
67
|
-
return true;
|
|
68
|
-
} else {
|
|
69
|
-
// Disparar hook de validación de token fallida
|
|
70
|
-
hooks.doAction('token_validation_failed', { token: token, reason: 'Token no encontrado en la base de datos' });
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error('[ERROR] TokenModel.validateToken:', error);
|
|
75
|
-
// Disparar hook de validación de token fallida por error
|
|
76
|
-
hooks.doAction('token_validation_error', { token: token, error: error.message });
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Obtiene un token por su valor
|
|
83
|
-
* @param {string} token - Valor del token
|
|
84
|
-
* @returns {Promise<Object|null>} - Registro del token o null si no existe
|
|
85
|
-
*/
|
|
86
|
-
async getToken(token) {
|
|
87
|
-
if (!token) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
return await this.queryBuilder
|
|
93
|
-
.reset()
|
|
94
|
-
.select('*')
|
|
95
|
-
.where('token', token)
|
|
96
|
-
.first();
|
|
97
|
-
} catch (error) {
|
|
98
|
-
console.error('[ERROR] TokenModel.getToken:', error);
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Obtiene todos los tokens
|
|
105
|
-
* @returns {Promise<Array>} - Lista de todos los tokens
|
|
106
|
-
*/
|
|
107
|
-
async getAllTokens() {
|
|
108
|
-
try {
|
|
109
|
-
return await this.queryBuilder
|
|
110
|
-
.reset()
|
|
111
|
-
.select('*')
|
|
112
|
-
.get();
|
|
113
|
-
} catch (error) {
|
|
114
|
-
console.error('[ERROR] TokenModel.getAllTokens:', error);
|
|
115
|
-
return [];
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Crea un nuevo token
|
|
121
|
-
* @param {Object} tokenData - Datos del token
|
|
122
|
-
* @returns {Promise<Object>} - Token creado
|
|
123
|
-
*/
|
|
124
|
-
async createToken(tokenData) {
|
|
125
|
-
try {
|
|
126
|
-
const result = await this.queryBuilder
|
|
127
|
-
.reset()
|
|
128
|
-
.insert(tokenData);
|
|
129
|
-
|
|
130
|
-
// Obtener y devolver el token recién creado
|
|
131
|
-
return await this.queryBuilder
|
|
132
|
-
.reset()
|
|
133
|
-
.select('*')
|
|
134
|
-
.where('id', result.insertId)
|
|
135
|
-
.first();
|
|
136
|
-
} catch (error) {
|
|
137
|
-
console.error('[ERROR] TokenModel.createToken:', error);
|
|
138
|
-
throw error;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Revoca un token marcándolo como inhabilitado
|
|
144
|
-
* @param {string} token - Token a revocar
|
|
145
|
-
* @returns {Promise<boolean>} - True si se revocó correctamente
|
|
146
|
-
*/
|
|
147
|
-
async revokeToken(token) {
|
|
148
|
-
try {
|
|
149
|
-
const result = await this.queryBuilder
|
|
150
|
-
.reset()
|
|
151
|
-
.update({
|
|
152
|
-
revoked: 1,
|
|
153
|
-
revoked_at: new Date()
|
|
154
|
-
})
|
|
155
|
-
.where('token', token);
|
|
156
|
-
|
|
157
|
-
return result.affectedRows > 0;
|
|
158
|
-
} catch (error) {
|
|
159
|
-
console.error(`[ERROR] TokenModel.revokeToken:`, error);
|
|
160
|
-
throw error;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Revoca todos los tokens de un usuario
|
|
166
|
-
* @param {number} userId - ID del usuario
|
|
167
|
-
* @returns {Promise<number>} - Número de tokens revocados
|
|
168
|
-
*/
|
|
169
|
-
async revokeAllUserTokens(userId) {
|
|
170
|
-
try {
|
|
171
|
-
const result = await this.queryBuilder
|
|
172
|
-
.reset()
|
|
173
|
-
.update({
|
|
174
|
-
revoked: 1,
|
|
175
|
-
revoked_at: new Date()
|
|
176
|
-
})
|
|
177
|
-
.where('user_id', userId);
|
|
178
|
-
|
|
179
|
-
return result.affectedRows;
|
|
180
|
-
} catch (error) {
|
|
181
|
-
console.error(`[ERROR] TokenModel.revokeAllUserTokens:`, error);
|
|
182
|
-
throw error;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Verifica si un token está revocado
|
|
188
|
-
* @param {string} token - Token a verificar
|
|
189
|
-
* @returns {Promise<boolean>} - True si el token está revocado
|
|
190
|
-
*/
|
|
191
|
-
async isTokenRevoked(token) {
|
|
192
|
-
try {
|
|
193
|
-
const tokenRecord = await this.queryBuilder
|
|
194
|
-
.reset()
|
|
195
|
-
.select(['revoked'])
|
|
196
|
-
.where('token', token)
|
|
197
|
-
.first();
|
|
198
|
-
|
|
199
|
-
return tokenRecord ? tokenRecord.revoked === 1 : true;
|
|
200
|
-
} catch (error) {
|
|
201
|
-
console.error(`[ERROR] TokenModel.isTokenRevoked:`, error);
|
|
202
|
-
return true; // En caso de error, asumir que está revocado por seguridad
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
module.exports = TokenModel;
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Modelo de autenticación simplificado
|
|
3
|
-
* AuthModel.js
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const UserModel = require('./UserModel');
|
|
7
|
-
|
|
8
|
-
class AuthModel {
|
|
9
|
-
constructor() {
|
|
10
|
-
this.userModel = new UserModel();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Autentica un usuario
|
|
15
|
-
* @param {string} username - Nombre de usuario o email
|
|
16
|
-
* @param {string} password - Contraseña
|
|
17
|
-
* @returns {Promise<Object|null>} - Usuario autenticado o null si falla
|
|
18
|
-
*/
|
|
19
|
-
async authenticateUser(username, password) {
|
|
20
|
-
try {
|
|
21
|
-
return await this.userModel.validateCredentials(username, password);
|
|
22
|
-
} catch (error) {
|
|
23
|
-
console.error('[ERROR] AuthModel.authenticateUser:', error);
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Registra un nuevo usuario
|
|
30
|
-
* @param {Object} userData - Datos del usuario
|
|
31
|
-
* @returns {Promise<Object>} - Usuario registrado
|
|
32
|
-
*/
|
|
33
|
-
async registerUser(userData) {
|
|
34
|
-
try {
|
|
35
|
-
return await this.userModel.createUser(userData);
|
|
36
|
-
} catch (error) {
|
|
37
|
-
console.error('[ERROR] AuthModel.registerUser:', error);
|
|
38
|
-
throw error;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Obtiene un usuario por ID
|
|
44
|
-
* @param {number} userId - ID del usuario
|
|
45
|
-
* @returns {Promise<Object|null>} - Usuario o null si no existe
|
|
46
|
-
*/
|
|
47
|
-
async getUserById(userId) {
|
|
48
|
-
try {
|
|
49
|
-
const user = await this.userModel.getUserById(userId);
|
|
50
|
-
if (!user) {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
...user,
|
|
56
|
-
roles: [], // No hay roles en esta versión simplificada
|
|
57
|
-
capabilities: [] // No hay capabilities en esta versión simplificada
|
|
58
|
-
};
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error('[ERROR] AuthModel.getUserById:', error);
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
module.exports = AuthModel;
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Modelo para la tabla users en el framework JERK
|
|
3
|
-
* UserModel.js
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const { ModelBase } = require('insitu-js');
|
|
7
|
-
const { getSharedAdapter } = require('../../utils/dbAdapter');
|
|
8
|
-
const { QueryBuilder } = require('insitu-js');
|
|
9
|
-
const bcrypt = require('bcrypt');
|
|
10
|
-
|
|
11
|
-
class UserModel extends ModelBase {
|
|
12
|
-
constructor(options = {}) {
|
|
13
|
-
// Obtener el adaptador centralizado
|
|
14
|
-
const adapter = getSharedAdapter();
|
|
15
|
-
|
|
16
|
-
super({
|
|
17
|
-
...options,
|
|
18
|
-
tableName: options.tableName || 'users',
|
|
19
|
-
adapter: adapter
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
// Inicializar QueryBuilder con el adaptador centralizado
|
|
23
|
-
this.queryBuilder = new QueryBuilder(adapter, 'users');
|
|
24
|
-
|
|
25
|
-
// Definir campos del modelo
|
|
26
|
-
this.fields = {
|
|
27
|
-
id: { type: 'integer', primaryKey: true, autoIncrement: true },
|
|
28
|
-
username: { type: 'string', required: true, unique: true },
|
|
29
|
-
password: { type: 'string', required: true },
|
|
30
|
-
email: { type: 'string', required: true, unique: true },
|
|
31
|
-
created_at: { type: 'datetime', auto: 'create' },
|
|
32
|
-
updated_at: { type: 'datetime', auto: 'update' },
|
|
33
|
-
is_active: { type: 'boolean', default: true }
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Crea un nuevo usuario con contraseña encriptada
|
|
39
|
-
* @param {Object} userData - Datos del usuario
|
|
40
|
-
* @returns {Promise<Object>} - Usuario creado
|
|
41
|
-
*/
|
|
42
|
-
async createUser(userData) {
|
|
43
|
-
try {
|
|
44
|
-
// Encriptar la contraseña
|
|
45
|
-
const hashedPassword = await bcrypt.hash(userData.password, 10);
|
|
46
|
-
|
|
47
|
-
const userDataWithHashedPassword = {
|
|
48
|
-
...userData,
|
|
49
|
-
password: hashedPassword
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const result = await this.queryBuilder
|
|
53
|
-
.reset()
|
|
54
|
-
.insert(userDataWithHashedPassword);
|
|
55
|
-
|
|
56
|
-
// Obtener y devolver el usuario recién creado
|
|
57
|
-
return await this.queryBuilder
|
|
58
|
-
.reset()
|
|
59
|
-
.select('*')
|
|
60
|
-
.where('id', result.insertId)
|
|
61
|
-
.first();
|
|
62
|
-
} catch (error) {
|
|
63
|
-
console.error('[ERROR] UserModel.createUser:', error);
|
|
64
|
-
throw error;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Valida las credenciales de un usuario
|
|
70
|
-
* @param {string} username - Nombre de usuario
|
|
71
|
-
* @param {string} password - Contraseña sin encriptar
|
|
72
|
-
* @returns {Promise<Object|null>} - Usuario si las credenciales son válidas, null si no
|
|
73
|
-
*/
|
|
74
|
-
async validateCredentials(username, password) {
|
|
75
|
-
try {
|
|
76
|
-
const user = await this.queryBuilder
|
|
77
|
-
.reset()
|
|
78
|
-
.select('*')
|
|
79
|
-
.where(function(qb) {
|
|
80
|
-
qb.where('username', username).orWhere('email', username); // Permitir login con email o username
|
|
81
|
-
})
|
|
82
|
-
.where('is_active', true)
|
|
83
|
-
.first();
|
|
84
|
-
|
|
85
|
-
if (!user) {
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Comparar la contraseña encriptada
|
|
90
|
-
const isPasswordValid = await bcrypt.compare(password, user.password);
|
|
91
|
-
|
|
92
|
-
if (isPasswordValid) {
|
|
93
|
-
// No devolver la contraseña en la respuesta
|
|
94
|
-
const { password: _, ...userWithoutPassword } = user;
|
|
95
|
-
return userWithoutPassword;
|
|
96
|
-
} else {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
} catch (error) {
|
|
100
|
-
console.error('[ERROR] UserModel.validateCredentials:', error);
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Obtiene un usuario por su ID
|
|
107
|
-
* @param {number} id - ID del usuario
|
|
108
|
-
* @returns {Promise<Object|null>} - Usuario o null si no existe
|
|
109
|
-
*/
|
|
110
|
-
async getUserById(id) {
|
|
111
|
-
try {
|
|
112
|
-
return await this.queryBuilder
|
|
113
|
-
.reset()
|
|
114
|
-
.select('*')
|
|
115
|
-
.where('id', id)
|
|
116
|
-
.first();
|
|
117
|
-
} catch (error) {
|
|
118
|
-
console.error('[ERROR] UserModel.getUserById:', error);
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Obtiene un usuario por su nombre de usuario o email
|
|
125
|
-
* @param {string} identifier - Nombre de usuario o email
|
|
126
|
-
* @returns {Promise<Object|null>} - Usuario o null si no existe
|
|
127
|
-
*/
|
|
128
|
-
async getUserByIdentifier(identifier) {
|
|
129
|
-
try {
|
|
130
|
-
return await this.queryBuilder
|
|
131
|
-
.reset()
|
|
132
|
-
.select('*')
|
|
133
|
-
.where(function(qb) {
|
|
134
|
-
qb.where('username', identifier).orWhere('email', identifier);
|
|
135
|
-
})
|
|
136
|
-
.first();
|
|
137
|
-
} catch (error) {
|
|
138
|
-
console.error('[ERROR] UserModel.getUserByIdentifier:', error);
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Actualiza un usuario
|
|
145
|
-
* @param {number} id - ID del usuario
|
|
146
|
-
* @param {Object} updateData - Datos a actualizar
|
|
147
|
-
* @returns {Promise<Object>} - Usuario actualizado
|
|
148
|
-
*/
|
|
149
|
-
async updateUser(id, updateData) {
|
|
150
|
-
try {
|
|
151
|
-
// Si se está actualizando la contraseña, encriptarla
|
|
152
|
-
if (updateData.password) {
|
|
153
|
-
updateData.password = await bcrypt.hash(updateData.password, 10);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
await this.queryBuilder
|
|
157
|
-
.reset()
|
|
158
|
-
.update(updateData)
|
|
159
|
-
.where('id', id);
|
|
160
|
-
|
|
161
|
-
// Devolver el usuario actualizado
|
|
162
|
-
return await this.getUserById(id);
|
|
163
|
-
} catch (error) {
|
|
164
|
-
console.error('[ERROR] UserModel.updateUser:', error);
|
|
165
|
-
throw error;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Elimina un usuario
|
|
171
|
-
* @param {number} id - ID del usuario
|
|
172
|
-
* @returns {Promise<boolean>} - True si se eliminó correctamente
|
|
173
|
-
*/
|
|
174
|
-
async deleteUser(id) {
|
|
175
|
-
try {
|
|
176
|
-
const result = await this.queryBuilder
|
|
177
|
-
.reset()
|
|
178
|
-
.delete()
|
|
179
|
-
.where('id', id);
|
|
180
|
-
|
|
181
|
-
return result.affectedRows > 0;
|
|
182
|
-
} catch (error) {
|
|
183
|
-
console.error('[ERROR] UserModel.deleteUser:', error);
|
|
184
|
-
throw error;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
module.exports = UserModel;
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Modelo para la tabla de completed_carts en el framework JERK
|
|
3
|
-
* Implementación del componente MVC CompletedCartModel.js
|
|
4
|
-
* Utiliza MariaDB como adaptador de base de datos
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { ModelBase } = require('insitu-js');
|
|
8
|
-
const { getSharedAdapter } = require('../../utils/dbAdapter');
|
|
9
|
-
const { QueryBuilder } = require('insitu-js');
|
|
10
|
-
|
|
11
|
-
class CompletedCartModel extends ModelBase {
|
|
12
|
-
constructor(options = {}) {
|
|
13
|
-
// Obtener el adaptador centralizado
|
|
14
|
-
const adapter = getSharedAdapter();
|
|
15
|
-
|
|
16
|
-
super({
|
|
17
|
-
...options,
|
|
18
|
-
tableName: options.tableName || 'completed_carts', // Tabla por defecto
|
|
19
|
-
adapter: adapter
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
// Inicializar QueryBuilder con el adaptador centralizado
|
|
23
|
-
this.queryBuilder = new QueryBuilder(adapter, 'completed_carts');
|
|
24
|
-
|
|
25
|
-
// Definir campos del modelo
|
|
26
|
-
this.fields = {
|
|
27
|
-
id: { type: 'integer', primaryKey: true, autoIncrement: true },
|
|
28
|
-
session_header_id: { type: 'integer' },
|
|
29
|
-
event_type: { type: 'string', required: true },
|
|
30
|
-
created_at: { type: 'datetime' },
|
|
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
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Consulta personalizada con paginación, filtros y búsqueda global
|
|
50
|
-
* @param {Object} filters - Filtros para la consulta
|
|
51
|
-
* @param {Object} pagination - Parámetros de paginación
|
|
52
|
-
* @param {string} search - Término de búsqueda global
|
|
53
|
-
* @returns {Promise<Object>} - Resultados con información de paginación
|
|
54
|
-
*/
|
|
55
|
-
async queryWithFilters(filters = {}, pagination = {}, search = '') {
|
|
56
|
-
try {
|
|
57
|
-
// Mostrar parámetros de filtro para depuración
|
|
58
|
-
console.log('Filtros recibidos en CompletedCartModel queryWithFilters:', filters);
|
|
59
|
-
|
|
60
|
-
// Extraer parámetros de paginación
|
|
61
|
-
const offset = pagination.offset !== undefined ? parseInt(pagination.offset) : 0;
|
|
62
|
-
const limit = pagination.limit ? parseInt(pagination.limit) : 10;
|
|
63
|
-
|
|
64
|
-
// Extraer parámetros de filtro específicos para fechas
|
|
65
|
-
const { date_from, date_to, event_type, session_id, wtf_session, url, site_url } = filters;
|
|
66
|
-
|
|
67
|
-
// Construir condiciones WHERE dinámicamente
|
|
68
|
-
let whereConditions = [];
|
|
69
|
-
let whereParams = [];
|
|
70
|
-
|
|
71
|
-
// Filtro por fecha desde (date_from) - para event_timestamp
|
|
72
|
-
if (date_from) {
|
|
73
|
-
// Si la fecha no tiene hora (es solo YYYY-MM-DD), agregar hora mínima del día
|
|
74
|
-
let startDate = date_from;
|
|
75
|
-
if (date_from.length === 10) { // Formato YYYY-MM-DD
|
|
76
|
-
startDate = `${date_from} 00:00:00`;
|
|
77
|
-
}
|
|
78
|
-
console.log('Aplicando filtro: event_timestamp >=', startDate);
|
|
79
|
-
whereConditions.push('event_timestamp >= ?');
|
|
80
|
-
whereParams.push(startDate);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Filtro por fecha hasta (date_to) - para event_timestamp
|
|
84
|
-
if (date_to) {
|
|
85
|
-
// Si la fecha de fin no tiene hora (es solo YYYY-MM-DD), agregar hora 23:59:59 para incluir todo el día
|
|
86
|
-
let endDate = date_to;
|
|
87
|
-
if (date_to.length === 10) { // Formato YYYY-MM-DD
|
|
88
|
-
endDate = `${date_to} 23:59:59`;
|
|
89
|
-
}
|
|
90
|
-
console.log('Aplicando filtro: event_timestamp <=', endDate);
|
|
91
|
-
whereConditions.push('event_timestamp <= ?');
|
|
92
|
-
whereParams.push(endDate);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Filtros por campos específicos
|
|
96
|
-
if (event_type) {
|
|
97
|
-
whereConditions.push('event_type = ?');
|
|
98
|
-
whereParams.push(event_type);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (session_id) {
|
|
102
|
-
whereConditions.push('session_id = ?');
|
|
103
|
-
whereParams.push(session_id);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (wtf_session) {
|
|
107
|
-
whereConditions.push('wtf_session = ?');
|
|
108
|
-
whereParams.push(wtf_session);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (url) {
|
|
112
|
-
whereConditions.push('url LIKE ?');
|
|
113
|
-
whereParams.push(`%${url}%`);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (site_url) {
|
|
117
|
-
whereConditions.push('site_url = ?');
|
|
118
|
-
whereParams.push(site_url);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Aplicar búsqueda global si se proporciona
|
|
122
|
-
if (search) {
|
|
123
|
-
// Campos en los que se realiza la búsqueda global
|
|
124
|
-
const searchFields = ['event_type', 'product_name', 'sku', 'url', 'session_id', 'wtf_session', 'site_url'];
|
|
125
|
-
|
|
126
|
-
const searchConditions = searchFields.map(field => `${field} LIKE ?`).join(' OR ');
|
|
127
|
-
whereConditions.push(`(${searchConditions})`);
|
|
128
|
-
|
|
129
|
-
// Agregar parámetros de búsqueda
|
|
130
|
-
searchFields.forEach(() => {
|
|
131
|
-
whereParams.push(`%${search}%`);
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Construir la cláusula WHERE completa
|
|
136
|
-
const whereClause = whereConditions.length > 0
|
|
137
|
-
? 'WHERE ' + whereConditions.join(' AND ')
|
|
138
|
-
: 'WHERE 1=1'; // Asegurar que siempre haya una condición
|
|
139
|
-
|
|
140
|
-
// Consulta SQL para obtener los registros
|
|
141
|
-
const query = `
|
|
142
|
-
SELECT *
|
|
143
|
-
FROM completed_carts
|
|
144
|
-
${whereClause}
|
|
145
|
-
ORDER BY id DESC
|
|
146
|
-
LIMIT ? OFFSET ?
|
|
147
|
-
`;
|
|
148
|
-
|
|
149
|
-
// Agregar parámetros de paginación
|
|
150
|
-
const allParams = [...whereParams, limit, offset];
|
|
151
|
-
|
|
152
|
-
// Usar el método complexQuery del QueryBuilder
|
|
153
|
-
const { QueryBuilder } = require('insitu-js');
|
|
154
|
-
const qb = new QueryBuilder(this.queryBuilder.adapter);
|
|
155
|
-
qb.complexQuery(query, allParams);
|
|
156
|
-
|
|
157
|
-
// Obtener los resultados
|
|
158
|
-
const rows = await qb.get();
|
|
159
|
-
|
|
160
|
-
// Consulta para obtener el conteo total con los mismos filtros
|
|
161
|
-
const countQuery = `
|
|
162
|
-
SELECT COUNT(*) as total
|
|
163
|
-
FROM completed_carts
|
|
164
|
-
${whereClause}
|
|
165
|
-
`;
|
|
166
|
-
|
|
167
|
-
// Usar el método complexQuery del QueryBuilder para la consulta de conteo
|
|
168
|
-
const countQb = new QueryBuilder(this.queryBuilder.adapter);
|
|
169
|
-
countQb.complexQuery(countQuery, whereParams); // Sin parámetros de paginación
|
|
170
|
-
|
|
171
|
-
const countResult = await countQb.first();
|
|
172
|
-
const total = countResult ? countResult.total : 0;
|
|
173
|
-
|
|
174
|
-
return {
|
|
175
|
-
total: parseInt(total),
|
|
176
|
-
rows: rows
|
|
177
|
-
};
|
|
178
|
-
} catch (error) {
|
|
179
|
-
console.error(`Error en queryWithFilters para ${this.tableName}:`, error.message);
|
|
180
|
-
throw error;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Obtiene completed carts con paginación, filtros y búsqueda
|
|
186
|
-
* @param {Object} queryParams - Parámetros de consulta (offset, limit, search, filtros)
|
|
187
|
-
* @returns {Promise<Object>} - Resultados con información de paginación
|
|
188
|
-
*/
|
|
189
|
-
async getCompletedCarts(queryParams = {}) {
|
|
190
|
-
// Obtener parámetros de paginación
|
|
191
|
-
const offset = parseInt(queryParams.offset) || 0;
|
|
192
|
-
const limit = parseInt(queryParams.limit) || 10;
|
|
193
|
-
const search = queryParams.search || '';
|
|
194
|
-
|
|
195
|
-
// Obtener otros parámetros de filtro excluyendo los de paginación y búsqueda
|
|
196
|
-
const filters = {};
|
|
197
|
-
for (const [key, value] of Object.entries(queryParams)) {
|
|
198
|
-
if (!['offset', 'limit', 'search', 'sort', 'order'].includes(key)) {
|
|
199
|
-
if (value !== '') { // Solo añadir filtros que no estén vacíos
|
|
200
|
-
filters[key] = value;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return await this.queryWithFilters(
|
|
206
|
-
filters,
|
|
207
|
-
{ offset, limit },
|
|
208
|
-
search
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
module.exports = CompletedCartModel;
|