/** * 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 }; }