]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/account/account.ts
Create a dedicated table to track video thumbnails
[github/Chocobozzz/PeerTube.git] / server / models / account / account.ts
CommitLineData
e4f97bab 1import * as Sequelize from 'sequelize'
3fd3ab2d 2import {
2422c46b
C
3 AllowNull,
4 BeforeDestroy,
5 BelongsTo,
6 Column,
7 CreatedAt,
8 Default,
9 DefaultScope,
10 ForeignKey,
11 HasMany,
12 Is,
09979f89
C
13 Model,
14 Scopes,
2422c46b 15 Table,
3fd3ab2d
C
16 UpdatedAt
17} from 'sequelize-typescript'
418d092a 18import { Account, AccountSummary } from '../../../shared/models/actors'
2422c46b 19import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts'
c5a893d5 20import { sendDeleteActor } from '../../lib/activitypub/send'
fadf619a 21import { ActorModel } from '../activitypub/actor'
3fd3ab2d 22import { ApplicationModel } from '../application/application'
3fd3ab2d 23import { ServerModel } from '../server/server'
2422c46b 24import { getSort, throwIfNotValid } from '../utils'
3fd3ab2d 25import { VideoChannelModel } from '../video/video-channel'
f05a1c30 26import { VideoCommentModel } from '../video/video-comment'
3fd3ab2d 27import { UserModel } from './user'
418d092a 28import { AvatarModel } from '../avatar/avatar'
418d092a 29import { VideoPlaylistModel } from '../video/video-playlist'
6dd9de95 30import { WEBSERVER } from '../../initializers/constants'
418d092a
C
31
32export enum ScopeNames {
33 SUMMARY = 'SUMMARY'
34}
3fd3ab2d 35
50d6de9c
C
36@DefaultScope({
37 include: [
3fd3ab2d 38 {
f37dc0dd
C
39 model: () => ActorModel, // Default scope includes avatar and server
40 required: true
e4f97bab 41 }
e4f97bab 42 ]
3fd3ab2d 43})
418d092a 44@Scopes({
09979f89 45 [ ScopeNames.SUMMARY ]: (whereActor?: Sequelize.WhereOptions<ActorModel>) => {
418d092a
C
46 return {
47 attributes: [ 'id', 'name' ],
48 include: [
49 {
50 attributes: [ 'id', 'uuid', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
51 model: ActorModel.unscoped(),
52 required: true,
53 where: whereActor,
54 include: [
55 {
56 attributes: [ 'host' ],
57 model: ServerModel.unscoped(),
58 required: false
59 },
60 {
61 model: AvatarModel.unscoped(),
62 required: false
63 }
64 ]
65 }
66 ]
67 }
68 }
69})
50d6de9c 70@Table({
8cd72bd3
C
71 tableName: 'account',
72 indexes: [
73 {
74 fields: [ 'actorId' ],
75 unique: true
76 },
77 {
78 fields: [ 'applicationId' ]
79 },
80 {
81 fields: [ 'userId' ]
82 }
83 ]
50d6de9c 84})
fadf619a 85export class AccountModel extends Model<AccountModel> {
3fd3ab2d 86
50d6de9c 87 @AllowNull(false)
50d6de9c
C
88 @Column
89 name: string
90
2422c46b
C
91 @AllowNull(true)
92 @Default(null)
93 @Is('AccountDescription', value => throwIfNotValid(value, isAccountDescriptionValid, 'description'))
94 @Column
95 description: string
96
3fd3ab2d
C
97 @CreatedAt
98 createdAt: Date
99
100 @UpdatedAt
101 updatedAt: Date
102
fadf619a 103 @ForeignKey(() => ActorModel)
3fd3ab2d 104 @Column
fadf619a 105 actorId: number
e4f97bab 106
fadf619a 107 @BelongsTo(() => ActorModel, {
e4f97bab 108 foreignKey: {
fadf619a 109 allowNull: false
e4f97bab
C
110 },
111 onDelete: 'cascade'
112 })
fadf619a 113 Actor: ActorModel
e4f97bab 114
3fd3ab2d
C
115 @ForeignKey(() => UserModel)
116 @Column
117 userId: number
118
119 @BelongsTo(() => UserModel, {
e4f97bab 120 foreignKey: {
e4f97bab
C
121 allowNull: true
122 },
123 onDelete: 'cascade'
124 })
3fd3ab2d
C
125 User: UserModel
126
127 @ForeignKey(() => ApplicationModel)
128 @Column
129 applicationId: number
e4f97bab 130
3fd3ab2d 131 @BelongsTo(() => ApplicationModel, {
e4f97bab 132 foreignKey: {
e4f97bab
C
133 allowNull: true
134 },
135 onDelete: 'cascade'
136 })
f05a1c30 137 Application: ApplicationModel
e4f97bab 138
3fd3ab2d 139 @HasMany(() => VideoChannelModel, {
e4f97bab 140 foreignKey: {
e4f97bab
C
141 allowNull: false
142 },
143 onDelete: 'cascade',
144 hooks: true
145 })
3fd3ab2d 146 VideoChannels: VideoChannelModel[]
e4f97bab 147
418d092a
C
148 @HasMany(() => VideoPlaylistModel, {
149 foreignKey: {
150 allowNull: false
151 },
152 onDelete: 'cascade',
153 hooks: true
154 })
155 VideoPlaylists: VideoPlaylistModel[]
156
f05a1c30
C
157 @HasMany(() => VideoCommentModel, {
158 foreignKey: {
159 allowNull: false
160 },
161 onDelete: 'cascade',
162 hooks: true
163 })
164 VideoComments: VideoCommentModel[]
165
166 @BeforeDestroy
167 static async sendDeleteIfOwned (instance: AccountModel, options) {
168 if (!instance.Actor) {
169 instance.Actor = await instance.$get('Actor', { transaction: options.transaction }) as ActorModel
170 }
171
c5a893d5 172 if (instance.isOwned()) {
c5a893d5
C
173 return sendDeleteActor(instance.Actor, options.transaction)
174 }
175
176 return undefined
e4f97bab
C
177 }
178
91411dba 179 static load (id: number, transaction?: Sequelize.Transaction) {
9b39106d 180 return AccountModel.findByPk(id, { transaction })
3fd3ab2d 181 }
2295ce6c 182
3fd3ab2d
C
183 static loadByUUID (uuid: string) {
184 const query = {
50d6de9c
C
185 include: [
186 {
187 model: ActorModel,
188 required: true,
189 where: {
190 uuid
191 }
192 }
193 ]
2295ce6c 194 }
60862425 195
3fd3ab2d 196 return AccountModel.findOne(query)
60862425 197 }
51548b31 198
92bf2f62
C
199 static loadByNameWithHost (nameWithHost: string) {
200 const [ accountName, host ] = nameWithHost.split('@')
201
6dd9de95 202 if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName)
92bf2f62
C
203
204 return AccountModel.loadByNameAndHost(accountName, host)
205 }
206
3fd3ab2d
C
207 static loadLocalByName (name: string) {
208 const query = {
209 where: {
3fd3ab2d
C
210 [ Sequelize.Op.or ]: [
211 {
212 userId: {
213 [ Sequelize.Op.ne ]: null
214 }
215 },
216 {
217 applicationId: {
218 [ Sequelize.Op.ne ]: null
219 }
220 }
221 ]
e8cb4409
C
222 },
223 include: [
224 {
225 model: ActorModel,
226 required: true,
227 where: {
228 preferredUsername: name
229 }
230 }
231 ]
232 }
233
234 return AccountModel.findOne(query)
235 }
236
8a19bee1 237 static loadByNameAndHost (name: string, host: string) {
e8cb4409
C
238 const query = {
239 include: [
240 {
241 model: ActorModel,
242 required: true,
243 where: {
244 preferredUsername: name
245 },
246 include: [
247 {
248 model: ServerModel,
249 required: true,
250 where: {
251 host
252 }
253 }
254 ]
255 }
256 ]
3fd3ab2d 257 }
7a7724e6 258
3fd3ab2d
C
259 return AccountModel.findOne(query)
260 }
7a7724e6 261
3fd3ab2d
C
262 static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
263 const query = {
fadf619a
C
264 include: [
265 {
266 model: ActorModel,
267 required: true,
268 where: {
269 url
270 }
271 }
272 ],
3fd3ab2d
C
273 transaction
274 }
e4f97bab 275
3fd3ab2d
C
276 return AccountModel.findOne(query)
277 }
e4f97bab 278
265ba139
C
279 static listForApi (start: number, count: number, sort: string) {
280 const query = {
281 offset: start,
282 limit: count,
6ff9c676 283 order: getSort(sort)
265ba139
C
284 }
285
286 return AccountModel.findAndCountAll(query)
c5a893d5
C
287 .then(({ rows, count }) => {
288 return {
289 data: rows,
290 total: count
291 }
292 })
265ba139
C
293 }
294
2feebf3e
C
295 static listLocalsForSitemap (sort: string) {
296 const query = {
297 attributes: [ ],
298 offset: 0,
299 order: getSort(sort),
300 include: [
301 {
302 attributes: [ 'preferredUsername', 'serverId' ],
303 model: ActorModel.unscoped(),
304 where: {
305 serverId: null
306 }
307 }
308 ]
309 }
310
311 return AccountModel
312 .unscoped()
313 .findAll(query)
314 }
315
c5911fd3 316 toFormattedJSON (): Account {
fadf619a
C
317 const actor = this.Actor.toFormattedJSON()
318 const account = {
3fd3ab2d 319 id: this.id,
244e76a5 320 displayName: this.getDisplayName(),
2422c46b 321 description: this.description,
3fd3ab2d 322 createdAt: this.createdAt,
79bd2632
C
323 updatedAt: this.updatedAt,
324 userId: this.userId ? this.userId : undefined
3fd3ab2d 325 }
fadf619a
C
326
327 return Object.assign(actor, account)
3fd3ab2d 328 }
e4f97bab 329
418d092a
C
330 toFormattedSummaryJSON (): AccountSummary {
331 const actor = this.Actor.toFormattedJSON()
332
333 return {
334 id: this.id,
335 uuid: actor.uuid,
336 name: actor.name,
337 displayName: this.getDisplayName(),
338 url: actor.url,
339 host: actor.host,
340 avatar: actor.avatar
341 }
342 }
343
3fd3ab2d 344 toActivityPubObject () {
2422c46b
C
345 const obj = this.Actor.toActivityPubObject(this.name, 'Account')
346
347 return Object.assign(obj, {
348 summary: this.description
349 })
e4f97bab
C
350 }
351
3fd3ab2d 352 isOwned () {
fadf619a 353 return this.Actor.isOwned()
3fd3ab2d 354 }
244e76a5 355
744d0eca
C
356 isOutdated () {
357 return this.Actor.isOutdated()
358 }
359
244e76a5
RK
360 getDisplayName () {
361 return this.name
362 }
63c93323 363}