🎓 Initial commit: Math2 Platform - Plataforma de Álgebra Lineal PRO
✨ 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 ✅
This commit is contained in:
571
docker/docker-compose.yml
Normal file
571
docker/docker-compose.yml
Normal file
@@ -0,0 +1,571 @@
|
||||
# ==================================================
|
||||
# DOCKER COMPOSE - VERSIÓN DETALLADA
|
||||
# Plataforma de Estudio de Matemáticas
|
||||
# ==================================================
|
||||
# Este archivo contiene configuraciones detalladas para
|
||||
# desarrollo, testing y producción.
|
||||
#
|
||||
# Uso:
|
||||
# docker-compose -f docker/docker-compose.yml up -d
|
||||
# ==================================================
|
||||
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
# ==================================================
|
||||
# POSTGRESQL - Base de Datos Principal
|
||||
# ==================================================
|
||||
postgres:
|
||||
image: postgres:15.4-alpine
|
||||
container_name: math-postgres
|
||||
restart: unless-stopped
|
||||
|
||||
environment:
|
||||
POSTGRES_USER: mathuser
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD:-math_secure_password_2024}
|
||||
POSTGRES_DB: mathdb
|
||||
POSTGRES_INITDB_ARGS: "-E UTF8 --locale=C"
|
||||
|
||||
volumes:
|
||||
# Persistencia de datos
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
# Scripts de inicialización
|
||||
- ./init-scripts:/docker-entrypoint-initdb.d:ro
|
||||
|
||||
ports:
|
||||
- "${POSTGRES_PORT:-5432}:5432"
|
||||
|
||||
command: [
|
||||
"postgres",
|
||||
"-c", "max_connections=200",
|
||||
"-c", "shared_buffers=256MB",
|
||||
"-c", "effective_cache_size=1GB",
|
||||
"-c", "maintenance_work_mem=64MB",
|
||||
"-c", "checkpoint_completion_target=0.9",
|
||||
"-c", "wal_buffers=16MB",
|
||||
"-c", "default_statistics_target=100",
|
||||
"-c", "random_page_cost=1.1",
|
||||
"-c", "effective_io_concurrency=200",
|
||||
"-c", "work_mem=1310kB",
|
||||
"-c", "min_wal_size=1GB",
|
||||
"-c", "max_wal_size=4GB",
|
||||
"-c", "log_statement=all",
|
||||
"-c", "log_duration=on"
|
||||
]
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U mathuser -d mathdb"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
networks:
|
||||
- math-network
|
||||
|
||||
labels:
|
||||
- "com.math-platform.description=PostgreSQL Database"
|
||||
- "com.math-platform.priority=1"
|
||||
|
||||
# ==================================================
|
||||
# REDIS - Cache & Message Queue
|
||||
# ==================================================
|
||||
redis:
|
||||
image: redis:7.2.3-alpine
|
||||
container_name: math-redis
|
||||
restart: unless-stopped
|
||||
|
||||
command: >
|
||||
redis-server
|
||||
--appendonly yes
|
||||
--appendfsync everysec
|
||||
--requirepass ${REDIS_PASSWORD:-redis_secure_password_2024}
|
||||
--maxmemory 256mb
|
||||
--maxmemory-policy allkeys-lru
|
||||
--tcp-backlog 511
|
||||
--timeout 0
|
||||
--tcp-keepalive 300
|
||||
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
|
||||
ports:
|
||||
- "${REDIS_PORT:-6379}:6379"
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
start_period: 5s
|
||||
|
||||
networks:
|
||||
- math-network
|
||||
|
||||
labels:
|
||||
- "com.math-platform.description=Redis Cache & Queue"
|
||||
- "com.math-platform.priority=2"
|
||||
|
||||
# ==================================================
|
||||
# BACKEND API - Node.js + Express + TypeScript
|
||||
# ==================================================
|
||||
backend:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.backend
|
||||
target: runner
|
||||
args:
|
||||
NODE_VERSION: "20"
|
||||
container_name: math-backend
|
||||
restart: unless-stopped
|
||||
|
||||
environment:
|
||||
# Node.js
|
||||
NODE_ENV: ${NODE_ENV:-production}
|
||||
PORT: 3001
|
||||
|
||||
# Database
|
||||
DATABASE_URL: postgresql://mathuser:${DB_PASSWORD:-math_secure_password_2024}@postgres:5432/mathdb
|
||||
DB_PASSWORD: ${DB_PASSWORD:-math_secure_password_2024}
|
||||
|
||||
# Redis
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: 6379
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-redis_secure_password_2024}
|
||||
|
||||
# AI (MiniMax-M2.5 via Aliyun DashScope)
|
||||
AI_API_BASE_URL: ${AI_API_BASE_URL:-https://coding-intl.dashscope.aliyuncs.com/v1}
|
||||
AI_API_KEY: ${AI_API_KEY:-your-dashscope-api-key-here}
|
||||
AI_MODEL: ${AI_MODEL:-MiniMax-M2.5}
|
||||
AI_MAX_TOKENS: ${AI_MAX_TOKENS:-2000}
|
||||
AI_TEMPERATURE: ${AI_TEMPERATURE:-0.7}
|
||||
|
||||
# Telegram
|
||||
TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN}
|
||||
TELEGRAM_ADMIN_CHAT_ID: ${TELEGRAM_ADMIN_CHAT_ID}
|
||||
|
||||
# JWT
|
||||
JWT_SECRET: ${JWT_SECRET:-jwt_secret_key_change_in_production}
|
||||
JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-7d}
|
||||
JWT_REFRESH_EXPIRES_IN: ${JWT_REFRESH_EXPIRES_IN:-30d}
|
||||
|
||||
# CORS
|
||||
CORS_ORIGIN: ${CORS_ORIGIN:-http://localhost:3000}
|
||||
|
||||
# Rate Limiting
|
||||
RATE_LIMIT_AUTH: ${RATE_LIMIT_AUTH:-5}
|
||||
RATE_LIMIT_API: ${RATE_LIMIT_API:-10}
|
||||
RATE_LIMIT_WINDOW_MS: ${RATE_LIMIT_WINDOW_MS:-60000}
|
||||
|
||||
volumes:
|
||||
# Code mounting (for development without rebuild)
|
||||
- ../backend:/app
|
||||
- /app/node_modules
|
||||
# PDFs directory (read-only)
|
||||
- ../pdfs:/app/pdfs:ro
|
||||
# Logs
|
||||
- ./logs/backend:/app/logs
|
||||
|
||||
ports:
|
||||
- "${BACKEND_PORT:-3001}:3001"
|
||||
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3001/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
networks:
|
||||
- math-network
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1.0'
|
||||
memory: 512M
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 128M
|
||||
|
||||
labels:
|
||||
- "com.math-platform.description=Backend API"
|
||||
- "com.math-platform.priority=3"
|
||||
|
||||
# ==================================================
|
||||
# FRONTEND - Next.js 14 App Router
|
||||
# ==================================================
|
||||
frontend:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.frontend
|
||||
target: runner
|
||||
args:
|
||||
NODE_VERSION: "20"
|
||||
container_name: math-frontend
|
||||
restart: unless-stopped
|
||||
|
||||
environment:
|
||||
NODE_ENV: ${NODE_ENV:-production}
|
||||
PORT: 3000
|
||||
|
||||
# API URL
|
||||
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://backend:3001}
|
||||
NEXT_PUBLIC_APP_NAME: ${NEXT_PUBLIC_APP_NAME:-Plataforma de Álgebra Lineal}
|
||||
|
||||
# Next.js
|
||||
NEXT_TELEMETRY_DISABLED: "1"
|
||||
|
||||
volumes:
|
||||
# Code mounting (for development)
|
||||
- ../frontend:/app
|
||||
- /app/node_modules
|
||||
- /app/.next
|
||||
# Logs
|
||||
- ./logs/frontend:/app/logs
|
||||
|
||||
ports:
|
||||
- "${FRONTEND_PORT:-3000}:3000"
|
||||
|
||||
depends_on:
|
||||
backend:
|
||||
condition: service_healthy
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
networks:
|
||||
- math-network
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1.0'
|
||||
memory: 512M
|
||||
reservations:
|
||||
cpus: '0.25'
|
||||
memory: 128M
|
||||
|
||||
labels:
|
||||
- "com.math-platform.description=Frontend (Next.js)"
|
||||
- "com.math-platform.priority=4"
|
||||
|
||||
# ==================================================
|
||||
# PDF WORKER - Procesamiento de PDFs
|
||||
# ==================================================
|
||||
pdf-worker:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.worker
|
||||
target: pdf-worker
|
||||
container_name: math-pdf-worker
|
||||
restart: unless-stopped
|
||||
|
||||
environment:
|
||||
NODE_ENV: ${NODE_ENV:-production}
|
||||
WORKER_TYPE: pdf
|
||||
|
||||
# Database
|
||||
DATABASE_URL: postgresql://mathuser:${DB_PASSWORD:-math_secure_password_2024}@postgres:5432/mathdb
|
||||
|
||||
# Redis
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: 6379
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-redis_secure_password_2024}
|
||||
|
||||
# PDF Processing
|
||||
PDF_PROCESSING_BATCH_SIZE: ${PDF_PROCESSING_BATCH_SIZE:-5}
|
||||
PDF_PROCESSING_TIMEOUT: ${PDF_PROCESSING_TIMEOUT:-300000}
|
||||
PDF_STORAGE_PATH: /app/pdfs
|
||||
PDF_PROCESSED_PATH: /app/pdfs/processed
|
||||
|
||||
# Worker
|
||||
WORKER_RETRY_ATTEMPTS: ${WORKER_RETRY_ATTEMPTS:-3}
|
||||
WORKER_RETRY_DELAY: ${WORKER_RETRY_DELAY:-5000}
|
||||
|
||||
volumes:
|
||||
- ../backend:/app
|
||||
- /app/node_modules
|
||||
- ../pdfs:/app/pdfs:ro
|
||||
- ../pdfs/processed:/app/pdfs/processed
|
||||
- ./logs/pdf-worker:/app/logs
|
||||
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "console.log('healthy')"]
|
||||
interval: 60s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
networks:
|
||||
- math-network
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
reservations:
|
||||
cpus: '0.1'
|
||||
memory: 64M
|
||||
replicas: ${PDF_WORKER_REPLICAS:-1}
|
||||
|
||||
labels:
|
||||
- "com.math-platform.description=PDF Processing Worker"
|
||||
- "com.math-platform.priority=5"
|
||||
|
||||
# ==================================================
|
||||
# EXERCISE WORKER - Generación de Ejercicios con IA
|
||||
# ==================================================
|
||||
exercise-worker:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.worker
|
||||
target: exercise-worker
|
||||
container_name: math-exercise-worker
|
||||
restart: unless-stopped
|
||||
|
||||
environment:
|
||||
NODE_ENV: ${NODE_ENV:-production}
|
||||
WORKER_TYPE: exercise
|
||||
|
||||
# Database
|
||||
DATABASE_URL: postgresql://mathuser:${DB_PASSWORD:-math_secure_password_2024}@postgres:5432/mathdb
|
||||
|
||||
# Redis
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: 6379
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-redis_secure_password_2024}
|
||||
|
||||
# AI
|
||||
AI_API_BASE_URL: ${AI_API_BASE_URL:-https://coding-intl.dashscope.aliyuncs.com/v1}
|
||||
AI_API_KEY: ${AI_API_KEY}
|
||||
AI_MODEL: ${AI_MODEL:-MiniMax-M2.5}
|
||||
AI_MAX_TOKENS: ${AI_MAX_TOKENS:-2000}
|
||||
AI_TEMPERATURE: ${AI_TEMPERATURE:-0.7}
|
||||
|
||||
# Worker
|
||||
WORKER_RETRY_ATTEMPTS: ${WORKER_RETRY_ATTEMPTS:-3}
|
||||
WORKER_RETRY_DELAY: ${WORKER_RETRY_DELAY:-5000}
|
||||
|
||||
volumes:
|
||||
- ../backend:/app
|
||||
- /app/node_modules
|
||||
- ./logs/exercise-worker:/app/logs
|
||||
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "console.log('healthy')"]
|
||||
interval: 60s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
networks:
|
||||
- math-network
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 256M
|
||||
reservations:
|
||||
cpus: '0.1'
|
||||
memory: 64M
|
||||
replicas: ${EXERCISE_WORKER_REPLICAS:-2}
|
||||
|
||||
labels:
|
||||
- "com.math-platform.description=Exercise Generation Worker (AI)"
|
||||
- "com.math-platform.priority=6"
|
||||
|
||||
# ==================================================
|
||||
# NOTIFICATION WORKER - Envío de Notificaciones Telegram
|
||||
# ==================================================
|
||||
notification-worker:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/Dockerfile.worker
|
||||
target: notification-worker
|
||||
container_name: math-notification-worker
|
||||
restart: unless-stopped
|
||||
|
||||
environment:
|
||||
NODE_ENV: ${NODE_ENV:-production}
|
||||
WORKER_TYPE: notification
|
||||
|
||||
# Database
|
||||
DATABASE_URL: postgresql://mathuser:${DB_PASSWORD:-math_secure_password_2024}@postgres:5432/mathdb
|
||||
|
||||
# Redis
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: 6379
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD:-redis_secure_password_2024}
|
||||
|
||||
# Telegram
|
||||
TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN}
|
||||
TELEGRAM_ADMIN_CHAT_ID: ${TELEGRAM_ADMIN_CHAT_ID}
|
||||
|
||||
# Worker
|
||||
WORKER_RETRY_ATTEMPTS: ${WORKER_RETRY_ATTEMPTS:-3}
|
||||
WORKER_RETRY_DELAY: ${WORKER_RETRY_DELAY:-5000}
|
||||
|
||||
volumes:
|
||||
- ../backend:/app
|
||||
- /app/node_modules
|
||||
- ./logs/notification-worker:/app/logs
|
||||
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "console.log('healthy')"]
|
||||
interval: 60s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
networks:
|
||||
- math-network
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.25'
|
||||
memory: 128M
|
||||
reservations:
|
||||
cpus: '0.05'
|
||||
memory: 32M
|
||||
replicas: ${NOTIFICATION_WORKER_REPLICAS:-1}
|
||||
|
||||
labels:
|
||||
- "com.math-platform.description=Notification Worker (Telegram)"
|
||||
- "com.math-platform.priority=7"
|
||||
|
||||
# ==================================================
|
||||
# NGINX - Reverse Proxy + Load Balancer
|
||||
# ==================================================
|
||||
nginx:
|
||||
image: nginx:1.25-alpine
|
||||
container_name: math-nginx
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./logs/nginx:/var/log/nginx
|
||||
# SSL certificates (uncomment for production)
|
||||
# - ./ssl:/etc/nginx/ssl:ro
|
||||
|
||||
ports:
|
||||
- "${NGINX_HTTP_PORT:-80}:80"
|
||||
- "${NGINX_HTTPS_PORT:-443}:443"
|
||||
|
||||
depends_on:
|
||||
- frontend
|
||||
- backend
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
networks:
|
||||
- math-network
|
||||
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.5'
|
||||
memory: 128M
|
||||
reservations:
|
||||
cpus: '0.1'
|
||||
memory: 32M
|
||||
|
||||
labels:
|
||||
- "com.math-platform.description=Nginx Reverse Proxy"
|
||||
- "com.math-platform.priority=8"
|
||||
|
||||
# ==================================================
|
||||
# VOLUMES - Persistencia de Datos
|
||||
# ==================================================
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ./data/postgres
|
||||
|
||||
redis_data:
|
||||
driver: local
|
||||
driver_opts:
|
||||
type: none
|
||||
o: bind
|
||||
device: ./data/redis
|
||||
|
||||
# ==================================================
|
||||
# NETWORKS - Comunicación entre Servicios
|
||||
# ==================================================
|
||||
networks:
|
||||
math-network:
|
||||
driver: bridge
|
||||
driver_opts:
|
||||
com.docker.network.bridge.name: math_br
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 172.20.0.0/16
|
||||
|
||||
# ==================================================
|
||||
# CONFIGURACIÓN ADICIONAL
|
||||
# ==================================================
|
||||
#
|
||||
# Perfiles de ejecución:
|
||||
#
|
||||
# Desarrollo:
|
||||
# docker-compose -f docker/docker-compose.yml --profile dev up
|
||||
#
|
||||
# Producción:
|
||||
# docker-compose -f docker/docker-compose.yml --profile prod up -d
|
||||
#
|
||||
# Escalado de workers:
|
||||
# docker-compose -f docker/docker-compose.yml up -d --scale exercise-worker=3
|
||||
#
|
||||
# Logs en tiempo real:
|
||||
# docker-compose -f docker/docker-compose.yml logs -f backend
|
||||
#
|
||||
# Reconstrucción completa:
|
||||
# docker-compose -f docker/docker-compose.yml build --no-cache
|
||||
#
|
||||
# Backup de base de datos:
|
||||
# docker-compose -f docker/docker-compose.yml exec postgres \
|
||||
# pg_dump -U mathuser mathdb > backup_$(date +%Y%m%d).sql
|
||||
#
|
||||
# Restauración de base de datos:
|
||||
# docker-compose -f docker/docker-compose.yml exec -T postgres \
|
||||
# psql -U mathuser mathdb < backup_20240323.sql
|
||||
Reference in New Issue
Block a user