Merge pull request #5700 from mempool/mononaut/standardize-api-errors
standardize API error strings & validation
This commit is contained in:
commit
332099cc01
@ -3,6 +3,10 @@ import logger from '../../logger';
|
|||||||
import bitcoinClient from './bitcoin-client';
|
import bitcoinClient from './bitcoin-client';
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
|
|
||||||
|
const BLOCKHASH_REGEX = /^[a-f0-9]{64}$/i;
|
||||||
|
const TXID_REGEX = /^[a-f0-9]{64}$/i;
|
||||||
|
const RAW_TX_REGEX = /^[a-f0-9]{2,}$/i;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a set of routes used by the accelerator server
|
* Define a set of routes used by the accelerator server
|
||||||
* Those routes are not designed to be public
|
* Those routes are not designed to be public
|
||||||
@ -10,7 +14,7 @@ import config from '../../config';
|
|||||||
class BitcoinBackendRoutes {
|
class BitcoinBackendRoutes {
|
||||||
private static tag = 'BitcoinBackendRoutes';
|
private static tag = 'BitcoinBackendRoutes';
|
||||||
|
|
||||||
public initRoutes(app: Application) {
|
public initRoutes(app: Application): void {
|
||||||
app
|
app
|
||||||
.get(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'get-mempool-entry', this.disableCache, this.$getMempoolEntry)
|
.get(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'get-mempool-entry', this.disableCache, this.$getMempoolEntry)
|
||||||
.post(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'decode-raw-transaction', this.disableCache, this.$decodeRawTransaction)
|
.post(config.MEMPOOL.API_URL_PREFIX + 'internal/bitcoin-core/' + 'decode-raw-transaction', this.disableCache, this.$decodeRawTransaction)
|
||||||
@ -47,9 +51,9 @@ class BitcoinBackendRoutes {
|
|||||||
*/
|
*/
|
||||||
private static handleException(e: any, fnName: string, res: Response): void {
|
private static handleException(e: any, fnName: string, res: Response): void {
|
||||||
if (typeof(e.code) === 'number') {
|
if (typeof(e.code) === 'number') {
|
||||||
res.status(400).send(JSON.stringify(e, ['code', 'message']));
|
res.status(400).send(JSON.stringify(e, ['code']));
|
||||||
} else {
|
} else {
|
||||||
const err = `exception in ${fnName}. ${e}. Details: ${JSON.stringify(e, ['code', 'message'])}`;
|
const err = `unknown exception in ${fnName}`;
|
||||||
logger.err(err, BitcoinBackendRoutes.tag);
|
logger.err(err, BitcoinBackendRoutes.tag);
|
||||||
res.status(500).send(err);
|
res.status(500).send(err);
|
||||||
}
|
}
|
||||||
@ -58,13 +62,13 @@ class BitcoinBackendRoutes {
|
|||||||
private async $getMempoolEntry(req: Request, res: Response): Promise<void> {
|
private async $getMempoolEntry(req: Request, res: Response): Promise<void> {
|
||||||
const txid = req.query.txid;
|
const txid = req.query.txid;
|
||||||
try {
|
try {
|
||||||
if (typeof(txid) !== 'string' || txid.length !== 64) {
|
if (typeof(txid) !== 'string' || txid.length !== 64 || !TXID_REGEX.test(txid)) {
|
||||||
res.status(400).send(`invalid param txid ${txid}. must be a string of 64 char`);
|
res.status(400).send(`invalid param txid. must be 64 hexadecimal characters`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const mempoolEntry = await bitcoinClient.getMempoolEntry(txid);
|
const mempoolEntry = await bitcoinClient.getMempoolEntry(txid);
|
||||||
if (!mempoolEntry) {
|
if (!mempoolEntry) {
|
||||||
res.status(404).send(`no mempool entry found for txid ${txid}`);
|
res.status(404).send();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.status(200).send(mempoolEntry);
|
res.status(200).send(mempoolEntry);
|
||||||
@ -76,13 +80,13 @@ class BitcoinBackendRoutes {
|
|||||||
private async $decodeRawTransaction(req: Request, res: Response): Promise<void> {
|
private async $decodeRawTransaction(req: Request, res: Response): Promise<void> {
|
||||||
const rawTx = req.body.rawTx;
|
const rawTx = req.body.rawTx;
|
||||||
try {
|
try {
|
||||||
if (typeof(rawTx) !== 'string') {
|
if (typeof(rawTx) !== 'string' || !RAW_TX_REGEX.test(rawTx)) {
|
||||||
res.status(400).send(`invalid param rawTx ${rawTx}. must be a string`);
|
res.status(400).send(`invalid param rawTx. must be a string of hexadecimal characters`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const decodedTx = await bitcoinClient.decodeRawTransaction(rawTx);
|
const decodedTx = await bitcoinClient.decodeRawTransaction(rawTx);
|
||||||
if (!decodedTx) {
|
if (!decodedTx) {
|
||||||
res.status(400).send(`unable to decode rawTx ${rawTx}`);
|
res.status(400).send(`unable to decode rawTx`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.status(200).send(decodedTx);
|
res.status(200).send(decodedTx);
|
||||||
@ -95,23 +99,23 @@ class BitcoinBackendRoutes {
|
|||||||
const txid = req.query.txid;
|
const txid = req.query.txid;
|
||||||
const verbose = req.query.verbose;
|
const verbose = req.query.verbose;
|
||||||
try {
|
try {
|
||||||
if (typeof(txid) !== 'string' || txid.length !== 64) {
|
if (typeof(txid) !== 'string' || txid.length !== 64 || !TXID_REGEX.test(txid)) {
|
||||||
res.status(400).send(`invalid param txid ${txid}. must be a string of 64 char`);
|
res.status(400).send(`invalid param txid. must be 64 hexadecimal characters`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof(verbose) !== 'string') {
|
if (typeof(verbose) !== 'string') {
|
||||||
res.status(400).send(`invalid param verbose ${verbose}. must be a string representing an integer`);
|
res.status(400).send(`invalid param verbose. must be a string representing an integer`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const verboseNumber = parseInt(verbose, 10);
|
const verboseNumber = parseInt(verbose, 10);
|
||||||
if (typeof(verboseNumber) !== 'number') {
|
if (typeof(verboseNumber) !== 'number') {
|
||||||
res.status(400).send(`invalid param verbose ${verbose}. must be a valid integer`);
|
res.status(400).send(`invalid param verbose. must be a valid integer`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const decodedTx = await bitcoinClient.getRawTransaction(txid, verboseNumber);
|
const decodedTx = await bitcoinClient.getRawTransaction(txid, verboseNumber);
|
||||||
if (!decodedTx) {
|
if (!decodedTx) {
|
||||||
res.status(400).send(`unable to get raw transaction for txid ${txid}`);
|
res.status(400).send(`unable to get raw transaction`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.status(200).send(decodedTx);
|
res.status(200).send(decodedTx);
|
||||||
@ -123,13 +127,13 @@ class BitcoinBackendRoutes {
|
|||||||
private async $sendRawTransaction(req: Request, res: Response): Promise<void> {
|
private async $sendRawTransaction(req: Request, res: Response): Promise<void> {
|
||||||
const rawTx = req.body.rawTx;
|
const rawTx = req.body.rawTx;
|
||||||
try {
|
try {
|
||||||
if (typeof(rawTx) !== 'string') {
|
if (typeof(rawTx) !== 'string' || !RAW_TX_REGEX.test(rawTx)) {
|
||||||
res.status(400).send(`invalid param rawTx ${rawTx}. must be a string`);
|
res.status(400).send(`invalid param rawTx. must be a string of hexadecimal characters`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const txHex = await bitcoinClient.sendRawTransaction(rawTx);
|
const txHex = await bitcoinClient.sendRawTransaction(rawTx);
|
||||||
if (!txHex) {
|
if (!txHex) {
|
||||||
res.status(400).send(`unable to send rawTx ${rawTx}`);
|
res.status(400).send(`unable to send rawTx`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.status(200).send(txHex);
|
res.status(200).send(txHex);
|
||||||
@ -141,13 +145,13 @@ class BitcoinBackendRoutes {
|
|||||||
private async $testMempoolAccept(req: Request, res: Response): Promise<void> {
|
private async $testMempoolAccept(req: Request, res: Response): Promise<void> {
|
||||||
const rawTxs = req.body.rawTxs;
|
const rawTxs = req.body.rawTxs;
|
||||||
try {
|
try {
|
||||||
if (typeof(rawTxs) !== 'object') {
|
if (typeof(rawTxs) !== 'object' || !Array.isArray(rawTxs) || rawTxs.some((tx) => typeof(tx) !== 'string' || !RAW_TX_REGEX.test(tx))) {
|
||||||
res.status(400).send(`invalid param rawTxs ${JSON.stringify(rawTxs)}. must be an array of string`);
|
res.status(400).send(`invalid param rawTxs. must be an array of strings of hexadecimal characters`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const txHex = await bitcoinClient.testMempoolAccept(rawTxs);
|
const txHex = await bitcoinClient.testMempoolAccept(rawTxs);
|
||||||
if (typeof(txHex) !== 'object' || txHex.length === 0) {
|
if (typeof(txHex) !== 'object' || txHex.length === 0) {
|
||||||
res.status(400).send(`testmempoolaccept failed for raw txs ${JSON.stringify(rawTxs)}, got an empty result`);
|
res.status(400).send(`testmempoolaccept failed for raw txs, got an empty result`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.status(200).send(txHex);
|
res.status(200).send(txHex);
|
||||||
@ -160,18 +164,18 @@ class BitcoinBackendRoutes {
|
|||||||
const txid = req.query.txid;
|
const txid = req.query.txid;
|
||||||
const verbose = req.query.verbose;
|
const verbose = req.query.verbose;
|
||||||
try {
|
try {
|
||||||
if (typeof(txid) !== 'string' || txid.length !== 64) {
|
if (typeof(txid) !== 'string' || txid.length !== 64 || !TXID_REGEX.test(txid)) {
|
||||||
res.status(400).send(`invalid param txid ${txid}. must be a string of 64 char`);
|
res.status(400).send(`invalid param txid. must be 64 hexadecimal characters`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof(verbose) !== 'string' || (verbose !== 'true' && verbose !== 'false')) {
|
if (typeof(verbose) !== 'string' || (verbose !== 'true' && verbose !== 'false')) {
|
||||||
res.status(400).send(`invalid param verbose ${verbose}. must be a string ('true' | 'false')`);
|
res.status(400).send(`invalid param verbose. must be a string ('true' | 'false')`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ancestors = await bitcoinClient.getMempoolAncestors(txid, verbose === 'true' ? true : false);
|
const ancestors = await bitcoinClient.getMempoolAncestors(txid, verbose === 'true' ? true : false);
|
||||||
if (!ancestors) {
|
if (!ancestors) {
|
||||||
res.status(400).send(`unable to get mempool ancestors for txid ${txid}`);
|
res.status(400).send(`unable to get mempool ancestors`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.status(200).send(ancestors);
|
res.status(200).send(ancestors);
|
||||||
@ -184,23 +188,23 @@ class BitcoinBackendRoutes {
|
|||||||
const blockHash = req.query.hash;
|
const blockHash = req.query.hash;
|
||||||
const verbosity = req.query.verbosity;
|
const verbosity = req.query.verbosity;
|
||||||
try {
|
try {
|
||||||
if (typeof(blockHash) !== 'string' || blockHash.length !== 64) {
|
if (typeof(blockHash) !== 'string' || blockHash.length !== 64 || !BLOCKHASH_REGEX.test(blockHash)) {
|
||||||
res.status(400).send(`invalid param blockHash ${blockHash}. must be a string of 64 char`);
|
res.status(400).send(`invalid param blockHash. must be 64 hexadecimal characters`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof(verbosity) !== 'string') {
|
if (typeof(verbosity) !== 'string') {
|
||||||
res.status(400).send(`invalid param verbosity ${verbosity}. must be a string representing an integer`);
|
res.status(400).send(`invalid param verbosity. must be a string representing an integer`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const verbosityNumber = parseInt(verbosity, 10);
|
const verbosityNumber = parseInt(verbosity, 10);
|
||||||
if (typeof(verbosityNumber) !== 'number') {
|
if (typeof(verbosityNumber) !== 'number') {
|
||||||
res.status(400).send(`invalid param verbosity ${verbosity}. must be a valid integer`);
|
res.status(400).send(`invalid param verbosity. must be a valid integer`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const block = await bitcoinClient.getBlock(blockHash, verbosityNumber);
|
const block = await bitcoinClient.getBlock(blockHash, verbosityNumber);
|
||||||
if (!block) {
|
if (!block) {
|
||||||
res.status(400).send(`unable to get block for block hash ${blockHash}`);
|
res.status(400).send(`unable to get block`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.status(200).send(block);
|
res.status(200).send(block);
|
||||||
@ -213,18 +217,18 @@ class BitcoinBackendRoutes {
|
|||||||
const blockHeight = req.query.height;
|
const blockHeight = req.query.height;
|
||||||
try {
|
try {
|
||||||
if (typeof(blockHeight) !== 'string') {
|
if (typeof(blockHeight) !== 'string') {
|
||||||
res.status(400).send(`invalid param blockHeight ${blockHeight}, must be a string representing an integer`);
|
res.status(400).send(`invalid param blockHeight, must be a string representing an integer`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const blockHeightNumber = parseInt(blockHeight, 10);
|
const blockHeightNumber = parseInt(blockHeight, 10);
|
||||||
if (typeof(blockHeightNumber) !== 'number') {
|
if (typeof(blockHeightNumber) !== 'number') {
|
||||||
res.status(400).send(`invalid param blockHeight ${blockHeight}. must be a valid integer`);
|
res.status(400).send(`invalid param blockHeight. must be a valid integer`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const block = await bitcoinClient.getBlockHash(blockHeightNumber);
|
const block = await bitcoinClient.getBlockHash(blockHeightNumber);
|
||||||
if (!block) {
|
if (!block) {
|
||||||
res.status(400).send(`unable to get block hash for block height ${blockHeightNumber}`);
|
res.status(400).send(`unable to get block hash`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.status(200).send(block);
|
res.status(200).send(block);
|
||||||
@ -247,4 +251,4 @@ class BitcoinBackendRoutes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new BitcoinBackendRoutes
|
export default new BitcoinBackendRoutes;
|
@ -22,6 +22,11 @@ import rbfCache from '../rbf-cache';
|
|||||||
import { calculateMempoolTxCpfp } from '../cpfp';
|
import { calculateMempoolTxCpfp } from '../cpfp';
|
||||||
import { handleError } from '../../utils/api';
|
import { handleError } from '../../utils/api';
|
||||||
|
|
||||||
|
const TXID_REGEX = /^[a-f0-9]{64}$/i;
|
||||||
|
const BLOCK_HASH_REGEX = /^[a-f0-9]{64}$/i;
|
||||||
|
const ADDRESS_REGEX = /^[a-z0-9]{2,120}$/i;
|
||||||
|
const SCRIPT_HASH_REGEX = /^([a-f0-9]{2})+$/i;
|
||||||
|
|
||||||
class BitcoinRoutes {
|
class BitcoinRoutes {
|
||||||
public initRoutes(app: Application) {
|
public initRoutes(app: Application) {
|
||||||
app
|
app
|
||||||
@ -90,7 +95,7 @@ class BitcoinRoutes {
|
|||||||
res.set('Content-Type', 'application/json');
|
res.set('Content-Type', 'application/json');
|
||||||
res.send(result);
|
res.send(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get init data');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +114,7 @@ class BitcoinRoutes {
|
|||||||
const result = mempoolBlocks.getMempoolBlocks();
|
const result = mempoolBlocks.getMempoolBlocks();
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get mempool blocks');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +126,10 @@ class BitcoinRoutes {
|
|||||||
const txIds: string[] = [];
|
const txIds: string[] = [];
|
||||||
for (const _txId in req.query.txId) {
|
for (const _txId in req.query.txId) {
|
||||||
if (typeof req.query.txId[_txId] === 'string') {
|
if (typeof req.query.txId[_txId] === 'string') {
|
||||||
txIds.push(req.query.txId[_txId].toString());
|
const txid = req.query.txId[_txId].toString();
|
||||||
|
if (TXID_REGEX.test(txid)) {
|
||||||
|
txIds.push(txid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,18 +148,22 @@ class BitcoinRoutes {
|
|||||||
handleError(req, res, 400, 'Too many txids requested');
|
handleError(req, res, 400, 'Too many txids requested');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (txids.some((txid) => !TXID_REGEX.test(txid))) {
|
||||||
|
handleError(req, res, 400, 'Invalid txids format');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const batchedOutspends = await bitcoinApi.$getBatchedOutspends(txids);
|
const batchedOutspends = await bitcoinApi.$getBatchedOutspends(txids);
|
||||||
res.json(batchedOutspends);
|
res.json(batchedOutspends);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get batched outspends');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async $getCpfpInfo(req: Request, res: Response) {
|
private async $getCpfpInfo(req: Request, res: Response) {
|
||||||
if (!/^[a-fA-F0-9]{64}$/.test(req.params.txId)) {
|
if (!TXID_REGEX.test(req.params.txId)) {
|
||||||
handleError(req, res, 501, `Invalid transaction ID.`);
|
handleError(req, res, 501, `Invalid transaction ID`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +196,7 @@ class BitcoinRoutes {
|
|||||||
try {
|
try {
|
||||||
cpfpInfo = await transactionRepository.$getCpfpInfo(req.params.txId);
|
cpfpInfo = await transactionRepository.$getCpfpInfo(req.params.txId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, 'failed to get CPFP info');
|
handleError(req, res, 500, 'Failed to get CPFP info');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,6 +217,10 @@ class BitcoinRoutes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getTransaction(req: Request, res: Response) {
|
private async getTransaction(req: Request, res: Response) {
|
||||||
|
if (!TXID_REGEX.test(req.params.txId)) {
|
||||||
|
handleError(req, res, 501, `Invalid transaction ID`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const transaction = await transactionUtils.$getTransactionExtended(req.params.txId, true, false, false, true);
|
const transaction = await transactionUtils.$getTransactionExtended(req.params.txId, true, false, false, true);
|
||||||
res.json(transaction);
|
res.json(transaction);
|
||||||
@ -212,12 +228,17 @@ class BitcoinRoutes {
|
|||||||
let statusCode = 500;
|
let statusCode = 500;
|
||||||
if (e instanceof Error && e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
if (e instanceof Error && e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||||
statusCode = 404;
|
statusCode = 404;
|
||||||
|
handleError(req, res, statusCode, 'No such mempool or blockchain transaction');
|
||||||
}
|
}
|
||||||
handleError(req, res, statusCode, e instanceof Error ? e.message : e);
|
handleError(req, res, statusCode, 'Failed to get transaction');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getRawTransaction(req: Request, res: Response) {
|
private async getRawTransaction(req: Request, res: Response) {
|
||||||
|
if (!TXID_REGEX.test(req.params.txId)) {
|
||||||
|
handleError(req, res, 501, `Invalid transaction ID`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const transaction: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(req.params.txId, true);
|
const transaction: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(req.params.txId, true);
|
||||||
res.setHeader('content-type', 'text/plain');
|
res.setHeader('content-type', 'text/plain');
|
||||||
@ -226,8 +247,9 @@ class BitcoinRoutes {
|
|||||||
let statusCode = 500;
|
let statusCode = 500;
|
||||||
if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||||
statusCode = 404;
|
statusCode = 404;
|
||||||
|
handleError(req, res, statusCode, 'No such mempool or blockchain transaction');
|
||||||
}
|
}
|
||||||
handleError(req, res, statusCode, e instanceof Error ? e.message : e);
|
handleError(req, res, statusCode, 'Failed to get raw transaction');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,14 +314,18 @@ class BitcoinRoutes {
|
|||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
if (e instanceof Error && new RegExp(notFoundError).test(e.message)) {
|
if (e instanceof Error && new RegExp(notFoundError).test(e.message)) {
|
||||||
handleError(req, res, 404, e.message);
|
handleError(req, res, 404, notFoundError);
|
||||||
} else {
|
} else {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to process PSBT');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getTransactionStatus(req: Request, res: Response) {
|
private async getTransactionStatus(req: Request, res: Response) {
|
||||||
|
if (!TXID_REGEX.test(req.params.txId)) {
|
||||||
|
handleError(req, res, 501, `Invalid transaction ID`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const transaction = await transactionUtils.$getTransactionExtended(req.params.txId, true);
|
const transaction = await transactionUtils.$getTransactionExtended(req.params.txId, true);
|
||||||
res.json(transaction.status);
|
res.json(transaction.status);
|
||||||
@ -307,36 +333,53 @@ class BitcoinRoutes {
|
|||||||
let statusCode = 500;
|
let statusCode = 500;
|
||||||
if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
if (e instanceof Error && e.message && e.message.indexOf('No such mempool or blockchain transaction') > -1) {
|
||||||
statusCode = 404;
|
statusCode = 404;
|
||||||
|
handleError(req, res, statusCode, 'No such mempool or blockchain transaction');
|
||||||
}
|
}
|
||||||
handleError(req, res, statusCode, e instanceof Error ? e.message : e);
|
handleError(req, res, statusCode, 'Failed to get transaction status');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getStrippedBlockTransactions(req: Request, res: Response) {
|
private async getStrippedBlockTransactions(req: Request, res: Response) {
|
||||||
|
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||||
|
handleError(req, res, 501, `Invalid block hash`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const transactions = await blocks.$getStrippedBlockTransactions(req.params.hash);
|
const transactions = await blocks.$getStrippedBlockTransactions(req.params.hash);
|
||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
||||||
res.json(transactions);
|
res.json(transactions);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get block summary');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getStrippedBlockTransaction(req: Request, res: Response) {
|
private async getStrippedBlockTransaction(req: Request, res: Response) {
|
||||||
|
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||||
|
handleError(req, res, 501, `Invalid block hash`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!TXID_REGEX.test(req.params.txid)) {
|
||||||
|
handleError(req, res, 501, `Invalid transaction ID`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const transaction = await blocks.$getSingleTxFromSummary(req.params.hash, req.params.txid);
|
const transaction = await blocks.$getSingleTxFromSummary(req.params.hash, req.params.txid);
|
||||||
if (!transaction) {
|
if (!transaction) {
|
||||||
handleError(req, res, 404, `transaction not found in summary`);
|
handleError(req, res, 404, `Transaction not found in summary`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
||||||
res.json(transaction);
|
res.json(transaction);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get transaction from summary');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getBlock(req: Request, res: Response) {
|
private async getBlock(req: Request, res: Response) {
|
||||||
|
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||||
|
handleError(req, res, 501, `Invalid block hash`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const block = await blocks.$getBlock(req.params.hash);
|
const block = await blocks.$getBlock(req.params.hash);
|
||||||
|
|
||||||
@ -348,53 +391,69 @@ class BitcoinRoutes {
|
|||||||
} else if (blockAge > 30 * day) {
|
} else if (blockAge > 30 * day) {
|
||||||
cacheDuration = 10 * day;
|
cacheDuration = 10 * day;
|
||||||
} else {
|
} else {
|
||||||
cacheDuration = 600
|
cacheDuration = 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * cacheDuration).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * cacheDuration).toUTCString());
|
||||||
res.json(block);
|
res.json(block);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get block');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getBlockHeader(req: Request, res: Response) {
|
private async getBlockHeader(req: Request, res: Response) {
|
||||||
|
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||||
|
handleError(req, res, 501, `Invalid block hash`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const blockHeader = await bitcoinApi.$getBlockHeader(req.params.hash);
|
const blockHeader = await bitcoinApi.$getBlockHeader(req.params.hash);
|
||||||
res.setHeader('content-type', 'text/plain');
|
res.setHeader('content-type', 'text/plain');
|
||||||
res.send(blockHeader);
|
res.send(blockHeader);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get block header');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getBlockAuditSummary(req: Request, res: Response) {
|
private async getBlockAuditSummary(req: Request, res: Response) {
|
||||||
|
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||||
|
handleError(req, res, 501, `Invalid block hash`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const auditSummary = await blocks.$getBlockAuditSummary(req.params.hash);
|
const auditSummary = await blocks.$getBlockAuditSummary(req.params.hash);
|
||||||
if (auditSummary) {
|
if (auditSummary) {
|
||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
||||||
res.json(auditSummary);
|
res.json(auditSummary);
|
||||||
} else {
|
} else {
|
||||||
handleError(req, res, 404, `audit not available`);
|
handleError(req, res, 404, `Audit not available`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get block audit summary');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async $getBlockTxAuditSummary(req: Request, res: Response) {
|
private async $getBlockTxAuditSummary(req: Request, res: Response) {
|
||||||
|
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||||
|
handleError(req, res, 501, `Invalid block hash`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!TXID_REGEX.test(req.params.txid)) {
|
||||||
|
handleError(req, res, 501, `Invalid transaction ID`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const auditSummary = await blocks.$getBlockTxAuditSummary(req.params.hash, req.params.txid);
|
const auditSummary = await blocks.$getBlockTxAuditSummary(req.params.hash, req.params.txid);
|
||||||
if (auditSummary) {
|
if (auditSummary) {
|
||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24 * 30).toUTCString());
|
||||||
res.json(auditSummary);
|
res.json(auditSummary);
|
||||||
} else {
|
} else {
|
||||||
handleError(req, res, 404, `transaction audit not available`);
|
handleError(req, res, 404, `Transaction audit not available`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get transaction audit summary');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -408,7 +467,7 @@ class BitcoinRoutes {
|
|||||||
return await this.getLegacyBlocks(req, res);
|
return await this.getLegacyBlocks(req, res);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get blocks');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,7 +509,7 @@ class BitcoinRoutes {
|
|||||||
res.json(await blocks.$getBlocksBetweenHeight(from, to));
|
res.json(await blocks.$getBlocksBetweenHeight(from, to));
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get blocks');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -485,11 +544,15 @@ class BitcoinRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(returnBlocks);
|
res.json(returnBlocks);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get blocks');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getBlockTransactions(req: Request, res: Response) {
|
private async getBlockTransactions(req: Request, res: Response) {
|
||||||
|
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||||
|
handleError(req, res, 501, `Invalid block hash`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0);
|
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 0);
|
||||||
|
|
||||||
@ -510,7 +573,7 @@ class BitcoinRoutes {
|
|||||||
res.json(transactions);
|
res.json(transactions);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 100);
|
loadingIndicators.setProgress('blocktxs-' + req.params.hash, 100);
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get block transactions');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,7 +582,7 @@ class BitcoinRoutes {
|
|||||||
const blockHash = await bitcoinApi.$getBlockHash(parseInt(req.params.height, 10));
|
const blockHash = await bitcoinApi.$getBlockHash(parseInt(req.params.height, 10));
|
||||||
res.send(blockHash);
|
res.send(blockHash);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get block at height');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -528,16 +591,20 @@ class BitcoinRoutes {
|
|||||||
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!ADDRESS_REGEX.test(req.params.address)) {
|
||||||
|
handleError(req, res, 501, `Invalid address`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const addressData = await bitcoinApi.$getAddress(req.params.address);
|
const addressData = await bitcoinApi.$getAddress(req.params.address);
|
||||||
res.json(addressData);
|
res.json(addressData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||||
handleError(req, res, 413, e instanceof Error ? e.message : e);
|
handleError(req, res, 413, e.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get address');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,6 +613,10 @@ class BitcoinRoutes {
|
|||||||
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!ADDRESS_REGEX.test(req.params.address)) {
|
||||||
|
handleError(req, res, 501, `Invalid address`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let lastTxId: string = '';
|
let lastTxId: string = '';
|
||||||
@ -556,10 +627,10 @@ class BitcoinRoutes {
|
|||||||
res.json(transactions);
|
res.json(transactions);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||||
handleError(req, res, 413, e instanceof Error ? e.message : e);
|
handleError(req, res, 413, e.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get address transactions');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,6 +646,10 @@ class BitcoinRoutes {
|
|||||||
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!SCRIPT_HASH_REGEX.test(req.params.scripthash)) {
|
||||||
|
handleError(req, res, 501, `Invalid scripthash`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// electrum expects scripthashes in little-endian
|
// electrum expects scripthashes in little-endian
|
||||||
@ -583,10 +658,10 @@ class BitcoinRoutes {
|
|||||||
res.json(addressData);
|
res.json(addressData);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||||
handleError(req, res, 413, e instanceof Error ? e.message : e);
|
handleError(req, res, 413, e.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get script hash');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -595,6 +670,10 @@ class BitcoinRoutes {
|
|||||||
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
handleError(req, res, 405, 'Address lookups cannot be used with bitcoind as backend.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!SCRIPT_HASH_REGEX.test(req.params.scripthash)) {
|
||||||
|
handleError(req, res, 501, `Invalid scripthash`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// electrum expects scripthashes in little-endian
|
// electrum expects scripthashes in little-endian
|
||||||
@ -607,10 +686,10 @@ class BitcoinRoutes {
|
|||||||
res.json(transactions);
|
res.json(transactions);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
|
||||||
handleError(req, res, 413, e instanceof Error ? e.message : e);
|
handleError(req, res, 413, e.message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get script hash transactions');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,10 +702,10 @@ class BitcoinRoutes {
|
|||||||
|
|
||||||
private async getAddressPrefix(req: Request, res: Response) {
|
private async getAddressPrefix(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const blockHash = await bitcoinApi.$getAddressPrefix(req.params.prefix);
|
const addressPrefix = await bitcoinApi.$getAddressPrefix(req.params.prefix);
|
||||||
res.send(blockHash);
|
res.send(addressPrefix);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get address prefix');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,7 +746,7 @@ class BitcoinRoutes {
|
|||||||
res.setHeader('content-type', 'text/plain');
|
res.setHeader('content-type', 'text/plain');
|
||||||
res.send(result.toString());
|
res.send(result.toString());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get height at tip');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -677,39 +756,55 @@ class BitcoinRoutes {
|
|||||||
res.setHeader('content-type', 'text/plain');
|
res.setHeader('content-type', 'text/plain');
|
||||||
res.send(result);
|
res.send(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get hash at tip');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getRawBlock(req: Request, res: Response) {
|
private async getRawBlock(req: Request, res: Response) {
|
||||||
|
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||||
|
handleError(req, res, 501, `Invalid block hash`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const result = await bitcoinApi.$getRawBlock(req.params.hash);
|
const result = await bitcoinApi.$getRawBlock(req.params.hash);
|
||||||
res.setHeader('content-type', 'application/octet-stream');
|
res.setHeader('content-type', 'application/octet-stream');
|
||||||
res.send(result);
|
res.send(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get raw block');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getTxIdsForBlock(req: Request, res: Response) {
|
private async getTxIdsForBlock(req: Request, res: Response) {
|
||||||
|
if (!BLOCK_HASH_REGEX.test(req.params.hash)) {
|
||||||
|
handleError(req, res, 501, `Invalid block hash`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const result = await bitcoinApi.$getTxIdsForBlock(req.params.hash);
|
const result = await bitcoinApi.$getTxIdsForBlock(req.params.hash);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get txids for block');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async validateAddress(req: Request, res: Response) {
|
private async validateAddress(req: Request, res: Response) {
|
||||||
|
if (!ADDRESS_REGEX.test(req.params.address)) {
|
||||||
|
handleError(req, res, 501, `Invalid address`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const result = await bitcoinClient.validateAddress(req.params.address);
|
const result = await bitcoinClient.validateAddress(req.params.address);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to validate address');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getRbfHistory(req: Request, res: Response) {
|
private async getRbfHistory(req: Request, res: Response) {
|
||||||
|
if (!TXID_REGEX.test(req.params.txId)) {
|
||||||
|
handleError(req, res, 501, `Invalid transaction ID`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const replacements = rbfCache.getRbfTree(req.params.txId) || null;
|
const replacements = rbfCache.getRbfTree(req.params.txId) || null;
|
||||||
const replaces = rbfCache.getReplaces(req.params.txId) || null;
|
const replaces = rbfCache.getReplaces(req.params.txId) || null;
|
||||||
@ -718,7 +813,7 @@ class BitcoinRoutes {
|
|||||||
replaces
|
replaces
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get rbf history');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -727,7 +822,7 @@ class BitcoinRoutes {
|
|||||||
const result = rbfCache.getRbfTrees(false);
|
const result = rbfCache.getRbfTrees(false);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get rbf trees');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -736,11 +831,15 @@ class BitcoinRoutes {
|
|||||||
const result = rbfCache.getRbfTrees(true);
|
const result = rbfCache.getRbfTrees(true);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get full rbf replacements');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getCachedTx(req: Request, res: Response) {
|
private async getCachedTx(req: Request, res: Response) {
|
||||||
|
if (!TXID_REGEX.test(req.params.txId)) {
|
||||||
|
handleError(req, res, 501, `Invalid transaction ID`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const result = rbfCache.getTx(req.params.txId);
|
const result = rbfCache.getTx(req.params.txId);
|
||||||
if (result) {
|
if (result) {
|
||||||
@ -749,16 +848,20 @@ class BitcoinRoutes {
|
|||||||
res.status(204).send();
|
res.status(204).send();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get cached tx');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getTransactionOutspends(req: Request, res: Response) {
|
private async getTransactionOutspends(req: Request, res: Response) {
|
||||||
|
if (!TXID_REGEX.test(req.params.txId)) {
|
||||||
|
handleError(req, res, 501, `Invalid transaction ID`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const result = await bitcoinApi.$getOutspends(req.params.txId);
|
const result = await bitcoinApi.$getOutspends(req.params.txId);
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get transaction outspends');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -771,7 +874,7 @@ class BitcoinRoutes {
|
|||||||
handleError(req, res, 503, `Service Temporarily Unavailable`);
|
handleError(req, res, 503, `Service Temporarily Unavailable`);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get difficulty change');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -782,8 +885,8 @@ class BitcoinRoutes {
|
|||||||
const txIdResult = await bitcoinApi.$sendRawTransaction(rawTx);
|
const txIdResult = await bitcoinApi.$sendRawTransaction(rawTx);
|
||||||
res.send(txIdResult);
|
res.send(txIdResult);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
handleError(req, res, 400, 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 })
|
||||||
: (e.message || 'Error'));
|
: 'Failed to send raw transaction');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -794,8 +897,8 @@ class BitcoinRoutes {
|
|||||||
const txIdResult = await bitcoinClient.sendRawTransaction(txHex);
|
const txIdResult = await bitcoinClient.sendRawTransaction(txHex);
|
||||||
res.send(txIdResult);
|
res.send(txIdResult);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
handleError(req, res, 400, 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 })
|
||||||
: (e.message || 'Error'));
|
: 'Failed to send raw transaction');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -806,8 +909,8 @@ class BitcoinRoutes {
|
|||||||
const result = await bitcoinApi.$testMempoolAccept(rawTxs, maxfeerate);
|
const result = await bitcoinApi.$testMempoolAccept(rawTxs, maxfeerate);
|
||||||
res.send(result);
|
res.send(result);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
handleError(req, res, 400, 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 })
|
||||||
: (e.message || 'Error'));
|
: 'Failed to test transactions');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -819,8 +922,8 @@ class BitcoinRoutes {
|
|||||||
const result = await bitcoinClient.submitPackage(rawTxs, maxfeerate ?? undefined, maxburnamount ?? undefined);
|
const result = await bitcoinClient.submitPackage(rawTxs, maxfeerate ?? undefined, maxburnamount ?? undefined);
|
||||||
res.send(result);
|
res.send(result);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
handleError(req, res, 400, e.message && e.code ? 'submitpackage RPC error: ' + JSON.stringify({ code: e.code, message: e.message })
|
handleError(req, res, 400, (e.message && e.code) ? 'submitpackage RPC error: ' + JSON.stringify({ code: e.code })
|
||||||
: (e.message || 'Error'));
|
: 'Failed to submit package');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ import { Application, Request, Response } from 'express';
|
|||||||
import channelsApi from './channels.api';
|
import channelsApi from './channels.api';
|
||||||
import { handleError } from '../../utils/api';
|
import { handleError } from '../../utils/api';
|
||||||
|
|
||||||
|
const TXID_REGEX = /^[a-f0-9]{64}$/i;
|
||||||
|
|
||||||
class ChannelsRoutes {
|
class ChannelsRoutes {
|
||||||
constructor() { }
|
constructor() { }
|
||||||
|
|
||||||
@ -23,7 +25,7 @@ class ChannelsRoutes {
|
|||||||
const channels = await channelsApi.$searchChannelsById(req.params.search);
|
const channels = await channelsApi.$searchChannelsById(req.params.search);
|
||||||
res.json(channels);
|
res.json(channels);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to search channels by id');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +41,7 @@ class ChannelsRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(channel);
|
res.json(channel);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get channel');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +72,7 @@ class ChannelsRoutes {
|
|||||||
res.header('X-Total-Count', channelsCount.toString());
|
res.header('X-Total-Count', channelsCount.toString());
|
||||||
res.json(channels);
|
res.json(channels);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get channels for node');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +85,10 @@ class ChannelsRoutes {
|
|||||||
const txIds: string[] = [];
|
const txIds: string[] = [];
|
||||||
for (const _txId in req.query.txId) {
|
for (const _txId in req.query.txId) {
|
||||||
if (typeof req.query.txId[_txId] === 'string') {
|
if (typeof req.query.txId[_txId] === 'string') {
|
||||||
txIds.push(req.query.txId[_txId].toString());
|
const txid = req.query.txId[_txId].toString();
|
||||||
|
if (TXID_REGEX.test(txid)) {
|
||||||
|
txIds.push(txid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const channels = await channelsApi.$getChannelsByTransactionId(txIds);
|
const channels = await channelsApi.$getChannelsByTransactionId(txIds);
|
||||||
@ -108,7 +113,7 @@ class ChannelsRoutes {
|
|||||||
|
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get channels by transaction ids');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +125,7 @@ class ChannelsRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(channels);
|
res.json(channels);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get penalty closed channels');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +138,7 @@ class ChannelsRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(channels);
|
res.json(channels);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get channel geodata');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ class GeneralLightningRoutes {
|
|||||||
channels: channels,
|
channels: channels,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to search for nodes and channels');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ class GeneralLightningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(statistics);
|
res.json(statistics);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get lightning statistics');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ class GeneralLightningRoutes {
|
|||||||
const statistics = await statisticsApi.$getLatestStatistics();
|
const statistics = await statisticsApi.$getLatestStatistics();
|
||||||
res.json(statistics);
|
res.json(statistics);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get lightning statistics');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ class NodesRoutes {
|
|||||||
const nodes = await nodesApi.$searchNodeByPublicKeyOrAlias(req.params.search);
|
const nodes = await nodesApi.$searchNodeByPublicKeyOrAlias(req.params.search);
|
||||||
res.json(nodes);
|
res.json(nodes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to search for node');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +188,7 @@ class NodesRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(nodes);
|
res.json(nodes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get node group');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ class NodesRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(node);
|
res.json(node);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get node');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +216,7 @@ class NodesRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(statistics);
|
res.json(statistics);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get historical node stats');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +232,7 @@ class NodesRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(node);
|
res.json(node);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get fee histogram');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +248,7 @@ class NodesRoutes {
|
|||||||
topByChannels: topChannelsNodes,
|
topByChannels: topChannelsNodes,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get nodes ranking');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +260,7 @@ class NodesRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(topCapacityNodes);
|
res.json(topCapacityNodes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get top nodes by capacity');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,7 +272,7 @@ class NodesRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(topCapacityNodes);
|
res.json(topCapacityNodes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get top nodes by channels');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +284,7 @@ class NodesRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(topCapacityNodes);
|
res.json(topCapacityNodes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get oldest nodes');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +296,7 @@ class NodesRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString());
|
||||||
res.json(nodesPerAs);
|
res.json(nodesPerAs);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get ISP ranking');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,7 +308,7 @@ class NodesRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString());
|
||||||
res.json(worldNodes);
|
res.json(worldNodes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get world nodes');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +336,7 @@ class NodesRoutes {
|
|||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get nodes per country');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,7 +363,7 @@ class NodesRoutes {
|
|||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get nodes per ISP');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,7 +375,7 @@ class NodesRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 600).toUTCString());
|
||||||
res.json(nodesPerAs);
|
res.json(nodesPerAs);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get nodes per country');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60).toUTCString());
|
||||||
res.json(pegs);
|
res.json(pegs);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get pegs by month');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60 * 60).toUTCString());
|
||||||
res.json(reserves);
|
res.json(reserves);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get reserves by month');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(currentSupply);
|
res.json(currentSupply);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get pegs');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(currentReserves);
|
res.json(currentReserves);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get reserves');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(auditStatus);
|
res.json(auditStatus);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get federation audit status');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +143,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(federationAddresses);
|
res.json(federationAddresses);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get federation addresses');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(federationAddresses);
|
res.json(federationAddresses);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get federation addresses');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(federationUtxos);
|
res.json(federationUtxos);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get federation utxos');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(expiredUtxos);
|
res.json(expiredUtxos);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get expired utxos');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(federationUtxos);
|
res.json(federationUtxos);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get federation utxos number');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(emergencySpentUtxos);
|
res.json(emergencySpentUtxos);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get emergency spent utxos');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(emergencySpentUtxos);
|
res.json(emergencySpentUtxos);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get emergency spent utxos stats');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +227,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(recentPegs);
|
res.json(recentPegs);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get pegs list');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(pegsVolume);
|
res.json(pegsVolume);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get pegs volume daily');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +251,7 @@ class LiquidRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 30).toUTCString());
|
||||||
res.json(pegsCount);
|
res.json(pegsCount);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get pegs count');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ class MiningRoutes {
|
|||||||
}
|
}
|
||||||
res.status(200).send(response);
|
res.status(200).send(response);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get historical prices');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ class MiningRoutes {
|
|||||||
if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) {
|
if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) {
|
||||||
handleError(req, res, 404, e.message);
|
handleError(req, res, 404, e.message);
|
||||||
} else {
|
} else {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get pool');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ class MiningRoutes {
|
|||||||
if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) {
|
if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) {
|
||||||
handleError(req, res, 404, e.message);
|
handleError(req, res, 404, e.message);
|
||||||
} else {
|
} else {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get blocks for pool');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,7 +130,7 @@ class MiningRoutes {
|
|||||||
res.json(pools);
|
res.json(pools);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get pools');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +144,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(stats);
|
res.json(stats);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get pools');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
||||||
res.json(hashrates);
|
res.json(hashrates);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get pools historical hashrate');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +175,7 @@ class MiningRoutes {
|
|||||||
if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) {
|
if (e instanceof Error && e.message.indexOf('This mining pool does not exist') > -1) {
|
||||||
handleError(req, res, 404, e.message);
|
handleError(req, res, 404, e.message);
|
||||||
} else {
|
} else {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get pool historical hashrate');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ class MiningRoutes {
|
|||||||
currentDifficulty: currentDifficulty,
|
currentDifficulty: currentDifficulty,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get historical hashrate');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +218,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(blockFees);
|
res.json(blockFees);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get historical block fees');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +236,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(blockFees);
|
res.json(blockFees);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get historical block fees');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,7 +250,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(blockRewards);
|
res.json(blockRewards);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get historical block rewards');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +264,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(blockFeeRates);
|
res.json(blockFeeRates);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get historical block fee rates');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,7 +282,7 @@ class MiningRoutes {
|
|||||||
weights: blockWeights
|
weights: blockWeights
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get historical block size and weight');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +294,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
||||||
res.json(difficulty.map(adj => [adj.time, adj.height, adj.difficulty, adj.adjustment]));
|
res.json(difficulty.map(adj => [adj.time, adj.height, adj.difficulty, adj.adjustment]));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get historical difficulty adjustments');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,7 +304,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(response);
|
res.json(response);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).end();
|
handleError(req, res, 500, 'Failed to get reward stats');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,7 +318,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(blocksHealth.map(health => [health.time, health.height, health.match_rate]));
|
res.json(blocksHealth.map(health => [health.time, health.height, health.match_rate]));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get historical blocks health');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,7 +336,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString());
|
||||||
res.json(audit);
|
res.json(audit);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get block audit');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,7 +359,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get height from timestamp');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,7 +372,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 60).toUTCString());
|
||||||
res.json(await BlocksAuditsRepository.$getBlockAuditScores(height, height - 15));
|
res.json(await BlocksAuditsRepository.$getBlockAuditScores(height, height - 15));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get block audit scores');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,7 +385,7 @@ class MiningRoutes {
|
|||||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString());
|
res.setHeader('Expires', new Date(Date.now() + 1000 * 3600 * 24).toUTCString());
|
||||||
res.json(audit || 'null');
|
res.json(audit || 'null');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get block audit score');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,7 +400,7 @@ class MiningRoutes {
|
|||||||
}
|
}
|
||||||
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(req.params.slug));
|
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(req.params.slug));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get accelerations by pool');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,7 +416,7 @@ class MiningRoutes {
|
|||||||
const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10);
|
const height = req.params.height === undefined ? undefined : parseInt(req.params.height, 10);
|
||||||
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, height));
|
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, height));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get accelerations by height');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,7 +431,7 @@ class MiningRoutes {
|
|||||||
}
|
}
|
||||||
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, null, req.params.interval));
|
res.status(200).send(await AccelerationRepository.$getAccelerationInfo(null, null, req.params.interval));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get recent accelerations');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,7 +446,7 @@ class MiningRoutes {
|
|||||||
}
|
}
|
||||||
res.status(200).send(await AccelerationRepository.$getAccelerationTotals(<string>req.query.pool, <string>req.query.interval));
|
res.status(200).send(await AccelerationRepository.$getAccelerationTotals(<string>req.query.pool, <string>req.query.interval));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get acceleration totals');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,7 +461,7 @@ class MiningRoutes {
|
|||||||
}
|
}
|
||||||
res.status(200).send(Object.values(accelerationApi.getAccelerations() || {}));
|
res.status(200).send(Object.values(accelerationApi.getAccelerations() || {}));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get active accelerations');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,7 +473,7 @@ class MiningRoutes {
|
|||||||
accelerationApi.accelerationRequested(req.params.txid);
|
accelerationApi.accelerationRequested(req.params.txid);
|
||||||
res.status(200).send();
|
res.status(200).send();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(req, res, 500, e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to request acceleration');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Application, Request, Response } from 'express';
|
import { Application, Request, Response } from 'express';
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
import WalletApi from './wallets';
|
import WalletApi from './wallets';
|
||||||
|
import { handleError } from '../../utils/api';
|
||||||
|
|
||||||
class ServicesRoutes {
|
class ServicesRoutes {
|
||||||
public initRoutes(app: Application): void {
|
public initRoutes(app: Application): void {
|
||||||
@ -18,7 +19,7 @@ class ServicesRoutes {
|
|||||||
const wallet = await WalletApi.getWallet(walletId);
|
const wallet = await WalletApi.getWallet(walletId);
|
||||||
res.status(200).send(wallet);
|
res.status(200).send(wallet);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get wallet');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Application, Request, Response } from 'express';
|
import { Application, Request, Response } from 'express';
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
import statisticsApi from './statistics-api';
|
import statisticsApi from './statistics-api';
|
||||||
|
import { handleError } from '../../utils/api';
|
||||||
class StatisticsRoutes {
|
class StatisticsRoutes {
|
||||||
public initRoutes(app: Application) {
|
public initRoutes(app: Application) {
|
||||||
app
|
app
|
||||||
@ -65,7 +65,7 @@ class StatisticsRoutes {
|
|||||||
}
|
}
|
||||||
res.json(result);
|
res.json(result);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
res.status(500).send(e instanceof Error ? e.message : e);
|
handleError(req, res, 500, 'Failed to get statistics');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,7 +324,9 @@ class Server {
|
|||||||
|
|
||||||
setUpHttpApiRoutes(): void {
|
setUpHttpApiRoutes(): void {
|
||||||
bitcoinRoutes.initRoutes(this.app);
|
bitcoinRoutes.initRoutes(this.app);
|
||||||
|
if (config.MEMPOOL.OFFICIAL) {
|
||||||
bitcoinCoreRoutes.initRoutes(this.app);
|
bitcoinCoreRoutes.initRoutes(this.app);
|
||||||
|
}
|
||||||
pricesRoutes.initRoutes(this.app);
|
pricesRoutes.initRoutes(this.app);
|
||||||
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && config.MEMPOOL.ENABLED) {
|
if (config.STATISTICS.ENABLED && config.DATABASE.ENABLED && config.MEMPOOL.ENABLED) {
|
||||||
statisticsRoutes.initRoutes(this.app);
|
statisticsRoutes.initRoutes(this.app);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user