starting rate limit middleware
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
'use strict';
|
||||
|
||||
const REDIS_DB = 8;
|
||||
|
||||
const ENDPOINTS_GROUPS_REDIS = {
|
||||
ENDPOINT_1: 'anonymous',
|
||||
ENDPOINT_2: 'static',
|
||||
ENDPOINT_3: 'static-named',
|
||||
ENDPOINT_5: 'dataview',
|
||||
ENDPOINT_6: 'dataview-search',
|
||||
ENDPOINT_7: 'analysis',
|
||||
ENDPOINT_8: 'tile',
|
||||
ENDPOINT_9: 'attributes',
|
||||
ENDPOINT_10: 'named-list',
|
||||
ENDPOINT_11: 'named-create',
|
||||
ENDPOINT_12: 'named-get',
|
||||
ENDPOINT_13: 'named',
|
||||
ENDPOINT_14: 'named-update',
|
||||
ENDPOINT_15: 'named-delete',
|
||||
ENDPOINT_16: 'named-options',
|
||||
ENDPOINT_17: 'named-tiles'
|
||||
};
|
||||
|
||||
/**
|
||||
* The full key is: rate-limit:store:{user}:{endpoint}
|
||||
* The value is a Redis hash:
|
||||
* maxBurst (b): Integer (as string)
|
||||
* countPerPeriod (c): Integer (as string)
|
||||
* period (p): Integer (as string)
|
||||
*/
|
||||
const ENDPOINT_KEY_REDIS = 'rate-limit:store:';
|
||||
|
||||
/**
|
||||
* The full key is: rate-limit:status:{user}:{endpoint}
|
||||
* This key is managed by redis-cell (CL.THROTTLE command)
|
||||
*/
|
||||
const USER_KEY_REDIS = 'rate-limit:status:';
|
||||
|
||||
|
||||
module.exports = function rateLimitMiddleware (metadataBackend) {
|
||||
return function rateLimit(req, res, next) {
|
||||
|
||||
const user = 'cdb';
|
||||
// const user = res.locals.user;
|
||||
const endpointGroup = getEndpointGroup();
|
||||
|
||||
const redisParams = [
|
||||
getLuaScript(),
|
||||
3,
|
||||
getStoreKey(user, endpointGroup), // KEY[1]
|
||||
getStatusKey(user, endpointGroup) // KEY[2]
|
||||
];
|
||||
|
||||
metadataBackend.redisCmd(REDIS_DB, 'EVAL', redisParams, function(err, {isBloqued, limit, remaining, retry, reset}) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
res.set({
|
||||
'X-Rate-Limit-Limit': limit,
|
||||
'X-Rate-Limit-Remaining': remaining,
|
||||
'X-Rate-Limit-Retry-After': retry,
|
||||
'X-Rate-Limit-Reset': reset
|
||||
});
|
||||
|
||||
if(isBloqued) {
|
||||
const err = new Error('You are over the limits.');
|
||||
err.http_status = 429;
|
||||
return next(err);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the endpoint key in Redis
|
||||
* @param {string} currentEndpoint
|
||||
*/
|
||||
function getEndpointGroup(currentEndpoint = null) {
|
||||
// TODO
|
||||
if (currentEndpoint) {
|
||||
return ENDPOINTS_GROUPS_REDIS.ENDPOINT_8;
|
||||
} else {
|
||||
// get endpoint from route path
|
||||
return ENDPOINTS_GROUPS_REDIS.ENDPOINT_8;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Redis key where the limits are saved by user and endpoint
|
||||
* @param {string} user
|
||||
* @param {string} endpointGroup
|
||||
*/
|
||||
function getStoreKey(user, endpointGroup) {
|
||||
return ENDPOINT_KEY_REDIS + user + ':' + endpointGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Redis key where the current state of the limit by user and endpoint
|
||||
* (so, the key where CL.THROTTLE works)
|
||||
* @param {string} user
|
||||
* @param {string} endpointGroup
|
||||
*/
|
||||
function getStatusKey(user, endpointGroup) {
|
||||
return USER_KEY_REDIS + user + ':' + endpointGroup;
|
||||
}
|
||||
|
||||
function getLuaScript(maxBurst, period) {
|
||||
return `
|
||||
local max = redis.call("HGETALL", KEYS[1])
|
||||
return redis.call("CL.THROTTLE", KEYS[2], ${maxBurst}, max, ${period})
|
||||
`;
|
||||
}
|
||||
Reference in New Issue
Block a user