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