cdd-cli 3.1.2 → 3.1.3
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/AUDIT_REPORT.md +1004 -0
- package/CHANGELOG.md +22 -0
- package/FIXES_APPLIED.md +421 -0
- package/dist/App.js +7 -9
- package/dist/components/ContainerCreationPrompt.js +1 -1
- package/dist/components/ContainerList.js +2 -4
- package/dist/components/ContainerRow.js +20 -14
- package/dist/components/ContainerSection.js +1 -1
- package/dist/helpers/actionHelpers.js +14 -1
- package/dist/helpers/dockerService/dockerService.js +6 -3
- package/dist/helpers/dockerService/serviceComponents/containerActions.js +60 -10
- package/dist/helpers/dockerService/serviceComponents/containerList.js +7 -2
- package/dist/helpers/dockerService/serviceComponents/containerLogs.js +33 -20
- package/dist/helpers/dockerService/serviceComponents/containerStats.js +12 -3
- package/dist/helpers/dockerService/serviceComponents/imageUtils.js +12 -0
- package/dist/helpers/exitWithMessage.js +11 -0
- package/dist/helpers/validationHelpers.js +15 -2
- package/dist/hooks/creation/useContainerCreation.js +2 -6
- package/dist/hooks/useContainers.js +1 -3
- package/dist/hooks/useControls.js +27 -2
- package/dist/hooks/useLogsStream.js +6 -0
- package/dist/index.js +1 -4
- package/package.json +1 -1
- package/src/components/ContainerRow.jsx +17 -5
- package/src/helpers/actionHelpers.js +1 -1
- package/src/helpers/dockerService/dockerService.js +7 -1
- package/src/helpers/dockerService/serviceComponents/containerActions.js +25 -9
- package/src/helpers/dockerService/serviceComponents/containerList.js +1 -1
- package/src/helpers/dockerService/serviceComponents/containerLogs.js +19 -15
- package/src/helpers/dockerService/serviceComponents/containerStats.js +9 -1
- package/src/helpers/validationHelpers.js +14 -2
- package/src/hooks/creation/useContainerCreation.js +2 -6
- package/src/hooks/useControls.js +5 -1
- package/test/validationHelpers.test.js +32 -0
package/AUDIT_REPORT.md
ADDED
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
# Informe de Auditoría de Código - CDD CLI
|
|
2
|
+
|
|
3
|
+
**Fecha:** 2025-10-17
|
|
4
|
+
**Proyecto:** CDD-CLI (CLI Docker Dashboard)
|
|
5
|
+
**Versión:** 3.1.2
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Resumen Ejecutivo
|
|
10
|
+
|
|
11
|
+
Este informe presenta los resultados de una auditoría completa del código fuente del proyecto CDD-CLI. Se identificaron **11 problemas críticos** y **15 mejoras recomendadas** que afectan la seguridad, rendimiento, mantenibilidad y robustez de la aplicación.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. ERRORES CRÍTICOS Y DE SEGURIDAD
|
|
16
|
+
|
|
17
|
+
### 1.1. ❌ Error de Orden de Declaración en `containerActions.js`
|
|
18
|
+
|
|
19
|
+
**Ubicación:** `src/helpers/dockerService/serviceComponents/containerActions.js` (líneas 1-16)
|
|
20
|
+
|
|
21
|
+
**Problema:**
|
|
22
|
+
```javascript
|
|
23
|
+
export async function removeContainer(containerId) {
|
|
24
|
+
const container = docker.getContainer(containerId);
|
|
25
|
+
// ...
|
|
26
|
+
}
|
|
27
|
+
import { docker } from "../dockerService";
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
La función `removeContainer` está declarada **antes** de la importación del módulo `docker`, lo que viola el estándar de ES6 modules y puede causar errores de referencia.
|
|
31
|
+
|
|
32
|
+
**¿Por qué está mal?**
|
|
33
|
+
- Las importaciones deben ir al principio del archivo en ES6 modules
|
|
34
|
+
- Esto puede causar errores en tiempo de ejecución dependiendo del compilador
|
|
35
|
+
- Viola las convenciones de código JavaScript/ES6
|
|
36
|
+
|
|
37
|
+
**Consecuencias:**
|
|
38
|
+
- Error potencial de `ReferenceError` si el código se ejecuta sin transpilación
|
|
39
|
+
- Confusión para otros desarrolladores
|
|
40
|
+
- Problemas de mantenibilidad
|
|
41
|
+
|
|
42
|
+
**Cómo mejorarlo:**
|
|
43
|
+
Mover la importación al inicio del archivo:
|
|
44
|
+
```javascript
|
|
45
|
+
import { docker } from "../dockerService";
|
|
46
|
+
import { imageExists, pullImage } from "./imageUtils.js";
|
|
47
|
+
|
|
48
|
+
export async function removeContainer(containerId) {
|
|
49
|
+
const container = docker.getContainer(containerId);
|
|
50
|
+
// ...
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
### 1.2. ❌ Validación de Puertos Obligatoria Incorrecta
|
|
57
|
+
|
|
58
|
+
**Ubicación:** `src/hooks/creation/useContainerCreation.js` (líneas 44-48)
|
|
59
|
+
|
|
60
|
+
**Problema:**
|
|
61
|
+
```javascript
|
|
62
|
+
if (!portInput.trim()) {
|
|
63
|
+
setMessage("You must specify at least one port to expose (e.g. 8080:80)");
|
|
64
|
+
setMessageColor("red");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
El código obliga al usuario a especificar puertos, pero muchos contenedores Docker **no necesitan** exponer puertos (servicios internos, workers, cron jobs, etc.).
|
|
70
|
+
|
|
71
|
+
**¿Por qué está mal?**
|
|
72
|
+
- No todos los contenedores requieren puertos expuestos
|
|
73
|
+
- Impide crear contenedores válidos sin puertos
|
|
74
|
+
- La lógica contradice el mensaje "Optional" mostrado al usuario
|
|
75
|
+
|
|
76
|
+
**Consecuencias:**
|
|
77
|
+
- Imposibilidad de crear contenedores sin puertos
|
|
78
|
+
- Mala experiencia de usuario
|
|
79
|
+
- Limitación funcional artificial
|
|
80
|
+
|
|
81
|
+
**Cómo mejorarlo:**
|
|
82
|
+
Hacer los puertos verdaderamente opcionales:
|
|
83
|
+
```javascript
|
|
84
|
+
if (portInput.trim() && !validatePorts(portInput)) {
|
|
85
|
+
setMessage("Port format must be host:container and both must be numbers (e.g. 8080:80)");
|
|
86
|
+
setMessageColor("red");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### 1.3. ❌ Falta de Manejo de Errores en `getLogsStream`
|
|
94
|
+
|
|
95
|
+
**Ubicación:** `src/helpers/dockerService/serviceComponents/containerLogs.js` (líneas 11-27)
|
|
96
|
+
|
|
97
|
+
**Problema:**
|
|
98
|
+
```javascript
|
|
99
|
+
export function getLogsStream(containerId, onData, onEnd, onError) {
|
|
100
|
+
const container = docker.getContainer(containerId);
|
|
101
|
+
container.logs({
|
|
102
|
+
follow: true,
|
|
103
|
+
stdout: true,
|
|
104
|
+
stderr: true,
|
|
105
|
+
tail: 100
|
|
106
|
+
}, (err, stream) => {
|
|
107
|
+
if (err) {
|
|
108
|
+
onError?.(err);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
stream.on('data', chunk => onData?.(chunk.toString()));
|
|
112
|
+
stream.on('end', () => onEnd?.());
|
|
113
|
+
stream.on('error', err => onError?.(err));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**¿Por qué está mal?**
|
|
119
|
+
- No hay try-catch para manejar excepciones síncronas
|
|
120
|
+
- Si `docker.getContainer()` falla, no se captura el error
|
|
121
|
+
- Puede causar crashes no manejados
|
|
122
|
+
|
|
123
|
+
**Consecuencias:**
|
|
124
|
+
- Crash de la aplicación si el contenedor no existe
|
|
125
|
+
- Mensajes de error no informativos
|
|
126
|
+
- Mal manejo de condiciones de error
|
|
127
|
+
|
|
128
|
+
**Cómo mejorarlo:**
|
|
129
|
+
```javascript
|
|
130
|
+
export function getLogsStream(containerId, onData, onEnd, onError) {
|
|
131
|
+
try {
|
|
132
|
+
const container = docker.getContainer(containerId);
|
|
133
|
+
container.logs({
|
|
134
|
+
follow: true,
|
|
135
|
+
stdout: true,
|
|
136
|
+
stderr: true,
|
|
137
|
+
tail: 100
|
|
138
|
+
}, (err, stream) => {
|
|
139
|
+
if (err) {
|
|
140
|
+
onError?.(err);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
stream.on('data', chunk => onData?.(chunk.toString()));
|
|
144
|
+
stream.on('end', () => onEnd?.());
|
|
145
|
+
stream.on('error', err => onError?.(err));
|
|
146
|
+
});
|
|
147
|
+
} catch (err) {
|
|
148
|
+
onError?.(err);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### 1.4. ❌ Path del Socket Docker Hardcodeado (Problema de Portabilidad)
|
|
156
|
+
|
|
157
|
+
**Ubicación:** `src/helpers/dockerService/dockerService.js` (línea 2)
|
|
158
|
+
|
|
159
|
+
**Problema:**
|
|
160
|
+
```javascript
|
|
161
|
+
const docker = new Docker({ socketPath: "/var/run/docker.sock" });
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**¿Por qué está mal?**
|
|
165
|
+
- El path `/var/run/docker.sock` solo funciona en Linux/Mac
|
|
166
|
+
- En Windows, Docker usa named pipes o TCP
|
|
167
|
+
- No permite configuración para diferentes entornos
|
|
168
|
+
|
|
169
|
+
**Consecuencias:**
|
|
170
|
+
- La aplicación **no funciona en Windows**
|
|
171
|
+
- Imposibilidad de usar Docker remoto
|
|
172
|
+
- Falta de flexibilidad para diferentes configuraciones
|
|
173
|
+
|
|
174
|
+
**Cómo mejorarlo:**
|
|
175
|
+
```javascript
|
|
176
|
+
import Docker from "dockerode";
|
|
177
|
+
|
|
178
|
+
// Usa la configuración por defecto de dockerode que maneja automáticamente:
|
|
179
|
+
// - /var/run/docker.sock en Linux/Mac
|
|
180
|
+
// - //./pipe/docker_engine en Windows
|
|
181
|
+
// - Variables de entorno DOCKER_HOST, DOCKER_CERT_PATH, etc.
|
|
182
|
+
const docker = new Docker();
|
|
183
|
+
|
|
184
|
+
export { docker };
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
### 1.5. ❌ Race Condition en Stats Fetching
|
|
190
|
+
|
|
191
|
+
**Ubicación:** `src/components/ContainerRow.jsx` (líneas 31-46)
|
|
192
|
+
|
|
193
|
+
**Problema:**
|
|
194
|
+
```javascript
|
|
195
|
+
useEffect(() => {
|
|
196
|
+
if (state !== "running") return;
|
|
197
|
+
const fetchStats = async () => {
|
|
198
|
+
try {
|
|
199
|
+
const s = await getStats(id);
|
|
200
|
+
setStats(s);
|
|
201
|
+
setStatsError("");
|
|
202
|
+
} catch (err) {
|
|
203
|
+
setStats({ cpuPercent: 0, memPercent: 0, netIO: { rx: 0, tx: 0 } });
|
|
204
|
+
setStatsError("Error fetching stats");
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
fetchStats();
|
|
208
|
+
const timer = setInterval(fetchStats, 1500);
|
|
209
|
+
return () => clearInterval(timer);
|
|
210
|
+
}, [id, state]);
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**¿Por qué está mal?**
|
|
214
|
+
- Si el estado del contenedor cambia rápidamente, pueden quedar llamadas pendientes
|
|
215
|
+
- No se cancela la promesa anterior cuando el componente se desmonta
|
|
216
|
+
- Puede causar memory leaks o actualizaciones de estado en componentes desmontados
|
|
217
|
+
|
|
218
|
+
**Consecuencias:**
|
|
219
|
+
- Warning: "Can't perform a React state update on an unmounted component"
|
|
220
|
+
- Posibles memory leaks
|
|
221
|
+
- Consumo innecesario de recursos
|
|
222
|
+
|
|
223
|
+
**Cómo mejorarlo:**
|
|
224
|
+
```javascript
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (state !== "running") return;
|
|
227
|
+
|
|
228
|
+
let isMounted = true;
|
|
229
|
+
|
|
230
|
+
const fetchStats = async () => {
|
|
231
|
+
try {
|
|
232
|
+
const s = await getStats(id);
|
|
233
|
+
if (isMounted) {
|
|
234
|
+
setStats(s);
|
|
235
|
+
setStatsError("");
|
|
236
|
+
}
|
|
237
|
+
} catch (err) {
|
|
238
|
+
if (isMounted) {
|
|
239
|
+
setStats({ cpuPercent: 0, memPercent: 0, netIO: { rx: 0, tx: 0 } });
|
|
240
|
+
setStatsError("Error fetching stats");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
fetchStats();
|
|
246
|
+
const timer = setInterval(fetchStats, 1500);
|
|
247
|
+
|
|
248
|
+
return () => {
|
|
249
|
+
isMounted = false;
|
|
250
|
+
clearInterval(timer);
|
|
251
|
+
};
|
|
252
|
+
}, [id, state]);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
### 1.6. ❌ Falta de Validación de Container Index
|
|
258
|
+
|
|
259
|
+
**Ubicación:** `src/hooks/useControls.js` (múltiples líneas)
|
|
260
|
+
|
|
261
|
+
**Problema:**
|
|
262
|
+
```javascript
|
|
263
|
+
if (input === "l" && containers[selected]) {
|
|
264
|
+
logsViewer.openLogs();
|
|
265
|
+
getLogsStream(
|
|
266
|
+
containers[selected].id,
|
|
267
|
+
// ...
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Aunque hay validación en algunos lugares (`containers[selected]`), no es consistente en todas las operaciones.
|
|
273
|
+
|
|
274
|
+
**¿Por qué está mal?**
|
|
275
|
+
- Si `containers` está vacío o `selected` es inválido, puede causar errores
|
|
276
|
+
- No hay validación consistente en todas las operaciones
|
|
277
|
+
- Puede causar crashes inesperados
|
|
278
|
+
|
|
279
|
+
**Consecuencias:**
|
|
280
|
+
- Posibles errores de tipo `Cannot read property 'id' of undefined`
|
|
281
|
+
- Experiencia de usuario inconsistente
|
|
282
|
+
- Crashes potenciales
|
|
283
|
+
|
|
284
|
+
**Cómo mejorarlo:**
|
|
285
|
+
Agregar validación consistente al inicio de cada operación:
|
|
286
|
+
```javascript
|
|
287
|
+
const container = containers[selected];
|
|
288
|
+
if (!container) {
|
|
289
|
+
actions.setMessage("No container selected");
|
|
290
|
+
actions.setMessageColor("red");
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
### 1.7. ❌ Memory Leak en Log Streaming
|
|
298
|
+
|
|
299
|
+
**Ubicación:** `src/hooks/useControls.js` (líneas 199-208)
|
|
300
|
+
|
|
301
|
+
**Problema:**
|
|
302
|
+
```javascript
|
|
303
|
+
if (input === "l" && containers[selected]) {
|
|
304
|
+
logsViewer.openLogs();
|
|
305
|
+
getLogsStream(
|
|
306
|
+
containers[selected].id,
|
|
307
|
+
(data) => logsViewer.setLogs((prev) => [...prev, ...data.split("\n").filter(Boolean)]),
|
|
308
|
+
() => {},
|
|
309
|
+
(err) => logsViewer.setLogs((prev) => [...prev, `Error: ${err.message}`])
|
|
310
|
+
);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
**¿Por qué está mal?**
|
|
316
|
+
- El stream de logs nunca se cierra explícitamente
|
|
317
|
+
- Los logs se acumulan indefinidamente en memoria (`[...prev, ...]`)
|
|
318
|
+
- No hay límite máximo de logs almacenados
|
|
319
|
+
|
|
320
|
+
**Consecuencias:**
|
|
321
|
+
- Memory leak creciente con el tiempo
|
|
322
|
+
- Posible crash por falta de memoria en streams largos
|
|
323
|
+
- Rendimiento degradado
|
|
324
|
+
|
|
325
|
+
**Cómo mejorarlo:**
|
|
326
|
+
```javascript
|
|
327
|
+
// Mantener referencia al stream para poder cerrarlo
|
|
328
|
+
let logStream = null;
|
|
329
|
+
|
|
330
|
+
// Al abrir logs:
|
|
331
|
+
logsViewer.openLogs();
|
|
332
|
+
logStream = getLogsStream(
|
|
333
|
+
containers[selected].id,
|
|
334
|
+
(data) => {
|
|
335
|
+
logsViewer.setLogs((prev) => {
|
|
336
|
+
const newLogs = [...prev, ...data.split("\n").filter(Boolean)];
|
|
337
|
+
// Limitar a últimas 1000 líneas
|
|
338
|
+
return newLogs.slice(-1000);
|
|
339
|
+
});
|
|
340
|
+
},
|
|
341
|
+
() => {},
|
|
342
|
+
(err) => logsViewer.setLogs((prev) => [...prev, `Error: ${err.message}`])
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
// Al cerrar logs:
|
|
346
|
+
if (logStream) {
|
|
347
|
+
logStream.destroy();
|
|
348
|
+
logStream = null;
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
### 1.8. ❌ Falta de Validación en Variables de Entorno
|
|
355
|
+
|
|
356
|
+
**Ubicación:** `src/helpers/validationHelpers.js` (líneas 13-16)
|
|
357
|
+
|
|
358
|
+
**Problema:**
|
|
359
|
+
```javascript
|
|
360
|
+
export function validateEnvVars(envInput) {
|
|
361
|
+
// Future specific validations
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**¿Por qué está mal?**
|
|
367
|
+
- No valida el formato de las variables de entorno
|
|
368
|
+
- Acepta cualquier entrada, incluso inválida
|
|
369
|
+
- El formato esperado `VAR=value` no se valida
|
|
370
|
+
|
|
371
|
+
**Consecuencias:**
|
|
372
|
+
- Variables de entorno malformadas pasan sin error
|
|
373
|
+
- Fallo silencioso al crear contenedores
|
|
374
|
+
- Mala experiencia de usuario
|
|
375
|
+
|
|
376
|
+
**Cómo mejorarlo:**
|
|
377
|
+
```javascript
|
|
378
|
+
export function validateEnvVars(envInput) {
|
|
379
|
+
if (!envInput || !envInput.trim()) return true; // Empty is valid
|
|
380
|
+
|
|
381
|
+
const vars = envInput.split(",").map(v => v.trim()).filter(Boolean);
|
|
382
|
+
const invalid = vars.find(v => {
|
|
383
|
+
const parts = v.split("=");
|
|
384
|
+
// Must have at least VAR=value format
|
|
385
|
+
if (parts.length < 2) return true;
|
|
386
|
+
const varName = parts[0].trim();
|
|
387
|
+
// Variable names should be alphanumeric with underscores
|
|
388
|
+
if (!/^[A-Z_][A-Z0-9_]*$/i.test(varName)) return true;
|
|
389
|
+
return false;
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
return !invalid;
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
### 1.9. ❌ CPU Stats Calculation Puede Dar Valores Incorrectos
|
|
399
|
+
|
|
400
|
+
**Ubicación:** `src/helpers/dockerService/serviceComponents/containerStats.js` (líneas 13-18)
|
|
401
|
+
|
|
402
|
+
**Problema:**
|
|
403
|
+
```javascript
|
|
404
|
+
const cpuDelta =
|
|
405
|
+
stream.cpu_stats.cpu_usage.total_usage -
|
|
406
|
+
stream.precpu_stats.cpu_usage.total_usage;
|
|
407
|
+
const systemDelta =
|
|
408
|
+
stream.cpu_stats.system_cpu_usage - stream.precpu_stats.system_cpu_usage;
|
|
409
|
+
const cpuPercent = systemDelta > 0 ? (cpuDelta / systemDelta) * 100 : 0;
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
**¿Por qué está mal?**
|
|
413
|
+
- No se multiplica por el número de CPUs
|
|
414
|
+
- El cálculo puede dar valores > 100% en sistemas multi-core
|
|
415
|
+
- No normaliza correctamente el porcentaje
|
|
416
|
+
|
|
417
|
+
**Consecuencias:**
|
|
418
|
+
- Estadísticas de CPU incorrectas/confusas
|
|
419
|
+
- Valores que exceden 100% en sistemas multi-core
|
|
420
|
+
- Información engañosa para el usuario
|
|
421
|
+
|
|
422
|
+
**Cómo mejorarlo:**
|
|
423
|
+
```javascript
|
|
424
|
+
const cpuDelta =
|
|
425
|
+
stream.cpu_stats.cpu_usage.total_usage -
|
|
426
|
+
stream.precpu_stats.cpu_usage.total_usage;
|
|
427
|
+
const systemDelta =
|
|
428
|
+
stream.cpu_stats.system_cpu_usage - stream.precpu_stats.system_cpu_usage;
|
|
429
|
+
|
|
430
|
+
// Número de CPUs
|
|
431
|
+
const numCpus = stream.cpu_stats.online_cpus ||
|
|
432
|
+
stream.cpu_stats.cpu_usage.percpu_usage?.length || 1;
|
|
433
|
+
|
|
434
|
+
// Calcular porcentaje normalizado
|
|
435
|
+
const cpuPercent = systemDelta > 0
|
|
436
|
+
? ((cpuDelta / systemDelta) * numCpus * 100)
|
|
437
|
+
: 0;
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
---
|
|
441
|
+
|
|
442
|
+
### 1.10. ❌ Falta de Timeout en Container Actions
|
|
443
|
+
|
|
444
|
+
**Ubicación:** `src/helpers/dockerService/serviceComponents/containerActions.js` (líneas 59-82)
|
|
445
|
+
|
|
446
|
+
**Problema:**
|
|
447
|
+
```javascript
|
|
448
|
+
export async function startContainer(containerId) {
|
|
449
|
+
const container = docker.getContainer(containerId);
|
|
450
|
+
await container.start();
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
**¿Por qué está mal?**
|
|
455
|
+
- No hay timeout para operaciones que pueden colgarse
|
|
456
|
+
- Un contenedor que no arranca puede dejar la UI bloqueada
|
|
457
|
+
- No hay forma de cancelar operaciones largas
|
|
458
|
+
|
|
459
|
+
**Consecuencias:**
|
|
460
|
+
- UI congelada en operaciones que fallan
|
|
461
|
+
- Imposibilidad de cancelar operaciones largas
|
|
462
|
+
- Mala experiencia de usuario
|
|
463
|
+
|
|
464
|
+
**Cómo mejorarlo:**
|
|
465
|
+
```javascript
|
|
466
|
+
// Helper para añadir timeout
|
|
467
|
+
function withTimeout(promise, ms = 30000) {
|
|
468
|
+
return Promise.race([
|
|
469
|
+
promise,
|
|
470
|
+
new Promise((_, reject) =>
|
|
471
|
+
setTimeout(() => reject(new Error('Operation timed out')), ms)
|
|
472
|
+
)
|
|
473
|
+
]);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export async function startContainer(containerId) {
|
|
477
|
+
const container = docker.getContainer(containerId);
|
|
478
|
+
await withTimeout(container.start(), 30000);
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
### 1.11. ❌ Mensaje de Error Duplicado en `handleAction`
|
|
485
|
+
|
|
486
|
+
**Ubicación:** `src/helpers/actionHelpers.js` (líneas 31-42)
|
|
487
|
+
|
|
488
|
+
**Problema:**
|
|
489
|
+
```javascript
|
|
490
|
+
try {
|
|
491
|
+
await actionFn(c.id);
|
|
492
|
+
setMessage(`${actionLabel} container...`); // ← Mismo mensaje que línea 31
|
|
493
|
+
setMessageColor("green");
|
|
494
|
+
setTimeout(() => setMessage(""), 3000);
|
|
495
|
+
} catch (err) {
|
|
496
|
+
setMessage(`Failed to ${actionLabel.toLowerCase()} container.`);
|
|
497
|
+
setMessageColor("red");
|
|
498
|
+
setTimeout(() => setMessage(""), 3000);
|
|
499
|
+
}
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
**¿Por qué está mal?**
|
|
503
|
+
- El mensaje de éxito es idéntico al mensaje de inicio
|
|
504
|
+
- No informa al usuario que la acción se completó
|
|
505
|
+
- Confunde "iniciando" con "completado"
|
|
506
|
+
|
|
507
|
+
**Consecuencias:**
|
|
508
|
+
- Usuario no sabe si la acción se completó
|
|
509
|
+
- Feedback confuso
|
|
510
|
+
- Mala experiencia de usuario
|
|
511
|
+
|
|
512
|
+
**Cómo mejorarlo:**
|
|
513
|
+
```javascript
|
|
514
|
+
try {
|
|
515
|
+
await actionFn(c.id);
|
|
516
|
+
setMessage(`${actionLabel} container completed successfully`);
|
|
517
|
+
setMessageColor("green");
|
|
518
|
+
setTimeout(() => setMessage(""), 3000);
|
|
519
|
+
}
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
## 2. PROBLEMAS DE CÓDIGO Y MEJORES PRÁCTICAS
|
|
525
|
+
|
|
526
|
+
### 2.1. ⚠️ Uso Inconsistente de Optional Chaining
|
|
527
|
+
|
|
528
|
+
**Ubicación:** Múltiples archivos
|
|
529
|
+
|
|
530
|
+
**Problema:**
|
|
531
|
+
El código usa `?.` en algunos lugares pero no en otros de forma inconsistente.
|
|
532
|
+
|
|
533
|
+
**Impacto:** Código propenso a errores
|
|
534
|
+
**Severidad:** Media
|
|
535
|
+
|
|
536
|
+
**Solución:**
|
|
537
|
+
Usar optional chaining consistentemente donde hay posibilidad de valores null/undefined.
|
|
538
|
+
|
|
539
|
+
---
|
|
540
|
+
|
|
541
|
+
### 2.2. ⚠️ Hardcoded Timeouts y Magic Numbers
|
|
542
|
+
|
|
543
|
+
**Ubicación:** Múltiples archivos
|
|
544
|
+
- `useContainers.js`: `3000` ms (línea 18)
|
|
545
|
+
- `ContainerRow.jsx`: `1500` ms (línea 44)
|
|
546
|
+
- `useControls.js`: `500` ms, `2000` ms, `3000` ms
|
|
547
|
+
|
|
548
|
+
**Problema:**
|
|
549
|
+
Números mágicos dispersos por el código sin constantes nombradas.
|
|
550
|
+
|
|
551
|
+
**Impacto:** Dificulta mantenimiento y ajuste
|
|
552
|
+
**Severidad:** Baja
|
|
553
|
+
|
|
554
|
+
**Solución:**
|
|
555
|
+
```javascript
|
|
556
|
+
// constants.js
|
|
557
|
+
export const REFRESH_INTERVALS = {
|
|
558
|
+
CONTAINER_LIST: 3000,
|
|
559
|
+
CONTAINER_STATS: 1500,
|
|
560
|
+
MESSAGE_TIMEOUT: 3000,
|
|
561
|
+
EXIT_DELAY: 500
|
|
562
|
+
};
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
### 2.3. ⚠️ Falta de PropTypes o TypeScript
|
|
568
|
+
|
|
569
|
+
**Ubicación:** Todos los componentes React
|
|
570
|
+
|
|
571
|
+
**Problema:**
|
|
572
|
+
No hay validación de tipos en props de componentes.
|
|
573
|
+
|
|
574
|
+
**Impacto:** Errores en tiempo de ejecución difíciles de debuggear
|
|
575
|
+
**Severidad:** Media
|
|
576
|
+
|
|
577
|
+
**Solución:**
|
|
578
|
+
Agregar PropTypes o migrar a TypeScript:
|
|
579
|
+
```javascript
|
|
580
|
+
import PropTypes from 'prop-types';
|
|
581
|
+
|
|
582
|
+
ContainerRow.propTypes = {
|
|
583
|
+
container: PropTypes.shape({
|
|
584
|
+
id: PropTypes.string.isRequired,
|
|
585
|
+
name: PropTypes.string.isRequired,
|
|
586
|
+
image: PropTypes.string.isRequired,
|
|
587
|
+
state: PropTypes.string.isRequired,
|
|
588
|
+
ports: PropTypes.array
|
|
589
|
+
}).isRequired
|
|
590
|
+
};
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
---
|
|
594
|
+
|
|
595
|
+
### 2.4. ⚠️ Falta de Tests Unitarios
|
|
596
|
+
|
|
597
|
+
**Ubicación:** General
|
|
598
|
+
|
|
599
|
+
**Problema:**
|
|
600
|
+
Solo hay 1 archivo de tests (validationHelpers.test.js) con 5 tests.
|
|
601
|
+
|
|
602
|
+
**Áreas sin cobertura:**
|
|
603
|
+
- Hooks personalizados
|
|
604
|
+
- Componentes React
|
|
605
|
+
- Servicios Docker
|
|
606
|
+
- Helpers de acciones
|
|
607
|
+
|
|
608
|
+
**Impacto:** Alto riesgo de regresiones
|
|
609
|
+
**Severidad:** Alta
|
|
610
|
+
|
|
611
|
+
**Solución:**
|
|
612
|
+
Agregar tests para:
|
|
613
|
+
- `useContainerActions`
|
|
614
|
+
- `useContainerCreation`
|
|
615
|
+
- `actionHelpers`
|
|
616
|
+
- Componentes críticos
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
### 2.5. ⚠️ Falta de Manejo de Casos Edge
|
|
621
|
+
|
|
622
|
+
**Ubicación:** `src/helpers/dockerService/serviceComponents/containerList.js`
|
|
623
|
+
|
|
624
|
+
**Problema:**
|
|
625
|
+
```javascript
|
|
626
|
+
name: container.Names[0].replace("/", ""),
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
No valida si `Names` está vacío.
|
|
630
|
+
|
|
631
|
+
**Impacto:** Crash potencial si Docker devuelve datos inesperados
|
|
632
|
+
**Severidad:** Media
|
|
633
|
+
|
|
634
|
+
**Solución:**
|
|
635
|
+
```javascript
|
|
636
|
+
name: (container.Names && container.Names[0] || 'Unknown').replace("/", ""),
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
---
|
|
640
|
+
|
|
641
|
+
### 2.6. ⚠️ Callback Hell en useControls
|
|
642
|
+
|
|
643
|
+
**Ubicación:** `src/hooks/useControls.js` (líneas 86-219)
|
|
644
|
+
|
|
645
|
+
**Problema:**
|
|
646
|
+
La función `useInput` tiene 133 líneas con múltiples niveles de anidación.
|
|
647
|
+
|
|
648
|
+
**Impacto:** Difícil de mantener y testear
|
|
649
|
+
**Severidad:** Media
|
|
650
|
+
|
|
651
|
+
**Solución:**
|
|
652
|
+
Extraer handlers a funciones separadas:
|
|
653
|
+
```javascript
|
|
654
|
+
function useControls(containers = []) {
|
|
655
|
+
// ... state ...
|
|
656
|
+
|
|
657
|
+
const handleEraseConfirmation = useCallback((input, key) => {
|
|
658
|
+
// lógica de confirmación
|
|
659
|
+
}, []);
|
|
660
|
+
|
|
661
|
+
const handleLogsInput = useCallback((input, key) => {
|
|
662
|
+
// lógica de logs
|
|
663
|
+
}, []);
|
|
664
|
+
|
|
665
|
+
const handleContainerCreation = useCallback((input, key) => {
|
|
666
|
+
// lógica de creación
|
|
667
|
+
}, []);
|
|
668
|
+
|
|
669
|
+
useInput((input, key) => {
|
|
670
|
+
if (confirmErase) return handleEraseConfirmation(input, key);
|
|
671
|
+
if (logsViewer.showLogs) return handleLogsInput(input, key);
|
|
672
|
+
if (creatingContainer) return handleContainerCreation(input, key);
|
|
673
|
+
// ... navigation and commands
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
---
|
|
679
|
+
|
|
680
|
+
### 2.7. ⚠️ Falta de Logging
|
|
681
|
+
|
|
682
|
+
**Ubicación:** General
|
|
683
|
+
|
|
684
|
+
**Problema:**
|
|
685
|
+
No hay sistema de logging para debuggear problemas.
|
|
686
|
+
|
|
687
|
+
**Impacto:** Difícil diagnosticar problemas en producción
|
|
688
|
+
**Severidad:** Media
|
|
689
|
+
|
|
690
|
+
**Solución:**
|
|
691
|
+
Implementar logger con niveles:
|
|
692
|
+
```javascript
|
|
693
|
+
// logger.js
|
|
694
|
+
export const logger = {
|
|
695
|
+
debug: (msg, ...args) => {
|
|
696
|
+
if (process.env.DEBUG) console.log('[DEBUG]', msg, ...args);
|
|
697
|
+
},
|
|
698
|
+
info: (msg, ...args) => console.log('[INFO]', msg, ...args),
|
|
699
|
+
warn: (msg, ...args) => console.warn('[WARN]', msg, ...args),
|
|
700
|
+
error: (msg, ...args) => console.error('[ERROR]', msg, ...args)
|
|
701
|
+
};
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
---
|
|
705
|
+
|
|
706
|
+
### 2.8. ⚠️ No Hay Manejo de Reconexión Docker
|
|
707
|
+
|
|
708
|
+
**Ubicación:** `src/helpers/dockerService/dockerService.js`
|
|
709
|
+
|
|
710
|
+
**Problema:**
|
|
711
|
+
Si la conexión a Docker se pierde, no hay reintentos ni manejo.
|
|
712
|
+
|
|
713
|
+
**Impacto:** La aplicación se rompe si Docker se reinicia
|
|
714
|
+
**Severidad:** Media
|
|
715
|
+
|
|
716
|
+
**Solución:**
|
|
717
|
+
Implementar lógica de retry con backoff exponencial.
|
|
718
|
+
|
|
719
|
+
---
|
|
720
|
+
|
|
721
|
+
### 2.9. ⚠️ Falta de Internacionalización (i18n)
|
|
722
|
+
|
|
723
|
+
**Ubicación:** Todos los mensajes están hardcodeados en inglés
|
|
724
|
+
|
|
725
|
+
**Problema:**
|
|
726
|
+
```javascript
|
|
727
|
+
setMessage("Container is already running.");
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
**Impacto:** No hay soporte multi-idioma
|
|
731
|
+
**Severidad:** Baja
|
|
732
|
+
|
|
733
|
+
**Solución:**
|
|
734
|
+
Implementar sistema i18n:
|
|
735
|
+
```javascript
|
|
736
|
+
import { t } from './i18n';
|
|
737
|
+
setMessage(t('container.already_running'));
|
|
738
|
+
```
|
|
739
|
+
|
|
740
|
+
---
|
|
741
|
+
|
|
742
|
+
### 2.10. ⚠️ Falta de Documentación de API
|
|
743
|
+
|
|
744
|
+
**Ubicación:** Varios archivos
|
|
745
|
+
|
|
746
|
+
**Problema:**
|
|
747
|
+
Aunque hay JSDoc en algunos lugares, es inconsistente y falta en muchas funciones importantes.
|
|
748
|
+
|
|
749
|
+
**Impacto:** Dificulta onboarding y mantenimiento
|
|
750
|
+
**Severidad:** Baja
|
|
751
|
+
|
|
752
|
+
**Solución:**
|
|
753
|
+
Completar JSDoc en todas las funciones públicas.
|
|
754
|
+
|
|
755
|
+
---
|
|
756
|
+
|
|
757
|
+
### 2.11. ⚠️ No Hay Validación de Imagen Name Format
|
|
758
|
+
|
|
759
|
+
**Ubicación:** `src/hooks/creation/useContainerCreation.js`
|
|
760
|
+
|
|
761
|
+
**Problema:**
|
|
762
|
+
```javascript
|
|
763
|
+
if (!imageName.trim()) {
|
|
764
|
+
setMessage("Image name cannot be empty.");
|
|
765
|
+
setMessageColor("red");
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
Solo valida que no esté vacío, pero no valida el formato.
|
|
771
|
+
|
|
772
|
+
**Impacto:** Fallos al crear contenedores con nombres inválidos
|
|
773
|
+
**Severidad:** Media
|
|
774
|
+
|
|
775
|
+
**Solución:**
|
|
776
|
+
```javascript
|
|
777
|
+
// Validar formato de imagen: [registry/]name[:tag]
|
|
778
|
+
const imageRegex = /^([\w\-\.]+\/)?[\w\-\.]+(:[\w\-\.]+)?$/;
|
|
779
|
+
if (!imageRegex.test(imageName.trim())) {
|
|
780
|
+
setMessage("Invalid image name format. Use: [registry/]name[:tag]");
|
|
781
|
+
setMessageColor("red");
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
---
|
|
787
|
+
|
|
788
|
+
### 2.12. ⚠️ Falta de Accessibility (a11y)
|
|
789
|
+
|
|
790
|
+
**Ubicación:** Componentes de UI
|
|
791
|
+
|
|
792
|
+
**Problema:**
|
|
793
|
+
No hay consideraciones de accesibilidad en la UI de terminal.
|
|
794
|
+
|
|
795
|
+
**Impacto:** Usuarios con screen readers o necesidades especiales no pueden usar la app
|
|
796
|
+
**Severidad:** Baja
|
|
797
|
+
|
|
798
|
+
**Solución:**
|
|
799
|
+
Agregar roles ARIA y mejor soporte de navegación por teclado.
|
|
800
|
+
|
|
801
|
+
---
|
|
802
|
+
|
|
803
|
+
### 2.13. ⚠️ Falta de Rate Limiting en Refresh
|
|
804
|
+
|
|
805
|
+
**Ubicación:** `src/hooks/useContainers.js`
|
|
806
|
+
|
|
807
|
+
**Problema:**
|
|
808
|
+
```javascript
|
|
809
|
+
const timer = setInterval(fetch, 3000);
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
Si `fetch` toma más de 3 segundos, se acumulan llamadas.
|
|
813
|
+
|
|
814
|
+
**Impacto:** Posible sobrecarga del sistema
|
|
815
|
+
**Severidad:** Media
|
|
816
|
+
|
|
817
|
+
**Solución:**
|
|
818
|
+
Usar setTimeout en lugar de setInterval:
|
|
819
|
+
```javascript
|
|
820
|
+
useEffect(() => {
|
|
821
|
+
let timeoutId;
|
|
822
|
+
const fetch = async () => {
|
|
823
|
+
setContainers(await getContainers());
|
|
824
|
+
timeoutId = setTimeout(fetch, 3000);
|
|
825
|
+
};
|
|
826
|
+
fetch();
|
|
827
|
+
return () => clearTimeout(timeoutId);
|
|
828
|
+
}, []);
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
---
|
|
832
|
+
|
|
833
|
+
### 2.14. ⚠️ Estados Intermedios No Manejados
|
|
834
|
+
|
|
835
|
+
**Ubicación:** `src/components/ContainerRow.jsx`
|
|
836
|
+
|
|
837
|
+
**Problema:**
|
|
838
|
+
```javascript
|
|
839
|
+
const stateText = (state) => {
|
|
840
|
+
if (state === "running") return { text: "🟢 RUNNING", color: "green" };
|
|
841
|
+
if (state === "exited") return { text: "🔴 EXITED", color: "red" };
|
|
842
|
+
if (state === "paused") return { text: "🟠 PAUSED", color: "yellow" };
|
|
843
|
+
return { text: state.toUpperCase(), color: "gray" };
|
|
844
|
+
};
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
Faltan estados como: `created`, `restarting`, `removing`, `dead`.
|
|
848
|
+
|
|
849
|
+
**Impacto:** Feedback pobre para estados menos comunes
|
|
850
|
+
**Severidad:** Baja
|
|
851
|
+
|
|
852
|
+
**Solución:**
|
|
853
|
+
Agregar todos los estados posibles de Docker.
|
|
854
|
+
|
|
855
|
+
---
|
|
856
|
+
|
|
857
|
+
### 2.15. ⚠️ Falta de Cleanup en Exit
|
|
858
|
+
|
|
859
|
+
**Ubicación:** `src/hooks/useControls.js`
|
|
860
|
+
|
|
861
|
+
**Problema:**
|
|
862
|
+
```javascript
|
|
863
|
+
if (input === "q") {
|
|
864
|
+
actions.setMessage("Exiting...");
|
|
865
|
+
actions.setMessageColor("yellow");
|
|
866
|
+
setTimeout(() => process.exit(0), 500);
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
```
|
|
870
|
+
|
|
871
|
+
No cierra streams ni limpia recursos antes de salir.
|
|
872
|
+
|
|
873
|
+
**Impacto:** Posibles recursos no liberados
|
|
874
|
+
**Severidad:** Baja
|
|
875
|
+
|
|
876
|
+
**Solución:**
|
|
877
|
+
```javascript
|
|
878
|
+
if (input === "q") {
|
|
879
|
+
actions.setMessage("Exiting...");
|
|
880
|
+
actions.setMessageColor("yellow");
|
|
881
|
+
// Cerrar todos los streams activos
|
|
882
|
+
logsViewer.closeLogs();
|
|
883
|
+
// Cleanup adicional
|
|
884
|
+
setTimeout(() => process.exit(0), 500);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
---
|
|
890
|
+
|
|
891
|
+
## 3. PROBLEMAS DE RENDIMIENTO
|
|
892
|
+
|
|
893
|
+
### 3.1. 🐌 Exceso de Re-renders en ContainerRow
|
|
894
|
+
|
|
895
|
+
**Problema:** Cada actualización de stats causa re-render completo del row.
|
|
896
|
+
|
|
897
|
+
**Solución:** Usar `React.memo()` y separar stats en componente aparte.
|
|
898
|
+
|
|
899
|
+
---
|
|
900
|
+
|
|
901
|
+
### 3.2. 🐌 Polling Innecesario
|
|
902
|
+
|
|
903
|
+
**Problema:** Polling cada 3 segundos incluso cuando la app está en background.
|
|
904
|
+
|
|
905
|
+
**Solución:** Usar Page Visibility API o websockets para actualizaciones en tiempo real.
|
|
906
|
+
|
|
907
|
+
---
|
|
908
|
+
|
|
909
|
+
### 3.3. 🐌 Arrays Spread en Loop
|
|
910
|
+
|
|
911
|
+
**Ubicación:** Log accumulation
|
|
912
|
+
|
|
913
|
+
**Problema:**
|
|
914
|
+
```javascript
|
|
915
|
+
logsViewer.setLogs((prev) => [...prev, ...data.split("\n").filter(Boolean)])
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
Crea nuevo array en cada línea de log.
|
|
919
|
+
|
|
920
|
+
**Solución:** Usar estructura más eficiente o batching.
|
|
921
|
+
|
|
922
|
+
---
|
|
923
|
+
|
|
924
|
+
## 4. RECOMENDACIONES GENERALES
|
|
925
|
+
|
|
926
|
+
### 4.1. Estructura de Proyecto
|
|
927
|
+
- ✅ Buena separación de concerns (components, hooks, helpers)
|
|
928
|
+
- ⚠️ Podría beneficiarse de un directorio `utils/` separado
|
|
929
|
+
- ⚠️ Considerar directorio `constants/` para valores constantes
|
|
930
|
+
|
|
931
|
+
### 4.2. Calidad de Código
|
|
932
|
+
- ✅ Buen uso de hooks personalizados
|
|
933
|
+
- ✅ Código generalmente legible
|
|
934
|
+
- ⚠️ Necesita más comentarios en lógica compleja
|
|
935
|
+
- ⚠️ Algunos archivos muy largos (useControls.js)
|
|
936
|
+
|
|
937
|
+
### 4.3. Testing
|
|
938
|
+
- ❌ Cobertura de tests insuficiente
|
|
939
|
+
- ❌ No hay tests de integración
|
|
940
|
+
- ❌ No hay tests E2E
|
|
941
|
+
|
|
942
|
+
### 4.4. Documentación
|
|
943
|
+
- ✅ README básico útil
|
|
944
|
+
- ⚠️ Falta documentación de arquitectura
|
|
945
|
+
- ⚠️ JSDoc inconsistente
|
|
946
|
+
|
|
947
|
+
### 4.5. Seguridad
|
|
948
|
+
- ⚠️ No hay validación de input de usuario en varios lugares
|
|
949
|
+
- ⚠️ Errores exponen información sensible
|
|
950
|
+
- ⚠️ No hay sanitización de output de Docker
|
|
951
|
+
|
|
952
|
+
---
|
|
953
|
+
|
|
954
|
+
## 5. PLAN DE ACCIÓN PRIORIZADO
|
|
955
|
+
|
|
956
|
+
### Prioridad ALTA (Crítico - resolver inmediatamente)
|
|
957
|
+
1. ✅ Corregir orden de imports en `containerActions.js`
|
|
958
|
+
2. ✅ Hacer puertos opcionales en creación de contenedores
|
|
959
|
+
3. ✅ Agregar manejo de errores en `getLogsStream`
|
|
960
|
+
4. ✅ Configurar Docker socket para cross-platform
|
|
961
|
+
5. ✅ Prevenir race conditions en stats fetching
|
|
962
|
+
|
|
963
|
+
### Prioridad MEDIA (Importante - resolver pronto)
|
|
964
|
+
6. Implementar validación de variables de entorno
|
|
965
|
+
7. Corregir cálculo de CPU stats
|
|
966
|
+
8. Agregar timeouts a operaciones Docker
|
|
967
|
+
9. Implementar límite de logs en memoria
|
|
968
|
+
10. Agregar tests unitarios básicos
|
|
969
|
+
|
|
970
|
+
### Prioridad BAJA (Mejoras - cuando sea posible)
|
|
971
|
+
11. Extraer magic numbers a constantes
|
|
972
|
+
12. Agregar PropTypes o TypeScript
|
|
973
|
+
13. Mejorar logging y debugging
|
|
974
|
+
14. Implementar i18n
|
|
975
|
+
15. Mejorar documentación
|
|
976
|
+
|
|
977
|
+
---
|
|
978
|
+
|
|
979
|
+
## 6. MÉTRICAS DE CÓDIGO
|
|
980
|
+
|
|
981
|
+
- **Líneas de código:** ~1,200
|
|
982
|
+
- **Archivos JavaScript/JSX:** 28
|
|
983
|
+
- **Cobertura de tests:** ~5%
|
|
984
|
+
- **Deuda técnica estimada:** 3-4 semanas de trabajo
|
|
985
|
+
|
|
986
|
+
---
|
|
987
|
+
|
|
988
|
+
## 7. CONCLUSIÓN
|
|
989
|
+
|
|
990
|
+
El proyecto CDD-CLI tiene una base sólida con buena separación de concerns y uso de React hooks. Sin embargo, presenta varios problemas críticos relacionados con:
|
|
991
|
+
|
|
992
|
+
1. **Manejo de errores insuficiente**
|
|
993
|
+
2. **Memory leaks potenciales**
|
|
994
|
+
3. **Falta de validación de inputs**
|
|
995
|
+
4. **Problemas de portabilidad**
|
|
996
|
+
5. **Cobertura de tests mínima**
|
|
997
|
+
|
|
998
|
+
Se recomienda abordar primero los **5 problemas de alta prioridad** antes de considerar nuevas features. Estos problemas pueden causar crashes, memory leaks y una mala experiencia de usuario.
|
|
999
|
+
|
|
1000
|
+
La implementación de las mejoras propuestas mejoraría significativamente la robustez, mantenibilidad y calidad general de la aplicación.
|
|
1001
|
+
|
|
1002
|
+
---
|
|
1003
|
+
|
|
1004
|
+
**Fin del informe de auditoría**
|