Spaces:
Sleeping
Sleeping
| /** | |
| * Fallback API Client with Hierarchical Retry System | |
| * سیستم fallback سلسله مراتبی با 10 پشتیبان | |
| */ | |
| class FallbackAPIClient { | |
| constructor() { | |
| // لیست 10 endpoint پشتیبان به ترتیب اولویت | |
| this.endpoints = [ | |
| 'https://Really-amin-crypto-api-clean.hf.space', | |
| 'https://really-amin-datasourceforcryptocurrency-2.hf.space', | |
| 'https://really-amin-datasourceforcryptocurrency.hf.space', | |
| 'http://localhost:7860', | |
| 'http://localhost:8000', | |
| 'https://api.coingecko.com/api/v3', | |
| 'https://api.coincap.io/v2', | |
| 'https://api.binance.com/api/v3', | |
| 'https://api.kraken.com/0/public', | |
| 'https://api.coinbase.com/v2' | |
| ]; | |
| // Cache برای نتایج موفق | |
| this.cache = new Map(); | |
| this.cacheTimeout = 60000; // 1 دقیقه | |
| // آمار برای monitoring | |
| this.stats = { | |
| totalRequests: 0, | |
| successfulRequests: 0, | |
| failedRequests: 0, | |
| endpointStats: {}, | |
| lastSuccessfulEndpoint: null | |
| }; | |
| // Initialize endpoint stats | |
| this.endpoints.forEach(endpoint => { | |
| this.stats.endpointStats[endpoint] = { | |
| requests: 0, | |
| successes: 0, | |
| failures: 0, | |
| avgResponseTime: 0, | |
| lastUsed: null | |
| }; | |
| }); | |
| } | |
| /** | |
| * درخواست با fallback سلسله مراتبی | |
| */ | |
| async request(path, options = {}) { | |
| const { | |
| method = 'GET', | |
| body = null, | |
| headers = {}, | |
| timeout = 10000, | |
| retryCount = 3, | |
| useCache = true | |
| } = options; | |
| // بررسی cache | |
| const cacheKey = `${method}:${path}:${JSON.stringify(body)}`; | |
| if (useCache && method === 'GET') { | |
| const cached = this.getFromCache(cacheKey); | |
| if (cached) { | |
| console.log('✅ Cache hit:', path); | |
| return cached; | |
| } | |
| } | |
| this.stats.totalRequests++; | |
| const errors = []; | |
| // تلاش با هر endpoint به ترتیب | |
| for (let i = 0; i < this.endpoints.length; i++) { | |
| const endpoint = this.endpoints[i]; | |
| const endpointStats = this.stats.endpointStats[endpoint]; | |
| try { | |
| console.log(`🔄 Trying endpoint ${i + 1}/${this.endpoints.length}: ${endpoint}`); | |
| const startTime = Date.now(); | |
| const result = await this.makeRequest(endpoint, path, { | |
| method, | |
| body, | |
| headers, | |
| timeout | |
| }); | |
| const responseTime = Date.now() - startTime; | |
| // بهروزرسانی آمار موفق | |
| endpointStats.requests++; | |
| endpointStats.successes++; | |
| endpointStats.lastUsed = new Date().toISOString(); | |
| endpointStats.avgResponseTime = | |
| (endpointStats.avgResponseTime * (endpointStats.successes - 1) + responseTime) / | |
| endpointStats.successes; | |
| this.stats.successfulRequests++; | |
| this.stats.lastSuccessfulEndpoint = endpoint; | |
| // ذخیره در cache | |
| if (useCache && method === 'GET') { | |
| this.saveToCache(cacheKey, result); | |
| } | |
| console.log(`✅ Success with endpoint ${i + 1}: ${endpoint} (${responseTime}ms)`); | |
| return result; | |
| } catch (error) { | |
| // بهروزرسانی آمار خطا | |
| endpointStats.requests++; | |
| endpointStats.failures++; | |
| errors.push({ | |
| endpoint, | |
| error: error.message, | |
| index: i + 1 | |
| }); | |
| console.warn(`❌ Failed endpoint ${i + 1}/${this.endpoints.length}: ${endpoint}`, error.message); | |
| // اگر آخرین endpoint بود، خطا بده | |
| if (i === this.endpoints.length - 1) { | |
| this.stats.failedRequests++; | |
| throw new Error( | |
| `All ${this.endpoints.length} endpoints failed:\n` + | |
| errors.map(e => `${e.index}. ${e.endpoint}: ${e.error}`).join('\n') | |
| ); | |
| } | |
| // صبر کوتاه قبل از تلاش بعدی | |
| await this.sleep(500); | |
| } | |
| } | |
| } | |
| /** | |
| * ساخت درخواست به یک endpoint | |
| */ | |
| async makeRequest(baseUrl, path, options) { | |
| const { method, body, headers, timeout } = options; | |
| // ساخت URL کامل | |
| const url = this.buildUrl(baseUrl, path); | |
| // ساخت AbortController برای timeout | |
| const controller = new AbortController(); | |
| const timeoutId = setTimeout(() => controller.abort(), timeout); | |
| try { | |
| const response = await fetch(url, { | |
| method, | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| ...headers | |
| }, | |
| body: body ? JSON.stringify(body) : null, | |
| signal: controller.signal | |
| }); | |
| clearTimeout(timeoutId); | |
| if (!response.ok) { | |
| throw new Error(`HTTP ${response.status}: ${response.statusText}`); | |
| } | |
| const data = await response.json(); | |
| return data; | |
| } catch (error) { | |
| clearTimeout(timeoutId); | |
| if (error.name === 'AbortError') { | |
| throw new Error(`Timeout after ${timeout}ms`); | |
| } | |
| throw error; | |
| } | |
| } | |
| /** | |
| * ساخت URL کامل | |
| */ | |
| buildUrl(baseUrl, path) { | |
| // حذف slash اضافی | |
| baseUrl = baseUrl.replace(/\/$/, ''); | |
| path = path.replace(/^\//, ''); | |
| // تطبیق path با endpoint های مختلف | |
| if (baseUrl.includes('coingecko')) { | |
| return this.adaptToCoinGecko(baseUrl, path); | |
| } else if (baseUrl.includes('coincap')) { | |
| return this.adaptToCoinCap(baseUrl, path); | |
| } else if (baseUrl.includes('binance')) { | |
| return this.adaptToBinance(baseUrl, path); | |
| } | |
| // پیشفرض | |
| return `${baseUrl}/${path}`; | |
| } | |
| /** | |
| * تطبیق با CoinGecko API | |
| */ | |
| adaptToCoinGecko(baseUrl, path) { | |
| if (path.includes('/api/coins/top')) { | |
| return `${baseUrl}/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=50`; | |
| } | |
| if (path.includes('/api/trending')) { | |
| return `${baseUrl}/search/trending`; | |
| } | |
| if (path.includes('/api/market')) { | |
| return `${baseUrl}/global`; | |
| } | |
| return `${baseUrl}/${path}`; | |
| } | |
| /** | |
| * تطبیق با CoinCap API | |
| */ | |
| adaptToCoinCap(baseUrl, path) { | |
| if (path.includes('/api/coins/top')) { | |
| return `${baseUrl}/assets?limit=50`; | |
| } | |
| return `${baseUrl}/${path}`; | |
| } | |
| /** | |
| * تطبیق با Binance API | |
| */ | |
| adaptToBinance(baseUrl, path) { | |
| if (path.includes('/api/coins/top')) { | |
| return `${baseUrl}/ticker/24hr`; | |
| } | |
| return `${baseUrl}/${path}`; | |
| } | |
| /** | |
| * Cache management | |
| */ | |
| getFromCache(key) { | |
| const cached = this.cache.get(key); | |
| if (!cached) return null; | |
| const now = Date.now(); | |
| if (now - cached.timestamp > this.cacheTimeout) { | |
| this.cache.delete(key); | |
| return null; | |
| } | |
| return cached.data; | |
| } | |
| saveToCache(key, data) { | |
| this.cache.set(key, { | |
| data, | |
| timestamp: Date.now() | |
| }); | |
| // پاکسازی cache قدیمی | |
| if (this.cache.size > 100) { | |
| const oldestKey = this.cache.keys().next().value; | |
| this.cache.delete(oldestKey); | |
| } | |
| } | |
| /** | |
| * Helper: sleep | |
| */ | |
| sleep(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)); | |
| } | |
| /** | |
| * دریافت آمار | |
| */ | |
| getStats() { | |
| return { | |
| ...this.stats, | |
| successRate: this.stats.totalRequests > 0 | |
| ? (this.stats.successfulRequests / this.stats.totalRequests * 100).toFixed(2) + '%' | |
| : '0%', | |
| cacheSize: this.cache.size | |
| }; | |
| } | |
| /** | |
| * ریست آمار | |
| */ | |
| resetStats() { | |
| this.stats.totalRequests = 0; | |
| this.stats.successfulRequests = 0; | |
| this.stats.failedRequests = 0; | |
| this.endpoints.forEach(endpoint => { | |
| this.stats.endpointStats[endpoint] = { | |
| requests: 0, | |
| successes: 0, | |
| failures: 0, | |
| avgResponseTime: 0, | |
| lastUsed: null | |
| }; | |
| }); | |
| } | |
| /** | |
| * پاکسازی cache | |
| */ | |
| clearCache() { | |
| this.cache.clear(); | |
| } | |
| /** | |
| * تغییر ترتیب endpoints بر اساس عملکرد | |
| */ | |
| optimizeEndpoints() { | |
| // مرتبسازی بر اساس نرخ موفقیت و سرعت | |
| this.endpoints.sort((a, b) => { | |
| const statsA = this.stats.endpointStats[a]; | |
| const statsB = this.stats.endpointStats[b]; | |
| const successRateA = statsA.requests > 0 ? statsA.successes / statsA.requests : 0; | |
| const successRateB = statsB.requests > 0 ? statsB.successes / statsB.requests : 0; | |
| if (successRateA !== successRateB) { | |
| return successRateB - successRateA; // بیشترین موفقیت اول | |
| } | |
| return statsA.avgResponseTime - statsB.avgResponseTime; // سریعتر اول | |
| }); | |
| console.log('✅ Endpoints optimized based on performance'); | |
| } | |
| } | |
| // ============================================================================ | |
| // API Methods با Fallback | |
| // ============================================================================ | |
| class CryptoAPI { | |
| constructor() { | |
| this.client = new FallbackAPIClient(); | |
| } | |
| // Health & Status | |
| async health() { | |
| return this.client.request('/api/health'); | |
| } | |
| async status() { | |
| return this.client.request('/api/status'); | |
| } | |
| // Market Data | |
| async getTopCoins(limit = 50) { | |
| return this.client.request(`/api/coins/top?limit=${limit}`); | |
| } | |
| async getTrending() { | |
| return this.client.request('/api/trending'); | |
| } | |
| async getMarket() { | |
| return this.client.request('/api/market'); | |
| } | |
| // Sentiment | |
| async getGlobalSentiment(timeframe = '1D') { | |
| return this.client.request(`/api/sentiment/global?timeframe=${timeframe}`); | |
| } | |
| async getAssetSentiment(symbol) { | |
| return this.client.request(`/api/sentiment/asset/${symbol}`); | |
| } | |
| // News | |
| async getNews(limit = 50) { | |
| return this.client.request(`/api/news?limit=${limit}`); | |
| } | |
| // Resources | |
| async getResources() { | |
| return this.client.request('/api/resources/summary'); | |
| } | |
| async getCategories() { | |
| return this.client.request('/api/categories'); | |
| } | |
| // Models | |
| async getModels() { | |
| return this.client.request('/api/models/list'); | |
| } | |
| // Stats | |
| getStats() { | |
| return this.client.getStats(); | |
| } | |
| optimizeEndpoints() { | |
| this.client.optimizeEndpoints(); | |
| } | |
| } | |
| // Export | |
| if (typeof module !== 'undefined' && module.exports) { | |
| module.exports = { FallbackAPIClient, CryptoAPI }; | |
| } | |