diff --git a/RUNBOOK.md b/RUNBOOK.md new file mode 100644 index 0000000..dfce03c --- /dev/null +++ b/RUNBOOK.md @@ -0,0 +1,63 @@ +# Explorer + Snap VMID 5000 Runbook + +Short runbook for building, deploying, and verifying the explorer and Chain 138 Snap site on VMID 5000. + +## Prerequisites + +- Access to Proxmox host that runs VMID 5000 (e.g. `pct` or SSH to `PROXMOX_HOST_R630_02`). +- Explorer repo: `explorer-monorepo`. Snap repo: `metamask-integration/chain138-snap`. + +## 1. Build Snap companion site (pathPrefix /snap) + +From **chain138-snap** repo root: + +```bash +GATSBY_PATH_PREFIX=/snap GATSBY_BUILD_SHA=$(git rev-parse --short HEAD) pnpm --filter site run build +``` + +Optional: `GATSBY_SNAP_API_BASE_URL=https://your-api.com` for production market/bridge/swap. + +Output: `packages/site/public/` and `static/version.json`. + +## 2. Deploy Snap site to VMID 5000 + +From **chain138-snap**: + +```bash +./scripts/deploy-snap-site-to-vmid5000.sh --build +# Or: ./scripts/deploy-snap-site-to-vmid5000.sh (existing build) +``` + +Deploys to `/var/www/html/snap/` and saves a rollback tarball. + +## 3. Nginx on VMID 5000 + +**From Proxmox host (one command):** `bash scripts/apply-nginx-snap-vmid5000.sh` (from explorer-monorepo). Or run **inside VMID 5000**: `bash scripts/fix-nginx-serve-custom-frontend.sh`. Configures `/snap`, `/snap/`, `/api/`, explorer root. Then: `nginx -t && systemctl reload nginx`. + +## 4. Verification + +- **Snap only:** `cd metamask-integration/chain138-snap && ./scripts/verify-snap-site-vmid5000.sh [BASE_URL]` +- **Full (explorer + API + Snap):** `cd explorer-monorepo && ./scripts/verify-vmid5000-all.sh [BASE_URL]` + +BASE_URL defaults to https://explorer.d-bis.org. After applying the nginx fix, `/snap` and `/snap/` should return **200** with content. + +## 5. Rollback Snap site + +Inside VM: `tar -xf /var/www/html/snap-rollback.tar -C /var/www/html/snap && chown -R www-data:www-data /var/www/html/snap` + +From host: use `/tmp/snap-site-last.tar` and `pct push` + `pct exec` to extract into `/var/www/html/snap`. + +## 6. Health / version + +- Snap version: https://explorer.d-bis.org/snap/version.json +- Explorer health: https://explorer.d-bis.org/health + +## 7. Troubleshooting + +- **502 on /api/:** Blockscout not on port 4000. See `docs/EXPLORER_API_ACCESS.md` and `scripts/fix-502-blockscout.sh`. +- **Snap 404/blank:** Nginx needs `location /snap/` and `location = /snap`; re-run `fix-nginx-serve-custom-frontend.sh` and redeploy Snap site. +- **Market/Bridge/Swap not loading:** Set `GATSBY_SNAP_API_BASE_URL`, rebuild, redeploy. + +**Monitoring (optional):** Run `verify-vmid5000-all.sh` on a schedule (e.g. cron) or after deploys; alert if the script exits non-zero. Use `/snap/version.json` and `/health` for load balancer or uptime checks. + +See `docs/EXPLORER_API_ACCESS.md` and `metamask-integration/chain138-snap/DEPLOY_VMID5000.md`. diff --git a/docs/EXPLORER_API_ACCESS.md b/docs/EXPLORER_API_ACCESS.md index 182034b..5b31735 100644 --- a/docs/EXPLORER_API_ACCESS.md +++ b/docs/EXPLORER_API_ACCESS.md @@ -2,6 +2,8 @@ The frontend is reachable at **https://explorer.d-bis.org** (FQDN) or by **VM IP** (**http://192.168.11.140**). In both cases it needs the **Blockscout v2 API** at the same origin under `/api/`. If you see **502 Bad Gateway**, **no blocks/transactions feeds**, or "Failed to load", the API may be unreachable. Use this checklist to verify and restore access. +**See also:** [EXPLORER_API_REFERENCE.md](EXPLORER_API_REFERENCE.md) for the list of Blockscout v2 endpoints used by the frontend. + --- ## No feeds (blocks/transactions empty or stuck on “Loading…”) @@ -217,15 +219,25 @@ if ($request_method = OPTIONS) { From the repo (Proxmox host or inside VMID 5000): ```bash -cd /home/intlc/projects/proxmox/explorer-monorepo -bash scripts/verify-explorer-api-access.sh +cd /path/to/explorer-monorepo +bash scripts/verify-explorer-api-access.sh [BASE_URL] ``` The script checks: - Blockscout (or API) responding on port 4000 (when run inside the VM). -- Nginx serving `/api/` (local and, if BASE_URL set, public URL). -- HTTP 200 on `/api/v2/stats` and optionally `/api/v2/blocks`. +- Nginx serving `/api/` and having `location /snap/` (when run inside the VM or with `pct`). +- HTTP 200 on `/api/v2/stats`, `/api/v2/blocks`, `/api/v2/transactions`. +- Explorer frontend at `/` returns 200. +- Chain 138 Snap companion site at `/snap/` returns 200 or 301 and contains expected content when 200. + +**Full verification (single place for all checks — API, explorer, Snap):** + +```bash +bash scripts/verify-vmid5000-all.sh [BASE_URL] +``` + +Run this after every deploy or nginx change to confirm explorer and Snap site are reachable and correct. --- diff --git a/scripts/apply-nginx-snap-vmid5000.sh b/scripts/apply-nginx-snap-vmid5000.sh new file mode 100755 index 0000000..bb0efab --- /dev/null +++ b/scripts/apply-nginx-snap-vmid5000.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Apply nginx config for explorer + /snap/ on VMID 5000 from the Proxmox host (or via SSH). +# Runs fix-nginx-serve-custom-frontend.sh inside the VM so /snap and /snap/ return 200. +# Usage: ./apply-nginx-snap-vmid5000.sh + +set -euo pipefail + +VMID=5000 +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +FIX_SCRIPT="$SCRIPT_DIR/fix-nginx-serve-custom-frontend.sh" +PROXMOX_HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}" + +if [ ! -f "$FIX_SCRIPT" ]; then + echo "❌ Fix script not found: $FIX_SCRIPT" + exit 1 +fi + +echo "==========================================" +echo "Apply nginx (explorer + /snap/) on VMID $VMID" +echo "==========================================" +echo "" + +if [ -f "/proc/1/cgroup" ] && grep -q "lxc" /proc/1/cgroup 2>/dev/null; then + echo "Running inside VMID $VMID – executing fix script directly" + bash "$FIX_SCRIPT" +elif command -v pct &>/dev/null; then + echo "Running from Proxmox host – executing fix script inside VM via pct" + pct exec $VMID -- bash -s < "$FIX_SCRIPT" +else + echo "Running from remote – pushing script and executing via SSH + pct" + TMP_SCRIPT="/tmp/fix-nginx-snap-$$.sh" + scp -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$FIX_SCRIPT" root@"${PROXMOX_HOST}:$TMP_SCRIPT" + ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no root@"${PROXMOX_HOST}" "pct exec $VMID -- bash -s < $TMP_SCRIPT; rm -f $TMP_SCRIPT" +fi + +echo "" +echo "✅ Nginx config applied. /snap and /snap/ should return 200." +echo "Verify: curl -sS -o /dev/null -w '%{http_code}' https://explorer.d-bis.org/snap/" +echo "" diff --git a/scripts/fix-nginx-serve-custom-frontend.sh b/scripts/fix-nginx-serve-custom-frontend.sh index b941eef..af6cd52 100755 --- a/scripts/fix-nginx-serve-custom-frontend.sh +++ b/scripts/fix-nginx-serve-custom-frontend.sh @@ -55,12 +55,25 @@ server { } # Serve custom frontend for root path (no-cache so fixes show after refresh) + # CSP with unsafe-eval required by ethers.js v5 (NPM proxies to port 80) location = / { root /var/www/html; add_header Cache-Control "no-store, no-cache, must-revalidate"; + add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://unpkg.com https://cdnjs.cloudflare.com; style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com; img-src 'self' data: https:; font-src 'self' https://cdnjs.cloudflare.com; connect-src 'self' https://explorer.d-bis.org wss://explorer.d-bis.org https://rpc-http-pub.d-bis.org wss://rpc-ws-pub.d-bis.org http://192.168.11.221:8545 ws://192.168.11.221:8546;" always; try_files /index.html =404; } + location = /favicon.ico { + root /var/www/html; + try_files /favicon.ico =404; + add_header Cache-Control "public, max-age=86400"; + } + location = /apple-touch-icon.png { + root /var/www/html; + try_files /apple-touch-icon.png =404; + add_header Cache-Control "public, max-age=86400"; + } + # Serve static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { root /var/www/html; @@ -105,6 +118,29 @@ server { try_files /index.html =404; } + # Chain 138 MetaMask Snap companion site (SPA at /snap/) + # /snap (no trailing slash) -> internal redirect so client gets 200 with content + location = /snap { + rewrite ^ /snap/ last; + } + location /snap/ { + alias /var/www/html/snap/; + try_files $uri $uri/ /snap/index.html; + add_header Cache-Control "no-store, no-cache, must-revalidate"; + } + + # Icons (exact match to avoid 404s) + location = /favicon.ico { + root /var/www/html; + try_files /favicon.ico =404; + add_header Cache-Control "public, max-age=86400"; + } + location = /apple-touch-icon.png { + root /var/www/html; + try_files /apple-touch-icon.png =404; + add_header Cache-Control "public, max-age=86400"; + } + # Serve static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { root /var/www/html; @@ -146,7 +182,7 @@ server { proxy_connect_timeout 75s; } - # All other paths serve custom frontend + # All other paths serve custom frontend (SPA fallback via try_files) location / { root /var/www/html; try_files $uri $uri/ /index.html; @@ -221,10 +257,10 @@ else echo " ./scripts/deploy-frontend-to-vmid5000.sh" fi -# Test HTTP endpoint +# Test HTTP endpoint (non-fatal: do not exit on curl/grep failure) echo "" echo "Testing HTTP endpoint:" -HTTP_RESPONSE=$(curl -s http://localhost/ 2>/dev/null | head -5) +HTTP_RESPONSE=$(curl -s --max-time 5 http://localhost/ 2>/dev/null | head -5) || true if echo "$HTTP_RESPONSE" | grep -q "SolaceScanScout\|/dev/null || echo 000)" +if [ "$HTTP_CODE" = "200" ]; then + echo "✅ $BASE_URL/ (explorer frontend) returns 200" + ((PASS++)) || true +else + echo "❌ $BASE_URL/ returned $HTTP_CODE (expected 200)" + ((FAIL++)) || true +fi + +# 7) Snap companion site /snap/ returns 200 or 301 (follow redirects for content) +SNAP_OUT="$(curl -sS -L -w '\n%{http_code}' --connect-timeout 10 "$BASE_URL/snap/" 2>/dev/null)" || true +SNAP_BODY="$(echo "$SNAP_OUT" | head -n -1)" +SNAP_CODE="$(echo "$SNAP_OUT" | tail -n 1)" +if [ "$SNAP_CODE" = "200" ] || [ "$SNAP_CODE" = "301" ]; then + echo "✅ $BASE_URL/snap/ (Chain 138 Snap site) returns $SNAP_CODE" + ((PASS++)) || true +else + echo "❌ $BASE_URL/snap/ returned $SNAP_CODE (expected 200 or 301)" + ((FAIL++)) || true +fi + +# 8) /snap/ response contains Snap app content (skip if 301 — redirect may not include body) +if echo "$SNAP_BODY" | head -c 8192 | grep -qE 'Connect|template-snap|Snap|MetaMask'; then + echo "✅ $BASE_URL/snap/ contains Snap app content" + ((PASS++)) || true +elif [ "$SNAP_CODE" = "301" ]; then + echo "⏭ $BASE_URL/snap/ returned 301 (redirect); content check skipped" +else + echo "❌ $BASE_URL/snap/ response missing expected content (Connect|Snap|MetaMask)" + ((FAIL++)) || true +fi + +# 9) Nginx has location /snap/ (when we can run inside VM) +if [ -n "$EXEC_PREFIX" ] || [ "$RUN_LOCAL" = true ]; then + if $EXEC_PREFIX bash -c 'grep -q "location /snap/" /etc/nginx/sites-available/blockscout 2>/dev/null'; then + echo "✅ Nginx has location /snap/" + ((PASS++)) || true + else + echo "❌ Nginx config has no location /snap/" + ((FAIL++)) || true + fi +else + echo "⏭ Skipping nginx /snap/ check (not inside VM and pct not used)" +fi + echo "" echo "==============================================" echo "Result: $PASS passed, $FAIL failed" diff --git a/scripts/verify-vmid5000-all.sh b/scripts/verify-vmid5000-all.sh new file mode 100755 index 0000000..323a319 --- /dev/null +++ b/scripts/verify-vmid5000-all.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Run all VMID 5000 deployment checks: explorer frontend, Blockscout API, Snap site. +# Usage: ./verify-vmid5000-all.sh [BASE_URL] +# BASE_URL defaults to https://explorer.d-bis.org + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BASE_URL="${1:-https://explorer.d-bis.org}" + +echo "==============================================" +echo "VMID 5000 – full verification" +echo "BASE_URL=$BASE_URL" +echo "==============================================" +echo "" + +bash "$SCRIPT_DIR/verify-explorer-api-access.sh" "$BASE_URL"