Fix quota inconstistencies with lives
This commit is contained in:
parent
b003d57518
commit
9a82ce2455
|
@ -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 })
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue