#!/usr/bin/env bash
#
# get-vault.sh — the small "bootstrap" script behind Vault's one-line
# installer, the same pattern cPanel/WHM, Plesk, and most one-line VPS
# installers use:
#
#   cd /home && curl -o latest -L https://execute.arrolic.com/vault/ && sh latest
#
# (or, piped directly: curl -fsSL https://execute.arrolic.com/vault/ | sudo bash)
#
# All this file does is: sanity-check the environment, download the actual
# Vault release archive from VAULT_URL, extract it, and hand off to the
# real installer (install.sh) inside it. It deliberately does NOT contain
# any application code itself — keeping this bootstrap tiny and simple means
# there's very little here that can go stale, so the one-line command you
# publish to customers never has to change even when Vault itself is
# updated: just replace the release archive at VAULT_URL with a newer one.
#
# The bare URL above (no filename) works because of how the distribution
# folder is set up on the download host — see docs/RELEASE.md for the full
# picture: an index.php there serves THIS file's exact bytes for any GET
# on that folder, a .htaccess turns off directory listing and blocks
# direct access to everything else in that folder, and
# scripts/release.sh packages a new vault-latest.tar.gz in one command
# whenever Vault itself is updated.
#
# ---------------------------------------------------------------------------
# SETUP (do this once, before publishing the one-line command below):
#
#   1. From inside this project folder, run:
#        bash scripts/release.sh
#      This builds vault-latest.tar.gz (excludes config.php and any local
#      dev artifacts, same as every other delivered package) right next to
#      this script — see docs/RELEASE.md for exactly what it does.
#
#   2. Upload the files docs/RELEASE.md lists (this script, the archive,
#      an index.php, a .htaccess — docs/RELEASE.md has ready-to-upload
#      copies of the last two) into the distribution folder on your
#      download host, e.g. execute.arrolic.com/vault/. Re-uploading a new
#      vault-latest.tar.gz there is how you ship updates: the one-line
#      command below never needs to change.
#
#   3. VAULT_URL below already points at execute.arrolic.com — only edit
#      it if you move the distribution folder somewhere else.
#
#   4. Hand out this one-liner (this is your "cPanel-style" install command):
#        cd /home && curl -o latest -L https://execute.arrolic.com/vault/ && sh latest
#      An operator can also override the archive location per-run without
#      you changing anything, e.g. to test a pre-release build:
#        curl -fsSL https://execute.arrolic.com/vault/ | sudo VAULT_URL=https://example.com/vault-beta.tar.gz bash
# ---------------------------------------------------------------------------

# This script uses bash-only features below (set -o pipefail, [[ ]], $EUID)
# for good reasons (pipefail in particular matters for the download check
# further down), so it needs to actually run under bash — but "sh latest"
# (the download-then-run half of the one-line command in the header above)
# runs it under whatever /bin/sh is, which on essentially every Debian/
# Ubuntu system (this script's whole supported OS list) is dash, not bash,
# and dash doesn't understand `set -o pipefail` at all. Rather than making
# every customer remember to type "bash latest" instead of "sh latest",
# detect that and hand this exact file straight to a real bash before
# anything bash-specific runs. $BASH_VERSION is only ever set once running
# under bash, so this block itself has to stay plain POSIX sh — no [[ ]],
# no "set -o pipefail" — since it may still be dash that evaluates it.
if [ -z "${BASH_VERSION:-}" ]; then
    exec bash "$0" "$@"
fi

set -euo pipefail

# ---- EDIT THIS ONE LINE if you ever move the distribution folder ----------
VAULT_URL="${VAULT_URL:-https://execute.arrolic.com/vault/vault-latest.tar.gz}"
# -----------------------------------------------------------------------------

echo "==> Vault one-line installer"

if [ "$EUID" -ne 0 ]; then
    echo "ERROR: this must be run as root." >&2
    echo "Try:   curl -fsSL <this script's URL> | sudo bash" >&2
    exit 1
fi

if [[ "$VAULT_URL" == *"YOUR-DOWNLOAD-HOST"* ]]; then
    echo "ERROR: get-vault.sh hasn't been configured yet." >&2
    echo "Edit VAULT_URL near the top of this file to point at your hosted" >&2
    echo "vault-latest.tar.gz (see the SETUP comment at the top of this file)," >&2
    echo "or re-run with VAULT_URL=https://... set explicitly." >&2
    exit 1
fi

DOWNLOADER=""
if command -v curl >/dev/null 2>&1; then
    DOWNLOADER="curl"
elif command -v wget >/dev/null 2>&1; then
    DOWNLOADER="wget"
else
    echo "==> Neither curl nor wget found — installing curl"
    apt-get update -y >/dev/null 2>&1 || true
    apt-get install -y curl
    DOWNLOADER="curl"
fi

WORKDIR="$(mktemp -d /tmp/vault-install.XXXXXX)"
ARCHIVE="${WORKDIR}/vault.tar.gz"
cleanup() { rm -rf "$WORKDIR"; }
trap cleanup EXIT

echo "==> Downloading Vault from ${VAULT_URL}"
if [ "$DOWNLOADER" = "curl" ]; then
    curl -fsSL --retry 3 -o "$ARCHIVE" "$VAULT_URL"
else
    wget -q -O "$ARCHIVE" "$VAULT_URL"
fi

if [ ! -s "$ARCHIVE" ]; then
    echo "ERROR: download failed or produced an empty file. Check VAULT_URL." >&2
    exit 1
fi

# Sanity-check it's actually a gzip archive before handing it to tar — a
# misconfigured VAULT_URL pointing at, say, an HTML "404 Not Found" page
# would otherwise fail with a confusing tar error deep inside extraction.
if ! (file "$ARCHIVE" 2>/dev/null | grep -qi 'gzip compressed' || gzip -t "$ARCHIVE" 2>/dev/null); then
    echo "ERROR: the downloaded file at VAULT_URL doesn't look like a valid" >&2
    echo ".tar.gz archive. Check that VAULT_URL points directly at the" >&2
    echo "release file, not a webpage or redirect your downloader can't follow." >&2
    exit 1
fi

echo "==> Extracting"
tar -xzf "$ARCHIVE" -C "$WORKDIR"

# The archive is expected to contain one top-level folder (e.g. vault-app/)
# holding install.sh — find it rather than assuming an exact name, so this
# bootstrap keeps working even if that folder gets renamed between releases.
EXTRACTED_DIR="$(find "$WORKDIR" -mindepth 1 -maxdepth 1 -type d -exec test -f '{}/install.sh' \; -print | head -n1)"
if [ -z "$EXTRACTED_DIR" ]; then
    echo "ERROR: couldn't find install.sh inside the downloaded archive." >&2
    echo "This usually means VAULT_URL points at the wrong file." >&2
    exit 1
fi

echo "==> Handing off to install.sh"
chmod +x "${EXTRACTED_DIR}/install.sh"

# Copy out of $WORKDIR before running: install.sh's "in-place" detection
# (see its own comments) will treat wherever it's run from as the permanent
# app directory, and $WORKDIR gets deleted by the `trap cleanup` above the
# moment this script exits — so the real app files need to already be
# somewhere permanent (install.sh's own /opt/vault-app fallback only
# triggers when config.php.example/app/ aren't found next to install.sh,
# which they are here, so it would otherwise "install in place" into a
# directory that's about to vanish).
PERMANENT_DIR="/opt/vault-app"
if [ -d "$PERMANENT_DIR" ] && [ -f "${PERMANENT_DIR}/config.php" ]; then
    echo "==> ${PERMANENT_DIR} already exists with a config.php — treating this as an update/re-run"
    echo "    (install.sh only touches files it manages; your data in storage/ and"
    echo "    your existing config.php are left alone)."

    # ------------------------------------------------------------------
    # Back up the CODE currently on disk before overwriting any of it —
    # the one thing an update can get wrong that storage/ and config.php
    # being excluded above doesn't already protect against. storage/
    # itself is deliberately NOT included here: it's never touched by the
    # rsync below, it can be large (real uploaded files), and backing it
    # up on every update would make updates slow for no safety benefit.
    # A failed backup must not block the update (set -e is active in this
    # script), so this step best-efforts and only warns on failure.
    # ------------------------------------------------------------------
    BACKUP_DIR="/opt/vault-backups"
    BACKUP_FILE="${BACKUP_DIR}/vault-app-$(date +%Y%m%d-%H%M%S).tar.gz"
    echo "==> Backing up current code to ${BACKUP_FILE} before updating"
    if mkdir -p "$BACKUP_DIR" && tar -czf "$BACKUP_FILE" --exclude='storage' -C "$(dirname "$PERMANENT_DIR")" "$(basename "$PERMANENT_DIR")"; then
        echo "==> Backup saved: ${BACKUP_FILE}"
        echo "    Roll back by extracting it over ${PERMANENT_DIR} (never touches storage/ or config.php):"
        echo "      tar -xzf ${BACKUP_FILE} -C $(dirname "$PERMANENT_DIR")"
    else
        echo "==> WARNING: backup step failed — continuing with the update anyway," >&2
        echo "    but there is no automatic rollback point for this run." >&2
    fi

    # Refresh the application code but never clobber runtime data.
    rsync -a --exclude 'storage' --exclude 'config.php' "${EXTRACTED_DIR}/" "${PERMANENT_DIR}/" 2>/dev/null \
        || cp -a "${EXTRACTED_DIR}/." "${PERMANENT_DIR}/"
else
    mkdir -p "$PERMANENT_DIR"
    cp -a "${EXTRACTED_DIR}/." "${PERMANENT_DIR}/"
fi

cd "$PERMANENT_DIR"
# Not `exec`: this bootstrap's own `trap cleanup EXIT` (which removes the
# downloaded archive under $WORKDIR) would never fire if we replaced this
# process outright — `exec` swaps out the running bash for a new one with no
# memory of traps set before it. Running install.sh as a normal child and
# forwarding its exit code gets the same end result with the temp files
# actually cleaned up afterward.
bash "${PERMANENT_DIR}/install.sh"
exit $?
