Added rate limiting logging and an error page for hitting the rate limit on opening characters

This commit is contained in:
Thaum Rystra
2018-04-02 14:26:55 +02:00
parent 164ba78c81
commit 742b26b0de
6 changed files with 79 additions and 7 deletions

View File

@@ -33,7 +33,6 @@ var ifKeyValid = function(apiKey, response, callback){
response.writeHead(403, "API key is invalid");
response.end();
} else if (isRateLimited(apiKey)){
console.log(`Rate limit hit by API key ${apiKey}`);
response.writeHead(429, "Too many requests");
response.end();
} else {
@@ -53,5 +52,11 @@ var rateLimiter = new RateLimiter();
rateLimiter.addRule({apiKey: String}, 2, 10000);
var isRateLimited = function(apiKey){
return !rateLimiter.check({apiKey}).allowed;
const limited = !rateLimiter.check({apiKey}).allowed
if (limited) {
console.log(`Rate limit hit by API key ${apiKey}`);
return true;
} else {
return false;
}
};

View File

@@ -13,6 +13,11 @@ Router.plugin("ensureSignedIn", {
Router.plugin("dataNotFound", {notFoundTemplate: "notFound"});
var handleSubError = function(e){
Session.set("error", {reason: e.reason, href: location.href});
Router.go("/error");
};
Router.map(function() {
this.route("/", {
name: "home",
@@ -36,7 +41,9 @@ Router.map(function() {
path: "/character/:_id/",
waitOn: function(){
return [
subsManager.subscribe("singleCharacter", this.params._id),
subsManager.subscribe(
"singleCharacter", this.params._id, {onError: handleSubError}
),
];
},
action: function(){
@@ -52,7 +59,9 @@ Router.map(function() {
path: "/character/:_id/:urlName",
waitOn: function(){
return [
subsManager.subscribe("singleCharacter", this.params._id),
subsManager.subscribe(
"singleCharacter", this.params._id, {onError: handleSubError}
),
];
},
data: function() {
@@ -82,7 +91,9 @@ Router.map(function() {
path: "/character/:_id/:urlName/print",
waitOn: function(){
return [
subsManager.subscribe("singleCharacter", this.params._id),
subsManager.subscribe(
"singleCharacter", this.params._id, {onError: handleSubError}
),
];
},
data: function() {
@@ -153,4 +164,11 @@ Router.map(function() {
document.title = appName;
},
});
this.route("/error", {
name: "error",
onAfterAction: function() {
document.title = `${appName} - Error`;
},
});
});

View File

@@ -0,0 +1,20 @@
<template name="error">
<app-header-layout has-scrolling-region fullbleed>
<app-header class="app-grey white-text" fixed>
<app-toolbar>
<paper-icon-button icon="menu" drawer-toggle></paper-icon-button>
</app-toolbar>
</app-header>
<div class="fit layout vertical center center-justified">
<div class="paper-font-subhead"
style="margin-left: 16px;
margin-right: 16px;
text-align: center;">
{{#if errorMessage}}
<div>{{errorMessage}}</div>
<paper-button class="try-again">Try Again</paper-button>
{{/if}}
</div>
</div>
</app-header-layout>
</template>

View File

@@ -0,0 +1,17 @@
Template.error.onRendered(function(){
const error = Session.get("error") || {};
if (error.href) window.history.replaceState("", "", error.href);
});
Template.error.helpers({
errorMessage: function(){
const error = Session.get("error") || {};
return error.reason;
},
});
Template.error.events({
"click .try-again": function(event, instance){
window.location.reload();
},
});

View File

@@ -0,0 +1,8 @@
logRateError = function(reply, ruleInput){
// reply = {allowed, timeToReset, numInvocationsLeft}
// ruleInput = {userId, clientAddress, type, name, connectionId}
console.log(
`Limit hit for ${ruleInput.type} "${ruleInput.name}" ` +
`by user ${ruleInput.userId} from ${ruleInput.clientAddress}`
);
}

View File

@@ -38,9 +38,13 @@ Meteor.publish("singleCharacter", function(characterId){
DDPRateLimiter.addRule({
name: "singleCharacter",
type: "subscription",
userId(){ return true; },
userId: null,
connectionId(){ return true; },
}, 8, 5000);
}, 8, 10000, function(reply, ruleInput){
if(!reply.allowed){
logRateError(reply, ruleInput);
}
});
Meteor.publish("singleCharacterName", function(characterId){
userId = this.userId;