✨ 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 ✅
360 lines
8.8 KiB
Bash
Executable File
360 lines
8.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# ========================================
|
|
# MATH PLATFORM - PRODUCTION DEPLOYMENT
|
|
# Enterprise Grade Deployment Script
|
|
# ========================================
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
BACKUP_DIR="$PROJECT_DIR/backups"
|
|
LOG_DIR="$PROJECT_DIR/logs"
|
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
|
|
|
# Logging functions
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
log_warning() {
|
|
echo -e "${YELLOW}[WARNING]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
# Error handler
|
|
error_handler() {
|
|
log_error "Deployment failed at line $1"
|
|
log_info "Rolling back to previous state..."
|
|
rollback_deployment
|
|
exit 1
|
|
}
|
|
|
|
trap 'error_handler $LINENO' ERR
|
|
|
|
# ========================================
|
|
# PRE-DEPLOYMENT CHECKS
|
|
# ========================================
|
|
|
|
check_prerequisites() {
|
|
log_info "Checking prerequisites..."
|
|
|
|
# Check if running as root (should NOT)
|
|
if [[ $EUID -eq 0 ]]; then
|
|
log_error "Do not run this script as root"
|
|
exit 1
|
|
fi
|
|
|
|
# Check required commands
|
|
local required_commands=("docker" "docker-compose" "curl" "wget")
|
|
for cmd in "${required_commands[@]}"; do
|
|
if ! command -v "$cmd" &> /dev/null; then
|
|
log_error "$cmd is required but not installed"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# Check Docker is running
|
|
if ! docker info &> /dev/null; then
|
|
log_error "Docker daemon is not running"
|
|
exit 1
|
|
fi
|
|
|
|
log_success "Prerequisites check passed"
|
|
}
|
|
|
|
check_environment() {
|
|
log_info "Checking environment variables..."
|
|
|
|
local required_vars=(
|
|
"DATABASE_URL"
|
|
"REDIS_PASSWORD"
|
|
"JWT_SECRET"
|
|
)
|
|
|
|
local missing_vars=()
|
|
for var in "${required_vars[@]}"; do
|
|
if [[ -z "${!var:-}" ]]; then
|
|
missing_vars+=("$var")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#missing_vars[@]} -gt 0 ]]; then
|
|
log_error "Missing required environment variables:"
|
|
printf '%s\n' "${missing_vars[@]}"
|
|
exit 1
|
|
fi
|
|
|
|
log_success "Environment variables check passed"
|
|
}
|
|
|
|
# ========================================
|
|
# DATABASE OPERATIONS
|
|
# ========================================
|
|
|
|
create_backup() {
|
|
log_info "Creating database backup..."
|
|
|
|
mkdir -p "$BACKUP_DIR"
|
|
local backup_file="$BACKUP_DIR/backup_$TIMESTAMP.sql"
|
|
|
|
# Extract database info from DATABASE_URL
|
|
local db_container="math-postgres-prod"
|
|
|
|
if docker ps | grep -q "$db_container"; then
|
|
if docker exec "$db_container" pg_dump -U mathuser mathdb > "$backup_file"; then
|
|
log_success "Database backup created: $backup_file"
|
|
|
|
# Compress backup
|
|
gzip "$backup_file"
|
|
log_success "Backup compressed: ${backup_file}.gz"
|
|
|
|
# Keep only last 7 backups
|
|
ls -t "$BACKUP_DIR"/backup_*.sql.gz | tail -n +8 | xargs -r rm
|
|
else
|
|
log_warning "Failed to create database backup, continuing anyway..."
|
|
fi
|
|
else
|
|
log_warning "Database container not running, skipping backup"
|
|
fi
|
|
}
|
|
|
|
run_migrations() {
|
|
log_info "Running database migrations..."
|
|
|
|
cd "$PROJECT_DIR"
|
|
|
|
# Create temporary migration container
|
|
docker-compose -f docker-compose.prod.yml run --rm backend npx prisma migrate deploy
|
|
|
|
log_success "Database migrations completed"
|
|
}
|
|
|
|
# ========================================
|
|
# DEPLOYMENT OPERATIONS
|
|
# ========================================
|
|
|
|
build_images() {
|
|
log_info "Building Docker images..."
|
|
|
|
cd "$PROJECT_DIR"
|
|
|
|
# Export version for builds
|
|
export VERSION=${VERSION:-1.0.0}
|
|
|
|
# Build all images
|
|
docker-compose -f docker-compose.prod.yml build --no-cache
|
|
|
|
log_success "Docker images built successfully"
|
|
}
|
|
|
|
zero_downtime_deploy() {
|
|
log_info "Starting zero-downtime deployment..."
|
|
|
|
cd "$PROJECT_DIR"
|
|
|
|
# Scale up new instances
|
|
log_info "Scaling up backend instances..."
|
|
docker-compose -f docker-compose.prod.yml up -d --no-deps --scale backend=3 backend
|
|
|
|
# Wait for health checks
|
|
log_info "Waiting for health checks (30s)..."
|
|
sleep 30
|
|
|
|
# Check health
|
|
if ! check_service_health "backend"; then
|
|
log_error "Backend health check failed after scaling"
|
|
return 1
|
|
fi
|
|
|
|
# Scale down to normal
|
|
log_info "Scaling down to normal capacity..."
|
|
docker-compose -f docker-compose.prod.yml up -d --no-deps --scale backend=2 backend
|
|
|
|
log_success "Zero-downtime deployment completed"
|
|
}
|
|
|
|
deploy_services() {
|
|
log_info "Deploying services..."
|
|
|
|
cd "$PROJECT_DIR"
|
|
|
|
# Pull latest images if using registry
|
|
if [[ "${PULL_IMAGES:-false}" == "true" ]]; then
|
|
log_info "Pulling latest images from registry..."
|
|
docker-compose -f docker-compose.prod.yml pull
|
|
fi
|
|
|
|
# Deploy all services
|
|
docker-compose -f docker-compose.prod.yml up -d --remove-orphans
|
|
|
|
log_success "Services deployed successfully"
|
|
}
|
|
|
|
# ========================================
|
|
# HEALTH CHECKS
|
|
# ========================================
|
|
|
|
check_service_health() {
|
|
local service=$1
|
|
local max_attempts=${2:-10}
|
|
local attempt=1
|
|
|
|
log_info "Checking health for service: $service"
|
|
|
|
while [[ $attempt -le $max_attempts ]]; do
|
|
if docker-compose -f docker-compose.prod.yml ps "$service" | grep -q "healthy"; then
|
|
log_success "$service is healthy"
|
|
return 0
|
|
fi
|
|
|
|
log_info "Health check attempt $attempt/$max_attempts..."
|
|
sleep 5
|
|
((attempt++))
|
|
done
|
|
|
|
log_error "$service failed health check after $max_attempts attempts"
|
|
return 1
|
|
}
|
|
|
|
run_health_checks() {
|
|
log_info "Running comprehensive health checks..."
|
|
|
|
local services=("postgres" "redis" "backend" "frontend" "nginx")
|
|
local failed_services=()
|
|
|
|
for service in "${services[@]}"; do
|
|
if ! check_service_health "$service"; then
|
|
failed_services+=("$service")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#failed_services[@]} -gt 0 ]]; then
|
|
log_error "Health checks failed for services:"
|
|
printf '%s\n' "${failed_services[@]}"
|
|
return 1
|
|
fi
|
|
|
|
# Check main health endpoint
|
|
local health_url="http://localhost/health"
|
|
if curl -sf "$health_url" &> /dev/null; then
|
|
log_success "Main health endpoint is responding"
|
|
else
|
|
log_warning "Main health endpoint not responding (might need SSL)"
|
|
fi
|
|
|
|
log_success "All health checks passed"
|
|
}
|
|
|
|
# ========================================
|
|
# ROLLBACK
|
|
# ========================================
|
|
|
|
rollback_deployment() {
|
|
log_warning "Initiating rollback..."
|
|
|
|
cd "$PROJECT_DIR"
|
|
|
|
# Get last successful version (if available)
|
|
local last_version=${LAST_VERSION:-"latest"}
|
|
|
|
# Revert to previous images
|
|
export VERSION="$last_version"
|
|
docker-compose -f docker-compose.prod.yml up -d
|
|
|
|
log_warning "Rollback completed"
|
|
}
|
|
|
|
# ========================================
|
|
# CLEANUP
|
|
# ========================================
|
|
|
|
cleanup() {
|
|
log_info "Running cleanup..."
|
|
|
|
# Remove old images
|
|
docker image prune -af --filter "until=24h" || true
|
|
|
|
# Remove unused volumes
|
|
docker volume prune -f || true
|
|
|
|
# Clean old logs
|
|
find "$LOG_DIR" -name "*.log" -type f -mtime +7 -delete 2>/dev/null || true
|
|
|
|
log_success "Cleanup completed"
|
|
}
|
|
|
|
# ========================================
|
|
# MAIN
|
|
# ========================================
|
|
|
|
main() {
|
|
log_info "🚀 Starting Math Platform Production Deployment"
|
|
log_info "Timestamp: $(date)"
|
|
log_info "Version: ${VERSION:-1.0.0}"
|
|
|
|
# Pre-deployment
|
|
check_prerequisites
|
|
check_environment
|
|
|
|
# Backup
|
|
create_backup
|
|
|
|
# Database
|
|
run_migrations
|
|
|
|
# Build and deploy
|
|
build_images
|
|
deploy_services
|
|
|
|
# Zero-downtime for backend
|
|
zero_downtime_deploy
|
|
|
|
# Verification
|
|
run_health_checks
|
|
|
|
# Cleanup
|
|
cleanup
|
|
|
|
log_success "✅ Deployment completed successfully!"
|
|
log_info "Services are running at:"
|
|
log_info " - Frontend: https://localhost"
|
|
log_info " - API: https://localhost/api"
|
|
log_info " - Grafana: http://localhost:3001 (if monitoring enabled)"
|
|
}
|
|
|
|
# Parse command line arguments
|
|
case "${1:-deploy}" in
|
|
"backup")
|
|
create_backup
|
|
;;
|
|
"migrate")
|
|
check_environment
|
|
run_migrations
|
|
;;
|
|
"rollback")
|
|
rollback_deployment
|
|
;;
|
|
"health")
|
|
run_health_checks
|
|
;;
|
|
"deploy"|*)
|
|
main
|
|
;;
|
|
esac
|