]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/lib/friends.js
Use async waterfall for better readability in friends lib
[github/Chocobozzz/PeerTube.git] / server / lib / friends.js
CommitLineData
9f10b292
C
1'use strict'
2
f0f5567b
C
3const async = require('async')
4const config = require('config')
5const fs = require('fs')
6const request = require('request')
7
8const constants = require('../initializers/constants')
9const logger = require('../helpers/logger')
10const peertubeCrypto = require('../helpers/peertubeCrypto')
11const Pods = require('../models/pods')
e3647ae2 12const requestsScheduler = require('../lib/requestsScheduler')
f0f5567b 13const requests = require('../helpers/requests')
cbe2f7c3 14const videos = require('../lib/videos')
f0f5567b
C
15const Videos = require('../models/videos')
16
17const http = config.get('webserver.https') ? 'https' : 'http'
18const host = config.get('webserver.host')
19const port = config.get('webserver.port')
20
21const pods = {
9f10b292
C
22 addVideoToFriends: addVideoToFriends,
23 hasFriends: hasFriends,
cbe2f7c3 24 getMyCertificate: getMyCertificate,
9f10b292
C
25 makeFriends: makeFriends,
26 quitFriends: quitFriends,
27 removeVideoToFriends: removeVideoToFriends
28}
29
30function addVideoToFriends (video) {
31 // To avoid duplicates
f0f5567b 32 const id = video.name + video.magnetUri
9f10b292
C
33 // ensure namePath is null
34 video.namePath = null
e3647ae2 35 requestsScheduler.addRequest(id, 'add', video)
9f10b292
C
36}
37
38function hasFriends (callback) {
39 Pods.count(function (err, count) {
40 if (err) return callback(err)
41
bc503c2a
C
42 const hasFriends = (count !== 0)
43 callback(null, hasFriends)
9f10b292
C
44 })
45}
46
cbe2f7c3
C
47function getMyCertificate (callback) {
48 fs.readFile(peertubeCrypto.getCertDir() + 'peertube.pub', 'utf8', callback)
49}
50
9f10b292 51function makeFriends (callback) {
bc503c2a 52 const podsScore = {}
9f10b292
C
53
54 logger.info('Make friends!')
cbe2f7c3 55 getMyCertificate(function (err, cert) {
9f10b292
C
56 if (err) {
57 logger.error('Cannot read public cert.')
58 return callback(err)
59 }
c173e565 60
f0f5567b 61 const urls = config.get('network.friends')
c173e565 62
bc503c2a
C
63 async.each(urls, function (url, callbackEach) {
64 computeForeignPodsList(url, podsScore, callbackEach)
89d1d8ba 65 }, function (err) {
c173e565
C
66 if (err) return callback(err)
67
bc503c2a
C
68 logger.debug('Pods scores computed.', { podsScore: podsScore })
69 const podsList = computeWinningPods(urls, podsScore)
70 logger.debug('Pods that we keep.', { podsToKeep: podsList })
9f10b292 71
bc503c2a 72 makeRequestsToWinningPods(cert, podsList, callback)
c173e565 73 })
9f10b292 74 })
9f10b292
C
75}
76
77function quitFriends (callback) {
78 // Stop pool requests
e3647ae2 79 requestsScheduler.deactivate()
9f10b292 80 // Flush pool requests
e3647ae2 81 requestsScheduler.forceSend()
9f10b292 82
e7ea2817
C
83 async.waterfall([
84 function getPodsList (callbackAsync) {
85 return Pods.list(callbackAsync)
86 },
87
88 function announceIQuitMyFriends (pods, callbackAsync) {
89 const request = {
90 method: 'POST',
91 path: '/api/' + constants.API_VERSION + '/pods/remove',
92 sign: true,
93 encrypt: true,
94 data: {
95 url: 'me' // Fake data
96 }
c173e565
C
97 }
98
e7ea2817
C
99 // Announce we quit them
100 requests.makeMultipleRetryRequest(request, pods, function (err) {
101 return callbackAsync(err)
102 })
103 },
c173e565 104
e7ea2817
C
105 function removePodsFromDB (callbackAsync) {
106 Pods.removeAll(function (err) {
107 return callbackAsync(err)
108 })
109 },
c173e565 110
e7ea2817
C
111 function listRemoteVideos (callbackAsync) {
112 logger.info('Broke friends, so sad :(')
c173e565 113
e7ea2817
C
114 Videos.listFromRemotes(callbackAsync)
115 },
c173e565 116
e7ea2817
C
117 function removeTheRemoteVideos (videosList, callbackAsync) {
118 videos.removeRemoteVideos(videosList, function (err) {
119 if (err) {
120 logger.error('Cannot remove remote videos.', { error: err })
121 return callbackAsync(err)
122 }
cbe2f7c3 123
e7ea2817 124 return callbackAsync(null)
c173e565 125 })
e7ea2817
C
126 }
127 ], function (err) {
128 // Don't forget to re activate the scheduler, even if there was an error
129 requestsScheduler.activate()
130
131 if (err) return callback(err)
132
133 logger.info('Removed all remote videos.')
134 return callback(null)
9f10b292
C
135 })
136}
c173e565 137
9f10b292
C
138function removeVideoToFriends (video) {
139 // To avoid duplicates
f0f5567b 140 const id = video.name + video.magnetUri
e3647ae2 141 requestsScheduler.addRequest(id, 'remove', video)
9f10b292 142}
c173e565 143
9f10b292 144// ---------------------------------------------------------------------------
c173e565 145
9f10b292 146module.exports = pods
c173e565 147
9f10b292 148// ---------------------------------------------------------------------------
c173e565 149
bc503c2a 150function computeForeignPodsList (url, podsScore, callback) {
89d1d8ba 151 // Let's give 1 point to the pod we ask the friends list
bc503c2a 152 podsScore[url] = 1
89d1d8ba 153
bc503c2a 154 getForeignPodsList(url, function (err, foreignPodsList) {
89d1d8ba 155 if (err) return callback(err)
bc503c2a 156 if (foreignPodsList.length === 0) return callback()
89d1d8ba 157
bc503c2a
C
158 foreignPodsList.forEach(function (foreignPod) {
159 const foreignUrl = foreignPod.url
89d1d8ba 160
bc503c2a
C
161 if (podsScore[foreignUrl]) podsScore[foreignUrl]++
162 else podsScore[foreignUrl] = 1
89d1d8ba 163 })
cbe2f7c3
C
164
165 callback()
89d1d8ba
C
166 })
167}
168
bc503c2a 169function computeWinningPods (urls, podsScore) {
89d1d8ba
C
170 // Build the list of pods to add
171 // Only add a pod if it exists in more than a half base pods
bc503c2a
C
172 const podsList = []
173 const baseScore = urls.length / 2
d6ea0175 174 Object.keys(podsScore).forEach(function (pod) {
bc503c2a 175 if (podsScore[pod] > baseScore) podsList.push({ url: pod })
89d1d8ba 176 })
e7ea2817 177
bc503c2a 178 return podsList
89d1d8ba
C
179}
180
9f10b292 181function getForeignPodsList (url, callback) {
f0f5567b 182 const path = '/api/' + constants.API_VERSION + '/pods'
c173e565 183
9f10b292
C
184 request.get(url + path, function (err, response, body) {
185 if (err) return callback(err)
8425cb89 186
9f10b292
C
187 callback(null, JSON.parse(body))
188 })
189}
89d1d8ba 190
bc503c2a 191function makeRequestsToWinningPods (cert, podsList, callback) {
89d1d8ba 192 // Stop pool requests
e3647ae2 193 requestsScheduler.deactivate()
89d1d8ba 194 // Flush pool requests
e3647ae2 195 requestsScheduler.forceSend()
89d1d8ba
C
196
197 // Get the list of our videos to send to our new friends
bc503c2a 198 Videos.listOwned(function (err, videosList) {
89d1d8ba
C
199 if (err) {
200 logger.error('Cannot get the list of videos we own.')
201 return callback(err)
202 }
203
f0f5567b 204 const data = {
89d1d8ba
C
205 url: http + '://' + host + ':' + port,
206 publicKey: cert,
bc503c2a 207 videos: videosList
89d1d8ba
C
208 }
209
210 requests.makeMultipleRetryRequest(
211 { method: 'POST', path: '/api/' + constants.API_VERSION + '/pods/', data: data },
212
bc503c2a 213 podsList,
89d1d8ba 214
e7ea2817 215 // Callback called after each request
bc503c2a 216 function eachRequest (err, response, body, url, pod, callbackEachRequest) {
89d1d8ba
C
217 // We add the pod if it responded correctly with its public certificate
218 if (!err && response.statusCode === 200) {
219 Pods.add({ url: pod.url, publicKey: body.cert, score: constants.FRIEND_BASE_SCORE }, function (err) {
220 if (err) {
221 logger.error('Error with adding %s pod.', pod.url, { error: err })
bc503c2a 222 return callbackEachRequest()
89d1d8ba 223 }
bc503c2a 224
cbe2f7c3 225 videos.createRemoteVideos(body.videos, function (err) {
89d1d8ba
C
226 if (err) {
227 logger.error('Error with adding videos of pod.', pod.url, { error: err })
bc503c2a 228 return callbackEachRequest()
89d1d8ba
C
229 }
230
231 logger.debug('Adding remote videos from %s.', pod.url, { videos: body.videos })
bc503c2a 232 return callbackEachRequest()
89d1d8ba
C
233 })
234 })
235 } else {
236 logger.error('Error with adding %s pod.', pod.url, { error: err || new Error('Status not 200') })
bc503c2a 237 return callbackEachRequest()
89d1d8ba
C
238 }
239 },
240
e7ea2817 241 // Final callback, we've ended all the requests
89d1d8ba
C
242 function endRequests (err) {
243 // Now we made new friends, we can re activate the pool of requests
e3647ae2 244 requestsScheduler.activate()
89d1d8ba
C
245
246 if (err) {
247 logger.error('There was some errors when we wanted to make friends.')
248 return callback(err)
249 }
250
251 logger.debug('makeRequestsToWinningPods finished.')
252 return callback(null)
253 }
254 )
255 })
256}