#!/bin/bash # ============================================================================= # đŸŗ Plesk Docker Deployment Script # ============================================================================= # Deploys pre-built Docker images to a Plesk server # For building images locally, use: pnpm plesk:images # ============================================================================= set -euo pipefail # ============================================================================= # Configuration # ============================================================================= SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" # Default paths (override via env vars) REPO_PATH="${REPO_PATH:-/var/www/vhosts/yourdomain.com/git/customer-portal}" COMPOSE_FILE="${COMPOSE_FILE:-$PROJECT_ROOT/docker/portainer/docker-compose.yml}" ENV_FILE="${ENV_FILE:-$PROJECT_ROOT/.env}" # Image settings IMAGE_FRONTEND="${IMAGE_FRONTEND:-portal-frontend}" IMAGE_BACKEND="${IMAGE_BACKEND:-portal-backend}" IMAGE_TAG="${IMAGE_TAG:-latest}" # ============================================================================= # Colors and Logging # ============================================================================= if [[ -t 1 ]]; then GREEN='\033[0;32m' YELLOW='\033[1;33m' RED='\033[0;31m' BLUE='\033[0;34m' NC='\033[0m' else GREEN='' YELLOW='' RED='' BLUE='' NC='' fi log() { echo -e "${GREEN}[PLESK]${NC} $*"; } warn() { echo -e "${YELLOW}[PLESK]${NC} WARNING: $*"; } error() { echo -e "${RED}[PLESK]${NC} ERROR: $*"; exit 1; } info() { echo -e "${BLUE}[PLESK]${NC} $*"; } # ============================================================================= # Usage # ============================================================================= usage() { cat < Path to environment file (default: .env) --compose Path to docker-compose file --tag Image tag to use (default: latest) Examples: $0 deploy # Full deployment $0 load # Load images from tarballs $0 status # Check service status $0 logs backend # Show backend logs EOF exit 0 } # ============================================================================= # Pre-flight Checks # ============================================================================= preflight_checks() { log "🔍 Running pre-flight checks..." # Check Docker if ! command -v docker &> /dev/null; then error "Docker is not installed. Please install Docker first." fi # Check Docker daemon if ! docker info >/dev/null 2>&1; then error "Docker daemon is not running." fi # Check docker compose if ! docker compose version >/dev/null 2>&1; then error "Docker Compose V2 is required. Please upgrade Docker." fi # Check environment file if [[ ! -f "$ENV_FILE" ]]; then if [[ -f "$PROJECT_ROOT/.env.production.example" ]]; then log "Creating environment file from template..." cp "$PROJECT_ROOT/.env.production.example" "$ENV_FILE" warn "Please edit $ENV_FILE with your production values!" error "Production environment not configured. Please set up .env" else error "Environment file not found: $ENV_FILE" fi fi # Check compose file if [[ ! -f "$COMPOSE_FILE" ]]; then error "Docker Compose file not found: $COMPOSE_FILE" fi log "✅ Pre-flight checks passed" } # ============================================================================= # Load Images from Tarballs # ============================================================================= load_images() { log "đŸ“Ļ Loading Docker images..." local search_dir="${1:-$PROJECT_ROOT}" local loaded=0 for img in "$IMAGE_FRONTEND" "$IMAGE_BACKEND"; do local tarball # Try compressed first, then uncompressed if [[ -f "$search_dir/${img}.latest.tar.gz" ]]; then tarball="$search_dir/${img}.latest.tar.gz" log "Loading $tarball..." gunzip -c "$tarball" | docker load loaded=$((loaded + 1)) elif [[ -f "$search_dir/${img}.${IMAGE_TAG}.tar.gz" ]]; then tarball="$search_dir/${img}.${IMAGE_TAG}.tar.gz" log "Loading $tarball..." gunzip -c "$tarball" | docker load loaded=$((loaded + 1)) elif [[ -f "$search_dir/${img}.latest.tar" ]]; then tarball="$search_dir/${img}.latest.tar" log "Loading $tarball..." docker load -i "$tarball" loaded=$((loaded + 1)) elif [[ -f "$search_dir/${img}.${IMAGE_TAG}.tar" ]]; then tarball="$search_dir/${img}.${IMAGE_TAG}.tar" log "Loading $tarball..." docker load -i "$tarball" loaded=$((loaded + 1)) else warn "No tarball found for $img" fi done if [[ $loaded -eq 0 ]]; then error "No image tarballs found in $search_dir" fi log "✅ Loaded $loaded images" } # ============================================================================= # Docker Compose Helpers # ============================================================================= dc() { docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" "$@" } # ============================================================================= # Deployment # ============================================================================= deploy() { log "🚀 Starting Plesk Docker deployment..." preflight_checks # Check if images exist, if not try to load from tarballs if ! docker image inspect "${IMAGE_FRONTEND}:latest" >/dev/null 2>&1 || \ ! docker image inspect "${IMAGE_BACKEND}:latest" >/dev/null 2>&1; then log "Images not found, attempting to load from tarballs..." load_images "$PROJECT_ROOT" || true fi # Verify images exist for img in "${IMAGE_FRONTEND}:latest" "${IMAGE_BACKEND}:latest"; do if ! docker image inspect "$img" >/dev/null 2>&1; then error "Required image not found: $img. Build images first with: pnpm plesk:images" fi done # Start infrastructure first log "đŸ—„ī¸ Starting database and cache..." dc up -d database cache # Wait for database log "âŗ Waiting for database..." local timeout=60 while [[ $timeout -gt 0 ]]; do if dc 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 # Start application services log "🚀 Starting application services..." dc up -d frontend backend # Health check log "đŸĨ Waiting for services to be healthy..." sleep 15 if dc ps | grep -q "unhealthy"; then warn "Some services may not be healthy - check logs with: $0 logs" else log "✅ All services healthy" fi log "🎉 Plesk Docker deployment completed!" echo "" info "📝 Next steps:" echo " 1. Configure Plesk reverse proxy to point to port 3000 (frontend)" echo " 2. Set up SSL certificates in Plesk" echo " 3. Test your application at your domain" echo "" info "📋 Useful commands:" echo " $0 status - Check service status" echo " $0 logs - View logs" echo " $0 restart - Restart services" } # ============================================================================= # Service Management # ============================================================================= start_services() { preflight_checks log "â–ļī¸ Starting services..." dc up -d log "✅ Services started" } stop_services() { log "âšī¸ Stopping services..." dc down log "✅ Services stopped" } restart_services() { log "🔄 Restarting services..." dc restart log "✅ Services restarted" } show_status() { log "📊 Service Status:" dc ps echo "" log "đŸĨ Health Status:" dc ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}" } show_logs() { local service="${1:-}" if [[ -n "$service" ]]; then dc logs -f "$service" else dc logs -f fi } # ============================================================================= # Argument Parsing # ============================================================================= COMMAND="${1:-help}" shift || true while [[ $# -gt 0 ]]; do case "$1" in --env-file) ENV_FILE="$2"; shift 2 ;; --compose) COMPOSE_FILE="$2"; shift 2 ;; --tag) IMAGE_TAG="$2"; shift 2 ;; -h|--help) usage ;; *) break ;; esac done export IMAGE_TAG # ============================================================================= # Main # ============================================================================= case "$COMMAND" in deploy) deploy ;; start) start_services ;; stop) stop_services ;; restart) restart_services ;; status) show_status ;; logs) show_logs "$@" ;; load) preflight_checks; load_images "${1:-$PROJECT_ROOT}" ;; help|*) usage ;; esac