- 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.
424 lines
16 KiB
Bash
Executable File
424 lines
16 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# Fix nginx to serve custom frontend from /var/www/html/
|
|
# Run this in VMID 5000
|
|
|
|
set -euo pipefail
|
|
|
|
CONFIG_FILE="/etc/nginx/sites-available/blockscout"
|
|
|
|
echo "=========================================="
|
|
echo "Updating Nginx to Serve Custom Frontend"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
# Step 1: Backup current config
|
|
echo "=== Step 1: Backing up nginx config ==="
|
|
cp "$CONFIG_FILE" "${CONFIG_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
|
|
echo "✅ Backup created"
|
|
echo ""
|
|
|
|
# Step 2: Create new config that serves custom frontend
|
|
echo "=== Step 2: Creating new nginx configuration ==="
|
|
cat > "$CONFIG_FILE" << 'NGINX_EOF'
|
|
# HTTP server
|
|
server {
|
|
listen 80;
|
|
listen [::]:80;
|
|
server_name explorer.d-bis.org 192.168.11.140;
|
|
|
|
location /.well-known/acme-challenge/ {
|
|
root /var/www/html;
|
|
try_files $uri =404;
|
|
}
|
|
|
|
# Explorer backend API (auth, features, AI, explorer-owned v1 helpers)
|
|
location /explorer-api/v1/ {
|
|
proxy_pass http://127.0.0.1:8081/api/v1/;
|
|
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_read_timeout 60s;
|
|
add_header Access-Control-Allow-Origin *;
|
|
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
|
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
|
}
|
|
|
|
# Blockscout API endpoint - MUST come before the redirect location
|
|
location /api/ {
|
|
proxy_pass http://127.0.0.1:4000;
|
|
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_read_timeout 300s;
|
|
proxy_connect_timeout 75s;
|
|
add_header Access-Control-Allow-Origin *;
|
|
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
|
add_header Access-Control-Allow-Headers "Content-Type";
|
|
}
|
|
|
|
# Token-aggregation API for live route-tree, quotes, and market data
|
|
location /token-aggregation/api/v1/ {
|
|
proxy_pass http://127.0.0.1:3001/api/v1/;
|
|
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_read_timeout 60s;
|
|
add_header Access-Control-Allow-Origin *;
|
|
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
|
add_header Access-Control-Allow-Headers "Content-Type";
|
|
}
|
|
|
|
# Explorer config API (token list, networks, capabilities) - serve from /var/www/html/config/
|
|
location = /api/config/token-list {
|
|
default_type application/json;
|
|
add_header Access-Control-Allow-Origin *;
|
|
add_header Cache-Control "public, max-age=3600";
|
|
alias /var/www/html/config/DUAL_CHAIN_TOKEN_LIST.tokenlist.json;
|
|
}
|
|
location = /api/config/networks {
|
|
default_type application/json;
|
|
add_header Access-Control-Allow-Origin *;
|
|
add_header Cache-Control "public, max-age=3600";
|
|
alias /var/www/html/config/DUAL_CHAIN_NETWORKS.json;
|
|
}
|
|
location = /api/config/capabilities {
|
|
default_type application/json;
|
|
add_header Access-Control-Allow-Origin *;
|
|
add_header Cache-Control "public, max-age=3600";
|
|
alias /var/www/html/config/CHAIN138_RPC_CAPABILITIES.json;
|
|
}
|
|
|
|
location /health {
|
|
access_log off;
|
|
proxy_pass http://127.0.0.1:4000/api/v2/status;
|
|
proxy_set_header Host $host;
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
# When NPMplus forwards HTTPS to this port as HTTP, do NOT redirect to HTTPS (avoids ERR_TOO_MANY_REDIRECTS)
|
|
set $redirect_http_to_https 1;
|
|
if ($http_x_forwarded_proto = "https") { set $redirect_http_to_https 0; }
|
|
if ($http_x_forwarded_proto = "HTTPS") { set $redirect_http_to_https 0; }
|
|
|
|
# Snap companion (must be before catch-all so /snap/ is served from disk)
|
|
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";
|
|
}
|
|
|
|
# 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 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;
|
|
}
|
|
|
|
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;
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
|
|
# Static JSON under /config/ (topology graph, optional operator verify snapshot)
|
|
location /config/ {
|
|
alias /var/www/html/config/;
|
|
add_header Access-Control-Allow-Origin *;
|
|
default_type application/json;
|
|
add_header Cache-Control "public, max-age=300";
|
|
}
|
|
|
|
# SPA paths on HTTP (for internal/LAN tests) - serve index.html before redirect
|
|
location ~ ^/(address|tx|block|token|tokens|blocks|transactions|bridge|weth|liquidity|watchlist|nft|home|analytics|operator|system|routes|pools|more)(/|$) {
|
|
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
|
|
location / {
|
|
if ($redirect_http_to_https = 1) { return 301 https://$host$request_uri; }
|
|
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;
|
|
}
|
|
}
|
|
|
|
# HTTPS server - Blockscout Explorer
|
|
server {
|
|
listen 443 ssl http2;
|
|
listen [::]:443 ssl http2;
|
|
server_name explorer.d-bis.org 192.168.11.140;
|
|
|
|
# SSL configuration
|
|
ssl_certificate /etc/letsencrypt/live/explorer.d-bis.org/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/explorer.d-bis.org/privkey.pem;
|
|
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
|
|
ssl_prefer_server_ciphers off;
|
|
ssl_session_cache shared:SSL:10m;
|
|
ssl_session_timeout 10m;
|
|
|
|
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
add_header X-Content-Type-Options "nosniff" always;
|
|
add_header X-XSS-Protection "1; mode=block" always;
|
|
|
|
access_log /var/log/nginx/blockscout-access.log;
|
|
error_log /var/log/nginx/blockscout-error.log;
|
|
|
|
# Serve custom frontend for root path (no-cache so fixes show after refresh)
|
|
location = / {
|
|
root /var/www/html;
|
|
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
|
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;
|
|
expires 1y;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
|
|
# Explorer backend API (auth, features, AI, explorer-owned v1 helpers)
|
|
location /explorer-api/v1/ {
|
|
proxy_pass http://127.0.0.1:8081/api/v1/;
|
|
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_read_timeout 60s;
|
|
add_header Access-Control-Allow-Origin *;
|
|
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
|
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
|
|
}
|
|
|
|
# Token-aggregation API for the explorer SPA live route-tree and pool intelligence.
|
|
location /token-aggregation/api/v1/ {
|
|
proxy_pass http://127.0.0.1:3001/api/v1/;
|
|
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_read_timeout 60s;
|
|
add_header Access-Control-Allow-Origin *;
|
|
}
|
|
|
|
# Explorer config API (token list, networks, capabilities) - serve from /var/www/html/config/
|
|
location = /api/config/token-list {
|
|
default_type application/json;
|
|
add_header Access-Control-Allow-Origin *;
|
|
add_header Cache-Control "public, max-age=3600";
|
|
alias /var/www/html/config/DUAL_CHAIN_TOKEN_LIST.tokenlist.json;
|
|
}
|
|
location = /api/config/networks {
|
|
default_type application/json;
|
|
add_header Access-Control-Allow-Origin *;
|
|
add_header Cache-Control "public, max-age=3600";
|
|
alias /var/www/html/config/DUAL_CHAIN_NETWORKS.json;
|
|
}
|
|
location = /api/config/capabilities {
|
|
default_type application/json;
|
|
add_header Access-Control-Allow-Origin *;
|
|
add_header Cache-Control "public, max-age=3600";
|
|
alias /var/www/html/config/CHAIN138_RPC_CAPABILITIES.json;
|
|
}
|
|
|
|
# API endpoint (for Blockscout API)
|
|
location /api/ {
|
|
proxy_pass http://127.0.0.1:4000;
|
|
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_read_timeout 300s;
|
|
proxy_connect_timeout 75s;
|
|
add_header Access-Control-Allow-Origin *;
|
|
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
|
|
add_header Access-Control-Allow-Headers "Content-Type";
|
|
}
|
|
|
|
location /health {
|
|
access_log off;
|
|
proxy_pass http://127.0.0.1:4000/api/v2/status;
|
|
proxy_set_header Host $host;
|
|
add_header Content-Type application/json;
|
|
}
|
|
|
|
# Proxy Blockscout UI paths (if needed)
|
|
location /blockscout/ {
|
|
proxy_pass http://127.0.0.1:4000/;
|
|
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_read_timeout 300s;
|
|
proxy_connect_timeout 75s;
|
|
}
|
|
|
|
# Static JSON: topology-graph.json, optional mission-control-verify.json
|
|
location /config/ {
|
|
alias /var/www/html/config/;
|
|
add_header Access-Control-Allow-Origin *;
|
|
default_type application/json;
|
|
add_header Cache-Control "public, max-age=300";
|
|
}
|
|
|
|
# SPA paths: path-based routing for explorer-spa.js
|
|
# Must serve index.html so path-based routing works (regex takes precedence over proxy)
|
|
location ~ ^/(address|tx|block|token|tokens|blocks|transactions|bridge|weth|liquidity|watchlist|nft|home|analytics|operator|system|routes|pools|more)(/|$) {
|
|
root /var/www/html;
|
|
try_files /index.html =404;
|
|
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
|
}
|
|
|
|
# All other paths serve custom frontend (SPA fallback via try_files)
|
|
location / {
|
|
root /var/www/html;
|
|
try_files $uri $uri/ /index.html;
|
|
}
|
|
}
|
|
|
|
map $http_upgrade $connection_upgrade {
|
|
default upgrade;
|
|
'' close;
|
|
}
|
|
NGINX_EOF
|
|
|
|
echo "✅ Configuration updated"
|
|
echo ""
|
|
|
|
# Step 3: Handle SSL certs if missing
|
|
echo "=== Step 3: Checking SSL certificates ==="
|
|
if [ ! -f /etc/letsencrypt/live/explorer.d-bis.org/fullchain.pem ]; then
|
|
echo "⚠️ Let's Encrypt certificate not found, creating self-signed..."
|
|
mkdir -p /etc/nginx/ssl
|
|
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
|
-keyout /etc/nginx/ssl/blockscout.key \
|
|
-out /etc/nginx/ssl/blockscout.crt \
|
|
-subj "/CN=explorer.d-bis.org" 2>/dev/null
|
|
sed -i 's|ssl_certificate /etc/letsencrypt/live/explorer.d-bis.org/fullchain.pem;|ssl_certificate /etc/nginx/ssl/blockscout.crt;|' "$CONFIG_FILE"
|
|
sed -i 's|ssl_certificate_key /etc/letsencrypt/live/explorer.d-bis.org/privkey.pem;|ssl_certificate_key /etc/nginx/ssl/blockscout.key;|' "$CONFIG_FILE"
|
|
echo "✅ Self-signed certificate created"
|
|
else
|
|
echo "✅ Let's Encrypt certificate found"
|
|
fi
|
|
echo ""
|
|
|
|
# Step 4: Ensure /var/www/html exists and has correct permissions
|
|
echo "=== Step 4: Preparing frontend directory ==="
|
|
mkdir -p /var/www/html
|
|
chown -R www-data:www-data /var/www/html 2>/dev/null || true
|
|
echo "✅ Directory prepared"
|
|
echo ""
|
|
|
|
# Step 5: Test and restart nginx
|
|
echo "=== Step 5: Testing and restarting nginx ==="
|
|
if nginx -t; then
|
|
echo "✅ Configuration valid"
|
|
systemctl restart nginx
|
|
echo "✅ Nginx restarted"
|
|
else
|
|
echo "❌ Configuration has errors"
|
|
echo "Restoring backup..."
|
|
cp "${CONFIG_FILE}.backup."* "$CONFIG_FILE" 2>/dev/null || true
|
|
exit 1
|
|
fi
|
|
echo ""
|
|
|
|
# Step 6: Verify
|
|
echo "=== Step 6: Verifying deployment ==="
|
|
sleep 2
|
|
|
|
# Check if custom frontend exists
|
|
if [ -f /var/www/html/index.html ]; then
|
|
echo "✅ Custom frontend file exists"
|
|
|
|
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"
|
|
echo " Deploy the custom frontend using:"
|
|
echo " ./scripts/deploy-frontend-to-vmid5000.sh"
|
|
fi
|
|
else
|
|
echo "⚠️ Custom frontend not found at /var/www/html/index.html"
|
|
echo " Deploy the custom frontend using:"
|
|
echo " ./scripts/deploy-frontend-to-vmid5000.sh"
|
|
fi
|
|
|
|
# Test HTTP endpoint (non-fatal: do not exit on curl/grep failure)
|
|
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 -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)"
|
|
echo "Response preview: $HTTP_RESPONSE"
|
|
fi
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo "Nginx Configuration Updated!"
|
|
echo "=========================================="
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo "1. Deploy custom frontend: ./scripts/deploy-frontend-to-vmid5000.sh"
|
|
echo "2. Or manually copy: cp explorer-monorepo/frontend/public/index.html /var/www/html/index.html"
|
|
echo ""
|