#!/usr/bin/env bash # Full first-time bootstrap on the VPS. # Idempotent — safe to re-run; rsync only ships diffs, docker compose only restarts changed services. # # Steps: # 1. mkdir /data/disclosure on VPS # 2. rsync compose files + web/ source + wiki/ + processing/png + processing/crops # 3. SSH: docker compose up -d db # 4. Wait for db healthy # 5. Apply Supabase init + chat schema # 6. docker compose up -d (everything else) # 7. Report URLs set -euo pipefail source "$(dirname "$0")/_lib.sh" LAPTOP_UFO_ROOT="${LAPTOP_UFO_ROOT:-/Users/guto/ufo}" STACK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" echo "================================================================" echo " STAGE A — create deploy dir + sync code (fast)" echo "================================================================" vps_ssh "mkdir -p ${VPS_DEPLOY_ROOT}/{web,wiki,processing,raw,migrations}" echo "" echo "→ Syncing infra/disclosure-stack/ (compose + kong + .env + scripts)" vps_rsync "${STACK_DIR}/docker-compose.yml" "${VPS_DEPLOY_ROOT}/docker-compose.yml" vps_rsync "${STACK_DIR}/kong.yml" "${VPS_DEPLOY_ROOT}/kong.yml" vps_rsync "${STACK_DIR}/init-db.sql" "${VPS_DEPLOY_ROOT}/migrations/00-init.sql" vps_rsync "${STACK_DIR}/../supabase/migrations/0001_chat_schema.sql" "${VPS_DEPLOY_ROOT}/migrations/01-chat-schema.sql" vps_rsync "${STACK_DIR}/../supabase/migrations/0002_chunks_retrieval.sql" "${VPS_DEPLOY_ROOT}/migrations/02-chunks-retrieval.sql" vps_rsync "${STACK_DIR}/.env" "${VPS_DEPLOY_ROOT}/.env" echo "" echo "→ Syncing web/ (Next.js source, excl. node_modules/.next)" if [ "$VPS_AUTH" = "password" ]; then SSHPASS="$VPS_PASSWORD" sshpass -e rsync -avz --delete \ --exclude node_modules --exclude .next --exclude .env.local \ -e "ssh -o StrictHostKeyChecking=accept-new -p $VPS_PORT" \ "${LAPTOP_UFO_ROOT}/web/" "${VPS_USER}@${VPS_HOST}:${VPS_DEPLOY_ROOT}/web/" else rsync -avz --delete \ --exclude node_modules --exclude .next --exclude .env.local \ -e "ssh -o StrictHostKeyChecking=accept-new -p $VPS_PORT -i ${VPS_SSH_KEY/#\~/$HOME}" \ "${LAPTOP_UFO_ROOT}/web/" "${VPS_USER}@${VPS_HOST}:${VPS_DEPLOY_ROOT}/web/" fi echo "" echo "================================================================" echo " STAGE B — sync wiki + processing (data — can be slow first time)" echo "================================================================" echo "→ Syncing wiki/ (markdown)" vps_rsync "${LAPTOP_UFO_ROOT}/wiki/" "${VPS_DEPLOY_ROOT}/wiki/" echo "" echo "→ Syncing processing/png/ (page images — large)" vps_rsync "${LAPTOP_UFO_ROOT}/processing/png/" "${VPS_DEPLOY_ROOT}/processing/png/" echo "" echo "→ Syncing processing/ocr/ (text)" vps_rsync "${LAPTOP_UFO_ROOT}/processing/ocr/" "${VPS_DEPLOY_ROOT}/processing/ocr/" echo "" echo "→ Syncing processing/crops/ + processing/tables/ + processing/uap-frames/" vps_rsync "${LAPTOP_UFO_ROOT}/processing/crops/" "${VPS_DEPLOY_ROOT}/processing/crops/" 2>&1 || true vps_rsync "${LAPTOP_UFO_ROOT}/processing/tables/" "${VPS_DEPLOY_ROOT}/processing/tables/" 2>&1 || true vps_rsync "${LAPTOP_UFO_ROOT}/processing/case-images/" "${VPS_DEPLOY_ROOT}/processing/case-images/" 2>&1 || true vps_rsync "${LAPTOP_UFO_ROOT}/processing/uap-frames/" "${VPS_DEPLOY_ROOT}/processing/uap-frames/" 2>&1 || true vps_rsync "${LAPTOP_UFO_ROOT}/processing/video-analysis/" "${VPS_DEPLOY_ROOT}/processing/video-analysis/" 2>&1 || true echo "" echo "================================================================" echo " STAGE C — start stack on VPS" echo "================================================================" vps_ssh "set -e cd ${VPS_DEPLOY_ROOT} echo '→ docker compose up -d db' docker compose up -d db echo '→ Waiting for db healthy…' for i in {1..30}; do STATUS=\$(docker inspect --format='{{.State.Health.Status}}' disclosure-db 2>/dev/null || echo starting) if [ \"\$STATUS\" = healthy ]; then echo ' ✓ db healthy'; break; fi sleep 2 done echo '' echo '→ Applying init-db.sql (roles + schemas)' docker exec -i disclosure-db psql -U postgres < migrations/00-init.sql 2>&1 | tail -5 || true echo '' echo '→ Applying chat schema' docker exec -i disclosure-db psql -U postgres < migrations/01-chat-schema.sql 2>&1 | tail -5 || true echo '' echo '→ Applying chunks retrieval schema (pgvector + hybrid_search)' docker exec -i disclosure-db psql -U postgres < migrations/02-chunks-retrieval.sql 2>&1 | tail -5 || true echo '' echo '→ docker compose up -d (entire stack incl. embed-service)' docker compose up -d echo '' echo '→ Status:' docker compose ps " echo "" echo "================================================================" echo " ✓ Bootstrap complete" echo "================================================================" echo "" echo "URLs (give DNS + TLS issuance ~5 min):" echo " Main app: https://${DOMAIN_MAIN}" echo " Studio: https://${DOMAIN_STUDIO}" echo " Supa API: https://${DOMAIN_API}" echo " Search: https://${DOMAIN_SEARCH}" echo "" echo "Tail container logs: ./scripts/logs.sh " echo "Service list: ./scripts/ssh.sh 'cd ${VPS_DEPLOY_ROOT} && docker compose ps'"