diff options
Diffstat (limited to 'server/lib/friends.js')
-rw-r--r-- | server/lib/friends.js | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/server/lib/friends.js b/server/lib/friends.js new file mode 100644 index 000000000..006a64404 --- /dev/null +++ b/server/lib/friends.js | |||
@@ -0,0 +1,228 @@ | |||
1 | 'use strict' | ||
2 | |||
3 | var async = require('async') | ||
4 | var config = require('config') | ||
5 | var fs = require('fs') | ||
6 | var request = require('request') | ||
7 | |||
8 | var constants = require('../initializers/constants') | ||
9 | var logger = require('../helpers/logger') | ||
10 | var peertubeCrypto = require('../helpers/peertubeCrypto') | ||
11 | var Pods = require('../models/pods') | ||
12 | var poolRequests = require('../lib/poolRequests') | ||
13 | var requests = require('../helpers/requests') | ||
14 | var Videos = require('../models/videos') | ||
15 | |||
16 | var http = config.get('webserver.https') ? 'https' : 'http' | ||
17 | var host = config.get('webserver.host') | ||
18 | var port = config.get('webserver.port') | ||
19 | |||
20 | var pods = { | ||
21 | addVideoToFriends: addVideoToFriends, | ||
22 | hasFriends: hasFriends, | ||
23 | makeFriends: makeFriends, | ||
24 | quitFriends: quitFriends, | ||
25 | removeVideoToFriends: removeVideoToFriends | ||
26 | } | ||
27 | |||
28 | function addVideoToFriends (video) { | ||
29 | // To avoid duplicates | ||
30 | var id = video.name + video.magnetUri | ||
31 | // ensure namePath is null | ||
32 | video.namePath = null | ||
33 | poolRequests.addRequest(id, 'add', video) | ||
34 | } | ||
35 | |||
36 | function hasFriends (callback) { | ||
37 | Pods.count(function (err, count) { | ||
38 | if (err) return callback(err) | ||
39 | |||
40 | var has_friends = (count !== 0) | ||
41 | callback(null, has_friends) | ||
42 | }) | ||
43 | } | ||
44 | |||
45 | function makeFriends (callback) { | ||
46 | var pods_score = {} | ||
47 | |||
48 | logger.info('Make friends!') | ||
49 | fs.readFile(peertubeCrypto.getCertDir() + 'peertube.pub', 'utf8', function (err, cert) { | ||
50 | if (err) { | ||
51 | logger.error('Cannot read public cert.') | ||
52 | return callback(err) | ||
53 | } | ||
54 | |||
55 | var urls = config.get('network.friends') | ||
56 | |||
57 | async.each(urls, function (url, callback) { | ||
58 | computeForeignPodsList(url, pods_score, callback) | ||
59 | }, function (err) { | ||
60 | if (err) return callback(err) | ||
61 | |||
62 | logger.debug('Pods scores computed.', { pods_score: pods_score }) | ||
63 | var pods_list = computeWinningPods(urls, pods_score) | ||
64 | logger.debug('Pods that we keep computed.', { pods_to_keep: pods_list }) | ||
65 | |||
66 | makeRequestsToWinningPods(cert, pods_list, callback) | ||
67 | }) | ||
68 | }) | ||
69 | } | ||
70 | |||
71 | function quitFriends (callback) { | ||
72 | // Stop pool requests | ||
73 | poolRequests.deactivate() | ||
74 | // Flush pool requests | ||
75 | poolRequests.forceSend() | ||
76 | |||
77 | Pods.list(function (err, pods) { | ||
78 | if (err) return callback(err) | ||
79 | |||
80 | var request = { | ||
81 | method: 'POST', | ||
82 | path: '/api/' + constants.API_VERSION + '/pods/remove', | ||
83 | sign: true, | ||
84 | encrypt: true, | ||
85 | data: { | ||
86 | url: 'me' // Fake data | ||
87 | } | ||
88 | } | ||
89 | |||
90 | // Announce we quit them | ||
91 | requests.makeMultipleRetryRequest(request, pods, function () { | ||
92 | Pods.removeAll(function (err) { | ||
93 | poolRequests.activate() | ||
94 | |||
95 | if (err) return callback(err) | ||
96 | |||
97 | logger.info('Broke friends, so sad :(') | ||
98 | |||
99 | Videos.removeAllRemotes(function (err) { | ||
100 | if (err) return callback(err) | ||
101 | |||
102 | logger.info('Removed all remote videos.') | ||
103 | callback(null) | ||
104 | }) | ||
105 | }) | ||
106 | }) | ||
107 | }) | ||
108 | } | ||
109 | |||
110 | function removeVideoToFriends (video) { | ||
111 | // To avoid duplicates | ||
112 | var id = video.name + video.magnetUri | ||
113 | poolRequests.addRequest(id, 'remove', video) | ||
114 | } | ||
115 | |||
116 | // --------------------------------------------------------------------------- | ||
117 | |||
118 | module.exports = pods | ||
119 | |||
120 | // --------------------------------------------------------------------------- | ||
121 | |||
122 | function computeForeignPodsList (url, pods_score, callback) { | ||
123 | // Let's give 1 point to the pod we ask the friends list | ||
124 | pods_score[url] = 1 | ||
125 | |||
126 | getForeignPodsList(url, function (err, foreign_pods_list) { | ||
127 | if (err) return callback(err) | ||
128 | if (foreign_pods_list.length === 0) return callback() | ||
129 | |||
130 | async.each(foreign_pods_list, function (foreign_pod, callback_each) { | ||
131 | var foreign_url = foreign_pod.url | ||
132 | |||
133 | if (pods_score[foreign_url]) pods_score[foreign_url]++ | ||
134 | else pods_score[foreign_url] = 1 | ||
135 | |||
136 | callback_each() | ||
137 | }, function () { | ||
138 | callback() | ||
139 | }) | ||
140 | }) | ||
141 | } | ||
142 | |||
143 | function computeWinningPods (urls, pods_score) { | ||
144 | // Build the list of pods to add | ||
145 | // Only add a pod if it exists in more than a half base pods | ||
146 | var pods_list = [] | ||
147 | var base_score = urls.length / 2 | ||
148 | Object.keys(pods_score).forEach(function (pod) { | ||
149 | if (pods_score[pod] > base_score) pods_list.push({ url: pod }) | ||
150 | }) | ||
151 | |||
152 | return pods_list | ||
153 | } | ||
154 | |||
155 | function getForeignPodsList (url, callback) { | ||
156 | var path = '/api/' + constants.API_VERSION + '/pods' | ||
157 | |||
158 | request.get(url + path, function (err, response, body) { | ||
159 | if (err) return callback(err) | ||
160 | |||
161 | callback(null, JSON.parse(body)) | ||
162 | }) | ||
163 | } | ||
164 | |||
165 | function makeRequestsToWinningPods (cert, pods_list, callback) { | ||
166 | // Stop pool requests | ||
167 | poolRequests.deactivate() | ||
168 | // Flush pool requests | ||
169 | poolRequests.forceSend() | ||
170 | |||
171 | // Get the list of our videos to send to our new friends | ||
172 | Videos.listOwned(function (err, videos_list) { | ||
173 | if (err) { | ||
174 | logger.error('Cannot get the list of videos we own.') | ||
175 | return callback(err) | ||
176 | } | ||
177 | |||
178 | var data = { | ||
179 | url: http + '://' + host + ':' + port, | ||
180 | publicKey: cert, | ||
181 | videos: videos_list | ||
182 | } | ||
183 | |||
184 | requests.makeMultipleRetryRequest( | ||
185 | { method: 'POST', path: '/api/' + constants.API_VERSION + '/pods/', data: data }, | ||
186 | |||
187 | pods_list, | ||
188 | |||
189 | function eachRequest (err, response, body, url, pod, callback_each_request) { | ||
190 | // We add the pod if it responded correctly with its public certificate | ||
191 | if (!err && response.statusCode === 200) { | ||
192 | Pods.add({ url: pod.url, publicKey: body.cert, score: constants.FRIEND_BASE_SCORE }, function (err) { | ||
193 | if (err) { | ||
194 | logger.error('Error with adding %s pod.', pod.url, { error: err }) | ||
195 | return callback_each_request() | ||
196 | } | ||
197 | |||
198 | Videos.addRemotes(body.videos, function (err) { | ||
199 | if (err) { | ||
200 | logger.error('Error with adding videos of pod.', pod.url, { error: err }) | ||
201 | return callback_each_request() | ||
202 | } | ||
203 | |||
204 | logger.debug('Adding remote videos from %s.', pod.url, { videos: body.videos }) | ||
205 | return callback_each_request() | ||
206 | }) | ||
207 | }) | ||
208 | } else { | ||
209 | logger.error('Error with adding %s pod.', pod.url, { error: err || new Error('Status not 200') }) | ||
210 | return callback_each_request() | ||
211 | } | ||
212 | }, | ||
213 | |||
214 | function endRequests (err) { | ||
215 | // Now we made new friends, we can re activate the pool of requests | ||
216 | poolRequests.activate() | ||
217 | |||
218 | if (err) { | ||
219 | logger.error('There was some errors when we wanted to make friends.') | ||
220 | return callback(err) | ||
221 | } | ||
222 | |||
223 | logger.debug('makeRequestsToWinningPods finished.') | ||
224 | return callback(null) | ||
225 | } | ||
226 | ) | ||
227 | }) | ||
228 | } | ||