@nocios/crudify-ui 1.3.0 → 1.3.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 +155 -1108
- package/README_DEPTH.md +1046 -0
- package/dist/index.d.mts +181 -1
- package/dist/index.d.ts +181 -1
- package/dist/index.js +393 -20
- package/dist/index.mjs +388 -19
- package/package.json +1 -1
- package/MIGRATION-GUIDE.md +0 -312
- package/MIGRATION.md +0 -201
- package/MIGRATION_EXAMPLE.md +0 -538
- package/TECHNICAL_SPECIFICATION.md +0 -344
- package/example-app.tsx +0 -197
- package/mejoras_npm_lib.md +0 -790
- package/tsup.config.ts +0 -9
package/mejoras_npm_lib.md
DELETED
|
@@ -1,790 +0,0 @@
|
|
|
1
|
-
# 📊 **REPORTE PROFUNDO DE ANÁLISIS - CRUDIA-UI & NPM-CRUDIFY-UI**
|
|
2
|
-
|
|
3
|
-
## 🎯 **TABLA DE EVALUACIÓN PRINCIPAL**
|
|
4
|
-
|
|
5
|
-
| **Criterio** | **Crudia-UI** | **npm-crudify-ui** | **Promedio Sistema** |
|
|
6
|
-
| ------------------------------------ | ------------- | ------------------ | -------------------- |
|
|
7
|
-
| **🔒 Robustez** | **78%** | **85%** | **82%** |
|
|
8
|
-
| **🛡️ Seguridad** | **82%** | **76%** | **79%** |
|
|
9
|
-
| **⚡ Simplicidad de Implementación** | **71%** | **89%** | **80%** |
|
|
10
|
-
| **📊 Puntuación General** | **77%** | **83%** | **80%** |
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## 🔍 **ANÁLISIS DETALLADO POR CATEGORÍA**
|
|
15
|
-
|
|
16
|
-
### 🔒 **ROBUSTEZ**
|
|
17
|
-
|
|
18
|
-
| **Aspecto** | **Crudia-UI** | **npm-crudify-ui** | **Evaluación** |
|
|
19
|
-
| -------------------------- | ------------- | ------------------ | -------------------------------- |
|
|
20
|
-
| **Manejo de Errores** | 85% | 90% | ✅ Excelente sistema multicapa |
|
|
21
|
-
| **Inicialización** | 70% | 95% | ✅ Singleton robusto en librería |
|
|
22
|
-
| **Estado Consistente** | 75% | 80% | ⚠️ Posibles inconsistencias |
|
|
23
|
-
| **Recuperación de Fallos** | 80% | 85% | ✅ Buenos mecanismos |
|
|
24
|
-
| **Concurrencia** | 70% | 90% | ✅ Thread-safe en librería |
|
|
25
|
-
|
|
26
|
-
**Fortalezas:**
|
|
27
|
-
|
|
28
|
-
- ✅ **npm-crudify-ui**: Sistema de inicialización singleton thread-safe
|
|
29
|
-
- ✅ **Ambos**: Error boundaries multicapa
|
|
30
|
-
- ✅ **Crudia-UI**: Caching inteligente con expiración
|
|
31
|
-
|
|
32
|
-
**Debilidades:**
|
|
33
|
-
|
|
34
|
-
- ❌ **Crudia-UI**: Múltiples instancias de crudify pueden causar conflictos
|
|
35
|
-
- ❌ **npm-crudify-ui**: Logging excesivo en producción
|
|
36
|
-
|
|
37
|
-
### 🛡️ **SEGURIDAD**
|
|
38
|
-
|
|
39
|
-
| **Aspecto** | **Crudia-UI** | **npm-crudify-ui** | **Evaluación** |
|
|
40
|
-
| ------------------------- | ------------- | ------------------ | ------------------------------------------ |
|
|
41
|
-
| **Autenticación** | 90% | 80% | ✅ JWT sólido, fingerprinting cuestionable |
|
|
42
|
-
| **Autorización** | 75% | 70% | ⚠️ Falta RBAC completo |
|
|
43
|
-
| **Validación Input** | 95% | 85% | ✅ Excelente en crudia-ui |
|
|
44
|
-
| **Almacenamiento Seguro** | 85% | 75% | ⚠️ Claves predecibles |
|
|
45
|
-
| **Protección XSS/CSRF** | 90% | 70% | ⚠️ Falta CSRF en librería |
|
|
46
|
-
| **Gestión de Tokens** | 80% | 80% | ⚠️ Sin refresh tokens |
|
|
47
|
-
|
|
48
|
-
**Fortalezas:**
|
|
49
|
-
|
|
50
|
-
- ✅ **Crudia-UI**: Validación exhaustiva de inputs con DOMPurify
|
|
51
|
-
- ✅ **Ambos**: Almacenamiento encriptado de tokens
|
|
52
|
-
- ✅ **Crudia-UI**: SecureString para manejo de passwords
|
|
53
|
-
|
|
54
|
-
**Vulnerabilidades Identificadas:**
|
|
55
|
-
|
|
56
|
-
- 🔴 **Alto Riesgo**: Error traces pueden filtrar información sensible
|
|
57
|
-
- 🟡 **Medio Riesgo**: Claves de encriptación basadas en fingerprinting predecible
|
|
58
|
-
- 🟡 **Medio Riesgo**: Sin protección CSRF explícita
|
|
59
|
-
- 🟡 **Medio Riesgo**: Falta timeout de sesión automático
|
|
60
|
-
|
|
61
|
-
### ⚡ **SIMPLICIDAD DE IMPLEMENTACIÓN**
|
|
62
|
-
|
|
63
|
-
| **Aspecto** | **Crudia-UI** | **npm-crudify-ui** | **Evaluación** |
|
|
64
|
-
| ------------------------ | ------------- | ------------------ | --------------------------------- |
|
|
65
|
-
| **Setup Inicial** | 60% | 95% | ✅ Librería muy simple de usar |
|
|
66
|
-
| **Configuración** | 70% | 85% | ✅ Múltiples fuentes automáticas |
|
|
67
|
-
| **API Consistency** | 75% | 90% | ✅ Hooks bien diseñados |
|
|
68
|
-
| **TypeScript Support** | 85% | 95% | ✅ Excelente tipado |
|
|
69
|
-
| **Learning Curve** | 65% | 85% | ⚠️ Curva pronunciada en crudia-ui |
|
|
70
|
-
| **Developer Experience** | 70% | 90% | ✅ Buena experiencia en librería |
|
|
71
|
-
|
|
72
|
-
**Fortalezas:**
|
|
73
|
-
|
|
74
|
-
- ✅ **npm-crudify-ui**: Un solo provider unifica todo
|
|
75
|
-
- ✅ **npm-crudify-ui**: Hooks especializados para diferentes casos
|
|
76
|
-
- ✅ **Ambos**: TypeScript first-class support
|
|
77
|
-
|
|
78
|
-
**Complejidades:**
|
|
79
|
-
|
|
80
|
-
- ❌ **Crudia-UI**: Múltiples providers requieren configuración manual
|
|
81
|
-
- ❌ **Crudia-UI**: Patrón complejo de inicialización distribuida
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## 🎯 **LISTADO DE MEJORAS PRIORITARIAS**
|
|
86
|
-
|
|
87
|
-
### 🔴 **PRIORIDAD ALTA (Implementar Inmediatamente)**
|
|
88
|
-
|
|
89
|
-
#### **Seguridad Crítica**
|
|
90
|
-
|
|
91
|
-
1. **🛡️ Sanitización de Errores en Producción**
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
// En lugar de:
|
|
95
|
-
console.error("Error:", error.stack);
|
|
96
|
-
|
|
97
|
-
// Implementar:
|
|
98
|
-
const sanitizedError = process.env.NODE_ENV === "production" ? { message: error.message, code: error.code } : error;
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
2. **🔐 Mejora del Sistema de Encriptación**
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
// Reemplazar fingerprinting predecible con:
|
|
105
|
-
private async generateSecureKey(): Promise<string> {
|
|
106
|
-
const entropy = new Uint8Array(32);
|
|
107
|
-
crypto.getRandomValues(entropy);
|
|
108
|
-
const key = await crypto.subtle.importKey(/* ... */);
|
|
109
|
-
return key;
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
3. **⏱️ Timeout de Sesión Automático**
|
|
114
|
-
```typescript
|
|
115
|
-
const SESSION_TIMEOUT = 30 * 60 * 1000; // 30 minutos
|
|
116
|
-
useEffect(() => {
|
|
117
|
-
const timeout = setTimeout(() => {
|
|
118
|
-
tokenManager.clearToken();
|
|
119
|
-
redirectToLogin();
|
|
120
|
-
}, SESSION_TIMEOUT);
|
|
121
|
-
return () => clearTimeout(timeout);
|
|
122
|
-
}, [lastActivity]);
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
#### **Robustez**
|
|
126
|
-
|
|
127
|
-
4. **📝 Logging Condicional**
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
const logger = {
|
|
131
|
-
debug: (msg: string, data?: any) => {
|
|
132
|
-
if (process.env.NODE_ENV === "development") {
|
|
133
|
-
console.log(`🔄 ${msg}`, data);
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
5. **🔄 Sistema de Refresh Tokens**
|
|
140
|
-
|
|
141
|
-
```typescript
|
|
142
|
-
interface TokenPair {
|
|
143
|
-
accessToken: string;
|
|
144
|
-
refreshToken: string;
|
|
145
|
-
expiresAt: number;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async function refreshTokenIfNeeded(): Promise<string> {
|
|
149
|
-
if (tokenExpiresSoon()) {
|
|
150
|
-
return await refreshAccessToken();
|
|
151
|
-
}
|
|
152
|
-
return currentToken;
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
### 🟡 **PRIORIDAD MEDIA (Siguientes 4-6 Semanas)**
|
|
157
|
-
|
|
158
|
-
#### **Arquitectura y Performance**
|
|
159
|
-
|
|
160
|
-
6. **⚡ React Error Boundaries en Librería**
|
|
161
|
-
|
|
162
|
-
```tsx
|
|
163
|
-
export function CrudifyErrorBoundary({ children }: { children: ReactNode }) {
|
|
164
|
-
return (
|
|
165
|
-
<ErrorBoundary FallbackComponent={CrudifyErrorFallback} onError={handleCrudifyError}>
|
|
166
|
-
{children}
|
|
167
|
-
</ErrorBoundary>
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
7. **🎯 Optimización de Re-renders**
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
const MemoizedComponent = memo(Component);
|
|
176
|
-
const expensiveValue = useMemo(() => computeExpensive(data), [data]);
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
8. **📦 Bundle Size Optimization**
|
|
180
|
-
|
|
181
|
-
```typescript
|
|
182
|
-
// Hacer MUI opcional:
|
|
183
|
-
export const OptionalMuiComponents = {
|
|
184
|
-
get DataGrid() {
|
|
185
|
-
return import("@mui/x-data-grid");
|
|
186
|
-
},
|
|
187
|
-
get Icons() {
|
|
188
|
-
return import("@mui/icons-material");
|
|
189
|
-
},
|
|
190
|
-
};
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
9. **🔍 Sistema de Testing Comprehensivo**
|
|
194
|
-
```typescript
|
|
195
|
-
describe("CrudifyDataProvider", () => {
|
|
196
|
-
it("should initialize correctly", async () => {
|
|
197
|
-
const { result } = renderHook(() => useCrudifyData(), {
|
|
198
|
-
wrapper: TestProviderWrapper,
|
|
199
|
-
});
|
|
200
|
-
await waitFor(() => {
|
|
201
|
-
expect(result.current.isReady).toBe(true);
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
});
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
#### **Seguridad Avanzada**
|
|
208
|
-
|
|
209
|
-
10. **🛡️ Implementación CSRF Protection**
|
|
210
|
-
|
|
211
|
-
```typescript
|
|
212
|
-
const csrfToken = await getCsrfToken();
|
|
213
|
-
const response = await crudify.request({
|
|
214
|
-
headers: { "X-CSRF-Token": csrfToken },
|
|
215
|
-
});
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
11. **👥 Role-Based Access Control (RBAC)**
|
|
219
|
-
```typescript
|
|
220
|
-
export function usePermission(action: string, resource: string) {
|
|
221
|
-
const { user } = useCrudifyAuth();
|
|
222
|
-
return user?.permissions?.includes(`${action}:${resource}`) ?? false;
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### 🟢 **PRIORIDAD BAJA (Futuras Iteraciones)**
|
|
227
|
-
|
|
228
|
-
#### **Developer Experience**
|
|
229
|
-
|
|
230
|
-
12. **📚 JSDoc Comprehensivo**
|
|
231
|
-
|
|
232
|
-
````typescript
|
|
233
|
-
/**
|
|
234
|
-
* Hook for managing CRUD operations with automatic initialization handling
|
|
235
|
-
* @example
|
|
236
|
-
* ```tsx
|
|
237
|
-
* const { readItems, loading } = useCrudifyData();
|
|
238
|
-
* const items = await readItems('products', { active: true });
|
|
239
|
-
* ```
|
|
240
|
-
*/
|
|
241
|
-
export function useCrudifyData(): UseCrudifyDataReturn;
|
|
242
|
-
````
|
|
243
|
-
|
|
244
|
-
13. **🔧 DevTools Integration**
|
|
245
|
-
|
|
246
|
-
```typescript
|
|
247
|
-
const CrudifyDevTools = () => {
|
|
248
|
-
const context = useCrudifyDataContext();
|
|
249
|
-
return <DevToolsPanel context={context} />;
|
|
250
|
-
};
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
14. **📖 Migration Guide Automático**
|
|
254
|
-
```typescript
|
|
255
|
-
export function createMigrationHelper() {
|
|
256
|
-
return {
|
|
257
|
-
detectLegacyUsage: () => {
|
|
258
|
-
/* scan for old patterns */
|
|
259
|
-
},
|
|
260
|
-
suggestUpgrades: () => {
|
|
261
|
-
/* provide upgrade paths */
|
|
262
|
-
},
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
#### **Extensibilidad**
|
|
268
|
-
|
|
269
|
-
15. **🔌 Plugin System**
|
|
270
|
-
|
|
271
|
-
```typescript
|
|
272
|
-
interface CrudifyPlugin {
|
|
273
|
-
name: string;
|
|
274
|
-
version: string;
|
|
275
|
-
install: (crudify: CrudifyInstance) => void;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
crudify.use(auditPlugin);
|
|
279
|
-
crudify.use(cachePlugin);
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
16. **🌐 Advanced Internationalization**
|
|
283
|
-
```typescript
|
|
284
|
-
export function useCrudifyI18n() {
|
|
285
|
-
return {
|
|
286
|
-
t: (key: string) => i18n.t(`crudify.${key}`),
|
|
287
|
-
formatError: (error: CrudifyError) => formatLocalizedError(error),
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
---
|
|
293
|
-
|
|
294
|
-
## 📈 **PROYECCIÓN DE MEJORAS**
|
|
295
|
-
|
|
296
|
-
### **Con Mejoras Prioritarias Implementadas:**
|
|
297
|
-
|
|
298
|
-
| **Criterio** | **Estado Actual** | **Proyección Post-Mejoras** | **Mejora** |
|
|
299
|
-
| ------------------ | ----------------- | --------------------------- | ---------- |
|
|
300
|
-
| **🔒 Robustez** | 82% | **92%** | +10% |
|
|
301
|
-
| **🛡️ Seguridad** | 79% | **91%** | +12% |
|
|
302
|
-
| **⚡ Simplicidad** | 80% | **87%** | +7% |
|
|
303
|
-
| **📊 General** | 80% | **90%** | +10% |
|
|
304
|
-
|
|
305
|
-
### **Cronograma Sugerido:**
|
|
306
|
-
|
|
307
|
-
- **📅 Semana 1-2**: Implementar mejoras de seguridad crítica (#1-3)
|
|
308
|
-
- **📅 Semana 3-4**: Robustez y logging (#4-5)
|
|
309
|
-
- **📅 Mes 2**: Performance y testing (#6-9)
|
|
310
|
-
- **📅 Mes 3**: Seguridad avanzada (#10-11)
|
|
311
|
-
- **📅 Trimestre 2**: Extensibilidad y DX (#12-16)
|
|
312
|
-
|
|
313
|
-
---
|
|
314
|
-
|
|
315
|
-
## 🎉 **CONCLUSIÓN**
|
|
316
|
-
|
|
317
|
-
El sistema **crudia-ui + npm-crudify-ui** presenta una **arquitectura sólida** con un **80% de calidad general**. Las principales fortalezas son el sistema de providers unificado, la robusta gestión de estados y la excelente integración TypeScript.
|
|
318
|
-
|
|
319
|
-
**🚀 Con las mejoras prioritarias implementadas, el sistema alcanzaría un 90% de calidad**, posicionándolo como una solución enterprise-ready para aplicaciones CRUD complejas.
|
|
320
|
-
|
|
321
|
-
---
|
|
322
|
-
|
|
323
|
-
## 📋 **ANÁLISIS TÉCNICO DETALLADO**
|
|
324
|
-
|
|
325
|
-
### **Crudia-UI - Fortalezas Identificadas:**
|
|
326
|
-
|
|
327
|
-
**Arquitectura:**
|
|
328
|
-
|
|
329
|
-
- React Context API usado apropiadamente para estado global
|
|
330
|
-
- Módulo de caching con expiración (5-10 minutos)
|
|
331
|
-
- Persistencia de preferencias de usuario para grid state
|
|
332
|
-
- Debounced data loading para prevenir llamadas API excesivas
|
|
333
|
-
|
|
334
|
-
**Seguridad:**
|
|
335
|
-
|
|
336
|
-
- Validación JWT con verificación de expiración
|
|
337
|
-
- Almacenamiento encriptado usando encrypted session storage
|
|
338
|
-
- Token expiration automatic cleanup
|
|
339
|
-
- Browser fingerprinting para claves de encriptación
|
|
340
|
-
- Validación comprehensiva para todos los tipos de input
|
|
341
|
-
- Prevención XSS usando DOMPurify
|
|
342
|
-
- Validación de email, password, teléfono, URL con reglas de seguridad
|
|
343
|
-
- Validación de file upload (tamaño, tipo)
|
|
344
|
-
- Prevención SQL injection através de consultas parametrizadas
|
|
345
|
-
|
|
346
|
-
**Manejo de Errores:**
|
|
347
|
-
|
|
348
|
-
- Multi-level error boundaries (aplicación, ruta, componente)
|
|
349
|
-
- Sistema comprehensivo de reporte de errores
|
|
350
|
-
- Browser fingerprinting para contexto de errores
|
|
351
|
-
- Utilidades de limpieza de datos sensibles
|
|
352
|
-
- Diferencias entre manejo de errores en desarrollo vs producción
|
|
353
|
-
|
|
354
|
-
### **npm-crudify-ui - Fortalezas Identificadas:**
|
|
355
|
-
|
|
356
|
-
**Inicialización:**
|
|
357
|
-
|
|
358
|
-
- Patrón Singleton para coordinación global
|
|
359
|
-
- Inicialización thread-safe
|
|
360
|
-
- Sistema de configuración con múltiples fuentes (props → env → cookies → defaults)
|
|
361
|
-
- Detección de cambios de configuración y re-inicialización automática
|
|
362
|
-
- Verificación comprehensiva de métodos crudify post-inicialización
|
|
363
|
-
|
|
364
|
-
**Arquitectura de Hooks:**
|
|
365
|
-
|
|
366
|
-
- Sistema de hooks en capas (alto nivel y bajo nivel)
|
|
367
|
-
- Hooks especializados: useCrudifyAuth, useCrudifyData, useCrudifyUser, useCrudifyConfig
|
|
368
|
-
- Verificación automática de inicialización previniendo llamadas API prematuras
|
|
369
|
-
- Error boundaries y degradación elegante
|
|
370
|
-
|
|
371
|
-
**Gestión de Estado:**
|
|
372
|
-
|
|
373
|
-
- Sincronización cross-tab para estado de autenticación
|
|
374
|
-
- Migración automática de localStorage a sessionStorage
|
|
375
|
-
- Limpieza automática de tokens expirados
|
|
376
|
-
- Sistema de configuración con resolución de prioridades
|
|
377
|
-
|
|
378
|
-
### **Vulnerabilidades Específicas Identificadas:**
|
|
379
|
-
|
|
380
|
-
**Alto Riesgo:**
|
|
381
|
-
|
|
382
|
-
1. **Information Disclosure**: Error stack traces pueden contener paths sensibles y data
|
|
383
|
-
2. **Predictable Encryption**: Claves de encriptación basadas en browser fingerprint
|
|
384
|
-
|
|
385
|
-
**Medio Riesgo:**
|
|
386
|
-
|
|
387
|
-
1. **Missing CSRF Protection**: No hay manejo explícito de tokens CSRF
|
|
388
|
-
2. **Session Management**: No hay logout automático por inactividad
|
|
389
|
-
3. **Input Validation**: Algunos edge cases en validación de números
|
|
390
|
-
|
|
391
|
-
**Bajo Riesgo:**
|
|
392
|
-
|
|
393
|
-
1. **Console Logging**: Data sensible visible en modo desarrollo
|
|
394
|
-
2. **Error Reporting**: Información detallada del sistema enviada a servicio externo
|
|
395
|
-
3. **Dependencies**: Algunas dependencias podrían actualizarse para patches de seguridad
|
|
396
|
-
|
|
397
|
-
---
|
|
398
|
-
|
|
399
|
-
## 🔧 **IMPLEMENTACIÓN DETALLADA DE MEJORAS**
|
|
400
|
-
|
|
401
|
-
### **1. Sistema de Logging Condicional (PRIORIDAD ALTA)**
|
|
402
|
-
|
|
403
|
-
**Archivo a modificar:** `src/core/Logger.ts` (nuevo)
|
|
404
|
-
|
|
405
|
-
```typescript
|
|
406
|
-
export enum LogLevel {
|
|
407
|
-
ERROR = 0,
|
|
408
|
-
WARN = 1,
|
|
409
|
-
INFO = 2,
|
|
410
|
-
DEBUG = 3,
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
export class CrudifyLogger {
|
|
414
|
-
private static instance: CrudifyLogger;
|
|
415
|
-
private logLevel: LogLevel;
|
|
416
|
-
|
|
417
|
-
constructor() {
|
|
418
|
-
this.logLevel = process.env.NODE_ENV === "production" ? LogLevel.ERROR : LogLevel.DEBUG;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
static getInstance(): CrudifyLogger {
|
|
422
|
-
if (!CrudifyLogger.instance) {
|
|
423
|
-
CrudifyLogger.instance = new CrudifyLogger();
|
|
424
|
-
}
|
|
425
|
-
return CrudifyLogger.instance;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
private shouldLog(level: LogLevel): boolean {
|
|
429
|
-
return level <= this.logLevel;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
private sanitizeForProduction(data: any): any {
|
|
433
|
-
if (this.logLevel === LogLevel.ERROR) {
|
|
434
|
-
// En producción, solo mostrar información esencial
|
|
435
|
-
if (data instanceof Error) {
|
|
436
|
-
return {
|
|
437
|
-
message: data.message,
|
|
438
|
-
name: data.name,
|
|
439
|
-
code: (data as any).code,
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
// Remover información sensible
|
|
443
|
-
return typeof data === "object" ? "[Object]" : "[Data]";
|
|
444
|
-
}
|
|
445
|
-
return data;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
error(message: string, data?: any): void {
|
|
449
|
-
if (this.shouldLog(LogLevel.ERROR)) {
|
|
450
|
-
console.error(`❌ ${message}`, this.sanitizeForProduction(data));
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
warn(message: string, data?: any): void {
|
|
455
|
-
if (this.shouldLog(LogLevel.WARN)) {
|
|
456
|
-
console.warn(`⚠️ ${message}`, this.sanitizeForProduction(data));
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
info(message: string, data?: any): void {
|
|
461
|
-
if (this.shouldLog(LogLevel.INFO)) {
|
|
462
|
-
console.info(`ℹ️ ${message}`, this.sanitizeForProduction(data));
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
debug(message: string, data?: any): void {
|
|
467
|
-
if (this.shouldLog(LogLevel.DEBUG)) {
|
|
468
|
-
console.log(`🔄 ${message}`, data);
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
export const logger = CrudifyLogger.getInstance();
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
**Uso en archivos existentes:**
|
|
477
|
-
|
|
478
|
-
```typescript
|
|
479
|
-
// Reemplazar todos los console.log/error con:
|
|
480
|
-
import { logger } from "../core/Logger";
|
|
481
|
-
|
|
482
|
-
// En lugar de:
|
|
483
|
-
console.log("🔄 useCrudifyInstance - waitForReady: Starting wait");
|
|
484
|
-
|
|
485
|
-
// Usar:
|
|
486
|
-
logger.debug("useCrudifyInstance - waitForReady: Starting wait");
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
### **2. Mejora del Sistema de Encriptación (PRIORIDAD ALTA)**
|
|
490
|
-
|
|
491
|
-
**Archivo a modificar:** `src/components/CrudifyLogin/utils/secureStorage.ts`
|
|
492
|
-
|
|
493
|
-
```typescript
|
|
494
|
-
export class EnhancedSecureStorage {
|
|
495
|
-
private static instance: EnhancedSecureStorage;
|
|
496
|
-
private encryptionKey: string | null = null;
|
|
497
|
-
|
|
498
|
-
static getInstance(): EnhancedSecureStorage {
|
|
499
|
-
if (!EnhancedSecureStorage.instance) {
|
|
500
|
-
EnhancedSecureStorage.instance = new EnhancedSecureStorage();
|
|
501
|
-
}
|
|
502
|
-
return EnhancedSecureStorage.instance;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
private async generateSecureEncryptionKey(): Promise<string> {
|
|
506
|
-
try {
|
|
507
|
-
// Usar Web Crypto API para generar clave segura
|
|
508
|
-
const keyMaterial = await crypto.subtle.generateKey({ name: "PBKDF2" }, false, ["deriveBits", "deriveKey"]);
|
|
509
|
-
|
|
510
|
-
// Combinar con datos del dispositivo de forma más segura
|
|
511
|
-
const deviceInfo = await this.getSecureDeviceInfo();
|
|
512
|
-
|
|
513
|
-
const key = await crypto.subtle.deriveKey(
|
|
514
|
-
{
|
|
515
|
-
name: "PBKDF2",
|
|
516
|
-
salt: new TextEncoder().encode(deviceInfo),
|
|
517
|
-
iterations: 100000,
|
|
518
|
-
hash: "SHA-256",
|
|
519
|
-
},
|
|
520
|
-
keyMaterial,
|
|
521
|
-
{ name: "AES-GCM", length: 256 },
|
|
522
|
-
true,
|
|
523
|
-
["encrypt", "decrypt"]
|
|
524
|
-
);
|
|
525
|
-
|
|
526
|
-
const exported = await crypto.subtle.exportKey("raw", key);
|
|
527
|
-
return Array.from(new Uint8Array(exported))
|
|
528
|
-
.map((b) => b.toString(16).padStart(2, "0"))
|
|
529
|
-
.join("");
|
|
530
|
-
} catch (error) {
|
|
531
|
-
// Fallback a método anterior si Web Crypto API no está disponible
|
|
532
|
-
logger.warn("Web Crypto API not available, using fallback method");
|
|
533
|
-
return this.generateFallbackKey();
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
private async getSecureDeviceInfo(): Promise<string> {
|
|
538
|
-
const info = [
|
|
539
|
-
navigator.userAgent,
|
|
540
|
-
navigator.language,
|
|
541
|
-
screen.colorDepth,
|
|
542
|
-
screen.width,
|
|
543
|
-
screen.height,
|
|
544
|
-
new Date().getTimezoneOffset(),
|
|
545
|
-
"crudify-secure-v2",
|
|
546
|
-
];
|
|
547
|
-
|
|
548
|
-
// Agregar información adicional si está disponible
|
|
549
|
-
if ("serviceWorker" in navigator) {
|
|
550
|
-
info.push("sw-available");
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
return info.join("|");
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
private generateFallbackKey(): string {
|
|
557
|
-
// Método de fallback mejorado
|
|
558
|
-
const entropy = Math.random().toString(36) + Date.now().toString(36);
|
|
559
|
-
const deviceInfo = [navigator.userAgent, navigator.language, new Date().getTimezoneOffset(), "crudify-fallback"].join("|");
|
|
560
|
-
|
|
561
|
-
return CryptoJS.SHA256(deviceInfo + entropy).toString();
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
async getEncryptionKey(): Promise<string> {
|
|
565
|
-
if (!this.encryptionKey) {
|
|
566
|
-
this.encryptionKey = await this.generateSecureEncryptionKey();
|
|
567
|
-
}
|
|
568
|
-
return this.encryptionKey;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
async setSecureItem(key: string, value: string, ttl?: number): Promise<void> {
|
|
572
|
-
try {
|
|
573
|
-
const encryptionKey = await this.getEncryptionKey();
|
|
574
|
-
|
|
575
|
-
const data = {
|
|
576
|
-
value,
|
|
577
|
-
timestamp: Date.now(),
|
|
578
|
-
ttl: ttl || 24 * 60 * 60 * 1000, // 24 horas por defecto
|
|
579
|
-
version: "2.0", // Para futuras migraciones
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
const encrypted = CryptoJS.AES.encrypt(JSON.stringify(data), encryptionKey).toString();
|
|
583
|
-
|
|
584
|
-
sessionStorage.setItem(`crudify_secure_${key}`, encrypted);
|
|
585
|
-
logger.debug(`Secure item stored: ${key}`);
|
|
586
|
-
} catch (error) {
|
|
587
|
-
logger.error("Failed to store secure item", error);
|
|
588
|
-
throw new Error("Failed to store secure data");
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
async getSecureItem(key: string): Promise<string | null> {
|
|
593
|
-
try {
|
|
594
|
-
const encryptionKey = await this.getEncryptionKey();
|
|
595
|
-
const encrypted = sessionStorage.getItem(`crudify_secure_${key}`);
|
|
596
|
-
|
|
597
|
-
if (!encrypted) {
|
|
598
|
-
return null;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
const decrypted = CryptoJS.AES.decrypt(encrypted, encryptionKey);
|
|
602
|
-
const decryptedString = decrypted.toString(CryptoJS.enc.Utf8);
|
|
603
|
-
|
|
604
|
-
if (!decryptedString) {
|
|
605
|
-
logger.warn(`Failed to decrypt item: ${key}`);
|
|
606
|
-
this.removeSecureItem(key);
|
|
607
|
-
return null;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
const data = JSON.parse(decryptedString);
|
|
611
|
-
|
|
612
|
-
// Verificar TTL
|
|
613
|
-
if (data.ttl && Date.now() - data.timestamp > data.ttl) {
|
|
614
|
-
logger.debug(`Expired item removed: ${key}`);
|
|
615
|
-
this.removeSecureItem(key);
|
|
616
|
-
return null;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
return data.value;
|
|
620
|
-
} catch (error) {
|
|
621
|
-
logger.error("Failed to retrieve secure item", error);
|
|
622
|
-
this.removeSecureItem(key);
|
|
623
|
-
return null;
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
removeSecureItem(key: string): void {
|
|
628
|
-
sessionStorage.removeItem(`crudify_secure_${key}`);
|
|
629
|
-
logger.debug(`Secure item removed: ${key}`);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
clearAllSecureItems(): void {
|
|
633
|
-
const keysToRemove: string[] = [];
|
|
634
|
-
for (let i = 0; i < sessionStorage.length; i++) {
|
|
635
|
-
const key = sessionStorage.key(i);
|
|
636
|
-
if (key && key.startsWith("crudify_secure_")) {
|
|
637
|
-
keysToRemove.push(key);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
keysToRemove.forEach((key) => sessionStorage.removeItem(key));
|
|
642
|
-
logger.debug(`Cleared ${keysToRemove.length} secure items`);
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
export const enhancedSecureStorage = EnhancedSecureStorage.getInstance();
|
|
647
|
-
```
|
|
648
|
-
|
|
649
|
-
### **3. Sistema de Timeout de Sesión (PRIORIDAD ALTA)**
|
|
650
|
-
|
|
651
|
-
**Archivo a modificar:** `src/hooks/useSessionTimeout.ts` (nuevo)
|
|
652
|
-
|
|
653
|
-
```typescript
|
|
654
|
-
import { useEffect, useCallback, useRef } from "react";
|
|
655
|
-
import { logger } from "../core/Logger";
|
|
656
|
-
import { useCrudifyAuth } from "./useCrudifyAuth";
|
|
657
|
-
|
|
658
|
-
interface UseSessionTimeoutOptions {
|
|
659
|
-
timeoutMinutes?: number;
|
|
660
|
-
warningMinutes?: number;
|
|
661
|
-
onWarning?: () => void;
|
|
662
|
-
onTimeout?: () => void;
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
export function useSessionTimeout(options: UseSessionTimeoutOptions = {}) {
|
|
666
|
-
const { timeoutMinutes = 30, warningMinutes = 5, onWarning, onTimeout } = options;
|
|
667
|
-
|
|
668
|
-
const { logout, isAuthenticated } = useCrudifyAuth();
|
|
669
|
-
const timeoutRef = useRef<NodeJS.Timeout>();
|
|
670
|
-
const warningRef = useRef<NodeJS.Timeout>();
|
|
671
|
-
const lastActivityRef = useRef<number>(Date.now());
|
|
672
|
-
|
|
673
|
-
const resetTimeout = useCallback(() => {
|
|
674
|
-
if (!isAuthenticated) return;
|
|
675
|
-
|
|
676
|
-
lastActivityRef.current = Date.now();
|
|
677
|
-
|
|
678
|
-
// Limpiar timeouts existentes
|
|
679
|
-
if (timeoutRef.current) {
|
|
680
|
-
clearTimeout(timeoutRef.current);
|
|
681
|
-
}
|
|
682
|
-
if (warningRef.current) {
|
|
683
|
-
clearTimeout(warningRef.current);
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
// Configurar warning
|
|
687
|
-
const warningMs = (timeoutMinutes - warningMinutes) * 60 * 1000;
|
|
688
|
-
if (warningMs > 0 && onWarning) {
|
|
689
|
-
warningRef.current = setTimeout(() => {
|
|
690
|
-
logger.warn("Session timeout warning triggered");
|
|
691
|
-
onWarning();
|
|
692
|
-
}, warningMs);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
// Configurar timeout
|
|
696
|
-
const timeoutMs = timeoutMinutes * 60 * 1000;
|
|
697
|
-
timeoutRef.current = setTimeout(() => {
|
|
698
|
-
logger.info("Session timeout reached, logging out");
|
|
699
|
-
if (onTimeout) {
|
|
700
|
-
onTimeout();
|
|
701
|
-
}
|
|
702
|
-
logout();
|
|
703
|
-
}, timeoutMs);
|
|
704
|
-
|
|
705
|
-
logger.debug(`Session timeout reset: ${timeoutMinutes} minutes`);
|
|
706
|
-
}, [isAuthenticated, timeoutMinutes, warningMinutes, onWarning, onTimeout, logout]);
|
|
707
|
-
|
|
708
|
-
const handleUserActivity = useCallback(() => {
|
|
709
|
-
resetTimeout();
|
|
710
|
-
}, [resetTimeout]);
|
|
711
|
-
|
|
712
|
-
useEffect(() => {
|
|
713
|
-
if (!isAuthenticated) {
|
|
714
|
-
// Limpiar timeouts si no está autenticado
|
|
715
|
-
if (timeoutRef.current) {
|
|
716
|
-
clearTimeout(timeoutRef.current);
|
|
717
|
-
}
|
|
718
|
-
if (warningRef.current) {
|
|
719
|
-
clearTimeout(warningRef.current);
|
|
720
|
-
}
|
|
721
|
-
return;
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
// Configurar listeners para actividad del usuario
|
|
725
|
-
const events = ["mousedown", "mousemove", "keypress", "scroll", "touchstart", "click"];
|
|
726
|
-
|
|
727
|
-
events.forEach((event) => {
|
|
728
|
-
document.addEventListener(event, handleUserActivity, true);
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
// Inicializar timeout
|
|
732
|
-
resetTimeout();
|
|
733
|
-
|
|
734
|
-
// Cleanup
|
|
735
|
-
return () => {
|
|
736
|
-
events.forEach((event) => {
|
|
737
|
-
document.removeEventListener(event, handleUserActivity, true);
|
|
738
|
-
});
|
|
739
|
-
|
|
740
|
-
if (timeoutRef.current) {
|
|
741
|
-
clearTimeout(timeoutRef.current);
|
|
742
|
-
}
|
|
743
|
-
if (warningRef.current) {
|
|
744
|
-
clearTimeout(warningRef.current);
|
|
745
|
-
}
|
|
746
|
-
};
|
|
747
|
-
}, [isAuthenticated, handleUserActivity, resetTimeout]);
|
|
748
|
-
|
|
749
|
-
return {
|
|
750
|
-
lastActivity: lastActivityRef.current,
|
|
751
|
-
resetTimeout: handleUserActivity,
|
|
752
|
-
timeRemaining: () => {
|
|
753
|
-
const elapsed = Date.now() - lastActivityRef.current;
|
|
754
|
-
const remaining = timeoutMinutes * 60 * 1000 - elapsed;
|
|
755
|
-
return Math.max(0, remaining);
|
|
756
|
-
},
|
|
757
|
-
};
|
|
758
|
-
}
|
|
759
|
-
```
|
|
760
|
-
|
|
761
|
-
**Integración en CrudifyDataProvider:**
|
|
762
|
-
|
|
763
|
-
```typescript
|
|
764
|
-
// En CrudifyDataProvider.tsx agregar:
|
|
765
|
-
import { useSessionTimeout } from "../hooks/useSessionTimeout";
|
|
766
|
-
|
|
767
|
-
export const CrudifyDataProvider: React.FC<CrudifyDataProviderProps> = ({ children, ...config }) => {
|
|
768
|
-
// ... código existente ...
|
|
769
|
-
|
|
770
|
-
// Agregar session timeout
|
|
771
|
-
useSessionTimeout({
|
|
772
|
-
timeoutMinutes: 30,
|
|
773
|
-
warningMinutes: 5,
|
|
774
|
-
onWarning: () => {
|
|
775
|
-
// Mostrar notificación de advertencia
|
|
776
|
-
console.warn("Su sesión expirará en 5 minutos");
|
|
777
|
-
},
|
|
778
|
-
onTimeout: () => {
|
|
779
|
-
// Mostrar notificación de logout
|
|
780
|
-
console.info("Sesión expirada por inactividad");
|
|
781
|
-
},
|
|
782
|
-
});
|
|
783
|
-
|
|
784
|
-
// ... resto del código ...
|
|
785
|
-
};
|
|
786
|
-
```
|
|
787
|
-
|
|
788
|
-
---
|
|
789
|
-
|
|
790
|
-
Estas son las implementaciones detalladas para las 3 mejoras de mayor prioridad. ¿Te gustaría que continúe con las siguientes mejoras o que implemente alguna de estas específicamente?
|