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