- ADR-0001: Multi-Agent Coworking Architecture with LLM Gateway Orchestrator - ADR-0002: Tier Assignment Strategy for Model Selection (cost-first escalation) - ADR-0003: Confidence Gate Thresholds & Learning Cycle Intervals (6h/12h/24h cycles) - ADR-0004: External Provider Fallback Chain Ordering (Cerebras → Groq → Mistral) - Enhanced client SDK: Offline Ollama fallback, health checks, exponential backoff retry - Integration tests: claude-code-integration.test.ts (14 test cases) - PHASE_2F_DEPLOYMENT.md: Pre-deployment checklist, automated deploy, rollback plan - Post-deployment verification procedures for health, client fallback, metrics
196 lines
6.8 KiB
Bash
Executable File
196 lines
6.8 KiB
Bash
Executable File
#!/bin/bash
|
|
# ============================================================
|
|
# LLM Gateway — Full server setup for Erik (82.165.222.127)
|
|
#
|
|
# Run ONCE on a fresh server. Idempotent: safe to re-run.
|
|
# Prerequisites: PostgreSQL 17, Node.js 22, git, pm2
|
|
# ============================================================
|
|
set -euo pipefail
|
|
|
|
GATEWAY_DIR="/opt/llm-gateway"
|
|
LOG_DIR="/var/log/llm-gateway"
|
|
GITEA_REPO="http://gitea.context-x.org/rene/llm-gateway.git"
|
|
DB_NAME="llm_gateway"
|
|
DB_USER="llm"
|
|
DB_PASS="llm_secure_password"
|
|
PM2_USER="${SUDO_USER:-root}"
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
|
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; exit 1; }
|
|
section() { echo -e "\n${GREEN}==============================${NC}"; echo -e "${GREEN}$*${NC}"; echo -e "${GREEN}==============================${NC}"; }
|
|
|
|
# -------------------------------------------------------
|
|
# 0. Preflight checks
|
|
# -------------------------------------------------------
|
|
section "0. Preflight checks"
|
|
|
|
[[ $EUID -eq 0 ]] || error "Run as root: sudo bash deploy/setup-erik.sh"
|
|
|
|
command -v node >/dev/null || error "Node.js not found. Install Node.js 22 first."
|
|
command -v npm >/dev/null || error "npm not found."
|
|
command -v psql >/dev/null || error "psql not found. Install PostgreSQL 17 first."
|
|
command -v pm2 >/dev/null || error "pm2 not found. Run: npm install -g pm2"
|
|
command -v git >/dev/null || error "git not found."
|
|
|
|
NODE_VER=$(node --version | cut -d. -f1 | tr -d 'v')
|
|
[[ $NODE_VER -ge 22 ]] || warn "Node.js 22+ recommended. Found: $(node --version)"
|
|
|
|
info "All preflight checks passed."
|
|
|
|
# -------------------------------------------------------
|
|
# 1. Create application directory
|
|
# -------------------------------------------------------
|
|
section "1. Creating application directory"
|
|
|
|
if [[ -d "$GATEWAY_DIR" ]]; then
|
|
warn "$GATEWAY_DIR already exists — skipping git clone (will pull later)"
|
|
else
|
|
git clone "$GITEA_REPO" "$GATEWAY_DIR"
|
|
info "Cloned repository to $GATEWAY_DIR"
|
|
fi
|
|
|
|
cd "$GATEWAY_DIR"
|
|
|
|
# -------------------------------------------------------
|
|
# 2. Create log directory
|
|
# -------------------------------------------------------
|
|
section "2. Creating log directory"
|
|
|
|
mkdir -p "$LOG_DIR"
|
|
chown -R "$PM2_USER:$PM2_USER" "$LOG_DIR" 2>/dev/null || true
|
|
info "Log directory: $LOG_DIR"
|
|
|
|
# -------------------------------------------------------
|
|
# 3. PostgreSQL — database + user + migrations
|
|
# -------------------------------------------------------
|
|
section "3. Setting up PostgreSQL"
|
|
|
|
# Create user if not exists
|
|
if sudo -u postgres psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'" | grep -q 1; then
|
|
info "PostgreSQL user '$DB_USER' already exists."
|
|
else
|
|
sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';"
|
|
info "Created PostgreSQL user '$DB_USER'."
|
|
fi
|
|
|
|
# Create database if not exists
|
|
if sudo -u postgres psql -tAc "SELECT 1 FROM pg_database WHERE datname='$DB_NAME'" | grep -q 1; then
|
|
info "Database '$DB_NAME' already exists."
|
|
else
|
|
sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;"
|
|
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;"
|
|
info "Created database '$DB_NAME'."
|
|
fi
|
|
|
|
# Run migrations
|
|
info "Running migrations..."
|
|
MIGRATION_DIR="$GATEWAY_DIR/packages/gateway/src/db/migrations"
|
|
if [[ -d "$MIGRATION_DIR" ]]; then
|
|
for sql_file in "$MIGRATION_DIR"/*.sql; do
|
|
[[ -f "$sql_file" ]] || continue
|
|
filename=$(basename "$sql_file")
|
|
info " Applying migration: $filename"
|
|
PGPASSWORD="$DB_PASS" psql -U "$DB_USER" -d "$DB_NAME" -h localhost -f "$sql_file" \
|
|
&& info " ✓ $filename" \
|
|
|| warn " Migration $filename may have already been applied (ignoring error)"
|
|
done
|
|
else
|
|
warn "Migration directory not found at $MIGRATION_DIR — skipping migrations"
|
|
fi
|
|
|
|
# Learning engine migrations
|
|
LEARNING_MIGRATION_DIR="$GATEWAY_DIR/packages/learning/src/db/migrations"
|
|
if [[ -d "$LEARNING_MIGRATION_DIR" ]]; then
|
|
for sql_file in "$LEARNING_MIGRATION_DIR"/*.sql; do
|
|
[[ -f "$sql_file" ]] || continue
|
|
filename=$(basename "$sql_file")
|
|
info " Applying learning migration: $filename"
|
|
PGPASSWORD="$DB_PASS" psql -U "$DB_USER" -d "$DB_NAME" -h localhost -f "$sql_file" \
|
|
&& info " ✓ $filename" \
|
|
|| warn " Migration $filename may have already been applied (ignoring error)"
|
|
done
|
|
fi
|
|
|
|
# -------------------------------------------------------
|
|
# 4. npm install + build
|
|
# -------------------------------------------------------
|
|
section "4. Installing dependencies and building"
|
|
|
|
cd "$GATEWAY_DIR"
|
|
npm install
|
|
npm run build
|
|
|
|
info "Build complete."
|
|
|
|
# -------------------------------------------------------
|
|
# 5. PM2 — register and start processes
|
|
# -------------------------------------------------------
|
|
section "5. Starting PM2 processes"
|
|
|
|
# If already registered, reload; otherwise start fresh
|
|
if pm2 list | grep -q "llm-gateway"; then
|
|
info "PM2 process 'llm-gateway' exists — reloading..."
|
|
pm2 reload llm-gateway
|
|
else
|
|
info "Starting PM2 processes from ecosystem config..."
|
|
pm2 start "$GATEWAY_DIR/deploy/ecosystem.config.cjs"
|
|
fi
|
|
|
|
# Save PM2 state so it survives reboots
|
|
pm2 save
|
|
|
|
# Register PM2 startup script (only if not already done)
|
|
if ! systemctl is-enabled pm2-root &>/dev/null 2>&1 && ! systemctl is-enabled "pm2-$PM2_USER" &>/dev/null 2>&1; then
|
|
info "Registering PM2 startup hook..."
|
|
pm2 startup systemd -u "$PM2_USER" --hp "/root" | tail -1 | bash || true
|
|
fi
|
|
|
|
# -------------------------------------------------------
|
|
# 6. Health check
|
|
# -------------------------------------------------------
|
|
section "6. Health check"
|
|
|
|
info "Waiting 5s for gateway to start..."
|
|
sleep 5
|
|
|
|
MAX_RETRIES=10
|
|
RETRY_DELAY=3
|
|
HEALTH_URL="http://localhost:3100/health/live"
|
|
|
|
for i in $(seq 1 $MAX_RETRIES); do
|
|
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$HEALTH_URL" 2>/dev/null || echo "000")
|
|
if [[ "$STATUS" == "200" ]]; then
|
|
info "Health check PASSED (HTTP 200)"
|
|
break
|
|
fi
|
|
if [[ $i -eq $MAX_RETRIES ]]; then
|
|
warn "Health check did not return 200 after ${MAX_RETRIES} attempts (got: $STATUS)"
|
|
warn "Check logs: pm2 logs llm-gateway"
|
|
else
|
|
info " Attempt $i/$MAX_RETRIES — got HTTP $STATUS, retrying in ${RETRY_DELAY}s..."
|
|
sleep $RETRY_DELAY
|
|
fi
|
|
done
|
|
|
|
# -------------------------------------------------------
|
|
# 7. Summary
|
|
# -------------------------------------------------------
|
|
section "Setup complete"
|
|
echo ""
|
|
echo " Gateway: http://localhost:3100"
|
|
echo " Health: http://localhost:3100/health"
|
|
echo " Logs: pm2 logs llm-gateway"
|
|
echo " PM2 UI: pm2 monit"
|
|
echo ""
|
|
echo " Next steps:"
|
|
echo " 1. Add Cloudflare tunnel ingress (see deploy/cloudflare-tunnel.md)"
|
|
echo " 2. Pull Ollama models: bash scripts/pull-models.sh"
|
|
echo " 3. Verify: curl http://localhost:3100/health"
|
|
echo ""
|