crypto-api-clean / static /shared /js /fallback-api-client.js
Really-amin's picture
Update static/shared/js/fallback-api-client.js
d362646 verified
/**
* 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 };
}