Chat Federation (and a lot more) WIP:
Note: websocket s2s is not working yet, still WIP. New Features * Chat Federation: * You can now connect to a remote chat with your local account. * This remote connection is done using a custom implementation of [XEP-0468: WebSocket S2S](https://xmpp.org/extensions/xep-0468.html), using some specific discovering method (so that it will work without any DNS configuration). Minor changes and fixes * Possibility to debug Prosody in development environments. * Using process.spawn instead of process.exec to launch Prosody (safer, and more optimal). * Prosody AppImage: fix path mapping: we only map necessary /etc/ subdir, so that the AppImage can access to /etc/resolv.conf, /etc/hosts, ... * Prosody AppImage: hidden debug mode to disable lua-unbound, that seems broken in some docker dev environments.
This commit is contained in:
@ -36,6 +36,7 @@ function remoteAuthenticatedConnectionEnabled (livechatInfos: LiveChatJSONLDAttr
|
||||
if (!livechatInfos.links) { return false }
|
||||
if (livechatInfos.type !== 'xmpp') { return false }
|
||||
for (const link of livechatInfos.links) {
|
||||
if (link.type === 'xmpp-peertube-livechat-ws-s2s') { return true }
|
||||
if (link.type === 'xmpp-s2s') { return true }
|
||||
}
|
||||
return false
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||
import type { RemoteVideoHandlerParams } from './types'
|
||||
import { storeVideoLiveChatInfos } from './storage'
|
||||
import { storeVideoLiveChatInfos, storeRemoteServerInfos } from './storage'
|
||||
import { sanitizePeertubeLiveChatInfos } from './sanitize'
|
||||
|
||||
/**
|
||||
@ -19,6 +19,9 @@ async function readIncomingAPVideo (
|
||||
peertubeLiveChat = sanitizePeertubeLiveChatInfos(peertubeLiveChat)
|
||||
|
||||
await storeVideoLiveChatInfos(options, video, peertubeLiveChat)
|
||||
if (video.remote) {
|
||||
await storeRemoteServerInfos(options, peertubeLiveChat)
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
|
@ -2,7 +2,7 @@ import type { RegisterServerOptions, VideoObject } from '@peertube/peertube-type
|
||||
import type { LiveChatVideoObject, VideoBuildResultContext, LiveChatJSONLDLink, LiveChatJSONLDAttribute } from './types'
|
||||
import { storeVideoLiveChatInfos } from './storage'
|
||||
import { videoHasWebchat } from '../../../shared/lib/video'
|
||||
import { getBoshUri, getWSUri } from '../uri/webchat'
|
||||
import { getBoshUri, getWSUri, getWSS2SUri } from '../uri/webchat'
|
||||
import { canonicalizePluginUri } from '../uri/canonicalize'
|
||||
import { getProsodyDomain } from '../prosody/config/domain'
|
||||
import { fillVideoCustomFields } from '../custom-fields'
|
||||
@ -32,7 +32,8 @@ async function videoBuildJSONLD (
|
||||
'prosody-room-type',
|
||||
'federation-dont-publish-remotely',
|
||||
'chat-no-anonymous',
|
||||
'prosody-room-allow-s2s'
|
||||
'prosody-room-allow-s2s',
|
||||
'prosody-s2s-port'
|
||||
])
|
||||
|
||||
if (settings['federation-dont-publish-remotely']) {
|
||||
@ -71,9 +72,23 @@ async function videoBuildJSONLD (
|
||||
}
|
||||
|
||||
const links: LiveChatJSONLDLink[] = []
|
||||
if (!settings['federation-dont-publish-remotely']) {
|
||||
const wsS2SUri = getWSS2SUri(options)
|
||||
if (wsS2SUri) {
|
||||
links.push({
|
||||
type: 'xmpp-peertube-livechat-ws-s2s',
|
||||
url: canonicalizePluginUri(options, wsS2SUri, {
|
||||
removePluginVersion: true,
|
||||
protocol: 'ws'
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
if (settings['prosody-room-allow-s2s']) {
|
||||
links.push({
|
||||
type: 'xmpp-s2s'
|
||||
type: 'xmpp-s2s',
|
||||
host: prosodyDomain,
|
||||
port: (settings['prosody-s2s-port'] as string) ?? ''
|
||||
})
|
||||
}
|
||||
if (!settings['chat-no-anonymous']) {
|
||||
|
@ -37,8 +37,34 @@ function sanitizePeertubeLiveChatInfos (chatInfos: any): LiveChatJSONLDAttribute
|
||||
})
|
||||
}
|
||||
if (link.type === 'xmpp-s2s') {
|
||||
if (!/^\d+$/.test(link.port)) {
|
||||
continue
|
||||
}
|
||||
const host = _validateHost(link.host)
|
||||
if (!host) {
|
||||
continue
|
||||
}
|
||||
r.links.push({
|
||||
type: link.type
|
||||
type: link.type,
|
||||
host,
|
||||
port: link.port
|
||||
})
|
||||
}
|
||||
if (link.type === 'xmpp-peertube-livechat-ws-s2s') {
|
||||
if ((typeof link.url) !== 'string') { continue }
|
||||
|
||||
if (
|
||||
!_validUrl(link.url, {
|
||||
noSearchParams: true,
|
||||
protocol: 'ws.'
|
||||
})
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
r.links.push({
|
||||
type: link.type,
|
||||
url: link.url
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -81,6 +107,16 @@ function _validUrl (s: string, constraints: URLConstraints): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
function _validateHost (s: string): false | string {
|
||||
try {
|
||||
if (s.includes('/')) { return false }
|
||||
const url = new URL('http://' + s)
|
||||
return url.hostname
|
||||
} catch (_err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
sanitizePeertubeLiveChatInfos
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { RegisterServerOptions, MVideoFullLight, MVideoAP, Video, MVideoThumbnail } from '@peertube/peertube-types'
|
||||
import type { LiveChatJSONLDAttribute } from './types'
|
||||
import type { LiveChatJSONLDAttribute, LiveChatJSONLDS2SLink, LiveChatJSONLDPeertubeWSS2SLink } from './types'
|
||||
import { sanitizePeertubeLiveChatInfos } from './sanitize'
|
||||
import { URL } from 'url'
|
||||
import * as fs from 'fs'
|
||||
@ -97,6 +97,60 @@ async function getVideoLiveChatInfos (
|
||||
return r
|
||||
}
|
||||
|
||||
/**
|
||||
* When receiving livechat information for remote servers, we store some information
|
||||
* about remote server capatibilities: has it s2s enabled? can it proxify s2s in Peertube?
|
||||
* These information can then be read by Prosody module mod_s2s_peertubelivechat.
|
||||
*
|
||||
* We simply store the more recent informations. Indeed, it should be consistent between videos.
|
||||
* @param options server optiosn
|
||||
* @param liveChatInfos livechat stored data
|
||||
*/
|
||||
async function storeRemoteServerInfos (
|
||||
options: RegisterServerOptions,
|
||||
liveChatInfos: LiveChatJSONLDAttribute
|
||||
): Promise<void> {
|
||||
if (!liveChatInfos) { return }
|
||||
|
||||
const logger = options.peertubeHelpers.logger
|
||||
|
||||
const roomJID = liveChatInfos.jid
|
||||
const host = roomJID.split('@')[1]
|
||||
if (!host) {
|
||||
logger.error(`Room JID seems not correct, no host: ${roomJID}`)
|
||||
return
|
||||
}
|
||||
if (host.includes('..')) {
|
||||
logger.error(`Room host seems not correct, contains ..: ${host}`)
|
||||
return
|
||||
}
|
||||
const dir = path.resolve(
|
||||
options.peertubeHelpers.plugin.getDataDirectoryPath(),
|
||||
'serverInfos',
|
||||
host
|
||||
)
|
||||
const s2sFilePath = path.resolve(dir, 's2s')
|
||||
const wsS2SFilePath = path.resolve(dir, 'ws-s2s')
|
||||
|
||||
const s2sLink = liveChatInfos.links.find(v => v.type === 'xmpp-s2s')
|
||||
if (s2sLink) {
|
||||
await _store(options, s2sFilePath, {
|
||||
host: (s2sLink as LiveChatJSONLDS2SLink).host,
|
||||
port: (s2sLink as LiveChatJSONLDS2SLink).port
|
||||
})
|
||||
} else {
|
||||
await _del(options, s2sFilePath)
|
||||
}
|
||||
const wsS2SLink = liveChatInfos.links.find(v => v.type === 'xmpp-peertube-livechat-ws-s2s')
|
||||
if (wsS2SLink) {
|
||||
await _store(options, wsS2SFilePath, {
|
||||
url: (wsS2SLink as LiveChatJSONLDPeertubeWSS2SLink).url
|
||||
})
|
||||
} else {
|
||||
await _del(options, wsS2SFilePath)
|
||||
}
|
||||
}
|
||||
|
||||
async function _getFilePath (
|
||||
options: RegisterServerOptions,
|
||||
remote: boolean,
|
||||
@ -152,13 +206,22 @@ async function _del (options: RegisterServerOptions, filePath: string): Promise<
|
||||
async function _store (options: RegisterServerOptions, filePath: string, content: any): Promise<void> {
|
||||
const logger = options.peertubeHelpers.logger
|
||||
try {
|
||||
const jsonContent = JSON.stringify(content)
|
||||
if (!fs.existsSync(filePath)) {
|
||||
const dir = path.dirname(filePath)
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true })
|
||||
}
|
||||
} else {
|
||||
// only write if the content is different
|
||||
try {
|
||||
const currentJSONContent = await fs.promises.readFile(filePath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
if (currentJSONContent === jsonContent) { return }
|
||||
} catch (_err) {}
|
||||
}
|
||||
await fs.promises.writeFile(filePath, JSON.stringify(content), {
|
||||
await fs.promises.writeFile(filePath, jsonContent, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
} catch (err) {
|
||||
@ -182,7 +245,16 @@ async function _get (options: RegisterServerOptions, filePath: string): Promise<
|
||||
}
|
||||
}
|
||||
|
||||
function getRemoteServerInfosDir (options: RegisterServerOptions): string {
|
||||
return path.resolve(
|
||||
options.peertubeHelpers.plugin.getDataDirectoryPath(),
|
||||
'serverInfos'
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
storeVideoLiveChatInfos,
|
||||
getVideoLiveChatInfos
|
||||
storeRemoteServerInfos,
|
||||
getVideoLiveChatInfos,
|
||||
getRemoteServerInfosDir
|
||||
}
|
||||
|
@ -4,8 +4,15 @@ interface VideoBuildResultContext {
|
||||
video: MVideoAP
|
||||
}
|
||||
|
||||
interface LiveChatJSONLDPeertubeWSS2SLink {
|
||||
type: 'xmpp-peertube-livechat-ws-s2s'
|
||||
url: string
|
||||
}
|
||||
|
||||
interface LiveChatJSONLDS2SLink {
|
||||
type: 'xmpp-s2s'
|
||||
host: string
|
||||
port: string
|
||||
}
|
||||
|
||||
interface LiveChatJSONLDAnonymousWebsocketLink {
|
||||
@ -20,7 +27,11 @@ interface LiveChatJSONLDAnonymousBOSHLink {
|
||||
jid: string
|
||||
}
|
||||
|
||||
type LiveChatJSONLDLink = LiveChatJSONLDS2SLink | LiveChatJSONLDAnonymousBOSHLink | LiveChatJSONLDAnonymousWebsocketLink
|
||||
type LiveChatJSONLDLink =
|
||||
LiveChatJSONLDPeertubeWSS2SLink
|
||||
| LiveChatJSONLDS2SLink
|
||||
| LiveChatJSONLDAnonymousBOSHLink
|
||||
| LiveChatJSONLDAnonymousWebsocketLink
|
||||
|
||||
interface LiveChatJSONLDInfos {
|
||||
type: 'xmpp'
|
||||
@ -42,6 +53,8 @@ interface RemoteVideoHandlerParams {
|
||||
export {
|
||||
VideoBuildResultContext,
|
||||
LiveChatJSONLDLink,
|
||||
LiveChatJSONLDS2SLink,
|
||||
LiveChatJSONLDPeertubeWSS2SLink,
|
||||
LiveChatJSONLDInfos,
|
||||
LiveChatJSONLDAttribute,
|
||||
LiveChatVideoObject,
|
||||
|
Reference in New Issue
Block a user