1 import { join } from 'path'
2 import * as Sequelize from 'sequelize'
18 } from 'sequelize-typescript'
19 import { Avatar } from '../../../shared/models/avatars/avatar.model'
20 import { activityPubContextify } from '../../helpers'
22 isAccountFollowersCountValid,
23 isAccountFollowingCountValid,
24 isAccountPrivateKeyValid,
25 isAccountPublicKeyValid,
27 } from '../../helpers/custom-validators/activitypub'
28 import { isUserUsernameValid } from '../../helpers/custom-validators/users'
29 import { AVATARS_DIR, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers'
30 import { sendDeleteAccount } from '../../lib/activitypub/send'
31 import { ApplicationModel } from '../application/application'
32 import { AvatarModel } from '../avatar/avatar'
33 import { ServerModel } from '../server/server'
34 import { throwIfNotValid } from '../utils'
35 import { VideoChannelModel } from '../video/video-channel'
36 import { AccountFollowModel } from './account-follow'
37 import { UserModel } from './user'
46 fields: [ 'serverId' ]
53 fields: [ 'applicationId' ],
57 fields: [ 'name', 'serverId', 'applicationId' ],
62 export class AccountModel extends Model<Account> {
65 @Default(DataType.UUIDV4)
67 @Column(DataType.UUID)
71 @Is('AccountName', value => throwIfNotValid(value, isUserUsernameValid, 'account name'))
76 @Is('AccountUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url'))
77 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
81 @Is('AccountPublicKey', value => throwIfNotValid(value, isAccountPublicKeyValid, 'public key'))
82 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY.max))
86 @Is('AccountPublicKey', value => throwIfNotValid(value, isAccountPrivateKeyValid, 'private key'))
87 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PRIVATE_KEY.max))
91 @Is('AccountFollowersCount', value => throwIfNotValid(value, isAccountFollowersCountValid, 'followers count'))
93 followersCount: number
96 @Is('AccountFollowersCount', value => throwIfNotValid(value, isAccountFollowingCountValid, 'following count'))
98 followingCount: number
101 @Is('AccountInboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'inbox url'))
102 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
106 @Is('AccountOutboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'outbox url'))
107 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
111 @Is('AccountSharedInboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'shared inbox url'))
112 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
113 sharedInboxUrl: string
116 @Is('AccountFollowersUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'followers url'))
117 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
121 @Is('AccountFollowingUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'following url'))
122 @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max))
131 @ForeignKey(() => AvatarModel)
135 @BelongsTo(() => AvatarModel, {
143 @ForeignKey(() => ServerModel)
147 @BelongsTo(() => ServerModel, {
155 @ForeignKey(() => UserModel)
159 @BelongsTo(() => UserModel, {
167 @ForeignKey(() => ApplicationModel)
169 applicationId: number
171 @BelongsTo(() => ApplicationModel, {
177 Application: ApplicationModel
179 @HasMany(() => VideoChannelModel, {
186 VideoChannels: VideoChannelModel[]
188 @HasMany(() => AccountFollowModel, {
195 AccountFollowing: AccountFollowModel[]
197 @HasMany(() => AccountFollowModel, {
199 name: 'targetAccountId',
205 AccountFollowers: AccountFollowModel[]
208 static sendDeleteIfOwned (instance: AccountModel) {
209 if (instance.isOwned()) {
210 return sendDeleteAccount(instance, undefined)
216 static loadApplication () {
217 return AccountModel.findOne({
220 model: ApplicationModel,
227 static load (id: number) {
228 return AccountModel.findById(id)
231 static loadByUUID (uuid: string) {
238 return AccountModel.findOne(query)
241 static loadLocalByName (name: string) {
245 [ Sequelize.Op.or ]: [
248 [ Sequelize.Op.ne ]: null
253 [ Sequelize.Op.ne ]: null
260 return AccountModel.findOne(query)
263 static loadByNameAndHost (name: string, host: string) {
279 return AccountModel.findOne(query)
282 static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
290 return AccountModel.findOne(query)
293 static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) {
297 [ Sequelize.Op.in ]: followersUrls
303 return AccountModel.findAll(query)
307 let host = CONFIG.WEBSERVER.HOST
309 let avatar: Avatar = null
313 path: join(AVATARS_DIR.ACCOUNT, this.Avatar.filename),
314 createdAt: this.Avatar.createdAt,
315 updatedAt: this.Avatar.updatedAt
320 host = this.Server.host
321 score = this.Server.score
330 followingCount: this.followingCount,
331 followersCount: this.followersCount,
332 createdAt: this.createdAt,
333 updatedAt: this.updatedAt,
338 toActivityPubObject () {
339 const type = this.serverId ? 'Application' as 'Application' : 'Person' as 'Person'
344 following: this.getFollowingUrl(),
345 followers: this.getFollowersUrl(),
346 inbox: this.inboxUrl,
347 outbox: this.outboxUrl,
348 preferredUsername: this.name,
352 sharedInbox: this.sharedInboxUrl
356 id: this.getPublicKeyUrl(),
358 publicKeyPem: this.publicKey
362 return activityPubContextify(json)
366 return this.serverId === null
369 getFollowerSharedInboxUrls (t: Sequelize.Transaction) {
371 attributes: [ 'sharedInboxUrl' ],
374 model: AccountFollowModel,
378 targetAccountId: this.id
385 return AccountModel.findAll(query)
386 .then(accounts => accounts.map(a => a.sharedInboxUrl))
390 return this.url + '/following'
394 return this.url + '/followers'
398 return this.url + '#main-key'