|
|
|
|
|
""" |
|
|
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!)"], |
|
|
"purpose": "DNS resolution services" |
|
|
} |
|
|
}, |
|
|
"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"] |
|
|
|
|
|
|