Fix quota inconstistencies with lives

This commit is contained in:
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 }) => { muxingSession.on('after-cleanup', ({ videoId }) => {
this.muxingSessions.delete(sessionId) this.muxingSessions.delete(sessionId)
LiveQuotaStore.Instance.removeLive(user.id, videoLive.id)
muxingSession.destroy() muxingSession.destroy()
return this.onAfterMuxingCleanup({ videoId, liveSession }) 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 // Don't use sequelize because we need to use a sub query
const query = UserModel.generateUserQuotaBaseSQL({ const query = UserModel.generateUserQuotaBaseSQL({
withSelect: true, withSelect: true,
whereUserId: '$userId' whereUserId: '$userId',
daily: false
}) })
const base = await UserModel.getTotalRawQuery(query, user.id) const base = await UserModel.getTotalRawQuery(query, user.id)
@ -187,7 +188,7 @@ async function getOriginalVideoFileTotalDailyFromUser (user: MUserId) {
const query = UserModel.generateUserQuotaBaseSQL({ const query = UserModel.generateUserQuotaBaseSQL({
withSelect: true, withSelect: true,
whereUserId: '$userId', whereUserId: '$userId',
where: '"video"."createdAt" > now() - interval \'24 hours\'' daily: true
}) })
const base = await UserModel.getTotalRawQuery(query, user.id) 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 { VideoLiveModel } from '../video/video-live'
import { VideoPlaylistModel } from '../video/video-playlist' import { VideoPlaylistModel } from '../video/video-playlist'
import { UserNotificationSettingModel } from './user-notification-setting' import { UserNotificationSettingModel } from './user-notification-setting'
import { LiveQuotaStore } from '@server/lib/live'
import { logger } from '@server/helpers/logger'
enum ScopeNames { enum ScopeNames {
FOR_ME_API = 'FOR_ME_API', FOR_ME_API = 'FOR_ME_API',
WITH_VIDEOCHANNELS = 'WITH_VIDEOCHANNELS', WITH_VIDEOCHANNELS = 'WITH_VIDEOCHANNELS',
WITH_QUOTA = 'WITH_QUOTA',
WITH_STATS = 'WITH_STATS' WITH_STATS = 'WITH_STATS'
} }
@ -153,7 +156,7 @@ enum ScopeNames {
} }
] ]
}, },
[ScopeNames.WITH_STATS]: { [ScopeNames.WITH_QUOTA]: {
attributes: { attributes: {
include: [ include: [
[ [
@ -161,12 +164,31 @@ enum ScopeNames {
'(' + '(' +
UserModel.generateUserQuotaBaseSQL({ UserModel.generateUserQuotaBaseSQL({
withSelect: false, withSelect: false,
whereUserId: '"UserModel"."id"' whereUserId: '"UserModel"."id"',
daily: false
}) + }) +
')' ')'
), ),
'videoQuotaUsed' 'videoQuotaUsed'
], ],
[
literal(
'(' +
UserModel.generateUserQuotaBaseSQL({
withSelect: false,
whereUserId: '"UserModel"."id"',
daily: true
}) +
')'
),
'videoQuotaUsedDaily'
]
]
}
},
[ScopeNames.WITH_STATS]: {
attributes: {
include: [
[ [
literal( literal(
'(' + '(' +
@ -474,21 +496,6 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
} }
const query: FindOptions = { const query: FindOptions = {
attributes: {
include: [
[
literal(
'(' +
UserModel.generateUserQuotaBaseSQL({
withSelect: false,
whereUserId: '"UserModel"."id"'
}) +
')'
),
'videoQuotaUsed'
]
]
},
offset: start, offset: start,
limit: count, limit: count,
order: getSort(sort), order: getSort(sort),
@ -497,7 +504,7 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
return Promise.all([ return Promise.all([
UserModel.unscoped().count(query), UserModel.unscoped().count(query),
UserModel.findAll(query) UserModel.scope([ 'defaultScope', ScopeNames.WITH_QUOTA ]).findAll(query)
]).then(([ total, data ]) => ({ total, data })) ]).then(([ total, data ]) => ({ total, data }))
} }
@ -579,7 +586,10 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
ScopeNames.WITH_VIDEOCHANNELS 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) return UserModel.scope(scopes).findByPk(id)
} }
@ -760,10 +770,10 @@ export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
static generateUserQuotaBaseSQL (options: { static generateUserQuotaBaseSQL (options: {
whereUserId: '$userId' | '"UserModel"."id"' whereUserId: '$userId' | '"UserModel"."id"'
withSelect: boolean withSelect: boolean
where?: string daily: boolean
}) { }) {
const andWhere = options.where const andWhere = options.daily === true
? 'AND ' + options.where ? 'AND "video"."createdAt" > now() - interval \'24 hours\''
: '' : ''
const videoChannelJoin = 'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' + 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, videoQuota: this.videoQuota,
videoQuotaDaily: this.videoQuotaDaily, videoQuotaDaily: this.videoQuotaDaily,
videoQuotaUsed: videoQuotaUsed !== undefined videoQuotaUsed: videoQuotaUsed !== undefined
? parseInt(videoQuotaUsed + '', 10) ? parseInt(videoQuotaUsed + '', 10) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
: undefined, : undefined,
videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined videoQuotaUsedDaily: videoQuotaUsedDaily !== undefined
? parseInt(videoQuotaUsedDaily + '', 10) ? parseInt(videoQuotaUsedDaily + '', 10) + LiveQuotaStore.Instance.getLiveQuotaOf(this.id)
: undefined, : undefined,
videosCount: videosCount !== undefined videosCount: videosCount !== undefined
? parseInt(videosCount + '', 10) ? parseInt(videosCount + '', 10)
: undefined, : undefined,

View File

@ -12,6 +12,7 @@ import {
PeerTubeServer, PeerTubeServer,
setAccessTokensToServers, setAccessTokensToServers,
setDefaultVideoChannel, setDefaultVideoChannel,
stopFfmpeg,
waitJobs, waitJobs,
waitUntilLiveReplacedByReplayOnAllServers, waitUntilLiveReplacedByReplayOnAllServers,
waitUntilLiveWaitingOnAllServers waitUntilLiveWaitingOnAllServers
@ -169,6 +170,30 @@ describe('Test live constraints', function () {
await servers[0].live.runAndTestStreamError({ token: userAccessToken, videoId: userVideoLiveoId, shouldHaveError: false }) 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 () { it('Should have max duration limit', async function () {
this.timeout(60000) this.timeout(60000)