]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/pod.ts
require -> import
[github/Chocobozzz/PeerTube.git] / server / models / pod.ts
CommitLineData
65fcc311
C
1import { each, waterfall } from 'async'
2import { map } from 'lodash'
e02643f3 3import * as Sequelize from 'sequelize'
9f10b292 4
65fcc311
C
5import { FRIEND_SCORE, PODS_SCORE } from '../initializers'
6import { logger, isHostValid } from '../helpers'
9f10b292 7
e02643f3
C
8import { addMethodsToModel } from './utils'
9import {
10 PodClass,
11 PodInstance,
12 PodAttributes,
13
14 PodMethods
15} from './pod-interface'
16
17let Pod: Sequelize.Model<PodInstance, PodAttributes>
18let toFormatedJSON: PodMethods.ToFormatedJSON
19let countAll: PodMethods.CountAll
20let incrementScores: PodMethods.IncrementScores
21let list: PodMethods.List
22let listAllIds: PodMethods.ListAllIds
23let listRandomPodIdsWithRequest: PodMethods.ListRandomPodIdsWithRequest
24let listBadPods: PodMethods.ListBadPods
25let load: PodMethods.Load
26let loadByHost: PodMethods.LoadByHost
27let removeAll: PodMethods.RemoveAll
28let updatePodsScore: PodMethods.UpdatePodsScore
29
30export default function (sequelize, DataTypes) {
31 Pod = sequelize.define('Pod',
feb4bdfd
C
32 {
33 host: {
67bf9b96
C
34 type: DataTypes.STRING,
35 allowNull: false,
36 validate: {
37 isHost: function (value) {
65fcc311 38 const res = isHostValid(value)
67bf9b96
C
39 if (res === false) throw new Error('Host not valid.')
40 }
41 }
feb4bdfd
C
42 },
43 publicKey: {
67bf9b96
C
44 type: DataTypes.STRING(5000),
45 allowNull: false
feb4bdfd
C
46 },
47 score: {
48 type: DataTypes.INTEGER,
65fcc311 49 defaultValue: FRIEND_SCORE.BASE,
67bf9b96
C
50 allowNull: false,
51 validate: {
52 isInt: true,
65fcc311 53 max: FRIEND_SCORE.MAX
67bf9b96 54 }
4793c343
C
55 },
56 email: {
57 type: DataTypes.STRING(400),
ad4a8a1c
C
58 allowNull: false,
59 validate: {
60 isEmail: true
61 }
feb4bdfd 62 }
feb4bdfd
C
63 },
64 {
319d072e
C
65 indexes: [
66 {
5d67f289
C
67 fields: [ 'host' ],
68 unique: true
319d072e
C
69 },
70 {
71 fields: [ 'score' ]
72 }
e02643f3 73 ]
feb4bdfd
C
74 }
75 )
76
e02643f3
C
77 const classMethods = [
78 associate,
79
80 countAll,
81 incrementScores,
82 list,
83 listAllIds,
84 listRandomPodIdsWithRequest,
85 listBadPods,
86 load,
87 loadByHost,
88 updatePodsScore,
89 removeAll
90 ]
91 const instanceMethods = [ toFormatedJSON ]
92 addMethodsToModel(Pod, classMethods, instanceMethods)
93
feb4bdfd 94 return Pod
53572423
C
95}
96
53572423
C
97// ------------------------------ METHODS ------------------------------
98
e02643f3 99toFormatedJSON = function () {
53572423 100 const json = {
feb4bdfd 101 id: this.id,
49abbbbe 102 host: this.host,
4793c343 103 email: this.email,
53572423 104 score: this.score,
feb4bdfd 105 createdAt: this.createdAt
53572423
C
106 }
107
108 return json
109}
110
a3ee6fa2
C
111// ------------------------------ Statics ------------------------------
112
feb4bdfd 113function associate (models) {
e02643f3 114 Pod.belongsToMany(models.Request, {
feb4bdfd
C
115 foreignKey: 'podId',
116 through: models.RequestToPod,
7920c273 117 onDelete: 'cascade'
feb4bdfd
C
118 })
119}
120
e02643f3
C
121countAll = function (callback) {
122 return Pod.count().asCallback(callback)
9f10b292 123}
45239549 124
e02643f3 125incrementScores = function (ids, value, callback) {
65fcc311 126 if (!callback) callback = function () { /* empty */ }
feb4bdfd
C
127
128 const update = {
e02643f3 129 score: Sequelize.literal('score +' + value)
feb4bdfd
C
130 }
131
67bf9b96 132 const options = {
feb4bdfd
C
133 where: {
134 id: {
135 $in: ids
136 }
67bf9b96
C
137 },
138 // In this case score is a literal and not an integer so we do not validate it
139 validate: false
feb4bdfd
C
140 }
141
e02643f3 142 return Pod.update(update, options).asCallback(callback)
9f10b292 143}
45239549 144
e02643f3
C
145list = function (callback) {
146 return Pod.findAll().asCallback(callback)
9f10b292 147}
8c308c2b 148
e02643f3 149listAllIds = function (transaction, callback) {
ed04d94f
C
150 if (!callback) {
151 callback = transaction
152 transaction = null
153 }
154
65fcc311 155 const query: any = {
feb4bdfd
C
156 attributes: [ 'id' ]
157 }
158
ed04d94f
C
159 if (transaction) query.transaction = transaction
160
e02643f3 161 return Pod.findAll(query).asCallback(function (err, pods) {
00057e85
C
162 if (err) return callback(err)
163
feb4bdfd 164 return callback(null, map(pods, 'id'))
00057e85 165 })
528a9efa
C
166}
167
e02643f3 168listRandomPodIdsWithRequest = function (limit, tableWithPods, tableWithPodsJoins, callback) {
e4c87ec2
C
169 if (!callback) {
170 callback = tableWithPodsJoins
171 tableWithPodsJoins = ''
172 }
173
e02643f3 174 Pod.count().asCallback(function (err, count) {
bd14d16a
C
175 if (err) return callback(err)
176
177 // Optimization...
178 if (count === 0) return callback(null, [])
179
180 let start = Math.floor(Math.random() * count) - limit
181 if (start < 0) start = 0
182
183 const query = {
184 attributes: [ 'id' ],
185 order: [
186 [ 'id', 'ASC' ]
187 ],
188 offset: start,
189 limit: limit,
190 where: {
191 id: {
192 $in: [
e02643f3 193 Sequelize.literal(`SELECT DISTINCT "${tableWithPods}"."podId" FROM "${tableWithPods}" ${tableWithPodsJoins}`)
bd14d16a
C
194 ]
195 }
196 }
197 }
198
e02643f3 199 return Pod.findAll(query).asCallback(function (err, pods) {
bd14d16a
C
200 if (err) return callback(err)
201
202 return callback(null, map(pods, 'id'))
203 })
204 })
205}
206
e02643f3 207listBadPods = function (callback) {
feb4bdfd
C
208 const query = {
209 where: {
210 score: { $lte: 0 }
211 }
212 }
213
e02643f3 214 return Pod.findAll(query).asCallback(callback)
9f10b292 215}
8c308c2b 216
e02643f3
C
217load = function (id, callback) {
218 return Pod.findById(id).asCallback(callback)
9f10b292 219}
c45f7f84 220
e02643f3 221loadByHost = function (host, callback) {
feb4bdfd
C
222 const query = {
223 where: {
224 host: host
225 }
226 }
227
e02643f3 228 return Pod.findOne(query).asCallback(callback)
9f10b292 229}
c45f7f84 230
e02643f3
C
231removeAll = function (callback) {
232 return Pod.destroy().asCallback(callback)
a3ee6fa2 233}
9e167724 234
e02643f3 235updatePodsScore = function (goodPods, badPods) {
9e167724
C
236 logger.info('Updating %d good pods and %d bad pods scores.', goodPods.length, badPods.length)
237
238 if (goodPods.length !== 0) {
e02643f3 239 incrementScores(goodPods, PODS_SCORE.BONUS, function (err) {
9e167724
C
240 if (err) logger.error('Cannot increment scores of good pods.', { error: err })
241 })
242 }
243
244 if (badPods.length !== 0) {
e02643f3 245 incrementScores(badPods, PODS_SCORE.MALUS, function (err) {
9e167724 246 if (err) logger.error('Cannot decrement scores of bad pods.', { error: err })
e02643f3 247 removeBadPods()
9e167724
C
248 })
249 }
250}
251
252// ---------------------------------------------------------------------------
253
254// Remove pods with a score of 0 (too many requests where they were unreachable)
255function removeBadPods () {
9e167724
C
256 waterfall([
257 function findBadPods (callback) {
e02643f3 258 listBadPods(function (err, pods) {
9e167724
C
259 if (err) {
260 logger.error('Cannot find bad pods.', { error: err })
261 return callback(err)
262 }
263
264 return callback(null, pods)
265 })
266 },
267
268 function removeTheseBadPods (pods, callback) {
65fcc311 269 each(pods, function (pod: any, callbackEach) {
9e167724
C
270 pod.destroy().asCallback(callbackEach)
271 }, function (err) {
272 return callback(err, pods.length)
273 })
274 }
275 ], function (err, numberOfPodsRemoved) {
276 if (err) {
277 logger.error('Cannot remove bad pods.', { error: err })
278 } else if (numberOfPodsRemoved) {
279 logger.info('Removed %d pods.', numberOfPodsRemoved)
280 } else {
281 logger.info('No need to remove bad pods.')
282 }
283 })
284}