diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 6225a9c1d..498003d98 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -20,6 +20,7 @@ import difficultyAdjustment from '../difficulty-adjustment'; import transactionRepository from '../../repositories/TransactionRepository'; import rbfCache from '../rbf-cache'; import { calculateMempoolTxCpfp } from '../cpfp'; +import { handleError } from '../../utils/api'; class BitcoinRoutes { public initRoutes(app: Application) { @@ -86,7 +87,7 @@ class BitcoinRoutes { res.set('Content-Type', 'application/json'); res.send(result); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -105,13 +106,13 @@ class BitcoinRoutes { const result = mempoolBlocks.getMempoolBlocks(); res.json(result); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } private getTransactionTimes(req: Request, res: Response) { if (!Array.isArray(req.query.txId)) { - res.status(500).send('Not an array'); + handleError(req, res, 500, 'Not an array'); return; } const txIds: string[] = []; @@ -128,12 +129,12 @@ class BitcoinRoutes { private async $getBatchedOutspends(req: Request, res: Response): Promise { const txids_csv = req.query.txids; if (!txids_csv || typeof txids_csv !== 'string') { - res.status(500).send('Invalid txids format'); + handleError(req, res, 500, 'Invalid txids format'); return; } const txids = txids_csv.split(','); if (txids.length > 50) { - res.status(400).send('Too many txids requested'); + handleError(req, res, 400, 'Too many txids requested'); return; } @@ -141,13 +142,13 @@ class BitcoinRoutes { const batchedOutspends = await bitcoinApi.$getBatchedOutspends(txids); res.json(batchedOutspends); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } private async $getCpfpInfo(req: Request, res: Response) { if (!/^[a-fA-F0-9]{64}$/.test(req.params.txId)) { - res.status(501).send(`Invalid transaction ID.`); + handleError(req, res, 501, `Invalid transaction ID.`); return; } @@ -180,7 +181,7 @@ class BitcoinRoutes { try { cpfpInfo = await transactionRepository.$getCpfpInfo(req.params.txId); } catch (e) { - res.status(500).send('failed to get CPFP info'); + handleError(req, res, 500, 'failed to get CPFP info'); return; } } @@ -209,7 +210,7 @@ class BitcoinRoutes { if (e instanceof Error && e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) { statusCode = 404; } - res.status(statusCode).send(e instanceof Error ? e.message : e); + handleError(req, res, statusCode, e instanceof Error ? e.message : e); } } @@ -223,7 +224,7 @@ class BitcoinRoutes { if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) { statusCode = 404; } - res.status(statusCode).send(e instanceof Error ? e.message : e); + handleError(req, res, statusCode, e instanceof Error ? e.message : e); } } @@ -284,13 +285,13 @@ class BitcoinRoutes { // Not modified // 422 Unprocessable Entity // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422 - res.status(422).send(`Psbt had no missing nonWitnessUtxos.`); + handleError(req, res, 422, `Psbt had no missing nonWitnessUtxos.`); } } catch (e: any) { if (e instanceof Error && new RegExp(notFoundError).test(e.message)) { - res.status(404).send(e.message); + handleError(req, res, 404, e.message); } else { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } } @@ -304,7 +305,7 @@ class BitcoinRoutes { if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) { statusCode = 404; } - res.status(statusCode).send(e instanceof Error ? e.message : e); + handleError(req, res, statusCode, e instanceof Error ? e.message : e); } } @@ -314,7 +315,7 @@ class BitcoinRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString()); res.json(transactions); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -336,7 +337,7 @@ class BitcoinRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * cacheDuration).toUTCString()); res.json(block); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -346,7 +347,7 @@ class BitcoinRoutes { res.setHeader('content-type', 'text/plain'); res.send(blockHeader); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -357,10 +358,11 @@ class BitcoinRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString()); res.json(auditSummary); } else { - return res.status(404).send(`audit not available`); + handleError(req, res, 404, `audit not available`); + return; } } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -371,7 +373,8 @@ class BitcoinRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString()); res.json(auditSummary); } else { - return res.status(404).send(`transaction audit not available`); + handleError(req, res, 404, `transaction audit not available`); + return; } } catch (e) { res.status(500).send(e instanceof Error ? e.message : e); @@ -388,42 +391,49 @@ class BitcoinRoutes { return await this.getLegacyBlocks(req, res); } } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } private async getBlocksByBulk(req: Request, res: Response) { try { if (['mainnet', 'testnet', 'signet'].includes(config.MEMPOOL.NETWORK) === false) { // Liquid - Not implemented - return res.status(404).send(`This API is only available for Bitcoin networks`); + handleError(req, res, 404, `This API is only available for Bitcoin networks`); + return; } if (config.MEMPOOL.MAX_BLOCKS_BULK_QUERY <= 0) { - return res.status(404).send(`This API is disabled. Set config.MEMPOOL.MAX_BLOCKS_BULK_QUERY to a positive number to enable it.`); + handleError(req, res, 404, `This API is disabled. Set config.MEMPOOL.MAX_BLOCKS_BULK_QUERY to a positive number to enable it.`); + return; } if (!Common.indexingEnabled()) { - return res.status(404).send(`Indexing is required for this API`); + handleError(req, res, 404, `Indexing is required for this API`); + return; } const from = parseInt(req.params.from, 10); if (!req.params.from || from < 0) { - return res.status(400).send(`Parameter 'from' must be a block height (integer)`); + handleError(req, res, 400, `Parameter 'from' must be a block height (integer)`); + return; } const to = req.params.to === undefined ? await bitcoinApi.$getBlockHeightTip() : parseInt(req.params.to, 10); if (to < 0) { - return res.status(400).send(`Parameter 'to' must be a block height (integer)`); + handleError(req, res, 400, `Parameter 'to' must be a block height (integer)`); + return; } if (from > to) { - return res.status(400).send(`Parameter 'to' must be a higher block height than 'from'`); + handleError(req, res, 400, `Parameter 'to' must be a higher block height than 'from'`); + return; } if ((to - from + 1) > config.MEMPOOL.MAX_BLOCKS_BULK_QUERY) { - return res.status(400).send(`You can only query ${config.MEMPOOL.MAX_BLOCKS_BULK_QUERY} blocks at once.`); + handleError(req, res, 400, `You can only query ${config.MEMPOOL.MAX_BLOCKS_BULK_QUERY} blocks at once.`); + return; } res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(await blocks.$getBlocksBetweenHeight(from, to)); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -458,10 +468,10 @@ class BitcoinRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(returnBlocks); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } - + private async getBlockTransactions(req: Request, res: Response) { try { loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0); @@ -483,7 +493,7 @@ class BitcoinRoutes { res.json(transactions); } catch (e) { loadingIndicators.setProgress('blocktxs-' + req.params.hash, 100); - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -492,13 +502,13 @@ class BitcoinRoutes { const blockHash = await bitcoinApi.$getBlockHash(parseInt(req.params.height, 10)); res.send(blockHash); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } private async getAddress(req: Request, res: Response) { if (config.MEMPOOL.BACKEND === 'none') { - res.status(405).send('Address lookups cannot be used with bitcoind as backend.'); + handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.'); return; } @@ -507,15 +517,16 @@ class BitcoinRoutes { res.json(addressData); } catch (e) { if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) { - return res.status(413).send(e instanceof Error ? e.message : e); + handleError(req, res, 413, e instanceof Error ? e.message : e); + return; } - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } private async getAddressTransactions(req: Request, res: Response): Promise { if (config.MEMPOOL.BACKEND === 'none') { - res.status(405).send('Address lookups cannot be used with bitcoind as backend.'); + handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.'); return; } @@ -528,23 +539,23 @@ class BitcoinRoutes { res.json(transactions); } catch (e) { if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) { - res.status(413).send(e instanceof Error ? e.message : e); + handleError(req, res, 413, e instanceof Error ? e.message : e); return; } - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } private async getAddressTransactionSummary(req: Request, res: Response): Promise { if (config.MEMPOOL.BACKEND !== 'esplora') { - res.status(405).send('Address summary lookups require mempool/electrs backend.'); + handleError(req, res, 405, 'Address summary lookups require mempool/electrs backend.'); return; } } private async getScriptHash(req: Request, res: Response) { if (config.MEMPOOL.BACKEND === 'none') { - res.status(405).send('Address lookups cannot be used with bitcoind as backend.'); + handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.'); return; } @@ -555,15 +566,16 @@ class BitcoinRoutes { res.json(addressData); } catch (e) { if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) { - return res.status(413).send(e instanceof Error ? e.message : e); + handleError(req, res, 413, e instanceof Error ? e.message : e); + return; } - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } private async getScriptHashTransactions(req: Request, res: Response): Promise { if (config.MEMPOOL.BACKEND === 'none') { - res.status(405).send('Address lookups cannot be used with bitcoind as backend.'); + handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.'); return; } @@ -578,16 +590,16 @@ class BitcoinRoutes { res.json(transactions); } catch (e) { if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) { - res.status(413).send(e instanceof Error ? e.message : e); + handleError(req, res, 413, e instanceof Error ? e.message : e); return; } - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } private async getScriptHashTransactionSummary(req: Request, res: Response): Promise { if (config.MEMPOOL.BACKEND !== 'esplora') { - res.status(405).send('Scripthash summary lookups require mempool/electrs backend.'); + handleError(req, res, 405, 'Scripthash summary lookups require mempool/electrs backend.'); return; } } @@ -597,7 +609,7 @@ class BitcoinRoutes { const blockHash = await bitcoinApi.$getAddressPrefix(req.params.prefix); res.send(blockHash); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -624,7 +636,7 @@ class BitcoinRoutes { const rawMempool = await bitcoinApi.$getRawMempool(); res.send(rawMempool); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -632,12 +644,13 @@ class BitcoinRoutes { try { const result = blocks.getCurrentBlockHeight(); if (!result) { - return res.status(503).send(`Service Temporarily Unavailable`); + handleError(req, res, 503, `Service Temporarily Unavailable`); + return; } res.setHeader('content-type', 'text/plain'); res.send(result.toString()); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -647,7 +660,7 @@ class BitcoinRoutes { res.setHeader('content-type', 'text/plain'); res.send(result); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -657,7 +670,7 @@ class BitcoinRoutes { res.setHeader('content-type', 'application/octet-stream'); res.send(result); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -666,7 +679,7 @@ class BitcoinRoutes { const result = await bitcoinApi.$getTxIdsForBlock(req.params.hash); res.json(result); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -675,7 +688,7 @@ class BitcoinRoutes { const result = await bitcoinClient.validateAddress(req.params.address); res.json(result); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -688,7 +701,7 @@ class BitcoinRoutes { replaces }); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -697,7 +710,7 @@ class BitcoinRoutes { const result = rbfCache.getRbfTrees(false); res.json(result); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -706,7 +719,7 @@ class BitcoinRoutes { const result = rbfCache.getRbfTrees(true); res.json(result); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -719,7 +732,7 @@ class BitcoinRoutes { res.status(204).send(); } } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -728,7 +741,7 @@ class BitcoinRoutes { const result = await bitcoinApi.$getOutspends(req.params.txId); res.json(result); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -738,10 +751,10 @@ class BitcoinRoutes { if (da) { res.json(da); } else { - res.status(503).send(`Service Temporarily Unavailable`); + handleError(req, res, 503, `Service Temporarily Unavailable`); } } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -752,7 +765,7 @@ class BitcoinRoutes { const txIdResult = await bitcoinApi.$sendRawTransaction(rawTx); res.send(txIdResult); } catch (e: any) { - res.status(400).send(e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message }) + handleError(req, res, 400, e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message }) : (e.message || 'Error')); } } @@ -764,7 +777,7 @@ class BitcoinRoutes { const txIdResult = await bitcoinClient.sendRawTransaction(txHex); res.send(txIdResult); } catch (e: any) { - res.status(400).send(e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message }) + handleError(req, res, 400, e.message && e.code ? 'sendrawtransaction RPC error: ' + JSON.stringify({ code: e.code, message: e.message }) : (e.message || 'Error')); } } @@ -776,8 +789,7 @@ class BitcoinRoutes { const result = await bitcoinApi.$testMempoolAccept(rawTxs, maxfeerate); res.send(result); } catch (e: any) { - res.setHeader('content-type', 'text/plain'); - res.status(400).send(e.message && e.code ? 'testmempoolaccept RPC error: ' + JSON.stringify({ code: e.code, message: e.message }) + handleError(req, res, 400, e.message && e.code ? 'testmempoolaccept RPC error: ' + JSON.stringify({ code: e.code, message: e.message }) : (e.message || 'Error')); } } diff --git a/backend/src/api/explorer/channels.routes.ts b/backend/src/api/explorer/channels.routes.ts index 391bf628e..8b4c3e8c8 100644 --- a/backend/src/api/explorer/channels.routes.ts +++ b/backend/src/api/explorer/channels.routes.ts @@ -1,6 +1,7 @@ import config from '../../config'; import { Application, Request, Response } from 'express'; import channelsApi from './channels.api'; +import { handleError } from '../../utils/api'; class ChannelsRoutes { constructor() { } @@ -22,7 +23,7 @@ class ChannelsRoutes { const channels = await channelsApi.$searchChannelsById(req.params.search); res.json(channels); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -38,7 +39,7 @@ class ChannelsRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(channel); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -53,11 +54,11 @@ class ChannelsRoutes { const status: string = typeof req.query.status === 'string' ? req.query.status : ''; if (index < -1) { - res.status(400).send('Invalid index'); + handleError(req, res, 400, 'Invalid index'); return; } if (['open', 'active', 'closed'].includes(status) === false) { - res.status(400).send('Invalid status'); + handleError(req, res, 400, 'Invalid status'); return; } @@ -69,14 +70,14 @@ class ChannelsRoutes { res.header('X-Total-Count', channelsCount.toString()); res.json(channels); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } private async $getChannelsByTransactionIds(req: Request, res: Response): Promise { try { if (!Array.isArray(req.query.txId)) { - res.status(400).send('Not an array'); + handleError(req, res, 400, 'Not an array'); return; } const txIds: string[] = []; @@ -107,7 +108,7 @@ class ChannelsRoutes { res.json(result); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -119,7 +120,7 @@ class ChannelsRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(channels); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -132,7 +133,7 @@ class ChannelsRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(channels); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } diff --git a/backend/src/api/explorer/general.routes.ts b/backend/src/api/explorer/general.routes.ts index 07620e84a..b4d0c635d 100644 --- a/backend/src/api/explorer/general.routes.ts +++ b/backend/src/api/explorer/general.routes.ts @@ -3,6 +3,8 @@ import { Application, Request, Response } from 'express'; import nodesApi from './nodes.api'; import channelsApi from './channels.api'; import statisticsApi from './statistics.api'; +import { handleError } from '../../utils/api'; + class GeneralLightningRoutes { constructor() { } @@ -27,7 +29,7 @@ class GeneralLightningRoutes { channels: channels, }); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -41,7 +43,7 @@ class GeneralLightningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(statistics); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -50,7 +52,7 @@ class GeneralLightningRoutes { const statistics = await statisticsApi.$getLatestStatistics(); res.json(statistics); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } } diff --git a/backend/src/api/explorer/nodes.routes.ts b/backend/src/api/explorer/nodes.routes.ts index 9d6373845..9ca2fd1c3 100644 --- a/backend/src/api/explorer/nodes.routes.ts +++ b/backend/src/api/explorer/nodes.routes.ts @@ -3,6 +3,7 @@ import { Application, Request, Response } from 'express'; import nodesApi from './nodes.api'; import DB from '../../database'; import { INodesRanking } from '../../mempool.interfaces'; +import { handleError } from '../../utils/api'; class NodesRoutes { constructor() { } @@ -31,7 +32,7 @@ class NodesRoutes { const nodes = await nodesApi.$searchNodeByPublicKeyOrAlias(req.params.search); res.json(nodes); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -181,13 +182,13 @@ class NodesRoutes { } } catch (e) {} } - + res.header('Pragma', 'public'); res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(nodes); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -195,7 +196,7 @@ class NodesRoutes { try { const node = await nodesApi.$getNode(req.params.public_key); if (!node) { - res.status(404).send('Node not found'); + handleError(req, res, 404, 'Node not found'); return; } res.header('Pragma', 'public'); @@ -203,7 +204,7 @@ class NodesRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(node); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -215,7 +216,7 @@ class NodesRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(statistics); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -223,7 +224,7 @@ class NodesRoutes { try { const node = await nodesApi.$getFeeHistogram(req.params.public_key); if (!node) { - res.status(404).send('Node not found'); + handleError(req, res, 404, 'Node not found'); return; } res.header('Pragma', 'public'); @@ -231,7 +232,7 @@ class NodesRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(node); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -247,7 +248,7 @@ class NodesRoutes { topByChannels: topChannelsNodes, }); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -259,7 +260,7 @@ class NodesRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(topCapacityNodes); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -271,7 +272,7 @@ class NodesRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(topCapacityNodes); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -283,7 +284,7 @@ class NodesRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(topCapacityNodes); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -295,7 +296,7 @@ class NodesRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString()); res.json(nodesPerAs); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -307,7 +308,7 @@ class NodesRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString()); res.json(worldNodes); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -322,7 +323,7 @@ class NodesRoutes { ); if (country.length === 0) { - res.status(404).send(`This country does not exist or does not host any lightning nodes on clearnet`); + handleError(req, res, 404, `This country does not exist or does not host any lightning nodes on clearnet`); return; } @@ -335,7 +336,7 @@ class NodesRoutes { nodes: nodes, }); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -349,7 +350,7 @@ class NodesRoutes { ); if (isp.length === 0) { - res.status(404).send(`This ISP does not exist or does not host any lightning nodes on clearnet`); + handleError(req, res, 404, `This ISP does not exist or does not host any lightning nodes on clearnet`); return; } @@ -362,7 +363,7 @@ class NodesRoutes { nodes: nodes, }); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -374,7 +375,7 @@ class NodesRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString()); res.json(nodesPerAs); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } } diff --git a/backend/src/api/liquid/liquid.routes.ts b/backend/src/api/liquid/liquid.routes.ts index 9ea61ca31..9dafd0def 100644 --- a/backend/src/api/liquid/liquid.routes.ts +++ b/backend/src/api/liquid/liquid.routes.ts @@ -3,6 +3,7 @@ import { Application, Request, Response } from 'express'; import config from '../../config'; import elementsParser from './elements-parser'; import icons from './icons'; +import { handleError } from '../../utils/api'; class LiquidRoutes { public initRoutes(app: Application) { @@ -42,7 +43,7 @@ class LiquidRoutes { res.setHeader('content-length', result.length); res.send(result); } else { - res.status(404).send('Asset icon not found'); + handleError(req, res, 404, 'Asset icon not found'); } } @@ -51,7 +52,7 @@ class LiquidRoutes { if (result) { res.json(result); } else { - res.status(404).send('Asset icons not found'); + handleError(req, res, 404, 'Asset icons not found'); } } @@ -82,7 +83,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60).toUTCString()); res.json(pegs); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -94,7 +95,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60).toUTCString()); res.json(reserves); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -106,7 +107,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(currentSupply); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -118,7 +119,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(currentReserves); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -130,7 +131,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(auditStatus); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -142,7 +143,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(federationAddresses); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -154,7 +155,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(federationAddresses); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -166,7 +167,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(federationUtxos); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -178,7 +179,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(expiredUtxos); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -190,7 +191,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(federationUtxos); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -202,7 +203,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(emergencySpentUtxos); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -214,7 +215,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(emergencySpentUtxos); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -226,7 +227,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(recentPegs); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -238,7 +239,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(pegsVolume); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -250,7 +251,7 @@ class LiquidRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString()); res.json(pegsCount); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } diff --git a/backend/src/api/mining/mining-routes.ts b/backend/src/api/mining/mining-routes.ts index 8f8bbac82..69e6d95d4 100644 --- a/backend/src/api/mining/mining-routes.ts +++ b/backend/src/api/mining/mining-routes.ts @@ -10,6 +10,7 @@ import mining from "./mining"; import PricesRepository from '../../repositories/PricesRepository'; import AccelerationRepository from '../../repositories/AccelerationRepository'; import accelerationApi from '../services/acceleration'; +import { handleError } from '../../utils/api'; class MiningRoutes { public initRoutes(app: Application) { @@ -53,12 +54,12 @@ class MiningRoutes { res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); if (['testnet', 'signet', 'liquidtestnet'].includes(config.MEMPOOL.NETWORK)) { - res.status(400).send('Prices are not available on testnets.'); + handleError(req, res, 400, 'Prices are not available on testnets.'); return; } const timestamp = parseInt(req.query.timestamp as string, 10) || 0; const currency = req.query.currency as string; - + let response; if (timestamp && currency) { response = await PricesRepository.$getNearestHistoricalPrice(timestamp, currency); @@ -71,7 +72,7 @@ class MiningRoutes { } res.status(200).send(response); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -84,9 +85,9 @@ class MiningRoutes { res.json(stats); } catch (e) { if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) { - res.status(404).send(e.message); + handleError(req, res, 404, e.message); } else { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } } @@ -103,9 +104,9 @@ class MiningRoutes { res.json(poolBlocks); } catch (e) { if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) { - res.status(404).send(e.message); + handleError(req, res, 404, e.message); } else { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } } @@ -129,7 +130,7 @@ class MiningRoutes { res.json(pools); } } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -143,7 +144,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(stats); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -157,7 +158,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); res.json(hashrates); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -172,9 +173,9 @@ class MiningRoutes { res.json(hashrates); } catch (e) { if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) { - res.status(404).send(e.message); + handleError(req, res, 404, e.message); } else { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } } @@ -203,7 +204,7 @@ class MiningRoutes { currentDifficulty: currentDifficulty, }); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -217,7 +218,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(blockFees); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -235,7 +236,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(blockFees); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -249,7 +250,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(blockRewards); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -263,7 +264,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(blockFeeRates); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -281,7 +282,7 @@ class MiningRoutes { weights: blockWeights }); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -293,7 +294,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); res.json(difficulty.map(adj => [adj.time, adj.height, adj.difficulty, adj.adjustment])); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -317,7 +318,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(blocksHealth.map(health => [health.time, health.height, health.match_rate])); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -326,7 +327,7 @@ class MiningRoutes { const audit = await BlocksAuditsRepository.$getBlockAudit(req.params.hash); if (!audit) { - res.status(204).send(`This block has not been audited.`); + handleError(req, res, 204, `This block has not been audited.`); return; } @@ -335,7 +336,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString()); res.json(audit); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -358,7 +359,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString()); res.json(result); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -371,7 +372,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); res.json(await BlocksAuditsRepository.$getBlockAuditScores(height, height - 15)); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -384,7 +385,7 @@ class MiningRoutes { res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString()); res.json(audit || 'null'); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -394,12 +395,12 @@ class MiningRoutes { res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) { - res.status(400).send('Acceleration data is not available.'); + handleError(req, res, 400, 'Acceleration data is not available.'); return; } res.status(200).send(await AccelerationRepository.$getAccelerationInfo(req.params.slug)); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -409,13 +410,13 @@ class MiningRoutes { res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString()); if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) { - res.status(400).send('Acceleration data is not available.'); + handleError(req, res, 400, 'Acceleration data is not available.'); return; } const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10); res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, height)); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -425,12 +426,12 @@ class MiningRoutes { res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) { - res.status(400).send('Acceleration data is not available.'); + handleError(req, res, 400, 'Acceleration data is not available.'); return; } res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, null, req.params.interval)); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -440,12 +441,12 @@ class MiningRoutes { res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) { - res.status(400).send('Acceleration data is not available.'); + handleError(req, res, 400, 'Acceleration data is not available.'); return; } res.status(200).send(await AccelerationRepository.$getAccelerationTotals(req.query.pool, req.query.interval)); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -455,12 +456,12 @@ class MiningRoutes { res.header('Cache-control', 'public'); res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString()); if (!config.MEMPOOL_SERVICES.ACCELERATIONS || ['testnet', 'signet', 'liquidtestnet', 'liquid'].includes(config.MEMPOOL.NETWORK)) { - res.status(400).send('Acceleration data is not available.'); + handleError(req, res, 400, 'Acceleration data is not available.'); return; } res.status(200).send(accelerationApi.accelerations || []); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } @@ -472,7 +473,7 @@ class MiningRoutes { accelerationApi.accelerationRequested(req.params.txid); res.status(200).send(); } catch (e) { - res.status(500).send(e instanceof Error ? e.message : e); + handleError(req, res, 500, e instanceof Error ? e.message : e); } } } diff --git a/backend/src/utils/api.ts b/backend/src/utils/api.ts new file mode 100644 index 000000000..69d746b9f --- /dev/null +++ b/backend/src/utils/api.ts @@ -0,0 +1,9 @@ +import { Request, Response } from 'express'; + +export function handleError(req: Request, res: Response, statusCode: number, errorMessage: string | unknown): void { + if (req.accepts('json')) { + res.status(statusCode).json({ error: errorMessage }); + } else { + res.status(statusCode).send(errorMessage); + } +} \ No newline at end of file