Server: fix migration scripts
[oweals/peertube.git] / server / models / request.js
1 'use strict'
2
3 const each = require('async/each')
4 const waterfall = require('async/waterfall')
5 const values = require('lodash/values')
6
7 const constants = require('../initializers/constants')
8 const logger = require('../helpers/logger')
9
10 // ---------------------------------------------------------------------------
11
12 module.exports = function (sequelize, DataTypes) {
13   const Request = sequelize.define('Request',
14     {
15       request: {
16         type: DataTypes.JSON,
17         allowNull: false
18       },
19       endpoint: {
20         type: DataTypes.ENUM(values(constants.REQUEST_ENDPOINTS)),
21         allowNull: false
22       }
23     },
24     {
25       classMethods: {
26         associate,
27
28         listWithLimitAndRandom,
29
30         countTotalRequests,
31         removeBadPods,
32         updatePodsScore,
33         removeAll,
34         removeWithEmptyTo
35       }
36     }
37   )
38
39   return Request
40 }
41
42 // ------------------------------ STATICS ------------------------------
43
44 function associate (models) {
45   this.belongsToMany(models.Pod, {
46     foreignKey: {
47       name: 'requestId',
48       allowNull: false
49     },
50     through: models.RequestToPod,
51     onDelete: 'CASCADE'
52   })
53 }
54
55 function countTotalRequests (callback) {
56   const query = {
57     include: [ this.sequelize.models.Pod ]
58   }
59
60   return this.count(query).asCallback(callback)
61 }
62
63 // Remove pods with a score of 0 (too many requests where they were unreachable)
64 function removeBadPods () {
65   const self = this
66
67   waterfall([
68     function findBadPods (callback) {
69       self.sequelize.models.Pod.listBadPods(function (err, pods) {
70         if (err) {
71           logger.error('Cannot find bad pods.', { error: err })
72           return callback(err)
73         }
74
75         return callback(null, pods)
76       })
77     },
78
79     function removeTheseBadPods (pods, callback) {
80       each(pods, function (pod, callbackEach) {
81         pod.destroy().asCallback(callbackEach)
82       }, function (err) {
83         return callback(err, pods.length)
84       })
85     }
86   ], function (err, numberOfPodsRemoved) {
87     if (err) {
88       logger.error('Cannot remove bad pods.', { error: err })
89     } else if (numberOfPodsRemoved) {
90       logger.info('Removed %d pods.', numberOfPodsRemoved)
91     } else {
92       logger.info('No need to remove bad pods.')
93     }
94   })
95 }
96
97 function updatePodsScore (goodPods, badPods) {
98   const self = this
99   const Pod = this.sequelize.models.Pod
100
101   logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
102
103   if (goodPods.length !== 0) {
104     Pod.incrementScores(goodPods, constants.PODS_SCORE.BONUS, function (err) {
105       if (err) logger.error('Cannot increment scores of good pods.', { error: err })
106     })
107   }
108
109   if (badPods.length !== 0) {
110     Pod.incrementScores(badPods, constants.PODS_SCORE.MALUS, function (err) {
111       if (err) logger.error('Cannot decrement scores of bad pods.', { error: err })
112       removeBadPods.call(self)
113     })
114   }
115 }
116
117 function listWithLimitAndRandom (limitPods, limitRequestsPerPod, callback) {
118   const self = this
119   const Pod = this.sequelize.models.Pod
120
121   Pod.listRandomPodIdsWithRequest(limitPods, function (err, podIds) {
122     if (err) return callback(err)
123
124     // We don't have friends that have requests
125     if (podIds.length === 0) return callback(null, [])
126
127     // The the first x requests of these pods
128     // It is very important to sort by id ASC to keep the requests order!
129     const query = {
130       order: [
131         [ 'id', 'ASC' ]
132       ],
133       include: [
134         {
135           model: self.sequelize.models.Pod,
136           where: {
137             id: {
138               $in: podIds
139             }
140           }
141         }
142       ]
143     }
144
145     self.findAll(query).asCallback(function (err, requests) {
146       if (err) return callback(err)
147
148       const requestsGrouped = groupAndTruncateRequests(requests, limitRequestsPerPod)
149       return callback(err, requestsGrouped)
150     })
151   })
152 }
153
154 function removeAll (callback) {
155   // Delete all requests
156   this.truncate({ cascade: true }).asCallback(callback)
157 }
158
159 function removeWithEmptyTo (callback) {
160   if (!callback) callback = function () {}
161
162   const query = {
163     where: {
164       id: {
165         $notIn: [
166           this.sequelize.literal('SELECT "requestId" FROM "RequestToPods"')
167         ]
168       }
169     }
170   }
171
172   this.destroy(query).asCallback(callback)
173 }
174
175 // ---------------------------------------------------------------------------
176
177 function groupAndTruncateRequests (requests, limitRequestsPerPod) {
178   const requestsGrouped = {}
179
180   requests.forEach(function (request) {
181     request.Pods.forEach(function (pod) {
182       if (!requestsGrouped[pod.id]) requestsGrouped[pod.id] = []
183
184       if (requestsGrouped[pod.id].length < limitRequestsPerPod) {
185         requestsGrouped[pod.id].push({
186           request,
187           pod
188         })
189       }
190     })
191   })
192
193   return requestsGrouped
194 }