diff options
author | Chocobozzz <me@florianbigard.com> | 2017-12-14 17:38:41 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2017-12-19 10:53:16 +0100 |
commit | 50d6de9c286abcb34ff4234d56d9cbb803db7665 (patch) | |
tree | f1732b27edcd05c7877a8358b8312f1e38c287ed /server/lib | |
parent | fadf619ad61a016c1c7fc53de5a8f398a4f77519 (diff) | |
download | PeerTube-50d6de9c286abcb34ff4234d56d9cbb803db7665.tar.gz PeerTube-50d6de9c286abcb34ff4234d56d9cbb803db7665.tar.zst PeerTube-50d6de9c286abcb34ff4234d56d9cbb803db7665.zip |
Begin moving video channel to actor
Diffstat (limited to 'server/lib')
36 files changed, 821 insertions, 1076 deletions
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 @@ | |||
1 | import * as Bluebird from 'bluebird' | ||
2 | import { Transaction } from 'sequelize' | ||
3 | import * as url from 'url' | ||
4 | import { ActivityPubActor } from '../../../shared/models/activitypub' | ||
5 | import { doRequest, logger, retryTransactionWrapper } from '../../helpers' | ||
6 | import { isRemoteAccountValid } from '../../helpers/custom-validators/activitypub' | ||
7 | import { ACTIVITY_PUB, sequelizeTypescript } from '../../initializers' | ||
8 | import { AccountModel } from '../../models/account/account' | ||
9 | import { ServerModel } from '../../models/server/server' | ||
10 | |||
11 | async function getOrCreateAccountAndServer (accountUrl: string) { | ||
12 | let account = await AccountModel.loadByUrl(accountUrl) | ||
13 | |||
14 | // We don't have this account in our database, fetch it on remote | ||
15 | if (!account) { | ||
16 | account = await fetchRemoteAccount(accountUrl) | ||
17 | if (account === undefined) throw new Error('Cannot fetch remote account.') | ||
18 | |||
19 | const options = { | ||
20 | arguments: [ account ], | ||
21 | errorMessage: 'Cannot save account and server with many retries.' | ||
22 | } | ||
23 | account = await retryTransactionWrapper(saveAccountAndServerIfNotExist, options) | ||
24 | } | ||
25 | |||
26 | return account | ||
27 | } | ||
28 | |||
29 | function saveAccountAndServerIfNotExist (account: AccountModel, t?: Transaction): Bluebird<AccountModel> | Promise<AccountModel> { | ||
30 | if (t !== undefined) { | ||
31 | return save(t) | ||
32 | } else { | ||
33 | return sequelizeTypescript.transaction(t => { | ||
34 | return save(t) | ||
35 | }) | ||
36 | } | ||
37 | |||
38 | async function save (t: Transaction) { | ||
39 | const accountHost = url.parse(account.url).host | ||
40 | |||
41 | const serverOptions = { | ||
42 | where: { | ||
43 | host: accountHost | ||
44 | }, | ||
45 | defaults: { | ||
46 | host: accountHost | ||
47 | }, | ||
48 | transaction: t | ||
49 | } | ||
50 | const [ server ] = await ServerModel.findOrCreate(serverOptions) | ||
51 | |||
52 | // Save our new account in database | ||
53 | account.set('serverId', server.id) | ||
54 | account = await account.save({ transaction: t }) | ||
55 | |||
56 | return account | ||
57 | } | ||
58 | } | ||
59 | |||
60 | async function fetchRemoteAccount (accountUrl: string) { | ||
61 | const options = { | ||
62 | uri: accountUrl, | ||
63 | method: 'GET', | ||
64 | headers: { | ||
65 | 'Accept': ACTIVITY_PUB.ACCEPT_HEADER | ||
66 | } | ||
67 | } | ||
68 | |||
69 | logger.info('Fetching remote account %s.', accountUrl) | ||
70 | |||
71 | let requestResult | ||
72 | try { | ||
73 | requestResult = await doRequest(options) | ||
74 | } catch (err) { | ||
75 | logger.warn('Cannot fetch remote account %s.', accountUrl, err) | ||
76 | return undefined | ||
77 | } | ||
78 | |||
79 | const accountJSON: ActivityPubActor = JSON.parse(requestResult.body) | ||
80 | if (isRemoteAccountValid(accountJSON) === false) { | ||
81 | logger.debug('Remote account JSON is not valid.', { accountJSON }) | ||
82 | return undefined | ||
83 | } | ||
84 | |||
85 | const followersCount = await fetchAccountCount(accountJSON.followers) | ||
86 | const followingCount = await fetchAccountCount(accountJSON.following) | ||
87 | |||
88 | return new AccountModel({ | ||
89 | uuid: accountJSON.uuid, | ||
90 | name: accountJSON.preferredUsername, | ||
91 | url: accountJSON.url, | ||
92 | publicKey: accountJSON.publicKey.publicKeyPem, | ||
93 | privateKey: null, | ||
94 | followersCount: followersCount, | ||
95 | followingCount: followingCount, | ||
96 | inboxUrl: accountJSON.inbox, | ||
97 | outboxUrl: accountJSON.outbox, | ||
98 | sharedInboxUrl: accountJSON.endpoints.sharedInbox, | ||
99 | followersUrl: accountJSON.followers, | ||
100 | followingUrl: accountJSON.following | ||
101 | }) | ||
102 | } | ||
103 | |||
104 | export { | ||
105 | getOrCreateAccountAndServer, | ||
106 | fetchRemoteAccount, | ||
107 | saveAccountAndServerIfNotExist | ||
108 | } | ||
109 | |||
110 | // --------------------------------------------------------------------------- | ||
111 | |||
112 | async function fetchAccountCount (url: string) { | ||
113 | const options = { | ||
114 | uri: url, | ||
115 | method: 'GET' | ||
116 | } | ||
117 | |||
118 | let requestResult | ||
119 | try { | ||
120 | requestResult = await doRequest(options) | ||
121 | } catch (err) { | ||
122 | logger.warn('Cannot fetch remote account count %s.', url, err) | ||
123 | return undefined | ||
124 | } | ||
125 | |||
126 | return requestResult.totalItems ? requestResult.totalItems : 0 | ||
127 | } | ||
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 @@ | |||
1 | import * as Bluebird from 'bluebird' | ||
2 | import { Transaction } from 'sequelize' | ||
3 | import * as url from 'url' | ||
4 | import { ActivityPubActor, ActivityPubActorType } from '../../../shared/models/activitypub' | ||
5 | import { ActivityPubAttributedTo } from '../../../shared/models/activitypub/objects' | ||
6 | import { createPrivateAndPublicKeys, doRequest, logger, retryTransactionWrapper } from '../../helpers' | ||
7 | import { isRemoteActorValid } from '../../helpers/custom-validators/activitypub' | ||
8 | import { ACTIVITY_PUB, CONFIG, sequelizeTypescript } from '../../initializers' | ||
9 | import { AccountModel } from '../../models/account/account' | ||
10 | import { ActorModel } from '../../models/activitypub/actor' | ||
11 | import { ServerModel } from '../../models/server/server' | ||
12 | import { VideoChannelModel } from '../../models/video/video-channel' | ||
13 | |||
14 | // Set account keys, this could be long so process after the account creation and do not block the client | ||
15 | function setAsyncActorKeys (actor: ActorModel) { | ||
16 | return createPrivateAndPublicKeys() | ||
17 | .then(({ publicKey, privateKey }) => { | ||
18 | actor.set('publicKey', publicKey) | ||
19 | actor.set('privateKey', privateKey) | ||
20 | return actor.save() | ||
21 | }) | ||
22 | .catch(err => { | ||
23 | logger.error('Cannot set public/private keys of actor %d.', actor.uuid, err) | ||
24 | return actor | ||
25 | }) | ||
26 | } | ||
27 | |||
28 | async function getOrCreateActorAndServerAndModel (actorUrl: string, recurseIfNeeded = true) { | ||
29 | let actor = await ActorModel.loadByUrl(actorUrl) | ||
30 | |||
31 | // We don't have this actor in our database, fetch it on remote | ||
32 | if (!actor) { | ||
33 | const result = await fetchRemoteActor(actorUrl) | ||
34 | if (result === undefined) throw new Error('Cannot fetch remote actor.') | ||
35 | |||
36 | // Create the attributed to actor | ||
37 | // In PeerTube a video channel is owned by an account | ||
38 | let ownerActor: ActorModel = undefined | ||
39 | if (recurseIfNeeded === true && result.actor.type === 'Group') { | ||
40 | const accountAttributedTo = result.attributedTo.find(a => a.type === 'Person') | ||
41 | if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actor.url) | ||
42 | |||
43 | try { | ||
44 | // Assert we don't recurse another time | ||
45 | ownerActor = await getOrCreateActorAndServerAndModel(accountAttributedTo.id, false) | ||
46 | } catch (err) { | ||
47 | logger.error('Cannot get or create account attributed to video channel ' + actor.url) | ||
48 | throw new Error(err) | ||
49 | } | ||
50 | } | ||
51 | |||
52 | const options = { | ||
53 | arguments: [ result, ownerActor ], | ||
54 | errorMessage: 'Cannot save actor and server with many retries.' | ||
55 | } | ||
56 | actor = await retryTransactionWrapper(saveActorAndServerAndModelIfNotExist, options) | ||
57 | } | ||
58 | |||
59 | return actor | ||
60 | } | ||
61 | |||
62 | function saveActorAndServerAndModelIfNotExist ( | ||
63 | result: FetchRemoteActorResult, | ||
64 | ownerActor?: ActorModel, | ||
65 | t?: Transaction | ||
66 | ): Bluebird<ActorModel> | Promise<ActorModel> { | ||
67 | let actor = result.actor | ||
68 | |||
69 | if (t !== undefined) return save(t) | ||
70 | |||
71 | return sequelizeTypescript.transaction(t => save(t)) | ||
72 | |||
73 | async function save (t: Transaction) { | ||
74 | const actorHost = url.parse(actor.url).host | ||
75 | |||
76 | const serverOptions = { | ||
77 | where: { | ||
78 | host: actorHost | ||
79 | }, | ||
80 | defaults: { | ||
81 | host: actorHost | ||
82 | }, | ||
83 | transaction: t | ||
84 | } | ||
85 | const [ server ] = await ServerModel.findOrCreate(serverOptions) | ||
86 | |||
87 | // Save our new account in database | ||
88 | actor.set('serverId', server.id) | ||
89 | |||
90 | // Force the actor creation, sometimes Sequelize skips the save() when it thinks the instance already exists | ||
91 | // (which could be false in a retried query) | ||
92 | const actorCreated = await ActorModel.create(actor.toJSON(), { transaction: t }) | ||
93 | |||
94 | if (actorCreated.type === 'Person' || actorCreated.type === 'Application') { | ||
95 | const account = await saveAccount(actorCreated, result, t) | ||
96 | actorCreated.Account = account | ||
97 | actorCreated.Account.Actor = actorCreated | ||
98 | } else if (actorCreated.type === 'Group') { // Video channel | ||
99 | const videoChannel = await saveVideoChannel(actorCreated, result, ownerActor, t) | ||
100 | actorCreated.VideoChannel = videoChannel | ||
101 | actorCreated.VideoChannel.Actor = actorCreated | ||
102 | } | ||
103 | |||
104 | return actorCreated | ||
105 | } | ||
106 | } | ||
107 | |||
108 | type FetchRemoteActorResult = { | ||
109 | actor: ActorModel | ||
110 | preferredUsername: string | ||
111 | summary: string | ||
112 | attributedTo: ActivityPubAttributedTo[] | ||
113 | } | ||
114 | async function fetchRemoteActor (actorUrl: string): Promise<FetchRemoteActorResult> { | ||
115 | const options = { | ||
116 | uri: actorUrl, | ||
117 | method: 'GET', | ||
118 | headers: { | ||
119 | 'Accept': ACTIVITY_PUB.ACCEPT_HEADER | ||
120 | } | ||
121 | } | ||
122 | |||
123 | logger.info('Fetching remote actor %s.', actorUrl) | ||
124 | |||
125 | let requestResult | ||
126 | try { | ||
127 | requestResult = await doRequest(options) | ||
128 | } catch (err) { | ||
129 | logger.warn('Cannot fetch remote actor %s.', actorUrl, err) | ||
130 | return undefined | ||
131 | } | ||
132 | |||
133 | const actorJSON: ActivityPubActor = JSON.parse(requestResult.body) | ||
134 | if (isRemoteActorValid(actorJSON) === false) { | ||
135 | logger.debug('Remote actor JSON is not valid.', { actorJSON: actorJSON }) | ||
136 | return undefined | ||
137 | } | ||
138 | |||
139 | const followersCount = await fetchActorTotalItems(actorJSON.followers) | ||
140 | const followingCount = await fetchActorTotalItems(actorJSON.following) | ||
141 | |||
142 | const actor = new ActorModel({ | ||
143 | type: actorJSON.type, | ||
144 | uuid: actorJSON.uuid, | ||
145 | name: actorJSON.name, | ||
146 | url: actorJSON.url, | ||
147 | publicKey: actorJSON.publicKey.publicKeyPem, | ||
148 | privateKey: null, | ||
149 | followersCount: followersCount, | ||
150 | followingCount: followingCount, | ||
151 | inboxUrl: actorJSON.inbox, | ||
152 | outboxUrl: actorJSON.outbox, | ||
153 | sharedInboxUrl: actorJSON.endpoints.sharedInbox, | ||
154 | followersUrl: actorJSON.followers, | ||
155 | followingUrl: actorJSON.following | ||
156 | }) | ||
157 | |||
158 | return { | ||
159 | actor, | ||
160 | preferredUsername: actorJSON.preferredUsername, | ||
161 | summary: actorJSON.summary, | ||
162 | attributedTo: actorJSON.attributedTo | ||
163 | } | ||
164 | } | ||
165 | |||
166 | function buildActorInstance (type: ActivityPubActorType, url: string, name: string, uuid?: string) { | ||
167 | return new ActorModel({ | ||
168 | type, | ||
169 | url, | ||
170 | name, | ||
171 | uuid, | ||
172 | publicKey: null, | ||
173 | privateKey: null, | ||
174 | followersCount: 0, | ||
175 | followingCount: 0, | ||
176 | inboxUrl: url + '/inbox', | ||
177 | outboxUrl: url + '/outbox', | ||
178 | sharedInboxUrl: CONFIG.WEBSERVER.URL + '/inbox', | ||
179 | followersUrl: url + '/followers', | ||
180 | followingUrl: url + '/following' | ||
181 | }) | ||
182 | } | ||
183 | |||
184 | export { | ||
185 | getOrCreateActorAndServerAndModel, | ||
186 | saveActorAndServerAndModelIfNotExist, | ||
187 | fetchRemoteActor, | ||
188 | buildActorInstance, | ||
189 | setAsyncActorKeys | ||
190 | } | ||
191 | |||
192 | // --------------------------------------------------------------------------- | ||
193 | |||
194 | async function fetchActorTotalItems (url: string) { | ||
195 | const options = { | ||
196 | uri: url, | ||
197 | method: 'GET' | ||
198 | } | ||
199 | |||
200 | let requestResult | ||
201 | try { | ||
202 | requestResult = await doRequest(options) | ||
203 | } catch (err) { | ||
204 | logger.warn('Cannot fetch remote actor count %s.', url, err) | ||
205 | return undefined | ||
206 | } | ||
207 | |||
208 | return requestResult.totalItems ? requestResult.totalItems : 0 | ||
209 | } | ||
210 | |||
211 | function saveAccount (actor: ActorModel, result: FetchRemoteActorResult, t: Transaction) { | ||
212 | const account = new AccountModel({ | ||
213 | name: result.preferredUsername, | ||
214 | actorId: actor.id | ||
215 | }) | ||
216 | |||
217 | return account.save({ transaction: t }) | ||
218 | } | ||
219 | |||
220 | async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResult, ownerActor: ActorModel, t: Transaction) { | ||
221 | const videoChannel = new VideoChannelModel({ | ||
222 | name: result.preferredUsername, | ||
223 | description: result.summary, | ||
224 | actorId: actor.id, | ||
225 | accountId: ownerActor.Account.id | ||
226 | }) | ||
227 | |||
228 | return videoChannel.save({ transaction: t }) | ||
229 | } | ||
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 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { AccountModel } from '../../models/account/account' | 2 | import { ActorModel } from '../../models/activitypub/actor' |
3 | import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../jobs/activitypub-http-job-scheduler' | 3 | import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../jobs/activitypub-http-job-scheduler' |
4 | 4 | ||
5 | async function addFetchOutboxJob (account: AccountModel, t: Transaction) { | 5 | async function addFetchOutboxJob (actor: ActorModel, t: Transaction) { |
6 | const jobPayload: ActivityPubHttpPayload = { | 6 | const jobPayload: ActivityPubHttpPayload = { |
7 | uris: [ account.outboxUrl ] | 7 | uris: [ actor.outboxUrl ] |
8 | } | 8 | } |
9 | 9 | ||
10 | return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpFetcherHandler', jobPayload) | 10 | 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 @@ | |||
1 | export * from './process' | 1 | export * from './process' |
2 | export * from './send' | 2 | export * from './send' |
3 | export * from './account' | 3 | export * from './actor' |
4 | export * from './fetch' | 4 | export * from './fetch' |
5 | export * from './share' | 5 | export * from './share' |
6 | export * from './video-channels' | ||
7 | export * from './videos' | 6 | export * from './videos' |
8 | export * from './url' | 7 | 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 @@ | |||
1 | export * from './process' | 1 | export * from './process' |
2 | export * from './process-accept' | 2 | export * from './process-accept' |
3 | export * from './process-add' | ||
4 | export * from './process-announce' | 3 | export * from './process-announce' |
5 | export * from './process-create' | 4 | export * from './process-create' |
6 | export * from './process-delete' | 5 | 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 @@ | |||
1 | import * as magnetUtil from 'magnet-uri' | 1 | import * as magnetUtil from 'magnet-uri' |
2 | import { VideoTorrentObject } from '../../../../shared' | 2 | import { VideoTorrentObject } from '../../../../shared' |
3 | import { VideoChannelObject } from '../../../../shared/models/activitypub/objects' | ||
4 | import { VideoPrivacy } from '../../../../shared/models/videos' | 3 | import { VideoPrivacy } from '../../../../shared/models/videos' |
5 | import { doRequest } from '../../../helpers' | 4 | import { doRequest } from '../../../helpers' |
6 | import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos' | 5 | import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos' |
7 | import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers' | 6 | import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers' |
8 | import { AccountModel } from '../../../models/account/account' | ||
9 | import { VideoModel } from '../../../models/video/video' | 7 | import { VideoModel } from '../../../models/video/video' |
10 | import { VideoChannelModel } from '../../../models/video/video-channel' | 8 | import { VideoChannelModel } from '../../../models/video/video-channel' |
11 | import { VideoChannelShareModel } from '../../../models/video/video-channel-share' | ||
12 | import { VideoShareModel } from '../../../models/video/video-share' | 9 | import { VideoShareModel } from '../../../models/video/video-share' |
13 | import { getOrCreateAccountAndServer } from '../account' | 10 | import { getOrCreateActorAndServerAndModel } from '../actor' |
14 | |||
15 | function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountModel) { | ||
16 | return { | ||
17 | name: videoChannelObject.name, | ||
18 | description: videoChannelObject.content, | ||
19 | uuid: videoChannelObject.uuid, | ||
20 | url: videoChannelObject.id, | ||
21 | createdAt: new Date(videoChannelObject.published), | ||
22 | updatedAt: new Date(videoChannelObject.updated), | ||
23 | remote: true, | ||
24 | accountId: account.id | ||
25 | } | ||
26 | } | ||
27 | 11 | ||
28 | async function videoActivityObjectToDBAttributes ( | 12 | async function videoActivityObjectToDBAttributes ( |
29 | videoChannel: VideoChannelModel, | 13 | videoChannel: VideoChannelModel, |
@@ -120,13 +104,13 @@ async function addVideoShares (instance: VideoModel, shares: string[]) { | |||
120 | uri: share, | 104 | uri: share, |
121 | json: true | 105 | json: true |
122 | }) | 106 | }) |
123 | const actor = json['actor'] | 107 | const actorUrl = json['actor'] |
124 | if (!actor) continue | 108 | if (!actorUrl) continue |
125 | 109 | ||
126 | const account = await getOrCreateAccountAndServer(actor) | 110 | const actor = await getOrCreateActorAndServerAndModel(actorUrl) |
127 | 111 | ||
128 | const entry = { | 112 | const entry = { |
129 | accountId: account.id, | 113 | actorId: actor.id, |
130 | videoId: instance.id | 114 | videoId: instance.id |
131 | } | 115 | } |
132 | 116 | ||
@@ -137,36 +121,10 @@ async function addVideoShares (instance: VideoModel, shares: string[]) { | |||
137 | } | 121 | } |
138 | } | 122 | } |
139 | 123 | ||
140 | async function addVideoChannelShares (instance: VideoChannelModel, shares: string[]) { | ||
141 | for (const share of shares) { | ||
142 | // Fetch url | ||
143 | const json = await doRequest({ | ||
144 | uri: share, | ||
145 | json: true | ||
146 | }) | ||
147 | const actor = json['actor'] | ||
148 | if (!actor) continue | ||
149 | |||
150 | const account = await getOrCreateAccountAndServer(actor) | ||
151 | |||
152 | const entry = { | ||
153 | accountId: account.id, | ||
154 | videoChannelId: instance.id | ||
155 | } | ||
156 | |||
157 | await VideoChannelShareModel.findOrCreate({ | ||
158 | where: entry, | ||
159 | defaults: entry | ||
160 | }) | ||
161 | } | ||
162 | } | ||
163 | |||
164 | // --------------------------------------------------------------------------- | 124 | // --------------------------------------------------------------------------- |
165 | 125 | ||
166 | export { | 126 | export { |
167 | videoFileActivityUrlToDBAttributes, | 127 | videoFileActivityUrlToDBAttributes, |
168 | videoActivityObjectToDBAttributes, | 128 | videoActivityObjectToDBAttributes, |
169 | videoChannelActivityObjectToDBAttributes, | ||
170 | addVideoChannelShares, | ||
171 | addVideoShares | 129 | addVideoShares |
172 | } | 130 | } |
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 @@ | |||
1 | import { ActivityAccept } from '../../../../shared/models/activitypub' | 1 | import { ActivityAccept } from '../../../../shared/models/activitypub' |
2 | import { AccountModel } from '../../../models/account/account' | 2 | import { ActorModel } from '../../../models/activitypub/actor' |
3 | import { AccountFollowModel } from '../../../models/account/account-follow' | 3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
4 | import { addFetchOutboxJob } from '../fetch' | 4 | import { addFetchOutboxJob } from '../fetch' |
5 | 5 | ||
6 | async function processAcceptActivity (activity: ActivityAccept, inboxAccount?: AccountModel) { | 6 | async function processAcceptActivity (activity: ActivityAccept, inboxActor?: ActorModel) { |
7 | if (inboxAccount === undefined) throw new Error('Need to accept on explicit inbox.') | 7 | if (inboxActor === undefined) throw new Error('Need to accept on explicit inbox.') |
8 | 8 | ||
9 | const targetAccount = await AccountModel.loadByUrl(activity.actor) | 9 | const targetActor = await ActorModel.loadByUrl(activity.actor) |
10 | 10 | ||
11 | return processAccept(inboxAccount, targetAccount) | 11 | return processAccept(inboxActor, targetActor) |
12 | } | 12 | } |
13 | 13 | ||
14 | // --------------------------------------------------------------------------- | 14 | // --------------------------------------------------------------------------- |
@@ -19,11 +19,11 @@ export { | |||
19 | 19 | ||
20 | // --------------------------------------------------------------------------- | 20 | // --------------------------------------------------------------------------- |
21 | 21 | ||
22 | async function processAccept (account: AccountModel, targetAccount: AccountModel) { | 22 | async function processAccept (actor: ActorModel, targetActor: ActorModel) { |
23 | const follow = await AccountFollowModel.loadByAccountAndTarget(account.id, targetAccount.id) | 23 | const follow = await ActorFollowModel.loadByActorAndTarget(actor.id, targetActor.id) |
24 | if (!follow) throw new Error('Cannot find associated follow.') | 24 | if (!follow) throw new Error('Cannot find associated follow.') |
25 | 25 | ||
26 | follow.set('state', 'accepted') | 26 | follow.set('state', 'accepted') |
27 | await follow.save() | 27 | await follow.save() |
28 | await addFetchOutboxJob(targetAccount, undefined) | 28 | await addFetchOutboxJob(targetActor, undefined) |
29 | } | 29 | } |
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 @@ | |||
1 | import * as Bluebird from 'bluebird' | ||
2 | import { VideoTorrentObject } from '../../../../shared' | ||
3 | import { ActivityAdd } from '../../../../shared/models/activitypub' | ||
4 | import { VideoRateType } from '../../../../shared/models/videos' | ||
5 | import { logger, retryTransactionWrapper } from '../../../helpers' | ||
6 | import { sequelizeTypescript } from '../../../initializers' | ||
7 | import { AccountModel } from '../../../models/account/account' | ||
8 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | ||
9 | import { TagModel } from '../../../models/video/tag' | ||
10 | import { VideoModel } from '../../../models/video/video' | ||
11 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
12 | import { VideoFileModel } from '../../../models/video/video-file' | ||
13 | import { getOrCreateAccountAndServer } from '../account' | ||
14 | import { getOrCreateVideoChannel } from '../video-channels' | ||
15 | import { generateThumbnailFromUrl } from '../videos' | ||
16 | import { addVideoShares, videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' | ||
17 | |||
18 | async function processAddActivity (activity: ActivityAdd) { | ||
19 | const activityObject = activity.object | ||
20 | const activityType = activityObject.type | ||
21 | const account = await getOrCreateAccountAndServer(activity.actor) | ||
22 | |||
23 | if (activityType === 'Video') { | ||
24 | const videoChannelUrl = activity.target | ||
25 | const videoChannel = await getOrCreateVideoChannel(account, videoChannelUrl) | ||
26 | |||
27 | return processAddVideo(account, activity, videoChannel, activityObject) | ||
28 | } | ||
29 | |||
30 | logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) | ||
31 | return Promise.resolve(undefined) | ||
32 | } | ||
33 | |||
34 | // --------------------------------------------------------------------------- | ||
35 | |||
36 | export { | ||
37 | processAddActivity | ||
38 | } | ||
39 | |||
40 | // --------------------------------------------------------------------------- | ||
41 | |||
42 | async function processAddVideo (account: AccountModel, | ||
43 | activity: ActivityAdd, | ||
44 | videoChannel: VideoChannelModel, | ||
45 | videoToCreateData: VideoTorrentObject) { | ||
46 | const options = { | ||
47 | arguments: [ account, activity, videoChannel, videoToCreateData ], | ||
48 | errorMessage: 'Cannot insert the remote video with many retries.' | ||
49 | } | ||
50 | |||
51 | const video = await retryTransactionWrapper(addRemoteVideo, options) | ||
52 | |||
53 | // Process outside the transaction because we could fetch remote data | ||
54 | if (videoToCreateData.likes && Array.isArray(videoToCreateData.likes.orderedItems)) { | ||
55 | await createRates(videoToCreateData.likes.orderedItems, video, 'like') | ||
56 | } | ||
57 | |||
58 | if (videoToCreateData.dislikes && Array.isArray(videoToCreateData.dislikes.orderedItems)) { | ||
59 | await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike') | ||
60 | } | ||
61 | |||
62 | if (videoToCreateData.shares && Array.isArray(videoToCreateData.shares.orderedItems)) { | ||
63 | await addVideoShares(video, videoToCreateData.shares.orderedItems) | ||
64 | } | ||
65 | |||
66 | return video | ||
67 | } | ||
68 | |||
69 | function addRemoteVideo (account: AccountModel, | ||
70 | activity: ActivityAdd, | ||
71 | videoChannel: VideoChannelModel, | ||
72 | videoToCreateData: VideoTorrentObject) { | ||
73 | logger.debug('Adding remote video %s.', videoToCreateData.id) | ||
74 | |||
75 | return sequelizeTypescript.transaction(async t => { | ||
76 | const sequelizeOptions = { | ||
77 | transaction: t | ||
78 | } | ||
79 | |||
80 | if (videoChannel.Account.id !== account.id) throw new Error('Video channel is not owned by this account.') | ||
81 | |||
82 | const videoFromDatabase = await VideoModel.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t) | ||
83 | if (videoFromDatabase) return videoFromDatabase | ||
84 | |||
85 | const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoToCreateData, activity.to, activity.cc) | ||
86 | const video = VideoModel.build(videoData) | ||
87 | |||
88 | // Don't block on request | ||
89 | generateThumbnailFromUrl(video, videoToCreateData.icon) | ||
90 | .catch(err => logger.warn('Cannot generate thumbnail of %s.', videoToCreateData.id, err)) | ||
91 | |||
92 | const videoCreated = await video.save(sequelizeOptions) | ||
93 | |||
94 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoCreated, videoToCreateData) | ||
95 | if (videoFileAttributes.length === 0) { | ||
96 | throw new Error('Cannot find valid files for video %s ' + videoToCreateData.url) | ||
97 | } | ||
98 | |||
99 | const tasks: Bluebird<any>[] = videoFileAttributes.map(f => VideoFileModel.create(f, { transaction: t })) | ||
100 | await Promise.all(tasks) | ||
101 | |||
102 | const tags = videoToCreateData.tag.map(t => t.name) | ||
103 | const tagInstances = await TagModel.findOrCreateTags(tags, t) | ||
104 | await videoCreated.$set('Tags', tagInstances, sequelizeOptions) | ||
105 | |||
106 | logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid) | ||
107 | |||
108 | return videoCreated | ||
109 | }) | ||
110 | } | ||
111 | |||
112 | async function createRates (accountUrls: string[], video: VideoModel, rate: VideoRateType) { | ||
113 | let rateCounts = 0 | ||
114 | const tasks: Bluebird<any>[] = [] | ||
115 | |||
116 | for (const accountUrl of accountUrls) { | ||
117 | const account = await getOrCreateAccountAndServer(accountUrl) | ||
118 | const p = AccountVideoRateModel | ||
119 | .create({ | ||
120 | videoId: video.id, | ||
121 | accountId: account.id, | ||
122 | type: rate | ||
123 | }) | ||
124 | .then(() => rateCounts += 1) | ||
125 | |||
126 | tasks.push(p) | ||
127 | } | ||
128 | |||
129 | await Promise.all(tasks) | ||
130 | |||
131 | logger.info('Adding %d %s to video %s.', rateCounts, rate, video.uuid) | ||
132 | |||
133 | // This is "likes" and "dislikes" | ||
134 | await video.increment(rate + 's', { by: rateCounts }) | ||
135 | |||
136 | return | ||
137 | } | ||
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 @@ | |||
1 | import { ActivityAdd, ActivityAnnounce, ActivityCreate } from '../../../../shared/models/activitypub' | 1 | import { ActivityAnnounce } from '../../../../shared/models/activitypub' |
2 | import { logger, retryTransactionWrapper } from '../../../helpers' | 2 | import { logger, retryTransactionWrapper } from '../../../helpers' |
3 | import { sequelizeTypescript } from '../../../initializers' | 3 | import { sequelizeTypescript } from '../../../initializers' |
4 | import { AccountModel } from '../../../models/account/account' | 4 | import { ActorModel } from '../../../models/activitypub/actor' |
5 | import { VideoModel } from '../../../models/video/video' | 5 | import { VideoModel } from '../../../models/video/video' |
6 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
7 | import { VideoChannelShareModel } from '../../../models/video/video-channel-share' | ||
8 | import { VideoShareModel } from '../../../models/video/video-share' | 6 | import { VideoShareModel } from '../../../models/video/video-share' |
9 | import { getOrCreateAccountAndServer } from '../account' | 7 | import { getOrCreateActorAndServerAndModel } from '../actor' |
10 | import { forwardActivity } from '../send/misc' | 8 | import { forwardActivity } from '../send/misc' |
11 | import { processAddActivity } from './process-add' | ||
12 | import { processCreateActivity } from './process-create' | 9 | import { processCreateActivity } from './process-create' |
13 | 10 | ||
14 | async function processAnnounceActivity (activity: ActivityAnnounce) { | 11 | async function processAnnounceActivity (activity: ActivityAnnounce) { |
15 | const announcedActivity = activity.object | 12 | const announcedActivity = activity.object |
16 | const accountAnnouncer = await getOrCreateAccountAndServer(activity.actor) | 13 | const actorAnnouncer = await getOrCreateActorAndServerAndModel(activity.actor) |
17 | 14 | ||
18 | if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'VideoChannel') { | 15 | if (announcedActivity.type === 'Create' && announcedActivity.object.type === 'Video') { |
19 | return processVideoChannelShare(accountAnnouncer, activity) | 16 | return processVideoShare(actorAnnouncer, activity) |
20 | } else if (announcedActivity.type === 'Add' && announcedActivity.object.type === 'Video') { | ||
21 | return processVideoShare(accountAnnouncer, activity) | ||
22 | } | 17 | } |
23 | 18 | ||
24 | logger.warn( | 19 | logger.warn( |
@@ -37,60 +32,24 @@ export { | |||
37 | 32 | ||
38 | // --------------------------------------------------------------------------- | 33 | // --------------------------------------------------------------------------- |
39 | 34 | ||
40 | function processVideoChannelShare (accountAnnouncer: AccountModel, activity: ActivityAnnounce) { | 35 | function processVideoShare (actorAnnouncer: ActorModel, activity: ActivityAnnounce) { |
41 | const options = { | 36 | const options = { |
42 | arguments: [ accountAnnouncer, activity ], | 37 | arguments: [ actorAnnouncer, activity ], |
43 | errorMessage: 'Cannot share the video channel with many retries.' | ||
44 | } | ||
45 | |||
46 | return retryTransactionWrapper(shareVideoChannel, options) | ||
47 | } | ||
48 | |||
49 | async function shareVideoChannel (accountAnnouncer: AccountModel, activity: ActivityAnnounce) { | ||
50 | const announcedActivity = activity.object as ActivityCreate | ||
51 | |||
52 | return sequelizeTypescript.transaction(async t => { | ||
53 | // Add share entry | ||
54 | const videoChannel: VideoChannelModel = await processCreateActivity(announcedActivity) | ||
55 | const share = { | ||
56 | accountId: accountAnnouncer.id, | ||
57 | videoChannelId: videoChannel.id | ||
58 | } | ||
59 | |||
60 | const [ , created ] = await VideoChannelShareModel.findOrCreate({ | ||
61 | where: share, | ||
62 | defaults: share, | ||
63 | transaction: t | ||
64 | }) | ||
65 | |||
66 | if (videoChannel.isOwned() && created === true) { | ||
67 | // Don't resend the activity to the sender | ||
68 | const exceptions = [ accountAnnouncer ] | ||
69 | await forwardActivity(activity, t, exceptions) | ||
70 | } | ||
71 | |||
72 | return undefined | ||
73 | }) | ||
74 | } | ||
75 | |||
76 | function processVideoShare (accountAnnouncer: AccountModel, activity: ActivityAnnounce) { | ||
77 | const options = { | ||
78 | arguments: [ accountAnnouncer, activity ], | ||
79 | errorMessage: 'Cannot share the video with many retries.' | 38 | errorMessage: 'Cannot share the video with many retries.' |
80 | } | 39 | } |
81 | 40 | ||
82 | return retryTransactionWrapper(shareVideo, options) | 41 | return retryTransactionWrapper(shareVideo, options) |
83 | } | 42 | } |
84 | 43 | ||
85 | function shareVideo (accountAnnouncer: AccountModel, activity: ActivityAnnounce) { | 44 | function shareVideo (actorAnnouncer: ActorModel, activity: ActivityAnnounce) { |
86 | const announcedActivity = activity.object as ActivityAdd | 45 | const announcedActivity = activity.object |
87 | 46 | ||
88 | return sequelizeTypescript.transaction(async t => { | 47 | return sequelizeTypescript.transaction(async t => { |
89 | // Add share entry | 48 | // Add share entry |
90 | const video: VideoModel = await processAddActivity(announcedActivity) | 49 | const video: VideoModel = await processCreateActivity(announcedActivity) |
91 | 50 | ||
92 | const share = { | 51 | const share = { |
93 | accountId: accountAnnouncer.id, | 52 | actorId: actorAnnouncer.id, |
94 | videoId: video.id | 53 | videoId: video.id |
95 | } | 54 | } |
96 | 55 | ||
@@ -102,7 +61,7 @@ function shareVideo (accountAnnouncer: AccountModel, activity: ActivityAnnounce) | |||
102 | 61 | ||
103 | if (video.isOwned() && created === true) { | 62 | if (video.isOwned() && created === true) { |
104 | // Don't resend the activity to the sender | 63 | // Don't resend the activity to the sender |
105 | const exceptions = [ accountAnnouncer ] | 64 | const exceptions = [ actorAnnouncer ] |
106 | await forwardActivity(activity, t, exceptions) | 65 | await forwardActivity(activity, t, exceptions) |
107 | } | 66 | } |
108 | 67 | ||
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 @@ | |||
1 | import { ActivityCreate, VideoChannelObject } from '../../../../shared' | 1 | import * as Bluebird from 'bluebird' |
2 | import { ActivityCreate, VideoTorrentObject } from '../../../../shared' | ||
2 | import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects' | 3 | import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects' |
4 | import { VideoRateType } from '../../../../shared/models/videos' | ||
3 | import { logger, retryTransactionWrapper } from '../../../helpers' | 5 | import { logger, retryTransactionWrapper } from '../../../helpers' |
4 | import { sequelizeTypescript } from '../../../initializers' | 6 | import { sequelizeTypescript } from '../../../initializers' |
5 | import { AccountModel } from '../../../models/account/account' | ||
6 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 7 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
8 | import { ActorModel } from '../../../models/activitypub/actor' | ||
9 | import { TagModel } from '../../../models/video/tag' | ||
7 | import { VideoModel } from '../../../models/video/video' | 10 | import { VideoModel } from '../../../models/video/video' |
8 | import { VideoAbuseModel } from '../../../models/video/video-abuse' | 11 | import { VideoAbuseModel } from '../../../models/video/video-abuse' |
9 | import { VideoChannelModel } from '../../../models/video/video-channel' | 12 | import { VideoFileModel } from '../../../models/video/video-file' |
10 | import { getOrCreateAccountAndServer } from '../account' | 13 | import { getOrCreateActorAndServerAndModel } from '../actor' |
11 | import { forwardActivity } from '../send/misc' | 14 | import { forwardActivity } from '../send/misc' |
12 | import { getVideoChannelActivityPubUrl } from '../url' | 15 | import { generateThumbnailFromUrl } from '../videos' |
13 | import { addVideoChannelShares, videoChannelActivityObjectToDBAttributes } from './misc' | 16 | import { addVideoShares, videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' |
14 | 17 | ||
15 | async function processCreateActivity (activity: ActivityCreate) { | 18 | async function processCreateActivity (activity: ActivityCreate) { |
16 | const activityObject = activity.object | 19 | const activityObject = activity.object |
17 | const activityType = activityObject.type | 20 | const activityType = activityObject.type |
18 | const account = await getOrCreateAccountAndServer(activity.actor) | 21 | const actor = await getOrCreateActorAndServerAndModel(activity.actor) |
19 | 22 | ||
20 | if (activityType === 'View') { | 23 | if (activityType === 'View') { |
21 | return processCreateView(account, activity) | 24 | return processCreateView(actor, activity) |
22 | } else if (activityType === 'Dislike') { | 25 | } else if (activityType === 'Dislike') { |
23 | return processCreateDislike(account, activity) | 26 | return processCreateDislike(actor, activity) |
24 | } else if (activityType === 'VideoChannel') { | 27 | } else if (activityType === 'Video') { |
25 | return processCreateVideoChannel(account, activityObject as VideoChannelObject) | 28 | return processCreateVideo(actor, activity) |
26 | } else if (activityType === 'Flag') { | 29 | } else if (activityType === 'Flag') { |
27 | return processCreateVideoAbuse(account, activityObject as VideoAbuseObject) | 30 | return processCreateVideoAbuse(actor, activityObject as VideoAbuseObject) |
28 | } | 31 | } |
29 | 32 | ||
30 | logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) | 33 | logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) |
@@ -39,17 +42,123 @@ export { | |||
39 | 42 | ||
40 | // --------------------------------------------------------------------------- | 43 | // --------------------------------------------------------------------------- |
41 | 44 | ||
42 | async function processCreateDislike (byAccount: AccountModel, activity: ActivityCreate) { | 45 | async function processCreateVideo ( |
46 | actor: ActorModel, | ||
47 | activity: ActivityCreate | ||
48 | ) { | ||
49 | const videoToCreateData = activity.object as VideoTorrentObject | ||
50 | |||
51 | const channel = videoToCreateData.attributedTo.find(a => a.type === 'Group') | ||
52 | if (!channel) throw new Error('Cannot find associated video channel to video ' + videoToCreateData.url) | ||
53 | |||
54 | const channelActor = await getOrCreateActorAndServerAndModel(channel.id) | ||
55 | |||
56 | const options = { | ||
57 | arguments: [ actor, activity, videoToCreateData, channelActor ], | ||
58 | errorMessage: 'Cannot insert the remote video with many retries.' | ||
59 | } | ||
60 | |||
61 | const video = await retryTransactionWrapper(createRemoteVideo, options) | ||
62 | |||
63 | // Process outside the transaction because we could fetch remote data | ||
64 | if (videoToCreateData.likes && Array.isArray(videoToCreateData.likes.orderedItems)) { | ||
65 | await createRates(videoToCreateData.likes.orderedItems, video, 'like') | ||
66 | } | ||
67 | |||
68 | if (videoToCreateData.dislikes && Array.isArray(videoToCreateData.dislikes.orderedItems)) { | ||
69 | await createRates(videoToCreateData.dislikes.orderedItems, video, 'dislike') | ||
70 | } | ||
71 | |||
72 | if (videoToCreateData.shares && Array.isArray(videoToCreateData.shares.orderedItems)) { | ||
73 | await addVideoShares(video, videoToCreateData.shares.orderedItems) | ||
74 | } | ||
75 | |||
76 | return video | ||
77 | } | ||
78 | |||
79 | function createRemoteVideo ( | ||
80 | account: ActorModel, | ||
81 | activity: ActivityCreate, | ||
82 | videoToCreateData: VideoTorrentObject, | ||
83 | channelActor: ActorModel | ||
84 | ) { | ||
85 | logger.debug('Adding remote video %s.', videoToCreateData.id) | ||
86 | |||
87 | return sequelizeTypescript.transaction(async t => { | ||
88 | const sequelizeOptions = { | ||
89 | transaction: t | ||
90 | } | ||
91 | const videoFromDatabase = await VideoModel.loadByUUIDOrURL(videoToCreateData.uuid, videoToCreateData.id, t) | ||
92 | if (videoFromDatabase) return videoFromDatabase | ||
93 | |||
94 | const videoData = await videoActivityObjectToDBAttributes(channelActor.VideoChannel, videoToCreateData, activity.to, activity.cc) | ||
95 | const video = VideoModel.build(videoData) | ||
96 | |||
97 | // Don't block on request | ||
98 | generateThumbnailFromUrl(video, videoToCreateData.icon) | ||
99 | .catch(err => logger.warn('Cannot generate thumbnail of %s.', videoToCreateData.id, err)) | ||
100 | |||
101 | const videoCreated = await video.save(sequelizeOptions) | ||
102 | |||
103 | const videoFileAttributes = videoFileActivityUrlToDBAttributes(videoCreated, videoToCreateData) | ||
104 | if (videoFileAttributes.length === 0) { | ||
105 | throw new Error('Cannot find valid files for video %s ' + videoToCreateData.url) | ||
106 | } | ||
107 | |||
108 | const tasks: Bluebird<any>[] = videoFileAttributes.map(f => VideoFileModel.create(f, { transaction: t })) | ||
109 | await Promise.all(tasks) | ||
110 | |||
111 | const tags = videoToCreateData.tag.map(t => t.name) | ||
112 | const tagInstances = await TagModel.findOrCreateTags(tags, t) | ||
113 | await videoCreated.$set('Tags', tagInstances, sequelizeOptions) | ||
114 | |||
115 | logger.info('Remote video with uuid %s inserted.', videoToCreateData.uuid) | ||
116 | |||
117 | return videoCreated | ||
118 | }) | ||
119 | } | ||
120 | |||
121 | async function createRates (actorUrls: string[], video: VideoModel, rate: VideoRateType) { | ||
122 | let rateCounts = 0 | ||
123 | const tasks: Bluebird<any>[] = [] | ||
124 | |||
125 | for (const actorUrl of actorUrls) { | ||
126 | const actor = await getOrCreateActorAndServerAndModel(actorUrl) | ||
127 | const p = AccountVideoRateModel | ||
128 | .create({ | ||
129 | videoId: video.id, | ||
130 | accountId: actor.Account.id, | ||
131 | type: rate | ||
132 | }) | ||
133 | .then(() => rateCounts += 1) | ||
134 | |||
135 | tasks.push(p) | ||
136 | } | ||
137 | |||
138 | await Promise.all(tasks) | ||
139 | |||
140 | logger.info('Adding %d %s to video %s.', rateCounts, rate, video.uuid) | ||
141 | |||
142 | // This is "likes" and "dislikes" | ||
143 | await video.increment(rate + 's', { by: rateCounts }) | ||
144 | |||
145 | return | ||
146 | } | ||
147 | |||
148 | async function processCreateDislike (byActor: ActorModel, activity: ActivityCreate) { | ||
43 | const options = { | 149 | const options = { |
44 | arguments: [ byAccount, activity ], | 150 | arguments: [ byActor, activity ], |
45 | errorMessage: 'Cannot dislike the video with many retries.' | 151 | errorMessage: 'Cannot dislike the video with many retries.' |
46 | } | 152 | } |
47 | 153 | ||
48 | return retryTransactionWrapper(createVideoDislike, options) | 154 | return retryTransactionWrapper(createVideoDislike, options) |
49 | } | 155 | } |
50 | 156 | ||
51 | function createVideoDislike (byAccount: AccountModel, activity: ActivityCreate) { | 157 | function createVideoDislike (byActor: ActorModel, activity: ActivityCreate) { |
52 | const dislike = activity.object as DislikeObject | 158 | const dislike = activity.object as DislikeObject |
159 | const byAccount = byActor.Account | ||
160 | |||
161 | if (!byAccount) throw new Error('Cannot create dislike with the non account actor ' + byActor.url) | ||
53 | 162 | ||
54 | return sequelizeTypescript.transaction(async t => { | 163 | return sequelizeTypescript.transaction(async t => { |
55 | const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t) | 164 | const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t) |
@@ -69,20 +178,20 @@ function createVideoDislike (byAccount: AccountModel, activity: ActivityCreate) | |||
69 | 178 | ||
70 | if (video.isOwned() && created === true) { | 179 | if (video.isOwned() && created === true) { |
71 | // Don't resend the activity to the sender | 180 | // Don't resend the activity to the sender |
72 | const exceptions = [ byAccount ] | 181 | const exceptions = [ byActor ] |
73 | await forwardActivity(activity, t, exceptions) | 182 | await forwardActivity(activity, t, exceptions) |
74 | } | 183 | } |
75 | }) | 184 | }) |
76 | } | 185 | } |
77 | 186 | ||
78 | async function processCreateView (byAccount: AccountModel, activity: ActivityCreate) { | 187 | async function processCreateView (byAccount: ActorModel, activity: ActivityCreate) { |
79 | const view = activity.object as ViewObject | 188 | const view = activity.object as ViewObject |
80 | 189 | ||
81 | const video = await VideoModel.loadByUrlAndPopulateAccount(view.object) | 190 | const video = await VideoModel.loadByUrlAndPopulateAccount(view.object) |
82 | 191 | ||
83 | if (!video) throw new Error('Unknown video ' + view.object) | 192 | if (!video) throw new Error('Unknown video ' + view.object) |
84 | 193 | ||
85 | const account = await AccountModel.loadByUrl(view.actor) | 194 | const account = await ActorModel.loadByUrl(view.actor) |
86 | if (!account) throw new Error('Unknown account ' + view.actor) | 195 | if (!account) throw new Error('Unknown account ' + view.actor) |
87 | 196 | ||
88 | await video.increment('views') | 197 | await video.increment('views') |
@@ -94,51 +203,21 @@ async function processCreateView (byAccount: AccountModel, activity: ActivityCre | |||
94 | } | 203 | } |
95 | } | 204 | } |
96 | 205 | ||
97 | async function processCreateVideoChannel (account: AccountModel, videoChannelToCreateData: VideoChannelObject) { | 206 | function processCreateVideoAbuse (actor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) { |
98 | const options = { | ||
99 | arguments: [ account, videoChannelToCreateData ], | ||
100 | errorMessage: 'Cannot insert the remote video channel with many retries.' | ||
101 | } | ||
102 | |||
103 | const videoChannel = await retryTransactionWrapper(addRemoteVideoChannel, options) | ||
104 | |||
105 | if (videoChannelToCreateData.shares && Array.isArray(videoChannelToCreateData.shares.orderedItems)) { | ||
106 | await addVideoChannelShares(videoChannel, videoChannelToCreateData.shares.orderedItems) | ||
107 | } | ||
108 | |||
109 | return videoChannel | ||
110 | } | ||
111 | |||
112 | function addRemoteVideoChannel (account: AccountModel, videoChannelToCreateData: VideoChannelObject) { | ||
113 | logger.debug('Adding remote video channel "%s".', videoChannelToCreateData.uuid) | ||
114 | |||
115 | return sequelizeTypescript.transaction(async t => { | ||
116 | let videoChannel = await VideoChannelModel.loadByUUIDOrUrl(videoChannelToCreateData.uuid, videoChannelToCreateData.id, t) | ||
117 | if (videoChannel) return videoChannel | ||
118 | |||
119 | const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account) | ||
120 | videoChannel = new VideoChannelModel(videoChannelData) | ||
121 | videoChannel.url = getVideoChannelActivityPubUrl(videoChannel) | ||
122 | |||
123 | videoChannel = await videoChannel.save({ transaction: t }) | ||
124 | logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid) | ||
125 | |||
126 | return videoChannel | ||
127 | }) | ||
128 | } | ||
129 | |||
130 | function processCreateVideoAbuse (account: AccountModel, videoAbuseToCreateData: VideoAbuseObject) { | ||
131 | const options = { | 207 | const options = { |
132 | arguments: [ account, videoAbuseToCreateData ], | 208 | arguments: [ actor, videoAbuseToCreateData ], |
133 | errorMessage: 'Cannot insert the remote video abuse with many retries.' | 209 | errorMessage: 'Cannot insert the remote video abuse with many retries.' |
134 | } | 210 | } |
135 | 211 | ||
136 | return retryTransactionWrapper(addRemoteVideoAbuse, options) | 212 | return retryTransactionWrapper(addRemoteVideoAbuse, options) |
137 | } | 213 | } |
138 | 214 | ||
139 | function addRemoteVideoAbuse (account: AccountModel, videoAbuseToCreateData: VideoAbuseObject) { | 215 | function addRemoteVideoAbuse (actor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) { |
140 | logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object) | 216 | logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object) |
141 | 217 | ||
218 | const account = actor.Account | ||
219 | if (!account) throw new Error('Cannot create dislike with the non account actor ' + actor.url) | ||
220 | |||
142 | return sequelizeTypescript.transaction(async t => { | 221 | return sequelizeTypescript.transaction(async t => { |
143 | const video = await VideoModel.loadByUrlAndPopulateAccount(videoAbuseToCreateData.object, t) | 222 | const video = await VideoModel.loadByUrlAndPopulateAccount(videoAbuseToCreateData.object, t) |
144 | if (!video) { | 223 | 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' | |||
2 | import { logger, retryTransactionWrapper } from '../../../helpers' | 2 | import { logger, retryTransactionWrapper } from '../../../helpers' |
3 | import { sequelizeTypescript } from '../../../initializers' | 3 | import { sequelizeTypescript } from '../../../initializers' |
4 | import { AccountModel } from '../../../models/account/account' | 4 | import { AccountModel } from '../../../models/account/account' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | ||
5 | import { VideoModel } from '../../../models/video/video' | 6 | import { VideoModel } from '../../../models/video/video' |
6 | import { VideoChannelModel } from '../../../models/video/video-channel' | 7 | import { VideoChannelModel } from '../../../models/video/video-channel' |
7 | import { getOrCreateAccountAndServer } from '../account' | 8 | import { getOrCreateActorAndServerAndModel } from '../actor' |
8 | 9 | ||
9 | async function processDeleteActivity (activity: ActivityDelete) { | 10 | async function processDeleteActivity (activity: ActivityDelete) { |
10 | const account = await getOrCreateAccountAndServer(activity.actor) | 11 | const actor = await getOrCreateActorAndServerAndModel(activity.actor) |
11 | 12 | ||
12 | if (account.url === activity.id) { | 13 | if (actor.url === activity.id) { |
13 | return processDeleteAccount(account) | 14 | if (actor.type === 'Person') { |
14 | } | 15 | if (!actor.Account) throw new Error('Actor ' + actor.url + ' is a person but we cannot find it in database.') |
15 | 16 | ||
16 | { | 17 | return processDeleteAccount(actor.Account) |
17 | let videoObject = await VideoModel.loadByUrlAndPopulateAccount(activity.id) | 18 | } else if (actor.type === 'Group') { |
18 | if (videoObject !== undefined) { | 19 | if (!actor.VideoChannel) throw new Error('Actor ' + actor.url + ' is a group but we cannot find it in database.') |
19 | return processDeleteVideo(account, videoObject) | 20 | |
21 | return processDeleteVideoChannel(actor.VideoChannel) | ||
20 | } | 22 | } |
21 | } | 23 | } |
22 | 24 | ||
23 | { | 25 | { |
24 | let videoChannelObject = await VideoChannelModel.loadByUrl(activity.id) | 26 | let videoObject = await VideoModel.loadByUrlAndPopulateAccount(activity.id) |
25 | if (videoChannelObject !== undefined) { | 27 | if (videoObject !== undefined) { |
26 | return processDeleteVideoChannel(account, videoChannelObject) | 28 | return processDeleteVideo(actor, videoObject) |
27 | } | 29 | } |
28 | } | 30 | } |
29 | 31 | ||
@@ -38,21 +40,21 @@ export { | |||
38 | 40 | ||
39 | // --------------------------------------------------------------------------- | 41 | // --------------------------------------------------------------------------- |
40 | 42 | ||
41 | async function processDeleteVideo (account: AccountModel, videoToDelete: VideoModel) { | 43 | async function processDeleteVideo (actor: ActorModel, videoToDelete: VideoModel) { |
42 | const options = { | 44 | const options = { |
43 | arguments: [ account, videoToDelete ], | 45 | arguments: [ actor, videoToDelete ], |
44 | errorMessage: 'Cannot remove the remote video with many retries.' | 46 | errorMessage: 'Cannot remove the remote video with many retries.' |
45 | } | 47 | } |
46 | 48 | ||
47 | await retryTransactionWrapper(deleteRemoteVideo, options) | 49 | await retryTransactionWrapper(deleteRemoteVideo, options) |
48 | } | 50 | } |
49 | 51 | ||
50 | async function deleteRemoteVideo (account: AccountModel, videoToDelete: VideoModel) { | 52 | async function deleteRemoteVideo (actor: ActorModel, videoToDelete: VideoModel) { |
51 | logger.debug('Removing remote video "%s".', videoToDelete.uuid) | 53 | logger.debug('Removing remote video "%s".', videoToDelete.uuid) |
52 | 54 | ||
53 | await sequelizeTypescript.transaction(async t => { | 55 | await sequelizeTypescript.transaction(async t => { |
54 | if (videoToDelete.VideoChannel.Account.id !== account.id) { | 56 | if (videoToDelete.VideoChannel.Account.Actor.id !== actor.id) { |
55 | throw new Error('Account ' + account.url + ' does not own video channel ' + videoToDelete.VideoChannel.url) | 57 | throw new Error('Account ' + actor.url + ' does not own video channel ' + videoToDelete.VideoChannel.Actor.url) |
56 | } | 58 | } |
57 | 59 | ||
58 | await videoToDelete.destroy({ transaction: t }) | 60 | await videoToDelete.destroy({ transaction: t }) |
@@ -61,44 +63,40 @@ async function deleteRemoteVideo (account: AccountModel, videoToDelete: VideoMod | |||
61 | logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) | 63 | logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) |
62 | } | 64 | } |
63 | 65 | ||
64 | async function processDeleteVideoChannel (account: AccountModel, videoChannelToRemove: VideoChannelModel) { | 66 | async function processDeleteAccount (accountToRemove: AccountModel) { |
65 | const options = { | 67 | const options = { |
66 | arguments: [ account, videoChannelToRemove ], | 68 | arguments: [ accountToRemove ], |
67 | errorMessage: 'Cannot remove the remote video channel with many retries.' | 69 | errorMessage: 'Cannot remove the remote account with many retries.' |
68 | } | 70 | } |
69 | 71 | ||
70 | await retryTransactionWrapper(deleteRemoteVideoChannel, options) | 72 | await retryTransactionWrapper(deleteRemoteAccount, options) |
71 | } | 73 | } |
72 | 74 | ||
73 | async function deleteRemoteVideoChannel (account: AccountModel, videoChannelToRemove: VideoChannelModel) { | 75 | async function deleteRemoteAccount (accountToRemove: AccountModel) { |
74 | logger.debug('Removing remote video channel "%s".', videoChannelToRemove.uuid) | 76 | logger.debug('Removing remote account "%s".', accountToRemove.Actor.uuid) |
75 | 77 | ||
76 | await sequelizeTypescript.transaction(async t => { | 78 | await sequelizeTypescript.transaction(async t => { |
77 | if (videoChannelToRemove.Account.id !== account.id) { | 79 | await accountToRemove.destroy({ transaction: t }) |
78 | throw new Error('Account ' + account.url + ' does not own video channel ' + videoChannelToRemove.url) | ||
79 | } | ||
80 | |||
81 | await videoChannelToRemove.destroy({ transaction: t }) | ||
82 | }) | 80 | }) |
83 | 81 | ||
84 | logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.uuid) | 82 | logger.info('Remote account with uuid %s removed.', accountToRemove.Actor.uuid) |
85 | } | 83 | } |
86 | 84 | ||
87 | async function processDeleteAccount (accountToRemove: AccountModel) { | 85 | async function processDeleteVideoChannel (videoChannelToRemove: VideoChannelModel) { |
88 | const options = { | 86 | const options = { |
89 | arguments: [ accountToRemove ], | 87 | arguments: [ videoChannelToRemove ], |
90 | errorMessage: 'Cannot remove the remote account with many retries.' | 88 | errorMessage: 'Cannot remove the remote video channel with many retries.' |
91 | } | 89 | } |
92 | 90 | ||
93 | await retryTransactionWrapper(deleteRemoteAccount, options) | 91 | await retryTransactionWrapper(deleteRemoteVideoChannel, options) |
94 | } | 92 | } |
95 | 93 | ||
96 | async function deleteRemoteAccount (accountToRemove: AccountModel) { | 94 | async function deleteRemoteVideoChannel (videoChannelToRemove: VideoChannelModel) { |
97 | logger.debug('Removing remote account "%s".', accountToRemove.uuid) | 95 | logger.debug('Removing remote video channel "%s".', videoChannelToRemove.Actor.uuid) |
98 | 96 | ||
99 | await sequelizeTypescript.transaction(async t => { | 97 | await sequelizeTypescript.transaction(async t => { |
100 | await accountToRemove.destroy({ transaction: t }) | 98 | await videoChannelToRemove.destroy({ transaction: t }) |
101 | }) | 99 | }) |
102 | 100 | ||
103 | logger.info('Remote account with uuid %s removed.', accountToRemove.uuid) | 101 | logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.Actor.uuid) |
104 | } | 102 | } |
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 @@ | |||
1 | import { ActivityFollow } from '../../../../shared/models/activitypub' | 1 | import { ActivityFollow } from '../../../../shared/models/activitypub' |
2 | import { logger, retryTransactionWrapper } from '../../../helpers' | 2 | import { logger, retryTransactionWrapper } from '../../../helpers' |
3 | import { sequelizeTypescript } from '../../../initializers' | 3 | import { sequelizeTypescript } from '../../../initializers' |
4 | import { AccountModel } from '../../../models/account/account' | 4 | import { ActorModel } from '../../../models/activitypub/actor' |
5 | import { AccountFollowModel } from '../../../models/account/account-follow' | 5 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
6 | import { getOrCreateAccountAndServer } from '../account' | 6 | import { getOrCreateActorAndServerAndModel } from '../actor' |
7 | import { sendAccept } from '../send' | 7 | import { sendAccept } from '../send' |
8 | 8 | ||
9 | async function processFollowActivity (activity: ActivityFollow) { | 9 | async function processFollowActivity (activity: ActivityFollow) { |
10 | const activityObject = activity.object | 10 | const activityObject = activity.object |
11 | const account = await getOrCreateAccountAndServer(activity.actor) | 11 | const actor = await getOrCreateActorAndServerAndModel(activity.actor) |
12 | 12 | ||
13 | return processFollow(account, activityObject) | 13 | return processFollow(actor, activityObject) |
14 | } | 14 | } |
15 | 15 | ||
16 | // --------------------------------------------------------------------------- | 16 | // --------------------------------------------------------------------------- |
@@ -21,46 +21,46 @@ export { | |||
21 | 21 | ||
22 | // --------------------------------------------------------------------------- | 22 | // --------------------------------------------------------------------------- |
23 | 23 | ||
24 | function processFollow (account: AccountModel, targetAccountURL: string) { | 24 | function processFollow (actor: ActorModel, targetActorURL: string) { |
25 | const options = { | 25 | const options = { |
26 | arguments: [ account, targetAccountURL ], | 26 | arguments: [ actor, targetActorURL ], |
27 | errorMessage: 'Cannot follow with many retries.' | 27 | errorMessage: 'Cannot follow with many retries.' |
28 | } | 28 | } |
29 | 29 | ||
30 | return retryTransactionWrapper(follow, options) | 30 | return retryTransactionWrapper(follow, options) |
31 | } | 31 | } |
32 | 32 | ||
33 | async function follow (account: AccountModel, targetAccountURL: string) { | 33 | async function follow (actor: ActorModel, targetActorURL: string) { |
34 | await sequelizeTypescript.transaction(async t => { | 34 | await sequelizeTypescript.transaction(async t => { |
35 | const targetAccount = await AccountModel.loadByUrl(targetAccountURL, t) | 35 | const targetActor = await ActorModel.loadByUrl(targetActorURL, t) |
36 | 36 | ||
37 | if (!targetAccount) throw new Error('Unknown account') | 37 | if (!targetActor) throw new Error('Unknown actor') |
38 | if (targetAccount.isOwned() === false) throw new Error('This is not a local account.') | 38 | if (targetActor.isOwned() === false) throw new Error('This is not a local actor.') |
39 | 39 | ||
40 | const [ accountFollow ] = await AccountFollowModel.findOrCreate({ | 40 | const [ actorFollow ] = await ActorFollowModel.findOrCreate({ |
41 | where: { | 41 | where: { |
42 | accountId: account.id, | 42 | actorId: actor.id, |
43 | targetAccountId: targetAccount.id | 43 | targetActorId: targetActor.id |
44 | }, | 44 | }, |
45 | defaults: { | 45 | defaults: { |
46 | accountId: account.id, | 46 | actorId: actor.id, |
47 | targetAccountId: targetAccount.id, | 47 | targetActorId: targetActor.id, |
48 | state: 'accepted' | 48 | state: 'accepted' |
49 | }, | 49 | }, |
50 | transaction: t | 50 | transaction: t |
51 | }) | 51 | }) |
52 | 52 | ||
53 | if (accountFollow.state !== 'accepted') { | 53 | if (actorFollow.state !== 'accepted') { |
54 | accountFollow.state = 'accepted' | 54 | actorFollow.state = 'accepted' |
55 | await accountFollow.save({ transaction: t }) | 55 | await actorFollow.save({ transaction: t }) |
56 | } | 56 | } |
57 | 57 | ||
58 | accountFollow.AccountFollower = account | 58 | actorFollow.ActorFollower = actor |
59 | accountFollow.AccountFollowing = targetAccount | 59 | actorFollow.ActorFollowing = targetActor |
60 | 60 | ||
61 | // Target sends to account he accepted the follow request | 61 | // Target sends to actor he accepted the follow request |
62 | return sendAccept(accountFollow, t) | 62 | return sendAccept(actorFollow, t) |
63 | }) | 63 | }) |
64 | 64 | ||
65 | logger.info('Account uuid %s is followed by account %s.', account.url, targetAccountURL) | 65 | logger.info('Actor uuid %s is followed by actor %s.', actor.url, targetActorURL) |
66 | } | 66 | } |
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 @@ | |||
1 | import { ActivityLike } from '../../../../shared/models/activitypub' | 1 | import { ActivityLike } from '../../../../shared/models/activitypub' |
2 | import { retryTransactionWrapper } from '../../../helpers' | 2 | import { retryTransactionWrapper } from '../../../helpers' |
3 | import { sequelizeTypescript } from '../../../initializers' | 3 | import { sequelizeTypescript } from '../../../initializers' |
4 | import { AccountModel } from '../../../models/account/account' | ||
5 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 4 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | ||
6 | import { VideoModel } from '../../../models/video/video' | 6 | import { VideoModel } from '../../../models/video/video' |
7 | import { getOrCreateAccountAndServer } from '../account' | 7 | import { getOrCreateActorAndServerAndModel } from '../actor' |
8 | import { forwardActivity } from '../send/misc' | 8 | import { forwardActivity } from '../send/misc' |
9 | 9 | ||
10 | async function processLikeActivity (activity: ActivityLike) { | 10 | async function processLikeActivity (activity: ActivityLike) { |
11 | const account = await getOrCreateAccountAndServer(activity.actor) | 11 | const actor = await getOrCreateActorAndServerAndModel(activity.actor) |
12 | 12 | ||
13 | return processLikeVideo(account, activity) | 13 | return processLikeVideo(actor, activity) |
14 | } | 14 | } |
15 | 15 | ||
16 | // --------------------------------------------------------------------------- | 16 | // --------------------------------------------------------------------------- |
@@ -21,18 +21,21 @@ export { | |||
21 | 21 | ||
22 | // --------------------------------------------------------------------------- | 22 | // --------------------------------------------------------------------------- |
23 | 23 | ||
24 | async function processLikeVideo (byAccount: AccountModel, activity: ActivityLike) { | 24 | async function processLikeVideo (actor: ActorModel, activity: ActivityLike) { |
25 | const options = { | 25 | const options = { |
26 | arguments: [ byAccount, activity ], | 26 | arguments: [ actor, activity ], |
27 | errorMessage: 'Cannot like the video with many retries.' | 27 | errorMessage: 'Cannot like the video with many retries.' |
28 | } | 28 | } |
29 | 29 | ||
30 | return retryTransactionWrapper(createVideoLike, options) | 30 | return retryTransactionWrapper(createVideoLike, options) |
31 | } | 31 | } |
32 | 32 | ||
33 | function createVideoLike (byAccount: AccountModel, activity: ActivityLike) { | 33 | function createVideoLike (byActor: ActorModel, activity: ActivityLike) { |
34 | const videoUrl = activity.object | 34 | const videoUrl = activity.object |
35 | 35 | ||
36 | const byAccount = byActor.Account | ||
37 | if (!byAccount) throw new Error('Cannot create like with the non account actor ' + byActor.url) | ||
38 | |||
36 | return sequelizeTypescript.transaction(async t => { | 39 | return sequelizeTypescript.transaction(async t => { |
37 | const video = await VideoModel.loadByUrlAndPopulateAccount(videoUrl) | 40 | const video = await VideoModel.loadByUrlAndPopulateAccount(videoUrl) |
38 | 41 | ||
@@ -52,7 +55,7 @@ function createVideoLike (byAccount: AccountModel, activity: ActivityLike) { | |||
52 | 55 | ||
53 | if (video.isOwned() && created === true) { | 56 | if (video.isOwned() && created === true) { |
54 | // Don't resend the activity to the sender | 57 | // Don't resend the activity to the sender |
55 | const exceptions = [ byAccount ] | 58 | const exceptions = [ byActor ] |
56 | await forwardActivity(activity, t, exceptions) | 59 | await forwardActivity(activity, t, exceptions) |
57 | } | 60 | } |
58 | }) | 61 | }) |
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' | |||
3 | import { logger, retryTransactionWrapper } from '../../../helpers' | 3 | import { logger, retryTransactionWrapper } from '../../../helpers' |
4 | import { sequelizeTypescript } from '../../../initializers' | 4 | import { sequelizeTypescript } from '../../../initializers' |
5 | import { AccountModel } from '../../../models/account/account' | 5 | import { AccountModel } from '../../../models/account/account' |
6 | import { AccountFollowModel } from '../../../models/account/account-follow' | ||
7 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 6 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
7 | import { ActorModel } from '../../../models/activitypub/actor' | ||
8 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | ||
8 | import { VideoModel } from '../../../models/video/video' | 9 | import { VideoModel } from '../../../models/video/video' |
9 | import { forwardActivity } from '../send/misc' | 10 | import { forwardActivity } from '../send/misc' |
10 | 11 | ||
@@ -32,21 +33,21 @@ export { | |||
32 | 33 | ||
33 | // --------------------------------------------------------------------------- | 34 | // --------------------------------------------------------------------------- |
34 | 35 | ||
35 | function processUndoLike (actor: string, activity: ActivityUndo) { | 36 | function processUndoLike (actorUrl: string, activity: ActivityUndo) { |
36 | const options = { | 37 | const options = { |
37 | arguments: [ actor, activity ], | 38 | arguments: [ actorUrl, activity ], |
38 | errorMessage: 'Cannot undo like with many retries.' | 39 | errorMessage: 'Cannot undo like with many retries.' |
39 | } | 40 | } |
40 | 41 | ||
41 | return retryTransactionWrapper(undoLike, options) | 42 | return retryTransactionWrapper(undoLike, options) |
42 | } | 43 | } |
43 | 44 | ||
44 | function undoLike (actor: string, activity: ActivityUndo) { | 45 | function undoLike (actorUrl: string, activity: ActivityUndo) { |
45 | const likeActivity = activity.object as ActivityLike | 46 | const likeActivity = activity.object as ActivityLike |
46 | 47 | ||
47 | return sequelizeTypescript.transaction(async t => { | 48 | return sequelizeTypescript.transaction(async t => { |
48 | const byAccount = await AccountModel.loadByUrl(actor, t) | 49 | const byAccount = await AccountModel.loadByUrl(actorUrl, t) |
49 | if (!byAccount) throw new Error('Unknown account ' + actor) | 50 | if (!byAccount) throw new Error('Unknown account ' + actorUrl) |
50 | 51 | ||
51 | const video = await VideoModel.loadByUrlAndPopulateAccount(likeActivity.object, t) | 52 | const video = await VideoModel.loadByUrlAndPopulateAccount(likeActivity.object, t) |
52 | if (!video) throw new Error('Unknown video ' + likeActivity.actor) | 53 | if (!video) throw new Error('Unknown video ' + likeActivity.actor) |
@@ -59,27 +60,27 @@ function undoLike (actor: string, activity: ActivityUndo) { | |||
59 | 60 | ||
60 | if (video.isOwned()) { | 61 | if (video.isOwned()) { |
61 | // Don't resend the activity to the sender | 62 | // Don't resend the activity to the sender |
62 | const exceptions = [ byAccount ] | 63 | const exceptions = [ byAccount.Actor ] |
63 | await forwardActivity(activity, t, exceptions) | 64 | await forwardActivity(activity, t, exceptions) |
64 | } | 65 | } |
65 | }) | 66 | }) |
66 | } | 67 | } |
67 | 68 | ||
68 | function processUndoDislike (actor: string, activity: ActivityUndo) { | 69 | function processUndoDislike (actorUrl: string, activity: ActivityUndo) { |
69 | const options = { | 70 | const options = { |
70 | arguments: [ actor, activity ], | 71 | arguments: [ actorUrl, activity ], |
71 | errorMessage: 'Cannot undo dislike with many retries.' | 72 | errorMessage: 'Cannot undo dislike with many retries.' |
72 | } | 73 | } |
73 | 74 | ||
74 | return retryTransactionWrapper(undoDislike, options) | 75 | return retryTransactionWrapper(undoDislike, options) |
75 | } | 76 | } |
76 | 77 | ||
77 | function undoDislike (actor: string, activity: ActivityUndo) { | 78 | function undoDislike (actorUrl: string, activity: ActivityUndo) { |
78 | const dislike = activity.object.object as DislikeObject | 79 | const dislike = activity.object.object as DislikeObject |
79 | 80 | ||
80 | return sequelizeTypescript.transaction(async t => { | 81 | return sequelizeTypescript.transaction(async t => { |
81 | const byAccount = await AccountModel.loadByUrl(actor, t) | 82 | const byAccount = await AccountModel.loadByUrl(actorUrl, t) |
82 | if (!byAccount) throw new Error('Unknown account ' + actor) | 83 | if (!byAccount) throw new Error('Unknown account ' + actorUrl) |
83 | 84 | ||
84 | const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t) | 85 | const video = await VideoModel.loadByUrlAndPopulateAccount(dislike.object, t) |
85 | if (!video) throw new Error('Unknown video ' + dislike.actor) | 86 | if (!video) throw new Error('Unknown video ' + dislike.actor) |
@@ -92,30 +93,30 @@ function undoDislike (actor: string, activity: ActivityUndo) { | |||
92 | 93 | ||
93 | if (video.isOwned()) { | 94 | if (video.isOwned()) { |
94 | // Don't resend the activity to the sender | 95 | // Don't resend the activity to the sender |
95 | const exceptions = [ byAccount ] | 96 | const exceptions = [ byAccount.Actor ] |
96 | await forwardActivity(activity, t, exceptions) | 97 | await forwardActivity(activity, t, exceptions) |
97 | } | 98 | } |
98 | }) | 99 | }) |
99 | } | 100 | } |
100 | 101 | ||
101 | function processUndoFollow (actor: string, followActivity: ActivityFollow) { | 102 | function processUndoFollow (actorUrl: string, followActivity: ActivityFollow) { |
102 | const options = { | 103 | const options = { |
103 | arguments: [ actor, followActivity ], | 104 | arguments: [ actorUrl, followActivity ], |
104 | errorMessage: 'Cannot undo follow with many retries.' | 105 | errorMessage: 'Cannot undo follow with many retries.' |
105 | } | 106 | } |
106 | 107 | ||
107 | return retryTransactionWrapper(undoFollow, options) | 108 | return retryTransactionWrapper(undoFollow, options) |
108 | } | 109 | } |
109 | 110 | ||
110 | function undoFollow (actor: string, followActivity: ActivityFollow) { | 111 | function undoFollow (actorUrl: string, followActivity: ActivityFollow) { |
111 | return sequelizeTypescript.transaction(async t => { | 112 | return sequelizeTypescript.transaction(async t => { |
112 | const follower = await AccountModel.loadByUrl(actor, t) | 113 | const follower = await ActorModel.loadByUrl(actorUrl, t) |
113 | const following = await AccountModel.loadByUrl(followActivity.object, t) | 114 | const following = await ActorModel.loadByUrl(followActivity.object, t) |
114 | const accountFollow = await AccountFollowModel.loadByAccountAndTarget(follower.id, following.id, t) | 115 | const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t) |
115 | 116 | ||
116 | if (!accountFollow) throw new Error(`'Unknown account follow ${follower.id} -> ${following.id}.`) | 117 | if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${following.id}.`) |
117 | 118 | ||
118 | await accountFollow.destroy({ transaction: t }) | 119 | await actorFollow.destroy({ transaction: t }) |
119 | 120 | ||
120 | return undefined | 121 | return undefined |
121 | }) | 122 | }) |
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 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { VideoChannelObject, VideoTorrentObject } from '../../../../shared' | ||
3 | import { ActivityUpdate } from '../../../../shared/models/activitypub' | 2 | import { ActivityUpdate } from '../../../../shared/models/activitypub' |
4 | import { logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers' | 3 | import { logger, resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers' |
5 | import { sequelizeTypescript } from '../../../initializers' | 4 | import { sequelizeTypescript } from '../../../initializers' |
6 | import { AccountModel } from '../../../models/account/account' | 5 | import { ActorModel } from '../../../models/activitypub/actor' |
7 | import { TagModel } from '../../../models/video/tag' | 6 | import { TagModel } from '../../../models/video/tag' |
8 | import { VideoModel } from '../../../models/video/video' | 7 | import { VideoModel } from '../../../models/video/video' |
9 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
10 | import { VideoFileModel } from '../../../models/video/video-file' | 8 | import { VideoFileModel } from '../../../models/video/video-file' |
11 | import { getOrCreateAccountAndServer } from '../account' | 9 | import { getOrCreateActorAndServerAndModel } from '../actor' |
12 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' | 10 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' |
13 | 11 | ||
14 | async function processUpdateActivity (activity: ActivityUpdate) { | 12 | async function processUpdateActivity (activity: ActivityUpdate) { |
15 | const account = await getOrCreateAccountAndServer(activity.actor) | 13 | const actor = await getOrCreateActorAndServerAndModel(activity.actor) |
16 | 14 | ||
17 | if (activity.object.type === 'Video') { | 15 | if (activity.object.type === 'Video') { |
18 | return processUpdateVideo(account, activity.object) | 16 | return processUpdateVideo(actor, activity) |
19 | } else if (activity.object.type === 'VideoChannel') { | ||
20 | return processUpdateVideoChannel(account, activity.object) | ||
21 | } | 17 | } |
22 | 18 | ||
23 | return | 19 | return |
@@ -31,16 +27,18 @@ export { | |||
31 | 27 | ||
32 | // --------------------------------------------------------------------------- | 28 | // --------------------------------------------------------------------------- |
33 | 29 | ||
34 | function processUpdateVideo (account: AccountModel, video: VideoTorrentObject) { | 30 | function processUpdateVideo (actor: ActorModel, activity: ActivityUpdate) { |
35 | const options = { | 31 | const options = { |
36 | arguments: [ account, video ], | 32 | arguments: [ actor, activity ], |
37 | errorMessage: 'Cannot update the remote video with many retries' | 33 | errorMessage: 'Cannot update the remote video with many retries' |
38 | } | 34 | } |
39 | 35 | ||
40 | return retryTransactionWrapper(updateRemoteVideo, options) | 36 | return retryTransactionWrapper(updateRemoteVideo, options) |
41 | } | 37 | } |
42 | 38 | ||
43 | async function updateRemoteVideo (account: AccountModel, videoAttributesToUpdate: VideoTorrentObject) { | 39 | async function updateRemoteVideo (actor: ActorModel, activity: ActivityUpdate) { |
40 | const videoAttributesToUpdate = activity.object | ||
41 | |||
44 | logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) | 42 | logger.debug('Updating remote video "%s".', videoAttributesToUpdate.uuid) |
45 | let videoInstance: VideoModel | 43 | let videoInstance: VideoModel |
46 | let videoFieldsSave: object | 44 | let videoFieldsSave: object |
@@ -54,23 +52,23 @@ async function updateRemoteVideo (account: AccountModel, videoAttributesToUpdate | |||
54 | const videoInstance = await VideoModel.loadByUrlAndPopulateAccount(videoAttributesToUpdate.id, t) | 52 | const videoInstance = await VideoModel.loadByUrlAndPopulateAccount(videoAttributesToUpdate.id, t) |
55 | if (!videoInstance) throw new Error('Video ' + videoAttributesToUpdate.id + ' not found.') | 53 | if (!videoInstance) throw new Error('Video ' + videoAttributesToUpdate.id + ' not found.') |
56 | 54 | ||
57 | if (videoInstance.VideoChannel.Account.id !== account.id) { | 55 | const videoChannel = videoInstance.VideoChannel |
58 | throw new Error('Account ' + account.url + ' does not own video channel ' + videoInstance.VideoChannel.url) | 56 | if (videoChannel.Account.Actor.id !== actor.id) { |
57 | throw new Error('Account ' + actor.url + ' does not own video channel ' + videoChannel.Actor.url) | ||
59 | } | 58 | } |
60 | 59 | ||
61 | const videoData = await videoActivityObjectToDBAttributes(videoInstance.VideoChannel, videoAttributesToUpdate) | 60 | const videoData = await videoActivityObjectToDBAttributes(videoChannel, videoAttributesToUpdate, activity.to, activity.cc) |
62 | videoInstance.set('name', videoData.name) | 61 | videoInstance.set('name', videoData.name) |
63 | videoInstance.set('category', videoData.category) | 62 | videoInstance.set('category', videoData.category) |
64 | videoInstance.set('licence', videoData.licence) | 63 | videoInstance.set('licence', videoData.licence) |
65 | videoInstance.set('language', videoData.language) | 64 | videoInstance.set('language', videoData.language) |
66 | videoInstance.set('nsfw', videoData.nsfw) | 65 | videoInstance.set('nsfw', videoData.nsfw) |
66 | videoInstance.set('privacy', videoData.privacy) | ||
67 | videoInstance.set('description', videoData.description) | 67 | videoInstance.set('description', videoData.description) |
68 | videoInstance.set('duration', videoData.duration) | 68 | videoInstance.set('duration', videoData.duration) |
69 | videoInstance.set('createdAt', videoData.createdAt) | 69 | videoInstance.set('createdAt', videoData.createdAt) |
70 | videoInstance.set('updatedAt', videoData.updatedAt) | 70 | videoInstance.set('updatedAt', videoData.updatedAt) |
71 | videoInstance.set('views', videoData.views) | 71 | videoInstance.set('views', videoData.views) |
72 | // videoInstance.set('likes', videoData.likes) | ||
73 | // videoInstance.set('dislikes', videoData.dislikes) | ||
74 | 72 | ||
75 | await videoInstance.save(sequelizeOptions) | 73 | await videoInstance.save(sequelizeOptions) |
76 | 74 | ||
@@ -101,36 +99,3 @@ async function updateRemoteVideo (account: AccountModel, videoAttributesToUpdate | |||
101 | throw err | 99 | throw err |
102 | } | 100 | } |
103 | } | 101 | } |
104 | |||
105 | async function processUpdateVideoChannel (account: AccountModel, videoChannel: VideoChannelObject) { | ||
106 | const options = { | ||
107 | arguments: [ account, videoChannel ], | ||
108 | errorMessage: 'Cannot update the remote video channel with many retries.' | ||
109 | } | ||
110 | |||
111 | await retryTransactionWrapper(updateRemoteVideoChannel, options) | ||
112 | } | ||
113 | |||
114 | async function updateRemoteVideoChannel (account: AccountModel, videoChannel: VideoChannelObject) { | ||
115 | logger.debug('Updating remote video channel "%s".', videoChannel.uuid) | ||
116 | |||
117 | await sequelizeTypescript.transaction(async t => { | ||
118 | const sequelizeOptions = { transaction: t } | ||
119 | |||
120 | const videoChannelInstance = await VideoChannelModel.loadByUrl(videoChannel.id) | ||
121 | if (!videoChannelInstance) throw new Error('Video ' + videoChannel.id + ' not found.') | ||
122 | |||
123 | if (videoChannelInstance.Account.id !== account.id) { | ||
124 | throw new Error('Account ' + account.id + ' does not own video channel ' + videoChannelInstance.url) | ||
125 | } | ||
126 | |||
127 | videoChannelInstance.set('name', videoChannel.name) | ||
128 | videoChannelInstance.set('description', videoChannel.content) | ||
129 | videoChannelInstance.set('createdAt', videoChannel.published) | ||
130 | videoChannelInstance.set('updatedAt', videoChannel.updated) | ||
131 | |||
132 | await videoChannelInstance.save(sequelizeOptions) | ||
133 | }) | ||
134 | |||
135 | logger.info('Remote video channel with uuid %s updated', videoChannel.uuid) | ||
136 | } | ||
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 @@ | |||
1 | import { Activity, ActivityType } from '../../../../shared/models/activitypub' | 1 | import { Activity, ActivityType } from '../../../../shared/models/activitypub' |
2 | import { logger } from '../../../helpers' | 2 | import { logger } from '../../../helpers' |
3 | import { AccountModel } from '../../../models/account/account' | 3 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { processAcceptActivity } from './process-accept' | 4 | import { processAcceptActivity } from './process-accept' |
5 | import { processAddActivity } from './process-add' | ||
6 | import { processAnnounceActivity } from './process-announce' | 5 | import { processAnnounceActivity } from './process-announce' |
7 | import { processCreateActivity } from './process-create' | 6 | import { processCreateActivity } from './process-create' |
8 | import { processDeleteActivity } from './process-delete' | 7 | import { processDeleteActivity } from './process-delete' |
@@ -11,9 +10,8 @@ import { processLikeActivity } from './process-like' | |||
11 | import { processUndoActivity } from './process-undo' | 10 | import { processUndoActivity } from './process-undo' |
12 | import { processUpdateActivity } from './process-update' | 11 | import { processUpdateActivity } from './process-update' |
13 | 12 | ||
14 | const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxAccount?: AccountModel) => Promise<any> } = { | 13 | const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxActor?: ActorModel) => Promise<any> } = { |
15 | Create: processCreateActivity, | 14 | Create: processCreateActivity, |
16 | Add: processAddActivity, | ||
17 | Update: processUpdateActivity, | 15 | Update: processUpdateActivity, |
18 | Delete: processDeleteActivity, | 16 | Delete: processDeleteActivity, |
19 | Follow: processFollowActivity, | 17 | Follow: processFollowActivity, |
@@ -23,11 +21,11 @@ const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxAccoun | |||
23 | Like: processLikeActivity | 21 | Like: processLikeActivity |
24 | } | 22 | } |
25 | 23 | ||
26 | async function processActivities (activities: Activity[], signatureAccount?: AccountModel, inboxAccount?: AccountModel) { | 24 | async function processActivities (activities: Activity[], signatureActor?: ActorModel, inboxActor?: ActorModel) { |
27 | for (const activity of activities) { | 25 | for (const activity of activities) { |
28 | // When we fetch remote data, we don't have signature | 26 | // When we fetch remote data, we don't have signature |
29 | if (signatureAccount && activity.actor !== signatureAccount.url) { | 27 | if (signatureActor && activity.actor !== signatureActor.url) { |
30 | logger.warn('Signature mismatch between %s and %s.', activity.actor, signatureAccount.url) | 28 | logger.warn('Signature mismatch between %s and %s.', activity.actor, signatureActor.url) |
31 | continue | 29 | continue |
32 | } | 30 | } |
33 | 31 | ||
@@ -38,7 +36,7 @@ async function processActivities (activities: Activity[], signatureAccount?: Acc | |||
38 | } | 36 | } |
39 | 37 | ||
40 | try { | 38 | try { |
41 | await activityProcessor(activity, inboxAccount) | 39 | await activityProcessor(activity, inboxActor) |
42 | } catch (err) { | 40 | } catch (err) { |
43 | logger.warn('Cannot process activity %s.', activity.type, err) | 41 | logger.warn('Cannot process activity %s.', activity.type, err) |
44 | } | 42 | } |
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 @@ | |||
1 | export * from './send-accept' | 1 | export * from './send-accept' |
2 | export * from './send-add' | ||
3 | export * from './send-announce' | 2 | export * from './send-announce' |
4 | export * from './send-create' | 3 | export * from './send-create' |
5 | export * from './send-delete' | 4 | 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' | |||
2 | import { Activity } from '../../../../shared/models/activitypub' | 2 | import { Activity } from '../../../../shared/models/activitypub' |
3 | import { logger } from '../../../helpers' | 3 | import { logger } from '../../../helpers' |
4 | import { ACTIVITY_PUB } from '../../../initializers' | 4 | import { ACTIVITY_PUB } from '../../../initializers' |
5 | import { AccountModel } from '../../../models/account/account' | 5 | import { ActorModel } from '../../../models/activitypub/actor' |
6 | import { AccountFollowModel } from '../../../models/account/account-follow' | 6 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
7 | import { VideoModel } from '../../../models/video/video' | 7 | import { VideoModel } from '../../../models/video/video' |
8 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
9 | import { VideoChannelShareModel } from '../../../models/video/video-channel-share' | ||
10 | import { VideoShareModel } from '../../../models/video/video-share' | 8 | import { VideoShareModel } from '../../../models/video/video-share' |
11 | import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../../jobs/activitypub-http-job-scheduler' | 9 | import { activitypubHttpJobScheduler, ActivityPubHttpPayload } from '../../jobs/activitypub-http-job-scheduler' |
12 | 10 | ||
13 | async function forwardActivity ( | 11 | async function forwardActivity ( |
14 | activity: Activity, | 12 | activity: Activity, |
15 | t: Transaction, | 13 | t: Transaction, |
16 | followersException: AccountModel[] = [] | 14 | followersException: ActorModel[] = [] |
17 | ) { | 15 | ) { |
18 | const to = activity.to || [] | 16 | const to = activity.to || [] |
19 | const cc = activity.cc || [] | 17 | const cc = activity.cc || [] |
@@ -25,11 +23,11 @@ async function forwardActivity ( | |||
25 | } | 23 | } |
26 | } | 24 | } |
27 | 25 | ||
28 | const toAccountFollowers = await AccountModel.listByFollowersUrls(followersUrls, t) | 26 | const toActorFollowers = await ActorModel.listByFollowersUrls(followersUrls, t) |
29 | const uris = await computeFollowerUris(toAccountFollowers, followersException, t) | 27 | const uris = await computeFollowerUris(toActorFollowers, followersException, t) |
30 | 28 | ||
31 | if (uris.length === 0) { | 29 | if (uris.length === 0) { |
32 | logger.info('0 followers for %s, no forwarding.', toAccountFollowers.map(a => a.id).join(', ')) | 30 | logger.info('0 followers for %s, no forwarding.', toActorFollowers.map(a => a.id).join(', ')) |
33 | return undefined | 31 | return undefined |
34 | } | 32 | } |
35 | 33 | ||
@@ -45,14 +43,14 @@ async function forwardActivity ( | |||
45 | 43 | ||
46 | async function broadcastToFollowers ( | 44 | async function broadcastToFollowers ( |
47 | data: any, | 45 | data: any, |
48 | byAccount: AccountModel, | 46 | byActor: ActorModel, |
49 | toAccountFollowers: AccountModel[], | 47 | toActorFollowers: ActorModel[], |
50 | t: Transaction, | 48 | t: Transaction, |
51 | followersException: AccountModel[] = [] | 49 | followersException: ActorModel[] = [] |
52 | ) { | 50 | ) { |
53 | const uris = await computeFollowerUris(toAccountFollowers, followersException, t) | 51 | const uris = await computeFollowerUris(toActorFollowers, followersException, t) |
54 | if (uris.length === 0) { | 52 | if (uris.length === 0) { |
55 | logger.info('0 followers for %s, no broadcasting.', toAccountFollowers.map(a => a.id).join(', ')) | 53 | logger.info('0 followers for %s, no broadcasting.', toActorFollowers.map(a => a.id).join(', ')) |
56 | return undefined | 54 | return undefined |
57 | } | 55 | } |
58 | 56 | ||
@@ -60,62 +58,48 @@ async function broadcastToFollowers ( | |||
60 | 58 | ||
61 | const jobPayload: ActivityPubHttpPayload = { | 59 | const jobPayload: ActivityPubHttpPayload = { |
62 | uris, | 60 | uris, |
63 | signatureAccountId: byAccount.id, | 61 | signatureActorId: byActor.id, |
64 | body: data | 62 | body: data |
65 | } | 63 | } |
66 | 64 | ||
67 | return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload) | 65 | return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload) |
68 | } | 66 | } |
69 | 67 | ||
70 | async function unicastTo (data: any, byAccount: AccountModel, toAccountUrl: string, t: Transaction) { | 68 | async function unicastTo (data: any, byActor: ActorModel, toActorUrl: string, t: Transaction) { |
71 | logger.debug('Creating unicast job.', { uri: toAccountUrl }) | 69 | logger.debug('Creating unicast job.', { uri: toActorUrl }) |
72 | 70 | ||
73 | const jobPayload: ActivityPubHttpPayload = { | 71 | const jobPayload: ActivityPubHttpPayload = { |
74 | uris: [ toAccountUrl ], | 72 | uris: [ toActorUrl ], |
75 | signatureAccountId: byAccount.id, | 73 | signatureActorId: byActor.id, |
76 | body: data | 74 | body: data |
77 | } | 75 | } |
78 | 76 | ||
79 | return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload) | 77 | return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload) |
80 | } | 78 | } |
81 | 79 | ||
82 | function getOriginVideoAudience (video: VideoModel, accountsInvolvedInVideo: AccountModel[]) { | 80 | function getOriginVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]) { |
83 | return { | 81 | return { |
84 | to: [ video.VideoChannel.Account.url ], | 82 | to: [ video.VideoChannel.Account.Actor.url ], |
85 | cc: accountsInvolvedInVideo.map(a => a.followersUrl) | 83 | cc: actorsInvolvedInVideo.map(a => a.followersUrl) |
86 | } | 84 | } |
87 | } | 85 | } |
88 | 86 | ||
89 | function getOriginVideoChannelAudience (videoChannel: VideoChannelModel, accountsInvolved: AccountModel[]) { | 87 | function getObjectFollowersAudience (actorsInvolvedInObject: ActorModel[]) { |
90 | return { | 88 | return { |
91 | to: [ videoChannel.Account.url ], | 89 | to: actorsInvolvedInObject.map(a => a.followersUrl), |
92 | cc: accountsInvolved.map(a => a.followersUrl) | ||
93 | } | ||
94 | } | ||
95 | |||
96 | function getObjectFollowersAudience (accountsInvolvedInObject: AccountModel[]) { | ||
97 | return { | ||
98 | to: accountsInvolvedInObject.map(a => a.followersUrl), | ||
99 | cc: [] | 90 | cc: [] |
100 | } | 91 | } |
101 | } | 92 | } |
102 | 93 | ||
103 | async function getAccountsInvolvedInVideo (video: VideoModel, t: Transaction) { | 94 | async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) { |
104 | const accountsToForwardView = await VideoShareModel.loadAccountsByShare(video.id, t) | 95 | const actorsToForwardView = await VideoShareModel.loadActorsByShare(video.id, t) |
105 | accountsToForwardView.push(video.VideoChannel.Account) | 96 | actorsToForwardView.push(video.VideoChannel.Account.Actor) |
106 | |||
107 | return accountsToForwardView | ||
108 | } | ||
109 | |||
110 | async function getAccountsInvolvedInVideoChannel (videoChannel: VideoChannelModel, t: Transaction) { | ||
111 | const accountsToForwardView = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t) | ||
112 | accountsToForwardView.push(videoChannel.Account) | ||
113 | 97 | ||
114 | return accountsToForwardView | 98 | return actorsToForwardView |
115 | } | 99 | } |
116 | 100 | ||
117 | async function getAudience (accountSender: AccountModel, t: Transaction, isPublic = true) { | 101 | async function getAudience (actorSender: ActorModel, t: Transaction, isPublic = true) { |
118 | const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls(t) | 102 | const followerInboxUrls = await actorSender.getFollowerSharedInboxUrls(t) |
119 | 103 | ||
120 | // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47 | 104 | // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47 |
121 | let to = [] | 105 | let to = [] |
@@ -132,10 +116,10 @@ async function getAudience (accountSender: AccountModel, t: Transaction, isPubli | |||
132 | return { to, cc } | 116 | return { to, cc } |
133 | } | 117 | } |
134 | 118 | ||
135 | async function computeFollowerUris (toAccountFollower: AccountModel[], followersException: AccountModel[], t: Transaction) { | 119 | async function computeFollowerUris (toActorFollower: ActorModel[], followersException: ActorModel[], t: Transaction) { |
136 | const toAccountFollowerIds = toAccountFollower.map(a => a.id) | 120 | const toActorFollowerIds = toActorFollower.map(a => a.id) |
137 | 121 | ||
138 | const result = await AccountFollowModel.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds, t) | 122 | const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t) |
139 | const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl) | 123 | const followersSharedInboxException = followersException.map(f => f.sharedInboxUrl) |
140 | return result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1) | 124 | return result.data.filter(sharedInbox => followersSharedInboxException.indexOf(sharedInbox) === -1) |
141 | } | 125 | } |
@@ -144,12 +128,10 @@ async function computeFollowerUris (toAccountFollower: AccountModel[], followers | |||
144 | 128 | ||
145 | export { | 129 | export { |
146 | broadcastToFollowers, | 130 | broadcastToFollowers, |
147 | getOriginVideoChannelAudience, | ||
148 | unicastTo, | 131 | unicastTo, |
149 | getAudience, | 132 | getAudience, |
150 | getOriginVideoAudience, | 133 | getOriginVideoAudience, |
151 | getAccountsInvolvedInVideo, | 134 | getActorsInvolvedInVideo, |
152 | getAccountsInvolvedInVideoChannel, | ||
153 | getObjectFollowersAudience, | 135 | getObjectFollowersAudience, |
154 | forwardActivity | 136 | forwardActivity |
155 | } | 137 | } |
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 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAccept } from '../../../../shared/models/activitypub' | 2 | import { ActivityAccept } from '../../../../shared/models/activitypub' |
3 | import { AccountModel } from '../../../models/account/account' | 3 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { AccountFollowModel } from '../../../models/account/account-follow' | 4 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
5 | import { getAccountFollowAcceptActivityPubUrl } from '../url' | 5 | import { getActorFollowAcceptActivityPubUrl } from '../url' |
6 | import { unicastTo } from './misc' | 6 | import { unicastTo } from './misc' |
7 | 7 | ||
8 | async function sendAccept (accountFollow: AccountFollowModel, t: Transaction) { | 8 | async function sendAccept (actorFollow: ActorFollowModel, t: Transaction) { |
9 | const follower = accountFollow.AccountFollower | 9 | const follower = actorFollow.ActorFollower |
10 | const me = accountFollow.AccountFollowing | 10 | const me = actorFollow.ActorFollowing |
11 | 11 | ||
12 | const url = getAccountFollowAcceptActivityPubUrl(accountFollow) | 12 | const url = getActorFollowAcceptActivityPubUrl(actorFollow) |
13 | const data = acceptActivityData(url, me) | 13 | const data = acceptActivityData(url, me) |
14 | 14 | ||
15 | return unicastTo(data, me, follower.inboxUrl, t) | 15 | return unicastTo(data, me, follower.inboxUrl, t) |
@@ -23,12 +23,10 @@ export { | |||
23 | 23 | ||
24 | // --------------------------------------------------------------------------- | 24 | // --------------------------------------------------------------------------- |
25 | 25 | ||
26 | function acceptActivityData (url: string, byAccount: AccountModel) { | 26 | function acceptActivityData (url: string, byActor: ActorModel): ActivityAccept { |
27 | const activity: ActivityAccept = { | 27 | return { |
28 | type: 'Accept', | 28 | type: 'Accept', |
29 | id: url, | 29 | id: url, |
30 | actor: byAccount.url | 30 | actor: byActor.url |
31 | } | 31 | } |
32 | |||
33 | return activity | ||
34 | } | 32 | } |
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 @@ | |||
1 | import { Transaction } from 'sequelize' | ||
2 | import { ActivityAdd } from '../../../../shared/models/activitypub' | ||
3 | import { VideoPrivacy } from '../../../../shared/models/videos' | ||
4 | import { AccountModel } from '../../../models/account/account' | ||
5 | import { VideoModel } from '../../../models/video/video' | ||
6 | import { broadcastToFollowers, getAudience } from './misc' | ||
7 | |||
8 | async function sendAddVideo (video: VideoModel, t: Transaction) { | ||
9 | const byAccount = video.VideoChannel.Account | ||
10 | |||
11 | const videoObject = video.toActivityPubObject() | ||
12 | const data = await addActivityData(video.url, byAccount, video, video.VideoChannel.url, videoObject, t) | ||
13 | |||
14 | return broadcastToFollowers(data, byAccount, [ byAccount ], t) | ||
15 | } | ||
16 | |||
17 | async function addActivityData ( | ||
18 | url: string, | ||
19 | byAccount: AccountModel, | ||
20 | video: VideoModel, | ||
21 | target: string, | ||
22 | object: any, | ||
23 | t: Transaction | ||
24 | ): Promise<ActivityAdd> { | ||
25 | const videoPublic = video.privacy === VideoPrivacy.PUBLIC | ||
26 | |||
27 | const { to, cc } = await getAudience(byAccount, t, videoPublic) | ||
28 | |||
29 | return { | ||
30 | type: 'Add', | ||
31 | id: url, | ||
32 | actor: byAccount.url, | ||
33 | to, | ||
34 | cc, | ||
35 | object, | ||
36 | target | ||
37 | } | ||
38 | } | ||
39 | |||
40 | // --------------------------------------------------------------------------- | ||
41 | |||
42 | export { | ||
43 | addActivityData, | ||
44 | sendAddVideo | ||
45 | } | ||
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 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAdd } from '../../../../shared/index' | ||
3 | import { ActivityAnnounce, ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' | 2 | import { ActivityAnnounce, ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' |
4 | import { AccountModel } from '../../../models/account/account' | 3 | import { VideoPrivacy } from '../../../../shared/models/videos' |
4 | import { ActorModel } from '../../../models/activitypub/actor' | ||
5 | import { VideoModel } from '../../../models/video/video' | 5 | import { VideoModel } from '../../../models/video/video' |
6 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
7 | import { getAnnounceActivityPubUrl } from '../url' | 6 | import { getAnnounceActivityPubUrl } from '../url' |
8 | import { | 7 | import { |
9 | broadcastToFollowers, | 8 | broadcastToFollowers, |
10 | getAccountsInvolvedInVideo, | 9 | getActorsInvolvedInVideo, |
11 | getAccountsInvolvedInVideoChannel, | ||
12 | getAudience, | 10 | getAudience, |
13 | getObjectFollowersAudience, | 11 | getObjectFollowersAudience, |
14 | getOriginVideoAudience, | 12 | getOriginVideoAudience, |
15 | getOriginVideoChannelAudience, | ||
16 | unicastTo | 13 | unicastTo |
17 | } from './misc' | 14 | } from './misc' |
18 | import { addActivityData } from './send-add' | ||
19 | import { createActivityData } from './send-create' | 15 | import { createActivityData } from './send-create' |
20 | 16 | ||
21 | async function buildVideoAnnounceToFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 17 | async function buildVideoAnnounceToFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { |
22 | const url = getAnnounceActivityPubUrl(video.url, byAccount) | 18 | const url = getAnnounceActivityPubUrl(video.url, byActor) |
19 | const videoObject = video.toActivityPubObject() | ||
23 | 20 | ||
24 | const videoChannel = video.VideoChannel | 21 | const announcedAudience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC) |
25 | const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject(), t) | 22 | const announcedActivity = await createActivityData(url, video.VideoChannel.Account.Actor, videoObject, t, announcedAudience) |
26 | 23 | ||
27 | const accountsToForwardView = await getAccountsInvolvedInVideo(video, t) | 24 | const accountsToForwardView = await getActorsInvolvedInVideo(video, t) |
28 | const audience = getObjectFollowersAudience(accountsToForwardView) | 25 | const audience = getObjectFollowersAudience(accountsToForwardView) |
29 | return announceActivityData(url, byAccount, announcedActivity, t, audience) | 26 | return announceActivityData(url, byActor, announcedActivity, t, audience) |
30 | } | 27 | } |
31 | 28 | ||
32 | async function sendVideoAnnounceToFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 29 | async function sendVideoAnnounceToFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { |
33 | const data = await buildVideoAnnounceToFollowers(byAccount, video, t) | 30 | const data = await buildVideoAnnounceToFollowers(byActor, video, t) |
34 | 31 | ||
35 | return broadcastToFollowers(data, byAccount, [ byAccount ], t) | 32 | return broadcastToFollowers(data, byActor, [ byActor ], t) |
36 | } | 33 | } |
37 | 34 | ||
38 | async function sendVideoAnnounceToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 35 | async function sendVideoAnnounceToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { |
39 | const url = getAnnounceActivityPubUrl(video.url, byAccount) | 36 | const url = getAnnounceActivityPubUrl(video.url, byActor) |
40 | 37 | ||
41 | const videoChannel = video.VideoChannel | 38 | const videoObject = video.toActivityPubObject() |
42 | const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject(), t) | 39 | const announcedActivity = await createActivityData(url, video.VideoChannel.Account.Actor, videoObject, t) |
43 | 40 | ||
44 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) | 41 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) |
45 | const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) | 42 | const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) |
46 | const data = await createActivityData(url, byAccount, announcedActivity, t, audience) | 43 | const data = await createActivityData(url, byActor, announcedActivity, t, audience) |
47 | 44 | ||
48 | return unicastTo(data, byAccount, videoChannel.Account.sharedInboxUrl, t) | 45 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) |
49 | } | ||
50 | |||
51 | async function buildVideoChannelAnnounceToFollowers (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) { | ||
52 | const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount) | ||
53 | const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject(), t) | ||
54 | |||
55 | const accountsToForwardView = await getAccountsInvolvedInVideoChannel(videoChannel, t) | ||
56 | const audience = getObjectFollowersAudience(accountsToForwardView) | ||
57 | return announceActivityData(url, byAccount, announcedActivity, t, audience) | ||
58 | } | ||
59 | |||
60 | async function sendVideoChannelAnnounceToFollowers (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) { | ||
61 | const data = await buildVideoChannelAnnounceToFollowers(byAccount, videoChannel, t) | ||
62 | |||
63 | return broadcastToFollowers(data, byAccount, [ byAccount ], t) | ||
64 | } | ||
65 | |||
66 | async function sendVideoChannelAnnounceToOrigin (byAccount: AccountModel, videoChannel: VideoChannelModel, t: Transaction) { | ||
67 | const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount) | ||
68 | const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject(), t) | ||
69 | |||
70 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideoChannel(videoChannel, t) | ||
71 | const audience = getOriginVideoChannelAudience(videoChannel, accountsInvolvedInVideo) | ||
72 | const data = await createActivityData(url, byAccount, announcedActivity, t, audience) | ||
73 | |||
74 | return unicastTo(data, byAccount, videoChannel.Account.sharedInboxUrl, t) | ||
75 | } | 46 | } |
76 | 47 | ||
77 | async function announceActivityData ( | 48 | async function announceActivityData ( |
78 | url: string, | 49 | url: string, |
79 | byAccount: AccountModel, | 50 | byActor: ActorModel, |
80 | object: ActivityCreate | ActivityAdd, | 51 | object: ActivityCreate, |
81 | t: Transaction, | 52 | t: Transaction, |
82 | audience?: ActivityAudience | 53 | audience?: ActivityAudience |
83 | ): Promise<ActivityAnnounce> { | 54 | ): Promise<ActivityAnnounce> { |
84 | if (!audience) { | 55 | if (!audience) { |
85 | audience = await getAudience(byAccount, t) | 56 | audience = await getAudience(byActor, t) |
86 | } | 57 | } |
87 | 58 | ||
88 | return { | 59 | return { |
@@ -90,7 +61,7 @@ async function announceActivityData ( | |||
90 | to: audience.to, | 61 | to: audience.to, |
91 | cc: audience.cc, | 62 | cc: audience.cc, |
92 | id: url, | 63 | id: url, |
93 | actor: byAccount.url, | 64 | actor: byActor.url, |
94 | object | 65 | object |
95 | } | 66 | } |
96 | } | 67 | } |
@@ -99,10 +70,7 @@ async function announceActivityData ( | |||
99 | 70 | ||
100 | export { | 71 | export { |
101 | sendVideoAnnounceToFollowers, | 72 | sendVideoAnnounceToFollowers, |
102 | sendVideoChannelAnnounceToFollowers, | ||
103 | sendVideoAnnounceToOrigin, | 73 | sendVideoAnnounceToOrigin, |
104 | sendVideoChannelAnnounceToOrigin, | ||
105 | announceActivityData, | 74 | announceActivityData, |
106 | buildVideoAnnounceToFollowers, | 75 | buildVideoAnnounceToFollowers |
107 | buildVideoChannelAnnounceToFollowers | ||
108 | } | 76 | } |
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 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityCreate } from '../../../../shared/models/activitypub' |
3 | import { getServerAccount } from '../../../helpers' | 3 | import { VideoPrivacy } from '../../../../shared/models/videos' |
4 | import { AccountModel } from '../../../models/account/account' | 4 | import { getServerActor } from '../../../helpers' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | ||
5 | import { VideoModel } from '../../../models/video/video' | 6 | import { VideoModel } from '../../../models/video/video' |
6 | import { VideoAbuseModel } from '../../../models/video/video-abuse' | 7 | import { VideoAbuseModel } from '../../../models/video/video-abuse' |
7 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
8 | import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url' | 8 | import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url' |
9 | import { | 9 | import { |
10 | broadcastToFollowers, | 10 | broadcastToFollowers, |
11 | getAccountsInvolvedInVideo, | 11 | getActorsInvolvedInVideo, |
12 | getAudience, | 12 | getAudience, |
13 | getObjectFollowersAudience, | 13 | getObjectFollowersAudience, |
14 | getOriginVideoAudience, | 14 | getOriginVideoAudience, |
15 | unicastTo | 15 | unicastTo |
16 | } from './misc' | 16 | } from './misc' |
17 | 17 | ||
18 | async function sendCreateVideoChannel (videoChannel: VideoChannelModel, t: Transaction) { | 18 | async function sendCreateVideo (video: VideoModel, t: Transaction) { |
19 | const byAccount = videoChannel.Account | 19 | const byActor = video.VideoChannel.Account.Actor |
20 | 20 | ||
21 | const videoChannelObject = videoChannel.toActivityPubObject() | 21 | const videoObject = video.toActivityPubObject() |
22 | const data = await createActivityData(videoChannel.url, byAccount, videoChannelObject, t) | 22 | const audience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC) |
23 | const data = await createActivityData(video.url, byActor, videoObject, t, audience) | ||
23 | 24 | ||
24 | return broadcastToFollowers(data, byAccount, [ byAccount ], t) | 25 | return broadcastToFollowers(data, byActor, [ byActor ], t) |
25 | } | 26 | } |
26 | 27 | ||
27 | async function sendVideoAbuse (byAccount: AccountModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) { | 28 | async function sendVideoAbuse (byActor: ActorModel, videoAbuse: VideoAbuseModel, video: VideoModel, t: Transaction) { |
28 | const url = getVideoAbuseActivityPubUrl(videoAbuse) | 29 | const url = getVideoAbuseActivityPubUrl(videoAbuse) |
29 | 30 | ||
30 | const audience = { to: [ video.VideoChannel.Account.url ], cc: [] } | 31 | const audience = { to: [ video.VideoChannel.Account.Actor.url ], cc: [] } |
31 | const data = await createActivityData(url, byAccount, videoAbuse.toActivityPubObject(), t, audience) | 32 | const data = await createActivityData(url, byActor, videoAbuse.toActivityPubObject(), t, audience) |
32 | 33 | ||
33 | return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) | 34 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) |
34 | } | 35 | } |
35 | 36 | ||
36 | async function sendCreateViewToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 37 | async function sendCreateViewToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { |
37 | const url = getVideoViewActivityPubUrl(byAccount, video) | 38 | const url = getVideoViewActivityPubUrl(byActor, video) |
38 | const viewActivity = createViewActivityData(byAccount, video) | 39 | const viewActivity = createViewActivityData(byActor, video) |
39 | 40 | ||
40 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) | 41 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) |
41 | const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) | 42 | const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) |
42 | const data = await createActivityData(url, byAccount, viewActivity, t, audience) | 43 | const data = await createActivityData(url, byActor, viewActivity, t, audience) |
43 | 44 | ||
44 | return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) | 45 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) |
45 | } | 46 | } |
46 | 47 | ||
47 | async function sendCreateViewToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 48 | async function sendCreateViewToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { |
48 | const url = getVideoViewActivityPubUrl(byAccount, video) | 49 | const url = getVideoViewActivityPubUrl(byActor, video) |
49 | const viewActivity = createViewActivityData(byAccount, video) | 50 | const viewActivity = createViewActivityData(byActor, video) |
50 | 51 | ||
51 | const accountsToForwardView = await getAccountsInvolvedInVideo(video, t) | 52 | const actorsToForwardView = await getActorsInvolvedInVideo(video, t) |
52 | const audience = getObjectFollowersAudience(accountsToForwardView) | 53 | const audience = getObjectFollowersAudience(actorsToForwardView) |
53 | const data = await createActivityData(url, byAccount, viewActivity, t, audience) | 54 | const data = await createActivityData(url, byActor, viewActivity, t, audience) |
54 | 55 | ||
55 | // Use the server account to send the view, because it could be an unregistered account | 56 | // Use the server actor to send the view |
56 | const serverAccount = await getServerAccount() | 57 | const serverActor = await getServerActor() |
57 | const followersException = [ byAccount ] | 58 | const followersException = [ byActor ] |
58 | return broadcastToFollowers(data, serverAccount, accountsToForwardView, t, followersException) | 59 | return broadcastToFollowers(data, serverActor, actorsToForwardView, t, followersException) |
59 | } | 60 | } |
60 | 61 | ||
61 | async function sendCreateDislikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 62 | async function sendCreateDislikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { |
62 | const url = getVideoDislikeActivityPubUrl(byAccount, video) | 63 | const url = getVideoDislikeActivityPubUrl(byActor, video) |
63 | const dislikeActivity = createDislikeActivityData(byAccount, video) | 64 | const dislikeActivity = createDislikeActivityData(byActor, video) |
64 | 65 | ||
65 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) | 66 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) |
66 | const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) | 67 | const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) |
67 | const data = await createActivityData(url, byAccount, dislikeActivity, t, audience) | 68 | const data = await createActivityData(url, byActor, dislikeActivity, t, audience) |
68 | 69 | ||
69 | return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) | 70 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) |
70 | } | 71 | } |
71 | 72 | ||
72 | async function sendCreateDislikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 73 | async function sendCreateDislikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { |
73 | const url = getVideoDislikeActivityPubUrl(byAccount, video) | 74 | const url = getVideoDislikeActivityPubUrl(byActor, video) |
74 | const dislikeActivity = createDislikeActivityData(byAccount, video) | 75 | const dislikeActivity = createDislikeActivityData(byActor, video) |
75 | 76 | ||
76 | const accountsToForwardView = await getAccountsInvolvedInVideo(video, t) | 77 | const actorsToForwardView = await getActorsInvolvedInVideo(video, t) |
77 | const audience = getObjectFollowersAudience(accountsToForwardView) | 78 | const audience = getObjectFollowersAudience(actorsToForwardView) |
78 | const data = await createActivityData(url, byAccount, dislikeActivity, t, audience) | 79 | const data = await createActivityData(url, byActor, dislikeActivity, t, audience) |
79 | 80 | ||
80 | const followersException = [ byAccount ] | 81 | const followersException = [ byActor ] |
81 | return broadcastToFollowers(data, byAccount, accountsToForwardView, t, followersException) | 82 | return broadcastToFollowers(data, byActor, actorsToForwardView, t, followersException) |
82 | } | 83 | } |
83 | 84 | ||
84 | async function createActivityData ( | 85 | async function createActivityData ( |
85 | url: string, | 86 | url: string, |
86 | byAccount: AccountModel, | 87 | byActor: ActorModel, |
87 | object: any, | 88 | object: any, |
88 | t: Transaction, | 89 | t: Transaction, |
89 | audience?: ActivityAudience | 90 | audience?: ActivityAudience |
90 | ): Promise<ActivityCreate> { | 91 | ): Promise<ActivityCreate> { |
91 | if (!audience) { | 92 | if (!audience) { |
92 | audience = await getAudience(byAccount, t) | 93 | audience = await getAudience(byActor, t) |
93 | } | 94 | } |
94 | 95 | ||
95 | return { | 96 | return { |
96 | type: 'Create', | 97 | type: 'Create', |
97 | id: url, | 98 | id: url, |
98 | actor: byAccount.url, | 99 | actor: byActor.url, |
99 | to: audience.to, | 100 | to: audience.to, |
100 | cc: audience.cc, | 101 | cc: audience.cc, |
101 | object | 102 | object |
102 | } | 103 | } |
103 | } | 104 | } |
104 | 105 | ||
105 | function createDislikeActivityData (byAccount: AccountModel, video: VideoModel) { | 106 | function createDislikeActivityData (byActor: ActorModel, video: VideoModel) { |
106 | return { | 107 | return { |
107 | type: 'Dislike', | 108 | type: 'Dislike', |
108 | actor: byAccount.url, | 109 | actor: byActor.url, |
109 | object: video.url | 110 | object: video.url |
110 | } | 111 | } |
111 | } | 112 | } |
@@ -113,7 +114,7 @@ function createDislikeActivityData (byAccount: AccountModel, video: VideoModel) | |||
113 | // --------------------------------------------------------------------------- | 114 | // --------------------------------------------------------------------------- |
114 | 115 | ||
115 | export { | 116 | export { |
116 | sendCreateVideoChannel, | 117 | sendCreateVideo, |
117 | sendVideoAbuse, | 118 | sendVideoAbuse, |
118 | createActivityData, | 119 | createActivityData, |
119 | sendCreateViewToOrigin, | 120 | sendCreateViewToOrigin, |
@@ -125,10 +126,10 @@ export { | |||
125 | 126 | ||
126 | // --------------------------------------------------------------------------- | 127 | // --------------------------------------------------------------------------- |
127 | 128 | ||
128 | function createViewActivityData (byAccount: AccountModel, video: VideoModel) { | 129 | function createViewActivityData (byActor: ActorModel, video: VideoModel) { |
129 | return { | 130 | return { |
130 | type: 'View', | 131 | type: 'View', |
131 | actor: byAccount.url, | 132 | actor: byActor.url, |
132 | object: video.url | 133 | object: video.url |
133 | } | 134 | } |
134 | } | 135 | } |
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 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityDelete } from '../../../../shared/models/activitypub' | 2 | import { ActivityDelete } from '../../../../shared/models/activitypub' |
3 | import { AccountModel } from '../../../models/account/account' | 3 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { VideoModel } from '../../../models/video/video' | 4 | import { VideoModel } from '../../../models/video/video' |
5 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
6 | import { VideoChannelShareModel } from '../../../models/video/video-channel-share' | ||
7 | import { VideoShareModel } from '../../../models/video/video-share' | 5 | import { VideoShareModel } from '../../../models/video/video-share' |
8 | import { broadcastToFollowers } from './misc' | 6 | import { broadcastToFollowers } from './misc' |
9 | 7 | ||
10 | async function sendDeleteVideoChannel (videoChannel: VideoChannelModel, t: Transaction) { | ||
11 | const byAccount = videoChannel.Account | ||
12 | |||
13 | const data = deleteActivityData(videoChannel.url, byAccount) | ||
14 | |||
15 | const accountsInvolved = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t) | ||
16 | accountsInvolved.push(byAccount) | ||
17 | |||
18 | return broadcastToFollowers(data, byAccount, accountsInvolved, t) | ||
19 | } | ||
20 | |||
21 | async function sendDeleteVideo (video: VideoModel, t: Transaction) { | 8 | async function sendDeleteVideo (video: VideoModel, t: Transaction) { |
22 | const byAccount = video.VideoChannel.Account | 9 | const byActor = video.VideoChannel.Account.Actor |
23 | 10 | ||
24 | const data = deleteActivityData(video.url, byAccount) | 11 | const data = deleteActivityData(video.url, byActor) |
25 | 12 | ||
26 | const accountsInvolved = await VideoShareModel.loadAccountsByShare(video.id, t) | 13 | const actorsInvolved = await VideoShareModel.loadActorsByShare(video.id, t) |
27 | accountsInvolved.push(byAccount) | 14 | actorsInvolved.push(byActor) |
28 | 15 | ||
29 | return broadcastToFollowers(data, byAccount, accountsInvolved, t) | 16 | return broadcastToFollowers(data, byActor, actorsInvolved, t) |
30 | } | 17 | } |
31 | 18 | ||
32 | async function sendDeleteAccount (account: AccountModel, t: Transaction) { | 19 | async function sendDeleteActor (byActor: ActorModel, t: Transaction) { |
33 | const data = deleteActivityData(account.url, account) | 20 | const data = deleteActivityData(byActor.url, byActor) |
34 | 21 | ||
35 | return broadcastToFollowers(data, account, [ account ], t) | 22 | return broadcastToFollowers(data, byActor, [ byActor ], t) |
36 | } | 23 | } |
37 | 24 | ||
38 | // --------------------------------------------------------------------------- | 25 | // --------------------------------------------------------------------------- |
39 | 26 | ||
40 | export { | 27 | export { |
41 | sendDeleteVideoChannel, | ||
42 | sendDeleteVideo, | 28 | sendDeleteVideo, |
43 | sendDeleteAccount | 29 | sendDeleteActor |
44 | } | 30 | } |
45 | 31 | ||
46 | // --------------------------------------------------------------------------- | 32 | // --------------------------------------------------------------------------- |
47 | 33 | ||
48 | function deleteActivityData (url: string, byAccount: AccountModel): ActivityDelete { | 34 | function deleteActivityData (url: string, byActor: ActorModel): ActivityDelete { |
49 | return { | 35 | return { |
50 | type: 'Delete', | 36 | type: 'Delete', |
51 | id: url, | 37 | id: url, |
52 | actor: byAccount.url | 38 | actor: byActor.url |
53 | } | 39 | } |
54 | } | 40 | } |
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 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityFollow } from '../../../../shared/models/activitypub' | 2 | import { ActivityFollow } from '../../../../shared/models/activitypub' |
3 | import { AccountModel } from '../../../models/account/account' | 3 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { AccountFollowModel } from '../../../models/account/account-follow' | 4 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
5 | import { getAccountFollowActivityPubUrl } from '../url' | 5 | import { getActorFollowActivityPubUrl } from '../url' |
6 | import { unicastTo } from './misc' | 6 | import { unicastTo } from './misc' |
7 | 7 | ||
8 | function sendFollow (accountFollow: AccountFollowModel, t: Transaction) { | 8 | function sendFollow (actorFollow: ActorFollowModel, t: Transaction) { |
9 | const me = accountFollow.AccountFollower | 9 | const me = actorFollow.ActorFollower |
10 | const following = accountFollow.AccountFollowing | 10 | const following = actorFollow.ActorFollowing |
11 | 11 | ||
12 | const url = getAccountFollowActivityPubUrl(accountFollow) | 12 | const url = getActorFollowActivityPubUrl(actorFollow) |
13 | const data = followActivityData(url, me, following) | 13 | const data = followActivityData(url, me, following) |
14 | 14 | ||
15 | return unicastTo(data, me, following.inboxUrl, t) | 15 | return unicastTo(data, me, following.inboxUrl, t) |
16 | } | 16 | } |
17 | 17 | ||
18 | function followActivityData (url: string, byAccount: AccountModel, targetAccount: AccountModel): ActivityFollow { | 18 | function followActivityData (url: string, byActor: ActorModel, targetActor: ActorModel): ActivityFollow { |
19 | return { | 19 | return { |
20 | type: 'Follow', | 20 | type: 'Follow', |
21 | id: url, | 21 | id: url, |
22 | actor: byAccount.url, | 22 | actor: byActor.url, |
23 | object: targetAccount.url | 23 | object: targetActor.url |
24 | } | 24 | } |
25 | } | 25 | } |
26 | 26 | ||
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 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityLike } from '../../../../shared/models/activitypub' |
3 | import { AccountModel } from '../../../models/account/account' | 3 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { VideoModel } from '../../../models/video/video' | 4 | import { VideoModel } from '../../../models/video/video' |
5 | import { getVideoLikeActivityPubUrl } from '../url' | 5 | import { getVideoLikeActivityPubUrl } from '../url' |
6 | import { | 6 | import { |
7 | broadcastToFollowers, | 7 | broadcastToFollowers, |
8 | getAccountsInvolvedInVideo, | 8 | getActorsInvolvedInVideo, |
9 | getAudience, | 9 | getAudience, |
10 | getOriginVideoAudience, | ||
11 | getObjectFollowersAudience, | 10 | getObjectFollowersAudience, |
11 | getOriginVideoAudience, | ||
12 | unicastTo | 12 | unicastTo |
13 | } from './misc' | 13 | } from './misc' |
14 | 14 | ||
15 | async function sendLikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 15 | async function sendLikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { |
16 | const url = getVideoLikeActivityPubUrl(byAccount, video) | 16 | const url = getVideoLikeActivityPubUrl(byActor, video) |
17 | 17 | ||
18 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) | 18 | const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) |
19 | const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) | 19 | const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) |
20 | const data = await likeActivityData(url, byAccount, video, t, audience) | 20 | const data = await likeActivityData(url, byActor, video, t, audience) |
21 | 21 | ||
22 | return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) | 22 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) |
23 | } | 23 | } |
24 | 24 | ||
25 | async function sendLikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 25 | async function sendLikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { |
26 | const url = getVideoLikeActivityPubUrl(byAccount, video) | 26 | const url = getVideoLikeActivityPubUrl(byActor, video) |
27 | 27 | ||
28 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) | 28 | const accountsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) |
29 | const audience = getObjectFollowersAudience(accountsInvolvedInVideo) | 29 | const audience = getObjectFollowersAudience(accountsInvolvedInVideo) |
30 | const data = await likeActivityData(url, byAccount, video, t, audience) | 30 | const data = await likeActivityData(url, byActor, video, t, audience) |
31 | 31 | ||
32 | const followersException = [ byAccount ] | 32 | const followersException = [ byActor ] |
33 | return broadcastToFollowers(data, byAccount, accountsInvolvedInVideo, t, followersException) | 33 | return broadcastToFollowers(data, byActor, accountsInvolvedInVideo, t, followersException) |
34 | } | 34 | } |
35 | 35 | ||
36 | async function likeActivityData ( | 36 | async function likeActivityData ( |
37 | url: string, | 37 | url: string, |
38 | byAccount: AccountModel, | 38 | byActor: ActorModel, |
39 | video: VideoModel, | 39 | video: VideoModel, |
40 | t: Transaction, | 40 | t: Transaction, |
41 | audience?: ActivityAudience | 41 | audience?: ActivityAudience |
42 | ): Promise<ActivityLike> { | 42 | ): Promise<ActivityLike> { |
43 | if (!audience) { | 43 | if (!audience) { |
44 | audience = await getAudience(byAccount, t) | 44 | audience = await getAudience(byActor, t) |
45 | } | 45 | } |
46 | 46 | ||
47 | return { | 47 | return { |
48 | type: 'Like', | 48 | type: 'Like', |
49 | id: url, | 49 | id: url, |
50 | actor: byAccount.url, | 50 | actor: byActor.url, |
51 | to: audience.to, | 51 | to: audience.to, |
52 | cc: audience.cc, | 52 | cc: audience.cc, |
53 | object: video.url | 53 | 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 { | |||
6 | ActivityLike, | 6 | ActivityLike, |
7 | ActivityUndo | 7 | ActivityUndo |
8 | } from '../../../../shared/models/activitypub' | 8 | } from '../../../../shared/models/activitypub' |
9 | import { AccountModel } from '../../../models/account/account' | 9 | import { ActorModel } from '../../../models/activitypub/actor' |
10 | import { AccountFollowModel } from '../../../models/account/account-follow' | 10 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
11 | import { VideoModel } from '../../../models/video/video' | 11 | import { VideoModel } from '../../../models/video/video' |
12 | import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' | 12 | import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url' |
13 | import { | 13 | import { |
14 | broadcastToFollowers, | 14 | broadcastToFollowers, |
15 | getAccountsInvolvedInVideo, | 15 | getActorsInvolvedInVideo, |
16 | getAudience, | 16 | getAudience, |
17 | getObjectFollowersAudience, | 17 | getObjectFollowersAudience, |
18 | getOriginVideoAudience, | 18 | getOriginVideoAudience, |
@@ -22,11 +22,11 @@ import { createActivityData, createDislikeActivityData } from './send-create' | |||
22 | import { followActivityData } from './send-follow' | 22 | import { followActivityData } from './send-follow' |
23 | import { likeActivityData } from './send-like' | 23 | import { likeActivityData } from './send-like' |
24 | 24 | ||
25 | async function sendUndoFollow (accountFollow: AccountFollowModel, t: Transaction) { | 25 | async function sendUndoFollow (actorFollow: ActorFollowModel, t: Transaction) { |
26 | const me = accountFollow.AccountFollower | 26 | const me = actorFollow.ActorFollower |
27 | const following = accountFollow.AccountFollowing | 27 | const following = actorFollow.ActorFollowing |
28 | 28 | ||
29 | const followUrl = getAccountFollowActivityPubUrl(accountFollow) | 29 | const followUrl = getActorFollowActivityPubUrl(actorFollow) |
30 | const undoUrl = getUndoActivityPubUrl(followUrl) | 30 | const undoUrl = getUndoActivityPubUrl(followUrl) |
31 | 31 | ||
32 | const object = followActivityData(followUrl, me, following) | 32 | const object = followActivityData(followUrl, me, following) |
@@ -35,58 +35,58 @@ async function sendUndoFollow (accountFollow: AccountFollowModel, t: Transaction | |||
35 | return unicastTo(data, me, following.inboxUrl, t) | 35 | return unicastTo(data, me, following.inboxUrl, t) |
36 | } | 36 | } |
37 | 37 | ||
38 | async function sendUndoLikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 38 | async function sendUndoLikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { |
39 | const likeUrl = getVideoLikeActivityPubUrl(byAccount, video) | 39 | const likeUrl = getVideoLikeActivityPubUrl(byActor, video) |
40 | const undoUrl = getUndoActivityPubUrl(likeUrl) | 40 | const undoUrl = getUndoActivityPubUrl(likeUrl) |
41 | 41 | ||
42 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) | 42 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) |
43 | const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) | 43 | const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) |
44 | const object = await likeActivityData(likeUrl, byAccount, video, t) | 44 | const object = await likeActivityData(likeUrl, byActor, video, t) |
45 | const data = await undoActivityData(undoUrl, byAccount, object, t, audience) | 45 | const data = await undoActivityData(undoUrl, byActor, object, t, audience) |
46 | 46 | ||
47 | return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) | 47 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) |
48 | } | 48 | } |
49 | 49 | ||
50 | async function sendUndoLikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 50 | async function sendUndoLikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { |
51 | const likeUrl = getVideoLikeActivityPubUrl(byAccount, video) | 51 | const likeUrl = getVideoLikeActivityPubUrl(byActor, video) |
52 | const undoUrl = getUndoActivityPubUrl(likeUrl) | 52 | const undoUrl = getUndoActivityPubUrl(likeUrl) |
53 | 53 | ||
54 | const toAccountsFollowers = await getAccountsInvolvedInVideo(video, t) | 54 | const toActorsFollowers = await getActorsInvolvedInVideo(video, t) |
55 | const audience = getObjectFollowersAudience(toAccountsFollowers) | 55 | const audience = getObjectFollowersAudience(toActorsFollowers) |
56 | const object = await likeActivityData(likeUrl, byAccount, video, t) | 56 | const object = await likeActivityData(likeUrl, byActor, video, t) |
57 | const data = await undoActivityData(undoUrl, byAccount, object, t, audience) | 57 | const data = await undoActivityData(undoUrl, byActor, object, t, audience) |
58 | 58 | ||
59 | const followersException = [ byAccount ] | 59 | const followersException = [ byActor ] |
60 | return broadcastToFollowers(data, byAccount, toAccountsFollowers, t, followersException) | 60 | return broadcastToFollowers(data, byActor, toActorsFollowers, t, followersException) |
61 | } | 61 | } |
62 | 62 | ||
63 | async function sendUndoDislikeToOrigin (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 63 | async function sendUndoDislikeToOrigin (byActor: ActorModel, video: VideoModel, t: Transaction) { |
64 | const dislikeUrl = getVideoDislikeActivityPubUrl(byAccount, video) | 64 | const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video) |
65 | const undoUrl = getUndoActivityPubUrl(dislikeUrl) | 65 | const undoUrl = getUndoActivityPubUrl(dislikeUrl) |
66 | 66 | ||
67 | const accountsInvolvedInVideo = await getAccountsInvolvedInVideo(video, t) | 67 | const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, t) |
68 | const audience = getOriginVideoAudience(video, accountsInvolvedInVideo) | 68 | const audience = getOriginVideoAudience(video, actorsInvolvedInVideo) |
69 | const dislikeActivity = createDislikeActivityData(byAccount, video) | 69 | const dislikeActivity = createDislikeActivityData(byActor, video) |
70 | const object = await createActivityData(undoUrl, byAccount, dislikeActivity, t) | 70 | const object = await createActivityData(undoUrl, byActor, dislikeActivity, t) |
71 | 71 | ||
72 | const data = await undoActivityData(undoUrl, byAccount, object, t, audience) | 72 | const data = await undoActivityData(undoUrl, byActor, object, t, audience) |
73 | 73 | ||
74 | return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t) | 74 | return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl, t) |
75 | } | 75 | } |
76 | 76 | ||
77 | async function sendUndoDislikeToVideoFollowers (byAccount: AccountModel, video: VideoModel, t: Transaction) { | 77 | async function sendUndoDislikeToVideoFollowers (byActor: ActorModel, video: VideoModel, t: Transaction) { |
78 | const dislikeUrl = getVideoDislikeActivityPubUrl(byAccount, video) | 78 | const dislikeUrl = getVideoDislikeActivityPubUrl(byActor, video) |
79 | const undoUrl = getUndoActivityPubUrl(dislikeUrl) | 79 | const undoUrl = getUndoActivityPubUrl(dislikeUrl) |
80 | 80 | ||
81 | const dislikeActivity = createDislikeActivityData(byAccount, video) | 81 | const dislikeActivity = createDislikeActivityData(byActor, video) |
82 | const object = await createActivityData(undoUrl, byAccount, dislikeActivity, t) | 82 | const object = await createActivityData(undoUrl, byActor, dislikeActivity, t) |
83 | 83 | ||
84 | const data = await undoActivityData(undoUrl, byAccount, object, t) | 84 | const data = await undoActivityData(undoUrl, byActor, object, t) |
85 | 85 | ||
86 | const toAccountsFollowers = await getAccountsInvolvedInVideo(video, t) | 86 | const toActorsFollowers = await getActorsInvolvedInVideo(video, t) |
87 | 87 | ||
88 | const followersException = [ byAccount ] | 88 | const followersException = [ byActor ] |
89 | return broadcastToFollowers(data, byAccount, toAccountsFollowers, t, followersException) | 89 | return broadcastToFollowers(data, byActor, toActorsFollowers, t, followersException) |
90 | } | 90 | } |
91 | 91 | ||
92 | // --------------------------------------------------------------------------- | 92 | // --------------------------------------------------------------------------- |
@@ -103,19 +103,19 @@ export { | |||
103 | 103 | ||
104 | async function undoActivityData ( | 104 | async function undoActivityData ( |
105 | url: string, | 105 | url: string, |
106 | byAccount: AccountModel, | 106 | byActor: ActorModel, |
107 | object: ActivityFollow | ActivityLike | ActivityCreate, | 107 | object: ActivityFollow | ActivityLike | ActivityCreate, |
108 | t: Transaction, | 108 | t: Transaction, |
109 | audience?: ActivityAudience | 109 | audience?: ActivityAudience |
110 | ): Promise<ActivityUndo> { | 110 | ): Promise<ActivityUndo> { |
111 | if (!audience) { | 111 | if (!audience) { |
112 | audience = await getAudience(byAccount, t) | 112 | audience = await getAudience(byActor, t) |
113 | } | 113 | } |
114 | 114 | ||
115 | return { | 115 | return { |
116 | type: 'Undo', | 116 | type: 'Undo', |
117 | id: url, | 117 | id: url, |
118 | actor: byAccount.url, | 118 | actor: byActor.url, |
119 | to: audience.to, | 119 | to: audience.to, |
120 | cc: audience.cc, | 120 | cc: audience.cc, |
121 | object | 121 | 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 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityUpdate } from '../../../../shared/models/activitypub' | 2 | import { ActivityAudience, ActivityUpdate } from '../../../../shared/models/activitypub' |
3 | import { AccountModel } from '../../../models/account/account' | 3 | import { VideoPrivacy } from '../../../../shared/models/videos' |
4 | import { ActorModel } from '../../../models/activitypub/actor' | ||
4 | import { VideoModel } from '../../../models/video/video' | 5 | import { VideoModel } from '../../../models/video/video' |
5 | import { VideoChannelModel } from '../../../models/video/video-channel' | ||
6 | import { VideoChannelShareModel } from '../../../models/video/video-channel-share' | ||
7 | import { VideoShareModel } from '../../../models/video/video-share' | 6 | import { VideoShareModel } from '../../../models/video/video-share' |
8 | import { getUpdateActivityPubUrl } from '../url' | 7 | import { getUpdateActivityPubUrl } from '../url' |
9 | import { broadcastToFollowers, getAudience } from './misc' | 8 | import { broadcastToFollowers, getAudience } from './misc' |
10 | 9 | ||
11 | async function sendUpdateVideoChannel (videoChannel: VideoChannelModel, t: Transaction) { | ||
12 | const byAccount = videoChannel.Account | ||
13 | |||
14 | const url = getUpdateActivityPubUrl(videoChannel.url, videoChannel.updatedAt.toISOString()) | ||
15 | const videoChannelObject = videoChannel.toActivityPubObject() | ||
16 | const data = await updateActivityData(url, byAccount, videoChannelObject, t) | ||
17 | |||
18 | const accountsInvolved = await VideoChannelShareModel.loadAccountsByShare(videoChannel.id, t) | ||
19 | accountsInvolved.push(byAccount) | ||
20 | |||
21 | return broadcastToFollowers(data, byAccount, accountsInvolved, t) | ||
22 | } | ||
23 | |||
24 | async function sendUpdateVideo (video: VideoModel, t: Transaction) { | 10 | async function sendUpdateVideo (video: VideoModel, t: Transaction) { |
25 | const byAccount = video.VideoChannel.Account | 11 | const byActor = video.VideoChannel.Account.Actor |
26 | 12 | ||
27 | const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString()) | 13 | const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString()) |
28 | const videoObject = video.toActivityPubObject() | 14 | const videoObject = video.toActivityPubObject() |
29 | const data = await updateActivityData(url, byAccount, videoObject, t) | 15 | const audience = await getAudience(byActor, t, video.privacy === VideoPrivacy.PUBLIC) |
16 | |||
17 | const data = await updateActivityData(url, byActor, videoObject, t, audience) | ||
30 | 18 | ||
31 | const accountsInvolved = await VideoShareModel.loadAccountsByShare(video.id, t) | 19 | const actorsInvolved = await VideoShareModel.loadActorsByShare(video.id, t) |
32 | accountsInvolved.push(byAccount) | 20 | actorsInvolved.push(byActor) |
33 | 21 | ||
34 | return broadcastToFollowers(data, byAccount, accountsInvolved, t) | 22 | return broadcastToFollowers(data, byActor, actorsInvolved, t) |
35 | } | 23 | } |
36 | 24 | ||
37 | // --------------------------------------------------------------------------- | 25 | // --------------------------------------------------------------------------- |
38 | 26 | ||
39 | export { | 27 | export { |
40 | sendUpdateVideoChannel, | ||
41 | sendUpdateVideo | 28 | sendUpdateVideo |
42 | } | 29 | } |
43 | 30 | ||
44 | // --------------------------------------------------------------------------- | 31 | // --------------------------------------------------------------------------- |
45 | 32 | ||
46 | async function updateActivityData (url: string, byAccount: AccountModel, object: any, t: Transaction): Promise<ActivityUpdate> { | 33 | async function updateActivityData ( |
47 | const { to, cc } = await getAudience(byAccount, t) | 34 | url: string, |
35 | byActor: ActorModel, | ||
36 | object: any, | ||
37 | t: Transaction, | ||
38 | audience?: ActivityAudience | ||
39 | ): Promise<ActivityUpdate> { | ||
40 | if (!audience) { | ||
41 | audience = await getAudience(byActor, t) | ||
42 | } | ||
43 | |||
48 | return { | 44 | return { |
49 | type: 'Update', | 45 | type: 'Update', |
50 | id: url, | 46 | id: url, |
51 | actor: byAccount.url, | 47 | actor: byActor.url, |
52 | to, | 48 | to: audience.to, |
53 | cc, | 49 | cc: audience.cc, |
54 | object | 50 | object |
55 | } | 51 | } |
56 | } | 52 | } |
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 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { getServerAccount } from '../../helpers' | 2 | import { getServerActor } from '../../helpers' |
3 | import { VideoModel } from '../../models/video/video' | 3 | import { VideoModel } from '../../models/video/video' |
4 | import { VideoChannelModel } from '../../models/video/video-channel' | ||
5 | import { VideoChannelShareModel } from '../../models/video/video-channel-share' | ||
6 | import { VideoShareModel } from '../../models/video/video-share' | 4 | import { VideoShareModel } from '../../models/video/video-share' |
7 | import { sendVideoAnnounceToFollowers, sendVideoChannelAnnounceToFollowers } from './send' | 5 | import { sendVideoAnnounceToFollowers } from './send' |
8 | |||
9 | async function shareVideoChannelByServer (videoChannel: VideoChannelModel, t: Transaction) { | ||
10 | const serverAccount = await getServerAccount() | ||
11 | |||
12 | await VideoChannelShareModel.create({ | ||
13 | accountId: serverAccount.id, | ||
14 | videoChannelId: videoChannel.id | ||
15 | }, { transaction: t }) | ||
16 | |||
17 | return sendVideoChannelAnnounceToFollowers(serverAccount, videoChannel, t) | ||
18 | } | ||
19 | 6 | ||
20 | async function shareVideoByServer (video: VideoModel, t: Transaction) { | 7 | async function shareVideoByServer (video: VideoModel, t: Transaction) { |
21 | const serverAccount = await getServerAccount() | 8 | const serverActor = await getServerActor() |
22 | 9 | ||
23 | await VideoShareModel.create({ | 10 | await VideoShareModel.create({ |
24 | accountId: serverAccount.id, | 11 | actorId: serverActor.id, |
25 | videoId: video.id | 12 | videoId: video.id |
26 | }, { transaction: t }) | 13 | }, { transaction: t }) |
27 | 14 | ||
28 | return sendVideoAnnounceToFollowers(serverAccount, video, t) | 15 | return sendVideoAnnounceToFollowers(serverActor, video, t) |
29 | } | 16 | } |
30 | 17 | ||
31 | export { | 18 | export { |
32 | shareVideoChannelByServer, | ||
33 | shareVideoByServer | 19 | shareVideoByServer |
34 | } | 20 | } |
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 @@ | |||
1 | import { CONFIG } from '../../initializers' | 1 | import { CONFIG } from '../../initializers' |
2 | import { AccountModel } from '../../models/account/account' | 2 | import { ActorModel } from '../../models/activitypub/actor' |
3 | import { AccountFollowModel } from '../../models/account/account-follow' | 3 | import { ActorFollowModel } from '../../models/activitypub/actor-follow' |
4 | import { VideoModel } from '../../models/video/video' | 4 | import { VideoModel } from '../../models/video/video' |
5 | import { VideoAbuseModel } from '../../models/video/video-abuse' | 5 | import { VideoAbuseModel } from '../../models/video/video-abuse' |
6 | import { VideoChannelModel } from '../../models/video/video-channel' | ||
7 | 6 | ||
8 | function getVideoActivityPubUrl (video: VideoModel) { | 7 | function getVideoActivityPubUrl (video: VideoModel) { |
9 | return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid | 8 | return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid |
10 | } | 9 | } |
11 | 10 | ||
12 | function getVideoChannelActivityPubUrl (videoChannel: VideoChannelModel) { | 11 | function getVideoChannelActivityPubUrl (videoChannelUUID: string) { |
13 | return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannel.uuid | 12 | return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannelUUID |
13 | } | ||
14 | |||
15 | function getApplicationActivityPubUrl () { | ||
16 | return CONFIG.WEBSERVER.URL + '/application/peertube' | ||
14 | } | 17 | } |
15 | 18 | ||
16 | function getAccountActivityPubUrl (accountName: string) { | 19 | function getAccountActivityPubUrl (accountName: string) { |
@@ -21,34 +24,34 @@ function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseModel) { | |||
21 | return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id | 24 | return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id |
22 | } | 25 | } |
23 | 26 | ||
24 | function getVideoViewActivityPubUrl (byAccount: AccountModel, video: VideoModel) { | 27 | function getVideoViewActivityPubUrl (byActor: ActorModel, video: VideoModel) { |
25 | return video.url + '/views/' + byAccount.uuid + '/' + new Date().toISOString() | 28 | return video.url + '/views/' + byActor.uuid + '/' + new Date().toISOString() |
26 | } | 29 | } |
27 | 30 | ||
28 | function getVideoLikeActivityPubUrl (byAccount: AccountModel, video: VideoModel) { | 31 | function getVideoLikeActivityPubUrl (byActor: ActorModel, video: VideoModel) { |
29 | return byAccount.url + '/likes/' + video.id | 32 | return byActor.url + '/likes/' + video.id |
30 | } | 33 | } |
31 | 34 | ||
32 | function getVideoDislikeActivityPubUrl (byAccount: AccountModel, video: VideoModel) { | 35 | function getVideoDislikeActivityPubUrl (byActor: ActorModel, video: VideoModel) { |
33 | return byAccount.url + '/dislikes/' + video.id | 36 | return byActor.url + '/dislikes/' + video.id |
34 | } | 37 | } |
35 | 38 | ||
36 | function getAccountFollowActivityPubUrl (accountFollow: AccountFollowModel) { | 39 | function getActorFollowActivityPubUrl (actorFollow: ActorFollowModel) { |
37 | const me = accountFollow.AccountFollower | 40 | const me = actorFollow.ActorFollower |
38 | const following = accountFollow.AccountFollowing | 41 | const following = actorFollow.ActorFollowing |
39 | 42 | ||
40 | return me.url + '/follows/' + following.id | 43 | return me.url + '/follows/' + following.id |
41 | } | 44 | } |
42 | 45 | ||
43 | function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowModel) { | 46 | function getActorFollowAcceptActivityPubUrl (actorFollow: ActorFollowModel) { |
44 | const follower = accountFollow.AccountFollower | 47 | const follower = actorFollow.ActorFollower |
45 | const me = accountFollow.AccountFollowing | 48 | const me = actorFollow.ActorFollowing |
46 | 49 | ||
47 | return follower.url + '/accepts/follows/' + me.id | 50 | return follower.url + '/accepts/follows/' + me.id |
48 | } | 51 | } |
49 | 52 | ||
50 | function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountModel) { | 53 | function getAnnounceActivityPubUrl (originalUrl: string, byActor: ActorModel) { |
51 | return originalUrl + '/announces/' + byAccount.id | 54 | return originalUrl + '/announces/' + byActor.id |
52 | } | 55 | } |
53 | 56 | ||
54 | function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) { | 57 | function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) { |
@@ -60,12 +63,13 @@ function getUndoActivityPubUrl (originalUrl: string) { | |||
60 | } | 63 | } |
61 | 64 | ||
62 | export { | 65 | export { |
66 | getApplicationActivityPubUrl, | ||
63 | getVideoActivityPubUrl, | 67 | getVideoActivityPubUrl, |
64 | getVideoChannelActivityPubUrl, | 68 | getVideoChannelActivityPubUrl, |
65 | getAccountActivityPubUrl, | 69 | getAccountActivityPubUrl, |
66 | getVideoAbuseActivityPubUrl, | 70 | getVideoAbuseActivityPubUrl, |
67 | getAccountFollowActivityPubUrl, | 71 | getActorFollowActivityPubUrl, |
68 | getAccountFollowAcceptActivityPubUrl, | 72 | getActorFollowAcceptActivityPubUrl, |
69 | getAnnounceActivityPubUrl, | 73 | getAnnounceActivityPubUrl, |
70 | getUpdateActivityPubUrl, | 74 | getUpdateActivityPubUrl, |
71 | getUndoActivityPubUrl, | 75 | 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 @@ | |||
1 | import { VideoChannelObject } from '../../../shared/models/activitypub/objects' | ||
2 | import { doRequest, logger } from '../../helpers' | ||
3 | import { isVideoChannelObjectValid } from '../../helpers/custom-validators/activitypub' | ||
4 | import { ACTIVITY_PUB } from '../../initializers' | ||
5 | import { AccountModel } from '../../models/account/account' | ||
6 | import { VideoChannelModel } from '../../models/video/video-channel' | ||
7 | import { videoChannelActivityObjectToDBAttributes } from './process/misc' | ||
8 | |||
9 | async function getOrCreateVideoChannel (ownerAccount: AccountModel, videoChannelUrl: string) { | ||
10 | let videoChannel = await VideoChannelModel.loadByUrl(videoChannelUrl) | ||
11 | |||
12 | // We don't have this account in our database, fetch it on remote | ||
13 | if (!videoChannel) { | ||
14 | videoChannel = await fetchRemoteVideoChannel(ownerAccount, videoChannelUrl) | ||
15 | if (videoChannel === undefined) throw new Error('Cannot fetch remote video channel.') | ||
16 | |||
17 | // Save our new video channel in database | ||
18 | await videoChannel.save() | ||
19 | } | ||
20 | |||
21 | return videoChannel | ||
22 | } | ||
23 | |||
24 | async function fetchRemoteVideoChannel (ownerAccount: AccountModel, videoChannelUrl: string) { | ||
25 | const options = { | ||
26 | uri: videoChannelUrl, | ||
27 | method: 'GET', | ||
28 | headers: { | ||
29 | 'Accept': ACTIVITY_PUB.ACCEPT_HEADER | ||
30 | } | ||
31 | } | ||
32 | |||
33 | logger.info('Fetching remote video channel %s.', videoChannelUrl) | ||
34 | |||
35 | let requestResult | ||
36 | try { | ||
37 | requestResult = await doRequest(options) | ||
38 | } catch (err) { | ||
39 | logger.warn('Cannot fetch remote video channel %s.', videoChannelUrl, err) | ||
40 | return undefined | ||
41 | } | ||
42 | |||
43 | const videoChannelJSON: VideoChannelObject = JSON.parse(requestResult.body) | ||
44 | if (isVideoChannelObjectValid(videoChannelJSON) === false) { | ||
45 | logger.debug('Remote video channel JSON is not valid.', { videoChannelJSON }) | ||
46 | return undefined | ||
47 | } | ||
48 | |||
49 | const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount) | ||
50 | const videoChannel = new VideoChannelModel(videoChannelAttributes) | ||
51 | videoChannel.Account = ownerAccount | ||
52 | |||
53 | return videoChannel | ||
54 | } | ||
55 | |||
56 | export { | ||
57 | getOrCreateVideoChannel, | ||
58 | fetchRemoteVideoChannel | ||
59 | } | ||
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 { | |||
19 | 19 | ||
20 | function fetchRemoteVideoPreview (video: VideoModel) { | 20 | function fetchRemoteVideoPreview (video: VideoModel) { |
21 | // FIXME: use url | 21 | // FIXME: use url |
22 | const host = video.VideoChannel.Account.Server.host | 22 | const host = video.VideoChannel.Account.Actor.Server.host |
23 | const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName()) | 23 | const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName()) |
24 | 24 | ||
25 | return request.get(REMOTE_SCHEME.HTTP + '://' + host + path) | 25 | return request.get(REMOTE_SCHEME.HTTP + '://' + host + path) |
@@ -27,7 +27,7 @@ function fetchRemoteVideoPreview (video: VideoModel) { | |||
27 | 27 | ||
28 | async function fetchRemoteVideoDescription (video: VideoModel) { | 28 | async function fetchRemoteVideoDescription (video: VideoModel) { |
29 | // FIXME: use url | 29 | // FIXME: use url |
30 | const host = video.VideoChannel.Account.Server.host | 30 | const host = video.VideoChannel.Account.Actor.Server.host |
31 | const path = video.getDescriptionPath() | 31 | const path = video.getDescriptionPath() |
32 | const options = { | 32 | const options = { |
33 | uri: REMOTE_SCHEME.HTTP + '://' + host + path, | 33 | uri: REMOTE_SCHEME.HTTP + '://' + host + path, |
@@ -50,43 +50,47 @@ function generateThumbnailFromUrl (video: VideoModel, icon: ActivityIconObject) | |||
50 | } | 50 | } |
51 | 51 | ||
52 | async function sendVideoRateChangeToFollowers ( | 52 | async function sendVideoRateChangeToFollowers ( |
53 | account: AccountModel, | 53 | account: AccountModel, |
54 | video: VideoModel, | 54 | video: VideoModel, |
55 | likes: number, | 55 | likes: number, |
56 | dislikes: number, | 56 | dislikes: number, |
57 | t: Transaction | 57 | t: Transaction |
58 | ) { | 58 | ) { |
59 | const actor = account.Actor | ||
60 | |||
59 | // Keep the order: first we undo and then we create | 61 | // Keep the order: first we undo and then we create |
60 | 62 | ||
61 | // Undo Like | 63 | // Undo Like |
62 | if (likes < 0) await sendUndoLikeToVideoFollowers(account, video, t) | 64 | if (likes < 0) await sendUndoLikeToVideoFollowers(actor, video, t) |
63 | // Undo Dislike | 65 | // Undo Dislike |
64 | if (dislikes < 0) await sendUndoDislikeToVideoFollowers(account, video, t) | 66 | if (dislikes < 0) await sendUndoDislikeToVideoFollowers(actor, video, t) |
65 | 67 | ||
66 | // Like | 68 | // Like |
67 | if (likes > 0) await sendLikeToVideoFollowers(account, video, t) | 69 | if (likes > 0) await sendLikeToVideoFollowers(actor, video, t) |
68 | // Dislike | 70 | // Dislike |
69 | if (dislikes > 0) await sendCreateDislikeToVideoFollowers(account, video, t) | 71 | if (dislikes > 0) await sendCreateDislikeToVideoFollowers(actor, video, t) |
70 | } | 72 | } |
71 | 73 | ||
72 | async function sendVideoRateChangeToOrigin ( | 74 | async function sendVideoRateChangeToOrigin ( |
73 | account: AccountModel, | 75 | account: AccountModel, |
74 | video: VideoModel, | 76 | video: VideoModel, |
75 | likes: number, | 77 | likes: number, |
76 | dislikes: number, | 78 | dislikes: number, |
77 | t: Transaction | 79 | t: Transaction |
78 | ) { | 80 | ) { |
81 | const actor = account.Actor | ||
82 | |||
79 | // Keep the order: first we undo and then we create | 83 | // Keep the order: first we undo and then we create |
80 | 84 | ||
81 | // Undo Like | 85 | // Undo Like |
82 | if (likes < 0) await sendUndoLikeToOrigin(account, video, t) | 86 | if (likes < 0) await sendUndoLikeToOrigin(actor, video, t) |
83 | // Undo Dislike | 87 | // Undo Dislike |
84 | if (dislikes < 0) await sendUndoDislikeToOrigin(account, video, t) | 88 | if (dislikes < 0) await sendUndoDislikeToOrigin(actor, video, t) |
85 | 89 | ||
86 | // Like | 90 | // Like |
87 | if (likes > 0) await sendLikeToOrigin(account, video, t) | 91 | if (likes > 0) await sendLikeToOrigin(actor, video, t) |
88 | // Dislike | 92 | // Dislike |
89 | if (dislikes > 0) await sendCreateDislikeToOrigin(account, video, t) | 93 | if (dislikes > 0) await sendCreateDislikeToOrigin(actor, video, t) |
90 | } | 94 | } |
91 | 95 | ||
92 | export { | 96 | export { |
diff --git a/server/lib/index.ts b/server/lib/index.ts deleted file mode 100644 index d22ecb665..000000000 --- a/server/lib/index.ts +++ /dev/null | |||
@@ -1,6 +0,0 @@ | |||
1 | export * from './activitypub' | ||
2 | export * from './cache' | ||
3 | export * from './jobs' | ||
4 | export * from './oauth-model' | ||
5 | export * from './user' | ||
6 | export * from './video-channel' | ||
diff --git a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts index 76da5b724..95a5d3ff2 100644 --- a/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts +++ b/server/lib/jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { JobCategory } from '../../../../shared' | 1 | import { JobCategory } from '../../../../shared' |
2 | import { buildSignedActivity, logger } from '../../../helpers' | 2 | import { buildSignedActivity, logger } from '../../../helpers' |
3 | import { ACTIVITY_PUB } from '../../../initializers' | 3 | import { ACTIVITY_PUB } from '../../../initializers' |
4 | import { AccountModel } from '../../../models/account/account' | 4 | import { ActorModel } from '../../../models/activitypub/actor' |
5 | import { JobHandler, JobScheduler } from '../job-scheduler' | 5 | import { JobHandler, JobScheduler } from '../job-scheduler' |
6 | 6 | ||
7 | import * as activitypubHttpBroadcastHandler from './activitypub-http-broadcast-handler' | 7 | import * as activitypubHttpBroadcastHandler from './activitypub-http-broadcast-handler' |
@@ -10,7 +10,7 @@ import * as activitypubHttpUnicastHandler from './activitypub-http-unicast-handl | |||
10 | 10 | ||
11 | type ActivityPubHttpPayload = { | 11 | type ActivityPubHttpPayload = { |
12 | uris: string[] | 12 | uris: string[] |
13 | signatureAccountId?: number | 13 | signatureActorId?: number |
14 | body?: any | 14 | body?: any |
15 | attemptNumber?: number | 15 | attemptNumber?: number |
16 | } | 16 | } |
@@ -44,10 +44,10 @@ function maybeRetryRequestLater (err: Error, payload: ActivityPubHttpPayload, ur | |||
44 | async function computeBody (payload: ActivityPubHttpPayload) { | 44 | async function computeBody (payload: ActivityPubHttpPayload) { |
45 | let body = payload.body | 45 | let body = payload.body |
46 | 46 | ||
47 | if (payload.signatureAccountId) { | 47 | if (payload.signatureActorId) { |
48 | const accountSignature = await AccountModel.load(payload.signatureAccountId) | 48 | const actorSignature = await ActorModel.load(payload.signatureActorId) |
49 | if (!accountSignature) throw new Error('Unknown signature account id.') | 49 | if (!actorSignature) throw new Error('Unknown signature account id.') |
50 | body = await buildSignedActivity(accountSignature, payload.body) | 50 | body = await buildSignedActivity(actorSignature, payload.body) |
51 | } | 51 | } |
52 | 52 | ||
53 | return body | 53 | return body |
diff --git a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts index 1786ce971..7df048006 100644 --- a/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts +++ b/server/lib/jobs/transcoding-job-scheduler/video-file-optimizer-handler.ts | |||
@@ -3,7 +3,7 @@ import { computeResolutionsToTranscode, logger } from '../../../helpers' | |||
3 | import { sequelizeTypescript } from '../../../initializers' | 3 | import { sequelizeTypescript } from '../../../initializers' |
4 | import { VideoModel } from '../../../models/video/video' | 4 | import { VideoModel } from '../../../models/video/video' |
5 | import { shareVideoByServer } from '../../activitypub' | 5 | import { shareVideoByServer } from '../../activitypub' |
6 | import { sendAddVideo } from '../../activitypub/send' | 6 | import { sendCreateVideo } from '../../activitypub/send' |
7 | import { JobScheduler } from '../job-scheduler' | 7 | import { JobScheduler } from '../job-scheduler' |
8 | import { TranscodingJobPayload } from './transcoding-job-scheduler' | 8 | import { TranscodingJobPayload } from './transcoding-job-scheduler' |
9 | 9 | ||
@@ -36,7 +36,8 @@ async function onSuccess (jobId: number, video: VideoModel, jobScheduler: JobSch | |||
36 | if (!videoDatabase) return undefined | 36 | if (!videoDatabase) return undefined |
37 | 37 | ||
38 | // Now we'll add the video's meta data to our followers | 38 | // Now we'll add the video's meta data to our followers |
39 | await sendAddVideo(video, undefined) | 39 | await sendCreateVideo(video, undefined) |
40 | // TODO: share by channel | ||
40 | await shareVideoByServer(video, undefined) | 41 | await shareVideoByServer(video, undefined) |
41 | 42 | ||
42 | const originalFileHeight = await videoDatabase.getOriginalFileHeight() | 43 | const originalFileHeight = await videoDatabase.getOriginalFileHeight() |
diff --git a/server/lib/user.ts b/server/lib/user.ts index 6aeb198b9..ec1466c6f 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts | |||
@@ -1,10 +1,9 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { createPrivateAndPublicKeys, logger } from '../helpers' | 2 | import { ActivityPubActorType } from '../../shared/models/activitypub' |
3 | import { CONFIG, sequelizeTypescript } from '../initializers' | 3 | import { sequelizeTypescript, SERVER_ACTOR_NAME } from '../initializers' |
4 | import { AccountModel } from '../models/account/account' | 4 | import { AccountModel } from '../models/account/account' |
5 | import { UserModel } from '../models/account/user' | 5 | import { UserModel } from '../models/account/user' |
6 | import { ActorModel } from '../models/activitypub/actor' | 6 | import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' |
7 | import { getAccountActivityPubUrl } from './activitypub' | ||
8 | import { createVideoChannel } from './video-channel' | 7 | import { createVideoChannel } from './video-channel' |
9 | 8 | ||
10 | async function createUserAccountAndChannel (user: UserModel, validateUser = true) { | 9 | async function createUserAccountAndChannel (user: UserModel, validateUser = true) { |
@@ -26,31 +25,22 @@ async function createUserAccountAndChannel (user: UserModel, validateUser = true | |||
26 | return { account: accountCreated, videoChannel } | 25 | return { account: accountCreated, videoChannel } |
27 | }) | 26 | }) |
28 | 27 | ||
29 | // Set account keys, this could be long so process after the account creation and do not block the client | 28 | account.Actor = await setAsyncActorKeys(account.Actor) |
30 | const { publicKey, privateKey } = await createPrivateAndPublicKeys() | 29 | videoChannel.Actor = await setAsyncActorKeys(videoChannel.Actor) |
31 | const actor = account.Actor | ||
32 | actor.set('publicKey', publicKey) | ||
33 | actor.set('privateKey', privateKey) | ||
34 | actor.save().catch(err => logger.error('Cannot set public/private keys of actor %d.', actor.uuid, err)) | ||
35 | 30 | ||
36 | return { account, videoChannel } | 31 | return { account, videoChannel } |
37 | } | 32 | } |
38 | 33 | ||
39 | async function createLocalAccountWithoutKeys (name: string, userId: number, applicationId: number, t: Sequelize.Transaction) { | 34 | async function createLocalAccountWithoutKeys ( |
35 | name: string, | ||
36 | userId: number, | ||
37 | applicationId: number, | ||
38 | t: Sequelize.Transaction, | ||
39 | type: ActivityPubActorType= 'Person' | ||
40 | ) { | ||
40 | const url = getAccountActivityPubUrl(name) | 41 | const url = getAccountActivityPubUrl(name) |
41 | 42 | ||
42 | const actorInstance = new ActorModel({ | 43 | const actorInstance = buildActorInstance(type, url, name) |
43 | url, | ||
44 | publicKey: null, | ||
45 | privateKey: null, | ||
46 | followersCount: 0, | ||
47 | followingCount: 0, | ||
48 | inboxUrl: url + '/inbox', | ||
49 | outboxUrl: url + '/outbox', | ||
50 | sharedInboxUrl: CONFIG.WEBSERVER.URL + '/inbox', | ||
51 | followersUrl: url + '/followers', | ||
52 | followingUrl: url + '/following' | ||
53 | }) | ||
54 | const actorInstanceCreated = await actorInstance.save({ transaction: t }) | 44 | const actorInstanceCreated = await actorInstance.save({ transaction: t }) |
55 | 45 | ||
56 | const accountInstance = new AccountModel({ | 46 | const accountInstance = new AccountModel({ |
@@ -67,9 +57,18 @@ async function createLocalAccountWithoutKeys (name: string, userId: number, appl | |||
67 | return accountInstanceCreated | 57 | return accountInstanceCreated |
68 | } | 58 | } |
69 | 59 | ||
60 | async function createApplicationActor (applicationId: number) { | ||
61 | const accountCreated = await createLocalAccountWithoutKeys(SERVER_ACTOR_NAME, null, applicationId, undefined, 'Application') | ||
62 | |||
63 | accountCreated.Actor = await setAsyncActorKeys(accountCreated.Actor) | ||
64 | |||
65 | return accountCreated | ||
66 | } | ||
67 | |||
70 | // --------------------------------------------------------------------------- | 68 | // --------------------------------------------------------------------------- |
71 | 69 | ||
72 | export { | 70 | export { |
71 | createApplicationActor, | ||
73 | createUserAccountAndChannel, | 72 | createUserAccountAndChannel, |
74 | createLocalAccountWithoutKeys | 73 | createLocalAccountWithoutKeys |
75 | } | 74 | } |
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index 97924aa9e..569b8f29d 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts | |||
@@ -1,26 +1,33 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import * as uuidv4 from 'uuid/v4' | ||
2 | import { VideoChannelCreate } from '../../shared/models' | 3 | import { VideoChannelCreate } from '../../shared/models' |
3 | import { AccountModel } from '../models/account/account' | 4 | import { AccountModel } from '../models/account/account' |
4 | import { VideoChannelModel } from '../models/video/video-channel' | 5 | import { VideoChannelModel } from '../models/video/video-channel' |
5 | import { getVideoChannelActivityPubUrl } from './activitypub' | 6 | import { buildActorInstance, getVideoChannelActivityPubUrl } from './activitypub' |
6 | 7 | ||
7 | async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { | 8 | async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountModel, t: Sequelize.Transaction) { |
9 | const uuid = uuidv4() | ||
10 | const url = getVideoChannelActivityPubUrl(uuid) | ||
11 | // We use the name as uuid | ||
12 | const actorInstance = buildActorInstance('Group', url, uuid, uuid) | ||
13 | |||
14 | const actorInstanceCreated = await actorInstance.save({ transaction: t }) | ||
15 | |||
8 | const videoChannelData = { | 16 | const videoChannelData = { |
9 | name: videoChannelInfo.name, | 17 | name: videoChannelInfo.name, |
10 | description: videoChannelInfo.description, | 18 | description: videoChannelInfo.description, |
11 | remote: false, | 19 | accountId: account.id, |
12 | accountId: account.id | 20 | actorId: actorInstanceCreated.id |
13 | } | 21 | } |
14 | 22 | ||
15 | const videoChannel = VideoChannelModel.build(videoChannelData) | 23 | const videoChannel = VideoChannelModel.build(videoChannelData) |
16 | videoChannel.set('url', getVideoChannelActivityPubUrl(videoChannel)) | ||
17 | 24 | ||
18 | const options = { transaction: t } | 25 | const options = { transaction: t } |
19 | |||
20 | const videoChannelCreated = await videoChannel.save(options) | 26 | const videoChannelCreated = await videoChannel.save(options) |
21 | 27 | ||
22 | // Do not forget to add Account information to the created video channel | 28 | // Do not forget to add Account/Actor information to the created video channel |
23 | videoChannelCreated.Account = account | 29 | videoChannelCreated.Account = account |
30 | videoChannelCreated.Actor = actorInstanceCreated | ||
24 | 31 | ||
25 | // No need to seed this empty video channel to followers | 32 | // No need to seed this empty video channel to followers |
26 | return videoChannelCreated | 33 | return videoChannelCreated |