Correctly forward like/dislikes and undo
This commit is contained in:
parent
d4f1e94c89
commit
63c93323ec
15 changed files with 246 additions and 134 deletions
|
@ -76,7 +76,7 @@ function addRemoteVideo (account: AccountInstance,
|
||||||
if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.')
|
if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.')
|
||||||
|
|
||||||
const videoFromDatabase = await db.Video.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t)
|
const videoFromDatabase = await db.Video.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t)
|
||||||
if (videoFromDatabase) throw new Error('Video with this UUID/Url already exists.')
|
if (videoFromDatabase) return videoFromDatabase
|
||||||
|
|
||||||
const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, activity.to, activity.cc)
|
const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, activity.to, activity.cc)
|
||||||
const video = db.Video.build(videoData)
|
const video = db.Video.build(videoData)
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { ActivityCreate, VideoChannelObject } from '../../../../shared'
|
import { ActivityCreate, VideoChannelObject } from '../../../../shared'
|
||||||
|
import { DislikeObject } from '../../../../shared/models/activitypub/objects/dislike-object'
|
||||||
import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object'
|
import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object'
|
||||||
import { ViewObject } from '../../../../shared/models/activitypub/objects/view-object'
|
import { ViewObject } from '../../../../shared/models/activitypub/objects/view-object'
|
||||||
import { logger, retryTransactionWrapper } from '../../../helpers'
|
import { logger, retryTransactionWrapper } from '../../../helpers'
|
||||||
import { database as db } from '../../../initializers'
|
import { database as db } from '../../../initializers'
|
||||||
import { AccountInstance } from '../../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { getOrCreateAccountAndServer } from '../account'
|
import { getOrCreateAccountAndServer } from '../account'
|
||||||
import { sendCreateDislikeToVideoFollowers, sendCreateViewToVideoFollowers } from '../send/send-create'
|
import { forwardActivity } from '../send/misc'
|
||||||
import { getVideoChannelActivityPubUrl } from '../url'
|
import { getVideoChannelActivityPubUrl } from '../url'
|
||||||
import { videoChannelActivityObjectToDBAttributes } from './misc'
|
import { videoChannelActivityObjectToDBAttributes } from './misc'
|
||||||
import { DislikeObject } from '../../../../shared/models/activitypub/objects/dislike-object'
|
|
||||||
|
|
||||||
async function processCreateActivity (activity: ActivityCreate) {
|
async function processCreateActivity (activity: ActivityCreate) {
|
||||||
const activityObject = activity.object
|
const activityObject = activity.object
|
||||||
|
@ -16,9 +16,9 @@ async function processCreateActivity (activity: ActivityCreate) {
|
||||||
const account = await getOrCreateAccountAndServer(activity.actor)
|
const account = await getOrCreateAccountAndServer(activity.actor)
|
||||||
|
|
||||||
if (activityType === 'View') {
|
if (activityType === 'View') {
|
||||||
return processCreateView(activityObject as ViewObject)
|
return processCreateView(account, activity)
|
||||||
} else if (activityType === 'Dislike') {
|
} else if (activityType === 'Dislike') {
|
||||||
return processCreateDislike(account, activityObject as DislikeObject)
|
return processCreateDislike(account, activity)
|
||||||
} else if (activityType === 'VideoChannel') {
|
} else if (activityType === 'VideoChannel') {
|
||||||
return processCreateVideoChannel(account, activityObject as VideoChannelObject)
|
return processCreateVideoChannel(account, activityObject as VideoChannelObject)
|
||||||
} else if (activityType === 'Flag') {
|
} else if (activityType === 'Flag') {
|
||||||
|
@ -37,19 +37,20 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processCreateDislike (byAccount: AccountInstance, dislike: DislikeObject) {
|
async function processCreateDislike (byAccount: AccountInstance, activity: ActivityCreate) {
|
||||||
const options = {
|
const options = {
|
||||||
arguments: [ byAccount, dislike ],
|
arguments: [ byAccount, activity ],
|
||||||
errorMessage: 'Cannot dislike the video with many retries.'
|
errorMessage: 'Cannot dislike the video with many retries.'
|
||||||
}
|
}
|
||||||
|
|
||||||
return retryTransactionWrapper(createVideoDislike, options)
|
return retryTransactionWrapper(createVideoDislike, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVideoDislike (byAccount: AccountInstance, dislike: DislikeObject) {
|
function createVideoDislike (byAccount: AccountInstance, activity: ActivityCreate) {
|
||||||
return db.sequelize.transaction(async t => {
|
const dislike = activity.object as DislikeObject
|
||||||
const video = await db.Video.loadByUrlAndPopulateAccount(dislike.object)
|
|
||||||
|
|
||||||
|
return db.sequelize.transaction(async t => {
|
||||||
|
const video = await db.Video.loadByUrlAndPopulateAccount(dislike.object, t)
|
||||||
if (!video) throw new Error('Unknown video ' + dislike.object)
|
if (!video) throw new Error('Unknown video ' + dislike.object)
|
||||||
|
|
||||||
const rate = {
|
const rate = {
|
||||||
|
@ -59,15 +60,22 @@ function createVideoDislike (byAccount: AccountInstance, dislike: DislikeObject)
|
||||||
}
|
}
|
||||||
const [ , created ] = await db.AccountVideoRate.findOrCreate({
|
const [ , created ] = await db.AccountVideoRate.findOrCreate({
|
||||||
where: rate,
|
where: rate,
|
||||||
defaults: rate
|
defaults: rate,
|
||||||
|
transaction: t
|
||||||
})
|
})
|
||||||
await video.increment('dislikes')
|
await video.increment('dislikes', { transaction: t })
|
||||||
|
|
||||||
if (video.isOwned() && created === true) await sendCreateDislikeToVideoFollowers(byAccount, video, undefined)
|
if (video.isOwned() && created === true) {
|
||||||
|
// Don't resend the activity to the sender
|
||||||
|
const exceptions = [ byAccount ]
|
||||||
|
await forwardActivity(activity, t, exceptions)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processCreateView (view: ViewObject) {
|
async function processCreateView (byAccount: AccountInstance, activity: ActivityCreate) {
|
||||||
|
const view = activity.object as ViewObject
|
||||||
|
|
||||||
const video = await db.Video.loadByUrlAndPopulateAccount(view.object)
|
const video = await db.Video.loadByUrlAndPopulateAccount(view.object)
|
||||||
|
|
||||||
if (!video) throw new Error('Unknown video ' + view.object)
|
if (!video) throw new Error('Unknown video ' + view.object)
|
||||||
|
@ -77,7 +85,11 @@ async function processCreateView (view: ViewObject) {
|
||||||
|
|
||||||
await video.increment('views')
|
await video.increment('views')
|
||||||
|
|
||||||
if (video.isOwned()) await sendCreateViewToVideoFollowers(account, video, undefined)
|
if (video.isOwned()) {
|
||||||
|
// Don't resend the activity to the sender
|
||||||
|
const exceptions = [ byAccount ]
|
||||||
|
await forwardActivity(activity, undefined, exceptions)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processCreateVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
|
function processCreateVideoChannel (account: AccountInstance, videoChannelToCreateData: VideoChannelObject) {
|
||||||
|
@ -94,7 +106,7 @@ function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateDa
|
||||||
|
|
||||||
return db.sequelize.transaction(async t => {
|
return db.sequelize.transaction(async t => {
|
||||||
let videoChannel = await db.VideoChannel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t)
|
let videoChannel = await db.VideoChannel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t)
|
||||||
if (videoChannel) throw new Error('Video channel with this URL/UUID already exists.')
|
if (videoChannel) return videoChannel
|
||||||
|
|
||||||
const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account)
|
const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account)
|
||||||
videoChannel = db.VideoChannel.build(videoChannelData)
|
videoChannel = db.VideoChannel.build(videoChannelData)
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { ActivityLike } from '../../../../shared/models/activitypub/activity'
|
import { ActivityLike } from '../../../../shared/models/activitypub/activity'
|
||||||
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { database as db } from '../../../initializers'
|
import { database as db } from '../../../initializers'
|
||||||
import { AccountInstance } from '../../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { getOrCreateAccountAndServer } from '../account'
|
import { getOrCreateAccountAndServer } from '../account'
|
||||||
import { sendLikeToVideoFollowers } from '../send/send-like'
|
import { forwardActivity } from '../send/misc'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
|
||||||
|
|
||||||
async function processLikeActivity (activity: ActivityLike) {
|
async function processLikeActivity (activity: ActivityLike) {
|
||||||
const account = await getOrCreateAccountAndServer(activity.actor)
|
const account = await getOrCreateAccountAndServer(activity.actor)
|
||||||
|
|
||||||
return processLikeVideo(account, activity.object)
|
return processLikeVideo(account, activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -19,16 +19,18 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function processLikeVideo (byAccount: AccountInstance, videoUrl: string) {
|
async function processLikeVideo (byAccount: AccountInstance, activity: ActivityLike) {
|
||||||
const options = {
|
const options = {
|
||||||
arguments: [ byAccount, videoUrl ],
|
arguments: [ byAccount, activity ],
|
||||||
errorMessage: 'Cannot like the video with many retries.'
|
errorMessage: 'Cannot like the video with many retries.'
|
||||||
}
|
}
|
||||||
|
|
||||||
return retryTransactionWrapper(createVideoLike, options)
|
return retryTransactionWrapper(createVideoLike, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVideoLike (byAccount: AccountInstance, videoUrl: string) {
|
function createVideoLike (byAccount: AccountInstance, activity: ActivityLike) {
|
||||||
|
const videoUrl = activity.object
|
||||||
|
|
||||||
return db.sequelize.transaction(async t => {
|
return db.sequelize.transaction(async t => {
|
||||||
const video = await db.Video.loadByUrlAndPopulateAccount(videoUrl)
|
const video = await db.Video.loadByUrlAndPopulateAccount(videoUrl)
|
||||||
|
|
||||||
|
@ -41,10 +43,15 @@ function createVideoLike (byAccount: AccountInstance, videoUrl: string) {
|
||||||
}
|
}
|
||||||
const [ , created ] = await db.AccountVideoRate.findOrCreate({
|
const [ , created ] = await db.AccountVideoRate.findOrCreate({
|
||||||
where: rate,
|
where: rate,
|
||||||
defaults: rate
|
defaults: rate,
|
||||||
|
transaction: t
|
||||||
})
|
})
|
||||||
await video.increment('likes')
|
await video.increment('likes', { transaction: t })
|
||||||
|
|
||||||
if (video.isOwned() && created === true) await sendLikeToVideoFollowers(byAccount, video, undefined)
|
if (video.isOwned() && created === true) {
|
||||||
|
// Don't resend the activity to the sender
|
||||||
|
const exceptions = [ byAccount ]
|
||||||
|
await forwardActivity(activity, t, exceptions)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,16 +3,15 @@ import { DislikeObject } from '../../../../shared/models/activitypub/objects/dis
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { database as db } from '../../../initializers'
|
import { database as db } from '../../../initializers'
|
||||||
import { sendUndoDislikeToVideoFollowers } from '../index'
|
import { forwardActivity } from '../send/misc'
|
||||||
import { sendUndoLikeToVideoFollowers } from '../send/send-undo'
|
|
||||||
|
|
||||||
async function processUndoActivity (activity: ActivityUndo) {
|
async function processUndoActivity (activity: ActivityUndo) {
|
||||||
const activityToUndo = activity.object
|
const activityToUndo = activity.object
|
||||||
|
|
||||||
if (activityToUndo.type === 'Like') {
|
if (activityToUndo.type === 'Like') {
|
||||||
return processUndoLike(activity.actor, activityToUndo)
|
return processUndoLike(activity.actor, activity)
|
||||||
} else if (activityToUndo.type === 'Create' && activityToUndo.object.type === 'Dislike') {
|
} else if (activityToUndo.type === 'Create' && activityToUndo.object.type === 'Dislike') {
|
||||||
return processUndoDislike(activity.actor, activityToUndo.object)
|
return processUndoDislike(activity.actor, activity)
|
||||||
} else if (activityToUndo.type === 'Follow') {
|
} else if (activityToUndo.type === 'Follow') {
|
||||||
return processUndoFollow(activity.actor, activityToUndo)
|
return processUndoFollow(activity.actor, activityToUndo)
|
||||||
}
|
}
|
||||||
|
@ -30,57 +29,69 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function processUndoLike (actor: string, likeActivity: ActivityLike) {
|
function processUndoLike (actor: string, activity: ActivityUndo) {
|
||||||
const options = {
|
const options = {
|
||||||
arguments: [ actor, likeActivity ],
|
arguments: [ actor, activity ],
|
||||||
errorMessage: 'Cannot undo like with many retries.'
|
errorMessage: 'Cannot undo like with many retries.'
|
||||||
}
|
}
|
||||||
|
|
||||||
return retryTransactionWrapper(undoLike, options)
|
return retryTransactionWrapper(undoLike, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function undoLike (actor: string, likeActivity: ActivityLike) {
|
function undoLike (actor: string, activity: ActivityUndo) {
|
||||||
|
const likeActivity = activity.object as ActivityLike
|
||||||
|
|
||||||
return db.sequelize.transaction(async t => {
|
return db.sequelize.transaction(async t => {
|
||||||
const byAccount = await db.Account.loadByUrl(actor, t)
|
const byAccount = await db.Account.loadByUrl(actor, t)
|
||||||
if (!byAccount) throw new Error('Unknown account ' + actor)
|
if (!byAccount) throw new Error('Unknown account ' + actor)
|
||||||
|
|
||||||
const video = await db.Video.loadByUrlAndPopulateAccount(likeActivity.object)
|
const video = await db.Video.loadByUrlAndPopulateAccount(likeActivity.object, t)
|
||||||
if (!video) throw new Error('Unknown video ' + likeActivity.actor)
|
if (!video) throw new Error('Unknown video ' + likeActivity.actor)
|
||||||
|
|
||||||
const rate = await db.AccountVideoRate.load(byAccount.id, video.id, t)
|
const rate = await db.AccountVideoRate.load(byAccount.id, video.id, t)
|
||||||
if (!rate) throw new Error(`Unknown rate by account ${byAccount.id} for video ${video.id}.`)
|
if (!rate) throw new Error(`Unknown rate by account ${byAccount.id} for video ${video.id}.`)
|
||||||
|
|
||||||
await rate.destroy({ transaction: t })
|
await rate.destroy({ transaction: t })
|
||||||
await video.decrement('likes')
|
await video.decrement('likes', { transaction: t })
|
||||||
|
|
||||||
if (video.isOwned()) await sendUndoLikeToVideoFollowers(byAccount, video, t)
|
if (video.isOwned()) {
|
||||||
|
// Don't resend the activity to the sender
|
||||||
|
const exceptions = [ byAccount ]
|
||||||
|
await forwardActivity(activity, t, exceptions)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function processUndoDislike (actor: string, dislikeCreateActivity: DislikeObject) {
|
function processUndoDislike (actor: string, activity: ActivityUndo) {
|
||||||
const options = {
|
const options = {
|
||||||
arguments: [ actor, dislikeCreateActivity ],
|
arguments: [ actor, activity ],
|
||||||
errorMessage: 'Cannot undo dislike with many retries.'
|
errorMessage: 'Cannot undo dislike with many retries.'
|
||||||
}
|
}
|
||||||
|
|
||||||
return retryTransactionWrapper(undoDislike, options)
|
return retryTransactionWrapper(undoDislike, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
function undoDislike (actor: string, dislike: DislikeObject) {
|
function undoDislike (actor: string, activity: ActivityUndo) {
|
||||||
|
const dislike = activity.object.object as DislikeObject
|
||||||
|
|
||||||
return db.sequelize.transaction(async t => {
|
return db.sequelize.transaction(async t => {
|
||||||
const byAccount = await db.Account.loadByUrl(actor, t)
|
const byAccount = await db.Account.loadByUrl(actor, t)
|
||||||
if (!byAccount) throw new Error('Unknown account ' + actor)
|
if (!byAccount) throw new Error('Unknown account ' + actor)
|
||||||
|
|
||||||
const video = await db.Video.loadByUrlAndPopulateAccount(dislike.object)
|
const video = await db.Video.loadByUrlAndPopulateAccount(dislike.object, t)
|
||||||
if (!video) throw new Error('Unknown video ' + dislike.actor)
|
if (!video) throw new Error('Unknown video ' + dislike.actor)
|
||||||
|
|
||||||
const rate = await db.AccountVideoRate.load(byAccount.id, video.id, t)
|
const rate = await db.AccountVideoRate.load(byAccount.id, video.id, t)
|
||||||
if (!rate) throw new Error(`Unknown rate by account ${byAccount.id} for video ${video.id}.`)
|
if (!rate) throw new Error(`Unknown rate by account ${byAccount.id} for video ${video.id}.`)
|
||||||
|
|
||||||
await rate.destroy({ transaction: t })
|
await rate.destroy({ transaction: t })
|
||||||
await video.decrement('dislikes')
|
await video.decrement('dislikes', { transaction: t })
|
||||||
|
|
||||||
if (video.isOwned()) await sendUndoDislikeToVideoFollowers(byAccount, video, t)
|
if (video.isOwned()) {
|
||||||
|
// Don't resend the activity to the sender
|
||||||
|
const exceptions = [ byAccount ]
|
||||||
|
await forwardActivity(activity, t, exceptions)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,45 @@ import { Transaction } from 'sequelize'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { ACTIVITY_PUB, database as db } from '../../../initializers'
|
import { ACTIVITY_PUB, database as db } from '../../../initializers'
|
||||||
import { AccountInstance } from '../../../models/account/account-interface'
|
import { AccountInstance } from '../../../models/account/account-interface'
|
||||||
import { activitypubHttpJobScheduler } from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler'
|
import {
|
||||||
|
activitypubHttpJobScheduler,
|
||||||
|
ActivityPubHttpPayload
|
||||||
|
} from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler'
|
||||||
import { VideoInstance } from '../../../models/video/video-interface'
|
import { VideoInstance } from '../../../models/video/video-interface'
|
||||||
|
import { Activity } from '../../../../shared/models/activitypub/activity'
|
||||||
|
|
||||||
|
async function forwardActivity (
|
||||||
|
activity: Activity,
|
||||||
|
t: Transaction,
|
||||||
|
followersException: AccountInstance[] = []
|
||||||
|
) {
|
||||||
|
const to = activity.to || []
|
||||||
|
const cc = activity.cc || []
|
||||||
|
|
||||||
|
const followersUrls: string[] = []
|
||||||
|
for (const dest of to.concat(cc)) {
|
||||||
|
if (dest.endsWith('/followers')) {
|
||||||
|
followersUrls.push(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toAccountFollowers = await db.Account.listByFollowersUrls(followersUrls)
|
||||||
|
const uris = await computeFollowerUris(toAccountFollowers, followersException)
|
||||||
|
|
||||||
|
if (uris.length === 0) {
|
||||||
|
logger.info('0 followers for %s, no forwarding.', toAccountFollowers.map(a => a.id).join(', '))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('Creating forwarding job.', { uris })
|
||||||
|
|
||||||
|
const jobPayload: ActivityPubHttpPayload = {
|
||||||
|
uris,
|
||||||
|
body: activity
|
||||||
|
}
|
||||||
|
|
||||||
|
return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
|
||||||
|
}
|
||||||
|
|
||||||
async function broadcastToFollowers (
|
async function broadcastToFollowers (
|
||||||
data: any,
|
data: any,
|
||||||
|
@ -12,18 +49,15 @@ async function broadcastToFollowers (
|
||||||
t: Transaction,
|
t: Transaction,
|
||||||
followersException: AccountInstance[] = []
|
followersException: AccountInstance[] = []
|
||||||
) {
|
) {
|
||||||
const toAccountFollowerIds = toAccountFollowers.map(a => a.id)
|
const uris = await computeFollowerUris(toAccountFollowers, followersException)
|
||||||
|
if (uris.length === 0) {
|
||||||
const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds)
|
logger.info('0 followers for %s, no broadcasting.', toAccountFollowers.map(a => a.id).join(', '))
|
||||||
if (result.data.length === 0) {
|
return
|
||||||
logger.info('Not broadcast because of 0 followers for %s.', toAccountFollowerIds.join(', '))
|
|
||||||
return undefined
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl)
|
logger.debug('Creating broadcast job.', { uris })
|
||||||
const uris = result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1)
|
|
||||||
|
|
||||||
const jobPayload = {
|
const jobPayload: ActivityPubHttpPayload = {
|
||||||
uris,
|
uris,
|
||||||
signatureAccountId: byAccount.id,
|
signatureAccountId: byAccount.id,
|
||||||
body: data
|
body: data
|
||||||
|
@ -33,7 +67,9 @@ async function broadcastToFollowers (
|
||||||
}
|
}
|
||||||
|
|
||||||
async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: string, t: Transaction) {
|
async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: string, t: Transaction) {
|
||||||
const jobPayload = {
|
logger.debug('Creating unicast job.', { uri: toAccountUrl })
|
||||||
|
|
||||||
|
const jobPayload: ActivityPubHttpPayload = {
|
||||||
uris: [ toAccountUrl ],
|
uris: [ toAccountUrl ],
|
||||||
signatureAccountId: byAccount.id,
|
signatureAccountId: byAccount.id,
|
||||||
body: data
|
body: data
|
||||||
|
@ -42,21 +78,21 @@ async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: s
|
||||||
return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
|
return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOriginVideoAudience (video: VideoInstance) {
|
function getOriginVideoAudience (video: VideoInstance, accountsInvolvedInVideo: AccountInstance[]) {
|
||||||
return {
|
return {
|
||||||
to: [ video.VideoChannel.Account.url ],
|
to: [ video.VideoChannel.Account.url ],
|
||||||
cc: [ video.VideoChannel.Account.url + '/followers' ]
|
cc: accountsInvolvedInVideo.map(a => a.followersUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVideoFollowersAudience (video: VideoInstance) {
|
function getVideoFollowersAudience (accountsInvolvedInVideo: AccountInstance[]) {
|
||||||
return {
|
return {
|
||||||
to: [ video.VideoChannel.Account.url + '/followers' ],
|
to: accountsInvolvedInVideo.map(a => a.followersUrl),
|
||||||
cc: []
|
cc: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getAccountsToForwardVideoAction (byAccount: AccountInstance, video: VideoInstance) {
|
async function getAccountsInvolvedInVideo (video: VideoInstance) {
|
||||||
const accountsToForwardView = await db.VideoShare.loadAccountsByShare(video.id)
|
const accountsToForwardView = await db.VideoShare.loadAccountsByShare(video.id)
|
||||||
accountsToForwardView.push(video.VideoChannel.Account)
|
accountsToForwardView.push(video.VideoChannel.Account)
|
||||||
|
|
||||||
|
@ -81,6 +117,16 @@ async function getAudience (accountSender: AccountInstance, isPublic = true) {
|
||||||
return { to, cc }
|
return { to, cc }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function computeFollowerUris (toAccountFollower: AccountInstance[], followersException: AccountInstance[]) {
|
||||||
|
const toAccountFollowerIds = toAccountFollower.map(a => a.id)
|
||||||
|
|
||||||
|
const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds)
|
||||||
|
const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl)
|
||||||
|
const uris = result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1)
|
||||||
|
|
||||||
|
return uris
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -88,6 +134,7 @@ export {
|
||||||
unicastTo,
|
unicastTo,
|
||||||
getAudience,
|
getAudience,
|
||||||
getOriginVideoAudience,
|
getOriginVideoAudience,
|
||||||
getAccountsToForwardVideoAction,
|
getAccountsInvolvedInVideo,
|
||||||
getVideoFollowersAudience
|
getVideoFollowersAudience,
|
||||||
|
forwardActivity
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityCreate } from '../../../../shared/models/activitypub/activity'
|
import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub/activity'
|
||||||
import { getServerAccount } from '../../../helpers/utils'
|
import { getServerAccount } from '../../../helpers/utils'
|
||||||
import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
|
import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
|
||||||
import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface'
|
import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface'
|
||||||
import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
|
import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
|
||||||
import {
|
import {
|
||||||
broadcastToFollowers,
|
broadcastToFollowers,
|
||||||
getAccountsToForwardVideoAction,
|
getAccountsInvolvedInVideo,
|
||||||
getAudience,
|
getAudience,
|
||||||
getOriginVideoAudience,
|
getOriginVideoAudience,
|
||||||
getVideoFollowersAudience,
|
getVideoFollowersAudience,
|
||||||
|
@ -35,7 +35,8 @@ async function sendCreateViewToOrigin (byAccount: AccountInstance, video: VideoI
|
||||||
const url = getVideoViewActivityPubUrl(byAccount, video)
|
const url = getVideoViewActivityPubUrl(byAccount, video)
|
||||||
const viewActivity = createViewActivityData(byAccount, video)
|
const viewActivity = createViewActivityData(byAccount, video)
|
||||||
|
|
||||||
const audience = getOriginVideoAudience(video)
|
const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video)
|
||||||
|
const audience = getOriginVideoAudience(video, accountsInvolvedInVideo)
|
||||||
const data = await createActivityData(url, byAccount, viewActivity, audience)
|
const data = await createActivityData(url, byAccount, viewActivity, audience)
|
||||||
|
|
||||||
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
|
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
|
||||||
|
@ -45,12 +46,12 @@ async function sendCreateViewToVideoFollowers (byAccount: AccountInstance, video
|
||||||
const url = getVideoViewActivityPubUrl(byAccount, video)
|
const url = getVideoViewActivityPubUrl(byAccount, video)
|
||||||
const viewActivity = createViewActivityData(byAccount, video)
|
const viewActivity = createViewActivityData(byAccount, video)
|
||||||
|
|
||||||
const audience = getVideoFollowersAudience(video)
|
const accountsToForwardView = await getAccountsInvolvedInVideo(video)
|
||||||
|
const audience = getVideoFollowersAudience(accountsToForwardView)
|
||||||
const data = await createActivityData(url, byAccount, viewActivity, audience)
|
const data = await createActivityData(url, byAccount, viewActivity, audience)
|
||||||
|
|
||||||
|
// Use the server account to send the view, because it could be an unregistered account
|
||||||
const serverAccount = await getServerAccount()
|
const serverAccount = await getServerAccount()
|
||||||
const accountsToForwardView = await getAccountsToForwardVideoAction(byAccount, video)
|
|
||||||
|
|
||||||
const followersException = [ byAccount ]
|
const followersException = [ byAccount ]
|
||||||
return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException)
|
return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException)
|
||||||
}
|
}
|
||||||
|
@ -59,7 +60,8 @@ async function sendCreateDislikeToOrigin (byAccount: AccountInstance, video: Vid
|
||||||
const url = getVideoDislikeActivityPubUrl(byAccount, video)
|
const url = getVideoDislikeActivityPubUrl(byAccount, video)
|
||||||
const dislikeActivity = createDislikeActivityData(byAccount, video)
|
const dislikeActivity = createDislikeActivityData(byAccount, video)
|
||||||
|
|
||||||
const audience = getOriginVideoAudience(video)
|
const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video)
|
||||||
|
const audience = getOriginVideoAudience(video, accountsInvolvedInVideo)
|
||||||
const data = await createActivityData(url, byAccount, dislikeActivity, audience)
|
const data = await createActivityData(url, byAccount, dislikeActivity, audience)
|
||||||
|
|
||||||
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
|
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
|
||||||
|
@ -69,17 +71,15 @@ async function sendCreateDislikeToVideoFollowers (byAccount: AccountInstance, vi
|
||||||
const url = getVideoDislikeActivityPubUrl(byAccount, video)
|
const url = getVideoDislikeActivityPubUrl(byAccount, video)
|
||||||
const dislikeActivity = createDislikeActivityData(byAccount, video)
|
const dislikeActivity = createDislikeActivityData(byAccount, video)
|
||||||
|
|
||||||
const audience = getVideoFollowersAudience(video)
|
const accountsToForwardView = await getAccountsInvolvedInVideo(video)
|
||||||
|
const audience = getVideoFollowersAudience(accountsToForwardView)
|
||||||
const data = await createActivityData(url, byAccount, dislikeActivity, audience)
|
const data = await createActivityData(url, byAccount, dislikeActivity, audience)
|
||||||
|
|
||||||
const accountsToForwardView = await getAccountsToForwardVideoAction(byAccount, video)
|
|
||||||
const serverAccount = await getServerAccount()
|
|
||||||
|
|
||||||
const followersException = [ byAccount ]
|
const followersException = [ byAccount ]
|
||||||
return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException)
|
return broadcastToFollowers(data, byAccount, accountsToForwardView, t, followersException)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createActivityData (url: string, byAccount: AccountInstance, object: any, audience?: { to: string[], cc: string[] }) {
|
async function createActivityData (url: string, byAccount: AccountInstance, object: any, audience?: ActivityAudience) {
|
||||||
if (!audience) {
|
if (!audience) {
|
||||||
audience = await getAudience(byAccount)
|
audience = await getAudience(byAccount)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityLike } from '../../../../shared/models/activitypub/activity'
|
import { ActivityLike } from '../../../../shared/models/activitypub/activity'
|
||||||
import { getServerAccount } from '../../../helpers/utils'
|
|
||||||
import { AccountInstance, VideoInstance } from '../../../models'
|
import { AccountInstance, VideoInstance } from '../../../models'
|
||||||
import { getVideoLikeActivityPubUrl } from '../url'
|
import { getVideoLikeActivityPubUrl } from '../url'
|
||||||
import {
|
import {
|
||||||
broadcastToFollowers,
|
broadcastToFollowers,
|
||||||
getAccountsToForwardVideoAction,
|
getAccountsInvolvedInVideo,
|
||||||
getAudience,
|
getAudience,
|
||||||
getOriginVideoAudience,
|
getOriginVideoAudience,
|
||||||
getVideoFollowersAudience,
|
getVideoFollowersAudience,
|
||||||
|
@ -15,7 +14,8 @@ import {
|
||||||
async function sendLikeToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
async function sendLikeToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
||||||
const url = getVideoLikeActivityPubUrl(byAccount, video)
|
const url = getVideoLikeActivityPubUrl(byAccount, video)
|
||||||
|
|
||||||
const audience = getOriginVideoAudience(video)
|
const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video)
|
||||||
|
const audience = getOriginVideoAudience(video, accountsInvolvedInVideo)
|
||||||
const data = await likeActivityData(url, byAccount, video, audience)
|
const data = await likeActivityData(url, byAccount, video, audience)
|
||||||
|
|
||||||
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
|
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
|
||||||
|
@ -24,14 +24,14 @@ async function sendLikeToOrigin (byAccount: AccountInstance, video: VideoInstanc
|
||||||
async function sendLikeToVideoFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
async function sendLikeToVideoFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
||||||
const url = getVideoLikeActivityPubUrl(byAccount, video)
|
const url = getVideoLikeActivityPubUrl(byAccount, video)
|
||||||
|
|
||||||
const audience = getVideoFollowersAudience(video)
|
const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video)
|
||||||
|
const audience = getVideoFollowersAudience(accountsInvolvedInVideo)
|
||||||
const data = await likeActivityData(url, byAccount, video, audience)
|
const data = await likeActivityData(url, byAccount, video, audience)
|
||||||
|
|
||||||
const accountsToForwardView = await getAccountsToForwardVideoAction(byAccount, video)
|
const toAccountsFollowers = await getAccountsInvolvedInVideo(video)
|
||||||
const serverAccount = await getServerAccount()
|
|
||||||
|
|
||||||
const followersException = [ byAccount ]
|
const followersException = [ byAccount ]
|
||||||
return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException)
|
return broadcastToFollowers(data, byAccount, toAccountsFollowers, t, followersException)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function likeActivityData (url: string, byAccount: AccountInstance, video: VideoInstance, audience?: { to: string[], cc: string[] }) {
|
async function likeActivityData (url: string, byAccount: AccountInstance, video: VideoInstance, audience?: { to: string[], cc: string[] }) {
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { Transaction } from 'sequelize'
|
import { Transaction } from 'sequelize'
|
||||||
import { ActivityCreate, ActivityFollow, ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub/activity'
|
import {
|
||||||
import { getServerAccount } from '../../../helpers/utils'
|
ActivityAudience,
|
||||||
|
ActivityCreate,
|
||||||
|
ActivityFollow,
|
||||||
|
ActivityLike,
|
||||||
|
ActivityUndo
|
||||||
|
} from '../../../../shared/models/activitypub/activity'
|
||||||
import { AccountInstance } from '../../../models'
|
import { AccountInstance } from '../../../models'
|
||||||
import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
|
import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
|
||||||
import { VideoInstance } from '../../../models/video/video-interface'
|
import { VideoInstance } from '../../../models/video/video-interface'
|
||||||
import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
|
import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
|
||||||
import { broadcastToFollowers, getAccountsToForwardVideoAction, unicastTo } from './misc'
|
import { broadcastToFollowers, getAccountsInvolvedInVideo, getAudience, getVideoFollowersAudience, unicastTo } from './misc'
|
||||||
import { createActivityData, createDislikeActivityData } from './send-create'
|
import { createActivityData, createDislikeActivityData } from './send-create'
|
||||||
import { followActivityData } from './send-follow'
|
import { followActivityData } from './send-follow'
|
||||||
import { likeActivityData } from './send-like'
|
import { likeActivityData } from './send-like'
|
||||||
|
@ -37,14 +42,13 @@ async function sendUndoLikeToVideoFollowers (byAccount: AccountInstance, video:
|
||||||
const likeUrl = getVideoLikeActivityPubUrl(byAccount, video)
|
const likeUrl = getVideoLikeActivityPubUrl(byAccount, video)
|
||||||
const undoUrl = getUndoActivityPubUrl(likeUrl)
|
const undoUrl = getUndoActivityPubUrl(likeUrl)
|
||||||
|
|
||||||
|
const toAccountsFollowers = await getAccountsInvolvedInVideo(video)
|
||||||
|
const audience = getVideoFollowersAudience(toAccountsFollowers)
|
||||||
const object = await likeActivityData(likeUrl, byAccount, video)
|
const object = await likeActivityData(likeUrl, byAccount, video)
|
||||||
const data = await undoActivityData(undoUrl, byAccount, object)
|
const data = await undoActivityData(undoUrl, byAccount, object, audience)
|
||||||
|
|
||||||
const accountsToForwardView = await getAccountsToForwardVideoAction(byAccount, video)
|
|
||||||
const serverAccount = await getServerAccount()
|
|
||||||
|
|
||||||
const followersException = [ byAccount ]
|
const followersException = [ byAccount ]
|
||||||
return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException)
|
return broadcastToFollowers(data, byAccount, toAccountsFollowers, t, followersException)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendUndoDislikeToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
async function sendUndoDislikeToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
|
||||||
|
@ -68,11 +72,10 @@ async function sendUndoDislikeToVideoFollowers (byAccount: AccountInstance, vide
|
||||||
|
|
||||||
const data = await undoActivityData(undoUrl, byAccount, object)
|
const data = await undoActivityData(undoUrl, byAccount, object)
|
||||||
|
|
||||||
const accountsToForwardView = await getAccountsToForwardVideoAction(byAccount, video)
|
const toAccountsFollowers = await getAccountsInvolvedInVideo(video)
|
||||||
const serverAccount = await getServerAccount()
|
|
||||||
|
|
||||||
const followersException = [ byAccount ]
|
const followersException = [ byAccount ]
|
||||||
return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException)
|
return broadcastToFollowers(data, byAccount, toAccountsFollowers, t, followersException)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
@ -87,11 +90,22 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function undoActivityData (url: string, byAccount: AccountInstance, object: ActivityFollow | ActivityLike | ActivityCreate) {
|
async function undoActivityData (
|
||||||
|
url: string,
|
||||||
|
byAccount: AccountInstance,
|
||||||
|
object: ActivityFollow | ActivityLike | ActivityCreate,
|
||||||
|
audience?: ActivityAudience
|
||||||
|
) {
|
||||||
|
if (!audience) {
|
||||||
|
audience = await getAudience(byAccount)
|
||||||
|
}
|
||||||
|
|
||||||
const activity: ActivityUndo = {
|
const activity: ActivityUndo = {
|
||||||
type: 'Undo',
|
type: 'Undo',
|
||||||
id: url,
|
id: url,
|
||||||
actor: byAccount.url,
|
actor: byAccount.url,
|
||||||
|
to: audience.to,
|
||||||
|
cc: audience.cc,
|
||||||
object
|
object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
import { logger } from '../../../helpers'
|
import { logger } from '../../../helpers'
|
||||||
import { buildSignedActivity } from '../../../helpers/activitypub'
|
|
||||||
import { doRequest } from '../../../helpers/requests'
|
import { doRequest } from '../../../helpers/requests'
|
||||||
import { database as db } from '../../../initializers'
|
import { ActivityPubHttpPayload, computeBody, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
|
||||||
import { ActivityPubHttpPayload, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
|
|
||||||
|
|
||||||
async function process (payload: ActivityPubHttpPayload, jobId: number) {
|
async function process (payload: ActivityPubHttpPayload, jobId: number) {
|
||||||
logger.info('Processing ActivityPub broadcast in job %d.', jobId)
|
logger.info('Processing ActivityPub broadcast in job %d.', jobId)
|
||||||
|
|
||||||
const accountSignature = await db.Account.load(payload.signatureAccountId)
|
const body = await computeBody(payload)
|
||||||
if (!accountSignature) throw new Error('Unknown signature account id.')
|
|
||||||
|
|
||||||
const signedBody = await buildSignedActivity(accountSignature, payload.body)
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
uri: '',
|
uri: '',
|
||||||
json: signedBody
|
json: body
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const uri of payload.uris) {
|
for (const uri of payload.uris) {
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { JobScheduler, JobHandler } from '../job-scheduler'
|
import { JobCategory } from '../../../../shared'
|
||||||
|
import { buildSignedActivity } from '../../../helpers/activitypub'
|
||||||
|
import { logger } from '../../../helpers/logger'
|
||||||
|
import { ACTIVITY_PUB } from '../../../initializers/constants'
|
||||||
|
import { database as db } from '../../../initializers/database'
|
||||||
|
import { JobHandler, JobScheduler } from '../job-scheduler'
|
||||||
|
|
||||||
import * as activitypubHttpBroadcastHandler from './activitypub-http-broadcast-handler'
|
import * as activitypubHttpBroadcastHandler from './activitypub-http-broadcast-handler'
|
||||||
import * as activitypubHttpUnicastHandler from './activitypub-http-unicast-handler'
|
|
||||||
import * as activitypubHttpFetcherHandler from './activitypub-http-fetcher-handler'
|
import * as activitypubHttpFetcherHandler from './activitypub-http-fetcher-handler'
|
||||||
import { JobCategory } from '../../../../shared'
|
import * as activitypubHttpUnicastHandler from './activitypub-http-unicast-handler'
|
||||||
import { ACTIVITY_PUB } from '../../../initializers/constants'
|
|
||||||
import { logger } from '../../../helpers/logger'
|
|
||||||
|
|
||||||
type ActivityPubHttpPayload = {
|
type ActivityPubHttpPayload = {
|
||||||
uris: string[]
|
uris: string[]
|
||||||
|
@ -40,8 +42,21 @@ function maybeRetryRequestLater (err: Error, payload: ActivityPubHttpPayload, ur
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function computeBody (payload: ActivityPubHttpPayload) {
|
||||||
|
let body = payload.body
|
||||||
|
|
||||||
|
if (payload.signatureAccountId) {
|
||||||
|
const accountSignature = await db.Account.load(payload.signatureAccountId)
|
||||||
|
if (!accountSignature) throw new Error('Unknown signature account id.')
|
||||||
|
body = await buildSignedActivity(accountSignature, payload.body)
|
||||||
|
}
|
||||||
|
|
||||||
|
return body
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ActivityPubHttpPayload,
|
ActivityPubHttpPayload,
|
||||||
activitypubHttpJobScheduler,
|
activitypubHttpJobScheduler,
|
||||||
maybeRetryRequestLater
|
maybeRetryRequestLater,
|
||||||
|
computeBody
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,17 @@
|
||||||
import { logger } from '../../../helpers'
|
import { logger } from '../../../helpers'
|
||||||
import { doRequest } from '../../../helpers/requests'
|
import { doRequest } from '../../../helpers/requests'
|
||||||
import { ActivityPubHttpPayload, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
|
import { ActivityPubHttpPayload, computeBody, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
|
||||||
import { database as db } from '../../../initializers/database'
|
|
||||||
import { buildSignedActivity } from '../../../helpers/activitypub'
|
|
||||||
|
|
||||||
async function process (payload: ActivityPubHttpPayload, jobId: number) {
|
async function process (payload: ActivityPubHttpPayload, jobId: number) {
|
||||||
logger.info('Processing ActivityPub unicast in job %d.', jobId)
|
logger.info('Processing ActivityPub unicast in job %d.', jobId)
|
||||||
|
|
||||||
const accountSignature = await db.Account.load(payload.signatureAccountId)
|
const body = await computeBody(payload)
|
||||||
if (!accountSignature) throw new Error('Unknown signature account id.')
|
|
||||||
|
|
||||||
const signedBody = await buildSignedActivity(accountSignature, payload.body)
|
|
||||||
const uri = payload.uris[0]
|
const uri = payload.uris[0]
|
||||||
const options = {
|
const options = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
uri,
|
uri,
|
||||||
json: signedBody
|
json: body
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -12,6 +12,7 @@ export namespace AccountMethods {
|
||||||
export type LoadByUrl = (url: string, transaction?: Sequelize.Transaction) => Bluebird<AccountInstance>
|
export type LoadByUrl = (url: string, transaction?: Sequelize.Transaction) => Bluebird<AccountInstance>
|
||||||
export type LoadLocalByName = (name: string) => Bluebird<AccountInstance>
|
export type LoadLocalByName = (name: string) => Bluebird<AccountInstance>
|
||||||
export type LoadByNameAndHost = (name: string, host: string) => Bluebird<AccountInstance>
|
export type LoadByNameAndHost = (name: string, host: string) => Bluebird<AccountInstance>
|
||||||
|
export type ListByFollowersUrls = (followerUrls: string[], transaction?: Sequelize.Transaction) => Bluebird<AccountInstance[]>
|
||||||
|
|
||||||
export type ToActivityPubObject = (this: AccountInstance) => ActivityPubActor
|
export type ToActivityPubObject = (this: AccountInstance) => ActivityPubActor
|
||||||
export type ToFormattedJSON = (this: AccountInstance) => FormattedAccount
|
export type ToFormattedJSON = (this: AccountInstance) => FormattedAccount
|
||||||
|
@ -29,6 +30,7 @@ export interface AccountClass {
|
||||||
loadByUrl: AccountMethods.LoadByUrl
|
loadByUrl: AccountMethods.LoadByUrl
|
||||||
loadLocalByName: AccountMethods.LoadLocalByName
|
loadLocalByName: AccountMethods.LoadLocalByName
|
||||||
loadByNameAndHost: AccountMethods.LoadByNameAndHost
|
loadByNameAndHost: AccountMethods.LoadByNameAndHost
|
||||||
|
listByFollowersUrls: AccountMethods.ListByFollowersUrls
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccountAttributes {
|
export interface AccountAttributes {
|
||||||
|
|
|
@ -26,6 +26,7 @@ let loadByUUID: AccountMethods.LoadByUUID
|
||||||
let loadByUrl: AccountMethods.LoadByUrl
|
let loadByUrl: AccountMethods.LoadByUrl
|
||||||
let loadLocalByName: AccountMethods.LoadLocalByName
|
let loadLocalByName: AccountMethods.LoadLocalByName
|
||||||
let loadByNameAndHost: AccountMethods.LoadByNameAndHost
|
let loadByNameAndHost: AccountMethods.LoadByNameAndHost
|
||||||
|
let listByFollowersUrls: AccountMethods.ListByFollowersUrls
|
||||||
let isOwned: AccountMethods.IsOwned
|
let isOwned: AccountMethods.IsOwned
|
||||||
let toActivityPubObject: AccountMethods.ToActivityPubObject
|
let toActivityPubObject: AccountMethods.ToActivityPubObject
|
||||||
let toFormattedJSON: AccountMethods.ToFormattedJSON
|
let toFormattedJSON: AccountMethods.ToFormattedJSON
|
||||||
|
@ -188,7 +189,8 @@ export default function defineAccount (sequelize: Sequelize.Sequelize, DataTypes
|
||||||
loadByUUID,
|
loadByUUID,
|
||||||
loadByUrl,
|
loadByUrl,
|
||||||
loadLocalByName,
|
loadLocalByName,
|
||||||
loadByNameAndHost
|
loadByNameAndHost,
|
||||||
|
listByFollowersUrls
|
||||||
]
|
]
|
||||||
const instanceMethods = [
|
const instanceMethods = [
|
||||||
isOwned,
|
isOwned,
|
||||||
|
@ -427,3 +429,16 @@ loadByUrl = function (url: string, transaction?: Sequelize.Transaction) {
|
||||||
|
|
||||||
return Account.findOne(query)
|
return Account.findOne(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
listByFollowersUrls = function (followersUrls: string[], transaction?: Sequelize.Transaction) {
|
||||||
|
const query: Sequelize.FindOptions<AccountAttributes> = {
|
||||||
|
where: {
|
||||||
|
followersUrl: {
|
||||||
|
[Sequelize.Op.in]: followersUrls
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
return Account.findAll(query)
|
||||||
|
}
|
||||||
|
|
|
@ -6,27 +6,8 @@ import { join } from 'path'
|
||||||
import * as Sequelize from 'sequelize'
|
import * as Sequelize from 'sequelize'
|
||||||
import { VideoPrivacy, VideoResolution } from '../../../shared'
|
import { VideoPrivacy, VideoResolution } from '../../../shared'
|
||||||
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
|
import { VideoTorrentObject } from '../../../shared/models/activitypub/objects/video-torrent-object'
|
||||||
import {
|
|
||||||
createTorrentPromise,
|
|
||||||
generateImageFromVideoFile,
|
|
||||||
getVideoFileHeight,
|
|
||||||
isVideoCategoryValid,
|
|
||||||
isVideoDescriptionValid,
|
|
||||||
isVideoDurationValid,
|
|
||||||
isVideoLanguageValid,
|
|
||||||
isVideoLicenceValid,
|
|
||||||
isVideoNameValid,
|
|
||||||
isVideoNSFWValid,
|
|
||||||
isVideoPrivacyValid,
|
|
||||||
logger,
|
|
||||||
renamePromise,
|
|
||||||
statPromise,
|
|
||||||
transcode,
|
|
||||||
unlinkPromise,
|
|
||||||
writeFilePromise
|
|
||||||
} from '../../helpers'
|
|
||||||
import { activityPubCollection } from '../../helpers/activitypub'
|
import { activityPubCollection } from '../../helpers/activitypub'
|
||||||
import { isVideoUrlValid } from '../../helpers/custom-validators/videos'
|
import { isVideoCategoryValid, isVideoLanguageValid, isVideoPrivacyValid, isVideoUrlValid } from '../../helpers/custom-validators/videos'
|
||||||
import {
|
import {
|
||||||
API_VERSION,
|
API_VERSION,
|
||||||
CONFIG,
|
CONFIG,
|
||||||
|
@ -39,7 +20,7 @@ import {
|
||||||
VIDEO_LANGUAGES,
|
VIDEO_LANGUAGES,
|
||||||
VIDEO_LICENCES,
|
VIDEO_LICENCES,
|
||||||
VIDEO_PRIVACIES
|
VIDEO_PRIVACIES
|
||||||
} from '../../initializers'
|
} from '../../initializers/constants'
|
||||||
import { sendDeleteVideo } from '../../lib/index'
|
import { sendDeleteVideo } from '../../lib/index'
|
||||||
|
|
||||||
import { addMethodsToModel, getSort } from '../utils'
|
import { addMethodsToModel, getSort } from '../utils'
|
||||||
|
@ -47,6 +28,10 @@ import { addMethodsToModel, getSort } from '../utils'
|
||||||
import { TagInstance } from './tag-interface'
|
import { TagInstance } from './tag-interface'
|
||||||
import { VideoFileInstance, VideoFileModel } from './video-file-interface'
|
import { VideoFileInstance, VideoFileModel } from './video-file-interface'
|
||||||
import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
|
import { VideoAttributes, VideoInstance, VideoMethods } from './video-interface'
|
||||||
|
import { isVideoNameValid, isVideoLicenceValid, isVideoNSFWValid, isVideoDescriptionValid, isVideoDurationValid } from '../../helpers/index'
|
||||||
|
import { logger } from '../../helpers/logger'
|
||||||
|
import { generateImageFromVideoFile, transcode, getVideoFileHeight } from '../../helpers/ffmpeg-utils'
|
||||||
|
import { createTorrentPromise, writeFilePromise, unlinkPromise, renamePromise, statPromise } from '../../helpers/core-utils'
|
||||||
|
|
||||||
let Video: Sequelize.Model<VideoInstance, VideoAttributes>
|
let Video: Sequelize.Model<VideoInstance, VideoAttributes>
|
||||||
let getOriginalFile: VideoMethods.GetOriginalFile
|
let getOriginalFile: VideoMethods.GetOriginalFile
|
||||||
|
@ -1013,6 +998,10 @@ loadAndPopulateAccountAndServerAndTags = function (id: number) {
|
||||||
model: Video['sequelize'].models.AccountVideoRate,
|
model: Video['sequelize'].models.AccountVideoRate,
|
||||||
include: [ Video['sequelize'].models.Account ]
|
include: [ Video['sequelize'].models.Account ]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
model: Video['sequelize'].models.VideoShare,
|
||||||
|
include: [ Video['sequelize'].models.Account ]
|
||||||
|
},
|
||||||
Video['sequelize'].models.Tag,
|
Video['sequelize'].models.Tag,
|
||||||
Video['sequelize'].models.VideoFile
|
Video['sequelize'].models.VideoFile
|
||||||
]
|
]
|
||||||
|
@ -1040,6 +1029,10 @@ loadByUUIDAndPopulateAccountAndServerAndTags = function (uuid: string) {
|
||||||
model: Video['sequelize'].models.AccountVideoRate,
|
model: Video['sequelize'].models.AccountVideoRate,
|
||||||
include: [ Video['sequelize'].models.Account ]
|
include: [ Video['sequelize'].models.Account ]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
model: Video['sequelize'].models.VideoShare,
|
||||||
|
include: [ Video['sequelize'].models.Account ]
|
||||||
|
},
|
||||||
Video['sequelize'].models.Tag,
|
Video['sequelize'].models.Tag,
|
||||||
Video['sequelize'].models.VideoFile
|
Video['sequelize'].models.VideoFile
|
||||||
]
|
]
|
||||||
|
|
|
@ -10,6 +10,11 @@ export type Activity = ActivityCreate | ActivityAdd | ActivityUpdate |
|
||||||
|
|
||||||
export type ActivityType = 'Create' | 'Add' | 'Update' | 'Delete' | 'Follow' | 'Accept' | 'Announce' | 'Undo' | 'Like'
|
export type ActivityType = 'Create' | 'Add' | 'Update' | 'Delete' | 'Follow' | 'Accept' | 'Announce' | 'Undo' | 'Like'
|
||||||
|
|
||||||
|
export interface ActivityAudience {
|
||||||
|
to: string[]
|
||||||
|
cc: string[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface BaseActivity {
|
export interface BaseActivity {
|
||||||
'@context'?: any[]
|
'@context'?: any[]
|
||||||
id: string
|
id: string
|
||||||
|
|
Loading…
Reference in a new issue