|
|
|
|
|
"""
|
|
|
Resource Hierarchy API
|
|
|
API endpoints for hierarchical resource monitoring
|
|
|
نمایش و مانیتورینگ سلسلهمراتب منابع
|
|
|
"""
|
|
|
|
|
|
from fastapi import APIRouter, HTTPException
|
|
|
from fastapi.responses import JSONResponse
|
|
|
from typing import Dict, Any
|
|
|
import logging
|
|
|
|
|
|
from backend.services.hierarchical_fallback_config import hierarchical_config, Priority
|
|
|
from backend.services.master_resource_orchestrator import master_orchestrator
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
router = APIRouter(tags=["Resource Hierarchy"])
|
|
|
|
|
|
|
|
|
@router.get("/api/hierarchy/overview")
|
|
|
async def get_hierarchy_overview():
|
|
|
"""
|
|
|
Get complete overview of hierarchical resource system
|
|
|
نمای کلی سیستم سلسلهمراتبی منابع
|
|
|
"""
|
|
|
try:
|
|
|
|
|
|
all_resources = hierarchical_config.get_all_resources_by_priority()
|
|
|
resource_counts = hierarchical_config.count_total_resources()
|
|
|
|
|
|
|
|
|
priority_counts = {
|
|
|
"CRITICAL": 0,
|
|
|
"HIGH": 0,
|
|
|
"MEDIUM": 0,
|
|
|
"LOW": 0,
|
|
|
"EMERGENCY": 0
|
|
|
}
|
|
|
|
|
|
total_resources = 0
|
|
|
for category, resources in all_resources.items():
|
|
|
for resource in resources:
|
|
|
priority_counts[resource.priority.name] += 1
|
|
|
total_resources += 1
|
|
|
|
|
|
return JSONResponse(content={
|
|
|
"success": True,
|
|
|
"summary": {
|
|
|
"total_resources": total_resources,
|
|
|
"total_categories": len(all_resources),
|
|
|
"message_fa": "همه منابع فعال هستند - هیچ منبعی بیکار نیست",
|
|
|
"message_en": "ALL resources are active - NO IDLE RESOURCES"
|
|
|
},
|
|
|
"by_category": {
|
|
|
"market_data": {
|
|
|
"count": resource_counts["market_data"],
|
|
|
"providers": ["Binance", "CoinGecko", "CoinCap", "CoinPaprika", "CMC×2", "CMC Info (NEW!)", "CryptoCompare", "Messari", "CoinLore", "DefiLlama", "CoinStats", "DIA", "Nomics", "BraveNewCoin", "FreeCryptoAPI", "CoinDesk"]
|
|
|
},
|
|
|
"news": {
|
|
|
"count": resource_counts["news"],
|
|
|
"providers": ["CryptoPanic", "CoinStats", "NewsAPI×2 (NEW!)", "CoinTelegraph", "CoinDesk", "Decrypt", "BitcoinMag", "CryptoSlate", "CryptoControl", "TheBlock"]
|
|
|
},
|
|
|
"sentiment": {
|
|
|
"count": resource_counts["sentiment"],
|
|
|
"providers": ["Alternative.me", "CFGI", "CoinGecko", "Reddit", "Messari", "LunarCrush", "Santiment", "TheTie"]
|
|
|
},
|
|
|
"onchain": {
|
|
|
"count": resource_counts["onchain_total"],
|
|
|
"explorers": {
|
|
|
"ethereum": ["Etherscan×2", "Blockchair", "Blockscout", "Ethplorer", "Etherchain", "Chainlens"],
|
|
|
"bsc": ["BscScan", "Blockchair", "BitQuery", "Nodereal", "Ankr", "BscTrace", "1inch"],
|
|
|
"tron": ["TronScan", "TronGrid", "Blockchair", "TronStack", "GetBlock"]
|
|
|
}
|
|
|
},
|
|
|
"rpc_nodes": {
|
|
|
"count": resource_counts["rpc_total"],
|
|
|
"chains": {
|
|
|
"ethereum": 10,
|
|
|
"bsc": 6,
|
|
|
"polygon": 4,
|
|
|
"tron": 3
|
|
|
}
|
|
|
},
|
|
|
"datasets": {
|
|
|
"count": resource_counts["datasets"],
|
|
|
"files": 186,
|
|
|
"providers": ["linxy/CryptoCoin (182 files)", "WinkingFace×4"]
|
|
|
},
|
|
|
"infrastructure": {
|
|
|
"count": resource_counts["infrastructure"],
|
|
|
"providers": ["Cloudflare DoH (NEW!)", "Google DoH (NEW!)", "ProxyScrape (NEW!)"],
|
|
|
"purpose": "DNS resolution & Proxy services for bypassing filters"
|
|
|
}
|
|
|
},
|
|
|
"by_priority": {
|
|
|
"CRITICAL": {
|
|
|
"count": priority_counts["CRITICAL"],
|
|
|
"description_fa": "سریعترین و قابل اعتمادترین منابع",
|
|
|
"description_en": "Fastest and most reliable resources"
|
|
|
},
|
|
|
"HIGH": {
|
|
|
"count": priority_counts["HIGH"],
|
|
|
"description_fa": "کیفیت بالا، سرعت خوب",
|
|
|
"description_en": "High quality, good speed"
|
|
|
},
|
|
|
"MEDIUM": {
|
|
|
"count": priority_counts["MEDIUM"],
|
|
|
"description_fa": "کیفیت استاندارد",
|
|
|
"description_en": "Standard quality"
|
|
|
},
|
|
|
"LOW": {
|
|
|
"count": priority_counts["LOW"],
|
|
|
"description_fa": "منابع پشتیبان",
|
|
|
"description_en": "Backup sources"
|
|
|
},
|
|
|
"EMERGENCY": {
|
|
|
"count": priority_counts["EMERGENCY"],
|
|
|
"description_fa": "آخرین راهحل",
|
|
|
"description_en": "Last resort"
|
|
|
}
|
|
|
},
|
|
|
"api_keys": {
|
|
|
"total": 8,
|
|
|
"active": [
|
|
|
"Etherscan Primary",
|
|
|
"Etherscan Backup",
|
|
|
"BscScan",
|
|
|
"TronScan",
|
|
|
"CoinMarketCap Key 1",
|
|
|
"CoinMarketCap Key 2",
|
|
|
"CryptoCompare",
|
|
|
"NewsAPI.org"
|
|
|
],
|
|
|
"status": "همه کلیدها فعال و موجود در سیستم"
|
|
|
}
|
|
|
})
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"Error getting hierarchy overview: {e}")
|
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
|
|
|
@router.get("/api/hierarchy/usage-stats")
|
|
|
async def get_usage_statistics():
|
|
|
"""
|
|
|
Get detailed usage statistics for all resources
|
|
|
آمار دقیق استفاده از همه منابع
|
|
|
"""
|
|
|
try:
|
|
|
stats = master_orchestrator.get_usage_statistics()
|
|
|
|
|
|
return JSONResponse(content={
|
|
|
"success": True,
|
|
|
"message_fa": "آمار استفاده از منابع - تضمین استفاده از همه منابع",
|
|
|
"message_en": "Resource usage statistics - Guaranteed utilization of ALL resources",
|
|
|
"statistics": stats,
|
|
|
"utilization_guarantee": {
|
|
|
"fa": "سیستم به صورت خودکار از همه منابع در صورت نیاز استفاده میکند",
|
|
|
"en": "System automatically uses all resources as needed",
|
|
|
"hierarchy_levels": 5,
|
|
|
"total_fallback_chain_length": "5 levels deep (CRITICAL → HIGH → MEDIUM → LOW → EMERGENCY)"
|
|
|
}
|
|
|
})
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"Error getting usage stats: {e}")
|
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
|
|
|
@router.get("/api/hierarchy/health-report")
|
|
|
async def get_health_report():
|
|
|
"""
|
|
|
Get health report for all resources
|
|
|
گزارش سلامت همه منابع
|
|
|
"""
|
|
|
try:
|
|
|
health_report = master_orchestrator.get_resource_health_report()
|
|
|
|
|
|
return JSONResponse(content={
|
|
|
"success": True,
|
|
|
"message_fa": "گزارش سلامت منابع",
|
|
|
"message_en": "Resource health report",
|
|
|
"health_report": health_report,
|
|
|
"recommendations_fa": [
|
|
|
"✅ منابع سالم: استفاده مداوم",
|
|
|
"⚠️ منابع ضعیف: نیاز به بررسی",
|
|
|
"❌ منابع خراب: منابع جایگزین فعال",
|
|
|
"💤 منابع استفاده نشده: در انتظار نیاز"
|
|
|
],
|
|
|
"recommendations_en": [
|
|
|
"✅ Healthy resources: Continue usage",
|
|
|
"⚠️ Degraded resources: Need attention",
|
|
|
"❌ Failed resources: Fallbacks active",
|
|
|
"💤 Unused resources: Waiting for demand"
|
|
|
]
|
|
|
})
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"Error getting health report: {e}")
|
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
|
|
|
@router.get("/api/hierarchy/resource-details/{category}")
|
|
|
async def get_resource_details(category: str):
|
|
|
"""
|
|
|
Get detailed information about resources in a specific category
|
|
|
اطلاعات دقیق منابع در یک دسته خاص
|
|
|
|
|
|
Categories: market_data, news, sentiment, onchain_ethereum, onchain_bsc, onchain_tron,
|
|
|
rpc_ethereum, rpc_bsc, rpc_polygon, rpc_tron, datasets
|
|
|
"""
|
|
|
try:
|
|
|
all_resources = hierarchical_config.get_all_resources_by_priority()
|
|
|
|
|
|
if category not in all_resources:
|
|
|
raise HTTPException(
|
|
|
status_code=404,
|
|
|
detail=f"Category '{category}' not found. Available: {list(all_resources.keys())}"
|
|
|
)
|
|
|
|
|
|
resources = all_resources[category]
|
|
|
|
|
|
|
|
|
resource_details = []
|
|
|
for idx, resource in enumerate(resources, 1):
|
|
|
resource_details.append({
|
|
|
"rank": idx,
|
|
|
"name": resource.name,
|
|
|
"base_url": resource.base_url,
|
|
|
"priority": resource.priority.name,
|
|
|
"priority_level": resource.priority.value,
|
|
|
"requires_auth": resource.requires_auth,
|
|
|
"has_api_key": bool(resource.api_key),
|
|
|
"rate_limit": resource.rate_limit or "Unlimited",
|
|
|
"features": resource.features or [],
|
|
|
"notes": resource.notes or "",
|
|
|
"notes_fa": resource.notes or ""
|
|
|
})
|
|
|
|
|
|
return JSONResponse(content={
|
|
|
"success": True,
|
|
|
"category": category,
|
|
|
"total_resources": len(resources),
|
|
|
"resources": resource_details,
|
|
|
"hierarchy_info": {
|
|
|
"fa": f"این دسته شامل {len(resources)} منبع به ترتیب اولویت است",
|
|
|
"en": f"This category contains {len(resources)} resources in priority order",
|
|
|
"utilization": "100% - همه منابع در زنجیره فالبک قرار دارند"
|
|
|
}
|
|
|
})
|
|
|
|
|
|
except HTTPException:
|
|
|
raise
|
|
|
except Exception as e:
|
|
|
logger.error(f"Error getting resource details: {e}")
|
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
|
|
|
@router.get("/api/hierarchy/fallback-chain/{category}")
|
|
|
async def get_fallback_chain(category: str):
|
|
|
"""
|
|
|
Get the complete fallback chain for a category
|
|
|
نمایش زنجیره کامل فالبک برای یک دسته
|
|
|
"""
|
|
|
try:
|
|
|
all_resources = hierarchical_config.get_all_resources_by_priority()
|
|
|
|
|
|
if category not in all_resources:
|
|
|
raise HTTPException(
|
|
|
status_code=404,
|
|
|
detail=f"Category '{category}' not found"
|
|
|
)
|
|
|
|
|
|
resources = all_resources[category]
|
|
|
|
|
|
|
|
|
fallback_chain = {
|
|
|
Priority.CRITICAL: [],
|
|
|
Priority.HIGH: [],
|
|
|
Priority.MEDIUM: [],
|
|
|
Priority.LOW: [],
|
|
|
Priority.EMERGENCY: []
|
|
|
}
|
|
|
|
|
|
for resource in resources:
|
|
|
fallback_chain[resource.priority].append(resource.name)
|
|
|
|
|
|
|
|
|
flow_steps = []
|
|
|
step_number = 1
|
|
|
|
|
|
for priority in [Priority.CRITICAL, Priority.HIGH, Priority.MEDIUM, Priority.LOW, Priority.EMERGENCY]:
|
|
|
if fallback_chain[priority]:
|
|
|
flow_steps.append({
|
|
|
"step": step_number,
|
|
|
"priority": priority.name,
|
|
|
"priority_level": priority.value,
|
|
|
"resources": fallback_chain[priority],
|
|
|
"count": len(fallback_chain[priority]),
|
|
|
"description_fa": f"سطح {priority.name}: تلاش با {len(fallback_chain[priority])} منبع",
|
|
|
"description_en": f"{priority.name} level: Try {len(fallback_chain[priority])} resources",
|
|
|
"action_on_fail_fa": "در صورت شکست، رفتن به سطح بعدی" if priority != Priority.EMERGENCY else "خطا 503 - همه منابع ناموفق",
|
|
|
"action_on_fail_en": "On failure, proceed to next level" if priority != Priority.EMERGENCY else "Error 503 - All resources failed"
|
|
|
})
|
|
|
step_number += 1
|
|
|
|
|
|
total_attempts = sum(len(resources) for resources in fallback_chain.values())
|
|
|
|
|
|
return JSONResponse(content={
|
|
|
"success": True,
|
|
|
"category": category,
|
|
|
"fallback_chain": {
|
|
|
"total_levels": len([s for s in flow_steps]),
|
|
|
"total_resources": total_attempts,
|
|
|
"flow": flow_steps
|
|
|
},
|
|
|
"guarantee": {
|
|
|
"fa": f"تضمین: سیستم {total_attempts} بار تلاش میکند قبل از اینکه خطا برگرداند",
|
|
|
"en": f"Guarantee: System tries {total_attempts} times before returning error",
|
|
|
"uptime_potential": "99.9%+"
|
|
|
},
|
|
|
"visualization": {
|
|
|
"fa": f"درخواست → CRITICAL ({len(fallback_chain[Priority.CRITICAL])}) → HIGH ({len(fallback_chain[Priority.HIGH])}) → MEDIUM ({len(fallback_chain[Priority.MEDIUM])}) → LOW ({len(fallback_chain[Priority.LOW])}) → EMERGENCY ({len(fallback_chain[Priority.EMERGENCY])}) → خطا/موفقیت",
|
|
|
"en": f"Request → CRITICAL ({len(fallback_chain[Priority.CRITICAL])}) → HIGH ({len(fallback_chain[Priority.HIGH])}) → MEDIUM ({len(fallback_chain[Priority.MEDIUM])}) → LOW ({len(fallback_chain[Priority.LOW])}) → EMERGENCY ({len(fallback_chain[Priority.EMERGENCY])}) → Error/Success"
|
|
|
}
|
|
|
})
|
|
|
|
|
|
except HTTPException:
|
|
|
raise
|
|
|
except Exception as e:
|
|
|
logger.error(f"Error getting fallback chain: {e}")
|
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
|
|
|
@router.get("/api/hierarchy/test-fallback/{category}")
|
|
|
async def test_fallback_system(category: str):
|
|
|
"""
|
|
|
Test the fallback system for a category (simulation)
|
|
|
تست سیستم فالبک برای یک دسته (شبیهسازی)
|
|
|
"""
|
|
|
try:
|
|
|
all_resources = hierarchical_config.get_all_resources_by_priority()
|
|
|
|
|
|
if category not in all_resources:
|
|
|
raise HTTPException(
|
|
|
status_code=404,
|
|
|
detail=f"Category '{category}' not found"
|
|
|
)
|
|
|
|
|
|
resources = all_resources[category]
|
|
|
|
|
|
|
|
|
simulation = {
|
|
|
"scenario": "All CRITICAL resources fail, system falls back",
|
|
|
"steps": []
|
|
|
}
|
|
|
|
|
|
for priority in [Priority.CRITICAL, Priority.HIGH, Priority.MEDIUM, Priority.LOW, Priority.EMERGENCY]:
|
|
|
priority_resources = [r for r in resources if r.priority == priority]
|
|
|
|
|
|
if priority_resources:
|
|
|
simulation["steps"].append({
|
|
|
"priority": priority.name,
|
|
|
"resources_tried": [r.name for r in priority_resources],
|
|
|
"count": len(priority_resources),
|
|
|
"simulated_result": "SUCCESS" if priority == Priority.HIGH else "Try next level",
|
|
|
"message_fa": f"✅ موفق در سطح {priority.name}" if priority == Priority.HIGH else f"❌ ناموفق، رفتن به سطح بعدی",
|
|
|
"message_en": f"✅ Success at {priority.name}" if priority == Priority.HIGH else f"❌ Failed, trying next level"
|
|
|
})
|
|
|
|
|
|
if priority == Priority.HIGH:
|
|
|
break
|
|
|
|
|
|
return JSONResponse(content={
|
|
|
"success": True,
|
|
|
"category": category,
|
|
|
"simulation": simulation,
|
|
|
"conclusion_fa": "حتی با شکست منابع CRITICAL، سیستم موفق به دریافت داده از سطح HIGH شد",
|
|
|
"conclusion_en": "Even with CRITICAL resources failing, system successfully retrieved data from HIGH level",
|
|
|
"no_idle_resources": "هیچ منبعی بیکار نمانده - همه در زنجیره فالبک هستند"
|
|
|
})
|
|
|
|
|
|
except HTTPException:
|
|
|
raise
|
|
|
except Exception as e:
|
|
|
logger.error(f"Error testing fallback: {e}")
|
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
|
|
|
|
|
|
__all__ = ["router"]
|
|
|
|
|
|
|