← back to Wallco Ai

deploy/curator-tunnel-finish-api.sh

83 lines

#!/bin/bash
# curator-tunnel-finish-api.sh — API path (DTD verdict C, 2026-06-24).
# Creates the wpb-curator tunnel WITHOUT cloudflared login/cert.pem, using a
# narrowly-scoped CF API token (Account:Cloudflare-Tunnel:Edit + Zone:DNS:Edit).
# Idempotent. No sudo. Installs a token-run LaunchAgent (survives reboot).
#
# Usage:  CF_TUNNEL_TOKEN=<scoped-api-token> bash curator-tunnel-finish-api.sh
set -euo pipefail

: "${CF_TUNNEL_TOKEN:?Set CF_TUNNEL_TOKEN to the scoped Cloudflare API token}"
ACCT=$(grep -E "^CLOUDFLARE_ACCOUNT_ID=" "$HOME/Projects/secrets-manager/.env" | cut -d= -f2)
ZONE=70cac5396b39727ac107663b45452038          # wallpapersback.com
NAME=wpb-curator
HOSTNAME=curator.wallpapersback.com
SERVICE=http://127.0.0.1:9916                    # local nginx (basic-auth wall)
API=https://api.cloudflare.com/client/v4
H=(-H "Authorization: Bearer $CF_TUNNEL_TOKEN" -H "Content-Type: application/json")
PLIST="$HOME/Library/LaunchAgents/com.steve.wpb-curator-tunnel.plist"

jqget(){ python3 -c "import sys,json;d=json.load(sys.stdin);print($1)"; }

# 1. Create (or find) the remotely-managed tunnel.
EXIST=$(curl -s "${H[@]}" "$API/accounts/$ACCT/cfd_tunnel?name=$NAME&is_deleted=false")
TID=$(echo "$EXIST" | jqget "(d['result'][0]['id'] if d.get('result') else '')" 2>/dev/null || echo "")
if [ -z "$TID" ]; then
  echo "→ creating tunnel $NAME"
  RESP=$(curl -s -X POST "${H[@]}" "$API/accounts/$ACCT/cfd_tunnel" \
    -d "{\"name\":\"$NAME\",\"config_src\":\"cloudflare\"}")
  echo "$RESP" | jqget "('ok' if d.get('success') else 'ERR: '+str(d.get('errors')))"
  TID=$(echo "$RESP" | jqget "d['result']['id']")
else
  echo "→ tunnel $NAME already exists"
fi
echo "  tunnel id: $TID"

# 2. Set ingress config (remotely-managed): hostname → local nginx, catch-all 404.
echo "→ setting ingress"
curl -s -X PUT "${H[@]}" "$API/accounts/$ACCT/cfd_tunnel/$TID/configurations" \
  -d "{\"config\":{\"ingress\":[{\"hostname\":\"$HOSTNAME\",\"service\":\"$SERVICE\"},{\"service\":\"http_status:404\"}]}}" \
  | jqget "('ok' if d.get('success') else 'ERR: '+str(d.get('errors')))"

# 3. Proxied CNAME: curator → <id>.cfargotunnel.com (upsert).
echo "→ DNS route $HOSTNAME"
REC=$(curl -s "${H[@]}" "$API/zones/$ZONE/dns_records?type=CNAME&name=$HOSTNAME")
RID=$(echo "$REC" | jqget "(d['result'][0]['id'] if d.get('result') else '')" 2>/dev/null || echo "")
BODY="{\"type\":\"CNAME\",\"name\":\"$HOSTNAME\",\"content\":\"$TID.cfargotunnel.com\",\"proxied\":true,\"ttl\":1}"
if [ -z "$RID" ]; then
  curl -s -X POST "${H[@]}" "$API/zones/$ZONE/dns_records" -d "$BODY" \
    | jqget "('ok' if d.get('success') else 'ERR: '+str(d.get('errors')))"
else
  curl -s -X PUT "${H[@]}" "$API/zones/$ZONE/dns_records/$RID" -d "$BODY" \
    | jqget "('ok(updated)' if d.get('success') else 'ERR: '+str(d.get('errors')))"
fi

# 4. Get the run token + install a token-run LaunchAgent.
RUNTOK=$(curl -s "${H[@]}" "$API/accounts/$ACCT/cfd_tunnel/$TID/token" | jqget "d['result']")
[ -n "$RUNTOK" ] && [ "$RUNTOK" != "None" ] || { echo "ERROR: no run token (check token scope)"; exit 1; }
TOKFILE="$HOME/.cloudflared/wpb-curator.token"; mkdir -p "$HOME/.cloudflared"
printf '%s' "$RUNTOK" > "$TOKFILE"; chmod 600 "$TOKFILE"

cat > "$PLIST" <<PLISTEOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
  <key>Label</key><string>com.steve.wpb-curator-tunnel</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/sh</string><string>-c</string>
    <string>exec $(command -v cloudflared) tunnel --no-autoupdate run --token "\$(cat $TOKFILE)"</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
  <key>StandardOutPath</key><string>$HOME/Library/Logs/wpb-curator-tunnel.log</string>
  <key>StandardErrorPath</key><string>$HOME/Library/Logs/wpb-curator-tunnel.log</string>
</dict></plist>
PLISTEOF
launchctl bootout "gui/$(id -u)/com.steve.wpb-curator-tunnel" 2>/dev/null || true
launchctl bootstrap "gui/$(id -u)" "$PLIST"
launchctl kickstart -k "gui/$(id -u)/com.steve.wpb-curator-tunnel" 2>/dev/null || true
echo "✓ tunnel up via API + LaunchAgent installed. Log: ~/Library/Logs/wpb-curator-tunnel.log"
echo "  curator.wallpapersback.com resolves after the GoDaddy→Cloudflare NS cutover."
echo "  (You may revoke the scoped CF token now — the run token is what keeps the tunnel alive.)"