Fonts: every text-carrying brand SVG (wordmark, OG, twitter-card, twitter-header, FB cover, LinkedIn banner, IG square) now @imports Fraunces + DM Sans + JetBrains Mono from Google Fonts inside a <style> block — so browsers render the website's own faces. Fallback stack (Georgia / system sans) still applies in renderers that ignore remote CSS (rsvg-convert). README documents the trade-off. New social sizes: - twitter-card-1200x675.svg — Twitter's current summary_large_image spec (was 1200×600, wrong) - twitter-header-1500x500.svg — profile banner with lower-left profile-photo safe zone - facebook-cover-820x312.svg — Facebook page cover - linkedin-banner-1584x396.svg — LinkedIn company banner with profile-photo safe zone - instagram-square-1080x1080.svg — IG feed / Threads / Discord (retired the wrong 1200×600 Twitter card) Deploy script fixes (scripts/deploy-deviant-navigate-st.sh): - Portable DNS check — getent → dig/nslookup fallback so it runs on Git Bash / macOS - Phase 1 awk: match the testchipnet :80 block SPECIFICALLY (was matching :80 and :443, causing two duplicate deviant.navigate.st :80 blocks and no :443) - Phase 3 skip check: verify a :443 block actually exists (was counting occurrences of the server_name line, which the Phase 1 bug already inflated) Live prod was patched manually before this commit; the script is now safe for greenfield.
158 lines
7 KiB
Bash
158 lines
7 KiB
Bash
#!/usr/bin/env bash
|
|
# Add deviant.navigate.st as an ICANN mirror of the deviant.x wallet on the
|
|
# shared VPS (130.12.242.84). Idempotent: safe to re-run.
|
|
#
|
|
# Runs in three phases:
|
|
# Phase 1: patch nginx.conf with :80 ACME block + reload
|
|
# Phase 2: issue LetsEncrypt cert via webroot (--webroot -w /opt/checkers/nginx/html/deviant)
|
|
# Phase 3: patch nginx.conf with :443 vhost + reload + verify
|
|
#
|
|
# Prereqs:
|
|
# - DNS: deviant.navigate.st -> 130.12.242.84 (verified before running)
|
|
# - SSH key: ~/.ssh/checkers_deploy
|
|
#
|
|
# Pattern cloned from checkers.x.navigate.st in the same nginx.conf.
|
|
# Single-file bind mount: edits happen in place (cat > file), NOT sed -i / mv.
|
|
|
|
set -euo pipefail
|
|
|
|
SSH_KEY="${SSH_KEY:-$HOME/.ssh/checkers_deploy}"
|
|
HOST="${HOST:-root@130.12.242.84}"
|
|
NAME="deviant.navigate.st"
|
|
NGINX_CONF="/opt/checkers/nginx/nginx.conf"
|
|
WEBROOT="/opt/checkers/nginx/html/deviant"
|
|
CONTAINER="checkers-nginx-1"
|
|
|
|
log() { printf '\n\033[1;36m▸ %s\033[0m\n' "$*"; }
|
|
sshx() { ssh -i "$SSH_KEY" -o ConnectTimeout=10 "$HOST" "$@"; }
|
|
|
|
# ---------------------------------------------------------------------------
|
|
log "Phase 0 — DNS check"
|
|
# Portable across Linux (getent), macOS/Windows-Git-Bash (nslookup/dig fallback).
|
|
resolved=""
|
|
if command -v getent >/dev/null 2>&1; then
|
|
resolved=$(getent hosts "$NAME" 2>/dev/null | awk '{print $1}' | head -1)
|
|
elif command -v dig >/dev/null 2>&1; then
|
|
resolved=$(dig +short "$NAME" @8.8.8.8 | head -1)
|
|
elif command -v nslookup >/dev/null 2>&1; then
|
|
resolved=$(nslookup "$NAME" 8.8.8.8 2>/dev/null | awk '/^Address: / && !/#53$/ {print $2; exit}')
|
|
fi
|
|
[[ -n "$resolved" ]] || { echo "ERROR: $NAME does not resolve (tried getent/dig/nslookup)"; exit 1; }
|
|
echo "$NAME resolves to $resolved"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
log "Phase 1 — install :80 ACME block + reload"
|
|
|
|
sshx "cp $NGINX_CONF ${NGINX_CONF}.bak.\$(date +%Y%m%d-%H%M%S)"
|
|
|
|
# If the :80 block for our name is already present, skip.
|
|
# Match specifically: server_name deviant.navigate.st; INSIDE a :80 block.
|
|
if sshx "awk 'BEGIN{IGNORECASE=0} /listen 80;/ {p80=1} p80 && /server_name deviant.navigate.st;/ {found=1; exit} /^ }\$/ {p80=0} END{exit found?0:1}' $NGINX_CONF"; then
|
|
echo ":80 block for $NAME already present, skipping Phase 1 insert"
|
|
else
|
|
# Insert the :80 block right after the FIRST closing brace that follows the testchipnet :80
|
|
# server_name line (i.e. the testchipnet :80 block's closing brace, NOT the :443 one).
|
|
sshx "awk '
|
|
{ print }
|
|
/^ server_name testchipnet\\.duckdns\\.org;\$/ && !seen_test {
|
|
in_test80=1
|
|
}
|
|
in_test80 && /^ \\}\$/ && !seen_test {
|
|
seen_test=1; in_test80=0
|
|
print \"\"
|
|
print \" # ── Deviant landing (deviant.navigate.st) — ICANN mirror of deviant.x ──\"
|
|
print \" server {\"
|
|
print \" listen 80;\"
|
|
print \" server_name deviant.navigate.st;\"
|
|
print \" location /.well-known/acme-challenge/ {\"
|
|
print \" root /usr/share/nginx/html/deviant;\"
|
|
print \" }\"
|
|
print \" location / { return 301 https://\\\$host\\\$request_uri; }\"
|
|
print \" }\"
|
|
}
|
|
' $NGINX_CONF > /tmp/nginx.conf.p1 && cat /tmp/nginx.conf.p1 > $NGINX_CONF && rm /tmp/nginx.conf.p1"
|
|
fi
|
|
|
|
log "nginx -t (phase 1)"
|
|
sshx "docker exec $CONTAINER nginx -t"
|
|
|
|
log "nginx -s reload (phase 1)"
|
|
sshx "docker exec $CONTAINER nginx -s reload"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
log "Phase 2 — LetsEncrypt cert via webroot"
|
|
|
|
if sshx "[ -d /etc/letsencrypt/live/$NAME ]"; then
|
|
echo "cert already present, skipping issuance"
|
|
else
|
|
sshx "certbot certonly --non-interactive --agree-tos --register-unsafely-without-email --webroot -w $WEBROOT -d $NAME"
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
log "Phase 3 — install :443 vhost + reload"
|
|
|
|
# If the :443 block for our name is already present, skip.
|
|
# Match specifically: server_name deviant.navigate.st; INSIDE a :443 block.
|
|
if sshx "awk '/listen 443 ssl;/ {p443=1} p443 && /server_name deviant.navigate.st;/ {found=1; exit} /^ }\$/ {p443=0} END{exit found?0:1}' $NGINX_CONF"; then
|
|
echo ":443 block already present, skipping Phase 3 insert"
|
|
else
|
|
# Insert the :443 block right after the existing testchipnet :443 block's closing brace.
|
|
# The testchipnet :443 block's LAST line has "try_files \$uri \$uri/ /index.html; }" — its
|
|
# closing brace comes on the next indented line. We match the try_files line and the
|
|
# following " }" to know we're at that specific block's end.
|
|
sshx "awk '
|
|
{ buf[++n] = \$0 }
|
|
END {
|
|
done=0
|
|
for (i=1; i<=n; i++) {
|
|
print buf[i]
|
|
if (!done && buf[i] ~ /try_files .* \\/index\\.html; \\}\$/) {
|
|
# find the next line that is exactly \" }\" - that closes the testchipnet 443 block
|
|
j = i+1
|
|
while (j<=n && buf[j] !~ /^ \\}\$/) { print buf[j]; j++ }
|
|
if (j<=n) { print buf[j]; i = j }
|
|
print \"\"
|
|
print \" # -- Deviant landing (deviant.navigate.st) -- HTTPS mirror of deviant.x --\"
|
|
print \" server {\"
|
|
print \" listen 443 ssl;\"
|
|
print \" server_name deviant.navigate.st;\"
|
|
print \" ssl_certificate /etc/letsencrypt/live/deviant.navigate.st/fullchain.pem;\"
|
|
print \" ssl_certificate_key /etc/letsencrypt/live/deviant.navigate.st/privkey.pem;\"
|
|
print \" ssl_protocols TLSv1.2 TLSv1.3;\"
|
|
print \" ssl_ciphers HIGH:!aNULL:!MD5;\"
|
|
print \" ssl_session_cache shared:SSL:10m;\"
|
|
print \" root /usr/share/nginx/html/deviant;\"
|
|
print \" index index.html;\"
|
|
print \" location = /sw.js { add_header Cache-Control \\\"no-cache\\\"; }\"
|
|
print \" location = /index.html { add_header Cache-Control \\\"no-cache\\\"; }\"
|
|
print \" location = /manifest.webmanifest { default_type application/manifest+json; add_header Cache-Control \\\"no-cache\\\"; }\"
|
|
print \" location /assets/ { add_header Cache-Control \\\"public, max-age=31536000, immutable\\\"; }\"
|
|
print \" location / { add_header Cache-Control \\\"no-cache\\\"; try_files \\\$uri \\\$uri/ /index.html; }\"
|
|
print \" }\"
|
|
done=1
|
|
}
|
|
}
|
|
}
|
|
' $NGINX_CONF > /tmp/nginx.conf.p3 && cat /tmp/nginx.conf.p3 > $NGINX_CONF && rm /tmp/nginx.conf.p3"
|
|
fi
|
|
|
|
log "nginx -t (phase 3)"
|
|
sshx "docker exec $CONTAINER nginx -t"
|
|
|
|
log "nginx -s reload (phase 3)"
|
|
sshx "docker exec $CONTAINER nginx -s reload"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
log "Verify"
|
|
sleep 2
|
|
echo "== HTTPS handshake =="
|
|
curl -sI "https://$NAME/" | head -6
|
|
echo
|
|
echo "== cert subject =="
|
|
echo | openssl s_client -connect "$NAME:443" -servername "$NAME" 2>/dev/null \
|
|
| openssl x509 -noout -subject -dates
|
|
echo
|
|
echo "== body =="
|
|
curl -s "https://$NAME/" | grep -oE '<title>[^<]+</title>' | head -1
|
|
|
|
log "Done. https://$NAME/ is live."
|