Monorepo for Aesthetic.Computer aesthetic.computer
at main 284 lines 11 kB view raw
1#!/usr/bin/env fish 2# Oven Deployment Script 3#!/usr/bin/env fish 4# Oven Deployment Script 5# Provisions and deploys the Oven video processing service to DigitalOcean 6 7# Colors for output 8set RED '\033[0;31m' 9set GREEN '\033[0;32m' 10set YELLOW '\033[1;33m' 11set NC '\033[0m' # No Color 12 13# Paths 14set SCRIPT_DIR (dirname (status --current-filename)) 15set VAULT_DIR "$SCRIPT_DIR/../aesthetic-computer-vault/oven" 16set DEPLOY_ENV "$VAULT_DIR/deploy.env" 17set SERVICE_ENV "$VAULT_DIR/.env" 18 19# Check for required files 20if not test -f $DEPLOY_ENV 21 echo -e "$RED❌ Deployment config not found: $DEPLOY_ENV$NC" 22 exit 1 23end 24 25if not test -f $SERVICE_ENV 26 echo -e "$RED❌ Service config not found: $SERVICE_ENV$NC" 27 exit 1 28end 29 30# Load deployment configuration (parse bash-style env file) 31echo -e "$GREEN🔧 Loading deployment configuration...$NC" 32for line in (cat $DEPLOY_ENV | grep -v '^#' | grep -v '^$' | grep '=') 33 set -l parts (string split '=' $line) 34 if test (count $parts) -ge 2 35 set -gx $parts[1] (string join '=' $parts[2..-1]) 36 end 37end 38 39set -e 40 41# Colors for output 42set RED '\033[0;31m' 43set GREEN '\033[0;32m' 44set YELLOW '\033[1;33m' 45set NC '\033[0m' # No Color 46 47# Paths 48set SCRIPT_DIR (dirname (status --current-filename)) 49set VAULT_DIR "$SCRIPT_DIR/../aesthetic-computer-vault/oven" 50set DEPLOY_ENV "$VAULT_DIR/deploy.env" 51set SERVICE_ENV "$VAULT_DIR/.env" 52 53# Check for required files 54if not test -f $DEPLOY_ENV 55 echo -e "$RED❌ Deployment config not found: $DEPLOY_ENV$NC" 56 exit 1 57end 58 59if not test -f $SERVICE_ENV 60 echo -e "$RED❌ Service config not found: $SERVICE_ENV$NC" 61 exit 1 62end 63 64# Load deployment configuration 65echo -e "$GREEN🔧 Loading deployment configuration...$NC" 66source $DEPLOY_ENV 67 68# Check for required tools 69if not command -v doctl &> /dev/null 70 echo -e "$RED❌ doctl not found. Install with: wget https://github.com/digitalocean/doctl/releases/download/v1.109.0/doctl-1.109.0-linux-amd64.tar.gz$NC" 71 exit 1 72end 73 74if not command -v node &> /dev/null 75 echo -e "$RED❌ node not found$NC" 76 exit 1 77end 78 79# Authenticate with DigitalOcean 80echo -e "$GREEN🔑 Authenticating with DigitalOcean...$NC" 81doctl auth init --access-token $DO_TOKEN 82 83# Check if droplet already exists 84echo -e "$GREEN🔍 Checking for existing droplet...$NC" 85set EXISTING_DROPLET (doctl compute droplet list --format Name,PublicIPv4 | grep "^$DROPLET_NAME" | awk '{print $2}') 86 87if test -n "$EXISTING_DROPLET" 88 echo -e "$YELLOW⚠️ Droplet $DROPLET_NAME already exists at $EXISTING_DROPLET$NC" 89 echo -e "$YELLOW Would you like to redeploy to the existing droplet? (y/n)$NC" 90 read -l CONFIRM 91 92 if test "$CONFIRM" != "y" 93 echo -e "$RED❌ Deployment cancelled$NC" 94 exit 1 95 end 96 97 set DROPLET_IP $EXISTING_DROPLET 98else 99 # Create SSH key 100 echo -e "$GREEN🔑 Creating SSH key...$NC" 101 set SSH_KEY_FILE "$HOME/.ssh/$SSH_KEY_NAME" 102 103 if not test -f $SSH_KEY_FILE 104 ssh-keygen -t ed25519 -f $SSH_KEY_FILE -N "" -C "oven@aesthetic.computer" 105 end 106 107 # Upload SSH key to DigitalOcean 108 set SSH_PUBLIC_KEY (cat "$SSH_KEY_FILE.pub") 109 doctl compute ssh-key import $SSH_KEY_NAME --public-key-file "$SSH_KEY_FILE.pub" 2>/dev/null; or true 110 111 # Create droplet 112 echo -e "$GREEN🚀 Creating droplet: $DROPLET_NAME...$NC" 113 echo -e "$YELLOW Size: $DROPLET_SIZE$NC" 114 echo -e "$YELLOW Region: $DROPLET_REGION$NC" 115 echo -e "$YELLOW Image: $DROPLET_IMAGE$NC" 116 117 doctl compute droplet create $DROPLET_NAME \ 118 --size $DROPLET_SIZE \ 119 --image $DROPLET_IMAGE \ 120 --region $DROPLET_REGION \ 121 --ssh-keys (doctl compute ssh-key list --format ID --no-header) \ 122 --wait 123 124 # Get droplet IP 125 echo -e "$GREEN⏳ Waiting for droplet to be ready...$NC" 126 sleep 30 127 128 set DROPLET_IP (doctl compute droplet list --format Name,PublicIPv4 | grep "^$DROPLET_NAME" | awk '{print $2}') 129 130 if test -z "$DROPLET_IP" 131 echo -e "$RED❌ Failed to get droplet IP$NC" 132 exit 1 133 end 134 135 echo -e "$GREEN✅ Droplet created: $DROPLET_IP$NC" 136 137 # Configure firewall 138 echo -e "$GREEN🔒 Configuring firewall...$NC" 139 doctl compute firewall create \ 140 --name "oven-firewall" \ 141 --inbound-rules "protocol:tcp,ports:22,sources:addresses:0.0.0.0/0,sources:addresses:::/0 protocol:tcp,ports:80,sources:addresses:0.0.0.0/0,sources:addresses:::/0 protocol:tcp,ports:443,sources:addresses:0.0.0.0/0,sources:addresses:::/0" \ 142 --outbound-rules "protocol:tcp,ports:all,destinations:addresses:0.0.0.0/0,destinations:addresses:::/0 protocol:udp,ports:all,destinations:addresses:0.0.0.0/0,destinations:addresses:::/0" \ 143 --droplet-ids (doctl compute droplet list --format ID --no-header | grep -A1 $DROPLET_NAME | tail -1) 2>/dev/null; or true 144end 145 146# Wait for SSH to be ready 147echo -e "$GREEN⏳ Waiting for SSH to be ready...$NC" 148set MAX_ATTEMPTS 30 149set ATTEMPT 1 150 151while test $ATTEMPT -le $MAX_ATTEMPTS 152 if ssh -i "$HOME/.ssh/$SSH_KEY_NAME" -o StrictHostKeyChecking=no -o ConnectTimeout=5 root@$DROPLET_IP "echo SSH ready" &>/dev/null 153 break 154 end 155 echo -e "$YELLOW Attempt $ATTEMPT/$MAX_ATTEMPTS...$NC" 156 sleep 10 157 set ATTEMPT (math $ATTEMPT + 1) 158end 159 160if test $ATTEMPT -gt $MAX_ATTEMPTS 161 echo -e "$RED❌ SSH connection timeout$NC" 162 exit 1 163end 164 165echo -e "$GREEN✅ SSH connection established$NC" 166 167# Create setup script 168echo -e "$GREEN📝 Creating server setup script...$NC" 169set SETUP_SCRIPT "/tmp/oven-setup.sh" 170 171echo '#!/bin/bash 172set -e 173 174echo "🔧 Setting up Oven server..." 175 176# Update system 177echo "📦 Updating system packages..." 178apt-get update 179apt-get upgrade -y 180 181# Install Node.js 20 182echo "📦 Installing Node.js 20..." 183curl -fsSL https://deb.nodesource.com/setup_20.x | bash - 184apt-get install -y nodejs 185 186# Install ffmpeg 187echo "📦 Installing ffmpeg..." 188apt-get install -y ffmpeg 189 190# Install Caddy 191echo "📦 Installing Caddy..." 192apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl 193curl -1sLf '"'"'https://dl.cloudsmith.io/public/caddy/stable/gpg.key'"'"' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg 194curl -1sLf '"'"'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt'"'"' | tee /etc/apt/sources.list.d/caddy-stable.list 195apt-get update 196apt-get install -y caddy 197 198# Create oven user 199echo "👤 Creating oven user..." 200useradd -m -s /bin/bash oven || true 201 202# Create directories 203echo "📁 Creating directories..." 204mkdir -p /opt/oven 205mkdir -p /var/log/oven 206chown -R oven:oven /opt/oven /var/log/oven 207 208echo "✅ Server setup complete"' > $SETUP_SCRIPT 209 210# Upload and run setup script 211echo -e "$GREEN📤 Uploading setup script...$NC" 212scp -i "$HOME/.ssh/$SSH_KEY_NAME" -o StrictHostKeyChecking=no $SETUP_SCRIPT root@$DROPLET_IP:/tmp/ 213ssh -i "$HOME/.ssh/$SSH_KEY_NAME" root@$DROPLET_IP "chmod +x /tmp/oven-setup.sh && /tmp/oven-setup.sh" 214 215# Upload service files 216echo -e "$GREEN📤 Uploading service files...$NC" 217scp -i "$HOME/.ssh/$SSH_KEY_NAME" -o StrictHostKeyChecking=no $SCRIPT_DIR/package.json root@$DROPLET_IP:/opt/oven/ 218scp -i "$HOME/.ssh/$SSH_KEY_NAME" -o StrictHostKeyChecking=no $SCRIPT_DIR/package-lock.json root@$DROPLET_IP:/opt/oven/ 219scp -i "$HOME/.ssh/$SSH_KEY_NAME" -o StrictHostKeyChecking=no $SCRIPT_DIR/server.mjs root@$DROPLET_IP:/opt/oven/ 220scp -i "$HOME/.ssh/$SSH_KEY_NAME" -o StrictHostKeyChecking=no $SCRIPT_DIR/baker.mjs root@$DROPLET_IP:/opt/oven/ 221scp -i "$HOME/.ssh/$SSH_KEY_NAME" -o StrictHostKeyChecking=no $SCRIPT_DIR/grabber.mjs root@$DROPLET_IP:/opt/oven/ 222scp -i "$HOME/.ssh/$SSH_KEY_NAME" -o StrictHostKeyChecking=no $SERVICE_ENV root@$DROPLET_IP:/opt/oven/.env 223 224# Update .env for production 225echo -e "$GREEN🔧 Configuring production environment...$NC" 226ssh -i "$HOME/.ssh/$SSH_KEY_NAME" root@$DROPLET_IP "cd /opt/oven && sed -i 's/NODE_ENV=development/NODE_ENV=production/g' .env && sed -i 's/PORT=3002/PORT=3000/g' .env" 227 228# Install dependencies 229echo -e "$GREEN📦 Installing Node.js dependencies...$NC" 230ssh -i "$HOME/.ssh/$SSH_KEY_NAME" root@$DROPLET_IP "cd /opt/oven && npm install --production" 231 232# Create systemd service 233echo -e "$GREEN🔧 Creating systemd service...$NC" 234ssh -i "$HOME/.ssh/$SSH_KEY_NAME" root@$DROPLET_IP "printf '[Unit]\nDescription=Oven Video Processing Service\nAfter=network.target\n\n[Service]\nType=simple\nUser=oven\nWorkingDirectory=/opt/oven\nEnvironment=NODE_ENV=production\nExecStart=/usr/bin/node /opt/oven/server.mjs\nRestart=always\nRestartSec=10\nStandardOutput=append:/var/log/oven/oven.log\nStandardError=append:/var/log/oven/oven-error.log\n\n[Install]\nWantedBy=multi-user.target\n' > /etc/systemd/system/oven.service" 235 236# Configure Caddy 237echo -e "$GREEN🔧 Configuring Caddy...$NC" 238ssh -i "$HOME/.ssh/$SSH_KEY_NAME" root@$DROPLET_IP "printf '{\n email me@jas.life\n}\n\n$OVEN_HOSTNAME {\n reverse_proxy localhost:3000\n encode gzip\n \n log {\n output file /var/log/caddy/oven.log\n }\n}\n' > /etc/caddy/Caddyfile" 239 240# Start services 241echo -e "$GREEN🚀 Starting services...$NC" 242ssh -i "$HOME/.ssh/$SSH_KEY_NAME" root@$DROPLET_IP "systemctl daemon-reload && systemctl enable oven && systemctl restart oven && systemctl restart caddy" 243 244# Wait for service to start 245echo -e "$GREEN⏳ Waiting for service to start...$NC" 246sleep 5 247 248# Check service status 249echo -e "$GREEN🔍 Checking service status...$NC" 250ssh -i "$HOME/.ssh/$SSH_KEY_NAME" root@$DROPLET_IP "systemctl status oven --no-pager | head -20" 251 252# Configure DNS 253echo -e "$GREEN🌐 Configuring DNS...$NC" 254echo -e "$YELLOW Creating A record: $OVEN_HOSTNAME -> $DROPLET_IP$NC" 255 256# Use Cloudflare API to create/update DNS record 257curl -X POST "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records" \ 258 -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ 259 -H "Content-Type: application/json" \ 260 --data "{\"type\":\"A\",\"name\":\"oven\",\"content\":\"$DROPLET_IP\",\"ttl\":1,\"proxied\":false}" \ 261 2>/dev/null || \ 262curl -X PUT "https://api.cloudflare.com/client/v4/zones/$CLOUDFLARE_ZONE_ID/dns_records" \ 263 -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \ 264 -H "Content-Type: application/json" \ 265 --data "{\"type\":\"A\",\"name\":\"oven\",\"content\":\"$DROPLET_IP\",\"ttl\":1,\"proxied\":false}" 266 267echo "" 268echo -e "$GREEN✅ Deployment complete!$NC" 269echo "" 270echo -e "$YELLOW📋 Service Information:$NC" 271echo -e " URL: https://$OVEN_HOSTNAME" 272echo -e " IP: $DROPLET_IP" 273echo -e " SSH: ssh -i ~/.ssh/$SSH_KEY_NAME root@$DROPLET_IP" 274echo "" 275echo -e "$YELLOW🔧 Useful commands:$NC" 276echo -e " Check status: systemctl status oven" 277echo -e " View logs: tail -f /var/log/oven/oven.log" 278echo -e " Restart: systemctl restart oven" 279echo "" 280echo -e "$YELLOW⚠️ Next steps:$NC" 281echo -e " 1. Update Netlify env var: OVEN_URL=https://$OVEN_HOSTNAME" 282echo -e " 2. Test health: curl https://$OVEN_HOSTNAME/health" 283echo -e " 3. Test dashboard: https://$OVEN_HOSTNAME" 284echo ""