brand/ subdirectory: - README.md — asset map, voice, mark rules - palette.md — signal + editorial themes, typography (Fraunces / DM Sans / DM+JetBrains Mono), extracted from deviant_website.html :root vars - icon/ — favicon.svg (64px), icon-512.svg (matches shells/app/public/icon.svg) - logo/ — deviant-mark.svg (dark), deviant-mark-onlight.svg (inverted), deviant-wordmark.svg (mark + wordmark + tagline) - social/ — og-1200x630.svg (Open Graph), twitter-card-1200x600.svg, avatar-400x400.svg (round-crop-safe) - source/ — README on exporting to PNG via rsvg-convert / Inkscape / ImageMagick scripts/deploy-deviant-navigate-st.sh: - Idempotent 3-phase deploy: install :80 ACME block → issue LetsEncrypt cert via webroot → install :443 vhost. Clones the pattern used by checkers.x.navigate.st in the same nginx.conf. - Edits /opt/checkers/nginx/nginx.conf IN PLACE (single-file bind-mount discipline per vps-access.md), takes a timestamped backup first, awk-injects each block only if absent. - Verifies with curl + openssl s_client. Reads SSH_KEY/HOST env overrides.
145 lines
6.1 KiB
Bash
145 lines
6.1 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"
|
|
getent hosts "$NAME" >/dev/null || { echo "ERROR: $NAME does not resolve"; exit 1; }
|
|
echo "$NAME resolves OK"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
log "Phase 1 — install :80 ACME block + reload"
|
|
|
|
sshx "cp $NGINX_CONF ${NGINX_CONF}.bak.\$(date +%Y%m%d-%H%M%S)"
|
|
|
|
# If our block already present, skip.
|
|
if sshx "grep -q 'server_name deviant.navigate.st;' $NGINX_CONF"; then
|
|
echo "vhost blocks for $NAME already present, skipping Phase 1 insert"
|
|
else
|
|
# Insert the :80 block right after the existing testchipnet :80 block's closing brace.
|
|
# awk-based in-place: read current, emit new content, cat back over the file.
|
|
sshx "awk '
|
|
{ print }
|
|
/server_name testchipnet\\.duckdns\\.org;/ { in_test=1 }
|
|
in_test && /^ }$/ {
|
|
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 \" }\"
|
|
in_test=0
|
|
}
|
|
' $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.
|
|
if sshx "awk '/server_name deviant.navigate.st;/ { c++ } END { exit (c>=2)?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."
|