macsync/deploy/deploy-server.sh
Natalie 576496ca3e feat(deploy): video-projects FUSE mount over DO Spaces
Generalize the photos-originals rclone-mount pattern to a video-projects
prefix so the video studio (and imajin ETL, per storage-portability-plan
§2.3) can read/write multi-GB project sources/renders as local files while
only hot data stays resident on plum (bounded VFS LRU cache). Lets a
small-disk laptop work with large footage without filling APFS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 21:10:13 -04:00

148 lines
5.2 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 black (10.0.0.11).
#
# Usage:
# ./deploy-server.sh
# ./deploy-server.sh --skip-build
BLACK_HOST="${BLACK_HOST:-10.0.0.11}"
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 black ($BLACK_HOST)${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
check_black() {
print_step "Checking SSH to $BLACK_HOST..."
if ! ssh -o ConnectTimeout=5 "$BLACK_HOST" 'echo ok' >/dev/null 2>&1; then
print_error "Cannot reach $BLACK_HOST — check network/SSH config"
exit 1
fi
print_success "Connected"
}
sync_source() {
print_step "Syncing src/server/ to $BLACK_HOST:$REMOTE_DIR..."
ssh "$BLACK_HOST" "sudo mkdir -p $REMOTE_DIR && sudo chown lilith:lilith $REMOTE_DIR"
rsync -az --delete \
--exclude 'node_modules/' \
--exclude '.bun/' \
--exclude 'data/' \
"$SERVER_SRC/" \
"$BLACK_HOST:$REMOTE_DIR/"
print_success "Source synced"
}
install_deps() {
print_step "Installing dependencies on black..."
ssh "$BLACK_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 "$BLACK_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 $BLACK_HOST:$ENV_DIR/env..."
ssh "$BLACK_HOST" "sudo mkdir -p $ENV_DIR && sudo tee $ENV_DIR/env > /dev/null" <<'EOF'
PORT=3201
NODE_ENV=production
QUINN_MACSYNC_DB_URL=REPLACE_WITH_DB_URL
SERVICE_TOKEN=REPLACE_WITH_SECRET
SSO_VALIDATE_URL=http://localhost:3025/auth/validate
# 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 "$BLACK_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" \
"$BLACK_HOST:/tmp/mac-sync-server.service"
ssh "$BLACK_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 "$BLACK_HOST" "sudo systemctl restart $SERVICE_NAME"
sleep 3
local status
status=$(ssh "$BLACK_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 $BLACK_HOST 'journalctl -u $SERVICE_NAME -n 30'"
fi
}
verify_health() {
print_step "Checking health endpoint..."
local port=3201
if ssh "$BLACK_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_black
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 $BLACK_HOST 'sudo systemctl restart $SERVICE_NAME'"
print_info "View logs: ssh $BLACK_HOST 'journalctl -u $SERVICE_NAME -f'"