|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|