#!/bin/sh
# Keep a mirror of stateofwatch.com current. Run it daily from cron, for example:
#   0 6 * * * ROOT=/var/www/stateofwatch sh /opt/mirror-update.sh
# Through Tor instead of the clearnet site:
#   SITE=http://sowatchmphqybq2wql4zvwdzmxwmxvbema2r6dptztdusapje3qzbpyd.onion CURL="curl -fsS --socks5-hostname 127.0.0.1:9050" sh mirror-update.sh
# Does nothing when the mirror already has the latest release. Needs curl, sha256sum, tar.
#
# Signature checking (recommended; needs gpgv). Once, fetch the release key and confirm its
# fingerprint matches the one on /mirrors, over both the clearnet site and the onion:
#   curl -fsS https://stateofwatch.com/stateofwatch-releases.asc | gpg --dearmor > /opt/stateofwatch-releases.gpg
#   gpg --show-keys /opt/stateofwatch-releases.gpg    # 5C80 F04A E59F 0FA9 7850  813D C0F8 4CF8 ECB4 7744
# Then run with KEYRING=/opt/stateofwatch-releases.gpg (an absolute path). With KEYRING set, a
# release that is unsigned or not signed by that key is refused.
set -eu
SITE="${SITE:-https://stateofwatch.com}"
ROOT="${ROOT:-/var/www/stateofwatch}"
CURL="${CURL:-curl -fsS}"

work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
cd "$work"

$CURL "$SITE/releases/latest.json" -o latest.json
file="$(sed -n 's/.*"file": *"\([^"]*\)".*/\1/p' latest.json)"
case "$file" in stateofwatch-v*.tar.gz) ;; *) echo "unexpected release name: $file" >&2; exit 1 ;; esac
if [ -f "$ROOT/.release" ] && [ "$(cat "$ROOT/.release")" = "$file" ]; then exit 0; fi

$CURL "$SITE/releases/$file" -o "$file"
$CURL "$SITE/releases/$file.sha256" -o "$file.sha256"
sha256sum -c "$file.sha256"
if [ -n "${KEYRING:-}" ]; then
  $CURL "$SITE/releases/$file.asc" -o "$file.asc"
  gpgv --keyring "$KEYRING" "$file.asc" "$file"
else
  echo "note: KEYRING not set, signature not checked (see the top of this script)" >&2
fi

mkdir site
tar -xzf "$file" -C site

# Source documents (PDFs, datasets, video) are not in the archive. The release lists each one in
# api/v1/documents.json as /docs/files/<sha256>.<ext> (covered by the checksum and signature
# above); fetch any this mirror doesn't have yet, keep the ones it does, and refuse any file whose
# hash doesn't match its name.
mkdir -p site/docs/files
if [ -f site/api/v1/documents.json ]; then
  for f in $(sed -n 's/^ *"file": *"\/docs\/files\/\([0-9a-f]\{64\}\.[a-z0-9]\{1,5\}\)".*/\1/p' site/api/v1/documents.json); do
    h="${f%%.*}"
    if [ -f "$ROOT/docs/files/$f" ]; then
      cp "$ROOT/docs/files/$f" "site/docs/files/$f"
    else
      $CURL "$SITE/docs/files/$f" -o "site/docs/files/$f"
    fi
    echo "$h  site/docs/files/$f" | sha256sum -c --quiet
  done
fi

echo "$file" > site/.release
rm -rf "$ROOT.old"
if [ -d "$ROOT" ]; then mv "$ROOT" "$ROOT.old"; fi
mv site "$ROOT"
rm -rf "$ROOT.old"
echo "mirror updated to $file"
