From 50d6de9c286abcb34ff4234d56d9cbb803db7665 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 14 Dec 2017 17:38:41 +0100 Subject: Begin moving video channel to actor --- server/lib/activitypub/account.ts | 127 ------------ server/lib/activitypub/actor.ts | 229 +++++++++++++++++++++ server/lib/activitypub/fetch.ts | 6 +- server/lib/activitypub/index.ts | 3 +- server/lib/activitypub/process/index.ts | 1 - server/lib/activitypub/process/misc.ts | 52 +---- server/lib/activitypub/process/process-accept.ts | 18 +- server/lib/activitypub/process/process-add.ts | 137 ------------ server/lib/activitypub/process/process-announce.ts | 67 ++---- server/lib/activitypub/process/process-create.ts | 187 ++++++++++++----- server/lib/activitypub/process/process-delete.ts | 72 ++++--- server/lib/activitypub/process/process-follow.ts | 48 ++--- server/lib/activitypub/process/process-like.ts | 19 +- server/lib/activitypub/process/process-undo.ts | 43 ++-- server/lib/activitypub/process/process-update.ts | 63 ++---- server/lib/activitypub/process/process.ts | 14 +- server/lib/activitypub/send/index.ts | 1 - server/lib/activitypub/send/misc.ts | 80 +++---- server/lib/activitypub/send/send-accept.ts | 22 +- server/lib/activitypub/send/send-add.ts | 45 ---- server/lib/activitypub/send/send-announce.ts | 84 +++----- server/lib/activitypub/send/send-create.ts | 107 +++++----- server/lib/activitypub/send/send-delete.ts | 38 ++-- server/lib/activitypub/send/send-follow.ts | 20 +- server/lib/activitypub/send/send-like.ts | 34 +-- server/lib/activitypub/send/send-undo.ts | 84 ++++---- server/lib/activitypub/send/send-update.ts | 52 +++-- server/lib/activitypub/share.ts | 24 +-- server/lib/activitypub/url.ts | 46 +++-- server/lib/activitypub/video-channels.ts | 59 ------ server/lib/activitypub/videos.ts | 28 +-- 31 files changed, 777 insertions(+), 1033 deletions(-) delete mode 100644 server/lib/activitypub/account.ts create mode 100644 server/lib/activitypub/actor.ts delete mode 100644 server/lib/activitypub/process/process-add.ts delete mode 100644 server/lib/activitypub/send/send-add.ts delete mode 100644 server/lib/activitypub/video-channels.ts (limited to 'server/lib/activitypub') diff --git a/server/lib/activitypub/account.ts b/server/lib/activitypub/account.ts deleted file mode 100644 index 45690b88d..000000000 --- a/server/lib/activitypub/account.ts +++ /dev/null @@ -1,127 +0,0 @@ -import * as Bluebird from 'bluebird' -import { Transaction } from 'sequelize' -import * as url from 'url' -import { ActivityPubActor } from '../../../shared/models/activitypub' -import { doRequest, logger, retryTransactionWrapper } from '../../helpers' -import { isRemoteAccountValid } from '../../helpers/custom-validators/activitypub' -import { ACTIVITY_PUB, sequelizeTypescript } from '../../initializers' -import { AccountModel } from '../../models/account/account' -import { ServerModel } from '../../models/server/server' - -async function getOrCreateAccountAndServer (accountUrl: string) { - let account = await AccountModel.loadByUrl(accountUrl) - - // We don't have this account in our database, fetch it on remote - if (!account) { - account = await fetchRemoteAccount(accountUrl) - if (account === undefined) throw new Error('Cannot fetch remote account.') - - const options = { - arguments: [ account ], - errorMessage: 'Cannot save account and server with many retries.' - } - account = await retryTransactionWrapper(saveAccountAndServerIfNotExist, options) - } - - return account -} - -function saveAccountAndServerIfNotExist (account: AccountModel, t?: Transaction): Bluebird | Promise { - if (t !== undefined) { - return save(t) - } else { - return sequelizeTypescript.transaction(t => { - return save(t) - }) - } - - async function save (t: Transaction) { - const accountHost = url.parse(account.url).host - - const serverOptions = { - where: { - host: accountHost - }, - defaults: { - host: accountHost - }, - transaction: t - } - const [ server ] = await ServerModel.findOrCreate(serverOptions) - - // Save our new account in database - account.set('serverId', server.id) - account = await account.save({ transaction: t }) - - return account - } -} - -async function fetchRemoteAccount (accountUrl: string) { - const options = { - uri: accountUrl, - method: 'GET', - headers: { - 'Accept': ACTIVITY_PUB.ACCEPT_HEADER - } - } - - logger.info('Fetching remote account %s.', accountUrl) - - let requestResult - try { - requestResult = await doRequest(options) - } catch (err) { - logger.warn('Cannot fetch remote account %s.', accountUrl, err) - return undefined - } - - const accountJSON: ActivityPubActor = JSON.parse(requestResult.body) - if (isRemoteAccountValid(accountJSON) === false) { - logger.debug('Remote account JSON is not valid.', { accountJSON }) - return undefined - } - - const followersCount = await fetchAccountCount(accountJSON.followers) - const followingCount = await fetchAccountCount(accountJSON.following) - - return new AccountModel({ - uuid: accountJSON.uuid, - name: accountJSON.preferredUsername, - url: accountJSON.url, - publicKey: accountJSON.publicKey.publicKeyPem, - privateKey: null, - followersCount: followersCount, - followingCount: followingCount, - inboxUrl: accountJSON.inbox, - outboxUrl: accountJSON.outbox, - sharedInboxUrl: accountJSON.endpoints.sharedInbox, - followersUrl: accountJSON.followers, - followingUrl: accountJSON.following - }) -} - -export { - getOrCreateAccountAndServer, - fetchRemoteAccount, - saveAccountAndServerIfNotExist -} - -// --------------------------------------------------------------------------- - -async function fetchAccountCount (url: string) { - const options = { - uri: url, - method: 'GET' - } - - let requestResult - try { - requestResult = await doRequest(options) - } catch (err) { - logger.warn('Cannot fetch remote account count %s.', url, err) - return undefined - } - - return requestResult.totalItems ? requestResult.totalItems : 0 -} diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts new file mode 100644 index 000000000..c3de4bdce --- /dev/null +++ b/server/lib/activitypub/actor.ts @@ -0,0 +1,229 @@ +import * as Bluebird from 'bluebird' +import { Transaction } from 'sequelize' +import * as url from 'url' +import { ActivityPubActor, ActivityPubActorType } from '../../../shared/models/activitypub' +import { ActivityPubAttributedTo } from '../../../shared/models/activitypub/objects' +import { createPrivateAndPublicKeys, doRequest, logger, retryTransactionWrapper } from '../../helpers' +import { isRemoteActorValid } from '../../helpers/custom-validators/activitypub' +import { ACTIVITY_PUB, CONFIG, sequelizeTypescript } from '../../initializers' +import { AccountModel } from '../../models/account/account' +import { ActorModel } from '../../models/activitypub/actor' +import { ServerModel } from '../../models/server/server' +import { VideoChannelModel } from '../../models/video/video-channel' + + // Set account keys, this could be long so process after the account creation and do not block the client +function setAsyncActorKeys (actor: ActorModel) { + return createPrivateAndPublicKeys() + .then(({ publicKey, privateKey }) => { + actor.set('publicKey', publicKey) + actor.set('privateKey', privateKey) + return actor.save() + }) + .catch(err => { + logger.error('Cannot set public/private keys of actor %d.', actor.uuid, err) + return actor + }) +} + +async function getOrCreateActorAndServerAndModel (actorUrl: string, recurseIfNeeded = true) { + let actor = await ActorModel.loadByUrl(actorUrl) + + // We don't have this actor in our database, fetch it on remote + if (!actor) { + const result = await fetchRemoteActor(actorUrl) + if (result === undefined) throw new Error('Cannot fetch remote actor.') + + // Create the attributed to actor + // In PeerTube a video channel is owned by an account + let ownerActor: ActorModel = undefined + if (recurseIfNeeded === true && result.actor.type === 'Group') { + const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person') + if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url) + + try { + // Assert we don't recurse another time + ownerActor = await getOrCreateActorAndServerAndModel(accountAttributedTo.id, false) + } catch (err) { + logger.error('Cannot get or create account attributed to video channel ' + actor.url) + throw new Error(err) + } + } + + const options = { + arguments: [ result, ownerActor ], + errorMessage: 'Cannot save actor and server with many retries.' + } + actor = await retryTransactionWrapper(saveActorAndServerAndModelIfNotExist, options) + } + + return actor +} + +function saveActorAndServerAndModelIfNotExist ( + result: FetchRemoteActorResult, + ownerActor?: ActorModel, + t?: Transaction +): Bluebird | Promise { + let actor = result.actor + + if (t !== undefined) return save(t) + + return sequelizeTypescript.transaction(t => save(t)) + + async function save (t: Transaction) { + const actorHost = url.parse(actor.url).host + + const serverOptions = { + where: { + host: actorHost + }, + defaults: { + host: actorHost + }, + transaction: t + } + const [ server ] = await ServerModel.findOrCreate(serverOptions) + + // Save our new account in database + actor.set('serverId', server.id) + + // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists + // (which could be false in a retried query) + const actorCreated = await ActorModel.create(actor.toJSON(), { transaction: t }) + + if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { + const account = await saveAccount(actorCreated, result, t) + actorCreated.Account = account + actorCreated.Account.Actor = actorCreated + } else if (actorCreated.type === 'Group') { // Video channel + const videoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) + actorCreated.VideoChannel = videoChannel + actorCreated.VideoChannel.Actor = actorCreated + } + + return actorCreated + } +} + +type FetchRemoteActorResult = { + actor: ActorModel + preferredUsername: string + summary: string + attributedTo: ActivityPubAttributedTo[] +} +async function fetchRemoteActor (actorUrl: string): Promise { + const options = { + uri: actorUrl, + method: 'GET', + headers: { + 'Accept': ACTIVITY_PUB.ACCEPT_HEADER + } + } + + logger.info('Fetching remote actor %s.', actorUrl) + + let requestResult + try { + requestResult = await doRequest(options) + } catch (err) { + logger.warn('Cannot fetch remote actor %s.', actorUrl, err) + return undefined + } + + const actorJSON: ActivityPubActor = JSON.parse(requestResult.body) + if (isRemoteActorValid(actorJSON) === false) { + logger.debug('Remote actor JSON is not valid.', { actorJSON: actorJSON }) + return undefined + } + + const followersCount = await fetchActorTotalItems(actorJSON.followers) + const followingCount = await fetchActorTotalItems(actorJSON.following) + + const actor = new ActorModel({ + type: actorJSON.type, + uuid: actorJSON.uuid, + name: actorJSON.name, + url: actorJSON.url, + publicKey: actorJSON.publicKey.publicKeyPem, + privateKey: null, + followersCount: followersCount, + followingCount: followingCount, + inboxUrl: actorJSON.inbox, + outboxUrl: actorJSON.outbox, + sharedInboxUrl: actorJSON.endpoints.sharedInbox, + followersUrl: actorJSON.followers, + followingUrl: actorJSON.following + }) + + return { + actor, + preferredUsername: actorJSON.preferredUsername, + summary: actorJSON.summary, + attributedTo: actorJSON.attributedTo + } +} + +function buildActorInstance (type: ActivityPubActorType, url: string, name: string, uuid?: string) { + return new ActorModel({ + type, + url, + name, + uuid, + publicKey: null, + privateKey: null, + followersCount: 0, + followingCount: 0, + inboxUrl: url + '/inbox', + outboxUrl: url + '/outbox', + sharedInboxUrl: CONFIG.WEBSERVER.URL + '/inbox', + followersUrl: url + '/followers', + followingUrl: url + '/following' + }) +} + +export { + getOrCreateActorAndServerAndModel, + saveActorAndServerAndModelIfNotExist, + fetchRemoteActor, + buildActorInstance, + setAsyncActorKeys +} + +// --------------------------------------------------------------------------- + +async function fetchActorTotalItems (url: string) { + const options = { + uri: url, + method: 'GET' + } + + let requestResult + try { + requestResult = await doRequest(options) + } catch (err) { + logger.warn('Cannot fetch remote actor count %s.', url, err) + return undefined + } + + return requestResult.totalItems ? requestResult.totalItems : 0 +} + +function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Transaction) { + const account = new AccountModel({ + name: result.preferredUsername, + actorId: actor.id + }) + + return account.save({ transaction: t }) +} + +async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResult, ownerActor: ActorModel, t: Transaction) { + const videoChannel = new VideoChannelModel({ + name: result.preferredUsername, + description: result.summary, + actorId: actor.id, + accountId: ownerActor.Account.id + }) + + return videoChannel.save({ transaction: t }) +} diff --git a/server/lib/activitypub/fetch.ts b/server/lib/activitypub/fetch.ts index aa4dea8e0..4fc97cc38 100644 --- a/server/lib/activitypub/fetch.ts +++ b/server/lib/activitypub/fetch.ts @@ -1,10 +1,10 @@ import { Transaction } from 'sequelize' -import { AccountModel } from '../../models/account/account' +import { ActorModel } from '../../models/activitypub/actor' import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../jobs/activitypub-http-job-scheduler' -async function addFetchOutboxJob (account: AccountModel, t: Transaction) { +async function addFetchOutboxJob (actor: ActorModel, t: Transaction) { const jobPayload: ActivityPubHttpPayload = { - uris: [ account.outboxUrl ] + uris: [ actor.outboxUrl ] } return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpFetcherHandler', jobPayload) diff --git a/server/lib/activitypub/index.ts b/server/lib/activitypub/index.ts index fcea662a6..94ed1edaa 100644 --- a/server/lib/activitypub/index.ts +++ b/server/lib/activitypub/index.ts @@ -1,8 +1,7 @@ export * from './process' export * from './send' -export * from './account' +export * from './actor' export * from './fetch' export * from './share' -export * from './video-channels' export * from './videos' export * from './url' diff --git a/server/lib/activitypub/process/index.ts b/server/lib/activitypub/process/index.ts index e25c261cc..db4980a72 100644 --- a/server/lib/activitypub/process/index.ts +++ b/server/lib/activitypub/process/index.ts @@ -1,6 +1,5 @@ export * from './process' export * from './process-accept' -export * from './process-add' export * from './process-announce' export * from './process-create' export * from './process-delete' diff --git a/server/lib/activitypub/process/misc.ts b/server/lib/activitypub/process/misc.ts index a775c858a..a9c6f913c 100644 --- a/server/lib/activitypub/process/misc.ts +++ b/server/lib/activitypub/process/misc.ts @@ -1,29 +1,13 @@ import * as magnetUtil from 'magnet-uri' import { VideoTorrentObject } from '../../../../shared' -import { VideoChannelObject } from '../../../../shared/models/activitypub/objects' import { VideoPrivacy } from '../../../../shared/models/videos' import { doRequest } from '../../../helpers' import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos' import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers' -import { AccountModel } from '../../../models/account/account' import { VideoModel } from '../../../models/video/video' import { VideoChannelModel } from '../../../models/video/video-channel' -import { VideoChannelShareModel } from '../../../models/video/video-channel-share' import { VideoShareModel } from '../../../models/video/video-share' -import { getOrCreateAccountAndServer } from '../account' - -function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountModel) { - return { - name: videoChannelObject.name, - description: videoChannelObject.content, - uuid: videoChannelObject.uuid, - url: videoChannelObject.id, - createdAt: new Date(videoChannelObject.published), - updatedAt: new Date(videoChannelObject.updated), - remote: true, - accountId: account.id - } -} +import { getOrCreateActorAndServerAndModel } from '../actor' async function videoActivityObjectToDBAttributes ( videoChannel: VideoChannelModel, @@ -120,13 +104,13 @@ async function addVideoShares (instance: VideoModel, shares: string[]) { uri: share, json: true }) - const actor = json['actor'] - if (!actor) continue + const actorUrl = json['actor'] + if (!actorUrl) continue - const account = await getOrCreateAccountAndServer(actor) + const actor = await getOrCreateActorAndServerAndModel(actorUrl) const entry = { - accountId: account.id, + actorId: actor.id, videoId: instance.id } @@ -137,36 +121,10 @@ async function addVideoShares (instance: VideoModel, shares: string[]) { } } -async function addVideoChannelShares (instance: VideoChannelModel, shares: string[]) { - for (const share of shares) { - // Fetch url - const json = await doRequest({ - uri: share, - json: true - }) - const actor = json['actor'] - if (!actor) continue - - const account = await getOrCreateAccountAndServer(actor) - - const entry = { - accountId: account.id, - videoChannelId: instance.id - } - - await VideoChannelShareModel.findOrCreate({ - where: entry, - defaults: entry - }) - } -} - // --------------------------------------------------------------------------- export { videoFileActivityUrlToDBAttributes, videoActivityObjectToDBAttributes, - videoChannelActivityObjectToDBAttributes, - addVideoChannelShares, addVideoShares } diff --git a/server/lib/activitypub/process/process-accept.ts b/server/lib/activitypub/process/process-accept.ts index 5b321f771..b9d906ec9 100644 --- a/server/lib/activitypub/process/process-accept.ts +++ b/server/lib/activitypub/process/process-accept.ts @@ -1,14 +1,14 @@ import { ActivityAccept } from '../../../../shared/models/activitypub' -import { AccountModel } from '../../../models/account/account' -import { AccountFollowModel } from '../../../models/account/account-follow' +import { ActorModel } from '../../../models/activitypub/actor' +import { ActorFollowModel } from '../../../models/activitypub/actor-follow' import { addFetchOutboxJob } from '../fetch' -async function processAcceptActivity (activity: ActivityAccept, inboxAccount?: AccountModel) { - if (inboxAccount === undefined) throw new Error('Need to accept on explicit inbox.') +async function processAcceptActivity (activity: ActivityAccept, inboxActor?: ActorModel) { + if (inboxActor === undefined) throw new Error('Need to accept on explicit inbox.') - const targetAccount = await AccountModel.loadByUrl(activity.actor) + const targetActor = await ActorModel.loadByUrl(activity.actor) - return processAccept(inboxAccount, targetAccount) + return processAccept(inboxActor, targetActor) } // --------------------------------------------------------------------------- @@ -19,11 +19,11 @@ export { // --------------------------------------------------------------------------- -async function processAccept (account: AccountModel, targetAccount: AccountModel) { - const follow = await AccountFollowModel.loadByAccountAndTarget(account.id, targetAccount.id) +async function processAccept (actor: ActorModel, targetActor: ActorModel) { + const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id) if (!follow) throw new Error('Cannot find associated follow.') follow.set('state', 'accepted') await follow.save() - await addFetchOutboxJob(targetAccount, undefined) + await addFetchOutboxJob(targetActor, undefined) } diff --git a/server/lib/activitypub/process/process-add.ts b/server/lib/activitypub/process/process-add.ts deleted file mode 100644 index 550593eab..000000000 --- a/server/lib/activitypub/process/process-add.ts +++ /dev/null @@ -1,137 +0,0 @@ -import * as Bluebird from 'bluebird' -import { VideoTorrentObject } from '../../../../shared' -import { ActivityAdd } from '../../../../shared/models/activitypub' -import { VideoRateType } from '../../../../shared/models/videos' -import { logger, retryTransactionWrapper } from '../../../helpers' -import { sequelizeTypescript } from '../../../initializers' -import { AccountModel } from '../../../models/account/account' -import { AccountVideoRateModel } from '../../../models/account/account-video-rate' -import { TagModel } from '../../../models/video/tag' -import { VideoModel } from '../../../models/video/video' -import { VideoChannelModel } from '../../../models/video/video-channel' -import { VideoFileModel } from '../../../models/video/video-file' -import { getOrCreateAccountAndServer } from '../account' -import { getOrCreateVideoChannel } from '../video-channels' -import { generateThumbnailFromUrl } from '../videos' -import { addVideoShares, videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' - -async function processAddActivity (activity: ActivityAdd) { - const activityObject = activity.object - const activityType = activityObject.type - const account = await getOrCreateAccountAndServer(activity.actor) - - if (activityType === 'Video') { - const videoChannelUrl = activity.target - const videoChannel = await getOrCreateVideoChannel(account, videoChannelUrl) - - return processAddVideo(account, activity, videoChannel, activityObject) - } - - logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) - return Promise.resolve(undefined) -} - -// --------------------------------------------------------------------------- - -export { - processAddActivity -} - -// --------------------------------------------------------------------------- - -async function processAddVideo (account: AccountModel, - activity: ActivityAdd, - videoChannel: VideoChannelModel, - videoToCreateData: VideoTorrentObject) { - const options = { - arguments: [ account, activity, videoChannel, videoToCreateData ], - errorMessage: 'Cannot insert the remote video with many retries.' - } - - const video = await retryTransactionWrapper(addRemoteVideo, options) - - // Process outside the transaction because we could fetch remote data - if (videoToCreateData.likes && Array.isArray(videoToCreateData.likes.orderedItems)) { - await createRates(videoToCreateData.likes.orderedItems, video, 'like') - } - - if (videoToCreateData.dislikes && Array.isArray(videoToCreateData.dislikes.orderedItems)) { - await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike') - } - - if (videoToCreateData.shares && Array.isArray(videoToCreateData.shares.orderedItems)) { - await addVideoShares(video, videoToCreateData.shares.orderedItems) - } - - return video -} - -function addRemoteVideo (account: AccountModel, - activity: ActivityAdd, - videoChannel: VideoChannelModel, - videoToCreateData: VideoTorrentObject) { - logger.debug('Adding remote video %s.', videoToCreateData.id) - - return sequelizeTypescript.transaction(async t => { - const sequelizeOptions = { - transaction: t - } - - if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.') - - const videoFromDatabase = await VideoModel.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t) - if (videoFromDatabase) return videoFromDatabase - - const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, activity.to, activity.cc) - const video = VideoModel.build(videoData) - - // Don't block on request - generateThumbnailFromUrl(video, videoToCreateData.icon) - .catch(err => logger.warn('Cannot generate thumbnail of %s.', videoToCreateData.id, err)) - - const videoCreated = await video.save(sequelizeOptions) - - const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoCreated, videoToCreateData) - if (videoFileAttributes.length === 0) { - throw new Error('Cannot find valid files for video %s ' + videoToCreateData.url) - } - - const tasks: Bluebird[] = videoFileAttributes.map(f => VideoFileModel.create(f, { transaction: t })) - await Promise.all(tasks) - - const tags = videoToCreateData.tag.map(t => t.name) - const tagInstances = await TagModel.findOrCreateTags(tags, t) - await videoCreated.$set('Tags', tagInstances, sequelizeOptions) - - logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid) - - return videoCreated - }) -} - -async function createRates (accountUrls: string[], video: VideoModel, rate: VideoRateType) { - let rateCounts = 0 - const tasks: Bluebird[] = [] - - for (const accountUrl of accountUrls) { - const account = await getOrCreateAccountAndServer(accountUrl) - const p = AccountVideoRateModel - .create({ - videoId: video.id, - accountId: account.id, - type: rate - }) - .then(() => rateCounts += 1) - - tasks.push(p) - } - - await Promise.all(tasks) - - logger.info('Adding %d %s to video %s.', rateCounts, rate, video.uuid) - - // This is "likes" and "dislikes" - await video.increment(rate + 's', { by: rateCounts }) - - return -} diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts index ff2c6d708..7dfee2f60 100644 --- a/server/lib/activitypub/process/process-announce.ts +++ b/server/lib/activitypub/process/process-announce.ts @@ -1,24 +1,19 @@ -import { ActivityAdd, ActivityAnnounce, ActivityCreate } from '../../../../shared/models/activitypub' +import { ActivityAnnounce } from '../../../../shared/models/activitypub' import { logger, retryTransactionWrapper } from '../../../helpers' import { sequelizeTypescript } from '../../../initializers' -import { AccountModel } from '../../../models/account/account' +import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' -import { VideoChannelModel } from '../../../models/video/video-channel' -import { VideoChannelShareModel } from '../../../models/video/video-channel-share' import { VideoShareModel } from '../../../models/video/video-share' -import { getOrCreateAccountAndServer } from '../account' +import { getOrCreateActorAndServerAndModel } from '../actor' import { forwardActivity } from '../send/misc' -import { processAddActivity } from './process-add' import { processCreateActivity } from './process-create' async function processAnnounceActivity (activity: ActivityAnnounce) { const announcedActivity = activity.object - const accountAnnouncer = await getOrCreateAccountAndServer(activity.actor) + const actorAnnouncer = await getOrCreateActorAndServerAndModel(activity.actor) - if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'VideoChannel') { - return processVideoChannelShare(accountAnnouncer, activity) - } else if (announcedActivity.type === 'Add' && announcedActivity.object.type === 'Video') { - return processVideoShare(accountAnnouncer, activity) + if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'Video') { + return processVideoShare(actorAnnouncer, activity) } logger.warn( @@ -37,60 +32,24 @@ export { // --------------------------------------------------------------------------- -function processVideoChannelShare (accountAnnouncer: AccountModel, activity: ActivityAnnounce) { +function processVideoShare (actorAnnouncer: ActorModel, activity: ActivityAnnounce) { const options = { - arguments: [ accountAnnouncer, activity ], - errorMessage: 'Cannot share the video channel with many retries.' - } - - return retryTransactionWrapper(shareVideoChannel, options) -} - -async function shareVideoChannel (accountAnnouncer: AccountModel, activity: ActivityAnnounce) { - const announcedActivity = activity.object as ActivityCreate - - return sequelizeTypescript.transaction(async t => { - // Add share entry - const videoChannel: VideoChannelModel = await processCreateActivity(announcedActivity) - const share = { - accountId: accountAnnouncer.id, - videoChannelId: videoChannel.id - } - - const [ , created ] = await VideoChannelShareModel.findOrCreate({ - where: share, - defaults: share, - transaction: t - }) - - if (videoChannel.isOwned() && created === true) { - // Don't resend the activity to the sender - const exceptions = [ accountAnnouncer ] - await forwardActivity(activity, t, exceptions) - } - - return undefined - }) -} - -function processVideoShare (accountAnnouncer: AccountModel, activity: ActivityAnnounce) { - const options = { - arguments: [ accountAnnouncer, activity ], + arguments: [ actorAnnouncer, activity ], errorMessage: 'Cannot share the video with many retries.' } return retryTransactionWrapper(shareVideo, options) } -function shareVideo (accountAnnouncer: AccountModel, activity: ActivityAnnounce) { - const announcedActivity = activity.object as ActivityAdd +function shareVideo (actorAnnouncer: ActorModel, activity: ActivityAnnounce) { + const announcedActivity = activity.object return sequelizeTypescript.transaction(async t => { // Add share entry - const video: VideoModel = await processAddActivity(announcedActivity) + const video: VideoModel = await processCreateActivity(announcedActivity) const share = { - accountId: accountAnnouncer.id, + actorId: actorAnnouncer.id, videoId: video.id } @@ -102,7 +61,7 @@ function shareVideo (accountAnnouncer: AccountModel, activity: ActivityAnnounce) if (video.isOwned() && created === true) { // Don't resend the activity to the sender - const exceptions = [ accountAnnouncer ] + const exceptions = [ actorAnnouncer ] await forwardActivity(activity, t, exceptions) } diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index c1eb2a8ab..1ddd817db 100644 --- a/server/lib/activitypub/process/process-create.ts +++ b/server/lib/activitypub/process/process-create.ts @@ -1,30 +1,33 @@ -import { ActivityCreate, VideoChannelObject } from '../../../../shared' +import * as Bluebird from 'bluebird' +import { ActivityCreate, VideoTorrentObject } from '../../../../shared' import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects' +import { VideoRateType } from '../../../../shared/models/videos' import { logger, retryTransactionWrapper } from '../../../helpers' import { sequelizeTypescript } from '../../../initializers' -import { AccountModel } from '../../../models/account/account' import { AccountVideoRateModel } from '../../../models/account/account-video-rate' +import { ActorModel } from '../../../models/activitypub/actor' +import { TagModel } from '../../../models/video/tag' import { VideoModel } from '../../../models/video/video' import { VideoAbuseModel } from '../../../models/video/video-abuse' -import { VideoChannelModel } from '../../../models/video/video-channel' -import { getOrCreateAccountAndServer } from '../account' +import { VideoFileModel } from '../../../models/video/video-file' +import { getOrCreateActorAndServerAndModel } from '../actor' import { forwardActivity } from '../send/misc' -import { getVideoChannelActivityPubUrl } from '../url' -import { addVideoChannelShares, videoChannelActivityObjectToDBAttributes } from './misc' +import { generateThumbnailFromUrl } from '../videos' +import { addVideoShares, videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' async function processCreateActivity (activity: ActivityCreate) { const activityObject = activity.object const activityType = activityObject.type - const account = await getOrCreateAccountAndServer(activity.actor) + const actor = await getOrCreateActorAndServerAndModel(activity.actor) if (activityType === 'View') { - return processCreateView(account, activity) + return processCreateView(actor, activity) } else if (activityType === 'Dislike') { - return processCreateDislike(account, activity) - } else if (activityType === 'VideoChannel') { - return processCreateVideoChannel(account, activityObject as VideoChannelObject) + return processCreateDislike(actor, activity) + } else if (activityType === 'Video') { + return processCreateVideo(actor, activity) } else if (activityType === 'Flag') { - return processCreateVideoAbuse(account, activityObject as VideoAbuseObject) + return processCreateVideoAbuse(actor, activityObject as VideoAbuseObject) } logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) @@ -39,17 +42,123 @@ export { // --------------------------------------------------------------------------- -async function processCreateDislike (byAccount: AccountModel, activity: ActivityCreate) { +async function processCreateVideo ( + actor: ActorModel, + activity: ActivityCreate +) { + const videoToCreateData = activity.object as VideoTorrentObject + + const channel = videoToCreateData.attributedTo.find(a => a.type === 'Group') + if (!channel) throw new Error('Cannot find associated video channel to video ' + videoToCreateData.url) + + const channelActor = await getOrCreateActorAndServerAndModel(channel.id) + + const options = { + arguments: [ actor, activity, videoToCreateData, channelActor ], + errorMessage: 'Cannot insert the remote video with many retries.' + } + + const video = await retryTransactionWrapper(createRemoteVideo, options) + + // Process outside the transaction because we could fetch remote data + if (videoToCreateData.likes && Array.isArray(videoToCreateData.likes.orderedItems)) { + await createRates(videoToCreateData.likes.orderedItems, video, 'like') + } + + if (videoToCreateData.dislikes && Array.isArray(videoToCreateData.dislikes.orderedItems)) { + await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike') + } + + if (videoToCreateData.shares && Array.isArray(videoToCreateData.shares.orderedItems)) { + await addVideoShares(video, videoToCreateData.shares.orderedItems) + } + + return video +} + +function createRemoteVideo ( + account: ActorModel, + activity: ActivityCreate, + videoToCreateData: VideoTorrentObject, + channelActor: ActorModel +) { + logger.debug('Adding remote video %s.', videoToCreateData.id) + + return sequelizeTypescript.transaction(async t => { + const sequelizeOptions = { + transaction: t + } + const videoFromDatabase = await VideoModel.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t) + if (videoFromDatabase) return videoFromDatabase + + const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoToCreateData, activity.to, activity.cc) + const video = VideoModel.build(videoData) + + // Don't block on request + generateThumbnailFromUrl(video, videoToCreateData.icon) + .catch(err => logger.warn('Cannot generate thumbnail of %s.', videoToCreateData.id, err)) + + const videoCreated = await video.save(sequelizeOptions) + + const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoCreated, videoToCreateData) + if (videoFileAttributes.length === 0) { + throw new Error('Cannot find valid files for video %s ' + videoToCreateData.url) + } + + const tasks: Bluebird[] = videoFileAttributes.map(f => VideoFileModel.create(f, { transaction: t })) + await Promise.all(tasks) + + const tags = videoToCreateData.tag.map(t => t.name) + const tagInstances = await TagModel.findOrCreateTags(tags, t) + await videoCreated.$set('Tags', tagInstances, sequelizeOptions) + + logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid) + + return videoCreated + }) +} + +async function createRates (actorUrls: string[], video: VideoModel, rate: VideoRateType) { + let rateCounts = 0 + const tasks: Bluebird[] = [] + + for (const actorUrl of actorUrls) { + const actor = await getOrCreateActorAndServerAndModel(actorUrl) + const p = AccountVideoRateModel + .create({ + videoId: video.id, + accountId: actor.Account.id, + type: rate + }) + .then(() => rateCounts += 1) + + tasks.push(p) + } + + await Promise.all(tasks) + + logger.info('Adding %d %s to video %s.', rateCounts, rate, video.uuid) + + // This is "likes" and "dislikes" + await video.increment(rate + 's', { by: rateCounts }) + + return +} + +async function processCreateDislike (byActor: ActorModel, activity: ActivityCreate) { const options = { - arguments: [ byAccount, activity ], + arguments: [ byActor, activity ], errorMessage: 'Cannot dislike the video with many retries.' } return retryTransactionWrapper(createVideoDislike, options) } -function createVideoDislike (byAccount: AccountModel, activity: ActivityCreate) { +function createVideoDislike (byActor: ActorModel, activity: ActivityCreate) { const dislike = activity.object as DislikeObject + const byAccount = byActor.Account + + if (!byAccount) throw new Error('Cannot create dislike with the non account actor ' + byActor.url) return sequelizeTypescript.transaction(async t => { const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t) @@ -69,20 +178,20 @@ function createVideoDislike (byAccount: AccountModel, activity: ActivityCreate) if (video.isOwned() && created === true) { // Don't resend the activity to the sender - const exceptions = [ byAccount ] + const exceptions = [ byActor ] await forwardActivity(activity, t, exceptions) } }) } -async function processCreateView (byAccount: AccountModel, activity: ActivityCreate) { +async function processCreateView (byAccount: ActorModel, activity: ActivityCreate) { const view = activity.object as ViewObject const video = await VideoModel.loadByUrlAndPopulateAccount(view.object) if (!video) throw new Error('Unknown video ' + view.object) - const account = await AccountModel.loadByUrl(view.actor) + const account = await ActorModel.loadByUrl(view.actor) if (!account) throw new Error('Unknown account ' + view.actor) await video.increment('views') @@ -94,51 +203,21 @@ async function processCreateView (byAccount: AccountModel, activity: ActivityCre } } -async function processCreateVideoChannel (account: AccountModel, videoChannelToCreateData: VideoChannelObject) { - const options = { - arguments: [ account, videoChannelToCreateData ], - errorMessage: 'Cannot insert the remote video channel with many retries.' - } - - const videoChannel = await retryTransactionWrapper(addRemoteVideoChannel, options) - - if (videoChannelToCreateData.shares && Array.isArray(videoChannelToCreateData.shares.orderedItems)) { - await addVideoChannelShares(videoChannel, videoChannelToCreateData.shares.orderedItems) - } - - return videoChannel -} - -function addRemoteVideoChannel (account: AccountModel, videoChannelToCreateData: VideoChannelObject) { - logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid) - - return sequelizeTypescript.transaction(async t => { - let videoChannel = await VideoChannelModel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t) - if (videoChannel) return videoChannel - - const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account) - videoChannel = new VideoChannelModel(videoChannelData) - videoChannel.url = getVideoChannelActivityPubUrl(videoChannel) - - videoChannel = await videoChannel.save({ transaction: t }) - logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid) - - return videoChannel - }) -} - -function processCreateVideoAbuse (account: AccountModel, videoAbuseToCreateData: VideoAbuseObject) { +function processCreateVideoAbuse (actor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) { const options = { - arguments: [ account, videoAbuseToCreateData ], + arguments: [ actor, videoAbuseToCreateData ], errorMessage: 'Cannot insert the remote video abuse with many retries.' } return retryTransactionWrapper(addRemoteVideoAbuse, options) } -function addRemoteVideoAbuse (account: AccountModel, videoAbuseToCreateData: VideoAbuseObject) { +function addRemoteVideoAbuse (actor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) { logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object) + const account = actor.Account + if (!account) throw new Error('Cannot create dislike with the non account actor ' + actor.url) + return sequelizeTypescript.transaction(async t => { const video = await VideoModel.loadByUrlAndPopulateAccount(videoAbuseToCreateData.object, t) if (!video) { diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index 8f280d37f..65a4e5bcc 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts @@ -2,28 +2,30 @@ import { ActivityDelete } from '../../../../shared/models/activitypub' import { logger, retryTransactionWrapper } from '../../../helpers' import { sequelizeTypescript } from '../../../initializers' import { AccountModel } from '../../../models/account/account' +import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' import { VideoChannelModel } from '../../../models/video/video-channel' -import { getOrCreateAccountAndServer } from '../account' +import { getOrCreateActorAndServerAndModel } from '../actor' async function processDeleteActivity (activity: ActivityDelete) { - const account = await getOrCreateAccountAndServer(activity.actor) + const actor = await getOrCreateActorAndServerAndModel(activity.actor) - if (account.url === activity.id) { - return processDeleteAccount(account) - } + if (actor.url === activity.id) { + if (actor.type === 'Person') { + if (!actor.Account) throw new Error('Actor ' + actor.url + ' is a person but we cannot find it in database.') - { - let videoObject = await VideoModel.loadByUrlAndPopulateAccount(activity.id) - if (videoObject !== undefined) { - return processDeleteVideo(account, videoObject) + return processDeleteAccount(actor.Account) + } else if (actor.type === 'Group') { + if (!actor.VideoChannel) throw new Error('Actor ' + actor.url + ' is a group but we cannot find it in database.') + + return processDeleteVideoChannel(actor.VideoChannel) } } { - let videoChannelObject = await VideoChannelModel.loadByUrl(activity.id) - if (videoChannelObject !== undefined) { - return processDeleteVideoChannel(account, videoChannelObject) + let videoObject = await VideoModel.loadByUrlAndPopulateAccount(activity.id) + if (videoObject !== undefined) { + return processDeleteVideo(actor, videoObject) } } @@ -38,21 +40,21 @@ export { // --------------------------------------------------------------------------- -async function processDeleteVideo (account: AccountModel, videoToDelete: VideoModel) { +async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel) { const options = { - arguments: [ account, videoToDelete ], + arguments: [ actor, videoToDelete ], errorMessage: 'Cannot remove the remote video with many retries.' } await retryTransactionWrapper(deleteRemoteVideo, options) } -async function deleteRemoteVideo (account: AccountModel, videoToDelete: VideoModel) { +async function deleteRemoteVideo (actor: ActorModel, videoToDelete: VideoModel) { logger.debug('Removing remote video "%s".', videoToDelete.uuid) await sequelizeTypescript.transaction(async t => { - if (videoToDelete.VideoChannel.Account.id !== account.id) { - throw new Error('Account ' + account.url + ' does not own video channel ' + videoToDelete.VideoChannel.url) + if (videoToDelete.VideoChannel.Account.Actor.id !== actor.id) { + throw new Error('Account ' + actor.url + ' does not own video channel ' + videoToDelete.VideoChannel.Actor.url) } await videoToDelete.destroy({ transaction: t }) @@ -61,44 +63,40 @@ async function deleteRemoteVideo (account: AccountModel, videoToDelete: VideoMod logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) } -async function processDeleteVideoChannel (account: AccountModel, videoChannelToRemove: VideoChannelModel) { +async function processDeleteAccount (accountToRemove: AccountModel) { const options = { - arguments: [ account, videoChannelToRemove ], - errorMessage: 'Cannot remove the remote video channel with many retries.' + arguments: [ accountToRemove ], + errorMessage: 'Cannot remove the remote account with many retries.' } - await retryTransactionWrapper(deleteRemoteVideoChannel, options) + await retryTransactionWrapper(deleteRemoteAccount, options) } -async function deleteRemoteVideoChannel (account: AccountModel, videoChannelToRemove: VideoChannelModel) { - logger.debug('Removing remote video channel "%s".', videoChannelToRemove.uuid) +async function deleteRemoteAccount (accountToRemove: AccountModel) { + logger.debug('Removing remote account "%s".', accountToRemove.Actor.uuid) await sequelizeTypescript.transaction(async t => { - if (videoChannelToRemove.Account.id !== account.id) { - throw new Error('Account ' + account.url + ' does not own video channel ' + videoChannelToRemove.url) - } - - await videoChannelToRemove.destroy({ transaction: t }) + await accountToRemove.destroy({ transaction: t }) }) - logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.uuid) + logger.info('Remote account with uuid %s removed.', accountToRemove.Actor.uuid) } -async function processDeleteAccount (accountToRemove: AccountModel) { +async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelModel) { const options = { - arguments: [ accountToRemove ], - errorMessage: 'Cannot remove the remote account with many retries.' + arguments: [ videoChannelToRemove ], + errorMessage: 'Cannot remove the remote video channel with many retries.' } - await retryTransactionWrapper(deleteRemoteAccount, options) + await retryTransactionWrapper(deleteRemoteVideoChannel, options) } -async function deleteRemoteAccount (accountToRemove: AccountModel) { - logger.debug('Removing remote account "%s".', accountToRemove.uuid) +async function deleteRemoteVideoChannel (videoChannelToRemove: VideoChannelModel) { + logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.uuid) await sequelizeTypescript.transaction(async t => { - await accountToRemove.destroy({ transaction: t }) + await videoChannelToRemove.destroy({ transaction: t }) }) - logger.info('Remote account with uuid %s removed.', accountToRemove.uuid) + logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.Actor.uuid) } diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index ccaee43a6..ec7a331f3 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts @@ -1,16 +1,16 @@ import { ActivityFollow } from '../../../../shared/models/activitypub' import { logger, retryTransactionWrapper } from '../../../helpers' import { sequelizeTypescript } from '../../../initializers' -import { AccountModel } from '../../../models/account/account' -import { AccountFollowModel } from '../../../models/account/account-follow' -import { getOrCreateAccountAndServer } from '../account' +import { ActorModel } from '../../../models/activitypub/actor' +import { ActorFollowModel } from '../../../models/activitypub/actor-follow' +import { getOrCreateActorAndServerAndModel } from '../actor' import { sendAccept } from '../send' async function processFollowActivity (activity: ActivityFollow) { const activityObject = activity.object - const account = await getOrCreateAccountAndServer(activity.actor) + const actor = await getOrCreateActorAndServerAndModel(activity.actor) - return processFollow(account, activityObject) + return processFollow(actor, activityObject) } // --------------------------------------------------------------------------- @@ -21,46 +21,46 @@ export { // --------------------------------------------------------------------------- -function processFollow (account: AccountModel, targetAccountURL: string) { +function processFollow (actor: ActorModel, targetActorURL: string) { const options = { - arguments: [ account, targetAccountURL ], + arguments: [ actor, targetActorURL ], errorMessage: 'Cannot follow with many retries.' } return retryTransactionWrapper(follow, options) } -async function follow (account: AccountModel, targetAccountURL: string) { +async function follow (actor: ActorModel, targetActorURL: string) { await sequelizeTypescript.transaction(async t => { - const targetAccount = await AccountModel.loadByUrl(targetAccountURL, t) + const targetActor = await ActorModel.loadByUrl(targetActorURL, t) - if (!targetAccount) throw new Error('Unknown account') - if (targetAccount.isOwned() === false) throw new Error('This is not a local account.') + if (!targetActor) throw new Error('Unknown actor') + if (targetActor.isOwned() === false) throw new Error('This is not a local actor.') - const [ accountFollow ] = await AccountFollowModel.findOrCreate({ + const [ actorFollow ] = await ActorFollowModel.findOrCreate({ where: { - accountId: account.id, - targetAccountId: targetAccount.id + actorId: actor.id, + targetActorId: targetActor.id }, defaults: { - accountId: account.id, - targetAccountId: targetAccount.id, + actorId: actor.id, + targetActorId: targetActor.id, state: 'accepted' }, transaction: t }) - if (accountFollow.state !== 'accepted') { - accountFollow.state = 'accepted' - await accountFollow.save({ transaction: t }) + if (actorFollow.state !== 'accepted') { + actorFollow.state = 'accepted' + await actorFollow.save({ transaction: t }) } - accountFollow.AccountFollower = account - accountFollow.AccountFollowing = targetAccount + actorFollow.ActorFollower = actor + actorFollow.ActorFollowing = targetActor - // Target sends to account he accepted the follow request - return sendAccept(accountFollow, t) + // Target sends to actor he accepted the follow request + return sendAccept(actorFollow, t) }) - logger.info('Account uuid %s is followed by account %s.', account.url, targetAccountURL) + logger.info('Actor uuid %s is followed by actor %s.', actor.url, targetActorURL) } diff --git a/server/lib/activitypub/process/process-like.ts b/server/lib/activitypub/process/process-like.ts index a6e391f1e..a7fcec21c 100644 --- a/server/lib/activitypub/process/process-like.ts +++ b/server/lib/activitypub/process/process-like.ts @@ -1,16 +1,16 @@ import { ActivityLike } from '../../../../shared/models/activitypub' import { retryTransactionWrapper } from '../../../helpers' import { sequelizeTypescript } from '../../../initializers' -import { AccountModel } from '../../../models/account/account' import { AccountVideoRateModel } from '../../../models/account/account-video-rate' +import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' -import { getOrCreateAccountAndServer } from '../account' +import { getOrCreateActorAndServerAndModel } from '../actor' import { forwardActivity } from '../send/misc' async function processLikeActivity (activity: ActivityLike) { - const account = await getOrCreateAccountAndServer(activity.actor) + const actor = await getOrCreateActorAndServerAndModel(activity.actor) - return processLikeVideo(account, activity) + return processLikeVideo(actor, activity) } // --------------------------------------------------------------------------- @@ -21,18 +21,21 @@ export { // --------------------------------------------------------------------------- -async function processLikeVideo (byAccount: AccountModel, activity: ActivityLike) { +async function processLikeVideo (actor: ActorModel, activity: ActivityLike) { const options = { - arguments: [ byAccount, activity ], + arguments: [ actor, activity ], errorMessage: 'Cannot like the video with many retries.' } return retryTransactionWrapper(createVideoLike, options) } -function createVideoLike (byAccount: AccountModel, activity: ActivityLike) { +function createVideoLike (byActor: ActorModel, activity: ActivityLike) { const videoUrl = activity.object + const byAccount = byActor.Account + if (!byAccount) throw new Error('Cannot create like with the non account actor ' + byActor.url) + return sequelizeTypescript.transaction(async t => { const video = await VideoModel.loadByUrlAndPopulateAccount(videoUrl) @@ -52,7 +55,7 @@ function createVideoLike (byAccount: AccountModel, activity: ActivityLike) { if (video.isOwned() && created === true) { // Don't resend the activity to the sender - const exceptions = [ byAccount ] + const exceptions = [ byActor ] await forwardActivity(activity, t, exceptions) } }) diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts index efa63122b..4a0181137 100644 --- a/server/lib/activitypub/process/process-undo.ts +++ b/server/lib/activitypub/process/process-undo.ts @@ -3,8 +3,9 @@ import { DislikeObject } from '../../../../shared/models/activitypub/objects' import { logger, retryTransactionWrapper } from '../../../helpers' import { sequelizeTypescript } from '../../../initializers' import { AccountModel } from '../../../models/account/account' -import { AccountFollowModel } from '../../../models/account/account-follow' import { AccountVideoRateModel } from '../../../models/account/account-video-rate' +import { ActorModel } from '../../../models/activitypub/actor' +import { ActorFollowModel } from '../../../models/activitypub/actor-follow' import { VideoModel } from '../../../models/video/video' import { forwardActivity } from '../send/misc' @@ -32,21 +33,21 @@ export { // --------------------------------------------------------------------------- -function processUndoLike (actor: string, activity: ActivityUndo) { +function processUndoLike (actorUrl: string, activity: ActivityUndo) { const options = { - arguments: [ actor, activity ], + arguments: [ actorUrl, activity ], errorMessage: 'Cannot undo like with many retries.' } return retryTransactionWrapper(undoLike, options) } -function undoLike (actor: string, activity: ActivityUndo) { +function undoLike (actorUrl: string, activity: ActivityUndo) { const likeActivity = activity.object as ActivityLike return sequelizeTypescript.transaction(async t => { - const byAccount = await AccountModel.loadByUrl(actor, t) - if (!byAccount) throw new Error('Unknown account ' + actor) + const byAccount = await AccountModel.loadByUrl(actorUrl, t) + if (!byAccount) throw new Error('Unknown account ' + actorUrl) const video = await VideoModel.loadByUrlAndPopulateAccount(likeActivity.object, t) if (!video) throw new Error('Unknown video ' + likeActivity.actor) @@ -59,27 +60,27 @@ function undoLike (actor: string, activity: ActivityUndo) { if (video.isOwned()) { // Don't resend the activity to the sender - const exceptions = [ byAccount ] + const exceptions = [ byAccount.Actor ] await forwardActivity(activity, t, exceptions) } }) } -function processUndoDislike (actor: string, activity: ActivityUndo) { +function processUndoDislike (actorUrl: string, activity: ActivityUndo) { const options = { - arguments: [ actor, activity ], + arguments: [ actorUrl, activity ], errorMessage: 'Cannot undo dislike with many retries.' } return retryTransactionWrapper(undoDislike, options) } -function undoDislike (actor: string, activity: ActivityUndo) { +function undoDislike (actorUrl: string, activity: ActivityUndo) { const dislike = activity.object.object as DislikeObject return sequelizeTypescript.transaction(async t => { - const byAccount = await AccountModel.loadByUrl(actor, t) - if (!byAccount) throw new Error('Unknown account ' + actor) + const byAccount = await AccountModel.loadByUrl(actorUrl, t) + if (!byAccount) throw new Error('Unknown account ' + actorUrl) const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t) if (!video) throw new Error('Unknown video ' + dislike.actor) @@ -92,30 +93,30 @@ function undoDislike (actor: string, activity: ActivityUndo) { if (video.isOwned()) { // Don't resend the activity to the sender - const exceptions = [ byAccount ] + const exceptions = [ byAccount.Actor ] await forwardActivity(activity, t, exceptions) } }) } -function processUndoFollow (actor: string, followActivity: ActivityFollow) { +function processUndoFollow (actorUrl: string, followActivity: ActivityFollow) { const options = { - arguments: [ actor, followActivity ], + arguments: [ actorUrl, followActivity ], errorMessage: 'Cannot undo follow with many retries.' } return retryTransactionWrapper(undoFollow, options) } -function undoFollow (actor: string, followActivity: ActivityFollow) { +function undoFollow (actorUrl: string, followActivity: ActivityFollow) { return sequelizeTypescript.transaction(async t => { - const follower = await AccountModel.loadByUrl(actor, t) - const following = await AccountModel.loadByUrl(followActivity.object, t) - const accountFollow = await AccountFollowModel.loadByAccountAndTarget(follower.id, following.id, t) + const follower = await ActorModel.loadByUrl(actorUrl, t) + const following = await ActorModel.loadByUrl(followActivity.object, t) + const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t) - if (!accountFollow) throw new Error(`'Unknown account follow ${follower.id} -> ${following.id}.`) + if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${following.id}.`) - await accountFollow.destroy({ transaction: t }) + await actorFollow.destroy({ transaction: t }) return undefined }) diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index 771021f0c..35912ee87 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts @@ -1,23 +1,19 @@ import * as Bluebird from 'bluebird' -import { VideoChannelObject, VideoTorrentObject } from '../../../../shared' import { ActivityUpdate } from '../../../../shared/models/activitypub' import { logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers' import { sequelizeTypescript } from '../../../initializers' -import { AccountModel } from '../../../models/account/account' +import { ActorModel } from '../../../models/activitypub/actor' import { TagModel } from '../../../models/video/tag' import { VideoModel } from '../../../models/video/video' -import { VideoChannelModel } from '../../../models/video/video-channel' import { VideoFileModel } from '../../../models/video/video-file' -import { getOrCreateAccountAndServer } from '../account' +import { getOrCreateActorAndServerAndModel } from '../actor' import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' async function processUpdateActivity (activity: ActivityUpdate) { - const account = await getOrCreateAccountAndServer(activity.actor) + const actor = await getOrCreateActorAndServerAndModel(activity.actor) if (activity.object.type === 'Video') { - return processUpdateVideo(account, activity.object) - } else if (activity.object.type === 'VideoChannel') { - return processUpdateVideoChannel(account, activity.object) + return processUpdateVideo(actor, activity) } return @@ -31,16 +27,18 @@ export { // --------------------------------------------------------------------------- -function processUpdateVideo (account: AccountModel, video: VideoTorrentObject) { +function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) { const options = { - arguments: [ account, video ], + arguments: [ actor, activity ], errorMessage: 'Cannot update the remote video with many retries' } return retryTransactionWrapper(updateRemoteVideo, options) } -async function updateRemoteVideo (account: AccountModel, videoAttributesToUpdate: VideoTorrentObject) { +async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) { + const videoAttributesToUpdate = activity.object + logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) let videoInstance: VideoModel let videoFieldsSave: object @@ -54,23 +52,23 @@ async function updateRemoteVideo (account: AccountModel, videoAttributesToUpdate const videoInstance = await VideoModel.loadByUrlAndPopulateAccount(videoAttributesToUpdate.id, t) if (!videoInstance) throw new Error('Video ' + videoAttributesToUpdate.id + ' not found.') - if (videoInstance.VideoChannel.Account.id !== account.id) { - throw new Error('Account ' + account.url + ' does not own video channel ' + videoInstance.VideoChannel.url) + const videoChannel = videoInstance.VideoChannel + if (videoChannel.Account.Actor.id !== actor.id) { + throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url) } - const videoData = await videoActivityObjectToDBAttributes(videoInstance.VideoChannel, videoAttributesToUpdate) + const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoAttributesToUpdate, activity.to, activity.cc) videoInstance.set('name', videoData.name) videoInstance.set('category', videoData.category) videoInstance.set('licence', videoData.licence) videoInstance.set('language', videoData.language) videoInstance.set('nsfw', videoData.nsfw) + videoInstance.set('privacy', videoData.privacy) videoInstance.set('description', videoData.description) videoInstance.set('duration', videoData.duration) videoInstance.set('createdAt', videoData.createdAt) videoInstance.set('updatedAt', videoData.updatedAt) videoInstance.set('views', videoData.views) - // videoInstance.set('likes', videoData.likes) - // videoInstance.set('dislikes', videoData.dislikes) await videoInstance.save(sequelizeOptions) @@ -101,36 +99,3 @@ async function updateRemoteVideo (account: AccountModel, videoAttributesToUpdate throw err } } - -async function processUpdateVideoChannel (account: AccountModel, videoChannel: VideoChannelObject) { - const options = { - arguments: [ account, videoChannel ], - errorMessage: 'Cannot update the remote video channel with many retries.' - } - - await retryTransactionWrapper(updateRemoteVideoChannel, options) -} - -async function updateRemoteVideoChannel (account: AccountModel, videoChannel: VideoChannelObject) { - logger.debug('Updating remote video channel "%s".', videoChannel.uuid) - - await sequelizeTypescript.transaction(async t => { - const sequelizeOptions = { transaction: t } - - const videoChannelInstance = await VideoChannelModel.loadByUrl(videoChannel.id) - if (!videoChannelInstance) throw new Error('Video ' + videoChannel.id + ' not found.') - - if (videoChannelInstance.Account.id !== account.id) { - throw new Error('Account ' + account.id + ' does not own video channel ' + videoChannelInstance.url) - } - - videoChannelInstance.set('name', videoChannel.name) - videoChannelInstance.set('description', videoChannel.content) - videoChannelInstance.set('createdAt', videoChannel.published) - videoChannelInstance.set('updatedAt', videoChannel.updated) - - await videoChannelInstance.save(sequelizeOptions) - }) - - logger.info('Remote video channel with uuid %s updated', videoChannel.uuid) -} diff --git a/server/lib/activitypub/process/process.ts b/server/lib/activitypub/process/process.ts index bfbf8053c..dfb60c1bf 100644 --- a/server/lib/activitypub/process/process.ts +++ b/server/lib/activitypub/process/process.ts @@ -1,8 +1,7 @@ import { Activity, ActivityType } from '../../../../shared/models/activitypub' import { logger } from '../../../helpers' -import { AccountModel } from '../../../models/account/account' +import { ActorModel } from '../../../models/activitypub/actor' import { processAcceptActivity } from './process-accept' -import { processAddActivity } from './process-add' import { processAnnounceActivity } from './process-announce' import { processCreateActivity } from './process-create' import { processDeleteActivity } from './process-delete' @@ -11,9 +10,8 @@ import { processLikeActivity } from './process-like' import { processUndoActivity } from './process-undo' import { processUpdateActivity } from './process-update' -const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxAccount?: AccountModel) => Promise } = { +const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxActor?: ActorModel) => Promise } = { Create: processCreateActivity, - Add: processAddActivity, Update: processUpdateActivity, Delete: processDeleteActivity, Follow: processFollowActivity, @@ -23,11 +21,11 @@ const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxAccoun Like: processLikeActivity } -async function processActivities (activities: Activity[], signatureAccount?: AccountModel, inboxAccount?: AccountModel) { +async function processActivities (activities: Activity[], signatureActor?: ActorModel, inboxActor?: ActorModel) { for (const activity of activities) { // When we fetch remote data, we don't have signature - if (signatureAccount && activity.actor !== signatureAccount.url) { - logger.warn('Signature mismatch between %s and %s.', activity.actor, signatureAccount.url) + if (signatureActor && activity.actor !== signatureActor.url) { + logger.warn('Signature mismatch between %s and %s.', activity.actor, signatureActor.url) continue } @@ -38,7 +36,7 @@ async function processActivities (activities: Activity[], signatureAccount?: Acc } try { - await activityProcessor(activity, inboxAccount) + await activityProcessor(activity, inboxActor) } catch (err) { logger.warn('Cannot process activity %s.', activity.type, err) } diff --git a/server/lib/activitypub/send/index.ts b/server/lib/activitypub/send/index.ts index ee8f3ad7e..79ba6c7fe 100644 --- a/server/lib/activitypub/send/index.ts +++ b/server/lib/activitypub/send/index.ts @@ -1,5 +1,4 @@ export * from './send-accept' -export * from './send-add' export * from './send-announce' export * from './send-create' export * from './send-delete' diff --git a/server/lib/activitypub/send/misc.ts b/server/lib/activitypub/send/misc.ts index ffc221477..14101e630 100644 --- a/server/lib/activitypub/send/misc.ts +++ b/server/lib/activitypub/send/misc.ts @@ -2,18 +2,16 @@ import { Transaction } from 'sequelize' import { Activity } from '../../../../shared/models/activitypub' import { logger } from '../../../helpers' import { ACTIVITY_PUB } from '../../../initializers' -import { AccountModel } from '../../../models/account/account' -import { AccountFollowModel } from '../../../models/account/account-follow' +import { ActorModel } from '../../../models/activitypub/actor' +import { ActorFollowModel } from '../../../models/activitypub/actor-follow' import { VideoModel } from '../../../models/video/video' -import { VideoChannelModel } from '../../../models/video/video-channel' -import { VideoChannelShareModel } from '../../../models/video/video-channel-share' import { VideoShareModel } from '../../../models/video/video-share' import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../../jobs/activitypub-http-job-scheduler' async function forwardActivity ( activity: Activity, t: Transaction, - followersException: AccountModel[] = [] + followersException: ActorModel[] = [] ) { const to = activity.to || [] const cc = activity.cc || [] @@ -25,11 +23,11 @@ async function forwardActivity ( } } - const toAccountFollowers = await AccountModel.listByFollowersUrls(followersUrls, t) - const uris = await computeFollowerUris(toAccountFollowers, followersException, t) + 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.', toAccountFollowers.map(a => a.id).join(', ')) + logger.info('0 followers for %s, no forwarding.', toActorFollowers.map(a => a.id).join(', ')) return undefined } @@ -45,14 +43,14 @@ async function forwardActivity ( async function broadcastToFollowers ( data: any, - byAccount: AccountModel, - toAccountFollowers: AccountModel[], + byActor: ActorModel, + toActorFollowers: ActorModel[], t: Transaction, - followersException: AccountModel[] = [] + followersException: ActorModel[] = [] ) { - const uris = await computeFollowerUris(toAccountFollowers, followersException, t) + const uris = await computeFollowerUris(toActorFollowers, followersException, t) if (uris.length === 0) { - logger.info('0 followers for %s, no broadcasting.', toAccountFollowers.map(a => a.id).join(', ')) + logger.info('0 followers for %s, no broadcasting.', toActorFollowers.map(a => a.id).join(', ')) return undefined } @@ -60,62 +58,48 @@ async function broadcastToFollowers ( const jobPayload: ActivityPubHttpPayload = { uris, - signatureAccountId: byAccount.id, + signatureActorId: byActor.id, body: data } return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload) } -async function unicastTo (data: any, byAccount: AccountModel, toAccountUrl: string, t: Transaction) { - logger.debug('Creating unicast job.', { uri: toAccountUrl }) +async function unicastTo (data: any, byActor: ActorModel, toActorUrl: string, t: Transaction) { + logger.debug('Creating unicast job.', { uri: toActorUrl }) const jobPayload: ActivityPubHttpPayload = { - uris: [ toAccountUrl ], - signatureAccountId: byAccount.id, + uris: [ toActorUrl ], + signatureActorId: byActor.id, body: data } return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload) } -function getOriginVideoAudience (video: VideoModel, accountsInvolvedInVideo: AccountModel[]) { +function getOriginVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]) { return { - to: [ video.VideoChannel.Account.url ], - cc: accountsInvolvedInVideo.map(a => a.followersUrl) + to: [ video.VideoChannel.Account.Actor.url ], + cc: actorsInvolvedInVideo.map(a => a.followersUrl) } } -function getOriginVideoChannelAudience (videoChannel: VideoChannelModel, accountsInvolved: AccountModel[]) { +function getObjectFollowersAudience (actorsInvolvedInObject: ActorModel[]) { return { - to: [ videoChannel.Account.url ], - cc: accountsInvolved.map(a => a.followersUrl) - } -} - -function getObjectFollowersAudience (accountsInvolvedInObject: AccountModel[]) { - return { - to: accountsInvolvedInObject.map(a => a.followersUrl), + to: actorsInvolvedInObject.map(a => a.followersUrl), cc: [] } } -async function getAccountsInvolvedInVideo (video: VideoModel, t: Transaction) { - const accountsToForwardView = await VideoShareModel.loadAccountsByShare(video.id, t) - accountsToForwardView.push(video.VideoChannel.Account) - - return accountsToForwardView -} - -async function getAccountsInvolvedInVideoChannel (videoChannel: VideoChannelModel, t: Transaction) { - const accountsToForwardView = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t) - accountsToForwardView.push(videoChannel.Account) +async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) { + const actorsToForwardView = await VideoShareModel.loadActorsByShare(video.id, t) + actorsToForwardView.push(video.VideoChannel.Account.Actor) - return accountsToForwardView + return actorsToForwardView } -async function getAudience (accountSender: AccountModel, t: Transaction, isPublic = true) { - const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls(t) +async function getAudience (actorSender: ActorModel, t: Transaction, isPublic = true) { + const followerInboxUrls = await actorSender.getFollowerSharedInboxUrls(t) // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47 let to = [] @@ -132,10 +116,10 @@ async function getAudience (accountSender: AccountModel, t: Transaction, isPubli return { to, cc } } -async function computeFollowerUris (toAccountFollower: AccountModel[], followersException: AccountModel[], t: Transaction) { - const toAccountFollowerIds = toAccountFollower.map(a => a.id) +async function computeFollowerUris (toActorFollower: ActorModel[], followersException: ActorModel[], t: Transaction) { + const toActorFollowerIds = toActorFollower.map(a => a.id) - const result = await AccountFollowModel.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds, t) + const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t) const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl) return result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1) } @@ -144,12 +128,10 @@ async function computeFollowerUris (toAccountFollower: AccountModel[], followers export { broadcastToFollowers, - getOriginVideoChannelAudience, unicastTo, getAudience, getOriginVideoAudience, - getAccountsInvolvedInVideo, - getAccountsInvolvedInVideoChannel, + getActorsInvolvedInVideo, getObjectFollowersAudience, forwardActivity } diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts index f160af3c9..7579884a7 100644 --- a/server/lib/activitypub/send/send-accept.ts +++ b/server/lib/activitypub/send/send-accept.ts @@ -1,15 +1,15 @@ import { Transaction } from 'sequelize' import { ActivityAccept } from '../../../../shared/models/activitypub' -import { AccountModel } from '../../../models/account/account' -import { AccountFollowModel } from '../../../models/account/account-follow' -import { getAccountFollowAcceptActivityPubUrl } from '../url' +import { ActorModel } from '../../../models/activitypub/actor' +import { ActorFollowModel } from '../../../models/activitypub/actor-follow' +import { getActorFollowAcceptActivityPubUrl } from '../url' import { unicastTo } from './misc' -async function sendAccept (accountFollow: AccountFollowModel, t: Transaction) { - const follower = accountFollow.AccountFollower - const me = accountFollow.AccountFollowing +async function sendAccept (actorFollow: ActorFollowModel, t: Transaction) { + const follower = actorFollow.ActorFollower + const me = actorFollow.ActorFollowing - const url = getAccountFollowAcceptActivityPubUrl(accountFollow) + const url = getActorFollowAcceptActivityPubUrl(actorFollow) const data = acceptActivityData(url, me) return unicastTo(data, me, follower.inboxUrl, t) @@ -23,12 +23,10 @@ export { // --------------------------------------------------------------------------- -function acceptActivityData (url: string, byAccount: AccountModel) { - const activity: ActivityAccept = { +function acceptActivityData (url: string, byActor: ActorModel): ActivityAccept { + return { type: 'Accept', id: url, - actor: byAccount.url + actor: byActor.url } - - return activity } diff --git a/server/lib/activitypub/send/send-add.ts b/server/lib/activitypub/send/send-add.ts deleted file mode 100644 index fd614db75..000000000 --- a/server/lib/activitypub/send/send-add.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Transaction } from 'sequelize' -import { ActivityAdd } from '../../../../shared/models/activitypub' -import { VideoPrivacy } from '../../../../shared/models/videos' -import { AccountModel } from '../../../models/account/account' -import { VideoModel } from '../../../models/video/video' -import { broadcastToFollowers, getAudience } from './misc' - -async function sendAddVideo (video: VideoModel, t: Transaction) { - const byAccount = video.VideoChannel.Account - - const videoObject = video.toActivityPubObject() - const data = await addActivityData(video.url, byAccount, video, video.VideoChannel.url, videoObject, t) - - return broadcastToFollowers(data, byAccount, [ byAccount ], t) -} - -async function addActivityData ( - url: string, - byAccount: AccountModel, - video: VideoModel, - target: string, - object: any, - t: Transaction -): Promise { - const videoPublic = video.privacy === VideoPrivacy.PUBLIC - - const { to, cc } = await getAudience(byAccount, t, videoPublic) - - return { - type: 'Add', - id: url, - actor: byAccount.url, - to, - cc, - object, - target - } -} - -// --------------------------------------------------------------------------- - -export { - addActivityData, - sendAddVideo -} diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts index e685323e8..578fbc630 100644 --- a/server/lib/activitypub/send/send-announce.ts +++ b/server/lib/activitypub/send/send-announce.ts @@ -1,88 +1,59 @@ import { Transaction } from 'sequelize' -import { ActivityAdd } from '../../../../shared/index' import { ActivityAnnounce, ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' -import { AccountModel } from '../../../models/account/account' +import { VideoPrivacy } from '../../../../shared/models/videos' +import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' -import { VideoChannelModel } from '../../../models/video/video-channel' import { getAnnounceActivityPubUrl } from '../url' import { broadcastToFollowers, - getAccountsInvolvedInVideo, - getAccountsInvolvedInVideoChannel, + getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getOriginVideoAudience, - getOriginVideoChannelAudience, unicastTo } from './misc' -import { addActivityData } from './send-add' import { createActivityData } from './send-create' -async function buildVideoAnnounceToFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const url = getAnnounceActivityPubUrl(video.url, byAccount) +async function buildVideoAnnounceToFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { + const url = getAnnounceActivityPubUrl(video.url, byActor) + const videoObject = video.toActivityPubObject() - const videoChannel = video.VideoChannel - const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject(), t) + const announcedAudience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC) + const announcedActivity = await createActivityData(url, video.VideoChannel.Account.Actor, videoObject, t, announcedAudience) - const accountsToForwardView = await getAccountsInvolvedInVideo(video, t) + const accountsToForwardView = await getActorsInvolvedInVideo(video, t) const audience = getObjectFollowersAudience(accountsToForwardView) - return announceActivityData(url, byAccount, announcedActivity, t, audience) + return announceActivityData(url, byActor, announcedActivity, t, audience) } -async function sendVideoAnnounceToFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const data = await buildVideoAnnounceToFollowers(byAccount, video, t) +async function sendVideoAnnounceToFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { + const data = await buildVideoAnnounceToFollowers(byActor, video, t) - return broadcastToFollowers(data, byAccount, [ byAccount ], t) + return broadcastToFollowers(data, byActor, [ byActor ], t) } -async function sendVideoAnnounceToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const url = getAnnounceActivityPubUrl(video.url, byAccount) +async function sendVideoAnnounceToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { + const url = getAnnounceActivityPubUrl(video.url, byActor) - const videoChannel = video.VideoChannel - const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject(), t) + const videoObject = video.toActivityPubObject() + const announcedActivity = await createActivityData(url, video.VideoChannel.Account.Actor, videoObject, t) - const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) - const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) - const data = await createActivityData(url, byAccount, announcedActivity, t, audience) + const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) + const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) + const data = await createActivityData(url, byActor, announcedActivity, t, audience) - return unicastTo(data, byAccount, videoChannel.Account.sharedInboxUrl, t) -} - -async function buildVideoChannelAnnounceToFollowers (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) { - const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount) - const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject(), t) - - const accountsToForwardView = await getAccountsInvolvedInVideoChannel(videoChannel, t) - const audience = getObjectFollowersAudience(accountsToForwardView) - return announceActivityData(url, byAccount, announcedActivity, t, audience) -} - -async function sendVideoChannelAnnounceToFollowers (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) { - const data = await buildVideoChannelAnnounceToFollowers(byAccount, videoChannel, t) - - return broadcastToFollowers(data, byAccount, [ byAccount ], t) -} - -async function sendVideoChannelAnnounceToOrigin (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) { - const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount) - const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject(), t) - - const accountsInvolvedInVideo = await getAccountsInvolvedInVideoChannel(videoChannel, t) - const audience = getOriginVideoChannelAudience(videoChannel, accountsInvolvedInVideo) - const data = await createActivityData(url, byAccount, announcedActivity, t, audience) - - return unicastTo(data, byAccount, videoChannel.Account.sharedInboxUrl, t) + return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) } async function announceActivityData ( url: string, - byAccount: AccountModel, - object: ActivityCreate | ActivityAdd, + byActor: ActorModel, + object: ActivityCreate, t: Transaction, audience?: ActivityAudience ): Promise { if (!audience) { - audience = await getAudience(byAccount, t) + audience = await getAudience(byActor, t) } return { @@ -90,7 +61,7 @@ async function announceActivityData ( to: audience.to, cc: audience.cc, id: url, - actor: byAccount.url, + actor: byActor.url, object } } @@ -99,10 +70,7 @@ async function announceActivityData ( export { sendVideoAnnounceToFollowers, - sendVideoChannelAnnounceToFollowers, sendVideoAnnounceToOrigin, - sendVideoChannelAnnounceToOrigin, announceActivityData, - buildVideoAnnounceToFollowers, - buildVideoChannelAnnounceToFollowers + buildVideoAnnounceToFollowers } diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index 9fbaa8196..d26c24838 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts @@ -1,111 +1,112 @@ import { Transaction } from 'sequelize' import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' -import { getServerAccount } from '../../../helpers' -import { AccountModel } from '../../../models/account/account' +import { VideoPrivacy } from '../../../../shared/models/videos' +import { getServerActor } from '../../../helpers' +import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' import { VideoAbuseModel } from '../../../models/video/video-abuse' -import { VideoChannelModel } from '../../../models/video/video-channel' import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url' import { broadcastToFollowers, - getAccountsInvolvedInVideo, + getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getOriginVideoAudience, unicastTo } from './misc' -async function sendCreateVideoChannel (videoChannel: VideoChannelModel, t: Transaction) { - const byAccount = videoChannel.Account +async function sendCreateVideo (video: VideoModel, t: Transaction) { + const byActor = video.VideoChannel.Account.Actor - const videoChannelObject = videoChannel.toActivityPubObject() - const data = await createActivityData(videoChannel.url, byAccount, videoChannelObject, t) + const videoObject = video.toActivityPubObject() + const audience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC) + const data = await createActivityData(video.url, byActor, videoObject, t, audience) - return broadcastToFollowers(data, byAccount, [ byAccount ], t) + return broadcastToFollowers(data, byActor, [ byActor ], t) } -async function sendVideoAbuse (byAccount: AccountModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) { +async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) { const url = getVideoAbuseActivityPubUrl(videoAbuse) - const audience = { to: [ video.VideoChannel.Account.url ], cc: [] } - const data = await createActivityData(url, byAccount, videoAbuse.toActivityPubObject(), t, audience) + const audience = { to: [ video.VideoChannel.Account.Actor.url ], cc: [] } + const data = await createActivityData(url, byActor, videoAbuse.toActivityPubObject(), t, audience) - return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) + return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) } -async function sendCreateViewToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const url = getVideoViewActivityPubUrl(byAccount, video) - const viewActivity = createViewActivityData(byAccount, video) +async function sendCreateViewToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { + const url = getVideoViewActivityPubUrl(byActor, video) + const viewActivity = createViewActivityData(byActor, video) - const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) - const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) - const data = await createActivityData(url, byAccount, viewActivity, t, audience) + const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) + const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) + const data = await createActivityData(url, byActor, viewActivity, t, audience) - return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) + return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) } -async function sendCreateViewToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const url = getVideoViewActivityPubUrl(byAccount, video) - const viewActivity = createViewActivityData(byAccount, video) +async function sendCreateViewToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { + const url = getVideoViewActivityPubUrl(byActor, video) + const viewActivity = createViewActivityData(byActor, video) - const accountsToForwardView = await getAccountsInvolvedInVideo(video, t) - const audience = getObjectFollowersAudience(accountsToForwardView) - const data = await createActivityData(url, byAccount, viewActivity, t, audience) + const actorsToForwardView = await getActorsInvolvedInVideo(video, t) + const audience = getObjectFollowersAudience(actorsToForwardView) + const data = await createActivityData(url, byActor, viewActivity, t, audience) - // Use the server account to send the view, because it could be an unregistered account - const serverAccount = await getServerAccount() - const followersException = [ byAccount ] - return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException) + // Use the server actor to send the view + const serverActor = await getServerActor() + const followersException = [ byActor ] + return broadcastToFollowers(data, serverActor, actorsToForwardView, t, followersException) } -async function sendCreateDislikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const url = getVideoDislikeActivityPubUrl(byAccount, video) - const dislikeActivity = createDislikeActivityData(byAccount, video) +async function sendCreateDislikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { + const url = getVideoDislikeActivityPubUrl(byActor, video) + const dislikeActivity = createDislikeActivityData(byActor, video) - const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) - const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) - const data = await createActivityData(url, byAccount, dislikeActivity, t, audience) + const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) + const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) + const data = await createActivityData(url, byActor, dislikeActivity, t, audience) - return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) + return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) } -async function sendCreateDislikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const url = getVideoDislikeActivityPubUrl(byAccount, video) - const dislikeActivity = createDislikeActivityData(byAccount, video) +async function sendCreateDislikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { + const url = getVideoDislikeActivityPubUrl(byActor, video) + const dislikeActivity = createDislikeActivityData(byActor, video) - const accountsToForwardView = await getAccountsInvolvedInVideo(video, t) - const audience = getObjectFollowersAudience(accountsToForwardView) - const data = await createActivityData(url, byAccount, dislikeActivity, t, audience) + const actorsToForwardView = await getActorsInvolvedInVideo(video, t) + const audience = getObjectFollowersAudience(actorsToForwardView) + const data = await createActivityData(url, byActor, dislikeActivity, t, audience) - const followersException = [ byAccount ] - return broadcastToFollowers(data, byAccount, accountsToForwardView, t, followersException) + const followersException = [ byActor ] + return broadcastToFollowers(data, byActor, actorsToForwardView, t, followersException) } async function createActivityData ( url: string, - byAccount: AccountModel, + byActor: ActorModel, object: any, t: Transaction, audience?: ActivityAudience ): Promise { if (!audience) { - audience = await getAudience(byAccount, t) + audience = await getAudience(byActor, t) } return { type: 'Create', id: url, - actor: byAccount.url, + actor: byActor.url, to: audience.to, cc: audience.cc, object } } -function createDislikeActivityData (byAccount: AccountModel, video: VideoModel) { +function createDislikeActivityData (byActor: ActorModel, video: VideoModel) { return { type: 'Dislike', - actor: byAccount.url, + actor: byActor.url, object: video.url } } @@ -113,7 +114,7 @@ function createDislikeActivityData (byAccount: AccountModel, video: VideoModel) // --------------------------------------------------------------------------- export { - sendCreateVideoChannel, + sendCreateVideo, sendVideoAbuse, createActivityData, sendCreateViewToOrigin, @@ -125,10 +126,10 @@ export { // --------------------------------------------------------------------------- -function createViewActivityData (byAccount: AccountModel, video: VideoModel) { +function createViewActivityData (byActor: ActorModel, video: VideoModel) { return { type: 'View', - actor: byAccount.url, + actor: byActor.url, object: video.url } } diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts index 0a45ea10f..4bc5db77e 100644 --- a/server/lib/activitypub/send/send-delete.ts +++ b/server/lib/activitypub/send/send-delete.ts @@ -1,54 +1,40 @@ import { Transaction } from 'sequelize' import { ActivityDelete } from '../../../../shared/models/activitypub' -import { AccountModel } from '../../../models/account/account' +import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' -import { VideoChannelModel } from '../../../models/video/video-channel' -import { VideoChannelShareModel } from '../../../models/video/video-channel-share' import { VideoShareModel } from '../../../models/video/video-share' import { broadcastToFollowers } from './misc' -async function sendDeleteVideoChannel (videoChannel: VideoChannelModel, t: Transaction) { - const byAccount = videoChannel.Account - - const data = deleteActivityData(videoChannel.url, byAccount) - - const accountsInvolved = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t) - accountsInvolved.push(byAccount) - - return broadcastToFollowers(data, byAccount, accountsInvolved, t) -} - async function sendDeleteVideo (video: VideoModel, t: Transaction) { - const byAccount = video.VideoChannel.Account + const byActor = video.VideoChannel.Account.Actor - const data = deleteActivityData(video.url, byAccount) + const data = deleteActivityData(video.url, byActor) - const accountsInvolved = await VideoShareModel.loadAccountsByShare(video.id, t) - accountsInvolved.push(byAccount) + const actorsInvolved = await VideoShareModel.loadActorsByShare(video.id, t) + actorsInvolved.push(byActor) - return broadcastToFollowers(data, byAccount, accountsInvolved, t) + return broadcastToFollowers(data, byActor, actorsInvolved, t) } -async function sendDeleteAccount (account: AccountModel, t: Transaction) { - const data = deleteActivityData(account.url, account) +async function sendDeleteActor (byActor: ActorModel, t: Transaction) { + const data = deleteActivityData(byActor.url, byActor) - return broadcastToFollowers(data, account, [ account ], t) + return broadcastToFollowers(data, byActor, [ byActor ], t) } // --------------------------------------------------------------------------- export { - sendDeleteVideoChannel, sendDeleteVideo, - sendDeleteAccount + sendDeleteActor } // --------------------------------------------------------------------------- -function deleteActivityData (url: string, byAccount: AccountModel): ActivityDelete { +function deleteActivityData (url: string, byActor: ActorModel): ActivityDelete { return { type: 'Delete', id: url, - actor: byAccount.url + actor: byActor.url } } diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts index 51735ddfd..eac60e94f 100644 --- a/server/lib/activitypub/send/send-follow.ts +++ b/server/lib/activitypub/send/send-follow.ts @@ -1,26 +1,26 @@ import { Transaction } from 'sequelize' import { ActivityFollow } from '../../../../shared/models/activitypub' -import { AccountModel } from '../../../models/account/account' -import { AccountFollowModel } from '../../../models/account/account-follow' -import { getAccountFollowActivityPubUrl } from '../url' +import { ActorModel } from '../../../models/activitypub/actor' +import { ActorFollowModel } from '../../../models/activitypub/actor-follow' +import { getActorFollowActivityPubUrl } from '../url' import { unicastTo } from './misc' -function sendFollow (accountFollow: AccountFollowModel, t: Transaction) { - const me = accountFollow.AccountFollower - const following = accountFollow.AccountFollowing +function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { + const me = actorFollow.ActorFollower + const following = actorFollow.ActorFollowing - const url = getAccountFollowActivityPubUrl(accountFollow) + const url = getActorFollowActivityPubUrl(actorFollow) const data = followActivityData(url, me, following) return unicastTo(data, me, following.inboxUrl, t) } -function followActivityData (url: string, byAccount: AccountModel, targetAccount: AccountModel): ActivityFollow { +function followActivityData (url: string, byActor: ActorModel, targetActor: ActorModel): ActivityFollow { return { type: 'Follow', id: url, - actor: byAccount.url, - object: targetAccount.url + actor: byActor.url, + object: targetActor.url } } diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts index 1a35d0db0..7e0c73796 100644 --- a/server/lib/activitypub/send/send-like.ts +++ b/server/lib/activitypub/send/send-like.ts @@ -1,53 +1,53 @@ import { Transaction } from 'sequelize' import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub' -import { AccountModel } from '../../../models/account/account' +import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' import { getVideoLikeActivityPubUrl } from '../url' import { broadcastToFollowers, - getAccountsInvolvedInVideo, + getActorsInvolvedInVideo, getAudience, - getOriginVideoAudience, getObjectFollowersAudience, + getOriginVideoAudience, unicastTo } from './misc' -async function sendLikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const url = getVideoLikeActivityPubUrl(byAccount, video) +async function sendLikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { + const url = getVideoLikeActivityPubUrl(byActor, video) - const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) + const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) - const data = await likeActivityData(url, byAccount, video, t, audience) + const data = await likeActivityData(url, byActor, video, t, audience) - return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) + return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) } -async function sendLikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const url = getVideoLikeActivityPubUrl(byAccount, video) +async function sendLikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { + const url = getVideoLikeActivityPubUrl(byActor, video) - const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) + const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) const audience = getObjectFollowersAudience(accountsInvolvedInVideo) - const data = await likeActivityData(url, byAccount, video, t, audience) + const data = await likeActivityData(url, byActor, video, t, audience) - const followersException = [ byAccount ] - return broadcastToFollowers(data, byAccount, accountsInvolvedInVideo, t, followersException) + const followersException = [ byActor ] + return broadcastToFollowers(data, byActor, accountsInvolvedInVideo, t, followersException) } async function likeActivityData ( url: string, - byAccount: AccountModel, + byActor: ActorModel, video: VideoModel, t: Transaction, audience?: ActivityAudience ): Promise { if (!audience) { - audience = await getAudience(byAccount, t) + audience = await getAudience(byActor, t) } return { type: 'Like', id: url, - actor: byAccount.url, + actor: byActor.url, to: audience.to, cc: audience.cc, object: video.url diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 699f920f0..92271b700 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts @@ -6,13 +6,13 @@ import { ActivityLike, ActivityUndo } from '../../../../shared/models/activitypub' -import { AccountModel } from '../../../models/account/account' -import { AccountFollowModel } from '../../../models/account/account-follow' +import { ActorModel } from '../../../models/activitypub/actor' +import { ActorFollowModel } from '../../../models/activitypub/actor-follow' import { VideoModel } from '../../../models/video/video' -import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' +import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' import { broadcastToFollowers, - getAccountsInvolvedInVideo, + getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getOriginVideoAudience, @@ -22,11 +22,11 @@ import { createActivityData, createDislikeActivityData } from './send-create' import { followActivityData } from './send-follow' import { likeActivityData } from './send-like' -async function sendUndoFollow (accountFollow: AccountFollowModel, t: Transaction) { - const me = accountFollow.AccountFollower - const following = accountFollow.AccountFollowing +async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { + const me = actorFollow.ActorFollower + const following = actorFollow.ActorFollowing - const followUrl = getAccountFollowActivityPubUrl(accountFollow) + const followUrl = getActorFollowActivityPubUrl(actorFollow) const undoUrl = getUndoActivityPubUrl(followUrl) const object = followActivityData(followUrl, me, following) @@ -35,58 +35,58 @@ async function sendUndoFollow (accountFollow: AccountFollowModel, t: Transaction return unicastTo(data, me, following.inboxUrl, t) } -async function sendUndoLikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const likeUrl = getVideoLikeActivityPubUrl(byAccount, video) +async function sendUndoLikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { + const likeUrl = getVideoLikeActivityPubUrl(byActor, video) const undoUrl = getUndoActivityPubUrl(likeUrl) - const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) - const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) - const object = await likeActivityData(likeUrl, byAccount, video, t) - const data = await undoActivityData(undoUrl, byAccount, object, t, audience) + const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) + const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) + const object = await likeActivityData(likeUrl, byActor, video, t) + const data = await undoActivityData(undoUrl, byActor, object, t, audience) - return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) + return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) } -async function sendUndoLikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const likeUrl = getVideoLikeActivityPubUrl(byAccount, video) +async function sendUndoLikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { + const likeUrl = getVideoLikeActivityPubUrl(byActor, video) const undoUrl = getUndoActivityPubUrl(likeUrl) - const toAccountsFollowers = await getAccountsInvolvedInVideo(video, t) - const audience = getObjectFollowersAudience(toAccountsFollowers) - const object = await likeActivityData(likeUrl, byAccount, video, t) - const data = await undoActivityData(undoUrl, byAccount, object, t, audience) + const toActorsFollowers = await getActorsInvolvedInVideo(video, t) + const audience = getObjectFollowersAudience(toActorsFollowers) + const object = await likeActivityData(likeUrl, byActor, video, t) + const data = await undoActivityData(undoUrl, byActor, object, t, audience) - const followersException = [ byAccount ] - return broadcastToFollowers(data, byAccount, toAccountsFollowers, t, followersException) + const followersException = [ byActor ] + return broadcastToFollowers(data, byActor, toActorsFollowers, t, followersException) } -async function sendUndoDislikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const dislikeUrl = getVideoDislikeActivityPubUrl(byAccount, video) +async function sendUndoDislikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { + const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video) const undoUrl = getUndoActivityPubUrl(dislikeUrl) - const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) - const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) - const dislikeActivity = createDislikeActivityData(byAccount, video) - const object = await createActivityData(undoUrl, byAccount, dislikeActivity, t) + const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) + const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) + const dislikeActivity = createDislikeActivityData(byActor, video) + const object = await createActivityData(undoUrl, byActor, dislikeActivity, t) - const data = await undoActivityData(undoUrl, byAccount, object, t, audience) + const data = await undoActivityData(undoUrl, byActor, object, t, audience) - return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) + return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) } -async function sendUndoDislikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { - const dislikeUrl = getVideoDislikeActivityPubUrl(byAccount, video) +async function sendUndoDislikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { + const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video) const undoUrl = getUndoActivityPubUrl(dislikeUrl) - const dislikeActivity = createDislikeActivityData(byAccount, video) - const object = await createActivityData(undoUrl, byAccount, dislikeActivity, t) + const dislikeActivity = createDislikeActivityData(byActor, video) + const object = await createActivityData(undoUrl, byActor, dislikeActivity, t) - const data = await undoActivityData(undoUrl, byAccount, object, t) + const data = await undoActivityData(undoUrl, byActor, object, t) - const toAccountsFollowers = await getAccountsInvolvedInVideo(video, t) + const toActorsFollowers = await getActorsInvolvedInVideo(video, t) - const followersException = [ byAccount ] - return broadcastToFollowers(data, byAccount, toAccountsFollowers, t, followersException) + const followersException = [ byActor ] + return broadcastToFollowers(data, byActor, toActorsFollowers, t, followersException) } // --------------------------------------------------------------------------- @@ -103,19 +103,19 @@ export { async function undoActivityData ( url: string, - byAccount: AccountModel, + byActor: ActorModel, object: ActivityFollow | ActivityLike | ActivityCreate, t: Transaction, audience?: ActivityAudience ): Promise { if (!audience) { - audience = await getAudience(byAccount, t) + audience = await getAudience(byActor, t) } return { type: 'Undo', id: url, - actor: byAccount.url, + actor: byActor.url, to: audience.to, cc: audience.cc, object diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index 9baf13a87..48bbbcac1 100644 --- a/server/lib/activitypub/send/send-update.ts +++ b/server/lib/activitypub/send/send-update.ts @@ -1,56 +1,52 @@ import { Transaction } from 'sequelize' -import { ActivityUpdate } from '../../../../shared/models/activitypub' -import { AccountModel } from '../../../models/account/account' +import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' +import { VideoPrivacy } from '../../../../shared/models/videos' +import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' -import { VideoChannelModel } from '../../../models/video/video-channel' -import { VideoChannelShareModel } from '../../../models/video/video-channel-share' import { VideoShareModel } from '../../../models/video/video-share' import { getUpdateActivityPubUrl } from '../url' import { broadcastToFollowers, getAudience } from './misc' -async function sendUpdateVideoChannel (videoChannel: VideoChannelModel, t: Transaction) { - const byAccount = videoChannel.Account - - const url = getUpdateActivityPubUrl(videoChannel.url, videoChannel.updatedAt.toISOString()) - const videoChannelObject = videoChannel.toActivityPubObject() - const data = await updateActivityData(url, byAccount, videoChannelObject, t) - - const accountsInvolved = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t) - accountsInvolved.push(byAccount) - - return broadcastToFollowers(data, byAccount, accountsInvolved, t) -} - async function sendUpdateVideo (video: VideoModel, t: Transaction) { - const byAccount = video.VideoChannel.Account + const byActor = video.VideoChannel.Account.Actor const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString()) const videoObject = video.toActivityPubObject() - const data = await updateActivityData(url, byAccount, videoObject, t) + const audience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC) + + const data = await updateActivityData(url, byActor, videoObject, t, audience) - const accountsInvolved = await VideoShareModel.loadAccountsByShare(video.id, t) - accountsInvolved.push(byAccount) + const actorsInvolved = await VideoShareModel.loadActorsByShare(video.id, t) + actorsInvolved.push(byActor) - return broadcastToFollowers(data, byAccount, accountsInvolved, t) + return broadcastToFollowers(data, byActor, actorsInvolved, t) } // --------------------------------------------------------------------------- export { - sendUpdateVideoChannel, sendUpdateVideo } // --------------------------------------------------------------------------- -async function updateActivityData (url: string, byAccount: AccountModel, object: any, t: Transaction): Promise { - const { to, cc } = await getAudience(byAccount, t) +async function updateActivityData ( + url: string, + byActor: ActorModel, + object: any, + t: Transaction, + audience?: ActivityAudience +): Promise { + if (!audience) { + audience = await getAudience(byActor, t) + } + return { type: 'Update', id: url, - actor: byAccount.url, - to, - cc, + actor: byActor.url, + to: audience.to, + cc: audience.cc, object } } diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts index 5bec61c05..f79c4e532 100644 --- a/server/lib/activitypub/share.ts +++ b/server/lib/activitypub/share.ts @@ -1,34 +1,20 @@ import { Transaction } from 'sequelize' -import { getServerAccount } from '../../helpers' +import { getServerActor } from '../../helpers' import { VideoModel } from '../../models/video/video' -import { VideoChannelModel } from '../../models/video/video-channel' -import { VideoChannelShareModel } from '../../models/video/video-channel-share' import { VideoShareModel } from '../../models/video/video-share' -import { sendVideoAnnounceToFollowers, sendVideoChannelAnnounceToFollowers } from './send' - -async function shareVideoChannelByServer (videoChannel: VideoChannelModel, t: Transaction) { - const serverAccount = await getServerAccount() - - await VideoChannelShareModel.create({ - accountId: serverAccount.id, - videoChannelId: videoChannel.id - }, { transaction: t }) - - return sendVideoChannelAnnounceToFollowers(serverAccount, videoChannel, t) -} +import { sendVideoAnnounceToFollowers } from './send' async function shareVideoByServer (video: VideoModel, t: Transaction) { - const serverAccount = await getServerAccount() + const serverActor = await getServerActor() await VideoShareModel.create({ - accountId: serverAccount.id, + actorId: serverActor.id, videoId: video.id }, { transaction: t }) - return sendVideoAnnounceToFollowers(serverAccount, video, t) + return sendVideoAnnounceToFollowers(serverActor, video, t) } export { - shareVideoChannelByServer, shareVideoByServer } diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts index 00b4e8852..bb2d4d11e 100644 --- a/server/lib/activitypub/url.ts +++ b/server/lib/activitypub/url.ts @@ -1,16 +1,19 @@ import { CONFIG } from '../../initializers' -import { AccountModel } from '../../models/account/account' -import { AccountFollowModel } from '../../models/account/account-follow' +import { ActorModel } from '../../models/activitypub/actor' +import { ActorFollowModel } from '../../models/activitypub/actor-follow' import { VideoModel } from '../../models/video/video' import { VideoAbuseModel } from '../../models/video/video-abuse' -import { VideoChannelModel } from '../../models/video/video-channel' function getVideoActivityPubUrl (video: VideoModel) { return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid } -function getVideoChannelActivityPubUrl (videoChannel: VideoChannelModel) { - return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannel.uuid +function getVideoChannelActivityPubUrl (videoChannelUUID: string) { + return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannelUUID +} + +function getApplicationActivityPubUrl () { + return CONFIG.WEBSERVER.URL + '/application/peertube' } function getAccountActivityPubUrl (accountName: string) { @@ -21,34 +24,34 @@ function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) { return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id } -function getVideoViewActivityPubUrl (byAccount: AccountModel, video: VideoModel) { - return video.url + '/views/' + byAccount.uuid + '/' + new Date().toISOString() +function getVideoViewActivityPubUrl (byActor: ActorModel, video: VideoModel) { + return video.url + '/views/' + byActor.uuid + '/' + new Date().toISOString() } -function getVideoLikeActivityPubUrl (byAccount: AccountModel, video: VideoModel) { - return byAccount.url + '/likes/' + video.id +function getVideoLikeActivityPubUrl (byActor: ActorModel, video: VideoModel) { + return byActor.url + '/likes/' + video.id } -function getVideoDislikeActivityPubUrl (byAccount: AccountModel, video: VideoModel) { - return byAccount.url + '/dislikes/' + video.id +function getVideoDislikeActivityPubUrl (byActor: ActorModel, video: VideoModel) { + return byActor.url + '/dislikes/' + video.id } -function getAccountFollowActivityPubUrl (accountFollow: AccountFollowModel) { - const me = accountFollow.AccountFollower - const following = accountFollow.AccountFollowing +function getActorFollowActivityPubUrl (actorFollow: ActorFollowModel) { + const me = actorFollow.ActorFollower + const following = actorFollow.ActorFollowing return me.url + '/follows/' + following.id } -function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowModel) { - const follower = accountFollow.AccountFollower - const me = accountFollow.AccountFollowing +function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModel) { + const follower = actorFollow.ActorFollower + const me = actorFollow.ActorFollowing return follower.url + '/accepts/follows/' + me.id } -function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountModel) { - return originalUrl + '/announces/' + byAccount.id +function getAnnounceActivityPubUrl (originalUrl: string, byActor: ActorModel) { + return originalUrl + '/announces/' + byActor.id } function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) { @@ -60,12 +63,13 @@ function getUndoActivityPubUrl (originalUrl: string) { } export { + getApplicationActivityPubUrl, getVideoActivityPubUrl, getVideoChannelActivityPubUrl, getAccountActivityPubUrl, getVideoAbuseActivityPubUrl, - getAccountFollowActivityPubUrl, - getAccountFollowAcceptActivityPubUrl, + getActorFollowActivityPubUrl, + getActorFollowAcceptActivityPubUrl, getAnnounceActivityPubUrl, getUpdateActivityPubUrl, getUndoActivityPubUrl, diff --git a/server/lib/activitypub/video-channels.ts b/server/lib/activitypub/video-channels.ts deleted file mode 100644 index c05a46f95..000000000 --- a/server/lib/activitypub/video-channels.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { VideoChannelObject } from '../../../shared/models/activitypub/objects' -import { doRequest, logger } from '../../helpers' -import { isVideoChannelObjectValid } from '../../helpers/custom-validators/activitypub' -import { ACTIVITY_PUB } from '../../initializers' -import { AccountModel } from '../../models/account/account' -import { VideoChannelModel } from '../../models/video/video-channel' -import { videoChannelActivityObjectToDBAttributes } from './process/misc' - -async function getOrCreateVideoChannel (ownerAccount: AccountModel, videoChannelUrl: string) { - let videoChannel = await VideoChannelModel.loadByUrl(videoChannelUrl) - - // We don't have this account in our database, fetch it on remote - if (!videoChannel) { - videoChannel = await fetchRemoteVideoChannel(ownerAccount, videoChannelUrl) - if (videoChannel === undefined) throw new Error('Cannot fetch remote video channel.') - - // Save our new video channel in database - await videoChannel.save() - } - - return videoChannel -} - -async function fetchRemoteVideoChannel (ownerAccount: AccountModel, videoChannelUrl: string) { - const options = { - uri: videoChannelUrl, - method: 'GET', - headers: { - 'Accept': ACTIVITY_PUB.ACCEPT_HEADER - } - } - - logger.info('Fetching remote video channel %s.', videoChannelUrl) - - let requestResult - try { - requestResult = await doRequest(options) - } catch (err) { - logger.warn('Cannot fetch remote video channel %s.', videoChannelUrl, err) - return undefined - } - - const videoChannelJSON: VideoChannelObject = JSON.parse(requestResult.body) - if (isVideoChannelObjectValid(videoChannelJSON) === false) { - logger.debug('Remote video channel JSON is not valid.', { videoChannelJSON }) - return undefined - } - - const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount) - const videoChannel = new VideoChannelModel(videoChannelAttributes) - videoChannel.Account = ownerAccount - - return videoChannel -} - -export { - getOrCreateVideoChannel, - fetchRemoteVideoChannel -} diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 14c07fec0..fab43757a 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts @@ -19,7 +19,7 @@ import { function fetchRemoteVideoPreview (video: VideoModel) { // FIXME: use url - const host = video.VideoChannel.Account.Server.host + const host = video.VideoChannel.Account.Actor.Server.host const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName()) return request.get(REMOTE_SCHEME.HTTP + '://' + host + path) @@ -27,7 +27,7 @@ function fetchRemoteVideoPreview (video: VideoModel) { async function fetchRemoteVideoDescription (video: VideoModel) { // FIXME: use url - const host = video.VideoChannel.Account.Server.host + const host = video.VideoChannel.Account.Actor.Server.host const path = video.getDescriptionPath() const options = { uri: REMOTE_SCHEME.HTTP + '://' + host + path, @@ -50,43 +50,47 @@ function generateThumbnailFromUrl (video: VideoModel, icon: ActivityIconObject) } async function sendVideoRateChangeToFollowers ( - account: AccountModel, + account: AccountModel, video: VideoModel, likes: number, dislikes: number, t: Transaction ) { + const actor = account.Actor + // Keep the order: first we undo and then we create // Undo Like - if (likes < 0) await sendUndoLikeToVideoFollowers(account, video, t) + if (likes < 0) await sendUndoLikeToVideoFollowers(actor, video, t) // Undo Dislike - if (dislikes < 0) await sendUndoDislikeToVideoFollowers(account, video, t) + if (dislikes < 0) await sendUndoDislikeToVideoFollowers(actor, video, t) // Like - if (likes > 0) await sendLikeToVideoFollowers(account, video, t) + if (likes > 0) await sendLikeToVideoFollowers(actor, video, t) // Dislike - if (dislikes > 0) await sendCreateDislikeToVideoFollowers(account, video, t) + if (dislikes > 0) await sendCreateDislikeToVideoFollowers(actor, video, t) } async function sendVideoRateChangeToOrigin ( - account: AccountModel, + account: AccountModel, video: VideoModel, likes: number, dislikes: number, t: Transaction ) { + const actor = account.Actor + // Keep the order: first we undo and then we create // Undo Like - if (likes < 0) await sendUndoLikeToOrigin(account, video, t) + if (likes < 0) await sendUndoLikeToOrigin(actor, video, t) // Undo Dislike - if (dislikes < 0) await sendUndoDislikeToOrigin(account, video, t) + if (dislikes < 0) await sendUndoDislikeToOrigin(actor, video, t) // Like - if (likes > 0) await sendLikeToOrigin(account, video, t) + if (likes > 0) await sendLikeToOrigin(actor, video, t) // Dislike - if (dislikes > 0) await sendCreateDislikeToOrigin(account, video, t) + if (dislikes > 0) await sendCreateDislikeToOrigin(actor, video, t) } export { -- cgit v1.2.3