✨ 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 ✅
512 lines
14 KiB
Markdown
512 lines
14 KiB
Markdown
# 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:**
|
|
1. **Controller**: Maneja HTTP requests/responses, validación de entrada
|
|
2. **Service**: Lógica de negocio, coordinación entre repositorios
|
|
3. **Repository**: Acceso a datos, queries Prisma
|
|
4. **Database**: PostgreSQL con Prisma ORM
|
|
|
|
**Ejemplo:**
|
|
```typescript
|
|
// 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:
|
|
|
|
```typescript
|
|
// 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:**
|
|
1. **Helmet** - Security headers
|
|
2. **CORS** - Cross-origin handling
|
|
3. **Compression** - gzip/deflate
|
|
4. **Request ID** - Correlation ID injection
|
|
5. **Logging** - Request/response logging
|
|
6. **Auth** - JWT verification
|
|
7. **Rate Limit** - Throttling
|
|
8. **Validation** - Zod schema validation
|
|
9. **Handler** - Route controller
|
|
10. **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
|
|
|
|
1. **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
|
|
|
|
2. **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
|
|
|
|
3. **Logout**:
|
|
- Agrega refresh token a Redis blacklist
|
|
- Client elimina tokens del storage
|
|
- TTL en Redis coincide con expiración del token
|
|
|
|
### Autorización
|
|
|
|
1. **RBAC (Role-Based Access Control)**:
|
|
- Roles: USER, TEACHER, ADMIN
|
|
- Permisos definidos por rol
|
|
- Middleware `requireAdmin` para rutas sensibles
|
|
|
|
2. **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
|
|
|
|
1. **XSS Protection**:
|
|
- DOMPurify para sanitización de LaTeX
|
|
- Helmet.js headers (CSP, X-Frame-Options)
|
|
- Escape de output en templates
|
|
|
|
2. **CSRF Protection**:
|
|
- Tokens CSRF en formularios
|
|
- Validación de Origin header
|
|
- SameSite cookies
|
|
|
|
3. **SQL Injection**:
|
|
- Uso exclusivo de Prisma ORM
|
|
- No queries raw sin validación
|
|
- Prepared statements automáticos
|
|
|
|
4. **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
|
|
|
|
1. **Connection Pooling**:
|
|
- PgBouncer para pooling de conexiones
|
|
- Prisma connection limits configurados
|
|
- Min/Max connections según carga
|
|
|
|
2. **Query Optimization**:
|
|
- Índices en campos de búsqueda frecuente
|
|
- Select fields específicos (no SELECT *)
|
|
- Caching de queries frecuentes en Redis
|
|
|
|
3. **Read Replicas**:
|
|
- Replicas para queries de lectura
|
|
- Master para writes
|
|
- Prisma read replica strategy
|
|
|
|
### Caching Strategy
|
|
|
|
1. **Redis Cache**:
|
|
- Session storage (JWT blacklist)
|
|
- Exercise data (TTL: 1 hora)
|
|
- Progress data (TTL: 5 minutos)
|
|
- Rankings (TTL: 1 minuto)
|
|
|
|
2. **CDN**:
|
|
- Assets estáticos en CDN
|
|
- KaTeX fonts y CSS
|
|
- Imágenes y PDFs
|
|
|
|
## Workers y Queue System
|
|
|
|
### Bull Queue Configuration
|
|
|
|
```typescript
|
|
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
|
|
|
|
1. **PDF Worker**:
|
|
- Procesa PDFs de /app/pdfs
|
|
- Extrae texto con pdf-parse
|
|
- Genera imágenes con pdf2pic
|
|
- Almacena metadata en ProcessedPdf
|
|
|
|
2. **Exercise Worker**:
|
|
- Genera ejercicios con AI
|
|
- Conecta a MiniMax-M2.5 API
|
|
- Valida notación matemática
|
|
- Guarda en database
|
|
|
|
3. **Notification Worker**:
|
|
- Envía notificaciones Telegram
|
|
- Procesa cola de notificaciones
|
|
- Rate limiting para bot API
|
|
|
|
### Escalado de Workers
|
|
|
|
```bash
|
|
# 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**:
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```yaml
|
|
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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
1. **Development**:
|
|
- Hot reload habilitado
|
|
- Logging detallado
|
|
- Prisma Studio disponible
|
|
- Mock services
|
|
|
|
2. **Staging**:
|
|
- Docker containers
|
|
- SSL/TLS habilitado
|
|
- CI/CD deployment
|
|
- Test data
|
|
|
|
3. **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
|