diff options
Diffstat (limited to 'server/lib')
22 files changed, 334 insertions, 26 deletions
diff --git a/server/lib/activitypub/account.ts b/server/lib/activitypub/account.ts new file mode 100644 index 000000000..704a92e13 --- /dev/null +++ b/server/lib/activitypub/account.ts | |||
@@ -0,0 +1,104 @@ | |||
1 | import * as url from 'url' | ||
2 | import { ActivityPubActor } from '../../../shared/models/activitypub/activitypub-actor' | ||
3 | import { isRemoteAccountValid } from '../../helpers/custom-validators/activitypub/account' | ||
4 | import { logger } from '../../helpers/logger' | ||
5 | import { doRequest } from '../../helpers/requests' | ||
6 | import { ACTIVITY_PUB } from '../../initializers/constants' | ||
7 | import { database as db } from '../../initializers/database' | ||
8 | |||
9 | async function getOrCreateAccount (accountUrl: string) { | ||
10 | let account = await db.Account.loadByUrl(accountUrl) | ||
11 | |||
12 | // We don't have this account in our database, fetch it on remote | ||
13 | if (!account) { | ||
14 | const res = await fetchRemoteAccountAndCreateServer(accountUrl) | ||
15 | if (res === undefined) throw new Error('Cannot fetch remote account.') | ||
16 | |||
17 | // Save our new account in database | ||
18 | account = await res.account.save() | ||
19 | } | ||
20 | |||
21 | return account | ||
22 | } | ||
23 | |||
24 | async function fetchRemoteAccountAndCreateServer (accountUrl: string) { | ||
25 | const options = { | ||
26 | uri: accountUrl, | ||
27 | method: 'GET', | ||
28 | headers: { | ||
29 | 'Accept': ACTIVITY_PUB.ACCEPT_HEADER | ||
30 | } | ||
31 | } | ||
32 | |||
33 | logger.info('Fetching remote account %s.', accountUrl) | ||
34 | |||
35 | let requestResult | ||
36 | try { | ||
37 | requestResult = await doRequest(options) | ||
38 | } catch (err) { | ||
39 | logger.warn('Cannot fetch remote account %s.', accountUrl, err) | ||
40 | return undefined | ||
41 | } | ||
42 | |||
43 | const accountJSON: ActivityPubActor = JSON.parse(requestResult.body) | ||
44 | if (isRemoteAccountValid(accountJSON) === false) { | ||
45 | logger.debug('Remote account JSON is not valid.', { accountJSON }) | ||
46 | return undefined | ||
47 | } | ||
48 | |||
49 | const followersCount = await fetchAccountCount(accountJSON.followers) | ||
50 | const followingCount = await fetchAccountCount(accountJSON.following) | ||
51 | |||
52 | const account = db.Account.build({ | ||
53 | uuid: accountJSON.uuid, | ||
54 | name: accountJSON.preferredUsername, | ||
55 | url: accountJSON.url, | ||
56 | publicKey: accountJSON.publicKey.publicKeyPem, | ||
57 | privateKey: null, | ||
58 | followersCount: followersCount, | ||
59 | followingCount: followingCount, | ||
60 | inboxUrl: accountJSON.inbox, | ||
61 | outboxUrl: accountJSON.outbox, | ||
62 | sharedInboxUrl: accountJSON.endpoints.sharedInbox, | ||
63 | followersUrl: accountJSON.followers, | ||
64 | followingUrl: accountJSON.following | ||
65 | }) | ||
66 | |||
67 | const accountHost = url.parse(account.url).host | ||
68 | const serverOptions = { | ||
69 | where: { | ||
70 | host: accountHost | ||
71 | }, | ||
72 | defaults: { | ||
73 | host: accountHost | ||
74 | } | ||
75 | } | ||
76 | const [ server ] = await db.Server.findOrCreate(serverOptions) | ||
77 | account.set('serverId', server.id) | ||
78 | |||
79 | return { account, server } | ||
80 | } | ||
81 | |||
82 | export { | ||
83 | getOrCreateAccount, | ||
84 | fetchRemoteAccountAndCreateServer | ||
85 | } | ||
86 | |||
87 | // --------------------------------------------------------------------------- | ||
88 | |||
89 | async function fetchAccountCount (url: string) { | ||
90 | const options = { | ||
91 | uri: url, | ||
92 | method: 'GET' | ||
93 | } | ||
94 | |||
95 | let requestResult | ||
96 | try { | ||
97 | requestResult = await doRequest(options) | ||
98 | } catch (err) { | ||
99 | logger.warn('Cannot fetch remote account count %s.', url, err) | ||
100 | return undefined | ||
101 | } | ||
102 | |||
103 | return requestResult.totalItems ? requestResult.totalItems : 0 | ||
104 | } | ||
diff --git a/server/lib/activitypub/index.ts b/server/lib/activitypub/index.ts index 1bea0a412..b1daa70bb 100644 --- a/server/lib/activitypub/index.ts +++ b/server/lib/activitypub/index.ts | |||
@@ -1,2 +1,7 @@ | |||
1 | export * from './process' | 1 | export * from './process' |
2 | export * from './send' | 2 | export * from './send' |
3 | export * from './account' | ||
4 | export * from './share' | ||
5 | export * from './video-channels' | ||
6 | export * from './videos' | ||
7 | export * from './url' | ||
diff --git a/server/lib/activitypub/process/process-add.ts b/server/lib/activitypub/process/process-add.ts index f064c1ab6..281036228 100644 --- a/server/lib/activitypub/process/process-add.ts +++ b/server/lib/activitypub/process/process-add.ts | |||
@@ -1,11 +1,14 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { VideoTorrentObject } from '../../../../shared' | 2 | import { VideoTorrentObject } from '../../../../shared' |
3 | import { ActivityAdd } from '../../../../shared/models/activitypub/activity' | 3 | import { ActivityAdd } from '../../../../shared/models/activitypub/activity' |
4 | import { generateThumbnailFromUrl, getOrCreateAccount, logger, retryTransactionWrapper } from '../../../helpers' | 4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
5 | import { getOrCreateVideoChannel } from '../../../helpers/activitypub' | 5 | import { logger } from '../../../helpers/logger' |
6 | import { database as db } from '../../../initializers' | 6 | import { database as db } from '../../../initializers' |
7 | import { AccountInstance } from '../../../models/account/account-interface' | 7 | import { AccountInstance } from '../../../models/account/account-interface' |
8 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' | 8 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' |
9 | import { getOrCreateAccount } from '../account' | ||
10 | import { getOrCreateVideoChannel } from '../video-channels' | ||
11 | import { generateThumbnailFromUrl } from '../videos' | ||
9 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' | 12 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' |
10 | 13 | ||
11 | async function processAddActivity (activity: ActivityAdd) { | 14 | async function processAddActivity (activity: ActivityAdd) { |
@@ -41,12 +44,10 @@ function processAddVideo (account: AccountInstance, activity: ActivityAdd, video | |||
41 | return retryTransactionWrapper(addRemoteVideo, options) | 44 | return retryTransactionWrapper(addRemoteVideo, options) |
42 | } | 45 | } |
43 | 46 | ||
44 | function addRemoteVideo ( | 47 | function addRemoteVideo (account: AccountInstance, |
45 | account: AccountInstance, | 48 | activity: ActivityAdd, |
46 | activity: ActivityAdd, | 49 | videoChannel: VideoChannelInstance, |
47 | videoChannel: VideoChannelInstance, | 50 | videoToCreateData: VideoTorrentObject) { |
48 | videoToCreateData: VideoTorrentObject | ||
49 | ) { | ||
50 | logger.debug('Adding remote video %s.', videoToCreateData.url) | 51 | logger.debug('Adding remote video %s.', videoToCreateData.url) |
51 | 52 | ||
52 | return db.sequelize.transaction(async t => { | 53 | return db.sequelize.transaction(async t => { |
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts index 656db08a9..40712ef03 100644 --- a/server/lib/activitypub/process/process-announce.ts +++ b/server/lib/activitypub/process/process-announce.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import { ActivityAnnounce } from '../../../../shared/models/activitypub/activity' | 1 | import { ActivityAnnounce } from '../../../../shared/models/activitypub/activity' |
2 | import { getOrCreateAccount } from '../../../helpers/activitypub' | ||
3 | import { logger } from '../../../helpers/logger' | 2 | import { logger } from '../../../helpers/logger' |
4 | import { database as db } from '../../../initializers/index' | 3 | import { database as db } from '../../../initializers/index' |
5 | import { VideoInstance } from '../../../models/index' | 4 | import { VideoInstance } from '../../../models/index' |
6 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' | 5 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' |
7 | import { processAddActivity } from './process-add' | 6 | import { processAddActivity } from './process-add' |
8 | import { processCreateActivity } from './process-create' | 7 | import { processCreateActivity } from './process-create' |
8 | import { getOrCreateAccount } from '../account' | ||
9 | 9 | ||
10 | async function processAnnounceActivity (activity: ActivityAnnounce) { | 10 | async function processAnnounceActivity (activity: ActivityAnnounce) { |
11 | const announcedActivity = activity.object | 11 | const announcedActivity = activity.object |
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index aac941a6c..fc635eb1f 100644 --- a/server/lib/activitypub/process/process-create.ts +++ b/server/lib/activitypub/process/process-create.ts | |||
@@ -1,9 +1,10 @@ | |||
1 | import { ActivityCreate, VideoChannelObject } from '../../../../shared' | 1 | import { ActivityCreate, VideoChannelObject } from '../../../../shared' |
2 | import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object' | 2 | import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object' |
3 | import { logger, retryTransactionWrapper } from '../../../helpers' | 3 | import { logger, retryTransactionWrapper } from '../../../helpers' |
4 | import { getOrCreateAccount, getVideoChannelActivityPubUrl } from '../../../helpers/activitypub' | ||
5 | import { database as db } from '../../../initializers' | 4 | import { database as db } from '../../../initializers' |
6 | import { AccountInstance } from '../../../models/account/account-interface' | 5 | import { AccountInstance } from '../../../models/account/account-interface' |
6 | import { getOrCreateAccount } from '../account' | ||
7 | import { getVideoChannelActivityPubUrl } from '../url' | ||
7 | import { videoChannelActivityObjectToDBAttributes } from './misc' | 8 | import { videoChannelActivityObjectToDBAttributes } from './misc' |
8 | 9 | ||
9 | async function processCreateActivity (activity: ActivityCreate) { | 10 | async function processCreateActivity (activity: ActivityCreate) { |
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index af5d964d4..0328d1a7d 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts | |||
@@ -1,11 +1,11 @@ | |||
1 | import { ActivityDelete } from '../../../../shared/models/activitypub/activity' | 1 | import { ActivityDelete } from '../../../../shared/models/activitypub/activity' |
2 | import { getOrCreateAccount } from '../../../helpers/activitypub' | ||
3 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
4 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
5 | import { database as db } from '../../../initializers' | 4 | import { database as db } from '../../../initializers' |
6 | import { AccountInstance } from '../../../models/account/account-interface' | 5 | import { AccountInstance } from '../../../models/account/account-interface' |
7 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' | 6 | import { VideoChannelInstance } from '../../../models/video/video-channel-interface' |
8 | import { VideoInstance } from '../../../models/video/video-interface' | 7 | import { VideoInstance } from '../../../models/video/video-interface' |
8 | import { getOrCreateAccount } from '../account' | ||
9 | 9 | ||
10 | async function processDeleteActivity (activity: ActivityDelete) { | 10 | async function processDeleteActivity (activity: ActivityDelete) { |
11 | const account = await getOrCreateAccount(activity.actor) | 11 | const account = await getOrCreateAccount(activity.actor) |
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index 553639580..41b38828c 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts | |||
@@ -1,9 +1,10 @@ | |||
1 | import { ActivityFollow } from '../../../../shared/models/activitypub/activity' | 1 | import { ActivityFollow } from '../../../../shared/models/activitypub/activity' |
2 | import { getOrCreateAccount, retryTransactionWrapper } from '../../../helpers' | 2 | import { retryTransactionWrapper } from '../../../helpers' |
3 | import { database as db } from '../../../initializers' | 3 | import { database as db } from '../../../initializers' |
4 | import { AccountInstance } from '../../../models/account/account-interface' | 4 | import { AccountInstance } from '../../../models/account/account-interface' |
5 | import { logger } from '../../../helpers/logger' | 5 | import { logger } from '../../../helpers/logger' |
6 | import { sendAccept } from '../send/send-accept' | 6 | import { sendAccept } from '../send/send-accept' |
7 | import { getOrCreateAccount } from '../account' | ||
7 | 8 | ||
8 | async function processFollowActivity (activity: ActivityFollow) { | 9 | async function processFollowActivity (activity: ActivityFollow) { |
9 | const activityObject = activity.object | 10 | const activityObject = activity.object |
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index a3bfb1baf..4876735b8 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts | |||
@@ -1,6 +1,5 @@ | |||
1 | import { VideoChannelObject, VideoTorrentObject } from '../../../../shared' | 1 | import { VideoChannelObject, VideoTorrentObject } from '../../../../shared' |
2 | import { ActivityUpdate } from '../../../../shared/models/activitypub/activity' | 2 | import { ActivityUpdate } from '../../../../shared/models/activitypub/activity' |
3 | import { getOrCreateAccount } from '../../../helpers/activitypub' | ||
4 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 3 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
5 | import { logger } from '../../../helpers/logger' | 4 | import { logger } from '../../../helpers/logger' |
6 | import { resetSequelizeInstance } from '../../../helpers/utils' | 5 | import { resetSequelizeInstance } from '../../../helpers/utils' |
@@ -9,6 +8,7 @@ import { AccountInstance } from '../../../models/account/account-interface' | |||
9 | import { VideoInstance } from '../../../models/video/video-interface' | 8 | import { VideoInstance } from '../../../models/video/video-interface' |
10 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' | 9 | import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' |
11 | import Bluebird = require('bluebird') | 10 | import Bluebird = require('bluebird') |
11 | import { getOrCreateAccount } from '../account' | ||
12 | 12 | ||
13 | async function processUpdateActivity (activity: ActivityUpdate) { | 13 | async function processUpdateActivity (activity: ActivityUpdate) { |
14 | const account = await getOrCreateAccount(activity.actor) | 14 | const account = await getOrCreateAccount(activity.actor) |
diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts index 0324a30fa..eeab19ed0 100644 --- a/server/lib/activitypub/send/send-accept.ts +++ b/server/lib/activitypub/send/send-accept.ts | |||
@@ -3,7 +3,7 @@ import { ActivityAccept } from '../../../../shared/models/activitypub/activity' | |||
3 | import { AccountInstance } from '../../../models' | 3 | import { AccountInstance } from '../../../models' |
4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' | 4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' |
5 | import { unicastTo } from './misc' | 5 | import { unicastTo } from './misc' |
6 | import { getAccountFollowAcceptActivityPubUrl } from '../../../helpers/activitypub' | 6 | import { getAccountFollowAcceptActivityPubUrl } from '../url' |
7 | 7 | ||
8 | async function sendAccept (accountFollow: AccountFollowInstance, t: Transaction) { | 8 | async function sendAccept (accountFollow: AccountFollowInstance, t: Transaction) { |
9 | const follower = accountFollow.AccountFollower | 9 | const follower = accountFollow.AccountFollower |
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts index b9217e4f6..4b3a4ef75 100644 --- a/server/lib/activitypub/send/send-announce.ts +++ b/server/lib/activitypub/send/send-announce.ts | |||
@@ -4,7 +4,7 @@ import { VideoChannelInstance } from '../../../models/video/video-channel-interf | |||
4 | import { broadcastToFollowers } from './misc' | 4 | import { broadcastToFollowers } from './misc' |
5 | import { addActivityData } from './send-add' | 5 | import { addActivityData } from './send-add' |
6 | import { createActivityData } from './send-create' | 6 | import { createActivityData } from './send-create' |
7 | import { getAnnounceActivityPubUrl } from '../../../helpers/activitypub' | 7 | import { getAnnounceActivityPubUrl } from '../url' |
8 | 8 | ||
9 | async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) { | 9 | async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) { |
10 | const url = getAnnounceActivityPubUrl(video.url, byAccount) | 10 | const url = getAnnounceActivityPubUrl(video.url, byAccount) |
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index 66bfeee89..14b666fc9 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts | |||
@@ -3,7 +3,7 @@ import { ActivityCreate } from '../../../../shared/models/activitypub/activity' | |||
3 | import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models' | 3 | import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models' |
4 | import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface' | 4 | import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface' |
5 | import { broadcastToFollowers, getAudience, unicastTo } from './misc' | 5 | import { broadcastToFollowers, getAudience, unicastTo } from './misc' |
6 | import { getVideoAbuseActivityPubUrl } from '../../../helpers/activitypub' | 6 | import { getVideoAbuseActivityPubUrl } from '../url' |
7 | 7 | ||
8 | async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { | 8 | async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { |
9 | const byAccount = videoChannel.Account | 9 | const byAccount = videoChannel.Account |
diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts index 48d641c22..3c01fb77c 100644 --- a/server/lib/activitypub/send/send-follow.ts +++ b/server/lib/activitypub/send/send-follow.ts | |||
@@ -2,8 +2,8 @@ import { Transaction } from 'sequelize' | |||
2 | import { ActivityFollow } from '../../../../shared/models/activitypub/activity' | 2 | import { ActivityFollow } from '../../../../shared/models/activitypub/activity' |
3 | import { AccountInstance } from '../../../models' | 3 | import { AccountInstance } from '../../../models' |
4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' | 4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' |
5 | import { getAccountFollowActivityPubUrl } from '../url' | ||
5 | import { unicastTo } from './misc' | 6 | import { unicastTo } from './misc' |
6 | import { getAccountFollowActivityPubUrl } from '../../../helpers/activitypub' | ||
7 | 7 | ||
8 | async function sendFollow (accountFollow: AccountFollowInstance, t: Transaction) { | 8 | async function sendFollow (accountFollow: AccountFollowInstance, t: Transaction) { |
9 | const me = accountFollow.AccountFollower | 9 | const me = accountFollow.AccountFollower |
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 39da824f3..77bee6639 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts | |||
@@ -3,8 +3,8 @@ import { ActivityFollow, ActivityUndo } from '../../../../shared/models/activity | |||
3 | import { AccountInstance } from '../../../models' | 3 | import { AccountInstance } from '../../../models' |
4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' | 4 | import { AccountFollowInstance } from '../../../models/account/account-follow-interface' |
5 | import { unicastTo } from './misc' | 5 | import { unicastTo } from './misc' |
6 | import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl } from '../../../helpers/activitypub' | ||
7 | import { followActivityData } from './send-follow' | 6 | import { followActivityData } from './send-follow' |
7 | import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl } from '../url' | ||
8 | 8 | ||
9 | async function sendUndoFollow (accountFollow: AccountFollowInstance, t: Transaction) { | 9 | async function sendUndoFollow (accountFollow: AccountFollowInstance, t: Transaction) { |
10 | const me = accountFollow.AccountFollower | 10 | const me = accountFollow.AccountFollower |
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index 42738f973..32cada06e 100644 --- a/server/lib/activitypub/send/send-update.ts +++ b/server/lib/activitypub/send/send-update.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | import { Transaction } from 'sequelize' | 1 | import { Transaction } from 'sequelize' |
2 | import { ActivityUpdate } from '../../../../shared/models/activitypub/activity' | 2 | import { ActivityUpdate } from '../../../../shared/models/activitypub/activity' |
3 | import { getUpdateActivityPubUrl } from '../../../helpers/activitypub' | ||
4 | import { database as db } from '../../../initializers' | 3 | import { database as db } from '../../../initializers' |
5 | import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models' | 4 | import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models' |
5 | import { getUpdateActivityPubUrl } from '../url' | ||
6 | import { broadcastToFollowers, getAudience } from './misc' | 6 | import { broadcastToFollowers, getAudience } from './misc' |
7 | 7 | ||
8 | async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { | 8 | async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) { |
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts new file mode 100644 index 000000000..689e200a6 --- /dev/null +++ b/server/lib/activitypub/share.ts | |||
@@ -0,0 +1,33 @@ | |||
1 | import { Transaction } from 'sequelize' | ||
2 | import { getServerAccount } from '../../helpers/utils' | ||
3 | import { database as db } from '../../initializers' | ||
4 | import { VideoChannelInstance } from '../../models/index' | ||
5 | import { VideoInstance } from '../../models/video/video-interface' | ||
6 | import { sendVideoAnnounce, sendVideoChannelAnnounce } from './send/send-announce' | ||
7 | |||
8 | async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t: Transaction) { | ||
9 | const serverAccount = await getServerAccount() | ||
10 | |||
11 | await db.VideoChannelShare.create({ | ||
12 | accountId: serverAccount.id, | ||
13 | videoChannelId: videoChannel.id | ||
14 | }, { transaction: t }) | ||
15 | |||
16 | return sendVideoChannelAnnounce(serverAccount, videoChannel, t) | ||
17 | } | ||
18 | |||
19 | async function shareVideoByServer (video: VideoInstance, t: Transaction) { | ||
20 | const serverAccount = await getServerAccount() | ||
21 | |||
22 | await db.VideoShare.create({ | ||
23 | accountId: serverAccount.id, | ||
24 | videoId: video.id | ||
25 | }, { transaction: t }) | ||
26 | |||
27 | return sendVideoAnnounce(serverAccount, video, t) | ||
28 | } | ||
29 | |||
30 | export { | ||
31 | shareVideoChannelByServer, | ||
32 | shareVideoByServer | ||
33 | } | ||
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts new file mode 100644 index 000000000..41ac0f9a8 --- /dev/null +++ b/server/lib/activitypub/url.ts | |||
@@ -0,0 +1,60 @@ | |||
1 | import { CONFIG } from '../../initializers/constants' | ||
2 | import { VideoInstance } from '../../models/video/video-interface' | ||
3 | import { VideoChannelInstance } from '../../models/video/video-channel-interface' | ||
4 | import { VideoAbuseInstance } from '../../models/video/video-abuse-interface' | ||
5 | import { AccountFollowInstance } from '../../models/account/account-follow-interface' | ||
6 | import { AccountInstance } from '../../models/account/account-interface' | ||
7 | |||
8 | function getVideoActivityPubUrl (video: VideoInstance) { | ||
9 | return CONFIG.WEBSERVER.URL + '/videos/watch/' + video.uuid | ||
10 | } | ||
11 | |||
12 | function getVideoChannelActivityPubUrl (videoChannel: VideoChannelInstance) { | ||
13 | return CONFIG.WEBSERVER.URL + '/video-channels/' + videoChannel.uuid | ||
14 | } | ||
15 | |||
16 | function getAccountActivityPubUrl (accountName: string) { | ||
17 | return CONFIG.WEBSERVER.URL + '/account/' + accountName | ||
18 | } | ||
19 | |||
20 | function getVideoAbuseActivityPubUrl (videoAbuse: VideoAbuseInstance) { | ||
21 | return CONFIG.WEBSERVER.URL + '/admin/video-abuses/' + videoAbuse.id | ||
22 | } | ||
23 | |||
24 | function getAccountFollowActivityPubUrl (accountFollow: AccountFollowInstance) { | ||
25 | const me = accountFollow.AccountFollower | ||
26 | const following = accountFollow.AccountFollowing | ||
27 | |||
28 | return me.url + '#follows/' + following.id | ||
29 | } | ||
30 | |||
31 | function getAccountFollowAcceptActivityPubUrl (accountFollow: AccountFollowInstance) { | ||
32 | const follower = accountFollow.AccountFollower | ||
33 | const me = accountFollow.AccountFollowing | ||
34 | |||
35 | return follower.url + '#accepts/follows/' + me.id | ||
36 | } | ||
37 | |||
38 | function getAnnounceActivityPubUrl (originalUrl: string, byAccount: AccountInstance) { | ||
39 | return originalUrl + '#announces/' + byAccount.id | ||
40 | } | ||
41 | |||
42 | function getUpdateActivityPubUrl (originalUrl: string, updatedAt: string) { | ||
43 | return originalUrl + '#updates/' + updatedAt | ||
44 | } | ||
45 | |||
46 | function getUndoActivityPubUrl (originalUrl: string) { | ||
47 | return originalUrl + '/undo' | ||
48 | } | ||
49 | |||
50 | export { | ||
51 | getVideoActivityPubUrl, | ||
52 | getVideoChannelActivityPubUrl, | ||
53 | getAccountActivityPubUrl, | ||
54 | getVideoAbuseActivityPubUrl, | ||
55 | getAccountFollowActivityPubUrl, | ||
56 | getAccountFollowAcceptActivityPubUrl, | ||
57 | getAnnounceActivityPubUrl, | ||
58 | getUpdateActivityPubUrl, | ||
59 | getUndoActivityPubUrl | ||
60 | } | ||
diff --git a/server/lib/activitypub/video-channels.ts b/server/lib/activitypub/video-channels.ts new file mode 100644 index 000000000..7339d79f9 --- /dev/null +++ b/server/lib/activitypub/video-channels.ts | |||
@@ -0,0 +1,60 @@ | |||
1 | import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object' | ||
2 | import { isVideoChannelObjectValid } from '../../helpers/custom-validators/activitypub/video-channels' | ||
3 | import { logger } from '../../helpers/logger' | ||
4 | import { doRequest } from '../../helpers/requests' | ||
5 | import { database as db } from '../../initializers' | ||
6 | import { ACTIVITY_PUB } from '../../initializers/constants' | ||
7 | import { AccountInstance } from '../../models/account/account-interface' | ||
8 | import { videoChannelActivityObjectToDBAttributes } from './process/misc' | ||
9 | |||
10 | async function getOrCreateVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) { | ||
11 | let videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl) | ||
12 | |||
13 | // We don't have this account in our database, fetch it on remote | ||
14 | if (!videoChannel) { | ||
15 | videoChannel = await fetchRemoteVideoChannel(ownerAccount, videoChannelUrl) | ||
16 | if (videoChannel === undefined) throw new Error('Cannot fetch remote video channel.') | ||
17 | |||
18 | // Save our new video channel in database | ||
19 | await videoChannel.save() | ||
20 | } | ||
21 | |||
22 | return videoChannel | ||
23 | } | ||
24 | |||
25 | async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) { | ||
26 | const options = { | ||
27 | uri: videoChannelUrl, | ||
28 | method: 'GET', | ||
29 | headers: { | ||
30 | 'Accept': ACTIVITY_PUB.ACCEPT_HEADER | ||
31 | } | ||
32 | } | ||
33 | |||
34 | logger.info('Fetching remote video channel %s.', videoChannelUrl) | ||
35 | |||
36 | let requestResult | ||
37 | try { | ||
38 | requestResult = await doRequest(options) | ||
39 | } catch (err) { | ||
40 | logger.warn('Cannot fetch remote video channel %s.', videoChannelUrl, err) | ||
41 | return undefined | ||
42 | } | ||
43 | |||
44 | const videoChannelJSON: VideoChannelObject = JSON.parse(requestResult.body) | ||
45 | if (isVideoChannelObjectValid(videoChannelJSON) === false) { | ||
46 | logger.debug('Remote video channel JSON is not valid.', { videoChannelJSON }) | ||
47 | return undefined | ||
48 | } | ||
49 | |||
50 | const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount) | ||
51 | const videoChannel = db.VideoChannel.build(videoChannelAttributes) | ||
52 | videoChannel.Account = ownerAccount | ||
53 | |||
54 | return videoChannel | ||
55 | } | ||
56 | |||
57 | export { | ||
58 | getOrCreateVideoChannel, | ||
59 | fetchRemoteVideoChannel | ||
60 | } | ||
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts new file mode 100644 index 000000000..944244893 --- /dev/null +++ b/server/lib/activitypub/videos.ts | |||
@@ -0,0 +1,44 @@ | |||
1 | import { join } from 'path' | ||
2 | import * as request from 'request' | ||
3 | import { ActivityIconObject } from '../../../shared/index' | ||
4 | import { doRequest, doRequestAndSaveToFile } from '../../helpers/requests' | ||
5 | import { CONFIG, REMOTE_SCHEME, STATIC_PATHS } from '../../initializers/constants' | ||
6 | import { VideoInstance } from '../../models/video/video-interface' | ||
7 | |||
8 | function fetchRemoteVideoPreview (video: VideoInstance) { | ||
9 | // FIXME: use url | ||
10 | const host = video.VideoChannel.Account.Server.host | ||
11 | const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName()) | ||
12 | |||
13 | return request.get(REMOTE_SCHEME.HTTP + '://' + host + path) | ||
14 | } | ||
15 | |||
16 | async function fetchRemoteVideoDescription (video: VideoInstance) { | ||
17 | // FIXME: use url | ||
18 | const host = video.VideoChannel.Account.Server.host | ||
19 | const path = video.getDescriptionPath() | ||
20 | const options = { | ||
21 | uri: REMOTE_SCHEME.HTTP + '://' + host + path, | ||
22 | json: true | ||
23 | } | ||
24 | |||
25 | const { body } = await doRequest(options) | ||
26 | return body.description ? body.description : '' | ||
27 | } | ||
28 | |||
29 | function generateThumbnailFromUrl (video: VideoInstance, icon: ActivityIconObject) { | ||
30 | const thumbnailName = video.getThumbnailName() | ||
31 | const thumbnailPath = join(CONFIG.STORAGE.THUMBNAILS_DIR, thumbnailName) | ||
32 | |||
33 | const options = { | ||
34 | method: 'GET', | ||
35 | uri: icon.url | ||
36 | } | ||
37 | return doRequestAndSaveToFile(options, thumbnailPath) | ||
38 | } | ||
39 | |||
40 | export { | ||
41 | fetchRemoteVideoPreview, | ||
42 | fetchRemoteVideoDescription, | ||
43 | generateThumbnailFromUrl | ||
44 | } | ||
diff --git a/server/lib/cache/videos-preview-cache.ts b/server/lib/cache/videos-preview-cache.ts index 0570f51e8..7f352f361 100644 --- a/server/lib/cache/videos-preview-cache.ts +++ b/server/lib/cache/videos-preview-cache.ts | |||
@@ -3,8 +3,9 @@ import { join } from 'path' | |||
3 | import { createWriteStream } from 'fs' | 3 | import { createWriteStream } from 'fs' |
4 | 4 | ||
5 | import { database as db, CONFIG, CACHE } from '../../initializers' | 5 | import { database as db, CONFIG, CACHE } from '../../initializers' |
6 | import { logger, unlinkPromise, fetchRemoteVideoPreview } from '../../helpers' | 6 | import { logger, unlinkPromise } from '../../helpers' |
7 | import { VideoInstance } from '../../models' | 7 | import { VideoInstance } from '../../models' |
8 | import { fetchRemoteVideoPreview } from '../activitypub/videos' | ||
8 | 9 | ||
9 | class VideosPreviewCache { | 10 | class VideosPreviewCache { |
10 | 11 | ||
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 f26110973..e65ab3ee1 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 | |||
@@ -1,13 +1,11 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import { computeResolutionsToTranscode, logger } from '../../../helpers' | 2 | import { computeResolutionsToTranscode, logger } from '../../../helpers' |
3 | |||
4 | import { database as db } from '../../../initializers/database' | 3 | import { database as db } from '../../../initializers/database' |
5 | import { VideoInstance } from '../../../models' | 4 | import { VideoInstance } from '../../../models' |
6 | 5 | import { sendAddVideo } from '../../activitypub/send/send-add' | |
7 | import { JobScheduler } from '../job-scheduler' | 6 | import { JobScheduler } from '../job-scheduler' |
8 | import { TranscodingJobPayload } from './transcoding-job-scheduler' | 7 | import { TranscodingJobPayload } from './transcoding-job-scheduler' |
9 | import { shareVideoByServer } from '../../../helpers/activitypub' | 8 | import { shareVideoByServer } from '../../activitypub/share' |
10 | import { sendAddVideo } from '../../activitypub/send/send-add' | ||
11 | 9 | ||
12 | async function process (data: TranscodingJobPayload, jobId: number) { | 10 | async function process (data: TranscodingJobPayload, jobId: number) { |
13 | const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID) | 11 | const video = await db.Video.loadByUUIDAndPopulateAccountAndServerAndTags(data.videoUUID) |
diff --git a/server/lib/user.ts b/server/lib/user.ts index d54ffc916..5653d8e65 100644 --- a/server/lib/user.ts +++ b/server/lib/user.ts | |||
@@ -5,7 +5,7 @@ import { CONFIG } from '../initializers/constants' | |||
5 | import { UserInstance } from '../models' | 5 | import { UserInstance } from '../models' |
6 | import { createVideoChannel } from './video-channel' | 6 | import { createVideoChannel } from './video-channel' |
7 | import { logger } from '../helpers/logger' | 7 | import { logger } from '../helpers/logger' |
8 | import { getAccountActivityPubUrl } from '../helpers/activitypub' | 8 | import { getAccountActivityPubUrl } from './activitypub/url' |
9 | 9 | ||
10 | async function createUserAccountAndChannel (user: UserInstance, validateUser = true) { | 10 | async function createUserAccountAndChannel (user: UserInstance, validateUser = true) { |
11 | const { account, videoChannel } = await db.sequelize.transaction(async t => { | 11 | const { account, videoChannel } = await db.sequelize.transaction(async t => { |
diff --git a/server/lib/video-channel.ts b/server/lib/video-channel.ts index 5235d9cb5..a939a23d5 100644 --- a/server/lib/video-channel.ts +++ b/server/lib/video-channel.ts | |||
@@ -3,7 +3,7 @@ import { VideoChannelCreate } from '../../shared/models' | |||
3 | import { logger } from '../helpers' | 3 | import { logger } from '../helpers' |
4 | import { database as db } from '../initializers' | 4 | import { database as db } from '../initializers' |
5 | import { AccountInstance } from '../models' | 5 | import { AccountInstance } from '../models' |
6 | import { getVideoChannelActivityPubUrl } from '../helpers/activitypub' | 6 | import { getVideoChannelActivityPubUrl } from './activitypub/url' |
7 | 7 | ||
8 | async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) { | 8 | async function createVideoChannel (videoChannelInfo: VideoChannelCreate, account: AccountInstance, t: Sequelize.Transaction) { |
9 | const videoChannelData = { | 9 | const videoChannelData = { |