]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/user/user-notification.ts
Fix video job error when video has been deleted
[github/Chocobozzz/PeerTube.git] / server / models / user / user-notification.ts
index a7f84e9cabee0447a7afe57d8f376b8e4548cbe2..6209cb4bfa81ae8f8c4f35a1d40551dbb8088eac 100644 (file)
-import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize'
-import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
+import { ModelIndexesOptions, Op, WhereOptions } from 'sequelize'
+import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
+import { getBiggestActorImage } from '@server/lib/actor-image'
 import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user'
-import { AttributesOnly } from '@shared/core-utils'
-import { UserNotification, UserNotificationType } from '../../../shared'
+import { uuidToShort } from '@shared/extra-utils'
+import { UserNotification, UserNotificationType } from '@shared/models'
+import { AttributesOnly } from '@shared/typescript-utils'
 import { isBooleanValid } from '../../helpers/custom-validators/misc'
 import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications'
 import { AbuseModel } from '../abuse/abuse'
-import { VideoAbuseModel } from '../abuse/video-abuse'
-import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse'
 import { AccountModel } from '../account/account'
-import { ActorModel } from '../actor/actor'
 import { ActorFollowModel } from '../actor/actor-follow'
-import { ActorImageModel } from '../actor/actor-image'
 import { ApplicationModel } from '../application/application'
 import { PluginModel } from '../server/plugin'
-import { ServerModel } from '../server/server'
-import { getSort, throwIfNotValid } from '../utils'
+import { throwIfNotValid } from '../utils'
 import { VideoModel } from '../video/video'
 import { VideoBlacklistModel } from '../video/video-blacklist'
-import { VideoChannelModel } from '../video/video-channel'
 import { VideoCommentModel } from '../video/video-comment'
 import { VideoImportModel } from '../video/video-import'
+import { UserNotificationListQueryBuilder } from './sql/user-notitication-list-query-builder'
 import { UserModel } from './user'
 
-enum ScopeNames {
-  WITH_ALL = 'WITH_ALL'
-}
-
-function buildActorWithAvatarInclude () {
-  return {
-    attributes: [ 'preferredUsername' ],
-    model: ActorModel.unscoped(),
-    required: true,
-    include: [
-      {
-        attributes: [ 'filename' ],
-        as: 'Avatar',
-        model: ActorImageModel.unscoped(),
-        required: false
-      },
-      {
-        attributes: [ 'host' ],
-        model: ServerModel.unscoped(),
-        required: false
-      }
-    ]
-  }
-}
-
-function buildVideoInclude (required: boolean) {
-  return {
-    attributes: [ 'id', 'uuid', 'name' ],
-    model: VideoModel.unscoped(),
-    required
-  }
-}
-
-function buildChannelInclude (required: boolean, withActor = false) {
-  return {
-    required,
-    attributes: [ 'id', 'name' ],
-    model: VideoChannelModel.unscoped(),
-    include: withActor === true ? [ buildActorWithAvatarInclude() ] : []
-  }
-}
-
-function buildAccountInclude (required: boolean, withActor = false) {
-  return {
-    required,
-    attributes: [ 'id', 'name' ],
-    model: AccountModel.unscoped(),
-    include: withActor === true ? [ buildActorWithAvatarInclude() ] : []
-  }
-}
-
-@Scopes(() => ({
-  [ScopeNames.WITH_ALL]: {
-    include: [
-      Object.assign(buildVideoInclude(false), {
-        include: [ buildChannelInclude(true, true) ]
-      }),
-
-      {
-        attributes: [ 'id', 'originCommentId' ],
-        model: VideoCommentModel.unscoped(),
-        required: false,
-        include: [
-          buildAccountInclude(true, true),
-          buildVideoInclude(true)
-        ]
-      },
-
-      {
-        attributes: [ 'id', 'state' ],
-        model: AbuseModel.unscoped(),
-        required: false,
-        include: [
-          {
-            attributes: [ 'id' ],
-            model: VideoAbuseModel.unscoped(),
-            required: false,
-            include: [ buildVideoInclude(false) ]
-          },
-          {
-            attributes: [ 'id' ],
-            model: VideoCommentAbuseModel.unscoped(),
-            required: false,
-            include: [
-              {
-                attributes: [ 'id', 'originCommentId' ],
-                model: VideoCommentModel.unscoped(),
-                required: false,
-                include: [
-                  {
-                    attributes: [ 'id', 'name', 'uuid' ],
-                    model: VideoModel.unscoped(),
-                    required: false
-                  }
-                ]
-              }
-            ]
-          },
-          {
-            model: AccountModel,
-            as: 'FlaggedAccount',
-            required: false,
-            include: [ buildActorWithAvatarInclude() ]
-          }
-        ]
-      },
-
-      {
-        attributes: [ 'id' ],
-        model: VideoBlacklistModel.unscoped(),
-        required: false,
-        include: [ buildVideoInclude(true) ]
-      },
-
-      {
-        attributes: [ 'id', 'magnetUri', 'targetUrl', 'torrentName' ],
-        model: VideoImportModel.unscoped(),
-        required: false,
-        include: [ buildVideoInclude(false) ]
-      },
-
-      {
-        attributes: [ 'id', 'name', 'type', 'latestVersion' ],
-        model: PluginModel.unscoped(),
-        required: false
-      },
-
-      {
-        attributes: [ 'id', 'latestPeerTubeVersion' ],
-        model: ApplicationModel.unscoped(),
-        required: false
-      },
-
-      {
-        attributes: [ 'id', 'state' ],
-        model: ActorFollowModel.unscoped(),
-        required: false,
-        include: [
-          {
-            attributes: [ 'preferredUsername' ],
-            model: ActorModel.unscoped(),
-            required: true,
-            as: 'ActorFollower',
-            include: [
-              {
-                attributes: [ 'id', 'name' ],
-                model: AccountModel.unscoped(),
-                required: true
-              },
-              {
-                attributes: [ 'filename' ],
-                as: 'Avatar',
-                model: ActorImageModel.unscoped(),
-                required: false
-              },
-              {
-                attributes: [ 'host' ],
-                model: ServerModel.unscoped(),
-                required: false
-              }
-            ]
-          },
-          {
-            attributes: [ 'preferredUsername', 'type' ],
-            model: ActorModel.unscoped(),
-            required: true,
-            as: 'ActorFollowing',
-            include: [
-              buildChannelInclude(false),
-              buildAccountInclude(false),
-              {
-                attributes: [ 'host' ],
-                model: ServerModel.unscoped(),
-                required: false
-              }
-            ]
-          }
-        ]
-      },
-
-      buildAccountInclude(false, true)
-    ]
-  }
-}))
 @Table({
   tableName: 'userNotification',
   indexes: [
@@ -341,7 +154,7 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
     },
     onDelete: 'cascade'
   })
-  Comment: VideoCommentModel
+  VideoComment: VideoCommentModel
 
   @ForeignKey(() => AbuseModel)
   @Column
@@ -430,10 +243,12 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
   static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) {
     const where = { userId }
 
-    const query: FindOptions = {
+    const query = {
+      userId,
+      unread,
       offset: start,
       limit: count,
-      order: getSort(sort),
+      sort,
       where
     }
 
@@ -444,8 +259,8 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
         .then(count => count || 0),
 
       count === 0
-        ? []
-        : UserNotificationModel.scope(ScopeNames.WITH_ALL).findAll(query)
+        ? [] as UserNotificationModelForApi[]
+        : new UserNotificationListQueryBuilder(this.sequelize, query).listNotifications()
     ]).then(([ total, data ]) => ({ total, data }))
   }
 
@@ -523,25 +338,31 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
 
   toFormattedJSON (this: UserNotificationModelForApi): UserNotification {
     const video = this.Video
-      ? Object.assign(this.formatVideo(this.Video), { channel: this.formatActor(this.Video.VideoChannel) })
+      ? {
+        ...this.formatVideo(this.Video),
+
+        channel: this.formatActor(this.Video.VideoChannel)
+      }
       : undefined
 
     const videoImport = this.VideoImport
       ? {
         id: this.VideoImport.id,
-        video: this.VideoImport.Video ? this.formatVideo(this.VideoImport.Video) : undefined,
+        video: this.VideoImport.Video
+          ? this.formatVideo(this.VideoImport.Video)
+          : undefined,
         torrentName: this.VideoImport.torrentName,
         magnetUri: this.VideoImport.magnetUri,
         targetUrl: this.VideoImport.targetUrl
       }
       : undefined
 
-    const comment = this.Comment
+    const comment = this.VideoComment
       ? {
-        id: this.Comment.id,
-        threadId: this.Comment.getThreadId(),
-        account: this.formatActor(this.Comment.Account),
-        video: this.formatVideo(this.Comment.Video)
+        id: this.VideoComment.id,
+        threadId: this.VideoComment.getThreadId(),
+        account: this.formatActor(this.VideoComment.Account),
+        video: this.formatVideo(this.VideoComment.Video)
       }
       : undefined
 
@@ -569,8 +390,9 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
           id: this.ActorFollow.ActorFollower.Account.id,
           displayName: this.ActorFollow.ActorFollower.Account.getDisplayName(),
           name: this.ActorFollow.ActorFollower.preferredUsername,
-          avatar: this.ActorFollow.ActorFollower.Avatar ? { path: this.ActorFollow.ActorFollower.Avatar.getStaticPath() } : undefined,
-          host: this.ActorFollow.ActorFollower.getHost()
+          host: this.ActorFollow.ActorFollower.getHost(),
+
+          ...this.formatAvatars(this.ActorFollow.ActorFollower.Avatars)
         },
         following: {
           type: actorFollowingType[this.ActorFollow.ActorFollowing.type],
@@ -611,15 +433,16 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
     }
   }
 
-  formatVideo (this: UserNotificationModelForApi, video: UserNotificationIncludes.VideoInclude) {
+  formatVideo (video: UserNotificationIncludes.VideoInclude) {
     return {
       id: video.id,
       uuid: video.uuid,
+      shortUUID: uuidToShort(video.uuid),
       name: video.name
     }
   }
 
-  formatAbuse (this: UserNotificationModelForApi, abuse: UserNotificationIncludes.AbuseInclude) {
+  formatAbuse (abuse: UserNotificationIncludes.AbuseInclude) {
     const commentAbuse = abuse.VideoCommentAbuse?.VideoComment
       ? {
         threadId: abuse.VideoCommentAbuse.VideoComment.getThreadId(),
@@ -628,15 +451,20 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
           ? {
             id: abuse.VideoCommentAbuse.VideoComment.Video.id,
             name: abuse.VideoCommentAbuse.VideoComment.Video.name,
+            shortUUID: uuidToShort(abuse.VideoCommentAbuse.VideoComment.Video.uuid),
             uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid
           }
           : undefined
       }
       : undefined
 
-    const videoAbuse = abuse.VideoAbuse?.Video ? this.formatVideo(abuse.VideoAbuse.Video) : undefined
+    const videoAbuse = abuse.VideoAbuse?.Video
+      ? this.formatVideo(abuse.VideoAbuse.Video)
+      : undefined
 
-    const accountAbuse = (!commentAbuse && !videoAbuse && abuse.FlaggedAccount) ? this.formatActor(abuse.FlaggedAccount) : undefined
+    const accountAbuse = (!commentAbuse && !videoAbuse && abuse.FlaggedAccount)
+      ? this.formatActor(abuse.FlaggedAccount)
+      : undefined
 
     return {
       id: abuse.id,
@@ -648,19 +476,32 @@ export class UserNotificationModel extends Model<Partial<AttributesOnly<UserNoti
   }
 
   formatActor (
-    this: UserNotificationModelForApi,
     accountOrChannel: UserNotificationIncludes.AccountIncludeActor | UserNotificationIncludes.VideoChannelIncludeActor
   ) {
-    const avatar = accountOrChannel.Actor.Avatar
-      ? { path: accountOrChannel.Actor.Avatar.getStaticPath() }
-      : undefined
-
     return {
       id: accountOrChannel.id,
       displayName: accountOrChannel.getDisplayName(),
       name: accountOrChannel.Actor.preferredUsername,
       host: accountOrChannel.Actor.getHost(),
-      avatar
+
+      ...this.formatAvatars(accountOrChannel.Actor.Avatars)
+    }
+  }
+
+  formatAvatars (avatars: UserNotificationIncludes.ActorImageInclude[]) {
+    if (!avatars || avatars.length === 0) return { avatar: undefined, avatars: [] }
+
+    return {
+      avatar: this.formatAvatar(getBiggestActorImage(avatars)),
+
+      avatars: avatars.map(a => this.formatAvatar(a))
+    }
+  }
+
+  formatAvatar (a: UserNotificationIncludes.ActorImageInclude) {
+    return {
+      path: a.getStaticPath(),
+      width: a.width
     }
   }
 }