Preview — /tmp/setup-public-handoff-https.sh

/tmp/setup-public-handoff-https.sh

#!/usr/bin/env bash
set -euo pipefail

HOST="vqngx6j8dc9q1lvt7g2a7a646q2t.jfmcommunications.com.au"
SLUG="vqngx6j8dc9q1lvt7g2a7a646q2t"
ROOT="/var/www/handoff/public"
EMAIL="simon@jfmcommunications.com.au"
SITE_NAME="handoff-public-ssl"
NGINX_SITE="/etc/nginx/sites-available/$SITE_NAME"
NGINX_LINK="/etc/nginx/sites-enabled/$SITE_NAME"
STAMP="$(date +%Y%m%d-%H%M%S)"
BACKUP_DIR="/root/handoff-https-backup-$STAMP"

echo "=== SETUP PUBLIC HANDOFF HTTPS ==="
date
echo "Host: $HOST"
echo "Root: $ROOT"
echo "Slug: $SLUG"
echo

mkdir -p "$BACKUP_DIR"

echo "=== PRE-CHECKS ==="
echo "--- DNS ---"
getent hosts "$HOST" || true

echo
echo "--- NGINX LISTENERS BEFORE ---"
ss -ltnp | grep -E ':80|:443|:9002|:1002' || true

echo
echo "--- HANDOFF FILE CHECK ---"
if [ ! -d "$ROOT" ]; then
  echo "ERROR: $ROOT does not exist."
  exit 1
fi

find "$ROOT" -maxdepth 3 -type f \( -iname 'HANDOVER*' -o -iname 'CHANGELOG*' -o -iname 'RULES*' -o -iname 'index.html' \) | sort | head -n 80 || true

if [ -f "$NGINX_SITE" ]; then
  cp -a "$NGINX_SITE" "$BACKUP_DIR/$(basename "$NGINX_SITE").bak"
fi

if [ -L "$NGINX_LINK" ]; then
  readlink -f "$NGINX_LINK" > "$BACKUP_DIR/sites-enabled-link.txt" || true
fi

echo
echo "=== INSTALL CERTBOT IF NEEDED ==="
if ! command -v certbot >/dev/null 2>&1; then
  apt-get update
  apt-get install -y certbot python3-certbot-nginx
else
  certbot --version || true
fi

echo
echo "=== WRITE TEMP HTTP-ONLY NGINX VHOST FOR LETSENCRYPT ==="
cat > "$NGINX_SITE" <<NGINX
server {
    listen 80;
    listen [::]:80;
    server_name $HOST;

    root $ROOT;
    index index.html index.htm;

    access_log /var/log/nginx/${SITE_NAME}.access.log;
    error_log  /var/log/nginx/${SITE_NAME}.error.log;

    location ^~ /.well-known/acme-challenge/ {
        root $ROOT;
        default_type "text/plain";
        try_files \$uri =404;
    }

    location = / {
        return 302 /$SLUG/;
    }

    location /server/ {
        try_files \$uri /$SLUG\$uri =404;
    }

    location / {
        try_files \$uri \$uri/ /$SLUG\$uri /$SLUG\$uri/ /$SLUG/index.html =404;
    }
}
NGINX

ln -sfn "$NGINX_SITE" "$NGINX_LINK"

echo
echo "=== NGINX TEST / RELOAD HTTP VHOST ==="
nginx -t
systemctl reload nginx

echo
echo "=== LOCAL HTTP CHECK ==="
curl -sS -I --max-time 10 -H "Host: $HOST" "http://127.0.0.1/$SLUG/" || true
curl -sS -I --max-time 10 -H "Host: $HOST" "http://127.0.0.1/server/HANDOVER.html" || true

echo
echo "=== REQUEST / RENEW LETSENCRYPT CERTIFICATE ==="
if [ -f "/etc/letsencrypt/live/$HOST/fullchain.pem" ] && [ -f "/etc/letsencrypt/live/$HOST/privkey.pem" ]; then
  echo "Existing certificate found for $HOST"
else
  certbot certonly \
    --webroot \
    -w "$ROOT" \
    -d "$HOST" \
    --agree-tos \
    --email "$EMAIL" \
    --non-interactive
fi

echo
echo "=== WRITE FINAL HTTPS NGINX VHOST ==="
cat > "$NGINX_SITE" <<NGINX
server {
    listen 80;
    listen [::]:80;
    server_name $HOST;

    root $ROOT;

    location ^~ /.well-known/acme-challenge/ {
        root $ROOT;
        default_type "text/plain";
        try_files \$uri =404;
    }

    location / {
        return 301 https://\$host\$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name $HOST;

    root $ROOT;
    index index.html index.htm;

    ssl_certificate     /etc/letsencrypt/live/$HOST/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/$HOST/privkey.pem;

    access_log /var/log/nginx/${SITE_NAME}.access.log;
    error_log  /var/log/nginx/${SITE_NAME}.error.log;

    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer" always;
    add_header X-Frame-Options "DENY" always;

    location = / {
        return 302 /$SLUG/;
    }

    location /server/ {
        try_files \$uri /$SLUG\$uri =404;
    }

    location / {
        try_files \$uri \$uri/ /$SLUG\$uri /$SLUG\$uri/ /$SLUG/index.html =404;
    }
}
NGINX

echo
echo "=== NGINX FINAL TEST / RELOAD ==="
nginx -t
systemctl reload nginx

echo
echo "=== CERTBOT RENEWAL TEST ==="
certbot renew --dry-run || true

echo
echo "=== HTTPS LOCAL CHECK ==="
curl -k -sS -I --max-time 10 -H "Host: $HOST" "https://127.0.0.1/$SLUG/" || true
curl -k -sS -I --max-time 10 -H "Host: $HOST" "https://127.0.0.1/server/HANDOVER.html" || true

echo
echo "=== UPDATE HANDOVER / CHANGELOG IF FOUND ==="
for BASE in /opt/legaltranscribe/scripts /var/www/handoff /root /var/www/goseek; do
  if [ -f "$BASE/CHANGELOG.md" ]; then
    cat >> "$BASE/CHANGELOG.md" <<MD

## 2026-05-30 - Public HTTPS handoff portal

### Changed
- Added normal HTTPS access for the handoff portal on $HOST.
- Added Nginx vhost $NGINX_SITE.
- Added Let's Encrypt certificate path /etc/letsencrypt/live/$HOST/.
- Preserved the slug path /$SLUG/ and added /server/ fallback mapping.

### Security
- Handoff remains static-only under $ROOT.
[REDACTED SECRET-LIKE LINE]
MD
    echo "Updated $BASE/CHANGELOG.md"
    break
  fi
done

for BASE in /opt/legaltranscribe/scripts /var/www/handoff /root /var/www/goseek; do
  if [ -f "$BASE/HANDOVER.md" ]; then
    cat >> "$BASE/HANDOVER.md" <<MD

---

## Update - 2026-05-30 - Public HTTPS handoff portal

The handoff portal is now intended to be reachable over standard HTTPS:

- https://$HOST/$SLUG/
- https://$HOST/server/HANDOVER.html

Nginx vhost:
- $NGINX_SITE

Certificate:
- /etc/letsencrypt/live/$HOST/fullchain.pem
- /etc/letsencrypt/live/$HOST/privkey.pem

Important:
- Keep only sanitised handoff files in $ROOT.
[REDACTED SECRET-LIKE LINE]
MD
    echo "Updated $BASE/HANDOVER.md"
    break
  fi
done

echo
echo "=== DONE ==="
echo "Backup dir: $BACKUP_DIR"
echo
echo "Public URLs to test:"
echo "https://$HOST/$SLUG/"
echo "https://$HOST/server/HANDOVER.html"