jerkjs 2.1.7 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +56 -0
- package/README.md +204 -4
- package/README_EN.md +1 -1
- package/README_PT.md +1 -1
- package/docs/ARQUITECTURA_ROUTES.md +84 -38
- package/{JERK_FRAMEWORK_DOCUMENTATION.md → docs/JERK_FRAMEWORK_DOCUMENTATION.md} +28 -2
- package/docs/JERK_MODELOS_HOWTO.md +566 -0
- package/index.js +41 -5
- package/jerk-qbuilder/CHANGELOG.md +71 -0
- package/jerk-qbuilder/HOWTO.md +325 -0
- package/jerk-qbuilder/README.md +52 -0
- package/lib/core/server.js +328 -27
- package/lib/loader/routeLoader.js +148 -117
- package/lib/mvc/GenericAdapter.js +136 -0
- package/lib/mvc/MariaDBAdapter.js +315 -0
- package/lib/mvc/MemoryAdapter.js +269 -0
- package/lib/mvc/ModelControllerExample.js +285 -0
- package/lib/mvc/controllerBase.js +77 -0
- package/lib/mvc/modelBase.js +383 -0
- package/lib/mvc/modelManager.js +284 -0
- package/lib/mvc/userModel.js +265 -0
- package/lib/mvc/viewEngine.js +32 -1
- package/lib/query/MariaDBAdapter.js +78 -0
- package/lib/query/consoleAdapter.js +184 -0
- package/lib/query/queryBuilder.js +953 -0
- package/lib/query/queryBuilderHooks.js +455 -0
- package/lib/query/queryBuilderMiddleware.js +332 -0
- package/lib/utils/mimeType.js +62 -0
- package/package.json +5 -3
- package/utils/find_file_path.sh +36 -0
- package/BUG_REPORTE_COMPRESION.txt +0 -72
- package/standard/CompressionTestController.js +0 -56
- package/standard/HealthController.js +0 -16
- package/standard/HomeController.js +0 -12
- package/standard/ProductController.js +0 -18
- package/standard/README.md +0 -47
- package/standard/UserController.js +0 -23
- package/standard/package.json +0 -22
- package/standard/routes.json +0 -65
- package/standard/server.js +0 -140
- package/standardA/controllers/AuthController.js +0 -82
- package/standardA/controllers/HomeController.js +0 -19
- package/standardA/controllers/UserController.js +0 -41
- package/standardA/server.js +0 -311
- package/standardA/views/auth/dashboard.html +0 -51
- package/standardA/views/auth/login.html +0 -47
- package/standardA/views/index.html +0 -32
- package/standardA/views/users/detail.html +0 -28
- package/standardA/views/users/list.html +0 -36
- /package/{JERK_FRAMEWORK_DIAGRAM.txt → docs/JERK_FRAMEWORK_DIAGRAM.txt} +0 -0
- /package/{JERK_FRAMEWORK_DIAGRAM_MERMAID.mmd → docs/JERK_FRAMEWORK_DIAGRAM_MERMAID.mmd} +0 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modelo de ejemplo para usuarios en el framework JERK
|
|
3
|
+
* Implementación del componente MVC userModel.js
|
|
4
|
+
* Extiende ModelBase para aprovechar todas las funcionalidades
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const ModelBase = require('./modelBase');
|
|
8
|
+
|
|
9
|
+
class UserModel extends ModelBase {
|
|
10
|
+
/**
|
|
11
|
+
* Constructor del modelo de usuario
|
|
12
|
+
* @param {Object} options - Opciones de configuración del modelo
|
|
13
|
+
*/
|
|
14
|
+
constructor(options = {}) {
|
|
15
|
+
super({
|
|
16
|
+
...options,
|
|
17
|
+
tableName: options.tableName || 'users'
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Definir campos del modelo
|
|
21
|
+
this.fields = {
|
|
22
|
+
id: { type: 'integer', primaryKey: true, autoIncrement: true },
|
|
23
|
+
username: { type: 'string', required: true, unique: true },
|
|
24
|
+
email: { type: 'string', required: true, unique: true },
|
|
25
|
+
password: { type: 'string', required: true },
|
|
26
|
+
firstName: { type: 'string' },
|
|
27
|
+
lastName: { type: 'string' },
|
|
28
|
+
createdAt: { type: 'datetime', default: 'CURRENT_TIMESTAMP' },
|
|
29
|
+
updatedAt: { type: 'datetime', default: 'CURRENT_TIMESTAMP' }
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Valida los datos del usuario antes de guardar
|
|
35
|
+
* @param {string} operation - Operación a realizar
|
|
36
|
+
* @param {Object} data - Datos a validar
|
|
37
|
+
* @returns {Object} - Resultado de la validación
|
|
38
|
+
*/
|
|
39
|
+
validate(operation, data) {
|
|
40
|
+
const result = { isValid: true, errors: [] };
|
|
41
|
+
|
|
42
|
+
// Validaciones específicas para creación
|
|
43
|
+
if (operation === 'create') {
|
|
44
|
+
if (!data.username) {
|
|
45
|
+
result.isValid = false;
|
|
46
|
+
result.errors.push('Username es requerido');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!data.email) {
|
|
50
|
+
result.isValid = false;
|
|
51
|
+
result.errors.push('Email es requerido');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!data.password) {
|
|
55
|
+
result.isValid = false;
|
|
56
|
+
result.errors.push('Password es requerido');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Validaciones para actualización
|
|
61
|
+
if (operation === 'update') {
|
|
62
|
+
// Validar solo los campos que se están actualizando
|
|
63
|
+
if (data.hasOwnProperty('username') && !data.username) {
|
|
64
|
+
result.isValid = false;
|
|
65
|
+
result.errors.push('Username no puede estar vacío si se proporciona');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (data.hasOwnProperty('email') && !data.email) {
|
|
69
|
+
result.isValid = false;
|
|
70
|
+
result.errors.push('Email no puede estar vacío si se proporciona');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (data.hasOwnProperty('password') && !data.password) {
|
|
74
|
+
result.isValid = false;
|
|
75
|
+
result.errors.push('Password no puede estar vacío si se proporciona');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Validar formato de email si se está actualizando
|
|
79
|
+
if (data.email && !this.isValidEmail(data.email)) {
|
|
80
|
+
result.isValid = false;
|
|
81
|
+
result.errors.push('Email no tiene un formato válido');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Validaciones comunes para creación y actualización
|
|
86
|
+
if (operation === 'create' || operation === 'update') {
|
|
87
|
+
// Validar formato de email
|
|
88
|
+
if (data.email && !this.isValidEmail(data.email)) {
|
|
89
|
+
result.isValid = false;
|
|
90
|
+
result.errors.push('Email no tiene un formato válido');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Disparar hook para validación
|
|
95
|
+
if (this.hooks) {
|
|
96
|
+
const validationResult = this.hooks.applyFilters(
|
|
97
|
+
'user_model_validate',
|
|
98
|
+
result,
|
|
99
|
+
operation,
|
|
100
|
+
data
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return validationResult;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Verifica si un email tiene formato válido
|
|
111
|
+
* @param {string} email - Email a validar
|
|
112
|
+
* @returns {boolean} - Verdadero si el email es válido
|
|
113
|
+
*/
|
|
114
|
+
isValidEmail(email) {
|
|
115
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
116
|
+
return emailRegex.test(email);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Busca un usuario por su nombre de usuario
|
|
121
|
+
* @param {string} username - Nombre de usuario a buscar
|
|
122
|
+
* @returns {Promise<Object|null>} - Usuario encontrado o null
|
|
123
|
+
*/
|
|
124
|
+
async findByUsername(username) {
|
|
125
|
+
return await this.findOne({ username });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Busca un usuario por su email
|
|
130
|
+
* @param {string} email - Email a buscar
|
|
131
|
+
* @returns {Promise<Object|null>} - Usuario encontrado o null
|
|
132
|
+
*/
|
|
133
|
+
async findByEmail(email) {
|
|
134
|
+
return await this.findOne({ email });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Crea un nuevo usuario con validación
|
|
139
|
+
* @param {Object} userData - Datos del usuario a crear
|
|
140
|
+
* @returns {Promise<Object>} - Usuario creado
|
|
141
|
+
*/
|
|
142
|
+
async createUser(userData) {
|
|
143
|
+
// Validar datos antes de crear
|
|
144
|
+
const validation = this.validate('create', userData);
|
|
145
|
+
|
|
146
|
+
if (!validation.isValid) {
|
|
147
|
+
throw new Error(`Validación fallida: ${validation.errors.join(', ')}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Encriptar contraseña antes de guardar
|
|
151
|
+
if (userData.password) {
|
|
152
|
+
userData.password = await this.hashPassword(userData.password);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return await this.create(userData);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Actualiza un usuario con validación
|
|
160
|
+
* @param {Object} conditions - Condiciones para encontrar el usuario
|
|
161
|
+
* @param {Object} userData - Datos del usuario a actualizar
|
|
162
|
+
* @returns {Promise<number>} - Número de filas afectadas
|
|
163
|
+
*/
|
|
164
|
+
async updateUser(conditions, userData) {
|
|
165
|
+
// Validar solo los campos que se están actualizando
|
|
166
|
+
const fieldsToUpdate = {};
|
|
167
|
+
for (const [key, value] of Object.entries(userData)) {
|
|
168
|
+
if (value !== undefined && value !== null) {
|
|
169
|
+
fieldsToUpdate[key] = value;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Validar datos antes de actualizar
|
|
174
|
+
const validation = this.validate('update', fieldsToUpdate);
|
|
175
|
+
|
|
176
|
+
if (!validation.isValid) {
|
|
177
|
+
throw new Error(`Validación fallida: ${validation.errors.join(', ')}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Encriptar contraseña si se proporciona
|
|
181
|
+
if (fieldsToUpdate.password) {
|
|
182
|
+
fieldsToUpdate.password = await this.hashPassword(fieldsToUpdate.password);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return await this.update(conditions, fieldsToUpdate);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Encripta una contraseña
|
|
190
|
+
* @param {string} password - Contraseña a encriptar
|
|
191
|
+
* @returns {Promise<string>} - Contraseña encriptada
|
|
192
|
+
*/
|
|
193
|
+
async hashPassword(password) {
|
|
194
|
+
// Importar bcrypt dinámicamente
|
|
195
|
+
const bcrypt = require('bcrypt');
|
|
196
|
+
const saltRounds = 10;
|
|
197
|
+
return await bcrypt.hash(password, saltRounds);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Verifica si una contraseña coincide con el hash
|
|
202
|
+
* @param {string} password - Contraseña sin encriptar
|
|
203
|
+
* @param {string} hashedPassword - Contraseña encriptada
|
|
204
|
+
* @returns {Promise<boolean>} - Verdadero si coinciden
|
|
205
|
+
*/
|
|
206
|
+
async verifyPassword(password, hashedPassword) {
|
|
207
|
+
// Importar bcrypt dinámicamente
|
|
208
|
+
const bcrypt = require('bcrypt');
|
|
209
|
+
return await bcrypt.compare(password, hashedPassword);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Obtiene usuarios con información paginada
|
|
214
|
+
* @param {Object} options - Opciones de paginación
|
|
215
|
+
* @param {number} options.page - Página a obtener (por defecto 1)
|
|
216
|
+
* @param {number} options.limit - Límite de resultados por página (por defecto 10)
|
|
217
|
+
* @param {Object} conditions - Condiciones de búsqueda
|
|
218
|
+
* @returns {Promise<Object>} - Resultados con información de paginación
|
|
219
|
+
*/
|
|
220
|
+
async getUsersPaginated(options = {}, conditions = {}) {
|
|
221
|
+
const { page = 1, limit = 10 } = options;
|
|
222
|
+
const offset = (page - 1) * limit;
|
|
223
|
+
|
|
224
|
+
// Obtener los usuarios para la página solicitada
|
|
225
|
+
const users = await this.find(conditions, {
|
|
226
|
+
limit,
|
|
227
|
+
offset,
|
|
228
|
+
orderBy: 'createdAt DESC'
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Contar el total de usuarios
|
|
232
|
+
const totalCount = await this.count(conditions);
|
|
233
|
+
|
|
234
|
+
// Calcular información de paginación
|
|
235
|
+
const totalPages = Math.ceil(totalCount / limit);
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
data: users,
|
|
239
|
+
pagination: {
|
|
240
|
+
currentPage: page,
|
|
241
|
+
totalPages,
|
|
242
|
+
totalItems: totalCount,
|
|
243
|
+
itemsPerPage: limit,
|
|
244
|
+
hasNextPage: page < totalPages,
|
|
245
|
+
hasPrevPage: page > 1
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Serializa el modelo de usuario a JSON
|
|
252
|
+
* @returns {Object} - Representación JSON del modelo
|
|
253
|
+
*/
|
|
254
|
+
toJSON() {
|
|
255
|
+
const baseJson = super.toJSON();
|
|
256
|
+
return {
|
|
257
|
+
...baseJson,
|
|
258
|
+
modelName: this.constructor.name,
|
|
259
|
+
tableName: this.tableName,
|
|
260
|
+
fieldCount: Object.keys(this.fields).length
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = UserModel;
|
package/lib/mvc/viewEngine.js
CHANGED
|
@@ -215,7 +215,7 @@ class ViewEngine {
|
|
|
215
215
|
render(viewName, data = {}, options = {}) {
|
|
216
216
|
// Obtener la ruta completa de la vista
|
|
217
217
|
const viewPath = this.getViewPath(viewName);
|
|
218
|
-
|
|
218
|
+
|
|
219
219
|
// Verificar si la vista existe
|
|
220
220
|
if (!fs.existsSync(viewPath)) {
|
|
221
221
|
throw new Error(`Vista no encontrada: ${viewPath}`);
|
|
@@ -238,6 +238,37 @@ class ViewEngine {
|
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
// Si es una vista de contenido (no un layout), procesarla y usarla como contenido para el layout
|
|
242
|
+
if (options.layout) {
|
|
243
|
+
// Procesar la vista actual con las variables
|
|
244
|
+
const processedViewContent = this.processTemplate(viewContent, data, options);
|
|
245
|
+
|
|
246
|
+
// Cargar el layout
|
|
247
|
+
const layoutPath = this.getViewPath(options.layout);
|
|
248
|
+
if (fs.existsSync(layoutPath)) {
|
|
249
|
+
let layoutContent = fs.readFileSync(layoutPath, 'utf8');
|
|
250
|
+
|
|
251
|
+
// Validar sintaxis del layout si está habilitado
|
|
252
|
+
if (options.validateSyntax !== false) {
|
|
253
|
+
const layoutValidationErrors = ViewEngine.validateTemplate(layoutContent);
|
|
254
|
+
if (layoutValidationErrors.length > 0) {
|
|
255
|
+
this.logger.warn(`Errores de sintaxis en el layout ${options.layout}:`, layoutValidationErrors);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Reemplazar el placeholder {{content}} con el contenido procesado
|
|
260
|
+
layoutContent = layoutContent.replace(/\{\{content\}\}/g, processedViewContent);
|
|
261
|
+
|
|
262
|
+
// Procesar el layout con las variables
|
|
263
|
+
layoutContent = this.processTemplate(layoutContent, data, options);
|
|
264
|
+
|
|
265
|
+
return layoutContent;
|
|
266
|
+
} else {
|
|
267
|
+
// Si no existe el layout, devolver solo la vista procesada
|
|
268
|
+
return this.processTemplate(viewContent, data, options);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
241
272
|
// Procesar bloques de inclusión (similar a <?php include ?>)
|
|
242
273
|
viewContent = this.processIncludes(viewContent, path.dirname(viewPath));
|
|
243
274
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptador de MariaDB para el QueryBuilder
|
|
3
|
+
* MariaDBAdapter.js
|
|
4
|
+
*
|
|
5
|
+
* Este adaptador simplemente ejecuta las consultas SQL construidas por el QueryBuilder
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const mariadb = require('mariadb');
|
|
9
|
+
|
|
10
|
+
class MariaDBAdapter {
|
|
11
|
+
/**
|
|
12
|
+
* Constructor del adaptador MariaDB
|
|
13
|
+
* @param {Object} options - Opciones de configuración
|
|
14
|
+
*/
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
// Configuración de la conexión a la base de datos
|
|
17
|
+
this.dbConfig = {
|
|
18
|
+
host: options.host || 'localhost',
|
|
19
|
+
user: options.user || 'root',
|
|
20
|
+
password: options.password || '',
|
|
21
|
+
database: options.database || 'otrack_db',
|
|
22
|
+
waitForConnections: options.waitForConnections !== false,
|
|
23
|
+
connectionLimit: options.connectionLimit || 10,
|
|
24
|
+
queueLimit: options.queueLimit || 0,
|
|
25
|
+
acquireTimeout: options.acquireTimeout || 60000,
|
|
26
|
+
timeout: options.timeout || 60000,
|
|
27
|
+
...options
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// Crear pool de conexiones
|
|
31
|
+
this.pool = mariadb.createPool(this.dbConfig);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Método para ejecutar una consulta SQL
|
|
36
|
+
* @param {string} sql - Consulta SQL construida por el QueryBuilder
|
|
37
|
+
* @param {Array} params - Parámetros de la consulta
|
|
38
|
+
* @returns {Promise<Object>} - Resultado de la consulta
|
|
39
|
+
*/
|
|
40
|
+
async query(sql, params = []) {
|
|
41
|
+
const connection = await this.pool.getConnection();
|
|
42
|
+
try {
|
|
43
|
+
return await connection.query(sql, params);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw error;
|
|
46
|
+
} finally {
|
|
47
|
+
connection.release();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Método para cerrar la conexión
|
|
53
|
+
*/
|
|
54
|
+
async close() {
|
|
55
|
+
try {
|
|
56
|
+
await this.pool.end();
|
|
57
|
+
console.log('Conexión a MariaDB cerrada correctamente');
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('Error al cerrar la conexión a MariaDB:', error.message);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Método para verificar si el adaptador está conectado
|
|
65
|
+
* @returns {Promise<boolean>} - Verdadero si está conectado
|
|
66
|
+
*/
|
|
67
|
+
async isConnected() {
|
|
68
|
+
try {
|
|
69
|
+
const connection = await this.pool.getConnection();
|
|
70
|
+
connection.release();
|
|
71
|
+
return true;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = MariaDBAdapter;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptador de ejemplo para mostrar consultas SQL por consola
|
|
3
|
+
* ConsoleAdapter.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class ConsoleAdapter {
|
|
7
|
+
constructor(options = {}) {
|
|
8
|
+
this.options = options;
|
|
9
|
+
this.queriesExecuted = 0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Método para ejecutar una consulta
|
|
14
|
+
* @param {string} sql - Consulta SQL
|
|
15
|
+
* @param {Array} params - Parámetros de la consulta
|
|
16
|
+
* @returns {Promise<Object>} - Resultado simulado
|
|
17
|
+
*/
|
|
18
|
+
async query(sql, params = []) {
|
|
19
|
+
this.queriesExecuted++;
|
|
20
|
+
|
|
21
|
+
console.log(`\n--- CONSULTA #${this.queriesExecuted} ---`);
|
|
22
|
+
console.log(`SQL: ${sql}`);
|
|
23
|
+
console.log(`Parámetros:`, params);
|
|
24
|
+
console.log('--- FIN CONSULTA ---\n');
|
|
25
|
+
|
|
26
|
+
// Simular un resultado dependiendo del tipo de consulta
|
|
27
|
+
if (sql.trim().toUpperCase().startsWith('SELECT')) {
|
|
28
|
+
// Para consultas SELECT, devolver un array vacío o con datos simulados
|
|
29
|
+
return [];
|
|
30
|
+
} else if (sql.trim().toUpperCase().startsWith('INSERT')) {
|
|
31
|
+
// Para INSERT, simular un ID insertado
|
|
32
|
+
return { insertId: Math.floor(Math.random() * 10000), affectedRows: 1 };
|
|
33
|
+
} else if (sql.trim().toUpperCase().startsWith('UPDATE') || sql.trim().toUpperCase().startsWith('DELETE')) {
|
|
34
|
+
// Para UPDATE/DELETE, simular filas afectadas
|
|
35
|
+
return { affectedRows: Math.floor(Math.random() * 5) + 1 };
|
|
36
|
+
} else {
|
|
37
|
+
// Para otros tipos de consulta, devolver un objeto vacío
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Método para crear un registro
|
|
44
|
+
* @param {string} tableName - Nombre de la tabla
|
|
45
|
+
* @param {Object} data - Datos a insertar
|
|
46
|
+
* @returns {Promise<Object>} - Resultado simulado
|
|
47
|
+
*/
|
|
48
|
+
async create(tableName, data) {
|
|
49
|
+
const columns = Object.keys(data).join(', ');
|
|
50
|
+
const placeholders = Object.keys(data).map(() => '?').join(', ');
|
|
51
|
+
const sql = `INSERT INTO \`${tableName}\` (${columns}) VALUES (${placeholders})`;
|
|
52
|
+
const params = Object.values(data);
|
|
53
|
+
|
|
54
|
+
return await this.query(sql, params);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Método para encontrar registros
|
|
59
|
+
* @param {string} tableName - Nombre de la tabla
|
|
60
|
+
* @param {Object} conditions - Condiciones de búsqueda
|
|
61
|
+
* @param {Object} options - Opciones adicionales
|
|
62
|
+
* @returns {Promise<Array>} - Resultados simulados
|
|
63
|
+
*/
|
|
64
|
+
async find(tableName, conditions, options = {}) {
|
|
65
|
+
let sql = `SELECT * FROM \`${tableName}\``;
|
|
66
|
+
const params = [];
|
|
67
|
+
|
|
68
|
+
if (conditions && Object.keys(conditions).length > 0) {
|
|
69
|
+
const conditionsList = [];
|
|
70
|
+
for (const [key, value] of Object.entries(conditions)) {
|
|
71
|
+
if (value !== undefined && value !== null) {
|
|
72
|
+
conditionsList.push(`${key} = ?`);
|
|
73
|
+
params.push(value);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (conditionsList.length > 0) {
|
|
77
|
+
sql += ' WHERE ' + conditionsList.join(' AND ');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return await this.query(sql, params);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Método para encontrar un solo registro
|
|
86
|
+
* @param {string} tableName - Nombre de la tabla
|
|
87
|
+
* @param {Object} conditions - Condiciones de búsqueda
|
|
88
|
+
* @param {Object} options - Opciones adicionales
|
|
89
|
+
* @returns {Promise<Object|null>} - Resultado simulado o null
|
|
90
|
+
*/
|
|
91
|
+
async findOne(tableName, conditions, options = {}) {
|
|
92
|
+
const results = await this.find(tableName, conditions, { ...options, limit: 1 });
|
|
93
|
+
return results.length > 0 ? results[0] : null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Método para actualizar registros
|
|
98
|
+
* @param {string} tableName - Nombre de la tabla
|
|
99
|
+
* @param {Object} conditions - Condiciones para seleccionar registros
|
|
100
|
+
* @param {Object} data - Datos a actualizar
|
|
101
|
+
* @returns {Promise<number>} - Número de filas afectadas simuladas
|
|
102
|
+
*/
|
|
103
|
+
async update(tableName, conditions, data) {
|
|
104
|
+
const dataEntries = Object.entries(data);
|
|
105
|
+
if (dataEntries.length === 0) {
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const setClause = dataEntries.map(([key]) => `${key} = ?`).join(', ');
|
|
110
|
+
const params = dataEntries.map(([, value]) => value);
|
|
111
|
+
|
|
112
|
+
const whereClause = [];
|
|
113
|
+
for (const [key, value] of Object.entries(conditions)) {
|
|
114
|
+
if (value !== undefined && value !== null) {
|
|
115
|
+
whereClause.push(`${key} = ?`);
|
|
116
|
+
params.push(value);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
let sql = `UPDATE \`${tableName}\` SET ${setClause}`;
|
|
121
|
+
if (whereClause.length > 0) {
|
|
122
|
+
sql += ' WHERE ' + whereClause.join(' AND ');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const result = await this.query(sql, params);
|
|
126
|
+
return result.affectedRows || 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Método para eliminar registros
|
|
131
|
+
* @param {string} tableName - Nombre de la tabla
|
|
132
|
+
* @param {Object} conditions - Condiciones para seleccionar registros
|
|
133
|
+
* @returns {Promise<number>} - Número de filas afectadas simuladas
|
|
134
|
+
*/
|
|
135
|
+
async delete(tableName, conditions) {
|
|
136
|
+
let sql = `DELETE FROM \`${tableName}\``;
|
|
137
|
+
const params = [];
|
|
138
|
+
|
|
139
|
+
if (conditions && Object.keys(conditions).length > 0) {
|
|
140
|
+
const conditionsList = [];
|
|
141
|
+
for (const [key, value] of Object.entries(conditions)) {
|
|
142
|
+
if (value !== undefined && value !== null) {
|
|
143
|
+
conditionsList.push(`${key} = ?`);
|
|
144
|
+
params.push(value);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (conditionsList.length > 0) {
|
|
148
|
+
sql += ' WHERE ' + conditionsList.join(' AND ');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const result = await this.query(sql, params);
|
|
153
|
+
return result.affectedRows || 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Método para contar registros
|
|
158
|
+
* @param {string} tableName - Nombre de la tabla
|
|
159
|
+
* @param {Object} conditions - Condiciones de conteo
|
|
160
|
+
* @returns {Promise<number>} - Número simulado de registros
|
|
161
|
+
*/
|
|
162
|
+
async count(tableName, conditions) {
|
|
163
|
+
let sql = `SELECT COUNT(*) as total FROM \`${tableName}\``;
|
|
164
|
+
const params = [];
|
|
165
|
+
|
|
166
|
+
if (conditions && Object.keys(conditions).length > 0) {
|
|
167
|
+
const conditionsList = [];
|
|
168
|
+
for (const [key, value] of Object.entries(conditions)) {
|
|
169
|
+
if (value !== undefined && value !== null) {
|
|
170
|
+
conditionsList.push(`${key} = ?`);
|
|
171
|
+
params.push(value);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (conditionsList.length > 0) {
|
|
175
|
+
sql += ' WHERE ' + conditionsList.join(' AND ');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const result = await this.query(sql, params);
|
|
180
|
+
return result[0] ? result[0].total : 0;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = ConsoleAdapter;
|