← back to Handbag Auth Nextjs
scripts/devops/deploy.sh
224 lines
#!/bin/bash
#
# LUXVAULT Zero-Downtime Deployment Script
# Deploys new version with health checks and automatic rollback
#
# Usage: ./deploy.sh [version]
#
set -euo pipefail
PROJECT_DIR="/root/Projects/handbag-auth-nextjs"
AUCTION_VIEWER="$PROJECT_DIR/auction-viewer"
LOG_FILE="$PROJECT_DIR/logs/deployment.log"
BACKUP_DIR="$PROJECT_DIR/backups/deployments"
VERSION="${1:-$(date +%Y%m%d_%H%M%S)}"
HEALTH_URL="http://45.61.58.125:7500/health"
HEALTH_TIMEOUT=30
HEALTH_RETRIES=10
# Colors
GREEN='\033[0;32m'
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $*" | tee -a "$LOG_FILE"
}
error() {
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR:${NC} $*" | tee -a "$LOG_FILE" >&2
}
warn() {
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING:${NC} $*" | tee -a "$LOG_FILE"
}
# Create necessary directories
mkdir -p "$BACKUP_DIR" "$PROJECT_DIR/logs"
log "========================================="
log "Starting deployment: $VERSION"
log "========================================="
# Step 1: Pre-deployment checks
log "Step 1: Running pre-deployment checks..."
if ! pm2 status auction-viewer | grep -q "online"; then
error "auction-viewer is not currently running!"
exit 1
fi
# Check current health
if ! curl -sf --max-time 5 "$HEALTH_URL" > /dev/null; then
error "Current deployment is unhealthy, aborting"
exit 1
fi
log "✓ Pre-deployment checks passed"
# Step 2: Create backup
log "Step 2: Creating deployment backup..."
BACKUP_NAME="deployment_backup_${VERSION}"
BACKUP_PATH="$BACKUP_DIR/$BACKUP_NAME"
mkdir -p "$BACKUP_PATH"
# Backup current server files
cp -r "$AUCTION_VIEWER" "$BACKUP_PATH/auction-viewer"
# Backup database
cp "$PROJECT_DIR/data/auction-history/auctions.db" "$BACKUP_PATH/auctions.db" 2>/dev/null || true
# Save PM2 configuration
pm2 save --force > /dev/null 2>&1 || true
log "✓ Backup created: $BACKUP_PATH"
# Step 3: Install dependencies (if package.json changed)
log "Step 3: Checking dependencies..."
cd "$AUCTION_VIEWER"
if [ -f "package.json" ]; then
if [ ! -d "node_modules" ] || [ "package.json" -nt "node_modules" ]; then
log "Installing/updating dependencies..."
npm ci --production 2>&1 | tee -a "$LOG_FILE"
log "✓ Dependencies updated"
else
log "✓ Dependencies up to date"
fi
fi
# Step 4: Configuration validation
log "Step 4: Validating configuration..."
if [ ! -f ".env" ]; then
warn "No .env file found, using defaults"
fi
# Check if required files exist
REQUIRED_FILES=("server.js" "package.json")
for file in "${REQUIRED_FILES[@]}"; do
if [ ! -f "$file" ]; then
error "Required file missing: $file"
exit 1
fi
done
log "✓ Configuration valid"
# Step 5: Reload PM2 with graceful restart
log "Step 5: Deploying new version..."
pm2 reload auction-viewer --update-env 2>&1 | tee -a "$LOG_FILE"
log "✓ PM2 reload initiated"
# Step 6: Health check with retries
log "Step 6: Verifying deployment health..."
sleep 3 # Give server time to start
for i in $(seq 1 $HEALTH_RETRIES); do
log "Health check attempt $i/$HEALTH_RETRIES..."
if curl -sf --max-time 5 "$HEALTH_URL" > /dev/null; then
HEALTH_RESPONSE=$(curl -s "$HEALTH_URL")
if echo "$HEALTH_RESPONSE" | grep -q '"status":"healthy"'; then
log "✓ Deployment healthy!"
break
else
warn "Health endpoint returned non-healthy status"
fi
else
warn "Health check failed"
fi
if [ $i -eq $HEALTH_RETRIES ]; then
error "Deployment failed health checks after $HEALTH_RETRIES attempts"
log "Rolling back..."
# Rollback
pm2 stop auction-viewer
rm -rf "$AUCTION_VIEWER"
cp -r "$BACKUP_PATH/auction-viewer" "$AUCTION_VIEWER"
pm2 restart auction-viewer
error "Rollback complete. Deployment failed."
exit 1
fi
sleep 3
done
# Step 7: Verify API functionality
log "Step 7: Testing API endpoints..."
API_ENDPOINTS=(
"/api/auctions?limit=1"
"/api/stats"
"/metrics"
)
for endpoint in "${API_ENDPOINTS[@]}"; do
if curl -sf --max-time 5 "http://45.61.58.125:7500${endpoint}" > /dev/null; then
log "✓ ${endpoint} responding"
else
error "API endpoint failed: ${endpoint}"
exit 1
fi
done
log "✓ All API endpoints responding"
# Step 8: Verify database connectivity
log "Step 8: Verifying database connectivity..."
DB_CHECK=$(curl -s "http://45.61.58.125:7500/api/stats" | grep -o '"total":[0-9]*' || echo "")
if [ -n "$DB_CHECK" ]; then
log "✓ Database connectivity confirmed: $DB_CHECK"
else
error "Database connectivity check failed"
exit 1
fi
# Step 9: Monitor for errors
log "Step 9: Monitoring for startup errors..."
sleep 5
ERROR_COUNT=$(pm2 logs auction-viewer --lines 20 --nostream 2>/dev/null | grep -c "ERROR" || true)
if [ "$ERROR_COUNT" -gt 3 ]; then
warn "High error count detected: $ERROR_COUNT errors"
pm2 logs auction-viewer --lines 20 --nostream | tee -a "$LOG_FILE"
fi
# Step 10: Cleanup old backups
log "Step 10: Cleanup old deployment backups..."
find "$BACKUP_DIR" -name "deployment_backup_*" -mtime +7 -exec rm -rf {} \; 2>/dev/null || true
BACKUP_COUNT=$(find "$BACKUP_DIR" -name "deployment_backup_*" -type d | wc -l)
log "✓ Cleanup complete. Remaining deployment backups: $BACKUP_COUNT"
# Deployment summary
log "========================================="
log "✅ DEPLOYMENT SUCCESSFUL"
log "========================================="
log "Version: $VERSION"
log "Health URL: $HEALTH_URL"
log "Dashboard: http://45.61.58.125:7500"
log "Backup location: $BACKUP_PATH"
log ""
log "Rollback command (if needed):"
log " pm2 stop auction-viewer"
log " rm -rf $AUCTION_VIEWER"
log " cp -r $BACKUP_PATH/auction-viewer $AUCTION_VIEWER"
log " pm2 restart auction-viewer"
log "========================================="
exit 0