node is set to true
Line | Hits | Source |
---|---|---|
1 | ||
2 | // GetStream client library for node and the browser | |
3 | // Author: Thierry Schellenbach | |
4 | // BSD License | |
5 | ||
6 | 1 | var StreamClient = require('./lib/client'); |
7 | 1 | var errors = require('./lib/errors'); |
8 | 1 | var request = require('request'); |
9 | ||
10 | 1 | function connect(apiKey, apiSecret, siteId) { |
11 | 23 | return new StreamClient(apiKey, apiSecret, siteId); |
12 | } | |
13 | ||
14 | 1 | module.exports.connect = connect; |
15 | 1 | module.exports.errors = errors; |
16 | 1 | module.exports.request = request; |
17 |
Line | Hits | Source |
---|---|---|
1 | 1 | var request = require('request'); |
2 | 1 | var StreamFeed = require('./feed'); |
3 | 1 | var signing = require('./signing'); |
4 | 1 | var errors = require('./errors'); |
5 | 1 | var crypto = require('crypto'); |
6 | ||
7 | 1 | var StreamClient = function () { |
8 | 23 | this.initialize.apply(this, arguments); |
9 | }; | |
10 | ||
11 | 1 | StreamClient.prototype = { |
12 | baseUrl: 'https://getstream.io', | |
13 | ||
14 | initialize: function (key, secret, siteId, fayeUrl) { | |
15 | /* | |
16 | * API key and secret | |
17 | * Secret is optional | |
18 | */ | |
19 | 23 | this.key = key; |
20 | 23 | this.secret = secret; |
21 | 23 | this.siteId = siteId; |
22 | 23 | this.fayeUrl = fayeUrl ? fayeUrl : 'https://getstream.io/faye'; |
23 | }, | |
24 | ||
25 | feed: function(feedId, token, siteId) { | |
26 | /* | |
27 | * Returns a feed object for the given feed id and token | |
28 | * Example: | |
29 | * | |
30 | * client.feed('user1', 'token2'); | |
31 | */ | |
32 | 46 | var match = feedId.match(/\:/g); |
33 | 46 | if (match === null || match.length != 1) { |
34 | 1 | throw new errors.FeedError('Wrong feed format ' + feedId + ' correct format is flat:1'); |
35 | } | |
36 | ||
37 | 45 | if (crypto.createHash && this.secret && !token) { |
38 | // we are server side, have a secret but no feed signature | |
39 | 44 | token = signing.sign(this.secret, feedId.replace(':', '')); |
40 | } | |
41 | ||
42 | 45 | if (!token) { |
43 | 0 | throw new errors.FeedError('Missing token, in client side mode please provide a feed secret'); |
44 | } | |
45 | ||
46 | 45 | var feed = new StreamFeed(this, feedId, token, siteId); |
47 | 45 | return feed; |
48 | }, | |
49 | ||
50 | enrichUrl: function(relativeUrl) { | |
51 | 20 | var url = this.baseUrl + relativeUrl; |
52 | 20 | if (url.indexOf('?') != -1) { |
53 | 0 | url += '&api_key=' + this.key; |
54 | } else { | |
55 | 20 | url += '?api_key=' + this.key; |
56 | } | |
57 | 20 | return url; |
58 | }, | |
59 | ||
60 | enrichKwargs: function(kwargs) { | |
61 | 20 | kwargs.url = this.enrichUrl(kwargs.url); |
62 | 20 | kwargs.json = true; |
63 | 20 | var secret = kwargs.secret || this.secret; |
64 | 20 | kwargs.headers = {}; |
65 | 20 | kwargs.headers.Authorization = secret; |
66 | 20 | return kwargs; |
67 | }, | |
68 | /* | |
69 | * Shortcuts for post, get and delete HTTP methods | |
70 | */ | |
71 | dummyCallback: function(error, response, body) { | |
72 | ||
73 | }, | |
74 | get: function(kwargs, cb) { | |
75 | 7 | cb = cb || this.dummyCallback; |
76 | 7 | kwargs = this.enrichKwargs(kwargs); |
77 | 7 | kwargs.method = 'GET'; |
78 | 7 | return request.get(kwargs, cb); |
79 | }, | |
80 | post: function(kwargs, cb) { | |
81 | 11 | cb = cb || this.dummyCallback; |
82 | 11 | kwargs = this.enrichKwargs(kwargs); |
83 | 11 | kwargs.method = 'POST'; |
84 | 11 | return request(kwargs, cb); |
85 | }, | |
86 | delete: function(kwargs, cb) { | |
87 | 2 | cb = cb || this.dummyCallback; |
88 | 2 | kwargs = this.enrichKwargs(kwargs); |
89 | 2 | kwargs.method = 'DELETE'; |
90 | 2 | return request(kwargs, cb); |
91 | } | |
92 | }; | |
93 | ||
94 | 1 | module.exports = StreamClient; |
95 |
Line | Hits | Source |
---|---|---|
1 | 1 | var errors = module.exports; |
2 | ||
3 | 1 | var canCapture = (typeof Error.captureStackTrace === 'function'); |
4 | 1 | var canStack = !!(new Error()).stack; |
5 | ||
6 | 1 | function ErrorAbstract(msg, constructor) { |
7 | 4 | this.message = msg; |
8 | ||
9 | 4 | Error.call(this, this.message); |
10 | ||
11 | 4 | if (canCapture) { |
12 | 4 | Error.captureStackTrace(this, constructor); |
13 | } | |
14 | 0 | else if (canStack) { |
15 | 0 | this.stack = (new Error()).stack; |
16 | } | |
17 | else { | |
18 | 0 | this.stack = ''; |
19 | } | |
20 | } | |
21 | 1 | errors._Abstract = ErrorAbstract; |
22 | 1 | ErrorAbstract.prototype = new Error(); |
23 | ||
24 | /** | |
25 | * FeedError | |
26 | * @param {String} [msg] - An error message that will probably end up in a log. | |
27 | */ | |
28 | 1 | errors.FeedError = function FeedError(msg) { |
29 | 1 | ErrorAbstract.call(this, msg); |
30 | }; | |
31 | 1 | errors.FeedError.prototype = new ErrorAbstract(); |
32 | ||
33 | ||
34 | 1 | errors.SiteError = function SiteError(msg) { |
35 | 1 | ErrorAbstract.call(this, msg); |
36 | }; | |
37 | 1 | errors.SiteError.prototype = new ErrorAbstract(); |
38 | ||
39 |
Line | Hits | Source |
---|---|---|
1 | ||
2 | 1 | var StreamFeed = function () { |
3 | 45 | this.initialize.apply(this, arguments); |
4 | }; | |
5 | ||
6 | 1 | StreamFeed.prototype = { |
7 | /* | |
8 | * The feed object contains convenience functions such add activity | |
9 | * remove activity etc | |
10 | * | |
11 | */ | |
12 | initialize: function(client, feed, token) { | |
13 | 45 | this.client = client; |
14 | 45 | this.feed = feed; |
15 | 45 | this.token = token; |
16 | 45 | this.feedUrl = feed.replace(':', '/'); |
17 | 45 | this.feedTogether = feed.replace(':', ''); |
18 | 45 | this.feedToken = this.feedTogether + ' ' + this.token; |
19 | 45 | this.fayeClient = null; |
20 | 45 | this.notificationChannel = 'site-' + this.client.siteId + '-feed-' + this.feedTogether; |
21 | }, | |
22 | addActivity: function(activity, callback) { | |
23 | /* | |
24 | * Adds the given activity to the feed and | |
25 | * calls the specified callback | |
26 | */ | |
27 | 9 | var xhr = this.client.post({ |
28 | 'url': '/api/feed/'+ this.feedUrl + '/', | |
29 | 'body': activity, | |
30 | 'secret': this.feedToken | |
31 | }, callback); | |
32 | 9 | return xhr; |
33 | }, | |
34 | removeActivity: function(activityId, callback) { | |
35 | 1 | var xhr = this.client.delete({ |
36 | 'url': '/api/feed/'+ this.feedUrl + '/' + activityId + '/', | |
37 | 'secret': this.feedToken | |
38 | }, callback); | |
39 | 1 | return xhr; |
40 | }, | |
41 | follow: function(target, callback) { | |
42 | 2 | var xhr = this.client.post({ |
43 | 'url': '/api/feed/'+ this.feedUrl + '/follows/', | |
44 | 'body': {'target': target}, | |
45 | 'secret': this.feedToken | |
46 | }, callback); | |
47 | 2 | return xhr; |
48 | }, | |
49 | unfollow: function(target, callback) { | |
50 | 1 | var xhr = this.client.delete({ |
51 | 'url': '/api/feed/'+ this.feedUrl + '/follows/' + target + '/', | |
52 | 'secret': this.feedToken | |
53 | }, callback); | |
54 | 1 | return xhr; |
55 | }, | |
56 | get: function(argumentHash, callback) { | |
57 | 7 | var xhr = this.client.get({ |
58 | 'url': '/api/feed/'+ this.feedUrl + '/', | |
59 | 'qs': argumentHash, | |
60 | 'secret': this.feedToken | |
61 | }, callback); | |
62 | 7 | return xhr; |
63 | }, | |
64 | ||
65 | getFayeAuthorization: function(){ | |
66 | 2 | var api_key = this.client.key; |
67 | 2 | var user_id = this.notificationChannel; |
68 | 2 | var signature = this.token; |
69 | 2 | return { |
70 | incoming: function(message, callback) { | |
71 | 3 | callback(message); |
72 | }, | |
73 | outgoing: function(message, callback) { | |
74 | 3 | message.ext = {'user_id': user_id, 'api_key':api_key, 'signature': signature}; |
75 | 3 | callback(message); |
76 | } | |
77 | }; | |
78 | }, | |
79 | ||
80 | getFayeClient: function(){ | |
81 | 3 | var Faye = require('faye'); |
82 | 3 | if (this.fayeClient === null){ |
83 | 2 | this.fayeClient = new Faye.Client(this.client.fayeUrl); |
84 | 2 | var authExtension = this.getFayeAuthorization(); |
85 | 2 | this.fayeClient.addExtension(authExtension); |
86 | } | |
87 | 3 | return this.fayeClient; |
88 | }, | |
89 | ||
90 | subscribe: function(callback){ | |
91 | 2 | if (!this.client.siteId) { |
92 | 1 | throw new errors.SiteError('Missing site id, which is needed to subscribe, use var client = stream.connect(key, secret, siteId);'); |
93 | } | |
94 | 1 | return this.getFayeClient().subscribe('/'+this.notificationChannel, callback); |
95 | } | |
96 | }; | |
97 | ||
98 | ||
99 | 1 | module.exports = StreamFeed; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | 1 | var crypto = require('crypto'); |
3 | ||
4 | ||
5 | 1 | function urlsafe_b64_encode(s) { |
6 | 44 | var escaped = s.replace('+', '-').replace('/', '_'); |
7 | 44 | return escaped.replace(/^=+/, '').replace(/=+$/, ''); |
8 | } | |
9 | ||
10 | ||
11 | 1 | exports.sign = function(secret, value) { |
12 | /* | |
13 | * Setup sha1 based on the secret | |
14 | * Get the digest of the value | |
15 | * Base64 encode the result | |
16 | * | |
17 | * Also see | |
18 | * https://github.com/tbarbugli/stream-ruby/blob/master/lib/stream/signer.rb | |
19 | * https://github.com/tschellenbach/stream-python/blob/master/stream/signing.py | |
20 | * | |
21 | * Steps | |
22 | * secret: tfq2sdqpj9g446sbv653x3aqmgn33hsn8uzdc9jpskaw8mj6vsnhzswuwptuj9su | |
23 | * value: flat1 | |
24 | * digest: Q\xb6\xd5+\x82\xd58\xdeu\x80\xc5\xe3\xb8\xa5bL1\xf1\xa3\xdb | |
25 | * result: UbbVK4LVON51gMXjuKViTDHxo9s | |
26 | */ | |
27 | 44 | var key = new crypto.createHash('sha1').update(secret).digest(); |
28 | 44 | var hmac = crypto.createHmac('sha1', key); |
29 | 44 | var signature = hmac.update(value).digest('base64'); |
30 | 44 | var urlsafe = urlsafe_b64_encode(signature); |
31 | 44 | return urlsafe; |
32 | }; |