]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/models/account/account.ts
Move config in its own file
[github/Chocobozzz/PeerTube.git] / server / models / account / account.ts
1 import * as Sequelize from 'sequelize'
2 import {
3 AllowNull,
4 BeforeDestroy,
5 BelongsTo,
6 Column,
7 CreatedAt,
8 Default,
9 DefaultScope,
10 ForeignKey,
11 HasMany,
12 Is,
13 Model,
14 Scopes,
15 Table,
16 UpdatedAt
17 } from 'sequelize-typescript'
18 import { Account, AccountSummary } from '../../../shared/models/actors'
19 import { isAccountDescriptionValid } from '../../helpers/custom-validators/accounts'
20 import { sendDeleteActor } from '../../lib/activitypub/send'
21 import { ActorModel } from '../activitypub/actor'
22 import { ApplicationModel } from '../application/application'
23 import { ServerModel } from '../server/server'
24 import { getSort, throwIfNotValid } from '../utils'
25 import { VideoChannelModel } from '../video/video-channel'
26 import { VideoCommentModel } from '../video/video-comment'
27 import { UserModel } from './user'
28 import { AvatarModel } from '../avatar/avatar'
29 import { VideoPlaylistModel } from '../video/video-playlist'
30 import { WEBSERVER } from '../../initializers/constants'
31
32 export enum ScopeNames {
33 SUMMARY = 'SUMMARY'
34 }
35
36 @DefaultScope({
37 include: [
38 {
39 model: () => ActorModel, // Default scope includes avatar and server
40 required: true
41 }
42 ]
43 })
44 @Scopes({
45 [ ScopeNames.SUMMARY ]: (whereActor?: Sequelize.WhereOptions<ActorModel>) => {
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 })
70 @Table({
71 tableName: 'account',
72 indexes: [
73 {
74 fields: [ 'actorId' ],
75 unique: true
76 },
77 {
78 fields: [ 'applicationId' ]
79 },
80 {
81 fields: [ 'userId' ]
82 }
83 ]
84 })
85 export class AccountModel extends Model<AccountModel> {
86
87 @AllowNull(false)
88 @Column
89 name: string
90
91 @AllowNull(true)
92 @Default(null)
93 @Is('AccountDescription', value => throwIfNotValid(value, isAccountDescriptionValid, 'description'))
94 @Column
95 description: string
96
97 @CreatedAt
98 createdAt: Date
99
100 @UpdatedAt
101 updatedAt: Date
102
103 @ForeignKey(() => ActorModel)
104 @Column
105 actorId: number
106
107 @BelongsTo(() => ActorModel, {
108 foreignKey: {
109 allowNull: false
110 },
111 onDelete: 'cascade'
112 })
113 Actor: ActorModel
114
115 @ForeignKey(() => UserModel)
116 @Column
117 userId: number
118
119 @BelongsTo(() => UserModel, {
120 foreignKey: {
121 allowNull: true
122 },
123 onDelete: 'cascade'
124 })
125 User: UserModel
126
127 @ForeignKey(() => ApplicationModel)
128 @Column
129 applicationId: number
130
131 @BelongsTo(() => ApplicationModel, {
132 foreignKey: {
133 allowNull: true
134 },
135 onDelete: 'cascade'
136 })
137 Application: ApplicationModel
138
139 @HasMany(() => VideoChannelModel, {
140 foreignKey: {
141 allowNull: false
142 },
143 onDelete: 'cascade',
144 hooks: true
145 })
146 VideoChannels: VideoChannelModel[]
147
148 @HasMany(() => VideoPlaylistModel, {
149 foreignKey: {
150 allowNull: false
151 },
152 onDelete: 'cascade',
153 hooks: true
154 })
155 VideoPlaylists: VideoPlaylistModel[]
156
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
172 if (instance.isOwned()) {
173 return sendDeleteActor(instance.Actor, options.transaction)
174 }
175
176 return undefined
177 }
178
179 static load (id: number, transaction?: Sequelize.Transaction) {
180 return AccountModel.findByPk(id, { transaction })
181 }
182
183 static loadByUUID (uuid: string) {
184 const query = {
185 include: [
186 {
187 model: ActorModel,
188 required: true,
189 where: {
190 uuid
191 }
192 }
193 ]
194 }
195
196 return AccountModel.findOne(query)
197 }
198
199 static loadByNameWithHost (nameWithHost: string) {
200 const [ accountName, host ] = nameWithHost.split('@')
201
202 if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName)
203
204 return AccountModel.loadByNameAndHost(accountName, host)
205 }
206
207 static loadLocalByName (name: string) {
208 const query = {
209 where: {
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 ]
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
237 static loadByNameAndHost (name: string, host: string) {
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 ]
257 }
258
259 return AccountModel.findOne(query)
260 }
261
262 static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
263 const query = {
264 include: [
265 {
266 model: ActorModel,
267 required: true,
268 where: {
269 url
270 }
271 }
272 ],
273 transaction
274 }
275
276 return AccountModel.findOne(query)
277 }
278
279 static listForApi (start: number, count: number, sort: string) {
280 const query = {
281 offset: start,
282 limit: count,
283 order: getSort(sort)
284 }
285
286 return AccountModel.findAndCountAll(query)
287 .then(({ rows, count }) => {
288 return {
289 data: rows,
290 total: count
291 }
292 })
293 }
294
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
316 toFormattedJSON (): Account {
317 const actor = this.Actor.toFormattedJSON()
318 const account = {
319 id: this.id,
320 displayName: this.getDisplayName(),
321 description: this.description,
322 createdAt: this.createdAt,
323 updatedAt: this.updatedAt,
324 userId: this.userId ? this.userId : undefined
325 }
326
327 return Object.assign(actor, account)
328 }
329
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
344 toActivityPubObject () {
345 const obj = this.Actor.toActivityPubObject(this.name, 'Account')
346
347 return Object.assign(obj, {
348 summary: this.description
349 })
350 }
351
352 isOwned () {
353 return this.Actor.isOwned()
354 }
355
356 isOutdated () {
357 return this.Actor.isOutdated()
358 }
359
360 getDisplayName () {
361 return this.name
362 }
363 }