mirror of
https://github.com/basicswap/basicswap.git
synced 2025-05-04 11:52:18 +00:00
JS: Final tweaks 429
This commit is contained in:
parent
0171ad6889
commit
e92d5560af
1 changed files with 148 additions and 117 deletions
|
@ -141,6 +141,7 @@ const api = {
|
||||||
try {
|
try {
|
||||||
const existingCache = localStorage.getItem(cacheKey);
|
const existingCache = localStorage.getItem(cacheKey);
|
||||||
let fallbackData = null;
|
let fallbackData = null;
|
||||||
|
|
||||||
if (existingCache) {
|
if (existingCache) {
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(existingCache);
|
const parsed = JSON.parse(existingCache);
|
||||||
|
@ -197,12 +198,14 @@ const api = {
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching CoinGecko data:', error);
|
console.error('Error fetching CoinGecko data:', error);
|
||||||
|
|
||||||
const cachedData = cache.get(cacheKey);
|
const cachedData = cache.get(cacheKey);
|
||||||
if (cachedData) {
|
if (cachedData) {
|
||||||
console.log('Using expired cache data due to error');
|
console.log('Using expired cache data due to error');
|
||||||
return cachedData.value;
|
return cachedData.value;
|
||||||
}
|
}
|
||||||
return { error: error.message };
|
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -279,11 +282,12 @@ const api = {
|
||||||
const rateLimiter = {
|
const rateLimiter = {
|
||||||
lastRequestTime: {},
|
lastRequestTime: {},
|
||||||
minRequestInterval: {
|
minRequestInterval: {
|
||||||
coingecko: 15000,
|
coingecko: 30000,
|
||||||
cryptocompare: 1000
|
cryptocompare: 2000
|
||||||
},
|
},
|
||||||
requestQueue: {},
|
requestQueue: {},
|
||||||
|
retryDelays: [2000, 5000, 10000],
|
||||||
|
|
||||||
canMakeRequest: function(apiName) {
|
canMakeRequest: function(apiName) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const lastRequest = this.lastRequestTime[apiName] || 0;
|
const lastRequest = this.lastRequestTime[apiName] || 0;
|
||||||
|
@ -300,7 +304,7 @@ const rateLimiter = {
|
||||||
return Math.max(0, this.minRequestInterval[apiName] - (now - lastRequest));
|
return Math.max(0, this.minRequestInterval[apiName] - (now - lastRequest));
|
||||||
},
|
},
|
||||||
|
|
||||||
queueRequest: async function(apiName, requestFn) {
|
queueRequest: async function(apiName, requestFn, retryCount = 0) {
|
||||||
if (!this.requestQueue[apiName]) {
|
if (!this.requestQueue[apiName]) {
|
||||||
this.requestQueue[apiName] = Promise.resolve();
|
this.requestQueue[apiName] = Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -314,15 +318,31 @@ const rateLimiter = {
|
||||||
await new Promise(resolve => setTimeout(resolve, waitTime));
|
await new Promise(resolve => setTimeout(resolve, waitTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateLastRequestTime(apiName);
|
try {
|
||||||
return await requestFn();
|
this.updateLastRequestTime(apiName);
|
||||||
|
return await requestFn();
|
||||||
|
} catch (error) {
|
||||||
|
if (error.message.includes('429') && retryCount < this.retryDelays.length) {
|
||||||
|
const delay = this.retryDelays[retryCount];
|
||||||
|
console.log(`Rate limit hit, retrying in ${delay/1000} seconds...`);
|
||||||
|
await new Promise(resolve => setTimeout(resolve, delay));
|
||||||
|
return this.queueRequest(apiName, requestFn, retryCount + 1);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.requestQueue[apiName] = executeRequest();
|
this.requestQueue[apiName] = executeRequest();
|
||||||
return await this.requestQueue[apiName];
|
return await this.requestQueue[apiName];
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Queue error for ${apiName}:`, error);
|
if (error.message.includes('429')) {
|
||||||
|
const cachedData = cache.get(`coinData_${apiName}`);
|
||||||
|
if (cachedData) {
|
||||||
|
console.log('Rate limit reached, using cached data');
|
||||||
|
return cachedData.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1180,132 +1200,143 @@ setupEventListeners: () => {
|
||||||
localStorage.setItem('nextRefreshTime', app.nextRefreshTime.toString());
|
localStorage.setItem('nextRefreshTime', app.nextRefreshTime.toString());
|
||||||
app.updateNextRefreshTime();
|
app.updateNextRefreshTime();
|
||||||
},
|
},
|
||||||
refreshAllData: async () => {
|
refreshAllData: async () => {
|
||||||
if (app.isRefreshing) {
|
if (app.isRefreshing) {
|
||||||
console.log('Refresh already in progress, skipping...');
|
console.log('Refresh already in progress, skipping...');
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
const lastGeckoRequest = rateLimiter.lastRequestTime['coingecko'] || 0;
|
|
||||||
const timeSinceLastRequest = Date.now() - lastGeckoRequest;
|
|
||||||
if (timeSinceLastRequest < rateLimiter.minRequestInterval.coingecko) {
|
|
||||||
let waitTime = Math.ceil((rateLimiter.minRequestInterval.coingecko - timeSinceLastRequest) / 1000);
|
|
||||||
|
|
||||||
ui.displayErrorMessage(`Rate limit: Please wait ${waitTime} seconds before refreshing`);
|
|
||||||
|
|
||||||
const countdownInterval = setInterval(() => {
|
|
||||||
waitTime--;
|
|
||||||
if (waitTime > 0) {
|
|
||||||
ui.displayErrorMessage(`Rate limit: Please wait ${waitTime} seconds before refreshing`);
|
|
||||||
} else {
|
|
||||||
clearInterval(countdownInterval);
|
|
||||||
ui.hideErrorMessage();
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Refreshing all data...');
|
|
||||||
app.isRefreshing = true;
|
|
||||||
ui.showLoader();
|
|
||||||
chartModule.showChartLoader();
|
|
||||||
|
|
||||||
try {
|
|
||||||
ui.hideErrorMessage();
|
|
||||||
cache.clear();
|
|
||||||
|
|
||||||
const btcUpdateSuccess = await app.updateBTCPrice();
|
|
||||||
if (!btcUpdateSuccess) {
|
|
||||||
console.warn('BTC price update failed, continuing with cached or default value');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
|
|
||||||
const allCoinData = await api.fetchCoinGeckoDataXHR();
|
const lastGeckoRequest = rateLimiter.lastRequestTime['coingecko'] || 0;
|
||||||
if (allCoinData.error) {
|
const timeSinceLastRequest = Date.now() - lastGeckoRequest;
|
||||||
throw new Error(`CoinGecko API Error: ${allCoinData.error}`);
|
const waitTime = Math.max(0, rateLimiter.minRequestInterval.coingecko - timeSinceLastRequest);
|
||||||
}
|
|
||||||
|
|
||||||
const failedCoins = [];
|
|
||||||
|
|
||||||
for (const coin of config.coins) {
|
|
||||||
const symbol = coin.symbol.toLowerCase();
|
|
||||||
const coinData = allCoinData[symbol];
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!coinData) {
|
|
||||||
throw new Error(`No data received`);
|
|
||||||
}
|
|
||||||
|
|
||||||
coinData.displayName = coin.displayName || coin.symbol;
|
|
||||||
ui.displayCoinData(coin.symbol, coinData);
|
|
||||||
|
|
||||||
const cacheKey = `coinData_${coin.symbol}`;
|
|
||||||
cache.set(cacheKey, coinData);
|
|
||||||
|
|
||||||
} catch (coinError) {
|
|
||||||
console.warn(`Failed to update ${coin.symbol}: ${coinError.message}`);
|
|
||||||
failedCoins.push(coin.symbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||||
|
|
||||||
if (chartModule.currentCoin) {
|
|
||||||
try {
|
|
||||||
await chartModule.updateChart(chartModule.currentCoin, true);
|
|
||||||
} catch (chartError) {
|
|
||||||
console.error('Chart update failed:', chartError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
app.lastRefreshedTime = new Date();
|
|
||||||
localStorage.setItem('lastRefreshedTime', app.lastRefreshedTime.getTime().toString());
|
|
||||||
ui.updateLastRefreshedTime();
|
|
||||||
|
|
||||||
if (failedCoins.length > 0) {
|
if (waitTime > 0) {
|
||||||
const failureMessage = failedCoins.length === config.coins.length
|
const seconds = Math.ceil(waitTime / 1000);
|
||||||
? 'Failed to update any coin data'
|
ui.displayErrorMessage(`Rate limit: Please wait ${seconds} seconds before refreshing`);
|
||||||
: `Failed to update some coins: ${failedCoins.join(', ')}`;
|
|
||||||
|
|
||||||
let countdown = 5;
|
|
||||||
ui.displayErrorMessage(`${failureMessage} (${countdown}s)`);
|
|
||||||
|
|
||||||
|
let remainingTime = seconds;
|
||||||
const countdownInterval = setInterval(() => {
|
const countdownInterval = setInterval(() => {
|
||||||
countdown--;
|
remainingTime--;
|
||||||
if (countdown > 0) {
|
if (remainingTime > 0) {
|
||||||
ui.displayErrorMessage(`${failureMessage} (${countdown}s)`);
|
ui.displayErrorMessage(`Rate limit: Please wait ${remainingTime} seconds before refreshing`);
|
||||||
} else {
|
} else {
|
||||||
clearInterval(countdownInterval);
|
clearInterval(countdownInterval);
|
||||||
ui.hideErrorMessage();
|
ui.hideErrorMessage();
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
console.log(`Refresh completed. Failed coins: ${failedCoins.length}`);
|
|
||||||
} catch (error) {
|
console.log('Starting refresh of all data...');
|
||||||
console.error('Critical error during refresh:', error);
|
app.isRefreshing = true;
|
||||||
let countdown = 10;
|
ui.showLoader();
|
||||||
ui.displayErrorMessage(`Refresh failed: ${error.message}. Please try again later. (${countdown}s)`);
|
chartModule.showChartLoader();
|
||||||
const countdownInterval = setInterval(() => {
|
|
||||||
countdown--;
|
try {
|
||||||
if (countdown > 0) {
|
ui.hideErrorMessage();
|
||||||
ui.displayErrorMessage(`Refresh failed: ${error.message}. Please try again later. (${countdown}s)`);
|
cache.clear();
|
||||||
} else {
|
|
||||||
clearInterval(countdownInterval);
|
const btcUpdateSuccess = await app.updateBTCPrice();
|
||||||
ui.hideErrorMessage();
|
if (!btcUpdateSuccess) {
|
||||||
|
console.warn('BTC price update failed, continuing with cached or default value');
|
||||||
}
|
}
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
} finally {
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
ui.hideLoader();
|
|
||||||
chartModule.hideChartLoader();
|
|
||||||
app.isRefreshing = false;
|
|
||||||
|
|
||||||
if (app.isAutoRefreshEnabled) {
|
const allCoinData = await api.fetchCoinGeckoDataXHR();
|
||||||
app.scheduleNextRefresh();
|
if (allCoinData.error) {
|
||||||
|
throw new Error(`CoinGecko API Error: ${allCoinData.error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const failedCoins = [];
|
||||||
|
|
||||||
|
for (const coin of config.coins) {
|
||||||
|
const symbol = coin.symbol.toLowerCase();
|
||||||
|
const coinData = allCoinData[symbol];
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!coinData) {
|
||||||
|
throw new Error(`No data received`);
|
||||||
|
}
|
||||||
|
|
||||||
|
coinData.displayName = coin.displayName || coin.symbol;
|
||||||
|
ui.displayCoinData(coin.symbol, coinData);
|
||||||
|
|
||||||
|
const cacheKey = `coinData_${coin.symbol}`;
|
||||||
|
cache.set(cacheKey, coinData);
|
||||||
|
|
||||||
|
} catch (coinError) {
|
||||||
|
console.warn(`Failed to update ${coin.symbol}: ${coinError.message}`);
|
||||||
|
failedCoins.push(coin.symbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||||
|
|
||||||
|
if (chartModule.currentCoin) {
|
||||||
|
try {
|
||||||
|
await chartModule.updateChart(chartModule.currentCoin, true);
|
||||||
|
} catch (chartError) {
|
||||||
|
console.error('Chart update failed:', chartError);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.lastRefreshedTime = new Date();
|
||||||
|
localStorage.setItem('lastRefreshedTime', app.lastRefreshedTime.getTime().toString());
|
||||||
|
ui.updateLastRefreshedTime();
|
||||||
|
|
||||||
|
if (failedCoins.length > 0) {
|
||||||
|
const failureMessage = failedCoins.length === config.coins.length
|
||||||
|
? 'Failed to update any coin data'
|
||||||
|
: `Failed to update some coins: ${failedCoins.join(', ')}`;
|
||||||
|
|
||||||
|
let countdown = 5;
|
||||||
|
ui.displayErrorMessage(`${failureMessage} (${countdown}s)`);
|
||||||
|
|
||||||
|
const countdownInterval = setInterval(() => {
|
||||||
|
countdown--;
|
||||||
|
if (countdown > 0) {
|
||||||
|
ui.displayErrorMessage(`${failureMessage} (${countdown}s)`);
|
||||||
|
} else {
|
||||||
|
clearInterval(countdownInterval);
|
||||||
|
ui.hideErrorMessage();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Refresh completed. Failed coins: ${failedCoins.length}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Critical error during refresh:', error);
|
||||||
|
|
||||||
|
|
||||||
|
let countdown = 10;
|
||||||
|
ui.displayErrorMessage(`Refresh failed: ${error.message}. Please try again later. (${countdown}s)`);
|
||||||
|
|
||||||
|
const countdownInterval = setInterval(() => {
|
||||||
|
countdown--;
|
||||||
|
if (countdown > 0) {
|
||||||
|
ui.displayErrorMessage(`Refresh failed: ${error.message}. Please try again later. (${countdown}s)`);
|
||||||
|
} else {
|
||||||
|
clearInterval(countdownInterval);
|
||||||
|
ui.hideErrorMessage();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
ui.hideLoader();
|
||||||
|
chartModule.hideChartLoader();
|
||||||
|
app.isRefreshing = false;
|
||||||
|
|
||||||
|
if (app.isAutoRefreshEnabled) {
|
||||||
|
app.scheduleNextRefresh();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
|
||||||
|
|
||||||
updateNextRefreshTime: () => {
|
updateNextRefreshTime: () => {
|
||||||
console.log('Updating next refresh time display');
|
console.log('Updating next refresh time display');
|
||||||
|
|
Loading…
Reference in a new issue