summaryrefslogtreecommitdiff
path: root/src/api/limitter.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/limitter.ts')
-rw-r--r--src/api/limitter.ts69
1 files changed, 69 insertions, 0 deletions
diff --git a/src/api/limitter.ts b/src/api/limitter.ts
new file mode 100644
index 0000000000..9cc25675d8
--- /dev/null
+++ b/src/api/limitter.ts
@@ -0,0 +1,69 @@
+import * as Limiter from 'ratelimiter';
+import limiterDB from '../db/redis';
+import { IEndpoint } from './endpoints';
+import { IAuthContext } from './authenticate';
+
+export default (endpoint: IEndpoint, ctx: IAuthContext) => new Promise((ok, reject) => {
+ const limitKey = endpoint.hasOwnProperty('limitKey')
+ ? endpoint.limitKey
+ : endpoint.name;
+
+ const hasMinInterval =
+ endpoint.hasOwnProperty('minInterval');
+
+ const hasRateLimit =
+ endpoint.hasOwnProperty('limitDuration') &&
+ endpoint.hasOwnProperty('limitMax');
+
+ if (hasMinInterval) {
+ min();
+ } else if (hasRateLimit) {
+ max();
+ } else {
+ ok();
+ }
+
+ // Short-term limit
+ function min(): void {
+ const minIntervalLimiter = new Limiter({
+ id: `${ctx.user._id}:${limitKey}:min`,
+ duration: endpoint.minInterval,
+ max: 1,
+ db: limiterDB
+ });
+
+ minIntervalLimiter.get((limitErr, limit) => {
+ if (limitErr) {
+ reject('ERR');
+ } else if (limit.remaining === 0) {
+ reject('BRIEF_REQUEST_INTERVAL');
+ } else {
+ if (hasRateLimit) {
+ max();
+ } else {
+ ok();
+ }
+ }
+ });
+ }
+
+ // Long term limit
+ function max(): void {
+ const limiter = new Limiter({
+ id: `${ctx.user._id}:${limitKey}`,
+ duration: endpoint.limitDuration,
+ max: endpoint.limitMax,
+ db: limiterDB
+ });
+
+ limiter.get((limitErr, limit) => {
+ if (limitErr) {
+ reject('ERR');
+ } else if (limit.remaining === 0) {
+ reject('RATE_LIMIT_EXCEEDED');
+ } else {
+ ok();
+ }
+ });
+ }
+});