jerkjs 2.1.7 → 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.
Files changed (54) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +201 -4
  3. package/index.js +29 -4
  4. package/lib/core/server.js +328 -27
  5. package/lib/loader/routeLoader.js +148 -117
  6. package/lib/mvc/GenericAdapter.js +136 -0
  7. package/lib/mvc/MariaDBAdapter.js +315 -0
  8. package/lib/mvc/MemoryAdapter.js +269 -0
  9. package/lib/mvc/ModelControllerExample.js +285 -0
  10. package/lib/mvc/controllerBase.js +60 -0
  11. package/lib/mvc/modelBase.js +383 -0
  12. package/lib/mvc/modelManager.js +284 -0
  13. package/lib/mvc/userModel.js +265 -0
  14. package/lib/mvc/viewEngine.js +32 -1
  15. package/lib/utils/mimeType.js +62 -0
  16. package/package.json +5 -3
  17. package/BUG_REPORTE_COMPRESION.txt +0 -72
  18. package/JERK_FRAMEWORK_DIAGRAM.txt +0 -492
  19. package/JERK_FRAMEWORK_DIAGRAM_MERMAID.mmd +0 -124
  20. package/JERK_FRAMEWORK_DOCUMENTATION.md +0 -527
  21. package/LICENSE +0 -201
  22. package/README_EN.md +0 -230
  23. package/README_PT.md +0 -230
  24. package/docs/ARQUITECTURA_ROUTES.md +0 -140
  25. package/docs/EXTENSION_MANUAL.md +0 -955
  26. package/docs/FIREWALL_MANUAL.md +0 -416
  27. package/docs/HOOK-2.0.md +0 -512
  28. package/docs/HOOKS_REFERENCE_IMPROVED.md +0 -596
  29. package/docs/MANUAL_API_SDK.md +0 -536
  30. package/docs/MARIADB_TOKENS_IMPLEMENTATION.md +0 -110
  31. package/docs/MIDDLEWARE_MANUAL.md +0 -518
  32. package/docs/OAUTH2_GOOGLE_MANUAL.md +0 -405
  33. package/docs/ROUTING_WITHOUT_JSON_GUIDE.md +0 -454
  34. package/docs/frontend-and-sessions.md +0 -353
  35. package/docs/guia_inicio_rapido_jerkjs.md +0 -113
  36. package/examples/examples.arj +0 -0
  37. package/standard/CompressionTestController.js +0 -56
  38. package/standard/HealthController.js +0 -16
  39. package/standard/HomeController.js +0 -12
  40. package/standard/ProductController.js +0 -18
  41. package/standard/README.md +0 -47
  42. package/standard/UserController.js +0 -23
  43. package/standard/package.json +0 -22
  44. package/standard/routes.json +0 -65
  45. package/standard/server.js +0 -140
  46. package/standardA/controllers/AuthController.js +0 -82
  47. package/standardA/controllers/HomeController.js +0 -19
  48. package/standardA/controllers/UserController.js +0 -41
  49. package/standardA/server.js +0 -311
  50. package/standardA/views/auth/dashboard.html +0 -51
  51. package/standardA/views/auth/login.html +0 -47
  52. package/standardA/views/index.html +0 -32
  53. package/standardA/views/users/detail.html +0 -28
  54. package/standardA/views/users/list.html +0 -36
@@ -1,536 +0,0 @@
1
- # Manual para Construir APIs con el Framework API SDK
2
-
3
- ## Índice
4
- 1. [Introducción](#introducción)
5
- 2. [Instalación y Configuración](#instalación-y-configuración)
6
- 3. [Conceptos Fundamentales](#conceptos-fundamentales)
7
- 4. [Creación de tu Primera API](#creación-de-tu-primera-api)
8
- 5. [Enrutamiento Avanzado](#enrutamiento-avanzado)
9
- 6. [Middleware y Seguridad](#middleware-y-seguridad)
10
- 7. [Gestión de Tokens](#gestión-de-tokens)
11
- 8. [Documentación Automática](#documentación-automática)
12
- 9. [Carga de Controladores y Rutas](#carga-de-controladores-y-rutas)
13
- 10. [Mejores Prácticas](#mejores-prácticas)
14
-
15
- ## Introducción
16
-
17
- El Framework API SDK es una solución completa para construir APIs RESTful con características avanzadas de seguridad, rendimiento y mantenibilidad. Proporciona una arquitectura modular que facilita la creación de servicios web robustos y escalables.
18
-
19
- ## Instalación y Configuración
20
-
21
- Para comenzar a usar el framework, primero debes instalarlo como dependencia:
22
-
23
- ```javascript
24
- const {
25
- APIServer,
26
- Router,
27
- Authenticator,
28
- Validator,
29
- Cors,
30
- RateLimiter,
31
- Logger,
32
- TokenManager,
33
- OpenApiGenerator
34
- } = require('@apisdkjs');
35
- ```
36
-
37
- ## Conceptos Fundamentales
38
-
39
- ### Componentes Principales
40
-
41
- - **APIServer**: El servidor HTTP central que maneja todas las solicitudes
42
- - **Router**: Sistema de enrutamiento para definir endpoints
43
- - **Authenticator**: Sistema de autenticación con múltiples estrategias
44
- - **Validator**: Validación de datos de entrada
45
- - **Middleware**: Componentes que procesan solicitudes/responses
46
-
47
- ### Flujo de Trabajo Básico
48
-
49
- 1. Crear una instancia del servidor
50
- 2. Definir rutas y handlers
51
- 3. Aplicar middleware según sea necesario
52
- 4. Iniciar el servidor
53
-
54
- ## Creación de tu Primera API
55
-
56
- ### Servidor Básico
57
-
58
- ```javascript
59
- const { APIServer, Router, Logger } = require('@apisdkjs');
60
-
61
- // Crear instancia del servidor
62
- const server = new APIServer({
63
- port: 3000,
64
- host: 'localhost'
65
- });
66
-
67
- // Crear instancia del router
68
- const router = new Router();
69
-
70
- // Definir rutas
71
- router.get('/', (req, res) => {
72
- res.writeHead(200, { 'Content-Type': 'application/json' });
73
- res.end(JSON.stringify({ message: '¡Hola Mundo!' }));
74
- });
75
-
76
- router.get('/users/:id', (req, res) => {
77
- const userId = req.params.id;
78
- res.writeHead(200, { 'Content-Type': 'application/json' });
79
- res.end(JSON.stringify({ id: userId, name: 'Usuario Ejemplo' }));
80
- });
81
-
82
- // Agregar rutas al servidor
83
- for (const route of router.getRoutes()) {
84
- server.addRoute(route.method, route.path, route.handler);
85
- }
86
-
87
- // Iniciar el servidor
88
- server.start();
89
- ```
90
-
91
- ### Controladores
92
-
93
- Los controladores son funciones que manejan la lógica de negocio para cada endpoint:
94
-
95
- ```javascript
96
- // controllers/userController.js
97
- const userController = {
98
- // GET /users
99
- getAllUsers: (req, res) => {
100
- const users = [
101
- { id: 1, name: 'Juan', email: 'juan@example.com' },
102
- { id: 2, name: 'María', email: 'maria@example.com' }
103
- ];
104
-
105
- res.writeHead(200, { 'Content-Type': 'application/json' });
106
- res.end(JSON.stringify(users));
107
- },
108
-
109
- // GET /users/:id
110
- getUserById: (req, res) => {
111
- const userId = parseInt(req.params.id);
112
- const user = { id: userId, name: 'Usuario Ejemplo', email: 'user@example.com' };
113
-
114
- if (!user) {
115
- res.writeHead(404, { 'Content-Type': 'application/json' });
116
- res.end(JSON.stringify({ error: 'Usuario no encontrado' }));
117
- return;
118
- }
119
-
120
- res.writeHead(200, { 'Content-Type': 'application/json' });
121
- res.end(JSON.stringify(user));
122
- },
123
-
124
- // POST /users
125
- createUser: (req, res) => {
126
- const userData = req.body;
127
- const newUser = {
128
- id: Date.now(),
129
- ...userData
130
- };
131
-
132
- res.writeHead(201, { 'Content-Type': 'application/json' });
133
- res.end(JSON.stringify(newUser));
134
- }
135
- };
136
-
137
- module.exports = userController;
138
- ```
139
-
140
- ## Enrutamiento Avanzado
141
-
142
- ### Rutas Parametrizadas
143
-
144
- El framework soporta rutas con parámetros:
145
-
146
- ```javascript
147
- const router = new Router();
148
-
149
- // Ruta con un parámetro
150
- router.get('/users/:id', (req, res) => {
151
- const userId = req.params.id;
152
- // Procesar solicitud
153
- });
154
-
155
- // Ruta con múltiples parámetros
156
- router.get('/users/:userId/posts/:postId', (req, res) => {
157
- const { userId, postId } = req.params;
158
- // Procesar solicitud
159
- });
160
- ```
161
-
162
- ### Rutas Anidadas
163
-
164
- Puedes combinar routers para organizar mejor tu API:
165
-
166
- ```javascript
167
- const mainRouter = new Router();
168
- const userRouter = new Router({ prefix: '/users' });
169
- const postRouter = new Router({ prefix: '/posts' });
170
-
171
- // Definir rutas para usuarios
172
- userRouter.get('/', (req, res) => {
173
- // Obtener todos los usuarios
174
- });
175
- userRouter.get('/:id', (req, res) => {
176
- // Obtener usuario por ID
177
- });
178
-
179
- // Definir rutas para posts
180
- postRouter.get('/', (req, res) => {
181
- // Obtener todos los posts
182
- });
183
- postRouter.get('/:id', (req, res) => {
184
- // Obtener post por ID
185
- });
186
-
187
- // Agregar routers anidados al router principal
188
- mainRouter.addNestedRouter('/api/v1', userRouter);
189
- mainRouter.addNestedRouter('/api/v1', postRouter);
190
- ```
191
-
192
- ## Middleware y Seguridad
193
-
194
- ### CORS
195
-
196
- Configura CORS para permitir solicitudes cross-origin:
197
-
198
- ```javascript
199
- const cors = new Cors({
200
- origin: ['http://localhost:3000', 'https://myapp.com'],
201
- methods: ['GET', 'POST', 'PUT', 'DELETE'],
202
- allowedHeaders: ['Content-Type', 'Authorization', 'X-API-Key']
203
- });
204
-
205
- server.use(cors.middleware());
206
- ```
207
-
208
- ### Rate Limiting
209
-
210
- Protege tu API contra abusos con limitación de tasa:
211
-
212
- ```javascript
213
- const rateLimiter = new RateLimiter({
214
- windowMs: 15 * 60 * 1000, // 15 minutos
215
- maxRequests: 100 // Límite de 100 solicitudes por ventana
216
- });
217
-
218
- // Aplicar a todo el servidor
219
- server.use(rateLimiter.middleware());
220
-
221
- // O aplicar a rutas específicas
222
- router.post('/login', rateLimiter.middleware(), (req, res) => {
223
- // Lógica de login
224
- });
225
- ```
226
-
227
- ### Autenticación
228
-
229
- El framework soporta múltiples estrategias de autenticación:
230
-
231
- ```javascript
232
- const authenticator = new Authenticator();
233
-
234
- // Estrategia JWT
235
- const jwtStrategy = authenticator.jwtStrategy('tu_secreto_jwt');
236
- authenticator.use('jwt', jwtStrategy);
237
-
238
- // Estrategia API Key
239
- const apiKeyStrategy = authenticator.apiKeyStrategy('X-API-Key', ['clave1', 'clave2']);
240
- authenticator.use('apiKey', apiKeyStrategy);
241
-
242
- // Aplicar autenticación a rutas
243
- router.get('/protected', authenticator.authenticate('jwt'), (req, res) => {
244
- // Ruta protegida - req.user contendrá la información del usuario
245
- res.writeHead(200, { 'Content-Type': 'application/json' });
246
- res.end(JSON.stringify({ message: 'Contenido protegido', user: req.user }));
247
- });
248
- ```
249
-
250
- ### Validación
251
-
252
- Valida los datos de entrada de tus endpoints:
253
-
254
- ```javascript
255
- const validator = new Validator();
256
-
257
- const userValidationSchema = {
258
- body: {
259
- name: ['required', 'string', 'minLength:2'],
260
- email: ['required', 'email'],
261
- age: ['required', 'number', 'min:18', 'max:120']
262
- }
263
- };
264
-
265
- router.post('/users',
266
- validator.validate(userValidationSchema),
267
- (req, res) => {
268
- // Si llega aquí, la validación pasó
269
- const userData = req.body;
270
- // Procesar solicitud
271
- }
272
- );
273
- ```
274
-
275
- ## Gestión de Tokens
276
-
277
- ### TokenManager
278
-
279
- El framework incluye un sistema completo de gestión de tokens:
280
-
281
- ```javascript
282
- const tokenManager = new TokenManager({
283
- storage: 'memory' // Opciones: 'memory', 'json', 'database'
284
- });
285
-
286
- // Generar un token
287
- const payload = { userId: 123, role: 'admin' };
288
- const token = tokenManager.generateToken(payload, 'tu_secreto', '1h');
289
-
290
- // Validar un token
291
- const decoded = tokenManager.validateToken(token, 'tu_secreto');
292
- if (decoded) {
293
- console.log('Token válido:', decoded);
294
- } else {
295
- console.log('Token inválido');
296
- }
297
-
298
- // Generar par de tokens (access y refresh)
299
- const tokenPair = tokenManager.generateTokenPair(
300
- { userId: 123, role: 'admin' },
301
- {
302
- jwtSecret: 'access_secret',
303
- refreshSecret: 'refresh_secret',
304
- accessExpiresIn: '15m',
305
- refreshExpiresIn: '7d'
306
- }
307
- );
308
- ```
309
-
310
- ### Adaptadores de Base de Datos
311
-
312
- Para almacenamiento persistente de tokens, puedes usar adaptadores:
313
-
314
- ```javascript
315
- const MariaDBTokenAdapter = require('@apisdkjs/lib/utils/mariadbTokenAdapter');
316
- const SQLiteTokenAdapter = require('@apisdkjs/lib/utils/sqliteTokenAdapter');
317
-
318
- // Usar MariaDB
319
- const dbAdapter = new MariaDBTokenAdapter({
320
- host: 'localhost',
321
- user: 'usuario',
322
- password: 'contraseña',
323
- database: 'mi_bd'
324
- });
325
-
326
- await dbAdapter.initialize();
327
-
328
- // Guardar token en base de datos
329
- await dbAdapter.saveToken(token, { userId: 123 }, 'access', new Date(Date.now() + 3600000));
330
- ```
331
-
332
- ## Documentación Automática
333
-
334
- ### OpenAPI Generator
335
-
336
- Genera documentación OpenAPI automáticamente:
337
-
338
- ```javascript
339
- const openApiGenerator = new OpenApiGenerator({
340
- title: 'Mi API',
341
- description: 'Documentación para Mi API',
342
- version: '1.0.0'
343
- });
344
-
345
- // Agregar rutas a la documentación
346
- openApiGenerator.addRoute({
347
- path: '/users',
348
- method: 'GET',
349
- config: {
350
- summary: 'Obtener todos los usuarios',
351
- description: 'Devuelve una lista de todos los usuarios registrados',
352
- responses: {
353
- '200': {
354
- description: 'Lista de usuarios',
355
- content: {
356
- 'application/json': {
357
- schema: {
358
- type: 'array',
359
- items: {
360
- type: 'object',
361
- properties: {
362
- id: { type: 'integer' },
363
- name: { type: 'string' },
364
- email: { type: 'string' }
365
- }
366
- }
367
- }
368
- }
369
- }
370
- }
371
- }
372
- }
373
- });
374
-
375
- // Agregar ruta de documentación al servidor
376
- openApiGenerator.addDocumentationRoute(server);
377
- ```
378
-
379
- ## Carga de Controladores y Rutas
380
-
381
- ### Carga Dinámica de Controladores
382
-
383
- Carga controladores desde archivos:
384
-
385
- ```javascript
386
- const { ControllerLoader } = require('@apisdkjs');
387
-
388
- const controllerLoader = new ControllerLoader();
389
-
390
- // Cargar un controlador específico
391
- const userController = controllerLoader.loadController('./controllers/userController.js');
392
-
393
- // Cargar todos los controladores de un directorio
394
- const controllers = controllerLoader.loadControllersFromDirectory('./controllers');
395
-
396
- // Obtener un handler específico de un controlador
397
- const getUserHandler = controllerLoader.getHandlerFromController(
398
- './controllers/userController.js',
399
- 'getUserById'
400
- );
401
- ```
402
-
403
- ### Carga de Rutas desde JSON
404
-
405
- Define rutas en archivos JSON y cárgalas dinámicamente:
406
-
407
- ```javascript
408
- // routes.json
409
- [
410
- {
411
- "path": "/users",
412
- "method": "GET",
413
- "controller": "./controllers/userController.js",
414
- "handler": "getAllUsers",
415
- "auth": "jwt"
416
- },
417
- {
418
- "path": "/users/:id",
419
- "method": "GET",
420
- "controller": "./controllers/userController.js",
421
- "handler": "getUserById",
422
- "auth": "jwt"
423
- },
424
- {
425
- "path": "/users",
426
- "method": "POST",
427
- "controller": "./controllers/userController.js",
428
- "handler": "createUser",
429
- "auth": "apiKey"
430
- }
431
- ]
432
- ```
433
-
434
- ```javascript
435
- const { RouteLoader } = require('@apisdkjs');
436
-
437
- const routeLoader = new RouteLoader();
438
- const server = new APIServer({ port: 3000 });
439
-
440
- // Cargar rutas desde archivo JSON
441
- await routeLoader.loadRoutes(server, './routes.json');
442
- ```
443
-
444
- ## Mejores Prácticas
445
-
446
- ### Organización del Código
447
-
448
- ```
449
- proyecto/
450
- ├── controllers/
451
- │ ├── userController.js
452
- │ └── postController.js
453
- ├── middleware/
454
- │ └── customMiddleware.js
455
- ├── routes/
456
- │ └── routes.json
457
- ├── utils/
458
- │ └── helpers.js
459
- ├── config/
460
- │ └── config.json
461
- └── app.js
462
- ```
463
-
464
- ### Manejo de Errores
465
-
466
- Implementa un middleware de manejo de errores global:
467
-
468
- ```javascript
469
- const errorHandler = (err, req, res, next) => {
470
- console.error('Error:', err);
471
-
472
- res.writeHead(500, { 'Content-Type': 'application/json' });
473
- res.end(JSON.stringify({
474
- error: 'Error interno del servidor',
475
- message: process.env.NODE_ENV === 'development' ? err.message : undefined
476
- }));
477
- };
478
-
479
- server.use(errorHandler);
480
- ```
481
-
482
- ### Logging
483
-
484
- Utiliza el sistema de logging del framework:
485
-
486
- ```javascript
487
- const logger = new Logger({ level: 'info' });
488
-
489
- // En tus handlers
490
- router.get('/users/:id', (req, res) => {
491
- logger.info(`Solicitud recibida para usuario ID: ${req.params.id}`);
492
-
493
- // Lógica del handler
494
- logger.info(`Usuario ${req.params.id} recuperado exitosamente`);
495
- });
496
- ```
497
-
498
- ### Configuración
499
-
500
- Usa el ConfigParser para manejar configuraciones:
501
-
502
- ```javascript
503
- const { ConfigParser } = require('@apisdkjs');
504
-
505
- const configParser = new ConfigParser();
506
-
507
- // Cargar desde archivo
508
- configParser.loadFromFile('./config.json');
509
-
510
- // Cargar desde variables de entorno
511
- configParser.loadFromEnv(process.env, {
512
- 'dbHost': 'DB_HOST',
513
- 'dbPort': 'DB_PORT',
514
- 'jwtSecret': 'JWT_SECRET'
515
- });
516
-
517
- // Obtener valores de configuración
518
- const dbHost = configParser.get('dbHost', 'localhost');
519
- const jwtSecret = configParser.get('jwtSecret');
520
- ```
521
-
522
- ### Seguridad Adicional
523
-
524
- Considera implementar auditoría de seguridad:
525
-
526
- ```javascript
527
- const auditLogger = new AuditLogger({
528
- logFile: './security-audit.log',
529
- events: ['request', 'response', 'error'],
530
- includeHeaders: true
531
- });
532
-
533
- server.use(auditLogger.middleware());
534
- ```
535
-
536
- Este manual proporciona una guía completa para construir APIs robustas y seguras con el Framework API SDK. Recuerda siempre seguir las mejores prácticas de seguridad y mantener tu código actualizado.
@@ -1,110 +0,0 @@
1
- # Implementación Completa: API SDK Framework v2.0 con Tokens en MariaDB
2
-
3
- ## Resumen Ejecutivo
4
-
5
- Hemos implementado exitosamente una solución completa de gestión de tokens con MariaDB para el API SDK Framework v2.0, demostrando:
6
-
7
- ✅ **Conexión funcional a MariaDB**
8
- ✅ **Almacenamiento seguro de tokens JWT**
9
- ✅ **Validación en tiempo real contra base de datos**
10
- ✅ **Revocación de tokens**
11
- ✅ **Soporte para tokens de acceso y refresh**
12
- ✅ **Gestión de expiración automática**
13
-
14
- ## Componentes Implementados
15
-
16
- ### 1. Adaptador de Tokens para MariaDB (`lib/utils/mariadbTokenAdapter.js`)
17
- - Conexión robusta a MariaDB usando pooling
18
- - Almacenamiento seguro de tokens con índices
19
- - Validación en tiempo real
20
- - Revocación de tokens
21
- - Gestión de expiración
22
-
23
- ### 2. Base de Datos
24
- - Base de datos `token_api_db` creada
25
- - Tabla `tokens` con estructura optimizada:
26
- - `id`: Identificador único
27
- - `token`: Token JWT almacenado
28
- - `user_id`: ID del usuario propietario
29
- - `token_type`: Tipo (access/refresh)
30
- - `expires_at`: Fecha de expiración
31
- - `revoked`: Indicador de revocación
32
-
33
- ### 3. Funcionalidades Clave
34
-
35
- #### Almacenamiento Seguro
36
- ```sql
37
- -- Tokens almacenados encriptados en base de datos
38
- INSERT INTO tokens (token, user_id, token_type, expires_at) VALUES (?, ?, ?, ?);
39
- ```
40
-
41
- #### Validación en Tiempo Real
42
- ```javascript
43
- // Validación instantánea contra la base de datos
44
- const tokenRecord = await tokenAdapter.validateToken(token);
45
- ```
46
-
47
- #### Revocación Inmediata
48
- ```javascript
49
- // Revocación que se refleja inmediatamente
50
- await tokenAdapter.revokeToken(token);
51
- ```
52
-
53
- #### Expiración Automática
54
- ```sql
55
- -- Consultas que consideran tokens expirados
56
- WHERE expires_at > NOW() AND revoked = FALSE
57
- ```
58
-
59
- ## Pruebas Realizadas
60
-
61
- ### 1. Prueba de Conexión
62
- - ✅ Conexión estable a MariaDB
63
- - ✅ Creación automática de base de datos y tablas
64
-
65
- ### 2. Prueba de Almacenamiento
66
- - ✅ Almacenamiento de tokens JWT
67
- - ✅ Diferenciación entre access y refresh tokens
68
-
69
- ### 3. Prueba de Validación
70
- - ✅ Validación correcta de tokens válidos
71
- - ✅ Rechazo de tokens inexistentes
72
-
73
- ### 4. Prueba de Expiración
74
- - ✅ Detección de tokens expirados
75
- - ✅ No validez de tokens fuera de tiempo
76
-
77
- ### 5. Prueba de Revocación
78
- - ✅ Revocación efectiva de tokens
79
- - ✅ No validez posterior a la revocación
80
-
81
- ## Beneficios de la Solución
82
-
83
- ### Seguridad
84
- - Tokens almacenados en base de datos en lugar de memoria
85
- - Posibilidad de revocación inmediata
86
- - Validación en tiempo real
87
-
88
- ### Escalabilidad
89
- - Uso de connection pooling
90
- - Índices para búsquedas rápidas
91
- - Gestión eficiente de recursos
92
-
93
- ### Control
94
- - Visibilidad completa de tokens activos
95
- - Auditoría de tokens por usuario
96
- - Gestión centralizada
97
-
98
- ## Uso en Aplicaciones Reales
99
-
100
- La implementación permite:
101
-
102
- 1. **Login seguro** con generación de tokens almacenados en MariaDB
103
- 2. **Acceso protegido** con validación contra base de datos
104
- 3. **Renovación automática** de tokens expirados
105
- 4. **Revocación inmediata** de tokens comprometidos
106
- 5. **Auditoría completa** de tokens por usuario
107
-
108
- ## Conclusión
109
-
110
- La implementación de tokens en MariaDB para el API SDK Framework v2.0 está **completa, funcional y lista para producción**, ofreciendo un nivel superior de seguridad y control sobre la autenticación basada en tokens.