X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=server%2Fmodels%2Faccount%2Fuser-notification.ts;h=bd89b8973e0a28186a8230d40708946b0752375a;hb=fb7194043d0486ce0a6a40b2ffbdf32878c33a6f;hp=ccf8277ab1ab45413f4c3b1d90b3278f347feeb1;hpb=883993c81ecc2388d4a4b37b29b81b6de73d264f;p=github%2FChocobozzz%2FPeerTube.git diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts index ccf8277ab..bd89b8973 100644 --- a/server/models/account/user-notification.ts +++ b/server/models/account/user-notification.ts @@ -1,34 +1,24 @@ -import { - AllowNull, - BelongsTo, - Column, - CreatedAt, - Default, - ForeignKey, - IFindOptions, - Is, - Model, - Scopes, - Table, - UpdatedAt -} from 'sequelize-typescript' +import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize' +import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' +import { UserNotificationIncludes, UserNotificationModelForApi } from '@server/types/models/user' import { UserNotification, UserNotificationType } from '../../../shared' -import { getSort, throwIfNotValid } from '../utils' import { isBooleanValid } from '../../helpers/custom-validators/misc' import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications' -import { UserModel } from './user' -import { VideoModel } from '../video/video' -import { VideoCommentModel } from '../video/video-comment' -import { Op } from 'sequelize' -import { VideoChannelModel } from '../video/video-channel' -import { AccountModel } from './account' -import { VideoAbuseModel } from '../video/video-abuse' -import { VideoBlacklistModel } from '../video/video-blacklist' -import { VideoImportModel } from '../video/video-import' +import { AbuseModel } from '../abuse/abuse' +import { VideoAbuseModel } from '../abuse/video-abuse' +import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' import { ActorModel } from '../activitypub/actor' import { ActorFollowModel } from '../activitypub/actor-follow' import { AvatarModel } from '../avatar/avatar' import { ServerModel } from '../server/server' +import { getSort, 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 { AccountModel } from './account' +import { UserModel } from './user' enum ScopeNames { WITH_ALL = 'WITH_ALL' @@ -37,17 +27,17 @@ enum ScopeNames { function buildActorWithAvatarInclude () { return { attributes: [ 'preferredUsername' ], - model: () => ActorModel.unscoped(), + model: ActorModel.unscoped(), required: true, include: [ { attributes: [ 'filename' ], - model: () => AvatarModel.unscoped(), + model: AvatarModel.unscoped(), required: false }, { attributes: [ 'host' ], - model: () => ServerModel.unscoped(), + model: ServerModel.unscoped(), required: false } ] @@ -57,7 +47,7 @@ function buildActorWithAvatarInclude () { function buildVideoInclude (required: boolean) { return { attributes: [ 'id', 'uuid', 'name' ], - model: () => VideoModel.unscoped(), + model: VideoModel.unscoped(), required } } @@ -66,7 +56,7 @@ function buildChannelInclude (required: boolean, withActor = false) { return { required, attributes: [ 'id', 'name' ], - model: () => VideoChannelModel.unscoped(), + model: VideoChannelModel.unscoped(), include: withActor === true ? [ buildActorWithAvatarInclude() ] : [] } } @@ -75,12 +65,12 @@ function buildAccountInclude (required: boolean, withActor = false) { return { required, attributes: [ 'id', 'name' ], - model: () => AccountModel.unscoped(), + model: AccountModel.unscoped(), include: withActor === true ? [ buildActorWithAvatarInclude() ] : [] } } -@Scopes({ +@Scopes(() => ({ [ScopeNames.WITH_ALL]: { include: [ Object.assign(buildVideoInclude(false), { @@ -89,7 +79,7 @@ function buildAccountInclude (required: boolean, withActor = false) { { attributes: [ 'id', 'originCommentId' ], - model: () => VideoCommentModel.unscoped(), + model: VideoCommentModel.unscoped(), required: false, include: [ buildAccountInclude(true, true), @@ -98,62 +88,99 @@ function buildAccountInclude (required: boolean, withActor = false) { }, { - attributes: [ 'id' ], - model: () => VideoAbuseModel.unscoped(), + attributes: [ 'id', 'state' ], + model: AbuseModel.unscoped(), required: false, - include: [ buildVideoInclude(true) ] + include: [ + { + attributes: [ 'id' ], + model: VideoAbuseModel.unscoped(), + required: false, + include: [ buildVideoInclude(true) ] + }, + { + attributes: [ 'id' ], + model: VideoCommentAbuseModel.unscoped(), + required: false, + include: [ + { + attributes: [ 'id', 'originCommentId' ], + model: VideoCommentModel, + required: true, + include: [ + { + attributes: [ 'id', 'name', 'uuid' ], + model: VideoModel.unscoped(), + required: true + } + ] + } + ] + }, + { + model: AccountModel, + as: 'FlaggedAccount', + required: true, + include: [ buildActorWithAvatarInclude() ] + } + ] }, { attributes: [ 'id' ], - model: () => VideoBlacklistModel.unscoped(), + model: VideoBlacklistModel.unscoped(), required: false, include: [ buildVideoInclude(true) ] }, { attributes: [ 'id', 'magnetUri', 'targetUrl', 'torrentName' ], - model: () => VideoImportModel.unscoped(), + model: VideoImportModel.unscoped(), required: false, include: [ buildVideoInclude(false) ] }, { - attributes: [ 'id' ], - model: () => ActorFollowModel.unscoped(), + attributes: [ 'id', 'state' ], + model: ActorFollowModel.unscoped(), required: false, include: [ { attributes: [ 'preferredUsername' ], - model: () => ActorModel.unscoped(), + model: ActorModel.unscoped(), required: true, as: 'ActorFollower', include: [ { attributes: [ 'id', 'name' ], - model: () => AccountModel.unscoped(), + model: AccountModel.unscoped(), required: true }, { attributes: [ 'filename' ], - model: () => AvatarModel.unscoped(), + model: AvatarModel.unscoped(), required: false }, { attributes: [ 'host' ], - model: () => ServerModel.unscoped(), + model: ServerModel.unscoped(), required: false } ] }, { - attributes: [ 'preferredUsername' ], - model: () => ActorModel.unscoped(), + attributes: [ 'preferredUsername', 'type' ], + model: ActorModel.unscoped(), required: true, as: 'ActorFollowing', include: [ buildChannelInclude(false), - buildAccountInclude(false) + buildAccountInclude(false), + { + attributes: [ 'host' ], + model: ServerModel.unscoped(), + required: false + } ] } ] @@ -162,7 +189,7 @@ function buildAccountInclude (required: boolean, withActor = false) { buildAccountInclude(false, true) ] } -}) +})) @Table({ tableName: 'userNotification', indexes: [ @@ -186,9 +213,9 @@ function buildAccountInclude (required: boolean, withActor = false) { } }, { - fields: [ 'videoAbuseId' ], + fields: [ 'abuseId' ], where: { - videoAbuseId: { + abuseId: { [Op.ne]: null } } @@ -225,7 +252,7 @@ function buildAccountInclude (required: boolean, withActor = false) { } } } - ] + ] as (ModelIndexesOptions & { where?: WhereOptions })[] }) export class UserNotificationModel extends Model { @@ -283,17 +310,17 @@ export class UserNotificationModel extends Model { }) Comment: VideoCommentModel - @ForeignKey(() => VideoAbuseModel) + @ForeignKey(() => AbuseModel) @Column - videoAbuseId: number + abuseId: number - @BelongsTo(() => VideoAbuseModel, { + @BelongsTo(() => AbuseModel, { foreignKey: { allowNull: true }, onDelete: 'cascade' }) - VideoAbuse: VideoAbuseModel + Abuse: AbuseModel @ForeignKey(() => VideoBlacklistModel) @Column @@ -344,25 +371,25 @@ export class UserNotificationModel extends Model { ActorFollow: ActorFollowModel static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) { - const query: IFindOptions = { + const where = { userId } + + const query: FindOptions = { offset: start, limit: count, order: getSort(sort), - where: { - userId - } + where } if (unread !== undefined) query.where['read'] = !unread - return UserNotificationModel.scope(ScopeNames.WITH_ALL) - .findAndCountAll(query) - .then(({ rows, count }) => { - return { - data: rows, - total: count - } - }) + return Promise.all([ + UserNotificationModel.count({ where }) + .then(count => count || 0), + + count === 0 + ? [] + : UserNotificationModel.scope(ScopeNames.WITH_ALL).findAll(query) + ]).then(([ total, data ]) => ({ total, data })) } static markAsRead (userId: number, notificationIds: number[]) { @@ -370,7 +397,7 @@ export class UserNotificationModel extends Model { where: { userId, id: { - [Op.any]: notificationIds + [Op.in]: notificationIds } } } @@ -384,9 +411,9 @@ export class UserNotificationModel extends Model { return UserNotificationModel.update({ read: true }, query) } - toFormattedJSON (): UserNotification { + toFormattedJSON (this: UserNotificationModelForApi): UserNotification { const video = this.Video - ? Object.assign(this.formatVideo(this.Video),{ channel: this.formatActor(this.Video.VideoChannel) }) + ? Object.assign(this.formatVideo(this.Video), { channel: this.formatActor(this.Video.VideoChannel) }) : undefined const videoImport = this.VideoImport ? { @@ -404,10 +431,7 @@ export class UserNotificationModel extends Model { video: this.formatVideo(this.Comment.Video) } : undefined - const videoAbuse = this.VideoAbuse ? { - id: this.VideoAbuse.id, - video: this.formatVideo(this.VideoAbuse.Video) - } : undefined + const abuse = this.Abuse ? this.formatAbuse(this.Abuse) : undefined const videoBlacklist = this.VideoBlacklist ? { id: this.VideoBlacklist.id, @@ -416,6 +440,11 @@ export class UserNotificationModel extends Model { const account = this.Account ? this.formatActor(this.Account) : undefined + const actorFollowingType = { + Application: 'instance' as 'instance', + Group: 'channel' as 'channel', + Person: 'account' as 'account' + } const actorFollow = this.ActorFollow ? { id: this.ActorFollow.id, state: this.ActorFollow.state, @@ -423,13 +452,14 @@ export class UserNotificationModel extends Model { 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.getWebserverPath() } : undefined, + avatar: this.ActorFollow.ActorFollower.Avatar ? { path: this.ActorFollow.ActorFollower.Avatar.getStaticPath() } : undefined, host: this.ActorFollow.ActorFollower.getHost() }, following: { - type: this.ActorFollow.ActorFollowing.VideoChannel ? 'channel' as 'channel' : 'account' as 'account', + type: actorFollowingType[this.ActorFollow.ActorFollowing.type], displayName: (this.ActorFollow.ActorFollowing.VideoChannel || this.ActorFollow.ActorFollowing.Account).getDisplayName(), - name: this.ActorFollow.ActorFollowing.preferredUsername + name: this.ActorFollow.ActorFollowing.preferredUsername, + host: this.ActorFollow.ActorFollowing.getHost() } } : undefined @@ -440,7 +470,7 @@ export class UserNotificationModel extends Model { video, videoImport, comment, - videoAbuse, + abuse, videoBlacklist, account, actorFollow, @@ -449,7 +479,7 @@ export class UserNotificationModel extends Model { } } - private formatVideo (video: VideoModel) { + formatVideo (this: UserNotificationModelForApi, video: UserNotificationIncludes.VideoInclude) { return { id: video.id, uuid: video.uuid, @@ -457,9 +487,36 @@ export class UserNotificationModel extends Model { } } - private formatActor (accountOrChannel: AccountModel | VideoChannelModel) { + formatAbuse (this: UserNotificationModelForApi, abuse: UserNotificationIncludes.AbuseInclude) { + const commentAbuse = abuse.VideoCommentAbuse?.VideoComment ? { + threadId: abuse.VideoCommentAbuse.VideoComment.getThreadId(), + + video: { + id: abuse.VideoCommentAbuse.VideoComment.Video.id, + name: abuse.VideoCommentAbuse.VideoComment.Video.name, + uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid + } + } : undefined + + const videoAbuse = abuse.VideoAbuse?.Video ? this.formatVideo(abuse.VideoAbuse.Video) : undefined + + const accountAbuse = (!commentAbuse && !videoAbuse) ? this.formatActor(abuse.FlaggedAccount) : undefined + + return { + id: abuse.id, + state: abuse.state, + video: videoAbuse, + comment: commentAbuse, + account: accountAbuse + } + } + + formatActor ( + this: UserNotificationModelForApi, + accountOrChannel: UserNotificationIncludes.AccountIncludeActor | UserNotificationIncludes.VideoChannelIncludeActor + ) { const avatar = accountOrChannel.Actor.Avatar - ? { path: accountOrChannel.Actor.Avatar.getWebserverPath() } + ? { path: accountOrChannel.Actor.Avatar.getStaticPath() } : undefined return {