Reverting work on DemoBot (it is now an external package).
This commit is contained in:
@ -3,28 +3,15 @@ For internal API, we will generate an api Key that must be provided as
|
||||
GET parameter for every API call.
|
||||
*/
|
||||
|
||||
async function _getKey ({ storageManager }: RegisterServerOptions, key: string): Promise<string> {
|
||||
let value: string = await storageManager.getData(key)
|
||||
async function getAPIKey ({ storageManager }: RegisterServerOptions): Promise<string> {
|
||||
let value: string = await storageManager.getData('APIKEY')
|
||||
if (!value) {
|
||||
value = Math.random().toString(36).slice(2, 12)
|
||||
await storageManager.storeData(key, value)
|
||||
await storageManager.storeData('APIKEY', value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
async function getAPIKey (options: RegisterServerOptions): Promise<string> {
|
||||
return _getKey(options, 'APIKEY')
|
||||
}
|
||||
|
||||
async function getExternalComponentKey (options: RegisterServerOptions, componentName: string): Promise<string> {
|
||||
if (!/^[A-Z]+$/.test(componentName)) {
|
||||
throw new Error('Invalid component name: ' + componentName)
|
||||
}
|
||||
const key = 'EXTERNALCOMPONENTKEY_' + componentName
|
||||
return _getKey(options, key)
|
||||
}
|
||||
|
||||
export {
|
||||
getAPIKey,
|
||||
getExternalComponentKey
|
||||
getAPIKey
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getProsodyConfig, getWorkingDir } from '../prosody/config'
|
||||
import { getProsodyConfig, getProsodyConfigContentForDiagnostic, getWorkingDir } from '../prosody/config'
|
||||
import { getProsodyAbout, testProsodyCorrectlyRunning } from '../prosody/ctl'
|
||||
import { newResult, TestResult } from './utils'
|
||||
import { getAPIKey } from '../apikey'
|
||||
@ -24,6 +24,7 @@ export async function diagProsody (test: string, options: RegisterServerOptions)
|
||||
let prosodyHost: string
|
||||
try {
|
||||
const wantedConfig = await getProsodyConfig(options)
|
||||
const filePath = wantedConfig.paths.config
|
||||
|
||||
result.messages.push(`Prosody will run on port '${wantedConfig.port}'`)
|
||||
prosodyPort = wantedConfig.port
|
||||
@ -49,44 +50,34 @@ export async function diagProsody (test: string, options: RegisterServerOptions)
|
||||
}
|
||||
result.messages.push(`Room content will be saved for '${wantedConfig.logExpiration.value}'`)
|
||||
|
||||
if (wantedConfig.bots.demobot) {
|
||||
result.messages.push(`The Demo bot is active for videos: ${wantedConfig.bots.demobot.join(', ')}`)
|
||||
}
|
||||
await fs.promises.access(filePath, fs.constants.R_OK) // throw an error if file does not exist.
|
||||
result.messages.push(`The prosody configuration file (${filePath}) exists`)
|
||||
const actualContent = await fs.promises.readFile(filePath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
|
||||
const configFiles = wantedConfig.getConfigFiles()
|
||||
for (const configFile of configFiles) {
|
||||
const filePath = configFile.path
|
||||
const configFileKey = configFile.key
|
||||
|
||||
await fs.promises.access(filePath, fs.constants.R_OK) // throw an error if file does not exist.
|
||||
result.messages.push(`The prosody '${configFileKey}' configuration file (${filePath}) exists`)
|
||||
const actualContent = await fs.promises.readFile(filePath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
result.debug.push({
|
||||
title: 'Current prosody configuration',
|
||||
// we have to hide secret keys and other values.
|
||||
// But here, we haven't them for actualContent.
|
||||
// So we will use values in wantedConfig, hopping it is enough.
|
||||
message: getProsodyConfigContentForDiagnostic(wantedConfig, actualContent)
|
||||
})
|
||||
|
||||
const wantedContent = wantedConfig.content
|
||||
if (actualContent === wantedContent) {
|
||||
result.messages.push('Prosody configuration file content is correct.')
|
||||
} else {
|
||||
result.messages.push('Prosody configuration file content is not correct.')
|
||||
result.debug.push({
|
||||
title: `Current prosody '${configFileKey}' configuration`,
|
||||
// we have to hide secret keys and other values.
|
||||
// But here, we haven't them for actualContent.
|
||||
// So we will use values in wantedConfig, hopping it is enough.
|
||||
message: wantedConfig.contentForDiagnostic(actualContent)
|
||||
title: 'Prosody configuration should be',
|
||||
// we have to hide secret keys and other values:
|
||||
message: getProsodyConfigContentForDiagnostic(wantedConfig)
|
||||
})
|
||||
|
||||
const wantedContent = configFile.content
|
||||
if (actualContent === wantedContent) {
|
||||
result.messages.push(`Prosody configuration file '${configFileKey}' content is correct.`)
|
||||
} else {
|
||||
result.messages.push(`Prosody configuration file '${configFileKey}'' content is not correct.`)
|
||||
result.debug.push({
|
||||
title: `Prosody configuration '${configFileKey}' should be`,
|
||||
// we have to hide secret keys and other values:
|
||||
message: wantedConfig.contentForDiagnostic(wantedContent)
|
||||
})
|
||||
return result
|
||||
}
|
||||
return result
|
||||
}
|
||||
} catch (error) {
|
||||
result.messages.push('Error when testing the prosody config: ' + (error as string))
|
||||
result.messages.push('Error when requiring the prosody config file: ' + (error as string))
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,8 @@ import { getBaseRouterRoute } from '../helpers'
|
||||
import { ProsodyFilePaths } from './config/paths'
|
||||
import { ConfigLogExpiration, ProsodyConfigContent } from './config/content'
|
||||
import { getProsodyDomain } from './config/domain'
|
||||
import { getAPIKey, getExternalComponentKey } from '../apikey'
|
||||
import { getAPIKey } from '../apikey'
|
||||
import type { ProsodyLogLevel } from './config/content'
|
||||
import { parseConfigDemoBotUUIDs } from './config/bots'
|
||||
|
||||
async function getWorkingDir (options: RegisterServerOptions): Promise<string> {
|
||||
const peertubeHelpers = options.peertubeHelpers
|
||||
@ -24,9 +23,9 @@ async function getWorkingDir (options: RegisterServerOptions): Promise<string> {
|
||||
/**
|
||||
* Creates the working dir if needed, and returns it.
|
||||
*/
|
||||
async function ensureWorkingDirs (options: RegisterServerOptions): Promise<string> {
|
||||
async function ensureWorkingDir (options: RegisterServerOptions): Promise<string> {
|
||||
const logger = options.peertubeHelpers.logger
|
||||
logger.debug('Calling ensureworkingDirs')
|
||||
logger.debug('Calling ensureworkingDir')
|
||||
|
||||
const paths = await getProsodyFilePaths(options)
|
||||
const dir = paths.dir
|
||||
@ -39,12 +38,10 @@ async function ensureWorkingDirs (options: RegisterServerOptions): Promise<strin
|
||||
await fs.promises.access(dir, fs.constants.W_OK) // will throw an error if no access
|
||||
logger.debug(`Write access ok on ${dir}`)
|
||||
|
||||
for (const path of [paths.data, paths.bots.dir]) {
|
||||
if (!fs.existsSync(path)) {
|
||||
logger.info(`The data dir ${path} does not exists, trying to create it`)
|
||||
await fs.promises.mkdir(path)
|
||||
logger.debug(`Working dir ${path} was created`)
|
||||
}
|
||||
if (!fs.existsSync(paths.data)) {
|
||||
logger.info(`The data dir ${paths.data} does not exists, trying to create it`)
|
||||
await fs.promises.mkdir(paths.data)
|
||||
logger.debug(`Working dir ${paths.data} was created`)
|
||||
}
|
||||
|
||||
return dir
|
||||
@ -62,61 +59,25 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise<Pro
|
||||
log: path.resolve(dir, 'prosody.log'),
|
||||
config: path.resolve(dir, 'prosody.cfg.lua'),
|
||||
data: path.resolve(dir, 'data'),
|
||||
bots: {
|
||||
dir: path.resolve(dir, 'bots'),
|
||||
demobot: path.resolve(dir, 'bots', 'demobot.js')
|
||||
},
|
||||
modules: path.resolve(__dirname, '../../prosody-modules')
|
||||
}
|
||||
}
|
||||
|
||||
interface ProsodyConfigBots {
|
||||
demobot?: string[] // if the demo bot is activated, here are the video UUIDS where it will be.
|
||||
}
|
||||
|
||||
type ProsodyConfigFilesKey = 'prosody' | 'demobot'
|
||||
type ProsodyConfigFiles = Array<{
|
||||
key: ProsodyConfigFilesKey
|
||||
path: string
|
||||
interface ProsodyConfig {
|
||||
content: string
|
||||
}>
|
||||
|
||||
class ProsodyConfig {
|
||||
constructor (
|
||||
private readonly configFiles: ProsodyConfigFiles,
|
||||
public paths: ProsodyFilePaths,
|
||||
public host: string,
|
||||
public port: string,
|
||||
public baseApiUrl: string,
|
||||
public roomType: 'video' | 'channel',
|
||||
public logByDefault: boolean,
|
||||
public logExpiration: ConfigLogExpiration,
|
||||
public bots: ProsodyConfigBots,
|
||||
public valuesToHideInDiagnostic: {[key: string]: string}
|
||||
) {}
|
||||
|
||||
public getConfigFiles (): ProsodyConfigFiles {
|
||||
return this.configFiles
|
||||
}
|
||||
|
||||
public contentForDiagnostic (content: string): string {
|
||||
let r: string = content
|
||||
for (const key in this.valuesToHideInDiagnostic) {
|
||||
// replaceAll not available, using trick:
|
||||
r = r.split(this.valuesToHideInDiagnostic[key]).join(`***${key}***`)
|
||||
}
|
||||
return r
|
||||
}
|
||||
paths: ProsodyFilePaths
|
||||
host: string
|
||||
port: string
|
||||
baseApiUrl: string
|
||||
roomType: 'video' | 'channel'
|
||||
logByDefault: boolean
|
||||
logExpiration: ConfigLogExpiration
|
||||
valuesToHideInDiagnostic: {[key: string]: string}
|
||||
}
|
||||
|
||||
async function getProsodyConfig (options: RegisterServerOptions): Promise<ProsodyConfig> {
|
||||
const logger = options.peertubeHelpers.logger
|
||||
logger.debug('Calling getProsodyConfig')
|
||||
|
||||
let useExternalComponents = false
|
||||
const bots: ProsodyConfigBots = {}
|
||||
const valuesToHideInDiagnostic: {[key: string]: string} = {}
|
||||
|
||||
const settings = await options.settingsManager.getSettings([
|
||||
'prosody-port',
|
||||
'prosody-muc-log-by-default',
|
||||
@ -124,25 +85,20 @@ async function getProsodyConfig (options: RegisterServerOptions): Promise<Prosod
|
||||
'prosody-c2s',
|
||||
'prosody-room-type',
|
||||
'prosody-peertube-uri',
|
||||
'prosody-c2s-port',
|
||||
'prosody-component-port',
|
||||
'chat-videos-list'
|
||||
'prosody-c2s-port'
|
||||
])
|
||||
|
||||
const valuesToHideInDiagnostic: {[key: string]: string} = {}
|
||||
const port = (settings['prosody-port'] as string) || '52800'
|
||||
if (!/^\d+$/.test(port)) {
|
||||
throw new Error('Invalid port')
|
||||
}
|
||||
const externalComponentsPort = (settings['prosody-component-port'] as string) || '53470'
|
||||
if (!/^\d+$/.test(externalComponentsPort)) {
|
||||
throw new Error('Invalid external components port')
|
||||
}
|
||||
const logByDefault = (settings['prosody-muc-log-by-default'] as boolean) ?? true
|
||||
const logExpirationSetting = (settings['prosody-muc-expiration'] as string) ?? DEFAULTLOGEXPIRATION
|
||||
const enableC2s = (settings['prosody-c2s'] as boolean) || false
|
||||
const prosodyDomain = await getProsodyDomain(options)
|
||||
const paths = await getProsodyFilePaths(options)
|
||||
const roomType = (settings['prosody-room-type']) === 'channel' ? 'channel' : 'video'
|
||||
const roomType = settings['prosody-room-type'] === 'channel' ? 'channel' : 'video'
|
||||
|
||||
const apikey = await getAPIKey(options)
|
||||
valuesToHideInDiagnostic.APIKey = apikey
|
||||
@ -196,57 +152,19 @@ async function getProsodyConfig (options: RegisterServerOptions): Promise<Prosod
|
||||
logLevel = 'info'
|
||||
}
|
||||
config.setLog(logLevel)
|
||||
|
||||
const demoBotUUIDs = parseConfigDemoBotUUIDs((settings['chat-videos-list'] as string) || '')
|
||||
let demoBotContentObj: string = 'null'
|
||||
if (demoBotUUIDs?.length > 0) {
|
||||
useExternalComponents = true
|
||||
const componentSecret = await getExternalComponentKey(options, 'DEMOBOT')
|
||||
valuesToHideInDiagnostic.ComponentSecret = componentSecret
|
||||
config.useDemoBot(componentSecret)
|
||||
bots.demobot = demoBotUUIDs
|
||||
demoBotContentObj = JSON.stringify({
|
||||
rooms: demoBotUUIDs,
|
||||
service: 'xmpp://127.0.0.1:' + externalComponentsPort,
|
||||
domain: 'demobot.' + prosodyDomain,
|
||||
mucDomain: 'room.' + prosodyDomain,
|
||||
password: componentSecret
|
||||
})
|
||||
}
|
||||
let demoBotContent = '"use strict";\n'
|
||||
demoBotContent += 'Object.defineProperty(exports, "__esModule", { value: true });\n'
|
||||
demoBotContent += `function getConf () { return ${demoBotContentObj}; }` + '\n'
|
||||
demoBotContent += 'exports.getConf = getConf;\n'
|
||||
|
||||
if (useExternalComponents) {
|
||||
config.useExternalComponents(externalComponentsPort)
|
||||
}
|
||||
|
||||
const content = config.write()
|
||||
|
||||
return new ProsodyConfig(
|
||||
[
|
||||
{
|
||||
key: 'prosody',
|
||||
path: paths.config,
|
||||
content: content
|
||||
},
|
||||
{
|
||||
key: 'demobot',
|
||||
path: paths.bots.demobot,
|
||||
content: demoBotContent
|
||||
}
|
||||
],
|
||||
return {
|
||||
content,
|
||||
paths,
|
||||
prosodyDomain,
|
||||
port,
|
||||
baseApiUrl,
|
||||
host: prosodyDomain,
|
||||
roomType,
|
||||
logByDefault,
|
||||
logExpiration,
|
||||
bots,
|
||||
valuesToHideInDiagnostic
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async function writeProsodyConfig (options: RegisterServerOptions): Promise<ProsodyConfig> {
|
||||
@ -254,19 +172,15 @@ async function writeProsodyConfig (options: RegisterServerOptions): Promise<Pros
|
||||
logger.debug('Calling writeProsodyConfig')
|
||||
|
||||
logger.debug('Ensuring that the working dir exists')
|
||||
await ensureWorkingDirs(options)
|
||||
await ensureWorkingDir(options)
|
||||
logger.debug('Computing the Prosody config content')
|
||||
const config = await getProsodyConfig(options)
|
||||
const content = config.content
|
||||
const fileName = config.paths.config
|
||||
|
||||
const configFiles = config.getConfigFiles()
|
||||
for (const configFile of configFiles) {
|
||||
const content = configFile.content
|
||||
const fileName = configFile.path
|
||||
|
||||
logger.info(`Writing prosody configuration file '${configFile.key}' to ${fileName}.`)
|
||||
await fs.promises.writeFile(fileName, content)
|
||||
logger.debug(`Prosody configuration file '${configFile.key}' writen.`)
|
||||
}
|
||||
logger.info(`Writing prosody configuration file to ${fileName}`)
|
||||
await fs.promises.writeFile(fileName, content)
|
||||
logger.debug('Prosody configuration file writen')
|
||||
|
||||
return config
|
||||
}
|
||||
@ -322,10 +236,20 @@ function readLogExpiration (options: RegisterServerOptions, logExpiration: strin
|
||||
}
|
||||
}
|
||||
|
||||
function getProsodyConfigContentForDiagnostic (config: ProsodyConfig, content?: string): string {
|
||||
let r: string = content ?? config.content
|
||||
for (const key in config.valuesToHideInDiagnostic) {
|
||||
// replaceAll not available, using trick:
|
||||
r = r.split(config.valuesToHideInDiagnostic[key]).join(`***${key}***`)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
export {
|
||||
getProsodyConfig,
|
||||
getWorkingDir,
|
||||
ensureWorkingDirs,
|
||||
ensureWorkingDir,
|
||||
getProsodyFilePaths,
|
||||
writeProsodyConfig
|
||||
writeProsodyConfig,
|
||||
getProsodyConfigContentForDiagnostic
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
function parseConfigDemoBotUUIDs (s: string): string[] {
|
||||
if (!s) {
|
||||
return []
|
||||
}
|
||||
let a = s.split('\n')
|
||||
// find lines that are like:
|
||||
// 6432f147-83c7-4fa3-b3b5-e49c2590e825 #!demobot
|
||||
a = a.filter(line => /#!demobot\b/.test(line))
|
||||
a = a.map(line => {
|
||||
return line.replace(/#.*$/, '')
|
||||
.replace(/^\s+/, '')
|
||||
.replace(/\s+$/, '')
|
||||
})
|
||||
return a.filter(line => line !== '')
|
||||
}
|
||||
|
||||
export {
|
||||
parseConfigDemoBotUUIDs
|
||||
}
|
@ -102,19 +102,16 @@ class ProsodyConfigVirtualHost extends ProsodyConfigBlock {
|
||||
|
||||
class ProsodyConfigComponent extends ProsodyConfigBlock {
|
||||
name: string
|
||||
type?: string
|
||||
type: string
|
||||
|
||||
constructor (name: string, type?: string) {
|
||||
constructor (type: string, name: string) {
|
||||
super(' ')
|
||||
this.type = type
|
||||
this.name = name
|
||||
}
|
||||
|
||||
write (): string {
|
||||
if (this.type !== undefined) {
|
||||
return `Component "${this.name}" "${this.type}"\n` + super.write()
|
||||
}
|
||||
return `Component "${this.name}"\n` + super.write()
|
||||
return `Component "${this.name}" "${this.type}"\n` + super.write()
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +123,6 @@ class ProsodyConfigContent {
|
||||
authenticated?: ProsodyConfigVirtualHost
|
||||
anon: ProsodyConfigVirtualHost
|
||||
muc: ProsodyConfigComponent
|
||||
externalComponents: ProsodyConfigComponent[] = []
|
||||
log: string
|
||||
prosodyDomain: string
|
||||
|
||||
@ -136,7 +132,7 @@ class ProsodyConfigContent {
|
||||
this.log = ''
|
||||
this.prosodyDomain = prosodyDomain
|
||||
this.anon = new ProsodyConfigVirtualHost('anon.' + prosodyDomain)
|
||||
this.muc = new ProsodyConfigComponent('room.' + prosodyDomain, 'muc')
|
||||
this.muc = new ProsodyConfigComponent('muc', 'room.' + prosodyDomain)
|
||||
|
||||
this.global.set('daemonize', false)
|
||||
this.global.set('allow_registration', false)
|
||||
@ -285,21 +281,6 @@ class ProsodyConfigContent {
|
||||
this.muc.set('peertubelivechat_test_peertube_api_url', apiurl)
|
||||
}
|
||||
|
||||
useExternalComponents (componentsPort: string): void {
|
||||
this.global.set('component_ports', [componentsPort])
|
||||
this.global.set('component_interfaces', ['127.0.0.1', '::1'])
|
||||
}
|
||||
|
||||
useDemoBot (componentSecret: string): void {
|
||||
const demoBotComponent = new ProsodyConfigComponent('demobot.' + this.prosodyDomain)
|
||||
demoBotComponent.set('component_secret', componentSecret)
|
||||
|
||||
// If we want the bot to be moderator, should do the trick:
|
||||
// this.global.add('admins', 'demobot.' + this.prosodyDomain)
|
||||
|
||||
this.externalComponents.push(demoBotComponent)
|
||||
}
|
||||
|
||||
setLog (level: ProsodyLogLevel, syslog?: ProsodyLogLevel[]): void {
|
||||
let log = ''
|
||||
log += 'log = {\n'
|
||||
@ -328,11 +309,6 @@ class ProsodyConfigContent {
|
||||
content += '\n\n'
|
||||
content += this.muc.write()
|
||||
content += '\n\n'
|
||||
this.externalComponents.forEach((externalComponent) => {
|
||||
content += '\n\n'
|
||||
content += externalComponent.write()
|
||||
content += '\n\n'
|
||||
})
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,6 @@ interface ProsodyFilePaths {
|
||||
log: string
|
||||
config: string
|
||||
data: string
|
||||
bots: {
|
||||
dir: string
|
||||
demobot: string
|
||||
}
|
||||
modules: string
|
||||
}
|
||||
|
||||
|
@ -113,23 +113,20 @@ async function testProsodyCorrectlyRunning (options: RegisterServerOptions): Pro
|
||||
|
||||
try {
|
||||
const wantedConfig = await getProsodyConfig(options)
|
||||
const configFiles = wantedConfig.getConfigFiles()
|
||||
for (const configFile of configFiles) {
|
||||
const filePath = configFile.path
|
||||
const filePath = wantedConfig.paths.config
|
||||
|
||||
await fs.promises.access(filePath, fs.constants.R_OK) // throw an error if file does not exist.
|
||||
result.messages.push(`The prosody configuration file (${configFile.key}: ${filePath}) exists`)
|
||||
const actualContent = await fs.promises.readFile(filePath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
await fs.promises.access(filePath, fs.constants.R_OK) // throw an error if file does not exist.
|
||||
result.messages.push(`The prosody configuration file (${filePath}) exists`)
|
||||
const actualContent = await fs.promises.readFile(filePath, {
|
||||
encoding: 'utf-8'
|
||||
})
|
||||
|
||||
const wantedContent = configFile.content
|
||||
if (actualContent === wantedContent) {
|
||||
result.messages.push(`Prosody configuration file '${configFile.key}' content is correct.`)
|
||||
} else {
|
||||
result.messages.push(`Prosody configuration file '${configFile.key}' content is not correct.`)
|
||||
return result
|
||||
}
|
||||
const wantedContent = wantedConfig.content
|
||||
if (actualContent === wantedContent) {
|
||||
result.messages.push('Prosody configuration file content is correct.')
|
||||
} else {
|
||||
result.messages.push('Prosody configuration file content is not correct.')
|
||||
return result
|
||||
}
|
||||
} catch (error) {
|
||||
result.messages.push('Error when requiring the prosody config file: ' + (error as string))
|
||||
|
@ -359,22 +359,6 @@ archiving for a specific room, by editing its properties.
|
||||
</ul>`
|
||||
})
|
||||
|
||||
registerSetting({
|
||||
name: 'prosody-component-port',
|
||||
label: 'The port to be use for external components',
|
||||
type: 'input',
|
||||
default: '53470',
|
||||
private: true,
|
||||
descriptionHTML:
|
||||
`The port that will be used for extra components used by the builtin Prosody server.<br>
|
||||
This is only used when one of these special features is used:<br>
|
||||
<ul>
|
||||
<li>Demo bot: this is a hidden feature, for demonstration purposes. See the documentation for more information.</li>
|
||||
</ul><br>
|
||||
Change it if this port is already in use on your server.
|
||||
`
|
||||
})
|
||||
|
||||
registerSetting({
|
||||
name: 'prosody-c2s',
|
||||
label: 'Enable client to server connections',
|
||||
|
Reference in New Issue
Block a user