refactor: rename SolaceScanScout to Solace and update related configurations
- Updated branding from "SolaceScanScout" to "Solace" across various files including deployment scripts, API responses, and documentation. - Changed default base URL for Playwright tests and updated security headers to reflect the new branding. - Enhanced README and API documentation to include new authentication endpoints and product access details. This refactor aligns the project branding and improves clarity in the API documentation.
This commit is contained in:
@@ -6,7 +6,7 @@ set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
echo "=== SolaceScanScout Tiered Architecture - Deployment & Testing ==="
|
||||
echo "=== SolaceScan Tiered Architecture - Deployment & Testing ==="
|
||||
echo ""
|
||||
|
||||
# Colors
|
||||
|
||||
@@ -12,6 +12,8 @@ JWT_SECRET_VALUE="${JWT_SECRET_VALUE:-}"
|
||||
EXPLORER_AI_MODEL_VALUE="${EXPLORER_AI_MODEL_VALUE:-grok-3}"
|
||||
EXPLORER_DATABASE_URL_VALUE="${EXPLORER_DATABASE_URL_VALUE:-}"
|
||||
SECURE_AI_ENV_FILE="${SECURE_AI_ENV_FILE:-$HOME/.secure-secrets/explorer-ai.env}"
|
||||
ACCESS_ADMIN_EMAILS_VALUE="${ACCESS_ADMIN_EMAILS:-}"
|
||||
ACCESS_INTERNAL_SECRET_VALUE="${ACCESS_INTERNAL_SECRET:-}"
|
||||
|
||||
if [ -f "$SECURE_AI_ENV_FILE" ]; then
|
||||
set -a
|
||||
@@ -55,14 +57,19 @@ echo "=== Step 4: Install backend, refresh docs, and ensure env ==="
|
||||
if [ -z "$JWT_SECRET_VALUE" ]; then
|
||||
JWT_SECRET_VALUE="$(openssl rand -hex 32)"
|
||||
fi
|
||||
if [ -z "$ACCESS_INTERNAL_SECRET_VALUE" ]; then
|
||||
ACCESS_INTERNAL_SECRET_VALUE="$(openssl rand -hex 32)"
|
||||
fi
|
||||
|
||||
export JWT_SECRET_VALUE
|
||||
export EXPLORER_AI_MODEL_VALUE
|
||||
export XAI_API_KEY_VALUE="${XAI_API_KEY:-}"
|
||||
export EXPLORER_DATABASE_URL_VALUE
|
||||
export ACCESS_ADMIN_EMAILS_VALUE
|
||||
export ACCESS_INTERNAL_SECRET_VALUE
|
||||
|
||||
ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no root@"$PROXMOX_HOST" \
|
||||
"JWT_SECRET_VALUE='$JWT_SECRET_VALUE' EXPLORER_AI_MODEL_VALUE='$EXPLORER_AI_MODEL_VALUE' XAI_API_KEY_VALUE='$XAI_API_KEY_VALUE' EXPLORER_DATABASE_URL_VALUE='$EXPLORER_DATABASE_URL_VALUE' bash -s" <<'REMOTE'
|
||||
"JWT_SECRET_VALUE='$JWT_SECRET_VALUE' EXPLORER_AI_MODEL_VALUE='$EXPLORER_AI_MODEL_VALUE' XAI_API_KEY_VALUE='$XAI_API_KEY_VALUE' EXPLORER_DATABASE_URL_VALUE='$EXPLORER_DATABASE_URL_VALUE' ACCESS_ADMIN_EMAILS_VALUE='$ACCESS_ADMIN_EMAILS_VALUE' ACCESS_INTERNAL_SECRET_VALUE='$ACCESS_INTERNAL_SECRET_VALUE' bash -s" <<'REMOTE'
|
||||
set -euo pipefail
|
||||
|
||||
VMID=5000
|
||||
@@ -121,8 +128,18 @@ EOF
|
||||
cat > /etc/systemd/system/explorer-config-api.service.d/security.conf <<EOF
|
||||
[Service]
|
||||
Environment=JWT_SECRET='"$JWT_SECRET_VALUE"'
|
||||
Environment=ACCESS_INTERNAL_SECRET='"$ACCESS_INTERNAL_SECRET_VALUE"'
|
||||
EOF
|
||||
|
||||
if [ -n "$ACCESS_ADMIN_EMAILS_VALUE" ]; then
|
||||
cat > /etc/systemd/system/explorer-config-api.service.d/access.conf <<EOF
|
||||
[Service]
|
||||
Environment=ACCESS_ADMIN_EMAILS='"$ACCESS_ADMIN_EMAILS_VALUE"'
|
||||
EOF
|
||||
else
|
||||
rm -f /etc/systemd/system/explorer-config-api.service.d/access.conf
|
||||
fi
|
||||
|
||||
if [ -n "$DB_URL" ]; then
|
||||
cat > /etc/systemd/system/explorer-config-api.service.d/database.conf <<EOF
|
||||
[Service]
|
||||
|
||||
@@ -240,7 +240,7 @@ if run_in_vm "test -f /var/www/html/index.html"; then
|
||||
echo "✅ Frontend file exists"
|
||||
|
||||
# Check if it contains expected content
|
||||
if run_in_vm "grep -q SolaceScanScout /var/www/html/index.html"; then
|
||||
if run_in_vm "grep -qiE 'SolaceScan|Chain 138 Explorer by DBIS' /var/www/html/index.html"; then
|
||||
echo "✅ Frontend content verified"
|
||||
else
|
||||
echo "⚠️ Frontend file exists but content may be incorrect"
|
||||
@@ -252,7 +252,7 @@ fi
|
||||
|
||||
# Test HTTP endpoint (non-fatal: do not exit on failure)
|
||||
HTTP_RESPONSE=$(run_in_vm "curl -s --max-time 5 http://localhost/ 2>/dev/null | head -5" 2>/dev/null) || true
|
||||
if echo "$HTTP_RESPONSE" | grep -q "SolaceScanScout\|<!DOCTYPE html"; then
|
||||
if echo "$HTTP_RESPONSE" | grep -qiE "SolaceScan|Chain 138 Explorer by DBIS|<!DOCTYPE html"; then
|
||||
echo "✅ Frontend is accessible via nginx"
|
||||
else
|
||||
echo "⚠️ Frontend may not be accessible (check nginx config)"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Deploy the current Next.js standalone frontend to VMID 5000.
|
||||
# This is the canonical deployment path for the current SolaceScanScout frontend.
|
||||
# This is the canonical deployment path for the current SolaceScan frontend.
|
||||
# It builds the local frontend, uploads the standalone bundle, installs a systemd
|
||||
# service, and starts the Node server on 127.0.0.1:3000 inside the container.
|
||||
|
||||
@@ -22,6 +22,17 @@ VERIFY_SCRIPT="${WORKSPACE_ROOT}/scripts/verify/check-explorer-e2e.sh"
|
||||
RELEASE_ID="$(date +%Y%m%d_%H%M%S)"
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
ARCHIVE_NAME="solacescanscout-next-${RELEASE_ID}.tar"
|
||||
STATIC_SYNC_FILES=(
|
||||
"index.html"
|
||||
"docs.html"
|
||||
"privacy.html"
|
||||
"terms.html"
|
||||
"acknowledgments.html"
|
||||
"chain138-command-center.html"
|
||||
"favicon.ico"
|
||||
"apple-touch-icon.png"
|
||||
"explorer-spa.js"
|
||||
)
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$TMP_DIR"
|
||||
@@ -64,7 +75,7 @@ run_in_vmid() {
|
||||
}
|
||||
|
||||
echo "=========================================="
|
||||
echo "Deploying Next SolaceScanScout Frontend"
|
||||
echo "Deploying Next SolaceScan Frontend"
|
||||
echo "=========================================="
|
||||
echo "VMID: $VMID"
|
||||
echo "Frontend root: $FRONTEND_ROOT"
|
||||
@@ -113,14 +124,34 @@ systemctl daemon-reload
|
||||
systemctl enable "\${SERVICE_NAME}.service" >/dev/null
|
||||
systemctl restart "\${SERVICE_NAME}.service"
|
||||
|
||||
for attempt in \$(seq 1 20); do
|
||||
for attempt in \$(seq 1 45); do
|
||||
if curl -fsS --max-time 5 "http://127.0.0.1:\${FRONTEND_PORT}/" > /tmp/\${SERVICE_NAME}-health.out; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
grep -qi "SolaceScanScout" /tmp/\${SERVICE_NAME}-health.out
|
||||
if ! grep -qiE "SolaceScan|Chain 138 Explorer by DBIS" /tmp/\${SERVICE_NAME}-health.out; then
|
||||
systemctl status "\${SERVICE_NAME}.service" --no-pager || true
|
||||
journalctl -u "\${SERVICE_NAME}.service" -n 50 --no-pager || true
|
||||
echo "Frontend health check did not find the expected SolaceScan marker." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p /var/www/html
|
||||
for relpath in ${STATIC_SYNC_FILES[*]}; do
|
||||
if [[ -f "\${RELEASE_DIR}/public/\${relpath}" ]]; then
|
||||
install -D -m 0644 "\${RELEASE_DIR}/public/\${relpath}" "/var/www/html/\${relpath}"
|
||||
fi
|
||||
done
|
||||
if [[ -d "\${RELEASE_DIR}/public/thirdparty" ]]; then
|
||||
mkdir -p /var/www/html/thirdparty
|
||||
cp -a "\${RELEASE_DIR}/public/thirdparty/." /var/www/html/thirdparty/
|
||||
fi
|
||||
if [[ -d "\${RELEASE_DIR}/public/config" ]]; then
|
||||
mkdir -p /var/www/html/config
|
||||
cp -a "\${RELEASE_DIR}/public/config/." /var/www/html/config/
|
||||
fi
|
||||
EOF
|
||||
chmod +x "${TMP_DIR}/install-next-frontend.sh"
|
||||
|
||||
@@ -136,7 +167,7 @@ echo ""
|
||||
|
||||
echo "== Verification =="
|
||||
run_in_vmid "systemctl is-active ${SERVICE_NAME}.service"
|
||||
run_in_vmid "curl -fsS --max-time 5 http://127.0.0.1:${FRONTEND_PORT}/ | grep -qi SolaceScanScout"
|
||||
run_in_vmid "curl -fsS --max-time 5 http://127.0.0.1:${FRONTEND_PORT}/ | grep -qiE 'SolaceScan|Chain 138 Explorer by DBIS'"
|
||||
echo "Service ${SERVICE_NAME} is running on 127.0.0.1:${FRONTEND_PORT}"
|
||||
echo ""
|
||||
echo "Nginx follow-up:"
|
||||
@@ -145,7 +176,7 @@ echo " while preserving /api/, /api/config/*, /explorer-api/v1/, /token-aggrega
|
||||
echo " Snippet: ${NGINX_SNIPPET}"
|
||||
if [[ -f "${VERIFY_SCRIPT}" ]]; then
|
||||
echo " After nginx/NPMplus cutover, verify with:"
|
||||
echo " bash ${VERIFY_SCRIPT} https://explorer.d-bis.org"
|
||||
echo " bash ${VERIFY_SCRIPT} https://blockscout.defi-oracle.io"
|
||||
fi
|
||||
echo ""
|
||||
echo "Next frontend deployment complete."
|
||||
|
||||
@@ -6,7 +6,7 @@ set -e
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
echo "=== SolaceScanScout Tiered Architecture Deployment ==="
|
||||
echo "=== SolaceScan Tiered Architecture Deployment ==="
|
||||
echo ""
|
||||
|
||||
# Step 1: Verify prerequisites
|
||||
@@ -171,4 +171,3 @@ echo "To stop the server:"
|
||||
echo " kill $SERVER_PID"
|
||||
echo " or: pkill -f api-server"
|
||||
echo ""
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ function collectUnexpectedConsoleErrors(page: Page, allowlist: RegExp[] = []) {
|
||||
|
||||
test.describe('Explorer Frontend - Route Coverage', () => {
|
||||
for (const route of [
|
||||
{ path: '/', heading: /SolaceScanScout/i },
|
||||
{ path: '/', heading: /SolaceScan/i },
|
||||
{ path: '/blocks', heading: /^Blocks$/i },
|
||||
{ path: '/transactions', heading: /^Transactions$/i },
|
||||
{ path: '/addresses', heading: /^Addresses$/i },
|
||||
|
||||
@@ -141,7 +141,7 @@ echo "=== 2. Frontend Content Tests ==="
|
||||
CONTENT_URL="$BASE_URL:80"
|
||||
|
||||
# Test homepage content
|
||||
test_content "$CONTENT_URL" "SolaceScanScout" "Homepage contains SolaceScanScout title"
|
||||
test_content "$CONTENT_URL" "SolaceScan" "Homepage contains SolaceScan title"
|
||||
|
||||
# Test explorer branding
|
||||
test_content "$CONTENT_URL" "Explorer" "Homepage contains explorer branding"
|
||||
@@ -347,12 +347,12 @@ PATH_CURL_EXTRA=""
|
||||
|
||||
# SPA serves index.html for all paths - verify path-based routing is present
|
||||
test_content "$PATH_TEST_BASE/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506" "fromPath" "Path-based routing code present (address URL)" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506" "SolaceScanScout" "Address path serves SPA shell" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/blocks" "SolaceScanScout" "Blocks path serves SPA" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/transactions" "SolaceScanScout" "Transactions path serves SPA" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/bridge" "SolaceScanScout" "Bridge path serves SPA" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/weth" "SolaceScanScout" "WETH path serves SPA" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/watchlist" "SolaceScanScout" "Watchlist path serves SPA" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506" "SolaceScan" "Address path serves SPA shell" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/blocks" "SolaceScan" "Blocks path serves SPA" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/transactions" "SolaceScan" "Transactions path serves SPA" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/bridge" "SolaceScan" "Bridge path serves SPA" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/weth" "SolaceScan" "WETH path serves SPA" "$PATH_CURL_EXTRA"
|
||||
test_content "$PATH_TEST_BASE/watchlist" "SolaceScan" "Watchlist path serves SPA" "$PATH_CURL_EXTRA"
|
||||
|
||||
# Verify nav links exist in HTML (use same base)
|
||||
test_content "$PATH_TEST_BASE/" "#/home" "Home nav link present" "$PATH_CURL_EXTRA"
|
||||
|
||||
@@ -120,7 +120,8 @@ server {
|
||||
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;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
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://blockscout.defi-oracle.io wss://blockscout.defi-oracle.io 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;
|
||||
}
|
||||
|
||||
@@ -155,6 +156,7 @@ server {
|
||||
root /var/www/html;
|
||||
try_files /index.html =404;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
}
|
||||
|
||||
# All other requests: redirect to HTTPS only when not already behind HTTPS proxy
|
||||
@@ -163,6 +165,7 @@ server {
|
||||
root /var/www/html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,7 +388,7 @@ sleep 2
|
||||
if [ -f /var/www/html/index.html ]; then
|
||||
echo "✅ Custom frontend file exists"
|
||||
|
||||
if grep -q "SolaceScanScout" /var/www/html/index.html; then
|
||||
if grep -qiE "SolaceScan|Chain 138 Explorer by DBIS" /var/www/html/index.html; then
|
||||
echo "✅ Custom frontend content verified"
|
||||
else
|
||||
echo "⚠️ Frontend file exists but may not be the custom one"
|
||||
@@ -402,7 +405,7 @@ fi
|
||||
echo ""
|
||||
echo "Testing HTTP endpoint:"
|
||||
HTTP_RESPONSE=$(curl -s --max-time 5 http://localhost/ 2>/dev/null | head -5) || true
|
||||
if echo "$HTTP_RESPONSE" | grep -q "SolaceScanScout\|<!DOCTYPE html"; then
|
||||
if echo "$HTTP_RESPONSE" | grep -qiE "SolaceScan|Chain 138 Explorer by DBIS|<!DOCTYPE html"; then
|
||||
echo "✅ Custom frontend is accessible via HTTP"
|
||||
else
|
||||
echo "⚠️ Frontend may not be accessible (check if file exists)"
|
||||
|
||||
159
scripts/install-rpc-access-gate-nginx-via-ssh.sh
Executable file
159
scripts/install-rpc-access-gate-nginx-via-ssh.sh
Executable file
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
RENDER_SCRIPT="$SCRIPT_DIR/render-rpc-access-gate-nginx.sh"
|
||||
|
||||
PRODUCT_SLUG=""
|
||||
SERVER_NAME=""
|
||||
SSH_HOST=""
|
||||
REMOTE_PATH=""
|
||||
REMOTE_TEST_CMD="nginx -t"
|
||||
REMOTE_RELOAD_CMD="systemctl reload nginx"
|
||||
INTERNAL_SECRET="${ACCESS_INTERNAL_SECRET:-}"
|
||||
VALIDATOR_URL="http://127.0.0.1:8081/api/v1/access/internal/validate-key"
|
||||
UPSTREAM_URL=""
|
||||
APPLY=0
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Safely render and install an explorer-managed RPC edge gate onto a remote nginx host.
|
||||
|
||||
Default mode is plan-only. Use --apply to copy the rendered config, run nginx -t,
|
||||
and reload nginx over SSH.
|
||||
|
||||
Usage:
|
||||
bash explorer-monorepo/scripts/install-rpc-access-gate-nginx-via-ssh.sh \
|
||||
--product thirdweb-rpc \
|
||||
--server-name thirdweb-rpc.example.org \
|
||||
--ssh-host root@192.168.11.217 \
|
||||
--internal-secret "$ACCESS_INTERNAL_SECRET" \
|
||||
[--remote-path /etc/nginx/conf.d/thirdweb-rpc-gated.conf] \
|
||||
[--validator-url http://127.0.0.1:8081/api/v1/access/internal/validate-key] \
|
||||
[--upstream http://192.168.11.217:8545] \
|
||||
[--apply]
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--product)
|
||||
PRODUCT_SLUG="$2"
|
||||
shift 2
|
||||
;;
|
||||
--server-name)
|
||||
SERVER_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--ssh-host)
|
||||
SSH_HOST="$2"
|
||||
shift 2
|
||||
;;
|
||||
--remote-path)
|
||||
REMOTE_PATH="$2"
|
||||
shift 2
|
||||
;;
|
||||
--internal-secret)
|
||||
INTERNAL_SECRET="$2"
|
||||
shift 2
|
||||
;;
|
||||
--validator-url)
|
||||
VALIDATOR_URL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--upstream)
|
||||
UPSTREAM_URL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--remote-test-cmd)
|
||||
REMOTE_TEST_CMD="$2"
|
||||
shift 2
|
||||
;;
|
||||
--remote-reload-cmd)
|
||||
REMOTE_RELOAD_CMD="$2"
|
||||
shift 2
|
||||
;;
|
||||
--apply)
|
||||
APPLY=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$PRODUCT_SLUG" || -z "$SERVER_NAME" || -z "$SSH_HOST" ]]; then
|
||||
echo "ERROR: --product, --server-name, and --ssh-host are required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$INTERNAL_SECRET" ]]; then
|
||||
echo "ERROR: --internal-secret is required. Set ACCESS_INTERNAL_SECRET or pass --internal-secret." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
REMOTE_PATH="${REMOTE_PATH:-/etc/nginx/conf.d/${PRODUCT_SLUG}-gated.conf}"
|
||||
TMP_RENDER="$(mktemp)"
|
||||
trap 'rm -f "$TMP_RENDER"' EXIT
|
||||
|
||||
render_args=(
|
||||
--product "$PRODUCT_SLUG"
|
||||
--server-name "$SERVER_NAME"
|
||||
--internal-secret "$INTERNAL_SECRET"
|
||||
--validator-url "$VALIDATOR_URL"
|
||||
--output "$TMP_RENDER"
|
||||
)
|
||||
|
||||
if [[ -n "$UPSTREAM_URL" ]]; then
|
||||
render_args+=(--upstream "$UPSTREAM_URL")
|
||||
fi
|
||||
|
||||
bash "$RENDER_SCRIPT" "${render_args[@]}" >/dev/null
|
||||
|
||||
echo "== RPC access gate installer =="
|
||||
echo "Product: $PRODUCT_SLUG"
|
||||
echo "Server name: $SERVER_NAME"
|
||||
echo "SSH host: $SSH_HOST"
|
||||
echo "Remote path: $REMOTE_PATH"
|
||||
echo "Validator: $VALIDATOR_URL"
|
||||
if [[ -n "$UPSTREAM_URL" ]]; then
|
||||
echo "Upstream: $UPSTREAM_URL"
|
||||
fi
|
||||
echo
|
||||
echo "-- Rendered config preview --"
|
||||
sed -n '1,220p' "$TMP_RENDER"
|
||||
echo
|
||||
|
||||
if [[ "$APPLY" -ne 1 ]]; then
|
||||
cat <<EOF
|
||||
Plan only. No remote changes were made.
|
||||
|
||||
To apply:
|
||||
bash explorer-monorepo/scripts/install-rpc-access-gate-nginx-via-ssh.sh \\
|
||||
--product "$PRODUCT_SLUG" \\
|
||||
--server-name "$SERVER_NAME" \\
|
||||
--ssh-host "$SSH_HOST" \\
|
||||
--internal-secret '***' \\
|
||||
--apply
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Copying rendered config to $SSH_HOST:$REMOTE_PATH ..."
|
||||
scp "$TMP_RENDER" "$SSH_HOST:$REMOTE_PATH"
|
||||
|
||||
echo "Testing nginx config on $SSH_HOST ..."
|
||||
ssh "$SSH_HOST" "$REMOTE_TEST_CMD"
|
||||
|
||||
echo "Reloading nginx on $SSH_HOST ..."
|
||||
ssh "$SSH_HOST" "$REMOTE_RELOAD_CMD"
|
||||
|
||||
echo "Install complete."
|
||||
163
scripts/render-rpc-access-gate-nginx.sh
Executable file
163
scripts/render-rpc-access-gate-nginx.sh
Executable file
@@ -0,0 +1,163 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PRODUCT_SLUG=""
|
||||
SERVER_NAME=""
|
||||
OUTPUT_PATH=""
|
||||
INTERNAL_SECRET="${ACCESS_INTERNAL_SECRET:-}"
|
||||
VALIDATOR_URL="http://127.0.0.1:8081/api/v1/access/internal/validate-key"
|
||||
UPSTREAM_URL=""
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Render a lane-specific nginx auth_request gate for explorer-managed RPC access.
|
||||
|
||||
Usage:
|
||||
bash explorer-monorepo/scripts/render-rpc-access-gate-nginx.sh \
|
||||
--product thirdweb-rpc \
|
||||
--server-name thirdweb-rpc.example.org \
|
||||
--internal-secret "$ACCESS_INTERNAL_SECRET" \
|
||||
[--output /etc/nginx/conf.d/thirdweb-rpc-gated.conf] \
|
||||
[--validator-url http://127.0.0.1:8081/api/v1/access/internal/validate-key] \
|
||||
[--upstream http://192.168.11.217:8545]
|
||||
|
||||
Supported products:
|
||||
- core-rpc
|
||||
- alltra-rpc
|
||||
- thirdweb-rpc
|
||||
|
||||
Notes:
|
||||
- --server-name is required because public/internal hostnames vary by deployment.
|
||||
- --internal-secret is required so nginx can authenticate to the explorer validator.
|
||||
- --output writes the rendered config to disk; otherwise the config is printed to stdout.
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--product)
|
||||
PRODUCT_SLUG="$2"
|
||||
shift 2
|
||||
;;
|
||||
--server-name)
|
||||
SERVER_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--output)
|
||||
OUTPUT_PATH="$2"
|
||||
shift 2
|
||||
;;
|
||||
--internal-secret)
|
||||
INTERNAL_SECRET="$2"
|
||||
shift 2
|
||||
;;
|
||||
--validator-url)
|
||||
VALIDATOR_URL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--upstream)
|
||||
UPSTREAM_URL="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
usage >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$PRODUCT_SLUG" ]]; then
|
||||
echo "ERROR: --product is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$SERVER_NAME" ]]; then
|
||||
echo "ERROR: --server-name is required." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "$INTERNAL_SECRET" ]]; then
|
||||
echo "ERROR: --internal-secret is required. Set ACCESS_INTERNAL_SECRET or pass --internal-secret." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$PRODUCT_SLUG" in
|
||||
core-rpc)
|
||||
DEFAULT_UPSTREAM_URL="http://192.168.11.211:8545"
|
||||
PRODUCT_COMMENT="Private Chain 138 Core RPC lane with approval-oriented access controls."
|
||||
;;
|
||||
alltra-rpc)
|
||||
DEFAULT_UPSTREAM_URL="http://192.168.11.212:8545"
|
||||
PRODUCT_COMMENT="Alltra-managed RPC lane for partner and subscription traffic."
|
||||
;;
|
||||
thirdweb-rpc)
|
||||
DEFAULT_UPSTREAM_URL="http://192.168.11.217:8545"
|
||||
PRODUCT_COMMENT="Thirdweb-managed RPC lane for SaaS and metered API-key traffic."
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unsupported product slug '$PRODUCT_SLUG'." >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
UPSTREAM_URL="${UPSTREAM_URL:-$DEFAULT_UPSTREAM_URL}"
|
||||
|
||||
rendered_config="$(
|
||||
cat <<EOF
|
||||
# Rendered by scripts/render-rpc-access-gate-nginx.sh
|
||||
# Product: ${PRODUCT_SLUG}
|
||||
# ${PRODUCT_COMMENT}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name ${SERVER_NAME};
|
||||
|
||||
location = /__access_validate_rpc {
|
||||
internal;
|
||||
proxy_pass ${VALIDATOR_URL};
|
||||
proxy_pass_request_body off;
|
||||
proxy_set_header Content-Length "";
|
||||
proxy_set_header X-Access-Internal-Secret "${INTERNAL_SECRET}";
|
||||
proxy_set_header X-API-Key \$http_x_api_key;
|
||||
proxy_set_header Authorization \$http_authorization;
|
||||
proxy_set_header X-Access-Method \$request_method;
|
||||
proxy_set_header X-Access-Request-Count "1";
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
location / {
|
||||
auth_request /__access_validate_rpc;
|
||||
auth_request_set \$validated_product \$upstream_http_x_validated_product;
|
||||
auth_request_set \$validated_tier \$upstream_http_x_validated_tier;
|
||||
auth_request_set \$validated_scopes \$upstream_http_x_validated_scopes;
|
||||
auth_request_set \$quota_remaining \$upstream_http_x_quota_remaining;
|
||||
|
||||
proxy_pass ${UPSTREAM_URL};
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host \$host;
|
||||
proxy_set_header X-Real-IP \$remote_addr;
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||
proxy_set_header X-Validated-Product \$validated_product;
|
||||
proxy_set_header X-Validated-Tier \$validated_tier;
|
||||
proxy_set_header X-Validated-Scopes \$validated_scopes;
|
||||
proxy_set_header X-Quota-Remaining \$quota_remaining;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)"
|
||||
|
||||
if [[ -n "$OUTPUT_PATH" ]]; then
|
||||
mkdir -p "$(dirname "$OUTPUT_PATH")"
|
||||
printf '%s\n' "$rendered_config" > "$OUTPUT_PATH"
|
||||
echo "Wrote rendered nginx gate config to: $OUTPUT_PATH"
|
||||
else
|
||||
printf '%s\n' "$rendered_config"
|
||||
fi
|
||||
@@ -6,7 +6,7 @@ set -e
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
echo "=== SolaceScanScout Tiered Architecture Setup ==="
|
||||
echo "=== SolaceScan Tiered Architecture Setup ==="
|
||||
echo ""
|
||||
|
||||
# Step 1: Run database migration
|
||||
@@ -86,4 +86,3 @@ echo "6. Start Track 2 indexers to populate indexed data"
|
||||
echo ""
|
||||
echo "For more information, see: docs/TIERED_ARCHITECTURE_IMPLEMENTATION.md"
|
||||
echo ""
|
||||
|
||||
|
||||
85
scripts/verify-explorer-access-edge-hook.sh
Normal file
85
scripts/verify-explorer-access-edge-hook.sh
Normal file
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
BASE_URL="https://explorer.d-bis.org"
|
||||
INTERNAL_SECRET="${ACCESS_INTERNAL_SECRET:-}"
|
||||
API_KEY="${ACCESS_TEST_API_KEY:-}"
|
||||
METHOD_NAME="eth_chainId"
|
||||
REQUEST_COUNT="1"
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--base-url)
|
||||
BASE_URL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--internal-secret)
|
||||
INTERNAL_SECRET="$2"
|
||||
shift 2
|
||||
;;
|
||||
--api-key)
|
||||
API_KEY="$2"
|
||||
shift 2
|
||||
;;
|
||||
--method)
|
||||
METHOD_NAME="$2"
|
||||
shift 2
|
||||
;;
|
||||
--request-count)
|
||||
REQUEST_COUNT="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
VALIDATE_URL="${BASE_URL%/}/explorer-api/v1/access/internal/validate-key"
|
||||
|
||||
echo "== Explorer access edge-hook verification =="
|
||||
echo "Validator: $VALIDATE_URL"
|
||||
|
||||
if [[ -z "$INTERNAL_SECRET" ]]; then
|
||||
echo "ERROR: internal secret is required. Set ACCESS_INTERNAL_SECRET or pass --internal-secret." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
headers_file="$(mktemp)"
|
||||
body_file="$(mktemp)"
|
||||
trap 'rm -f "$headers_file" "$body_file"' EXIT
|
||||
|
||||
curl_args=(
|
||||
-sS
|
||||
-D "$headers_file"
|
||||
-o "$body_file"
|
||||
-w "%{http_code}"
|
||||
-X GET
|
||||
-H "X-Access-Internal-Secret: $INTERNAL_SECRET"
|
||||
-H "X-Access-Method: $METHOD_NAME"
|
||||
-H "X-Access-Request-Count: $REQUEST_COUNT"
|
||||
)
|
||||
|
||||
if [[ -n "$API_KEY" ]]; then
|
||||
curl_args+=(-H "X-API-Key: $API_KEY")
|
||||
fi
|
||||
|
||||
status="$(curl "${curl_args[@]}" "$VALIDATE_URL")"
|
||||
|
||||
echo "HTTP status: $status"
|
||||
|
||||
if [[ "$status" == "200" ]]; then
|
||||
echo "Validation accepted."
|
||||
echo "Returned headers:"
|
||||
grep -Ei '^(x-validated-|x-quota-remaining:)' "$headers_file" || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Validation rejected."
|
||||
if [[ -s "$body_file" ]]; then
|
||||
echo "Response body:"
|
||||
cat "$body_file"
|
||||
fi
|
||||
exit 1
|
||||
@@ -16,3 +16,14 @@ echo "=============================================="
|
||||
echo ""
|
||||
|
||||
bash "$SCRIPT_DIR/verify-explorer-api-access.sh" "$BASE_URL"
|
||||
|
||||
if [[ -n "${ACCESS_INTERNAL_SECRET:-}" ]]; then
|
||||
echo ""
|
||||
echo "Running access edge-hook verification with ACCESS_INTERNAL_SECRET..."
|
||||
bash "$SCRIPT_DIR/verify-explorer-access-edge-hook.sh" \
|
||||
--base-url "$BASE_URL" \
|
||||
--internal-secret "$ACCESS_INTERNAL_SECRET"
|
||||
else
|
||||
echo ""
|
||||
echo "Skipping access edge-hook verification because ACCESS_INTERNAL_SECRET is not set."
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user