From 1e46001448898fd859375dda8b0081b3f4dfaab9 Mon Sep 17 00:00:00 2001 From: Tom Kirkpatrick Date: Sun, 7 Jan 2024 07:56:43 +0100 Subject: [PATCH 1/4] Move homepage to root url --- src/custom.d.ts | 2 +- src/server.tsx | 86 +++++++++++++++++++++++++++++-------------------- 2 files changed, 52 insertions(+), 36 deletions(-) diff --git a/src/custom.d.ts b/src/custom.d.ts index 4e475fb..af1261b 100644 --- a/src/custom.d.ts +++ b/src/custom.d.ts @@ -15,7 +15,7 @@ type FeeByBlockTarget = { [key: string]: number; }; -type Data = { +type Estimates = { current_block_hash: string | null; fee_by_block_target: FeeByBlockTarget; }; diff --git a/src/server.tsx b/src/server.tsx index 0124e4e..f1f3212 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -24,7 +24,7 @@ const TIMEOUT: number = 3000; console.info('---'); console.info(`Using port: ${port}`); console.info(`Using base URL: ${baseUrl}`); -console.info(`Using Esplora host: ${esploraBaseUrl}`); +console.info(`Using Esplora base URL: ${esploraBaseUrl}`); console.info(`Using Mempool base URL: ${mempoolBaseUrl}`); console.info(`Using Mempool estimation depth: ${mempoolDepth}`); console.info(`Using fee multiplier: ${feeMultiplier}`); @@ -130,6 +130,31 @@ async function fetchData() { return await Promise.allSettled(tasks); } +/** + * Gets the current fee estimates from the cache or fetches them if they are not cached. + */ +async function getEstimates() : Promise { + let estimates: Estimates | undefined = cache.get('estimates'); + + if (!estimates) { + const results = await fetchData(); + console.debug('Fetch tasks completed', results); + + const { blocksTipHash, mempoolFeeEstimates, esploraFeeEstimates: esploraFeeEstimates } = assignResults(results); + const feeByBlockTarget = calculateFees(mempoolFeeEstimates, esploraFeeEstimates); + + estimates = { + current_block_hash: blocksTipHash, + fee_by_block_target: feeByBlockTarget + }; + + cache.set('estimates', estimates); + } + + console.debug('Got estimates', estimates); + return estimates; +} + /** * Assigns the results of the fetch tasks to variables. */ @@ -213,7 +238,7 @@ const Layout = (props: SiteData) => { ) } -const Content = (props: { siteData: SiteData; data: object }) => ( +const Content = (props: { siteData: SiteData; estimates: Estimates }) => (
@@ -247,7 +272,7 @@ const Content = (props: { siteData: SiteData; data: object }) => (
-          {raw(JSON.stringify(props.data, null, 2))}
+          {raw(JSON.stringify(props.estimates, null, 2))}
         
@@ -258,44 +283,35 @@ const Content = (props: { siteData: SiteData; data: object }) => ( /** * Returns the current fee estimates for the Bitcoin network. */ -app.get('/v1/fee-estimates', async (c) => { - try { - let data: Data | undefined = cache.get('data'); - - if (!data) { - const results = await fetchData(); - console.debug('Fetch tasks completed', results); - - const { blocksTipHash, mempoolFeeEstimates, esploraFeeEstimates: esploraFeeEstimates } = assignResults(results); - const feeByBlockTarget = calculateFees(mempoolFeeEstimates, esploraFeeEstimates); - - data = { - current_block_hash: blocksTipHash, - fee_by_block_target: feeByBlockTarget - }; +app.get('/', async (c) => { + var estimates = await getEstimates(); + + // Set cache headers. + c.res.headers.set('Cache-Control', `public, max-age=${stdTTL}`) + + const props = { + siteData: { + title: 'Bitcoin Blended Fee Estimator', + subtitle: 'A blend of mempool-based and history-based Bitcoin fee estimates.', + }, + estimates, + } - cache.set('data', data); - } + return c.html(); +}); - console.debug('Returning data', data); +/** + * Returns the current fee estimates for the Bitcoin network. + */ +app.get('/v1/fee-estimates', async (c) => { + try { + var estimates = await getEstimates(); // Set cache headers. c.res.headers.set('Cache-Control', `public, max-age=${stdTTL}`) - // Return html if the request accepts it. - if (c.req.raw.headers.get('Accept')?.includes('text/html')) { - const props = { - siteData: { - title: 'Bitcoin Blended Fee Estimator', - subtitle: 'A blend of mempool-based and history-based Bitcoin fee estimates.', - }, - data, - } - return c.html() - } - - // Otherwise return json. - return c.json(data); + // Return the estimates. + return c.json(estimates); } catch (error) { console.error(error); From 48a01ecba92b0713303682a6a6d08ee95adb6f93 Mon Sep 17 00:00:00 2001 From: Tom Kirkpatrick Date: Sun, 7 Jan 2024 07:58:01 +0100 Subject: [PATCH 2/4] Remove redundent prop --- src/server.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.tsx b/src/server.tsx index f1f3212..c2af58f 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -140,7 +140,7 @@ async function getEstimates() : Promise { const results = await fetchData(); console.debug('Fetch tasks completed', results); - const { blocksTipHash, mempoolFeeEstimates, esploraFeeEstimates: esploraFeeEstimates } = assignResults(results); + const { blocksTipHash, mempoolFeeEstimates, esploraFeeEstimates } = assignResults(results); const feeByBlockTarget = calculateFees(mempoolFeeEstimates, esploraFeeEstimates); estimates = { From 500c3d65e53f3bc34778ff5d17b79fb0190092c8 Mon Sep 17 00:00:00 2001 From: Tom Kirkpatrick Date: Sun, 7 Jan 2024 07:58:52 +0100 Subject: [PATCH 3/4] Extract cache key to const --- src/server.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/server.tsx b/src/server.tsx index c2af58f..d3f8067 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -34,6 +34,7 @@ console.info('---'); // Initialize the cache. const cache = new NodeCache({ stdTTL: stdTTL, checkperiod: checkperiod }); +const CACHE_KEY = 'estimates'; /** * Fetches data from the given URL with a timeout. @@ -134,7 +135,7 @@ async function fetchData() { * Gets the current fee estimates from the cache or fetches them if they are not cached. */ async function getEstimates() : Promise { - let estimates: Estimates | undefined = cache.get('estimates'); + let estimates: Estimates | undefined = cache.get(CACHE_KEY); if (!estimates) { const results = await fetchData(); @@ -148,7 +149,7 @@ async function getEstimates() : Promise { fee_by_block_target: feeByBlockTarget }; - cache.set('estimates', estimates); + cache.set(CACHE_KEY, estimates); } console.debug('Got estimates', estimates); From b1157b397619826b2cd9a6ed858c40066fe9fa25 Mon Sep 17 00:00:00 2001 From: Tom Kirkpatrick Date: Sun, 7 Jan 2024 08:03:51 +0100 Subject: [PATCH 4/4] Handle error fetching estimates --- src/server.tsx | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/server.tsx b/src/server.tsx index d3f8067..3e4e275 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -282,13 +282,24 @@ const Content = (props: { siteData: SiteData; estimates: Estimates }) => ( ); /** - * Returns the current fee estimates for the Bitcoin network. + * Returns the current fee estimates for the Bitcoin network, rendered as HTML. */ app.get('/', async (c) => { - var estimates = await getEstimates(); + let estimates : Estimates | undefined; - // Set cache headers. - c.res.headers.set('Cache-Control', `public, max-age=${stdTTL}`) + try { + estimates = await getEstimates(); + + // Set cache headers. + c.res.headers.set('Cache-Control', `public, max-age=${stdTTL}`) + + } catch (error) { + console.error(error); + estimates = { + current_block_hash: null, + fee_by_block_target: {} + }; + } const props = { siteData: { @@ -302,7 +313,7 @@ app.get('/', async (c) => { }); /** - * Returns the current fee estimates for the Bitcoin network. + * Returns the current fee estimates for the Bitcoin network, rendered as JSON. */ app.get('/v1/fee-estimates', async (c) => { try {