]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/lib/activitypub/process/process-update.ts
Send account activitypub update events
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / process / process-update.ts
1 import * as Bluebird from 'bluebird'
2 import { ActivityUpdate } from '../../../../shared/models/activitypub'
3 import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor'
4 import { VideoTorrentObject } from '../../../../shared/models/activitypub/objects'
5 import { retryTransactionWrapper } from '../../../helpers/database-utils'
6 import { logger } from '../../../helpers/logger'
7 import { resetSequelizeInstance } from '../../../helpers/utils'
8 import { sequelizeTypescript } from '../../../initializers'
9 import { AccountModel } from '../../../models/account/account'
10 import { ActorModel } from '../../../models/activitypub/actor'
11 import { AvatarModel } from '../../../models/avatar/avatar'
12 import { TagModel } from '../../../models/video/tag'
13 import { VideoModel } from '../../../models/video/video'
14 import { VideoFileModel } from '../../../models/video/video-file'
15 import { fetchActorTotalItems, fetchAvatarIfExists, getOrCreateActorAndServerAndModel } from '../actor'
16 import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
17
18 async function processUpdateActivity (activity: ActivityUpdate) {
19 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
20
21 if (activity.object.type === 'Video') {
22 return processUpdateVideo(actor, activity)
23 } else if (activity.object.type === 'Person') {
24 return processUpdateAccount(actor, activity)
25 }
26
27 return
28 }
29
30 // ---------------------------------------------------------------------------
31
32 export {
33 processUpdateActivity
34 }
35
36 // ---------------------------------------------------------------------------
37
38 function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) {
39 const options = {
40 arguments: [ actor, activity ],
41 errorMessage: 'Cannot update the remote video with many retries'
42 }
43
44 return retryTransactionWrapper(updateRemoteVideo, options)
45 }
46
47 async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) {
48 const videoAttributesToUpdate = activity.object as VideoTorrentObject
49
50 logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid)
51 let videoInstance: VideoModel
52 let videoFieldsSave: any
53
54 try {
55 await sequelizeTypescript.transaction(async t => {
56 const sequelizeOptions = {
57 transaction: t
58 }
59
60 const videoInstance = await VideoModel.loadByUrlAndPopulateAccount(videoAttributesToUpdate.id, t)
61 if (!videoInstance) throw new Error('Video ' + videoAttributesToUpdate.id + ' not found.')
62
63 videoFieldsSave = videoInstance.toJSON()
64
65 const videoChannel = videoInstance.VideoChannel
66 if (videoChannel.Account.Actor.id !== actor.id) {
67 throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url)
68 }
69
70 const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoAttributesToUpdate, activity.to, activity.cc)
71 videoInstance.set('name', videoData.name)
72 videoInstance.set('category', videoData.category)
73 videoInstance.set('licence', videoData.licence)
74 videoInstance.set('language', videoData.language)
75 videoInstance.set('nsfw', videoData.nsfw)
76 videoInstance.set('commentsEnabled', videoData.commentsEnabled)
77 videoInstance.set('privacy', videoData.privacy)
78 videoInstance.set('description', videoData.description)
79 videoInstance.set('duration', videoData.duration)
80 videoInstance.set('createdAt', videoData.createdAt)
81 videoInstance.set('updatedAt', videoData.updatedAt)
82 videoInstance.set('views', videoData.views)
83
84 await videoInstance.save(sequelizeOptions)
85
86 // Remove old video files
87 const videoFileDestroyTasks: Bluebird<void>[] = []
88 for (const videoFile of videoInstance.VideoFiles) {
89 videoFileDestroyTasks.push(videoFile.destroy(sequelizeOptions))
90 }
91 await Promise.all(videoFileDestroyTasks)
92
93 const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoInstance, videoAttributesToUpdate)
94 const tasks: Bluebird<any>[] = videoFileAttributes.map(f => VideoFileModel.create(f))
95 await Promise.all(tasks)
96
97 const tags = videoAttributesToUpdate.tag.map(t => t.name)
98 const tagInstances = await TagModel.findOrCreateTags(tags, t)
99 await videoInstance.$set('Tags', tagInstances, sequelizeOptions)
100 })
101
102 logger.info('Remote video with uuid %s updated', videoAttributesToUpdate.uuid)
103 } catch (err) {
104 if (videoInstance !== undefined && videoFieldsSave !== undefined) {
105 resetSequelizeInstance(videoInstance, videoFieldsSave)
106 }
107
108 // This is just a debug because we will retry the insert
109 logger.debug('Cannot update the remote video.', err)
110 throw err
111 }
112 }
113
114 function processUpdateAccount (actor: ActorModel, activity: ActivityUpdate) {
115 const options = {
116 arguments: [ actor, activity ],
117 errorMessage: 'Cannot update the remote account with many retries'
118 }
119
120 return retryTransactionWrapper(updateRemoteAccount, options)
121 }
122
123 async function updateRemoteAccount (actor: ActorModel, activity: ActivityUpdate) {
124 const accountAttributesToUpdate = activity.object as ActivityPubActor
125
126 logger.debug('Updating remote account "%s".', accountAttributesToUpdate.uuid)
127 let actorInstance: ActorModel
128 let accountInstance: AccountModel
129 let actorFieldsSave: object
130 let accountFieldsSave: object
131
132 // Fetch icon?
133 const avatarName = await fetchAvatarIfExists(accountAttributesToUpdate)
134
135 try {
136 await sequelizeTypescript.transaction(async t => {
137 actorInstance = await ActorModel.loadByUrl(accountAttributesToUpdate.id, t)
138 if (!actorInstance) throw new Error('Actor ' + accountAttributesToUpdate.id + ' not found.')
139
140 actorFieldsSave = actorInstance.toJSON()
141 accountInstance = actorInstance.Account
142 accountFieldsSave = actorInstance.Account.toJSON()
143
144 const followersCount = await fetchActorTotalItems(accountAttributesToUpdate.followers)
145 const followingCount = await fetchActorTotalItems(accountAttributesToUpdate.following)
146
147 actorInstance.set('type', accountAttributesToUpdate.type)
148 actorInstance.set('uuid', accountAttributesToUpdate.uuid)
149 actorInstance.set('preferredUsername', accountAttributesToUpdate.preferredUsername)
150 actorInstance.set('url', accountAttributesToUpdate.id)
151 actorInstance.set('publicKey', accountAttributesToUpdate.publicKey.publicKeyPem)
152 actorInstance.set('followersCount', followersCount)
153 actorInstance.set('followingCount', followingCount)
154 actorInstance.set('inboxUrl', accountAttributesToUpdate.inbox)
155 actorInstance.set('outboxUrl', accountAttributesToUpdate.outbox)
156 actorInstance.set('sharedInboxUrl', accountAttributesToUpdate.endpoints.sharedInbox)
157 actorInstance.set('followersUrl', accountAttributesToUpdate.followers)
158 actorInstance.set('followingUrl', accountAttributesToUpdate.following)
159
160 if (avatarName !== undefined) {
161 if (actorInstance.avatarId) {
162 await actorInstance.Avatar.destroy({ transaction: t })
163 }
164
165 const avatar = await AvatarModel.create({
166 filename: avatarName
167 }, { transaction: t })
168
169 actor.set('avatarId', avatar.id)
170 }
171
172 await actor.save({ transaction: t })
173
174 actor.Account.set('name', accountAttributesToUpdate.name || accountAttributesToUpdate.preferredUsername)
175 await actor.Account.save({ transaction: t })
176 })
177
178 logger.info('Remote account with uuid %s updated', accountAttributesToUpdate.uuid)
179 } catch (err) {
180 if (actorInstance !== undefined && actorFieldsSave !== undefined) {
181 resetSequelizeInstance(actorInstance, actorFieldsSave)
182 }
183
184 if (accountInstance !== undefined && accountFieldsSave !== undefined) {
185 resetSequelizeInstance(accountInstance, accountFieldsSave)
186 }
187
188 // This is just a debug because we will retry the insert
189 logger.debug('Cannot update the remote account.', err)
190 throw err
191 }
192 }