205 lines
6.9 KiB
Bash
Executable File
205 lines
6.9 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# 🚀 Production Environment Manager
|
|
# Manages production Docker deployment with organized structure
|
|
|
|
set -e
|
|
|
|
# Configuration
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
COMPOSE_FILE="$PROJECT_ROOT/docker/prod/docker-compose.yml"
|
|
ENV_FILE="$PROJECT_ROOT/.env"
|
|
PROJECT_NAME="portal-prod"
|
|
|
|
# Colors
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
RED='\033[0;31m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[PROD] $1${NC}"; }
|
|
warn() { echo -e "${YELLOW}[PROD] $1${NC}"; }
|
|
info() { echo -e "${BLUE}[PROD] $1${NC}"; }
|
|
error() { echo -e "${RED}[PROD] ERROR: $1${NC}"; exit 1; }
|
|
|
|
# Change to project root
|
|
cd "$PROJECT_ROOT"
|
|
|
|
# Check environment file
|
|
check_env() {
|
|
if [ ! -f "$ENV_FILE" ]; then
|
|
log "Creating environment file from template..."
|
|
cp .env.example .env
|
|
error "Environment file created at $ENV_FILE. Please edit it with your production values before deploying."
|
|
fi
|
|
|
|
# Check for required variables
|
|
required_vars=("DATABASE_URL" "REDIS_URL" "JWT_SECRET" "POSTGRES_PASSWORD" "CORS_ORIGIN" "NEXT_PUBLIC_API_BASE" "BFF_PORT")
|
|
for var in "${required_vars[@]}"; do
|
|
if ! grep -q "^$var=" "$ENV_FILE" || grep -q "^$var=.*CHANGE_THIS" "$ENV_FILE"; then
|
|
error "Required environment variable $var is not properly set in $ENV_FILE"
|
|
fi
|
|
done
|
|
}
|
|
issue_cert() {
|
|
local domain="$1"
|
|
local email="$2"
|
|
if [ -z "$domain" ] || [ -z "$email" ]; then
|
|
error "Usage: $0 issue-cert <domain> <email>"
|
|
fi
|
|
|
|
log "🔐 Issuing certificate for $domain ..."
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" run --rm -p 80:80 \
|
|
-e CERTBOT_DOMAIN="$domain" -e CERTBOT_EMAIL="$email" certbot \
|
|
certonly --webroot -w /var/www/certbot -d "$domain" --agree-tos --no-eff-email
|
|
log "✅ Certificate issuance process completed"
|
|
}
|
|
|
|
# Build production images
|
|
build_images() {
|
|
log "🔨 Building production images..."
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" build --no-cache
|
|
log "✅ Images built successfully"
|
|
}
|
|
|
|
# Deploy production environment
|
|
deploy() {
|
|
log "🚀 Deploying production environment..."
|
|
|
|
check_env
|
|
build_images
|
|
|
|
# Start database and cache first
|
|
log "🗄️ Starting database and cache..."
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" up -d database cache
|
|
|
|
# Wait for database
|
|
log "⏳ Waiting for database..."
|
|
timeout=60
|
|
while [ $timeout -gt 0 ]; do
|
|
if docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" exec -T database pg_isready -U portal -d portal_prod 2>/dev/null; then
|
|
log "✅ Database is ready"
|
|
break
|
|
fi
|
|
sleep 2
|
|
timeout=$((timeout - 2))
|
|
done
|
|
|
|
if [ $timeout -eq 0 ]; then
|
|
error "Database failed to start within 60 seconds"
|
|
fi
|
|
|
|
# Run migrations
|
|
log "🔄 Running database migrations..."
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" run --rm backend pnpm db:migrate || warn "Migration may have failed"
|
|
|
|
# Start application services (proxy + apps)
|
|
log "🚀 Starting application services..."
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" up -d proxy frontend backend
|
|
|
|
# Health checks
|
|
log "🏥 Performing health checks..."
|
|
sleep 15
|
|
|
|
if docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" ps | grep -q "unhealthy"; then
|
|
warn "Some services may not be healthy - check logs"
|
|
fi
|
|
|
|
log "✅ Production deployment completed!"
|
|
log "🔗 Frontend: http://localhost:3000"
|
|
log "🔗 Backend API: http://localhost:4000"
|
|
}
|
|
|
|
# Stop production services
|
|
stop() {
|
|
log "⏹️ Stopping production services..."
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" down
|
|
log "✅ Services stopped"
|
|
}
|
|
|
|
# Update deployment
|
|
update() {
|
|
log "🔄 Updating production deployment..."
|
|
check_env
|
|
build_images
|
|
|
|
# Zero-downtime update
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" up -d --force-recreate --no-deps backend frontend
|
|
|
|
log "✅ Production update completed"
|
|
}
|
|
|
|
# Show status with health checks
|
|
status() {
|
|
log "📊 Production Services Status:"
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" ps
|
|
|
|
log "🏥 Health Status:"
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" exec proxy wget --spider -q http://localhost/healthz && echo "✅ Proxy/Frontend healthy" || echo "❌ Proxy/Frontend unhealthy"
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" exec backend wget --spider -q http://localhost:4000/health && echo "✅ Backend healthy" || echo "❌ Backend unhealthy"
|
|
}
|
|
|
|
# Show logs
|
|
logs() {
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" logs -f "${@:2}"
|
|
}
|
|
|
|
# Backup database
|
|
backup() {
|
|
log "💾 Creating database backup..."
|
|
backup_file="backup_$(date +%Y%m%d_%H%M%S).sql"
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" exec -T database pg_dump -U portal portal_prod > "$backup_file"
|
|
log "✅ Database backup created: $backup_file"
|
|
}
|
|
|
|
# Clean up
|
|
cleanup() {
|
|
log "🧹 Cleaning up old containers and images..."
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" down --remove-orphans
|
|
docker system prune -f
|
|
docker image prune -f
|
|
log "✅ Cleanup completed"
|
|
}
|
|
|
|
# Main function
|
|
case "${1:-help}" in
|
|
"deploy") deploy ;;
|
|
"start")
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" up -d
|
|
log "✅ Production services started"
|
|
;;
|
|
"stop") stop ;;
|
|
"restart")
|
|
docker-compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" -p "$PROJECT_NAME" restart
|
|
log "✅ Services restarted"
|
|
;;
|
|
"update") update ;;
|
|
"build") build_images ;;
|
|
"issue-cert") issue_cert "$2" "$3" ;;
|
|
"status") status ;;
|
|
"logs") logs "$@" ;;
|
|
"backup") backup ;;
|
|
"cleanup") cleanup ;;
|
|
"help"|*)
|
|
echo "🚀 Production Environment Manager"
|
|
echo ""
|
|
echo "Usage: $0 {command}"
|
|
echo ""
|
|
echo "Commands:"
|
|
echo " deploy - Full deployment (build + start + migrate)"
|
|
echo " start - Start all production services"
|
|
echo " stop - Stop all production services"
|
|
echo " restart - Restart all services"
|
|
echo " update - Update deployment with new images"
|
|
echo " build - Build production images"
|
|
echo " status - Show service status and health"
|
|
echo " logs - Show service logs"
|
|
echo " backup - Create database backup"
|
|
echo " cleanup - Clean up old containers and images"
|
|
echo " help - Show this help"
|
|
exit 0
|
|
;;
|
|
esac
|