diff options
Diffstat (limited to 'server/models/account/account.ts')
-rw-r--r-- | server/models/account/account.ts | 234 |
1 files changed, 33 insertions, 201 deletions
diff --git a/server/models/account/account.ts b/server/models/account/account.ts index d6758fa10..b26395fd4 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts | |||
@@ -1,4 +1,3 @@ | |||
1 | import { join } from 'path' | ||
2 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
3 | import { | 2 | import { |
4 | AfterDestroy, | 3 | AfterDestroy, |
@@ -16,24 +15,13 @@ import { | |||
16 | Table, | 15 | Table, |
17 | UpdatedAt | 16 | UpdatedAt |
18 | } from 'sequelize-typescript' | 17 | } from 'sequelize-typescript' |
19 | import { Avatar } from '../../../shared/models/avatars/avatar.model' | ||
20 | import { activityPubContextify } from '../../helpers' | ||
21 | import { | ||
22 | isAccountFollowersCountValid, | ||
23 | isAccountFollowingCountValid, | ||
24 | isAccountPrivateKeyValid, | ||
25 | isAccountPublicKeyValid, | ||
26 | isActivityPubUrlValid | ||
27 | } from '../../helpers/custom-validators/activitypub' | ||
28 | import { isUserUsernameValid } from '../../helpers/custom-validators/users' | 18 | import { isUserUsernameValid } from '../../helpers/custom-validators/users' |
29 | import { AVATARS_DIR, CONFIG, CONSTRAINTS_FIELDS } from '../../initializers' | ||
30 | import { sendDeleteAccount } from '../../lib/activitypub/send' | 19 | import { sendDeleteAccount } from '../../lib/activitypub/send' |
20 | import { ActorModel } from '../activitypub/actor' | ||
31 | import { ApplicationModel } from '../application/application' | 21 | import { ApplicationModel } from '../application/application' |
32 | import { AvatarModel } from '../avatar/avatar' | ||
33 | import { ServerModel } from '../server/server' | 22 | import { ServerModel } from '../server/server' |
34 | import { throwIfNotValid } from '../utils' | 23 | import { throwIfNotValid } from '../utils' |
35 | import { VideoChannelModel } from '../video/video-channel' | 24 | import { VideoChannelModel } from '../video/video-channel' |
36 | import { AccountFollowModel } from './account-follow' | ||
37 | import { UserModel } from './user' | 25 | import { UserModel } from './user' |
38 | 26 | ||
39 | @Table({ | 27 | @Table({ |
@@ -59,68 +47,7 @@ import { UserModel } from './user' | |||
59 | } | 47 | } |
60 | ] | 48 | ] |
61 | }) | 49 | }) |
62 | export class AccountModel extends Model<Account> { | 50 | export class AccountModel extends Model<AccountModel> { |
63 | |||
64 | @AllowNull(false) | ||
65 | @Default(DataType.UUIDV4) | ||
66 | @IsUUID(4) | ||
67 | @Column(DataType.UUID) | ||
68 | uuid: string | ||
69 | |||
70 | @AllowNull(false) | ||
71 | @Is('AccountName', value => throwIfNotValid(value, isUserUsernameValid, 'account name')) | ||
72 | @Column | ||
73 | name: string | ||
74 | |||
75 | @AllowNull(false) | ||
76 | @Is('AccountUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'url')) | ||
77 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max)) | ||
78 | url: string | ||
79 | |||
80 | @AllowNull(true) | ||
81 | @Is('AccountPublicKey', value => throwIfNotValid(value, isAccountPublicKeyValid, 'public key')) | ||
82 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PUBLIC_KEY.max)) | ||
83 | publicKey: string | ||
84 | |||
85 | @AllowNull(true) | ||
86 | @Is('AccountPublicKey', value => throwIfNotValid(value, isAccountPrivateKeyValid, 'private key')) | ||
87 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.PRIVATE_KEY.max)) | ||
88 | privateKey: string | ||
89 | |||
90 | @AllowNull(false) | ||
91 | @Is('AccountFollowersCount', value => throwIfNotValid(value, isAccountFollowersCountValid, 'followers count')) | ||
92 | @Column | ||
93 | followersCount: number | ||
94 | |||
95 | @AllowNull(false) | ||
96 | @Is('AccountFollowersCount', value => throwIfNotValid(value, isAccountFollowingCountValid, 'following count')) | ||
97 | @Column | ||
98 | followingCount: number | ||
99 | |||
100 | @AllowNull(false) | ||
101 | @Is('AccountInboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'inbox url')) | ||
102 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max)) | ||
103 | inboxUrl: string | ||
104 | |||
105 | @AllowNull(false) | ||
106 | @Is('AccountOutboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'outbox url')) | ||
107 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max)) | ||
108 | outboxUrl: string | ||
109 | |||
110 | @AllowNull(false) | ||
111 | @Is('AccountSharedInboxUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'shared inbox url')) | ||
112 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max)) | ||
113 | sharedInboxUrl: string | ||
114 | |||
115 | @AllowNull(false) | ||
116 | @Is('AccountFollowersUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'followers url')) | ||
117 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max)) | ||
118 | followersUrl: string | ||
119 | |||
120 | @AllowNull(false) | ||
121 | @Is('AccountFollowingUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'following url')) | ||
122 | @Column(DataType.STRING(CONSTRAINTS_FIELDS.ACCOUNTS.URL.max)) | ||
123 | followingUrl: string | ||
124 | 51 | ||
125 | @CreatedAt | 52 | @CreatedAt |
126 | createdAt: Date | 53 | createdAt: Date |
@@ -128,29 +55,17 @@ export class AccountModel extends Model<Account> { | |||
128 | @UpdatedAt | 55 | @UpdatedAt |
129 | updatedAt: Date | 56 | updatedAt: Date |
130 | 57 | ||
131 | @ForeignKey(() => AvatarModel) | 58 | @ForeignKey(() => ActorModel) |
132 | @Column | ||
133 | avatarId: number | ||
134 | |||
135 | @BelongsTo(() => AvatarModel, { | ||
136 | foreignKey: { | ||
137 | allowNull: true | ||
138 | }, | ||
139 | onDelete: 'cascade' | ||
140 | }) | ||
141 | Avatar: AvatarModel | ||
142 | |||
143 | @ForeignKey(() => ServerModel) | ||
144 | @Column | 59 | @Column |
145 | serverId: number | 60 | actorId: number |
146 | 61 | ||
147 | @BelongsTo(() => ServerModel, { | 62 | @BelongsTo(() => ActorModel, { |
148 | foreignKey: { | 63 | foreignKey: { |
149 | allowNull: true | 64 | allowNull: false |
150 | }, | 65 | }, |
151 | onDelete: 'cascade' | 66 | onDelete: 'cascade' |
152 | }) | 67 | }) |
153 | Server: ServerModel | 68 | Actor: ActorModel |
154 | 69 | ||
155 | @ForeignKey(() => UserModel) | 70 | @ForeignKey(() => UserModel) |
156 | @Column | 71 | @Column |
@@ -185,25 +100,6 @@ export class AccountModel extends Model<Account> { | |||
185 | }) | 100 | }) |
186 | VideoChannels: VideoChannelModel[] | 101 | VideoChannels: VideoChannelModel[] |
187 | 102 | ||
188 | @HasMany(() => AccountFollowModel, { | ||
189 | foreignKey: { | ||
190 | name: 'accountId', | ||
191 | allowNull: false | ||
192 | }, | ||
193 | onDelete: 'cascade' | ||
194 | }) | ||
195 | AccountFollowing: AccountFollowModel[] | ||
196 | |||
197 | @HasMany(() => AccountFollowModel, { | ||
198 | foreignKey: { | ||
199 | name: 'targetAccountId', | ||
200 | allowNull: false | ||
201 | }, | ||
202 | as: 'followers', | ||
203 | onDelete: 'cascade' | ||
204 | }) | ||
205 | AccountFollowers: AccountFollowModel[] | ||
206 | |||
207 | @AfterDestroy | 103 | @AfterDestroy |
208 | static sendDeleteIfOwned (instance: AccountModel) { | 104 | static sendDeleteIfOwned (instance: AccountModel) { |
209 | if (instance.isOwned()) { | 105 | if (instance.isOwned()) { |
@@ -281,9 +177,15 @@ export class AccountModel extends Model<Account> { | |||
281 | 177 | ||
282 | static loadByUrl (url: string, transaction?: Sequelize.Transaction) { | 178 | static loadByUrl (url: string, transaction?: Sequelize.Transaction) { |
283 | const query = { | 179 | const query = { |
284 | where: { | 180 | include: [ |
285 | url | 181 | { |
286 | }, | 182 | model: ActorModel, |
183 | required: true, | ||
184 | where: { | ||
185 | url | ||
186 | } | ||
187 | } | ||
188 | ], | ||
287 | transaction | 189 | transaction |
288 | } | 190 | } |
289 | 191 | ||
@@ -292,11 +194,17 @@ export class AccountModel extends Model<Account> { | |||
292 | 194 | ||
293 | static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) { | 195 | static listByFollowersUrls (followersUrls: string[], transaction?: Sequelize.Transaction) { |
294 | const query = { | 196 | const query = { |
295 | where: { | 197 | include: [ |
296 | followersUrl: { | 198 | { |
297 | [ Sequelize.Op.in ]: followersUrls | 199 | model: ActorModel, |
200 | required: true, | ||
201 | where: { | ||
202 | followersUrl: { | ||
203 | [ Sequelize.Op.in ]: followersUrls | ||
204 | } | ||
205 | } | ||
298 | } | 206 | } |
299 | }, | 207 | ], |
300 | transaction | 208 | transaction |
301 | } | 209 | } |
302 | 210 | ||
@@ -304,97 +212,21 @@ export class AccountModel extends Model<Account> { | |||
304 | } | 212 | } |
305 | 213 | ||
306 | toFormattedJSON () { | 214 | toFormattedJSON () { |
307 | let host = CONFIG.WEBSERVER.HOST | 215 | const actor = this.Actor.toFormattedJSON() |
308 | let score: number | 216 | const account = { |
309 | let avatar: Avatar = null | ||
310 | |||
311 | if (this.Avatar) { | ||
312 | avatar = { | ||
313 | path: join(AVATARS_DIR.ACCOUNT, this.Avatar.filename), | ||
314 | createdAt: this.Avatar.createdAt, | ||
315 | updatedAt: this.Avatar.updatedAt | ||
316 | } | ||
317 | } | ||
318 | |||
319 | if (this.Server) { | ||
320 | host = this.Server.host | ||
321 | score = this.Server.score | ||
322 | } | ||
323 | |||
324 | return { | ||
325 | id: this.id, | 217 | id: this.id, |
326 | uuid: this.uuid, | ||
327 | host, | ||
328 | score, | ||
329 | name: this.name, | ||
330 | followingCount: this.followingCount, | ||
331 | followersCount: this.followersCount, | ||
332 | createdAt: this.createdAt, | 218 | createdAt: this.createdAt, |
333 | updatedAt: this.updatedAt, | 219 | updatedAt: this.updatedAt |
334 | avatar | ||
335 | } | 220 | } |
221 | |||
222 | return Object.assign(actor, account) | ||
336 | } | 223 | } |
337 | 224 | ||
338 | toActivityPubObject () { | 225 | toActivityPubObject () { |
339 | const type = this.serverId ? 'Application' as 'Application' : 'Person' as 'Person' | 226 | return this.Actor.toActivityPubObject(this.name, this.uuid, 'Account') |
340 | |||
341 | const json = { | ||
342 | type, | ||
343 | id: this.url, | ||
344 | following: this.getFollowingUrl(), | ||
345 | followers: this.getFollowersUrl(), | ||
346 | inbox: this.inboxUrl, | ||
347 | outbox: this.outboxUrl, | ||
348 | preferredUsername: this.name, | ||
349 | url: this.url, | ||
350 | name: this.name, | ||
351 | endpoints: { | ||
352 | sharedInbox: this.sharedInboxUrl | ||
353 | }, | ||
354 | uuid: this.uuid, | ||
355 | publicKey: { | ||
356 | id: this.getPublicKeyUrl(), | ||
357 | owner: this.url, | ||
358 | publicKeyPem: this.publicKey | ||
359 | } | ||
360 | } | ||
361 | |||
362 | return activityPubContextify(json) | ||
363 | } | 227 | } |
364 | 228 | ||
365 | isOwned () { | 229 | isOwned () { |
366 | return this.serverId === null | 230 | return this.Actor.isOwned() |
367 | } | ||
368 | |||
369 | getFollowerSharedInboxUrls (t: Sequelize.Transaction) { | ||
370 | const query = { | ||
371 | attributes: [ 'sharedInboxUrl' ], | ||
372 | include: [ | ||
373 | { | ||
374 | model: AccountFollowModel, | ||
375 | required: true, | ||
376 | as: 'followers', | ||
377 | where: { | ||
378 | targetAccountId: this.id | ||
379 | } | ||
380 | } | ||
381 | ], | ||
382 | transaction: t | ||
383 | } | ||
384 | |||
385 | return AccountModel.findAll(query) | ||
386 | .then(accounts => accounts.map(a => a.sharedInboxUrl)) | ||
387 | } | ||
388 | |||
389 | getFollowingUrl () { | ||
390 | return this.url + '/following' | ||
391 | } | ||
392 | |||
393 | getFollowersUrl () { | ||
394 | return this.url + '/followers' | ||
395 | } | ||
396 | |||
397 | getPublicKeyUrl () { | ||
398 | return this.url + '#main-key' | ||
399 | } | 231 | } |
400 | } | 232 | } |