#!/usr/bin/env bash # Run this ON the staging EC2 instance (via SSH) after provisioning. # Installs Docker and nginx, then configures the Cloudflare Origin Certificate. # No Certbot or Let's Encrypt — Cloudflare terminates SSL at the edge. # # Prerequisites: # 1. Generate a Cloudflare Origin Certificate for staging.musehub.ai: # Cloudflare Dashboard → staging.musehub.ai → SSL/TLS → Origin Server # → Create Certificate → RSA 2048 → 15 years # Save certificate → /etc/ssl/cloudflare/origin.pem # Save private key → /etc/ssl/cloudflare/origin.key # # 2. In Cloudflare: SSL/TLS → Overview → set mode to "Full (Strict)" # # Usage (from your Mac): # scp -i ~/.ssh/musehub-key.pem -r deploy/ ubuntu@:/home/ubuntu/ # # Copy cert files: # scp -i ~/.ssh/musehub-key.pem origin.pem origin.key ubuntu@:/home/ubuntu/ # ssh -i ~/.ssh/musehub-key.pem ubuntu@ # chmod +x ~/deploy/setup-ec2-staging.sh && ~/deploy/setup-ec2-staging.sh # # After this script completes, sync the code and start the stack: # rsync -az --exclude='.env' --exclude='node_modules/' --exclude='__pycache__/' \ # --exclude='.muse/' --exclude='*.pyc' \ # -e 'ssh -i ~/.ssh/musehub-key.pem' \ # ~/musehub/ ubuntu@:/opt/musehub/ # ssh -i ~/.ssh/musehub-key.pem ubuntu@ \ # 'cd /opt/musehub && sudo docker compose up -d --build' set -euo pipefail DOMAIN="staging.musehub.ai" APP_DIR="/opt/musehub" echo "==> [1/7] System update" sudo apt-get update -qq && sudo apt-get upgrade -y -qq echo "==> [2/7] Install Docker" curl -fsSL https://get.docker.com | sudo sh sudo usermod -aG docker ubuntu echo " Docker installed." echo "==> [3/7] Install nginx" sudo apt-get install -y -qq nginx echo "==> [4/7] Install Cloudflare Origin Certificate" sudo mkdir -p /etc/ssl/cloudflare # Copy certs from home dir if present (scp'd in before running this script) if [ -f ~/origin.pem ] && [ -f ~/origin.key ]; then sudo cp ~/origin.pem /etc/ssl/cloudflare/origin.pem sudo cp ~/origin.key /etc/ssl/cloudflare/origin.key sudo chmod 644 /etc/ssl/cloudflare/origin.pem sudo chmod 640 /etc/ssl/cloudflare/origin.key sudo chown root:root /etc/ssl/cloudflare/origin.pem /etc/ssl/cloudflare/origin.key echo " Origin certificate installed." else echo "" echo " !! CERT FILES NOT FOUND — install them manually before nginx will start:" echo " scp origin.pem origin.key ubuntu@:/home/ubuntu/" echo " sudo cp ~/origin.pem /etc/ssl/cloudflare/origin.pem" echo " sudo cp ~/origin.key /etc/ssl/cloudflare/origin.key" echo " sudo chmod 640 /etc/ssl/cloudflare/origin.key" echo " sudo nginx -s reload" echo "" fi echo "==> [5/7] Configure nginx" # Blue-green active-port file: deploy.sh rewrites this and does nginx -s reload # to switch slots with zero downtime. Starts pointing at blue (1337). echo "server 127.0.0.1:1337;" | sudo tee /etc/nginx/musehub-active-port > /dev/null # Install the Cloudflare-ready nginx config, substituting the domain sudo sed "s/DOMAIN_PLACEHOLDER/$DOMAIN/g" \ "$(dirname "$0")/nginx-cf.conf" \ | sudo tee /etc/nginx/sites-available/musehub-staging > /dev/null sudo ln -sf /etc/nginx/sites-available/musehub-staging /etc/nginx/sites-enabled/musehub-staging sudo rm -f /etc/nginx/sites-enabled/default sudo nginx -t sudo systemctl reload nginx echo " nginx configured for $DOMAIN" echo "==> [6/7] Create app directory" sudo mkdir -p "$APP_DIR" sudo chown ubuntu:ubuntu "$APP_DIR" echo " $APP_DIR ready (code will be rsynced from your Mac)" echo "==> [7/7] Create staging .env" if [ ! -f "$APP_DIR/.env" ]; then DB_PASSWORD=$(openssl rand -hex 16) WEBHOOK_SECRET_KEY=$(python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" 2>/dev/null || openssl rand -base64 32) RUNNER_TOKEN=$(openssl rand -hex 32) cat > "$APP_DIR/.env" << EOF DEBUG=false DATABASE_URL=postgresql+asyncpg://musehub:${DB_PASSWORD}@postgres:5432/musehub DB_PASSWORD=${DB_PASSWORD} CORS_ORIGINS=["https://staging.musehub.ai"] WEBHOOK_SECRET_KEY=${WEBHOOK_SECRET_KEY} MUSEHUB_ALLOWED_ORIGINS=["staging.musehub.ai"] RUNNER_TOKEN=${RUNNER_TOKEN} EOF echo " .env created" echo "" echo " !! SAVE THESE — they are in $APP_DIR/.env !!" cat "$APP_DIR/.env" echo "" else echo " .env already exists, skipping" fi echo "" echo "============================================================" echo " STAGING SETUP COMPLETE" echo "============================================================" echo " Domain : https://$DOMAIN" echo " App dir : $APP_DIR" echo " Secrets : $APP_DIR/.env (back these up!)" echo "" echo " Next: sync code from your Mac and start the stack:" echo " rsync -az --exclude='.env' --exclude='node_modules/' --exclude='__pycache__/' \\" echo " --exclude='.muse/' --exclude='*.pyc' \\" echo " -e 'ssh -i ~/.ssh/musehub-key.pem' \\" echo " ~/musehub/ ubuntu@\$(hostname -I | awk '{print \$1}'):/opt/musehub/" echo "" echo " cd /opt/musehub && sudo docker compose up -d postgres musehub-runner" echo " sudo docker compose build musehub" echo " sudo docker compose run --rm --no-deps musehub alembic upgrade head" echo " sudo docker run -d --name musehub-blue --network musehub-internal \\" echo " --env-file .env -e SKIP_MIGRATIONS=1 \\" echo " -e DATABASE_URL=postgresql+asyncpg://musehub:\$(grep ^DB_PASSWORD .env | cut -d= -f2)@postgres:5432/musehub \\" echo " -v musehub_data:/data -p 127.0.0.1:1337:1337 --restart unless-stopped musehub/musehub" echo " bash deploy/deploy.sh --init # sets .active-slot=blue" echo "" echo " Subsequent deploys (zero downtime):" echo " rsync ... && bash deploy/deploy.sh" echo "============================================================"