]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/account/user-notification.ts
Upgrade sequelize
[github/Chocobozzz/PeerTube.git] / server / models / account / user-notification.ts
CommitLineData
1735c825 1import { AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
cef534ed
C
2import { UserNotification, UserNotificationType } from '../../../shared'
3import { getSort, throwIfNotValid } from '../utils'
4import { isBooleanValid } from '../../helpers/custom-validators/misc'
5import { isUserNotificationTypeValid } from '../../helpers/custom-validators/user-notifications'
6import { UserModel } from './user'
7import { VideoModel } from '../video/video'
8import { VideoCommentModel } from '../video/video-comment'
3acc5084 9import { FindOptions, ModelIndexesOptions, Op, WhereOptions } from 'sequelize'
cef534ed
C
10import { VideoChannelModel } from '../video/video-channel'
11import { AccountModel } from './account'
12import { VideoAbuseModel } from '../video/video-abuse'
13import { VideoBlacklistModel } from '../video/video-blacklist'
dc133480 14import { VideoImportModel } from '../video/video-import'
f7cc67b4
C
15import { ActorModel } from '../activitypub/actor'
16import { ActorFollowModel } from '../activitypub/actor-follow'
457bb213 17import { AvatarModel } from '../avatar/avatar'
38967f7b 18import { ServerModel } from '../server/server'
cef534ed
C
19
20enum ScopeNames {
21 WITH_ALL = 'WITH_ALL'
22}
23
457bb213
C
24function buildActorWithAvatarInclude () {
25 return {
26 attributes: [ 'preferredUsername' ],
3acc5084 27 model: ActorModel.unscoped(),
457bb213
C
28 required: true,
29 include: [
30 {
31 attributes: [ 'filename' ],
3acc5084 32 model: AvatarModel.unscoped(),
457bb213 33 required: false
38967f7b
C
34 },
35 {
36 attributes: [ 'host' ],
3acc5084 37 model: ServerModel.unscoped(),
38967f7b 38 required: false
457bb213
C
39 }
40 ]
41 }
42}
43
dc133480
C
44function buildVideoInclude (required: boolean) {
45 return {
46 attributes: [ 'id', 'uuid', 'name' ],
3acc5084 47 model: VideoModel.unscoped(),
dc133480
C
48 required
49 }
50}
51
457bb213 52function buildChannelInclude (required: boolean, withActor = false) {
dc133480 53 return {
f7cc67b4 54 required,
dc133480 55 attributes: [ 'id', 'name' ],
3acc5084 56 model: VideoChannelModel.unscoped(),
457bb213 57 include: withActor === true ? [ buildActorWithAvatarInclude() ] : []
dc133480
C
58 }
59}
60
457bb213 61function buildAccountInclude (required: boolean, withActor = false) {
dc133480 62 return {
f7cc67b4 63 required,
dc133480 64 attributes: [ 'id', 'name' ],
3acc5084 65 model: AccountModel.unscoped(),
457bb213 66 include: withActor === true ? [ buildActorWithAvatarInclude() ] : []
dc133480
C
67 }
68}
69
3acc5084 70@Scopes(() => ({
cef534ed
C
71 [ScopeNames.WITH_ALL]: {
72 include: [
dc133480 73 Object.assign(buildVideoInclude(false), {
457bb213 74 include: [ buildChannelInclude(true, true) ]
dc133480 75 }),
457bb213 76
cef534ed 77 {
dc133480 78 attributes: [ 'id', 'originCommentId' ],
3acc5084 79 model: VideoCommentModel.unscoped(),
cef534ed
C
80 required: false,
81 include: [
457bb213 82 buildAccountInclude(true, true),
dc133480 83 buildVideoInclude(true)
cef534ed
C
84 ]
85 },
457bb213 86
cef534ed
C
87 {
88 attributes: [ 'id' ],
3acc5084 89 model: VideoAbuseModel.unscoped(),
cef534ed 90 required: false,
dc133480 91 include: [ buildVideoInclude(true) ]
cef534ed 92 },
457bb213 93
cef534ed
C
94 {
95 attributes: [ 'id' ],
3acc5084 96 model: VideoBlacklistModel.unscoped(),
cef534ed 97 required: false,
dc133480
C
98 include: [ buildVideoInclude(true) ]
99 },
457bb213 100
dc133480
C
101 {
102 attributes: [ 'id', 'magnetUri', 'targetUrl', 'torrentName' ],
3acc5084 103 model: VideoImportModel.unscoped(),
dc133480
C
104 required: false,
105 include: [ buildVideoInclude(false) ]
f7cc67b4 106 },
457bb213 107
f7cc67b4 108 {
8ce1ba6e 109 attributes: [ 'id', 'state' ],
3acc5084 110 model: ActorFollowModel.unscoped(),
f7cc67b4
C
111 required: false,
112 include: [
113 {
114 attributes: [ 'preferredUsername' ],
3acc5084 115 model: ActorModel.unscoped(),
f7cc67b4
C
116 required: true,
117 as: 'ActorFollower',
457bb213
C
118 include: [
119 {
120 attributes: [ 'id', 'name' ],
3acc5084 121 model: AccountModel.unscoped(),
457bb213
C
122 required: true
123 },
124 {
125 attributes: [ 'filename' ],
3acc5084 126 model: AvatarModel.unscoped(),
457bb213 127 required: false
38967f7b
C
128 },
129 {
130 attributes: [ 'host' ],
3acc5084 131 model: ServerModel.unscoped(),
38967f7b 132 required: false
457bb213
C
133 }
134 ]
f7cc67b4
C
135 },
136 {
137 attributes: [ 'preferredUsername' ],
3acc5084 138 model: ActorModel.unscoped(),
f7cc67b4
C
139 required: true,
140 as: 'ActorFollowing',
141 include: [
142 buildChannelInclude(false),
143 buildAccountInclude(false)
144 ]
145 }
146 ]
457bb213
C
147 },
148
149 buildAccountInclude(false, true)
3acc5084 150 ]
cef534ed 151 }
3acc5084 152}))
cef534ed
C
153@Table({
154 tableName: 'userNotification',
155 indexes: [
156 {
457bb213 157 fields: [ 'userId' ]
cef534ed
C
158 },
159 {
457bb213
C
160 fields: [ 'videoId' ],
161 where: {
162 videoId: {
163 [Op.ne]: null
164 }
165 }
166 },
167 {
168 fields: [ 'commentId' ],
169 where: {
170 commentId: {
171 [Op.ne]: null
172 }
173 }
174 },
175 {
176 fields: [ 'videoAbuseId' ],
177 where: {
178 videoAbuseId: {
179 [Op.ne]: null
180 }
181 }
182 },
183 {
184 fields: [ 'videoBlacklistId' ],
185 where: {
186 videoBlacklistId: {
187 [Op.ne]: null
188 }
189 }
190 },
191 {
192 fields: [ 'videoImportId' ],
193 where: {
194 videoImportId: {
195 [Op.ne]: null
196 }
197 }
198 },
199 {
200 fields: [ 'accountId' ],
201 where: {
202 accountId: {
203 [Op.ne]: null
204 }
205 }
206 },
207 {
208 fields: [ 'actorFollowId' ],
209 where: {
210 actorFollowId: {
211 [Op.ne]: null
212 }
213 }
cef534ed 214 }
3acc5084 215 ] as (ModelIndexesOptions & { where?: WhereOptions })[]
cef534ed
C
216})
217export class UserNotificationModel extends Model<UserNotificationModel> {
218
219 @AllowNull(false)
220 @Default(null)
221 @Is('UserNotificationType', value => throwIfNotValid(value, isUserNotificationTypeValid, 'type'))
222 @Column
223 type: UserNotificationType
224
225 @AllowNull(false)
226 @Default(false)
227 @Is('UserNotificationRead', value => throwIfNotValid(value, isBooleanValid, 'read'))
228 @Column
229 read: boolean
230
231 @CreatedAt
232 createdAt: Date
233
234 @UpdatedAt
235 updatedAt: Date
236
237 @ForeignKey(() => UserModel)
238 @Column
239 userId: number
240
241 @BelongsTo(() => UserModel, {
242 foreignKey: {
243 allowNull: false
244 },
245 onDelete: 'cascade'
246 })
247 User: UserModel
248
249 @ForeignKey(() => VideoModel)
250 @Column
251 videoId: number
252
253 @BelongsTo(() => VideoModel, {
254 foreignKey: {
255 allowNull: true
256 },
257 onDelete: 'cascade'
258 })
259 Video: VideoModel
260
261 @ForeignKey(() => VideoCommentModel)
262 @Column
263 commentId: number
264
265 @BelongsTo(() => VideoCommentModel, {
266 foreignKey: {
267 allowNull: true
268 },
269 onDelete: 'cascade'
270 })
271 Comment: VideoCommentModel
272
273 @ForeignKey(() => VideoAbuseModel)
274 @Column
275 videoAbuseId: number
276
277 @BelongsTo(() => VideoAbuseModel, {
278 foreignKey: {
279 allowNull: true
280 },
281 onDelete: 'cascade'
282 })
283 VideoAbuse: VideoAbuseModel
284
285 @ForeignKey(() => VideoBlacklistModel)
286 @Column
287 videoBlacklistId: number
288
289 @BelongsTo(() => VideoBlacklistModel, {
290 foreignKey: {
291 allowNull: true
292 },
293 onDelete: 'cascade'
294 })
295 VideoBlacklist: VideoBlacklistModel
296
dc133480
C
297 @ForeignKey(() => VideoImportModel)
298 @Column
299 videoImportId: number
300
301 @BelongsTo(() => VideoImportModel, {
302 foreignKey: {
303 allowNull: true
304 },
305 onDelete: 'cascade'
306 })
307 VideoImport: VideoImportModel
308
f7cc67b4
C
309 @ForeignKey(() => AccountModel)
310 @Column
311 accountId: number
312
313 @BelongsTo(() => AccountModel, {
314 foreignKey: {
315 allowNull: true
316 },
317 onDelete: 'cascade'
318 })
319 Account: AccountModel
320
321 @ForeignKey(() => ActorFollowModel)
322 @Column
323 actorFollowId: number
324
325 @BelongsTo(() => ActorFollowModel, {
326 foreignKey: {
327 allowNull: true
328 },
329 onDelete: 'cascade'
330 })
331 ActorFollow: ActorFollowModel
332
dc133480 333 static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) {
1735c825 334 const query: FindOptions = {
cef534ed
C
335 offset: start,
336 limit: count,
337 order: getSort(sort),
338 where: {
339 userId
340 }
341 }
342
dc133480
C
343 if (unread !== undefined) query.where['read'] = !unread
344
cef534ed
C
345 return UserNotificationModel.scope(ScopeNames.WITH_ALL)
346 .findAndCountAll(query)
347 .then(({ rows, count }) => {
348 return {
349 data: rows,
350 total: count
351 }
352 })
353 }
354
355 static markAsRead (userId: number, notificationIds: number[]) {
356 const query = {
357 where: {
358 userId,
359 id: {
3acc5084 360 [Op.in]: notificationIds // FIXME: sequelize ANY seems broken
cef534ed
C
361 }
362 }
363 }
364
365 return UserNotificationModel.update({ read: true }, query)
366 }
367
2f1548fd
C
368 static markAllAsRead (userId: number) {
369 const query = { where: { userId } }
370
371 return UserNotificationModel.update({ read: true }, query)
372 }
373
cef534ed 374 toFormattedJSON (): UserNotification {
457bb213
C
375 const video = this.Video
376 ? Object.assign(this.formatVideo(this.Video),{ channel: this.formatActor(this.Video.VideoChannel) })
377 : undefined
dc133480
C
378
379 const videoImport = this.VideoImport ? {
380 id: this.VideoImport.id,
381 video: this.VideoImport.Video ? this.formatVideo(this.VideoImport.Video) : undefined,
382 torrentName: this.VideoImport.torrentName,
383 magnetUri: this.VideoImport.magnetUri,
384 targetUrl: this.VideoImport.targetUrl
cef534ed
C
385 } : undefined
386
387 const comment = this.Comment ? {
388 id: this.Comment.id,
dc133480 389 threadId: this.Comment.getThreadId(),
457bb213 390 account: this.formatActor(this.Comment.Account),
dc133480 391 video: this.formatVideo(this.Comment.Video)
cef534ed
C
392 } : undefined
393
394 const videoAbuse = this.VideoAbuse ? {
395 id: this.VideoAbuse.id,
dc133480 396 video: this.formatVideo(this.VideoAbuse.Video)
cef534ed
C
397 } : undefined
398
399 const videoBlacklist = this.VideoBlacklist ? {
400 id: this.VideoBlacklist.id,
dc133480 401 video: this.formatVideo(this.VideoBlacklist.Video)
cef534ed
C
402 } : undefined
403
457bb213 404 const account = this.Account ? this.formatActor(this.Account) : undefined
f7cc67b4
C
405
406 const actorFollow = this.ActorFollow ? {
407 id: this.ActorFollow.id,
883993c8 408 state: this.ActorFollow.state,
f7cc67b4 409 follower: {
457bb213 410 id: this.ActorFollow.ActorFollower.Account.id,
f7cc67b4 411 displayName: this.ActorFollow.ActorFollower.Account.getDisplayName(),
457bb213 412 name: this.ActorFollow.ActorFollower.preferredUsername,
38967f7b
C
413 avatar: this.ActorFollow.ActorFollower.Avatar ? { path: this.ActorFollow.ActorFollower.Avatar.getWebserverPath() } : undefined,
414 host: this.ActorFollow.ActorFollower.getHost()
f7cc67b4
C
415 },
416 following: {
417 type: this.ActorFollow.ActorFollowing.VideoChannel ? 'channel' as 'channel' : 'account' as 'account',
418 displayName: (this.ActorFollow.ActorFollowing.VideoChannel || this.ActorFollow.ActorFollowing.Account).getDisplayName(),
419 name: this.ActorFollow.ActorFollowing.preferredUsername
420 }
421 } : undefined
422
cef534ed
C
423 return {
424 id: this.id,
425 type: this.type,
426 read: this.read,
427 video,
dc133480 428 videoImport,
cef534ed
C
429 comment,
430 videoAbuse,
431 videoBlacklist,
f7cc67b4
C
432 account,
433 actorFollow,
cef534ed
C
434 createdAt: this.createdAt.toISOString(),
435 updatedAt: this.updatedAt.toISOString()
436 }
437 }
dc133480
C
438
439 private formatVideo (video: VideoModel) {
440 return {
441 id: video.id,
442 uuid: video.uuid,
443 name: video.name
444 }
445 }
457bb213
C
446
447 private formatActor (accountOrChannel: AccountModel | VideoChannelModel) {
448 const avatar = accountOrChannel.Actor.Avatar
449 ? { path: accountOrChannel.Actor.Avatar.getWebserverPath() }
450 : undefined
451
452 return {
453 id: accountOrChannel.id,
454 displayName: accountOrChannel.getDisplayName(),
455 name: accountOrChannel.Actor.preferredUsername,
38967f7b 456 host: accountOrChannel.Actor.getHost(),
457bb213
C
457 avatar
458 }
459 }
cef534ed 460}