aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/account
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/account')
-rw-r--r--server/models/account/account.ts5
-rw-r--r--server/models/account/actor-image.ts100
-rw-r--r--server/models/account/user-notification-setting.ts26
-rw-r--r--server/models/account/user-notification.ts98
-rw-r--r--server/models/account/user.ts20
5 files changed, 228 insertions, 21 deletions
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index c72f9c63d..312451abe 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -33,7 +33,7 @@ import {
33import { ActorModel } from '../activitypub/actor' 33import { ActorModel } from '../activitypub/actor'
34import { ActorFollowModel } from '../activitypub/actor-follow' 34import { ActorFollowModel } from '../activitypub/actor-follow'
35import { ApplicationModel } from '../application/application' 35import { ApplicationModel } from '../application/application'
36import { AvatarModel } from '../avatar/avatar' 36import { ActorImageModel } from './actor-image'
37import { ServerModel } from '../server/server' 37import { ServerModel } from '../server/server'
38import { ServerBlocklistModel } from '../server/server-blocklist' 38import { ServerBlocklistModel } from '../server/server-blocklist'
39import { getSort, throwIfNotValid } from '../utils' 39import { getSort, throwIfNotValid } from '../utils'
@@ -82,7 +82,8 @@ export type SummaryOptions = {
82 serverInclude, 82 serverInclude,
83 83
84 { 84 {
85 model: AvatarModel.unscoped(), 85 model: ActorImageModel.unscoped(),
86 as: 'Avatar',
86 required: false 87 required: false
87 } 88 }
88 ] 89 ]
diff --git a/server/models/account/actor-image.ts b/server/models/account/actor-image.ts
new file mode 100644
index 000000000..ae05b4969
--- /dev/null
+++ b/server/models/account/actor-image.ts
@@ -0,0 +1,100 @@
1import { remove } from 'fs-extra'
2import { join } from 'path'
3import { AfterDestroy, AllowNull, Column, CreatedAt, Default, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
4import { MActorImageFormattable } from '@server/types/models'
5import { ActorImageType } from '@shared/models'
6import { ActorImage } from '../../../shared/models/actors/actor-image.model'
7import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
8import { logger } from '../../helpers/logger'
9import { CONFIG } from '../../initializers/config'
10import { LAZY_STATIC_PATHS } from '../../initializers/constants'
11import { throwIfNotValid } from '../utils'
12
13@Table({
14 tableName: 'actorImage',
15 indexes: [
16 {
17 fields: [ 'filename' ],
18 unique: true
19 }
20 ]
21})
22export class ActorImageModel extends Model {
23
24 @AllowNull(false)
25 @Column
26 filename: string
27
28 @AllowNull(true)
29 @Default(null)
30 @Column
31 height: number
32
33 @AllowNull(true)
34 @Default(null)
35 @Column
36 width: number
37
38 @AllowNull(true)
39 @Is('ActorImageFileUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'fileUrl', true))
40 @Column
41 fileUrl: string
42
43 @AllowNull(false)
44 @Column
45 onDisk: boolean
46
47 @AllowNull(false)
48 @Column
49 type: ActorImageType
50
51 @CreatedAt
52 createdAt: Date
53
54 @UpdatedAt
55 updatedAt: Date
56
57 @AfterDestroy
58 static removeFilesAndSendDelete (instance: ActorImageModel) {
59 logger.info('Removing actor image file %s.', instance.filename)
60
61 // Don't block the transaction
62 instance.removeImage()
63 .catch(err => logger.error('Cannot remove actor image file %s.', instance.filename, err))
64 }
65
66 static loadByName (filename: string) {
67 const query = {
68 where: {
69 filename
70 }
71 }
72
73 return ActorImageModel.findOne(query)
74 }
75
76 toFormattedJSON (this: MActorImageFormattable): ActorImage {
77 return {
78 path: this.getStaticPath(),
79 createdAt: this.createdAt,
80 updatedAt: this.updatedAt
81 }
82 }
83
84 getStaticPath () {
85 if (this.type === ActorImageType.AVATAR) {
86 return join(LAZY_STATIC_PATHS.AVATARS, this.filename)
87 }
88
89 return join(LAZY_STATIC_PATHS.BANNERS, this.filename)
90 }
91
92 getPath () {
93 return join(CONFIG.STORAGE.ACTOR_IMAGES, this.filename)
94 }
95
96 removeImage () {
97 const imagePath = join(CONFIG.STORAGE.ACTOR_IMAGES, this.filename)
98 return remove(imagePath)
99 }
100}
diff --git a/server/models/account/user-notification-setting.ts b/server/models/account/user-notification-setting.ts
index ebab8b6d2..138051528 100644
--- a/server/models/account/user-notification-setting.ts
+++ b/server/models/account/user-notification-setting.ts
@@ -12,10 +12,10 @@ import {
12 Table, 12 Table,
13 UpdatedAt 13 UpdatedAt
14} from 'sequelize-typescript' 14} from 'sequelize-typescript'
15import { TokensCache } from '@server/lib/auth/tokens-cache'
15import { MNotificationSettingFormattable } from '@server/types/models' 16import { MNotificationSettingFormattable } from '@server/types/models'
16import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model' 17import { UserNotificationSetting, UserNotificationSettingValue } from '../../../shared/models/users/user-notification-setting.model'
17import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications' 18import { isUserNotificationSettingValid } from '../../helpers/custom-validators/user-notifications'
18import { clearCacheByUserId } from '../../lib/oauth-model'
19import { throwIfNotValid } from '../utils' 19import { throwIfNotValid } from '../utils'
20import { UserModel } from './user' 20import { UserModel } from './user'
21 21
@@ -156,6 +156,24 @@ export class UserNotificationSettingModel extends Model {
156 @Column 156 @Column
157 abuseNewMessage: UserNotificationSettingValue 157 abuseNewMessage: UserNotificationSettingValue
158 158
159 @AllowNull(false)
160 @Default(null)
161 @Is(
162 'UserNotificationSettingNewPeerTubeVersion',
163 value => throwIfNotValid(value, isUserNotificationSettingValid, 'newPeerTubeVersion')
164 )
165 @Column
166 newPeerTubeVersion: UserNotificationSettingValue
167
168 @AllowNull(false)
169 @Default(null)
170 @Is(
171 'UserNotificationSettingNewPeerPluginVersion',
172 value => throwIfNotValid(value, isUserNotificationSettingValid, 'newPluginVersion')
173 )
174 @Column
175 newPluginVersion: UserNotificationSettingValue
176
159 @ForeignKey(() => UserModel) 177 @ForeignKey(() => UserModel)
160 @Column 178 @Column
161 userId: number 179 userId: number
@@ -177,7 +195,7 @@ export class UserNotificationSettingModel extends Model {
177 @AfterUpdate 195 @AfterUpdate
178 @AfterDestroy 196 @AfterDestroy
179 static removeTokenCache (instance: UserNotificationSettingModel) { 197 static removeTokenCache (instance: UserNotificationSettingModel) {
180 return clearCacheByUserId(instance.userId) 198 return TokensCache.Instance.clearCacheByUserId(instance.userId)
181 } 199 }
182 200
183 toFormattedJSON (this: MNotificationSettingFormattable): UserNotificationSetting { 201 toFormattedJSON (this: MNotificationSettingFormattable): UserNotificationSetting {
@@ -195,7 +213,9 @@ export class UserNotificationSettingModel extends Model {
195 newInstanceFollower: this.newInstanceFollower, 213 newInstanceFollower: this.newInstanceFollower,
196 autoInstanceFollowing: this.autoInstanceFollowing, 214 autoInstanceFollowing: this.autoInstanceFollowing,
197 abuseNewMessage: this.abuseNewMessage, 215 abuseNewMessage: this.abuseNewMessage,
198 abuseStateChange: this.abuseStateChange 216 abuseStateChange: this.abuseStateChange,
217 newPeerTubeVersion: this.newPeerTubeVersion,
218 newPluginVersion: this.newPluginVersion
199 } 219 }
200 } 220 }
201} 221}
diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts
index add129644..805095002 100644
--- a/server/models/account/user-notification.ts
+++ b/server/models/account/user-notification.ts
@@ -9,7 +9,8 @@ import { VideoAbuseModel } from '../abuse/video-abuse'
9import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse' 9import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse'
10import { ActorModel } from '../activitypub/actor' 10import { ActorModel } from '../activitypub/actor'
11import { ActorFollowModel } from '../activitypub/actor-follow' 11import { ActorFollowModel } from '../activitypub/actor-follow'
12import { AvatarModel } from '../avatar/avatar' 12import { ApplicationModel } from '../application/application'
13import { PluginModel } from '../server/plugin'
13import { ServerModel } from '../server/server' 14import { ServerModel } from '../server/server'
14import { getSort, throwIfNotValid } from '../utils' 15import { getSort, throwIfNotValid } from '../utils'
15import { VideoModel } from '../video/video' 16import { VideoModel } from '../video/video'
@@ -18,6 +19,7 @@ import { VideoChannelModel } from '../video/video-channel'
18import { VideoCommentModel } from '../video/video-comment' 19import { VideoCommentModel } from '../video/video-comment'
19import { VideoImportModel } from '../video/video-import' 20import { VideoImportModel } from '../video/video-import'
20import { AccountModel } from './account' 21import { AccountModel } from './account'
22import { ActorImageModel } from './actor-image'
21import { UserModel } from './user' 23import { UserModel } from './user'
22 24
23enum ScopeNames { 25enum ScopeNames {
@@ -32,7 +34,8 @@ function buildActorWithAvatarInclude () {
32 include: [ 34 include: [
33 { 35 {
34 attributes: [ 'filename' ], 36 attributes: [ 'filename' ],
35 model: AvatarModel.unscoped(), 37 as: 'Avatar',
38 model: ActorImageModel.unscoped(),
36 required: false 39 required: false
37 }, 40 },
38 { 41 {
@@ -96,7 +99,7 @@ function buildAccountInclude (required: boolean, withActor = false) {
96 attributes: [ 'id' ], 99 attributes: [ 'id' ],
97 model: VideoAbuseModel.unscoped(), 100 model: VideoAbuseModel.unscoped(),
98 required: false, 101 required: false,
99 include: [ buildVideoInclude(true) ] 102 include: [ buildVideoInclude(false) ]
100 }, 103 },
101 { 104 {
102 attributes: [ 'id' ], 105 attributes: [ 'id' ],
@@ -106,12 +109,12 @@ function buildAccountInclude (required: boolean, withActor = false) {
106 { 109 {
107 attributes: [ 'id', 'originCommentId' ], 110 attributes: [ 'id', 'originCommentId' ],
108 model: VideoCommentModel.unscoped(), 111 model: VideoCommentModel.unscoped(),
109 required: true, 112 required: false,
110 include: [ 113 include: [
111 { 114 {
112 attributes: [ 'id', 'name', 'uuid' ], 115 attributes: [ 'id', 'name', 'uuid' ],
113 model: VideoModel.unscoped(), 116 model: VideoModel.unscoped(),
114 required: true 117 required: false
115 } 118 }
116 ] 119 ]
117 } 120 }
@@ -120,7 +123,7 @@ function buildAccountInclude (required: boolean, withActor = false) {
120 { 123 {
121 model: AccountModel, 124 model: AccountModel,
122 as: 'FlaggedAccount', 125 as: 'FlaggedAccount',
123 required: true, 126 required: false,
124 include: [ buildActorWithAvatarInclude() ] 127 include: [ buildActorWithAvatarInclude() ]
125 } 128 }
126 ] 129 ]
@@ -141,6 +144,18 @@ function buildAccountInclude (required: boolean, withActor = false) {
141 }, 144 },
142 145
143 { 146 {
147 attributes: [ 'id', 'name', 'type', 'latestVersion' ],
148 model: PluginModel.unscoped(),
149 required: false
150 },
151
152 {
153 attributes: [ 'id', 'latestPeerTubeVersion' ],
154 model: ApplicationModel.unscoped(),
155 required: false
156 },
157
158 {
144 attributes: [ 'id', 'state' ], 159 attributes: [ 'id', 'state' ],
145 model: ActorFollowModel.unscoped(), 160 model: ActorFollowModel.unscoped(),
146 required: false, 161 required: false,
@@ -158,7 +173,8 @@ function buildAccountInclude (required: boolean, withActor = false) {
158 }, 173 },
159 { 174 {
160 attributes: [ 'filename' ], 175 attributes: [ 'filename' ],
161 model: AvatarModel.unscoped(), 176 as: 'Avatar',
177 model: ActorImageModel.unscoped(),
162 required: false 178 required: false
163 }, 179 },
164 { 180 {
@@ -251,6 +267,22 @@ function buildAccountInclude (required: boolean, withActor = false) {
251 [Op.ne]: null 267 [Op.ne]: null
252 } 268 }
253 } 269 }
270 },
271 {
272 fields: [ 'pluginId' ],
273 where: {
274 pluginId: {
275 [Op.ne]: null
276 }
277 }
278 },
279 {
280 fields: [ 'applicationId' ],
281 where: {
282 applicationId: {
283 [Op.ne]: null
284 }
285 }
254 } 286 }
255 ] as (ModelIndexesOptions & { where?: WhereOptions })[] 287 ] as (ModelIndexesOptions & { where?: WhereOptions })[]
256}) 288})
@@ -370,6 +402,30 @@ export class UserNotificationModel extends Model {
370 }) 402 })
371 ActorFollow: ActorFollowModel 403 ActorFollow: ActorFollowModel
372 404
405 @ForeignKey(() => PluginModel)
406 @Column
407 pluginId: number
408
409 @BelongsTo(() => PluginModel, {
410 foreignKey: {
411 allowNull: true
412 },
413 onDelete: 'cascade'
414 })
415 Plugin: PluginModel
416
417 @ForeignKey(() => ApplicationModel)
418 @Column
419 applicationId: number
420
421 @BelongsTo(() => ApplicationModel, {
422 foreignKey: {
423 allowNull: true
424 },
425 onDelete: 'cascade'
426 })
427 Application: ApplicationModel
428
373 static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) { 429 static listForApi (userId: number, start: number, count: number, sort: string, unread?: boolean) {
374 const where = { userId } 430 const where = { userId }
375 431
@@ -524,6 +580,18 @@ export class UserNotificationModel extends Model {
524 } 580 }
525 : undefined 581 : undefined
526 582
583 const plugin = this.Plugin
584 ? {
585 name: this.Plugin.name,
586 type: this.Plugin.type,
587 latestVersion: this.Plugin.latestVersion
588 }
589 : undefined
590
591 const peertube = this.Application
592 ? { latestVersion: this.Application.latestPeerTubeVersion }
593 : undefined
594
527 return { 595 return {
528 id: this.id, 596 id: this.id,
529 type: this.type, 597 type: this.type,
@@ -535,6 +603,8 @@ export class UserNotificationModel extends Model {
535 videoBlacklist, 603 videoBlacklist,
536 account, 604 account,
537 actorFollow, 605 actorFollow,
606 plugin,
607 peertube,
538 createdAt: this.createdAt.toISOString(), 608 createdAt: this.createdAt.toISOString(),
539 updatedAt: this.updatedAt.toISOString() 609 updatedAt: this.updatedAt.toISOString()
540 } 610 }
@@ -553,17 +623,19 @@ export class UserNotificationModel extends Model {
553 ? { 623 ? {
554 threadId: abuse.VideoCommentAbuse.VideoComment.getThreadId(), 624 threadId: abuse.VideoCommentAbuse.VideoComment.getThreadId(),
555 625
556 video: { 626 video: abuse.VideoCommentAbuse.VideoComment.Video
557 id: abuse.VideoCommentAbuse.VideoComment.Video.id, 627 ? {
558 name: abuse.VideoCommentAbuse.VideoComment.Video.name, 628 id: abuse.VideoCommentAbuse.VideoComment.Video.id,
559 uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid 629 name: abuse.VideoCommentAbuse.VideoComment.Video.name,
560 } 630 uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid
631 }
632 : undefined
561 } 633 }
562 : undefined 634 : undefined
563 635
564 const videoAbuse = abuse.VideoAbuse?.Video ? this.formatVideo(abuse.VideoAbuse.Video) : undefined 636 const videoAbuse = abuse.VideoAbuse?.Video ? this.formatVideo(abuse.VideoAbuse.Video) : undefined
565 637
566 const accountAbuse = (!commentAbuse && !videoAbuse) ? this.formatActor(abuse.FlaggedAccount) : undefined 638 const accountAbuse = (!commentAbuse && !videoAbuse && abuse.FlaggedAccount) ? this.formatActor(abuse.FlaggedAccount) : undefined
567 639
568 return { 640 return {
569 id: abuse.id, 641 id: abuse.id,
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index c1f22b76a..00c6d73aa 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -21,6 +21,7 @@ import {
21 Table, 21 Table,
22 UpdatedAt 22 UpdatedAt
23} from 'sequelize-typescript' 23} from 'sequelize-typescript'
24import { TokensCache } from '@server/lib/auth/tokens-cache'
24import { 25import {
25 MMyUserFormattable, 26 MMyUserFormattable,
26 MUser, 27 MUser,
@@ -58,7 +59,6 @@ import {
58} from '../../helpers/custom-validators/users' 59} from '../../helpers/custom-validators/users'
59import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto' 60import { comparePassword, cryptPassword } from '../../helpers/peertube-crypto'
60import { DEFAULT_USER_THEME_NAME, NSFW_POLICY_TYPES } from '../../initializers/constants' 61import { DEFAULT_USER_THEME_NAME, NSFW_POLICY_TYPES } from '../../initializers/constants'
61import { clearCacheByUserId } from '../../lib/oauth-model'
62import { getThemeOrDefault } from '../../lib/plugins/theme-utils' 62import { getThemeOrDefault } from '../../lib/plugins/theme-utils'
63import { ActorModel } from '../activitypub/actor' 63import { ActorModel } from '../activitypub/actor'
64import { ActorFollowModel } from '../activitypub/actor-follow' 64import { ActorFollowModel } from '../activitypub/actor-follow'
@@ -71,6 +71,7 @@ import { VideoLiveModel } from '../video/video-live'
71import { VideoPlaylistModel } from '../video/video-playlist' 71import { VideoPlaylistModel } from '../video/video-playlist'
72import { AccountModel } from './account' 72import { AccountModel } from './account'
73import { UserNotificationSettingModel } from './user-notification-setting' 73import { UserNotificationSettingModel } from './user-notification-setting'
74import { ActorImageModel } from './actor-image'
74 75
75enum ScopeNames { 76enum ScopeNames {
76 FOR_ME_API = 'FOR_ME_API', 77 FOR_ME_API = 'FOR_ME_API',
@@ -97,7 +98,20 @@ enum ScopeNames {
97 model: AccountModel, 98 model: AccountModel,
98 include: [ 99 include: [
99 { 100 {
100 model: VideoChannelModel 101 model: VideoChannelModel.unscoped(),
102 include: [
103 {
104 model: ActorModel,
105 required: true,
106 include: [
107 {
108 model: ActorImageModel,
109 as: 'Banner',
110 required: false
111 }
112 ]
113 }
114 ]
101 }, 115 },
102 { 116 {
103 attributes: [ 'id', 'name', 'type' ], 117 attributes: [ 'id', 'name', 'type' ],
@@ -411,7 +425,7 @@ export class UserModel extends Model {
411 @AfterUpdate 425 @AfterUpdate
412 @AfterDestroy 426 @AfterDestroy
413 static removeTokenCache (instance: UserModel) { 427 static removeTokenCache (instance: UserModel) {
414 return clearCacheByUserId(instance.id) 428 return TokensCache.Instance.clearCacheByUserId(instance.id)
415 } 429 }
416 430
417 static countTotal () { 431 static countTotal () {