#!/bin/bash set -e # ───────────────────────────────────────────────────────────────────────────── # Semblance — deploy script (run directly on server) # /opt/semblance/deploy.sh # ───────────────────────────────────────────────────────────────────────────── DEPLOY_DIR="/opt/semblance" FRONTEND_DEST="/var/www/html/semblance" echo "======================================" echo "Semblance — starting deployment" echo "======================================" cd "$DEPLOY_DIR" # ── Pre-flight ──────────────────────────────────────────────────────────────── echo "" echo "[pre-flight] Checking backend/.env..." if [ ! -f backend/.env ]; then echo "ERROR: backend/.env not found." echo " cp backend/.env.example backend/.env && nano backend/.env" exit 1 fi for VAR in SECRET_KEY JWT_SECRET_KEY OPENAI_API_KEY GEMINI_API_KEY; do if ! grep -q "^${VAR}=.\+" backend/.env 2>/dev/null; then echo "ERROR: $VAR is not set in backend/.env" exit 1 fi done echo "✓ backend/.env OK" # ── Step 1: Pull latest code ────────────────────────────────────────────────── echo "" echo "[1/4] Pulling latest code..." git pull # ── Step 2: Build frontend (Node in Docker) ─────────────────────────────────── echo "" echo "[2/4] Building frontend..." sudo mkdir -p "$FRONTEND_DEST" docker compose --profile build run --rm frontend sudo chown -R www-data:www-data "$FRONTEND_DEST" echo "✓ Frontend deployed to $FRONTEND_DEST" # ── Step 3: Ensure Apache proxy config ─────────────────────────────────────── echo "" echo "[3/4] Ensuring Apache proxy config..." CONF="/etc/apache2/sites-enabled/optical-dev.oliver.solutions.conf" if sudo grep -q "semblance_back" "$CONF" 2>/dev/null; then echo "✓ Apache semblance config already present" # Ensure COOP header is present (needed for MSAL popup auth) if ! sudo grep -q "Cross-Origin-Opener-Policy" "$CONF" 2>/dev/null; then echo "Adding COOP header for MSAL popup support..." sudo sed -i "s|RewriteRule \^ index.html \[L\]|RewriteRule ^ index.html [L]\n Header set Cross-Origin-Opener-Policy \"same-origin-allow-popups\"|" "$CONF" sudo apache2ctl configtest && sudo systemctl reload apache2 echo "✓ COOP header added" fi else echo "Adding semblance blocks to Apache config..." sudo sed -i "s|| # ----------------------------------------------------------------\n # Semblance — Quart/Hypercorn backend at :5137\n # ----------------------------------------------------------------\n\n # WebSocket (Socket.IO)\n RewriteCond %{HTTP:Upgrade} websocket [NC]\n RewriteCond %{HTTP:Connection} upgrade [NC]\n RewriteRule ^/semblance_back/socket.io/(.*) ws://127.0.0.1:5137/socket.io/\$1 [P,L]\n\n # REST API\n ProxyPass /semblance_back/api/ http://127.0.0.1:5137/api/\n ProxyPassReverse /semblance_back/api/ http://127.0.0.1:5137/api/\n\n # Socket.IO HTTP polling fallback\n ProxyPass /semblance_back/socket.io/ http://127.0.0.1:5137/socket.io/\n ProxyPassReverse /semblance_back/socket.io/ http://127.0.0.1:5137/socket.io/\n\n # Semblance SPA\n Alias /semblance $FRONTEND_DEST\n \n Options -Indexes +FollowSymLinks\n AllowOverride None\n Require all granted\n RewriteEngine On\n RewriteBase /semblance/\n RewriteCond %{REQUEST_FILENAME} !-f\n RewriteCond %{REQUEST_FILENAME} !-d\n RewriteRule ^ index.html [L]\n Header set Cross-Origin-Opener-Policy \"same-origin-allow-popups\"\n \n\n|" "$CONF" sudo apache2ctl configtest && sudo systemctl reload apache2 echo "✓ Apache config updated and reloaded" fi # ── Step 4: Rebuild and restart backend + mongo ─────────────────────────────── echo "" echo "[4/5] Rebuilding and restarting Docker services..." docker compose up -d --build # Wait for backend to be healthy before running migrations echo "Waiting for backend container to be healthy..." TRIES=0 until docker compose exec backend python -c "import sys; sys.exit(0)" 2>/dev/null; do TRIES=$((TRIES + 1)) if [ "$TRIES" -ge 30 ]; then echo "ERROR: Backend container did not become ready in time." docker compose logs --tail=30 backend exit 1 fi sleep 2 done echo "✓ Backend container is ready" # ── Step 5: Run migrations / seed scripts ──────────────────────────────────── echo "" echo "[5/5] Running migrations..." # Seed model pricing (idempotent — safe to run on every deploy) echo " → seed_model_pricing.py" docker compose exec backend python scripts/seed_model_pricing.py echo " ✓ Model pricing seeded" echo "" echo "Container status:" docker compose ps echo "" echo "======================================" echo "Deployment complete!" echo " https://optical-dev.oliver.solutions/semblance/" echo "======================================"