diff options
Diffstat (limited to 'server/lib/activitypub')
-rw-r--r-- | server/lib/activitypub/actor.ts | 2 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-undo.ts | 25 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-update.ts | 10 | ||||
-rw-r--r-- | server/lib/activitypub/send/send-undo.ts | 29 | ||||
-rw-r--r-- | server/lib/activitypub/share.ts | 45 | ||||
-rw-r--r-- | server/lib/activitypub/videos.ts | 13 |
6 files changed, 101 insertions, 23 deletions
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index b0cf9bb17..5773fc34f 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts | |||
@@ -353,7 +353,7 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu | |||
353 | return videoChannelCreated | 353 | return videoChannelCreated |
354 | } | 354 | } |
355 | 355 | ||
356 | async function refreshActorIfNeeded (actor: ActorModel) { | 356 | async function refreshActorIfNeeded (actor: ActorModel): Promise<ActorModel> { |
357 | if (!actor.isOutdated()) return actor | 357 | if (!actor.isOutdated()) return actor |
358 | 358 | ||
359 | try { | 359 | try { |
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts index 565e70289..9b024d15f 100644 --- a/server/lib/activitypub/process/process-undo.ts +++ b/server/lib/activitypub/process/process-undo.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { ActivityFollow, ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub' | 1 | import { ActivityAnnounce, ActivityFollow, ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub' |
2 | import { DislikeObject } from '../../../../shared/models/activitypub/objects' | 2 | import { DislikeObject } from '../../../../shared/models/activitypub/objects' |
3 | import { getActorUrl } from '../../../helpers/activitypub' | 3 | import { getActorUrl } from '../../../helpers/activitypub' |
4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
@@ -10,6 +10,7 @@ import { ActorModel } from '../../../models/activitypub/actor' | |||
10 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 10 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
11 | import { forwardActivity } from '../send/misc' | 11 | import { forwardActivity } from '../send/misc' |
12 | import { getOrCreateAccountAndVideoAndChannel } from '../videos' | 12 | import { getOrCreateAccountAndVideoAndChannel } from '../videos' |
13 | import { VideoShareModel } from '../../../models/video/video-share' | ||
13 | 14 | ||
14 | async function processUndoActivity (activity: ActivityUndo) { | 15 | async function processUndoActivity (activity: ActivityUndo) { |
15 | const activityToUndo = activity.object | 16 | const activityToUndo = activity.object |
@@ -22,6 +23,8 @@ async function processUndoActivity (activity: ActivityUndo) { | |||
22 | return processUndoDislike(actorUrl, activity) | 23 | return processUndoDislike(actorUrl, activity) |
23 | } else if (activityToUndo.type === 'Follow') { | 24 | } else if (activityToUndo.type === 'Follow') { |
24 | return processUndoFollow(actorUrl, activityToUndo) | 25 | return processUndoFollow(actorUrl, activityToUndo) |
26 | } else if (activityToUndo.type === 'Announce') { | ||
27 | return processUndoAnnounce(actorUrl, activityToUndo) | ||
25 | } | 28 | } |
26 | 29 | ||
27 | logger.warn('Unknown activity object type %s -> %s when undo activity.', activityToUndo.type, { activity: activity.id }) | 30 | logger.warn('Unknown activity object type %s -> %s when undo activity.', activityToUndo.type, { activity: activity.id }) |
@@ -123,3 +126,23 @@ function undoFollow (actorUrl: string, followActivity: ActivityFollow) { | |||
123 | return undefined | 126 | return undefined |
124 | }) | 127 | }) |
125 | } | 128 | } |
129 | |||
130 | function processUndoAnnounce (actorUrl: string, announceActivity: ActivityAnnounce) { | ||
131 | const options = { | ||
132 | arguments: [ actorUrl, announceActivity ], | ||
133 | errorMessage: 'Cannot undo announce with many retries.' | ||
134 | } | ||
135 | |||
136 | return retryTransactionWrapper(undoAnnounce, options) | ||
137 | } | ||
138 | |||
139 | function undoAnnounce (actorUrl: string, announceActivity: ActivityAnnounce) { | ||
140 | return sequelizeTypescript.transaction(async t => { | ||
141 | const share = await VideoShareModel.loadByUrl(announceActivity.id, t) | ||
142 | if (!share) throw new Error(`'Unknown video share ${announceActivity.id}.`) | ||
143 | |||
144 | await share.destroy({ transaction: t }) | ||
145 | |||
146 | return undefined | ||
147 | }) | ||
148 | } | ||
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index 51e3cc4e3..0dd657c2b 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -14,7 +14,7 @@ import { VideoFileModel } from '../../../models/video/video-file' | |||
14 | import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor' | 14 | import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor' |
15 | import { | 15 | import { |
16 | generateThumbnailFromUrl, | 16 | generateThumbnailFromUrl, |
17 | getOrCreateAccountAndVideoAndChannel, | 17 | getOrCreateAccountAndVideoAndChannel, getOrCreateVideoChannel, |
18 | videoActivityObjectToDBAttributes, | 18 | videoActivityObjectToDBAttributes, |
19 | videoFileActivityUrlToDBAttributes | 19 | videoFileActivityUrlToDBAttributes |
20 | } from '../videos' | 20 | } from '../videos' |
@@ -54,6 +54,10 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) { | |||
54 | 54 | ||
55 | const res = await getOrCreateAccountAndVideoAndChannel(videoAttributesToUpdate.id) | 55 | const res = await getOrCreateAccountAndVideoAndChannel(videoAttributesToUpdate.id) |
56 | 56 | ||
57 | // Fetch video channel outside the transaction | ||
58 | const newVideoChannelActor = await getOrCreateVideoChannel(videoAttributesToUpdate) | ||
59 | const newVideoChannel = newVideoChannelActor.VideoChannel | ||
60 | |||
57 | logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) | 61 | logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) |
58 | let videoInstance = res.video | 62 | let videoInstance = res.video |
59 | let videoFieldsSave: any | 63 | let videoFieldsSave: any |
@@ -66,12 +70,13 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) { | |||
66 | 70 | ||
67 | videoFieldsSave = videoInstance.toJSON() | 71 | videoFieldsSave = videoInstance.toJSON() |
68 | 72 | ||
73 | // Check actor has the right to update the video | ||
69 | const videoChannel = videoInstance.VideoChannel | 74 | const videoChannel = videoInstance.VideoChannel |
70 | if (videoChannel.Account.Actor.id !== actor.id) { | 75 | if (videoChannel.Account.Actor.id !== actor.id) { |
71 | throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url) | 76 | throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url) |
72 | } | 77 | } |
73 | 78 | ||
74 | const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoAttributesToUpdate, activity.to) | 79 | const videoData = await videoActivityObjectToDBAttributes(newVideoChannel, videoAttributesToUpdate, activity.to) |
75 | videoInstance.set('name', videoData.name) | 80 | videoInstance.set('name', videoData.name) |
76 | videoInstance.set('uuid', videoData.uuid) | 81 | videoInstance.set('uuid', videoData.uuid) |
77 | videoInstance.set('url', videoData.url) | 82 | videoInstance.set('url', videoData.url) |
@@ -87,6 +92,7 @@ async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) { | |||
87 | videoInstance.set('updatedAt', videoData.updatedAt) | 92 | videoInstance.set('updatedAt', videoData.updatedAt) |
88 | videoInstance.set('views', videoData.views) | 93 | videoInstance.set('views', videoData.views) |
89 | videoInstance.set('privacy', videoData.privacy) | 94 | videoInstance.set('privacy', videoData.privacy) |
95 | videoInstance.set('channelId', videoData.channelId) | ||
90 | 96 | ||
91 | await videoInstance.save(sequelizeOptions) | 97 | await videoInstance.save(sequelizeOptions) |
92 | 98 | ||
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index bd49d452e..adee2192f 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts | |||
@@ -1,5 +1,12 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityCreate, ActivityFollow, ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub' | 2 | import { |
3 | ActivityAnnounce, | ||
4 | ActivityAudience, | ||
5 | ActivityCreate, | ||
6 | ActivityFollow, | ||
7 | ActivityLike, | ||
8 | ActivityUndo | ||
9 | } from '../../../../shared/models/activitypub' | ||
3 | import { ActorModel } from '../../../models/activitypub/actor' | 10 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 11 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
5 | import { VideoModel } from '../../../models/video/video' | 12 | import { VideoModel } from '../../../models/video/video' |
@@ -16,6 +23,8 @@ import { | |||
16 | import { createActivityData, createDislikeActivityData } from './send-create' | 23 | import { createActivityData, createDislikeActivityData } from './send-create' |
17 | import { followActivityData } from './send-follow' | 24 | import { followActivityData } from './send-follow' |
18 | import { likeActivityData } from './send-like' | 25 | import { likeActivityData } from './send-like' |
26 | import { VideoShareModel } from '../../../models/video/video-share' | ||
27 | import { buildVideoAnnounce } from './send-announce' | ||
19 | 28 | ||
20 | async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { | 29 | async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { |
21 | const me = actorFollow.ActorFollower | 30 | const me = actorFollow.ActorFollower |
@@ -58,7 +67,7 @@ async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Trans | |||
58 | 67 | ||
59 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) | 68 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) |
60 | const dislikeActivity = createDislikeActivityData(byActor, video) | 69 | const dislikeActivity = createDislikeActivityData(byActor, video) |
61 | const object = await createActivityData(undoUrl, byActor, dislikeActivity, t) | 70 | const object = await createActivityData(dislikeUrl, byActor, dislikeActivity, t) |
62 | 71 | ||
63 | if (video.isOwned() === false) { | 72 | if (video.isOwned() === false) { |
64 | const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) | 73 | const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) |
@@ -73,12 +82,24 @@ async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Trans | |||
73 | return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException) | 82 | return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException) |
74 | } | 83 | } |
75 | 84 | ||
85 | async function sendUndoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) { | ||
86 | const undoUrl = getUndoActivityPubUrl(videoShare.url) | ||
87 | |||
88 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) | ||
89 | const object = await buildVideoAnnounce(byActor, videoShare, video, t) | ||
90 | const data = await undoActivityData(undoUrl, byActor, object, t) | ||
91 | |||
92 | const followersException = [ byActor ] | ||
93 | return broadcastToFollowers(data, byActor, actorsInvolvedInVideo, t, followersException) | ||
94 | } | ||
95 | |||
76 | // --------------------------------------------------------------------------- | 96 | // --------------------------------------------------------------------------- |
77 | 97 | ||
78 | export { | 98 | export { |
79 | sendUndoFollow, | 99 | sendUndoFollow, |
80 | sendUndoLike, | 100 | sendUndoLike, |
81 | sendUndoDislike | 101 | sendUndoDislike, |
102 | sendUndoAnnounce | ||
82 | } | 103 | } |
83 | 104 | ||
84 | // --------------------------------------------------------------------------- | 105 | // --------------------------------------------------------------------------- |
@@ -86,7 +107,7 @@ export { | |||
86 | async function undoActivityData ( | 107 | async function undoActivityData ( |
87 | url: string, | 108 | url: string, |
88 | byActor: ActorModel, | 109 | byActor: ActorModel, |
89 | object: ActivityFollow | ActivityLike | ActivityCreate, | 110 | object: ActivityFollow | ActivityLike | ActivityCreate | ActivityAnnounce, |
90 | t: Transaction, | 111 | t: Transaction, |
91 | audience?: ActivityAudience | 112 | audience?: ActivityAudience |
92 | ): Promise<ActivityUndo> { | 113 | ): Promise<ActivityUndo> { |
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts index f256f8d21..698414867 100644 --- a/server/lib/activitypub/share.ts +++ b/server/lib/activitypub/share.ts | |||
@@ -3,16 +3,37 @@ import { VideoPrivacy } from '../../../shared/models/videos' | |||
3 | import { getServerActor } from '../../helpers/utils' | 3 | import { getServerActor } from '../../helpers/utils' |
4 | import { VideoModel } from '../../models/video/video' | 4 | import { VideoModel } from '../../models/video/video' |
5 | import { VideoShareModel } from '../../models/video/video-share' | 5 | import { VideoShareModel } from '../../models/video/video-share' |
6 | import { sendVideoAnnounce } from './send' | 6 | import { sendUndoAnnounce, sendVideoAnnounce } from './send' |
7 | import { getAnnounceActivityPubUrl } from './url' | 7 | import { getAnnounceActivityPubUrl } from './url' |
8 | import { VideoChannelModel } from '../../models/video/video-channel' | ||
8 | 9 | ||
9 | async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) { | 10 | async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) { |
10 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined | 11 | if (video.privacy === VideoPrivacy.PRIVATE) return undefined |
11 | 12 | ||
13 | return Promise.all([ | ||
14 | shareByServer(video, t), | ||
15 | shareByVideoChannel(video, t) | ||
16 | ]) | ||
17 | } | ||
18 | |||
19 | async function changeVideoChannelShare (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { | ||
20 | await undoShareByVideoChannel(video, oldVideoChannel, t) | ||
21 | |||
22 | await shareByVideoChannel(video, t) | ||
23 | } | ||
24 | |||
25 | export { | ||
26 | changeVideoChannelShare, | ||
27 | shareVideoByServerAndChannel | ||
28 | } | ||
29 | |||
30 | // --------------------------------------------------------------------------- | ||
31 | |||
32 | async function shareByServer (video: VideoModel, t: Transaction) { | ||
12 | const serverActor = await getServerActor() | 33 | const serverActor = await getServerActor() |
13 | 34 | ||
14 | const serverShareUrl = getAnnounceActivityPubUrl(video.url, serverActor) | 35 | const serverShareUrl = getAnnounceActivityPubUrl(video.url, serverActor) |
15 | const serverSharePromise = VideoShareModel.findOrCreate({ | 36 | return VideoShareModel.findOrCreate({ |
16 | defaults: { | 37 | defaults: { |
17 | actorId: serverActor.id, | 38 | actorId: serverActor.id, |
18 | videoId: video.id, | 39 | videoId: video.id, |
@@ -27,9 +48,11 @@ async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) | |||
27 | 48 | ||
28 | return undefined | 49 | return undefined |
29 | }) | 50 | }) |
51 | } | ||
30 | 52 | ||
53 | async function shareByVideoChannel (video: VideoModel, t: Transaction) { | ||
31 | const videoChannelShareUrl = getAnnounceActivityPubUrl(video.url, video.VideoChannel.Actor) | 54 | const videoChannelShareUrl = getAnnounceActivityPubUrl(video.url, video.VideoChannel.Actor) |
32 | const videoChannelSharePromise = VideoShareModel.findOrCreate({ | 55 | return VideoShareModel.findOrCreate({ |
33 | defaults: { | 56 | defaults: { |
34 | actorId: video.VideoChannel.actorId, | 57 | actorId: video.VideoChannel.actorId, |
35 | videoId: video.id, | 58 | videoId: video.id, |
@@ -40,17 +63,17 @@ async function shareVideoByServerAndChannel (video: VideoModel, t: Transaction) | |||
40 | }, | 63 | }, |
41 | transaction: t | 64 | transaction: t |
42 | }).then(([ videoChannelShare, created ]) => { | 65 | }).then(([ videoChannelShare, created ]) => { |
43 | if (created) return sendVideoAnnounce(serverActor, videoChannelShare, video, t) | 66 | if (created) return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t) |
44 | 67 | ||
45 | return undefined | 68 | return undefined |
46 | }) | 69 | }) |
47 | |||
48 | return Promise.all([ | ||
49 | serverSharePromise, | ||
50 | videoChannelSharePromise | ||
51 | ]) | ||
52 | } | 70 | } |
53 | 71 | ||
54 | export { | 72 | async function undoShareByVideoChannel (video: VideoModel, oldVideoChannel: VideoChannelModel, t: Transaction) { |
55 | shareVideoByServerAndChannel | 73 | // Load old share |
74 | const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t) | ||
75 | if (!oldShare) return new Error('Cannot find old video channel share ' + oldVideoChannel.actorId + ' for video ' + video.id) | ||
76 | |||
77 | await sendUndoAnnounce(oldVideoChannel.Actor, oldShare, video, t) | ||
78 | await oldShare.destroy({ transaction: t }) | ||
56 | } | 79 | } |
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index b81acbb35..2899acff3 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -137,6 +137,13 @@ function videoFileActivityUrlToDBAttributes (videoCreated: VideoModel, videoObje | |||
137 | return attributes | 137 | return attributes |
138 | } | 138 | } |
139 | 139 | ||
140 | function getOrCreateVideoChannel (videoObject: VideoTorrentObject) { | ||
141 | const channel = videoObject.attributedTo.find(a => a.type === 'Group') | ||
142 | if (!channel) throw new Error('Cannot find associated video channel to video ' + videoObject.url) | ||
143 | |||
144 | return getOrCreateActorAndServerAndModel(channel.id) | ||
145 | } | ||
146 | |||
140 | async function getOrCreateVideo (videoObject: VideoTorrentObject, channelActor: ActorModel) { | 147 | async function getOrCreateVideo (videoObject: VideoTorrentObject, channelActor: ActorModel) { |
141 | logger.debug('Adding remote video %s.', videoObject.id) | 148 | logger.debug('Adding remote video %s.', videoObject.id) |
142 | 149 | ||
@@ -199,10 +206,7 @@ async function getOrCreateAccountAndVideoAndChannel (videoObject: VideoTorrentOb | |||
199 | actor = await getOrCreateActorAndServerAndModel(actorObj.id) | 206 | actor = await getOrCreateActorAndServerAndModel(actorObj.id) |
200 | } | 207 | } |
201 | 208 | ||
202 | const channel = videoObject.attributedTo.find(a => a.type === 'Group') | 209 | const channelActor = await getOrCreateVideoChannel(videoObject) |
203 | if (!channel) throw new Error('Cannot find associated video channel to video ' + videoObject.url) | ||
204 | |||
205 | const channelActor = await getOrCreateActorAndServerAndModel(channel.id) | ||
206 | 210 | ||
207 | const options = { | 211 | const options = { |
208 | arguments: [ videoObject, channelActor ], | 212 | arguments: [ videoObject, channelActor ], |
@@ -301,6 +305,7 @@ export { | |||
301 | videoActivityObjectToDBAttributes, | 305 | videoActivityObjectToDBAttributes, |
302 | videoFileActivityUrlToDBAttributes, | 306 | videoFileActivityUrlToDBAttributes, |
303 | getOrCreateVideo, | 307 | getOrCreateVideo, |
308 | getOrCreateVideoChannel, | ||
304 | addVideoShares} | 309 | addVideoShares} |
305 | 310 | ||
306 | // --------------------------------------------------------------------------- | 311 | // --------------------------------------------------------------------------- |