added rate limiting

This commit is contained in:
Ben
2018-09-01 19:57:51 +01:00
parent c33f7bdd7b
commit 8ef777ae44
8 changed files with 82 additions and 17 deletions

View File

@@ -3,6 +3,7 @@ import {Config} from './config/config'
import {Database} from './models/database/database';
import {Server} from './server';
import {Router} from './controllers/routes/router';
import {middleware, MiddleWare} from './controllers/middleware/index';
import {User} from './models/user/user';
@@ -15,6 +16,7 @@ async function init() {
await Database.testConnection();
await Server.start();
await Router.initEndpoints();
await MiddleWare.RateLimits.init();
Logger.ready();

View File

@@ -1,8 +1,13 @@
import stringify from 'json-stringify-safe';
import {Logger} from '../models/logger';
import {Logger} from '../../models/logger';
export class MiddleWare {
static async end(req, res, next) {
await MiddleWare.RateLimits.request(req, res, next);
await MiddleWare.analytics(req, res, next);
}
static analytics(req, res, next) {
// TODO: Send data such as IP to an anyaltitics model
Logger.middleware(`${req.method} request to ${req.url}`)
@@ -13,3 +18,5 @@ export class MiddleWare {
next();
}
}
MiddleWare.RateLimits = require('./rateLimits').RateLimits;

View File

@@ -0,0 +1,53 @@
import {Logger} from '../../models/logger';
import {MiddleWare} from './index';
let requestsPerSecond = 2;
// let disposeTime = 20000; //ms 1800000 = 30 mins
let buckets = {}
export class RateLimits extends MiddleWare{
static async request(req, res, next) {
let ip = req.connection.remoteAddress;
if (!buckets[ip]) {
console.log(`New bucket`)
RateLimits.newBucket(ip);
next();
return;
}
buckets[ip].lastUsed = new Date().getTime();
if (buckets[ip].tokens.length <= 0) {
Logger.middleware(`${ip} is being rate limited`);
res.status(422).end('422 TO MANY REQUESTS');
return;
}
buckets[ip].tokens.pop();
next();
}
static newBucket(ip) {
buckets[ip] = {ip: ip, tokens: [], lastUsed: new Date().getTime()};
for (let i = 0; i < requestsPerSecond; i++) {
buckets[ip].tokens.push(1);
}
}
static tick() {
for (let bucket in buckets) {
// if (buckets[bucket].lastUsed += disposeTime >= new Date().getTime()) {
// delete buckets[bucket]; // remove element here, don't redefine
// continue;
// }
if (buckets[bucket].tokens.length > requestsPerSecond) continue;
buckets[bucket].tokens.push(1);
}
}
static init() {
Logger.info('Initialized ratelimiting middleware');
setInterval(RateLimits.tick, 1000 / requestsPerSecond);
}
}

View File

@@ -1,6 +1,6 @@
import {Logger} from '../../models/logger';
import {Server} from '../../server';
import {MiddleWare} from '../middleware';
import {MiddleWare} from '../middleware/index';
import {StatusCodes} from '../status';
import {Controllers} from '../index';
@@ -12,11 +12,11 @@ export class Router {
app = Server.App;
app.get('/', [MiddleWare.analytics, Router.frontPage]);
app.get('/', [MiddleWare.end, Router.frontPage]);
app.get('/user/:id', [MiddleWare.analytics]);
app.delete('/user/:id', [MiddleWare.analytics]);
app.post('/user', [MiddleWare.analytics, Controllers.UserController.newUser]);
app.get('/user/:id', [MiddleWare.end]);
app.delete('/user/:id', [MiddleWare.end]);
app.post('/user', [MiddleWare.end, Controllers.UserController.newUser]);
app.use([StatusCodes.pageNotFound]);
Logger.info('HTTP endpoints settup');

View File

@@ -31,12 +31,15 @@ export class UserController extends ControllerHandler {
if (errors.count() > 0) {
errors.endpoint();
next();
return;
}
let success = new User(id, username, password, email, ip, 1234).insert();
let user = new User(id, username, password, email, ip, 1234)
let success = await user.insert();
if (success == -1) {
errors.addError(500, 'Internal server error', 'An error occured with the databse').endpoint();
errors.addError(500, 'Internal server error').endpoint();
next();
return;
}
new API.user(res, id, username, email, new Date().toLocaleString(), token).endpoint();

View File

@@ -21,19 +21,18 @@ export class APIErrors extends API {
}
addError(statusCode, message, verbose) {
verbose = verbose || message;
this.errors.error.errors.push({status: statusCode, title: message, detail: verbose});
this.errors.status.code = statusCode;
this.errors.status.type = message;
this.errors.status.message = verbose;
}
count() {
return this.errors.error.errors.length;
}
count() { return this.errors.error.errors.length }
endpoint() {
this.res.setHeader('Content-type', 'application/json');
this.res.status(this.errors.status.code);
this.res.end(JSON.stringify(this.errors, false, 4));
this.res
.status(this.errors.status.code)
.end(JSON.stringify(this.errors, false, 4));
}
}

View File

@@ -27,8 +27,9 @@ export class User extends API {
}
endpoint() {
this.res.setHeader('Content-type', 'application/json');
this.res.status(200);
this.res.end(JSON.stringify(this.response, false, 4));
this.res
.setHeader('Content-type', 'application/json')
.status(200)
.end(JSON.stringify(this.response, false, 4));
}
}