Chat Federation: fetch remote server information when missing.
This commit is contained in:
parent
6ed69d2c2f
commit
e719dc3079
@ -1,6 +1,8 @@
|
|||||||
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||||
|
import { hasRemoteServerInfos, storeRemoteServerInfos } from './storage'
|
||||||
import { getBaseRouterRoute } from '../helpers'
|
import { getBaseRouterRoute } from '../helpers'
|
||||||
import { canonicalizePluginUri } from '../uri/canonicalize'
|
import { canonicalizePluginUri } from '../uri/canonicalize'
|
||||||
|
import { sanitizePeertubeLiveChatServerInfos } from './sanitize'
|
||||||
import { URL } from 'url'
|
import { URL } from 'url'
|
||||||
const got = require('got')
|
const got = require('got')
|
||||||
|
|
||||||
@ -33,13 +35,18 @@ const got = require('got')
|
|||||||
* @param remoteInstanceUrl remote instance url to check (as readed in the request header)
|
* @param remoteInstanceUrl remote instance url to check (as readed in the request header)
|
||||||
* @returns true if the remote instance is ok
|
* @returns true if the remote instance is ok
|
||||||
*/
|
*/
|
||||||
async function remoteServerInfos (
|
async function fetchMissingRemoteServerInfos (
|
||||||
options: RegisterServerOptions,
|
options: RegisterServerOptions,
|
||||||
remoteInstanceUrl: string
|
remoteInstanceUrl: string
|
||||||
): Promise<boolean> {
|
): Promise<void> {
|
||||||
const logger = options.peertubeHelpers.logger
|
const logger = options.peertubeHelpers.logger
|
||||||
logger.debug(`remoteServerInfos: checking if we have remote server infos for host ${remoteInstanceUrl}.`)
|
logger.debug(`remoteServerInfos: checking if we have remote server infos for host ${remoteInstanceUrl}.`)
|
||||||
|
|
||||||
|
// FIXME: add a max age.
|
||||||
|
if (await hasRemoteServerInfos(options, remoteInstanceUrl)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let url: string
|
let url: string
|
||||||
try {
|
try {
|
||||||
const u = new URL(remoteInstanceUrl)
|
const u = new URL(remoteInstanceUrl)
|
||||||
@ -53,7 +60,7 @@ async function remoteServerInfos (
|
|||||||
})
|
})
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
logger.info('remoteServerInfos: Invalid remote instance url provided: ' + remoteInstanceUrl)
|
logger.info('remoteServerInfos: Invalid remote instance url provided: ' + remoteInstanceUrl)
|
||||||
return false
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -66,18 +73,18 @@ async function remoteServerInfos (
|
|||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
logger.info('remoteServerInfos: Invalid remote server options')
|
logger.info('remoteServerInfos: Invalid remote server options')
|
||||||
return false
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME/TODO
|
const serverInfos = sanitizePeertubeLiveChatServerInfos(options, response)
|
||||||
|
if (serverInfos) {
|
||||||
return true
|
await storeRemoteServerInfos(options, serverInfos)
|
||||||
|
}
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
logger.info('remoteServerInfos: Can\'t get remote instance informations using url ' + url)
|
logger.info('remoteServerInfos: Can\'t get remote instance informations using url ' + url)
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
remoteServerInfos
|
fetchMissingRemoteServerInfos
|
||||||
}
|
}
|
@ -20,7 +20,9 @@ async function readIncomingAPVideo (
|
|||||||
|
|
||||||
await storeVideoLiveChatInfos(options, video, peertubeLiveChat)
|
await storeVideoLiveChatInfos(options, video, peertubeLiveChat)
|
||||||
if (video.remote) {
|
if (video.remote) {
|
||||||
await storeRemoteServerInfos(options, peertubeLiveChat)
|
if (peertubeLiveChat !== false && peertubeLiveChat.xmppserver) {
|
||||||
|
await storeRemoteServerInfos(options, peertubeLiveChat.xmppserver)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||||
import type { LiveChatJSONLDAttributeV1 } from './types'
|
import type { LiveChatJSONLDAttributeV1, PeertubeXMPPServerInfos } from './types'
|
||||||
import { URL } from 'url'
|
import { URL } from 'url'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,7 +28,24 @@ function sanitizePeertubeLiveChatInfos (options: RegisterServerOptions, chatInfo
|
|||||||
if (!chatInfos.xmppserver || (typeof chatInfos.xmppserver !== 'object')) {
|
if (!chatInfos.xmppserver || (typeof chatInfos.xmppserver !== 'object')) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const xmppserver = chatInfos.xmppserver
|
const xmppserver = sanitizePeertubeLiveChatServerInfos(options, chatInfos.xmppserver)
|
||||||
|
if (!xmppserver) { return false }
|
||||||
|
|
||||||
|
const r: LiveChatJSONLDAttributeV1 = {
|
||||||
|
type: chatInfos.type,
|
||||||
|
jid: chatInfos.jid,
|
||||||
|
xmppserver
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizePeertubeLiveChatServerInfos (
|
||||||
|
options: RegisterServerOptions, xmppserver: any
|
||||||
|
): PeertubeXMPPServerInfos | false {
|
||||||
|
if (!xmppserver || (typeof xmppserver !== 'object')) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if ((typeof xmppserver.host) !== 'string') { return false }
|
if ((typeof xmppserver.host) !== 'string') { return false }
|
||||||
const host = _validateHost(xmppserver.host)
|
const host = _validateHost(xmppserver.host)
|
||||||
@ -37,20 +54,16 @@ function sanitizePeertubeLiveChatInfos (options: RegisterServerOptions, chatInfo
|
|||||||
const muc = _validateHost(xmppserver.muc)
|
const muc = _validateHost(xmppserver.muc)
|
||||||
if (!muc) { return false }
|
if (!muc) { return false }
|
||||||
|
|
||||||
const r: LiveChatJSONLDAttributeV1 = {
|
const r: PeertubeXMPPServerInfos = {
|
||||||
type: chatInfos.type,
|
host,
|
||||||
jid: chatInfos.jid,
|
muc
|
||||||
xmppserver: {
|
|
||||||
host,
|
|
||||||
muc
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xmppserver.directs2s) {
|
if (xmppserver.directs2s) {
|
||||||
if ((typeof xmppserver.directs2s) === 'object') {
|
if ((typeof xmppserver.directs2s) === 'object') {
|
||||||
const port = xmppserver.directs2s.port
|
const port = xmppserver.directs2s.port
|
||||||
if ((typeof port === 'string') && /^\d+$/.test(port)) {
|
if ((typeof port === 'string') && /^\d+$/.test(port)) {
|
||||||
r.xmppserver.directs2s = {
|
r.directs2s = {
|
||||||
port
|
port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,7 +76,7 @@ function sanitizePeertubeLiveChatInfos (options: RegisterServerOptions, chatInfo
|
|||||||
noSearchParams: true,
|
noSearchParams: true,
|
||||||
protocol: 'ws.'
|
protocol: 'ws.'
|
||||||
})) {
|
})) {
|
||||||
r.xmppserver.websockets2s = {
|
r.websockets2s = {
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,7 +85,7 @@ function sanitizePeertubeLiveChatInfos (options: RegisterServerOptions, chatInfo
|
|||||||
if (xmppserver.anonymous) {
|
if (xmppserver.anonymous) {
|
||||||
const virtualhost = _validateHost(xmppserver.anonymous.virtualhost)
|
const virtualhost = _validateHost(xmppserver.anonymous.virtualhost)
|
||||||
if (virtualhost) {
|
if (virtualhost) {
|
||||||
r.xmppserver.anonymous = {
|
r.anonymous = {
|
||||||
virtualhost
|
virtualhost
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +94,7 @@ function sanitizePeertubeLiveChatInfos (options: RegisterServerOptions, chatInfo
|
|||||||
noSearchParams: true,
|
noSearchParams: true,
|
||||||
protocol: 'http.'
|
protocol: 'http.'
|
||||||
})) {
|
})) {
|
||||||
r.xmppserver.anonymous.bosh = bosh
|
r.anonymous.bosh = bosh
|
||||||
}
|
}
|
||||||
|
|
||||||
const websocket = xmppserver.anonymous.websocket
|
const websocket = xmppserver.anonymous.websocket
|
||||||
@ -89,7 +102,7 @@ function sanitizePeertubeLiveChatInfos (options: RegisterServerOptions, chatInfo
|
|||||||
noSearchParams: true,
|
noSearchParams: true,
|
||||||
protocol: 'ws.'
|
protocol: 'ws.'
|
||||||
})) {
|
})) {
|
||||||
r.xmppserver.anonymous.websocket = websocket
|
r.anonymous.websocket = websocket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,6 +219,23 @@ function _sanitizePeertubeLiveChatInfosV0 (options: RegisterServerOptions, chatI
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
function sanitizeXMPPHost (options: RegisterServerOptions, host: any): false | string {
|
||||||
sanitizePeertubeLiveChatInfos
|
return _validateHost(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeXMPPHostFromInstanceUrl (_options: RegisterServerOptions, s: any): false | string {
|
||||||
|
try {
|
||||||
|
if (typeof s !== 'string') { return false }
|
||||||
|
const url = new URL(s)
|
||||||
|
return url.hostname
|
||||||
|
} catch (_err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
sanitizePeertubeLiveChatInfos,
|
||||||
|
sanitizePeertubeLiveChatServerInfos,
|
||||||
|
sanitizeXMPPHost,
|
||||||
|
sanitizeXMPPHostFromInstanceUrl
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { RegisterServerOptions, MVideoFullLight, MVideoAP, Video, MVideoThumbnail } from '@peertube/peertube-types'
|
import type { RegisterServerOptions, MVideoFullLight, MVideoAP, Video, MVideoThumbnail } from '@peertube/peertube-types'
|
||||||
import type { LiveChatJSONLDAttribute, LiveChatJSONLDAttributeV1 } from './types'
|
import type { LiveChatJSONLDAttribute, LiveChatJSONLDAttributeV1, PeertubeXMPPServerInfos } from './types'
|
||||||
import { sanitizePeertubeLiveChatInfos } from './sanitize'
|
import { sanitizePeertubeLiveChatInfos, sanitizeXMPPHostFromInstanceUrl } from './sanitize'
|
||||||
import { URL } from 'url'
|
import { URL } from 'url'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
@ -111,21 +111,18 @@ async function getVideoLiveChatInfos (
|
|||||||
* kind of urls.
|
* kind of urls.
|
||||||
*
|
*
|
||||||
* @param options server optiosn
|
* @param options server optiosn
|
||||||
* @param liveChatInfos livechat stored data
|
* @param xmppserver remote server informations
|
||||||
*/
|
*/
|
||||||
async function storeRemoteServerInfos (
|
async function storeRemoteServerInfos (
|
||||||
options: RegisterServerOptions,
|
options: RegisterServerOptions,
|
||||||
liveChatInfos: LiveChatJSONLDAttributeV1
|
xmppserver: PeertubeXMPPServerInfos
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!liveChatInfos) { return }
|
|
||||||
if (!liveChatInfos.xmppserver) { return }
|
|
||||||
|
|
||||||
const logger = options.peertubeHelpers.logger
|
const logger = options.peertubeHelpers.logger
|
||||||
|
|
||||||
const mainHost = liveChatInfos.xmppserver.host
|
const mainHost = xmppserver.host
|
||||||
const hosts = [
|
const hosts = [
|
||||||
liveChatInfos.xmppserver.host,
|
xmppserver.host,
|
||||||
liveChatInfos.xmppserver.muc
|
xmppserver.muc
|
||||||
]
|
]
|
||||||
|
|
||||||
for (const host of hosts) {
|
for (const host of hosts) {
|
||||||
@ -144,27 +141,55 @@ async function storeRemoteServerInfos (
|
|||||||
)
|
)
|
||||||
const s2sFilePath = path.resolve(dir, 's2s')
|
const s2sFilePath = path.resolve(dir, 's2s')
|
||||||
const wsS2SFilePath = path.resolve(dir, 'ws-s2s')
|
const wsS2SFilePath = path.resolve(dir, 'ws-s2s')
|
||||||
|
const timestampFilePath = path.resolve(dir, 'last-update')
|
||||||
|
|
||||||
if (liveChatInfos.xmppserver.directs2s?.port) {
|
if (xmppserver.directs2s?.port) {
|
||||||
await _store(options, s2sFilePath, {
|
await _store(options, s2sFilePath, {
|
||||||
host: mainHost,
|
host: mainHost,
|
||||||
port: liveChatInfos.xmppserver.directs2s.port
|
port: xmppserver.directs2s.port
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
await _del(options, s2sFilePath)
|
await _del(options, s2sFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (liveChatInfos.xmppserver.websockets2s?.url) {
|
if (xmppserver.websockets2s?.url) {
|
||||||
await _store(options, wsS2SFilePath, {
|
await _store(options, wsS2SFilePath, {
|
||||||
host: mainHost,
|
host: mainHost,
|
||||||
url: liveChatInfos.xmppserver.websockets2s.url
|
url: xmppserver.websockets2s.url
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
await _del(options, wsS2SFilePath)
|
await _del(options, wsS2SFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await _store(options, timestampFilePath, {
|
||||||
|
timestamp: (new Date()).getTime()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if we have the remote hosts informations.
|
||||||
|
* @param options server options
|
||||||
|
* @param host host domain
|
||||||
|
*/
|
||||||
|
async function hasRemoteServerInfos (options: RegisterServerOptions, hostParam: any): Promise<boolean> {
|
||||||
|
const host = sanitizeXMPPHostFromInstanceUrl(options, hostParam)
|
||||||
|
if (!host) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (host.includes('..')) {
|
||||||
|
options.peertubeHelpers.logger.error(`Host seems not correct, contains ..: ${host}`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const filePath = path.resolve(
|
||||||
|
options.peertubeHelpers.plugin.getDataDirectoryPath(),
|
||||||
|
'serverInfos',
|
||||||
|
host,
|
||||||
|
'last-update'
|
||||||
|
)
|
||||||
|
return fs.existsSync(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
async function _getFilePath (
|
async function _getFilePath (
|
||||||
options: RegisterServerOptions,
|
options: RegisterServerOptions,
|
||||||
remote: boolean,
|
remote: boolean,
|
||||||
@ -269,6 +294,7 @@ function getRemoteServerInfosDir (options: RegisterServerOptions): string {
|
|||||||
export {
|
export {
|
||||||
storeVideoLiveChatInfos,
|
storeVideoLiveChatInfos,
|
||||||
storeRemoteServerInfos,
|
storeRemoteServerInfos,
|
||||||
|
hasRemoteServerInfos,
|
||||||
getVideoLiveChatInfos,
|
getVideoLiveChatInfos,
|
||||||
getRemoteServerInfosDir
|
getRemoteServerInfosDir
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import { getProsodyDomain } from '../prosody/config/domain'
|
|||||||
import { fillVideoCustomFields } from '../custom-fields'
|
import { fillVideoCustomFields } from '../custom-fields'
|
||||||
import { getChannelInfosById } from '../database/channel'
|
import { getChannelInfosById } from '../database/channel'
|
||||||
import { ensureProsodyRunning } from '../prosody/ctl'
|
import { ensureProsodyRunning } from '../prosody/ctl'
|
||||||
|
import { serverBuildInfos } from '../federation/outgoing'
|
||||||
import { isDebugMode } from '../debug'
|
import { isDebugMode } from '../debug'
|
||||||
|
|
||||||
// See here for description: https://modules.prosody.im/mod_muc_http_defaults.html
|
// See here for description: https://modules.prosody.im/mod_muc_http_defaults.html
|
||||||
@ -224,14 +225,13 @@ async function initApiRouter (options: RegisterServerOptions): Promise<Router> {
|
|||||||
}
|
}
|
||||||
))
|
))
|
||||||
|
|
||||||
// router.get('/federation_server_infos', asyncMiddleware(
|
router.get('/federation_server_infos', asyncMiddleware(
|
||||||
// async (req: Request, res: Response, _next: NextFunction) => {
|
async (req: Request, res: Response, _next: NextFunction) => {
|
||||||
// logger.info('federation_server_infos api call')
|
logger.info('federation_server_infos api call')
|
||||||
// // TODO/FIXME: return server infos.
|
const result = await serverBuildInfos(options)
|
||||||
// // TODO/FIXME: store these informations on the other side.
|
res.json(result)
|
||||||
// res.json({ ok: true })
|
}
|
||||||
// }
|
))
|
||||||
// ))
|
|
||||||
|
|
||||||
if (isDebugMode(options)) {
|
if (isDebugMode(options)) {
|
||||||
// Only add this route if the debug mode is enabled at time of the server launch.
|
// Only add this route if the debug mode is enabled at time of the server launch.
|
||||||
|
@ -18,7 +18,7 @@ import { LiveChatJSONLDAttributeV1 } from '../federation/types'
|
|||||||
import {
|
import {
|
||||||
anonymousConnectionInfos, compatibleRemoteAuthenticatedConnectionEnabled
|
anonymousConnectionInfos, compatibleRemoteAuthenticatedConnectionEnabled
|
||||||
} from '../federation/connection-infos'
|
} from '../federation/connection-infos'
|
||||||
// import { remoteServerInfos } from '../federation/remote-infos'
|
import { fetchMissingRemoteServerInfos } from '../federation/fetch-infos'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
const got = require('got')
|
const got = require('got')
|
||||||
|
|
||||||
@ -290,11 +290,11 @@ async function initWebchatRouter (options: RegisterServerOptionsV5): Promise<Rou
|
|||||||
// If the incomming request is from a remote Peertube instance, we must ensure that we know
|
// If the incomming request is from a remote Peertube instance, we must ensure that we know
|
||||||
// how to connect to it using Websocket S2S (for the dialback mecanism).
|
// how to connect to it using Websocket S2S (for the dialback mecanism).
|
||||||
const remoteInstanceUrl = request.headers['peertube-livechat-ws-s2s-instance-url']
|
const remoteInstanceUrl = request.headers['peertube-livechat-ws-s2s-instance-url']
|
||||||
if (remoteInstanceUrl && (typeof remoteInstanceUrl !== 'string')) {
|
if (remoteInstanceUrl && (typeof remoteInstanceUrl === 'string')) {
|
||||||
// Note: remoteServerInfos will store the information,
|
// Note: fetchMissingRemoteServerInfos will store the information,
|
||||||
// so that the Prosody mod_s2s_peertubelivechat module can access them.
|
// so that the Prosody mod_s2s_peertubelivechat module can access them.
|
||||||
// TODO
|
// We dont need to read the result.
|
||||||
// await remoteServerInfos(options, remoteInstanceUrl)
|
await fetchMissingRemoteServerInfos(options, remoteInstanceUrl)
|
||||||
}
|
}
|
||||||
currentS2SWebsocketProxy.ws(request, socket, head)
|
currentS2SWebsocketProxy.ws(request, socket, head)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user