From dcc34c8bf675de1280cb2cc32301cb8fd74bae57 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Sun, 15 Feb 2026 05:43:22 +0100 Subject: [PATCH] feat(peertube): Add PeerTube video platform package New secubox-app-peertube package for self-hosted video streaming: - LXC Debian container with PostgreSQL, Redis, Node.js, FFmpeg - peertubectl control script with install/update/emancipate commands - UCI configuration for server, transcoding, live streaming, storage - procd init script with respawn support - HAProxy integration with WebSocket and extended timeouts - RTMP live streaming support (optional) - S3/object storage support (configurable) - Admin commands for user management - Backup/restore functionality Commands: peertubectl install - Create LXC container with full stack peertubectl emancipate - Full exposure with HAProxy + ACME peertubectl admin create-user - Create user accounts peertubectl live enable - Enable RTMP live streaming peertubectl backup/restore - Database backup Co-Authored-By: Claude Opus 4.5 --- package/secubox/secubox-app-peertube/Makefile | 45 + .../files/etc/config/peertube | 45 + .../files/etc/init.d/peertube | 35 + .../files/usr/sbin/peertubectl | 1083 +++++++++++++++++ 4 files changed, 1208 insertions(+) create mode 100644 package/secubox/secubox-app-peertube/Makefile create mode 100644 package/secubox/secubox-app-peertube/files/etc/config/peertube create mode 100644 package/secubox/secubox-app-peertube/files/etc/init.d/peertube create mode 100644 package/secubox/secubox-app-peertube/files/usr/sbin/peertubectl diff --git a/package/secubox/secubox-app-peertube/Makefile b/package/secubox/secubox-app-peertube/Makefile new file mode 100644 index 00000000..74b1679b --- /dev/null +++ b/package/secubox/secubox-app-peertube/Makefile @@ -0,0 +1,45 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=secubox-app-peertube +PKG_RELEASE:=1 +PKG_VERSION:=1.0.0 +PKG_ARCH:=all +PKG_MAINTAINER:=CyberMind Studio +PKG_LICENSE:=AGPL-3.0 + +include $(INCLUDE_DIR)/package.mk + +define Package/secubox-app-peertube + SECTION:=net + CATEGORY:=Network + PKGARCH:=all + SUBMENU:=SecuBox Apps + TITLE:=SecuBox PeerTube Video Platform + DEPENDS:=+lxc +lxc-common +wget-ssl +tar +jsonfilter +endef + +define Package/secubox-app-peertube/description +PeerTube federated video streaming platform. +Runs in an LXC Debian container with PostgreSQL, Redis, and Node.js. +Supports video hosting, live streaming, and ActivityPub federation. +endef + +define Package/secubox-app-peertube/conffiles +/etc/config/peertube +endef + +define Build/Compile +endef + +define Package/secubox-app-peertube/install + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./files/etc/config/peertube $(1)/etc/config/peertube + + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./files/etc/init.d/peertube $(1)/etc/init.d/peertube + + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) ./files/usr/sbin/peertubectl $(1)/usr/sbin/peertubectl +endef + +$(eval $(call BuildPackage,secubox-app-peertube)) diff --git a/package/secubox/secubox-app-peertube/files/etc/config/peertube b/package/secubox/secubox-app-peertube/files/etc/config/peertube new file mode 100644 index 00000000..5187000e --- /dev/null +++ b/package/secubox/secubox-app-peertube/files/etc/config/peertube @@ -0,0 +1,45 @@ +config peertube 'main' + option enabled '0' + option data_path '/srv/peertube' + option videos_path '/srv/peertube/videos' + option memory_limit '2048' + option timezone 'Europe/Paris' + +config peertube 'server' + option hostname 'peertube.local' + option port '9000' + option https '1' + option webserver_hostname '' + +config peertube 'live' + option enabled '0' + option rtmp_port '1935' + option max_duration '7200' + option allow_replay '1' + option transcoding_enabled '1' + +config peertube 'transcoding' + option enabled '1' + option threads '2' + option allow_audio_files '1' + option hls_enabled '1' + list resolutions '480p' + list resolutions '720p' + +config peertube 'storage' + option external_enabled '0' + option s3_endpoint '' + option s3_region '' + option s3_bucket '' + option s3_access_key '' + option s3_secret_key '' + +config peertube 'network' + option domain '' + option haproxy '0' + option haproxy_ssl '1' + option firewall_wan '0' + +config peertube 'admin' + option email 'admin@localhost' + option initial_password '' diff --git a/package/secubox/secubox-app-peertube/files/etc/init.d/peertube b/package/secubox/secubox-app-peertube/files/etc/init.d/peertube new file mode 100644 index 00000000..4f060254 --- /dev/null +++ b/package/secubox/secubox-app-peertube/files/etc/init.d/peertube @@ -0,0 +1,35 @@ +#!/bin/sh /etc/rc.common + +START=95 +STOP=10 +USE_PROCD=1 + +SERVICE_BIN="/usr/sbin/peertubectl" + +start_service() { + local enabled + config_load peertube + config_get enabled main enabled 0 + + [ "$enabled" = "1" ] || return 0 + + procd_open_instance + procd_set_param command "$SERVICE_BIN" service-run + procd_set_param respawn 3600 5 5 + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_close_instance +} + +stop_service() { + "$SERVICE_BIN" service-stop >/dev/null 2>&1 +} + +reload_service() { + stop + start +} + +service_triggers() { + procd_add_reload_trigger "peertube" +} diff --git a/package/secubox/secubox-app-peertube/files/usr/sbin/peertubectl b/package/secubox/secubox-app-peertube/files/usr/sbin/peertubectl new file mode 100644 index 00000000..b7c020cf --- /dev/null +++ b/package/secubox/secubox-app-peertube/files/usr/sbin/peertubectl @@ -0,0 +1,1083 @@ +#!/bin/sh +# SecuBox PeerTube Manager - LXC Debian container with PostgreSQL/Redis/Node.js + +CONFIG="peertube" +LXC_NAME="peertube" +LXC_PATH="/srv/lxc" +LXC_ROOTFS="$LXC_PATH/$LXC_NAME/rootfs" +LXC_CONF="$LXC_PATH/$LXC_NAME/config" +DATA_PATH_DEFAULT="/srv/peertube" +PEERTUBE_VERSION="6.3.2" +OPKG_UPDATED=0 + +usage() { + cat <<'USAGE' +Usage: peertubectl + +Installation: + install Create LXC container with PeerTube stack + uninstall Remove container (preserves data) + update Update PeerTube to latest version + check Run prerequisite checks + +Service: + start Start PeerTube (via init) + stop Stop PeerTube + restart Restart PeerTube + status Show container and service status + logs [N] Show last N lines of logs (default: 50) + shell Open interactive shell in container + +Administration: + admin create-user --username --email [--password

] + admin reset-password --username + admin list-users List all users + +Live Streaming: + live enable Enable RTMP live streaming + live disable Disable RTMP live streaming + live status Show live streaming status + +Exposure: + configure-haproxy Setup HAProxy vhost with WebSocket support + emancipate Full exposure (HAProxy + ACME + firewall) + +Backup: + backup [path] Backup database and config + restore Restore from backup + +Internal: + service-run Run container via procd + service-stop Stop container +USAGE +} + +# ---------- helpers ---------- + +require_root() { [ "$(id -u)" -eq 0 ]; } + +uci_get() { + local key="$1" + local section="${2:-main}" + uci -q get ${CONFIG}.${section}.$key +} + +uci_set() { + local key="$1" + local value="$2" + local section="${3:-main}" + uci set ${CONFIG}.${section}.$key="$value" +} + +log_info() { echo "[INFO] $*"; logger -t peertubectl "$*"; } +log_warn() { echo "[WARN] $*"; logger -t peertubectl -p warning "$*"; } +log_error() { echo "[ERROR] $*" >&2; logger -t peertubectl -p err "$*"; } + +ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; } + +ensure_packages() { + for pkg in "$@"; do + if ! opkg status "$pkg" 2>/dev/null | grep -q "Status:.*installed"; then + if [ "$OPKG_UPDATED" -eq 0 ]; then + opkg update || return 1 + OPKG_UPDATED=1 + fi + opkg install "$pkg" || return 1 + fi + done +} + +defaults() { + data_path="$(uci_get data_path || echo $DATA_PATH_DEFAULT)" + videos_path="$(uci_get videos_path || echo $DATA_PATH_DEFAULT/videos)" + memory_limit="$(uci_get memory_limit || echo 2048)" + port="$(uci_get port server || echo 9000)" + hostname="$(uci_get hostname server || echo peertube.local)" + timezone="$(uci_get timezone || echo Europe/Paris)" +} + +detect_arch() { + case "$(uname -m)" in + aarch64) echo "aarch64" ;; + armv7l) echo "armv7" ;; + x86_64) echo "x86_64" ;; + *) echo "x86_64" ;; + esac +} + +generate_password() { + head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24 +} + +# ---------- LXC helpers ---------- + +lxc_running() { + lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING" +} + +lxc_exists() { + [ -f "$LXC_CONF" ] && [ -d "$LXC_ROOTFS" ] +} + +lxc_exec() { + lxc-attach -n "$LXC_NAME" -- "$@" +} + +lxc_stop() { + if lxc_running; then + lxc-stop -n "$LXC_NAME" -k 2>/dev/null || true + sleep 2 + fi +} + +# ---------- rootfs creation ---------- + +lxc_create_rootfs() { + local arch=$(detect_arch) + + # Map to Debian architecture names + local debian_arch + case "$arch" in + aarch64) debian_arch="arm64" ;; + armv7) debian_arch="armhf" ;; + x86_64) debian_arch="amd64" ;; + *) debian_arch="amd64" ;; + esac + + ensure_dir "$LXC_ROOTFS" + + # Minimal Debian rootfs via tarball from LXC image server + local rootfs_url="https://images.linuxcontainers.org/images/debian/bookworm/${debian_arch}/default/" + log_info "Downloading Debian bookworm rootfs for ${debian_arch}..." + + # Get latest build directory + local latest_path + latest_path=$(wget -q -O - "$rootfs_url" 2>/dev/null | grep -oE '[0-9]{8}_[0-9]{2}:[0-9]{2}' | tail -1) + if [ -z "$latest_path" ]; then + log_error "Failed to find latest Debian rootfs build" + return 1 + fi + + local tarball="/tmp/debian-peertube.tar.xz" + local tarball_url="${rootfs_url}${latest_path}/rootfs.tar.xz" + wget -q -O "$tarball" "$tarball_url" || { + log_error "Failed to download Debian rootfs from $tarball_url" + return 1 + } + + tar -xJf "$tarball" -C "$LXC_ROOTFS" || { + log_error "Failed to extract Debian rootfs" + return 1 + } + rm -f "$tarball" + + # DNS + cp /etc/resolv.conf "$LXC_ROOTFS/etc/resolv.conf" 2>/dev/null || \ + echo "nameserver 8.8.8.8" > "$LXC_ROOTFS/etc/resolv.conf" + + # Create minimal /dev for chroot operations + mkdir -p "$LXC_ROOTFS/dev" + [ -c "$LXC_ROOTFS/dev/null" ] || mknod -m 666 "$LXC_ROOTFS/dev/null" c 1 3 2>/dev/null + [ -c "$LXC_ROOTFS/dev/zero" ] || mknod -m 666 "$LXC_ROOTFS/dev/zero" c 1 5 2>/dev/null + [ -c "$LXC_ROOTFS/dev/random" ] || mknod -m 666 "$LXC_ROOTFS/dev/random" c 1 8 2>/dev/null + [ -c "$LXC_ROOTFS/dev/urandom" ] || mknod -m 666 "$LXC_ROOTFS/dev/urandom" c 1 9 2>/dev/null + + # Configure apt sources + cat > "$LXC_ROOTFS/etc/apt/sources.list" <<'SOURCES' +deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware +deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware +deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware +SOURCES + + # Install PeerTube stack + log_info "Installing PeerTube dependencies (this takes a while)..." + chroot "$LXC_ROOTFS" /bin/sh -c " + export DEBIAN_FRONTEND=noninteractive + apt-get update && \ + apt-get install -y --no-install-recommends \ + curl wget ca-certificates gnupg \ + postgresql postgresql-contrib \ + redis-server \ + ffmpeg \ + python3 \ + g++ make \ + openssl \ + procps + " || { + log_error "Failed to install base dependencies" + return 1 + } + + # Add NodeSource repository for Node.js 18 LTS + log_info "Installing Node.js 18 LTS..." + chroot "$LXC_ROOTFS" /bin/sh -c " + export DEBIAN_FRONTEND=noninteractive + curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \ + apt-get install -y nodejs && \ + npm install -g yarn + " || { + log_error "Failed to install Node.js" + return 1 + } + + # Create peertube user + chroot "$LXC_ROOTFS" /bin/sh -c " + useradd -r -s /bin/bash -d /opt/peertube -m peertube + " + + # Download and install PeerTube + log_info "Downloading PeerTube v${PEERTUBE_VERSION}..." + local pt_url="https://github.com/Chocobozzz/PeerTube/releases/download/v${PEERTUBE_VERSION}/peertube-v${PEERTUBE_VERSION}.zip" + wget -q -O "/tmp/peertube.zip" "$pt_url" || { + log_error "Failed to download PeerTube" + return 1 + } + + chroot "$LXC_ROOTFS" /bin/sh -c " + apt-get install -y --no-install-recommends unzip + cd /opt/peertube + unzip /tmp/peertube.zip + mv peertube-v${PEERTUBE_VERSION} peertube-latest + cd peertube-latest + yarn install --production --pure-lockfile + chown -R peertube:peertube /opt/peertube + " || { + log_error "Failed to install PeerTube" + return 1 + } + rm -f /tmp/peertube.zip + + # Create directories + mkdir -p "$LXC_ROOTFS/opt/peertube/config" + mkdir -p "$LXC_ROOTFS/opt/peertube/storage" + + # Create startup script + create_startup_script + + # Clean up apt cache + chroot "$LXC_ROOTFS" /bin/sh -c " + apt-get clean + rm -rf /var/lib/apt/lists/* + " + + log_info "Rootfs created successfully" +} + +create_startup_script() { + cat > "$LXC_ROOTFS/opt/start-peertube.sh" <<'STARTUP' +#!/bin/bash +set -e + +export PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + +# Start PostgreSQL +echo "[PEERTUBE] Starting PostgreSQL..." +service postgresql start +sleep 3 + +# Start Redis +echo "[PEERTUBE] Starting Redis..." +service redis-server start +sleep 2 + +# Initialize database on first run +if [ ! -f /opt/peertube/.initialized ]; then + echo "[PEERTUBE] First run - initializing database..." + + # Generate database password + DB_PASSWORD=$(head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24) + + # Create database and user + su - postgres -c "psql -c \"CREATE USER peertube WITH PASSWORD '${DB_PASSWORD}';\"" + su - postgres -c "psql -c \"CREATE DATABASE peertube OWNER peertube;\"" + su - postgres -c "psql -c \"GRANT ALL PRIVILEGES ON DATABASE peertube TO peertube;\"" + + # Create extensions + su - postgres -c "psql -d peertube -c 'CREATE EXTENSION IF NOT EXISTS pg_trgm;'" + su - postgres -c "psql -d peertube -c 'CREATE EXTENSION IF NOT EXISTS unaccent;'" + + # Save password for config generation + echo "$DB_PASSWORD" > /opt/peertube/.db_password + chmod 600 /opt/peertube/.db_password + + touch /opt/peertube/.initialized + echo "[PEERTUBE] Database initialized" +fi + +# Generate PeerTube config if not exists +if [ ! -f /opt/peertube/config/production.yaml ]; then + echo "[PEERTUBE] Generating production config..." + + DB_PASSWORD=$(cat /opt/peertube/.db_password 2>/dev/null || echo "peertube") + SECRET=$(head -c 64 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 32) + + cat > /opt/peertube/config/production.yaml < "$LXC_CONF" </dev/null + /etc/init.d/peertube disable 2>/dev/null + lxc_stop + + # Remove container but keep data + rm -rf "$LXC_ROOTFS" "$LXC_CONF" + + uci_set enabled '0' + uci commit "$CONFIG" + + defaults + log_info "Container removed. Data preserved in $data_path" +} + +cmd_update() { + require_root || { log_error "Must run as root"; return 1; } + + log_info "Updating PeerTube..." + lxc_stop + + # Get current and new version + local new_version + new_version=$(wget -q -O - "https://api.github.com/repos/Chocobozzz/PeerTube/releases/latest" 2>/dev/null | \ + jsonfilter -e '@.tag_name' | sed 's/^v//') + + if [ -z "$new_version" ]; then + log_error "Failed to get latest version" + return 1 + fi + + log_info "Updating to version $new_version..." + + # Download new version + local pt_url="https://github.com/Chocobozzz/PeerTube/releases/download/v${new_version}/peertube-v${new_version}.zip" + wget -q -O "/tmp/peertube.zip" "$pt_url" || { + log_error "Failed to download PeerTube" + return 1 + } + + # Extract and install + chroot "$LXC_ROOTFS" /bin/sh -c " + cd /opt/peertube + unzip -o /tmp/peertube.zip + mv peertube-v${new_version} peertube-latest-new + cd peertube-latest-new + yarn install --production --pure-lockfile + cd .. + rm -rf peertube-latest-old + mv peertube-latest peertube-latest-old + mv peertube-latest-new peertube-latest + chown -R peertube:peertube /opt/peertube + " + rm -f /tmp/peertube.zip + + /etc/init.d/peertube start + log_info "Update complete (version $new_version)" +} + +cmd_check() { + echo "PeerTube Prerequisites Check" + echo "=============================" + + # LXC + if command -v lxc-start >/dev/null 2>&1; then + echo "[OK] LXC installed" + else + echo "[FAIL] LXC not installed" + fi + + # Container exists + if lxc_exists; then + echo "[OK] Container exists" + else + echo "[--] Container not created" + fi + + # Container running + if lxc_running; then + echo "[OK] Container running" + else + echo "[--] Container not running" + fi + + # PeerTube port + defaults + if netstat -tln 2>/dev/null | grep -q ":${port} " || \ + grep -q ":$(printf '%04X' $port) " /proc/net/tcp 2>/dev/null; then + echo "[OK] PeerTube listening on port $port" + else + echo "[--] PeerTube not listening" + fi + + # Services in container + if lxc_running; then + if lxc_exec pgrep postgres >/dev/null 2>&1; then + echo "[OK] PostgreSQL running" + else + echo "[FAIL] PostgreSQL not running" + fi + + if lxc_exec pgrep redis-server >/dev/null 2>&1; then + echo "[OK] Redis running" + else + echo "[FAIL] Redis not running" + fi + + if lxc_exec pgrep -f "node dist/server" >/dev/null 2>&1; then + echo "[OK] PeerTube process running" + else + echo "[FAIL] PeerTube process not running" + fi + fi +} + +cmd_status() { + defaults + + # JSON output for RPCD + if [ "$1" = "--json" ]; then + local running=0 + local postgres=0 + local redis=0 + local peertube_proc=0 + + lxc_running && running=1 + if [ "$running" = "1" ]; then + lxc_exec pgrep postgres >/dev/null 2>&1 && postgres=1 + lxc_exec pgrep redis-server >/dev/null 2>&1 && redis=1 + lxc_exec pgrep -f "node dist/server" >/dev/null 2>&1 && peertube_proc=1 + fi + + cat </dev/null 2>&1 && echo " PostgreSQL: UP" || echo " PostgreSQL: DOWN" + lxc_exec pgrep redis-server >/dev/null 2>&1 && echo " Redis: UP" || echo " Redis: DOWN" + lxc_exec pgrep -f "node dist/server" >/dev/null 2>&1 && echo " PeerTube: UP" || echo " PeerTube: DOWN" + else + echo "State: STOPPED" + fi + + echo "" + local lan_ip=$(uci -q get network.lan.ipaddr || echo '192.168.255.1') + echo "Access URL: http://${lan_ip}:${port}" +} + +cmd_logs() { + local lines="${1:-50}" + + if lxc_running; then + echo "=== PeerTube logs ===" + lxc_exec tail -n "$lines" /opt/peertube/storage/logs/peertube.log 2>/dev/null || \ + echo "No PeerTube logs found" + else + echo "Container not running" + fi +} + +cmd_shell() { + if lxc_running; then + lxc_exec /bin/bash || lxc_exec /bin/sh + else + log_error "Container not running" + return 1 + fi +} + +cmd_start() { + require_root || { log_error "Must run as root"; return 1; } + /etc/init.d/peertube start +} + +cmd_stop() { + require_root || { log_error "Must run as root"; return 1; } + /etc/init.d/peertube stop +} + +cmd_restart() { + require_root || { log_error "Must run as root"; return 1; } + /etc/init.d/peertube restart +} + +# ---------- admin commands ---------- + +cmd_admin() { + local subcmd="$1" + shift + + case "$subcmd" in + create-user) + cmd_admin_create_user "$@" + ;; + reset-password) + cmd_admin_reset_password "$@" + ;; + list-users) + cmd_admin_list_users + ;; + *) + echo "Usage: peertubectl admin " + return 1 + ;; + esac +} + +cmd_admin_create_user() { + local username="" email="" password="" admin="" + + while [ $# -gt 0 ]; do + case "$1" in + --username) username="$2"; shift 2 ;; + --email) email="$2"; shift 2 ;; + --password) password="$2"; shift 2 ;; + --admin) admin="--role 0"; shift ;; + *) shift ;; + esac + done + + [ -z "$username" ] || [ -z "$email" ] && { + echo "Usage: peertubectl admin create-user --username --email [--password

] [--admin]" + return 1 + } + + [ -z "$password" ] && password=$(generate_password) + + lxc_running || { log_error "Container not running"; return 1; } + + lxc_exec su - peertube -c "cd /opt/peertube/peertube-latest && \ + NODE_ENV=production NODE_CONFIG_DIR=/opt/peertube/config \ + npm run create-user -- \ + --url https://localhost \ + --username '$username' \ + --email '$email' \ + --password '$password' \ + $admin" + + log_info "User created: $username" + log_info "Password: $password" +} + +cmd_admin_reset_password() { + local username="" + + while [ $# -gt 0 ]; do + case "$1" in + --username) username="$2"; shift 2 ;; + *) shift ;; + esac + done + + [ -z "$username" ] && { + echo "Usage: peertubectl admin reset-password --username " + return 1 + } + + local password=$(generate_password) + + lxc_running || { log_error "Container not running"; return 1; } + + lxc_exec su - peertube -c "cd /opt/peertube/peertube-latest && \ + NODE_ENV=production NODE_CONFIG_DIR=/opt/peertube/config \ + npm run reset-password -- \ + --username '$username'" + + log_info "Password reset initiated for: $username" +} + +cmd_admin_list_users() { + lxc_running || { log_error "Container not running"; return 1; } + + lxc_exec su - postgres -c "psql -d peertube -c 'SELECT id, username, email, \"createdAt\" FROM \"user\" ORDER BY id;'" +} + +# ---------- live streaming ---------- + +cmd_live() { + local subcmd="$1" + + case "$subcmd" in + enable) + uci_set enabled '1' live + uci commit "$CONFIG" + # Update PeerTube config + if lxc_running; then + lxc_exec sed -i 's/enabled: false/enabled: true/' /opt/peertube/config/production.yaml + # Open RTMP port + local rtmp_port=$(uci_get rtmp_port live || echo 1935) + uci add firewall rule + uci set firewall.@rule[-1].name='PeerTube-RTMP' + uci set firewall.@rule[-1].src='wan' + uci set firewall.@rule[-1].dest_port="$rtmp_port" + uci set firewall.@rule[-1].proto='tcp' + uci set firewall.@rule[-1].target='ACCEPT' + uci commit firewall + /etc/init.d/firewall reload + fi + log_info "Live streaming enabled (RTMP port: $rtmp_port)" + ;; + disable) + uci_set enabled '0' live + uci commit "$CONFIG" + if lxc_running; then + lxc_exec sed -i 's/enabled: true/enabled: false/' /opt/peertube/config/production.yaml + fi + log_info "Live streaming disabled" + ;; + status) + local enabled=$(uci_get enabled live || echo 0) + local rtmp_port=$(uci_get rtmp_port live || echo 1935) + echo "Live Streaming: $([ "$enabled" = "1" ] && echo "ENABLED" || echo "DISABLED")" + echo "RTMP Port: $rtmp_port" + ;; + *) + echo "Usage: peertubectl live " + return 1 + ;; + esac +} + +# ---------- HAProxy integration ---------- + +cmd_configure_haproxy() { + require_root || { log_error "Must run as root"; return 1; } + defaults + + local domain=$(uci_get domain network) + [ -z "$domain" ] && domain="$hostname" + + # Create backend with extended timeouts for streaming/WebSocket + local backend_name="peertube_web" + + uci set haproxy.${backend_name}=backend + uci set haproxy.${backend_name}.name="$backend_name" + uci set haproxy.${backend_name}.mode='http' + uci set haproxy.${backend_name}.balance='roundrobin' + uci set haproxy.${backend_name}.enabled='1' + uci set haproxy.${backend_name}.timeout_server='3600s' + uci set haproxy.${backend_name}.timeout_tunnel='3600s' + uci set haproxy.${backend_name}.server="peertube 127.0.0.1:${port} check" + + # Create vhost + local vhost_name=$(echo "$domain" | tr '.-' '_') + uci set haproxy.${vhost_name}=vhost + uci set haproxy.${vhost_name}.domain="$domain" + uci set haproxy.${vhost_name}.backend="$backend_name" + uci set haproxy.${vhost_name}.ssl='1' + uci set haproxy.${vhost_name}.ssl_redirect='1' + uci set haproxy.${vhost_name}.acme='1' + uci set haproxy.${vhost_name}.enabled='1' + + uci commit haproxy + + # Update network config + uci_set haproxy '1' network + uci_set domain "$domain" network + uci commit "$CONFIG" + + # Regenerate and reload + if command -v haproxyctl >/dev/null 2>&1; then + haproxyctl generate + /etc/init.d/haproxy reload + fi + + log_info "HAProxy configured for $domain" + log_info "SSL certificate will be requested via ACME" +} + +cmd_emancipate() { + local domain="$1" + + [ -z "$domain" ] && { + echo "Usage: peertubectl emancipate " + return 1 + } + + require_root || { log_error "Must run as root"; return 1; } + + log_info "Emancipating PeerTube at $domain..." + + # Update hostname + uci_set hostname "$domain" server + uci_set domain "$domain" network + uci commit "$CONFIG" + + # Update PeerTube config + if lxc_running; then + lxc_exec sed -i "s/hostname: '.*'/hostname: '$domain'/" /opt/peertube/config/production.yaml + fi + + # Configure HAProxy + cmd_configure_haproxy + + # Sync mitmproxy routes + if command -v mitmproxyctl >/dev/null 2>&1; then + mitmproxyctl sync-routes 2>/dev/null + fi + + # Open firewall if needed + local wan_open=$(uci_get firewall_wan network) + if [ "$wan_open" = "1" ]; then + uci add firewall rule + uci set firewall.@rule[-1].name='PeerTube-HTTPS' + uci set firewall.@rule[-1].src='wan' + uci set firewall.@rule[-1].dest_port='443' + uci set firewall.@rule[-1].proto='tcp' + uci set firewall.@rule[-1].target='ACCEPT' + uci commit firewall + /etc/init.d/firewall reload + fi + + log_info "" + log_info "==============================================" + log_info " PeerTube Emancipated!" + log_info "==============================================" + log_info "" + log_info " URL: https://$domain" + log_info " SSL: ACME certificate requested" + log_info "" + log_info " Verify: curl -v https://$domain" + log_info "" +} + +# ---------- backup/restore ---------- + +cmd_backup() { + local backup_path="${1:-/srv/peertube/backup}" + + require_root || { log_error "Must run as root"; return 1; } + lxc_running || { log_error "Container must be running"; return 1; } + + ensure_dir "$backup_path" + + local timestamp=$(date +%Y%m%d_%H%M%S) + local backup_file="$backup_path/peertube_${timestamp}.tar.gz" + + log_info "Creating backup..." + + # Dump PostgreSQL + lxc_exec su - postgres -c "pg_dump peertube" > "$backup_path/peertube_db_${timestamp}.sql" + + # Create tarball with config and database dump + defaults + tar -czf "$backup_file" \ + -C "$data_path" config \ + -C "$backup_path" "peertube_db_${timestamp}.sql" + + rm -f "$backup_path/peertube_db_${timestamp}.sql" + + log_info "Backup created: $backup_file" +} + +cmd_restore() { + local backup_file="$1" + + [ -z "$backup_file" ] || [ ! -f "$backup_file" ] && { + echo "Usage: peertubectl restore " + return 1 + } + + require_root || { log_error "Must run as root"; return 1; } + lxc_running || { log_error "Container must be running"; return 1; } + + log_info "Restoring from $backup_file..." + + local tmp_dir="/tmp/peertube_restore_$$" + mkdir -p "$tmp_dir" + + tar -xzf "$backup_file" -C "$tmp_dir" + + # Restore config + defaults + cp -a "$tmp_dir/config/"* "$data_path/config/" + + # Restore database + local sql_file=$(ls "$tmp_dir"/peertube_db_*.sql 2>/dev/null | head -1) + if [ -n "$sql_file" ]; then + lxc_exec su - postgres -c "dropdb --if-exists peertube" + lxc_exec su - postgres -c "createdb -O peertube peertube" + cat "$sql_file" | lxc_exec su - postgres -c "psql peertube" + fi + + rm -rf "$tmp_dir" + + log_info "Restore complete. Restart PeerTube to apply changes." +} + +# ---------- service management ---------- + +cmd_service_run() { + require_root || exit 1 + defaults + + # Verify container exists + lxc_exists || { log_error "Container not found. Run: peertubectl install"; exit 1; } + + log_info "Starting PeerTube container..." + + # Start container in foreground + exec lxc-start -n "$LXC_NAME" -F -f "$LXC_CONF" +} + +cmd_service_stop() { + log_info "Stopping PeerTube container..." + lxc_stop +} + +# ---------- main ---------- + +case "$1" in + install) cmd_install ;; + uninstall) cmd_uninstall ;; + update) cmd_update ;; + check) cmd_check ;; + start) cmd_start ;; + stop) cmd_stop ;; + restart) cmd_restart ;; + status) shift; cmd_status "$@" ;; + logs) shift; cmd_logs "$@" ;; + shell) cmd_shell ;; + admin) shift; cmd_admin "$@" ;; + live) shift; cmd_live "$@" ;; + configure-haproxy) cmd_configure_haproxy ;; + emancipate) shift; cmd_emancipate "$@" ;; + backup) shift; cmd_backup "$@" ;; + restore) shift; cmd_restore "$@" ;; + service-run) cmd_service_run ;; + service-stop) cmd_service_stop ;; + *) usage; exit 1 ;; +esac