From bee0abffff73804d816b90c7fd599e0a51c09d61 Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Tue, 28 Aug 2018 02:01:35 -0500 Subject: 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 --- server/models/account/user.ts | 68 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 9 deletions(-) (limited to 'server/models') 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 { isUserPasswordValid, isUserRoleValid, isUserUsernameValid, - isUserVideoQuotaValid + isUserVideoQuotaValid, + isUserVideoQuotaDailyValid } from '../../helpers/custom-validators/users' import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' import { OAuthTokenModel } from '../oauth/oauth-token' @@ -124,6 +125,11 @@ export class UserModel extends Model { @Column(DataType.BIGINT) videoQuota: number + @AllowNull(false) + @Is('UserVideoQuotaDaily', value => throwIfNotValid(value, isUserVideoQuotaDailyValid, 'video quota daily')) + @Column(DataType.BIGINT) + videoQuotaDaily: number + @CreatedAt createdAt: Date @@ -271,7 +277,32 @@ export class UserModel extends Model { 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' + 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + - 'WHERE "account"."userId" = $userId GROUP BY "video"."id") t' + 'WHERE "account"."userId" = $userId ' + + 'GROUP BY "video"."id") t' + + const options = { + bind: { userId: user.id }, + type: Sequelize.QueryTypes.SELECT + } + return UserModel.sequelize.query(query, options) + .then(([ { total } ]) => { + if (total === null) return 0 + + return parseInt(total, 10) + }) + } + + // Returns comulative size of all video files uploaded in the last 24 hours. + static getOriginalVideoFileTotalDailyFromUser (user: UserModel) { + // Don't use sequelize because we need to use a sub query + const query = 'SELECT SUM("size") AS "total" FROM ' + + '(SELECT MAX("videoFile"."size") AS "size" FROM "videoFile" ' + + 'INNER JOIN "video" ON "videoFile"."videoId" = "video"."id" ' + + 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + + 'INNER JOIN "account" ON "videoChannel"."accountId" = "account"."id" ' + + 'WHERE "account"."userId" = $userId ' + + 'AND "video"."createdAt" > now() - interval \'24 hours\'' + + 'GROUP BY "video"."id") t' const options = { bind: { userId: user.id }, @@ -303,6 +334,7 @@ export class UserModel extends Model { toFormattedJSON (): User { const videoQuotaUsed = this.get('videoQuotaUsed') + const videoQuotaUsedDaily = this.get('videoQuotaUsedDaily') const json = { id: this.id, @@ -313,12 +345,18 @@ export class UserModel extends Model { role: this.role, roleLabel: USER_ROLE_LABELS[ this.role ], videoQuota: this.videoQuota, + videoQuotaDaily: this.videoQuotaDaily, createdAt: this.createdAt, blocked: this.blocked, blockedReason: this.blockedReason, account: this.Account.toFormattedJSON(), videoChannels: [], - videoQuotaUsed: videoQuotaUsed !== undefined ? parseInt(videoQuotaUsed, 10) : undefined + videoQuotaUsed: videoQuotaUsed !== undefined + ? parseInt(videoQuotaUsed, 10) + : undefined, + videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined + ? parseInt(videoQuotaUsedDaily, 10) + : undefined } if (Array.isArray(this.Account.VideoChannels) === true) { @@ -335,12 +373,24 @@ export class UserModel extends Model { return json } - isAbleToUploadVideo (videoFile: { size: number }) { - if (this.videoQuota === -1) return Promise.resolve(true) + async isAbleToUploadVideo (videoFile: { size: number }) { + if (this.videoQuota === -1 && this.videoQuotaDaily === -1) return Promise.resolve(true) - return UserModel.getOriginalVideoFileTotalFromUser(this) - .then(totalBytes => { - return (videoFile.size + totalBytes) < this.videoQuota - }) + const [ totalBytes, totalBytesDaily ] = await Promise.all([ + UserModel.getOriginalVideoFileTotalFromUser(this), + UserModel.getOriginalVideoFileTotalDailyFromUser(this) + ]) + + const uploadedTotal = videoFile.size + totalBytes + const uploadedDaily = videoFile.size + totalBytesDaily + if (this.videoQuotaDaily === -1) { + return uploadedTotal < this.videoQuota + } + if (this.videoQuota === -1) { + return uploadedDaily < this.videoQuotaDaily + } + + return (uploadedTotal < this.videoQuota) && + (uploadedDaily < this.videoQuotaDaily) } } -- cgit v1.2.3