#!/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 ""