Your Name
feat: UI improvements and error suppression - Enhanced dashboard and market pages with improved header buttons, logo, and currency symbol display - Stopped animated ticker - Removed pie chart legends - Added error suppressor for external service errors (SSE, Permissions-Policy warnings) - Improved header button prominence and icon appearance - Enhanced logo with glow effects and better design - Fixed currency symbol visibility in market tables
8b7b267
| """ | |
| Async HTTP Client with Retry Logic | |
| """ | |
| import aiohttp | |
| import asyncio | |
| from typing import Dict, Optional, Any | |
| from datetime import datetime | |
| import logging | |
| logger = logging.getLogger(__name__) | |
| class APIClient: | |
| def __init__(self, timeout: int = 10, max_retries: int = 3): | |
| self.timeout = aiohttp.ClientTimeout(total=timeout) | |
| self.max_retries = max_retries | |
| self.session: Optional[aiohttp.ClientSession] = None | |
| async def __aenter__(self): | |
| self.session = aiohttp.ClientSession(timeout=self.timeout) | |
| return self | |
| async def __aexit__(self, exc_type, exc_val, exc_tb): | |
| if self.session: | |
| await self.session.close() | |
| async def get( | |
| self, | |
| url: str, | |
| headers: Optional[Dict] = None, | |
| params: Optional[Dict] = None, | |
| retry_count: int = 0 | |
| ) -> Dict[str, Any]: | |
| """Make GET request with retry logic""" | |
| start_time = datetime.utcnow() | |
| try: | |
| async with self.session.get(url, headers=headers, params=params) as response: | |
| elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000) | |
| # Try to parse JSON response | |
| try: | |
| data = await response.json() | |
| except: | |
| data = await response.text() | |
| return { | |
| "success": response.status == 200, | |
| "status_code": response.status, | |
| "data": data, | |
| "response_time_ms": elapsed_ms, | |
| "error": None if response.status == 200 else { | |
| "type": "http_error", | |
| "message": f"HTTP {response.status}" | |
| } | |
| } | |
| except asyncio.TimeoutError: | |
| elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000) | |
| if retry_count < self.max_retries: | |
| logger.warning(f"Timeout for {url}, retrying ({retry_count + 1}/{self.max_retries})") | |
| await asyncio.sleep(2 ** retry_count) # Exponential backoff | |
| return await self.get(url, headers, params, retry_count + 1) | |
| return { | |
| "success": False, | |
| "status_code": 0, | |
| "data": None, | |
| "response_time_ms": elapsed_ms, | |
| "error": {"type": "timeout", "message": "Request timeout"} | |
| } | |
| except aiohttp.ClientError as e: | |
| elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000) | |
| return { | |
| "success": False, | |
| "status_code": 0, | |
| "data": None, | |
| "response_time_ms": elapsed_ms, | |
| "error": {"type": "client_error", "message": str(e)} | |
| } | |
| except Exception as e: | |
| elapsed_ms = int((datetime.utcnow() - start_time).total_seconds() * 1000) | |
| logger.error(f"Unexpected error for {url}: {e}") | |
| return { | |
| "success": False, | |
| "status_code": 0, | |
| "data": None, | |
| "response_time_ms": elapsed_ms, | |
| "error": {"type": "unknown", "message": str(e)} | |
| } | |