diff options
author | Felix Ableitner <me@nutomic.com> | 2018-08-28 02:01:35 -0500 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-08-28 09:01:35 +0200 |
commit | bee0abffff73804d816b90c7fd599e0a51c09d61 (patch) | |
tree | fae6d58637f9c63a3800090277f8e130b43442dd /server/models/account/user.ts | |
parent | c907c2fa3fd7c0a741117a0204d0ebca675124bd (diff) | |
download | PeerTube-bee0abffff73804d816b90c7fd599e0a51c09d61.tar.gz PeerTube-bee0abffff73804d816b90c7fd599e0a51c09d61.tar.zst PeerTube-bee0abffff73804d816b90c7fd599e0a51c09d61.zip |
Implement daily upload limit (#956)
* Implement daily upload limit (ref #652)
* remove duplicate code
* review fixes
* fix tests?
* whitespace fixes, finish leftover todo
* fix tests
* added some new tests
* use different config value for tests
* remove todo
Diffstat (limited to 'server/models/account/user.ts')
-rw-r--r-- | server/models/account/user.ts | 68 |
1 files changed, 59 insertions, 9 deletions
diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 0150df4ce..178012eae 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts | |||
@@ -27,7 +27,8 @@ import { | |||
27 | isUserPasswordValid, | 27 | isUserPasswordValid, |
28 | isUserRoleValid, | 28 | isUserRoleValid, |
29 | isUserUsernameValid, | 29 | isUserUsernameValid, |
30 | isUserVideoQuotaValid | 30 | isUserVideoQuotaValid, |
31 | isUserVideoQuotaDailyValid | ||
31 | } from '../../helpers/custom-validators/users' | 32 | } from '../../helpers/custom-validators/users' |
32 | import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' | 33 | import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' |
33 | import { OAuthTokenModel } from '../oauth/oauth-token' | 34 | import { OAuthTokenModel } from '../oauth/oauth-token' |
@@ -124,6 +125,11 @@ export class UserModel extends Model<UserModel> { | |||
124 | @Column(DataType.BIGINT) | 125 | @Column(DataType.BIGINT) |
125 | videoQuota: number | 126 | videoQuota: number |
126 | 127 | ||
128 | @AllowNull(false) | ||
129 | @Is('UserVideoQuotaDaily', value => throwIfNotValid(value, isUserVideoQuotaDailyValid, 'video quota daily')) | ||
130 | @Column(DataType.BIGINT) | ||
131 | videoQuotaDaily: number | ||
132 | |||
127 | @CreatedAt | 133 | @CreatedAt |
128 | createdAt: Date | 134 | createdAt: Date |
129 | 135 | ||
@@ -271,7 +277,32 @@ export class UserModel extends Model<UserModel> { | |||
271 | 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' + | 277 | 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' + |
272 | 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + | 278 | 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + |
273 | 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + | 279 | 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + |
274 | 'WHERE "account"."userId" = $userId GROUP BY "video"."id") t' | 280 | 'WHERE "account"."userId" = $userId ' + |
281 | 'GROUP BY "video"."id") t' | ||
282 | |||
283 | const options = { | ||
284 | bind: { userId: user.id }, | ||
285 | type: Sequelize.QueryTypes.SELECT | ||
286 | } | ||
287 | return UserModel.sequelize.query(query, options) | ||
288 | .then(([ { total } ]) => { | ||
289 | if (total === null) return 0 | ||
290 | |||
291 | return parseInt(total, 10) | ||
292 | }) | ||
293 | } | ||
294 | |||
295 | // Returns comulative size of all video files uploaded in the last 24 hours. | ||
296 | static getOriginalVideoFileTotalDailyFromUser (user: UserModel) { | ||
297 | // Don't use sequelize because we need to use a sub query | ||
298 | const query = 'SELECT SUM("size") AS "total" FROM ' + | ||
299 | '(SELECT MAX("videoFile"."size") AS "size" FROM "videoFile" ' + | ||
300 | 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' + | ||
301 | 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + | ||
302 | 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + | ||
303 | 'WHERE "account"."userId" = $userId ' + | ||
304 | 'AND "video"."createdAt" > now() - interval \'24 hours\'' + | ||
305 | 'GROUP BY "video"."id") t' | ||
275 | 306 | ||
276 | const options = { | 307 | const options = { |
277 | bind: { userId: user.id }, | 308 | bind: { userId: user.id }, |
@@ -303,6 +334,7 @@ export class UserModel extends Model<UserModel> { | |||
303 | 334 | ||
304 | toFormattedJSON (): User { | 335 | toFormattedJSON (): User { |
305 | const videoQuotaUsed = this.get('videoQuotaUsed') | 336 | const videoQuotaUsed = this.get('videoQuotaUsed') |
337 | const videoQuotaUsedDaily = this.get('videoQuotaUsedDaily') | ||
306 | 338 | ||
307 | const json = { | 339 | const json = { |
308 | id: this.id, | 340 | id: this.id, |
@@ -313,12 +345,18 @@ export class UserModel extends Model<UserModel> { | |||
313 | role: this.role, | 345 | role: this.role, |
314 | roleLabel: USER_ROLE_LABELS[ this.role ], | 346 | roleLabel: USER_ROLE_LABELS[ this.role ], |
315 | videoQuota: this.videoQuota, | 347 | videoQuota: this.videoQuota, |
348 | videoQuotaDaily: this.videoQuotaDaily, | ||
316 | createdAt: this.createdAt, | 349 | createdAt: this.createdAt, |
317 | blocked: this.blocked, | 350 | blocked: this.blocked, |
318 | blockedReason: this.blockedReason, | 351 | blockedReason: this.blockedReason, |
319 | account: this.Account.toFormattedJSON(), | 352 | account: this.Account.toFormattedJSON(), |
320 | videoChannels: [], | 353 | videoChannels: [], |
321 | videoQuotaUsed: videoQuotaUsed !== undefined ? parseInt(videoQuotaUsed, 10) : undefined | 354 | videoQuotaUsed: videoQuotaUsed !== undefined |
355 | ? parseInt(videoQuotaUsed, 10) | ||
356 | : undefined, | ||
357 | videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined | ||
358 | ? parseInt(videoQuotaUsedDaily, 10) | ||
359 | : undefined | ||
322 | } | 360 | } |
323 | 361 | ||
324 | if (Array.isArray(this.Account.VideoChannels) === true) { | 362 | if (Array.isArray(this.Account.VideoChannels) === true) { |
@@ -335,12 +373,24 @@ export class UserModel extends Model<UserModel> { | |||
335 | return json | 373 | return json |
336 | } | 374 | } |
337 | 375 | ||
338 | isAbleToUploadVideo (videoFile: { size: number }) { | 376 | async isAbleToUploadVideo (videoFile: { size: number }) { |
339 | if (this.videoQuota === -1) return Promise.resolve(true) | 377 | if (this.videoQuota === -1 && this.videoQuotaDaily === -1) return Promise.resolve(true) |
340 | 378 | ||
341 | return UserModel.getOriginalVideoFileTotalFromUser(this) | 379 | const [ totalBytes, totalBytesDaily ] = await Promise.all([ |
342 | .then(totalBytes => { | 380 | UserModel.getOriginalVideoFileTotalFromUser(this), |
343 | return (videoFile.size + totalBytes) < this.videoQuota | 381 | UserModel.getOriginalVideoFileTotalDailyFromUser(this) |
344 | }) | 382 | ]) |
383 | |||
384 | const uploadedTotal = videoFile.size + totalBytes | ||
385 | const uploadedDaily = videoFile.size + totalBytesDaily | ||
386 | if (this.videoQuotaDaily === -1) { | ||
387 | return uploadedTotal < this.videoQuota | ||
388 | } | ||
389 | if (this.videoQuota === -1) { | ||
390 | return uploadedDaily < this.videoQuotaDaily | ||
391 | } | ||
392 | |||
393 | return (uploadedTotal < this.videoQuota) && | ||
394 | (uploadedDaily < this.videoQuotaDaily) | ||
345 | } | 395 | } |
346 | } | 396 | } |