#!/usr/bin/env sh
# Prepares a fresh Linux host for Isovara Cloud Server and installs a release:
# Docker Engine and the Compose plugin, the firewall, the release (checked
# against its SHA-256 and the vendor's signature), the stack, and the nightly
# backup. For a standalone server on your own hardware or any cloud VM; the
# OpenTofu modules in deploy/{azure,aws,gcp} do the same at first boot.
#
# Usage (as root, or with sudo):
#   sh prepare-host.sh --host cloud.example.com --release isovara-cloud-<version>.tar.gz [options]
#
#   --host NAME        name or IP address devices and browsers use (required)
#   --release FILE|URL release archive; an https:// URL is downloaded
#   --sha256 HEX       its SHA-256 (required for a URL; for a file, read from FILE.sha256 when present)
#   --dir DIR          install folder (default /opt/isovara)
#   --database-url URL use an external PostgreSQL instead of the bundled one
#   --no-firewall      leave the firewall alone (default: ufw allows 22, 80, 443, 8883)
#   --no-backup-cron   do not add the nightly backup to root's crontab
#
# Supported: Ubuntu 22.04 and 24.04, Debian 12 (apt). On other systems install
# Docker and the Compose plugin yourself, then run install.sh from the release.
# Run it again to repair; install.sh never replaces passwords or data.
set -eu

HOST="" RELEASE="" SHA="" DIR=/opt/isovara DBURL="" FIREWALL=1 CRON=1
while [ $# -gt 0 ]; do
  case "$1" in
    --host) HOST="$2"; shift 2 ;;
    --release) RELEASE="$2"; shift 2 ;;
    --sha256) SHA="$2"; shift 2 ;;
    --dir) DIR="$2"; shift 2 ;;
    --database-url) DBURL="$2"; shift 2 ;;
    --no-firewall) FIREWALL=0; shift ;;
    --no-backup-cron) CRON=0; shift ;;
    -h|--help) sed -n '2,24p' "$0"; exit 0 ;;
    *) echo "unknown option: $1 (see --help)"; exit 2 ;;
  esac
done
[ -n "$HOST" ] || { echo "give --host (the name or IP devices and browsers use)"; exit 2; }
[ -n "$RELEASE" ] || { echo "give --release (a release archive or its https:// address)"; exit 2; }
[ "$(id -u)" = 0 ] || { echo "run as root (sudo sh $0 ...)"; exit 1; }

step() { printf '\n== %s\n' "$*"; }

step "Docker"
if ! command -v docker >/dev/null || ! docker compose version >/dev/null 2>&1; then
  command -v apt-get >/dev/null || { echo "no apt-get: install Docker Engine and the Compose plugin, then run install.sh"; exit 1; }
  export DEBIAN_FRONTEND=noninteractive
  apt-get update -q
  # Distribution packages: docker.io and the Compose v2 plugin.
  apt-get install -yq docker.io curl openssl ca-certificates
  apt-get install -yq docker-compose-v2 2>/dev/null || apt-get install -yq docker-compose-plugin
  systemctl enable --now docker
fi
docker compose version

step "Release"
mkdir -p "$DIR"
case "$RELEASE" in
  https://*)
    [ -n "$SHA" ] || { echo "give --sha256 with a release URL"; exit 2; }
    ARCHIVE=$(mktemp /tmp/node32-release.XXXXXX)
    curl -fsSL -o "$ARCHIVE" "$RELEASE"
    ;;
  http://*) echo "download releases over https only"; exit 2 ;;
  *)
    ARCHIVE="$RELEASE"
    [ -f "$ARCHIVE" ] || { echo "no such file: $ARCHIVE"; exit 2; }
    if [ -z "$SHA" ] && [ -f "$ARCHIVE.sha256" ]; then SHA=$(cut -d' ' -f1 "$ARCHIVE.sha256"); fi
    ;;
esac
if [ -n "$SHA" ]; then
  echo "$SHA  $ARCHIVE" | sha256sum -c -
else
  echo "warning: no SHA-256 given; relying on the release signature alone"
fi
# The archive holds one folder, isovara-cloud-<version>/; its contents go into DIR.
tar -xzf "$ARCHIVE" --strip-components=1 -C "$DIR"
case "$RELEASE" in https://*) rm -f "$ARCHIVE" ;; esac

if [ -n "$DBURL" ]; then
  umask 077
  touch "$DIR/.env"
  grep -q '^ISOVARA_DATABASE_URL=' "$DIR/.env" || echo "ISOVARA_DATABASE_URL=$DBURL" >> "$DIR/.env"
fi

if [ "$FIREWALL" = 1 ]; then
  step "Firewall"
  if command -v ufw >/dev/null; then
    ufw allow 22/tcp >/dev/null
    for p in $(grep -E '^ISOVARA_(DEVICE|WEB|MQTT)_PORT=' "$DIR/.env" 2>/dev/null | cut -d= -f2) 80 443 8883; do
      ufw allow "$p/tcp" >/dev/null
    done
    ufw --force enable >/dev/null
    ufw status | sed -n '1,12p'
  else
    echo "ufw is not installed: open TCP 80 (devices), 443 (web) and 8883 (MQTT) in your firewall"
  fi
fi

step "Install"
# install.sh checks the vendor's signature on every file before anything runs.
sh "$DIR/install.sh" "$HOST" | tee "$DIR/first-boot.txt"

if [ "$CRON" = 1 ]; then
  step "Nightly backup"
  line="15 3 * * * cd $DIR && sh backup.sh >> backups/backup.log 2>&1"
  ( crontab -l 2>/dev/null | grep -vF "sh backup.sh" || true; echo "$line" ) | crontab -
  echo "added: $line"
  echo "Copy $DIR/backups off this host regularly (see the backup runbook)."
fi

printf '\nDone. What the installer printed is kept in %s/first-boot.txt (it holds the setup token: delete it after the first sign-in).\n' "$DIR"
