Really-amin's picture
Upload 196 files
dd7ffbd verified
const DEFAULT_TTL = 60 * 1000; // 1 minute cache
class ApiClient {
constructor() {
const origin = window?.location?.origin ?? '';
this.baseURL = origin.replace(/\/$/, '');
this.cache = new Map();
this.requestLogs = [];
this.errorLogs = [];
this.logSubscribers = new Set();
this.errorSubscribers = new Set();
}
buildUrl(endpoint) {
if (!endpoint.startsWith('/')) {
return `${this.baseURL}/${endpoint}`;
}
return `${this.baseURL}${endpoint}`;
}
notifyLog(entry) {
this.requestLogs.push(entry);
this.requestLogs = this.requestLogs.slice(-100);
this.logSubscribers.forEach((cb) => cb(entry));
}
notifyError(entry) {
this.errorLogs.push(entry);
this.errorLogs = this.errorLogs.slice(-100);
this.errorSubscribers.forEach((cb) => cb(entry));
}
onLog(callback) {
this.logSubscribers.add(callback);
return () => this.logSubscribers.delete(callback);
}
onError(callback) {
this.errorSubscribers.add(callback);
return () => this.errorSubscribers.delete(callback);
}
getLogs() {
return [...this.requestLogs];
}
getErrors() {
return [...this.errorLogs];
}
async request(method, endpoint, { body, cache = true, ttl = DEFAULT_TTL } = {}) {
const url = this.buildUrl(endpoint);
const cacheKey = `${method}:${url}`;
if (method === 'GET' && cache && this.cache.has(cacheKey)) {
const cached = this.cache.get(cacheKey);
if (Date.now() - cached.timestamp < ttl) {
return { ok: true, data: cached.data, cached: true };
}
}
const started = performance.now();
const randomId = (window.crypto && window.crypto.randomUUID && window.crypto.randomUUID())
|| `${Date.now()}-${Math.random()}`;
const entry = {
id: randomId,
method,
endpoint,
status: 'pending',
duration: 0,
time: new Date().toISOString(),
};
try {
const response = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
},
body: body ? JSON.stringify(body) : undefined,
});
const duration = performance.now() - started;
entry.duration = Math.round(duration);
entry.status = response.status;
const contentType = response.headers.get('content-type') || '';
let data = null;
if (contentType.includes('application/json')) {
data = await response.json();
} else if (contentType.includes('text')) {
data = await response.text();
}
if (!response.ok) {
const error = new Error((data && data.message) || response.statusText || 'Unknown error');
error.status = response.status;
throw error;
}
if (method === 'GET' && cache) {
this.cache.set(cacheKey, { timestamp: Date.now(), data });
}
this.notifyLog({ ...entry, success: true });
return { ok: true, data };
} catch (error) {
const duration = performance.now() - started;
entry.duration = Math.round(duration);
entry.status = error.status || 'error';
this.notifyLog({ ...entry, success: false, error: error.message });
this.notifyError({
message: error.message,
endpoint,
method,
time: new Date().toISOString(),
});
return { ok: false, error: error.message };
}
}
get(endpoint, options) {
return this.request('GET', endpoint, options);
}
post(endpoint, body, options = {}) {
return this.request('POST', endpoint, { ...options, body });
}
// ===== Specific API helpers =====
getHealth() {
return this.get('/api/health');
}
getTopCoins(limit = 10) {
return this.get(`/api/coins/top?limit=${limit}`);
}
getCoinDetails(symbol) {
return this.get(`/api/coins/${symbol}`);
}
getMarketStats() {
return this.get('/api/market/stats');
}
getLatestNews(limit = 20) {
return this.get(`/api/news/latest?limit=${limit}`);
}
getProviders() {
return this.get('/api/providers');
}
getPriceChart(symbol, timeframe = '7d') {
return this.get(`/api/charts/price/${symbol}?timeframe=${timeframe}`);
}
analyzeChart(symbol, timeframe = '7d', indicators = []) {
return this.post('/api/charts/analyze', { symbol, timeframe, indicators });
}
runQuery(payload) {
return this.post('/api/query', payload);
}
analyzeSentiment(payload) {
return this.post('/api/sentiment/analyze', payload);
}
summarizeNews(item) {
return this.post('/api/news/summarize', item);
}
getDatasetsList() {
return this.get('/api/datasets/list');
}
getDatasetSample(name) {
return this.get(`/api/datasets/sample?name=${encodeURIComponent(name)}`);
}
getModelsList() {
return this.get('/api/models/list');
}
testModel(payload) {
return this.post('/api/models/test', payload);
}
}
const apiClient = new ApiClient();
export default apiClient;