#!/bin/bash
#
# Deploy the working tree to dev.convoro.co.
#
# 🚨 The exclusions are the point of this file. A hand-written rsync overwrote
# the live `.env` with a laptop's — APP_URL of localhost:8080, a local database
# name, a local APP_KEY — and took the site down. The old key is gone; it cost
# two remember-me cookies, and it would have cost every 2FA enrolment and every
# API token had any existed yet.
#
# The lesson is not "be careful with rsync". It is that a deploy must not be
# retyped each time, because the exclusion you forget is the one that matters.
# Anything below that names a server-owned path is load-bearing.
#
#   tools/deploy-dev            deploy, migrate, clear caches
#   tools/deploy-dev --dry-run  show what would change and stop
#
set -euo pipefail

HOST=${CONVORO_DEV_HOST:-root@103.195.100.103}
DIR=${CONVORO_DEV_DIR:-/var/www/convoro-dev}
ROOT="$(cd "$(dirname "$0")/.." && pwd)"

DRY=""
if [ "${1:-}" = "--dry-run" ]; then
    DRY="--dry-run --itemize-changes"
fi

# Server-owned, never sent. `.env` holds that install's database credentials and
# its APP_KEY; storage and content are the site's own state; config/site.php is
# written by the installer on the machine it runs on.
#
# 🚨 `config/site.php` must match `Environment::SITE_CONFIG`. It carries the
# database password and the APP_KEY, and this rsync runs with `--delete` — so
# if the name here ever drifts from the name the installer writes, a deploy
# does not overwrite that file, it *removes* it, and the APP_KEY with it.
# (`config/config.php` was an earlier name for the same thing and nothing
# writes it; kept excluded because a box that has one should keep it.)
EXCLUDE=(
    --exclude='.git'
    --exclude='node_modules'
    --exclude='.env'
    --exclude='.env.*'
    --exclude='config/site.php'
    --exclude='config/config.php'
    # 🚨 Where the extension installer extracts uploaded packages — the site's
    # own state, like content/, not source. Without this the rsync `--delete`
    # uninstalls every extension the site has, silently, on the next deploy.
    # Caught by a dry run before the first deploy from a second machine; it
    # would have taken out topic_stars and hello_hooks on dev, and everything
    # a real site had installed anywhere else.
    --exclude='extensions/'
    --exclude='storage/cache'
    --exclude='storage/logs'
    --exclude='storage/sessions'
    --exclude='storage/templates'
    --exclude='content'
    --exclude='tests/shots'
)

echo "→ ${HOST}:${DIR}"

# A copy of the server's own .env before anything is written, kept outside the
# deploy directory so a bad deploy is recoverable rather than terminal.
if [ -z "$DRY" ]; then
    ssh "$HOST" "install -m 600 '${DIR}/.env' /root/convoro-dev.env.bak 2>/dev/null || true"
fi

# 🚨 `--no-owner --no-group`, not plain `-a`. Running as root on the far end,
# rsync -a happily stamps the laptop's uid on everything it sends, and the
# deployed tree ended up owned by 501:staff — a user that does not exist there.
# It survived only because the site reads more than it writes; the first thing
# www-data needed to write into a directory it no longer owned would have failed
# somewhere with nothing to connect it back to here.
#
# shellcheck disable=SC2086
rsync -rlptzD --no-owner --no-group --delete $DRY "${EXCLUDE[@]}" "${ROOT}/" "${HOST}:${DIR}/"

if [ -n "$DRY" ]; then
    echo "→ dry run, nothing changed."
    exit 0
fi

# As www-data, because that is who the site runs as and who owns what these
# write. Running them as root leaves root-owned cache files the site cannot
# replace, which fails later and somewhere else.
ssh "$HOST" "cd '${DIR}' \
    && chown -R www-data:www-data storage content \
    && sudo -u www-data php tools/convoro migrate \
    && sudo -u www-data php tools/convoro cache:clear"

echo "→ checking"

for path in / /forums /downloads; do
    code=$(curl -s -o /dev/null -w '%{http_code}' "https://dev.convoro.co${path}")
    printf '   %-14s %s\n' "$path" "$code"

    if [ "$code" != "200" ]; then
        echo "   ✗ ${path} came back ${code} — the deploy is not good."
        exit 1
    fi
done

# 🚨 An image served by PHP, not a file on disk.
#
# Avatars and covers live in content/, outside the web root, and are streamed by
# a controller — but their URLs end in .png and .jpg, so a static-asset rule
# matching on extension will claim them and 404 them before PHP is asked. That
# happened on this very site: uploads succeeded, the site said so, and every
# profile picture was a broken-image icon until somebody reported it.
#
# The status code alone does not catch it, because the wrong answer is a
# perfectly valid 404. The content type is what tells the truth.
avatar=$(ssh "$HOST" "mysql -N convoro_dev -e \"SELECT avatar FROM cv_users WHERE avatar IS NOT NULL AND avatar <> '' LIMIT 1;\" 2>/dev/null")

if [ -n "$avatar" ]; then
    kind=$(curl -s -o /dev/null -w '%{content_type}' "https://dev.convoro.co${avatar}")
    printf '   %-14s %s\n' "an avatar" "$kind"

    case "$kind" in
        image/*) ;;
        *)
            echo "   ✗ an avatar came back as '${kind}', not an image."
            echo "     nginx is almost certainly claiming /avatar/ before PHP sees it."
            echo "     See docs/deploy/nginx.conf — the ^~ blocks."
            exit 1
            ;;
    esac
fi

echo "→ dev is up."
