|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const API_BASE_URL = window.location.origin; |
|
|
|
|
|
|
|
|
window.API_CONFIG = { |
|
|
|
|
|
baseUrl: API_BASE_URL, |
|
|
apiUrl: `${API_BASE_URL}/api`, |
|
|
smartApiUrl: `${API_BASE_URL}/api/smart`, |
|
|
|
|
|
|
|
|
endpoints: { |
|
|
|
|
|
smart: { |
|
|
market: `${API_BASE_URL}/api/smart/market`, |
|
|
news: `${API_BASE_URL}/api/smart/news`, |
|
|
sentiment: `${API_BASE_URL}/api/smart/sentiment`, |
|
|
whaleAlerts: `${API_BASE_URL}/api/smart/whale-alerts`, |
|
|
blockchain: `${API_BASE_URL}/api/smart/blockchain`, |
|
|
healthReport: `${API_BASE_URL}/api/smart/health-report`, |
|
|
stats: `${API_BASE_URL}/api/smart/stats`, |
|
|
}, |
|
|
|
|
|
|
|
|
market: `${API_BASE_URL}/api/market`, |
|
|
marketHistory: `${API_BASE_URL}/api/market/history`, |
|
|
sentiment: `${API_BASE_URL}/api/sentiment/analyze`, |
|
|
health: `${API_BASE_URL}/api/health`, |
|
|
|
|
|
|
|
|
alphavantage: { |
|
|
health: `${API_BASE_URL}/api/alphavantage/health`, |
|
|
prices: `${API_BASE_URL}/api/alphavantage/prices`, |
|
|
ohlcv: `${API_BASE_URL}/api/alphavantage/ohlcv`, |
|
|
marketStatus: `${API_BASE_URL}/api/alphavantage/market-status`, |
|
|
cryptoRating: `${API_BASE_URL}/api/alphavantage/crypto-rating`, |
|
|
quote: `${API_BASE_URL}/api/alphavantage/quote`, |
|
|
}, |
|
|
|
|
|
|
|
|
massive: { |
|
|
health: `${API_BASE_URL}/api/massive/health`, |
|
|
dividends: `${API_BASE_URL}/api/massive/dividends`, |
|
|
splits: `${API_BASE_URL}/api/massive/splits`, |
|
|
quotes: `${API_BASE_URL}/api/massive/quotes`, |
|
|
trades: `${API_BASE_URL}/api/massive/trades`, |
|
|
aggregates: `${API_BASE_URL}/api/massive/aggregates`, |
|
|
ticker: `${API_BASE_URL}/api/massive/ticker`, |
|
|
marketStatus: `${API_BASE_URL}/api/massive/market-status`, |
|
|
}, |
|
|
|
|
|
|
|
|
docs: `${API_BASE_URL}/docs`, |
|
|
redoc: `${API_BASE_URL}/redoc`, |
|
|
}, |
|
|
|
|
|
|
|
|
features: { |
|
|
useSmartFallback: true, |
|
|
resourceRotation: true, |
|
|
proxySupport: true, |
|
|
backgroundCollection: true, |
|
|
healthMonitoring: true, |
|
|
autoCleanup: true, |
|
|
}, |
|
|
|
|
|
|
|
|
request: { |
|
|
timeout: 30000, |
|
|
retries: 3, |
|
|
retryDelay: 1000, |
|
|
}, |
|
|
|
|
|
|
|
|
resources: { |
|
|
total: '305+', |
|
|
categories: { |
|
|
marketData: 21, |
|
|
blockExplorers: 40, |
|
|
news: 15, |
|
|
sentiment: 12, |
|
|
whaleTracking: 9, |
|
|
onchainAnalytics: 13, |
|
|
rpcNodes: 24, |
|
|
localBackend: 106, |
|
|
corsProxies: 7, |
|
|
} |
|
|
} |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SmartAPIClient { |
|
|
constructor(config = window.API_CONFIG) { |
|
|
this.config = config; |
|
|
this.authToken = this.getAuthToken(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getAuthToken() { |
|
|
|
|
|
let token = localStorage.getItem('hf_token'); |
|
|
|
|
|
|
|
|
if (!token) { |
|
|
token = sessionStorage.getItem('hf_token'); |
|
|
} |
|
|
|
|
|
|
|
|
if (!token) { |
|
|
const params = new URLSearchParams(window.location.search); |
|
|
token = params.get('token'); |
|
|
} |
|
|
|
|
|
return token; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setAuthToken(token) { |
|
|
this.authToken = token; |
|
|
localStorage.setItem('hf_token', token); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getHeaders() { |
|
|
const headers = { |
|
|
'Content-Type': 'application/json', |
|
|
'Accept': 'application/json', |
|
|
}; |
|
|
|
|
|
if (this.authToken) { |
|
|
headers['Authorization'] = `Bearer ${this.authToken}`; |
|
|
} |
|
|
|
|
|
return headers; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async fetchWithRetry(url, options = {}, retries = 3) { |
|
|
for (let i = 0; i < retries; i++) { |
|
|
try { |
|
|
const response = await fetch(url, { |
|
|
...options, |
|
|
headers: { |
|
|
...this.getHeaders(), |
|
|
...options.headers, |
|
|
}, |
|
|
timeout: this.config.request.timeout, |
|
|
}); |
|
|
|
|
|
if (!response.ok) { |
|
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`); |
|
|
} |
|
|
|
|
|
return await response.json(); |
|
|
} catch (error) { |
|
|
console.warn(`Attempt ${i + 1} failed:`, error); |
|
|
|
|
|
if (i === retries - 1) { |
|
|
throw error; |
|
|
} |
|
|
|
|
|
|
|
|
await new Promise(resolve => |
|
|
setTimeout(resolve, this.config.request.retryDelay * (i + 1)) |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getMarketData(limit = 100) { |
|
|
try { |
|
|
|
|
|
return await this.fetchWithRetry( |
|
|
`${this.config.endpoints.smart.market}?limit=${limit}` |
|
|
); |
|
|
} catch (error) { |
|
|
console.error('Smart market data failed:', error); |
|
|
|
|
|
|
|
|
try { |
|
|
return await this.fetchWithRetry( |
|
|
`${this.config.endpoints.market}?limit=${limit}` |
|
|
); |
|
|
} catch (fallbackError) { |
|
|
console.error('All market data endpoints failed'); |
|
|
throw fallbackError; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getNews(limit = 20) { |
|
|
try { |
|
|
return await this.fetchWithRetry( |
|
|
`${this.config.endpoints.smart.news}?limit=${limit}` |
|
|
); |
|
|
} catch (error) { |
|
|
console.error('Smart news failed:', error); |
|
|
throw error; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getSentiment(symbol = null) { |
|
|
const url = symbol |
|
|
? `${this.config.endpoints.smart.sentiment}?symbol=${symbol}` |
|
|
: this.config.endpoints.smart.sentiment; |
|
|
|
|
|
try { |
|
|
return await this.fetchWithRetry(url); |
|
|
} catch (error) { |
|
|
console.error('Smart sentiment failed:', error); |
|
|
throw error; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getWhaleAlerts(limit = 20) { |
|
|
try { |
|
|
return await this.fetchWithRetry( |
|
|
`${this.config.endpoints.smart.whaleAlerts}?limit=${limit}` |
|
|
); |
|
|
} catch (error) { |
|
|
console.error('Smart whale alerts failed:', error); |
|
|
throw error; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getBlockchainData(chain = 'ethereum') { |
|
|
try { |
|
|
return await this.fetchWithRetry( |
|
|
`${this.config.endpoints.smart.blockchain}/${chain}` |
|
|
); |
|
|
} catch (error) { |
|
|
console.error('Smart blockchain data failed:', error); |
|
|
throw error; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getHealthReport() { |
|
|
try { |
|
|
return await this.fetchWithRetry( |
|
|
this.config.endpoints.smart.healthReport |
|
|
); |
|
|
} catch (error) { |
|
|
console.error('Health report failed:', error); |
|
|
throw error; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getStats() { |
|
|
try { |
|
|
return await this.fetchWithRetry( |
|
|
this.config.endpoints.smart.stats |
|
|
); |
|
|
} catch (error) { |
|
|
console.error('Stats failed:', error); |
|
|
throw error; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getAlphaVantageData(endpoint, params = {}) { |
|
|
const url = new URL(endpoint); |
|
|
Object.keys(params).forEach(key => |
|
|
url.searchParams.append(key, params[key]) |
|
|
); |
|
|
|
|
|
try { |
|
|
return await this.fetchWithRetry(url.toString()); |
|
|
} catch (error) { |
|
|
console.error('Alpha Vantage request failed:', error); |
|
|
throw error; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async getMassiveData(endpoint, params = {}) { |
|
|
const url = new URL(endpoint); |
|
|
Object.keys(params).forEach(key => |
|
|
url.searchParams.append(key, params[key]) |
|
|
); |
|
|
|
|
|
try { |
|
|
return await this.fetchWithRetry(url.toString()); |
|
|
} catch (error) { |
|
|
console.error('Massive.com request failed:', error); |
|
|
throw error; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
window.apiClient = new SmartAPIClient(); |
|
|
|
|
|
|
|
|
if (typeof module !== 'undefined' && module.exports) { |
|
|
module.exports = { API_CONFIG, SmartAPIClient }; |
|
|
} |
|
|
|
|
|
console.log('β
API Configuration loaded successfully'); |
|
|
console.log('π Smart Fallback System: 305+ resources available'); |
|
|
console.log('π Resource rotation: ENABLED'); |
|
|
console.log('π Proxy support: ENABLED'); |
|
|
console.log('β¨ Features:', window.API_CONFIG.features); |
|
|
|