diff --git a/README.md b/README.md
index e99c9c06..f70527ca 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,6 @@ Getting started
`git clone https://github.com/ThaumRystra/DiceCloud1 dicecloud`
`cd dicecloud`
-`cd rpg-docs`
+`cd app`
`bower install`
`meteor`
diff --git a/app/Routes/API.js b/app/Routes/API.js
index 6ad6d71b..7df32907 100644
--- a/app/Routes/API.js
+++ b/app/Routes/API.js
@@ -6,7 +6,7 @@ Router.map(function() {
this.response.setHeader("Content-Type", "application/json");
var query = this.params.query;
var key = query && query.key;
- ifKeyValid(key, this.response, () =>
+ ifKeyValid(key, this.response, "vmixCharacter", () =>
this.response.end(vMixCharacter(this.params._id))
);
},
@@ -18,25 +18,46 @@ Router.map(function() {
this.response.setHeader("Content-Type", "application/json");
var query = this.params.query;
var key = query && query.key;
- ifKeyValid(key, this.response, () =>
+ ifKeyValid(key, this.response, "vmixParty", () =>
this.response.end(vMixParty(this.params._id))
);
},
});
+
+ this.route("jsonCharacterSheet", {
+ path: "/character/:_id/json",
+ where: "server",
+ action: function() {
+ this.response.setHeader("Content-Type", "application/json");
+ var query = this.params.query;
+ var key = query && query.key;
+ ifKeyValid(key, this.response, "jsonCharacterSheet", () => {
+ if (canViewCharacter(this.params._id, userIdFromKey(key))){
+ this.response.end(JSONExport(this.params._id))
+ } else {
+ this.response.writeHead(403, "You do not have permission to view this character");
+ this.response.end();
+ }
+ }
+ );
+ },
+ });
});
-var ifKeyValid = function(apiKey, response, callback){
+var ifKeyValid = function(apiKey, response, method, callback){
if (!apiKey){
response.writeHead(403, "You must use an api key to access this api");
response.end();
} else if (!isKeyValid(apiKey)){
response.writeHead(403, "API key is invalid");
response.end();
- } else if (isRateLimited(apiKey)){
+ } else if (isRateLimited(apiKey, method)){
response.writeHead(429, "Too many requests");
- response.end();
+ response.end(JSON.stringify({
+ "timeToReset": rateLimiter.check({apiKey: apiKey, method: method}).timeToReset
+ }));
} else {
- rateLimiter.increment({apiKey})
+ rateLimiter.increment({apiKey: apiKey, method: method})
callback();
}
};
@@ -48,11 +69,19 @@ var isKeyValid = function(apiKey){
return !blackListed;
};
-var rateLimiter = new RateLimiter();
-rateLimiter.addRule({apiKey: String}, 2, 10000);
+var userIdFromKey = function(apiKey){
+ var user = Meteor.users.findOne({apiKey}); // we know user exists from isKeyValid
+ return user._id;
+}
-var isRateLimited = function(apiKey){
- const limited = !rateLimiter.check({apiKey}).allowed
+var rateLimiter = new RateLimiter();
+rateLimiter.addRule({apiKey: String}, 5, 5000);
+rateLimiter.addRule({apiKey: String, method: "vmixCharacter"}, 2, 10000);
+rateLimiter.addRule({apiKey: String, method: "vmixParty"}, 2, 10000);
+rateLimiter.addRule({apiKey: String, method: "jsonCharacterSheet"}, 5, 5000);
+
+var isRateLimited = function(apiKey, method){
+ const limited = !rateLimiter.check({apiKey: apiKey, method: method}).allowed
if (limited) {
console.log(`Rate limit hit by API key ${apiKey}`);
return true;
diff --git a/app/lib/functions/characterJSON.js b/app/lib/functions/characterJSON.js
new file mode 100644
index 00000000..861bb796
--- /dev/null
+++ b/app/lib/functions/characterJSON.js
@@ -0,0 +1,17 @@
+JSONExport = function(charId) {
+ var character = {
+ "attacks": Attacks.find({charId: charId}).fetch(),
+ "characters": Characters.find({_id: charId}).fetch(),
+ "classes": Classes.find({charId: charId}).fetch(),
+ "containers": Containers.find({charId: charId}).fetch(),
+ "effects": Effects.find({charId: charId}).fetch(),
+ "experience": Experiences.find({charId: charId}).fetch(),
+ "features": Features.find({charId: charId}).fetch(),
+ "items": Items.find({charId: charId}).fetch(),
+ "notes": Notes.find({charId: charId}).fetch(),
+ "proficiencies": Proficiencies.find({charId: charId}).fetch(),
+ "spellLists": SpellLists.find({charId: charId}).fetch(),
+ "spells": Spells.find({charId: charId}).fetch()
+ };
+ return JSON.stringify(character);
+}
diff --git a/app/lib/functions/permissions.js b/app/lib/functions/permissions.js
index 9e658d03..8f160297 100644
--- a/app/lib/functions/permissions.js
+++ b/app/lib/functions/permissions.js
@@ -9,10 +9,11 @@ canViewCharacter = function(charId, userId){
userId = userId || Meteor.userId();
var char = Characters.findOne(
charId,
- {fields: {owner: 1, writers: 1, readers: 1}}
+ {fields: {owner: 1, writers: 1, readers: 1, "settings.viewPermission": 1}}
);
if (!char) return true;
return userId === char.owner ||
+ char.settings.viewPermission === "public" ||
_.contains(char.writers, userId) ||
_.contains(char.readers, userId);
};
diff --git a/app/public/browserconfig.xml b/app/public/browserconfig.xml
index 7e4b2b57..e3ad7fcc 100644
--- a/app/public/browserconfig.xml
+++ b/app/public/browserconfig.xml
@@ -1,12 +1,12 @@
-
-
-
-
-
-
-
-
- #b91d1d
-
-
-
+
+
+
+
+
+
+
+
+ #b91d1d
+
+
+