#!/usr/bin/env bash # # FreeLLMAPI one-line installer (#250, idea by @StealthTensor). # # curl -fsSL https://tashfeenahmed.github.io/freellmapi/install.sh | bash # # What it does: # 1. Checks for Docker + Compose. # 2. Creates an install dir (default ~/freellmapi, override FREELLMAPI_DIR). # 3. Writes a docker-compose.yml pinned to ghcr.io/tashfeenahmed/freellmapi:latest # and a .env with a freshly generated ENCRYPTION_KEY (kept on re-runs). # 4. Pulls the image, starts the container, waits for the health endpoint. # # Options (env vars): # FREELLMAPI_DIR install directory (default: ~/freellmapi) # PORT host port for the dashboard (default: 3001) # HOST_BIND host interface to publish on (default: 127.0.0.1). # Set HOST_BIND=0.0.0.0 to reach it from your LAN — only on # a trusted network; the proxy is single-user. # # Re-running is safe: existing .env (and your encryption key) is preserved, # the compose file is refreshed, and the container is updated to :latest. set -euo pipefail INSTALL_DIR="${FREELLMAPI_DIR:-$HOME/freellmapi}" PORT="${PORT:-3001}" HOST_BIND="${HOST_BIND:-127.0.0.1}" IMAGE="ghcr.io/tashfeenahmed/freellmapi:latest" say() { printf '\033[1;32m==>\033[0m %s\n' "$*"; } warn() { printf '\033[1;33mWARN\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31mERROR\033[0m %s\n' "$*" >&2; exit 1; } # ── 1. prerequisites ───────────────────────────────────────────────────────── command -v docker >/dev/null 2>&1 || die "Docker is required. Install it from https://docs.docker.com/get-docker/ and re-run." if docker compose version >/dev/null 2>&1; then COMPOSE=(docker compose) elif command -v docker-compose >/dev/null 2>&1; then COMPOSE=(docker-compose) else die "Docker Compose is required (the 'docker compose' plugin or 'docker-compose')." fi docker info >/dev/null 2>&1 || die "Docker daemon isn't running (or you lack permission). Start Docker and re-run." # ── 2. install dir ─────────────────────────────────────────────────────────── mkdir -p "$INSTALL_DIR" cd "$INSTALL_DIR" say "Installing into $INSTALL_DIR" # ── 3. .env (generated once — your at-rest encryption key lives here) ──────── if [ -f .env ]; then say "Keeping existing .env (encryption key preserved)" else if command -v openssl >/dev/null 2>&1; then KEY="$(openssl rand -hex 32)" else # /dev/urandom fallback when openssl isn't installed KEY="$(od -vN 32 -An -tx1 /dev/urandom | tr -d ' \n')" fi umask 077 cat > .env < docker-compose.yml < { if (!res.ok) process.exit(1); }).catch(() => process.exit(1));" ] interval: 30s timeout: 5s start_period: 15s retries: 3 volumes: freellmapi-data: EOF # ── 5. pull + start ────────────────────────────────────────────────────────── say "Pulling $IMAGE" "${COMPOSE[@]}" pull --quiet 2>/dev/null || "${COMPOSE[@]}" pull say "Starting FreeLLMAPI" "${COMPOSE[@]}" up -d # ── 6. wait for health ─────────────────────────────────────────────────────── say "Waiting for the server to come up" HEALTH_HOST="127.0.0.1" [ "$HOST_BIND" != "0.0.0.0" ] && [ "$HOST_BIND" != "127.0.0.1" ] && HEALTH_HOST="$HOST_BIND" for i in $(seq 1 30); do if curl -fsS "http://$HEALTH_HOST:$PORT/api/ping" >/dev/null 2>&1; then HEALTHY=1 break fi sleep 2 done if [ "${HEALTHY:-0}" = "1" ]; then cat <