Split types and typings
[oweals/peertube.git] / server / lib / activitypub / share.ts
1 import { Transaction } from 'sequelize'
2 import { VideoShareModel } from '../../models/video/video-share'
3 import { sendUndoAnnounce, sendVideoAnnounce } from './send'
4 import { getVideoAnnounceActivityPubUrl } from './url'
5 import * as Bluebird from 'bluebird'
6 import { doRequest } from '../../helpers/requests'
7 import { getOrCreateActorAndServerAndModel } from './actor'
8 import { logger } from '../../helpers/logger'
9 import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
10 import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
11 import { MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../types/models/video'
12 import { getServerActor } from '@server/models/application/application'
13
14 async function shareVideoByServerAndChannel (video: MVideoAccountLight, t: Transaction) {
15   if (!video.hasPrivacyForFederation()) return undefined
16
17   return Promise.all([
18     shareByServer(video, t),
19     shareByVideoChannel(video, t)
20   ])
21 }
22
23 async function changeVideoChannelShare (
24   video: MVideoAccountLight,
25   oldVideoChannel: MChannelActorLight,
26   t: Transaction
27 ) {
28   logger.info('Updating video channel of video %s: %s -> %s.', video.uuid, oldVideoChannel.name, video.VideoChannel.name)
29
30   await undoShareByVideoChannel(video, oldVideoChannel, t)
31
32   await shareByVideoChannel(video, t)
33 }
34
35 async function addVideoShares (shareUrls: string[], video: MVideoId) {
36   await Bluebird.map(shareUrls, async shareUrl => {
37     try {
38       // Fetch url
39       const { body } = await doRequest<any>({
40         uri: shareUrl,
41         json: true,
42         activityPub: true
43       })
44       if (!body || !body.actor) throw new Error('Body or body actor is invalid')
45
46       const actorUrl = getAPId(body.actor)
47       if (checkUrlsSameHost(shareUrl, actorUrl) !== true) {
48         throw new Error(`Actor url ${actorUrl} has not the same host than the share url ${shareUrl}`)
49       }
50
51       const actor = await getOrCreateActorAndServerAndModel(actorUrl)
52
53       const entry = {
54         actorId: actor.id,
55         videoId: video.id,
56         url: shareUrl
57       }
58
59       await VideoShareModel.upsert(entry)
60     } catch (err) {
61       logger.warn('Cannot add share %s.', shareUrl, { err })
62     }
63   }, { concurrency: CRAWL_REQUEST_CONCURRENCY })
64 }
65
66 export {
67   changeVideoChannelShare,
68   addVideoShares,
69   shareVideoByServerAndChannel
70 }
71
72 // ---------------------------------------------------------------------------
73
74 async function shareByServer (video: MVideo, t: Transaction) {
75   const serverActor = await getServerActor()
76
77   const serverShareUrl = getVideoAnnounceActivityPubUrl(serverActor, video)
78   const [ serverShare ] = await VideoShareModel.findOrCreate({
79     defaults: {
80       actorId: serverActor.id,
81       videoId: video.id,
82       url: serverShareUrl
83     },
84     where: {
85       url: serverShareUrl
86     },
87     transaction: t
88   })
89
90   return sendVideoAnnounce(serverActor, serverShare, video, t)
91 }
92
93 async function shareByVideoChannel (video: MVideoAccountLight, t: Transaction) {
94   const videoChannelShareUrl = getVideoAnnounceActivityPubUrl(video.VideoChannel.Actor, video)
95   const [ videoChannelShare ] = await VideoShareModel.findOrCreate({
96     defaults: {
97       actorId: video.VideoChannel.actorId,
98       videoId: video.id,
99       url: videoChannelShareUrl
100     },
101     where: {
102       url: videoChannelShareUrl
103     },
104     transaction: t
105   })
106
107   return sendVideoAnnounce(video.VideoChannel.Actor, videoChannelShare, video, t)
108 }
109
110 async function undoShareByVideoChannel (video: MVideo, oldVideoChannel: MChannelActorLight, t: Transaction) {
111   // Load old share
112   const oldShare = await VideoShareModel.load(oldVideoChannel.actorId, video.id, t)
113   if (!oldShare) return new Error('Cannot find old video channel share ' + oldVideoChannel.actorId + ' for video ' + video.id)
114
115   await sendUndoAnnounce(oldVideoChannel.Actor, oldShare, video, t)
116   await oldShare.destroy({ transaction: t })
117 }