#!/bin/bash # YourDreamNameHere Production Deployment Script # This script deploys the YDN application to a fresh Ubuntu 24.04 server set -euo pipefail # Configuration DEPLOYMENT_USER="${DEPLOYMENT_USER:-root}" DEPLOYMENT_HOST="${DEPLOYMENT_HOST:-}" DOMAIN="${DOMAIN:-yourdreamnamehere.com}" DOCKER_REGISTRY="${DOCKER_REGISTRY:-}" VERSION="${VERSION:-latest}" ENVIRONMENT="${ENVIRONMENT:-production}" ENABLE_MONITORING="${ENABLE_MONITORING:-true}" ENABLE_LOGGING="${ENABLE_LOGGING:-true}" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 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" } # Validate environment validate_environment() { log_info "Validating deployment environment..." if [ -z "$DEPLOYMENT_HOST" ]; then log_error "DEPLOYMENT_HOST environment variable is required" exit 1 fi if [ -z "$DOMAIN" ]; then log_error "DOMAIN environment variable is required" exit 1 fi # Test SSH connection if ! ssh -o ConnectTimeout=10 -o BatchMode=yes "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" "echo 'SSH connection successful'"; then log_error "Failed to connect to $DEPLOYMENT_HOST via SSH" exit 1 fi log_success "Environment validation passed" } # Prepare remote server prepare_server() { log_info "Preparing remote server..." ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << 'EOF' set -euo pipefail # Update system apt-get update apt-get upgrade -y # Install required packages apt-get install -y \ curl \ wget \ git \ htop \ unzip \ software-properties-common \ apt-transport-https \ ca-certificates \ gnupg \ lsb-release \ ufw \ fail2ban \ logrotate \ certbot \ python3-certbot-nginx # Install Docker if ! command -v docker &> /dev/null; then curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null apt-get update apt-get install -y docker-ce docker-ce-cli containerd.io systemctl enable docker systemctl start docker fi # Install Docker Compose if ! command -v docker-compose &> /dev/null; then curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose fi # Create deployment directory mkdir -p /opt/ydn cd /opt/ydn # Create application user if ! id "ydn" &>/dev/null; then useradd -m -s /bin/bash ydn usermod -aG docker ydn fi # Set up directory structure mkdir -p {configs,scripts,logs,ssl,backups,data} chown -R ydn:ydn /opt/ydn # Configure firewall ufw --force reset ufw default deny incoming ufw default allow outgoing ufw allow ssh ufw allow 80/tcp ufw allow 443/tcp ufw --force enable # Configure fail2ban cat > /etc/fail2ban/jail.local << 'FAIL2BAN' [DEFAULT] bantime = 3600 findtime = 600 maxretry = 3 [sshd] enabled = true port = ssh logpath = /var/log/auth.log [nginx-http-auth] enabled = true port = http,https logpath = /var/log/nginx/error.log FAIL2BAN systemctl enable fail2ban systemctl restart fail2ban # Set up log rotation cat > /etc/logrotate.d/ydn << 'LOGROTATE' /opt/ydn/logs/*.log { daily missingok rotate 30 compress delaycompress notifempty create 644 ydn ydn postrotate docker kill -s USR1 ydn-nginx 2>/dev/null || true endscript } LOGROTATE log_success "Server preparation completed" EOF log_success "Remote server prepared" } # Deploy application files deploy_files() { log_info "Deploying application files..." # Create temporary deployment package TEMP_DIR=$(mktemp -d) trap "rm -rf $TEMP_DIR" EXIT # Copy necessary files cp -r "$PROJECT_DIR"/* "$TEMP_DIR/" # Create production environment file cat > "$TEMP_DIR/.env.prod" << EOF # Production Environment Configuration APP_ENV=production APP_NAME=YourDreamNameHere DOMAIN=$DOMAIN # Database Configuration DB_HOST=ydn-db DB_PORT=5432 DB_USER=ydn_user DB_PASSWORD=$(openssl rand -base64 32) DB_NAME=ydn_db DB_SSLMODE=require # Redis Configuration REDIS_HOST=ydn-redis REDIS_PORT=6379 REDIS_PASSWORD=$(openssl rand -base64 32) REDIS_DB=0 # JWT Configuration JWT_SECRET=$(openssl rand -base64 64) JWT_EXPIRY=24h # Stripe Configuration STRIPE_PUBLIC_KEY=$STRIPE_PUBLIC_KEY STRIPE_SECRET_KEY=$STRIPE_SECRET_KEY STRIPE_WEBHOOK_SECRET=$STRIPE_WEBHOOK_SECRET STRIPE_PRICE_ID=$STRIPE_PRICE_ID # OVH Configuration OVH_ENDPOINT=$OVH_ENDPOINT OVH_APPLICATION_KEY=$OVH_APPLICATION_KEY OVH_APPLICATION_SECRET=$OVH_APPLICATION_SECRET OVH_CONSUMER_KEY=$OVH_CONSUMER_KEY # Email Configuration SMTP_HOST=$SMTP_HOST SMTP_PORT=$SMTP_PORT SMTP_USER=$SMTP_USER SMTP_PASSWORD=$SMTP_PASSWORD SMTP_FROM=$SMTP_FROM # Dolibarr Configuration DOLIBARR_URL=https://$DOMAIN/dolibarr DOLIBARR_API_TOKEN=$DOLIBARR_API_TOKEN # Monitoring GRAFANA_ADMIN_PASSWORD=$(openssl rand -base64 16) # Version VERSION=$VERSION EOF # Copy files to remote server scp -r "$TEMP_DIR/"* "$DEPLOYMENT_USER@$DEPLOYMENT_HOST:/opt/ydn/" # Set correct permissions ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" "chown -R ydn:ydn /opt/ydn" log_success "Application files deployed" } # Generate SSL certificates setup_ssl() { log_info "Setting up SSL certificates..." ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << EOF set -euo pipefail cd /opt/ydn # Generate initial nginx config for SSL challenge cat > configs/nginx.init.conf << 'NGINX' events { worker_connections 1024; } http { server { listen 80; server_name $DOMAIN; location /.well-known/acme-challenge/ { root /var/www/html; } location / { return 301 https://\$server_name\$request_uri; } } } NGINX # Start temporary nginx for SSL challenge docker run -d \ --name ydn-nginx-temp \ -p 80:80 \ -v /opt/ydn/configs/nginx.init.conf:/etc/nginx/nginx.conf:ro \ -v /var/www/html:/var/www/html \ nginx:alpine # Wait for nginx to start sleep 10 # Request SSL certificate if [ ! -d "/etc/letsencrypt/live/$DOMAIN" ]; then certbot certonly --webroot \ -w /var/www/html \ -d $DOMAIN \ --email admin@$DOMAIN \ --agree-tos \ --no-eff-email \ --non-interactive fi # Stop temporary nginx docker stop ydn-nginx-temp docker rm ydn-nginx-temp # Copy certificates to project directory mkdir -p ssl cp /etc/letsencrypt/live/$DOMAIN/fullchain.pem ssl/ cp /etc/letsencrypt/live/$DOMAIN/privkey.pem ssl/ chown -R ydn:ydn ssl # Set up automatic renewal echo "0 12 * * * /usr/bin/certbot renew --quiet --deploy-hook 'docker kill -s HUP ydn-nginx'" | crontab - EOF log_success "SSL certificates configured" } # Deploy application stack deploy_application() { log_info "Deploying application stack..." ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << EOF set -euo pipefail cd /opt/ydn # Switch to ydn user sudo -u ydn bash << 'USER_SCRIPT' set -euo pipefail # Load environment variables source .env.prod # Build and push Docker image (if registry is configured) if [ -n "$DOCKER_REGISTRY" ]; then docker build -t $DOCKER_REGISTRY/ydn-app:\$VERSION . docker push $DOCKER_REGISTRY/ydn-app:\$VERSION fi # Create production docker-compose override cat > docker-compose.override.yml << 'OVERRIDE' version: '3.8' services: ydn-app: image: ${DOCKER_REGISTRY:-ydn-app}:ydn-app:\${VERSION:-latest} environment: - \${APP_ENV} - \${DB_HOST} - \${DB_USER} - \${DB_PASSWORD} - \${DB_NAME} - \${DB_SSLMODE} - \${REDIS_HOST} - \${REDIS_PASSWORD} - \${JWT_SECRET} - \${STRIPE_PUBLIC_KEY} - \${STRIPE_SECRET_KEY} - \${STRIPE_WEBHOOK_SECRET} - \${STRIPE_PRICE_ID} - \${OVH_ENDPOINT} - \${OVH_APPLICATION_KEY} - \${OVH_APPLICATION_SECRET} - \${OVH_CONSUMER_KEY} - \${SMTP_HOST} - \${SMTP_USER} - \${SMTP_PASSWORD} - \${SMTP_FROM} - \${DOLIBARR_URL} - \${DOLIBARR_API_TOKEN} ydn-nginx: volumes: - ./ssl:/etc/nginx/ssl:ro environment: - DOMAIN=\${DOMAIN} ydn-backup: environment: - DB_PASSWORD=\${DB_PASSWORD} - BACKUP_WEBHOOK_URL=\${BACKUP_WEBHOOK_URL:-} OVERRIDE # Deploy with monitoring if enabled if [ "$ENABLE_MONITORING" = "true" ]; then docker-compose -f docker-compose.prod.yml --profile monitoring up -d else docker-compose -f docker-compose.prod.yml up -d fi # Wait for services to be ready sleep 30 # Run database migrations docker-compose -f docker-compose.prod.yml exec ydn-app /app/main migrate || true USER_SCRIPT EOF log_success "Application stack deployed" } # Health check health_check() { log_info "Performing health checks..." # Wait for application to start sleep 60 # Check if services are running ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << EOF set -euo pipefail cd /opt/ydn # Check Docker containers if ! docker-compose -f docker-compose.prod.yml ps | grep -q "Up"; then echo "ERROR: Some containers are not running" docker-compose -f docker-compose.prod.yml ps exit 1 fi # Check application health for i in {1..30}; do if curl -f http://localhost/health > /dev/null 2>&1; then echo "Application health check passed" break fi if [ \$i -eq 30 ]; then echo "ERROR: Application health check failed" exit 1 fi sleep 10 done # Check SSL certificate if ! curl -f https://$DOMAIN/health > /dev/null 2>&1; then echo "ERROR: SSL health check failed" exit 1 fi echo "All health checks passed" EOF log_success "Health checks completed" } # Setup monitoring setup_monitoring() { if [ "$ENABLE_MONITORING" != "true" ]; then log_info "Monitoring is disabled" return 0 fi log_info "Setting up monitoring..." ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << EOF set -euo pipefail cd /opt/ydn # Configure Prometheus cat > configs/prometheus.prod.yml << 'PROMETHEUS' global: scrape_interval: 15s evaluation_interval: 15s rule_files: - "rules/*.yml" scrape_configs: - job_name: 'ydn-app' static_configs: - targets: ['ydn-app:8080'] metrics_path: /metrics - job_name: 'nginx' static_configs: - targets: ['ydn-nginx:9113'] - job_name: 'postgres' static_configs: - targets: ['ydn-db:5432'] - job_name: 'redis' static_configs: - targets: ['ydn-redis:6379'] alerting: alertmanagers: - static_configs: - targets: - alertmanager:9093 PROMETHEUS # Restart monitoring services sudo -u ydn docker-compose -f docker-compose.prod.yml --profile monitoring restart ydn-prometheus ydn-grafana EOF log_success "Monitoring configured" } # Deploy to production deploy_production() { log_info "Starting production deployment..." validate_environment prepare_server deploy_files setup_ssl deploy_application setup_monitoring health_check log_success "🎉 Production deployment completed successfully!" log_info "Application is available at: https://$DOMAIN" if [ "$ENABLE_MONITORING" = "true" ]; then log_info "Grafana is available at: https://$DOMAIN/grafana" fi log_info "Dolibarr is available at: https://$DOMAIN/dolibarr" } # Rollback deployment rollback() { log_warning "Rolling back deployment..." ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" << EOF set -euo pipefail cd /opt/ydn # Get previous version from git PREVIOUS_VERSION=\$(git log --format=%H -n 2 | tail -n 1) # Checkout previous version sudo -u ydn git checkout \$PREVIOUS_VERSION # Redeploy sudo -u ydn docker-compose -f docker-compose.prod.yml down sudo -u ydn docker-compose -f docker-compose.prod.yml up -d EOF log_success "Rollback completed" } # Show help show_help() { cat << EOF YourDreamNameHere Production Deployment Script Usage: $0 [OPTIONS] Commands: deploy Deploy to production (default) rollback Rollback to previous version status Show deployment status help Show this help message Environment Variables: DEPLOYMENT_HOST Target server hostname/IP (required) DOMAIN Domain name (required) DOCKER_REGISTRY Docker registry URL (optional) VERSION Version tag (default: latest) ENABLE_MONITORING Enable monitoring (default: true) ENABLE_LOGGING Enable logging (default: true) STRIPE_PUBLIC_KEY Stripe public key (required) STRIPE_SECRET_KEY Stripe secret key (required) STRIPE_WEBHOOK_SECRET Stripe webhook secret (required) STRIPE_PRICE_ID Stripe price ID (required) OVH_ENDPOINT OVH API endpoint OVH_APPLICATION_KEY OVH application key OVH_APPLICATION_SECRET OVH application secret OVH_CONSUMER_KEY OVH consumer key SMTP_HOST SMTP server hostname SMTP_PORT SMTP server port SMTP_USER SMTP username SMTP_PASSWORD SMTP password SMTP_FROM From email address DOLIBARR_API_TOKEN Dolibarr API token BACKUP_WEBHOOK_URL Backup notification webhook URL (optional) Examples: # Deploy to production DEPLOYMENT_HOST=192.168.1.100 DOMAIN=example.com $0 deploy # Deploy with monitoring disabled DEPLOYMENT_HOST=192.168.1.100 DOMAIN=example.com ENABLE_MONITORING=false $0 deploy # Rollback deployment DEPLOYMENT_HOST=192.168.1.100 DOMAIN=example.com $0 rollback EOF } # Main execution case "${1:-deploy}" in deploy) deploy_production ;; rollback) rollback ;; status) ssh "$DEPLOYMENT_USER@$DEPLOYMENT_HOST" "cd /opt/ydn && docker-compose -f docker-compose.prod.yml ps" ;; help|--help|-h) show_help ;; *) log_error "Unknown command: $1" show_help exit 1 ;; esac