diff options
Diffstat (limited to 'server/lib/friends.js')
-rw-r--r-- | server/lib/friends.js | 405 |
1 files changed, 0 insertions, 405 deletions
diff --git a/server/lib/friends.js b/server/lib/friends.js deleted file mode 100644 index 6dd32406c..000000000 --- a/server/lib/friends.js +++ /dev/null | |||
@@ -1,405 +0,0 @@ | |||
1 | 'use strict' | ||
2 | |||
3 | const each = require('async/each') | ||
4 | const eachLimit = require('async/eachLimit') | ||
5 | const eachSeries = require('async/eachSeries') | ||
6 | const series = require('async/series') | ||
7 | const request = require('request') | ||
8 | const waterfall = require('async/waterfall') | ||
9 | |||
10 | const constants = require('../initializers/constants') | ||
11 | const db = require('../initializers/database') | ||
12 | const logger = require('../helpers/logger') | ||
13 | const peertubeCrypto = require('../helpers/peertube-crypto') | ||
14 | const requests = require('../helpers/requests') | ||
15 | const utils = require('../helpers/utils') | ||
16 | const RequestScheduler = require('./request/request-scheduler') | ||
17 | const RequestVideoQaduScheduler = require('./request/request-video-qadu-scheduler') | ||
18 | const RequestVideoEventScheduler = require('./request/request-video-event-scheduler') | ||
19 | |||
20 | const ENDPOINT_ACTIONS = constants.REQUEST_ENDPOINT_ACTIONS[constants.REQUEST_ENDPOINTS.VIDEOS] | ||
21 | |||
22 | const requestScheduler = new RequestScheduler() | ||
23 | const requestVideoQaduScheduler = new RequestVideoQaduScheduler() | ||
24 | const requestVideoEventScheduler = new RequestVideoEventScheduler() | ||
25 | |||
26 | const friends = { | ||
27 | activate, | ||
28 | addVideoToFriends, | ||
29 | updateVideoToFriends, | ||
30 | reportAbuseVideoToFriend, | ||
31 | quickAndDirtyUpdateVideoToFriends, | ||
32 | quickAndDirtyUpdatesVideoToFriends, | ||
33 | addEventToRemoteVideo, | ||
34 | addEventsToRemoteVideo, | ||
35 | hasFriends, | ||
36 | makeFriends, | ||
37 | quitFriends, | ||
38 | removeVideoToFriends, | ||
39 | sendOwnedVideosToPod, | ||
40 | getRequestScheduler, | ||
41 | getRequestVideoQaduScheduler, | ||
42 | getRequestVideoEventScheduler | ||
43 | } | ||
44 | |||
45 | function activate () { | ||
46 | requestScheduler.activate() | ||
47 | requestVideoQaduScheduler.activate() | ||
48 | requestVideoEventScheduler.activate() | ||
49 | } | ||
50 | |||
51 | function addVideoToFriends (videoData, transaction, callback) { | ||
52 | const options = { | ||
53 | type: ENDPOINT_ACTIONS.ADD, | ||
54 | endpoint: constants.REQUEST_ENDPOINTS.VIDEOS, | ||
55 | data: videoData, | ||
56 | transaction | ||
57 | } | ||
58 | createRequest(options, callback) | ||
59 | } | ||
60 | |||
61 | function updateVideoToFriends (videoData, transaction, callback) { | ||
62 | const options = { | ||
63 | type: ENDPOINT_ACTIONS.UPDATE, | ||
64 | endpoint: constants.REQUEST_ENDPOINTS.VIDEOS, | ||
65 | data: videoData, | ||
66 | transaction | ||
67 | } | ||
68 | createRequest(options, callback) | ||
69 | } | ||
70 | |||
71 | function removeVideoToFriends (videoParams) { | ||
72 | const options = { | ||
73 | type: ENDPOINT_ACTIONS.REMOVE, | ||
74 | endpoint: constants.REQUEST_ENDPOINTS.VIDEOS, | ||
75 | data: videoParams | ||
76 | } | ||
77 | createRequest(options) | ||
78 | } | ||
79 | |||
80 | function reportAbuseVideoToFriend (reportData, video) { | ||
81 | const options = { | ||
82 | type: ENDPOINT_ACTIONS.REPORT_ABUSE, | ||
83 | endpoint: constants.REQUEST_ENDPOINTS.VIDEOS, | ||
84 | data: reportData, | ||
85 | toIds: [ video.Author.podId ] | ||
86 | } | ||
87 | createRequest(options) | ||
88 | } | ||
89 | |||
90 | function quickAndDirtyUpdateVideoToFriends (qaduParams, transaction, callback) { | ||
91 | const options = { | ||
92 | videoId: qaduParams.videoId, | ||
93 | type: qaduParams.type, | ||
94 | transaction | ||
95 | } | ||
96 | return createVideoQaduRequest(options, callback) | ||
97 | } | ||
98 | |||
99 | function quickAndDirtyUpdatesVideoToFriends (qadusParams, transaction, finalCallback) { | ||
100 | const tasks = [] | ||
101 | |||
102 | qadusParams.forEach(function (qaduParams) { | ||
103 | const fun = function (callback) { | ||
104 | quickAndDirtyUpdateVideoToFriends(qaduParams, transaction, callback) | ||
105 | } | ||
106 | |||
107 | tasks.push(fun) | ||
108 | }) | ||
109 | |||
110 | series(tasks, finalCallback) | ||
111 | } | ||
112 | |||
113 | function addEventToRemoteVideo (eventParams, transaction, callback) { | ||
114 | const options = { | ||
115 | videoId: eventParams.videoId, | ||
116 | type: eventParams.type, | ||
117 | transaction | ||
118 | } | ||
119 | createVideoEventRequest(options, callback) | ||
120 | } | ||
121 | |||
122 | function addEventsToRemoteVideo (eventsParams, transaction, finalCallback) { | ||
123 | const tasks = [] | ||
124 | |||
125 | eventsParams.forEach(function (eventParams) { | ||
126 | const fun = function (callback) { | ||
127 | addEventToRemoteVideo(eventParams, transaction, callback) | ||
128 | } | ||
129 | |||
130 | tasks.push(fun) | ||
131 | }) | ||
132 | |||
133 | series(tasks, finalCallback) | ||
134 | } | ||
135 | |||
136 | function hasFriends (callback) { | ||
137 | db.Pod.countAll(function (err, count) { | ||
138 | if (err) return callback(err) | ||
139 | |||
140 | const hasFriends = (count !== 0) | ||
141 | callback(null, hasFriends) | ||
142 | }) | ||
143 | } | ||
144 | |||
145 | function makeFriends (hosts, callback) { | ||
146 | const podsScore = {} | ||
147 | |||
148 | logger.info('Make friends!') | ||
149 | peertubeCrypto.getMyPublicCert(function (err, cert) { | ||
150 | if (err) { | ||
151 | logger.error('Cannot read public cert.') | ||
152 | return callback(err) | ||
153 | } | ||
154 | |||
155 | eachSeries(hosts, function (host, callbackEach) { | ||
156 | computeForeignPodsList(host, podsScore, callbackEach) | ||
157 | }, function (err) { | ||
158 | if (err) return callback(err) | ||
159 | |||
160 | logger.debug('Pods scores computed.', { podsScore: podsScore }) | ||
161 | const podsList = computeWinningPods(hosts, podsScore) | ||
162 | logger.debug('Pods that we keep.', { podsToKeep: podsList }) | ||
163 | |||
164 | makeRequestsToWinningPods(cert, podsList, callback) | ||
165 | }) | ||
166 | }) | ||
167 | } | ||
168 | |||
169 | function quitFriends (callback) { | ||
170 | // Stop pool requests | ||
171 | requestScheduler.deactivate() | ||
172 | |||
173 | waterfall([ | ||
174 | function flushRequests (callbackAsync) { | ||
175 | requestScheduler.flush(err => callbackAsync(err)) | ||
176 | }, | ||
177 | |||
178 | function flushVideoQaduRequests (callbackAsync) { | ||
179 | requestVideoQaduScheduler.flush(err => callbackAsync(err)) | ||
180 | }, | ||
181 | |||
182 | function getPodsList (callbackAsync) { | ||
183 | return db.Pod.list(callbackAsync) | ||
184 | }, | ||
185 | |||
186 | function announceIQuitMyFriends (pods, callbackAsync) { | ||
187 | const requestParams = { | ||
188 | method: 'POST', | ||
189 | path: '/api/' + constants.API_VERSION + '/remote/pods/remove', | ||
190 | sign: true | ||
191 | } | ||
192 | |||
193 | // Announce we quit them | ||
194 | // We don't care if the request fails | ||
195 | // The other pod will exclude us automatically after a while | ||
196 | eachLimit(pods, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) { | ||
197 | requestParams.toPod = pod | ||
198 | requests.makeSecureRequest(requestParams, callbackEach) | ||
199 | }, function (err) { | ||
200 | if (err) { | ||
201 | logger.error('Some errors while quitting friends.', { err: err }) | ||
202 | // Don't stop the process | ||
203 | } | ||
204 | |||
205 | return callbackAsync(null, pods) | ||
206 | }) | ||
207 | }, | ||
208 | |||
209 | function removePodsFromDB (pods, callbackAsync) { | ||
210 | each(pods, function (pod, callbackEach) { | ||
211 | pod.destroy().asCallback(callbackEach) | ||
212 | }, callbackAsync) | ||
213 | } | ||
214 | ], function (err) { | ||
215 | // Don't forget to re activate the scheduler, even if there was an error | ||
216 | requestScheduler.activate() | ||
217 | |||
218 | if (err) return callback(err) | ||
219 | |||
220 | logger.info('Removed all remote videos.') | ||
221 | return callback(null) | ||
222 | }) | ||
223 | } | ||
224 | |||
225 | function sendOwnedVideosToPod (podId) { | ||
226 | db.Video.listOwnedAndPopulateAuthorAndTags(function (err, videosList) { | ||
227 | if (err) { | ||
228 | logger.error('Cannot get the list of videos we own.') | ||
229 | return | ||
230 | } | ||
231 | |||
232 | videosList.forEach(function (video) { | ||
233 | video.toAddRemoteJSON(function (err, remoteVideo) { | ||
234 | if (err) { | ||
235 | logger.error('Cannot convert video to remote.', { error: err }) | ||
236 | // Don't break the process | ||
237 | return | ||
238 | } | ||
239 | |||
240 | const options = { | ||
241 | type: 'add', | ||
242 | endpoint: constants.REQUEST_ENDPOINTS.VIDEOS, | ||
243 | data: remoteVideo, | ||
244 | toIds: [ podId ] | ||
245 | } | ||
246 | createRequest(options) | ||
247 | }) | ||
248 | }) | ||
249 | }) | ||
250 | } | ||
251 | |||
252 | function getRequestScheduler () { | ||
253 | return requestScheduler | ||
254 | } | ||
255 | |||
256 | function getRequestVideoQaduScheduler () { | ||
257 | return requestVideoQaduScheduler | ||
258 | } | ||
259 | |||
260 | function getRequestVideoEventScheduler () { | ||
261 | return requestVideoEventScheduler | ||
262 | } | ||
263 | |||
264 | // --------------------------------------------------------------------------- | ||
265 | |||
266 | module.exports = friends | ||
267 | |||
268 | // --------------------------------------------------------------------------- | ||
269 | |||
270 | function computeForeignPodsList (host, podsScore, callback) { | ||
271 | getForeignPodsList(host, function (err, res) { | ||
272 | if (err) return callback(err) | ||
273 | |||
274 | const foreignPodsList = res.data | ||
275 | |||
276 | // Let's give 1 point to the pod we ask the friends list | ||
277 | foreignPodsList.push({ host }) | ||
278 | |||
279 | foreignPodsList.forEach(function (foreignPod) { | ||
280 | const foreignPodHost = foreignPod.host | ||
281 | |||
282 | if (podsScore[foreignPodHost]) podsScore[foreignPodHost]++ | ||
283 | else podsScore[foreignPodHost] = 1 | ||
284 | }) | ||
285 | |||
286 | return callback() | ||
287 | }) | ||
288 | } | ||
289 | |||
290 | function computeWinningPods (hosts, podsScore) { | ||
291 | // Build the list of pods to add | ||
292 | // Only add a pod if it exists in more than a half base pods | ||
293 | const podsList = [] | ||
294 | const baseScore = hosts.length / 2 | ||
295 | |||
296 | Object.keys(podsScore).forEach(function (podHost) { | ||
297 | // If the pod is not me and with a good score we add it | ||
298 | if (isMe(podHost) === false && podsScore[podHost] > baseScore) { | ||
299 | podsList.push({ host: podHost }) | ||
300 | } | ||
301 | }) | ||
302 | |||
303 | return podsList | ||
304 | } | ||
305 | |||
306 | function getForeignPodsList (host, callback) { | ||
307 | const path = '/api/' + constants.API_VERSION + '/pods' | ||
308 | |||
309 | request.get(constants.REMOTE_SCHEME.HTTP + '://' + host + path, function (err, response, body) { | ||
310 | if (err) return callback(err) | ||
311 | |||
312 | try { | ||
313 | const json = JSON.parse(body) | ||
314 | return callback(null, json) | ||
315 | } catch (err) { | ||
316 | return callback(err) | ||
317 | } | ||
318 | }) | ||
319 | } | ||
320 | |||
321 | function makeRequestsToWinningPods (cert, podsList, callback) { | ||
322 | // Stop pool requests | ||
323 | requestScheduler.deactivate() | ||
324 | // Flush pool requests | ||
325 | requestScheduler.forceSend() | ||
326 | |||
327 | eachLimit(podsList, constants.REQUESTS_IN_PARALLEL, function (pod, callbackEach) { | ||
328 | const params = { | ||
329 | url: constants.REMOTE_SCHEME.HTTP + '://' + pod.host + '/api/' + constants.API_VERSION + '/pods/', | ||
330 | method: 'POST', | ||
331 | json: { | ||
332 | host: constants.CONFIG.WEBSERVER.HOST, | ||
333 | email: constants.CONFIG.ADMIN.EMAIL, | ||
334 | publicKey: cert | ||
335 | } | ||
336 | } | ||
337 | |||
338 | requests.makeRetryRequest(params, function (err, res, body) { | ||
339 | if (err) { | ||
340 | logger.error('Error with adding %s pod.', pod.host, { error: err }) | ||
341 | // Don't break the process | ||
342 | return callbackEach() | ||
343 | } | ||
344 | |||
345 | if (res.statusCode === 200) { | ||
346 | const podObj = db.Pod.build({ host: pod.host, publicKey: body.cert, email: body.email }) | ||
347 | podObj.save().asCallback(function (err, podCreated) { | ||
348 | if (err) { | ||
349 | logger.error('Cannot add friend %s pod.', pod.host, { error: err }) | ||
350 | return callbackEach() | ||
351 | } | ||
352 | |||
353 | // Add our videos to the request scheduler | ||
354 | sendOwnedVideosToPod(podCreated.id) | ||
355 | |||
356 | return callbackEach() | ||
357 | }) | ||
358 | } else { | ||
359 | logger.error('Status not 200 for %s pod.', pod.host) | ||
360 | return callbackEach() | ||
361 | } | ||
362 | }) | ||
363 | }, function endRequests () { | ||
364 | // Final callback, we've ended all the requests | ||
365 | // Now we made new friends, we can re activate the pool of requests | ||
366 | requestScheduler.activate() | ||
367 | |||
368 | logger.debug('makeRequestsToWinningPods finished.') | ||
369 | return callback() | ||
370 | }) | ||
371 | } | ||
372 | |||
373 | // Wrapper that populate "toIds" argument with all our friends if it is not specified | ||
374 | // { type, endpoint, data, toIds, transaction } | ||
375 | function createRequest (options, callback) { | ||
376 | if (!callback) callback = function () {} | ||
377 | if (options.toIds) return requestScheduler.createRequest(options, callback) | ||
378 | |||
379 | // If the "toIds" pods is not specified, we send the request to all our friends | ||
380 | db.Pod.listAllIds(options.transaction, function (err, podIds) { | ||
381 | if (err) { | ||
382 | logger.error('Cannot get pod ids', { error: err }) | ||
383 | return | ||
384 | } | ||
385 | |||
386 | const newOptions = Object.assign(options, { toIds: podIds }) | ||
387 | return requestScheduler.createRequest(newOptions, callback) | ||
388 | }) | ||
389 | } | ||
390 | |||
391 | function createVideoQaduRequest (options, callback) { | ||
392 | if (!callback) callback = utils.createEmptyCallback() | ||
393 | |||
394 | requestVideoQaduScheduler.createRequest(options, callback) | ||
395 | } | ||
396 | |||
397 | function createVideoEventRequest (options, callback) { | ||
398 | if (!callback) callback = utils.createEmptyCallback() | ||
399 | |||
400 | requestVideoEventScheduler.createRequest(options, callback) | ||
401 | } | ||
402 | |||
403 | function isMe (host) { | ||
404 | return host === constants.CONFIG.WEBSERVER.HOST | ||
405 | } | ||