aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/activitypub/actors/updater.ts
blob: 471688f11feec66a825567c78b6a87286608cb18 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import { resetSequelizeInstance } from '@server/helpers/database-utils'
import { logger } from '@server/helpers/logger'
import { sequelizeTypescript } from '@server/initializers/database'
import { VideoChannelModel } from '@server/models/video/video-channel'
import { MAccount, MActor, MActorFull, MChannel } from '@server/types/models'
import { ActivityPubActor, ActorImageType } from '@shared/models'
import { updateActorImageInstance } from './image'
import { fetchActorFollowsCount } from './shared'
import { getImageInfoFromObject } from './shared/object-to-model-attributes'

export class APActorUpdater {

  private accountOrChannel: MAccount | MChannel

  private readonly actorFieldsSave: object
  private readonly accountOrChannelFieldsSave: object

  constructor (
    private readonly actorObject: ActivityPubActor,
    private readonly actor: MActorFull
  ) {
    this.actorFieldsSave = this.actor.toJSON()

    if (this.actorObject.type === 'Group') this.accountOrChannel = this.actor.VideoChannel
    else this.accountOrChannel = this.actor.Account

    this.accountOrChannelFieldsSave = this.accountOrChannel.toJSON()
  }

  async update () {
    const avatarInfo = getImageInfoFromObject(this.actorObject, ActorImageType.AVATAR)
    const bannerInfo = getImageInfoFromObject(this.actorObject, ActorImageType.BANNER)

    try {
      await sequelizeTypescript.transaction(async t => {
        await this.updateActorInstance(this.actor, this.actorObject)

        await updateActorImageInstance(this.actor, ActorImageType.AVATAR, avatarInfo, t)
        await updateActorImageInstance(this.actor, ActorImageType.BANNER, bannerInfo, t)

        await this.actor.save({ transaction: t })

        this.accountOrChannel.name = this.actorObject.name || this.actorObject.preferredUsername
        this.accountOrChannel.description = this.actorObject.summary

        if (this.accountOrChannel instanceof VideoChannelModel) this.accountOrChannel.support = this.actorObject.support

        await this.accountOrChannel.save({ transaction: t })
      })

      logger.info('Remote account %s updated', this.actorObject.url)
    } catch (err) {
      if (this.actor !== undefined && this.actorFieldsSave !== undefined) {
        resetSequelizeInstance(this.actor, this.actorFieldsSave)
      }

      if (this.accountOrChannel !== undefined && this.accountOrChannelFieldsSave !== undefined) {
        resetSequelizeInstance(this.accountOrChannel, this.accountOrChannelFieldsSave)
      }

      // This is just a debug because we will retry the insert
      logger.debug('Cannot update the remote account.', { err })
      throw err
    }
  }

  private async updateActorInstance (actorInstance: MActor, actorObject: ActivityPubActor) {
    const { followersCount, followingCount } = await fetchActorFollowsCount(actorObject)

    actorInstance.type = actorObject.type
    actorInstance.preferredUsername = actorObject.preferredUsername
    actorInstance.url = actorObject.id
    actorInstance.publicKey = actorObject.publicKey.publicKeyPem
    actorInstance.followersCount = followersCount
    actorInstance.followingCount = followingCount
    actorInstance.inboxUrl = actorObject.inbox
    actorInstance.outboxUrl = actorObject.outbox
    actorInstance.followersUrl = actorObject.followers
    actorInstance.followingUrl = actorObject.following

    if (actorObject.published) actorInstance.remoteCreatedAt = new Date(actorObject.published)

    if (actorObject.endpoints?.sharedInbox) {
      actorInstance.sharedInboxUrl = actorObject.endpoints.sharedInbox
    }

    // Force actor update
    actorInstance.changed('updatedAt', true)
  }
}