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)
- 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 video = db.Video.build(videoData)
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 { ViewObject } from '../../../../shared/models/activitypub/objects/view-object'
import { logger, retryTransactionWrapper } from '../../../helpers'
import { database as db } from '../../../initializers'
import { AccountInstance } from '../../../models/account/account-interface'
import { getOrCreateAccountAndServer } from '../account'
-import { sendCreateDislikeToVideoFollowers, sendCreateViewToVideoFollowers } from '../send/send-create'
+import { forwardActivity } from '../send/misc'
import { getVideoChannelActivityPubUrl } from '../url'
import { videoChannelActivityObjectToDBAttributes } from './misc'
-import { DislikeObject } from '../../../../shared/models/activitypub/objects/dislike-object'
async function processCreateActivity (activity: ActivityCreate) {
const activityObject = activity.object
const account = await getOrCreateAccountAndServer(activity.actor)
if (activityType === 'View') {
- return processCreateView(activityObject as ViewObject)
+ return processCreateView(account, activity)
} else if (activityType === 'Dislike') {
- return processCreateDislike(account, activityObject as DislikeObject)
+ return processCreateDislike(account, activity)
} else if (activityType === 'VideoChannel') {
return processCreateVideoChannel(account, activityObject as VideoChannelObject)
} else if (activityType === 'Flag') {
// ---------------------------------------------------------------------------
-async function processCreateDislike (byAccount: AccountInstance, dislike: DislikeObject) {
+async function processCreateDislike (byAccount: AccountInstance, activity: ActivityCreate) {
const options = {
- arguments: [ byAccount, dislike ],
+ arguments: [ byAccount, activity ],
errorMessage: 'Cannot dislike the video with many retries.'
}
return retryTransactionWrapper(createVideoDislike, options)
}
-function createVideoDislike (byAccount: AccountInstance, dislike: DislikeObject) {
- return db.sequelize.transaction(async t => {
- const video = await db.Video.loadByUrlAndPopulateAccount(dislike.object)
+function createVideoDislike (byAccount: AccountInstance, activity: ActivityCreate) {
+ const dislike = activity.object as DislikeObject
+ return db.sequelize.transaction(async t => {
+ const video = await db.Video.loadByUrlAndPopulateAccount(dislike.object, t)
if (!video) throw new Error('Unknown video ' + dislike.object)
const rate = {
}
const [ , created ] = await db.AccountVideoRate.findOrCreate({
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)
if (!video) throw new Error('Unknown video ' + view.object)
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) {
return db.sequelize.transaction(async 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)
videoChannel = db.VideoChannel.build(videoChannelData)
import { ActivityLike } from '../../../../shared/models/activitypub/activity'
+import { retryTransactionWrapper } from '../../../helpers/database-utils'
import { database as db } from '../../../initializers'
import { AccountInstance } from '../../../models/account/account-interface'
import { getOrCreateAccountAndServer } from '../account'
-import { sendLikeToVideoFollowers } from '../send/send-like'
-import { retryTransactionWrapper } from '../../../helpers/database-utils'
+import { forwardActivity } from '../send/misc'
async function processLikeActivity (activity: ActivityLike) {
const account = await getOrCreateAccountAndServer(activity.actor)
- return processLikeVideo(account, activity.object)
+ return processLikeVideo(account, activity)
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
-async function processLikeVideo (byAccount: AccountInstance, videoUrl: string) {
+async function processLikeVideo (byAccount: AccountInstance, activity: ActivityLike) {
const options = {
- arguments: [ byAccount, videoUrl ],
+ arguments: [ byAccount, activity ],
errorMessage: 'Cannot like the video with many retries.'
}
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 => {
const video = await db.Video.loadByUrlAndPopulateAccount(videoUrl)
}
const [ , created ] = await db.AccountVideoRate.findOrCreate({
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)
+ }
})
}
import { retryTransactionWrapper } from '../../../helpers/database-utils'
import { logger } from '../../../helpers/logger'
import { database as db } from '../../../initializers'
-import { sendUndoDislikeToVideoFollowers } from '../index'
-import { sendUndoLikeToVideoFollowers } from '../send/send-undo'
+import { forwardActivity } from '../send/misc'
async function processUndoActivity (activity: ActivityUndo) {
const activityToUndo = activity.object
if (activityToUndo.type === 'Like') {
- return processUndoLike(activity.actor, activityToUndo)
+ return processUndoLike(activity.actor, activity)
} 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') {
return processUndoFollow(activity.actor, activityToUndo)
}
// ---------------------------------------------------------------------------
-function processUndoLike (actor: string, likeActivity: ActivityLike) {
+function processUndoLike (actor: string, activity: ActivityUndo) {
const options = {
- arguments: [ actor, likeActivity ],
+ arguments: [ actor, activity ],
errorMessage: 'Cannot undo like with many retries.'
}
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 => {
const byAccount = await db.Account.loadByUrl(actor, t)
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)
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}.`)
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 = {
- arguments: [ actor, dislikeCreateActivity ],
+ arguments: [ actor, activity ],
errorMessage: 'Cannot undo dislike with many retries.'
}
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 => {
const byAccount = await db.Account.loadByUrl(actor, t)
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)
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}.`)
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)
+ }
})
}
import { logger } from '../../../helpers/logger'
import { ACTIVITY_PUB, database as db } from '../../../initializers'
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 { 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 (
data: any,
t: Transaction,
followersException: AccountInstance[] = []
) {
- const toAccountFollowerIds = toAccountFollowers.map(a => a.id)
-
- const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds)
- if (result.data.length === 0) {
- logger.info('Not broadcast because of 0 followers for %s.', toAccountFollowerIds.join(', '))
- return undefined
+ const uris = await computeFollowerUris(toAccountFollowers, followersException)
+ if (uris.length === 0) {
+ logger.info('0 followers for %s, no broadcasting.', toAccountFollowers.map(a => a.id).join(', '))
+ return
}
- const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl)
- const uris = result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1)
+ logger.debug('Creating broadcast job.', { uris })
- const jobPayload = {
+ const jobPayload: ActivityPubHttpPayload = {
uris,
signatureAccountId: byAccount.id,
body: data
}
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 ],
signatureAccountId: byAccount.id,
body: data
return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
}
-function getOriginVideoAudience (video: VideoInstance) {
+function getOriginVideoAudience (video: VideoInstance, accountsInvolvedInVideo: AccountInstance[]) {
return {
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 {
- to: [ video.VideoChannel.Account.url + '/followers' ],
+ to: accountsInvolvedInVideo.map(a => a.followersUrl),
cc: []
}
}
-async function getAccountsToForwardVideoAction (byAccount: AccountInstance, video: VideoInstance) {
+async function getAccountsInvolvedInVideo (video: VideoInstance) {
const accountsToForwardView = await db.VideoShare.loadAccountsByShare(video.id)
accountsToForwardView.push(video.VideoChannel.Account)
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 {
unicastTo,
getAudience,
getOriginVideoAudience,
- getAccountsToForwardVideoAction,
- getVideoFollowersAudience
+ getAccountsInvolvedInVideo,
+ getVideoFollowersAudience,
+ forwardActivity
}
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 { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface'
import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
import {
broadcastToFollowers,
- getAccountsToForwardVideoAction,
+ getAccountsInvolvedInVideo,
getAudience,
getOriginVideoAudience,
getVideoFollowersAudience,
const url = getVideoViewActivityPubUrl(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)
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
const url = getVideoViewActivityPubUrl(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)
+ // Use the server account to send the view, because it could be an unregistered account
const serverAccount = await getServerAccount()
- const accountsToForwardView = await getAccountsToForwardVideoAction(byAccount, video)
-
const followersException = [ byAccount ]
return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException)
}
const url = getVideoDislikeActivityPubUrl(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)
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
const url = getVideoDislikeActivityPubUrl(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 accountsToForwardView = await getAccountsToForwardVideoAction(byAccount, video)
- const serverAccount = await getServerAccount()
-
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) {
audience = await getAudience(byAccount)
}
import { Transaction } from 'sequelize'
import { ActivityLike } from '../../../../shared/models/activitypub/activity'
-import { getServerAccount } from '../../../helpers/utils'
import { AccountInstance, VideoInstance } from '../../../models'
import { getVideoLikeActivityPubUrl } from '../url'
import {
broadcastToFollowers,
- getAccountsToForwardVideoAction,
+ getAccountsInvolvedInVideo,
getAudience,
getOriginVideoAudience,
getVideoFollowersAudience,
async function sendLikeToOrigin (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
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)
return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
async function sendLikeToVideoFollowers (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
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 accountsToForwardView = await getAccountsToForwardVideoAction(byAccount, video)
- const serverAccount = await getServerAccount()
+ const toAccountsFollowers = await getAccountsInvolvedInVideo(video)
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[] }) {
import { Transaction } from 'sequelize'
-import { ActivityCreate, ActivityFollow, ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub/activity'
-import { getServerAccount } from '../../../helpers/utils'
+import {
+ ActivityAudience,
+ ActivityCreate,
+ ActivityFollow,
+ ActivityLike,
+ ActivityUndo
+} from '../../../../shared/models/activitypub/activity'
import { AccountInstance } from '../../../models'
import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
import { VideoInstance } from '../../../models/video/video-interface'
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 { followActivityData } from './send-follow'
import { likeActivityData } from './send-like'
const likeUrl = getVideoLikeActivityPubUrl(byAccount, video)
const undoUrl = getUndoActivityPubUrl(likeUrl)
+ const toAccountsFollowers = await getAccountsInvolvedInVideo(video)
+ const audience = getVideoFollowersAudience(toAccountsFollowers)
const object = await likeActivityData(likeUrl, byAccount, video)
- const data = await undoActivityData(undoUrl, byAccount, object)
-
- const accountsToForwardView = await getAccountsToForwardVideoAction(byAccount, video)
- const serverAccount = await getServerAccount()
+ const data = await undoActivityData(undoUrl, byAccount, object, audience)
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) {
const data = await undoActivityData(undoUrl, byAccount, object)
- const accountsToForwardView = await getAccountsToForwardVideoAction(byAccount, video)
- const serverAccount = await getServerAccount()
+ const toAccountsFollowers = await getAccountsInvolvedInVideo(video)
const followersException = [ byAccount ]
- return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException)
+ return broadcastToFollowers(data, byAccount, toAccountsFollowers, t, followersException)
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
-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 = {
type: 'Undo',
id: url,
actor: byAccount.url,
+ to: audience.to,
+ cc: audience.cc,
object
}
import { logger } from '../../../helpers'
-import { buildSignedActivity } from '../../../helpers/activitypub'
import { doRequest } from '../../../helpers/requests'
-import { database as db } from '../../../initializers'
-import { ActivityPubHttpPayload, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
+import { ActivityPubHttpPayload, computeBody, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
async function process (payload: ActivityPubHttpPayload, jobId: number) {
logger.info('Processing ActivityPub broadcast in job %d.', jobId)
- const accountSignature = await db.Account.load(payload.signatureAccountId)
- if (!accountSignature) throw new Error('Unknown signature account id.')
-
- const signedBody = await buildSignedActivity(accountSignature, payload.body)
+ const body = await computeBody(payload)
const options = {
method: 'POST',
uri: '',
- json: signedBody
+ json: body
}
for (const uri of payload.uris) {
-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 activitypubHttpUnicastHandler from './activitypub-http-unicast-handler'
import * as activitypubHttpFetcherHandler from './activitypub-http-fetcher-handler'
-import { JobCategory } from '../../../../shared'
-import { ACTIVITY_PUB } from '../../../initializers/constants'
-import { logger } from '../../../helpers/logger'
+import * as activitypubHttpUnicastHandler from './activitypub-http-unicast-handler'
type ActivityPubHttpPayload = {
uris: string[]
}
}
+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 {
ActivityPubHttpPayload,
activitypubHttpJobScheduler,
- maybeRetryRequestLater
+ maybeRetryRequestLater,
+ computeBody
}
import { logger } from '../../../helpers'
import { doRequest } from '../../../helpers/requests'
-import { ActivityPubHttpPayload, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
-import { database as db } from '../../../initializers/database'
-import { buildSignedActivity } from '../../../helpers/activitypub'
+import { ActivityPubHttpPayload, computeBody, maybeRetryRequestLater } from './activitypub-http-job-scheduler'
async function process (payload: ActivityPubHttpPayload, jobId: number) {
logger.info('Processing ActivityPub unicast in job %d.', jobId)
- const accountSignature = await db.Account.load(payload.signatureAccountId)
- if (!accountSignature) throw new Error('Unknown signature account id.')
+ const body = await computeBody(payload)
- const signedBody = await buildSignedActivity(accountSignature, payload.body)
const uri = payload.uris[0]
const options = {
method: 'POST',
uri,
- json: signedBody
+ json: body
}
try {
export type LoadByUrl = (url: string, transaction?: Sequelize.Transaction) => Bluebird<AccountInstance>
export type LoadLocalByName = (name: 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 ToFormattedJSON = (this: AccountInstance) => FormattedAccount
loadByUrl: AccountMethods.LoadByUrl
loadLocalByName: AccountMethods.LoadLocalByName
loadByNameAndHost: AccountMethods.LoadByNameAndHost
+ listByFollowersUrls: AccountMethods.ListByFollowersUrls
}
export interface AccountAttributes {
let loadByUrl: AccountMethods.LoadByUrl
let loadLocalByName: AccountMethods.LoadLocalByName
let loadByNameAndHost: AccountMethods.LoadByNameAndHost
+let listByFollowersUrls: AccountMethods.ListByFollowersUrls
let isOwned: AccountMethods.IsOwned
let toActivityPubObject: AccountMethods.ToActivityPubObject
let toFormattedJSON: AccountMethods.ToFormattedJSON
loadByUUID,
loadByUrl,
loadLocalByName,
- loadByNameAndHost
+ loadByNameAndHost,
+ listByFollowersUrls
]
const instanceMethods = [
isOwned,
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)
+}
import * as Sequelize from 'sequelize'
import { VideoPrivacy, VideoResolution } from '../../../shared'
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 { isVideoUrlValid } from '../../helpers/custom-validators/videos'
+import { isVideoCategoryValid, isVideoLanguageValid, isVideoPrivacyValid, isVideoUrlValid } from '../../helpers/custom-validators/videos'
import {
API_VERSION,
CONFIG,
VIDEO_LANGUAGES,
VIDEO_LICENCES,
VIDEO_PRIVACIES
-} from '../../initializers'
+} from '../../initializers/constants'
import { sendDeleteVideo } from '../../lib/index'
import { addMethodsToModel, getSort } from '../utils'
import { TagInstance } from './tag-interface'
import { VideoFileInstance, VideoFileModel } from './video-file-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 getOriginalFile: VideoMethods.GetOriginalFile
model: Video['sequelize'].models.AccountVideoRate,
include: [ Video['sequelize'].models.Account ]
},
+ {
+ model: Video['sequelize'].models.VideoShare,
+ include: [ Video['sequelize'].models.Account ]
+ },
Video['sequelize'].models.Tag,
Video['sequelize'].models.VideoFile
]
model: Video['sequelize'].models.AccountVideoRate,
include: [ Video['sequelize'].models.Account ]
},
+ {
+ model: Video['sequelize'].models.VideoShare,
+ include: [ Video['sequelize'].models.Account ]
+ },
Video['sequelize'].models.Tag,
Video['sequelize'].models.VideoFile
]
export type ActivityType = 'Create' | 'Add' | 'Update' | 'Delete' | 'Follow' | 'Accept' | 'Announce' | 'Undo' | 'Like'
+export interface ActivityAudience {
+ to: string[]
+ cc: string[]
+}
+
export interface BaseActivity {
'@context'?: any[]
id: string