# 🚀 PLAN DE IMPLEMENTACIÓN - MEJORAS DE LA APLICACIÓN **Proyecto:** Guía TES Digital **Fecha de Inicio:** 2025-01-07 **Versión del Plan:** 1.0.0 **Duración Total:** 20 semanas (~5 meses) --- ## 📊 RESUMEN EJECUTIVO (1 Página) ### Estado Actual del Proyecto **Calificación General: 6.2/10** ⚠️ **BUENO CON MEJORAS NECESARIAS** | Categoría | Calificación | Estado | Prioridad | |-----------|--------------|--------|-----------| | **Seguridad** | 5.1/10 | 🔴 Vulnerable | 🔴 Crítica | | **Arquitectura** | 6.3/10 | ⚠️ Mejorable | 🟡 Media | | **UX/UI Frontend** | 6.6/10 | ⚠️ Mejorable | 🟡 Media | | **Modernización** | 6.5/10 | ⚠️ Desactualizado | 🟡 Media | | **Limpieza de Código** | 6.0/10 | ⚠️ Necesita limpieza | 🟡 Media | | **Medios/Assets** | 5.0/10 | 🔴 No optimizados | 🔴 Alta | ### Objetivos del Plan **Objetivo Principal:** Convertir el proyecto de "BUENO con mejoras necesarias" a "EXCELENTE y escalable" en 20 semanas. **Metas Específicas:** 1. ✅ **Seguridad:** Subir de 5.1/10 a 9.0/10 (eliminar vulnerabilidades críticas) 2. ✅ **Performance:** Optimizar assets (reducir 30MB → 4MB, -83%) 3. ✅ **Calidad:** Migrar backend a TypeScript (mejorar mantenibilidad) 4. ✅ **Modernización:** Actualizar React 18 → 19, Vite 5 → 7 5. ✅ **Limpieza:** Eliminar código muerto, assets no usados, dependencias obsoletas 6. ✅ **Testing:** Añadir suite de tests (objetivo: 60% cobertura) ### Recursos Estimados | Fase | Duración | Esfuerzo (horas) | Prioridad | ROI | |------|----------|------------------|-----------|-----| | **Fase 1: Estabilización** | 4 semanas | 80-120h | 🔴 Alta | ⭐⭐⭐⭐⭐ | | **Fase 2: Optimización** | 8 semanas | 160-240h | 🟡 Media | ⭐⭐⭐⭐ | | **Fase 3: Escalabilidad** | 8 semanas | 160-240h | 🟢 Media | ⭐⭐⭐ | | **Total** | **20 semanas** | **400-600h** | - | - | ### Riesgos y Mitigación | Riesgo | Probabilidad | Impacto | Mitigación | |--------|--------------|---------|------------| | **Breaking changes en migraciones** | Media | Alto | Feature flags, tests incrementales | | **Tiempo subestimado** | Alta | Medio | Buffer 20% en cada fase | | **Regresiones** | Media | Alto | Tests automatizados antes de cada release | | **Conflicto con features nuevas** | Baja | Medio | Priorizar estabilidad sobre features | --- ## 🗓️ HOJA DE RUTA VISUAL (Timeline) ``` SEMANA │ 1 2 3 4 │ 5 6 7 8 9 10 11 12 │ 13 14 15 16 17 18 19 20 │ ──────────┼──────────────────────────────────────────────────────────────────┤ FASE 1: │██████████████████████████████████████████████████████████████████│ ESTAB. │ Seguridad │ Limpieza │ Tests │ Backend TS │ │ ──────────┼──────────────────────────────────────────────────────────────────┤ FASE 2: │ │████████████████████████████████████████████████████│ OPTIMIZ. │ │ React 19 │ Vite 7 │ Assets │ UX │ Dependencias │ ──────────┼──────────────────────────────────────────────────────────────────┤ FASE 3: │ │ │████████████████████████████████│ ESCALAB. │ │ │ Cache │ Monitoring │ Cloud │ ──────────┼──────────────────────────────────────────────────────────────────┤ MILESTONE │ 🔒 Seguro │ ⚡ Optimizado │ 📈 Escalable │ ``` **Hitos Clave:** - **Semana 4:** ✅ Seguridad crítica resuelta - **Semana 8:** ✅ Backend migrado a TypeScript - **Semana 12:** ✅ Stack actualizado, assets optimizados - **Semana 20:** ✅ Proyecto escalable y monitoreado --- ## ✅ ESTADO ACTUAL (2026-01-19) ### Pendientes prioritarios para continuar el plan **Fase 1 – Seguridad (Semana 1):** - [ ] Validación de `JWT_SECRET` en startup - [ ] Rate limiting en endpoints críticos - [ ] Security headers (Helmet) - [ ] CORS restringido en producción - [ ] Validación de inputs (Zod) en backend - [ ] Sanitización HTML/texto y fix XSS en `MarkdownViewer` - [ ] Verificación HMAC en `webhook-deploy.sh` - [ ] Validación de variables de entorno en backend **Fase 1 – Limpieza (Semana 2):** - [ ] Optimización de imágenes grandes (WebP + PNG comprimido) - [ ] Renombrado de archivos con espacios y actualización de referencias - [ ] Eliminación de carpetas vacías - [ ] Verificación de referencias rotas en guías - [ ] Depuración de dependencias no usadas (depcheck) - [ ] Consolidación de scripts duplicados - [ ] Detección y eliminación de código muerto (ts-prune/unimported) **Fase 1 – Tests (Semana 3):** - [ ] Definir stack de tests definitivo (Jest vs Vitest) y configurar scripts - [ ] Cobertura mínima 20% con tests de componentes/servicios/hooks críticos **Fase 1 – Backend TS (Semana 4):** - [ ] Completar migración TypeScript en backend (rutas y servicios) - [ ] Revisar scripts de build y arranque en producción **Operación / Deploy:** - [ ] Actualizar Node.js del servidor a >= 20.19 para compatibilidad con Vite 7 - [ ] Corregir hook `post-receive` (reset con `--work-tree`) para evitar fallos de despliegue **Contenido clínico pendiente (alto impacto):** - [ ] Completar 8 capítulos placeholder del manual - [ ] Añadir cálculo rápido de Naloxona en checklist de Intoxicaciones - [ ] Añadir guía rápida de peso RN en checklist de Parto --- ## 📋 FASE 1: ESTABILIZACIÓN (Semanas 1-4) ### Objetivo de la Fase **Hacer el proyecto seguro, estable y testeable** **Duración:** 4 semanas **Esfuerzo:** 80-120 horas **Prioridad:** 🔴 ALTA **ROI:** ⭐⭐⭐⭐⭐ (Muy Alto) --- ### Semana 1: Seguridad Crítica (20-30 horas) #### 🔴 Día 1-2: Fixes de Seguridad Inmediatos **Objetivo:** Eliminar vulnerabilidades críticas **Tareas:** - [ ] **JWT Secret Validation** (2 horas) - Ubicación: `backend/src/routes/auth.js:11` - Validar `JWT_SECRET` en startup (no usar fallback débil) - Generar secret seguro: `openssl rand -base64 32` ```javascript // backend/src/config/security.js (NUEVO) import dotenv from 'dotenv'; dotenv.config(); export function validateSecurityConfig() { const JWT_SECRET = process.env.JWT_SECRET; if (!JWT_SECRET || JWT_SECRET === 'emerges-tes-secret-key-change-in-production') { console.error('❌ CRÍTICO: JWT_SECRET no configurado'); console.error(' Generar con: openssl rand -base64 32'); process.exit(1); } const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET; if (!WEBHOOK_SECRET && process.env.NODE_ENV === 'production') { console.error('❌ CRÍTICO: WEBHOOK_SECRET no configurado en producción'); process.exit(1); } return { JWT_SECRET, WEBHOOK_SECRET }; } ``` - [ ] **Rate Limiting** (3 horas) - Ubicación: `backend/src/index.js` - Instalar: `npm install express-rate-limit` - Configurar rate limiters por endpoint ```javascript // backend/src/middleware/rate-limit.js (NUEVO) import rateLimit from 'express-rate-limit'; // General API limiter export const generalLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutos max: 100, // 100 requests por IP message: 'Demasiadas peticiones desde esta IP, intenta en 15 minutos', standardHeaders: true, legacyHeaders: false, }); // Auth endpoints (más estricto) export const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5, // 5 intentos de login por IP message: 'Demasiados intentos de login, intenta en 15 minutos', skipSuccessfulRequests: true, }); // Content creation (medio) export const contentWriteLimiter = rateLimit({ windowMs: 60 * 60 * 1000, // 1 hora max: 20, // 20 creaciones por hora message: 'Límite de creación de contenido alcanzado, intenta en 1 hora', }); ``` - [ ] **Helmet.js Headers** (2 horas) - Instalar: `npm install helmet` - Configurar headers de seguridad ```javascript // backend/src/middleware/security-headers.js (NUEVO) import helmet from 'helmet'; export const securityHeaders = helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], imgSrc: ["'self'", "data:", "https:"], connectSrc: ["'self'"], fontSrc: ["'self'"], objectSrc: ["'none'"], mediaSrc: ["'self'"], frameSrc: ["'none'"], }, }, hsts: { maxAge: 31536000, // 1 año includeSubDomains: true, preload: true, }, crossOriginEmbedderPolicy: false, // Para compatibilidad crossOriginResourcePolicy: { policy: "cross-origin" }, // Para assets }); ``` - [ ] **CORS Fix** (1 hora) - Ubicación: `backend/src/index.js` - Limitar orígenes incluso en desarrollo ```javascript // backend/src/config/cors.js (NUEVO) const isDevelopment = process.env.NODE_ENV !== 'production'; const defaultDevOrigins = ['http://localhost:8096', 'http://localhost:5174']; const allowedOrigins = process.env.CORS_ORIGINS ? process.env.CORS_ORIGINS.split(',') : (isDevelopment ? defaultDevOrigins : []); if (!isDevelopment && allowedOrigins.length === 0) { console.error('❌ CRÍTICO: CORS_ORIGINS no configurado en producción'); process.exit(1); } export const corsConfig = { origin: (origin, callback) => { if (!origin || allowedOrigins.includes(origin)) { callback(null, true); } else { callback(new Error('Origen no permitido por CORS')); } }, credentials: true, optionsSuccessStatus: 200, }; ``` **Deliverables:** - ✅ Validación de secrets en startup - ✅ Rate limiting implementado - ✅ Security headers configurados - ✅ CORS restringido **Checklist de Verificación:** - [ ] App no arranca sin `JWT_SECRET` configurado - [ ] Rate limiting activo en `/api/auth/login` - [ ] Headers de seguridad presentes en respuestas - [ ] CORS solo permite orígenes configurados --- #### 🔴 Día 3-4: Validación de Inputs **Objetivo:** Prevenir inyecciones SQL y XSS **Tareas:** - [ ] **Validación con Zod** (6 horas) - Crear schemas de validación para todos los endpoints - Ubicación: `backend/src/validators/` (NUEVO) ```javascript // backend/src/validators/content.js (NUEVO) import { z } from 'zod'; export const createContentSchema = z.object({ id: z.string().min(1).max(100).regex(/^[a-z0-9-]+$/), type: z.enum(['protocol', 'guide', 'manual', 'checklist']), title: z.string().min(1).max(500), content: z.record(z.any()), // JSONB priority: z.enum(['critica', 'alta', 'media', 'baja']).optional(), category: z.string().max(100).optional(), }); export const updateContentSchema = createContentSchema.partial(); ``` - [ ] **Sanitización de Inputs** (3 horas) - Instalar: `npm install dompurify jsdom` - Sanitizar contenido HTML en frontend ```typescript // src/utils/sanitize.ts (NUEVO) import DOMPurify from 'dompurify'; import { JSDOM } from 'jsdom'; const window = new JSDOM('').window; const purify = DOMPurify(window as any); export function sanitizeHTML(html: string): string { return purify.sanitize(html, { ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'ul', 'ol', 'li', 'h1', 'h2', 'h3'], ALLOWED_ATTR: [], }); } export function sanitizeText(text: string): string { return text .replace(/[<>]/g, '') // Eliminar < > .replace(/javascript:/gi, '') // Eliminar javascript: .trim(); } ``` - [ ] **Fix XSS en MarkdownViewer** (2 horas) - Ubicación: `src/components/content/MarkdownViewer.tsx:278` - Usar `sanitizeHTML` para contenido HTML ```typescript // src/components/content/MarkdownViewer.tsx import { sanitizeHTML } from '@/utils/sanitize'; img: ({ node, src, alt, ...props }: any) => { // Sanitizar src y alt const safeSrc = sanitizeText(src || ''); const safeAlt = sanitizeText(alt || ''); return ( {safeAlt} ); } ``` **Deliverables:** - ✅ Validación Zod en todos los endpoints - ✅ Sanitización de inputs en frontend - ✅ XSS fixes en componentes críticos --- #### 🔴 Día 5: Webhook Security y Environment Variables **Objetivo:** Asegurar scripts de deploy **Tareas:** - [ ] **Webhook HMAC Verification** (4 horas) - Ubicación: `webhook-deploy.sh:10` - Verificar HMAC signature de GitHub ```bash # webhook-deploy.sh (ACTUALIZAR) #!/bin/bash WEBHOOK_SECRET="${WEBHOOK_SECRET}" if [ -z "$WEBHOOK_SECRET" ]; then echo "ERROR: WEBHOOK_SECRET no configurado" exit 1 fi # Leer payload y signature PAYLOAD=$(cat) SIGNATURE=$(echo "$PAYLOAD" | jq -r '.headers["X-Hub-Signature-256"]' 2>/dev/null || echo '') if [ -z "$SIGNATURE" ]; then echo "ERROR: Firma HMAC no encontrada" exit 1 fi # Verificar HMAC EXPECTED_SIGNATURE="sha256=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | cut -d' ' -f2)" if [ "$SIGNATURE" != "$EXPECTED_SIGNATURE" ]; then echo "ERROR: Firma HMAC inválida" exit 1 fi # Proceder con deploy... ``` - [ ] **Environment Variables Validation** (2 horas) - Crear script de validación al startup ```javascript // backend/src/config/env.js (NUEVO) import dotenv from 'dotenv'; dotenv.config(); const requiredEnvVars = [ 'DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASSWORD', 'JWT_SECRET', ]; export function validateEnv() { const missing = requiredEnvVars.filter(key => !process.env[key]); if (missing.length > 0) { console.error('❌ CRÍTICO: Variables de entorno faltantes:'); missing.forEach(key => console.error(` - ${key}`)); process.exit(1); } // Validar formatos if (process.env.JWT_SECRET && process.env.JWT_SECRET.length < 32) { console.error('❌ CRÍTICO: JWT_SECRET debe tener al menos 32 caracteres'); process.exit(1); } console.log('✅ Variables de entorno validadas correctamente'); } ``` **Deliverables:** - ✅ Webhook con verificación HMAC - ✅ Validación de env vars en startup --- ### Semana 2: Limpieza de Código y Assets (20-30 horas) #### 🟡 Día 1-2: Limpieza de Assets **Objetivo:** Optimizar medios y eliminar duplicados **Tareas:** - [ ] **Eliminar dist/assets/** (5 min) ```bash rm -rf dist/assets/ # Verificar que dist/ está en .gitignore ``` - [ ] **Optimizar Imágenes Grandes** (6 horas) - Instalar herramientas: `sudo apt-get install imagemagick webp pngquant` - Optimizar 3 imágenes >5MB en `assets/images/bloque_00/` ```bash # scripts/optimize-images.sh (CREAR) #!/bin/bash SRC_DIR="assets/images/bloque_00" DEST_DIR="public/assets/infografias/bloque-0-fundamentos" mkdir -p "$DEST_DIR" for img in "$SRC_DIR"/*.png; do if [ -f "$img" ]; then filename=$(basename "$img" .png) clean_name=$(echo "$filename" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | tr -d ':' | sed 's/[^a-z0-9-]//g') # Convertir a WebP (85% calidad, max 1920px) convert "$img" -resize 1920x1920> -quality 85 "$DEST_DIR/$clean_name.webp " # PNG optimizado como fallback pngquant --quality=65-80 "$img" --output "$DEST_DIR/$clean_name_optimized.png" || \ convert "$img" -resize 1920x1920> -quality 85 "$DEST_DIR/$clean_name_optimized.png" echo "✅ Optimizado: $filename -> $clean_name.webp" fi done echo "📊 Espacio recuperado: ~15 MB" ``` - [ ] **Renombrar Archivos con Espacios** (2 horas) - Renombrar 4 archivos con espacios ```bash # scripts/fix-asset-names.sh (CREAR) find public/assets -type f -name "* *" | while read file; do newname=$(echo "$file" | tr '[:upper:]' '[:lower:]' | tr ' ' '-' | sed 's/[^a-z0-9-._\/]//g') if [ "$file" != "$newname" ]; then mv "$file" "$newname" echo "✅ Renombrado: $(basename "$file") -> $(basename "$newname")" echo "⚠️ Actualizar referencias en código" fi done ``` - [ ] **Eliminar Carpetas Vacías** (30 min) ```bash find assets public/assets -type d -empty -not -path "./.git/*" -delete ``` - [ ] **Verificar Referencias Rotas** (2 horas) - Verificar imágenes referenciadas en `guia-refuerzo-rcp-adulto-svb.json` que no existen - Añadir placeholders o eliminar referencias **Deliverables:** - ✅ ~30 MB de espacio recuperado - ✅ Imágenes optimizadas (WebP + PNG comprimido) - ✅ Archivos renombrados sin espacios - ✅ Referencias actualizadas --- #### 🟡 Día 3-4: Limpieza de Código **Objetivo:** Eliminar código muerto y archivos innecesarios **Tareas:** - [ ] **Ejecutar depcheck** (1 hora) ```bash # Frontend npx depcheck --ignores="@types/*,eslint-*,typescript-*,@vitejs/*,vite" # Backend cd backend && npx depcheck ``` - [ ] **Eliminar Dependencias No Usadas** (2 horas) - Eliminar dependencias confirmadas no usadas - Actualizar `package.json` - [ ] **Eliminar Archivos .backup** (30 min) ```bash find . -name "*.backup" -not -path "./node_modules/*" -delete ``` - [ ] **Eliminar Scripts Duplicados** (2 horas) - Consolidar `cleanup_completo.sh` y `cleanup_project.sh` - Consolidar `deploy.sh`, `desplegar.sh`, `docker.sh` - Documentar diferencias si todas son necesarias - [ ] **Detectar Código Muerto** (3 horas) ```bash # Detectar exports no usados npx ts-prune | head -50 # Detectar imports no usados npx unimported ``` **Deliverables:** - ✅ Dependencias no usadas eliminadas - ✅ Archivos de backup eliminados - ✅ Scripts consolidados - ✅ Código muerto identificado y eliminado --- ### Semana 3: Test Suite Básico (20-30 horas) #### 🟡 Día 1-3: Setup de Testing **Objetivo:** Implementar infraestructura de tests **Tareas:** - [ ] **Configurar Jest + React Testing Library** (4 horas) - Instalar: `npm install --save-dev jest @testing-library/react @testing-library/jest-dom @testing-library/user-event jest-environment-jsdom` ```json // package.json (ACTUALIZAR) { "scripts": { "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage" }, "jest": { "testEnvironment": "jsdom", "setupFilesAfterEnv": ["/src/test/setup.ts"], "moduleNameMapper": { "^@/(.*)$": "/src/$1" }, "collectCoverageFrom": [ "src/**/*.{ts,tsx}", "!src/**/*.d.ts", "!src/main.tsx", "!src/vite-env.d.ts" ] } } ``` ```typescript // src/test/setup.ts (CREAR) import '@testing-library/jest-dom'; import { expect, afterEach } from 'vitest'; import { cleanup } from '@testing-library/react'; afterEach(() => { cleanup(); }); ``` - [ ] **Tests de Componentes Críticos** (8 horas) - `ErrorBoundary.test.tsx` - `MarkdownViewer.test.tsx` - `ContentAdapter.test.ts` - `ProcedureCard.test.tsx` ```typescript // src/components/ErrorBoundary.test.tsx (CREAR) import { render, screen } from '@testing-library/react'; import ErrorBoundary from './ErrorBoundary'; describe('ErrorBoundary', () => { it('should render children when no error', () => { render(
Test Content
); expect(screen.getByText('Test Content')).toBeInTheDocument(); }); it('should render error UI when error occurs', () => { const ThrowError = () => { throw new Error('Test error'); }; render( ); expect(screen.getByText(/Error al cargar/i)).toBeInTheDocument(); }); }); ``` - [ ] **Tests de Servicios** (4 horas) - `content-adapter.test.ts` - `indexeddb.test.ts` ```typescript // src/services/content-adapter.test.ts (CREAR) import { describe, it, expect, beforeEach } from 'vitest'; import { ContentAdapterFactory } from './content-adapter'; describe('ContentAdapterFactory', () => { beforeEach(() => { // Reset localStorage localStorage.clear(); }); it('should create LocalContentAdapter by default', () => { const adapter = ContentAdapterFactory.create(); expect(adapter).toBeDefined(); expect(adapter.constructor.name).toBe('LocalContentAdapter'); }); it('should create ExternalContentAdapter when API_URL is set', () => { process.env.VITE_API_URL = 'http://localhost:3000'; const adapter = ContentAdapterFactory.create(); expect(adapter.constructor.name).toBe('ExternalContentAdapter'); delete process.env.VITE_API_URL; }); }); ``` - [ ] **Tests de Hooks** (4 horas) - `useFavorites.test.ts` - `useSearchHistory.test.ts` - `usePWAInstall.test.ts` **Deliverables:** - ✅ Jest configurado - ✅ Tests de componentes críticos (objetivo: 10 tests) - ✅ Tests de servicios (objetivo: 5 tests) - ✅ Tests de hooks (objetivo: 5 tests) - ✅ Cobertura objetivo: 20% (mínimo inicial) --- ### Semana 4: Backend TypeScript - Parte 1 (20-30 horas) #### 🟡 Día 1-3: Migración Inicial **Objetivo:** Configurar TypeScript en backend **Tareas:** - [ ] **Configurar TypeScript** (3 horas) - Instalar: `npm install --save-dev typescript @types/node @types/express @types/pg @types/bcrypt @types/jsonwebtoken` - Crear `backend/tsconfig.json` ```json // backend/tsconfig.json (CREAR) { "compilerOptions": { "target": "ES2020", "module": "ES2020", "lib": ["ES2020"], "moduleResolution": "node", "rootDir": "./src", "outDir": "./dist", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "allowSyntheticDefaultImports": true, "declaration": true, "declarationMap": true, "sourceMap": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ``` - [ ] **Migrar Config y Database** (4 horas) - Renombrar: `config/database.js` → `config/database.ts` - Añadir tipos a Pool y queries ```typescript // backend/config/database.ts (MIGRAR) import pg from 'pg'; import dotenv from 'dotenv'; dotenv.config(); const { Pool } = pg; export const pool = new Pool({ host: process.env.DB_HOST || 'localhost', port: parseInt(process.env.DB_PORT || '5432', 10), database: process.env.DB_NAME || 'emerges_tes', user: process.env.DB_USER || 'postgres', password: process.env.DB_PASSWORD || '', max: 20, idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }); export async function query(text: string, params?: any[]): Promise { return await pool.query(text, params); } export async function testConnection(): Promise { try { const result = await query('SELECT NOW()'); console.log('✅ Conexión a PostgreSQL exitosa:', result.rows[0].now); return true; } catch (error) { console.error('❌ Error conectando a PostgreSQL:', (error as Error).message); return false; } } export async function closePool(): Promise { await pool.end(); } ``` - [ ] **Migrar Middleware** (3 horas) - Renombrar: `middleware/auth.js` → `middleware/auth.ts` - Añadir tipos a funciones de autenticación ```typescript // backend/src/middleware/auth.ts (MIGRAR) import { Request, Response, NextFunction } from 'express'; import jwt from 'jsonwebtoken'; import { query } from '../../config/database.js'; import { validateSecurityConfig } from '../config/security.js'; const { JWT_SECRET } = validateSecurityConfig(); export interface AuthRequest extends Request { user?: { id: string; username: string; role: string; }; } export async function authenticate( req: AuthRequest, res: Response, next: NextFunction ): Promise { try { const token = req.headers.authorization?.replace('Bearer ', ''); if (!token) { res.status(401).json({ error: 'Token de autenticación requerido' }); return; } const decoded = jwt.verify(token, JWT_SECRET) as { userId: string }; const result = await query( 'SELECT id, username, role FROM emerges_content.users WHERE id = $1', [decoded.userId] ); if (result.rows.length === 0) { res.status(401).json({ error: 'Usuario no encontrado' }); return; } req.user = result.rows[0]; next(); } catch (error) { res.status(401).json({ error: 'Token inválido' }); } } export function requirePermission(permission: string) { return (req: AuthRequest, res: Response, next: NextFunction): void => { if (!req.user) { res.status(401).json({ error: 'No autenticado' }); return; } // Lógica de permisos (simplificada) if (req.user.role === 'super_admin') { next(); return; } // Verificar permisos específicos según role // TODO: Implementar sistema de permisos completo next(); }; } ``` - [ ] **Actualizar Scripts de Build** (2 horas) - Actualizar `package.json` scripts para compilar TypeScript ```json // backend/package.json (ACTUALIZAR) { "scripts": { "build": "tsc", "dev": "tsc --watch", "start": "node dist/index.js", "start:dev": "tsx src/index.js" }, "devDependencies": { "tsx": "^4.7.0" } } ``` **Deliverables:** - ✅ TypeScript configurado en backend - ✅ Config y Database migrados - ✅ Middleware migrado - ✅ Scripts de build actualizados --- #### 🟡 Día 4-5: Migración de Rutas - Parte 1 **Objetivo:** Migrar rutas críticas a TypeScript **Tareas:** - [ ] **Migrar auth.js** (4 horas) - Renombrar: `routes/auth.js` → `routes/auth.ts` - Añadir tipos a todas las funciones - [ ] **Migrar content.js** (6 horas) - Renombrar: `routes/content.js` → `routes/content.ts` - Añadir tipos a queries y responses - [ ] **Migrar content-pack.js** (3 horas) - Renombrar: `routes/content-pack.js` → `routes/content-pack.ts` **Deliverables:** - ✅ 3 rutas principales migradas a TypeScript - ✅ Tipos añadidos a funciones críticas --- **🎯 MILESTONE SEMANA 4:** Backend parcialmente migrado a TypeScript (50%) --- ## 📋 FASE 2: OPTIMIZACIÓN (Semanas 5-12) ### Objetivo de la Fase **Optimizar performance, actualizar stack y mejorar UX** **Duración:** 8 semanas **Esfuerzo:** 160-240 horas **Prioridad:** 🟡 MEDIA **ROI:** ⭐⭐⭐⭐ (Alto) --- ### Semana 5-6: Backend TypeScript - Parte 2 (40-60 horas) #### Continuación de Migración **Tareas:** - [ ] Migrar rutas restantes (`drugs.js`, `stats.js`, `media.js`, etc.) - [ ] Migrar servicios (`pack-generator.js`, `scorm-generator.js`) - [ ] Migrar utils (si existen) - [ ] Actualizar todos los scripts en `backend/scripts/` **Deliverables:** - ✅ Backend 100% TypeScript - ✅ Type safety completo - ✅ Mejor autocomplete en IDE --- ### Semana 7-8: React 18 → 19 (40-60 horas) #### Actualización de React **Tareas:** - [ ] Actualizar React y React DOM a 19.2.3 - [ ] Actualizar `@types/react` y `@types/react-dom` - [ ] Verificar compatibilidad Radix UI - [ ] Actualizar código que use APIs deprecated - [ ] Implementar nuevas APIs (metadata, forms) donde aplicable - [ ] Testing exhaustivo de componentes **Deliverables:** - ✅ React 19 funcionando - ✅ Nuevas APIs implementadas - ✅ Tests pasando --- ### ✅ Semana 9-10: Vite 5 → 7 - **COMPLETADA** (2026-01-11) #### Actualización de Vite **Tareas:** - ✅ Actualizar Vite a 7.3.1 - ✅ Actualizar `@vitejs/plugin-react-swc` a 4.2.2 - ✅ Actualizar Vitest a 4.0.17 - ✅ Refactorizar plugins personalizados (compatibles con Vite 7) - ✅ Actualizar `vite.config.ts` (añadido dompurify a clasificación) - ✅ Verificar code splitting funciona correctamente **Deliverables:** - ✅ Vite 7.3.1 funcionando - ✅ Build funciona correctamente - ✅ Plugins actualizados y compatibles - ✅ Tests pasando (35 tests) - ✅ Sin errores de lint --- ### ✅ Semana 11-12: Optimización de UX/UI y Dependencias - **COMPLETADA** (2026-01-11) #### Mejoras de UX/UI **Tareas:** - ✅ Crear componente `BaseCard` unificado - ✅ Implementar debounce en búsqueda (hook `useDebounce`) - ✅ Añadir aria-labels a botones (Index, DecisionTreeViewer, Ictus) - ✅ Optimizar re-renders con `React.memo` (DrugCard, ProcedureCard, TESMedicationCard, GuideCard) - ✅ Implementar lazy loading de imágenes (ya implementado con `loading="lazy"`) - ⏸️ Mejorar estados de carga/error consistentes (pendiente para futura iteración) #### Actualización de Dependencias **Tareas:** - ⏸️ Actualizar Zod 3 → 4 (requiere análisis de breaking changes, pendiente) - ✅ Actualizar dependencias menores (patches): @tanstack/react-query, react-hook-form, typescript-eslint - ✅ Verificar compatibilidad (tests pasando: 35 tests) - ✅ Testing de compatibilidad **Deliverables:** - ✅ Componente BaseCard unificado creado - ✅ UX mejorada (debounce, aria-labels, optimizaciones) - ✅ Dependencias menores actualizadas - ⏸️ Zod 3 → 4 pendiente (requiere migración cuidadosa) --- **🎯 MILESTONE SEMANA 12:** Stack actualizado, performance optimizado --- ## 📋 FASE 3: ESCALABILIDAD (Semanas 13-20) ### Objetivo de la Fase **Preparar para escalabilidad y monitoreo** **Duración:** 8 semanas **Esfuerzo:** 160-240 horas **Prioridad:** 🟢 MEDIA **ROI:** ⭐⭐⭐ (Moderado) --- ### ✅ Semana 13-14: Caché y Optimización de BD - **COMPLETADA** (2026-01-11) #### Implementar Redis Cache **Tareas:** - ✅ Instalar cliente: `npm install ioredis @types/ioredis` - ✅ Crear servicio de caché (`backend/src/services/cache.ts`) - ✅ Implementar caché para Content Pack (TTL: 1 hora) - ✅ Implementar caché para stats (TTL: 5 minutos) - todos los endpoints - ✅ Añadir invalidación de caché al crear/actualizar contenido ```javascript // backend/src/services/cache.js (NUEVO) import Redis from 'ioredis'; const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379'); export async function get(key: string): Promise { const value = await redis.get(key); return value ? JSON.parse(value) : null; } export async function set(key: string, value: any, ttlSeconds: number = 3600): Promise { await redis.setex(key, ttlSeconds, JSON.stringify(value)); } export async function invalidate(pattern: string): Promise { const keys = await redis.keys(pattern); if (keys.length > 0) { await redis.del(...keys); } } ``` #### Optimización de Queries **Tareas:** - [ ] Analizar queries lentas con `EXPLAIN ANALYZE` - [ ] Añadir índices faltantes si necesario - [ ] Optimizar queries N+1 en stats - [ ] Implementar paginación eficiente **Deliverables:** - ✅ Redis cache implementado - ✅ Queries optimizadas - ✅ Response times mejorados (~50% más rápido) --- ### ✅ Semana 15-16: Monitoring y Logging - **COMPLETADA** (2026-01-11) #### Implementar Logging Estructurado **Tareas:** - ✅ Instalar Winston: `npm install winston winston-daily-rotate-file` - ✅ Configurar logging por niveles (debug, info, warn, error) - ✅ Añadir logging a endpoints críticos (content, auth, stats) - ✅ Configurar rotación de logs (diaria, con compresión, retención 7-14 días) - ✅ Middleware de request logging implementado ```javascript // backend/src/utils/logger.js (NUEVO) import winston from 'winston'; export const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), transports: [ new winston.transports.File({ filename: 'logs/error.log', level: 'error' }), new winston.transports.File({ filename: 'logs/combined.log' }), new winston.transports.Console({ format: winston.format.simple() }) ], }); ``` #### Health Checks Avanzados **Tareas:** - ✅ Implementar health check endpoint detallado (`/api/health/detailed`) - ✅ Verificar conexión a BD (con tiempo de respuesta) - ✅ Verificar conexión a Redis (con tiempo de respuesta) - ✅ Verificar espacio en disco (con alertas si >90%) - ✅ Añadir métricas básicas (memoria, uptime, timestamp) **Deliverables:** - ✅ Logging estructurado implementado - ✅ Health checks avanzados implementados - ✅ Rotación automática de logs configurada - ✅ Logs estructurados en formato JSON - ✅ Métricas básicas disponibles --- ### ✅ Semana 17-18: CDN y Optimización de Assets - **COMPLETADA** (2026-01-11) #### Configurar CDN **Tareas:** - ⏸️ Configurar Cloudflare o similar (pendiente - requiere configuración externa) - ✅ Configurar cache headers apropiados (en service worker) - ✅ Implementar service worker para offline (ya implementado) - ⏸️ Optimizar imágenes restantes (pendiente - requiere herramientas externas) #### Performance Frontend **Tareas:** - ✅ Implementar code splitting más granular (ya implementado con lazy loading) - ✅ Añadir preloading de recursos críticos (`src/utils/preload.ts`) - ✅ Optimizar bundle size (minificación, assets inline < 4KB) - ✅ Implementar lazy loading de rutas (ya implementado) - ✅ Prefetch de rutas en hover (`useRoutePrefetch` hook) - ✅ Optimizar configuración de React Query (staleTime, gcTime, retry) **Deliverables:** - ✅ Preloading de recursos críticos implementado - ✅ Prefetch de rutas implementado - ✅ Bundle size optimizado - ✅ React Query optimizado - ⏸️ CDN pendiente (requiere servicio externo) --- ### Semana 19-20: Documentación y Cierre (40-60 horas) #### Documentación Técnica **Tareas:** - [ ] Documentar arquitectura actualizada - [ ] Crear guías de deployment - [ ] Documentar APIs - [ ] Crear guías de contribución #### Cierre de Fase **Tareas:** - [ ] Revisión completa de todas las mejoras - [ ] Testing end-to-end - [ ] Documentar lecciones aprendidas - [ ] Planificar siguientes pasos **Deliverables:** - ✅ Documentación completa - ✅ Proyecto estable y escalable - ✅ Plan de mantenimiento --- **🎯 MILESTONE SEMANA 20:** Proyecto escalable y monitoreado --- ## ✅ CHECKLISTS DIARIOS/SEMANALES ### Checklist Diario (5-10 min) ``` [ ] ✅ Ejecutar tests: npm test [ ] ✅ Verificar que build funciona: npm run build [ ] ✅ Revisar logs de errores: tail -f logs/error.log [ ] ✅ Verificar que app arranca: npm run dev [ ] ✅ Commits pequeños y descriptivos ``` ### Checklist Semanal (30-60 min) ``` [ ] ✅ Revisar dependencias vulnerables: npm audit [ ] ✅ Verificar dependencias no usadas: npx depcheck [ ] ✅ Revisar código muerto: npx ts-prune | head -20 [ ] ✅ Ejecutar lint: npm run lint [ ] ✅ Actualizar documentación de cambios [ ] ✅ Revisar métricas de performance [ ] ✅ Backup de base de datos (si aplica) ``` ### Checklist por Milestone **Milestone 1 (Semana 4):** - [ ] ✅ Todas las vulnerabilidades críticas resueltas - [ ] ✅ Rate limiting activo - [ ] ✅ Security headers configurados - [ ] ✅ Test suite básico funcionando (20% cobertura) - [ ] ✅ Backend parcialmente migrado a TypeScript **Milestone 2 (Semana 12):** - [ ] ✅ Backend 100% TypeScript - [ ] ✅ React 19 funcionando - [ ] ✅ Vite 7 funcionando - [ ] ✅ Assets optimizados (30MB → 4MB) - [ ] ✅ UX mejorada (componentes unificados) **Milestone 3 (Semana 20):** - [ ] ✅ Redis cache implementado - [ ] ✅ Logging estructurado activo - [ ] ✅ Monitoring configurado - [ ] ✅ CDN configurado - [ ] ✅ Documentación completa --- ## 📊 SISTEMA DE SEGUIMIENTO ### Métricas de Progreso **Métricas Técnicas:** - Cobertura de tests: 0% → 60% objetivo - Vulnerabilidades críticas: 6 → 0 - Bundle size: Actual → -30% objetivo - Response time API: Actual → -50% objetivo - Código TypeScript: 50% → 100% **Métricas de Calidad:** - Code quality score: 6.2/10 → 8.5/10 objetivo - Deuda técnica: Alta → Media - Documentación: Básica → Completa ### Dashboard de Progreso ```typescript // Crear: scripts/progress-dashboard.ts (OPCIONAL) // Dashboard simple para tracking de progreso interface ProgressMetric { name: string; current: number; target: number; unit: string; } const metrics: ProgressMetric[] = [ { name: 'Test Coverage', current: 0, target: 60, unit: '%' }, { name: 'Critical Vulnerabilities', current: 6, target: 0, unit: '' }, { name: 'TypeScript Coverage (Backend)', current: 50, target: 100, unit: '%' }, { name: 'Assets Size', current: 36, target: 4, unit: 'MB' }, ]; // Generar reporte semanal ``` ### Reportes Semanales **Formato de Reporte Semanal:** ```markdown # Reporte Semanal - Semana X ## ✅ Completado - [Lista de tareas completadas] ## 🚧 En Progreso - [Lista de tareas en progreso] ## ⚠️ Bloqueos - [Lista de bloqueos y cómo resolverlos] ## 📊 Métricas - Test Coverage: X% (↑ Y% desde última semana) - Vulnerabilidades: X (↓ Y desde última semana) - Assets Size: X MB (↓ Y MB desde última semana) ## 🎯 Próxima Semana - [Objetivos para próxima semana] ``` --- ## 📝 PLANTILLAS REUTILIZABLES ### Plantilla de Pull Request ```markdown ## Descripción [Descripción breve de los cambios] ## Tipo de Cambio - [ ] 🐛 Bug fix - [ ] ✨ Nueva feature - [ ] 🔧 Refactor - [ ] 📝 Documentación - [ ] ⚡ Performance - [ ] 🔒 Seguridad ## Checklist - [ ] Tests añadidos/actualizados - [ ] Documentación actualizada - [ ] Build pasa correctamente - [ ] Lint pasa correctamente - [ ] No hay vulnerabilidades nuevas (npm audit) - [ ] Cambios probados localmente ## Screenshots (si aplica) [Capturas de pantalla] ## Testing [Descripción de cómo probar los cambios] ``` ### Plantilla de Issue ```markdown ## Descripción [Descripción del problema o feature] ## Prioridad - [ ] 🔴 Crítica - [ ] 🟡 Alta - [ ] 🟢 Media - [ ] ⚪ Baja ## Criterios de Aceptación - [ ] [Criterio 1] - [ ] [Criterio 2] - [ ] [Criterio 3] ## Archivos Afectados - `ruta/archivo.ts` ## Esfuerzo Estimado [X horas] ``` ### Plantilla de Commit ``` ():