- 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.
231 lines
9.4 KiB
Bash
231 lines
9.4 KiB
Bash
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
VMID="${VMID:-5000}"
|
|
PROXMOX_HOST="${PROXMOX_HOST_R630_02:-192.168.11.12}"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
BACKEND_DIR="$REPO_ROOT/explorer-monorepo/backend"
|
|
TMP_DIR="$(mktemp -d)"
|
|
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
|
|
# Source the local secrets file so deploys do not depend on repo-stored API keys.
|
|
source "$SECURE_AI_ENV_FILE"
|
|
set +a
|
|
fi
|
|
|
|
cleanup() {
|
|
rm -rf "$TMP_DIR"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
echo "=========================================="
|
|
echo "Deploying Explorer AI Backend to VMID $VMID"
|
|
echo "=========================================="
|
|
|
|
echo "=== Step 1: Build explorer backend ==="
|
|
(
|
|
cd "$BACKEND_DIR"
|
|
go build -o "$TMP_DIR/explorer-config-api" ./api/rest/cmd
|
|
)
|
|
echo "✅ Backend built"
|
|
|
|
echo "=== Step 2: Prepare AI docs bundle ==="
|
|
mkdir -p "$TMP_DIR/explorer-ai-docs/docs/11-references" "$TMP_DIR/explorer-ai-docs/explorer-monorepo/docs"
|
|
cp "$REPO_ROOT/docs/11-references/ADDRESS_MATRIX_AND_STATUS.md" "$TMP_DIR/explorer-ai-docs/docs/11-references/"
|
|
cp "$REPO_ROOT/docs/11-references/LIQUIDITY_POOLS_MASTER_MAP.md" "$TMP_DIR/explorer-ai-docs/docs/11-references/"
|
|
cp "$REPO_ROOT/docs/11-references/DEPLOYED_TOKENS_BRIDGES_LPS_AND_ROUTING_STATUS.md" "$TMP_DIR/explorer-ai-docs/docs/11-references/"
|
|
cp "$REPO_ROOT/docs/11-references/EXPLORER_TOKEN_LIST_CROSSCHECK.md" "$TMP_DIR/explorer-ai-docs/docs/11-references/"
|
|
cp "$REPO_ROOT/explorer-monorepo/docs/EXPLORER_API_ACCESS.md" "$TMP_DIR/explorer-ai-docs/explorer-monorepo/docs/"
|
|
tar -C "$TMP_DIR" -czf "$TMP_DIR/explorer-ai-docs.tar.gz" explorer-ai-docs
|
|
echo "✅ Docs bundle prepared"
|
|
|
|
echo "=== Step 3: Upload artifacts ==="
|
|
scp -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$TMP_DIR/explorer-config-api" root@"$PROXMOX_HOST":/tmp/explorer-config-api
|
|
scp -o ConnectTimeout=10 -o StrictHostKeyChecking=no "$TMP_DIR/explorer-ai-docs.tar.gz" root@"$PROXMOX_HOST":/tmp/explorer-ai-docs.tar.gz
|
|
echo "✅ Artifacts uploaded"
|
|
|
|
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' ACCESS_ADMIN_EMAILS_VALUE='$ACCESS_ADMIN_EMAILS_VALUE' ACCESS_INTERNAL_SECRET_VALUE='$ACCESS_INTERNAL_SECRET_VALUE' bash -s" <<'REMOTE'
|
|
set -euo pipefail
|
|
|
|
VMID=5000
|
|
DB_URL="$EXPLORER_DATABASE_URL_VALUE"
|
|
if [ -z "$DB_URL" ]; then
|
|
DB_CONTAINER_IP="$(pct exec "$VMID" -- docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' blockscout-postgres 2>/dev/null || true)"
|
|
if [ -n "$DB_CONTAINER_IP" ]; then
|
|
DB_URL="postgresql://blockscout:blockscout@${DB_CONTAINER_IP}:5432/blockscout?sslmode=disable"
|
|
fi
|
|
fi
|
|
|
|
pct exec "$VMID" -- bash -lc 'mkdir -p /opt/explorer-ai-docs /etc/systemd/system/explorer-config-api.service.d'
|
|
pct push "$VMID" /tmp/explorer-ai-docs.tar.gz /tmp/explorer-ai-docs.tar.gz --perms 0644
|
|
pct push "$VMID" /tmp/explorer-config-api /usr/local/bin/explorer-config-api.new --perms 0755
|
|
|
|
pct exec "$VMID" -- env \
|
|
DB_URL="$DB_URL" \
|
|
EXPLORER_AI_MODEL_VALUE="$EXPLORER_AI_MODEL_VALUE" \
|
|
JWT_SECRET_VALUE="$JWT_SECRET_VALUE" \
|
|
XAI_API_KEY_VALUE="$XAI_API_KEY_VALUE" \
|
|
bash -lc '
|
|
set -euo pipefail
|
|
rm -rf /opt/explorer-ai-docs/*
|
|
tar -xzf /tmp/explorer-ai-docs.tar.gz -C /opt
|
|
rm -f /tmp/explorer-ai-docs.tar.gz
|
|
mv /usr/local/bin/explorer-config-api.new /usr/local/bin/explorer-config-api
|
|
chmod 0755 /usr/local/bin/explorer-config-api
|
|
|
|
cat > /etc/systemd/system/explorer-config-api.service <<EOF
|
|
[Unit]
|
|
Description=Explorer Config API (MetaMask networks and token list)
|
|
After=network-online.target docker.service
|
|
Wants=network-online.target docker.service
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=root
|
|
WorkingDirectory=/usr/local/bin
|
|
Environment=PORT=8081
|
|
Environment=CHAIN_ID=138
|
|
ExecStart=/usr/local/bin/explorer-config-api
|
|
Restart=on-failure
|
|
RestartSec=5s
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
cat > /etc/systemd/system/explorer-config-api.service.d/ai.conf <<EOF
|
|
[Service]
|
|
Environment=TOKEN_AGGREGATION_API_BASE=http://127.0.0.1:3001
|
|
Environment=EXPLORER_AI_WORKSPACE_ROOT=/opt/explorer-ai-docs
|
|
Environment=EXPLORER_AI_MODEL='"$EXPLORER_AI_MODEL_VALUE"'
|
|
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]
|
|
Environment=DATABASE_URL='"$DB_URL"'
|
|
EOF
|
|
chmod 600 /etc/systemd/system/explorer-config-api.service.d/database.conf
|
|
fi
|
|
|
|
rm -f /etc/systemd/system/explorer-config-api.service.d/openai.conf
|
|
|
|
if [ -n "'"$XAI_API_KEY_VALUE"'" ]; then
|
|
cat > /etc/systemd/system/explorer-config-api.service.d/xai.conf <<EOF
|
|
[Service]
|
|
Environment=XAI_API_KEY='"$XAI_API_KEY_VALUE"'
|
|
EOF
|
|
chmod 600 /etc/systemd/system/explorer-config-api.service.d/xai.conf
|
|
else
|
|
rm -f /etc/systemd/system/explorer-config-api.service.d/xai.conf
|
|
fi
|
|
|
|
systemctl daemon-reload
|
|
systemctl restart explorer-config-api
|
|
sleep 2
|
|
systemctl is-active explorer-config-api
|
|
'
|
|
REMOTE
|
|
echo "✅ Backend installed and service restarted"
|
|
|
|
echo "=== Step 5: Normalize nginx explorer backend prefix ==="
|
|
ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no root@"$PROXMOX_HOST" "VMID='$VMID' bash -s" <<'REMOTE'
|
|
set -euo pipefail
|
|
|
|
pct exec "$VMID" -- python3 - <<'PY'
|
|
from pathlib import Path
|
|
import re
|
|
path = Path('/etc/nginx/sites-available/blockscout')
|
|
text = path.read_text()
|
|
explorer_block = ''' # 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";
|
|
}
|
|
|
|
'''
|
|
escaped_explorer_block = explorer_block.replace('$', '\\$')
|
|
if escaped_explorer_block in text:
|
|
text = text.replace(escaped_explorer_block, explorer_block)
|
|
|
|
legacy_patterns = [
|
|
r"\n\s*# Explorer AI endpoints on the explorer backend service \(HTTP\)\n\s*location /api/v1/ai/ \{.*?\n\s*\}\n",
|
|
r"\n\s*location = /api/v1/features \{.*?\n\s*\}\n",
|
|
r"\n\s*# Explorer AI endpoints on the explorer backend service\n\s*location /api/v1/ai/ \{.*?\n\s*\}\n",
|
|
]
|
|
for pattern in legacy_patterns:
|
|
text = re.sub(pattern, "\n", text, flags=re.S)
|
|
|
|
http_needle = ' # Blockscout API endpoint - MUST come before the redirect location\n'
|
|
legacy_http_needle = ' # API endpoint - MUST come before the redirect location\n'
|
|
if explorer_block not in text:
|
|
if http_needle in text:
|
|
text = text.replace(http_needle, explorer_block + http_needle, 1)
|
|
elif legacy_http_needle in text:
|
|
text = text.replace(legacy_http_needle, explorer_block + ' # Blockscout API endpoint - MUST come before the redirect location\n', 1)
|
|
|
|
https_needle = ' # Token-aggregation API for the explorer SPA live route-tree and pool intelligence.\n'
|
|
if explorer_block not in text[text.find('# HTTPS server - Blockscout Explorer'):]:
|
|
text = text.replace(' # Token-aggregation API at /api/v1/ for the Snap site. Service runs on port 3001.\n location /api/v1/ {\n proxy_pass http://127.0.0.1:3001/api/v1/;\n proxy_http_version 1.1;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_read_timeout 60s;\n add_header Access-Control-Allow-Origin *;\n }\n\n', explorer_block, 1)
|
|
path.write_text(text)
|
|
PY
|
|
pct exec "$VMID" -- bash -lc 'nginx -t && nginx -s reload'
|
|
REMOTE
|
|
echo "✅ Nginx normalized"
|
|
|
|
echo "=== Step 6: Verify core explorer AI routes ==="
|
|
curl -fsS "https://explorer.d-bis.org/explorer-api/v1/features" >/dev/null
|
|
curl -fsS "https://explorer.d-bis.org/explorer-api/v1/ai/context?q=cUSDT" >/dev/null
|
|
echo "✅ Explorer AI routes respond publicly"
|
|
|
|
echo ""
|
|
echo "Deployment complete."
|