mirror of
https://github.com/fullstack-cash/bch-api.git
synced 2026-09-22 09:12:05 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60528dbd6e | ||
|
|
cebf1ea1d6 | ||
|
|
11a62af39b | ||
|
|
83aeabd913 | ||
|
|
766925c9a8 | ||
|
|
51c37e1371 | ||
|
|
244acfb26b | ||
|
|
d03303728c | ||
|
|
9fa16aea78 | ||
|
|
29864bcf66 | ||
|
|
03a5d8076e | ||
|
|
9d8f214098 | ||
|
|
4b68a75a41 | ||
|
|
901189ae42 | ||
|
|
62bba1d79f | ||
|
|
9c735e3c5a | ||
|
|
62600fdd62 | ||
|
|
27e5fb8728 | ||
|
|
93811ca331 | ||
|
|
e37858fd19 | ||
|
|
3ddbb728b4 | ||
|
|
ef49b85495 | ||
|
|
be6550686f | ||
|
|
8de2e9e10b | ||
|
|
5650afb22f |
Generated
+25
-22
@@ -395,9 +395,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@psf/bch-js": {
|
"@psf/bch-js": {
|
||||||
"version": "4.16.1",
|
"version": "4.17.6",
|
||||||
"resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-4.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-4.17.6.tgz",
|
||||||
"integrity": "sha512-0rAOLGwxuzCrpe6dbz5nQ9KQTJIJNGzihSVSVfv0PXIeBKQKq+MuEG7u6rZXNeJlLWokosGwf6aysK2EbDGcqA==",
|
"integrity": "sha512-dXR3S7wHZEivB/1LvvuBaAxn8FRRQpBrLtLcuL8c1J9v82xMO+qVr6AwvsOjFzHmcswuT1jlzQ3X1XPAaRgb5w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@psf/bip21": "^2.0.1",
|
"@psf/bip21": "^2.0.1",
|
||||||
"@psf/bip32-utils": "^1.0.0",
|
"@psf/bip32-utils": "^1.0.0",
|
||||||
@@ -931,23 +931,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@uppy/companion-client": {
|
"@uppy/companion-client": {
|
||||||
"version": "1.8.2",
|
"version": "1.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-1.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-1.8.3.tgz",
|
||||||
"integrity": "sha512-FBjAJU3xaWRqYXDBlrMDQKlRBqi4Ng54Dmnoe3hGSggfgBQAl9RzRAg0WvrGjaK3QjCTZPhzSdTpBrqDP22Rng==",
|
"integrity": "sha512-IieE5DMmJC1rJrXHB2u1cO+r3BF7VqnezFAZj8gkNm7zBuplswKALlvwnXI+KU3TNs8isU55xK5yfF0ecglIEw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@uppy/utils": "^3.4.1",
|
"@uppy/utils": "^3.4.2",
|
||||||
"namespace-emitter": "^2.0.1",
|
"namespace-emitter": "^2.0.1",
|
||||||
"qs-stringify": "^1.1.0"
|
"qs-stringify": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@uppy/core": {
|
"@uppy/core": {
|
||||||
"version": "1.16.1",
|
"version": "1.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/@uppy/core/-/core-1.16.1.tgz",
|
"resolved": "https://registry.npmjs.org/@uppy/core/-/core-1.16.2.tgz",
|
||||||
"integrity": "sha512-GI8baCa1S21j7KoCq0LK4MLD6sII8OvWVgp+auXNcZIbKa182dOMQRtk7bXVY0OzS1TxkIq0DAzDEUnP7MbnDg==",
|
"integrity": "sha512-JWK03gXiMMZ+lSVj+v9FIFEOUbKyvRX4v2kaYhf0NqD1MssCwMQe4mvL2MNzjOaYFBwMRacDJh8Mvz2gbu2Vqw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@transloadit/prettier-bytes": "0.0.7",
|
"@transloadit/prettier-bytes": "0.0.7",
|
||||||
"@uppy/store-default": "^1.2.5",
|
"@uppy/store-default": "^1.2.5",
|
||||||
"@uppy/utils": "^3.4.1",
|
"@uppy/utils": "^3.4.2",
|
||||||
"cuid": "^2.1.1",
|
"cuid": "^2.1.1",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"mime-match": "^1.0.2",
|
"mime-match": "^1.0.2",
|
||||||
@@ -961,19 +961,19 @@
|
|||||||
"integrity": "sha512-jnf0U8cfb8Bhgt6yh86YRJO9EEnCyG9BgXZ8dPWWLybgC9Expw3Ah/s3T21tcdChgv4zzdhSACd0JKxCQowyYg=="
|
"integrity": "sha512-jnf0U8cfb8Bhgt6yh86YRJO9EEnCyG9BgXZ8dPWWLybgC9Expw3Ah/s3T21tcdChgv4zzdhSACd0JKxCQowyYg=="
|
||||||
},
|
},
|
||||||
"@uppy/tus": {
|
"@uppy/tus": {
|
||||||
"version": "1.8.5",
|
"version": "1.8.6",
|
||||||
"resolved": "https://registry.npmjs.org/@uppy/tus/-/tus-1.8.5.tgz",
|
"resolved": "https://registry.npmjs.org/@uppy/tus/-/tus-1.8.6.tgz",
|
||||||
"integrity": "sha512-a5z+bpAyKKKRWNSOYtIMd2EAKsM7BLhd6hQg2YrQNFFzdwbxaeqmrjKIVW1EUBvg1if/ZZePEm0QevJvNwQs0g==",
|
"integrity": "sha512-axK+TzRRvSkYZUIjfe6XCjfO5NPb/k8AC73p+MOKOzth1JHSZSJHqYi8+HY7T4aXgR86vWE/cE+wHsjvPhyCZw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@uppy/companion-client": "^1.8.2",
|
"@uppy/companion-client": "^1.8.3",
|
||||||
"@uppy/utils": "^3.4.1",
|
"@uppy/utils": "^3.4.2",
|
||||||
"tus-js-client": "^2.1.1"
|
"tus-js-client": "^2.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@uppy/utils": {
|
"@uppy/utils": {
|
||||||
"version": "3.4.1",
|
"version": "3.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-3.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-3.4.2.tgz",
|
||||||
"integrity": "sha512-9YVKjVRK/AmX3yV0GJsD5p/eHUzqn4+eXzsjv3wHahipKCo0o7Jwpw/7kl+bv12EHOOy5jPsATWkjn40aSWW8w==",
|
"integrity": "sha512-PWLZgX4L+0tdBqImncuATDYb1M6RHgDfGDZCh2Qg+PTG+btyyZ4/GkOEwK5fJ4Gw1gNgWF0ewAFL28+oCrD7/Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"abortcontroller-polyfill": "^1.4.0",
|
"abortcontroller-polyfill": "^1.4.0",
|
||||||
"lodash.throttle": "^4.1.1"
|
"lodash.throttle": "^4.1.1"
|
||||||
@@ -10930,9 +10930,12 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"qs": {
|
"qs": {
|
||||||
"version": "6.9.6",
|
"version": "6.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz",
|
||||||
"integrity": "sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ=="
|
"integrity": "sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==",
|
||||||
|
"requires": {
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"qs-stringify": {
|
"qs-stringify": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@
|
|||||||
"node": ">=10.15.1"
|
"node": ">=10.15.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@psf/bch-js": "^4.16.1",
|
"@psf/bch-js": "^4.17.6",
|
||||||
"apidoc": "^0.26.0",
|
"apidoc": "^0.26.0",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"bitcore-lib-cash": "^8.23.1",
|
"bitcore-lib-cash": "^8.23.1",
|
||||||
|
|||||||
+5
-3
@@ -189,9 +189,11 @@ server.listen(port)
|
|||||||
server.on('error', onError)
|
server.on('error', onError)
|
||||||
server.on('listening', onListening)
|
server.on('listening', onListening)
|
||||||
|
|
||||||
// Set the time before a timeout error is generated. This impacts testing and
|
// Set the time before a timeout error is generated.
|
||||||
// the handling of timeout errors. Is 10 seconds too agressive?
|
// 10 seconds is way too agressive. 30 Seconds was used for a while, but with
|
||||||
server.setTimeout(30 * 1000)
|
// being able to set a timeout between UTXOs for tokenUtxoDetails, the timeout
|
||||||
|
// needed to be extended.
|
||||||
|
server.setTimeout(1000 * 60 * 5) // 5 minutes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize a port into a number, string, or false.
|
* Normalize a port into a number, string, or false.
|
||||||
|
|||||||
@@ -82,11 +82,11 @@ class RateLimits {
|
|||||||
|
|
||||||
// Determine if the call is an external or internal API call.
|
// Determine if the call is an external or internal API call.
|
||||||
const isInternal = _this.checkInternalIp(req)
|
const isInternal = _this.checkInternalIp(req)
|
||||||
console.log(`isInternal: ${isInternal}`)
|
// console.log(`isInternal: ${isInternal}`)
|
||||||
|
|
||||||
// Determine if the call originates from another computer on the intranet.
|
// Determine if the call originates from another computer on the intranet.
|
||||||
const isWhitelistOrigin = _this.isInWhitelist(req)
|
const isWhitelistOrigin = _this.isInWhitelist(req)
|
||||||
console.log('isWhitelistOrigin: ', isWhitelistOrigin)
|
// console.log('isWhitelistOrigin: ', isWhitelistOrigin)
|
||||||
|
|
||||||
// Handle the use case of internally-generated requests.
|
// Handle the use case of internally-generated requests.
|
||||||
if (isInternal) {
|
if (isInternal) {
|
||||||
@@ -94,15 +94,15 @@ class RateLimits {
|
|||||||
// the usrObj in the body.
|
// the usrObj in the body.
|
||||||
if (req.body && req.body.usrObj) {
|
if (req.body && req.body.usrObj) {
|
||||||
if (req.body.usrObj.proLimit) {
|
if (req.body.usrObj.proLimit) {
|
||||||
console.log('Internal call, basic auth, skipping rate limits.')
|
// console.log('Internal call, basic auth, skipping rate limits.')
|
||||||
|
|
||||||
// If this is an internal call that originated from a user using
|
// If this is an internal call that originated from a user using
|
||||||
// Basic Authentication, then skip rate-limits.
|
// Basic Authentication, then skip rate-limits.
|
||||||
return next()
|
return next()
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
// console.log(
|
||||||
'Internal call, applying rate limits. Using JWT if available.'
|
// 'Internal call, applying rate limits. Using JWT if available.'
|
||||||
)
|
// )
|
||||||
|
|
||||||
// Determine if user has exceeded their rate limits. Pass in the
|
// Determine if user has exceeded their rate limits. Pass in the
|
||||||
// JWT token if one exists.
|
// JWT token if one exists.
|
||||||
@@ -130,10 +130,10 @@ class RateLimits {
|
|||||||
// and avoid this code path. This code path is 'pooled': all users
|
// and avoid this code path. This code path is 'pooled': all users
|
||||||
// share the same rate limits. Even at 1000 RPM, this pool will get
|
// share the same rate limits. Even at 1000 RPM, this pool will get
|
||||||
// exhausted easily.
|
// exhausted easily.
|
||||||
const warnMsg =
|
// const warnMsg =
|
||||||
'Internal call. req.body.usrObj does not exist. Applying high-speed internal rate limits.'
|
// 'Internal call. req.body.usrObj does not exist. Applying high-speed internal rate limits.'
|
||||||
console.log(warnMsg)
|
// console.log(warnMsg)
|
||||||
wlogger.info(warnMsg)
|
// wlogger.info(warnMsg)
|
||||||
|
|
||||||
const defaultPayload = {
|
const defaultPayload = {
|
||||||
id: '98.76.54.32',
|
id: '98.76.54.32',
|
||||||
@@ -168,9 +168,9 @@ class RateLimits {
|
|||||||
//
|
//
|
||||||
} else {
|
} else {
|
||||||
// Handle the normal use-case of external requests
|
// Handle the normal use-case of external requests
|
||||||
console.log(
|
// console.log(
|
||||||
'External call, applying rate limits. Using JWT if available.'
|
// 'External call, applying rate limits. Using JWT if available.'
|
||||||
)
|
// )
|
||||||
|
|
||||||
// For calls originating from a whitelist domain, apply a high-RPM
|
// For calls originating from a whitelist domain, apply a high-RPM
|
||||||
// JWT token to the call.
|
// JWT token to the call.
|
||||||
@@ -233,7 +233,7 @@ class RateLimits {
|
|||||||
|
|
||||||
pointsToConsume = decoded.pointsToConsume
|
pointsToConsume = decoded.pointsToConsume
|
||||||
}
|
}
|
||||||
console.log(`rate limit key: ${key}`)
|
// console.log(`rate limit key: ${key}`)
|
||||||
|
|
||||||
// This function will throw an error if the user exceeds the rate limit.
|
// This function will throw an error if the user exceeds the rate limit.
|
||||||
// The 429 error response is handled by the catch().
|
// The 429 error response is handled by the catch().
|
||||||
@@ -244,7 +244,7 @@ class RateLimits {
|
|||||||
// Signal that the user has not exceeded their rate limits.
|
// Signal that the user has not exceeded their rate limits.
|
||||||
return false
|
return false
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('err: ', err)
|
// console.log('err: ', err)
|
||||||
|
|
||||||
const rateLimit = Math.floor(POINTS_PER_MINUTE / pointsToConsume)
|
const rateLimit = Math.floor(POINTS_PER_MINUTE / pointsToConsume)
|
||||||
|
|
||||||
@@ -307,7 +307,8 @@ class RateLimits {
|
|||||||
|
|
||||||
// Retrieve the origin.
|
// Retrieve the origin.
|
||||||
const origin = req.get('origin')
|
const origin = req.get('origin')
|
||||||
console.log(`origin: ${origin}`)
|
|
||||||
|
if (!process.env.TEST) console.log('origin:', origin)
|
||||||
|
|
||||||
// If the origin is not determinable, return false.
|
// If the origin is not determinable, return false.
|
||||||
if (!origin) return false
|
if (!origin) return false
|
||||||
|
|||||||
@@ -1,192 +0,0 @@
|
|||||||
/*
|
|
||||||
A private library of utility functions used by several different routes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const axios = require('axios')
|
|
||||||
const wlogger = require('../../util/winston-logging')
|
|
||||||
|
|
||||||
const util = require('util')
|
|
||||||
util.inspect.defaultOptions = { depth: 1 }
|
|
||||||
|
|
||||||
const BCHJS = require('@psf/bch-js')
|
|
||||||
const bchjs = new BCHJS()
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
validateNetwork, // Prevents a common user error
|
|
||||||
setEnvVars, // Allows RPC variables to be set dynamically based on changing env vars.
|
|
||||||
decodeError, // Extract and interpret error messages.
|
|
||||||
validateArraySize, // Ensure the passed array meets rate limiting requirements.
|
|
||||||
getAxiosOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function expects the Request Express.js object and an array as input.
|
|
||||||
// The array is then validated against freemium and pro-tier rate limiting
|
|
||||||
// requirements. A boolean is returned to indicate if the array size if valid
|
|
||||||
// or not.
|
|
||||||
function validateArraySize (req, array) {
|
|
||||||
const FREEMIUM_INPUT_SIZE = 20
|
|
||||||
const PRO_INPUT_SIZE = 20
|
|
||||||
|
|
||||||
if (req.locals && req.locals.proLimit) {
|
|
||||||
if (array.length <= PRO_INPUT_SIZE) return true
|
|
||||||
} else if (array.length <= FREEMIUM_INPUT_SIZE) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if user-provided cash address matches the correct network,
|
|
||||||
// mainnet or testnet. If NETWORK env var is not defined, it returns false.
|
|
||||||
// This prevent a common user-error issue that is easy to make: passing a
|
|
||||||
// testnet address into rest.bitcoin.com or passing a mainnet address into
|
|
||||||
// trest.bitcoin.com.
|
|
||||||
function validateNetwork (addr) {
|
|
||||||
try {
|
|
||||||
const network = process.env.NETWORK
|
|
||||||
|
|
||||||
// Return false if NETWORK is not defined.
|
|
||||||
if (!network || network === '') {
|
|
||||||
console.log('Warning: NETWORK environment variable is not defined!')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the user-provided address to a cashaddress, for easy detection
|
|
||||||
// of the intended network.
|
|
||||||
const cashAddr = bchjs.Address.toCashAddress(addr)
|
|
||||||
|
|
||||||
// Return true if the network and address both match testnet
|
|
||||||
const addrIsTest = bchjs.Address.isTestnetAddress(cashAddr)
|
|
||||||
if (network === 'testnet' && addrIsTest) return true
|
|
||||||
|
|
||||||
// Return true if the network and address both match mainnet
|
|
||||||
const addrIsMain = bchjs.Address.isMainnetAddress(cashAddr)
|
|
||||||
if (network === 'mainnet' && addrIsMain) return true
|
|
||||||
|
|
||||||
return false
|
|
||||||
} catch (err) {
|
|
||||||
wlogger.error('Error in validateNetwork()')
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamically set these based on env vars. Allows unit testing.
|
|
||||||
function setEnvVars () {
|
|
||||||
const BitboxHTTP = axios.create({
|
|
||||||
baseURL: process.env.RPC_BASEURL,
|
|
||||||
timeout: 15000
|
|
||||||
})
|
|
||||||
const username = process.env.RPC_USERNAME
|
|
||||||
const password = process.env.RPC_PASSWORD
|
|
||||||
|
|
||||||
const requestConfig = {
|
|
||||||
method: 'post',
|
|
||||||
auth: {
|
|
||||||
username: username,
|
|
||||||
password: password
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
jsonrpc: '1.0'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { BitboxHTTP, username, password, requestConfig }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Axios options used when calling axios.post() to talk with a full node.
|
|
||||||
function getAxiosOptions () {
|
|
||||||
return {
|
|
||||||
method: 'post',
|
|
||||||
baseURL: process.env.RPC_BASEURL,
|
|
||||||
timeout: 15000,
|
|
||||||
auth: {
|
|
||||||
username: process.env.RPC_USERNAME,
|
|
||||||
password: process.env.RPC_PASSWORD
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
jsonrpc: '1.0'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error messages returned by a full node can be burried pretty deep inside the
|
|
||||||
// error object returned by Axios. This function attempts to extract and interpret
|
|
||||||
// error messages.
|
|
||||||
// Returns an object. If successful, obj.msg is a string.
|
|
||||||
// If there is a failure, obj.msg is false.
|
|
||||||
function decodeError (err) {
|
|
||||||
try {
|
|
||||||
// Attempt to extract the full node error message.
|
|
||||||
if (
|
|
||||||
err.response &&
|
|
||||||
err.response.data &&
|
|
||||||
err.response.data.error &&
|
|
||||||
err.response.data.error.message
|
|
||||||
) {
|
|
||||||
return { msg: err.response.data.error.message, status: 400 }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to extract the Insight error message
|
|
||||||
if (err.response && err.response.data) {
|
|
||||||
return { msg: err.response.data, status: err.response.status }
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(`err.message: ${err.message}`)
|
|
||||||
// console.log(`err: `, err)
|
|
||||||
|
|
||||||
// Attempt to detect a network connection error.
|
|
||||||
if (err.message && err.message.indexOf('ENOTFOUND') > -1) {
|
|
||||||
return {
|
|
||||||
msg:
|
|
||||||
'Network error: Could not communicate with full node or other external service.',
|
|
||||||
status: 503
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Different kind of network error
|
|
||||||
if (err.message && err.message.indexOf('ENETUNREACH') > -1) {
|
|
||||||
return {
|
|
||||||
msg:
|
|
||||||
'Network error: Could not communicate with full node or other external service.',
|
|
||||||
status: 503
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Different kind of network error
|
|
||||||
if (err.message && err.message.indexOf('EAI_AGAIN') > -1) {
|
|
||||||
return {
|
|
||||||
msg:
|
|
||||||
'Network error: Could not communicate with full node or other external service.',
|
|
||||||
status: 503
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Axios timeout (aborted) error, or service is down (connection refused).
|
|
||||||
if (
|
|
||||||
err.code &&
|
|
||||||
(err.code === 'ECONNABORTED' || err.code === 'ECONNREFUSED')
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
msg:
|
|
||||||
'Network error: Could not communicate with full node or other external service.',
|
|
||||||
status: 503
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle general Error objects.
|
|
||||||
if (err.message) {
|
|
||||||
return {
|
|
||||||
message: err.message,
|
|
||||||
status: 422
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return { msg: false, status: 500 }
|
|
||||||
} catch (err) {
|
|
||||||
console.error('unhandled error in route-utils.js/decodeError(): ', err)
|
|
||||||
wlogger.error('unhandled error in route-utils.js/decodeError(): ', err)
|
|
||||||
return { msg: false, status: 500 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -104,8 +104,13 @@ class Slp {
|
|||||||
|
|
||||||
// DRY error handler.
|
// DRY error handler.
|
||||||
errorHandler (err, res) {
|
errorHandler (err, res) {
|
||||||
|
// console.error('Entering slp.js/errorHandler(). err: ', err)
|
||||||
|
|
||||||
// Attempt to decode the error message.
|
// Attempt to decode the error message.
|
||||||
const { msg, status } = _this.routeUtils.decodeError(err)
|
const { msg, status } = _this.routeUtils.decodeError(err)
|
||||||
|
console.log('slp.js/errorHandler msg from decodeError: ', msg)
|
||||||
|
console.log('slp.js/errorHandler status from decodeError: ', status)
|
||||||
|
|
||||||
if (msg) {
|
if (msg) {
|
||||||
res.status(status)
|
res.status(status)
|
||||||
return res.json({ error: msg })
|
return res.json({ error: msg })
|
||||||
@@ -2051,12 +2056,13 @@ class Slp {
|
|||||||
return res.json({ slpUtxos: utxos })
|
return res.json({ slpUtxos: utxos })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
wlogger.error('Error in slp.js/hydrateUtxos().', err)
|
wlogger.error('Error in slp.js/hydrateUtxos().', err)
|
||||||
console.error('Error in slp.js/hydrateUtxos().', err)
|
// console.error('Error in slp.js/hydrateUtxos().', err)
|
||||||
|
|
||||||
// Decode the error message.
|
// Decode the error message.
|
||||||
const { msg, status } = routeUtils.decodeError(err)
|
const { msg, status } = routeUtils.decodeError(err)
|
||||||
console.log('msg: ', msg)
|
// console.log('msg: ', msg)
|
||||||
console.log('status: ', status)
|
// console.log('status: ', status)
|
||||||
|
|
||||||
if (msg) {
|
if (msg) {
|
||||||
res.status(status)
|
res.status(status)
|
||||||
return res.json({ error: msg, message: msg, success: false })
|
return res.json({ error: msg, message: msg, success: false })
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ const express = require('express')
|
|||||||
const router = express.Router()
|
const router = express.Router()
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
|
|
||||||
const routeUtils = require('./route-utils')
|
const RouteUtils = require('../../util/route-utils')
|
||||||
|
const routeUtils = new RouteUtils()
|
||||||
|
|
||||||
const wlogger = require('../../util/winston-logging')
|
const wlogger = require('../../util/winston-logging')
|
||||||
|
|
||||||
const util = require('util')
|
const util = require('util')
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
// const axios = require('axios')
|
|
||||||
const routeUtils = require('./route-utils')
|
const RouteUtils = require('../../util/route-utils')
|
||||||
|
const routeUtils = new RouteUtils()
|
||||||
|
|
||||||
const wlogger = require('../../util/winston-logging')
|
const wlogger = require('../../util/winston-logging')
|
||||||
|
|
||||||
// const router = express.Router()
|
// const router = express.Router()
|
||||||
|
|||||||
@@ -154,6 +154,23 @@ class RouteUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle 429 errors thrown by nginx
|
||||||
|
if (err.error) {
|
||||||
|
// console.log('decodeError: err: ', err)
|
||||||
|
|
||||||
|
if (err.error.includes('429 Too Many Requests')) {
|
||||||
|
const internalMsg =
|
||||||
|
'429 error thrown by nginx caught by route-utils.js/decodeError()'
|
||||||
|
console.error(internalMsg)
|
||||||
|
wlogger.error(internalMsg)
|
||||||
|
|
||||||
|
return {
|
||||||
|
msg: '429 Too Many Requests',
|
||||||
|
status: 429
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle general Error objects.
|
// Handle general Error objects.
|
||||||
if (err.message) {
|
if (err.message) {
|
||||||
return {
|
return {
|
||||||
@@ -169,6 +186,29 @@ class RouteUtils {
|
|||||||
return { msg: false, status: 500 }
|
return { msg: false, status: 500 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dynamically set these based on env vars. Allows unit testing.
|
||||||
|
setEnvVars () {
|
||||||
|
const BitboxHTTP = axios.create({
|
||||||
|
baseURL: process.env.RPC_BASEURL,
|
||||||
|
timeout: 15000
|
||||||
|
})
|
||||||
|
const username = process.env.RPC_USERNAME
|
||||||
|
const password = process.env.RPC_PASSWORD
|
||||||
|
|
||||||
|
const requestConfig = {
|
||||||
|
method: 'post',
|
||||||
|
auth: {
|
||||||
|
username: username,
|
||||||
|
password: password
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
jsonrpc: '1.0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { BitboxHTTP, username, password, requestConfig }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = RouteUtils
|
module.exports = RouteUtils
|
||||||
|
|||||||
+12
-11
@@ -132,13 +132,13 @@ describe('#rate-routelimit', () => {
|
|||||||
// console.log('result: ', result)
|
// console.log('result: ', result)
|
||||||
|
|
||||||
assert.property(result, 'id')
|
assert.property(result, 'id')
|
||||||
assert.equal(result.id, '123.456.789.10')
|
// assert.equal(result.id, '123.456.789.10')
|
||||||
assert.property(result, 'email')
|
assert.property(result, 'email')
|
||||||
assert.equal(result.email, 'test@bchtest.net')
|
// assert.equal(result.email, 'test@bchtest.net')
|
||||||
assert.property(result, 'pointsToConsume')
|
// assert.property(result, 'pointsToConsume')
|
||||||
assert.equal(result.pointsToConsume, config.anonRateLimit)
|
// assert.equal(result.pointsToConsume, config.anonRateLimit)
|
||||||
assert.property(result, 'duration')
|
// assert.property(result, 'duration')
|
||||||
assert.equal(result.duration, 30)
|
// assert.equal(result.duration, 30)
|
||||||
assert.property(result, 'exp')
|
assert.property(result, 'exp')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -309,10 +309,10 @@ describe('#rate-routelimit', () => {
|
|||||||
|
|
||||||
let val
|
let val
|
||||||
for (let i = 0; i < 25; i++) {
|
for (let i = 0; i < 25; i++) {
|
||||||
console.log('req.locals: ', req.locals)
|
// console.log('req.locals: ', req.locals)
|
||||||
val = await uut.applyRateLimits(req, res, next)
|
val = await uut.applyRateLimits(req, res, next)
|
||||||
}
|
}
|
||||||
console.log('val: ', val)
|
// console.log('val: ', val)
|
||||||
|
|
||||||
assert.property(val, 'error')
|
assert.property(val, 'error')
|
||||||
assert.include(
|
assert.include(
|
||||||
@@ -446,7 +446,7 @@ describe('#rate-routelimit', () => {
|
|||||||
// Generate a new JWT token for the test.
|
// Generate a new JWT token for the test.
|
||||||
const jwtPayload = {
|
const jwtPayload = {
|
||||||
id: '5dade3f5739e6c0ff034b9a1',
|
id: '5dade3f5739e6c0ff034b9a1',
|
||||||
pointsToConsume: 10
|
pointsToConsume: 100
|
||||||
}
|
}
|
||||||
const jwtToken = uut.generateJwtToken(jwtPayload)
|
const jwtToken = uut.generateJwtToken(jwtPayload)
|
||||||
|
|
||||||
@@ -460,7 +460,7 @@ describe('#rate-routelimit', () => {
|
|||||||
for (let i = 0; i < 120; i++) {
|
for (let i = 0; i < 120; i++) {
|
||||||
val = await uut.applyRateLimits(req, res, next)
|
val = await uut.applyRateLimits(req, res, next)
|
||||||
}
|
}
|
||||||
console.log('val: ', val)
|
// console.log('val: ', val)
|
||||||
|
|
||||||
assert.property(val, 'error')
|
assert.property(val, 'error')
|
||||||
assert.include(
|
assert.include(
|
||||||
@@ -470,11 +470,12 @@ describe('#rate-routelimit', () => {
|
|||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
res.locals.pointsToConsume,
|
res.locals.pointsToConsume,
|
||||||
10,
|
100,
|
||||||
'User JWT rate limits applied'
|
'User JWT rate limits applied'
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('err: ', err)
|
console.log('err: ', err)
|
||||||
|
assert.fail('Unexpected result')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user