From 57e4e1c1a95c3a81a967f54ecc2a510d8b0e129c Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Fri, 18 Mar 2022 11:17:35 +0100 Subject: Don't store remote rates of remote videos In the future we'll stop to expose all available rates to improve users privacy --- server/lib/activitypub/send/send-accept.ts | 4 +- server/lib/activitypub/send/send-announce.ts | 7 +- server/lib/activitypub/send/send-create.ts | 21 +- server/lib/activitypub/send/send-delete.ts | 7 +- server/lib/activitypub/send/send-dislike.ts | 10 +- server/lib/activitypub/send/send-flag.ts | 4 +- server/lib/activitypub/send/send-follow.ts | 4 +- server/lib/activitypub/send/send-like.ts | 10 +- server/lib/activitypub/send/send-reject.ts | 4 +- server/lib/activitypub/send/send-undo.ts | 60 ++++-- server/lib/activitypub/send/send-update.ts | 15 +- server/lib/activitypub/send/send-view.ts | 4 +- .../lib/activitypub/send/shared/audience-utils.ts | 74 +++++++ server/lib/activitypub/send/shared/index.ts | 2 + server/lib/activitypub/send/shared/send-utils.ts | 232 +++++++++++++++++++++ server/lib/activitypub/send/utils.ts | 214 ------------------- 16 files changed, 396 insertions(+), 276 deletions(-) create mode 100644 server/lib/activitypub/send/shared/audience-utils.ts create mode 100644 server/lib/activitypub/send/shared/index.ts create mode 100644 server/lib/activitypub/send/shared/send-utils.ts delete mode 100644 server/lib/activitypub/send/utils.ts (limited to 'server/lib/activitypub/send') diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts index bb387e2c0..939f06d9e 100644 --- a/server/lib/activitypub/send/send-accept.ts +++ b/server/lib/activitypub/send/send-accept.ts @@ -1,9 +1,9 @@ -import { ActivityAccept, ActivityFollow } from '../../../../shared/models/activitypub' +import { ActivityAccept, ActivityFollow } from '@shared/models' import { logger } from '../../../helpers/logger' import { MActor, MActorFollowActors } from '../../../types/models' import { getLocalActorFollowAcceptActivityPubUrl } from '../url' import { buildFollowActivity } from './send-follow' -import { unicastTo } from './utils' +import { unicastTo } from './shared/send-utils' function sendAccept (actorFollow: MActorFollowActors) { const follower = actorFollow.ActorFollower diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts index 471dcfa77..7897beb75 100644 --- a/server/lib/activitypub/send/send-announce.ts +++ b/server/lib/activitypub/send/send-announce.ts @@ -1,10 +1,11 @@ import { Transaction } from 'sequelize' -import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/activitypub' -import { broadcastToFollowers } from './utils' -import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf } from '../audience' +import { ActivityAnnounce, ActivityAudience } from '@shared/models' import { logger } from '../../../helpers/logger' import { MActorLight, MVideo } from '../../../types/models' import { MVideoShare } from '../../../types/models/video' +import { audiencify, getAudience } from '../audience' +import { getActorsInvolvedInVideo, getAudienceFromFollowersOf } from './shared' +import { broadcastToFollowers } from './shared/send-utils' async function buildAnnounceWithVideoAudience ( byActor: MActorLight, diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index baded642a..f6d897220 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts @@ -1,11 +1,8 @@ import { Transaction } from 'sequelize' -import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' -import { VideoPrivacy } from '../../../../shared/models/videos' -import { VideoCommentModel } from '../../../models/video/video-comment' -import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' -import { audiencify, getActorsInvolvedInVideo, getAudience, getAudienceFromFollowersOf, getVideoCommentAudience } from '../audience' +import { getServerActor } from '@server/models/application/application' +import { ActivityAudience, ActivityCreate, ContextType, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' import { logger, loggerTagsFactory } from '../../../helpers/logger' -import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' +import { VideoCommentModel } from '../../../models/video/video-comment' import { MActorLight, MCommentOwnerVideo, @@ -15,8 +12,16 @@ import { MVideoRedundancyFileVideo, MVideoRedundancyStreamingPlaylistVideo } from '../../../types/models' -import { getServerActor } from '@server/models/application/application' -import { ContextType } from '@shared/models/activitypub/context' +import { audiencify, getAudience } from '../audience' +import { + broadcastToActors, + broadcastToFollowers, + getActorsInvolvedInVideo, + getAudienceFromFollowersOf, + getVideoCommentAudience, + sendVideoRelatedActivity, + unicastTo +} from './shared' const lTags = loggerTagsFactory('ap', 'create') diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts index d31f8c10b..39216cdeb 100644 --- a/server/lib/activitypub/send/send-delete.ts +++ b/server/lib/activitypub/send/send-delete.ts @@ -1,15 +1,16 @@ import { Transaction } from 'sequelize' import { getServerActor } from '@server/models/application/application' -import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' +import { ActivityAudience, ActivityDelete } from '@shared/models' import { logger } from '../../../helpers/logger' import { ActorModel } from '../../../models/actor/actor' import { VideoCommentModel } from '../../../models/video/video-comment' import { VideoShareModel } from '../../../models/video/video-share' import { MActorUrl } from '../../../types/models' import { MCommentOwnerVideo, MVideoAccountLight, MVideoPlaylistFullSummary } from '../../../types/models/video' -import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience' +import { audiencify } from '../audience' import { getDeleteActivityPubUrl } from '../url' -import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' +import { getActorsInvolvedInVideo, getVideoCommentAudience } from './shared' +import { broadcastToActors, broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './shared/send-utils' async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transaction) { logger.info('Creating job to broadcast delete of video %s.', video.url) diff --git a/server/lib/activitypub/send/send-dislike.ts b/server/lib/activitypub/send/send-dislike.ts index 274230535..ecb11e9bf 100644 --- a/server/lib/activitypub/send/send-dislike.ts +++ b/server/lib/activitypub/send/send-dislike.ts @@ -1,10 +1,10 @@ import { Transaction } from 'sequelize' -import { getVideoDislikeActivityPubUrlByLocalActor } from '../url' +import { ActivityAudience, ActivityDislike } from '@shared/models' import { logger } from '../../../helpers/logger' -import { ActivityAudience, ActivityDislike } from '../../../../shared/models/activitypub' -import { sendVideoRelatedActivity } from './utils' -import { audiencify, getAudience } from '../audience' import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../types/models' +import { audiencify, getAudience } from '../audience' +import { getVideoDislikeActivityPubUrlByLocalActor } from '../url' +import { sendVideoActivityToOrigin } from './shared/send-utils' function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { logger.info('Creating job to dislike %s.', video.url) @@ -15,7 +15,7 @@ function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction return buildDislikeActivity(url, byActor, video, audience) } - return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) + return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction: t }) } function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike { diff --git a/server/lib/activitypub/send/send-flag.ts b/server/lib/activitypub/send/send-flag.ts index b0483b5a0..6df4e7eb8 100644 --- a/server/lib/activitypub/send/send-flag.ts +++ b/server/lib/activitypub/send/send-flag.ts @@ -1,10 +1,10 @@ import { Transaction } from 'sequelize' -import { ActivityAudience, ActivityFlag } from '../../../../shared/models/activitypub' +import { ActivityAudience, ActivityFlag } from '@shared/models' import { logger } from '../../../helpers/logger' import { MAbuseAP, MAccountLight, MActor } from '../../../types/models' import { audiencify, getAudience } from '../audience' import { getLocalAbuseActivityPubUrl } from '../url' -import { unicastTo } from './utils' +import { unicastTo } from './shared/send-utils' function sendAbuse (byActor: MActor, abuse: MAbuseAP, flaggedAccount: MAccountLight, t: Transaction) { if (!flaggedAccount.Actor.serverId) return // Local user diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts index 9219640dd..aeeb50a2a 100644 --- a/server/lib/activitypub/send/send-follow.ts +++ b/server/lib/activitypub/send/send-follow.ts @@ -1,8 +1,8 @@ import { Transaction } from 'sequelize' -import { ActivityFollow } from '../../../../shared/models/activitypub' +import { ActivityFollow } from '@shared/models' import { logger } from '../../../helpers/logger' import { MActor, MActorFollowActors } from '../../../types/models' -import { unicastTo } from './utils' +import { unicastTo } from './shared/send-utils' function sendFollow (actorFollow: MActorFollowActors, t: Transaction) { const me = actorFollow.ActorFollower diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts index ed6dfcf56..a5fe95e0a 100644 --- a/server/lib/activitypub/send/send-like.ts +++ b/server/lib/activitypub/send/send-like.ts @@ -1,10 +1,10 @@ import { Transaction } from 'sequelize' -import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub' -import { getVideoLikeActivityPubUrlByLocalActor } from '../url' -import { sendVideoRelatedActivity } from './utils' -import { audiencify, getAudience } from '../audience' +import { ActivityAudience, ActivityLike } from '@shared/models' import { logger } from '../../../helpers/logger' import { MActor, MActorAudience, MVideoAccountLight, MVideoUrl } from '../../../types/models' +import { audiencify, getAudience } from '../audience' +import { getVideoLikeActivityPubUrlByLocalActor } from '../url' +import { sendVideoActivityToOrigin } from './shared/send-utils' function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { logger.info('Creating job to like %s.', video.url) @@ -15,7 +15,7 @@ function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { return buildLikeActivity(url, byActor, video, audience) } - return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction: t }) + return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction: t }) } function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike { diff --git a/server/lib/activitypub/send/send-reject.ts b/server/lib/activitypub/send/send-reject.ts index 8d74a7848..01b8f743b 100644 --- a/server/lib/activitypub/send/send-reject.ts +++ b/server/lib/activitypub/send/send-reject.ts @@ -1,9 +1,9 @@ -import { ActivityFollow, ActivityReject } from '../../../../shared/models/activitypub' +import { ActivityFollow, ActivityReject } from '@shared/models' import { logger } from '../../../helpers/logger' import { MActor } from '../../../types/models' import { getLocalActorFollowRejectActivityPubUrl } from '../url' import { buildFollowActivity } from './send-follow' -import { unicastTo } from './utils' +import { unicastTo } from './shared/send-utils' function sendReject (followUrl: string, follower: MActor, following: MActor) { if (!follower.serverId) { // This should never happen diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index d2b738bef..948ca0d7a 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts @@ -7,7 +7,7 @@ import { ActivityFollow, ActivityLike, ActivityUndo -} from '../../../../shared/models/activitypub' +} from '@shared/models' import { logger } from '../../../helpers/logger' import { VideoModel } from '../../../models/video/video' import { @@ -27,7 +27,7 @@ import { buildCreateActivity } from './send-create' import { buildDislikeActivity } from './send-dislike' import { buildFollowActivity } from './send-follow' import { buildLikeActivity } from './send-like' -import { broadcastToFollowers, sendVideoRelatedActivity, unicastTo } from './utils' +import { broadcastToFollowers, sendVideoActivityToOrigin, sendVideoRelatedActivity, unicastTo } from './shared/send-utils' function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) { const me = actorFollow.ActorFollower @@ -46,6 +46,8 @@ function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) { t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl)) } +// --------------------------------------------------------------------------- + async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) { logger.info('Creating job to undo announce %s.', videoShare.url) @@ -58,13 +60,30 @@ async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException) } +async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) { + logger.info('Creating job to undo cache file %s.', redundancyModel.url) + + const associatedVideo = redundancyModel.getVideo() + if (!associatedVideo) { + logger.warn('Cannot send undo activity for redundancy %s: no video files associated.', redundancyModel.url) + return + } + + const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(associatedVideo.id) + const createActivity = buildCreateActivity(redundancyModel.url, byActor, redundancyModel.toActivityPubObject()) + + return sendUndoVideoRelatedActivity({ byActor, video, url: redundancyModel.url, activity: createActivity, transaction: t }) +} + +// --------------------------------------------------------------------------- + async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { logger.info('Creating job to undo a like of video %s.', video.url) const likeUrl = getVideoLikeActivityPubUrlByLocalActor(byActor, video) const likeActivity = buildLikeActivity(likeUrl, byActor, video) - return sendUndoVideoRelatedActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) + return sendUndoVideoToOriginActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t }) } async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) { @@ -73,22 +92,7 @@ async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: T const dislikeUrl = getVideoDislikeActivityPubUrlByLocalActor(byActor, video) const dislikeActivity = buildDislikeActivity(dislikeUrl, byActor, video) - return sendUndoVideoRelatedActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) -} - -async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) { - logger.info('Creating job to undo cache file %s.', redundancyModel.url) - - const associatedVideo = redundancyModel.getVideo() - if (!associatedVideo) { - logger.warn('Cannot send undo activity for redundancy %s: no video files associated.', redundancyModel.url) - return - } - - const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(associatedVideo.id) - const createActivity = buildCreateActivity(redundancyModel.url, byActor, redundancyModel.toActivityPubObject()) - - return sendUndoVideoRelatedActivity({ byActor, video, url: redundancyModel.url, activity: createActivity, transaction: t }) + return sendUndoVideoToOriginActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t }) } // --------------------------------------------------------------------------- @@ -126,7 +130,7 @@ async function sendUndoVideoRelatedActivity (options: { byActor: MActor video: MVideoAccountLight url: string - activity: ActivityFollow | ActivityLike | ActivityDislike | ActivityCreate | ActivityAnnounce + activity: ActivityFollow | ActivityCreate | ActivityAnnounce transaction: Transaction }) { const activityBuilder = (audience: ActivityAudience) => { @@ -137,3 +141,19 @@ async function sendUndoVideoRelatedActivity (options: { return sendVideoRelatedActivity(activityBuilder, options) } + +async function sendUndoVideoToOriginActivity (options: { + byActor: MActor + video: MVideoAccountLight + url: string + activity: ActivityLike | ActivityDislike + transaction: Transaction +}) { + const activityBuilder = (audience: ActivityAudience) => { + const undoUrl = getUndoActivityPubUrl(options.url) + + return undoActivityData(undoUrl, options.byActor, options.activity, audience) + } + + return sendVideoActivityToOrigin(activityBuilder, options) +} diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index bcf6e1569..7c9e72cbc 100644 --- a/server/lib/activitypub/send/send-update.ts +++ b/server/lib/activitypub/send/send-update.ts @@ -1,14 +1,10 @@ import { Transaction } from 'sequelize' -import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' -import { VideoPrivacy } from '../../../../shared/models/videos' +import { getServerActor } from '@server/models/application/application' +import { ActivityAudience, ActivityUpdate, VideoPlaylistPrivacy, VideoPrivacy } from '@shared/models' +import { logger } from '../../../helpers/logger' import { AccountModel } from '../../../models/account/account' import { VideoModel } from '../../../models/video/video' import { VideoShareModel } from '../../../models/video/video-share' -import { getUpdateActivityPubUrl } from '../url' -import { broadcastToFollowers, sendVideoRelatedActivity } from './utils' -import { audiencify, getActorsInvolvedInVideo, getAudience } from '../audience' -import { logger } from '../../../helpers/logger' -import { VideoPlaylistPrivacy } from '../../../../shared/models/videos/playlist/video-playlist-privacy.model' import { MAccountDefault, MActor, @@ -19,7 +15,10 @@ import { MVideoPlaylistFull, MVideoRedundancyVideo } from '../../../types/models' -import { getServerActor } from '@server/models/application/application' +import { audiencify, getAudience } from '../audience' +import { getUpdateActivityPubUrl } from '../url' +import { getActorsInvolvedInVideo } from './shared' +import { broadcastToFollowers, sendVideoRelatedActivity } from './shared/send-utils' async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) { const video = videoArg as MVideoAP diff --git a/server/lib/activitypub/send/send-view.ts b/server/lib/activitypub/send/send-view.ts index b12583e26..1f97307b9 100644 --- a/server/lib/activitypub/send/send-view.ts +++ b/server/lib/activitypub/send/send-view.ts @@ -1,12 +1,12 @@ import { Transaction } from 'sequelize' import { VideoViews } from '@server/lib/video-views' import { MActorAudience, MVideoImmutable, MVideoUrl } from '@server/types/models' -import { ActivityAudience, ActivityView } from '../../../../shared/models/activitypub' +import { ActivityAudience, ActivityView } from '@shared/models' import { logger } from '../../../helpers/logger' import { ActorModel } from '../../../models/actor/actor' import { audiencify, getAudience } from '../audience' import { getLocalVideoViewActivityPubUrl } from '../url' -import { sendVideoRelatedActivity } from './utils' +import { sendVideoRelatedActivity } from './shared/send-utils' async function sendView (byActor: ActorModel, video: MVideoImmutable, t: Transaction) { logger.info('Creating job to send view of %s.', video.url) diff --git a/server/lib/activitypub/send/shared/audience-utils.ts b/server/lib/activitypub/send/shared/audience-utils.ts new file mode 100644 index 000000000..a5f64a08d --- /dev/null +++ b/server/lib/activitypub/send/shared/audience-utils.ts @@ -0,0 +1,74 @@ +import { Transaction } from 'sequelize/dist' +import { ACTIVITY_PUB } from '@server/initializers/constants' +import { ActorModel } from '@server/models/actor/actor' +import { VideoModel } from '@server/models/video/video' +import { VideoShareModel } from '@server/models/video/video-share' +import { MActorFollowersUrl, MActorLight, MActorUrl, MCommentOwner, MCommentOwnerVideo, MVideoId } from '@server/types/models' +import { ActivityAudience } from '@shared/models' + +function getOriginVideoAudience (accountActor: MActorUrl, actorsInvolvedInVideo: MActorFollowersUrl[] = []): ActivityAudience { + return { + to: [ accountActor.url ], + cc: actorsInvolvedInVideo.map(a => a.followersUrl) + } +} + +function getVideoCommentAudience ( + videoComment: MCommentOwnerVideo, + threadParentComments: MCommentOwner[], + actorsInvolvedInVideo: MActorFollowersUrl[], + isOrigin = false +): ActivityAudience { + const to = [ ACTIVITY_PUB.PUBLIC ] + const cc: string[] = [] + + // Owner of the video we comment + if (isOrigin === false) { + cc.push(videoComment.Video.VideoChannel.Account.Actor.url) + } + + // Followers of the poster + cc.push(videoComment.Account.Actor.followersUrl) + + // Send to actors we reply to + for (const parentComment of threadParentComments) { + if (parentComment.isDeleted()) continue + + cc.push(parentComment.Account.Actor.url) + } + + return { + to, + cc: cc.concat(actorsInvolvedInVideo.map(a => a.followersUrl)) + } +} + +function getAudienceFromFollowersOf (actorsInvolvedInObject: MActorFollowersUrl[]): ActivityAudience { + return { + to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)), + cc: [] + } +} + +async function getActorsInvolvedInVideo (video: MVideoId, t: Transaction) { + const actors: MActorLight[] = await VideoShareModel.loadActorsByShare(video.id, t) + + const videoAll = video as VideoModel + + const videoActor = videoAll.VideoChannel?.Account + ? videoAll.VideoChannel.Account.Actor + : await ActorModel.loadFromAccountByVideoId(video.id, t) + + actors.push(videoActor) + + return actors +} + +// --------------------------------------------------------------------------- + +export { + getOriginVideoAudience, + getActorsInvolvedInVideo, + getAudienceFromFollowersOf, + getVideoCommentAudience +} diff --git a/server/lib/activitypub/send/shared/index.ts b/server/lib/activitypub/send/shared/index.ts new file mode 100644 index 000000000..bda579115 --- /dev/null +++ b/server/lib/activitypub/send/shared/index.ts @@ -0,0 +1,2 @@ +export * from './audience-utils' +export * from './send-utils' diff --git a/server/lib/activitypub/send/shared/send-utils.ts b/server/lib/activitypub/send/shared/send-utils.ts new file mode 100644 index 000000000..9e8f12fa8 --- /dev/null +++ b/server/lib/activitypub/send/shared/send-utils.ts @@ -0,0 +1,232 @@ +import { Transaction } from 'sequelize' +import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache' +import { getServerActor } from '@server/models/application/application' +import { Activity, ActivityAudience } from '@shared/models' +import { ContextType } from '@shared/models/activitypub/context' +import { afterCommitIfTransaction } from '../../../../helpers/database-utils' +import { logger } from '../../../../helpers/logger' +import { ActorModel } from '../../../../models/actor/actor' +import { ActorFollowModel } from '../../../../models/actor/actor-follow' +import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../../types/models' +import { JobQueue } from '../../../job-queue' +import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getOriginVideoAudience } from './audience-utils' + +async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { + byActor: MActorLight + video: MVideoImmutable | MVideoAccountLight + transaction?: Transaction + contextType?: ContextType +}) { + const { byActor, video, transaction, contextType } = options + + const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, transaction) + + // Send to origin + if (video.isOwned() === false) { + return sendVideoActivityToOrigin(activityBuilder, options) + } + + // Send to followers + const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo) + const activity = activityBuilder(audience) + + const actorsException = [ byActor ] + + return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, transaction, actorsException, contextType) +} + +async function sendVideoActivityToOrigin (activityBuilder: (audience: ActivityAudience) => Activity, options: { + byActor: MActorLight + video: MVideoImmutable | MVideoAccountLight + actorsInvolvedInVideo?: MActorLight[] + transaction?: Transaction + contextType?: ContextType +}) { + const { byActor, video, actorsInvolvedInVideo, transaction, contextType } = options + + if (video.isOwned()) throw new Error('Cannot send activity to owned video origin ' + video.url) + + let accountActor: MActorLight = (video as MVideoAccountLight).VideoChannel?.Account?.Actor + if (!accountActor) accountActor = await ActorModel.loadAccountActorByVideoId(video.id, transaction) + + const audience = getOriginVideoAudience(accountActor, actorsInvolvedInVideo) + const activity = activityBuilder(audience) + + return afterCommitIfTransaction(transaction, () => { + return unicastTo(activity, byActor, accountActor.getSharedInbox(), contextType) + }) +} + +// --------------------------------------------------------------------------- + +async function forwardVideoRelatedActivity ( + activity: Activity, + t: Transaction, + followersException: MActorWithInboxes[], + video: MVideoId +) { + // Mastodon does not add our announces in audience, so we forward to them manually + const additionalActors = await getActorsInvolvedInVideo(video, t) + const additionalFollowerUrls = additionalActors.map(a => a.followersUrl) + + return forwardActivity(activity, t, followersException, additionalFollowerUrls) +} + +async function forwardActivity ( + activity: Activity, + t: Transaction, + followersException: MActorWithInboxes[] = [], + additionalFollowerUrls: string[] = [] +) { + logger.info('Forwarding activity %s.', activity.id) + + const to = activity.to || [] + const cc = activity.cc || [] + + const followersUrls = additionalFollowerUrls + for (const dest of to.concat(cc)) { + if (dest.endsWith('/followers')) { + followersUrls.push(dest) + } + } + + const toActorFollowers = await ActorModel.listByFollowersUrls(followersUrls, t) + const uris = await computeFollowerUris(toActorFollowers, followersException, t) + + if (uris.length === 0) { + logger.info('0 followers for %s, no forwarding.', toActorFollowers.map(a => a.id).join(', ')) + return undefined + } + + logger.debug('Creating forwarding job.', { uris }) + + const payload = { + uris, + body: activity + } + return afterCommitIfTransaction(t, () => JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })) +} + +// --------------------------------------------------------------------------- + +async function broadcastToFollowers ( + data: any, + byActor: MActorId, + toFollowersOf: MActorId[], + t: Transaction, + actorsException: MActorWithInboxes[] = [], + contextType?: ContextType +) { + const uris = await computeFollowerUris(toFollowersOf, actorsException, t) + + return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType)) +} + +async function broadcastToActors ( + data: any, + byActor: MActorId, + toActors: MActor[], + t?: Transaction, + actorsException: MActorWithInboxes[] = [], + contextType?: ContextType +) { + const uris = await computeUris(toActors, actorsException) + return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType)) +} + +function broadcastTo (uris: string[], data: any, byActor: MActorId, contextType?: ContextType) { + if (uris.length === 0) return undefined + + const broadcastUris: string[] = [] + const unicastUris: string[] = [] + + // Bad URIs could be slow to respond, prefer to process them in a dedicated queue + for (const uri of uris) { + if (ActorFollowHealthCache.Instance.isBadInbox(uri)) { + unicastUris.push(uri) + } else { + broadcastUris.push(uri) + } + } + + logger.debug('Creating broadcast job.', { broadcastUris, unicastUris }) + + if (broadcastUris.length !== 0) { + const payload = { + uris: broadcastUris, + signatureActorId: byActor.id, + body: data, + contextType + } + + JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }) + } + + for (const unicastUri of unicastUris) { + const payload = { + uri: unicastUri, + signatureActorId: byActor.id, + body: data, + contextType + } + + JobQueue.Instance.createJob({ type: 'activitypub-http-unicast', payload }) + } +} + +function unicastTo (data: any, byActor: MActorId, toActorUrl: string, contextType?: ContextType) { + logger.debug('Creating unicast job.', { uri: toActorUrl }) + + const payload = { + uri: toActorUrl, + signatureActorId: byActor.id, + body: data, + contextType + } + + JobQueue.Instance.createJob({ type: 'activitypub-http-unicast', payload }) +} + +// --------------------------------------------------------------------------- + +export { + broadcastToFollowers, + unicastTo, + forwardActivity, + broadcastToActors, + sendVideoActivityToOrigin, + forwardVideoRelatedActivity, + sendVideoRelatedActivity +} + +// --------------------------------------------------------------------------- + +async function computeFollowerUris (toFollowersOf: MActorId[], actorsException: MActorWithInboxes[], t: Transaction) { + const toActorFollowerIds = toFollowersOf.map(a => a.id) + + const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t) + const sharedInboxesException = await buildSharedInboxesException(actorsException) + + return result.data.filter(sharedInbox => sharedInboxesException.includes(sharedInbox) === false) +} + +async function computeUris (toActors: MActor[], actorsException: MActorWithInboxes[] = []) { + const serverActor = await getServerActor() + const targetUrls = toActors + .filter(a => a.id !== serverActor.id) // Don't send to ourselves + .map(a => a.getSharedInbox()) + + const toActorSharedInboxesSet = new Set(targetUrls) + + const sharedInboxesException = await buildSharedInboxesException(actorsException) + return Array.from(toActorSharedInboxesSet) + .filter(sharedInbox => sharedInboxesException.includes(sharedInbox) === false) +} + +async function buildSharedInboxesException (actorsException: MActorWithInboxes[]) { + const serverActor = await getServerActor() + + return actorsException + .map(f => f.getSharedInbox()) + .concat([ serverActor.sharedInboxUrl ]) +} diff --git a/server/lib/activitypub/send/utils.ts b/server/lib/activitypub/send/utils.ts deleted file mode 100644 index 7729703b8..000000000 --- a/server/lib/activitypub/send/utils.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { Transaction } from 'sequelize' -import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache' -import { getServerActor } from '@server/models/application/application' -import { ContextType } from '@shared/models/activitypub/context' -import { Activity, ActivityAudience } from '../../../../shared/models/activitypub' -import { afterCommitIfTransaction } from '../../../helpers/database-utils' -import { logger } from '../../../helpers/logger' -import { ActorModel } from '../../../models/actor/actor' -import { ActorFollowModel } from '../../../models/actor/actor-follow' -import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../types/models' -import { JobQueue } from '../../job-queue' -import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience' - -async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: { - byActor: MActorLight - video: MVideoImmutable | MVideoAccountLight - transaction?: Transaction - contextType?: ContextType -}) { - const { byActor, video, transaction, contextType } = options - - const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, transaction) - - // Send to origin - if (video.isOwned() === false) { - let accountActor: MActorLight = (video as MVideoAccountLight).VideoChannel?.Account?.Actor - - if (!accountActor) accountActor = await ActorModel.loadAccountActorByVideoId(video.id, transaction) - - const audience = getRemoteVideoAudience(accountActor, actorsInvolvedInVideo) - const activity = activityBuilder(audience) - - return afterCommitIfTransaction(transaction, () => { - return unicastTo(activity, byActor, accountActor.getSharedInbox(), contextType) - }) - } - - // Send to followers - const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo) - const activity = activityBuilder(audience) - - const actorsException = [ byActor ] - - return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, transaction, actorsException, contextType) -} - -async function forwardVideoRelatedActivity ( - activity: Activity, - t: Transaction, - followersException: MActorWithInboxes[], - video: MVideoId -) { - // Mastodon does not add our announces in audience, so we forward to them manually - const additionalActors = await getActorsInvolvedInVideo(video, t) - const additionalFollowerUrls = additionalActors.map(a => a.followersUrl) - - return forwardActivity(activity, t, followersException, additionalFollowerUrls) -} - -async function forwardActivity ( - activity: Activity, - t: Transaction, - followersException: MActorWithInboxes[] = [], - additionalFollowerUrls: string[] = [] -) { - logger.info('Forwarding activity %s.', activity.id) - - const to = activity.to || [] - const cc = activity.cc || [] - - const followersUrls = additionalFollowerUrls - for (const dest of to.concat(cc)) { - if (dest.endsWith('/followers')) { - followersUrls.push(dest) - } - } - - const toActorFollowers = await ActorModel.listByFollowersUrls(followersUrls, t) - const uris = await computeFollowerUris(toActorFollowers, followersException, t) - - if (uris.length === 0) { - logger.info('0 followers for %s, no forwarding.', toActorFollowers.map(a => a.id).join(', ')) - return undefined - } - - logger.debug('Creating forwarding job.', { uris }) - - const payload = { - uris, - body: activity - } - return afterCommitIfTransaction(t, () => JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })) -} - -async function broadcastToFollowers ( - data: any, - byActor: MActorId, - toFollowersOf: MActorId[], - t: Transaction, - actorsException: MActorWithInboxes[] = [], - contextType?: ContextType -) { - const uris = await computeFollowerUris(toFollowersOf, actorsException, t) - - return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType)) -} - -async function broadcastToActors ( - data: any, - byActor: MActorId, - toActors: MActor[], - t?: Transaction, - actorsException: MActorWithInboxes[] = [], - contextType?: ContextType -) { - const uris = await computeUris(toActors, actorsException) - return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType)) -} - -function broadcastTo (uris: string[], data: any, byActor: MActorId, contextType?: ContextType) { - if (uris.length === 0) return undefined - - const broadcastUris: string[] = [] - const unicastUris: string[] = [] - - // Bad URIs could be slow to respond, prefer to process them in a dedicated queue - for (const uri of uris) { - if (ActorFollowHealthCache.Instance.isBadInbox(uri)) { - unicastUris.push(uri) - } else { - broadcastUris.push(uri) - } - } - - logger.debug('Creating broadcast job.', { broadcastUris, unicastUris }) - - if (broadcastUris.length !== 0) { - const payload = { - uris: broadcastUris, - signatureActorId: byActor.id, - body: data, - contextType - } - - JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }) - } - - for (const unicastUri of unicastUris) { - const payload = { - uri: unicastUri, - signatureActorId: byActor.id, - body: data, - contextType - } - - JobQueue.Instance.createJob({ type: 'activitypub-http-unicast', payload }) - } -} - -function unicastTo (data: any, byActor: MActorId, toActorUrl: string, contextType?: ContextType) { - logger.debug('Creating unicast job.', { uri: toActorUrl }) - - const payload = { - uri: toActorUrl, - signatureActorId: byActor.id, - body: data, - contextType - } - - JobQueue.Instance.createJob({ type: 'activitypub-http-unicast', payload }) -} - -// --------------------------------------------------------------------------- - -export { - broadcastToFollowers, - unicastTo, - forwardActivity, - broadcastToActors, - forwardVideoRelatedActivity, - sendVideoRelatedActivity -} - -// --------------------------------------------------------------------------- - -async function computeFollowerUris (toFollowersOf: MActorId[], actorsException: MActorWithInboxes[], t: Transaction) { - const toActorFollowerIds = toFollowersOf.map(a => a.id) - - const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t) - const sharedInboxesException = await buildSharedInboxesException(actorsException) - - return result.data.filter(sharedInbox => sharedInboxesException.includes(sharedInbox) === false) -} - -async function computeUris (toActors: MActor[], actorsException: MActorWithInboxes[] = []) { - const serverActor = await getServerActor() - const targetUrls = toActors - .filter(a => a.id !== serverActor.id) // Don't send to ourselves - .map(a => a.getSharedInbox()) - - const toActorSharedInboxesSet = new Set(targetUrls) - - const sharedInboxesException = await buildSharedInboxesException(actorsException) - return Array.from(toActorSharedInboxesSet) - .filter(sharedInbox => sharedInboxesException.includes(sharedInbox) === false) -} - -async function buildSharedInboxesException (actorsException: MActorWithInboxes[]) { - const serverActor = await getServerActor() - - return actorsException - .map(f => f.getSharedInbox()) - .concat([ serverActor.sharedInboxUrl ]) -} -- cgit v1.2.3