]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/helpers/activitypub.ts
Stronger model typings
[github/Chocobozzz/PeerTube.git] / server / helpers / activitypub.ts
CommitLineData
8fffe21a
C
1import * as Bluebird from 'bluebird'
2import * as validator from 'validator'
3fd3ab2d 3import { ResultList } from '../../shared/models'
361805c4 4import { Activity } from '../../shared/models/activitypub'
74dc3bca 5import { ACTIVITY_PUB } from '../initializers/constants'
50d6de9c 6import { ActorModel } from '../models/activitypub/actor'
f7509cbe 7import { signJsonLDObject } from './peertube-crypto'
8fffe21a 8import { pageToStartAndCount } from './core-utils'
5c6d985f 9import { parse } from 'url'
453e83ea 10import { MActor } from '../typings/models'
571389d4
C
11
12function activityPubContextify <T> (data: T) {
2186386c 13 return Object.assign(data, {
e4f97bab
C
14 '@context': [
15 'https://www.w3.org/ns/activitystreams',
16 'https://w3id.org/security/v1',
17 {
2186386c 18 RsaSignature2017: 'https://w3id.org/security#RsaSignature2017',
09209296 19 pt: 'https://joinpeertube.org/ns#',
f7509cbe 20 sc: 'http://schema.org#',
2186386c 21 Hashtag: 'as:Hashtag',
f7509cbe
C
22 uuid: 'sc:identifier',
23 category: 'sc:category',
24 licence: 'sc:license',
25 subtitleLanguage: 'sc:subtitleLanguage',
2186386c 26 sensitive: 'as:sensitive',
f7509cbe 27 language: 'sc:inLanguage',
f7509cbe 28 expires: 'sc:expires',
09209296 29 CacheFile: 'pt:CacheFile',
7519127b 30 Infohash: 'pt:Infohash',
ae3171b6
C
31 originallyPublishedAt: 'sc:datePublished',
32 views: {
4e10f4b2 33 '@type': 'sc:Number',
ae3171b6
C
34 '@id': 'pt:views'
35 },
36 state: {
4e10f4b2 37 '@type': 'sc:Number',
ae3171b6
C
38 '@id': 'pt:state'
39 },
40 size: {
4e10f4b2 41 '@type': 'sc:Number',
ae3171b6
C
42 '@id': 'pt:size'
43 },
44 fps: {
4e10f4b2 45 '@type': 'sc:Number',
ae3171b6
C
46 '@id': 'pt:fps'
47 },
48 startTimestamp: {
4e10f4b2 49 '@type': 'sc:Number',
ae3171b6
C
50 '@id': 'pt:startTimestamp'
51 },
52 stopTimestamp: {
4e10f4b2 53 '@type': 'sc:Number',
ae3171b6
C
54 '@id': 'pt:stopTimestamp'
55 },
56 position: {
4e10f4b2 57 '@type': 'sc:Number',
ae3171b6
C
58 '@id': 'pt:position'
59 },
60 commentsEnabled: {
4e10f4b2 61 '@type': 'sc:Boolean',
ae3171b6
C
62 '@id': 'pt:commentsEnabled'
63 },
64 downloadEnabled: {
4e10f4b2 65 '@type': 'sc:Boolean',
ae3171b6
C
66 '@id': 'pt:downloadEnabled'
67 },
68 waitTranscoding: {
4e10f4b2 69 '@type': 'sc:Boolean',
ae3171b6
C
70 '@id': 'pt:waitTranscoding'
71 },
72 support: {
4e10f4b2 73 '@type': 'sc:Text',
ae3171b6
C
74 '@id': 'pt:support'
75 }
2fe86927
C
76 },
77 {
78 likes: {
79 '@id': 'as:likes',
80 '@type': '@id'
81 },
82 dislikes: {
83 '@id': 'as:dislikes',
84 '@type': '@id'
85 },
418d092a
C
86 playlists: {
87 '@id': 'pt:playlists',
88 '@type': '@id'
89 },
2fe86927
C
90 shares: {
91 '@id': 'as:shares',
92 '@type': '@id'
93 },
94 comments: {
95 '@id': 'as:comments',
96 '@type': '@id'
97 }
e4f97bab
C
98 }
99 ]
100 })
101}
102
8fffe21a 103type ActivityPubCollectionPaginationHandler = (start: number, count: number) => Bluebird<ResultList<any>> | Promise<ResultList<any>>
babecc3c 104async function activityPubCollectionPagination (baseUrl: string, handler: ActivityPubCollectionPaginationHandler, page?: any) {
8fffe21a
C
105 if (!page || !validator.isInt(page)) {
106 // We just display the first page URL, we only need the total items
107 const result = await handler(0, 1)
108
109 return {
babecc3c 110 id: baseUrl,
418d092a 111 type: 'OrderedCollectionPage',
8fffe21a 112 totalItems: result.total,
babecc3c 113 first: baseUrl + '?page=1'
8fffe21a 114 }
16b90975 115 }
16b90975 116
8fffe21a
C
117 const { start, count } = pageToStartAndCount(page, ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE)
118 const result = await handler(start, count)
119
c1e791ba
RK
120 let next: string | undefined
121 let prev: string | undefined
e71bcc0f 122
c46edbc2
C
123 // Assert page is a number
124 page = parseInt(page, 10)
125
e71bcc0f 126 // There are more results
c46edbc2 127 if (result.total > page * ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE) {
babecc3c 128 next = baseUrl + '?page=' + (page + 1)
e71bcc0f
C
129 }
130
131 if (page > 1) {
babecc3c 132 prev = baseUrl + '?page=' + (page - 1)
e71bcc0f
C
133 }
134
8fffe21a 135 return {
babecc3c 136 id: baseUrl + '?page=' + page,
e71bcc0f
C
137 type: 'OrderedCollectionPage',
138 prev,
139 next,
babecc3c 140 partOf: baseUrl,
8fffe21a
C
141 orderedItems: result.data,
142 totalItems: result.total
e4f97bab
C
143 }
144
e4f97bab
C
145}
146
453e83ea 147function buildSignedActivity (byActor: MActor, data: Object) {
afffe988
C
148 const activity = activityPubContextify(data)
149
f7509cbe 150 return signJsonLDObject(byActor, activity) as Promise<Activity>
afffe988
C
151}
152
848f499d 153function getAPId (activity: string | { id: string }) {
361805c4 154 if (typeof activity === 'string') return activity
6be84cbc 155
361805c4 156 return activity.id
6be84cbc
C
157}
158
5c6d985f
C
159function checkUrlsSameHost (url1: string, url2: string) {
160 const idHost = parse(url1).host
161 const actorHost = parse(url2).host
162
163 return idHost && actorHost && idHost.toLowerCase() === actorHost.toLowerCase()
6be84cbc
C
164}
165
e4f97bab
C
166// ---------------------------------------------------------------------------
167
168export {
5c6d985f 169 checkUrlsSameHost,
848f499d 170 getAPId,
e4f97bab 171 activityPubContextify,
0d0e8dd0 172 activityPubCollectionPagination,
892211e8 173 buildSignedActivity
e4f97bab 174}