/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"