Files
proxmox/scripts/verify/check-public-report-api.sh
defiQUG b85101f4c2 fix(env): safe dotenv sourcing under set -u; report API prefix fallback
- load-project-env: _lpr_dotenv_source / _lpr_source_relaxed so smom-dbis-138/.env
  lines like ${ARBITRUM_MAINNET_RPC} do not abort scripts using set -u
- check-public-report-api: detect /token-aggregation vs apex /api/v1 for networks
- run-completable-tasks: enforce public report API (remove SKIP_EXIT bypass)
- Document verifier behavior in TOKEN_AGGREGATION_REPORT_API_RUNBOOK and verify README

Made-with: Cursor
2026-03-31 23:18:37 -07:00

155 lines
5.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Verify that the public token-aggregation/report API is reachable and not misrouted to Blockscout.
# Usage: bash scripts/verify/check-public-report-api.sh [base_url]
# base_url: Optional API base, defaults to https://explorer.d-bis.org
#
# Exit codes:
# 0 = all expected endpoints returned token-aggregation-style JSON
# 1 = one or more endpoints returned the wrong shape or were unreachable
# Set SKIP_EXIT=1 to print diagnostics but exit 0.
# Set KEEP_GOING=1 to keep checking every endpoint before exiting non-zero.
# Set SKIP_BRIDGE_ROUTES=0 to assert /api/v1/bridge/routes payload shape.
# Set SKIP_BRIDGE_PREFLIGHT=0 to assert /api/v1/bridge/preflight payload shape.
set -euo pipefail
BASE_URL="${1:-https://explorer.d-bis.org}"
SKIP_EXIT="${SKIP_EXIT:-0}"
KEEP_GOING="${KEEP_GOING:-0}"
HAD_FAILURE=0
log() { printf '%s\n' "$*"; }
ok() { printf '[OK] %s\n' "$*"; }
warn() { printf '[WARN] %s\n' "$*"; }
# Use apex /api/v1 first; if misrouted to Blockscout, try /token-aggregation/api/v1 (nginx layouts differ).
detect_token_aggregation_prefix() {
local path="/api/v1/networks"
local prefix url response status body
for prefix in "" "/token-aggregation"; do
url="${BASE_URL%/}${prefix}${path}"
response="$(curl -sSL --max-time 15 -w $'\n%{http_code}' "$url" 2>/dev/null)" || continue
status="$(printf '%s' "$response" | tail -n 1)"
body="$(printf '%s' "$response" | sed '$d')"
if printf '%s' "$body" | jq -e 'type == "object" and has("message") and has("result") and has("status")' >/dev/null 2>&1; then
continue
fi
if printf '%s' "$body" | jq -e 'type == "object" and (.networks | type == "array")' >/dev/null 2>&1; then
printf '%s\n' "$prefix"
return 0
fi
done
printf '%s\n' ""
}
fail() {
printf '[FAIL] %s\n' "$*"
HAD_FAILURE=1
if [[ "$SKIP_EXIT" != "1" && "$KEEP_GOING" != "1" ]]; then
exit 1
fi
}
check_json_shape() {
local name="$1"
local url="$2"
local jq_expr="$3"
local expected_desc="$4"
local response
local body
local status
if ! response="$(curl -sSL --max-time 20 -w $'\n%{http_code}' "$url" 2>/dev/null)"; then
fail "$name request failed: $url"
return 0
fi
status="$(printf '%s' "$response" | tail -n 1)"
body="$(printf '%s' "$response" | sed '$d')"
if printf '%s' "$body" | jq -e 'type == "object" and has("message") and has("result") and has("status")' >/dev/null 2>&1; then
fail "$name is returning Blockscout-style JSON (HTTP $status) instead of token-aggregation JSON. See docs/04-configuration/TOKEN_AGGREGATION_REPORT_API_RUNBOOK.md"
return 0
fi
if printf '%s' "$body" | jq -e 'type == "object" and has("error")' >/dev/null 2>&1; then
local api_error
api_error="$(printf '%s' "$body" | jq -r '.error' 2>/dev/null || echo 'unknown error')"
fail "$name returned token-aggregation error payload (HTTP $status): $api_error"
return 0
fi
if printf '%s' "$body" | jq -e "$jq_expr" >/dev/null 2>&1; then
ok "$name healthy ($expected_desc, HTTP $status)"
return 0
fi
local summary
summary="$(printf '%s' "$body" | head -c 240 | tr '\n' ' ')"
fail "$name returned unexpected payload (HTTP $status). Expected $expected_desc. Sample: $summary"
}
log "=== Public report API check ==="
log "Base URL: $BASE_URL"
TA_PREFIX="$(detect_token_aggregation_prefix)"
if [[ -n "$TA_PREFIX" ]]; then
log "Using token-aggregation path prefix: ${TA_PREFIX}/api/v1/"
else
log "Using token-aggregation path prefix: /api/v1/ (default; may still be Blockscout if both layouts fail shape test)"
fi
log ""
check_json_shape \
"token-list" \
"${BASE_URL%/}${TA_PREFIX}/api/v1/report/token-list?chainId=138" \
'type == "object" and (.tokens | type == "array")' \
'object with .tokens[]'
check_json_shape \
"coingecko report" \
"${BASE_URL%/}${TA_PREFIX}/api/v1/report/coingecko?chainId=138" \
'type == "object"' \
'token-aggregation report JSON object'
check_json_shape \
"cmc report" \
"${BASE_URL%/}${TA_PREFIX}/api/v1/report/cmc?chainId=138" \
'type == "object"' \
'token-aggregation report JSON object'
check_json_shape \
"networks" \
"${BASE_URL%/}${TA_PREFIX}/api/v1/networks" \
'type == "object" and (.networks | type == "array")' \
'object with .networks[]'
# Bridge routes (requires token-aggregation build with GET /api/v1/bridge/routes). Off by default until edge is deployed.
if [[ "${SKIP_BRIDGE_ROUTES:-1}" != "1" ]]; then
check_json_shape \
"bridge-routes" \
"${BASE_URL%/}${TA_PREFIX}/api/v1/bridge/routes" \
'type == "object" and (.chain138Bridges | type == "object") and (.routes | type == "object")' \
'object with .chain138Bridges and .routes'
fi
# GRU preflight (shape only; does not require all pairs to be runtime-ready). Off by default until edge is deployed.
if [[ "${SKIP_BRIDGE_PREFLIGHT:-1}" != "1" ]]; then
check_json_shape \
"bridge-preflight" \
"${BASE_URL%/}${TA_PREFIX}/api/v1/bridge/preflight" \
'type == "object" and (.gruTransport | type == "object") and (.gruTransport.summary.transportPairs | type == "number") and (.gruTransport.blockedPairs | type == "array")' \
'object with .gruTransport.summary and .gruTransport.blockedPairs[]'
fi
log ""
if (( HAD_FAILURE > 0 )); then
if [[ "$SKIP_EXIT" == "1" ]]; then
warn "SKIP_EXIT=1 set: non-healthy endpoints were reported without failing."
elif [[ "$KEEP_GOING" == "1" ]]; then
exit 1
fi
elif [[ "$SKIP_EXIT" == "1" ]]; then
warn "SKIP_EXIT=1 set: non-healthy endpoints were reported without failing."
else
ok "Public report API endpoints look healthy."
fi