feat(jitsi): Add Jitsi Meet video conferencing integration
- secubox-app-jitsi: Docker-based Jitsi stack with jitsctl control CLI - luci-app-jitsi: LuCI web configuration interface - Catalog entry for SecuBox AppStore Features: - End-to-end encrypted video conferencing - HAProxy integration with WebSocket/SSL support - Mesh federation for SecuBox P2P network - User authentication management - Backup/restore functionality Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
760408c36f
commit
00082fe066
29
package/secubox/luci-app-jitsi/Makefile
Normal file
29
package/secubox/luci-app-jitsi/Makefile
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:=LuCI Jitsi Meet Configuration
|
||||||
|
LUCI_DEPENDS:=+secubox-app-jitsi
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
|
||||||
|
PKG_NAME:=luci-app-jitsi
|
||||||
|
PKG_VERSION:=1.0.0
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
||||||
|
PKG_LICENSE:=Apache-2.0
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
define Package/luci-app-jitsi/install
|
||||||
|
$(INSTALL_DIR) $(1)/usr/share/luci/menu.d
|
||||||
|
$(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-jitsi.json $(1)/usr/share/luci/menu.d/
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
|
||||||
|
$(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-jitsi.json $(1)/usr/share/rpcd/acl.d/
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/www/luci-static/resources/view/jitsi
|
||||||
|
$(INSTALL_DATA) ./htdocs/luci-static/resources/view/jitsi/*.js $(1)/www/luci-static/resources/view/jitsi/
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/usr/libexec/rpcd
|
||||||
|
$(INSTALL_BIN) ./root/usr/libexec/rpcd/luci.jitsi $(1)/usr/libexec/rpcd/
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,luci-app-jitsi))
|
||||||
@ -0,0 +1,298 @@
|
|||||||
|
'use strict';
|
||||||
|
'require view';
|
||||||
|
'require form';
|
||||||
|
'require uci';
|
||||||
|
'require rpc';
|
||||||
|
'require poll';
|
||||||
|
|
||||||
|
var callJitsiStatus = rpc.declare({
|
||||||
|
object: 'luci.jitsi',
|
||||||
|
method: 'status',
|
||||||
|
expect: { '': {} }
|
||||||
|
});
|
||||||
|
|
||||||
|
var callJitsiStart = rpc.declare({
|
||||||
|
object: 'luci.jitsi',
|
||||||
|
method: 'start'
|
||||||
|
});
|
||||||
|
|
||||||
|
var callJitsiStop = rpc.declare({
|
||||||
|
object: 'luci.jitsi',
|
||||||
|
method: 'stop'
|
||||||
|
});
|
||||||
|
|
||||||
|
var callJitsiRestart = rpc.declare({
|
||||||
|
object: 'luci.jitsi',
|
||||||
|
method: 'restart'
|
||||||
|
});
|
||||||
|
|
||||||
|
var callJitsiInstall = rpc.declare({
|
||||||
|
object: 'luci.jitsi',
|
||||||
|
method: 'install'
|
||||||
|
});
|
||||||
|
|
||||||
|
var callJitsiGenerateConfig = rpc.declare({
|
||||||
|
object: 'luci.jitsi',
|
||||||
|
method: 'generate_config'
|
||||||
|
});
|
||||||
|
|
||||||
|
return view.extend({
|
||||||
|
load: function() {
|
||||||
|
return Promise.all([
|
||||||
|
uci.load('jitsi'),
|
||||||
|
callJitsiStatus()
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function(data) {
|
||||||
|
var status = data[1];
|
||||||
|
var m, s, o;
|
||||||
|
|
||||||
|
m = new form.Map('jitsi', _('Jitsi Meet Video Conferencing'),
|
||||||
|
_('Self-hosted video conferencing with end-to-end encryption.'));
|
||||||
|
|
||||||
|
// Status section
|
||||||
|
s = m.section(form.NamedSection, 'main', 'jitsi', _('Service Status'));
|
||||||
|
s.anonymous = true;
|
||||||
|
|
||||||
|
o = s.option(form.DummyValue, '_status', _('Status'));
|
||||||
|
o.rawhtml = true;
|
||||||
|
o.cfgvalue = function() {
|
||||||
|
var html = '<div style="display:flex;gap:20px;flex-wrap:wrap;">';
|
||||||
|
|
||||||
|
if (!status.docker_available) {
|
||||||
|
html += '<span style="color:red;font-weight:bold;">Docker not available - Install docker package</span>';
|
||||||
|
} else {
|
||||||
|
// Container status
|
||||||
|
var containers = status.containers || {};
|
||||||
|
for (var name in containers) {
|
||||||
|
var state = containers[name];
|
||||||
|
var color = state === 'running' ? 'green' : (state === 'not_found' ? 'gray' : 'orange');
|
||||||
|
html += '<span><b>' + name.replace('jitsi-', '') + ':</b> ';
|
||||||
|
html += '<span style="color:' + color + '">' + state + '</span></span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
if (status.stats) {
|
||||||
|
html += '<span style="margin-left:20px;">';
|
||||||
|
html += '<b>Active:</b> ' + (status.stats.conferences || 0) + ' conferences, ';
|
||||||
|
html += (status.stats.participants || 0) + ' participants';
|
||||||
|
html += '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
return html;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Control buttons
|
||||||
|
o = s.option(form.Button, '_start', _('Start'));
|
||||||
|
o.inputtitle = _('Start');
|
||||||
|
o.inputstyle = 'apply';
|
||||||
|
o.onclick = function() {
|
||||||
|
return callJitsiStart().then(function() {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
o = s.option(form.Button, '_stop', _('Stop'));
|
||||||
|
o.inputtitle = _('Stop');
|
||||||
|
o.inputstyle = 'remove';
|
||||||
|
o.onclick = function() {
|
||||||
|
return callJitsiStop().then(function() {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
o = s.option(form.Button, '_restart', _('Restart'));
|
||||||
|
o.inputtitle = _('Restart');
|
||||||
|
o.inputstyle = 'reload';
|
||||||
|
o.onclick = function() {
|
||||||
|
return callJitsiRestart().then(function() {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configuration section
|
||||||
|
s = m.section(form.NamedSection, 'main', 'jitsi', _('General Configuration'));
|
||||||
|
s.anonymous = true;
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'enabled', _('Enabled'),
|
||||||
|
_('Enable Jitsi Meet service'));
|
||||||
|
o.rmempty = false;
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'domain', _('Domain'),
|
||||||
|
_('Public domain for Jitsi Meet (e.g., meet.example.com)'));
|
||||||
|
o.placeholder = 'meet.example.com';
|
||||||
|
o.rmempty = false;
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'public_url', _('Public URL'),
|
||||||
|
_('Full public URL (leave empty to auto-generate from domain)'));
|
||||||
|
o.placeholder = 'https://meet.example.com';
|
||||||
|
o.optional = true;
|
||||||
|
|
||||||
|
o = s.option(form.ListValue, 'timezone', _('Timezone'));
|
||||||
|
o.value('UTC', 'UTC');
|
||||||
|
o.value('Europe/Paris', 'Europe/Paris');
|
||||||
|
o.value('Europe/London', 'Europe/London');
|
||||||
|
o.value('America/New_York', 'America/New York');
|
||||||
|
o.value('America/Los_Angeles', 'America/Los Angeles');
|
||||||
|
o.value('Asia/Tokyo', 'Asia/Tokyo');
|
||||||
|
o.default = 'UTC';
|
||||||
|
|
||||||
|
// Web settings
|
||||||
|
s = m.section(form.NamedSection, 'web', 'jitsi', _('Web Interface'));
|
||||||
|
s.anonymous = true;
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'port', _('HTTPS Port'),
|
||||||
|
_('Port for web interface'));
|
||||||
|
o.datatype = 'port';
|
||||||
|
o.default = '8443';
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'enable_guests', _('Allow Guests'),
|
||||||
|
_('Allow users to join without authentication'));
|
||||||
|
o.default = '1';
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'enable_auth', _('Require Authentication'),
|
||||||
|
_('Require login to create meetings'));
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
|
o = s.option(form.ListValue, 'default_language', _('Default Language'));
|
||||||
|
o.value('en', 'English');
|
||||||
|
o.value('fr', 'French');
|
||||||
|
o.value('de', 'German');
|
||||||
|
o.value('es', 'Spanish');
|
||||||
|
o.value('it', 'Italian');
|
||||||
|
o.value('pt', 'Portuguese');
|
||||||
|
o.value('zh', 'Chinese');
|
||||||
|
o.value('ja', 'Japanese');
|
||||||
|
o.default = 'en';
|
||||||
|
|
||||||
|
// JVB settings
|
||||||
|
s = m.section(form.NamedSection, 'jvb', 'jitsi', _('Video Bridge (JVB)'));
|
||||||
|
s.anonymous = true;
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'port', _('Media Port (UDP)'),
|
||||||
|
_('UDP port for video/audio streams'));
|
||||||
|
o.datatype = 'port';
|
||||||
|
o.default = '10000';
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'enable_tcp_fallback', _('Enable TCP Fallback'),
|
||||||
|
_('Allow TCP fallback for restrictive networks'));
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'tcp_port', _('TCP Fallback Port'),
|
||||||
|
_('TCP port for fallback'));
|
||||||
|
o.datatype = 'port';
|
||||||
|
o.default = '4443';
|
||||||
|
o.depends('enable_tcp_fallback', '1');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'stun_servers', _('STUN Servers'),
|
||||||
|
_('STUN servers for NAT traversal'));
|
||||||
|
o.placeholder = 'meet-jit-si-turnrelay.jitsi.net:443';
|
||||||
|
|
||||||
|
// Security settings
|
||||||
|
s = m.section(form.NamedSection, 'security', 'jitsi', _('Security'));
|
||||||
|
s.anonymous = true;
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'lobby_enabled', _('Enable Lobby'),
|
||||||
|
_('Require moderator approval to join'));
|
||||||
|
o.default = '1';
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'password_required', _('Require Room Password'),
|
||||||
|
_('Prompt for password when creating rooms'));
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'jwt_enabled', _('Enable JWT Authentication'),
|
||||||
|
_('Use JWT tokens for authentication'));
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'jwt_app_id', _('JWT App ID'));
|
||||||
|
o.depends('jwt_enabled', '1');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'jwt_app_secret', _('JWT App Secret'));
|
||||||
|
o.password = true;
|
||||||
|
o.depends('jwt_enabled', '1');
|
||||||
|
|
||||||
|
// TURN settings
|
||||||
|
s = m.section(form.NamedSection, 'turn', 'jitsi', _('TURN Server'));
|
||||||
|
s.anonymous = true;
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'enabled', _('Use External TURN'),
|
||||||
|
_('Use external TURN server for better NAT traversal'));
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'server', _('TURN Server'));
|
||||||
|
o.placeholder = 'turn.example.com:443';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'username', _('Username'));
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
o = s.option(form.Value, 'password', _('Password'));
|
||||||
|
o.password = true;
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
// Mesh integration
|
||||||
|
s = m.section(form.NamedSection, 'mesh', 'jitsi', _('Mesh Integration'));
|
||||||
|
s.anonymous = true;
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'enabled', _('Enable Mesh Integration'),
|
||||||
|
_('Announce Jitsi on SecuBox mesh network'));
|
||||||
|
o.default = '0';
|
||||||
|
|
||||||
|
o = s.option(form.Flag, 'announce_service', _('Announce Service'),
|
||||||
|
_('Register with mesh DNS'));
|
||||||
|
o.default = '1';
|
||||||
|
o.depends('enabled', '1');
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
s = m.section(form.NamedSection, 'main', 'jitsi', _('Actions'));
|
||||||
|
s.anonymous = true;
|
||||||
|
|
||||||
|
o = s.option(form.Button, '_install', _('Install Containers'));
|
||||||
|
o.inputtitle = _('Install');
|
||||||
|
o.inputstyle = 'apply';
|
||||||
|
o.onclick = function() {
|
||||||
|
if (confirm(_('This will download Docker images. Continue?'))) {
|
||||||
|
return callJitsiInstall().then(function(res) {
|
||||||
|
alert(res.output || 'Installation started');
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
o = s.option(form.Button, '_regenerate', _('Regenerate Configuration'));
|
||||||
|
o.inputtitle = _('Regenerate');
|
||||||
|
o.inputstyle = 'reload';
|
||||||
|
o.onclick = function() {
|
||||||
|
return callJitsiGenerateConfig().then(function(res) {
|
||||||
|
alert(res.output || 'Configuration regenerated');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Help section
|
||||||
|
s = m.section(form.NamedSection, 'main', 'jitsi', _('Quick Start'));
|
||||||
|
s.anonymous = true;
|
||||||
|
|
||||||
|
o = s.option(form.DummyValue, '_help');
|
||||||
|
o.rawhtml = true;
|
||||||
|
o.cfgvalue = function() {
|
||||||
|
var domain = uci.get('jitsi', 'main', 'domain') || 'meet.example.com';
|
||||||
|
return '<div style="background:#f5f5f5;padding:15px;border-radius:5px;">' +
|
||||||
|
'<h4>Setup Steps:</h4>' +
|
||||||
|
'<ol>' +
|
||||||
|
'<li>Set your domain above</li>' +
|
||||||
|
'<li>Click "Install Containers" to download images</li>' +
|
||||||
|
'<li>Enable the service and save</li>' +
|
||||||
|
'<li>Configure HAProxy/DNS to point to this device</li>' +
|
||||||
|
'</ol>' +
|
||||||
|
'<h4>Access:</h4>' +
|
||||||
|
'<p>Web: <a href="https://' + domain + '" target="_blank">https://' + domain + '</a></p>' +
|
||||||
|
'<p>CLI: <code>jitsctl status</code></p>' +
|
||||||
|
'</div>';
|
||||||
|
};
|
||||||
|
|
||||||
|
return m.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
120
package/secubox/luci-app-jitsi/root/usr/libexec/rpcd/luci.jitsi
Normal file
120
package/secubox/luci-app-jitsi/root/usr/libexec/rpcd/luci.jitsi
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. /lib/functions.sh
|
||||||
|
. /usr/share/libubox/jshn.sh
|
||||||
|
|
||||||
|
JITSI_DIR="/srv/jitsi"
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
list)
|
||||||
|
echo '{"status":{},"start":{},"stop":{},"restart":{},"install":{},"generate_config":{},"add_user":{"username":"str","password":"str"},"remove_user":{"username":"str"},"list_users":{},"logs":{"service":"str","lines":"int"}}'
|
||||||
|
;;
|
||||||
|
call)
|
||||||
|
case "$2" in
|
||||||
|
status)
|
||||||
|
# Get service status
|
||||||
|
json_init
|
||||||
|
|
||||||
|
# Check enabled
|
||||||
|
enabled=$(uci -q get jitsi.main.enabled)
|
||||||
|
domain=$(uci -q get jitsi.main.domain)
|
||||||
|
|
||||||
|
json_add_boolean "enabled" ${enabled:-0}
|
||||||
|
json_add_string "domain" "$domain"
|
||||||
|
|
||||||
|
# Check containers
|
||||||
|
if command -v docker >/dev/null 2>&1; then
|
||||||
|
json_add_boolean "docker_available" 1
|
||||||
|
|
||||||
|
json_add_object "containers"
|
||||||
|
for container in jitsi-web jitsi-prosody jitsi-jicofo jitsi-jvb; do
|
||||||
|
state=$(docker inspect -f '{{.State.Status}}' "$container" 2>/dev/null)
|
||||||
|
json_add_string "$container" "${state:-not_found}"
|
||||||
|
done
|
||||||
|
json_close_object
|
||||||
|
|
||||||
|
# Get stats from JVB if running
|
||||||
|
stats=$(docker exec jitsi-jvb curl -s http://localhost:8080/colibri/stats 2>/dev/null)
|
||||||
|
if [ -n "$stats" ]; then
|
||||||
|
conferences=$(echo "$stats" | jsonfilter -e '@.conferences' 2>/dev/null)
|
||||||
|
participants=$(echo "$stats" | jsonfilter -e '@.participants' 2>/dev/null)
|
||||||
|
json_add_object "stats"
|
||||||
|
json_add_int "conferences" ${conferences:-0}
|
||||||
|
json_add_int "participants" ${participants:-0}
|
||||||
|
json_close_object
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
json_add_boolean "docker_available" 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_dump
|
||||||
|
;;
|
||||||
|
start)
|
||||||
|
/etc/init.d/jitsi start >/dev/null 2>&1
|
||||||
|
echo '{"success":true}'
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
/etc/init.d/jitsi stop >/dev/null 2>&1
|
||||||
|
echo '{"success":true}'
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
/etc/init.d/jitsi restart >/dev/null 2>&1
|
||||||
|
echo '{"success":true}'
|
||||||
|
;;
|
||||||
|
install)
|
||||||
|
output=$(/usr/sbin/jitsctl install 2>&1)
|
||||||
|
json_init
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "output" "$output"
|
||||||
|
json_dump
|
||||||
|
;;
|
||||||
|
generate_config)
|
||||||
|
output=$(/usr/sbin/jitsctl generate-config 2>&1)
|
||||||
|
json_init
|
||||||
|
json_add_boolean "success" 1
|
||||||
|
json_add_string "output" "$output"
|
||||||
|
json_dump
|
||||||
|
;;
|
||||||
|
add_user)
|
||||||
|
read -r input
|
||||||
|
username=$(echo "$input" | jsonfilter -e '@.username')
|
||||||
|
password=$(echo "$input" | jsonfilter -e '@.password')
|
||||||
|
output=$(/usr/sbin/jitsctl add-user "$username" "$password" 2>&1)
|
||||||
|
code=$?
|
||||||
|
json_init
|
||||||
|
json_add_boolean "success" $((code == 0))
|
||||||
|
json_add_string "output" "$output"
|
||||||
|
json_dump
|
||||||
|
;;
|
||||||
|
remove_user)
|
||||||
|
read -r input
|
||||||
|
username=$(echo "$input" | jsonfilter -e '@.username')
|
||||||
|
output=$(/usr/sbin/jitsctl remove-user "$username" 2>&1)
|
||||||
|
code=$?
|
||||||
|
json_init
|
||||||
|
json_add_boolean "success" $((code == 0))
|
||||||
|
json_add_string "output" "$output"
|
||||||
|
json_dump
|
||||||
|
;;
|
||||||
|
list_users)
|
||||||
|
domain=$(uci -q get jitsi.main.domain)
|
||||||
|
users=$(docker exec jitsi-prosody ls -1 /config/data/"$domain"/accounts/ 2>/dev/null | sed 's/\.dat$//' | tr '\n' ',')
|
||||||
|
json_init
|
||||||
|
json_add_string "users" "$users"
|
||||||
|
json_dump
|
||||||
|
;;
|
||||||
|
logs)
|
||||||
|
read -r input
|
||||||
|
service=$(echo "$input" | jsonfilter -e '@.service')
|
||||||
|
lines=$(echo "$input" | jsonfilter -e '@.lines')
|
||||||
|
[ -z "$lines" ] && lines=50
|
||||||
|
logs=$(cd "$JITSI_DIR" && docker-compose logs --tail="$lines" ${service:-} 2>&1 | tail -100)
|
||||||
|
json_init
|
||||||
|
json_add_string "logs" "$logs"
|
||||||
|
json_dump
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"admin/services/jitsi": {
|
||||||
|
"title": "Jitsi Meet",
|
||||||
|
"order": 60,
|
||||||
|
"action": {
|
||||||
|
"type": "view",
|
||||||
|
"path": "jitsi/config"
|
||||||
|
},
|
||||||
|
"depends": {
|
||||||
|
"acl": ["luci-app-jitsi"],
|
||||||
|
"uci": {"jitsi": true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"luci-app-jitsi": {
|
||||||
|
"description": "Grant access to Jitsi Meet configuration",
|
||||||
|
"read": {
|
||||||
|
"file": {
|
||||||
|
"/etc/config/jitsi": ["read"],
|
||||||
|
"/srv/jitsi/.env": ["read"]
|
||||||
|
},
|
||||||
|
"ubus": {
|
||||||
|
"file": ["read", "stat"],
|
||||||
|
"luci.jitsi": ["*"]
|
||||||
|
},
|
||||||
|
"uci": ["jitsi"]
|
||||||
|
},
|
||||||
|
"write": {
|
||||||
|
"file": {
|
||||||
|
"/etc/config/jitsi": ["write"]
|
||||||
|
},
|
||||||
|
"ubus": {
|
||||||
|
"luci.jitsi": ["*"]
|
||||||
|
},
|
||||||
|
"uci": ["jitsi"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
90
package/secubox/secubox-app-jitsi/Makefile
Normal file
90
package/secubox/secubox-app-jitsi/Makefile
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
PKG_NAME:=secubox-app-jitsi
|
||||||
|
PKG_VERSION:=1.0.0
|
||||||
|
PKG_RELEASE:=1
|
||||||
|
PKG_ARCH:=all
|
||||||
|
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
|
||||||
|
PKG_LICENSE:=Apache-2.0
|
||||||
|
|
||||||
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
define Package/secubox-app-jitsi
|
||||||
|
SECTION:=utils
|
||||||
|
CATEGORY:=Utilities
|
||||||
|
PKGARCH:=all
|
||||||
|
SUBMENU:=SecuBox Apps
|
||||||
|
TITLE:=SecuBox Jitsi Meet Video Conferencing
|
||||||
|
DEPENDS:=+docker +docker-compose +wget +openssl-util
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/secubox-app-jitsi/description
|
||||||
|
Jitsi Meet - Secure, fully featured video conferencing for SecuBox.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- End-to-end encrypted video conferences
|
||||||
|
- No account required for guests
|
||||||
|
- Screen sharing and recording
|
||||||
|
- Chat, reactions, and virtual backgrounds
|
||||||
|
- Mobile app support (iOS/Android)
|
||||||
|
- WebRTC-based, works in any browser
|
||||||
|
- Self-hosted for complete privacy
|
||||||
|
|
||||||
|
Runs via Docker containers for easy deployment.
|
||||||
|
Integrates with HAProxy for SSL termination.
|
||||||
|
Configure in /etc/config/jitsi.
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/secubox-app-jitsi/conffiles
|
||||||
|
/etc/config/jitsi
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/secubox-app-jitsi/install
|
||||||
|
$(INSTALL_DIR) $(1)/etc/config
|
||||||
|
$(INSTALL_CONF) ./files/etc/config/jitsi $(1)/etc/config/jitsi
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/etc/init.d
|
||||||
|
$(INSTALL_BIN) ./files/etc/init.d/jitsi $(1)/etc/init.d/jitsi
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/usr/sbin
|
||||||
|
$(INSTALL_BIN) ./files/usr/sbin/jitsctl $(1)/usr/sbin/jitsctl
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/usr/share/jitsi
|
||||||
|
$(INSTALL_DATA) ./files/usr/share/jitsi/docker-compose.yml $(1)/usr/share/jitsi/
|
||||||
|
$(INSTALL_DATA) ./files/usr/share/jitsi/env.template $(1)/usr/share/jitsi/
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/usr/share/jitsi/prosody
|
||||||
|
$(INSTALL_DATA) ./files/usr/share/jitsi/prosody/prosody.cfg.lua.template $(1)/usr/share/jitsi/prosody/
|
||||||
|
|
||||||
|
$(INSTALL_DIR) $(1)/usr/lib/secubox/haproxy.d
|
||||||
|
$(INSTALL_DATA) ./files/usr/lib/secubox/haproxy.d/jitsi.cfg $(1)/usr/lib/secubox/haproxy.d/
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Package/secubox-app-jitsi/postinst
|
||||||
|
#!/bin/sh
|
||||||
|
[ -n "$${IPKG_INSTROOT}" ] || {
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " Jitsi Meet Video Conferencing Installed"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Quick Start:"
|
||||||
|
echo " 1. Configure: uci set jitsi.main.domain='meet.example.com'"
|
||||||
|
echo " 2. Install: jitsctl install"
|
||||||
|
echo " 3. Start: /etc/init.d/jitsi start"
|
||||||
|
echo ""
|
||||||
|
echo "Control commands:"
|
||||||
|
echo " jitsctl status - Show service status"
|
||||||
|
echo " jitsctl logs - View container logs"
|
||||||
|
echo " jitsctl shell - Access container shell"
|
||||||
|
echo ""
|
||||||
|
echo "Web interface: https://<your-domain>"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
exit 0
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(eval $(call BuildPackage,secubox-app-jitsi))
|
||||||
188
package/secubox/secubox-app-jitsi/README.md
Normal file
188
package/secubox/secubox-app-jitsi/README.md
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
# SecuBox Jitsi Meet
|
||||||
|
|
||||||
|
Self-hosted video conferencing with end-to-end encryption for SecuBox.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Secure Video Calls**: End-to-end encrypted video conferences
|
||||||
|
- **No Account Required**: Guests can join without registration
|
||||||
|
- **Screen Sharing**: Share your screen with participants
|
||||||
|
- **Chat & Reactions**: In-meeting chat and emoji reactions
|
||||||
|
- **Breakout Rooms**: Split meetings into smaller groups
|
||||||
|
- **Recording**: Optional recording to Dropbox (requires setup)
|
||||||
|
- **Mobile Support**: iOS and Android apps available
|
||||||
|
- **HAProxy Integration**: Automatic SSL and reverse proxy setup
|
||||||
|
- **Mesh Federation**: Announce service on SecuBox mesh network
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Docker and docker-compose
|
||||||
|
- 2GB+ RAM (4GB recommended)
|
||||||
|
- Public domain with DNS pointing to your SecuBox
|
||||||
|
- SSL certificate (via Let's Encrypt or HAProxy)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install
|
||||||
|
opkg install secubox-app-jitsi luci-app-jitsi
|
||||||
|
|
||||||
|
# Configure domain
|
||||||
|
uci set jitsi.main.domain='meet.example.com'
|
||||||
|
uci set jitsi.main.enabled='1'
|
||||||
|
uci commit jitsi
|
||||||
|
|
||||||
|
# Install Docker containers
|
||||||
|
jitsctl install
|
||||||
|
|
||||||
|
# Start service
|
||||||
|
/etc/init.d/jitsi start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Via LuCI
|
||||||
|
Navigate to **Services > Jitsi Meet** in the LuCI web interface.
|
||||||
|
|
||||||
|
### Via CLI
|
||||||
|
```bash
|
||||||
|
# Show status
|
||||||
|
jitsctl status
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
jitsctl logs
|
||||||
|
|
||||||
|
# Add authenticated user
|
||||||
|
jitsctl add-user admin secretpassword
|
||||||
|
|
||||||
|
# Regenerate configuration
|
||||||
|
jitsctl generate-config
|
||||||
|
|
||||||
|
# Restart containers
|
||||||
|
jitsctl restart
|
||||||
|
```
|
||||||
|
|
||||||
|
### UCI Options
|
||||||
|
|
||||||
|
```
|
||||||
|
config jitsi 'main'
|
||||||
|
option enabled '1'
|
||||||
|
option domain 'meet.example.com'
|
||||||
|
option timezone 'Europe/Paris'
|
||||||
|
|
||||||
|
config jitsi 'web'
|
||||||
|
option port '8443'
|
||||||
|
option enable_guests '1'
|
||||||
|
option enable_auth '0'
|
||||||
|
option default_language 'en'
|
||||||
|
|
||||||
|
config jitsi 'jvb'
|
||||||
|
option port '10000'
|
||||||
|
option enable_tcp_fallback '0'
|
||||||
|
option stun_servers 'meet-jit-si-turnrelay.jitsi.net:443'
|
||||||
|
|
||||||
|
config jitsi 'security'
|
||||||
|
option lobby_enabled '1'
|
||||||
|
option password_required '0'
|
||||||
|
option jwt_enabled '0'
|
||||||
|
```
|
||||||
|
|
||||||
|
## HAProxy Integration
|
||||||
|
|
||||||
|
If secubox-app-haproxy is installed, Jitsi will automatically configure a vhost:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
jitsctl configure-haproxy
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates:
|
||||||
|
- HTTPS frontend on port 443
|
||||||
|
- WebSocket support for real-time communication
|
||||||
|
- SSL termination (using your certificate)
|
||||||
|
|
||||||
|
## Firewall
|
||||||
|
|
||||||
|
The following ports are required:
|
||||||
|
|
||||||
|
| Port | Protocol | Description |
|
||||||
|
|------|----------|-------------|
|
||||||
|
| 443 | TCP | HTTPS (via HAProxy) |
|
||||||
|
| 8443 | TCP | Direct web access |
|
||||||
|
| 10000 | UDP | Video/audio streams |
|
||||||
|
| 4443 | TCP | TCP fallback (optional) |
|
||||||
|
|
||||||
|
Firewall rules are automatically added during installation.
|
||||||
|
|
||||||
|
## Mesh Integration
|
||||||
|
|
||||||
|
Enable mesh federation to:
|
||||||
|
- Announce Jitsi on the SecuBox mesh network
|
||||||
|
- Auto-register DNS entry (e.g., meet.c3box.mesh.local)
|
||||||
|
- Enable multi-node video bridge deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uci set jitsi.mesh.enabled='1'
|
||||||
|
uci commit jitsi
|
||||||
|
/etc/init.d/jitsi restart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Containers not starting
|
||||||
|
```bash
|
||||||
|
# Check Docker status
|
||||||
|
docker ps -a
|
||||||
|
|
||||||
|
# View container logs
|
||||||
|
jitsctl logs web
|
||||||
|
jitsctl logs prosody
|
||||||
|
jitsctl logs jicofo
|
||||||
|
jitsctl logs jvb
|
||||||
|
```
|
||||||
|
|
||||||
|
### Video/audio not working
|
||||||
|
1. Check UDP port 10000 is open on firewall
|
||||||
|
2. Verify STUN servers are reachable
|
||||||
|
3. Enable TCP fallback if behind strict NAT
|
||||||
|
|
||||||
|
### Authentication issues
|
||||||
|
```bash
|
||||||
|
# List users
|
||||||
|
jitsctl list-users
|
||||||
|
|
||||||
|
# Reset user password
|
||||||
|
jitsctl remove-user admin
|
||||||
|
jitsctl add-user admin newpassword
|
||||||
|
```
|
||||||
|
|
||||||
|
## Backup & Restore
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create backup
|
||||||
|
jitsctl backup /tmp/jitsi-backup.tar.gz
|
||||||
|
|
||||||
|
# Restore
|
||||||
|
jitsctl restore /tmp/jitsi-backup.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────┐
|
||||||
|
│ HAProxy (443) │
|
||||||
|
│ SSL Termination │
|
||||||
|
└──────────────────────┬──────────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌──────────────────────┴──────────────────────────────────┐
|
||||||
|
│ Docker Network: meet.jitsi │
|
||||||
|
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
|
||||||
|
│ │ Web │ │ Prosody │ │ Jicofo │ │ JVB │ │
|
||||||
|
│ │ :8443 │ │ :5222 │ │ :8888 │ │ :10000/UDP │ │
|
||||||
|
│ │ React │ │ XMPP │ │ Focus │ │ Media │ │
|
||||||
|
│ └─────────┘ └─────────┘ └─────────┘ └─────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Apache 2.0 - See LICENSE file for details.
|
||||||
53
package/secubox/secubox-app-jitsi/files/etc/config/jitsi
Normal file
53
package/secubox/secubox-app-jitsi/files/etc/config/jitsi
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Jitsi Meet Video Conferencing Configuration
|
||||||
|
|
||||||
|
config jitsi 'main'
|
||||||
|
option enabled '0'
|
||||||
|
option domain 'meet.secubox.local'
|
||||||
|
option public_url ''
|
||||||
|
option container_type 'docker'
|
||||||
|
option timezone 'Europe/Paris'
|
||||||
|
option letsencrypt '0'
|
||||||
|
option letsencrypt_email ''
|
||||||
|
|
||||||
|
config jitsi 'web'
|
||||||
|
option port '8443'
|
||||||
|
option enable_guests '1'
|
||||||
|
option enable_auth '0'
|
||||||
|
option default_language 'en'
|
||||||
|
option enable_transcription '0'
|
||||||
|
option enable_recording '0'
|
||||||
|
option watermark_link ''
|
||||||
|
|
||||||
|
config jitsi 'jvb'
|
||||||
|
option port '10000'
|
||||||
|
option enable_tcp_fallback '0'
|
||||||
|
option tcp_port '4443'
|
||||||
|
option stun_servers 'meet-jit-si-turnrelay.jitsi.net:443'
|
||||||
|
option max_bandwidth '10000000'
|
||||||
|
|
||||||
|
config jitsi 'prosody'
|
||||||
|
option c2s_port '5222'
|
||||||
|
option http_port '5280'
|
||||||
|
option guest_domain_enabled '1'
|
||||||
|
|
||||||
|
config jitsi 'jicofo'
|
||||||
|
option port '8888'
|
||||||
|
option enable_ocontrol '0'
|
||||||
|
|
||||||
|
config jitsi 'security'
|
||||||
|
option jwt_enabled '0'
|
||||||
|
option jwt_app_id ''
|
||||||
|
option jwt_app_secret ''
|
||||||
|
option lobby_enabled '1'
|
||||||
|
option password_required '0'
|
||||||
|
|
||||||
|
config jitsi 'mesh'
|
||||||
|
option enabled '0'
|
||||||
|
option announce_service '1'
|
||||||
|
option cascade_jvb '0'
|
||||||
|
|
||||||
|
config jitsi 'turn'
|
||||||
|
option enabled '0'
|
||||||
|
option server ''
|
||||||
|
option username ''
|
||||||
|
option password ''
|
||||||
49
package/secubox/secubox-app-jitsi/files/etc/init.d/jitsi
Normal file
49
package/secubox/secubox-app-jitsi/files/etc/init.d/jitsi
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#!/bin/sh /etc/rc.common
|
||||||
|
# Jitsi Meet Video Conferencing Service
|
||||||
|
|
||||||
|
START=95
|
||||||
|
STOP=15
|
||||||
|
USE_PROCD=1
|
||||||
|
|
||||||
|
JITSI_DIR="/srv/jitsi"
|
||||||
|
COMPOSE_FILE="$JITSI_DIR/docker-compose.yml"
|
||||||
|
|
||||||
|
start_service() {
|
||||||
|
local enabled=$(uci -q get jitsi.main.enabled)
|
||||||
|
[ "$enabled" != "1" ] && return 0
|
||||||
|
|
||||||
|
# Check Docker
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
logger -t jitsi "Docker not available"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate config if needed
|
||||||
|
if [ ! -f "$JITSI_DIR/.env" ]; then
|
||||||
|
/usr/sbin/jitsctl generate-config
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Start containers
|
||||||
|
cd "$JITSI_DIR"
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
logger -t jitsi "Jitsi Meet started"
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_service() {
|
||||||
|
if [ -f "$COMPOSE_FILE" ]; then
|
||||||
|
cd "$JITSI_DIR"
|
||||||
|
docker-compose down
|
||||||
|
fi
|
||||||
|
logger -t jitsi "Jitsi Meet stopped"
|
||||||
|
}
|
||||||
|
|
||||||
|
reload_service() {
|
||||||
|
/usr/sbin/jitsctl generate-config
|
||||||
|
cd "$JITSI_DIR"
|
||||||
|
docker-compose restart
|
||||||
|
}
|
||||||
|
|
||||||
|
status() {
|
||||||
|
/usr/sbin/jitsctl status
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
# HAProxy configuration snippet for Jitsi Meet
|
||||||
|
# Include in main haproxy.cfg or via haproxy.d directory
|
||||||
|
|
||||||
|
# Frontend rule for Jitsi (add to your HTTPS frontend)
|
||||||
|
# acl is_jitsi hdr(host) -i meet.example.com
|
||||||
|
# use_backend jitsi_web if is_jitsi
|
||||||
|
|
||||||
|
backend jitsi_web
|
||||||
|
mode http
|
||||||
|
option httpchk GET /
|
||||||
|
http-check expect status 200
|
||||||
|
# WebSocket support
|
||||||
|
option http-server-close
|
||||||
|
timeout tunnel 1h
|
||||||
|
server jitsi 127.0.0.1:8443 check ssl verify none
|
||||||
|
|
||||||
|
# XMPP BOSH/WebSocket backend (if needed separately)
|
||||||
|
backend jitsi_xmpp
|
||||||
|
mode http
|
||||||
|
option httpchk GET /http-bind
|
||||||
|
server prosody 127.0.0.1:5280 check
|
||||||
667
package/secubox/secubox-app-jitsi/files/usr/sbin/jitsctl
Normal file
667
package/secubox/secubox-app-jitsi/files/usr/sbin/jitsctl
Normal file
@ -0,0 +1,667 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Jitsi Meet Control Script for SecuBox
|
||||||
|
# Manages Jitsi Meet Docker deployment
|
||||||
|
|
||||||
|
VERSION="1.0.0"
|
||||||
|
JITSI_DIR="/srv/jitsi"
|
||||||
|
CONFIG_DIR="$JITSI_DIR/.jitsi-meet-cfg"
|
||||||
|
TEMPLATE_DIR="/usr/share/jitsi"
|
||||||
|
ENV_FILE="$JITSI_DIR/.env"
|
||||||
|
COMPOSE_FILE="$JITSI_DIR/docker-compose.yml"
|
||||||
|
|
||||||
|
# Jitsi Docker image versions
|
||||||
|
JITSI_IMAGE_VERSION="stable-9location1"
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
log() { echo -e "${GREEN}[JITSI]${NC} $1"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Configuration Generation
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
generate_secret() {
|
||||||
|
openssl rand -hex 16 2>/dev/null || head -c 32 /dev/urandom | md5sum | cut -d' ' -f1
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_config() {
|
||||||
|
log "Generating Jitsi configuration..."
|
||||||
|
|
||||||
|
mkdir -p "$JITSI_DIR"
|
||||||
|
mkdir -p "$CONFIG_DIR"/{web,transcripts,prosody/config,prosody/prosody-plugins-custom,jicofo,jvb,jigasi,jibri}
|
||||||
|
|
||||||
|
# Read UCI configuration
|
||||||
|
local domain=$(uci -q get jitsi.main.domain || echo "meet.secubox.local")
|
||||||
|
local public_url=$(uci -q get jitsi.main.public_url)
|
||||||
|
[ -z "$public_url" ] && public_url="https://$domain"
|
||||||
|
local timezone=$(uci -q get jitsi.main.timezone || echo "UTC")
|
||||||
|
local letsencrypt=$(uci -q get jitsi.main.letsencrypt || echo "0")
|
||||||
|
local le_email=$(uci -q get jitsi.main.letsencrypt_email)
|
||||||
|
|
||||||
|
# Web config
|
||||||
|
local web_port=$(uci -q get jitsi.web.port || echo "8443")
|
||||||
|
local enable_guests=$(uci -q get jitsi.web.enable_guests || echo "1")
|
||||||
|
local enable_auth=$(uci -q get jitsi.web.enable_auth || echo "0")
|
||||||
|
local default_lang=$(uci -q get jitsi.web.default_language || echo "en")
|
||||||
|
|
||||||
|
# JVB config
|
||||||
|
local jvb_port=$(uci -q get jitsi.jvb.port || echo "10000")
|
||||||
|
local jvb_tcp=$(uci -q get jitsi.jvb.enable_tcp_fallback || echo "0")
|
||||||
|
local jvb_tcp_port=$(uci -q get jitsi.jvb.tcp_port || echo "4443")
|
||||||
|
local stun_servers=$(uci -q get jitsi.jvb.stun_servers || echo "meet-jit-si-turnrelay.jitsi.net:443")
|
||||||
|
|
||||||
|
# Security
|
||||||
|
local jwt_enabled=$(uci -q get jitsi.security.jwt_enabled || echo "0")
|
||||||
|
local jwt_app_id=$(uci -q get jitsi.security.jwt_app_id)
|
||||||
|
local jwt_secret=$(uci -q get jitsi.security.jwt_app_secret)
|
||||||
|
local lobby=$(uci -q get jitsi.security.lobby_enabled || echo "1")
|
||||||
|
|
||||||
|
# TURN
|
||||||
|
local turn_enabled=$(uci -q get jitsi.turn.enabled || echo "0")
|
||||||
|
local turn_server=$(uci -q get jitsi.turn.server)
|
||||||
|
local turn_user=$(uci -q get jitsi.turn.username)
|
||||||
|
local turn_pass=$(uci -q get jitsi.turn.password)
|
||||||
|
|
||||||
|
# Generate secrets if not already stored
|
||||||
|
local jicofo_secret=$(uci -q get jitsi.secrets.jicofo_component_secret)
|
||||||
|
local jicofo_auth_pass=$(uci -q get jitsi.secrets.jicofo_auth_password)
|
||||||
|
local jvb_auth_pass=$(uci -q get jitsi.secrets.jvb_auth_password)
|
||||||
|
|
||||||
|
if [ -z "$jicofo_secret" ]; then
|
||||||
|
jicofo_secret=$(generate_secret)
|
||||||
|
uci -q set jitsi.secrets=jitsi
|
||||||
|
uci -q set jitsi.secrets.jicofo_component_secret="$jicofo_secret"
|
||||||
|
fi
|
||||||
|
if [ -z "$jicofo_auth_pass" ]; then
|
||||||
|
jicofo_auth_pass=$(generate_secret)
|
||||||
|
uci -q set jitsi.secrets.jicofo_auth_password="$jicofo_auth_pass"
|
||||||
|
fi
|
||||||
|
if [ -z "$jvb_auth_pass" ]; then
|
||||||
|
jvb_auth_pass=$(generate_secret)
|
||||||
|
uci -q set jitsi.secrets.jvb_auth_password="$jvb_auth_pass"
|
||||||
|
uci commit jitsi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine auth type
|
||||||
|
local auth_type="internal"
|
||||||
|
[ "$enable_auth" = "1" ] && auth_type="internal_hashed"
|
||||||
|
[ "$jwt_enabled" = "1" ] && auth_type="jwt"
|
||||||
|
|
||||||
|
# Write .env file
|
||||||
|
cat > "$ENV_FILE" << EOF
|
||||||
|
# Jitsi Meet Configuration
|
||||||
|
# Generated by SecuBox jitsctl v$VERSION
|
||||||
|
# $(date)
|
||||||
|
|
||||||
|
# Core settings
|
||||||
|
CONFIG=$CONFIG_DIR
|
||||||
|
HTTP_PORT=8000
|
||||||
|
HTTPS_PORT=$web_port
|
||||||
|
TZ=$timezone
|
||||||
|
|
||||||
|
# Domain settings
|
||||||
|
PUBLIC_URL=$public_url
|
||||||
|
XMPP_DOMAIN=$domain
|
||||||
|
XMPP_SERVER=xmpp.$domain
|
||||||
|
JVB_BREWERY_MUC=jvbbrewery
|
||||||
|
XMPP_AUTH_DOMAIN=auth.$domain
|
||||||
|
XMPP_GUEST_DOMAIN=guest.$domain
|
||||||
|
XMPP_MUC_DOMAIN=muc.$domain
|
||||||
|
XMPP_INTERNAL_MUC_DOMAIN=internal-muc.$domain
|
||||||
|
XMPP_MODULES=
|
||||||
|
XMPP_MUC_MODULES=
|
||||||
|
XMPP_INTERNAL_MUC_MODULES=
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
ENABLE_AUTH=$enable_auth
|
||||||
|
ENABLE_GUESTS=$enable_guests
|
||||||
|
AUTH_TYPE=$auth_type
|
||||||
|
|
||||||
|
# JWT (if enabled)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ "$jwt_enabled" = "1" ]; then
|
||||||
|
cat >> "$ENV_FILE" << EOF
|
||||||
|
JWT_APP_ID=$jwt_app_id
|
||||||
|
JWT_APP_SECRET=$jwt_secret
|
||||||
|
JWT_ACCEPTED_ISSUERS=$jwt_app_id
|
||||||
|
JWT_ACCEPTED_AUDIENCES=$jwt_app_id
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat >> "$ENV_FILE" << EOF
|
||||||
|
|
||||||
|
# XMPP secrets
|
||||||
|
JICOFO_COMPONENT_SECRET=$jicofo_secret
|
||||||
|
JICOFO_AUTH_USER=focus
|
||||||
|
JICOFO_AUTH_PASSWORD=$jicofo_auth_pass
|
||||||
|
JVB_AUTH_USER=jvb
|
||||||
|
JVB_AUTH_PASSWORD=$jvb_auth_pass
|
||||||
|
|
||||||
|
# JVB settings
|
||||||
|
JVB_PORT=$jvb_port
|
||||||
|
JVB_STUN_SERVERS=$stun_servers
|
||||||
|
JVB_TCP_HARVESTER_DISABLED=$([ "$jvb_tcp" = "1" ] && echo "false" || echo "true")
|
||||||
|
JVB_TCP_PORT=$jvb_tcp_port
|
||||||
|
|
||||||
|
# Features
|
||||||
|
ENABLE_LOBBY=$lobby
|
||||||
|
ENABLE_BREAKOUT_ROOMS=1
|
||||||
|
ENABLE_PREJOIN_PAGE=1
|
||||||
|
ENABLE_WELCOME_PAGE=1
|
||||||
|
ENABLE_CLOSE_PAGE=0
|
||||||
|
ENABLE_NOISY_MIC_DETECTION=1
|
||||||
|
ENABLE_TALK_WHILE_MUTED=1
|
||||||
|
ENABLE_REACTIONS=1
|
||||||
|
|
||||||
|
# UI settings
|
||||||
|
DEFAULT_LANGUAGE=$default_lang
|
||||||
|
DISABLE_AUDIO_LEVELS=0
|
||||||
|
DISABLE_POLLS=0
|
||||||
|
ENABLE_CALENDAR=0
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# Add TURN config if enabled
|
||||||
|
if [ "$turn_enabled" = "1" ] && [ -n "$turn_server" ]; then
|
||||||
|
cat >> "$ENV_FILE" << EOF
|
||||||
|
|
||||||
|
# TURN server
|
||||||
|
TURN_CREDENTIALS=$turn_user
|
||||||
|
TURN_SECRET=$turn_pass
|
||||||
|
TURN_HOST=$turn_server
|
||||||
|
TURN_PORT=443
|
||||||
|
TURNS_HOST=$turn_server
|
||||||
|
TURNS_PORT=443
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add Let's Encrypt config
|
||||||
|
if [ "$letsencrypt" = "1" ] && [ -n "$le_email" ]; then
|
||||||
|
cat >> "$ENV_FILE" << EOF
|
||||||
|
|
||||||
|
# Let's Encrypt
|
||||||
|
ENABLE_LETSENCRYPT=1
|
||||||
|
LETSENCRYPT_DOMAIN=$domain
|
||||||
|
LETSENCRYPT_EMAIL=$le_email
|
||||||
|
LETSENCRYPT_USE_STAGING=0
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy docker-compose.yml
|
||||||
|
cp "$TEMPLATE_DIR/docker-compose.yml" "$COMPOSE_FILE"
|
||||||
|
|
||||||
|
log "Configuration generated at $ENV_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Installation
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
install_jitsi() {
|
||||||
|
log "Installing Jitsi Meet..."
|
||||||
|
|
||||||
|
# Check Docker
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
error "Docker is required. Install with: opkg install docker docker-compose"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create directories
|
||||||
|
mkdir -p "$JITSI_DIR"
|
||||||
|
mkdir -p "$CONFIG_DIR"
|
||||||
|
|
||||||
|
# Generate configuration
|
||||||
|
generate_config
|
||||||
|
|
||||||
|
# Pull images
|
||||||
|
log "Pulling Jitsi Docker images (this may take a while)..."
|
||||||
|
cd "$JITSI_DIR"
|
||||||
|
docker-compose pull
|
||||||
|
|
||||||
|
# Configure HAProxy if available
|
||||||
|
if [ -x /usr/sbin/haproxyctl ]; then
|
||||||
|
configure_haproxy
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Configure firewall
|
||||||
|
configure_firewall
|
||||||
|
|
||||||
|
# Register with mesh
|
||||||
|
register_mesh_service
|
||||||
|
|
||||||
|
log "Jitsi Meet installed successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Set your domain: uci set jitsi.main.domain='meet.example.com'"
|
||||||
|
echo " 2. Commit changes: uci commit jitsi"
|
||||||
|
echo " 3. Regenerate: jitsctl generate-config"
|
||||||
|
echo " 4. Enable service: uci set jitsi.main.enabled=1 && uci commit jitsi"
|
||||||
|
echo " 5. Start: /etc/init.d/jitsi start"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# HAProxy Integration
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
configure_haproxy() {
|
||||||
|
log "Configuring HAProxy for Jitsi..."
|
||||||
|
|
||||||
|
local domain=$(uci -q get jitsi.main.domain || echo "meet.secubox.local")
|
||||||
|
local web_port=$(uci -q get jitsi.web.port || echo "8443")
|
||||||
|
|
||||||
|
# Check if vhost already exists
|
||||||
|
local existing=$(uci show haproxy 2>/dev/null | grep "\.domain='$domain'" | head -1)
|
||||||
|
if [ -n "$existing" ]; then
|
||||||
|
warn "HAProxy vhost for $domain already exists"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add backend
|
||||||
|
uci -q add haproxy backend
|
||||||
|
uci -q set haproxy.@backend[-1].name='jitsi_web'
|
||||||
|
uci -q set haproxy.@backend[-1].mode='http'
|
||||||
|
uci -q add_list haproxy.@backend[-1].server="jitsi 127.0.0.1:$web_port check"
|
||||||
|
|
||||||
|
# Add vhost
|
||||||
|
uci -q add haproxy vhost
|
||||||
|
uci -q set haproxy.@vhost[-1].enabled='1'
|
||||||
|
uci -q set haproxy.@vhost[-1].domain="$domain"
|
||||||
|
uci -q set haproxy.@vhost[-1].backend='jitsi_web'
|
||||||
|
uci -q set haproxy.@vhost[-1].ssl='1'
|
||||||
|
uci -q set haproxy.@vhost[-1].ssl_redirect='1'
|
||||||
|
uci -q set haproxy.@vhost[-1].websocket='1'
|
||||||
|
|
||||||
|
uci commit haproxy
|
||||||
|
|
||||||
|
# Reload HAProxy
|
||||||
|
/etc/init.d/haproxy reload 2>/dev/null
|
||||||
|
|
||||||
|
log "HAProxy configured for $domain"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Firewall
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
configure_firewall() {
|
||||||
|
log "Configuring firewall..."
|
||||||
|
|
||||||
|
local jvb_port=$(uci -q get jitsi.jvb.port || echo "10000")
|
||||||
|
local jvb_tcp_port=$(uci -q get jitsi.jvb.tcp_port || echo "4443")
|
||||||
|
|
||||||
|
# JVB UDP port (required for video)
|
||||||
|
if ! uci show firewall 2>/dev/null | grep -q "Jitsi-JVB"; then
|
||||||
|
uci add firewall rule
|
||||||
|
uci set firewall.@rule[-1].name='Jitsi-JVB'
|
||||||
|
uci set firewall.@rule[-1].src='wan'
|
||||||
|
uci set firewall.@rule[-1].dest_port="$jvb_port"
|
||||||
|
uci set firewall.@rule[-1].proto='udp'
|
||||||
|
uci set firewall.@rule[-1].target='ACCEPT'
|
||||||
|
uci set firewall.@rule[-1].enabled='1'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# JVB TCP fallback port
|
||||||
|
local jvb_tcp=$(uci -q get jitsi.jvb.enable_tcp_fallback || echo "0")
|
||||||
|
if [ "$jvb_tcp" = "1" ]; then
|
||||||
|
if ! uci show firewall 2>/dev/null | grep -q "Jitsi-JVB-TCP"; then
|
||||||
|
uci add firewall rule
|
||||||
|
uci set firewall.@rule[-1].name='Jitsi-JVB-TCP'
|
||||||
|
uci set firewall.@rule[-1].src='wan'
|
||||||
|
uci set firewall.@rule[-1].dest_port="$jvb_tcp_port"
|
||||||
|
uci set firewall.@rule[-1].proto='tcp'
|
||||||
|
uci set firewall.@rule[-1].target='ACCEPT'
|
||||||
|
uci set firewall.@rule[-1].enabled='1'
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
uci commit firewall
|
||||||
|
/etc/init.d/firewall reload 2>/dev/null
|
||||||
|
|
||||||
|
log "Firewall configured"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Mesh Integration
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
register_mesh_service() {
|
||||||
|
local mesh_enabled=$(uci -q get jitsi.mesh.enabled || echo "0")
|
||||||
|
[ "$mesh_enabled" != "1" ] && return 0
|
||||||
|
|
||||||
|
local domain=$(uci -q get jitsi.main.domain)
|
||||||
|
local web_port=$(uci -q get jitsi.web.port || echo "8443")
|
||||||
|
|
||||||
|
# Register with P2P daemon
|
||||||
|
if [ -x /usr/sbin/secubox-p2p ]; then
|
||||||
|
/usr/sbin/secubox-p2p register-service jitsi-meet "$web_port" 2>/dev/null
|
||||||
|
log "Registered Jitsi with mesh network"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Add DNS entry if DNS federation enabled
|
||||||
|
local dns_enabled=$(uci -q get secubox-p2p.dns.enabled || echo "0")
|
||||||
|
if [ "$dns_enabled" = "1" ]; then
|
||||||
|
local dns_domain=$(uci -q get secubox-p2p.dns.base_domain || echo "mesh.local")
|
||||||
|
local hostname=$(echo "$domain" | cut -d'.' -f1)
|
||||||
|
log "Mesh DNS: $hostname.$dns_domain"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# User Management
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
add_user() {
|
||||||
|
local username="$1"
|
||||||
|
local password="$2"
|
||||||
|
|
||||||
|
if [ -z "$username" ] || [ -z "$password" ]; then
|
||||||
|
echo "Usage: jitsctl add-user <username> <password>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local domain=$(uci -q get jitsi.main.domain)
|
||||||
|
|
||||||
|
log "Adding user: $username"
|
||||||
|
|
||||||
|
docker exec jitsi-prosody prosodyctl register "$username" "$domain" "$password"
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log "User $username added successfully"
|
||||||
|
else
|
||||||
|
error "Failed to add user $username"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_user() {
|
||||||
|
local username="$1"
|
||||||
|
|
||||||
|
if [ -z "$username" ]; then
|
||||||
|
echo "Usage: jitsctl remove-user <username>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local domain=$(uci -q get jitsi.main.domain)
|
||||||
|
|
||||||
|
log "Removing user: $username"
|
||||||
|
|
||||||
|
docker exec jitsi-prosody prosodyctl unregister "$username" "$domain"
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log "User $username removed"
|
||||||
|
else
|
||||||
|
error "Failed to remove user $username"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
list_users() {
|
||||||
|
local domain=$(uci -q get jitsi.main.domain)
|
||||||
|
|
||||||
|
echo "Registered users for $domain:"
|
||||||
|
docker exec jitsi-prosody ls -1 /config/data/"$domain"/accounts/ 2>/dev/null | sed 's/\.dat$//'
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Status & Logs
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
show_status() {
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo " Jitsi Meet Status v$VERSION"
|
||||||
|
echo "========================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
local enabled=$(uci -q get jitsi.main.enabled)
|
||||||
|
local domain=$(uci -q get jitsi.main.domain)
|
||||||
|
local public_url=$(uci -q get jitsi.main.public_url)
|
||||||
|
[ -z "$public_url" ] && public_url="https://$domain"
|
||||||
|
|
||||||
|
echo "Configuration:"
|
||||||
|
echo " Enabled: $([ "$enabled" = "1" ] && echo -e "${GREEN}Yes${NC}" || echo -e "${RED}No${NC}")"
|
||||||
|
echo " Domain: $domain"
|
||||||
|
echo " Public URL: $public_url"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if ! command -v docker >/dev/null 2>&1; then
|
||||||
|
echo -e "Docker: ${RED}Not installed${NC}"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Containers:"
|
||||||
|
for container in jitsi-web jitsi-prosody jitsi-jicofo jitsi-jvb; do
|
||||||
|
local state=$(docker inspect -f '{{.State.Status}}' "$container" 2>/dev/null)
|
||||||
|
if [ "$state" = "running" ]; then
|
||||||
|
echo -e " $container: ${GREEN}Running${NC}"
|
||||||
|
elif [ -n "$state" ]; then
|
||||||
|
echo -e " $container: ${YELLOW}$state${NC}"
|
||||||
|
else
|
||||||
|
echo -e " $container: ${RED}Not found${NC}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Show ports
|
||||||
|
local jvb_port=$(uci -q get jitsi.jvb.port || echo "10000")
|
||||||
|
local web_port=$(uci -q get jitsi.web.port || echo "8443")
|
||||||
|
echo "Ports:"
|
||||||
|
echo " Web: $web_port/tcp"
|
||||||
|
echo " JVB Media: $jvb_port/udp"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Show active conferences (if available)
|
||||||
|
local stats=$(docker exec jitsi-jvb curl -s http://localhost:8080/colibri/stats 2>/dev/null)
|
||||||
|
if [ -n "$stats" ]; then
|
||||||
|
local conferences=$(echo "$stats" | jsonfilter -e '@.conferences' 2>/dev/null || echo "0")
|
||||||
|
local participants=$(echo "$stats" | jsonfilter -e '@.participants' 2>/dev/null || echo "0")
|
||||||
|
echo "Active:"
|
||||||
|
echo " Conferences: $conferences"
|
||||||
|
echo " Participants: $participants"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
show_logs() {
|
||||||
|
local service="$1"
|
||||||
|
local lines="${2:-50}"
|
||||||
|
|
||||||
|
cd "$JITSI_DIR"
|
||||||
|
|
||||||
|
case "$service" in
|
||||||
|
web|prosody|jicofo|jvb)
|
||||||
|
docker-compose logs --tail="$lines" "$service"
|
||||||
|
;;
|
||||||
|
all|"")
|
||||||
|
docker-compose logs --tail="$lines"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: jitsctl logs [web|prosody|jicofo|jvb|all] [lines]"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
shell() {
|
||||||
|
local container="${1:-jitsi-prosody}"
|
||||||
|
|
||||||
|
log "Connecting to $container..."
|
||||||
|
docker exec -it "$container" /bin/bash 2>/dev/null || docker exec -it "$container" /bin/sh
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Upgrade
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
upgrade() {
|
||||||
|
log "Upgrading Jitsi Meet..."
|
||||||
|
|
||||||
|
cd "$JITSI_DIR"
|
||||||
|
|
||||||
|
# Pull latest images
|
||||||
|
docker-compose pull
|
||||||
|
|
||||||
|
# Restart with new images
|
||||||
|
docker-compose down
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Cleanup old images
|
||||||
|
docker image prune -f
|
||||||
|
|
||||||
|
log "Jitsi Meet upgraded"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Backup / Restore
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
backup() {
|
||||||
|
local backup_file="${1:-/tmp/jitsi-backup-$(date +%Y%m%d-%H%M%S).tar.gz}"
|
||||||
|
|
||||||
|
log "Creating backup..."
|
||||||
|
|
||||||
|
tar -czf "$backup_file" \
|
||||||
|
-C / \
|
||||||
|
etc/config/jitsi \
|
||||||
|
"$CONFIG_DIR" \
|
||||||
|
"$ENV_FILE" \
|
||||||
|
2>/dev/null
|
||||||
|
|
||||||
|
if [ -f "$backup_file" ]; then
|
||||||
|
local size=$(ls -lh "$backup_file" | awk '{print $5}')
|
||||||
|
log "Backup created: $backup_file ($size)"
|
||||||
|
else
|
||||||
|
error "Backup failed"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
restore() {
|
||||||
|
local backup_file="$1"
|
||||||
|
|
||||||
|
if [ -z "$backup_file" ] || [ ! -f "$backup_file" ]; then
|
||||||
|
echo "Usage: jitsctl restore <backup_file>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Restoring from $backup_file..."
|
||||||
|
|
||||||
|
# Stop service
|
||||||
|
/etc/init.d/jitsi stop 2>/dev/null
|
||||||
|
|
||||||
|
# Extract backup
|
||||||
|
tar -xzf "$backup_file" -C /
|
||||||
|
|
||||||
|
# Restart
|
||||||
|
/etc/init.d/jitsi start
|
||||||
|
|
||||||
|
log "Restore complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Main
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
show_help() {
|
||||||
|
cat << EOF
|
||||||
|
Jitsi Meet Control v$VERSION
|
||||||
|
|
||||||
|
Usage: jitsctl <command> [options]
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
install Install Jitsi Meet Docker stack
|
||||||
|
generate-config Generate/update configuration from UCI
|
||||||
|
status Show service status
|
||||||
|
logs [svc] [n] Show container logs
|
||||||
|
shell [container] Access container shell
|
||||||
|
|
||||||
|
start Start all containers
|
||||||
|
stop Stop all containers
|
||||||
|
restart Restart all containers
|
||||||
|
upgrade Upgrade to latest images
|
||||||
|
|
||||||
|
add-user <u> <p> Add authenticated user
|
||||||
|
remove-user <u> Remove user
|
||||||
|
list-users List registered users
|
||||||
|
|
||||||
|
backup [file] Create configuration backup
|
||||||
|
restore <file> Restore from backup
|
||||||
|
|
||||||
|
configure-haproxy Add HAProxy vhost
|
||||||
|
configure-fw Configure firewall rules
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
jitsctl install
|
||||||
|
jitsctl status
|
||||||
|
jitsctl logs jvb 100
|
||||||
|
jitsctl add-user admin secretpassword
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
install)
|
||||||
|
install_jitsi
|
||||||
|
;;
|
||||||
|
generate-config)
|
||||||
|
generate_config
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
show_status
|
||||||
|
;;
|
||||||
|
logs)
|
||||||
|
show_logs "$2" "$3"
|
||||||
|
;;
|
||||||
|
shell)
|
||||||
|
shell "$2"
|
||||||
|
;;
|
||||||
|
start)
|
||||||
|
cd "$JITSI_DIR" && docker-compose up -d
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
cd "$JITSI_DIR" && docker-compose down
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
cd "$JITSI_DIR" && docker-compose restart
|
||||||
|
;;
|
||||||
|
upgrade)
|
||||||
|
upgrade
|
||||||
|
;;
|
||||||
|
add-user)
|
||||||
|
add_user "$2" "$3"
|
||||||
|
;;
|
||||||
|
remove-user)
|
||||||
|
remove_user "$2"
|
||||||
|
;;
|
||||||
|
list-users)
|
||||||
|
list_users
|
||||||
|
;;
|
||||||
|
backup)
|
||||||
|
backup "$2"
|
||||||
|
;;
|
||||||
|
restore)
|
||||||
|
restore "$2"
|
||||||
|
;;
|
||||||
|
configure-haproxy)
|
||||||
|
configure_haproxy
|
||||||
|
;;
|
||||||
|
configure-fw)
|
||||||
|
configure_firewall
|
||||||
|
;;
|
||||||
|
-h|--help|help)
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
show_help
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
exit 0
|
||||||
@ -0,0 +1,138 @@
|
|||||||
|
# Jitsi Meet Docker Compose for SecuBox
|
||||||
|
# Based on https://github.com/jitsi/docker-jitsi-meet
|
||||||
|
|
||||||
|
version: '3.5'
|
||||||
|
|
||||||
|
services:
|
||||||
|
# Frontend web server
|
||||||
|
web:
|
||||||
|
image: jitsi/web:${JITSI_IMAGE_VERSION:-stable}
|
||||||
|
container_name: jitsi-web
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '${HTTP_PORT:-8000}:80'
|
||||||
|
- '${HTTPS_PORT:-8443}:443'
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG}/web:/config:Z
|
||||||
|
- ${CONFIG}/transcripts:/usr/share/jitsi-meet/transcripts:Z
|
||||||
|
environment:
|
||||||
|
- ENABLE_AUTH
|
||||||
|
- ENABLE_GUESTS
|
||||||
|
- ENABLE_LOBBY
|
||||||
|
- ENABLE_PREJOIN_PAGE
|
||||||
|
- ENABLE_WELCOME_PAGE
|
||||||
|
- ENABLE_BREAKOUT_ROOMS
|
||||||
|
- ENABLE_REACTIONS
|
||||||
|
- ENABLE_NOISY_MIC_DETECTION
|
||||||
|
- ENABLE_TALK_WHILE_MUTED
|
||||||
|
- DEFAULT_LANGUAGE
|
||||||
|
- PUBLIC_URL
|
||||||
|
- TZ
|
||||||
|
- XMPP_AUTH_DOMAIN
|
||||||
|
- XMPP_BOSH_URL_BASE
|
||||||
|
- XMPP_DOMAIN
|
||||||
|
- XMPP_GUEST_DOMAIN
|
||||||
|
- XMPP_MUC_DOMAIN
|
||||||
|
networks:
|
||||||
|
meet.jitsi:
|
||||||
|
aliases:
|
||||||
|
- ${XMPP_DOMAIN}
|
||||||
|
|
||||||
|
# XMPP server
|
||||||
|
prosody:
|
||||||
|
image: jitsi/prosody:${JITSI_IMAGE_VERSION:-stable}
|
||||||
|
container_name: jitsi-prosody
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- '5222'
|
||||||
|
- '5347'
|
||||||
|
- '5280'
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG}/prosody/config:/config:Z
|
||||||
|
- ${CONFIG}/prosody/prosody-plugins-custom:/prosody-plugins-custom:Z
|
||||||
|
environment:
|
||||||
|
- AUTH_TYPE
|
||||||
|
- ENABLE_AUTH
|
||||||
|
- ENABLE_GUESTS
|
||||||
|
- ENABLE_LOBBY
|
||||||
|
- ENABLE_XMPP_WEBSOCKET
|
||||||
|
- JICOFO_AUTH_USER
|
||||||
|
- JICOFO_AUTH_PASSWORD
|
||||||
|
- JICOFO_COMPONENT_SECRET
|
||||||
|
- JVB_AUTH_USER
|
||||||
|
- JVB_AUTH_PASSWORD
|
||||||
|
- JWT_APP_ID
|
||||||
|
- JWT_APP_SECRET
|
||||||
|
- JWT_ACCEPTED_ISSUERS
|
||||||
|
- JWT_ACCEPTED_AUDIENCES
|
||||||
|
- PUBLIC_URL
|
||||||
|
- TZ
|
||||||
|
- XMPP_DOMAIN
|
||||||
|
- XMPP_AUTH_DOMAIN
|
||||||
|
- XMPP_GUEST_DOMAIN
|
||||||
|
- XMPP_MUC_DOMAIN
|
||||||
|
- XMPP_INTERNAL_MUC_DOMAIN
|
||||||
|
networks:
|
||||||
|
meet.jitsi:
|
||||||
|
aliases:
|
||||||
|
- ${XMPP_SERVER:-xmpp.meet.jitsi}
|
||||||
|
|
||||||
|
# Conference focus
|
||||||
|
jicofo:
|
||||||
|
image: jitsi/jicofo:${JITSI_IMAGE_VERSION:-stable}
|
||||||
|
container_name: jitsi-jicofo
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG}/jicofo:/config:Z
|
||||||
|
environment:
|
||||||
|
- AUTH_TYPE
|
||||||
|
- ENABLE_AUTH
|
||||||
|
- ENABLE_AUTO_OWNER
|
||||||
|
- JICOFO_AUTH_USER
|
||||||
|
- JICOFO_AUTH_PASSWORD
|
||||||
|
- JICOFO_ENABLE_BRIDGE_HEALTH_CHECKS
|
||||||
|
- JICOFO_ENABLE_HEALTH_CHECKS
|
||||||
|
- JVB_BREWERY_MUC
|
||||||
|
- TZ
|
||||||
|
- XMPP_DOMAIN
|
||||||
|
- XMPP_AUTH_DOMAIN
|
||||||
|
- XMPP_INTERNAL_MUC_DOMAIN
|
||||||
|
- XMPP_MUC_DOMAIN
|
||||||
|
- XMPP_SERVER
|
||||||
|
depends_on:
|
||||||
|
- prosody
|
||||||
|
networks:
|
||||||
|
meet.jitsi:
|
||||||
|
|
||||||
|
# Video bridge (SFU)
|
||||||
|
jvb:
|
||||||
|
image: jitsi/jvb:${JITSI_IMAGE_VERSION:-stable}
|
||||||
|
container_name: jitsi-jvb
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '${JVB_PORT:-10000}:${JVB_PORT:-10000}/udp'
|
||||||
|
- '127.0.0.1:8080:8080'
|
||||||
|
volumes:
|
||||||
|
- ${CONFIG}/jvb:/config:Z
|
||||||
|
environment:
|
||||||
|
- DOCKER_HOST_ADDRESS
|
||||||
|
- ENABLE_COLIBRI_WEBSOCKET
|
||||||
|
- JVB_AUTH_USER
|
||||||
|
- JVB_AUTH_PASSWORD
|
||||||
|
- JVB_BREWERY_MUC
|
||||||
|
- JVB_PORT
|
||||||
|
- JVB_STUN_SERVERS
|
||||||
|
- JVB_ENABLE_APIS
|
||||||
|
- PUBLIC_URL
|
||||||
|
- TZ
|
||||||
|
- XMPP_AUTH_DOMAIN
|
||||||
|
- XMPP_INTERNAL_MUC_DOMAIN
|
||||||
|
- XMPP_SERVER
|
||||||
|
depends_on:
|
||||||
|
- prosody
|
||||||
|
networks:
|
||||||
|
meet.jitsi:
|
||||||
|
|
||||||
|
networks:
|
||||||
|
meet.jitsi:
|
||||||
|
driver: bridge
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
# Jitsi Meet Environment Template
|
||||||
|
# This file is processed by jitsctl to generate .env
|
||||||
|
|
||||||
|
CONFIG=/srv/jitsi/.jitsi-meet-cfg
|
||||||
|
HTTP_PORT=8000
|
||||||
|
HTTPS_PORT=8443
|
||||||
|
TZ=UTC
|
||||||
|
|
||||||
|
PUBLIC_URL=https://meet.example.com
|
||||||
|
XMPP_DOMAIN=meet.example.com
|
||||||
|
XMPP_SERVER=xmpp.meet.example.com
|
||||||
|
XMPP_AUTH_DOMAIN=auth.meet.example.com
|
||||||
|
XMPP_GUEST_DOMAIN=guest.meet.example.com
|
||||||
|
XMPP_MUC_DOMAIN=muc.meet.example.com
|
||||||
|
XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.example.com
|
||||||
|
|
||||||
|
ENABLE_AUTH=0
|
||||||
|
ENABLE_GUESTS=1
|
||||||
|
AUTH_TYPE=internal
|
||||||
|
|
||||||
|
JICOFO_COMPONENT_SECRET=changeme
|
||||||
|
JICOFO_AUTH_USER=focus
|
||||||
|
JICOFO_AUTH_PASSWORD=changeme
|
||||||
|
JVB_AUTH_USER=jvb
|
||||||
|
JVB_AUTH_PASSWORD=changeme
|
||||||
|
JVB_BREWERY_MUC=jvbbrewery
|
||||||
|
|
||||||
|
JVB_PORT=10000
|
||||||
|
JVB_STUN_SERVERS=meet-jit-si-turnrelay.jitsi.net:443
|
||||||
|
|
||||||
|
ENABLE_LOBBY=1
|
||||||
|
ENABLE_BREAKOUT_ROOMS=1
|
||||||
|
ENABLE_PREJOIN_PAGE=1
|
||||||
|
ENABLE_WELCOME_PAGE=1
|
||||||
|
ENABLE_REACTIONS=1
|
||||||
|
|
||||||
|
DEFAULT_LANGUAGE=en
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
-- Prosody configuration template for SecuBox Jitsi
|
||||||
|
-- This is a reference template; actual config is generated by Docker
|
||||||
|
|
||||||
|
plugin_paths = { "/prosody-plugins/", "/prosody-plugins-custom" }
|
||||||
|
|
||||||
|
muc_mapper_domain_base = "XMPP_DOMAIN";
|
||||||
|
muc_mapper_domain_prefix = "muc";
|
||||||
|
|
||||||
|
http_default_host = "XMPP_DOMAIN"
|
||||||
|
|
||||||
|
consider_bosh_secure = true;
|
||||||
|
consider_websocket_secure = true;
|
||||||
|
|
||||||
|
cross_domain_bosh = true;
|
||||||
|
cross_domain_websocket = true;
|
||||||
|
|
||||||
|
VirtualHost "XMPP_DOMAIN"
|
||||||
|
authentication = "AUTH_TYPE"
|
||||||
|
ssl = {
|
||||||
|
key = "/config/certs/XMPP_DOMAIN.key";
|
||||||
|
certificate = "/config/certs/XMPP_DOMAIN.crt";
|
||||||
|
}
|
||||||
|
modules_enabled = {
|
||||||
|
"bosh";
|
||||||
|
"websocket";
|
||||||
|
"smacks";
|
||||||
|
"pubsub";
|
||||||
|
"ping";
|
||||||
|
"speakerstats";
|
||||||
|
"conference_duration";
|
||||||
|
"room_metadata";
|
||||||
|
"muc_lobby_rooms";
|
||||||
|
"muc_breakout_rooms";
|
||||||
|
"av_moderation";
|
||||||
|
"polls";
|
||||||
|
}
|
||||||
|
main_muc = "MUC_DOMAIN"
|
||||||
|
lobby_muc = "lobby.XMPP_DOMAIN"
|
||||||
|
breakout_rooms_muc = "breakout.XMPP_DOMAIN"
|
||||||
|
|
||||||
|
VirtualHost "GUEST_DOMAIN"
|
||||||
|
authentication = "anonymous"
|
||||||
|
|
||||||
|
Component "MUC_DOMAIN" "muc"
|
||||||
|
storage = "memory"
|
||||||
|
modules_enabled = {
|
||||||
|
"muc_meeting_id";
|
||||||
|
"muc_domain_mapper";
|
||||||
|
"polls";
|
||||||
|
}
|
||||||
|
muc_room_locking = false
|
||||||
|
muc_room_default_public_jids = true
|
||||||
|
|
||||||
|
Component "AUTH_DOMAIN" "speakerstats_component"
|
||||||
|
muc_component = "MUC_DOMAIN"
|
||||||
|
|
||||||
|
Component "focus.XMPP_DOMAIN" "client_proxy"
|
||||||
|
target_address = "focus@AUTH_DOMAIN"
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"id": "secubox-app-jitsi",
|
||||||
|
"name": "Jitsi Meet",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"category": "communication",
|
||||||
|
"description": "Self-hosted video conferencing with end-to-end encryption",
|
||||||
|
"long_description": "Jitsi Meet is a secure, fully featured, and completely free video conferencing solution. Host your own video meetings with end-to-end encryption, no account required for guests, screen sharing, chat, reactions, and more. Runs via Docker containers for easy deployment.",
|
||||||
|
"icon": "video",
|
||||||
|
"author": "CyberMind Studio",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"homepage": "https://jitsi.org",
|
||||||
|
"repository": "https://github.com/jitsi/jitsi-meet",
|
||||||
|
"packages": [
|
||||||
|
"secubox-app-jitsi",
|
||||||
|
"luci-app-jitsi"
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"docker",
|
||||||
|
"docker-compose"
|
||||||
|
],
|
||||||
|
"optional": [
|
||||||
|
"secubox-app-haproxy"
|
||||||
|
],
|
||||||
|
"ports": [
|
||||||
|
{"port": 8443, "protocol": "tcp", "description": "Web interface (HTTPS)"},
|
||||||
|
{"port": 10000, "protocol": "udp", "description": "Video/audio streams"},
|
||||||
|
{"port": 4443, "protocol": "tcp", "description": "TCP fallback"}
|
||||||
|
],
|
||||||
|
"resources": {
|
||||||
|
"ram_min": "2048",
|
||||||
|
"ram_recommended": "4096",
|
||||||
|
"disk": "500",
|
||||||
|
"cpu": "medium"
|
||||||
|
},
|
||||||
|
"features": [
|
||||||
|
"End-to-end encrypted video",
|
||||||
|
"No account required for guests",
|
||||||
|
"Screen sharing",
|
||||||
|
"Chat and reactions",
|
||||||
|
"Virtual backgrounds",
|
||||||
|
"Breakout rooms",
|
||||||
|
"Recording (optional)",
|
||||||
|
"Mobile app support",
|
||||||
|
"HAProxy integration",
|
||||||
|
"Mesh federation"
|
||||||
|
],
|
||||||
|
"tags": ["video", "conferencing", "webrtc", "communication", "meetings", "collaboration"],
|
||||||
|
"screenshots": [],
|
||||||
|
"documentation": "https://jitsi.github.io/handbook/docs/intro",
|
||||||
|
"config_file": "/etc/config/jitsi",
|
||||||
|
"service_name": "jitsi",
|
||||||
|
"web_interface": {
|
||||||
|
"port": 8443,
|
||||||
|
"path": "/",
|
||||||
|
"ssl": true
|
||||||
|
},
|
||||||
|
"haproxy_integration": true,
|
||||||
|
"mesh_service": true
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user