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.
Files changed (51) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +204 -4
  3. package/README_EN.md +1 -1
  4. package/README_PT.md +1 -1
  5. package/docs/ARQUITECTURA_ROUTES.md +84 -38
  6. package/{JERK_FRAMEWORK_DOCUMENTATION.md → docs/JERK_FRAMEWORK_DOCUMENTATION.md} +28 -2
  7. package/docs/JERK_MODELOS_HOWTO.md +566 -0
  8. package/index.js +41 -5
  9. package/jerk-qbuilder/CHANGELOG.md +71 -0
  10. package/jerk-qbuilder/HOWTO.md +325 -0
  11. package/jerk-qbuilder/README.md +52 -0
  12. package/lib/core/server.js +328 -27
  13. package/lib/loader/routeLoader.js +148 -117
  14. package/lib/mvc/GenericAdapter.js +136 -0
  15. package/lib/mvc/MariaDBAdapter.js +315 -0
  16. package/lib/mvc/MemoryAdapter.js +269 -0
  17. package/lib/mvc/ModelControllerExample.js +285 -0
  18. package/lib/mvc/controllerBase.js +77 -0
  19. package/lib/mvc/modelBase.js +383 -0
  20. package/lib/mvc/modelManager.js +284 -0
  21. package/lib/mvc/userModel.js +265 -0
  22. package/lib/mvc/viewEngine.js +32 -1
  23. package/lib/query/MariaDBAdapter.js +78 -0
  24. package/lib/query/consoleAdapter.js +184 -0
  25. package/lib/query/queryBuilder.js +953 -0
  26. package/lib/query/queryBuilderHooks.js +455 -0
  27. package/lib/query/queryBuilderMiddleware.js +332 -0
  28. package/lib/utils/mimeType.js +62 -0
  29. package/package.json +5 -3
  30. package/utils/find_file_path.sh +36 -0
  31. package/BUG_REPORTE_COMPRESION.txt +0 -72
  32. package/standard/CompressionTestController.js +0 -56
  33. package/standard/HealthController.js +0 -16
  34. package/standard/HomeController.js +0 -12
  35. package/standard/ProductController.js +0 -18
  36. package/standard/README.md +0 -47
  37. package/standard/UserController.js +0 -23
  38. package/standard/package.json +0 -22
  39. package/standard/routes.json +0 -65
  40. package/standard/server.js +0 -140
  41. package/standardA/controllers/AuthController.js +0 -82
  42. package/standardA/controllers/HomeController.js +0 -19
  43. package/standardA/controllers/UserController.js +0 -41
  44. package/standardA/server.js +0 -311
  45. package/standardA/views/auth/dashboard.html +0 -51
  46. package/standardA/views/auth/login.html +0 -47
  47. package/standardA/views/index.html +0 -32
  48. package/standardA/views/users/detail.html +0 -28
  49. package/standardA/views/users/list.html +0 -36
  50. /package/{JERK_FRAMEWORK_DIAGRAM.txt → docs/JERK_FRAMEWORK_DIAGRAM.txt} +0 -0
  51. /package/{JERK_FRAMEWORK_DIAGRAM_MERMAID.mmd → docs/JERK_FRAMEWORK_DIAGRAM_MERMAID.mmd} +0 -0
@@ -1,311 +0,0 @@
1
- const { APIServer, Logger, Authenticator, Cors, RateLimiter, Compressor, Firewall, SessionManager, ViewEngine, ControllerBase, hooks } = require('../index.js');
2
-
3
- class StandardAServer {
4
- constructor() {
5
- // Obtener puerto de variable de entorno, obligatorio
6
- this.port = parseInt(process.env.PORT) || 3000;
7
- this.host = process.env.HOST || 'localhost';
8
-
9
- // Inicializar componentes
10
- this.server = new APIServer({
11
- port: this.port,
12
- host: this.host,
13
- https: process.env.USE_HTTPS === 'true',
14
- key: process.env.HTTPS_KEY_PATH,
15
- cert: process.env.HTTPS_CERT_PATH,
16
- requestTimeout: parseInt(process.env.REQUEST_TIMEOUT) || 120000,
17
- connectionTimeout: parseInt(process.env.CONNECTION_TIMEOUT) || 120000,
18
- maxBodySize: parseInt(process.env.MAX_BODY_SIZE) || 10 * 1024 * 1024
19
- });
20
-
21
- this.logger = new Logger();
22
- this.authenticator = new Authenticator({ logger: this.logger });
23
- this.cors = new Cors();
24
- this.rateLimiter = new RateLimiter();
25
- this.compressor = new Compressor({ hooks: hooks });
26
- this.firewall = new Firewall({ logger: this.logger });
27
- this.sessionManager = new SessionManager();
28
- // Inicializar ViewEngine con hooks para diagnóstico
29
- this.viewEngine = new ViewEngine({ hooks: hooks });
30
-
31
- // Registrar hooks para diagnóstico del sistema de vistas
32
- this.setupViewEngineDiagnostics();
33
-
34
- // Registrar hooks para mostrar información al iniciar
35
- this.setupHooks();
36
- }
37
-
38
- setupViewEngineDiagnostics() {
39
- // Hook para diagnosticar la obtención de rutas de vistas
40
- hooks.addFilter('view_get_path_before_processing', (viewName, viewEngine) => {
41
- console.log(`[DIAGNÓSTICO VIEW] getViewPath llamado con: "${viewName}"`);
42
- return viewName;
43
- });
44
-
45
- // Hook para diagnosticar la detección de extensión
46
- hooks.addAction('view_extension_detection', (viewName, hasExtension, viewEngine) => {
47
- console.log(`[DIAGNÓSTICO VIEW] Nombre: "${viewName}", Tiene extensión: ${hasExtension}`);
48
- });
49
-
50
- // Hook para diagnosticar la normalización del path
51
- hooks.addFilter('view_normalized_path', (normalizedPath, viewName, hasExtension, viewEngine) => {
52
- console.log(`[DIAGNÓSTICO VIEW] Path normalizado: "${normalizedPath}", Original: "${viewName}", Tiene extensión: ${hasExtension}`);
53
- return normalizedPath;
54
- });
55
-
56
- // Hook para diagnosticar la ruta final
57
- hooks.addFilter('view_get_path_after_processing', (viewPath, viewName, normalizedPath, viewEngine) => {
58
- console.log(`[DIAGNÓSTICO VIEW] Ruta final: "${viewPath}", Nombre original: "${viewName}", Path normalizado: "${normalizedPath}"`);
59
- return viewPath;
60
- });
61
- }
62
-
63
- setupHooks() {
64
- // Hook para mostrar información antes de iniciar el servidor
65
- hooks.addAction('pre_server_start', (server) => {
66
- this.logger.info('=== INICIANDO SERVIDOR ESTÁNDAR JERK (Sin routes.json) ===');
67
- this.logger.info(`Puerto: ${this.port}`);
68
- this.logger.info('Características habilitadas:');
69
- this.logger.info('- Autenticación (JWT, API Key, Basic, OAuth2, OIDC)');
70
- this.logger.info('- CORS');
71
- this.logger.info('- Rate Limiting');
72
- this.logger.info('- Compresión');
73
- this.logger.info('- Firewall');
74
- this.logger.info('- Sesiones');
75
- this.logger.info('- Sistema de Hooks/Filters');
76
- this.logger.info('- MVC (Modelo-Vista-Controlador)');
77
- this.logger.info('- Motor de plantillas');
78
- this.logger.info('- Manejo de errores');
79
-
80
- // Configurar rutas sin usar routes.json
81
- this.configureRoutes();
82
- });
83
-
84
- // Hook para mostrar información después de iniciar el servidor
85
- hooks.addAction('post_server_start', (server) => {
86
- this.logger.info(`Servidor estándar JERK escuchando en http://${this.host}:${this.port}`);
87
- });
88
- }
89
-
90
- configureRoutes() {
91
- // Importar controladores
92
- const HomeController = require('./controllers/HomeController');
93
- const UserController = require('./controllers/UserController');
94
- const AuthController = require('./controllers/AuthController');
95
-
96
- // Ruta principal
97
- this.server.addRoute('GET', '/', (req, res) => {
98
- HomeController.setRequestResponse(req, res);
99
- HomeController.index(req, res);
100
- });
101
-
102
- // Rutas de usuarios
103
- this.server.addRoute('GET', '/users', (req, res) => {
104
- UserController.setRequestResponse(req, res);
105
- UserController.getAll(req, res);
106
- });
107
-
108
- this.server.addRoute('GET', '/users/:id', (req, res) => {
109
- UserController.setRequestResponse(req, res);
110
- UserController.getById(req, res);
111
- });
112
-
113
- // Rutas de autenticación con sesiones
114
- this.server.addRoute('GET', '/login', (req, res) => {
115
- AuthController.setRequestResponse(req, res);
116
- AuthController.showLogin(req, res);
117
- });
118
-
119
- this.server.addRoute('POST', '/auth/login', (req, res) => {
120
- AuthController.setRequestResponse(req, res);
121
- AuthController.handleLogin(req, res);
122
- }, {
123
- contentType: 'application/json'
124
- });
125
-
126
- this.server.addRoute('POST', '/auth/logout', (req, res) => {
127
- AuthController.setRequestResponse(req, res);
128
- AuthController.handleLogout(req, res);
129
- }, {
130
- contentType: 'application/json'
131
- });
132
-
133
- // Ruta protegida con autenticación por sesión
134
- this.server.addRoute('GET', '/dashboard', (req, res) => {
135
- // Verificar si el usuario está autenticado a través de sesión
136
- if (!req.session || !req.session.data || !req.session.data.authenticated) {
137
- res.writeHead(302, { 'Location': '/login' });
138
- res.end();
139
- return;
140
- }
141
-
142
- AuthController.setRequestResponse(req, res);
143
- AuthController.showDashboard(req, res);
144
- });
145
-
146
- // Ruta protegida con autenticación JWT - usando middleware de autenticación
147
- // Verificar que JWT_SECRET esté definido antes de crear la ruta
148
- const jwtSecret = process.env.JWT_SECRET;
149
- if (!jwtSecret) {
150
- this.logger.warn('Advertencia: JWT_SECRET no está definido. La ruta /api/protected no estará disponible.');
151
- } else {
152
- // Registrar estrategia JWT en el authenticator como se hace en los ejemplos
153
- this.authenticator.use('jwt-standardA', async (req, options = {}) => {
154
- const authHeader = req.headers.authorization;
155
- const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
156
-
157
- if (!token) {
158
- return false;
159
- }
160
-
161
- try {
162
- // Usar jsonwebtoken para validar el token como en los ejemplos
163
- const jwt = require('jsonwebtoken');
164
- const decoded = jwt.verify(token, jwtSecret);
165
-
166
- // Agregar información del usuario a la solicitud
167
- req.user = decoded;
168
- return true;
169
- } catch (error) {
170
- // Token inválido o expirado
171
- return false;
172
- }
173
- });
174
-
175
- this.server.addRoute('GET', '/api/protected', (req, res) => {
176
- // Aplicar autenticación JWT manualmente
177
- const authHeader = req.headers.authorization;
178
- const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
179
-
180
- if (!token) {
181
- res.writeHead(401, { 'Content-Type': 'application/json' });
182
- res.end(JSON.stringify({ error: 'Token no proporcionado' }));
183
- return;
184
- }
185
-
186
- try {
187
- // Usar jsonwebtoken para validar el token como en los ejemplos
188
- const jwt = require('jsonwebtoken');
189
- const decoded = jwt.verify(token, process.env.JWT_SECRET);
190
-
191
- // Agregar información del usuario a la solicitud
192
- req.user = decoded;
193
-
194
- // Continuar con la lógica del controlador
195
- AuthController.setRequestResponse(req, res);
196
- AuthController.protectedApiEndpoint(req, res);
197
- } catch (error) {
198
- res.writeHead(401, { 'Content-Type': 'application/json' });
199
- res.end(JSON.stringify({ error: 'Token inválido o expirado' }));
200
- }
201
- }, {
202
- contentType: 'application/json'
203
- });
204
- }
205
-
206
- // Endpoint para login JWT
207
- this.server.addRoute('POST', '/api/login', (req, res) => {
208
- const { username, password } = req.body;
209
-
210
- // Validación básica
211
- if (!username || !password) {
212
- res.writeHead(400, { 'Content-Type': 'application/json' });
213
- res.end(JSON.stringify({
214
- success: false,
215
- message: 'Usuario y contraseña son requeridos'
216
- }));
217
- return;
218
- }
219
-
220
- // Lógica de autenticación simulada
221
- if (username === 'admin' && password === 'password123') {
222
- // Verificar que JWT_SECRET esté definido en las variables de entorno
223
- const jwtSecret = process.env.JWT_SECRET;
224
- if (!jwtSecret) {
225
- this.logger.error('ERROR: JWT_SECRET no está definido en las variables de entorno');
226
- res.writeHead(500, { 'Content-Type': 'application/json' });
227
- res.end(JSON.stringify({
228
- success: false,
229
- message: 'Configuración de seguridad incompleta - JWT_SECRET no definido'
230
- }));
231
- return;
232
- }
233
-
234
- // Generar un token JWT usando jsonwebtoken directamente como en los ejemplos
235
- const jwt = require('jsonwebtoken');
236
- const payload = {
237
- userId: 1,
238
- username: username,
239
- role: 'admin'
240
- };
241
-
242
- const token = jwt.sign(payload, jwtSecret, { expiresIn: '1h' });
243
-
244
- res.writeHead(200, { 'Content-Type': 'application/json' });
245
- res.end(JSON.stringify({
246
- success: true,
247
- token,
248
- user: { id: 1, username: 'admin', role: 'admin' }
249
- }));
250
- } else {
251
- res.writeHead(401, { 'Content-Type': 'application/json' });
252
- res.end(JSON.stringify({
253
- success: false,
254
- message: 'Credenciales inválidas'
255
- }));
256
- }
257
- }, {
258
- contentType: 'application/json'
259
- });
260
-
261
- // Ruta de API con content-type JSON
262
- this.server.addRoute('GET', '/api/data', (req, res) => {
263
- res.end(JSON.stringify({ message: 'Datos de la API', timestamp: new Date().toISOString() }));
264
- }, {
265
- contentType: 'application/json'
266
- });
267
-
268
- this.logger.info('Rutas configuradas sin usar routes.json');
269
- }
270
-
271
- // Método para configurar middlewares estándar
272
- configureStandardMiddlewares() {
273
- // Middleware de firewall (debe ir primero)
274
- this.server.use(this.firewall.middleware());
275
-
276
- // Middleware de compresión
277
- this.server.use(this.compressor.middleware());
278
-
279
- // Middleware de CORS
280
- this.server.use(this.cors.middleware());
281
-
282
- // Middleware de limitación de tasa
283
- this.server.use(this.rateLimiter.middleware());
284
-
285
- // Middleware de sesión
286
- this.server.use(this.sessionManager.middleware());
287
-
288
- // Configurar el motor de vistas en el servidor
289
- this.server.viewEngine = this.viewEngine;
290
- const path = require('path');
291
- this.viewEngine.viewsPath = path.resolve(__dirname, 'views');
292
- }
293
-
294
- start() {
295
- // Configurar middlewares estándar
296
- this.configureStandardMiddlewares();
297
-
298
- // Iniciar el servidor
299
- this.server.start();
300
- }
301
-
302
- stop() {
303
- this.server.stop();
304
- }
305
- }
306
-
307
- // Crear e iniciar el servidor estándar
308
- const standardAServer = new StandardAServer();
309
- standardAServer.start();
310
-
311
- module.exports = StandardAServer;
@@ -1,51 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>{{title}}</title>
5
- <meta charset="UTF-8">
6
- <style>
7
- body { font-family: Arial, sans-serif; margin: 40px; background-color: #f5f5f5; }
8
- .container { max-width: 800px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
9
- .header { background-color: #007bff; color: white; padding: 15px; border-radius: 5px; margin-bottom: 20px; }
10
- .user-info { background-color: #f8f9fa; padding: 15px; border-radius: 5px; margin-bottom: 20px; }
11
- .nav { margin: 20px 0; }
12
- .nav a { display: inline-block; margin-right: 15px; padding: 8px 15px; background-color: #28a745; color: white; text-decoration: none; border-radius: 3px; }
13
- .nav a:hover { background-color: #218838; }
14
- .section { margin: 20px 0; padding: 15px; border-left: 4px solid #007bff; background-color: #f8f9fa; }
15
- </style>
16
- </head>
17
- <body>
18
- <div class="container">
19
- <div class="header">
20
- <h1>{{title}}</h1>
21
- </div>
22
-
23
- <div class="user-info">
24
- <h3>Bienvenido, {{user.username}}!</h3>
25
- <p>Rol: {{user.role}}</p>
26
- </div>
27
-
28
- <div class="nav">
29
- <a href="/">Inicio</a>
30
- <a href="/users">Usuarios</a>
31
- <a href="/auth/logout">Cerrar Sesión</a>
32
- </div>
33
-
34
- <div class="section">
35
- <h2>Panel de Control</h2>
36
- <p>Este es un área protegida que solo pueden acceder usuarios autenticados.</p>
37
- <p>Aquí puedes gestionar la configuración de la aplicación, ver estadísticas y realizar tareas administrativas.</p>
38
- </div>
39
-
40
- <div class="section">
41
- <h2>Funcionalidades Disponibles</h2>
42
- <ul>
43
- <li>Administración de usuarios</li>
44
- <li>Configuración del sistema</li>
45
- <li>Visualización de estadísticas</li>
46
- <li>Registro de actividad</li>
47
- </ul>
48
- </div>
49
- </div>
50
- </body>
51
- </html>
@@ -1,47 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>{{title}}</title>
5
- <meta charset="UTF-8">
6
- <style>
7
- body { font-family: Arial, sans-serif; margin: 40px; background-color: #f5f5f5; }
8
- .container { max-width: 400px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
9
- .form-group { margin-bottom: 15px; }
10
- label { display: block; margin-bottom: 5px; font-weight: bold; }
11
- input[type="text"], input[type="password"] { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; box-sizing: border-box; }
12
- button { background-color: #007bff; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; width: 100%; }
13
- button:hover { background-color: #0056b3; }
14
- .error { color: red; margin-bottom: 15px; }
15
- .nav { margin-top: 20px; text-align: center; }
16
- .nav a { color: #007bff; text-decoration: none; }
17
- .nav a:hover { text-decoration: underline; }
18
- </style>
19
- </head>
20
- <body>
21
- <div class="container">
22
- <h1>{{title}}</h1>
23
-
24
- {{if error}}
25
- <div class="error">{{error}}</div>
26
- {{endif}}
27
-
28
- <form method="POST" action="/auth/login">
29
- <div class="form-group">
30
- <label for="username">Usuario:</label>
31
- <input type="text" id="username" name="username" required>
32
- </div>
33
-
34
- <div class="form-group">
35
- <label for="password">Contraseña:</label>
36
- <input type="password" id="password" name="password" required>
37
- </div>
38
-
39
- <button type="submit">Iniciar Sesión</button>
40
- </form>
41
-
42
- <div class="nav">
43
- <a href="/">Volver al inicio</a>
44
- </div>
45
- </div>
46
- </body>
47
- </html>
@@ -1,32 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>{{title}}</title>
5
- <meta charset="UTF-8">
6
- <style>
7
- body { font-family: Arial, sans-serif; margin: 40px; }
8
- .header { background-color: #f0f0f0; padding: 20px; border-radius: 5px; }
9
- .content { margin-top: 20px; }
10
- .nav { margin-top: 20px; }
11
- .nav a { display: inline-block; margin-right: 15px; padding: 8px 15px; background-color: #007bff; color: white; text-decoration: none; border-radius: 3px; }
12
- .nav a:hover { background-color: #0056b3; }
13
- </style>
14
- </head>
15
- <body>
16
- <div class="header">
17
- <h1>{{title}}</h1>
18
- <p>{{message}}</p>
19
- </div>
20
-
21
- <div class="content">
22
- <h2>Ejemplo de aplicación sin routes.json</h2>
23
- <p>Este servidor utiliza el método <code>addRoute()</code> con todas las funcionalidades equivalentes a las de <code>routes.json</code>.</p>
24
-
25
- <div class="nav">
26
- <a href="/">Inicio</a>
27
- <a href="/users">Usuarios</a>
28
- <a href="/api/data">API Data</a>
29
- </div>
30
- </div>
31
- </body>
32
- </html>
@@ -1,28 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>Detalle del Usuario</title>
5
- <meta charset="UTF-8">
6
- <style>
7
- body { font-family: Arial, sans-serif; margin: 40px; }
8
- .header { background-color: #f0f0f0; padding: 20px; border-radius: 5px; }
9
- .user-info { background-color: #f9f9f9; padding: 20px; border-radius: 5px; margin-top: 20px; }
10
- .back-link { margin-top: 20px; }
11
- </style>
12
- </head>
13
- <body>
14
- <div class="header">
15
- <h1>Detalle del Usuario</h1>
16
- <div class="back-link">
17
- <a href="/users">← Volver a la lista de usuarios</a>
18
- </div>
19
- </div>
20
-
21
- <div class="user-info">
22
- <h2>{{user.name}}</h2>
23
- <p><strong>ID:</strong> {{user.id}}</p>
24
- <p><strong>Email:</strong> {{user.email}}</p>
25
- <p><strong>Registrado:</strong> {{user.registered}}</p>
26
- </div>
27
- </body>
28
- </html>
@@ -1,36 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>{{title}}</title>
5
- <meta charset="UTF-8">
6
- <style>
7
- body { font-family: Arial, sans-serif; margin: 40px; }
8
- .header { background-color: #f0f0f0; padding: 20px; border-radius: 5px; }
9
- .content { margin-top: 20px; }
10
- ul { list-style-type: none; padding: 0; }
11
- li { background-color: #f9f9f9; margin: 5px 0; padding: 10px; border-radius: 3px; }
12
- </style>
13
- </head>
14
- <body>
15
- <div class="header">
16
- <h1>{{title}}</h1>
17
- <p>{{message}}</p>
18
- </div>
19
-
20
- <div class="content">
21
- <h2>Usuarios</h2>
22
- {{if users}}
23
- <ul>
24
- {{foreach:users}}
25
- <li>
26
- <strong>{{item.name}}</strong> - {{item.email}}
27
- <br><a href="/users/{{item.id}}">Ver detalles</a>
28
- </li>
29
- {{endforeach}}
30
- </ul>
31
- {{else}}
32
- <p>No hay usuarios disponibles.</p>
33
- {{endif}}
34
- </div>
35
- </body>
36
- </html>