jerkjs 2.1.6 → 2.2.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 +36 -0
- package/README.md +202 -5
- package/index.js +29 -4
- package/lib/core/server.js +328 -27
- package/lib/loader/routeLoader.js +148 -117
- package/lib/middleware/compressor.js +87 -18
- 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 +60 -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/utils/mimeType.js +62 -0
- package/package.json +5 -3
- package/JERK_FRAMEWORK_DIAGRAM.txt +0 -492
- package/JERK_FRAMEWORK_DIAGRAM_MERMAID.mmd +0 -124
- package/JERK_FRAMEWORK_DOCUMENTATION.md +0 -527
- package/LICENSE +0 -201
- package/README_EN.md +0 -230
- package/README_PT.md +0 -230
- package/docs/ARQUITECTURA_ROUTES.md +0 -140
- package/docs/EXTENSION_MANUAL.md +0 -955
- package/docs/FIREWALL_MANUAL.md +0 -416
- package/docs/HOOK-2.0.md +0 -512
- package/docs/HOOKS_REFERENCE_IMPROVED.md +0 -596
- package/docs/MANUAL_API_SDK.md +0 -536
- package/docs/MARIADB_TOKENS_IMPLEMENTATION.md +0 -110
- package/docs/MIDDLEWARE_MANUAL.md +0 -518
- package/docs/OAUTH2_GOOGLE_MANUAL.md +0 -405
- package/docs/ROUTING_WITHOUT_JSON_GUIDE.md +0 -454
- package/docs/frontend-and-sessions.md +0 -353
- package/docs/guia_inicio_rapido_jerkjs.md +0 -113
- package/examples/examples.arj +0 -0
- 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/standard/routes.json
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"path": "/",
|
|
4
|
-
"method": "GET",
|
|
5
|
-
"controller": "HomeController",
|
|
6
|
-
"handler": "index",
|
|
7
|
-
"contentType": "text/html"
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
"path": "/api/users",
|
|
11
|
-
"method": "GET",
|
|
12
|
-
"controller": "UserController",
|
|
13
|
-
"handler": "getAllUsers",
|
|
14
|
-
"contentType": "application/json"
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
"path": "/api/users/:id",
|
|
18
|
-
"method": "GET",
|
|
19
|
-
"controller": "UserController",
|
|
20
|
-
"handler": "getUserById",
|
|
21
|
-
"contentType": "application/json"
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
"path": "/api/users",
|
|
25
|
-
"method": "POST",
|
|
26
|
-
"controller": "UserController",
|
|
27
|
-
"handler": "createUser",
|
|
28
|
-
"contentType": "application/json"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"path": "/api/products",
|
|
32
|
-
"method": "GET",
|
|
33
|
-
"controller": "ProductController",
|
|
34
|
-
"handler": "getAllProducts",
|
|
35
|
-
"contentType": "application/json"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"path": "/api/products/:id",
|
|
39
|
-
"method": "GET",
|
|
40
|
-
"controller": "ProductController",
|
|
41
|
-
"handler": "getProductById",
|
|
42
|
-
"contentType": "application/json"
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"path": "/health",
|
|
46
|
-
"method": "GET",
|
|
47
|
-
"controller": "HealthController",
|
|
48
|
-
"handler": "checkHealth",
|
|
49
|
-
"contentType": "application/json"
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"path": "/test-compression",
|
|
53
|
-
"method": "GET",
|
|
54
|
-
"controller": "CompressionTestController",
|
|
55
|
-
"handler": "getLargeData",
|
|
56
|
-
"contentType": "application/json"
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"path": "/test-compression-hooks",
|
|
60
|
-
"method": "GET",
|
|
61
|
-
"controller": "CompressionTestController",
|
|
62
|
-
"handler": "testCompressionHooks",
|
|
63
|
-
"contentType": "application/json"
|
|
64
|
-
}
|
|
65
|
-
]
|
package/standard/server.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Servidor estándar para JERK Framework v2.1
|
|
3
|
-
* Este servidor implementa todas las características del framework
|
|
4
|
-
* y carga rutas desde routes.json
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const {
|
|
8
|
-
APIServer,
|
|
9
|
-
ControllerLoader,
|
|
10
|
-
Logger,
|
|
11
|
-
Authenticator,
|
|
12
|
-
Cors,
|
|
13
|
-
RateLimiter,
|
|
14
|
-
Compressor,
|
|
15
|
-
Firewall,
|
|
16
|
-
SessionManager,
|
|
17
|
-
hooks
|
|
18
|
-
} = require('../index.js');
|
|
19
|
-
|
|
20
|
-
const RouteLoader = require('../lib/loader/routeLoader');
|
|
21
|
-
|
|
22
|
-
class StandardServer {
|
|
23
|
-
constructor() {
|
|
24
|
-
// Obtener puerto de variable de entorno, obligatorio
|
|
25
|
-
this.port = parseInt(process.env.PORT);
|
|
26
|
-
if (isNaN(this.port)) {
|
|
27
|
-
throw new Error('La variable de entorno PORT es obligatoria');
|
|
28
|
-
}
|
|
29
|
-
this.host = process.env.HOST || 'localhost';
|
|
30
|
-
|
|
31
|
-
// Inicializar componentes
|
|
32
|
-
this.server = new APIServer({
|
|
33
|
-
port: this.port,
|
|
34
|
-
host: this.host,
|
|
35
|
-
https: process.env.USE_HTTPS === 'true',
|
|
36
|
-
key: process.env.HTTPS_KEY_PATH,
|
|
37
|
-
cert: process.env.HTTPS_CERT_PATH,
|
|
38
|
-
requestTimeout: parseInt(process.env.REQUEST_TIMEOUT) || 120000,
|
|
39
|
-
connectionTimeout: parseInt(process.env.CONNECTION_TIMEOUT) || 120000,
|
|
40
|
-
maxBodySize: parseInt(process.env.MAX_BODY_SIZE) || 10 * 1024 * 1024
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
this.logger = new Logger();
|
|
44
|
-
this.authenticator = new Authenticator({ logger: this.logger });
|
|
45
|
-
this.cors = new Cors();
|
|
46
|
-
this.rateLimiter = new RateLimiter();
|
|
47
|
-
this.compressor = new Compressor({ hooks: hooks });
|
|
48
|
-
this.firewall = new Firewall({ logger: this.logger });
|
|
49
|
-
this.sessionManager = new SessionManager();
|
|
50
|
-
|
|
51
|
-
// Registrar hooks para mostrar información al iniciar
|
|
52
|
-
this.setupHooks();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
setupHooks() {
|
|
56
|
-
// Hook para mostrar información antes de iniciar el servidor
|
|
57
|
-
hooks.addAction('pre_server_start', (server) => {
|
|
58
|
-
this.logger.info('=== INICIANDO SERVIDOR ESTÁNDAR JERK ===');
|
|
59
|
-
this.logger.info(`Puerto: ${this.port}`);
|
|
60
|
-
this.logger.info('Características habilitadas:');
|
|
61
|
-
this.logger.info('- Autenticación (JWT, API Key, Basic, OAuth2, OIDC)');
|
|
62
|
-
this.logger.info('- CORS');
|
|
63
|
-
this.logger.info('- Rate Limiting');
|
|
64
|
-
this.logger.info('- Compresión');
|
|
65
|
-
this.logger.info('- Firewall');
|
|
66
|
-
this.logger.info('- Sesiones');
|
|
67
|
-
this.logger.info('- Sistema de Hooks/Filters');
|
|
68
|
-
this.logger.info('- MVC (Modelo-Vista-Controlador)');
|
|
69
|
-
this.logger.info('- Motor de plantillas');
|
|
70
|
-
this.logger.info('- Manejo de errores');
|
|
71
|
-
|
|
72
|
-
// Cargar rutas antes de iniciar el servidor
|
|
73
|
-
this.loadRoutes();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// Hook para mostrar información después de iniciar el servidor
|
|
77
|
-
hooks.addAction('post_server_start', (server) => {
|
|
78
|
-
this.logger.info(`Servidor estándar JERK escuchando en http://${this.host}:${this.port}`);
|
|
79
|
-
this.displayLoadedRoutes();
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
loadRoutes() {
|
|
84
|
-
try {
|
|
85
|
-
// Instanciar RouteLoader y cargar rutas desde routes.json
|
|
86
|
-
const routeLoader = new RouteLoader();
|
|
87
|
-
routeLoader.loadRoutes(this.server, './routes.json');
|
|
88
|
-
this.logger.info('Rutas cargadas desde routes.json');
|
|
89
|
-
} catch (error) {
|
|
90
|
-
this.logger.error('Error al cargar rutas:', error.message);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
displayLoadedRoutes() {
|
|
95
|
-
if (this.server.routes && this.server.routes.length > 0) {
|
|
96
|
-
this.logger.info('=== RUTAS CARGADAS ===');
|
|
97
|
-
this.server.routes.forEach((route, index) => {
|
|
98
|
-
this.logger.info(`${index + 1}. ${route.method} ${route.path}`);
|
|
99
|
-
});
|
|
100
|
-
} else {
|
|
101
|
-
this.logger.warn('No se encontraron rutas para mostrar');
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Método para configurar middlewares estándar
|
|
106
|
-
configureStandardMiddlewares() {
|
|
107
|
-
// Middleware de firewall (debe ir primero)
|
|
108
|
-
this.server.use(this.firewall.middleware());
|
|
109
|
-
|
|
110
|
-
// Middleware de compresión
|
|
111
|
-
this.server.use(this.compressor.middleware());
|
|
112
|
-
|
|
113
|
-
// Middleware de CORS
|
|
114
|
-
this.server.use(this.cors.middleware());
|
|
115
|
-
|
|
116
|
-
// Middleware de limitación de tasa
|
|
117
|
-
this.server.use(this.rateLimiter.middleware());
|
|
118
|
-
|
|
119
|
-
// Middleware de sesión
|
|
120
|
-
this.server.use(this.sessionManager.middleware());
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
start() {
|
|
124
|
-
// Configurar middlewares estándar
|
|
125
|
-
this.configureStandardMiddlewares();
|
|
126
|
-
|
|
127
|
-
// Iniciar el servidor
|
|
128
|
-
this.server.start();
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
stop() {
|
|
132
|
-
this.server.stop();
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Crear e iniciar el servidor estándar
|
|
137
|
-
const standardServer = new StandardServer();
|
|
138
|
-
standardServer.start();
|
|
139
|
-
|
|
140
|
-
module.exports = StandardServer;
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
const ControllerBase = require('../../lib/mvc/controllerBase');
|
|
2
|
-
|
|
3
|
-
class AuthController extends ControllerBase {
|
|
4
|
-
constructor(options = {}) {
|
|
5
|
-
super(options);
|
|
6
|
-
this.viewsPath = './standardA/views'; // Ruta a las vistas
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
// Mostrar formulario de login
|
|
10
|
-
showLogin(req, res) {
|
|
11
|
-
this.set('title', 'Iniciar Sesión');
|
|
12
|
-
this.render(res, 'auth/login');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Manejar el proceso de login con sesiones
|
|
16
|
-
async handleLogin(req, res) {
|
|
17
|
-
const { username, password } = req.body;
|
|
18
|
-
|
|
19
|
-
// Validación básica
|
|
20
|
-
if (!username || !password) {
|
|
21
|
-
this.set('title', 'Error de Login');
|
|
22
|
-
this.set('error', 'Por favor, proporciona usuario y contraseña');
|
|
23
|
-
this.render(res, 'auth/login');
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Lógica de autenticación simulada (en una aplicación real, esto sería contra una base de datos)
|
|
28
|
-
if (username === 'admin' && password === 'password123') {
|
|
29
|
-
// Crear sesión para el usuario
|
|
30
|
-
if (req.session) {
|
|
31
|
-
req.session.create({
|
|
32
|
-
authenticated: true,
|
|
33
|
-
userId: 1,
|
|
34
|
-
username: 'admin',
|
|
35
|
-
role: 'admin',
|
|
36
|
-
loginTime: new Date().toISOString()
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Redirigir al dashboard
|
|
41
|
-
res.writeHead(302, { 'Location': '/dashboard' });
|
|
42
|
-
res.end();
|
|
43
|
-
} else {
|
|
44
|
-
this.set('title', 'Error de Login');
|
|
45
|
-
this.set('error', 'Credenciales inválidas');
|
|
46
|
-
this.render(res, 'auth/login');
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Manejar logout con sesiones
|
|
51
|
-
handleLogout(req, res) {
|
|
52
|
-
// Destruir la sesión
|
|
53
|
-
if (req.session) {
|
|
54
|
-
req.session.destroy();
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
res.writeHead(302, { 'Location': '/' });
|
|
58
|
-
res.end();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Mostrar el dashboard (requiere autenticación por sesión)
|
|
62
|
-
showDashboard(req, res) {
|
|
63
|
-
// Esta ruta está protegida por el middleware de sesión
|
|
64
|
-
this.set('title', 'Panel de Control');
|
|
65
|
-
this.set('user', req.session?.data || { username: 'Invitado' });
|
|
66
|
-
this.render(res, 'auth/dashboard');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Endpoint protegido con JWT (este endpoint se protege con el middleware de autenticación JWT)
|
|
70
|
-
protectedApiEndpoint(req, res) {
|
|
71
|
-
// Esta ruta está protegida por el middleware de autenticación JWT
|
|
72
|
-
// La información del usuario autenticado está disponible en req.user
|
|
73
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
74
|
-
res.end(JSON.stringify({
|
|
75
|
-
message: 'Acceso concedido a recurso protegido',
|
|
76
|
-
user: req.user,
|
|
77
|
-
timestamp: new Date().toISOString()
|
|
78
|
-
}));
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
module.exports = new AuthController();
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const ControllerBase = require('../../lib/mvc/controllerBase');
|
|
2
|
-
|
|
3
|
-
class HomeController extends ControllerBase {
|
|
4
|
-
constructor(options = {}) {
|
|
5
|
-
super(options);
|
|
6
|
-
this.viewsPath = './standardA/views'; // Ruta a las vistas
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
index(req, res) {
|
|
10
|
-
// Establecer variables para la vista
|
|
11
|
-
this.set('title', 'Página de Inicio');
|
|
12
|
-
this.set('message', 'Bienvenido al ejemplo sin routes.json');
|
|
13
|
-
|
|
14
|
-
// Renderizar la vista
|
|
15
|
-
this.render(res, 'index');
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
module.exports = new HomeController();
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
const ControllerBase = require('../../lib/mvc/controllerBase');
|
|
2
|
-
|
|
3
|
-
class UserController extends ControllerBase {
|
|
4
|
-
constructor(options = {}) {
|
|
5
|
-
super(options);
|
|
6
|
-
this.viewsPath = './standardA/views'; // Ruta a las vistas
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
getAll(req, res) {
|
|
10
|
-
const users = [
|
|
11
|
-
{ id: 1, name: 'Juan Pérez', email: 'juan@example.com' },
|
|
12
|
-
{ id: 2, name: 'Ana García', email: 'ana@example.com' },
|
|
13
|
-
{ id: 3, name: 'Luis Rodríguez', email: 'luis@example.com' }
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
// Establecer variables para la vista
|
|
17
|
-
this.set('title', 'Lista de Usuarios');
|
|
18
|
-
this.set('users', users);
|
|
19
|
-
|
|
20
|
-
// Renderizar la vista
|
|
21
|
-
this.render(res, 'users/list');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
getById(req, res) {
|
|
25
|
-
const userId = req.params.id;
|
|
26
|
-
const user = {
|
|
27
|
-
id: userId,
|
|
28
|
-
name: `Usuario ${userId}`,
|
|
29
|
-
email: `usuario${userId}@example.com`,
|
|
30
|
-
registered: new Date().toISOString()
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
// Establecer variables para la vista
|
|
34
|
-
this.set('user', user);
|
|
35
|
-
|
|
36
|
-
// Renderizar la vista
|
|
37
|
-
this.render(res, 'users/detail');
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
module.exports = new UserController();
|
package/standardA/server.js
DELETED
|
@@ -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;
|