diff --git a/docs/04-configuration/TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md b/docs/04-configuration/TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md index 313f2fb0..425d4a02 100644 --- a/docs/04-configuration/TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md +++ b/docs/04-configuration/TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md @@ -15,9 +15,22 @@ bash metamask-integration/chain138-snap/scripts/verify-snap-api-and-icons.sh htt **If you see "no .tokens" or "no .networks":** The `/api/v1/` path is likely proxied to Blockscout (or another backend) instead of token-aggregation. Proceed to §2. **Repo check:** `scripts/verify/check-public-report-api.sh` tries apex `/api/v1/` first, then `/token-aggregation/api/v1/`, and uses whichever returns a `.networks` array. -### 1.1 HTTPS 502 on `/token-aggregation/` while LAN is OK +### 1.1 HTTPS 502 / 522 / redirect loops on `/token-aggregation/` while LAN is OK -If `curl https://explorer.d-bis.org/token-aggregation/api/v1/networks` returns **502** but `curl -H "Host: explorer.d-bis.org" http://192.168.11.140/token-aggregation/api/v1/networks` is **200**, nginx and `token-aggregation` on VMID **5000** are healthy; suspect **WAN port-forward or public IP routing** (one public IP may forward correctly while another does not). Compare `curl -k --resolve explorer.d-bis.org:443:` across routed NPM addresses, fix UDM/NAT or Cloudflare **A** for `explorer`, or rely on LAN verification: `bash scripts/verify/check-public-report-api.sh "http://192.168.11.140"`. **`run-completable-tasks-from-anywhere.sh`** retries that LAN URL automatically if the public HTTPS check fails. +If `curl -H "Host: explorer.d-bis.org" http://192.168.11.140/token-aggregation/api/v1/networks` is **200** but the public URL fails, nginx and `token-aggregation` on VMID **5000** are healthy; the break is usually **Cloudflare ↔ NPM ↔ WAN**. + +**Operator sequence (repo scripts, from LAN with `.env`):** + +1. **d-bis.org SSL mode Full** (fixes Flexible + NPM “SSL forced” redirect loops): + `bash scripts/cloudflare/set-d-bis-org-zone-ssl-mode.sh full` +2. **Explorer A record** (default primary WAN `76.53.10.36`, orange-cloud preserved unless you export `EXPLORER_D_BIS_CF_PROXIED`): + `bash scripts/cloudflare/configure-explorer-d-bis-dns-wan.sh` + If only an alternate WAN forwards cleanly to the NPM row for `explorer`, set e.g. `EXPLORER_D_BIS_WAN_A=76.53.10.34` (see `config/ip-addresses.conf` `PUBLIC_IP_ER605_WAN1`) and re-run. +3. **Verify:** `bash scripts/verify/check-public-report-api.sh` (or `BASE_URL=…` as below). + +**LAN-only verification** (bypasses public DNS): +`bash scripts/verify/check-public-report-api.sh "http://192.168.11.140"` +**`run-completable-tasks-from-anywhere.sh`** retries that LAN URL automatically if the public HTTPS check fails. --- diff --git a/scripts/cloudflare/configure-explorer-d-bis-dns-wan.sh b/scripts/cloudflare/configure-explorer-d-bis-dns-wan.sh new file mode 100755 index 00000000..01a565c3 --- /dev/null +++ b/scripts/cloudflare/configure-explorer-d-bis-dns-wan.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# Point explorer.d-bis.org (Cloudflare A) at a WAN IP that forwards cleanly to NPM for +# long paths like /token-aggregation/api/v1/* (see TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md §1.1). +# +# Default A record content is 76.53.10.36 (primary WAN / NPM in repo defaults). Override with EXPLORER_D_BIS_WAN_A +# (e.g. 76.53.10.34 from ip-addresses.conf PUBLIC_IP_ER605_WAN1 if port-forward differs on the primary IP). +# Proxied: if EXPLORER_D_BIS_CF_PROXIED is set, use it; else preserve the existing Cloudflare value when updating; +# else false for a new record. After changing proxy mode, run: bash scripts/cloudflare/set-d-bis-org-zone-ssl-mode.sh full +# +# Usage: bash scripts/cloudflare/configure-explorer-d-bis-dns-wan.sh +# Requires: .env with Cloudflare auth + CLOUDFLARE_ZONE_ID_D_BIS_ORG (or CLOUDFLARE_ZONE_ID). +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" +# shellcheck source=/dev/null +source config/ip-addresses.conf 2>/dev/null || true +[ -f .env ] && set +u && source .env 2>/dev/null || true && set -u + +ZONE_ID="${CLOUDFLARE_ZONE_ID:-${CLOUDFLARE_ZONE_ID_D_BIS_ORG:-}}" +HOSTNAME="explorer.d-bis.org" +NAME_LABEL="explorer" +TARGET_IP="${EXPLORER_D_BIS_WAN_A:-76.53.10.36}" + +if [ -n "${CLOUDFLARE_API_TOKEN:-}" ]; then + AUTH_H=(-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN") +elif [ -n "${CLOUDFLARE_API_KEY:-}" ] && [ -n "${CLOUDFLARE_EMAIL:-}" ]; then + AUTH_H=(-H "X-Auth-Email: $CLOUDFLARE_EMAIL" -H "X-Auth-Key: $CLOUDFLARE_API_KEY") +else + echo "Set CLOUDFLARE_API_TOKEN or (CLOUDFLARE_EMAIL + CLOUDFLARE_API_KEY) in .env" >&2 + exit 1 +fi + +[ -z "${ZONE_ID:-}" ] && { echo "Set CLOUDFLARE_ZONE_ID or CLOUDFLARE_ZONE_ID_D_BIS_ORG in .env" >&2; exit 1; } + +EXISTING=$(curl -s -X GET "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records?name=${HOSTNAME}" \ + "${AUTH_H[@]}" -H "Content-Type: application/json") +RECORD_ID=$(echo "$EXISTING" | jq -r '.result[0].id // empty') +CURRENT_CONTENT=$(echo "$EXISTING" | jq -r '.result[0].content // empty') +CURRENT_PROXIED=$(echo "$EXISTING" | jq -r '.result[0].proxied // false') + +if [[ -v EXPLORER_D_BIS_CF_PROXIED ]]; then + if [[ "${EXPLORER_D_BIS_CF_PROXIED}" == "true" ]] || [[ "${EXPLORER_D_BIS_CF_PROXIED}" == "1" ]]; then + _CF_PROXIED_JSON="true" + else + _CF_PROXIED_JSON="false" + fi +elif [ -n "$RECORD_ID" ] && [ "$RECORD_ID" != "null" ]; then + [[ "${CURRENT_PROXIED}" == "true" ]] && _CF_PROXIED_JSON="true" || _CF_PROXIED_JSON="false" +else + _CF_PROXIED_JSON="false" +fi + +DATA=$(jq -n \ + --arg name "$NAME_LABEL" \ + --arg content "$TARGET_IP" \ + --argjson proxied "${_CF_PROXIED_JSON}" \ + '{type:"A",name:$name,content:$content,ttl:1,proxied:$proxied}') + +echo "explorer.d-bis.org A → ${TARGET_IP} (proxied=${_CF_PROXIED_JSON})" + +if [ -n "$RECORD_ID" ] && [ "$RECORD_ID" != "null" ]; then + if [ "$CURRENT_CONTENT" = "$TARGET_IP" ] && [[ "${CURRENT_PROXIED}" == "${_CF_PROXIED_JSON}" ]]; then + echo " ${HOSTNAME}: OK (already A → ${TARGET_IP}, proxied=${_CF_PROXIED_JSON})" + exit 0 + fi + UPD=$(curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records/${RECORD_ID}" \ + "${AUTH_H[@]}" -H "Content-Type: application/json" -d "$DATA") + if echo "$UPD" | jq -e '.success == true' >/dev/null 2>&1; then + echo " ${HOSTNAME}: Updated A → ${TARGET_IP} (proxied=${_CF_PROXIED_JSON})" + else + echo " ${HOSTNAME}: Update failed ($(echo "$UPD" | jq -c '.errors' 2>/dev/null))" >&2 + exit 1 + fi +else + CR=$(curl -s -X POST "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/dns_records" \ + "${AUTH_H[@]}" -H "Content-Type: application/json" -d "$DATA") + if echo "$CR" | jq -e '.success == true' >/dev/null 2>&1; then + echo " ${HOSTNAME}: Created A → ${TARGET_IP} (proxied=${_CF_PROXIED_JSON})" + else + echo " ${HOSTNAME}: Create failed ($(echo "$CR" | jq -c '.errors' 2>/dev/null))" >&2 + exit 1 + fi +fi + +echo "If browsers or curl show redirect loops on HTTPS, set d-bis.org SSL to Full: bash scripts/cloudflare/set-d-bis-org-zone-ssl-mode.sh full" diff --git a/scripts/cloudflare/set-d-bis-org-zone-ssl-mode.sh b/scripts/cloudflare/set-d-bis-org-zone-ssl-mode.sh new file mode 100755 index 00000000..5b0b9b37 --- /dev/null +++ b/scripts/cloudflare/set-d-bis-org-zone-ssl-mode.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# Set Cloudflare d-bis.org zone SSL/TLS mode (same fix as sankofa: Flexible + NPM SSL forced → redirect loops). +# +# Usage: bash scripts/cloudflare/set-d-bis-org-zone-ssl-mode.sh [full|strict|flexible|off] [--dry-run] +# Env: CLOUDFLARE_ZONE_ID_D_BIS_ORG (or CLOUDFLARE_ZONE_ID), CLOUDFLARE_API_TOKEN (or email + global key) +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +cd "$PROJECT_ROOT" +# shellcheck source=/dev/null +source config/ip-addresses.conf 2>/dev/null || true +if [ -f .env ]; then set +u && set -a && source .env && set +a && set -u; fi + +MODE="${1:-full}" +DRY=false +[[ "${2:-}" == "--dry-run" ]] || [[ "${1:-}" == "--dry-run" ]] && DRY=true +[[ "$MODE" == "--dry-run" ]] && MODE="full" && DRY=true + +case "$MODE" in + full|strict|flexible|off) ;; + *) + echo "Usage: $0 [full|strict|flexible|off] [--dry-run]" >&2 + exit 1 + ;; +esac + +ZONE_ID="${CLOUDFLARE_ZONE_ID_D_BIS_ORG:-${CLOUDFLARE_ZONE_ID:-}}" +if [ -z "$ZONE_ID" ]; then + echo "Set CLOUDFLARE_ZONE_ID_D_BIS_ORG (or CLOUDFLARE_ZONE_ID) in .env" >&2 + exit 1 +fi + +if [ -n "${CLOUDFLARE_API_TOKEN:-}" ]; then + AUTH_H=(-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN") +elif [ -n "${CLOUDFLARE_API_KEY:-}" ] && [ -n "${CLOUDFLARE_EMAIL:-}" ]; then + AUTH_H=(-H "X-Auth-Email: $CLOUDFLARE_EMAIL" -H "X-Auth-Key: $CLOUDFLARE_API_KEY") +else + echo "Set CLOUDFLARE_API_TOKEN or CLOUDFLARE_EMAIL + CLOUDFLARE_API_KEY" >&2 + exit 1 +fi + +if $DRY; then + echo "[dry-run] Would PATCH zones/$ZONE_ID/settings/ssl value=$MODE (d-bis.org)" + exit 0 +fi + +BODY=$(jq -n --arg v "$MODE" '{value:$v}') +RESP=$(curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/settings/ssl" \ + "${AUTH_H[@]}" -H "Content-Type: application/json" -d "$BODY") + +if echo "$RESP" | jq -e '.success == true' >/dev/null 2>&1; then + echo "OK: d-bis.org SSL mode set to $MODE" +else + echo "$RESP" | jq . >&2 || echo "$RESP" >&2 + exit 1 +fi