173 lines
7.4 KiB
Bash
173 lines
7.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:-gpt-5.4-mini}"
|
||
|
|
EXPLORER_DATABASE_URL_VALUE="${EXPLORER_DATABASE_URL_VALUE:-}"
|
||
|
|
|
||
|
|
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
|
||
|
|
|
||
|
|
export JWT_SECRET_VALUE
|
||
|
|
export EXPLORER_AI_MODEL_VALUE
|
||
|
|
export OPENAI_API_KEY_VALUE="${OPENAI_API_KEY:-}"
|
||
|
|
export EXPLORER_DATABASE_URL_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' OPENAI_API_KEY_VALUE='$OPENAI_API_KEY_VALUE' EXPLORER_DATABASE_URL_VALUE='$EXPLORER_DATABASE_URL_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" \
|
||
|
|
OPENAI_API_KEY_VALUE="$OPENAI_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.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"'
|
||
|
|
EOF
|
||
|
|
|
||
|
|
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
|
||
|
|
|
||
|
|
if [ -n "'"$OPENAI_API_KEY_VALUE"'" ]; then
|
||
|
|
cat > /etc/systemd/system/explorer-config-api.service.d/openai.conf <<EOF
|
||
|
|
[Service]
|
||
|
|
Environment=OPENAI_API_KEY='"$OPENAI_API_KEY_VALUE"'
|
||
|
|
EOF
|
||
|
|
chmod 600 /etc/systemd/system/explorer-config-api.service.d/openai.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
|
||
|
|
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)
|
||
|
|
|
||
|
|
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."
|