#!/bin/sh
# Slowpoke, in one command:
#
#   curl -fsSL https://getslowpoke.com/install.sh | sh
#
# Brings up the server and its PostgreSQL, generates the database password itself, and asks
# nothing. Running it again is safe: it keeps what is already there and updates the images.
#
#   --dir <directory>  where compose.yaml and .env live (default /opt/slowpoke, or ./slowpoke)
#   --port <port>      where the panel answers (default 8080)
#   --sqlite           no database container: one file, to try it out
#   --image <name>     an image other than the public one (for anyone building their own)
set -eu

IMAGE="${SLOWPOKE_IMAGE:-ghcr.io/getslowpoke/server:latest}"
PORT="${SLOWPOKE_PORT:-8080}"
DIR="${SLOWPOKE_DIR:-}"
SQLITE=0

while [ $# -gt 0 ]; do
	case "$1" in
		--dir) DIR="${2:-}"; shift 2 ;;
		--port) PORT="${2:-}"; shift 2 ;;
		--image) IMAGE="${2:-}"; shift 2 ;;
		--sqlite) SQLITE=1; shift ;;
		-h|--help) sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
		*) echo "unknown option: $1" >&2; exit 2 ;;
	esac
done

say() { printf '%s\n' "$*"; }
die() { printf '\n%s\n' "$*" >&2; exit 1; }

# ------------------------------------------------------------------ what it needs
command -v docker >/dev/null 2>&1 || die "Docker is needed: https://docs.docker.com/engine/install/
Then run this again."
docker compose version >/dev/null 2>&1 || die "Docker's compose plugin is needed (docker compose).
On Debian and Ubuntu: sudo apt-get install docker-compose-plugin"
docker info >/dev/null 2>&1 || die "Docker is not answering. If it is running but this user cannot talk to it:
  sudo usermod -aG docker \$USER   (then log out and back in)
or run this again with sudo."

if [ -z "$DIR" ]; then
	if [ "$(id -u)" = 0 ]; then DIR=/opt/slowpoke; else DIR="$PWD/slowpoke"; fi
fi
mkdir -p "$DIR"
cd "$DIR"

# ------------------------------------------------------------------ the password, once
# Running the installer again must not change the password of a database that already exists:
# that is the fastest way to make a month of somebody's data unreadable.
if [ -f .env ] && grep -q '^POSTGRES_PASSWORD=' .env; then
	say "Found an installation in $DIR: keeping the password it already has."
else
	if command -v openssl >/dev/null 2>&1; then
		pw=$(openssl rand -hex 24)
	else
		pw=$(head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n')
	fi
	[ -n "$pw" ] || die "cannot generate a password on this machine"
	umask 077
	printf 'POSTGRES_PASSWORD=%s\nSLOWPOKE_PORT=%s\n' "$pw" "$PORT" > .env
	say "Database password generated, written to $DIR/.env and readable by nobody else."
fi
# The port is always the one of the last command.
if grep -q '^SLOWPOKE_PORT=' .env 2>/dev/null; then
	sed -i.bak "s/^SLOWPOKE_PORT=.*/SLOWPOKE_PORT=$PORT/" .env && rm -f .env.bak
else
	printf 'SLOWPOKE_PORT=%s\n' "$PORT" >> .env
fi

# ------------------------------------------------------------------ the configuration
if [ "$SQLITE" = 1 ]; then
	cat > compose.yaml <<YAML
# Slowpoke on a single file, to try it out. For anything that matters use PostgreSQL: run the
# installer again without --sqlite (what you tried stays in this volume).
services:
  server:
    image: $IMAGE
    environment:
      SLOWPOKE_DATABASE_URL: sqlite:///data/slowpoke.db
      SLOWPOKE_PUBLIC_URL: \${SLOWPOKE_PUBLIC_URL:-}
    volumes: ["slowpoke-data:/data"]
    ports: ["\${SLOWPOKE_PORT:-8080}:8080"]
    restart: unless-stopped

volumes:
  slowpoke-data:
YAML
else
	cat > compose.yaml <<'YAML'
# Slowpoke and its PostgreSQL. The password is in .env, generated by the installer.
services:
  server:
    image: IMAGE_PLACEHOLDER
    environment:
      SLOWPOKE_DATABASE_URL: postgres://slowpoke:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD missing from .env}@postgres:5432/slowpoke?sslmode=disable
      # The address agents and browsers use, e.g. https://slowpoke.example.com
      SLOWPOKE_PUBLIC_URL: ${SLOWPOKE_PUBLIC_URL:-}
    ports: ["${SLOWPOKE_PORT:-8080}:8080"]
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped

  postgres:
    image: postgres:17-alpine
    environment:
      POSTGRES_USER: slowpoke
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD missing from .env}
      POSTGRES_DB: slowpoke
    volumes: ["postgres-data:/var/lib/postgresql/data"]
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "slowpoke"]
      interval: 5s
      retries: 20
    restart: unless-stopped

volumes:
  postgres-data:
YAML
	sed -i.bak "s|IMAGE_PLACEHOLDER|$IMAGE|" compose.yaml && rm -f compose.yaml.bak
fi

# ------------------------------------------------------------------ up
say "Fetching the images and starting (the first time takes a few minutes)."
# A pull that fails is not the end: the images may already be here, and "up" says so clearly
# if they are not. This is also what lets anyone run it against an image they built themselves.
docker compose pull --quiet 2>/dev/null || docker compose pull 2>/dev/null ||
	say "Could not fetch the images: using what is already on this machine."
docker compose up -d

printf 'Waiting for it to answer'
i=0
while [ $i -lt 60 ]; do
	if curl -fsS --max-time 3 "http://127.0.0.1:$PORT/healthz" >/dev/null 2>&1; then
		printf '\n'
		break
	fi
	printf '.'; sleep 2; i=$((i + 1))
done
curl -fsS --max-time 5 "http://127.0.0.1:$PORT/healthz" >/dev/null 2>&1 || die "
Nothing answers on http://127.0.0.1:$PORT . What it says about itself:
  cd $DIR && docker compose logs --tail 50"

host=$(hostname -I 2>/dev/null | awk '{print $1}')
[ -n "${host:-}" ] || host=127.0.0.1

# Until an administrator exists, the first visit asks for this code: without it, whoever reached
# port $PORT first would become the administrator. An older image has no setup-code command and
# no code to ask for, so nothing is printed then.
code=$(docker compose exec -T server slowpoke-server setup-code 2>/dev/null | tr -d '\r' |
	grep -E '^[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$' || true)
if [ -n "$code" ]; then
	open_line="Open  http://$host:$PORT/setup?code=$code
     and create the administrator (setup code: $code), then the first project."
else
	open_line="Open  http://$host:$PORT  and create the first user and the first project."
fi

cat <<END

Slowpoke is up.

  1. $open_line
  2. On every machine you want to watch, run the line the panel gives you:
       curl -fsSL http://$host:$PORT/install.sh | sudo sh -s -- --token spa_...

  The configuration is in $DIR. To stop it:  cd $DIR && docker compose down
END
