aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'server/helpers')
-rw-r--r--server/helpers/activitypub.ts64
-rw-r--r--server/helpers/custom-validators/activitypub/activity.ts6
-rw-r--r--server/helpers/custom-validators/activitypub/misc.ts2
-rw-r--r--server/helpers/custom-validators/activitypub/videos.ts42
-rw-r--r--server/helpers/database-utils.ts8
5 files changed, 93 insertions, 29 deletions
diff --git a/server/helpers/activitypub.ts b/server/helpers/activitypub.ts
index b376b8ca2..c710117cd 100644
--- a/server/helpers/activitypub.ts
+++ b/server/helpers/activitypub.ts
@@ -4,13 +4,18 @@ import * as Sequelize from 'sequelize'
4import * as url from 'url' 4import * as url from 'url'
5import { ActivityIconObject } from '../../shared/index' 5import { ActivityIconObject } from '../../shared/index'
6import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor' 6import { ActivityPubActor } from '../../shared/models/activitypub/activitypub-actor'
7import { VideoChannelObject } from '../../shared/models/activitypub/objects/video-channel-object'
7import { ResultList } from '../../shared/models/result-list.model' 8import { ResultList } from '../../shared/models/result-list.model'
8import { database as db, REMOTE_SCHEME } from '../initializers' 9import { database as db, REMOTE_SCHEME } from '../initializers'
9import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants' 10import { ACTIVITY_PUB_ACCEPT_HEADER, CONFIG, STATIC_PATHS } from '../initializers/constants'
10import { sendAnnounce } from '../lib/activitypub/send-request' 11import { videoChannelActivityObjectToDBAttributes } from '../lib/activitypub/misc'
12import { sendVideoAnnounce } from '../lib/activitypub/send-request'
13import { sendVideoChannelAnnounce } from '../lib/index'
14import { AccountInstance } from '../models/account/account-interface'
11import { VideoChannelInstance } from '../models/video/video-channel-interface' 15import { VideoChannelInstance } from '../models/video/video-channel-interface'
12import { VideoInstance } from '../models/video/video-interface' 16import { VideoInstance } from '../models/video/video-interface'
13import { isRemoteAccountValid } from './custom-validators' 17import { isRemoteAccountValid } from './custom-validators'
18import { isVideoChannelObjectValid } from './custom-validators/activitypub/videos'
14import { logger } from './logger' 19import { logger } from './logger'
15import { doRequest, doRequestAndSaveToFile } from './requests' 20import { doRequest, doRequestAndSaveToFile } from './requests'
16import { getServerAccount } from './utils' 21import { getServerAccount } from './utils'
@@ -34,7 +39,7 @@ async function shareVideoChannelByServer (videoChannel: VideoChannelInstance, t:
34 videoChannelId: videoChannel.id 39 videoChannelId: videoChannel.id
35 }, { transaction: t }) 40 }, { transaction: t })
36 41
37 return sendAnnounce(serverAccount, videoChannel, t) 42 return sendVideoChannelAnnounce(serverAccount, videoChannel, t)
38} 43}
39 44
40async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transaction) { 45async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transaction) {
@@ -45,7 +50,7 @@ async function shareVideoByServer (video: VideoInstance, t: Sequelize.Transactio
45 videoId: video.id 50 videoId: video.id
46 }, { transaction: t }) 51 }, { transaction: t })
47 52
48 return sendAnnounce(serverAccount, video, t) 53 return sendVideoAnnounce(serverAccount, video, t)
49} 54}
50 55
51function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account' | 'videoAbuse', id: string) { 56function getActivityPubUrl (type: 'video' | 'videoChannel' | 'account' | 'videoAbuse', id: string) {
@@ -66,13 +71,27 @@ async function getOrCreateAccount (accountUrl: string) {
66 if (res === undefined) throw new Error('Cannot fetch remote account.') 71 if (res === undefined) throw new Error('Cannot fetch remote account.')
67 72
68 // Save our new account in database 73 // Save our new account in database
69 const account = res.account 74 account = await res.account.save()
70 await account.save()
71 } 75 }
72 76
73 return account 77 return account
74} 78}
75 79
80async function getOrCreateVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) {
81 let videoChannel = await db.VideoChannel.loadByUrl(videoChannelUrl)
82
83 // We don't have this account in our database, fetch it on remote
84 if (!videoChannel) {
85 videoChannel = await fetchRemoteVideoChannel(ownerAccount, videoChannelUrl)
86 if (videoChannel === undefined) throw new Error('Cannot fetch remote video channel.')
87
88 // Save our new video channel in database
89 await videoChannel.save()
90 }
91
92 return videoChannel
93}
94
76async function fetchRemoteAccountAndCreateServer (accountUrl: string) { 95async function fetchRemoteAccountAndCreateServer (accountUrl: string) {
77 const options = { 96 const options = {
78 uri: accountUrl, 97 uri: accountUrl,
@@ -131,6 +150,38 @@ async function fetchRemoteAccountAndCreateServer (accountUrl: string) {
131 return { account, server } 150 return { account, server }
132} 151}
133 152
153async function fetchRemoteVideoChannel (ownerAccount: AccountInstance, videoChannelUrl: string) {
154 const options = {
155 uri: videoChannelUrl,
156 method: 'GET',
157 headers: {
158 'Accept': ACTIVITY_PUB_ACCEPT_HEADER
159 }
160 }
161
162 logger.info('Fetching remote video channel %s.', videoChannelUrl)
163
164 let requestResult
165 try {
166 requestResult = await doRequest(options)
167 } catch (err) {
168 logger.warn('Cannot fetch remote video channel %s.', videoChannelUrl, err)
169 return undefined
170 }
171
172 const videoChannelJSON: VideoChannelObject = JSON.parse(requestResult.body)
173 if (isVideoChannelObjectValid(videoChannelJSON) === false) {
174 logger.debug('Remote video channel JSON is not valid.', { videoChannelJSON })
175 return undefined
176 }
177
178 const videoChannelAttributes = videoChannelActivityObjectToDBAttributes(videoChannelJSON, ownerAccount)
179 const videoChannel = db.VideoChannel.build(videoChannelAttributes)
180 videoChannel.Account = ownerAccount
181
182 return videoChannel
183}
184
134function fetchRemoteVideoPreview (video: VideoInstance) { 185function fetchRemoteVideoPreview (video: VideoInstance) {
135 // FIXME: use url 186 // FIXME: use url
136 const host = video.VideoChannel.Account.Server.host 187 const host = video.VideoChannel.Account.Server.host
@@ -200,7 +251,8 @@ export {
200 fetchRemoteVideoPreview, 251 fetchRemoteVideoPreview,
201 fetchRemoteVideoDescription, 252 fetchRemoteVideoDescription,
202 shareVideoChannelByServer, 253 shareVideoChannelByServer,
203 shareVideoByServer 254 shareVideoByServer,
255 getOrCreateVideoChannel
204} 256}
205 257
206// --------------------------------------------------------------------------- 258// ---------------------------------------------------------------------------
diff --git a/server/helpers/custom-validators/activitypub/activity.ts b/server/helpers/custom-validators/activitypub/activity.ts
index 08e5ae0aa..8084cf7b0 100644
--- a/server/helpers/custom-validators/activitypub/activity.ts
+++ b/server/helpers/custom-validators/activitypub/activity.ts
@@ -2,8 +2,7 @@ import * as validator from 'validator'
2import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './account' 2import { isAccountAcceptActivityValid, isAccountDeleteActivityValid, isAccountFollowActivityValid } from './account'
3import { isActivityPubUrlValid } from './misc' 3import { isActivityPubUrlValid } from './misc'
4import { 4import {
5 isVideoAnnounceValid, 5 isAnnounceValid,
6 isVideoChannelAnnounceValid,
7 isVideoChannelCreateActivityValid, 6 isVideoChannelCreateActivityValid,
8 isVideoChannelDeleteActivityValid, 7 isVideoChannelDeleteActivityValid,
9 isVideoChannelUpdateActivityValid, 8 isVideoChannelUpdateActivityValid,
@@ -37,8 +36,7 @@ function isActivityValid (activity: any) {
37 isAccountFollowActivityValid(activity) || 36 isAccountFollowActivityValid(activity) ||
38 isAccountAcceptActivityValid(activity) || 37 isAccountAcceptActivityValid(activity) ||
39 isVideoFlagValid(activity) || 38 isVideoFlagValid(activity) ||
40 isVideoAnnounceValid(activity) || 39 isAnnounceValid(activity)
41 isVideoChannelAnnounceValid(activity)
42} 40}
43 41
44// --------------------------------------------------------------------------- 42// ---------------------------------------------------------------------------
diff --git a/server/helpers/custom-validators/activitypub/misc.ts b/server/helpers/custom-validators/activitypub/misc.ts
index 665a63a73..f09a764b6 100644
--- a/server/helpers/custom-validators/activitypub/misc.ts
+++ b/server/helpers/custom-validators/activitypub/misc.ts
@@ -21,7 +21,7 @@ function isActivityPubUrlValid (url: string) {
21} 21}
22 22
23function isBaseActivityValid (activity: any, type: string) { 23function isBaseActivityValid (activity: any, type: string) {
24 return Array.isArray(activity['@context']) && 24 return (activity['@context'] === undefined || Array.isArray(activity['@context'])) &&
25 activity.type === type && 25 activity.type === type &&
26 isActivityPubUrlValid(activity.id) && 26 isActivityPubUrlValid(activity.id) &&
27 isActivityPubUrlValid(activity.actor) && 27 isActivityPubUrlValid(activity.actor) &&
diff --git a/server/helpers/custom-validators/activitypub/videos.ts b/server/helpers/custom-validators/activitypub/videos.ts
index 89c49b0df..8486297ad 100644
--- a/server/helpers/custom-validators/activitypub/videos.ts
+++ b/server/helpers/custom-validators/activitypub/videos.ts
@@ -39,6 +39,7 @@ function isActivityPubVideoDurationValid (value: string) {
39 39
40function isVideoTorrentObjectValid (video: any) { 40function isVideoTorrentObjectValid (video: any) {
41 return video.type === 'Video' && 41 return video.type === 'Video' &&
42 isActivityPubUrlValid(video.id) &&
42 isVideoNameValid(video.name) && 43 isVideoNameValid(video.name) &&
43 isActivityPubVideoDurationValid(video.duration) && 44 isActivityPubVideoDurationValid(video.duration) &&
44 isUUIDValid(video.uuid) && 45 isUUIDValid(video.uuid) &&
@@ -62,14 +63,12 @@ function isVideoFlagValid (activity: any) {
62 isActivityPubUrlValid(activity.object) 63 isActivityPubUrlValid(activity.object)
63} 64}
64 65
65function isVideoAnnounceValid (activity: any) { 66function isAnnounceValid (activity: any) {
66 return isBaseActivityValid(activity, 'Announce') && 67 return isBaseActivityValid(activity, 'Announce') &&
67 isVideoTorrentObjectValid(activity.object) 68 (
68} 69 isVideoChannelCreateActivityValid(activity.object) ||
69 70 isVideoTorrentAddActivityValid(activity.object)
70function isVideoChannelAnnounceValid (activity: any) { 71 )
71 return isBaseActivityValid(activity, 'Announce') &&
72 isVideoChannelObjectValid(activity.object)
73} 72}
74 73
75function isVideoChannelCreateActivityValid (activity: any) { 74function isVideoChannelCreateActivityValid (activity: any) {
@@ -88,8 +87,11 @@ function isVideoChannelDeleteActivityValid (activity: any) {
88 87
89function isVideoChannelObjectValid (videoChannel: any) { 88function isVideoChannelObjectValid (videoChannel: any) {
90 return videoChannel.type === 'VideoChannel' && 89 return videoChannel.type === 'VideoChannel' &&
90 isActivityPubUrlValid(videoChannel.id) &&
91 isVideoChannelNameValid(videoChannel.name) && 91 isVideoChannelNameValid(videoChannel.name) &&
92 isVideoChannelDescriptionValid(videoChannel.description) && 92 isVideoChannelDescriptionValid(videoChannel.content) &&
93 isDateValid(videoChannel.published) &&
94 isDateValid(videoChannel.updated) &&
93 isUUIDValid(videoChannel.uuid) 95 isUUIDValid(videoChannel.uuid)
94} 96}
95 97
@@ -103,8 +105,8 @@ export {
103 isVideoChannelDeleteActivityValid, 105 isVideoChannelDeleteActivityValid,
104 isVideoTorrentDeleteActivityValid, 106 isVideoTorrentDeleteActivityValid,
105 isVideoFlagValid, 107 isVideoFlagValid,
106 isVideoAnnounceValid, 108 isAnnounceValid,
107 isVideoChannelAnnounceValid 109 isVideoChannelObjectValid
108} 110}
109 111
110// --------------------------------------------------------------------------- 112// ---------------------------------------------------------------------------
@@ -148,8 +150,20 @@ function setValidRemoteVideoUrls (video: any) {
148 150
149function isRemoteVideoUrlValid (url: any) { 151function isRemoteVideoUrlValid (url: any) {
150 return url.type === 'Link' && 152 return url.type === 'Link' &&
151 ACTIVITY_PUB.VIDEO_URL_MIME_TYPES.indexOf(url.mimeType) !== -1 && 153 (
152 isVideoUrlValid(url.url) && 154 ACTIVITY_PUB.URL_MIME_TYPES.VIDEO.indexOf(url.mimeType) !== -1 &&
153 validator.isInt(url.width + '', { min: 0 }) && 155 isVideoUrlValid(url.url) &&
154 validator.isInt(url.size + '', { min: 0 }) 156 validator.isInt(url.width + '', { min: 0 }) &&
157 validator.isInt(url.size + '', { min: 0 })
158 ) ||
159 (
160 ACTIVITY_PUB.URL_MIME_TYPES.TORRENT.indexOf(url.mimeType) !== -1 &&
161 isVideoUrlValid(url.url) &&
162 validator.isInt(url.width + '', { min: 0 })
163 ) ||
164 (
165 ACTIVITY_PUB.URL_MIME_TYPES.MAGNET.indexOf(url.mimeType) !== -1 &&
166 validator.isLength(url.url, { min: 5 }) &&
167 validator.isInt(url.width + '', { min: 0 })
168 )
155} 169}
diff --git a/server/helpers/database-utils.ts b/server/helpers/database-utils.ts
index d62462d35..169b80065 100644
--- a/server/helpers/database-utils.ts
+++ b/server/helpers/database-utils.ts
@@ -1,10 +1,10 @@
1// TODO: import from ES6 when retry typing file will include errorFilter function 1// TODO: import from ES6 when retry typing file will include errorFilter function
2import * as retry from 'async/retry' 2import * as retry from 'async/retry'
3 3import * as Bluebird from 'bluebird'
4import { logger } from './logger' 4import { logger } from './logger'
5 5
6type RetryTransactionWrapperOptions = { errorMessage: string, arguments?: any[] } 6type RetryTransactionWrapperOptions = { errorMessage: string, arguments?: any[] }
7function retryTransactionWrapper (functionToRetry: (...args) => Promise<any>, options: RetryTransactionWrapperOptions) { 7function retryTransactionWrapper (functionToRetry: (...args) => Promise<any> | Bluebird<any>, options: RetryTransactionWrapperOptions) {
8 const args = options.arguments ? options.arguments : [] 8 const args = options.arguments ? options.arguments : []
9 9
10 return transactionRetryer(callback => { 10 return transactionRetryer(callback => {
@@ -13,8 +13,8 @@ function retryTransactionWrapper (functionToRetry: (...args) => Promise<any>, op
13 .catch(err => callback(err)) 13 .catch(err => callback(err))
14 }) 14 })
15 .catch(err => { 15 .catch(err => {
16 // Do not throw the error, continue the process
17 logger.error(options.errorMessage, err) 16 logger.error(options.errorMessage, err)
17 throw err
18 }) 18 })
19} 19}
20 20
@@ -28,7 +28,7 @@ function transactionRetryer (func: Function) {
28 logger.debug('Maybe retrying the transaction function.', { willRetry }) 28 logger.debug('Maybe retrying the transaction function.', { willRetry })
29 return willRetry 29 return willRetry
30 } 30 }
31 }, func, err => err ? rej(err) : res()) 31 }, func, (err, data) => err ? rej(err) : res(data))
32 }) 32 })
33} 33}
34 34