File size: 4,624 Bytes
3a660a3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
"""
Smart Provider API Router
Exposes smart provider service with rate limiting, caching, and intelligent fallback
"""

from fastapi import APIRouter, HTTPException, Query
from fastapi.responses import JSONResponse
from typing import List, Optional
import logging

from backend.services.smart_provider_service import get_smart_provider_service

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/api/smart-providers", tags=["Smart Providers"])


@router.get("/market-prices")
async def get_market_prices(
    symbols: Optional[str] = Query(None, description="Comma-separated list of symbols (e.g., BTC,ETH,BNB)"),
    limit: int = Query(100, ge=1, le=250, description="Number of results to return")
):
    """
    Get market prices with smart provider fallback
    
    Features:
    - Smart provider rotation (Binance → CoinCap → CoinGecko)
    - Automatic rate limit handling with exponential backoff
    - Provider-specific caching (30s to 5min)
    - 429 error prevention for CoinGecko
    """
    try:
        service = get_smart_provider_service()
        
        # Parse symbols
        symbol_list = None
        if symbols:
            symbol_list = [s.strip().upper() for s in symbols.split(',')]
        
        # Get prices with smart fallback
        result = await service.get_market_prices(symbols=symbol_list, limit=limit)
        
        return JSONResponse(content={
            "success": True,
            "data": result['data'],
            "meta": {
                "source": result['source'],
                "cached": result.get('cached', False),
                "timestamp": result['timestamp'],
                "count": len(result['data']),
                "error": result.get('error')
            }
        })
    
    except Exception as e:
        logger.error(f"Error fetching market prices: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/provider-stats")
async def get_provider_stats():
    """
    Get statistics for all providers
    
    Returns:
    - Provider health status
    - Success/failure rates
    - Rate limit hits
    - Backoff status
    - Cache statistics
    """
    try:
        service = get_smart_provider_service()
        stats = service.get_provider_stats()
        
        return JSONResponse(content={
            "success": True,
            "stats": stats
        })
    
    except Exception as e:
        logger.error(f"Error fetching provider stats: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/reset-provider/{provider_name}")
async def reset_provider(provider_name: str):
    """
    Reset a specific provider's backoff and stats
    
    Use this to manually reset a provider that's in backoff mode
    """
    try:
        service = get_smart_provider_service()
        service.reset_provider(provider_name)
        
        return JSONResponse(content={
            "success": True,
            "message": f"Provider {provider_name} reset successfully"
        })
    
    except Exception as e:
        logger.error(f"Error resetting provider: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.post("/clear-cache")
async def clear_cache():
    """
    Clear all cached data
    
    Use this to force fresh data from providers
    """
    try:
        service = get_smart_provider_service()
        service.clear_cache()
        
        return JSONResponse(content={
            "success": True,
            "message": "Cache cleared successfully"
        })
    
    except Exception as e:
        logger.error(f"Error clearing cache: {e}")
        raise HTTPException(status_code=500, detail=str(e))


@router.get("/health")
async def health_check():
    """
    Check health of smart provider service
    """
    try:
        service = get_smart_provider_service()
        stats = service.get_provider_stats()
        
        # Count available providers
        available_count = sum(
            1 for p in stats['providers'].values() 
            if p.get('is_available', False)
        )
        
        total_count = len(stats['providers'])
        
        return JSONResponse(content={
            "success": True,
            "status": "healthy" if available_count > 0 else "degraded",
            "available_providers": available_count,
            "total_providers": total_count,
            "cache_entries": stats['cache']['valid_entries']
        })
    
    except Exception as e:
        logger.error(f"Error checking health: {e}")
        raise HTTPException(status_code=500, detail=str(e))


__all__ = ["router"]