aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/friends.js
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/friends.js')
-rw-r--r--server/lib/friends.js405
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
3const each = require('async/each')
4const eachLimit = require('async/eachLimit')
5const eachSeries = require('async/eachSeries')
6const series = require('async/series')
7const request = require('request')
8const waterfall = require('async/waterfall')
9
10const constants = require('../initializers/constants')
11const db = require('../initializers/database')
12const logger = require('../helpers/logger')
13const peertubeCrypto = require('../helpers/peertube-crypto')
14const requests = require('../helpers/requests')
15const utils = require('../helpers/utils')
16const RequestScheduler = require('./request/request-scheduler')
17const RequestVideoQaduScheduler = require('./request/request-video-qadu-scheduler')
18const RequestVideoEventScheduler = require('./request/request-video-event-scheduler')
19
20const ENDPOINT_ACTIONS = constants.REQUEST_ENDPOINT_ACTIONS[constants.REQUEST_ENDPOINTS.VIDEOS]
21
22const requestScheduler = new RequestScheduler()
23const requestVideoQaduScheduler = new RequestVideoQaduScheduler()
24const requestVideoEventScheduler = new RequestVideoEventScheduler()
25
26const 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
45function activate () {
46 requestScheduler.activate()
47 requestVideoQaduScheduler.activate()
48 requestVideoEventScheduler.activate()
49}
50
51function 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
61function 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
71function 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
80function 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
90function 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
99function 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
113function addEventToRemoteVideo (eventParams, transaction, callback) {
114 const options = {
115 videoId: eventParams.videoId,
116 type: eventParams.type,
117 transaction
118 }
119 createVideoEventRequest(options, callback)
120}
121
122function 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
136function 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
145function 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
169function 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
225function 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
252function getRequestScheduler () {
253 return requestScheduler
254}
255
256function getRequestVideoQaduScheduler () {
257 return requestVideoQaduScheduler
258}
259
260function getRequestVideoEventScheduler () {
261 return requestVideoEventScheduler
262}
263
264// ---------------------------------------------------------------------------
265
266module.exports = friends
267
268// ---------------------------------------------------------------------------
269
270function 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
290function 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
306function 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
321function 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 }
375function 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
391function createVideoQaduRequest (options, callback) {
392 if (!callback) callback = utils.createEmptyCallback()
393
394 requestVideoQaduScheduler.createRequest(options, callback)
395}
396
397function createVideoEventRequest (options, callback) {
398 if (!callback) callback = utils.createEmptyCallback()
399
400 requestVideoEventScheduler.createRequest(options, callback)
401}
402
403function isMe (host) {
404 return host === constants.CONFIG.WEBSERVER.HOST
405}