@@ -28,6 +28,7 @@ import {
2828 addIso ,
2929 fromCode ,
3030 isIsoCode ,
31+ logger ,
3132 normalizeDate ,
3233 subIso ,
3334 toCode ,
@@ -329,62 +330,110 @@ const getRedisMarkets = async (
329330 const now = new Date ( )
330331 const nowTimestamp = now . getTime ( )
331332
332- const jsonString = await getAsync ( `${ REDIS_COINRANK_KEY_PREFIX } _${ fiatCode } ` )
333- let redisResult : CoinrankRedis = JSON . parse ( jsonString )
334-
335- if ( fiatCode !== defaultFiatCode ) {
336- const lastUpdated =
337- redisResult != null ? new Date ( redisResult . lastUpdate ) . getTime ( ) : 0
338-
339- // If no result in redis or it's out of date
340- if ( redisResult == null || nowTimestamp - lastUpdated > EXPIRE_TIME ) {
341- // Attempt to scale prices by foreign exchange rate
333+ try {
334+ // First try to get data for the requested fiat code
335+ const jsonString = await getAsync (
336+ `${ REDIS_COINRANK_KEY_PREFIX } _${ fiatCode } `
337+ )
342338
343- // Get exchange rate
344- const result = await fetch (
345- `${ ratesServerAddress } /v2/exchangeRate?currency_pair=${ defaultFiatCode } _${ fiatCode } `
346- )
347- const resultJson = await result . json ( )
348- const { exchangeRate } = asExchangeRateResponse ( resultJson )
349- const rate = Number ( exchangeRate )
339+ // Only parse if jsonString is not null/undefined
340+ let redisResult : CoinrankRedis | undefined
341+ if ( jsonString !== null && jsonString !== undefined ) {
342+ try {
343+ redisResult = JSON . parse ( jsonString )
344+
345+ // If we have valid data that hasn't expired, return it
346+ const lastUpdated =
347+ redisResult != null ? new Date ( redisResult . lastUpdate ) . getTime ( ) : 0
348+ if ( nowTimestamp - lastUpdated <= EXPIRE_TIME ) {
349+ return redisResult
350+ }
351+ // We have cached data but it's expired - we'll try to refresh it below
352+ // but will fall back to this expired data if refresh fails
353+ } catch ( e ) {
354+ // JSON parsing error, redisResult remains undefined
355+ logger ( `Error parsing Redis data for ${ fiatCode } : ${ e } ` )
356+ // Continue to try to get fresh data
357+ }
358+ }
350359
351- // Get USD rankings
352- const jsonString = await getAsync (
353- `${ REDIS_COINRANK_KEY_PREFIX } _${ defaultFiatCode } `
354- )
355- redisResult = JSON . parse ( jsonString )
356- let { markets } = redisResult
357-
358- // Modify fiat-related fields with the forex rate
359- markets = markets . map ( m => ( {
360- ...m ,
361- marketCap : m . marketCap * rate ,
362- price : m . price * rate ,
363- volume24h : m . volume24h * rate ,
364- high24h : m . high24h * rate ,
365- low24h : m . low24h * rate ,
366- priceChange24h : m . priceChange24h * rate ,
367- marketCapChange24h : m . marketCapChange24h * rate ,
368- circulatingSupply : m . circulatingSupply * rate ,
369- totalSupply : m . totalSupply * rate ,
370- maxSupply : m . maxSupply * rate ,
371- allTimeHigh : m . allTimeHigh * rate ,
372- allTimeLow : m . allTimeLow * rate
373- } ) )
374-
375- // Update redis cache
376- const redisData : CoinrankRedis = {
377- markets,
378- lastUpdate : now . toISOString ( )
360+ // If we need to convert from USD (either no data or expired data)
361+ if ( fiatCode !== defaultFiatCode ) {
362+ try {
363+ // Get exchange rate
364+ const result = await fetch (
365+ `${ ratesServerAddress } /v2/exchangeRate?currency_pair=${ defaultFiatCode } _${ fiatCode } `
366+ )
367+ if ( ! result . ok ) {
368+ throw new Error ( `Exchange rate API returned status ${ result . status } ` )
369+ }
370+
371+ const resultJson = await result . json ( )
372+ const { exchangeRate } = asExchangeRateResponse ( resultJson )
373+ const rate = Number ( exchangeRate )
374+
375+ // Validate the rate
376+ if ( rate == null || isNaN ( rate ) || rate <= 0 ) {
377+ throw new Error ( `Invalid exchange rate: ${ exchangeRate } ` )
378+ }
379+
380+ // Get USD rankings
381+ const usdJsonString = await getAsync (
382+ `${ REDIS_COINRANK_KEY_PREFIX } _${ defaultFiatCode } `
383+ )
384+ if ( usdJsonString == null ) {
385+ throw new Error ( `No USD data available in Redis` )
386+ }
387+
388+ const usdRedisResult = JSON . parse ( usdJsonString )
389+ let { markets } = usdRedisResult
390+
391+ // Modify fiat-related fields with the forex rate
392+ markets = markets . map ( m => ( {
393+ ...m ,
394+ marketCap : m . marketCap * rate ,
395+ price : m . price * rate ,
396+ volume24h : m . volume24h * rate ,
397+ high24h : m . high24h * rate ,
398+ low24h : m . low24h * rate ,
399+ priceChange24h : m . priceChange24h * rate ,
400+ marketCapChange24h : m . marketCapChange24h * rate ,
401+ circulatingSupply : m . circulatingSupply * rate ,
402+ totalSupply : m . totalSupply * rate ,
403+ maxSupply : m . maxSupply * rate ,
404+ allTimeHigh : m . allTimeHigh * rate ,
405+ allTimeLow : m . allTimeLow * rate
406+ } ) )
407+
408+ // Update redis cache
409+ const redisData : CoinrankRedis = {
410+ markets,
411+ lastUpdate : now . toISOString ( )
412+ }
413+ await setAsync (
414+ `${ REDIS_COINRANK_KEY_PREFIX } _${ fiatCode } ` ,
415+ JSON . stringify ( redisData )
416+ )
417+
418+ return redisData
419+ } catch ( e ) {
420+ logger ( `Error converting USD data to ${ fiatCode } : ${ e } ` )
421+ // If conversion fails but we have cached data (even if expired), return that
422+ if ( redisResult != null ) {
423+ logger ( `Falling back to cached data for ${ fiatCode } ` )
424+ return redisResult
425+ }
426+ // Only return undefined if we have no cached data at all
427+ return undefined
379428 }
380- await setAsync (
381- `${ REDIS_COINRANK_KEY_PREFIX } _${ fiatCode } ` ,
382- JSON . stringify ( redisData )
383- )
429+ } else {
430+ // For USD requests when data is missing or expired
431+ return redisResult
384432 }
433+ } catch ( e ) {
434+ logger ( `Error in getRedisMarkets for ${ fiatCode } : ${ e } ` )
435+ return undefined
385436 }
386-
387- return redisResult
388437}
389438
390439const sendCoinrankList : express . RequestHandler = async (
0 commit comments