Embedding Prosody using AppImage:
Thanks to this commit, there is no more need to manually install Prosody on the server. The plugin now build and embed an AppImage of Prosody. In this commit: * building and using a Prosody AppImage. * Adding a launcher in the AppImage: the first command argument tells if we want to run prosody or prosodyctl * prosodyCtl functions now uses the AppImage. * Prosody AppImage: extract once at the startup, then run the squashfs
This commit is contained in:
parent
91ea442ce6
commit
459d92cef9
11
CHANGELOG.md
11
CHANGELOG.md
@ -21,10 +21,21 @@ the server will try to use the new mode after updating the plugin.
|
|||||||
If you don't want the chat server to be active, just uninstall the plugin
|
If you don't want the chat server to be active, just uninstall the plugin
|
||||||
(settings won't be lost, you just have to download it again).
|
(settings won't be lost, you just have to download it again).
|
||||||
|
|
||||||
|
### Important Notes
|
||||||
|
|
||||||
|
This version of the plugin comes with a builtin Prosody AppImage.
|
||||||
|
|
||||||
|
If you were using this plugin before this version, and if you had installed Prosody manually,
|
||||||
|
you can safely uninstall Prosody.
|
||||||
|
|
||||||
|
If you were using the custom Peertube docker image that is embedding Prosody, you can switch back to the official
|
||||||
|
Peertube image.
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
||||||
* Removed deprecated modes, only keeping «Prosody server controlled by Peertube».
|
* Removed deprecated modes, only keeping «Prosody server controlled by Peertube».
|
||||||
* BOSH proxy optimization + enabling websocket.
|
* BOSH proxy optimization + enabling websocket.
|
||||||
|
* Builtin Prosody AppImage. No more manual installation required.
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
|
||||||
|
54
build-prosody.sh
Normal file
54
build-prosody.sh
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
set -x
|
||||||
|
|
||||||
|
rootdir="$(pwd)"
|
||||||
|
prosody_build_dir="$rootdir/build/prosody"
|
||||||
|
prosody_destination_dir="$rootdir/dist/server/prosody"
|
||||||
|
|
||||||
|
if [[ ! -d "$prosody_build_dir" ]]; then
|
||||||
|
mkdir -p "$prosody_build_dir"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$prosody_build_dir"
|
||||||
|
|
||||||
|
if [ -f "$prosody_build_dir/livechat-prosody-x86_64.AppImage" ]; then
|
||||||
|
echo "Prosody image already built."
|
||||||
|
else
|
||||||
|
echo "Prosody image must be build..."
|
||||||
|
|
||||||
|
# Prerequisite: you must have python3-venv installed on your system
|
||||||
|
if [[ ! -d "venv" ]]; then
|
||||||
|
echo "Creating the python venv..."
|
||||||
|
python3 -m venv venv
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Activating the python venv..."
|
||||||
|
source venv/bin/activate
|
||||||
|
|
||||||
|
echo "Installing appimage-builder..."
|
||||||
|
pip3 install appimage-builder
|
||||||
|
|
||||||
|
echo "Copying appimage source files..."
|
||||||
|
cp "$rootdir/prosody/appimage_x86_64.yml" "$prosody_build_dir/appimage_x86_64.yml"
|
||||||
|
cp "$rootdir/prosody/launcher.lua" "$prosody_build_dir/launcher.lua"
|
||||||
|
|
||||||
|
echo "Building Prosody..."
|
||||||
|
appimage-builder --recipe "$prosody_build_dir/appimage_x86_64.yml"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Copying Prosody dist files..."
|
||||||
|
mkdir -p "$prosody_destination_dir" && cp $prosody_build_dir/livechat-prosody-x86_64.AppImage "$prosody_destination_dir/"
|
||||||
|
|
||||||
|
# For some obscur reason, if we keep AppDir and appimage-build folders,
|
||||||
|
# and if we try to install the plugin using the Peertube CLI,
|
||||||
|
# the installation fails because there are some subfolders that are right protected.
|
||||||
|
# To avoid that, we clean them:
|
||||||
|
echo "Cleaning build folders..."
|
||||||
|
rm -rf "$prosody_build_dir/AppDir"
|
||||||
|
rm -rf "$prosody_build_dir/appimage-build"
|
||||||
|
|
||||||
|
echo "Prosody AppImage OK."
|
||||||
|
|
||||||
|
exit 0
|
@ -88,6 +88,7 @@
|
|||||||
"clean:light": "rm -rf dist/*",
|
"clean:light": "rm -rf dist/*",
|
||||||
"prepare": "npm run clean && npm run build",
|
"prepare": "npm run clean && npm run build",
|
||||||
"build:converse": "bash conversejs/build-conversejs.sh",
|
"build:converse": "bash conversejs/build-conversejs.sh",
|
||||||
|
"build:prosody": "bash build-prosody.sh",
|
||||||
"build:images": "mkdir -p dist/client/images && npx svgo -f public/images/ -o dist/client/images/",
|
"build:images": "mkdir -p dist/client/images && npx svgo -f public/images/ -o dist/client/images/",
|
||||||
"build:avatars": "mkdir -p dist/server/avatars && ./build-avatars.js",
|
"build:avatars": "mkdir -p dist/server/avatars && ./build-avatars.js",
|
||||||
"build:webpack": "webpack --mode=production",
|
"build:webpack": "webpack --mode=production",
|
||||||
@ -95,7 +96,7 @@
|
|||||||
"build:serverconverse": "mkdir -p dist/server/conversejs && cp conversejs/index.html dist/server/conversejs/",
|
"build:serverconverse": "mkdir -p dist/server/conversejs && cp conversejs/index.html dist/server/conversejs/",
|
||||||
"build:prosodymodules": "mkdir -p dist/server/prosody-modules && cp -r prosody-modules/* dist/server/prosody-modules/",
|
"build:prosodymodules": "mkdir -p dist/server/prosody-modules && cp -r prosody-modules/* dist/server/prosody-modules/",
|
||||||
"build:styles": "sass assets:dist/assets",
|
"build:styles": "sass assets:dist/assets",
|
||||||
"build": "npm-run-all -s clean:light -p build:converse build:images build:avatars build:webpack build:server build:serverconverse build:prosodymodules build:styles",
|
"build": "npm-run-all -s clean:light -p build:converse build:prosody build:images build:avatars build:webpack build:server build:serverconverse build:prosodymodules build:styles",
|
||||||
"lint": "npm-run-all -s lint:script lint:styles",
|
"lint": "npm-run-all -s lint:script lint:styles",
|
||||||
"lint:script": "npx eslint --ext .js --ext .ts .",
|
"lint:script": "npx eslint --ext .js --ext .ts .",
|
||||||
"lint:styles": "stylelint 'conversejs/**/*.scss' 'assets/**/*.css'",
|
"lint:styles": "stylelint 'conversejs/**/*.scss' 'assets/**/*.css'",
|
||||||
|
59
prosody/appimage_x86_64.yml
Normal file
59
prosody/appimage_x86_64.yml
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# This file is meant to be used with appimage-builder: https://appimage-builder.readthedocs.io
|
||||||
|
# See it is use in the build-prosody.sh script.
|
||||||
|
|
||||||
|
version: 1
|
||||||
|
|
||||||
|
script:
|
||||||
|
# Remove any previous build
|
||||||
|
- rm -rf AppDir | true
|
||||||
|
# Make usr dirs
|
||||||
|
- mkdir -p AppDir/usr/bin
|
||||||
|
# Copy the launcher code into the AppDir
|
||||||
|
- cp ./launcher.lua AppDir/usr/bin/
|
||||||
|
|
||||||
|
AppDir:
|
||||||
|
path: ./AppDir
|
||||||
|
|
||||||
|
app_info:
|
||||||
|
id: org.peertube-plugin-livechat.prosody
|
||||||
|
name: prosody
|
||||||
|
icon: utilities-terminal
|
||||||
|
version: 1.0.0
|
||||||
|
exec: usr/bin/lua5.2
|
||||||
|
exec_args: "$APPDIR/usr/bin/launcher.lua $@"
|
||||||
|
|
||||||
|
apt:
|
||||||
|
arch: amd64
|
||||||
|
sources:
|
||||||
|
- sourceline: 'deb [arch=amd64] https://deb.debian.org/debian/ bullseye main contrib'
|
||||||
|
key_url: 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x648ACFD622F3D138'
|
||||||
|
- sourceline: 'deb [arch=amd64] https://deb.debian.org/debian/ bullseye-backports main contrib'
|
||||||
|
|
||||||
|
include:
|
||||||
|
- lua5.2
|
||||||
|
- prosody
|
||||||
|
|
||||||
|
files:
|
||||||
|
exclude:
|
||||||
|
- usr/share/man
|
||||||
|
- usr/share/doc/*/README.*
|
||||||
|
- usr/share/doc/*/changelog.*
|
||||||
|
- usr/share/doc/*/NEWS.*
|
||||||
|
- usr/share/doc/*/TODO.*
|
||||||
|
- etc/init.d/*
|
||||||
|
- etc/logrotate.d/*
|
||||||
|
|
||||||
|
runtime:
|
||||||
|
# Here we use the path_mappings to rewrite, on runtime, all paths.
|
||||||
|
# Note: this assume that peertube-plugin-livechat is not in a subdir of one of following mappings.
|
||||||
|
# This seems a reasonable assumption.
|
||||||
|
path_mappings:
|
||||||
|
- /etc/:$APPDIR/etc/
|
||||||
|
- /lib/:$APPDIR/lib/
|
||||||
|
- /lib64/:$APPDIR/lib64/
|
||||||
|
- /runtime/:$APPDIR/runtime/
|
||||||
|
- /usr/:$APPDIR/usr/
|
||||||
|
|
||||||
|
AppImage:
|
||||||
|
arch: x86_64
|
||||||
|
file_name: 'livechat-prosody-x86_64.AppImage'
|
13
prosody/launcher.lua
Normal file
13
prosody/launcher.lua
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env lua
|
||||||
|
|
||||||
|
-- This file is a launcher, that takes the first argument to choose what to launch.
|
||||||
|
|
||||||
|
local what = table.remove(arg, 1);
|
||||||
|
if what == 'prosody' then
|
||||||
|
dofile('/usr/bin/prosody');
|
||||||
|
elseif what == 'prosodyctl' then
|
||||||
|
dofile('/usr/bin/prosodyctl');
|
||||||
|
else
|
||||||
|
print("Unknown command: "..what);
|
||||||
|
os.exit(1);
|
||||||
|
end
|
@ -33,6 +33,8 @@ export async function diagProsody (test: string, options: RegisterServerOptions)
|
|||||||
|
|
||||||
result.messages.push(`Prosody will use ${wantedConfig.baseApiUrl} as base uri from api calls`)
|
result.messages.push(`Prosody will use ${wantedConfig.baseApiUrl} as base uri from api calls`)
|
||||||
|
|
||||||
|
result.messages.push(`Prosody path will be '${wantedConfig.paths.exec}'`)
|
||||||
|
|
||||||
result.messages.push(`Prosody modules path will be '${wantedConfig.paths.modules}'`)
|
result.messages.push(`Prosody modules path will be '${wantedConfig.paths.modules}'`)
|
||||||
|
|
||||||
result.messages.push(`Prosody rooms will be grouped by '${wantedConfig.roomType}'.`)
|
result.messages.push(`Prosody rooms will be grouped by '${wantedConfig.roomType}'.`)
|
||||||
|
@ -54,6 +54,24 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise<Pro
|
|||||||
logger.debug('Calling getProsodyFilePaths')
|
logger.debug('Calling getProsodyFilePaths')
|
||||||
|
|
||||||
const dir = await getWorkingDir(options)
|
const dir = await getWorkingDir(options)
|
||||||
|
const settings = await options.settingsManager.getSettings(['use-system-prosody'])
|
||||||
|
let exec
|
||||||
|
let execArgs: string[]
|
||||||
|
let execCtl
|
||||||
|
let execCtlArgs: string[]
|
||||||
|
let appImageToExtract
|
||||||
|
if (settings['use-system-prosody']) {
|
||||||
|
exec = 'prosody'
|
||||||
|
execArgs = []
|
||||||
|
execCtl = 'prosodyctl'
|
||||||
|
execCtlArgs = []
|
||||||
|
} else {
|
||||||
|
appImageToExtract = path.resolve(__dirname, '../../prosody/livechat-prosody-x86_64.AppImage')
|
||||||
|
exec = path.resolve(dir, 'squashfs-root/AppRun') // the AppImage will be extracted in the working dir
|
||||||
|
execArgs = ['prosody']
|
||||||
|
execCtl = exec
|
||||||
|
execCtlArgs = ['prosodyctl']
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
dir: dir,
|
dir: dir,
|
||||||
pid: path.resolve(dir, 'prosody.pid'),
|
pid: path.resolve(dir, 'prosody.pid'),
|
||||||
@ -62,7 +80,12 @@ async function getProsodyFilePaths (options: RegisterServerOptions): Promise<Pro
|
|||||||
config: path.resolve(dir, 'prosody.cfg.lua'),
|
config: path.resolve(dir, 'prosody.cfg.lua'),
|
||||||
data: path.resolve(dir, 'data'),
|
data: path.resolve(dir, 'data'),
|
||||||
modules: path.resolve(__dirname, '../../prosody-modules'),
|
modules: path.resolve(__dirname, '../../prosody-modules'),
|
||||||
avatars: path.resolve(__dirname, '../../avatars')
|
avatars: path.resolve(__dirname, '../../avatars'),
|
||||||
|
exec,
|
||||||
|
execArgs,
|
||||||
|
execCtl,
|
||||||
|
execCtlArgs,
|
||||||
|
appImageToExtract
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,11 @@ interface ProsodyFilePaths {
|
|||||||
data: string
|
data: string
|
||||||
modules: string
|
modules: string
|
||||||
avatars: string
|
avatars: string
|
||||||
|
exec: string
|
||||||
|
execArgs: string[]
|
||||||
|
execCtl: string
|
||||||
|
execCtlArgs: string[]
|
||||||
|
appImageToExtract?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -5,6 +5,38 @@ import { disableProxyRoute, enableProxyRoute } from '../routers/webchat'
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as child_process from 'child_process'
|
import * as child_process from 'child_process'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function prepares the binaries for the embeded Prosody (if needed).
|
||||||
|
* @param options
|
||||||
|
*/
|
||||||
|
async function prepareProsody (options: RegisterServerOptions): Promise<void> {
|
||||||
|
const logger = options.peertubeHelpers.logger
|
||||||
|
const filePaths = await getProsodyFilePaths(options)
|
||||||
|
const appImageToExtract = filePaths.appImageToExtract
|
||||||
|
if (!appImageToExtract) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const spawned = child_process.spawn(appImageToExtract, ['--appimage-extract'], {
|
||||||
|
cwd: filePaths.dir,
|
||||||
|
env: {
|
||||||
|
...process.env
|
||||||
|
}
|
||||||
|
})
|
||||||
|
spawned.stdout.on('data', (data) => {
|
||||||
|
logger.debug(`AppImage extract printed: ${data as string}`)
|
||||||
|
})
|
||||||
|
spawned.stderr.on('data', (data) => {
|
||||||
|
logger.error(`AppImage extract has errors: ${data as string}`)
|
||||||
|
})
|
||||||
|
spawned.on('error', reject)
|
||||||
|
spawned.on('close', (_code) => { // 'close' and not 'exit', to be sure it is finished.
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
interface ProsodyCtlResult {
|
interface ProsodyCtlResult {
|
||||||
code: number | null
|
code: number | null
|
||||||
stdout: string
|
stdout: string
|
||||||
@ -23,11 +55,13 @@ async function prosodyCtl (options: RegisterServerOptions, command: string): Pro
|
|||||||
let d: string = ''
|
let d: string = ''
|
||||||
let e: string = ''
|
let e: string = ''
|
||||||
let m: string = ''
|
let m: string = ''
|
||||||
const spawned = child_process.spawn('prosodyctl', [
|
const cmdArgs = [
|
||||||
|
...filePaths.execCtlArgs,
|
||||||
'--config',
|
'--config',
|
||||||
filePaths.config,
|
filePaths.config,
|
||||||
command
|
command
|
||||||
], {
|
]
|
||||||
|
const spawned = child_process.spawn(filePaths.execCtl, cmdArgs, {
|
||||||
cwd: filePaths.dir,
|
cwd: filePaths.dir,
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
@ -44,7 +78,10 @@ async function prosodyCtl (options: RegisterServerOptions, command: string): Pro
|
|||||||
m += data as string
|
m += data as string
|
||||||
})
|
})
|
||||||
spawned.on('error', reject)
|
spawned.on('error', reject)
|
||||||
spawned.on('exit', (code) => {
|
|
||||||
|
// on 'close' and not 'exit', to be sure everything is done
|
||||||
|
// (else it can cause trouble by cleaning AppImage extract too soon)
|
||||||
|
spawned.on('close', (code) => {
|
||||||
resolve({
|
resolve({
|
||||||
code: code,
|
code: code,
|
||||||
stdout: d,
|
stdout: d,
|
||||||
@ -163,8 +200,9 @@ async function ensureProsodyRunning (options: RegisterServerOptions): Promise<vo
|
|||||||
const filePaths = config.paths
|
const filePaths = config.paths
|
||||||
|
|
||||||
// launch prosody
|
// launch prosody
|
||||||
logger.info('Going to launch prosody')
|
const execCmd = filePaths.exec + (filePaths.execArgs.length ? ' ' + filePaths.execArgs.join(' ') : '')
|
||||||
const prosody = child_process.exec('prosody', {
|
logger.info('Going to launch prosody (' + execCmd + ')')
|
||||||
|
const prosody = child_process.exec(execCmd, {
|
||||||
cwd: filePaths.dir,
|
cwd: filePaths.dir,
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
@ -248,6 +286,7 @@ export {
|
|||||||
getProsodyAbout,
|
getProsodyAbout,
|
||||||
testProsodyRunning,
|
testProsodyRunning,
|
||||||
testProsodyCorrectlyRunning,
|
testProsodyCorrectlyRunning,
|
||||||
|
prepareProsody,
|
||||||
ensureProsodyRunning,
|
ensureProsodyRunning,
|
||||||
ensureProsodyNotRunning
|
ensureProsodyNotRunning
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,11 @@ function initSettings (options: RegisterServerOptions): void {
|
|||||||
(if this button is not opening a new window, please try to refresh the page).`
|
(if this button is not opening a new window, please try to refresh the page).`
|
||||||
})
|
})
|
||||||
|
|
||||||
// ********** Chat Mode
|
// ********** Chat Server
|
||||||
registerSetting({
|
registerSetting({
|
||||||
type: 'html',
|
type: 'html',
|
||||||
private: true,
|
private: true,
|
||||||
descriptionHTML: '<h3>Chat mode</h3>'
|
descriptionHTML: '<h3>Chat Server</h3>'
|
||||||
})
|
})
|
||||||
registerSetting({
|
registerSetting({
|
||||||
name: 'chat-help-builtin-prosody',
|
name: 'chat-help-builtin-prosody',
|
||||||
@ -39,14 +39,22 @@ function initSettings (options: RegisterServerOptions): void {
|
|||||||
label: 'Prosody server',
|
label: 'Prosody server',
|
||||||
descriptionHTML: `This plugin uses the Prosody XMPP server to handle chat rooms.<br>
|
descriptionHTML: `This plugin uses the Prosody XMPP server to handle chat rooms.<br>
|
||||||
The Peertube server will control this Prosody server.<br>
|
The Peertube server will control this Prosody server.<br>
|
||||||
Important Note: you have to install Prosody on your server.
|
By default, this plugin comes with a Prosody AppImage.`,
|
||||||
Please read the <a
|
|
||||||
href="https://github.com/JohnXLivingston/peertube-plugin-livechat/blob/main/documentation/prosody.md"
|
|
||||||
target="_blank"
|
|
||||||
>documentation</a>.`,
|
|
||||||
private: true
|
private: true
|
||||||
})
|
})
|
||||||
|
registerSetting({
|
||||||
|
name: 'use-system-prosody',
|
||||||
|
type: 'input-checkbox',
|
||||||
|
label: 'Use system Prosody',
|
||||||
|
descriptionHTML: `Warning: don't check this settings if you are not sure of what you are doing.<br>
|
||||||
|
By checking this settings, your Peertube will use the Prosody server that comes with your system,
|
||||||
|
and not the embeded AppImage.<br>
|
||||||
|
Only use this if you encounter problems with the embedded Prosody.`,
|
||||||
|
private: true,
|
||||||
|
default: false
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: fix the settings order. Since there is no more multiple chat-mode, the order is not good.
|
||||||
registerSetting({
|
registerSetting({
|
||||||
name: 'prosody-list-rooms',
|
name: 'prosody-list-rooms',
|
||||||
label: 'List existing rooms',
|
label: 'List existing rooms',
|
||||||
|
@ -3,7 +3,7 @@ import { migrateSettings } from './lib/migration/settings'
|
|||||||
import { initSettings } from './lib/settings'
|
import { initSettings } from './lib/settings'
|
||||||
import { initCustomFields } from './lib/custom-fields'
|
import { initCustomFields } from './lib/custom-fields'
|
||||||
import { initRouters } from './lib/routers/index'
|
import { initRouters } from './lib/routers/index'
|
||||||
import { ensureProsodyRunning, ensureProsodyNotRunning } from './lib/prosody/ctl'
|
import { prepareProsody, ensureProsodyRunning, ensureProsodyNotRunning } from './lib/prosody/ctl'
|
||||||
import decache from 'decache'
|
import decache from 'decache'
|
||||||
|
|
||||||
// FIXME: Peertube unregister don't have any parameter.
|
// FIXME: Peertube unregister don't have any parameter.
|
||||||
@ -25,6 +25,7 @@ async function register (options: RegisterServerOptions): Promise<any> {
|
|||||||
await initRouters(options)
|
await initRouters(options)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
await prepareProsody(options)
|
||||||
await ensureProsodyRunning(options)
|
await ensureProsodyRunning(options)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
options.peertubeHelpers.logger.error('Error when launching Prosody: ' + (error as string))
|
options.peertubeHelpers.logger.error('Error when launching Prosody: ' + (error as string))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user