Fix quota inconstistencies with lives

pull/4977/head
Chocobozzz 2022-05-04 10:07:06 +02:00
parent b003d57518
commit 9a82ce2455
No known key found for this signature in database
GPG Key ID: 583A612D890159BE
4 changed files with 67 additions and 26 deletions

View File

@ -331,6 +331,8 @@ class LiveManager {
muxingSession.on('after-cleanup', ({ videoId }) => {
this.muxingSessions.delete(sessionId)
LiveQuotaStore.Instance.removeLive(user.id, videoLive.id)
muxingSession.destroy()
return this.onAfterMuxingCleanup({ videoId, liveSession })

View File

@ -173,7 +173,8 @@ async function getOriginalVideoFileTotalFromUser (user: MUserId) {
// Don't use sequelize because we need to use a sub query
const query = UserModel.generateUserQuotaBaseSQL({
withSelect: true,
whereUserId: '$userId'
whereUserId: '$userId',
daily: false
})
const base = await UserModel.getTotalRawQuery(query, user.id)
@ -187,7 +188,7 @@ async function getOriginalVideoFileTotalDailyFromUser (user: MUserId) {
const query = UserModel.generateUserQuotaBaseSQL({
withSelect: true,
whereUserId: '$userId',
where: '"video"."createdAt" > now() - interval \'24 hours\''
daily: true
})
const base = await UserModel.getTotalRawQuery(query, user.id)

View File

@ -72,10 +72,13 @@ import { VideoImportModel } from '../video/video-import'
import { VideoLiveModel } from '../video/video-live'
import { VideoPlaylistModel } from '../video/video-playlist'
import { UserNotificationSettingModel } from './user-notification-setting'
import { LiveQuotaStore } from '@server/lib/live'
import { logger } from '@server/helpers/logger'
enum ScopeNames {
FOR_ME_API = 'FOR_ME_API',
WITH_VIDEOCHANNELS = 'WITH_VIDEOCHANNELS',
WITH_QUOTA = 'WITH_QUOTA',
WITH_STATS = 'WITH_STATS'
}
@ -153,7 +156,7 @@ enum ScopeNames {
}
]
},
[ScopeNames.WITH_STATS]: {
[ScopeNames.WITH_QUOTA]: {
attributes: {
include: [
[
@ -161,12 +164,31 @@ enum ScopeNames {
'(' +
UserModel.generateUserQuotaBaseSQL({
withSelect: false,
whereUserId: '"UserModel"."id"'
whereUserId: '"UserModel"."id"',
daily: false
}) +
')'
),
'videoQuotaUsed'
],
[
literal(
'(' +
UserModel.generateUserQuotaBaseSQL({
withSelect: false,
whereUserId: '"UserModel"."id"',
daily: true
}) +
')'
),
'videoQuotaUsedDaily'
]
]
}
},
[ScopeNames.WITH_STATS]: {
attributes: {
include: [
[
literal(
'(' +
@ -474,21 +496,6 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
}
const query: FindOptions = {
attributes: {
include: [
[
literal(
'(' +
UserModel.generateUserQuotaBaseSQL({
withSelect: false,
whereUserId: '"UserModel"."id"'
}) +
')'
),
'videoQuotaUsed'
]
]
},
offset: start,
limit: count,
order: getSort(sort),
@ -497,7 +504,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
return Promise.all([
UserModel.unscoped().count(query),
UserModel.findAll(query)
UserModel.scope([ 'defaultScope', ScopeNames.WITH_QUOTA ]).findAll(query)
]).then(([ total, data ]) => ({ total, data }))
}
@ -579,7 +586,10 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
ScopeNames.WITH_VIDEOCHANNELS
]
if (withStats) scopes.push(ScopeNames.WITH_STATS)
if (withStats) {
scopes.push(ScopeNames.WITH_QUOTA)
scopes.push(ScopeNames.WITH_STATS)
}
return UserModel.scope(scopes).findByPk(id)
}
@ -760,10 +770,10 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
static generateUserQuotaBaseSQL (options: {
whereUserId: '$userId' | '"UserModel"."id"'
withSelect: boolean
where?: string
daily: boolean
}) {
const andWhere = options.where
? 'AND ' + options.where
const andWhere = options.daily === true
? 'AND "video"."createdAt" > now() - interval \'24 hours\''
: ''
const videoChannelJoin = 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' +
@ -904,12 +914,15 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
videoQuota: this.videoQuota,
videoQuotaDaily: this.videoQuotaDaily,
videoQuotaUsed: videoQuotaUsed !== undefined
? parseInt(videoQuotaUsed + '', 10)
? parseInt(videoQuotaUsed + '', 10) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
: undefined,
videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined
? parseInt(videoQuotaUsedDaily + '', 10)
? parseInt(videoQuotaUsedDaily + '', 10) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
: undefined,
videosCount: videosCount !== undefined
? parseInt(videosCount + '', 10)
: undefined,

View File

@ -12,6 +12,7 @@ import {
PeerTubeServer,
setAccessTokensToServers,
setDefaultVideoChannel,
stopFfmpeg,
waitJobs,
waitUntilLiveReplacedByReplayOnAllServers,
waitUntilLiveWaitingOnAllServers
@ -169,6 +170,30 @@ describe('Test live constraints', function () {
await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: false })
})
it('Should have the same quota in admin and as a user', async function () {
this.timeout(120000)
const userVideoLiveoId = await createLiveWrapper({ replay: true, permanent: false })
const ffmpegCommand = await servers[0].live.sendRTMPStreamInVideo({ token: userAccessToken, videoId: userVideoLiveoId })
await servers[0].live.waitUntilPublished({ videoId: userVideoLiveoId })
await wait(3000)
const quotaUser = await servers[0].users.getMyQuotaUsed({ token: userAccessToken })
const { data } = await servers[0].users.list()
const quotaAdmin = data.find(u => u.username === 'user1')
expect(quotaUser.videoQuotaUsed).to.equal(quotaAdmin.videoQuotaUsed)
expect(quotaUser.videoQuotaUsedDaily).to.equal(quotaAdmin.videoQuotaUsedDaily)
expect(quotaUser.videoQuotaUsed).to.be.above(10)
expect(quotaUser.videoQuotaUsedDaily).to.be.above(10)
await stopFfmpeg(ffmpegCommand)
})
it('Should have max duration limit', async function () {
this.timeout(60000)