T-HUB/infra/occupymedia/edge/edge-activate.sh
glass beadian 161f06f1e9 Ship edge-activate.sh: workstation-side activation + pool verification
One-shot watcher for the workstation: probes ping.occupymedia.cc through a
reachable edge IP, flips the four hostnames after the port-forward verifies,
checks the public path for 6 minutes and auto-reverts to TUNNEL if the new
pool assignment is broken too. Retires itself if the tunnel heals first.
README updated to match.
2026-10-10 21:26:54 +00:00

129 lines
6.9 KiB
Bash
Executable file

#!/bin/bash
# edge-activate.sh — one-shot activation: flips the four hostnames to ORIGIN mode
# once the router port-forward verifiably works THROUGH CLOUDFLARE (the real
# production path: working edge pool -> CF -> origin:443 -> nginx -> apps).
#
# Probe: https://ping.occupymedia.cc (proxied A -> public IP). nginx returns 200
# "edge-ok" only when the whole chain works. While the forward is down CF
# answers 521 (origin unreachable), which counts as not-live.
#
# Also keeps ping's A record synced to the current public IP (the IP is dynamic).
# Retires itself if the tunnel path heals first (flip not needed).
set -uo pipefail
source /home/moxexley/projects/SuperPoster/.env 2>/dev/null || { echo "no .env"; exit 1; }
ZONE="4bd0ac2e13a735d2e0068ce3feea2996"
HOSTS=(app api tube chat)
TARGET_MODE=${TARGET_MODE:-ORIGIN} # ORIGIN (CF-proxied) or GREY (direct DNS)
LOG=/tmp/opencode/edge-activate.log
SSH="ssh -i $HOME/.ssh/devserver -o ConnectTimeout=10 root@proxnode2"
log() { echo "$(date -u '+%H:%M:%S') $*" >> "$LOG"; }
get_ip() { $SSH "curl -s -m 8 ifconfig.me" 2>/dev/null; }
probe_edge() { curl -s -4 -o /dev/null -w '%{http_code}' -m 20 \
--resolve "ping.occupymedia.cc:443:104.21.59.202" https://ping.occupymedia.cc 2>/dev/null; }
revert_to_tunnel() {
T="Authorization: Bearer $CLOUDFLARE_API_TOKEN"
ZURL="https://api.cloudflare.com/client/v4/zones/$ZONE/dns_records"
for h in "${HOSTS[@]}"; do
aid=$(curl -s -m 15 -H "$T" "$ZURL?type=A&name=$h.occupymedia.cc" | python3 -c "import json,sys; r=json.load(sys.stdin)['result']; print(r[0]['id'] if r else '')")
[[ -n $aid ]] && curl -s -m 15 -X DELETE -H "$T" "$ZURL/$aid" > /dev/null
cid=$(curl -s -m 15 -H "$T" "$ZURL?type=CNAME&name=$h.occupymedia.cc" | python3 -c "import json,sys; r=json.load(sys.stdin)['result']; print(r[0]['id'] if r else '')")
if [[ -z $cid ]]; then
curl -s -m 15 -X POST -H "$T" -H "Content-Type: application/json" \
-d "{\"type\":\"CNAME\",\"name\":\"$h.occupymedia.cc\",\"content\":\"e5695d8b-94eb-4e2b-9600-ed3cbf20e541.cfargotunnel.com\",\"proxied\":true}" \
"$ZURL" > /dev/null
fi
done
$SSH "pct exec 150 -- bash -c 'cd /opt/anarchoposter/infra/occupymedia/edge && sed -i \"s/^EDGE_ENABLED=.*/EDGE_ENABLED=false/\" watcher.env && echo \"mode=TUNNEL failures=0 tunnel_ok=0 origin_ok=1 ip=$1\" > edge.state'" >/dev/null 2>&1
}
probe_default() { curl -s -o /dev/null -w '%{http_code}' -m 8 https://app.occupymedia.cc 2>/dev/null; }
ping_record_id() { curl -s -m 15 -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/dns_records?type=A&name=ping.occupymedia.cc" \
| python3 -c "import json,sys; r=json.load(sys.stdin)['result']; print(r[0]['id'] if r else '')" 2>/dev/null; }
sync_ping_ip() { # $1 = current public IP
local rid content
rid=$(ping_record_id)
[[ -n $rid ]] || { log "ping record missing — recreating"; \
curl -s -m 15 -X POST -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" -H "Content-Type: application/json" \
-d "{\"type\":\"A\",\"name\":\"ping.occupymedia.cc\",\"content\":\"$1\",\"proxied\":true,\"ttl\":60}" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/dns_records" > /dev/null; return; }
content=$(curl -s -m 15 -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
"https://api.cloudflare.com/client/v4/zones/$ZONE/dns_records/$rid" \
| python3 -c "import json,sys; print(json.load(sys.stdin)['result']['content'])" 2>/dev/null)
if [[ $content != "$1" ]]; then
curl -s -m 15 -X PATCH -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" -H "Content-Type: application/json" \
-d "{\"content\":\"$1\"}" "https://api.cloudflare.com/client/v4/zones/$ZONE/dns_records/$rid" > /dev/null
log "ping A synced $content -> $1"
fi
}
fails=0
while true; do
sleep 60
ip=$(get_ip)
[[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || { log "ip lookup failed"; continue; }
sync_ping_ip "$ip"
default_code=$(probe_default)
if [[ $default_code == 200 || $default_code == 307 ]]; then
log "tunnel path healed (default=$default_code) — activation not needed, retiring."
exit 0
fi
code=$(probe_edge)
if [[ $code == 200 ]]; then
fails=$((fails+1))
log "edge probe via CF -> $code (live, $fails/2)"
else
fails=0
log "edge probe via CF -> $code (forward not live yet)"
continue
fi
if [[ $fails -ge 2 ]]; then
log "origin path confirmed live via Cloudflare ($code x2) — ACTIVATING as $TARGET_MODE"
$SSH "pct exec 150 -- sed -i 's/^EDGE_ENABLED=.*/EDGE_ENABLED=true/; s/^TARGET_MODE=.*/TARGET_MODE=$TARGET_MODE/' /opt/anarchoposter/infra/occupymedia/edge/watcher.env" >/dev/null 2>&1
if [[ $TARGET_MODE == GREY ]]; then
$SSH "pct exec 150 -- bash -c 'cd /opt/anarchoposter/infra/occupymedia/edge && echo OPEN > firewall.mode && ./edge-firewall.sh'" >/dev/null 2>&1
log "firewall relaxed to OPEN (grey mode: visitors connect directly)"
fi
proxied=true
[[ $TARGET_MODE == GREY ]] && proxied=false
T="Authorization: Bearer $CLOUDFLARE_API_TOKEN"
ZURL="https://api.cloudflare.com/client/v4/zones/$ZONE/dns_records"
for h in "${HOSTS[@]}"; do
cid=$(curl -s -m 15 -H "$T" "$ZURL?type=CNAME&name=$h.occupymedia.cc" | python3 -c "import json,sys; r=json.load(sys.stdin)['result']; print(r[0]['id'] if r else '')")
[[ -n $cid ]] && curl -s -m 15 -X DELETE -H "$T" "$ZURL/$cid" > /dev/null
aid=$(curl -s -m 15 -H "$T" "$ZURL?type=A&name=$h.occupymedia.cc" | python3 -c "import json,sys; r=json.load(sys.stdin)['result']; print(r[0]['id'] if r else '')")
if [[ -n $aid ]]; then
curl -s -m 15 -X PATCH -H "$T" -H "Content-Type: application/json" -d "{\"content\":\"$ip\",\"proxied\":$proxied}" "$ZURL/$aid" > /dev/null
else
curl -s -m 15 -X POST -H "$T" -H "Content-Type: application/json" -d "{\"type\":\"A\",\"name\":\"$h.occupymedia.cc\",\"content\":\"$ip\",\"proxied\":$proxied}" "$ZURL" > /dev/null
fi
log "$h -> A $ip proxied=$proxied"
done
$SSH "pct exec 150 -- bash -c 'cd /opt/anarchoposter/infra/occupymedia/edge && echo \"mode=$TARGET_MODE failures=0 tunnel_ok=0 origin_ok=1 ip=$ip\" > edge.state'" >/dev/null 2>&1
log "DNS flipped to $TARGET_MODE mode. Verifying public path for 6 min..."
verified=0
for i in 1 2 3 4 5 6 7 8 9 10 11 12; do
sleep 30
dc=$(probe_default)
if [[ $dc == 200 || $dc == 307 ]]; then verified=1; break; fi
log "post-flip check $i/12: default=$dc"
done
if [[ $verified == 1 ]]; then
log "PUBLIC PATH VERIFIED — $TARGET_MODE mode live, site reachable. CT watcher owns failover."
else
log "default path STILL broken post-flip — $TARGET_MODE pool assignment is dead too. REVERTING to TUNNEL."
revert_to_tunnel "$ip"
log "Reverted to TUNNEL + watcher gated off. If guaranteed reachability is required: rerun with TARGET_MODE=GREY (direct DNS, origin IP exposed, firewall OPEN)."
fi
exit 0
fi
done