process-update.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import { ActivityUpdate, CacheFileObject, VideoTorrentObject } from '../../../../shared/models/activitypub'
  2. import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor'
  3. import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils'
  4. import { logger } from '../../../helpers/logger'
  5. import { sequelizeTypescript } from '../../../initializers'
  6. import { AccountModel } from '../../../models/account/account'
  7. import { ActorModel } from '../../../models/activitypub/actor'
  8. import { VideoChannelModel } from '../../../models/video/video-channel'
  9. import { fetchAvatarIfExists, updateActorAvatarInstance, updateActorInstance } from '../actor'
  10. import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos'
  11. import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos'
  12. import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file'
  13. import { createOrUpdateCacheFile } from '../cache-file'
  14. import { forwardVideoRelatedActivity } from '../send/utils'
  15. import { PlaylistObject } from '../../../../shared/models/activitypub/objects/playlist-object'
  16. import { createOrUpdateVideoPlaylist } from '../playlist'
  17. async function processUpdateActivity (activity: ActivityUpdate, byActor: ActorModel) {
  18. const objectType = activity.object.type
  19. if (objectType === 'Video') {
  20. return retryTransactionWrapper(processUpdateVideo, byActor, activity)
  21. }
  22. if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') {
  23. // We need more attributes
  24. const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url)
  25. return retryTransactionWrapper(processUpdateActor, byActorFull, activity)
  26. }
  27. if (objectType === 'CacheFile') {
  28. // We need more attributes
  29. const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url)
  30. return retryTransactionWrapper(processUpdateCacheFile, byActorFull, activity)
  31. }
  32. if (objectType === 'Playlist') {
  33. return retryTransactionWrapper(processUpdatePlaylist, byActor, activity)
  34. }
  35. return undefined
  36. }
  37. // ---------------------------------------------------------------------------
  38. export {
  39. processUpdateActivity
  40. }
  41. // ---------------------------------------------------------------------------
  42. async function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) {
  43. const videoObject = activity.object as VideoTorrentObject
  44. if (sanitizeAndCheckVideoTorrentObject(videoObject) === false) {
  45. logger.debug('Video sent by update is not valid.', { videoObject })
  46. return undefined
  47. }
  48. const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoObject.id, allowRefresh: false })
  49. const channelActor = await getOrCreateVideoChannelFromVideoObject(videoObject)
  50. const updateOptions = {
  51. video,
  52. videoObject,
  53. account: actor.Account,
  54. channel: channelActor.VideoChannel,
  55. overrideTo: activity.to
  56. }
  57. return updateVideoFromAP(updateOptions)
  58. }
  59. async function processUpdateCacheFile (byActor: ActorModel, activity: ActivityUpdate) {
  60. const cacheFileObject = activity.object as CacheFileObject
  61. if (!isCacheFileObjectValid(cacheFileObject)) {
  62. logger.debug('Cache file object sent by update is not valid.', { cacheFileObject })
  63. return undefined
  64. }
  65. const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object })
  66. await sequelizeTypescript.transaction(async t => {
  67. await createOrUpdateCacheFile(cacheFileObject, video, byActor, t)
  68. })
  69. if (video.isOwned()) {
  70. // Don't resend the activity to the sender
  71. const exceptions = [ byActor ]
  72. await forwardVideoRelatedActivity(activity, undefined, exceptions, video)
  73. }
  74. }
  75. async function processUpdateActor (actor: ActorModel, activity: ActivityUpdate) {
  76. const actorAttributesToUpdate = activity.object as ActivityPubActor
  77. logger.debug('Updating remote account "%s".', actorAttributesToUpdate.url)
  78. let accountOrChannelInstance: AccountModel | VideoChannelModel
  79. let actorFieldsSave: object
  80. let accountOrChannelFieldsSave: object
  81. // Fetch icon?
  82. const avatarName = await fetchAvatarIfExists(actorAttributesToUpdate)
  83. try {
  84. await sequelizeTypescript.transaction(async t => {
  85. actorFieldsSave = actor.toJSON()
  86. if (actorAttributesToUpdate.type === 'Group') accountOrChannelInstance = actor.VideoChannel
  87. else accountOrChannelInstance = actor.Account
  88. accountOrChannelFieldsSave = accountOrChannelInstance.toJSON()
  89. await updateActorInstance(actor, actorAttributesToUpdate)
  90. if (avatarName !== undefined) {
  91. await updateActorAvatarInstance(actor, avatarName, t)
  92. }
  93. await actor.save({ transaction: t })
  94. accountOrChannelInstance.name = actorAttributesToUpdate.name || actorAttributesToUpdate.preferredUsername
  95. accountOrChannelInstance.description = actorAttributesToUpdate.summary
  96. if (accountOrChannelInstance instanceof VideoChannelModel) accountOrChannelInstance.support = actorAttributesToUpdate.support
  97. await accountOrChannelInstance.save({ transaction: t })
  98. })
  99. logger.info('Remote account %s updated', actorAttributesToUpdate.url)
  100. } catch (err) {
  101. if (actor !== undefined && actorFieldsSave !== undefined) {
  102. resetSequelizeInstance(actor, actorFieldsSave)
  103. }
  104. if (accountOrChannelInstance !== undefined && accountOrChannelFieldsSave !== undefined) {
  105. resetSequelizeInstance(accountOrChannelInstance, accountOrChannelFieldsSave)
  106. }
  107. // This is just a debug because we will retry the insert
  108. logger.debug('Cannot update the remote account.', { err })
  109. throw err
  110. }
  111. }
  112. async function processUpdatePlaylist (byActor: ActorModel, activity: ActivityUpdate) {
  113. const playlistObject = activity.object as PlaylistObject
  114. const byAccount = byActor.Account
  115. if (!byAccount) throw new Error('Cannot update video playlist with the non account actor ' + byActor.url)
  116. await createOrUpdateVideoPlaylist(playlistObject, byAccount, activity.to)
  117. }