macsync/deploy/deploy-server.sh
Natalie 4ea358035a chore(mac-sync): manifest + deploy + bunfig updates
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 11:35:13 -04:00

165 lines
6.4 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
set -euo pipefail
# Deploy mac-sync-server to the DO backend droplet (lilith-store-backend).
#
# Homelan `black` (10.0.0.11) is dead — the server now runs on DigitalOcean per
# the uvlava rebuild (~/.claude/plans/nested-jingling-truffle.md, replacement
# item #8). Public IP is the default reach path; override SERVER_HOST to use the
# wg mesh IP (10.9.0.5) or the VPC private IP (10.20.0.2) where appropriate.
#
# Usage:
# ./deploy-server.sh
# ./deploy-server.sh --skip-build
# SERVER_HOST=10.9.0.5 ./deploy-server.sh # over the wg mesh instead
# Backend droplet: public 209.38.51.98 · wg 10.9.0.5 · VPC 10.20.0.2.
SERVER_HOST="${SERVER_HOST:-209.38.51.98}"
REMOTE_DIR="/opt/mac-sync-server"
ENV_DIR="/etc/mac-sync-server"
SERVICE_NAME="mac-sync-server"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SERVER_SRC="$SCRIPT_DIR/../src/server"
SKIP_BUILD=false
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
print_step() { echo -e "${GREEN}${NC} $1"; }
print_info() { echo -e "${BLUE}${NC} $1"; }
print_warning() { echo -e "${YELLOW}${NC} $1"; }
print_error() { echo -e "${RED}${NC} $1"; }
print_success() { echo -e "${GREEN}${NC} $1"; }
for arg in "$@"; do
case "$arg" in
--skip-build) SKIP_BUILD=true ;;
esac
done
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${BLUE} Mac Sync Server — Deploy to backend droplet ($SERVER_HOST)${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
check_host() {
print_step "Checking SSH to $SERVER_HOST..."
if ! ssh -o ConnectTimeout=5 "$SERVER_HOST" 'echo ok' >/dev/null 2>&1; then
print_error "Cannot reach $SERVER_HOST — check network/SSH config"
exit 1
fi
print_success "Connected"
}
sync_source() {
print_step "Syncing src/server/ to $SERVER_HOST:$REMOTE_DIR..."
ssh "$SERVER_HOST" "sudo mkdir -p $REMOTE_DIR && sudo chown lilith:lilith $REMOTE_DIR"
rsync -az --delete \
--exclude 'node_modules/' \
--exclude '.bun/' \
--exclude 'data/' \
"$SERVER_SRC/" \
"$SERVER_HOST:$REMOTE_DIR/"
print_success "Source synced"
}
install_deps() {
print_step "Installing dependencies on the droplet..."
ssh "$SERVER_HOST" "cd $REMOTE_DIR && bun install --frozen-lockfile"
print_success "Dependencies installed"
}
provision_env() {
# Create /etc/mac-sync-server/env if it doesn't exist.
# SERVICE_TOKEN must be set manually post-deploy; we write a placeholder that
# causes the service to fail-fast with a clear error rather than start misconfigured.
if ssh "$SERVER_HOST" "test -f $ENV_DIR/env" 2>/dev/null; then
print_info "env file exists at $ENV_DIR/env — skipping (preserving existing secrets)"
return
fi
print_step "Creating env file at $SERVER_HOST:$ENV_DIR/env..."
ssh "$SERVER_HOST" "sudo mkdir -p $ENV_DIR && sudo tee $ENV_DIR/env > /dev/null" <<'EOF'
PORT=3201
NODE_ENV=production
# DO Managed PG (nyc3, private :25060) — reached VPC-direct from the droplet, or
# via the wg→pgBouncer bridge (:6432) from off-droplet. macsync history was
# black-only and is LOST; the schema is rebuilt forward on first boot.
QUINN_MACSYNC_DB_URL=REPLACE_WITH_DB_URL
SERVICE_TOKEN=REPLACE_WITH_SECRET
SSO_VALIDATE_URL=http://localhost:3025/auth/validate
# Prospect classifier external lists (Handoff 01 #3 — both OPTIONAL; the
# classifier degrades gracefully when unset). BLOCK_LIST_PATH points at the
# block-list.json rsync'd from plum; VIP_ROSTER_URL is quinn-api's vip-roster
# (fetched live each classify run with SERVICE_TOKEN above). quinn-api lives on
# vps-0 today (wg 10.9.0.1:3030); moves to the backend droplet later (uvlava #9).
# BLOCK_LIST_PATH=/etc/mac-sync-server/block-list.json
# VIP_ROSTER_URL=http://10.9.0.1:3030/api/vip-roster
# Object storage. local = ./data/blobs (dev). s3 = any S3-compatible store.
STORAGE_BACKEND=s3
STORAGE_LOCAL_PATH=/opt/mac-sync-server/data/blobs
# DO Spaces (nyc3). Path-style is required for this bucket's writes.
S3_ENDPOINT=https://nyc3.digitaloceanspaces.com
S3_ACCESS_KEY=REPLACE_FROM_VAULT_do-spaces-uvlava.access
S3_SECRET_KEY=REPLACE_FROM_VAULT_do-spaces-uvlava.secret
S3_BUCKET=lilith-quinn-media
S3_REGION=us-east-1
S3_FORCE_PATH_STYLE=true
S3_PRESIGN_TTL_SECONDS=900
# Embedding inference endpoint — required, no default (fail fast on misconfig).
MODEL_BOSS_EMBED_URL=REPLACE_WITH_INFERENCE_URL
EOF
ssh "$SERVER_HOST" "sudo chmod 640 $ENV_DIR/env && sudo chown root:lilith $ENV_DIR/env"
print_warning "env file written — set SERVICE_TOKEN before starting: sudo nano $ENV_DIR/env"
}
install_systemd() {
print_step "Installing systemd unit..."
rsync -az "$SCRIPT_DIR/systemd/mac-sync-server.service" \
"$SERVER_HOST:/tmp/mac-sync-server.service"
ssh "$SERVER_HOST" \
"sudo cp /tmp/mac-sync-server.service /etc/systemd/system/mac-sync-server.service && \
sudo systemctl daemon-reload && \
sudo systemctl enable mac-sync-server"
print_success "Systemd unit installed and enabled"
}
restart_service() {
print_step "Restarting $SERVICE_NAME..."
ssh "$SERVER_HOST" "sudo systemctl restart $SERVICE_NAME"
sleep 3
local status
status=$(ssh "$SERVER_HOST" "systemctl is-active $SERVICE_NAME" 2>/dev/null || echo "unknown")
if [[ "$status" == "active" ]]; then
print_success "Service active"
else
print_warning "Service status: $status"
print_info "Check: ssh $SERVER_HOST 'journalctl -u $SERVICE_NAME -n 30'"
fi
}
verify_health() {
print_step "Checking health endpoint..."
local port=3201
if ssh "$SERVER_HOST" "curl -sf http://localhost:$port/health > /dev/null 2>&1"; then
print_success "Health check passed (port $port)"
else
print_warning "Health check failed — service may still be starting or SERVICE_TOKEN unset"
fi
}
check_host
sync_source
install_deps
provision_env
install_systemd
restart_service
verify_health
echo ""
print_success "Server deploy complete"
echo ""
print_info "If SERVICE_TOKEN was just set, run: ssh $SERVER_HOST 'sudo systemctl restart $SERVICE_NAME'"
print_info "View logs: ssh $SERVER_HOST 'journalctl -u $SERVICE_NAME -f'"