jerkjs 2.0.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/LICENSE +200 -0
- package/README.md +171 -0
- package/doc/EXTENSION_MANUAL.md +958 -0
- package/doc/FIREWALL_MANUAL.md +419 -0
- package/doc/HOOKS_REFERENCE_IMPROVED.md +599 -0
- package/doc/MANUAL_API_SDK.md +539 -0
- package/doc/MANUAL_MVC.md +397 -0
- package/doc/MARIADB_TOKENS_IMPLEMENTATION.md +113 -0
- package/doc/MIDDLEWARE_MANUAL.md +521 -0
- package/doc/OAUTH2_GOOGLE_MANUAL.md +408 -0
- package/doc/frontend-and-sessions.md +356 -0
- package/examples/advanced/controllers/productController.js +64 -0
- package/examples/advanced/controllers/userController.js +85 -0
- package/examples/advanced/routes.json +51 -0
- package/examples/advanced_example.js +93 -0
- package/examples/basic/controllers/userController.js +85 -0
- package/examples/basic_example.js +72 -0
- package/examples/frontend/README.md +71 -0
- package/examples/frontend/app.js +71 -0
- package/examples/frontend/controllers/apiController.js +39 -0
- package/examples/frontend/controllers/authController.js +220 -0
- package/examples/frontend/controllers/formController.js +47 -0
- package/examples/frontend/controllers/messageController.js +96 -0
- package/examples/frontend/controllers/pageController.js +178 -0
- package/examples/frontend/controllers/staticController.js +167 -0
- package/examples/frontend/routes.json +90 -0
- package/examples/mvc_example/app.js +138 -0
- package/examples/mvc_example/views/home/index.html +26 -0
- package/examples/mvc_example/views/home/simple.html +3 -0
- package/examples/mvc_example/views/layout.html +23 -0
- package/examples/mvc_example/views/test.html +3 -0
- package/examples/mvc_example/views/user/invalid.html +6 -0
- package/examples/mvc_example/views/user/list.html +36 -0
- package/examples/mvc_example/views/user/notfound.html +6 -0
- package/examples/mvc_example/views/user/profile.html +11 -0
- package/examples/mvc_routes_example/app.js +34 -0
- package/examples/mvc_routes_example/controllers/mainController.js +27 -0
- package/examples/mvc_routes_example/controllers/productController.js +47 -0
- package/examples/mvc_routes_example/controllers/userController.js +76 -0
- package/examples/mvc_routes_example/routes.json +30 -0
- package/examples/mvc_routes_example/views/layout.html +31 -0
- package/examples/mvc_routes_example/views/main/index.html +11 -0
- package/examples/mvc_routes_example/views/product/catalog.html +24 -0
- package/examples/mvc_routes_example/views/user/invalid.html +6 -0
- package/examples/mvc_routes_example/views/user/list.html +40 -0
- package/examples/mvc_routes_example/views/user/notfound.html +6 -0
- package/examples/mvc_routes_example/views/user/profile.html +18 -0
- package/examples/public/README.md +92 -0
- package/examples/public/app.js +72 -0
- package/examples/public/controllers/healthController.js +20 -0
- package/examples/public/controllers/mainController.js +22 -0
- package/examples/public/controllers/userController.js +139 -0
- package/examples/public/routes.json +51 -0
- package/examples/v2/README.md +72 -0
- package/examples/v2/app.js +74 -0
- package/examples/v2/app_fixed.js +74 -0
- package/examples/v2/controllers/authController.js +64 -0
- package/examples/v2/controllers/mainController.js +24 -0
- package/examples/v2/controllers/protectedController.js +12 -0
- package/examples/v2/controllers/userController.js +16 -0
- package/examples/v2/package.json +27 -0
- package/examples/v2/routes.json +30 -0
- package/examples/v2/test_api.sh +47 -0
- package/examples/v2/tokens_example.sqlite +0 -0
- package/examples/v2.1_firewall_demo/README.md +113 -0
- package/examples/v2.1_firewall_demo/app.js +182 -0
- package/examples/v2.1_firewall_demo/package.json +27 -0
- package/examples/v2.1_hooks_demo/README.md +85 -0
- package/examples/v2.1_hooks_demo/app.js +101 -0
- package/examples/v2.1_hooks_demo/controllers/hooksController.js +29 -0
- package/examples/v2.1_hooks_demo/controllers/mainController.js +18 -0
- package/examples/v2.1_hooks_demo/package.json +27 -0
- package/examples/v2.1_hooks_demo/routes.json +16 -0
- package/examples/v2.1_openapi_demo/README.md +82 -0
- package/examples/v2.1_openapi_demo/app.js +296 -0
- package/examples/v2.1_openapi_demo/package.json +26 -0
- package/examples/v2_cors/README.md +82 -0
- package/examples/v2_cors/app.js +108 -0
- package/examples/v2_cors/package.json +23 -0
- package/examples/v2_json_auth/README.md +83 -0
- package/examples/v2_json_auth/app.js +72 -0
- package/examples/v2_json_auth/controllers/authController.js +67 -0
- package/examples/v2_json_auth/controllers/mainController.js +16 -0
- package/examples/v2_json_auth/controllers/protectedController.js +12 -0
- package/examples/v2_json_auth/controllers/tokenController.js +28 -0
- package/examples/v2_json_auth/controllers/userController.js +15 -0
- package/examples/v2_json_auth/package.json +26 -0
- package/examples/v2_json_auth/routes.json +37 -0
- package/examples/v2_json_auth/tokens.json +20 -0
- package/examples/v2_mariadb_auth/README.md +94 -0
- package/examples/v2_mariadb_auth/app.js +81 -0
- package/examples/v2_mariadb_auth/controllers/authController.js +95 -0
- package/examples/v2_mariadb_auth/controllers/mainController.js +31 -0
- package/examples/v2_mariadb_auth/controllers/protectedController.js +12 -0
- package/examples/v2_mariadb_auth/controllers/userController.js +17 -0
- package/examples/v2_mariadb_auth/package.json +27 -0
- package/examples/v2_mariadb_auth/routes.json +37 -0
- package/examples/v2_no_auth/README.md +75 -0
- package/examples/v2_no_auth/app.js +72 -0
- package/examples/v2_no_auth/controllers/healthController.js +14 -0
- package/examples/v2_no_auth/controllers/mainController.js +19 -0
- package/examples/v2_no_auth/controllers/productController.js +31 -0
- package/examples/v2_no_auth/controllers/publicController.js +16 -0
- package/examples/v2_no_auth/package.json +22 -0
- package/examples/v2_no_auth/routes.json +37 -0
- package/examples/v2_oauth/README.md +70 -0
- package/examples/v2_oauth/app.js +90 -0
- package/examples/v2_oauth/controllers/mainController.js +45 -0
- package/examples/v2_oauth/controllers/oauthController.js +247 -0
- package/examples/v2_oauth/controllers/protectedController.js +13 -0
- package/examples/v2_oauth/controllers/userController.js +17 -0
- package/examples/v2_oauth/package.json +26 -0
- package/examples/v2_oauth/routes.json +44 -0
- package/examples/v2_openapi/README.md +77 -0
- package/examples/v2_openapi/app.js +222 -0
- package/examples/v2_openapi/controllers/authController.js +52 -0
- package/examples/v2_openapi/controllers/mainController.js +26 -0
- package/examples/v2_openapi/controllers/productController.js +17 -0
- package/examples/v2_openapi/controllers/userController.js +27 -0
- package/examples/v2_openapi/package.json +26 -0
- package/examples/v2_openapi/routes.json +37 -0
- package/generate_token.js +10 -0
- package/index.js +85 -0
- package/jerk.jpg +0 -0
- package/lib/core/handler.js +86 -0
- package/lib/core/hooks.js +224 -0
- package/lib/core/router.js +204 -0
- package/lib/core/securityEnhancedServer.js +752 -0
- package/lib/core/server.js +369 -0
- package/lib/loader/controllerLoader.js +175 -0
- package/lib/loader/routeLoader.js +341 -0
- package/lib/middleware/auditLogger.js +208 -0
- package/lib/middleware/authenticator.js +565 -0
- package/lib/middleware/compressor.js +218 -0
- package/lib/middleware/cors.js +135 -0
- package/lib/middleware/firewall.js +443 -0
- package/lib/middleware/rateLimiter.js +210 -0
- package/lib/middleware/session.js +301 -0
- package/lib/middleware/validator.js +193 -0
- package/lib/mvc/controllerBase.js +207 -0
- package/lib/mvc/viewEngine.js +752 -0
- package/lib/utils/configParser.js +223 -0
- package/lib/utils/logger.js +145 -0
- package/lib/utils/mariadbTokenAdapter.js +226 -0
- package/lib/utils/openapiGenerator.js +140 -0
- package/lib/utils/sqliteTokenAdapter.js +224 -0
- package/lib/utils/tokenManager.js +254 -0
- package/package.json +47 -0
- package/v2examplle/v2_json_auth/README.md +83 -0
- package/v2examplle/v2_json_auth/app.js +72 -0
- package/v2examplle/v2_json_auth/controllers/authController.js +67 -0
- package/v2examplle/v2_json_auth/controllers/mainController.js +16 -0
- package/v2examplle/v2_json_auth/controllers/protectedController.js +12 -0
- package/v2examplle/v2_json_auth/controllers/tokenController.js +28 -0
- package/v2examplle/v2_json_auth/controllers/userController.js +15 -0
- package/v2examplle/v2_json_auth/package.json +26 -0
- package/v2examplle/v2_json_auth/routes.json +37 -0
- package/v2examplle/v2_json_auth/tokens.json +20 -0
- package/v2examplle/v2_mariadb_auth/README.md +94 -0
- package/v2examplle/v2_mariadb_auth/app.js +81 -0
- package/v2examplle/v2_mariadb_auth/controllers/authController.js +95 -0
- package/v2examplle/v2_mariadb_auth/controllers/mainController.js +31 -0
- package/v2examplle/v2_mariadb_auth/controllers/protectedController.js +12 -0
- package/v2examplle/v2_mariadb_auth/controllers/userController.js +17 -0
- package/v2examplle/v2_mariadb_auth/package.json +27 -0
- package/v2examplle/v2_mariadb_auth/routes.json +37 -0
- package/v2examplle/v2_sqlite_auth/README.md +72 -0
- package/v2examplle/v2_sqlite_auth/app.js +74 -0
- package/v2examplle/v2_sqlite_auth/app_fixed.js +74 -0
- package/v2examplle/v2_sqlite_auth/controllers/authController.js +64 -0
- package/v2examplle/v2_sqlite_auth/controllers/mainController.js +24 -0
- package/v2examplle/v2_sqlite_auth/controllers/protectedController.js +12 -0
- package/v2examplle/v2_sqlite_auth/controllers/userController.js +16 -0
- package/v2examplle/v2_sqlite_auth/package.json +27 -0
- package/v2examplle/v2_sqlite_auth/routes.json +30 -0
- package/v2examplle/v2_sqlite_auth/test_api.sh +47 -0
- package/v2examplle/v2_sqlite_auth/tokens_example.sqlite +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="es">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{title}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
body { font-family: Arial, sans-serif; margin: 40px; background-color: #f5f5f5; }
|
|
9
|
+
.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); }
|
|
10
|
+
h1 { color: #333; }
|
|
11
|
+
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
|
12
|
+
th, td { padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }
|
|
13
|
+
th { background-color: #f2f2f2; }
|
|
14
|
+
.active { color: green; }
|
|
15
|
+
.inactive { color: red; }
|
|
16
|
+
</style>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<div class="container">
|
|
20
|
+
{{content}}
|
|
21
|
+
</div>
|
|
22
|
+
</body>
|
|
23
|
+
</html>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<h1>{{title}}</h1>
|
|
2
|
+
|
|
3
|
+
{{if users}}
|
|
4
|
+
<table>
|
|
5
|
+
<thead>
|
|
6
|
+
<tr>
|
|
7
|
+
<th>ID</th>
|
|
8
|
+
<th>Nombre</th>
|
|
9
|
+
<th>Email</th>
|
|
10
|
+
<th>Estado</th>
|
|
11
|
+
<th>Acciones</th>
|
|
12
|
+
</tr>
|
|
13
|
+
</thead>
|
|
14
|
+
<tbody>
|
|
15
|
+
{{foreach:users}}
|
|
16
|
+
<tr>
|
|
17
|
+
<td>{{item.id}}</td>
|
|
18
|
+
<td>{{item.name}}</td>
|
|
19
|
+
<td>{{item.email}}</td>
|
|
20
|
+
<td>
|
|
21
|
+
{{if item.active}}
|
|
22
|
+
<span class="active">Activo</span>
|
|
23
|
+
{{else}}
|
|
24
|
+
<span class="inactive">Inactivo</span>
|
|
25
|
+
{{endif}}
|
|
26
|
+
</td>
|
|
27
|
+
<td><a href="/profile?id={{item.id}}">Ver Perfil</a></td>
|
|
28
|
+
</tr>
|
|
29
|
+
{{endforeach}}
|
|
30
|
+
</tbody>
|
|
31
|
+
</table>
|
|
32
|
+
{{else}}
|
|
33
|
+
<p>No hay usuarios registrados.</p>
|
|
34
|
+
{{endif}}
|
|
35
|
+
|
|
36
|
+
<a href="/">Volver a la página de inicio</a>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<h1>{{title}}</h1>
|
|
2
|
+
|
|
3
|
+
<div>
|
|
4
|
+
<h2>Información del Usuario</h2>
|
|
5
|
+
<p><strong>ID:</strong> {{user.id}}</p>
|
|
6
|
+
<p><strong>Nombre:</strong> {{user.name}}</p>
|
|
7
|
+
<p><strong>Email:</strong> {{user.email}}</p>
|
|
8
|
+
<p><strong>Registrado:</strong> {{user.registered}}</p>
|
|
9
|
+
</div>
|
|
10
|
+
|
|
11
|
+
<a href="/users">Volver a la lista de usuarios</a>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ejemplo de uso del sistema MVC con definición de rutas desde archivo JSON
|
|
3
|
+
* Demostración de vistas, helpers y filtros
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { APIServer, RouteLoader, ControllerLoader, ViewEngine } = require('../../index');
|
|
7
|
+
|
|
8
|
+
// Crear instancia del servidor
|
|
9
|
+
const server = new APIServer({
|
|
10
|
+
port: 9001,
|
|
11
|
+
host: 'localhost'
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
// Crear instancia del cargador de rutas
|
|
15
|
+
const routeLoader = new RouteLoader();
|
|
16
|
+
|
|
17
|
+
// Cargar las rutas desde el archivo JSON
|
|
18
|
+
routeLoader.loadRoutes(server, './examples/mvc_routes_example/routes.json')
|
|
19
|
+
.then(routes => {
|
|
20
|
+
console.log(`${routes.length} rutas cargadas exitosamente`);
|
|
21
|
+
console.log('Rutas disponibles:');
|
|
22
|
+
console.log('- http://localhost:9001/ (Página de inicio)');
|
|
23
|
+
console.log('- http://localhost:9001/users (Lista de usuarios)');
|
|
24
|
+
console.log('- http://localhost:9001/profile?id=1 (Perfil de usuario)');
|
|
25
|
+
console.log('- http://localhost:9001/products (Catálogo de productos)');
|
|
26
|
+
|
|
27
|
+
// Iniciar el servidor
|
|
28
|
+
server.start(() => {
|
|
29
|
+
console.log('Servidor MVC con rutas JSON iniciado en http://localhost:9001');
|
|
30
|
+
});
|
|
31
|
+
})
|
|
32
|
+
.catch(error => {
|
|
33
|
+
console.error('Error al cargar las rutas:', error);
|
|
34
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const { ControllerBase } = require('../../../index');
|
|
2
|
+
|
|
3
|
+
class MainController extends ControllerBase {
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
super(options);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
index(req, res) {
|
|
9
|
+
this.set('title', 'Inicio - Ejemplo MVC con Routes');
|
|
10
|
+
this.set('message', '¡Bienvenido al ejemplo de MVC con definición de rutas!');
|
|
11
|
+
|
|
12
|
+
this.render(res, 'main/index', {
|
|
13
|
+
currentTime: new Date().toISOString(),
|
|
14
|
+
version: '2.3.1'
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Exportar métodos directamente para que el RouteLoader pueda acceder a ellos
|
|
20
|
+
const controllerInstance = new MainController({ viewsPath: './examples/mvc_routes_example/views' });
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
index: (req, res) => {
|
|
24
|
+
controllerInstance.setRequestResponse(req, res);
|
|
25
|
+
controllerInstance.index(req, res);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const { ControllerBase } = require('../../../index');
|
|
2
|
+
|
|
3
|
+
class ProductController extends ControllerBase {
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
super(options);
|
|
6
|
+
|
|
7
|
+
// Registrar helpers personalizados para este controlador
|
|
8
|
+
this.getViewEngine().addHelper('calculateDiscount', (price, discountPercent) => {
|
|
9
|
+
return price - (price * discountPercent / 100);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
this.getViewEngine().addHelper('formatCurrency', (value, symbol = '$') => {
|
|
13
|
+
return `${symbol}${Number(value).toFixed(2)}`;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
this.getViewEngine().addHelper('isOnSale', (product) => {
|
|
17
|
+
return product.discount > 0;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
catalog(req, res) {
|
|
22
|
+
const products = [
|
|
23
|
+
{ id: 1, name: 'Laptop Pro', price: 1200, discount: 10, category: 'Electronics' },
|
|
24
|
+
{ id: 2, name: 'Mouse Inalámbrico', price: 25, discount: 15, category: 'Accessories' },
|
|
25
|
+
{ id: 3, name: 'Teclado Mecánico', price: 80, discount: 0, category: 'Accessories' },
|
|
26
|
+
{ id: 4, name: 'Monitor 4K', price: 400, discount: 20, category: 'Electronics' },
|
|
27
|
+
{ id: 5, name: 'Auriculares', price: 150, discount: 5, category: 'Audio' }
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
this.set('title', 'Catálogo de Productos');
|
|
31
|
+
this.set('products', products);
|
|
32
|
+
|
|
33
|
+
this.render(res, 'product/catalog', {
|
|
34
|
+
currencySymbol: 'USD'
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Exportar métodos directamente para que el RouteLoader pueda acceder a ellos
|
|
40
|
+
const controllerInstance = new ProductController({ viewsPath: './examples/mvc_routes_example/views' });
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
catalog: (req, res) => {
|
|
44
|
+
controllerInstance.setRequestResponse(req, res);
|
|
45
|
+
controllerInstance.catalog(req, res);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const { ControllerBase } = require('../../../index');
|
|
2
|
+
|
|
3
|
+
class UserController extends ControllerBase {
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
super(options);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
list(req, res) {
|
|
9
|
+
const users = [
|
|
10
|
+
{ id: 1, name: 'Ana García', email: 'ana@example.com', registered: '2026-01-01', active: true },
|
|
11
|
+
{ id: 2, name: 'Carlos López', email: 'carlos@example.com', registered: '2026-01-02', active: false },
|
|
12
|
+
{ id: 3, name: 'Laura Martínez', email: 'laura@example.com', registered: '2026-01-03', active: true },
|
|
13
|
+
{ id: 4, name: 'Pedro Rodríguez', email: 'pedro@example.com', registered: '2026-01-04', active: true },
|
|
14
|
+
{ id: 5, name: 'María Sánchez', email: 'maria@example.com', registered: '2026-01-05', active: false }
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
this.set('title', 'Lista de Usuarios');
|
|
18
|
+
this.set('users', users);
|
|
19
|
+
|
|
20
|
+
this.render(res, 'user/list', {
|
|
21
|
+
totalUsers: users.length
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
profile(req, res) {
|
|
26
|
+
const userIdParam = this.input('id', '1');
|
|
27
|
+
|
|
28
|
+
// Validar que el ID sea un número entero positivo
|
|
29
|
+
if (!/^\d+$/.test(userIdParam) || parseInt(userIdParam) <= 0) {
|
|
30
|
+
this.set('title', 'ID de usuario inválido');
|
|
31
|
+
this.render(res, 'user/invalid', {
|
|
32
|
+
userId: userIdParam
|
|
33
|
+
});
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const userId = parseInt(userIdParam);
|
|
38
|
+
|
|
39
|
+
const users = [
|
|
40
|
+
{ id: 1, name: 'Ana García', email: 'ana@example.com', registered: '2026-01-01', active: true },
|
|
41
|
+
{ id: 2, name: 'Carlos López', email: 'carlos@example.com', registered: '2026-01-02', active: false },
|
|
42
|
+
{ id: 3, name: 'Laura Martínez', email: 'laura@example.com', registered: '2026-01-03', active: true },
|
|
43
|
+
{ id: 4, name: 'Pedro Rodríguez', email: 'pedro@example.com', registered: '2026-01-04', active: true },
|
|
44
|
+
{ id: 5, name: 'María Sánchez', email: 'maria@example.com', registered: '2026-01-05', active: false }
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
const user = users.find(u => u.id === userId);
|
|
48
|
+
|
|
49
|
+
if (!user) {
|
|
50
|
+
this.set('title', 'Usuario no encontrado');
|
|
51
|
+
this.render(res, 'user/notfound', {
|
|
52
|
+
userId: userId
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.set('title', `Perfil de ${user.name}`);
|
|
58
|
+
this.set('user', user);
|
|
59
|
+
|
|
60
|
+
this.render(res, 'user/profile');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Exportar métodos directamente para que el RouteLoader pueda acceder a ellos
|
|
65
|
+
const controllerInstance = new UserController({ viewsPath: './examples/mvc_routes_example/views' });
|
|
66
|
+
|
|
67
|
+
module.exports = {
|
|
68
|
+
list: (req, res) => {
|
|
69
|
+
controllerInstance.setRequestResponse(req, res);
|
|
70
|
+
controllerInstance.list(req, res);
|
|
71
|
+
},
|
|
72
|
+
profile: (req, res) => {
|
|
73
|
+
controllerInstance.setRequestResponse(req, res);
|
|
74
|
+
controllerInstance.profile(req, res);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"method": "GET",
|
|
4
|
+
"path": "/",
|
|
5
|
+
"controller": "./examples/mvc_routes_example/controllers/mainController.js",
|
|
6
|
+
"handler": "index",
|
|
7
|
+
"auth": "none"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"method": "GET",
|
|
11
|
+
"path": "/users",
|
|
12
|
+
"controller": "./examples/mvc_routes_example/controllers/userController.js",
|
|
13
|
+
"handler": "list",
|
|
14
|
+
"auth": "none"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"method": "GET",
|
|
18
|
+
"path": "/profile",
|
|
19
|
+
"controller": "./examples/mvc_routes_example/controllers/userController.js",
|
|
20
|
+
"handler": "profile",
|
|
21
|
+
"auth": "none"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"method": "GET",
|
|
25
|
+
"path": "/products",
|
|
26
|
+
"controller": "./examples/mvc_routes_example/controllers/productController.js",
|
|
27
|
+
"handler": "catalog",
|
|
28
|
+
"auth": "none"
|
|
29
|
+
}
|
|
30
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="es">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>{{title}}</title>
|
|
7
|
+
<style>
|
|
8
|
+
body { font-family: Arial, sans-serif; margin: 40px; background-color: #f5f5f5; }
|
|
9
|
+
.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); }
|
|
10
|
+
h1 { color: #333; }
|
|
11
|
+
nav { margin: 20px 0; }
|
|
12
|
+
nav a { margin-right: 15px; text-decoration: none; color: #007bff; }
|
|
13
|
+
nav a:hover { text-decoration: underline; }
|
|
14
|
+
</style>
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
<div class="container">
|
|
18
|
+
<h1>{{title}}</h1>
|
|
19
|
+
<p>{{message}}</p>
|
|
20
|
+
|
|
21
|
+
<nav>
|
|
22
|
+
<a href="/">Inicio</a>
|
|
23
|
+
<a href="/users">Usuarios</a>
|
|
24
|
+
<a href="/products">Productos</a>
|
|
25
|
+
</nav>
|
|
26
|
+
|
|
27
|
+
<p>Versión: {{version}}</p>
|
|
28
|
+
<p>Hora actual: {{currentTime}}</p>
|
|
29
|
+
</div>
|
|
30
|
+
</body>
|
|
31
|
+
</html>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<h1>{{title}}</h1>
|
|
2
|
+
|
|
3
|
+
{{if products}}
|
|
4
|
+
<div class="product-grid">
|
|
5
|
+
{{foreach:products}}
|
|
6
|
+
<div class="product-card">
|
|
7
|
+
<h3>{{item.name}}</h3>
|
|
8
|
+
<p>Categoría: {{item.category}}</p>
|
|
9
|
+
<p>Precio original: {{formatCurrency(item.price)}}</p>
|
|
10
|
+
<p>Descuento: {{item.discount}}%</p>
|
|
11
|
+
{{if item.discount > 0}}
|
|
12
|
+
<p>Precio con descuento: {{formatCurrency(calculateDiscount(item.price, item.discount))}}</p>
|
|
13
|
+
<p class="onsale">¡En oferta!</p>
|
|
14
|
+
{{else}}
|
|
15
|
+
<p class="regular">Precio regular</p>
|
|
16
|
+
{{endif}}
|
|
17
|
+
</div>
|
|
18
|
+
{{endforeach}}
|
|
19
|
+
</div>
|
|
20
|
+
{{else}}
|
|
21
|
+
<p>No hay productos disponibles.</p>
|
|
22
|
+
{{endif}}
|
|
23
|
+
|
|
24
|
+
<a href="/">Volver al inicio</a>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<h1>{{title}}</h1>
|
|
2
|
+
|
|
3
|
+
{{if users}}
|
|
4
|
+
<p>Total de usuarios: {{totalUsers}}</p>
|
|
5
|
+
|
|
6
|
+
<table>
|
|
7
|
+
<thead>
|
|
8
|
+
<tr>
|
|
9
|
+
<th>ID</th>
|
|
10
|
+
<th>Nombre</th>
|
|
11
|
+
<th>Email</th>
|
|
12
|
+
<th>Registrado</th>
|
|
13
|
+
<th>Estado</th>
|
|
14
|
+
<th>Acciones</th>
|
|
15
|
+
</tr>
|
|
16
|
+
</thead>
|
|
17
|
+
<tbody>
|
|
18
|
+
{{foreach:users}}
|
|
19
|
+
<tr>
|
|
20
|
+
<td>{{item.id}}</td>
|
|
21
|
+
<td>{{item.name}}</td>
|
|
22
|
+
<td>{{item.email}}</td>
|
|
23
|
+
<td>{{item.registered|date:'DD/MM/YYYY'}}</td>
|
|
24
|
+
<td>
|
|
25
|
+
{{if item.active}}
|
|
26
|
+
<span class="active">Activo</span>
|
|
27
|
+
{{else}}
|
|
28
|
+
<span class="inactive">Inactivo</span>
|
|
29
|
+
{{endif}}
|
|
30
|
+
</td>
|
|
31
|
+
<td><a href="/profile?id={{item.id}}">Ver Perfil</a></td>
|
|
32
|
+
</tr>
|
|
33
|
+
{{endforeach}}
|
|
34
|
+
</tbody>
|
|
35
|
+
</table>
|
|
36
|
+
{{else}}
|
|
37
|
+
<p>No hay usuarios registrados.</p>
|
|
38
|
+
{{endif}}
|
|
39
|
+
|
|
40
|
+
<a href="/">Volver al inicio</a>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<h1>{{title}}</h1>
|
|
2
|
+
|
|
3
|
+
<div>
|
|
4
|
+
<h2>Información del Usuario</h2>
|
|
5
|
+
<p><strong>ID:</strong> {{user.id}}</p>
|
|
6
|
+
<p><strong>Nombre:</strong> {{user.name}}</p>
|
|
7
|
+
<p><strong>Email:</strong> {{user.email}}</p>
|
|
8
|
+
<p><strong>Registrado:</strong> {{user.registered|date:'DD/MM/YYYY'}}</p>
|
|
9
|
+
<p><strong>Estado:</strong>
|
|
10
|
+
{{if user.active}}
|
|
11
|
+
<span class="active">Activo</span>
|
|
12
|
+
{{else}}
|
|
13
|
+
<span class="inactive">Inactivo</span>
|
|
14
|
+
{{endif}}
|
|
15
|
+
</p>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<a href="/users">Volver a la lista de usuarios</a>
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# API Pública de Ejemplo
|
|
2
|
+
|
|
3
|
+
Este ejemplo demuestra cómo crear una API pública usando el Framework API SDK JS. La API incluye endpoints para gestionar usuarios y está protegida con CORS y rate limiting.
|
|
4
|
+
|
|
5
|
+
## Características
|
|
6
|
+
|
|
7
|
+
- API REST pública sin autenticación requerida
|
|
8
|
+
- Endpoints para gestión de usuarios (CRUD completo)
|
|
9
|
+
- Protección con CORS para permitir solicitudes desde cualquier origen
|
|
10
|
+
- Rate limiting para prevenir abusos
|
|
11
|
+
- Endpoints de salud del sistema
|
|
12
|
+
- Controladores organizados por funcionalidad
|
|
13
|
+
|
|
14
|
+
## Configuración
|
|
15
|
+
|
|
16
|
+
1. Asegúrate de tener instaladas las dependencias del framework API SDK
|
|
17
|
+
2. No se requiere configuración adicional para esta API pública
|
|
18
|
+
|
|
19
|
+
## Uso
|
|
20
|
+
|
|
21
|
+
1. Inicia el servidor:
|
|
22
|
+
```bash
|
|
23
|
+
node app.js
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
2. El servidor escuchará en `http://localhost:8081`
|
|
27
|
+
|
|
28
|
+
## Endpoints
|
|
29
|
+
|
|
30
|
+
- `GET /` - Información general de la API (público)
|
|
31
|
+
- `GET /users` - Lista todos los usuarios (público)
|
|
32
|
+
- `GET /users/:id` - Obtiene un usuario específico (público)
|
|
33
|
+
- `POST /users` - Crea un nuevo usuario (público)
|
|
34
|
+
- `PUT /users/:id` - Actualiza un usuario existente (público)
|
|
35
|
+
- `DELETE /users/:id` - Elimina un usuario (público)
|
|
36
|
+
- `GET /health` - Verifica el estado del servicio (público)
|
|
37
|
+
|
|
38
|
+
## Ejemplos de uso
|
|
39
|
+
|
|
40
|
+
1. Obtener todos los usuarios:
|
|
41
|
+
```bash
|
|
42
|
+
curl -X GET http://localhost:8081/users
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
2. Obtener un usuario específico:
|
|
46
|
+
```bash
|
|
47
|
+
curl -X GET http://localhost:8081/users/1
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
3. Crear un nuevo usuario:
|
|
51
|
+
```bash
|
|
52
|
+
curl -X POST http://localhost:8081/users \
|
|
53
|
+
-H "Content-Type: application/json" \
|
|
54
|
+
-d '{"name": "Nuevo Usuario", "email": "nuevo@ejemplo.com", "age": 28}'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
4. Actualizar un usuario:
|
|
58
|
+
```bash
|
|
59
|
+
curl -X PUT http://localhost:8081/users/1 \
|
|
60
|
+
-H "Content-Type: application/json" \
|
|
61
|
+
-d '{"name": "Usuario Actualizado", "email": "actualizado@ejemplo.com"}'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
5. Eliminar un usuario:
|
|
65
|
+
```bash
|
|
66
|
+
curl -X DELETE http://localhost:8081/users/1
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
6. Verificar estado del servicio:
|
|
70
|
+
```bash
|
|
71
|
+
curl -X GET http://localhost:8081/health
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Estructura del proyecto
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
examples/public/
|
|
78
|
+
├── app.js # Punto de entrada de la aplicación
|
|
79
|
+
├── routes.json # Definición de rutas
|
|
80
|
+
├── controllers/ # Controladores de las rutas
|
|
81
|
+
│ ├── mainController.js
|
|
82
|
+
│ ├── userController.js
|
|
83
|
+
│ └── healthController.js
|
|
84
|
+
└── README.md
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Seguridad
|
|
88
|
+
|
|
89
|
+
- CORS configurado para permitir solicitudes desde cualquier origen
|
|
90
|
+
- Rate limiting configurado para prevenir abusos (100 solicitudes por 15 minutos)
|
|
91
|
+
- No se requiere autenticación para acceder a los endpoints
|
|
92
|
+
- Validación de entrada en endpoints de creación/actualización
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
const {
|
|
2
|
+
APIServer,
|
|
3
|
+
Router,
|
|
4
|
+
RouteLoader,
|
|
5
|
+
Logger,
|
|
6
|
+
Cors,
|
|
7
|
+
RateLimiter
|
|
8
|
+
} = require('../../index.js');
|
|
9
|
+
|
|
10
|
+
async function startServer() {
|
|
11
|
+
// Crear instancia del servidor
|
|
12
|
+
const server = new APIServer({
|
|
13
|
+
port: 8081,
|
|
14
|
+
host: 'localhost',
|
|
15
|
+
requestTimeout: 30000, // 30 segundos
|
|
16
|
+
connectionTimeout: 30000 // 30 segundos
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Crear instancia del logger
|
|
20
|
+
const logger = new Logger({ level: 'info' });
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
// Configurar CORS para permitir solicitudes desde cualquier origen
|
|
24
|
+
const cors = new Cors({
|
|
25
|
+
origin: '*',
|
|
26
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
27
|
+
allowedHeaders: [
|
|
28
|
+
'Content-Type',
|
|
29
|
+
'Authorization',
|
|
30
|
+
'X-Requested-With',
|
|
31
|
+
'Accept',
|
|
32
|
+
'Origin'
|
|
33
|
+
]
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Aplicar middleware de CORS
|
|
37
|
+
server.use(cors.middleware());
|
|
38
|
+
|
|
39
|
+
// Configurar Rate Limiter para prevenir abusos
|
|
40
|
+
const rateLimiter = new RateLimiter({
|
|
41
|
+
windowMs: 15 * 60 * 1000, // 15 minutos
|
|
42
|
+
maxRequests: 100 // Límite de 100 solicitudes por ventana
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Aplicar middleware de rate limiting
|
|
46
|
+
server.use(rateLimiter.middleware());
|
|
47
|
+
|
|
48
|
+
// Cargar rutas desde archivo JSON
|
|
49
|
+
const routeLoader = new RouteLoader();
|
|
50
|
+
await routeLoader.loadRoutes(server, './routes.json');
|
|
51
|
+
|
|
52
|
+
// Iniciar el servidor
|
|
53
|
+
server.start();
|
|
54
|
+
|
|
55
|
+
logger.info('Servidor público iniciado en http://localhost:8081');
|
|
56
|
+
logger.info('Endpoints disponibles:');
|
|
57
|
+
logger.info('- GET / (Información del API)');
|
|
58
|
+
logger.info('- GET /users (Lista de usuarios)');
|
|
59
|
+
logger.info('- GET /users/:id (Obtener usuario específico)');
|
|
60
|
+
logger.info('- POST /users (Crear nuevo usuario)');
|
|
61
|
+
logger.info('- PUT /users/:id (Actualizar usuario)');
|
|
62
|
+
logger.info('- DELETE /users/:id (Eliminar usuario)');
|
|
63
|
+
} catch (error) {
|
|
64
|
+
logger.error('Error iniciando el servidor:', error.message);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Iniciar el servidor
|
|
70
|
+
startServer();
|
|
71
|
+
|
|
72
|
+
module.exports = { startServer };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const healthController = {
|
|
2
|
+
checkHealth: (req, res) => {
|
|
3
|
+
const healthStatus = {
|
|
4
|
+
status: 'healthy',
|
|
5
|
+
timestamp: new Date().toISOString(),
|
|
6
|
+
uptime: process.uptime(),
|
|
7
|
+
message: 'API Pública de Ejemplo está operando correctamente',
|
|
8
|
+
version: '1.0.0',
|
|
9
|
+
endpoints: {
|
|
10
|
+
total: 7,
|
|
11
|
+
active: true
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
16
|
+
res.end(JSON.stringify(healthStatus));
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
module.exports = healthController;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const mainController = {
|
|
2
|
+
home: (req, res) => {
|
|
3
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
4
|
+
res.end(JSON.stringify({
|
|
5
|
+
message: 'Bienvenido a la API Pública de Ejemplo',
|
|
6
|
+
version: '1.0.0',
|
|
7
|
+
description: 'Esta es una API pública de ejemplo creada con el Framework API SDK JS',
|
|
8
|
+
endpoints: {
|
|
9
|
+
'GET /': 'Este mensaje',
|
|
10
|
+
'GET /users': 'Obtener lista de usuarios',
|
|
11
|
+
'GET /users/:id': 'Obtener usuario específico',
|
|
12
|
+
'POST /users': 'Crear nuevo usuario',
|
|
13
|
+
'PUT /users/:id': 'Actualizar usuario',
|
|
14
|
+
'DELETE /users/:id': 'Eliminar usuario',
|
|
15
|
+
'GET /health': 'Verificar estado del servicio'
|
|
16
|
+
},
|
|
17
|
+
documentation: 'Visita el README para más información sobre cómo usar esta API'
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
module.exports = mainController;
|