Merge branch 'feature-patreon-accounts'

This commit is contained in:
Stefan Zermatten
2019-03-07 13:47:32 +02:00
9 changed files with 682 additions and 226 deletions

View File

@@ -74,6 +74,41 @@ Schemas.User = new SimpleSchema({
type: String, type: String,
optional: true, optional: true,
}, },
patreon: {
type: Object,
optional: true,
},
"patreon.accessToken": {
type: String,
optional: true,
},
"patreon.refreshToken": {
type: String,
optional: true,
},
"patreon.tokenExpiryDate": {
type: Date,
optional: true,
},
"patreon.userId": {
type: String,
optional: true,
index: 1,
},
"patreon.entitledCents": {
type: Number,
decimal: false,
optional: true,
},
"patreon.entitledCentsOverride": {
type: Number,
decimal: false,
optional: true,
},
"patreon.error": {
type: String,
optional: true,
},
}); });
Meteor.users.attachSchema(Schemas.User); Meteor.users.attachSchema(Schemas.User);

View File

@@ -21,6 +21,9 @@
<a href="/account" style="text-decoration: underline; cursor: pointer; font-size: 16px;"> <a href="/account" style="text-decoration: underline; cursor: pointer; font-size: 16px;">
{{profileLink}} {{profileLink}}
</a> </a>
<a href="/account" style="text-decoration: underline; cursor: pointer; font-size: 16px; margin-left: 8px;">
{{patreonTier}} tier
</a>
{{else}} {{else}}
<a href="/sign-in" style="text-decoration: underline; cursor: pointer; font-size: 16px;"> <a href="/sign-in" style="text-decoration: underline; cursor: pointer; font-size: 16px;">
Sign in Sign in
@@ -41,6 +44,14 @@
Characters Characters
</paper-icon-item> </paper-icon-item>
</a> </a>
{{#if isTier5}}
<a href="/library" tabindex="-1">
<paper-icon-item id="libary">
<iron-icon icon="book" item-icon></iron-icon>
Library (beta)
</paper-icon-item>
</a>
{{/if}}
<a href="/guide" tabindex="-1"> <a href="/guide" tabindex="-1">
<paper-icon-item id="guide"> <paper-icon-item id="guide">
<iron-icon icon="social:school" item-icon></iron-icon> <iron-icon icon="social:school" item-icon></iron-icon>

View File

@@ -17,6 +17,27 @@ Template.appDrawer.helpers({
let post = PatreonPosts.findOne({}, {sort: {date: -1}}); let post = PatreonPosts.findOne({}, {sort: {date: -1}});
return (post && post.link) || 'https://www.patreon.com/dicecloud'; return (post && post.link) || 'https://www.patreon.com/dicecloud';
}, },
isTier5: function(){
let user = Meteor.user();
if (!user) return false;
patreon = user.patreon;
if (!patreon) return false;
return patreon.entitledCents >= 500 || patreon.entitledCentsOverride >= 500;
},
patreonTier: function(){
let user = Meteor.user();
if (!user) return;
patreon = user.patreon;
if (!patreon) return "free";
let entitledCents = patreon.entitledCents || 0;
if (patreon.entitledCentsOverride > entitledCents){
return "$" + (patreon.entitledCentsOverride / 100).toFixed(0);
} else if (!patreon.entitledCents){
return "free";
} else {
return "$" + (patreon.entitledCents / 100).toFixed(0);
}
},
}); });
let drawerLayout; let drawerLayout;

View File

@@ -62,6 +62,28 @@
{{/if}} {{/if}}
</td> </td>
</tr> </tr>
<tr>
<td>
Patreon
</td>
{{#if patreon.accessToken}}
<td>
{{tier}} tier
</td>
<td>
<paper-icon-button icon="refresh" class="refreshPatreon">
</paper-icon-button>
</td>
{{else}}
<td>
<a href="{{patreonLoginUrl}}">
<paper-button raised class="connectPatreon">
Connect Patreon account
</paper-button>
</a>
</td>
{{/if}}
</tr>
</table> </table>
<div style="max-width: 250px"> <div style="max-width: 250px">
{{> atForm state="signIn"}} {{> atForm state="signIn"}}

View File

@@ -1,5 +1,10 @@
import { format as formatUrl } from 'url';
const CLIENT_ID = Meteor.settings.public.patreon.clientId;
Template.profile.onCreated(function(){ Template.profile.onCreated(function(){
this.showApiKey = new ReactiveVar(false); this.showApiKey = new ReactiveVar(false);
this.loadingPatreon = new ReactiveVar(false);
}); });
Template.profile.helpers({ Template.profile.helpers({
@@ -12,6 +17,40 @@ Template.profile.helpers({
showApiKey: function(){ showApiKey: function(){
return Template.instance().showApiKey.get(); return Template.instance().showApiKey.get();
}, },
patreonLoginUrl: function(){
return formatUrl({
protocol: 'https',
host: 'patreon.com',
pathname: '/oauth2/authorize',
query: {
response_type: 'code',
client_id: CLIENT_ID,
redirect_uri: Meteor.absoluteUrl() + 'patreon-redirect',
state: Meteor.userId(),
scope: 'identity',
},
});
},
patreon: function(){
let user = Meteor.user();
return user && user.patreon || {};
},
tier: function(){
let user = Meteor.user();
if (!user) return;
patreon = user.patreon;
if (!patreon) return;
let entitledCents = patreon.entitledCents || 0;
if (Template.instance().loadingPatreon.get()){
return "loading..."
} else if (patreon.entitledCentsOverride > entitledCents){
return `$ ${(patreon.entitledCentsOverride / 100).toFixed(0)} (overridden)`;
} else if (patreon.entitledCents === undefined){
return "?";
} else {
return "$" + (patreon.entitledCents / 100).toFixed(0);
}
},
}); });
Template.profile.events({ Template.profile.events({
@@ -39,4 +78,10 @@ Template.profile.events({
Meteor.call("generateMyApiKey"); Meteor.call("generateMyApiKey");
instance.showApiKey.set(true); instance.showApiKey.set(true);
}, },
"click .refreshPatreon": function(event, instance){
instance.loadingPatreon.set(true);
Meteor.call("updateMyPatreonDetails", (error) => {
instance.loadingPatreon.set(false);
});
},
}); });

515
app/package-lock.json generated
View File

@@ -26,21 +26,14 @@
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
}, },
"ajv": { "ajv": {
"version": "5.5.2", "version": "6.10.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
"requires": { "requires": {
"co": "^4.6.0", "fast-deep-equal": "^2.0.1",
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0", "fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0" "json-schema-traverse": "^0.4.1",
}, "uri-js": "^4.2.2"
"dependencies": {
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
}
} }
}, },
"ansi-regex": { "ansi-regex": {
@@ -53,16 +46,34 @@
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
}, },
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
"requires": {
"safer-buffer": "~2.1.0"
}
},
"assert-plus": { "assert-plus": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
}, },
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"aws-sign2": { "aws-sign2": {
"version": "0.7.0", "version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
}, },
"aws4": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
},
"bcrypt": { "bcrypt": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-1.0.3.tgz", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-1.0.3.tgz",
@@ -72,6 +83,14 @@
"node-pre-gyp": "0.6.36" "node-pre-gyp": "0.6.36"
} }
}, },
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"requires": {
"tweetnacl": "^0.14.3"
}
},
"block-stream": { "block-stream": {
"version": "0.0.9", "version": "0.0.9",
"resolved": false, "resolved": false,
@@ -108,6 +127,11 @@
"window-or-global": "^1.0.1" "window-or-global": "^1.0.1"
} }
}, },
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"cliui": { "cliui": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@@ -135,6 +159,14 @@
"resolved": false, "resolved": false,
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
}, },
"combined-stream": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
"integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"core-js": { "core-js": {
"version": "2.5.7", "version": "2.5.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
@@ -155,6 +187,14 @@
"which": "^1.2.9" "which": "^1.2.9"
} }
}, },
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"debug": { "debug": {
"version": "2.6.9", "version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -168,11 +208,33 @@
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
}, },
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"dijkstrajs": { "dijkstrajs": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz", "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.1.tgz",
"integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs=" "integrity": "sha1-082BIh4+pAdCz83lVtTpnpjdxxs="
}, },
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"encoding": {
"version": "0.1.12",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
"integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
"requires": {
"iconv-lite": "~0.4.13"
}
},
"error-ex": { "error-ex": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -195,15 +257,20 @@
"strip-eof": "^1.0.0" "strip-eof": "^1.0.0"
} }
}, },
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"extsprintf": { "extsprintf": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
}, },
"fast-deep-equal": { "fast-deep-equal": {
"version": "1.1.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
}, },
"fast-json-stable-stringify": { "fast-json-stable-stringify": {
"version": "2.0.0", "version": "2.0.0",
@@ -228,36 +295,26 @@
"locate-path": "^2.0.0" "locate-path": "^2.0.0"
} }
}, },
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"form-data": { "form-data": {
"version": "2.3.2", "version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"requires": { "requires": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "1.0.6", "combined-stream": "^1.0.6",
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
},
"dependencies": {
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"combined-stream": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
}
} }
}, },
"form-urlencoded": {
"version": "2.0.9",
"resolved": "https://registry.npmjs.org/form-urlencoded/-/form-urlencoded-2.0.9.tgz",
"integrity": "sha512-fWUzNiOnYa126vFAT6TFXd1mhJrvD8IqmQ9ilZPjkLYQfaRreBr5fIUoOpPlWtqaAG64nzoE7u5zSetifab9IA=="
},
"get-caller-file": { "get-caller-file": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
@@ -268,6 +325,14 @@
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
}, },
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"graceful-fs": { "graceful-fs": {
"version": "4.1.11", "version": "4.1.11",
"resolved": false, "resolved": false,
@@ -279,11 +344,11 @@
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
}, },
"har-validator": { "har-validator": {
"version": "5.0.3", "version": "5.1.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
"requires": { "requires": {
"ajv": "^5.1.0", "ajv": "^6.5.5",
"har-schema": "^2.0.0" "har-schema": "^2.0.0"
} }
}, },
@@ -300,74 +365,14 @@
"assert-plus": "^1.0.0", "assert-plus": "^1.0.0",
"jsprim": "^1.2.2", "jsprim": "^1.2.2",
"sshpk": "^1.7.0" "sshpk": "^1.7.0"
}, }
"dependencies": { },
"asn1": { "iconv-lite": {
"version": "0.2.3", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
}, "requires": {
"bcrypt-pbkdf": { "safer-buffer": ">= 2.1.2 < 3"
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
"integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
"optional": true,
"requires": {
"tweetnacl": "^0.14.3"
}
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"ecc-jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
"optional": true,
"requires": {
"jsbn": "~0.1.0"
}
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"optional": true
},
"sshpk": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz",
"integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=",
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"tweetnacl": "~0.14.0"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"optional": true
}
} }
}, },
"inherits": { "inherits": {
@@ -401,11 +406,24 @@
"number-is-nan": "^1.0.0" "number-is-nan": "^1.0.0"
} }
}, },
"is-plain-object": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
"requires": {
"isobject": "^3.0.1"
}
},
"is-stream": { "is-stream": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
}, },
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"isarray": { "isarray": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.4.tgz",
@@ -416,10 +434,49 @@
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
}, },
"isobject": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8="
},
"isomorphic-fetch": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
"integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
"requires": {
"node-fetch": "^1.0.1",
"whatwg-fetch": ">=0.10.0"
}
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
},
"json-schema-traverse": { "json-schema-traverse": {
"version": "0.3.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"jsonapi-datastore": {
"version": "0.4.0-beta",
"resolved": "https://registry.npmjs.org/jsonapi-datastore/-/jsonapi-datastore-0.4.0-beta.tgz",
"integrity": "sha1-tJn86STUXivDxheGgVIAY+I2HxA="
}, },
"jsprim": { "jsprim": {
"version": "1.4.1", "version": "1.4.1",
@@ -430,13 +487,6 @@
"extsprintf": "1.3.0", "extsprintf": "1.3.0",
"json-schema": "0.2.3", "json-schema": "0.2.3",
"verror": "1.10.0" "verror": "1.10.0"
},
"dependencies": {
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
}
} }
}, },
"lcid": { "lcid": {
@@ -1228,16 +1278,16 @@
} }
}, },
"mime-db": { "mime-db": {
"version": "1.33.0", "version": "1.38.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz",
"integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg=="
}, },
"mime-types": { "mime-types": {
"version": "2.1.18", "version": "2.1.22",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz",
"integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==",
"requires": { "requires": {
"mime-db": "~1.33.0" "mime-db": "~1.38.0"
} }
}, },
"mimic-fn": { "mimic-fn": {
@@ -1255,6 +1305,15 @@
"resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz",
"integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=" "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U="
}, },
"node-fetch": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
"integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
"requires": {
"encoding": "^0.1.11",
"is-stream": "^1.0.1"
}
},
"node-pre-gyp": { "node-pre-gyp": {
"version": "0.6.36", "version": "0.6.36",
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz", "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz",
@@ -1511,6 +1570,11 @@
"resolved": false, "resolved": false,
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
}, },
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
},
"os-homedir": { "os-homedir": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
@@ -1592,6 +1656,17 @@
"pify": "^2.0.0" "pify": "^2.0.0"
} }
}, },
"patreon": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/patreon/-/patreon-0.4.1.tgz",
"integrity": "sha512-aLhjx4rg2BArTq0Kg61MrM4dkJnTQ9kPN8F6a2IlQoYVEtIH7kUK/dprClTx+QYQKlXMfKksN9NCux1YarQJsQ==",
"requires": {
"form-urlencoded": "^2.0.4",
"is-plain-object": "^2.0.4",
"isomorphic-fetch": "^2.2.1",
"jsonapi-datastore": "^0.4.0-beta"
}
},
"performance-now": { "performance-now": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -1612,6 +1687,16 @@
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
}, },
"psl": {
"version": "1.1.31",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
"integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw=="
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
"qrcode": { "qrcode": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.0.tgz", "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.3.0.tgz",
@@ -1687,103 +1772,30 @@
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==" "integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
}, },
"request": { "request": {
"version": "2.87.0", "version": "2.88.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
"integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
"requires": { "requires": {
"aws-sign2": "~0.7.0", "aws-sign2": "~0.7.0",
"aws4": "^1.6.0", "aws4": "^1.8.0",
"caseless": "~0.12.0", "caseless": "~0.12.0",
"combined-stream": "~1.0.5", "combined-stream": "~1.0.6",
"extend": "~3.0.1", "extend": "~3.0.2",
"forever-agent": "~0.6.1", "forever-agent": "~0.6.1",
"form-data": "~2.3.1", "form-data": "~2.3.2",
"har-validator": "~5.0.3", "har-validator": "~5.1.0",
"http-signature": "~1.2.0", "http-signature": "~1.2.0",
"is-typedarray": "~1.0.0", "is-typedarray": "~1.0.0",
"isstream": "~0.1.2", "isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1", "json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.17", "mime-types": "~2.1.19",
"oauth-sign": "~0.8.2", "oauth-sign": "~0.9.0",
"performance-now": "^2.1.0", "performance-now": "^2.1.0",
"qs": "~6.5.1", "qs": "~6.5.2",
"safe-buffer": "^5.1.1", "safe-buffer": "^5.1.2",
"tough-cookie": "~2.3.3", "tough-cookie": "~2.4.3",
"tunnel-agent": "^0.6.0", "tunnel-agent": "^0.6.0",
"uuid": "^3.1.0" "uuid": "^3.3.2"
},
"dependencies": {
"aws4": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz",
"integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w=="
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"combined-stream": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz",
"integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"extend": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"oauth-sign": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"uuid": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz",
"integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA=="
}
} }
}, },
"require-directory": { "require-directory": {
@@ -1883,6 +1895,16 @@
} }
} }
}, },
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"semver": { "semver": {
"version": "5.4.1", "version": "5.4.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
@@ -1953,6 +1975,22 @@
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz",
"integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w==" "integrity": "sha512-TfOfPcYGBB5sDuPn3deByxPhmfegAhpDYKSOXZQN81Oyrrif8ZCodOLzK3AesELnCx03kikhyDwh0pfvvQvF8w=="
}, },
"sshpk": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
}
},
"string-width": { "string-width": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
@@ -2164,10 +2202,11 @@
} }
}, },
"tough-cookie": { "tough-cookie": {
"version": "2.3.4", "version": "2.4.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
"integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
"requires": { "requires": {
"psl": "^1.1.24",
"punycode": "^1.4.1" "punycode": "^1.4.1"
}, },
"dependencies": { "dependencies": {
@@ -2178,11 +2217,37 @@
} }
} }
}, },
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
},
"underscore": { "underscore": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz",
"integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg=="
}, },
"uri-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
"requires": {
"punycode": "^2.1.0"
}
},
"uuid": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
},
"validate-npm-package-license": { "validate-npm-package-license": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@@ -2200,13 +2265,6 @@
"assert-plus": "^1.0.0", "assert-plus": "^1.0.0",
"core-util-is": "1.0.2", "core-util-is": "1.0.2",
"extsprintf": "^1.2.0" "extsprintf": "^1.2.0"
},
"dependencies": {
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
}
} }
}, },
"webcomponents.js": { "webcomponents.js": {
@@ -2214,6 +2272,11 @@
"resolved": "https://registry.npmjs.org/webcomponents.js/-/webcomponents.js-0.7.24.tgz", "resolved": "https://registry.npmjs.org/webcomponents.js/-/webcomponents.js-0.7.24.tgz",
"integrity": "sha1-IRb7+hRo7EFqe+/aozPh0Rj2nAQ=" "integrity": "sha1-IRb7+hRo7EFqe+/aozPh0Rj2nAQ="
}, },
"whatwg-fetch": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
"integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
},
"which": { "which": {
"version": "1.3.1", "version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",

View File

@@ -19,7 +19,9 @@
"fibers": "^2.0.2", "fibers": "^2.0.2",
"file-saver": "^2.0.1", "file-saver": "^2.0.1",
"meteor-node-stubs": "^0.3.3", "meteor-node-stubs": "^0.3.3",
"patreon": "^0.4.1",
"qrcode": "^1.3.0", "qrcode": "^1.3.0",
"request": "^2.88.0",
"source-map-support": "^0.5.9", "source-map-support": "^0.5.9",
"underscore": "^1.9.1" "underscore": "^1.9.1"
} }

View File

@@ -0,0 +1,256 @@
import request from 'request';
const CLIENT_ID = Meteor.settings.public.patreon.clientId;
const CLIENT_SECRET = Meteor.settings.patreon.clientSecret;
const CREATOR_ACCESS_TOKEN = Meteor.settings.patreon.creatorAccessToken;
const CAMPAIGN_ID = Meteor.settings.public.patreon.campaignId;
// Handle redirects from patreon
Router.map(function () {
this.route("patreon-redirect", {
path: "/patreon-redirect",
where: "server",
action: function () {
let route = this;
let userId = route.params.query.state;
let singleUseCode = route.params.query.code;
requestToken(singleUseCode, Meteor.bindEnvironment((error, response, body) => {
// Should return an access token, valid for 1 month, which needs to be
// stored and used to make requests on behalf of the user
if (error){
writePatreonError(userId, error);
return;
}
let token;
try {
token = JSON.parse(body);
writePatreonToken(userId, token);
} catch(error) {
writePatreonError(userId, error);
return;
}
updateIdentity(token.access_token, userId);
}));
route.response.writeHead(302, {
'Location': Meteor.absoluteUrl() + "account",
});
route.response.end();
},
});
});
const requestToken = function(singleUseCode, callback){
request({
method: "POST",
uri: "https://www.patreon.com/api/oauth2/token",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
qs: {
code: singleUseCode,
grant_type: "authorization_code",
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
redirect_uri: Meteor.absoluteUrl() + 'patreon-redirect',
},
}, callback);
}
const getIdentity = function(accessToken, callback){
request({
uri: "https://www.patreon.com/api/oauth2/v2/identity",
headers:{
Authorization: "Bearer " + accessToken,
},
qs: {
"include": "memberships",
"fields[member]": "currently_entitled_amount_cents",
}
}, callback);
};
// Should return a new access token for the user
// callback is called with (error, response, body)
const refreshAccessToken = Meteor.wrapAsync(function(refreshToken, userId, callback){
request({
method: "POST",
uri: "https://www.patreon.com/api/oauth2/token",
qs: {
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
}
}, Meteor.bindEnvironment((error, response, body) => {
// Should return an access token, valid for 1 month, which needs to be
// stored and used to make requests on behalf of the user
if (error){
callback(error)
return;
}
let token;
try {
token = JSON.parse(body);
writePatreonToken(userId, token);
callback(undefined, token.access_token);
} catch(error) {
callback(error);
}
}));
});
const updateIdentity = Meteor.wrapAsync(function(accessToken, userId, callback){
getIdentity(accessToken, Meteor.bindEnvironment((error, response, body) => {
if (error){
writePatreonError(userId, error);
return;
}
try {
let identity = JSON.parse(body);
let membership = identity.included[0];
let entitledAmount = membership && membership.attributes
.currently_entitled_amount_cents || 0;
let patreonUserId = identity.data.id;
writeEntitledCentsAndId(userId, entitledAmount, patreonUserId);
if (callback) callback();
} catch(error) {
writePatreonError(userId, error);
if(callback) callback(error);
}
}));
});
Meteor.methods({
updateMyPatreonDetails(){
const userId = this.userId;
if (!userId) throw new Meteor.Error("not-logged-in", "You must be logged in to update Patreon details");
const user = Meteor.users.findOne(userId, {fields: {patreon: 1}});
Meteor.users.update(userId, {$unset: {"patreon.entitledCents": 1}});
if (!user.patreon || !user.patreon.accessToken){
throw new Meteor.Error("no-patreon-access", "Patreon access token not found for this user");
}
let accessToken = user.patreon.accessToken;
if (user.patreon.tokenExpiryDate < new Date()){
// Token expired, refresh it before continuing
accessToken = refreshAccessToken(user.patreon.refreshToken, userId);
}
updateIdentity(accessToken, userId);
},
});
const writePatreonToken = function(userId, {
access_token, refresh_token, expires_in
}){
// The expiry date is now plus `expires_in` seconds
let expiryDate = new Date();
expiryDate.setSeconds(expiryDate.getSeconds() + expires_in);
// Expire a day early so we don't accidentally miss it
expiryDate.setDate(expiryDate.getDate() - 1);
// Write
Meteor.users.update(userId, {
$set: {
"patreon.accessToken": access_token,
"patreon.refreshToken": refresh_token,
"patreon.tokenExpiryDate": expiryDate,
},
$unset: {
"patreon.error": 1,
},
});
};
const writeEntitledCentsAndId = function(userId, amount, patreonUserId){
Meteor.users.update(userId, {
$set: {
"patreon.entitledCents": amount,
"patreon.userId": patreonUserId,
},
$unset: {
"patreon.error": 1,
},
});
};
const writePatreonError = function(userId, error){
console.error({patreonError: error});
Meteor.users.update(userId, {
$set: {
"patreon.error": error.toString(),
},
});
}
const requestMembers = Meteor.wrapAsync(function(cursor, members, callback){
request({
uri: `https://www.patreon.com/api/oauth2/v2/campaigns/${CAMPAIGN_ID}/members`,
headers:{
Authorization: "Bearer " + CREATOR_ACCESS_TOKEN,
},
qs: {
"include": "user",
"fields[member]": "currently_entitled_amount_cents",
"page[cursor]": cursor,
}
}, (error, reponse, body) => {
if (error){
callback(error);
return;
}
let json = JSON.parse(body);
if (json.errors) {
callback(json.errors);
return;
}
let newMembers = json.data.map(member => ({
id: member.relationships.user.data.id,
entitledCents: member.attributes.currently_entitled_amount_cents,
}));
members.push(...newMembers);
let next = json.meta.pagination.cursors && json.meta.pagination.cursors.next;
if (next){
callback(undefined, next);
} else {
callback(undefined);
}
});
});
const updatePatreonMembersEntitledCents = function(){
let next = "";
let members = [];
do {
next = requestMembers(next, members);
} while (next)
members.forEach(({id, entitledCents}) => {
Meteor.users.update({
"patreon.userId": id
}, {$set: {
"patreon.entitledCents":entitledCents,
}});
});
return members;
}
// Method to run a manual update
Meteor.methods({
updatePatreonMembersEntitledCents(){
const user = Meteor.users.findOne(this.userId);
if (!user || !_.contains(user.roles, "admin")) throw new Meteor.Error(
"permission-error", "You need to be logged in as an admin to run this method"
);
return updatePatreonMembersEntitledCents();
},
});
// Cron job to run the update automatically
Meteor.startup(() => {
SyncedCron.add({
name: "updatePatreonMembersEntitledCents",
schedule: function(parser) {
return parser.text('every 4 hours');
},
job: updatePatreonMembersEntitledCents,
});
})

View File

@@ -7,6 +7,7 @@ Meteor.publish("user", function(){
apiKey: 1, apiKey: 1,
librarySubscriptions: 1, librarySubscriptions: 1,
lastPatreonPostClicked: 1, lastPatreonPostClicked: 1,
patreon: 1,
}}), }}),
PatreonPosts.find({},{sort: {dateAdded: -1}, limit: 1}) PatreonPosts.find({},{sort: {dateAdded: -1}, limit: 1})
]; ];