✨ Características: - 45 ejercicios universitarios (Basic → Advanced) - Renderizado LaTeX profesional - IA generativa (Z.ai/DashScope) - Docker 9 servicios - Tests 123/123 pasando - Seguridad enterprise (JWT, XSS, Rate limiting) 🐳 Infraestructura: - Next.js 14 + Node.js 20 - PostgreSQL 15 + Redis 7 - Docker Compose completo - Nginx + SSL ready 📚 Documentación: - 5 informes técnicos completos - README profesional - Scripts de deployment automatizados Estado: Producción lista ✅
14 KiB
14 KiB
Arquitectura del Sistema
Stack Tecnológico
Frontend
- Framework: Next.js 14 (App Router)
- Lenguaje: TypeScript 5.4 (strict mode)
- Estilos: Tailwind CSS 3.4 + shadcn/ui
- State Management: Zustand 4.5
- HTTP Client: Axios 1.6
- Validación: Zod 3.22 + react-hook-form
- Testing: Vitest 1.3 + React Testing Library 14
- Math Rendering: KaTeX 0.16 + react-katex
Backend
- Runtime: Node.js 20 LTS
- Framework: Express.js 4.18
- Lenguaje: TypeScript 5.4
- ORM: Prisma 5.11
- Database: PostgreSQL 15
- Cache/Queue: Redis 7
- Auth: JWT (jsonwebtoken) + bcrypt
- Validation: Zod 3.22
- Logging: Winston 3.12 (JSON structured)
- Rate Limiting: express-rate-limit + rate-limit-redis
- Security: Helmet.js, CORS, DOMPurify
- Testing: Vitest 1.3 + Supertest 6.3
Infrastructure
- Container: Docker 24 + Docker Compose
- Reverse Proxy: Nginx 1.24
- Queue System: Bull 4.12 + Redis
- AI Service: MiniMax-M2.5 (Aliyun DashScope)
- Notifications: Telegram Bot API
- PDF Processing: pdf-parse + pdf2pic
Patrones de Diseño
1. Repository Pattern
Controller → Service → Repository → Database
Flujo de datos:
- Controller: Maneja HTTP requests/responses, validación de entrada
- Service: Lógica de negocio, coordinación entre repositorios
- Repository: Acceso a datos, queries Prisma
- Database: PostgreSQL con Prisma ORM
Ejemplo:
// ExerciseController.ts
async getExercise(req: Request, res: Response) {
const { id } = req.params;
const exercise = await this.exerciseService.getById(id);
res.json({ success: true, data: exercise });
}
// ExerciseService.ts
async getById(id: string) {
const exercise = await this.exerciseRepository.findById(id);
if (!exercise) throw new NotFoundError('Exercise not found');
return exercise;
}
// ExerciseRepository.ts
async findById(id: string) {
return prisma.exercise.findUnique({
where: { id },
include: { topic: true, module: true }
});
}
2. Dependency Injection
Usando TSyringe para inversión de control:
// Container setup
container.register<IExerciseRepository>('ExerciseRepository', {
useClass: ExerciseRepository
});
container.register<ExerciseService>('ExerciseService', {
useClass: ExerciseService
});
// Controller injection
@injectable()
class ExerciseController {
constructor(
@inject('ExerciseService') private service: ExerciseService
) {}
}
3. Middleware Pipeline
Request → Security → Auth → Validation → Rate Limit → Handler → Response
Orden de middlewares:
- Helmet - Security headers
- CORS - Cross-origin handling
- Compression - gzip/deflate
- Request ID - Correlation ID injection
- Logging - Request/response logging
- Auth - JWT verification
- Rate Limit - Throttling
- Validation - Zod schema validation
- Handler - Route controller
- Error Handler - Centralized error handling
Estructura de Módulos
Backend Modules
src/modules/
├── auth/ # Autenticación y autorización
│ ├── controllers/
│ ├── services/
│ ├── repositories/
│ ├── middleware/
│ └── types/
├── exercise/ # Gestión de ejercicios
│ ├── controllers/
│ ├── services/
│ ├── repositories/
│ └── types/
├── module/ # Módulos pedagógicos
├── topic/ # Temas de estudio
├── progress/ # Progreso del usuario
├── ranking/ # Sistema de rankings
├── achievement/ # Logros y gamificación
├── pdf/ # Procesamiento de PDFs
├── notification/ # Notificaciones (Telegram)
└── ai/ # Integración con AI
Frontend Structure
src/
├── app/ # Next.js App Router
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page
│ ├── auth/ # Auth routes
│ ├── dashboard/ # Dashboard routes
│ ├── modules/ # Module routes
│ ├── exercises/ # Exercise routes
│ └── api/ # API routes
├── components/
│ ├── ui/ # shadcn/ui components
│ ├── math/ # Math components (KaTeX)
│ ├── auth/ # Auth components
│ ├── exercises/ # Exercise components
│ └── layout/ # Layout components
├── lib/
│ ├── api.ts # API client
│ ├── utils.ts # Utilities
│ ├── validators.ts # Zod schemas
│ └── constants.ts # App constants
├── store/
│ ├── useAuthStore.ts # Auth state
│ ├── useModuleStore.ts # Module state
│ └── useExerciseStore.ts # Exercise state
├── hooks/
│ ├── useAuth.ts # Auth hook
│ └── useProgress.ts # Progress hook
└── types/
└── index.ts # TypeScript definitions
Seguridad
Autenticación
-
Login Flow:
- Client envía credentials
- Server valida con bcrypt
- Genera access token (15 min) + refresh token (7 días)
- Refresh tokens almacenados en Redis blacklist
- JWT firmado con HS256 y secret seguro
-
Token Refresh:
- Access token expira después de 15 minutos
- Client usa refresh token para obtener nuevo access token
- Refresh token rota en cada uso
- Tokens antiguos agregados a blacklist
-
Logout:
- Agrega refresh token a Redis blacklist
- Client elimina tokens del storage
- TTL en Redis coincide con expiración del token
Autorización
-
RBAC (Role-Based Access Control):
- Roles: USER, TEACHER, ADMIN
- Permisos definidos por rol
- Middleware
requireAdminpara rutas sensibles
-
Resource Ownership:
- Verificación de ownership en recursos
- Usuarios solo acceden a sus propios datos
- Teachers pueden ver datos de sus estudiantes
Protección Web
-
XSS Protection:
- DOMPurify para sanitización de LaTeX
- Helmet.js headers (CSP, X-Frame-Options)
- Escape de output en templates
-
CSRF Protection:
- Tokens CSRF en formularios
- Validación de Origin header
- SameSite cookies
-
SQL Injection:
- Uso exclusivo de Prisma ORM
- No queries raw sin validación
- Prepared statements automáticos
-
Input Validation:
- Zod schemas en todos los endpoints
- Validación de tipo y formato
- Sanitización de input
Escalabilidad
Horizontal Scaling
- Docker Swarm / Kubernetes ready
- Stateless backend design - No session state en servidor
- Redis Cluster para sessions y cache
- PostgreSQL Read Replicas para queries de lectura
Database Optimization
-
Connection Pooling:
- PgBouncer para pooling de conexiones
- Prisma connection limits configurados
- Min/Max connections según carga
-
Query Optimization:
- Índices en campos de búsqueda frecuente
- Select fields específicos (no SELECT *)
- Caching de queries frecuentes en Redis
-
Read Replicas:
- Replicas para queries de lectura
- Master para writes
- Prisma read replica strategy
Caching Strategy
-
Redis Cache:
- Session storage (JWT blacklist)
- Exercise data (TTL: 1 hora)
- Progress data (TTL: 5 minutos)
- Rankings (TTL: 1 minuto)
-
CDN:
- Assets estáticos en CDN
- KaTeX fonts y CSS
- Imágenes y PDFs
Workers y Queue System
Bull Queue Configuration
const exerciseQueue = new Bull('exercise-generation', {
redis: { host: REDIS_HOST, port: REDIS_PORT },
defaultJobOptions: {
attempts: 3,
backoff: { type: 'exponential', delay: 2000 },
removeOnComplete: 100,
removeOnFail: 50
}
});
Workers
-
PDF Worker:
- Procesa PDFs de /app/pdfs
- Extrae texto con pdf-parse
- Genera imágenes con pdf2pic
- Almacena metadata en ProcessedPdf
-
Exercise Worker:
- Genera ejercicios con AI
- Conecta a MiniMax-M2.5 API
- Valida notación matemática
- Guarda en database
-
Notification Worker:
- Envía notificaciones Telegram
- Procesa cola de notificaciones
- Rate limiting para bot API
Escalado de Workers
# Escalar a 3 workers
docker-compose up -d --scale exercise-worker=3
# Load balancing automático por Bull
# Cada worker procesa jobs de la cola
Observabilidad
Logging
Winston Configuration:
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
defaultMeta: {
service: 'math-platform-api',
version: process.env.npm_package_version
},
transports: [
new winston.transports.File({ filename: 'logs/error.log', level: 'error' }),
new winston.transports.File({ filename: 'logs/combined.log' })
]
});
Correlation IDs:
- UUID generado en middleware
- Propagado a todos los servicios
- Incluido en logs y respuestas de error
Health Checks
// Health check endpoint
app.get('/health', async (req, res) => {
const checks = {
database: await checkDatabase(),
redis: await checkRedis(),
ai: await checkAIService()
};
const healthy = Object.values(checks).every(c => c.status === 'up');
res.status(healthy ? 200 : 503).json({
status: healthy ? 'healthy' : 'unhealthy',
timestamp: new Date().toISOString(),
version: process.env.npm_package_version,
checks
});
});
Docker Health Checks
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Flujo de Datos
Autenticación
┌─────────┐ ┌──────────┐ ┌────────────┐ ┌──────────┐
│ Client │────▶│ Nginx │────▶│ Backend │────▶│ PostgreSQL│
└─────────┘ └──────────┘ └────────────┘ └──────────┘
│
▼
┌──────────┐
│ Redis │
│ Blacklist│
└──────────┘
Ejercicio Attempt
┌─────────┐ ┌──────────┐ ┌─────────────┐
│ Client │────▶│ Backend │────▶│ Validation │
└─────────┘ └──────────┘ └─────────────┘
│
┌─────────────────┼─────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ PostgreSQL│ │ Redis │ │ AI API │
│ Score │ │ Progress │ │ Validate │
└──────────┘ └──────────┘ └──────────┘
AI Exercise Generation
┌─────────┐ ┌──────────┐ ┌───────────────┐
│ Client │────▶│ Backend │────▶│ Bull Queue │
└─────────┘ └──────────┘ └───────────────┘
│
▼
┌─────────────────────┐
│ Exercise Worker │
│ (MiniMax-M2.5 API) │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ PostgreSQL │
│ (Store Exercise) │
└─────────────────────┘
Configuración de Entorno
Variables de Entorno
# Database
DATABASE_URL="postgresql://user:pass@localhost:5432/mathdb"
DB_PASSWORD="secure_password"
# Redis
REDIS_HOST="redis"
REDIS_PORT=6379
REDIS_PASSWORD="redis_password"
# JWT
JWT_SECRET="your-super-secret-jwt-key-min-32-chars"
JWT_EXPIRES_IN="15m"
JWT_REFRESH_EXPIRES_IN="7d"
# AI
AI_API_BASE_URL="https://api.example.com/v1"
AI_API_KEY="your-ai-api-key"
AI_MODEL="MiniMax-M2.5"
# Telegram
TELEGRAM_BOT_TOKEN="your-bot-token"
TELEGRAM_ADMIN_CHAT_ID="admin-chat-id"
# App
NODE_ENV="production"
PORT=3001
LOG_LEVEL="info"
Perfiles de Entorno
-
Development:
- Hot reload habilitado
- Logging detallado
- Prisma Studio disponible
- Mock services
-
Staging:
- Docker containers
- SSL/TLS habilitado
- CI/CD deployment
- Test data
-
Production:
- Multi-stage Docker builds
- SSL/TLS con Let's Encrypt
- Read replicas
- CDN activo
- Monitoring completo
Convenciones de Código
TypeScript
- ✅ Siempre tipos explícitos (no
any) - ✅ Interfaces para objetos complejos
- ✅ Enums para valores fijos
- ✅ Never usar
console.log(usar logger) - ✅ Strict mode enabled
- ✅ Path aliases (
@/components,@/lib)
Nomenclatura
- Components: PascalCase (
ExerciseSolver.tsx) - Hooks: camelCase con use (
useAuth.ts) - Utils: camelCase (
formatDate.ts) - Constants: UPPER_SNAKE_CASE
- Types/Interfaces: PascalCase con sufijo Type (
UserType) - Enums: PascalCase (
ExerciseDifficulty)
Testing
- Cada feature necesita tests
- Cobertura mínima: 70%
- E2E para flows críticos
- Mock de servicios externos
- Tests de integración para DB