Added rate limiting logging and an error page for hitting the rate limit on opening characters
This commit is contained in:
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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`;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
20
rpg-docs/client/views/meta/error/error.html
Normal file
20
rpg-docs/client/views/meta/error/error.html
Normal 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>
|
||||
17
rpg-docs/client/views/meta/error/error.js
Normal file
17
rpg-docs/client/views/meta/error/error.js
Normal 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();
|
||||
},
|
||||
});
|
||||
8
rpg-docs/server/lib/logRateError.js
Normal file
8
rpg-docs/server/lib/logRateError.js
Normal 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}`
|
||||
);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user