123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- import { ContextType } from '@peertube/peertube-models'
- import { ACTIVITY_PUB, REMOTE_SCHEME } from '@server/initializers/constants.js'
- import { buildDigest } from './peertube-crypto.js'
- import type { signJsonLDObject } from './peertube-jsonld.js'
- import { doJSONRequest } from './requests.js'
- import { isArray } from './custom-validators/misc.js'
- export type ContextFilter = <T> (arg: T) => Promise<T>
- export function buildGlobalHTTPHeaders (
- body: any,
- digestBuilder: typeof buildDigest
- ) {
- return {
- 'digest': digestBuilder(body),
- 'content-type': 'application/activity+json',
- 'accept': ACTIVITY_PUB.ACCEPT_HEADER
- }
- }
- export async function activityPubContextify <T> (data: T, type: ContextType, contextFilter: ContextFilter) {
- return { ...await getContextData(type, contextFilter), ...data }
- }
- export async function signAndContextify <T> (options: {
- byActor: { url: string, privateKey: string }
- data: T
- contextType: ContextType | null
- contextFilter: ContextFilter
- signerFunction: typeof signJsonLDObject<T>
- }) {
- const { byActor, data, contextType, contextFilter, signerFunction } = options
- const activity = contextType
- ? await activityPubContextify(data, contextType, contextFilter)
- : data
- return signerFunction({ byActor, data: activity })
- }
- export async function getApplicationActorOfHost (host: string) {
- const url = REMOTE_SCHEME.HTTP + '://' + host + '/.well-known/nodeinfo'
- const { body } = await doJSONRequest<{ links: { rel: string, href: string }[] }>(url)
- if (!isArray(body.links)) return undefined
- const found = body.links.find(l => l.rel === 'https://www.w3.org/ns/activitystreams#Application')
- return found?.href || undefined
- }
- // ---------------------------------------------------------------------------
- // Private
- // ---------------------------------------------------------------------------
- type ContextValue = { [ id: string ]: (string | { '@type': string, '@id': string }) }
- const contextStore: { [ id in ContextType ]: (string | { [ id: string ]: string })[] } = {
- Video: buildContext({
- Hashtag: 'as:Hashtag',
- uuid: 'sc:identifier',
- category: 'sc:category',
- licence: 'sc:license',
- subtitleLanguage: 'sc:subtitleLanguage',
- sensitive: 'as:sensitive',
- language: 'sc:inLanguage',
- identifier: 'sc:identifier',
- isLiveBroadcast: 'sc:isLiveBroadcast',
- liveSaveReplay: {
- '@type': 'sc:Boolean',
- '@id': 'pt:liveSaveReplay'
- },
- permanentLive: {
- '@type': 'sc:Boolean',
- '@id': 'pt:permanentLive'
- },
- latencyMode: {
- '@type': 'sc:Number',
- '@id': 'pt:latencyMode'
- },
- Infohash: 'pt:Infohash',
- tileWidth: {
- '@type': 'sc:Number',
- '@id': 'pt:tileWidth'
- },
- tileHeight: {
- '@type': 'sc:Number',
- '@id': 'pt:tileHeight'
- },
- tileDuration: {
- '@type': 'sc:Number',
- '@id': 'pt:tileDuration'
- },
- originallyPublishedAt: 'sc:datePublished',
- uploadDate: 'sc:uploadDate',
- hasParts: 'sc:hasParts',
- views: {
- '@type': 'sc:Number',
- '@id': 'pt:views'
- },
- state: {
- '@type': 'sc:Number',
- '@id': 'pt:state'
- },
- size: {
- '@type': 'sc:Number',
- '@id': 'pt:size'
- },
- fps: {
- '@type': 'sc:Number',
- '@id': 'pt:fps'
- },
- commentsEnabled: {
- '@type': 'sc:Boolean',
- '@id': 'pt:commentsEnabled'
- },
- downloadEnabled: {
- '@type': 'sc:Boolean',
- '@id': 'pt:downloadEnabled'
- },
- waitTranscoding: {
- '@type': 'sc:Boolean',
- '@id': 'pt:waitTranscoding'
- },
- support: {
- '@type': 'sc:Text',
- '@id': 'pt:support'
- },
- likes: {
- '@id': 'as:likes',
- '@type': '@id'
- },
- dislikes: {
- '@id': 'as:dislikes',
- '@type': '@id'
- },
- shares: {
- '@id': 'as:shares',
- '@type': '@id'
- },
- comments: {
- '@id': 'as:comments',
- '@type': '@id'
- }
- }),
- Playlist: buildContext({
- Playlist: 'pt:Playlist',
- PlaylistElement: 'pt:PlaylistElement',
- position: {
- '@type': 'sc:Number',
- '@id': 'pt:position'
- },
- startTimestamp: {
- '@type': 'sc:Number',
- '@id': 'pt:startTimestamp'
- },
- stopTimestamp: {
- '@type': 'sc:Number',
- '@id': 'pt:stopTimestamp'
- },
- uuid: 'sc:identifier'
- }),
- CacheFile: buildContext({
- expires: 'sc:expires',
- CacheFile: 'pt:CacheFile'
- }),
- Flag: buildContext({
- Hashtag: 'as:Hashtag'
- }),
- Actor: buildContext({
- playlists: {
- '@id': 'pt:playlists',
- '@type': '@id'
- },
- support: {
- '@type': 'sc:Text',
- '@id': 'pt:support'
- },
- // TODO: remove in a few versions, introduced in 4.2
- icons: 'as:icon'
- }),
- WatchAction: buildContext({
- WatchAction: 'sc:WatchAction',
- startTimestamp: {
- '@type': 'sc:Number',
- '@id': 'pt:startTimestamp'
- },
- stopTimestamp: {
- '@type': 'sc:Number',
- '@id': 'pt:stopTimestamp'
- },
- watchSection: {
- '@type': 'sc:Number',
- '@id': 'pt:stopTimestamp'
- },
- uuid: 'sc:identifier'
- }),
- View: buildContext({
- WatchAction: 'sc:WatchAction',
- InteractionCounter: 'sc:InteractionCounter',
- interactionType: 'sc:interactionType',
- userInteractionCount: 'sc:userInteractionCount'
- }),
- Collection: buildContext(),
- Follow: buildContext(),
- Reject: buildContext(),
- Accept: buildContext(),
- Announce: buildContext(),
- Comment: buildContext(),
- Delete: buildContext(),
- Rate: buildContext(),
- Chapters: buildContext({
- name: 'sc:name',
- hasPart: 'sc:hasPart',
- endOffset: 'sc:endOffset',
- startOffset: 'sc:startOffset'
- })
- }
- async function getContextData (type: ContextType, contextFilter: ContextFilter) {
- const contextData = contextFilter
- ? await contextFilter(contextStore[type])
- : contextStore[type]
- return { '@context': contextData }
- }
- function buildContext (contextValue?: ContextValue) {
- const baseContext = [
- 'https://www.w3.org/ns/activitystreams',
- 'https://w3id.org/security/v1',
- {
- RsaSignature2017: 'https://w3id.org/security#RsaSignature2017'
- }
- ]
- if (!contextValue) return baseContext
- return [
- ...baseContext,
- {
- pt: 'https://joinpeertube.org/ns#',
- sc: 'http://schema.org/',
- ...contextValue
- }
- ]
- }
|