]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/user/user.ts
Fix quota inconstistencies with lives
[github/Chocobozzz/PeerTube.git] / server / models / user / user.ts
index 8d2564e54626b9fed988ab3f774a81046a566f07..85720abfb59348a2b1547456cb7a383e96f09b8d 100644 (file)
@@ -31,6 +31,7 @@ import {
   MUserWithNotificationSetting,
   MVideoWithRights
 } from '@server/types/models'
+import { AttributesOnly } from '@shared/typescript-utils'
 import { hasUserRight, USER_ROLE_LABELS } from '../../../shared/core-utils/users'
 import { AbuseState, MyUser, UserRight, VideoPlaylistType, VideoPrivacy } from '../../../shared/models'
 import { User, UserRole } from '../../../shared/models/users'
@@ -38,8 +39,6 @@ import { UserAdminFlag } from '../../../shared/models/users/user-flag.model'
 import { NSFWPolicyType } from '../../../shared/models/videos/nsfw-policy.type'
 import { isThemeNameValid } from '../../helpers/custom-validators/plugins'
 import {
-  isNoInstanceConfigWarningModal,
-  isNoWelcomeModal,
   isUserAdminFlagsValid,
   isUserAutoPlayNextVideoPlaylistValid,
   isUserAutoPlayNextVideoValid,
@@ -47,15 +46,16 @@ import {
   isUserBlockedReasonValid,
   isUserBlockedValid,
   isUserEmailVerifiedValid,
+  isUserNoModal,
   isUserNSFWPolicyValid,
+  isUserP2PEnabledValid,
   isUserPasswordValid,
   isUserRoleValid,
   isUserUsernameValid,
   isUserVideoLanguages,
   isUserVideoQuotaDailyValid,
   isUserVideoQuotaValid,
-  isUserVideosHistoryEnabledValid,
-  isUserWebTorrentEnabledValid
+  isUserVideosHistoryEnabledValid
 } from '../../helpers/custom-validators/users'
 import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto'
 import { DEFAULT_USER_THEME_NAME, NSFW_POLICY_TYPES } from '../../initializers/constants'
@@ -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'
 }
 
@@ -106,7 +109,7 @@ enum ScopeNames {
                 include: [
                   {
                     model: ActorImageModel,
-                    as: 'Banner',
+                    as: 'Banners',
                     required: false
                   }
                 ]
@@ -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(
             '(' +
@@ -233,7 +255,7 @@ enum ScopeNames {
     }
   ]
 })
-export class UserModel extends Model {
+export class UserModel extends Model<Partial<AttributesOnly<UserModel>>> {
 
   @AllowNull(true)
   @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true))
@@ -267,10 +289,9 @@ export class UserModel extends Model {
   nsfwPolicy: NSFWPolicyType
 
   @AllowNull(false)
-  @Default(true)
-  @Is('UserWebTorrentEnabled', value => throwIfNotValid(value, isUserWebTorrentEnabledValid, 'WebTorrent enabled'))
+  @Is('p2pEnabled', value => throwIfNotValid(value, isUserP2PEnabledValid, 'P2P enabled'))
   @Column
-  webTorrentEnabled: boolean
+  p2pEnabled: boolean
 
   @AllowNull(false)
   @Default(true)
@@ -348,7 +369,7 @@ export class UserModel extends Model {
   @Default(false)
   @Is(
     'UserNoInstanceConfigWarningModal',
-    value => throwIfNotValid(value, isNoInstanceConfigWarningModal, 'no instance config warning modal')
+    value => throwIfNotValid(value, isUserNoModal, 'no instance config warning modal')
   )
   @Column
   noInstanceConfigWarningModal: boolean
@@ -356,12 +377,21 @@ export class UserModel extends Model {
   @AllowNull(false)
   @Default(false)
   @Is(
-    'UserNoInstanceConfigWarningModal',
-    value => throwIfNotValid(value, isNoWelcomeModal, 'no welcome modal')
+    'UserNoWelcomeModal',
+    value => throwIfNotValid(value, isUserNoModal, 'no welcome modal')
   )
   @Column
   noWelcomeModal: boolean
 
+  @AllowNull(false)
+  @Default(false)
+  @Is(
+    'UserNoAccountSetupWarningModal',
+    value => throwIfNotValid(value, isUserNoModal, 'no account setup warning modal')
+  )
+  @Column
+  noAccountSetupWarningModal: boolean
+
   @AllowNull(true)
   @Default(null)
   @Column
@@ -466,34 +496,16 @@ export class UserModel extends Model {
     }
 
     const query: FindOptions = {
-      attributes: {
-        include: [
-          [
-            literal(
-              '(' +
-                UserModel.generateUserQuotaBaseSQL({
-                  withSelect: false,
-                  whereUserId: '"UserModel"."id"'
-                }) +
-              ')'
-            ),
-            'videoQuotaUsed'
-          ] as any // FIXME: typings
-        ]
-      },
       offset: start,
       limit: count,
       order: getSort(sort),
       where
     }
 
-    return UserModel.findAndCountAll(query)
-                    .then(({ rows, count }) => {
-                      return {
-                        data: rows,
-                        total: count
-                      }
-                    })
+    return Promise.all([
+      UserModel.unscoped().count(query),
+      UserModel.scope([ 'defaultScope', ScopeNames.WITH_QUOTA ]).findAll(query)
+    ]).then(([ total, data ]) => ({ total, data }))
   }
 
   static listWithRight (right: UserRight): Promise<MUserDefault[]> {
@@ -574,7 +586,10 @@ export class UserModel extends Model {
       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)
   }
@@ -615,7 +630,7 @@ export class UserModel extends Model {
     const query = {
       where: {
         [Op.or]: [
-          where(fn('lower', col('username')), fn('lower', username)),
+          where(fn('lower', col('username')), fn('lower', username) as any),
 
           { email }
         ]
@@ -755,10 +770,10 @@ export class UserModel extends Model {
   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" ' +
@@ -883,7 +898,11 @@ export class UserModel extends Model {
       emailVerified: this.emailVerified,
 
       nsfwPolicy: this.nsfwPolicy,
-      webTorrentEnabled: this.webTorrentEnabled,
+
+      // FIXME: deprecated in 4.1
+      webTorrentEnabled: this.p2pEnabled,
+      p2pEnabled: this.p2pEnabled,
+
       videosHistoryEnabled: this.videosHistoryEnabled,
       autoPlayVideo: this.autoPlayVideo,
       autoPlayNextVideo: this.autoPlayNextVideo,
@@ -895,12 +914,15 @@ export class UserModel extends Model {
 
       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,
@@ -919,6 +941,7 @@ export class UserModel extends Model {
 
       noInstanceConfigWarningModal: this.noInstanceConfigWarningModal,
       noWelcomeModal: this.noWelcomeModal,
+      noAccountSetupWarningModal: this.noAccountSetupWarningModal,
 
       blocked: this.blocked,
       blockedReason: this.blockedReason,
@@ -957,7 +980,7 @@ export class UserModel extends Model {
   }
 
   toMeFormattedJSON (this: MMyUserFormattable): MyUser {
-    const formatted = this.toFormattedJSON()
+    const formatted = this.toFormattedJSON({ withAdminFlags: true })
 
     const specialPlaylists = this.Account.VideoPlaylists
                                  .map(p => ({ id: p.id, name: p.name, type: p.type }))