#!/usr/bin/env bash set -euo pipefail # ── OTIS Intelligence Install Script ───────────────────────────────────────── # Usage: curl -fsSL get.otisintelligence.co.uk | bash # Installs OTIS on any Linux, macOS, or Windows (WSL) machine with Docker. OTIS_IMAGE="ghcr.io/omihon/otis-api:latest" OTIS_DIR="${HOME}/.otis" COMPOSE_FILE="${OTIS_DIR}/docker-compose.yml" ENV_FILE="${OTIS_DIR}/.env" APP_URL="https://app.otisintelligence.co.uk" # ── Colours ─────────────────────────────────────────────────────────────────── RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m' BLUE='\033[0;34m'; BOLD='\033[1m'; NC='\033[0m' info() { echo -e "${BLUE}▶${NC} $*"; } success() { echo -e "${GREEN}✓${NC} $*"; } warn() { echo -e "${YELLOW}⚠${NC} $*"; } error() { echo -e "${RED}✗${NC} $*"; exit 1; } header() { echo -e "\n${BOLD}$*${NC}"; } # ── Banner ──────────────────────────────────────────────────────────────────── echo "" echo -e "${BOLD} ╔═══════════════════════════════════╗${NC}" echo -e "${BOLD} ║ OTIS Intelligence Setup ║${NC}" echo -e "${BOLD} ╚═══════════════════════════════════╝${NC}" echo "" # ── Detect OS ──────────────────────────────────────────────────────────────── header "Detecting environment" OS="$(uname -s)" ARCH="$(uname -m)" case "${OS}" in Linux*) OS_NAME="Linux" ;; Darwin*) OS_NAME="macOS" ;; CYGWIN*|MINGW*|MSYS*) OS_NAME="Windows/WSL" ;; *) error "Unsupported operating system: ${OS}" ;; esac info "Operating system: ${OS_NAME} (${ARCH})" # ── Check Docker ────────────────────────────────────────────────────────────── header "Checking Docker" if command -v docker &>/dev/null; then DOCKER_VERSION=$(docker --version | awk '{print $3}' | tr -d ',') success "Docker found: ${DOCKER_VERSION}" else warn "Docker not found. Installing Docker..." if [[ "${OS_NAME}" == "Linux" ]]; then curl -fsSL https://get.docker.com | sh sudo usermod -aG docker "${USER}" || true success "Docker installed" elif [[ "${OS_NAME}" == "macOS" ]]; then error "Docker not found. Please install Docker Desktop from https://www.docker.com/products/docker-desktop and re-run this script." else error "Docker not found. Please install Docker Desktop from https://www.docker.com/products/docker-desktop and re-run this script." fi fi # Check Docker is running if ! docker info &>/dev/null; then error "Docker is installed but not running. Please start Docker and re-run this script." fi success "Docker is running" # Check docker compose if docker compose version &>/dev/null; then success "Docker Compose available" elif command -v docker-compose &>/dev/null; then success "Docker Compose (standalone) available" else error "Docker Compose not found. Please install Docker Compose and re-run this script." fi # ── Collect configuration ───────────────────────────────────────────────────── header "Configuration" echo "" echo " You will need:" echo " 1. Your OTIS activation token (from your Omihon Central email)" echo " 2. The address this OTIS instance will be accessed from" echo " (e.g. http://localhost:8001 or https://otis.yourcompany.com)" echo "" read -rp " Enter your activation token: " OTIS_TOKEN if [[ -z "${OTIS_TOKEN}" ]]; then error "Activation token is required" fi read -rp " Enter the access address [http://localhost:8001]: " OTIS_URL OTIS_URL="${OTIS_URL:-http://localhost:8001}" # Generate random postgres password PG_PASSWORD=$(openssl rand -hex 16 2>/dev/null || cat /dev/urandom | tr -dc 'a-f0-9' | head -c 32) # ── Create OTIS directory ───────────────────────────────────────────────────── header "Setting up OTIS" mkdir -p "${OTIS_DIR}" info "Installing to ${OTIS_DIR}" # ── Write docker-compose.yml ────────────────────────────────────────────────── cat > "${COMPOSE_FILE}" << EOF services: db: image: postgres:15-alpine restart: unless-stopped environment: POSTGRES_USER: otis POSTGRES_PASSWORD: ${PG_PASSWORD} POSTGRES_DB: otis volumes: - otis_postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U otis"] interval: 5s timeout: 5s retries: 5 api: image: ${OTIS_IMAGE} restart: unless-stopped ports: - "8001:8001" depends_on: db: condition: service_healthy env_file: .env volumes: otis_postgres_data: EOF # ── Write .env ──────────────────────────────────────────────────────────────── cat > "${ENV_FILE}" << EOF # OTIS Configuration — generated by install script POSTGRES_USER=otis POSTGRES_PASSWORD=${PG_PASSWORD} POSTGRES_DB=otis DATABASE_URL=postgresql://otis:${PG_PASSWORD}@db:5432/otis OMIHON_CENTRAL_URL=https://api.omihoncentral.co.uk APP_URL=${OTIS_URL} SECRET_KEY=$(openssl rand -hex 32 2>/dev/null || cat /dev/urandom | tr -dc 'a-f0-9' | head -c 64) EOF success "Configuration written" # ── Pull image ──────────────────────────────────────────────────────────────── header "Pulling OTIS image" info "Pulling ${OTIS_IMAGE}..." docker pull "${OTIS_IMAGE}" success "Image pulled" # ── Start OTIS ──────────────────────────────────────────────────────────────── header "Starting OTIS" cd "${OTIS_DIR}" docker compose up -d success "OTIS started" # ── Wait for ready ──────────────────────────────────────────────────────────── info "Waiting for OTIS to be ready..." RETRIES=30 until docker compose exec -T api curl -sf http://localhost:8001/health &>/dev/null || [[ $RETRIES -eq 0 ]]; do sleep 2 RETRIES=$((RETRIES - 1)) done if [[ $RETRIES -eq 0 ]]; then warn "OTIS did not become ready in time. Check logs with: docker compose -f ${COMPOSE_FILE} logs api" else success "OTIS is ready" fi # ── Done ────────────────────────────────────────────────────────────────────── echo "" echo -e "${BOLD}╔══════════════════════════════════════════════╗${NC}" echo -e "${BOLD}║ OTIS is installed! ║${NC}" echo -e "${BOLD}╚══════════════════════════════════════════════╝${NC}" echo "" echo -e " ${GREEN}Next step:${NC} Activate your OTIS instance" echo "" echo -e " Open this URL in your browser:" echo -e " ${BOLD}${APP_URL}/activate${NC}" echo "" echo -e " Use this token when prompted:" echo -e " ${BOLD}${OTIS_TOKEN}${NC}" echo "" echo -e " Useful commands:" echo -e " ${BLUE}docker compose -f ${COMPOSE_FILE} logs -f api${NC} — view logs" echo -e " ${BLUE}docker compose -f ${COMPOSE_FILE} restart${NC} — restart OTIS" echo -e " ${BLUE}docker compose -f ${COMPOSE_FILE} down${NC} — stop OTIS" echo -e " ${BLUE}docker compose -f ${COMPOSE_FILE} pull && docker compose -f ${COMPOSE_FILE} up -d${NC} — update OTIS" echo ""