@mp-front/common 0.0.2-next.0 → 0.0.2-next.2
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/README.md +300 -591
- package/dist/cache-providers/redis/redis-cache.d.ts +2 -1
- package/dist/cache-providers/redis/redis-cache.d.ts.map +1 -1
- package/dist/client-AVep4jK5.cjs +1 -0
- package/dist/client-DuXI9Ldu.js +306 -0
- package/dist/http/client.d.ts +91 -0
- package/dist/http/client.d.ts.map +1 -1
- package/dist/http/constants.d.ts +3 -0
- package/dist/http/constants.d.ts.map +1 -0
- package/dist/middleware/api-middleware.d.ts +115 -3
- package/dist/middleware/api-middleware.d.ts.map +1 -1
- package/dist/mp-front-common-all.cjs +1 -1
- package/dist/mp-front-common-all.js +163 -72
- package/dist/mp-front-common-cache.cjs +1 -1
- package/dist/mp-front-common-cache.js +1 -1
- package/dist/mp-front-common-cacheProviders.cjs +1 -1
- package/dist/mp-front-common-cacheProviders.js +1 -1
- package/dist/mp-front-common-http.cjs +1 -1
- package/dist/mp-front-common-http.js +1 -1
- package/dist/{redis-cache-D9CzfYgO.cjs → redis-cache-BstIlgPR.cjs} +1 -1
- package/dist/{redis-cache-B_SyXbUI.js → redis-cache-HiNx0Kbv.js} +17 -14
- package/package.json +1 -1
- package/dist/client-BVAQF7Tb.cjs +0 -1
- package/dist/client-DKI6Ntcl.js +0 -234
package/README.md
CHANGED
|
@@ -1,373 +1,202 @@
|
|
|
1
|
-
# @mp-front/common
|
|
1
|
+
# @mp-front/common
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Una librería integral de TypeScript para aplicaciones frontend con autenticación, caché, cliente HTTP y utilidades de middleware construida con RxJS y patrones modernos.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
2. [Módulos y Componentes](#módulos-y-componentes)
|
|
7
|
-
3. [Guías de Implementación](#guías-de-implementación)
|
|
8
|
-
4. [Configuración](#configuración)
|
|
9
|
-
5. [Casos de Uso Avanzados](#casos-de-uso-avanzados)
|
|
5
|
+
## Comenzando
|
|
10
6
|
|
|
11
|
-
|
|
7
|
+
### Instalación
|
|
12
8
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
```bash
|
|
10
|
+
npm install @mp-front/common
|
|
11
|
+
```
|
|
16
12
|
|
|
17
|
-
###
|
|
13
|
+
### Dependencias Peer
|
|
18
14
|
|
|
15
|
+
```bash
|
|
16
|
+
npm install ioredis@5.3.2 next-auth@4.24.13 node-jose@2.2.0 rxjs@7.8.1
|
|
19
17
|
```
|
|
20
|
-
src/
|
|
21
|
-
├── adapters/ # Adaptadores NextAuth (Redis)
|
|
22
|
-
├── auth/ # Servicios de autenticación
|
|
23
|
-
├── cache/ # Gestión de caché
|
|
24
|
-
│ ├── session-handler/ # Manejo de sesiones
|
|
25
|
-
│ └── terminal-handler/ # Manejo de terminales
|
|
26
|
-
├── cache-providers/ # Proveedores de caché
|
|
27
|
-
│ └── redis/ # Implementación Redis
|
|
28
|
-
├── engine/ # Motor de autenticación Azure
|
|
29
|
-
├── errors/ # Sistema de errores
|
|
30
|
-
├── helpers/ # Utilidades generales
|
|
31
|
-
├── http/ # Cliente HTTP
|
|
32
|
-
├── middleware/ # Middlewares API
|
|
33
|
-
└── rxjs/ # Utilidades RxJS
|
|
34
|
-
```
|
|
35
18
|
|
|
36
|
-
###
|
|
19
|
+
### Inicio Rápido
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
import { HttpClient } from '@mp-front/common/http'
|
|
23
|
+
import { Logger } from '@mp-front/common/helpers'
|
|
37
24
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- **Type Safety**: TypeScript completo con tipos estrictos
|
|
41
|
-
- **Singleton Pattern**: Para gestores globales (ErrorHandler, LoadingHandler)
|
|
42
|
-
- **Encriptación**: Seguridad en datos sensibles con node-jose
|
|
25
|
+
const client = new HttpClient()
|
|
26
|
+
const logger = new Logger()
|
|
43
27
|
|
|
44
|
-
|
|
28
|
+
client.get('/api/users').subscribe(users => {
|
|
29
|
+
logger.logInfo('Usuarios cargados', JSON.stringify(users))
|
|
30
|
+
})
|
|
31
|
+
```
|
|
45
32
|
|
|
46
|
-
##
|
|
33
|
+
## Arquitectura
|
|
47
34
|
|
|
48
|
-
|
|
35
|
+
La librería sigue una arquitectura modular que permite tree-shaking e importaciones selectivas:
|
|
49
36
|
|
|
50
|
-
|
|
37
|
+
```
|
|
38
|
+
@mp-front/common/
|
|
39
|
+
├── auth # Servicios de autenticación
|
|
40
|
+
├── engine # Integración con Azure Entra ID
|
|
41
|
+
├── http # Cliente HTTP con RxJS
|
|
42
|
+
├── cache # Caché de sesión y terminal
|
|
43
|
+
├── cache-providers # Implementación de caché Redis
|
|
44
|
+
├── helpers # Utilidades (Logger, Encoder, Encrypter)
|
|
45
|
+
├── errors # Sistema de manejo de errores
|
|
46
|
+
├── rxjs # Utilidades y manejadores RxJS
|
|
47
|
+
├── middleware # Middleware de API para Next.js
|
|
48
|
+
└── adapters # Adaptadores NextAuth
|
|
49
|
+
```
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
## Referencia de API
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
### Autenticación
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
- Retorna Observable con el token
|
|
58
|
-
- Logging integrado de requests/responses
|
|
55
|
+
#### AuthorizationService
|
|
59
56
|
|
|
60
|
-
|
|
57
|
+
Servicio para autenticación de tokens del backend.
|
|
61
58
|
|
|
62
59
|
```typescript
|
|
63
|
-
import { AuthorizationService } from
|
|
60
|
+
import { AuthorizationService } from '@mp-front/common/auth'
|
|
64
61
|
|
|
65
62
|
const authService = new AuthorizationService()
|
|
66
63
|
|
|
67
|
-
// Obtener token
|
|
68
64
|
authService.get().subscribe({
|
|
69
|
-
next: token =>
|
|
70
|
-
|
|
71
|
-
// Usar token en headers
|
|
72
|
-
},
|
|
73
|
-
error: error => {
|
|
74
|
-
console.error("Error de autenticación:", error)
|
|
75
|
-
},
|
|
65
|
+
next: token => console.log('Token:', token),
|
|
66
|
+
error: error => console.error('Error de auth:', error)
|
|
76
67
|
})
|
|
77
68
|
```
|
|
78
69
|
|
|
79
|
-
**Variables de
|
|
80
|
-
|
|
70
|
+
**Variables de Entorno:**
|
|
81
71
|
```env
|
|
82
72
|
API_AUTH_BACK_URL=https://api.example.com/auth
|
|
83
|
-
API_AUTH_BACK_USERNAME_AUTH=
|
|
84
|
-
API_AUTH_BACK_PASSWORD_AUTH=
|
|
73
|
+
API_AUTH_BACK_USERNAME_AUTH=username
|
|
74
|
+
API_AUTH_BACK_PASSWORD_AUTH=password
|
|
85
75
|
ID_FRONT=frontend-app-id
|
|
86
76
|
```
|
|
87
77
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
### 2. Engine Module (`@mp-front/common/engine`)
|
|
78
|
+
### Motor Azure
|
|
91
79
|
|
|
92
80
|
#### AuthEngine
|
|
93
81
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
**Características:**
|
|
97
|
-
|
|
98
|
-
- Verificación de roles de usuario
|
|
99
|
-
- Verificación de grupos de usuario
|
|
100
|
-
- Integración con Microsoft Graph API
|
|
101
|
-
|
|
102
|
-
**Métodos principales:**
|
|
103
|
-
|
|
104
|
-
##### `getRoles(email: string, idObjApp: string)`
|
|
105
|
-
|
|
106
|
-
Obtiene los roles asignados a un usuario en una aplicación.
|
|
82
|
+
Integración con Azure Entra ID para verificación de roles y grupos.
|
|
107
83
|
|
|
108
84
|
```typescript
|
|
109
|
-
import { AuthEngine } from
|
|
85
|
+
import { AuthEngine } from '@mp-front/common/engine'
|
|
110
86
|
|
|
111
87
|
const authEngine = new AuthEngine()
|
|
112
88
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
},
|
|
89
|
+
// Verificar roles de usuario
|
|
90
|
+
authEngine.getRoles('user@example.com', 'app-object-id').subscribe({
|
|
91
|
+
next: ({ roles }) => console.log('Roles del usuario:', roles)
|
|
117
92
|
})
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
##### `inRole(email: string, idRole: string, idObjApp: string)`
|
|
121
93
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
authEngine.inRole("user@example.com", "role-id", "app-object-id").subscribe({
|
|
126
|
-
next: hasRole => {
|
|
127
|
-
if (hasRole) {
|
|
128
|
-
console.log("Usuario tiene el rol")
|
|
129
|
-
}
|
|
130
|
-
},
|
|
94
|
+
// Verificar rol específico
|
|
95
|
+
authEngine.inRole('user@example.com', 'role-id', 'app-object-id').subscribe({
|
|
96
|
+
next: hasRole => console.log('Tiene rol:', hasRole)
|
|
131
97
|
})
|
|
132
|
-
```
|
|
133
98
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
authEngine.inGroup("user@example.com", "group-id").subscribe({
|
|
140
|
-
next: inGroup => {
|
|
141
|
-
if (inGroup) {
|
|
142
|
-
console.log("Usuario pertenece al grupo")
|
|
143
|
-
}
|
|
144
|
-
},
|
|
99
|
+
// Verificar membresía de grupo
|
|
100
|
+
authEngine.inGroup('user@example.com', 'group-id').subscribe({
|
|
101
|
+
next: inGroup => console.log('En grupo:', inGroup)
|
|
145
102
|
})
|
|
146
103
|
```
|
|
147
104
|
|
|
148
|
-
**Variables de
|
|
149
|
-
|
|
105
|
+
**Variables de Entorno:**
|
|
150
106
|
```env
|
|
151
107
|
AZURE_AD_GRAPH_GET_APP_ROLES=https://graph.microsoft.com/v1.0/applications/{id-obj}/appRoles
|
|
152
108
|
AZURE_AD_GRAPH_GET_USER_BY_EMAIL=https://graph.microsoft.com/v1.0/users/{user-mail}
|
|
153
109
|
AZURE_AD_GRAPH_GROUPS=https://graph.microsoft.com/v1.0/users/idUser/memberOf
|
|
154
110
|
```
|
|
155
111
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
### 3. HTTP Module (`@mp-front/common/http`)
|
|
112
|
+
### Cliente HTTP
|
|
159
113
|
|
|
160
114
|
#### HttpClient
|
|
161
115
|
|
|
162
|
-
Cliente HTTP basado en RxJS con
|
|
163
|
-
|
|
164
|
-
**Características:**
|
|
165
|
-
|
|
166
|
-
- Métodos HTTP: GET, POST, PUT, PATCH, DELETE
|
|
167
|
-
- Encoding/Decoding automático de datos
|
|
168
|
-
- Manejo de loading state global
|
|
169
|
-
- Manejo de errores centralizado
|
|
170
|
-
- Logging de requests/responses
|
|
171
|
-
|
|
172
|
-
**Métodos:**
|
|
173
|
-
|
|
174
|
-
##### GET Request
|
|
116
|
+
Cliente HTTP basado en RxJS con estados de carga automáticos y manejo de errores.
|
|
175
117
|
|
|
176
118
|
```typescript
|
|
177
|
-
import { HttpClient } from
|
|
119
|
+
import { HttpClient } from '@mp-front/common/http'
|
|
178
120
|
|
|
179
121
|
const client = new HttpClient()
|
|
180
122
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
client.get<User>("/api/users/1").subscribe({
|
|
188
|
-
next: user => {
|
|
189
|
-
console.log("Usuario:", user)
|
|
190
|
-
},
|
|
191
|
-
error: error => {
|
|
192
|
-
console.error("Error:", error)
|
|
193
|
-
},
|
|
123
|
+
// Petición GET
|
|
124
|
+
client.get<User[]>('/api/users', {
|
|
125
|
+
params: { page: 1, limit: 10 },
|
|
126
|
+
headers: { 'Authorization': 'Bearer token' }
|
|
127
|
+
}).subscribe(users => {
|
|
128
|
+
console.log('Usuarios:', users)
|
|
194
129
|
})
|
|
195
130
|
|
|
196
|
-
//
|
|
197
|
-
client
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
.
|
|
202
|
-
console.log("Usuarios:", users)
|
|
203
|
-
})
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
##### POST Request
|
|
207
|
-
|
|
208
|
-
```typescript
|
|
209
|
-
client
|
|
210
|
-
.post<User>("/api/users", {
|
|
211
|
-
name: "John Doe",
|
|
212
|
-
email: "john@example.com",
|
|
213
|
-
})
|
|
214
|
-
.subscribe(newUser => {
|
|
215
|
-
console.log("Usuario creado:", newUser)
|
|
216
|
-
})
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
##### PUT Request
|
|
220
|
-
|
|
221
|
-
```typescript
|
|
222
|
-
client
|
|
223
|
-
.put<User>("/api/users/1", {
|
|
224
|
-
name: "Jane Doe",
|
|
225
|
-
})
|
|
226
|
-
.subscribe(updatedUser => {
|
|
227
|
-
console.log("Usuario actualizado:", updatedUser)
|
|
228
|
-
})
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
##### DELETE Request
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
client.delete("/api/users/1").subscribe(() => {
|
|
235
|
-
console.log("Usuario eliminado")
|
|
131
|
+
// Petición POST
|
|
132
|
+
client.post<User>('/api/users', {
|
|
133
|
+
name: 'Juan Pérez',
|
|
134
|
+
email: 'juan@example.com'
|
|
135
|
+
}).subscribe(newUser => {
|
|
136
|
+
console.log('Creado:', newUser)
|
|
236
137
|
})
|
|
237
|
-
```
|
|
238
138
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
```typescript
|
|
242
|
-
const client = new HttpClient()
|
|
139
|
+
// Deshabilitar carga global
|
|
243
140
|
client.setIsLoadingEnabled(false)
|
|
244
|
-
|
|
245
|
-
// Este request no activará el loading global
|
|
246
|
-
client.get("/api/data").subscribe(data => {
|
|
247
|
-
console.log(data)
|
|
248
|
-
})
|
|
249
141
|
```
|
|
250
142
|
|
|
251
|
-
|
|
143
|
+
**Métodos:**
|
|
144
|
+
- `get<T>(url, options?)` - Petición GET
|
|
145
|
+
- `post<T>(url, body, options?)` - Petición POST
|
|
146
|
+
- `put<T>(url, body, options?)` - Petición PUT
|
|
147
|
+
- `patch<T>(url, body, options?)` - Petición PATCH
|
|
148
|
+
- `delete<T>(url, options?)` - Petición DELETE
|
|
252
149
|
|
|
253
|
-
###
|
|
150
|
+
### Gestión de Caché
|
|
254
151
|
|
|
255
152
|
#### SessionCache
|
|
256
153
|
|
|
257
154
|
Gestión de sesiones de usuario con Redis.
|
|
258
155
|
|
|
259
|
-
**Características:**
|
|
260
|
-
|
|
261
|
-
- Almacenamiento de sesión en Redis
|
|
262
|
-
- Renovación automática de timeout
|
|
263
|
-
- Integración con NextAuth
|
|
264
|
-
- Datos de usuario y tienda
|
|
265
|
-
|
|
266
|
-
**Métodos:**
|
|
267
|
-
|
|
268
|
-
##### `getBasicSession()`
|
|
269
|
-
|
|
270
|
-
Obtiene la sesión básica del usuario.
|
|
271
|
-
|
|
272
156
|
```typescript
|
|
273
|
-
import { SessionCache } from
|
|
157
|
+
import { SessionCache } from '@mp-front/common/cache'
|
|
274
158
|
|
|
275
|
-
const sessionCache = new SessionCache(
|
|
159
|
+
const sessionCache = new SessionCache('user-id-123')
|
|
276
160
|
|
|
161
|
+
// Obtener sesión básica
|
|
277
162
|
const session = await sessionCache.getBasicSession()
|
|
163
|
+
console.log('Usuario:', session.user)
|
|
278
164
|
|
|
279
|
-
|
|
280
|
-
// {
|
|
281
|
-
// name: 'John Doe',
|
|
282
|
-
// email: 'john@example.com',
|
|
283
|
-
// image: 'https://...',
|
|
284
|
-
// cveUsuario: '12345',
|
|
285
|
-
// rol: 'admin',
|
|
286
|
-
// appGroups: ['group1', 'group2']
|
|
287
|
-
// }
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
##### `getUserAndShoppingStore()`
|
|
291
|
-
|
|
292
|
-
Obtiene sesión completa con datos de tienda.
|
|
293
|
-
|
|
294
|
-
```typescript
|
|
165
|
+
// Obtener sesión completa con datos de tienda
|
|
295
166
|
const fullSession = await sessionCache.getUserAndShoppingStore()
|
|
296
|
-
|
|
297
|
-
console.log("Sesión completa:", fullSession)
|
|
298
167
|
```
|
|
299
168
|
|
|
300
169
|
#### TerminalCache
|
|
301
170
|
|
|
302
|
-
Gestión de datos de terminal
|
|
303
|
-
|
|
304
|
-
**Características:**
|
|
305
|
-
|
|
306
|
-
- Almacenamiento por clave de vendedor
|
|
307
|
-
- Generación de UUID único por vendedor
|
|
308
|
-
- Timeout configurable
|
|
309
|
-
|
|
310
|
-
**Métodos:**
|
|
311
|
-
|
|
312
|
-
##### `set(sellerKey: string, terminal: TerminalValue)`
|
|
313
|
-
|
|
314
|
-
Guarda datos de terminal.
|
|
171
|
+
Gestión de datos de terminal.
|
|
315
172
|
|
|
316
173
|
```typescript
|
|
317
|
-
import { TerminalCache } from
|
|
174
|
+
import { TerminalCache } from '@mp-front/common/cache'
|
|
318
175
|
|
|
319
176
|
const terminalCache = new TerminalCache()
|
|
320
177
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
178
|
+
// Establecer datos de terminal
|
|
179
|
+
await terminalCache.set('SELLER001', {
|
|
180
|
+
terminalId: 'TERM123',
|
|
181
|
+
location: 'Tienda A',
|
|
182
|
+
status: 'active'
|
|
325
183
|
})
|
|
326
|
-
```
|
|
327
|
-
|
|
328
|
-
##### `get(sellerKey: string)`
|
|
329
184
|
|
|
330
|
-
|
|
185
|
+
// Obtener datos de terminal
|
|
186
|
+
const terminal = await terminalCache.get('SELLER001')
|
|
331
187
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (terminal) {
|
|
336
|
-
console.log("Terminal:", terminal)
|
|
337
|
-
}
|
|
188
|
+
// Eliminar datos de terminal
|
|
189
|
+
await terminalCache.delete('SELLER001')
|
|
338
190
|
```
|
|
339
191
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
Elimina datos de terminal.
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
await terminalCache.delete("SELLER001")
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
---
|
|
349
|
-
|
|
350
|
-
### 5. Cache Providers Module (`@mp-front/common/cache-providers`)
|
|
192
|
+
### Proveedores de Caché
|
|
351
193
|
|
|
352
194
|
#### RedisCache
|
|
353
195
|
|
|
354
|
-
Proveedor genérico de caché con
|
|
355
|
-
|
|
356
|
-
**Características:**
|
|
357
|
-
|
|
358
|
-
- Operaciones CRUD completas
|
|
359
|
-
- Encriptación opcional
|
|
360
|
-
- TTL (Time To Live) configurable
|
|
361
|
-
- Soporte para tipos genéricos
|
|
362
|
-
|
|
363
|
-
**Métodos:**
|
|
364
|
-
|
|
365
|
-
##### `set(params: ISetStateParams<T>)`
|
|
366
|
-
|
|
367
|
-
Guarda datos en Redis.
|
|
196
|
+
Proveedor genérico de caché Redis con soporte de encriptación.
|
|
368
197
|
|
|
369
198
|
```typescript
|
|
370
|
-
import { RedisCache } from
|
|
199
|
+
import { RedisCache } from '@mp-front/common/cache-providers'
|
|
371
200
|
|
|
372
201
|
interface UserData {
|
|
373
202
|
name: string
|
|
@@ -376,218 +205,165 @@ interface UserData {
|
|
|
376
205
|
|
|
377
206
|
const redis = new RedisCache<UserData>()
|
|
378
207
|
|
|
208
|
+
// Establecer datos con encriptación
|
|
379
209
|
await redis.set({
|
|
380
|
-
prefix:
|
|
381
|
-
idData:
|
|
382
|
-
body: {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
},
|
|
386
|
-
expire: 3600, // 1 hora en segundos
|
|
387
|
-
encrypted: true, // Opcional: encriptar datos
|
|
210
|
+
prefix: 'user:123',
|
|
211
|
+
idData: 'profile',
|
|
212
|
+
body: { name: 'Juan', email: 'juan@example.com' },
|
|
213
|
+
expire: 3600,
|
|
214
|
+
encrypted: true
|
|
388
215
|
})
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
##### `get<T>(prefix: string, idData: string)`
|
|
392
|
-
|
|
393
|
-
Obtiene datos de Redis.
|
|
394
216
|
|
|
395
|
-
|
|
396
|
-
const { data, sha } = await redis.get<UserData>(
|
|
397
|
-
|
|
398
|
-
console.log("Datos:", data)
|
|
399
|
-
console.log("SHA key:", sha)
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
##### `delete(prefix: string, idData: string)`
|
|
217
|
+
// Obtener datos
|
|
218
|
+
const { data, sha } = await redis.get<UserData>('user:123', 'profile')
|
|
403
219
|
|
|
404
|
-
|
|
220
|
+
// Eliminar datos
|
|
221
|
+
await redis.delete('user:123', 'profile')
|
|
405
222
|
|
|
406
|
-
|
|
407
|
-
await redis.
|
|
223
|
+
// Operaciones simples
|
|
224
|
+
const value = await redis.simpleGet('key')
|
|
225
|
+
await redis.simpleHSet('hash-key', 'field', 'value')
|
|
226
|
+
const allFields = await redis.simpleHGetAll('hash-key')
|
|
408
227
|
```
|
|
409
228
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
```typescript
|
|
413
|
-
// GET simple
|
|
414
|
-
const value = await redis.simpleGet("key")
|
|
415
|
-
|
|
416
|
-
// HGET (hash get)
|
|
417
|
-
const fieldValue = await redis.simpleHGet("hash-key", "field")
|
|
418
|
-
|
|
419
|
-
// HSET (hash set)
|
|
420
|
-
await redis.simpleHSet("hash-key", "field", "value")
|
|
421
|
-
|
|
422
|
-
// HGETALL (obtener todo el hash)
|
|
423
|
-
const allFields = await redis.simpleHGetAll("hash-key")
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
**Variables de entorno:**
|
|
427
|
-
|
|
229
|
+
**Variables de Entorno:**
|
|
428
230
|
```env
|
|
429
231
|
REDIS_URL=redis://localhost:6379
|
|
430
232
|
TIMEOUT_SESSION_MINUTES=60
|
|
431
|
-
SECRET_SIGNATURE=
|
|
233
|
+
SECRET_SIGNATURE=tu-clave-secreta-min-32-chars
|
|
432
234
|
```
|
|
433
235
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
### 6. Helpers Module (`@mp-front/common/helpers`)
|
|
236
|
+
### Utilidades
|
|
437
237
|
|
|
438
238
|
#### Logger
|
|
439
239
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
**Niveles de log:**
|
|
443
|
-
|
|
444
|
-
- `error`: Errores críticos
|
|
445
|
-
- `warn`: Advertencias
|
|
446
|
-
- `info`: Información general
|
|
447
|
-
- `http`: Logs HTTP
|
|
448
|
-
- `verbose`: Información detallada
|
|
449
|
-
- `debug`: Debugging
|
|
450
|
-
- `silly`: Todo
|
|
451
|
-
|
|
452
|
-
**Uso:**
|
|
240
|
+
Logging estructurado con niveles y colores.
|
|
453
241
|
|
|
454
242
|
```typescript
|
|
455
|
-
import { Logger } from
|
|
243
|
+
import { Logger } from '@mp-front/common/helpers'
|
|
456
244
|
|
|
457
245
|
const logger = new Logger()
|
|
458
246
|
|
|
459
|
-
logger.logError(
|
|
460
|
-
logger.logWarn(
|
|
461
|
-
logger.logInfo(
|
|
462
|
-
logger.logDebug(
|
|
247
|
+
logger.logError('Error crítico', JSON.stringify(error))
|
|
248
|
+
logger.logWarn('Uso de API deprecada')
|
|
249
|
+
logger.logInfo('Usuario autenticado exitosamente')
|
|
250
|
+
logger.logDebug('Datos de petición:', JSON.stringify(data))
|
|
463
251
|
```
|
|
464
252
|
|
|
465
|
-
**
|
|
253
|
+
**Niveles de Log:** `error`, `warn`, `info`, `http`, `verbose`, `debug`, `silly`
|
|
466
254
|
|
|
255
|
+
**Variables de Entorno:**
|
|
467
256
|
```env
|
|
468
|
-
NEXT_PUBLIC_APP_LOGS_NAME=
|
|
257
|
+
NEXT_PUBLIC_APP_LOGS_NAME=MiApp
|
|
469
258
|
NEXT_PUBLIC_LOGS_LEVEL=debug
|
|
470
259
|
NEXT_PUBLIC_SILENT_LOGS=false
|
|
471
260
|
```
|
|
472
261
|
|
|
473
262
|
#### Encoder
|
|
474
263
|
|
|
475
|
-
Codificación/decodificación
|
|
264
|
+
Codificación/decodificación Base64 para datos de API.
|
|
476
265
|
|
|
477
266
|
```typescript
|
|
478
|
-
import { Encoder } from
|
|
267
|
+
import { Encoder } from '@mp-front/common/helpers'
|
|
479
268
|
|
|
480
269
|
const encoder = new Encoder()
|
|
481
270
|
|
|
482
|
-
// Codificar
|
|
483
|
-
const encoded = encoder.encode({ name:
|
|
484
|
-
// Resultado: { info:
|
|
271
|
+
// Codificar datos
|
|
272
|
+
const encoded = encoder.encode({ name: 'Juan' }, 'request-id-123')
|
|
273
|
+
// Resultado: { info: 'base64...', requestID: 'request-id-123' }
|
|
485
274
|
|
|
486
|
-
// Decodificar
|
|
275
|
+
// Decodificar datos
|
|
487
276
|
const decoded = encoder.decode(encoded)
|
|
488
|
-
// Resultado: { name: '
|
|
277
|
+
// Resultado: { name: 'Juan' }
|
|
489
278
|
```
|
|
490
279
|
|
|
491
280
|
#### Encrypter
|
|
492
281
|
|
|
493
|
-
Encriptación/desencriptación con node-jose
|
|
282
|
+
Encriptación/desencriptación JWE con node-jose.
|
|
494
283
|
|
|
495
284
|
```typescript
|
|
496
|
-
import { Encrypter } from
|
|
285
|
+
import { Encrypter } from '@mp-front/common/helpers'
|
|
497
286
|
|
|
498
287
|
const encrypter = new Encrypter()
|
|
499
288
|
|
|
500
|
-
// Encriptar
|
|
289
|
+
// Encriptar datos sensibles
|
|
501
290
|
const encrypted = await encrypter.encrypt({
|
|
502
|
-
password:
|
|
503
|
-
apiKey:
|
|
291
|
+
password: 'secret123',
|
|
292
|
+
apiKey: 'key-xyz'
|
|
504
293
|
})
|
|
505
294
|
|
|
506
|
-
// Desencriptar
|
|
295
|
+
// Desencriptar datos
|
|
507
296
|
const decrypted = await encrypter.decrypt(encrypted)
|
|
508
297
|
|
|
509
|
-
// Verificar si
|
|
298
|
+
// Verificar si los datos están encriptados
|
|
510
299
|
const isEncrypted = await encrypter.isEncrypted(someString)
|
|
511
300
|
|
|
512
|
-
// Generar SHA256
|
|
513
|
-
const sha = encrypter.generateSHA({ userId:
|
|
301
|
+
// Generar hash SHA256
|
|
302
|
+
const sha = encrypter.generateSHA({ userId: '123' })
|
|
514
303
|
```
|
|
515
304
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
### 7. Errors Module (`@mp-front/common/errors`)
|
|
305
|
+
### Manejo de Errores
|
|
519
306
|
|
|
520
307
|
#### ErrorHandler
|
|
521
308
|
|
|
522
|
-
|
|
309
|
+
Gestión global de errores con patrón Singleton.
|
|
523
310
|
|
|
524
311
|
```typescript
|
|
525
|
-
import { ErrorHandler } from
|
|
312
|
+
import { ErrorHandler } from '@mp-front/common/errors'
|
|
526
313
|
|
|
527
314
|
// Suscribirse a errores globales
|
|
528
|
-
ErrorHandler.getInstance()
|
|
529
|
-
.
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
showNotification({
|
|
535
|
-
type: "error",
|
|
536
|
-
message: error.message,
|
|
537
|
-
code: error.code,
|
|
538
|
-
})
|
|
315
|
+
ErrorHandler.getInstance().getSubject().subscribe(error => {
|
|
316
|
+
console.error('Error global:', error)
|
|
317
|
+
showNotification({
|
|
318
|
+
type: 'error',
|
|
319
|
+
message: error.message,
|
|
320
|
+
code: error.code
|
|
539
321
|
})
|
|
322
|
+
})
|
|
540
323
|
```
|
|
541
324
|
|
|
542
325
|
#### RuntimeError
|
|
543
326
|
|
|
544
|
-
Clase de error personalizada.
|
|
327
|
+
Clase de error personalizada con seguimiento de peticiones.
|
|
545
328
|
|
|
546
329
|
```typescript
|
|
547
|
-
import { RuntimeError, RuntimeErrorCode } from
|
|
330
|
+
import { RuntimeError, RuntimeErrorCode } from '@mp-front/common/errors'
|
|
548
331
|
|
|
549
|
-
// Lanzar error
|
|
550
|
-
throw new RuntimeError(RuntimeErrorCode.VALIDATION_ERROR,
|
|
332
|
+
// Lanzar error personalizado
|
|
333
|
+
throw new RuntimeError(RuntimeErrorCode.VALIDATION_ERROR, 'request-id-123')
|
|
551
334
|
|
|
552
|
-
// En
|
|
335
|
+
// En bloques catch
|
|
553
336
|
try {
|
|
554
337
|
// código
|
|
555
338
|
} catch (error) {
|
|
556
|
-
throw new RuntimeError(
|
|
339
|
+
throw new RuntimeError('CUSTOM_ERROR', requestId)
|
|
557
340
|
}
|
|
558
341
|
```
|
|
559
342
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
### 8. RxJS Module (`@mp-front/common/rxjs`)
|
|
343
|
+
### Utilidades RxJS
|
|
563
344
|
|
|
564
345
|
#### LoadingHandler
|
|
565
346
|
|
|
566
|
-
|
|
347
|
+
Gestión global del estado de carga.
|
|
567
348
|
|
|
568
349
|
```typescript
|
|
569
|
-
import { LoadingHandler } from
|
|
570
|
-
|
|
571
|
-
// Suscribirse al estado de loading
|
|
572
|
-
LoadingHandler.getInstance()
|
|
573
|
-
.getSubject()
|
|
574
|
-
.subscribe(isLoading => {
|
|
575
|
-
if (isLoading) {
|
|
576
|
-
showSpinner()
|
|
577
|
-
} else {
|
|
578
|
-
hideSpinner()
|
|
579
|
-
}
|
|
580
|
-
})
|
|
350
|
+
import { LoadingHandler } from '@mp-front/common/rxjs'
|
|
581
351
|
|
|
582
|
-
//
|
|
583
|
-
LoadingHandler.getInstance().
|
|
352
|
+
// Suscribirse al estado de carga
|
|
353
|
+
LoadingHandler.getInstance().getSubject().subscribe(isLoading => {
|
|
354
|
+
if (isLoading) {
|
|
355
|
+
showSpinner()
|
|
356
|
+
} else {
|
|
357
|
+
hideSpinner()
|
|
358
|
+
}
|
|
359
|
+
})
|
|
584
360
|
|
|
585
|
-
//
|
|
586
|
-
LoadingHandler.getInstance().setSubject(
|
|
361
|
+
// Controlar carga manualmente
|
|
362
|
+
LoadingHandler.getInstance().setSubject(true) // Mostrar carga
|
|
363
|
+
LoadingHandler.getInstance().setSubject(false) // Ocultar carga
|
|
587
364
|
```
|
|
588
365
|
|
|
589
366
|
**Integración con React:**
|
|
590
|
-
|
|
591
367
|
```typescript
|
|
592
368
|
import { useEffect, useState } from 'react'
|
|
593
369
|
import { LoadingHandler } from '@mp-front/common/rxjs'
|
|
@@ -612,93 +388,98 @@ function App() {
|
|
|
612
388
|
}
|
|
613
389
|
```
|
|
614
390
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
### 9. Middleware Module (`@mp-front/common/middleware`)
|
|
391
|
+
### Middleware
|
|
618
392
|
|
|
619
393
|
#### ApiMiddleware
|
|
620
394
|
|
|
621
|
-
Middleware
|
|
395
|
+
Middleware de API para Next.js con codificación/decodificación automática.
|
|
622
396
|
|
|
623
|
-
|
|
397
|
+
```typescript
|
|
398
|
+
import { ApiMiddleware } from '@mp-front/common/middleware'
|
|
399
|
+
import { of } from 'rxjs'
|
|
624
400
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
401
|
+
interface CreateUserRequest {
|
|
402
|
+
name: string
|
|
403
|
+
email: string
|
|
404
|
+
}
|
|
629
405
|
|
|
630
|
-
|
|
406
|
+
interface CreateUserResponse {
|
|
407
|
+
id: string
|
|
408
|
+
name: string
|
|
409
|
+
email: string
|
|
410
|
+
}
|
|
631
411
|
|
|
632
|
-
|
|
633
|
-
|
|
412
|
+
class UserApiMiddleware extends ApiMiddleware {
|
|
413
|
+
createUser = this.get<CreateUserRequest, CreateUserResponse>(
|
|
414
|
+
(params, { requestID, headers }) => {
|
|
415
|
+
// Validación
|
|
416
|
+
if (!params.name || !params.email) {
|
|
417
|
+
throw new RuntimeError('VALIDATION_ERROR', requestID)
|
|
418
|
+
}
|
|
634
419
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
super()
|
|
638
|
-
}
|
|
639
|
-
}
|
|
420
|
+
// Obtener sesión
|
|
421
|
+
const session = this.getSession()
|
|
640
422
|
|
|
641
|
-
|
|
423
|
+
// Lógica de negocio
|
|
424
|
+
const newUser = {
|
|
425
|
+
id: generateId(),
|
|
426
|
+
name: params.name,
|
|
427
|
+
email: params.email
|
|
428
|
+
}
|
|
642
429
|
|
|
643
|
-
|
|
644
|
-
|
|
430
|
+
return of(newUser)
|
|
431
|
+
}
|
|
432
|
+
)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export const POST = new UserApiMiddleware().createUser
|
|
436
|
+
```
|
|
645
437
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
438
|
+
**Manejador de Subida de Archivos:**
|
|
439
|
+
```typescript
|
|
440
|
+
export const POST = middleware.getFormData<ResponseType>(
|
|
441
|
+
(params, files) => {
|
|
442
|
+
// Procesar archivos
|
|
650
443
|
return of({
|
|
651
|
-
|
|
652
|
-
|
|
444
|
+
uploadedFiles: files.map(f => ({
|
|
445
|
+
name: f.name,
|
|
446
|
+
size: f.size
|
|
447
|
+
}))
|
|
653
448
|
})
|
|
654
449
|
}
|
|
655
450
|
)
|
|
656
451
|
```
|
|
657
452
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
```typescript
|
|
661
|
-
export const POST = middleware.getFile<ResponseType>(file => {
|
|
662
|
-
// Procesar archivo
|
|
663
|
-
return of({
|
|
664
|
-
filename: file.name,
|
|
665
|
-
size: file.size,
|
|
666
|
-
})
|
|
667
|
-
})
|
|
668
|
-
```
|
|
669
|
-
|
|
670
|
-
---
|
|
671
|
-
|
|
672
|
-
### 10. Adapters Module (`@mp-front/common/adapters`)
|
|
453
|
+
### Adaptadores
|
|
673
454
|
|
|
674
455
|
#### IORedisAdapter
|
|
675
456
|
|
|
676
|
-
Adaptador
|
|
457
|
+
Adaptador NextAuth para Redis.
|
|
677
458
|
|
|
678
459
|
```typescript
|
|
679
|
-
import { IORedisAdapter } from
|
|
460
|
+
import { IORedisAdapter } from '@mp-front/common/adapters'
|
|
680
461
|
|
|
681
462
|
export const authOptions = {
|
|
682
463
|
adapter: IORedisAdapter({
|
|
683
|
-
expire: 3600
|
|
464
|
+
expire: 3600 // segundos
|
|
684
465
|
}),
|
|
685
|
-
// ... otras opciones
|
|
466
|
+
// ... otras opciones de NextAuth
|
|
686
467
|
}
|
|
687
468
|
```
|
|
688
469
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
## ⚙️ Configuración Completa
|
|
470
|
+
## Configuración
|
|
692
471
|
|
|
693
472
|
### Variables de Entorno
|
|
694
473
|
|
|
474
|
+
Crea un archivo `.env.local` con las siguientes variables:
|
|
475
|
+
|
|
695
476
|
```env
|
|
696
477
|
# Logging
|
|
697
|
-
NEXT_PUBLIC_APP_LOGS_NAME=
|
|
478
|
+
NEXT_PUBLIC_APP_LOGS_NAME=MiApp
|
|
698
479
|
NEXT_PUBLIC_LOGS_LEVEL=debug
|
|
699
480
|
NEXT_PUBLIC_SILENT_LOGS=false
|
|
700
481
|
|
|
701
|
-
#
|
|
482
|
+
# Backend de Autenticación
|
|
702
483
|
API_AUTH_BACK_URL=https://api.example.com/auth
|
|
703
484
|
API_AUTH_BACK_USERNAME_AUTH=username
|
|
704
485
|
API_AUTH_BACK_PASSWORD_AUTH=password
|
|
@@ -714,20 +495,19 @@ REDIS_URL=redis://localhost:6379
|
|
|
714
495
|
TIMEOUT_SESSION_MINUTES=60
|
|
715
496
|
PREFIX_LOGIN=app
|
|
716
497
|
|
|
717
|
-
#
|
|
718
|
-
SECRET_SIGNATURE=
|
|
498
|
+
# Encriptación
|
|
499
|
+
SECRET_SIGNATURE=tu-clave-secreta-min-32-chars
|
|
719
500
|
```
|
|
720
501
|
|
|
721
|
-
|
|
502
|
+
## Ejemplos
|
|
722
503
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
### 1. Sistema de Autenticación Completo
|
|
504
|
+
### Sistema de Autenticación Completo
|
|
726
505
|
|
|
727
506
|
```typescript
|
|
728
|
-
import { AuthorizationService } from
|
|
729
|
-
import { HttpClient } from
|
|
730
|
-
import { SessionCache } from
|
|
507
|
+
import { AuthorizationService } from '@mp-front/common/auth'
|
|
508
|
+
import { HttpClient } from '@mp-front/common/http'
|
|
509
|
+
import { SessionCache } from '@mp-front/common/cache'
|
|
510
|
+
import { firstValueFrom } from 'rxjs'
|
|
731
511
|
|
|
732
512
|
class AuthSystem {
|
|
733
513
|
private authService = new AuthorizationService()
|
|
@@ -742,29 +522,23 @@ class AuthSystem {
|
|
|
742
522
|
this.sessionCache = new SessionCache(userId)
|
|
743
523
|
const session = await this.sessionCache.getBasicSession()
|
|
744
524
|
|
|
745
|
-
// Hacer
|
|
746
|
-
this.httpClient
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
})
|
|
752
|
-
.subscribe(data => {
|
|
753
|
-
console.log("Datos protegidos:", data)
|
|
754
|
-
})
|
|
525
|
+
// Hacer petición autenticada
|
|
526
|
+
this.httpClient.get('/api/protected', {
|
|
527
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
528
|
+
}).subscribe(data => {
|
|
529
|
+
console.log('Datos protegidos:', data)
|
|
530
|
+
})
|
|
755
531
|
}
|
|
756
532
|
}
|
|
757
533
|
```
|
|
758
534
|
|
|
759
|
-
###
|
|
535
|
+
### Sistema de Caché Multi-nivel
|
|
760
536
|
|
|
761
537
|
```typescript
|
|
762
|
-
import { RedisCache } from
|
|
763
|
-
import { Encrypter } from "@mp-front/common/helpers"
|
|
538
|
+
import { RedisCache } from '@mp-front/common/cache-providers'
|
|
764
539
|
|
|
765
540
|
class CacheSystem<T> {
|
|
766
541
|
private redis = new RedisCache<T>()
|
|
767
|
-
private encrypter = new Encrypter()
|
|
768
542
|
private memoryCache = new Map<string, T>()
|
|
769
543
|
|
|
770
544
|
async get(key: string): Promise<T | null> {
|
|
@@ -775,7 +549,7 @@ class CacheSystem<T> {
|
|
|
775
549
|
|
|
776
550
|
// Nivel 2: Redis
|
|
777
551
|
try {
|
|
778
|
-
const { data } = await this.redis.get<T>(key,
|
|
552
|
+
const { data } = await this.redis.get<T>(key, 'data')
|
|
779
553
|
this.memoryCache.set(key, data)
|
|
780
554
|
return data
|
|
781
555
|
} catch {
|
|
@@ -784,129 +558,62 @@ class CacheSystem<T> {
|
|
|
784
558
|
}
|
|
785
559
|
|
|
786
560
|
async set(key: string, value: T, ttl: number = 3600) {
|
|
787
|
-
// Guardar en memoria
|
|
788
561
|
this.memoryCache.set(key, value)
|
|
789
|
-
|
|
790
|
-
// Guardar en Redis
|
|
562
|
+
|
|
791
563
|
await this.redis.set({
|
|
792
564
|
prefix: key,
|
|
793
|
-
idData:
|
|
565
|
+
idData: 'data',
|
|
794
566
|
body: value,
|
|
795
567
|
expire: ttl,
|
|
796
|
-
encrypted: true
|
|
568
|
+
encrypted: true
|
|
797
569
|
})
|
|
798
570
|
}
|
|
799
571
|
}
|
|
800
572
|
```
|
|
801
573
|
|
|
802
|
-
###
|
|
574
|
+
### Sistema Global de Errores
|
|
803
575
|
|
|
804
576
|
```typescript
|
|
805
|
-
import { ErrorHandler, RuntimeError } from
|
|
806
|
-
import { LoadingHandler } from
|
|
577
|
+
import { ErrorHandler, RuntimeError } from '@mp-front/common/errors'
|
|
578
|
+
import { LoadingHandler } from '@mp-front/common/rxjs'
|
|
807
579
|
|
|
808
580
|
class GlobalErrorSystem {
|
|
809
581
|
constructor() {
|
|
810
582
|
this.setupErrorHandling()
|
|
811
|
-
this.setupLoadingHandling()
|
|
812
583
|
}
|
|
813
584
|
|
|
814
585
|
private setupErrorHandling() {
|
|
815
|
-
ErrorHandler.getInstance()
|
|
816
|
-
|
|
817
|
-
.
|
|
818
|
-
// Ocultar loading
|
|
819
|
-
LoadingHandler.getInstance().setSubject(false)
|
|
820
|
-
|
|
821
|
-
// Log error
|
|
822
|
-
console.error("Error:", error)
|
|
586
|
+
ErrorHandler.getInstance().getSubject().subscribe(error => {
|
|
587
|
+
// Ocultar carga
|
|
588
|
+
LoadingHandler.getInstance().setSubject(false)
|
|
823
589
|
|
|
824
|
-
|
|
825
|
-
|
|
590
|
+
// Registrar error
|
|
591
|
+
console.error('Error:', error)
|
|
826
592
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
private setupLoadingHandling() {
|
|
833
|
-
LoadingHandler.getInstance()
|
|
834
|
-
.getSubject()
|
|
835
|
-
.subscribe(isLoading => {
|
|
836
|
-
document.body.classList.toggle("loading", isLoading)
|
|
837
|
-
})
|
|
593
|
+
// Mostrar notificación
|
|
594
|
+
this.showErrorNotification(error)
|
|
595
|
+
})
|
|
838
596
|
}
|
|
839
597
|
|
|
840
598
|
private showErrorNotification(error: RuntimeError) {
|
|
841
|
-
//
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
private trackError(error: RuntimeError) {
|
|
845
|
-
// Enviar a Sentry, DataDog, etc.
|
|
599
|
+
// La implementación depende de tu sistema de notificaciones
|
|
846
600
|
}
|
|
847
601
|
}
|
|
848
602
|
```
|
|
849
603
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
```typescript
|
|
853
|
-
import { ApiMiddleware } from "@mp-front/common/middleware"
|
|
854
|
-
import { RuntimeError } from "@mp-front/common/errors"
|
|
855
|
-
import { of, throwError } from "rxjs"
|
|
856
|
-
|
|
857
|
-
interface CreateUserRequest {
|
|
858
|
-
name: string
|
|
859
|
-
email: string
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
interface CreateUserResponse {
|
|
863
|
-
id: string
|
|
864
|
-
name: string
|
|
865
|
-
email: string
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
class UserApiMiddleware extends ApiMiddleware {
|
|
869
|
-
createUser = this.get<CreateUserRequest, CreateUserResponse>(
|
|
870
|
-
(params, { requestID }) => {
|
|
871
|
-
// Validación
|
|
872
|
-
if (!params.name || !params.email) {
|
|
873
|
-
throw new RuntimeError("VALIDATION_ERROR", requestID)
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
// Obtener sesión
|
|
877
|
-
const session = this.getSession()
|
|
878
|
-
|
|
879
|
-
// Lógica de negocio
|
|
880
|
-
const newUser = {
|
|
881
|
-
id: generateId(),
|
|
882
|
-
name: params.name,
|
|
883
|
-
email: params.email,
|
|
884
|
-
}
|
|
885
|
-
|
|
886
|
-
return of(newUser)
|
|
887
|
-
}
|
|
888
|
-
)
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
export const POST = new UserApiMiddleware().createUser
|
|
892
|
-
```
|
|
893
|
-
|
|
894
|
-
---
|
|
604
|
+
## Mejores Prácticas
|
|
895
605
|
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
### 1. Manejo de Suscripciones RxJS
|
|
606
|
+
### Gestión de Suscripciones RxJS
|
|
899
607
|
|
|
900
608
|
```typescript
|
|
901
|
-
import { Component, OnDestroy } from
|
|
902
|
-
import { Subject, takeUntil } from
|
|
609
|
+
import { Component, OnDestroy } from '@angular/core'
|
|
610
|
+
import { Subject, takeUntil } from 'rxjs'
|
|
903
611
|
|
|
904
612
|
class MyComponent implements OnDestroy {
|
|
905
613
|
private destroy$ = new Subject<void>()
|
|
906
614
|
|
|
907
615
|
ngOnInit() {
|
|
908
|
-
this.httpClient
|
|
909
|
-
.get("/api/data")
|
|
616
|
+
this.httpClient.get('/api/data')
|
|
910
617
|
.pipe(takeUntil(this.destroy$))
|
|
911
618
|
.subscribe(data => {
|
|
912
619
|
// Procesar datos
|
|
@@ -920,7 +627,7 @@ class MyComponent implements OnDestroy {
|
|
|
920
627
|
}
|
|
921
628
|
```
|
|
922
629
|
|
|
923
|
-
###
|
|
630
|
+
### Seguridad de Tipos
|
|
924
631
|
|
|
925
632
|
```typescript
|
|
926
633
|
// Definir interfaces
|
|
@@ -934,22 +641,21 @@ interface User {
|
|
|
934
641
|
const redis = new RedisCache<User>()
|
|
935
642
|
const client = new HttpClient()
|
|
936
643
|
|
|
937
|
-
client.get<User>(
|
|
644
|
+
client.get<User>('/api/user/1').subscribe(user => {
|
|
938
645
|
// TypeScript sabe que user es de tipo User
|
|
939
646
|
console.log(user.name)
|
|
940
647
|
})
|
|
941
648
|
```
|
|
942
649
|
|
|
943
|
-
###
|
|
650
|
+
### Manejo de Errores
|
|
944
651
|
|
|
945
652
|
```typescript
|
|
946
|
-
import { catchError, of } from
|
|
653
|
+
import { catchError, of } from 'rxjs'
|
|
947
654
|
|
|
948
|
-
client
|
|
949
|
-
.get<Data>("/api/data")
|
|
655
|
+
client.get<Data>('/api/data')
|
|
950
656
|
.pipe(
|
|
951
657
|
catchError(error => {
|
|
952
|
-
console.error(
|
|
658
|
+
console.error('Error:', error)
|
|
953
659
|
return of(null) // Valor por defecto
|
|
954
660
|
})
|
|
955
661
|
)
|
|
@@ -960,47 +666,50 @@ client
|
|
|
960
666
|
})
|
|
961
667
|
```
|
|
962
668
|
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
## 🔧 Troubleshooting
|
|
669
|
+
## Solución de Problemas
|
|
966
670
|
|
|
967
|
-
###
|
|
968
|
-
|
|
969
|
-
**Solución:**
|
|
671
|
+
### Problemas de Conexión Redis
|
|
970
672
|
|
|
971
673
|
```typescript
|
|
972
|
-
import { RedisCache } from
|
|
674
|
+
import { RedisCache } from '@mp-front/common/cache-providers'
|
|
973
675
|
|
|
974
676
|
const redis = new RedisCache()
|
|
975
677
|
|
|
976
|
-
redis
|
|
977
|
-
.
|
|
978
|
-
.
|
|
979
|
-
console.log("Redis conectado")
|
|
980
|
-
})
|
|
981
|
-
.catch(error => {
|
|
982
|
-
console.error("Error de conexión:", error)
|
|
983
|
-
})
|
|
678
|
+
redis.statusHost()
|
|
679
|
+
.then(() => console.log('Redis conectado'))
|
|
680
|
+
.catch(error => console.error('Error de conexión:', error))
|
|
984
681
|
```
|
|
985
682
|
|
|
986
|
-
###
|
|
987
|
-
|
|
988
|
-
**Verificar:**
|
|
683
|
+
### Los Logs No Aparecen
|
|
989
684
|
|
|
685
|
+
Verifica estas variables de entorno:
|
|
990
686
|
1. `NEXT_PUBLIC_SILENT_LOGS=false`
|
|
991
687
|
2. `NEXT_PUBLIC_LOGS_LEVEL` está configurado correctamente
|
|
992
|
-
3. El nivel
|
|
688
|
+
3. El nivel de log es apropiado para tus mensajes
|
|
993
689
|
|
|
994
|
-
###
|
|
995
|
-
|
|
996
|
-
**Verificar:**
|
|
690
|
+
### Fallas de Encriptación
|
|
997
691
|
|
|
692
|
+
Verifica:
|
|
998
693
|
1. `SECRET_SIGNATURE` está definido en `.env`
|
|
999
694
|
2. La clave tiene al menos 32 caracteres
|
|
1000
|
-
3.
|
|
695
|
+
3. Se usa la misma clave para operaciones de encriptar/desencriptar
|
|
696
|
+
|
|
697
|
+
## Guía de Migración
|
|
698
|
+
|
|
699
|
+
### De v0.0.1 a v0.0.2
|
|
700
|
+
|
|
701
|
+
- Actualiza las rutas de importación para usar importaciones modulares
|
|
702
|
+
- Reemplaza métodos deprecados con la nueva API
|
|
703
|
+
- Actualiza nombres de variables de entorno
|
|
704
|
+
|
|
705
|
+
## Contribuir
|
|
1001
706
|
|
|
1002
|
-
|
|
707
|
+
1. Haz fork del repositorio
|
|
708
|
+
2. Crea una rama de feature
|
|
709
|
+
3. Realiza tus cambios
|
|
710
|
+
4. Agrega pruebas
|
|
711
|
+
5. Envía un pull request
|
|
1003
712
|
|
|
1004
|
-
##
|
|
713
|
+
## Licencia
|
|
1005
714
|
|
|
1006
|
-
|
|
715
|
+
Privado - Solo uso interno
|