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,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controlador de ejemplo que demuestra la interacción con modelos en el framework JERK
|
|
3
|
+
* Implementación del componente MVC ModelControllerExample.js
|
|
4
|
+
* Muestra cómo los controladores pueden interactuar con modelos
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const ControllerBase = require('./controllerBase');
|
|
8
|
+
|
|
9
|
+
// Importar componentes dinámicamente para evitar dependencias circulares
|
|
10
|
+
let _ModelManager, _UserModel, _MemoryAdapter;
|
|
11
|
+
|
|
12
|
+
const loadComponents = () => {
|
|
13
|
+
if (!_ModelManager || !_UserModel || !_MemoryAdapter) {
|
|
14
|
+
const components = require('../../index.js');
|
|
15
|
+
_ModelManager = components.ModelManager;
|
|
16
|
+
_UserModel = components.UserModel;
|
|
17
|
+
_MemoryAdapter = components.MemoryAdapter;
|
|
18
|
+
}
|
|
19
|
+
return { _ModelManager, _UserModel, _MemoryAdapter };
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
class ModelControllerExample extends ControllerBase {
|
|
23
|
+
constructor() {
|
|
24
|
+
super();
|
|
25
|
+
|
|
26
|
+
// Cargar componentes dinámicamente
|
|
27
|
+
const { _ModelManager, _MemoryAdapter } = loadComponents();
|
|
28
|
+
|
|
29
|
+
// Crear instancia del gestor de modelos
|
|
30
|
+
this.modelManager = new _ModelManager();
|
|
31
|
+
|
|
32
|
+
// Crear e registrar un adaptador de memoria
|
|
33
|
+
const memoryAdapter = new _MemoryAdapter();
|
|
34
|
+
this.modelManager.registerAdapter('memory', memoryAdapter);
|
|
35
|
+
|
|
36
|
+
// Crear e registrar el modelo de usuario
|
|
37
|
+
this.userModel = this.modelManager.createModel('User', {
|
|
38
|
+
adapterName: 'memory',
|
|
39
|
+
modelOptions: { tableName: 'users' }
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Asociar el modelo con este controlador para comunicación bidireccional
|
|
43
|
+
this.modelManager.associateModelWithController('User', this);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Método para obtener todos los usuarios
|
|
48
|
+
* @param {Object} req - Objeto de solicitud
|
|
49
|
+
* @param {Object} res - Objeto de respuesta
|
|
50
|
+
*/
|
|
51
|
+
async getAllUsers(req, res) {
|
|
52
|
+
try {
|
|
53
|
+
const users = await this.userModel.find({});
|
|
54
|
+
|
|
55
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
56
|
+
res.end(JSON.stringify({
|
|
57
|
+
success: true,
|
|
58
|
+
data: users,
|
|
59
|
+
count: users.length
|
|
60
|
+
}));
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error('Error obteniendo usuarios:', error);
|
|
63
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
64
|
+
res.end(JSON.stringify({
|
|
65
|
+
success: false,
|
|
66
|
+
error: error.message
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Método para obtener un usuario por ID
|
|
73
|
+
* @param {Object} req - Objeto de solicitud
|
|
74
|
+
* @param {Object} res - Objeto de respuesta
|
|
75
|
+
*/
|
|
76
|
+
async getUserById(req, res) {
|
|
77
|
+
try {
|
|
78
|
+
const userId = req.params.id;
|
|
79
|
+
|
|
80
|
+
if (!userId) {
|
|
81
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
82
|
+
res.end(JSON.stringify({
|
|
83
|
+
success: false,
|
|
84
|
+
error: 'ID de usuario es requerido'
|
|
85
|
+
}));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const user = await this.userModel.findOne({ id: parseInt(userId) });
|
|
90
|
+
|
|
91
|
+
if (!user) {
|
|
92
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
93
|
+
res.end(JSON.stringify({
|
|
94
|
+
success: false,
|
|
95
|
+
error: 'Usuario no encontrado'
|
|
96
|
+
}));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
101
|
+
res.end(JSON.stringify({
|
|
102
|
+
success: true,
|
|
103
|
+
data: user
|
|
104
|
+
}));
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error('Error obteniendo usuario por ID:', error);
|
|
107
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
108
|
+
res.end(JSON.stringify({
|
|
109
|
+
success: false,
|
|
110
|
+
error: error.message
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Método para crear un nuevo usuario
|
|
117
|
+
* @param {Object} req - Objeto de solicitud
|
|
118
|
+
* @param {Object} res - Objeto de respuesta
|
|
119
|
+
*/
|
|
120
|
+
async createUser(req, res) {
|
|
121
|
+
try {
|
|
122
|
+
const userData = req.body;
|
|
123
|
+
|
|
124
|
+
// Validar que se proporcionen los datos necesarios
|
|
125
|
+
if (!userData.username || !userData.email || !userData.password) {
|
|
126
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
127
|
+
res.end(JSON.stringify({
|
|
128
|
+
success: false,
|
|
129
|
+
error: 'Username, email y password son requeridos'
|
|
130
|
+
}));
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Crear el usuario usando el modelo
|
|
135
|
+
const newUser = await this.userModel.createUser(userData);
|
|
136
|
+
|
|
137
|
+
res.writeHead(201, { 'Content-Type': 'application/json' });
|
|
138
|
+
res.end(JSON.stringify({
|
|
139
|
+
success: true,
|
|
140
|
+
data: newUser
|
|
141
|
+
}));
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error('Error creando usuario:', error);
|
|
144
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
145
|
+
res.end(JSON.stringify({
|
|
146
|
+
success: false,
|
|
147
|
+
error: error.message
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Método para actualizar un usuario
|
|
154
|
+
* @param {Object} req - Objeto de solicitud
|
|
155
|
+
* @param {Object} res - Objeto de respuesta
|
|
156
|
+
*/
|
|
157
|
+
async updateUser(req, res) {
|
|
158
|
+
try {
|
|
159
|
+
const userId = req.params.id;
|
|
160
|
+
const userData = req.body;
|
|
161
|
+
|
|
162
|
+
if (!userId) {
|
|
163
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
164
|
+
res.end(JSON.stringify({
|
|
165
|
+
success: false,
|
|
166
|
+
error: 'ID de usuario es requerido'
|
|
167
|
+
}));
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Actualizar el usuario usando el modelo
|
|
172
|
+
const affectedRows = await this.userModel.updateUser({ id: parseInt(userId) }, userData);
|
|
173
|
+
|
|
174
|
+
if (affectedRows === 0) {
|
|
175
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
176
|
+
res.end(JSON.stringify({
|
|
177
|
+
success: false,
|
|
178
|
+
error: 'Usuario no encontrado'
|
|
179
|
+
}));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
184
|
+
res.end(JSON.stringify({
|
|
185
|
+
success: true,
|
|
186
|
+
message: 'Usuario actualizado exitosamente',
|
|
187
|
+
affectedRows
|
|
188
|
+
}));
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error('Error actualizando usuario:', error);
|
|
191
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
192
|
+
res.end(JSON.stringify({
|
|
193
|
+
success: false,
|
|
194
|
+
error: error.message
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Método para eliminar un usuario
|
|
201
|
+
* @param {Object} req - Objeto de solicitud
|
|
202
|
+
* @param {Object} res - Objeto de respuesta
|
|
203
|
+
*/
|
|
204
|
+
async deleteUser(req, res) {
|
|
205
|
+
try {
|
|
206
|
+
const userId = req.params.id;
|
|
207
|
+
|
|
208
|
+
if (!userId) {
|
|
209
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
210
|
+
res.end(JSON.stringify({
|
|
211
|
+
success: false,
|
|
212
|
+
error: 'ID de usuario es requerido'
|
|
213
|
+
}));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Eliminar el usuario usando el modelo
|
|
218
|
+
const deletedRows = await this.userModel.delete({ id: parseInt(userId) });
|
|
219
|
+
|
|
220
|
+
if (deletedRows === 0) {
|
|
221
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
222
|
+
res.end(JSON.stringify({
|
|
223
|
+
success: false,
|
|
224
|
+
error: 'Usuario no encontrado'
|
|
225
|
+
}));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
230
|
+
res.end(JSON.stringify({
|
|
231
|
+
success: true,
|
|
232
|
+
message: 'Usuario eliminado exitosamente',
|
|
233
|
+
deletedRows
|
|
234
|
+
}));
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error('Error eliminando usuario:', error);
|
|
237
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
238
|
+
res.end(JSON.stringify({
|
|
239
|
+
success: false,
|
|
240
|
+
error: error.message
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Método para obtener usuarios con paginación
|
|
247
|
+
* @param {Object} req - Objeto de solicitud
|
|
248
|
+
* @param {Object} res - Objeto de respuesta
|
|
249
|
+
*/
|
|
250
|
+
async getUsersPaginated(req, res) {
|
|
251
|
+
try {
|
|
252
|
+
// Obtener parámetros de paginación de la consulta
|
|
253
|
+
const page = parseInt(req.query.page) || 1;
|
|
254
|
+
const limit = parseInt(req.query.limit) || 10;
|
|
255
|
+
|
|
256
|
+
// Validar parámetros
|
|
257
|
+
if (page < 1 || limit < 1 || limit > 100) {
|
|
258
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
259
|
+
res.end(JSON.stringify({
|
|
260
|
+
success: false,
|
|
261
|
+
error: 'Parámetros de paginación inválidos'
|
|
262
|
+
}));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Obtener usuarios con paginación usando el modelo
|
|
267
|
+
const result = await this.userModel.getUsersPaginated({ page, limit });
|
|
268
|
+
|
|
269
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
270
|
+
res.end(JSON.stringify({
|
|
271
|
+
success: true,
|
|
272
|
+
...result
|
|
273
|
+
}));
|
|
274
|
+
} catch (error) {
|
|
275
|
+
console.error('Error obteniendo usuarios paginados:', error);
|
|
276
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
277
|
+
res.end(JSON.stringify({
|
|
278
|
+
success: false,
|
|
279
|
+
error: error.message
|
|
280
|
+
}));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
module.exports = ModelControllerExample;
|
|
@@ -202,6 +202,83 @@ class ControllerBase {
|
|
|
202
202
|
clearViewData() {
|
|
203
203
|
this.viewData = {};
|
|
204
204
|
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Carga un modelo para su uso en el controlador
|
|
208
|
+
* @param {string} modelName - Nombre del modelo a cargar
|
|
209
|
+
* @param {Object} options - Opciones para la creación del modelo
|
|
210
|
+
* @returns {ModelBase} - Instancia del modelo cargado
|
|
211
|
+
*/
|
|
212
|
+
loadModel(modelName, options = {}) {
|
|
213
|
+
try {
|
|
214
|
+
// Construir la ruta del modelo
|
|
215
|
+
// Buscar en diferentes ubicaciones posibles
|
|
216
|
+
let ModelClass;
|
|
217
|
+
|
|
218
|
+
// Array de rutas posibles para buscar el modelo
|
|
219
|
+
// Usamos rutas absolutas basadas en el directorio actual de trabajo
|
|
220
|
+
const possiblePaths = [
|
|
221
|
+
`./models/${modelName}`, // En el subdirectorio models del directorio actual
|
|
222
|
+
`./models/${modelName}.js`, // Con extensión explícita
|
|
223
|
+
`../models/${modelName}`, // En el directorio models del directorio padre
|
|
224
|
+
`../models/${modelName}.js`, // Con extensión explícita
|
|
225
|
+
`../../models/${modelName}`, // En el directorio models del directorio abuelo
|
|
226
|
+
`../../models/${modelName}.js`, // Con extensión explícita
|
|
227
|
+
`../../../models/${modelName}`, // En el directorio models del directorio bisabuelo
|
|
228
|
+
`../../../models/${modelName}.js`, // Con extensión explícita
|
|
229
|
+
`./${modelName}`, // En el directorio actual sin subdirectorio
|
|
230
|
+
`./${modelName}.js` // En el directorio actual con extensión
|
|
231
|
+
];
|
|
232
|
+
|
|
233
|
+
let lastError = null;
|
|
234
|
+
|
|
235
|
+
for (const path of possiblePaths) {
|
|
236
|
+
try {
|
|
237
|
+
// Usar require con la ruta absoluta desde el directorio actual
|
|
238
|
+
ModelClass = require(path);
|
|
239
|
+
// Si la importación fue exitosa, salir del bucle
|
|
240
|
+
break;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
// Guardar el último error para propósitos de depuración
|
|
243
|
+
lastError = error;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Si no se encontró el modelo en ninguna ubicación
|
|
249
|
+
if (!ModelClass) {
|
|
250
|
+
throw new Error(`Modelo '${modelName}' no encontrado en ubicaciones estándar. Ultimo error: ${lastError?.message || 'desconocido'}`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Crear instancia del modelo con las opciones proporcionadas
|
|
254
|
+
const modelInstance = new ModelClass(options);
|
|
255
|
+
|
|
256
|
+
// Guardar referencia del modelo en el controlador
|
|
257
|
+
if (!this.models) {
|
|
258
|
+
this.models = {};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
this.models[modelName] = modelInstance;
|
|
262
|
+
|
|
263
|
+
return modelInstance;
|
|
264
|
+
} catch (error) {
|
|
265
|
+
console.error(`Error cargando modelo '${modelName}':`, error.message);
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Obtiene un modelo previamente cargado
|
|
272
|
+
* @param {string} modelName - Nombre del modelo a obtener
|
|
273
|
+
* @returns {ModelBase|null} - Instancia del modelo o null si no está cargado
|
|
274
|
+
*/
|
|
275
|
+
getModel(modelName) {
|
|
276
|
+
if (!this.models) {
|
|
277
|
+
this.models = {};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return this.models[modelName] || null;
|
|
281
|
+
}
|
|
205
282
|
}
|
|
206
283
|
|
|
207
284
|
module.exports = ControllerBase;
|