Migrate blockchain-explorer and CLI to centralized aitbc package utilities
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Successful in 9s
Blockchain Synchronization Verification / sync-verification (push) Failing after 2s
CLI Tests / test-cli (push) Failing after 4s
Documentation Validation / validate-docs (push) Successful in 17s
Documentation Validation / validate-policies-strict (push) Successful in 9s
Integration Tests / test-service-integration (push) Successful in 2m40s
Multi-Node Blockchain Health Monitoring / health-check (push) Failing after 7s
P2P Network Verification / p2p-verification (push) Successful in 6s
Python Tests / test-python (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled
Package Tests / Python package - aitbc-agent-sdk (push) Failing after 31s
Package Tests / Python package - aitbc-core (push) Failing after 35s
Package Tests / Python package - aitbc-crypto (push) Successful in 24s
Package Tests / JavaScript package - aitbc-sdk-js (push) Successful in 15s
Package Tests / JavaScript package - aitbc-token (push) Successful in 32s
Production Tests / Production Integration Tests (push) Failing after 10s
Package Tests / Python package - aitbc-sdk (push) Failing after 10m21s

- Add DataLayer, MockDataGenerator, RealDataFetcher, and get_data_layer to aitbc package exports
- Migrate blockchain-explorer/main.py to use aitbc.get_data_layer for mock/real data toggle
- Add data layer integration to search_transactions, search_blocks, and analytics_overview endpoints
- Migrate CLI blockchain commands to use chain registry instead of hardcoded chain list
- Replace hardcoded ['ait-devnet', 'ait-testnet'] with get
This commit is contained in:
aitbc
2026-04-25 08:01:36 +02:00
parent 3030a3720f
commit 8e1f5864a6
6 changed files with 862 additions and 184 deletions

View File

@@ -4,20 +4,28 @@ AITBC Blockchain Explorer - Enhanced Version
Advanced web interface with search, analytics, and export capabilities
"""
import asyncio
import httpx
import json
import csv
import io
import re
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Any, Union
from fastapi import FastAPI, HTTPException, Request, Query, Response
from fastapi.responses import HTMLResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles
from typing import Optional
import os
from fastapi import FastAPI, HTTPException, Query
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel, Field
import uvicorn
# Import data layer for toggle between mock and real data
try:
from aitbc import get_data_layer
USE_DATA_LAYER = True
except ImportError:
USE_DATA_LAYER = False
app = FastAPI(title="AITBC Blockchain Explorer", version="0.1.0")
# Validation patterns for user inputs to prevent SSRF
@@ -1025,36 +1033,46 @@ async def search_transactions(
):
"""Advanced transaction search"""
try:
# Build query parameters for blockchain node
params = {}
if address:
params["address"] = address
if amount_min:
params["amount_min"] = amount_min
if amount_max:
params["amount_max"] = amount_max
if tx_type:
params["type"] = tx_type
if since:
params["since"] = since
if until:
params["until"] = until
params["limit"] = limit
params["offset"] = offset
params["chain_id"] = chain_id
rpc_url = BLOCKCHAIN_RPC_URLS.get(chain_id, BLOCKCHAIN_RPC_URLS[DEFAULT_CHAIN])
async with httpx.AsyncClient() as client:
response = await client.get(f"{rpc_url}/rpc/search/transactions", params=params)
if response.status_code == 200:
return response.json()
elif response.status_code == 404:
return []
else:
raise HTTPException(
status_code=response.status_code,
detail=f"Failed to fetch transactions from blockchain RPC: {response.text}"
)
if USE_DATA_LAYER:
# Use data layer with toggle support
data_layer = get_data_layer()
rpc_url = BLOCKCHAIN_RPC_URLS.get(chain_id, BLOCKCHAIN_RPC_URLS[DEFAULT_CHAIN])
return await data_layer.get_transactions(
address, amount_min, amount_max, tx_type, since, until,
limit, offset, chain_id, rpc_url
)
else:
# Original implementation without data layer
# Build query parameters
params = {}
if address:
params["address"] = address
if amount_min:
params["amount_min"] = amount_min
if amount_max:
params["amount_max"] = amount_max
if tx_type:
params["type"] = tx_type
if since:
params["since"] = since
if until:
params["until"] = until
params["limit"] = limit
params["offset"] = offset
params["chain_id"] = chain_id
rpc_url = BLOCKCHAIN_RPC_URLS.get(chain_id, BLOCKCHAIN_RPC_URLS[DEFAULT_CHAIN])
async with httpx.AsyncClient() as client:
response = await client.get(f"{rpc_url}/rpc/search/transactions", params=params)
if response.status_code == 200:
return response.json()
elif response.status_code == 404:
return []
else:
raise HTTPException(
status_code=response.status_code,
detail=f"Failed to fetch transactions from blockchain RPC: {response.text}"
)
except httpx.RequestError as e:
raise HTTPException(status_code=503, detail=f"Blockchain RPC unavailable: {str(e)}")
except Exception as e:
@@ -1072,32 +1090,40 @@ async def search_blocks(
):
"""Advanced block search"""
try:
# Build query parameters
params = {}
if validator:
params["validator"] = validator
if since:
params["since"] = since
if until:
params["until"] = until
if min_tx:
params["min_tx"] = min_tx
params["limit"] = limit
params["offset"] = offset
params["chain_id"] = chain_id
rpc_url = BLOCKCHAIN_RPC_URLS.get(chain_id, BLOCKCHAIN_RPC_URLS[DEFAULT_CHAIN])
async with httpx.AsyncClient() as client:
response = await client.get(f"{rpc_url}/rpc/search/blocks", params=params)
if response.status_code == 200:
return response.json()
elif response.status_code == 404:
return []
else:
raise HTTPException(
status_code=response.status_code,
detail=f"Failed to fetch blocks from blockchain RPC: {response.text}"
)
if USE_DATA_LAYER:
# Use data layer with toggle support
data_layer = get_data_layer()
rpc_url = BLOCKCHAIN_RPC_URLS.get(chain_id, BLOCKCHAIN_RPC_URLS[DEFAULT_CHAIN])
return await data_layer.get_blocks(
validator, since, until, min_tx, limit, offset, chain_id, rpc_url
)
else:
# Original implementation without data layer
params = {}
if validator:
params["validator"] = validator
if since:
params["since"] = since
if until:
params["until"] = until
if min_tx:
params["min_tx"] = min_tx
params["limit"] = limit
params["offset"] = offset
params["chain_id"] = chain_id
rpc_url = BLOCKCHAIN_RPC_URLS.get(chain_id, BLOCKCHAIN_RPC_URLS[DEFAULT_CHAIN])
async with httpx.AsyncClient() as client:
response = await client.get(f"{rpc_url}/rpc/search/blocks", params=params)
if response.status_code == 200:
return response.json()
elif response.status_code == 404:
return []
else:
raise HTTPException(
status_code=response.status_code,
detail=f"Failed to fetch blocks from blockchain RPC: {response.text}"
)
except httpx.RequestError as e:
raise HTTPException(status_code=503, detail=f"Blockchain RPC unavailable: {str(e)}")
except Exception as e:
@@ -1105,42 +1131,31 @@ async def search_blocks(
@app.get("/api/analytics/overview")
async def analytics_overview(period: str = "24h"):
"""Get analytics overview"""
"""Get analytics overview from blockchain RPC"""
try:
# Generate mock analytics data
now = datetime.now()
if period == "1h":
labels = [f"{i:02d}:{(i*5)%60:02d}" for i in range(12)]
volume_values = [10 + i * 2 for i in range(12)]
activity_values = [5 + i for i in range(12)]
elif period == "24h":
labels = [f"{i:02d}:00" for i in range(0, 24, 2)]
volume_values = [50 + i * 5 for i in range(12)]
activity_values = [20 + i * 3 for i in range(12)]
elif period == "7d":
labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
volume_values = [500, 600, 550, 700, 800, 650, 750]
activity_values = [200, 250, 220, 300, 350, 280, 320]
else: # 30d
labels = [f"Week {i+1}" for i in range(4)]
volume_values = [3000, 3500, 3200, 3800]
activity_values = [1200, 1400, 1300, 1500]
return {
"total_transactions": "1,234",
"transaction_volume": "5,678.90 AITBC",
"active_addresses": "89",
"avg_block_time": "2.1s",
"volume_data": {
"labels": labels,
"values": volume_values
},
"activity_data": {
"labels": labels,
"values": activity_values
}
}
if USE_DATA_LAYER:
# Use data layer with toggle support
data_layer = get_data_layer()
rpc_url = BLOCKCHAIN_RPC_URLS.get(DEFAULT_CHAIN)
return await data_layer.get_analytics_overview(period, rpc_url)
else:
# Original implementation without data layer
rpc_url = BLOCKCHAIN_RPC_URLS.get(DEFAULT_CHAIN)
params = {"period": period}
async with httpx.AsyncClient() as client:
response = await client.get(f"{rpc_url}/rpc/analytics/overview", params=params)
if response.status_code == 200:
return response.json()
elif response.status_code == 404:
raise HTTPException(status_code=501, detail="Analytics endpoint not available on blockchain RPC")
else:
raise HTTPException(
status_code=response.status_code,
detail=f"Failed to fetch analytics from blockchain RPC: {response.text}"
)
except httpx.RequestError as e:
raise HTTPException(status_code=503, detail=f"Blockchain RPC unavailable: {str(e)}")
except Exception as e:
raise HTTPException(status_code=500, detail=f"Analytics failed: {str(e)}")