mirror of
https://github.com/fullstack-cash/bch-api.git
synced 2026-09-21 16:52:04 -07:00
Got rough workflow sketched out for new rate limit handler
This commit is contained in:
@@ -11,7 +11,7 @@ The rate limit handling is designed for these four use cases:
|
|||||||
- Users who want to buy different RPM tiers: 100, 250, 600
|
- Users who want to buy different RPM tiers: 100, 250, 600
|
||||||
- Basic Authentication which should not have any rate limits applied.
|
- Basic Authentication which should not have any rate limits applied.
|
||||||
|
|
||||||
The Basci Auth use cases is considered when determining internal rate limits.
|
The Basic Auth use cases is considered when determining internal rate limits.
|
||||||
The internal rate limits should not be applied to calls from those users.
|
The internal rate limits should not be applied to calls from those users.
|
||||||
|
|
||||||
A lot of attention has been paid to passing rate-limit information for the user
|
A lot of attention has been paid to passing rate-limit information for the user
|
||||||
@@ -25,10 +25,10 @@ const Redis = require('ioredis')
|
|||||||
const { RateLimiterRedis } = require('rate-limiter-flexible')
|
const { RateLimiterRedis } = require('rate-limiter-flexible')
|
||||||
|
|
||||||
// local libraries.
|
// local libraries.
|
||||||
// const wlogger = require('../util/winston-logging')
|
const wlogger = require('../util/winston-logging')
|
||||||
const config = require('../../config')
|
const config = require('../../config')
|
||||||
|
|
||||||
// let _this // Global pointer to instance of class, when 'this' context is lost.
|
let _this // Global pointer to instance of class, when 'this' context is lost.
|
||||||
|
|
||||||
// Setup Redis to track rate limits for each user.
|
// Setup Redis to track rate limits for each user.
|
||||||
const redisOptions = {
|
const redisOptions = {
|
||||||
@@ -45,14 +45,14 @@ const rateLimitOptions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
// const ANON_LIMITS = config.anonRateLimit
|
const ANON_LIMITS = config.anonRateLimit
|
||||||
// const WHITELIST_RATE_LIMIT = config.whitelistRateLimit
|
const WHITELIST_RATE_LIMIT = config.whitelistRateLimit
|
||||||
// const WHITELIST_DOMAINS = config.whitelistDomains
|
const WHITELIST_DOMAINS = config.whitelistDomains
|
||||||
// const INTERNAL_RATE_LIMIT = 1
|
const INTERNAL_RATE_LIMIT = 1
|
||||||
|
|
||||||
class RateLimits {
|
class RateLimits {
|
||||||
constructor () {
|
constructor () {
|
||||||
// _this = this
|
_this = this
|
||||||
|
|
||||||
this.jwt = jwt
|
this.jwt = jwt
|
||||||
this.rateLimiter = new RateLimiterRedis(rateLimitOptions)
|
this.rateLimiter = new RateLimiterRedis(rateLimitOptions)
|
||||||
@@ -63,8 +63,8 @@ class RateLimits {
|
|||||||
// support this function.
|
// support this function.
|
||||||
async applyRateLimits (req, res, next) {
|
async applyRateLimits (req, res, next) {
|
||||||
try {
|
try {
|
||||||
// let userId
|
let userId
|
||||||
// let decoded = {}
|
const decoded = {}
|
||||||
|
|
||||||
// Create a re*Q*.locals object if not passed in.
|
// Create a re*Q*.locals object if not passed in.
|
||||||
// req.locals.proLimit will be true if the user is using Basic Authentication.
|
// req.locals.proLimit will be true if the user is using Basic Authentication.
|
||||||
@@ -83,8 +83,98 @@ class RateLimits {
|
|||||||
rateLimitTriggered: false
|
rateLimitTriggered: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
|
|
||||||
|
// Exit if the user has already authenticated with Basic Authentication.
|
||||||
|
if (req.locals.proLimit) {
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if the call is an external or internal API call.
|
||||||
|
const isInternal = this.checkInternalIp(req)
|
||||||
|
|
||||||
|
// Determine if the call originates from another computer on the intranet.
|
||||||
|
const isWhitelistOrigin = this.isInWhitelist(req)
|
||||||
|
console.log('isWhitelistOrigin: ', isWhitelistOrigin)
|
||||||
|
|
||||||
|
// Handle the use case of internally-generated requests.
|
||||||
|
if (isInternal) {
|
||||||
|
// Internal API calls should pass the authentication data in through the
|
||||||
|
// the usrObj in the body.
|
||||||
|
if (req.body && req.body.usrObj) {
|
||||||
|
//
|
||||||
|
if (req.body.usrObj.proLimit) {
|
||||||
|
// If this is an internal call that originated from a user using
|
||||||
|
// Basic Authentication, then skip rate-limits.
|
||||||
|
return next()
|
||||||
|
//
|
||||||
|
} else if (req.body.usrObj.jwtToken) {
|
||||||
|
// Internal call originated from a user using a JWT token.
|
||||||
|
console.log(
|
||||||
|
'Internal call originated from a user using a JWT token'
|
||||||
|
)
|
||||||
|
//
|
||||||
|
} else {
|
||||||
|
// Internal call originates from an anonymous user.
|
||||||
|
console.log('Internal call originates from an anonymous user')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
wlogger.error('Error in route-ratelimit2.js/applyRateLimits(): ', err)
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a boolean if the origin of the request matches a domain in the
|
||||||
|
// whitelist.
|
||||||
|
isInWhitelist (req) {
|
||||||
|
try {
|
||||||
|
const retVal = false // Default value.
|
||||||
|
|
||||||
|
// Retrieve the origin.
|
||||||
|
const origin = req.get('origin')
|
||||||
|
|
||||||
|
// console.log(`WHITELIST_DOMAINS: ${JSON.stringify(WHITELIST_DOMAINS, null, 2)}`)
|
||||||
|
|
||||||
|
for (let i = 0; i < WHITELIST_DOMAINS.length; i++) {
|
||||||
|
const thisDomain = WHITELIST_DOMAINS[i]
|
||||||
|
|
||||||
|
if (origin.toString().indexOf(thisDomain) > -1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal
|
||||||
|
} catch (err) {
|
||||||
|
wlogger.error(
|
||||||
|
'Error in route-ratelimit.js/isInWhitelist(). Returning false by default.'
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks the request object to see if it's IP address matches an internal
|
||||||
|
// IP address. That means the call is an internal API call and should be
|
||||||
|
// treated differently than an external API call.
|
||||||
|
checkInternalIp (req) {
|
||||||
|
try {
|
||||||
|
// Default value
|
||||||
|
let isInternal = false
|
||||||
|
|
||||||
|
const ip = req.ip
|
||||||
|
|
||||||
|
if (ip.includes('127.0.0.1')) isInternal = true
|
||||||
|
|
||||||
|
if (ip.includes('172.17.')) isInternal = true
|
||||||
|
|
||||||
|
return isInternal
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
'Error in checkInternalIp(). Returning false be default. Err: ',
|
||||||
|
err
|
||||||
|
)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user