123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
- 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 { FindOptions, ModelIndexesOptions, Op, WhereOptions } 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 { ActorModel } from '../activitypub/actor'
- import { ActorFollowModel } from '../activitypub/actor-follow'
- import { AvatarModel } from '../avatar/avatar'
- import { ServerModel } from '../server/server'
- enum ScopeNames {
- WITH_ALL = 'WITH_ALL'
- }
- function buildActorWithAvatarInclude () {
- return {
- attributes: [ 'preferredUsername' ],
- model: ActorModel.unscoped(),
- required: true,
- include: [
- {
- attributes: [ 'filename' ],
- model: AvatarModel.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' ],
- model: VideoAbuseModel.unscoped(),
- required: false,
- include: [ buildVideoInclude(true) ]
- },
- {
- 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', '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' ],
- model: AvatarModel.unscoped(),
- required: false
- },
- {
- attributes: [ 'host' ],
- model: ServerModel.unscoped(),
- required: false
- }
- ]
- },
- {
- attributes: [ 'preferredUsername' ],
- model: ActorModel.unscoped(),
- required: true,
- as: 'ActorFollowing',
- include: [
- buildChannelInclude(false),
- buildAccountInclude(false)
- ]
- }
- ]
- },
- buildAccountInclude(false, true)
- ]
- }
- }))
- @Table({
- tableName: 'userNotification',
- indexes: [
- {
- fields: [ 'userId' ]
- },
- {
- fields: [ 'videoId' ],
- where: {
- videoId: {
- [Op.ne]: null
- }
- }
- },
- {
- fields: [ 'commentId' ],
- where: {
- commentId: {
- [Op.ne]: null
- }
- }
- },
- {
- fields: [ 'videoAbuseId' ],
- where: {
- videoAbuseId: {
- [Op.ne]: null
- }
- }
- },
- {
- fields: [ 'videoBlacklistId' ],
- where: {
- videoBlacklistId: {
- [Op.ne]: null
- }
- }
- },
- {
- fields: [ 'videoImportId' ],
- where: {
- videoImportId: {
- [Op.ne]: null
- }
- }
- },
- {
- fields: [ 'accountId' ],
- where: {
- accountId: {
- [Op.ne]: null
- }
- }
- },
- {
- fields: [ 'actorFollowId' ],
- where: {
- actorFollowId: {
- [Op.ne]: null
- }
- }
- }
- ] as (ModelIndexesOptions & { where?: WhereOptions })[]
- })
- export class UserNotificationModel extends Model<UserNotificationModel> {
- @AllowNull(false)
- @Default(null)
- @Is('UserNotificationType', value => throwIfNotValid(value, isUserNotificationTypeValid, 'type'))
- @Column
- type: UserNotificationType
- @AllowNull(false)
- @Default(false)
- @Is('UserNotificationRead', value => throwIfNotValid(value, isBooleanValid, 'read'))
- @Column
- read: boolean
- @CreatedAt
- createdAt: Date
- @UpdatedAt
- updatedAt: Date
- @ForeignKey(() => UserModel)
- @Column
- userId: number
- @BelongsTo(() => UserModel, {
- foreignKey: {
- allowNull: false
- },
- onDelete: 'cascade'
- })
- User: UserModel
- @ForeignKey(() => VideoModel)
- @Column
- videoId: number
- @BelongsTo(() => VideoModel, {
- foreignKey: {
- allowNull: true
- },
- onDelete: 'cascade'
- })
- Video: VideoModel
- @ForeignKey(() => VideoCommentModel)
- @Column
- commentId: number
- @BelongsTo(() => VideoCommentModel, {
- foreignKey: {
- allowNull: true
- },
- onDelete: 'cascade'
- })
- Comment: VideoCommentModel
- @ForeignKey(() => VideoAbuseModel)
- @Column
- videoAbuseId: number
- @BelongsTo(() => VideoAbuseModel, {
- foreignKey: {
- allowNull: true
- },
- onDelete: 'cascade'
- })
- VideoAbuse: VideoAbuseModel
- @ForeignKey(() => VideoBlacklistModel)
- @Column
- videoBlacklistId: number
- @BelongsTo(() => VideoBlacklistModel, {
- foreignKey: {
- allowNull: true
- },
- onDelete: 'cascade'
- })
- VideoBlacklist: VideoBlacklistModel
- @ForeignKey(() => VideoImportModel)
- @Column
- videoImportId: number
- @BelongsTo(() => VideoImportModel, {
- foreignKey: {
- allowNull: true
- },
- onDelete: 'cascade'
- })
- VideoImport: VideoImportModel
- @ForeignKey(() => AccountModel)
- @Column
- accountId: number
- @BelongsTo(() => AccountModel, {
- foreignKey: {
- allowNull: true
- },
- onDelete: 'cascade'
- })
- Account: AccountModel
- @ForeignKey(() => ActorFollowModel)
- @Column
- actorFollowId: number
- @BelongsTo(() => ActorFollowModel, {
- foreignKey: {
- allowNull: true
- },
- onDelete: 'cascade'
- })
- ActorFollow: ActorFollowModel
- static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) {
- const query: FindOptions = {
- offset: start,
- limit: count,
- order: getSort(sort),
- where: {
- userId
- }
- }
- if (unread !== undefined) query.where['read'] = !unread
- return UserNotificationModel.scope(ScopeNames.WITH_ALL)
- .findAndCountAll(query)
- .then(({ rows, count }) => {
- return {
- data: rows,
- total: count
- }
- })
- }
- static markAsRead (userId: number, notificationIds: number[]) {
- const query = {
- where: {
- userId,
- id: {
- [Op.in]: notificationIds // FIXME: sequelize ANY seems broken
- }
- }
- }
- return UserNotificationModel.update({ read: true }, query)
- }
- static markAllAsRead (userId: number) {
- const query = { where: { userId } }
- return UserNotificationModel.update({ read: true }, query)
- }
- toFormattedJSON (): UserNotification {
- const video = this.Video
- ? Object.assign(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,
- torrentName: this.VideoImport.torrentName,
- magnetUri: this.VideoImport.magnetUri,
- targetUrl: this.VideoImport.targetUrl
- } : undefined
- const comment = this.Comment ? {
- id: this.Comment.id,
- threadId: this.Comment.getThreadId(),
- account: this.formatActor(this.Comment.Account),
- video: this.formatVideo(this.Comment.Video)
- } : undefined
- const videoAbuse = this.VideoAbuse ? {
- id: this.VideoAbuse.id,
- video: this.formatVideo(this.VideoAbuse.Video)
- } : undefined
- const videoBlacklist = this.VideoBlacklist ? {
- id: this.VideoBlacklist.id,
- video: this.formatVideo(this.VideoBlacklist.Video)
- } : undefined
- const account = this.Account ? this.formatActor(this.Account) : undefined
- const actorFollow = this.ActorFollow ? {
- id: this.ActorFollow.id,
- state: this.ActorFollow.state,
- follower: {
- 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,
- host: this.ActorFollow.ActorFollower.getHost()
- },
- following: {
- type: this.ActorFollow.ActorFollowing.VideoChannel ? 'channel' as 'channel' : 'account' as 'account',
- displayName: (this.ActorFollow.ActorFollowing.VideoChannel || this.ActorFollow.ActorFollowing.Account).getDisplayName(),
- name: this.ActorFollow.ActorFollowing.preferredUsername
- }
- } : undefined
- return {
- id: this.id,
- type: this.type,
- read: this.read,
- video,
- videoImport,
- comment,
- videoAbuse,
- videoBlacklist,
- account,
- actorFollow,
- createdAt: this.createdAt.toISOString(),
- updatedAt: this.updatedAt.toISOString()
- }
- }
- private formatVideo (video: VideoModel) {
- return {
- id: video.id,
- uuid: video.uuid,
- name: video.name
- }
- }
- private formatActor (accountOrChannel: AccountModel | VideoChannelModel) {
- const avatar = accountOrChannel.Actor.Avatar
- ? { path: accountOrChannel.Actor.Avatar.getWebserverPath() }
- : undefined
- return {
- id: accountOrChannel.id,
- displayName: accountOrChannel.getDisplayName(),
- name: accountOrChannel.Actor.preferredUsername,
- host: accountOrChannel.Actor.getHost(),
- avatar
- }
- }
- }
|