secubox-openwrt/package/secubox/secubox-app-jellyfin/files/usr/sbin/jellyfinctl
CyberMind-FR 2b8fb1cd62 feat(apps): Convert Docker-based apps to LXC
Converted secubox-app-jellyfin, secubox-app-mailserver, and added
secubox-app-roundcube to use LXC containers instead of Docker.

Changes:
- jellyfinctl: Now uses LXC at 192.168.255.31
- mailserverctl: New controller for Alpine LXC with Postfix/Dovecot
- roundcubectl: New package for Roundcube webmail LXC

All controllers support:
- Bootstrap Alpine rootfs using static apk
- LXC configuration generation
- HAProxy integration with waf_bypass
- Start/stop/status commands

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 09:07:33 +01:00

363 lines
9.8 KiB
Bash

#!/bin/sh
# SecuBox Jellyfin Media Server manager
# LXC-based deployment with HAProxy, Firewall, Mesh P2P integration
VERSION="3.0.0"
CONFIG="jellyfin"
CONTAINER="jellyfin"
LXC_PATH="/srv/lxc/jellyfin"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[JELLYFIN]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }
# ============================================================================
# Configuration Helpers
# ============================================================================
uci_get() { uci -q get ${CONFIG}.$1; }
uci_set() { uci -q set ${CONFIG}.$1="$2"; }
require_root() {
[ "$(id -u)" -eq 0 ] || { error "Root required"; exit 1; }
}
defaults() {
data_path="$(uci_get main.data_path)"
[ -z "$data_path" ] && data_path="/srv/jellyfin"
media_path="$(uci_get main.media_path)"
[ -z "$media_path" ] && media_path="/srv/SHARE"
port="$(uci_get main.port)"
[ -z "$port" ] && port="8096"
ip_address="$(uci_get main.ip_address)"
[ -z "$ip_address" ] && ip_address="192.168.255.31"
domain="$(uci_get network.domain)"
[ -z "$domain" ] && domain="jellyfin.secubox.local"
}
ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; }
# ============================================================================
# LXC Helpers
# ============================================================================
lxc_running() {
lxc-info -n "$CONTAINER" 2>/dev/null | grep -q "State:.*RUNNING"
}
lxc_exists() {
[ -d "$LXC_PATH/rootfs" ]
}
create_lxc_config() {
defaults
cat > "$LXC_PATH/config" << EOF
lxc.uts.name = jellyfin
lxc.rootfs.path = dir:${LXC_PATH}/rootfs
lxc.net.0.type = veth
lxc.net.0.link = br-lan
lxc.net.0.flags = up
lxc.net.0.ipv4.address = ${ip_address}/24
lxc.net.0.ipv4.gateway = 192.168.255.1
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
lxc.mount.entry = ${media_path} srv/SHARE none bind,ro 0 0
lxc.mount.entry = ${data_path}/config config none bind 0 0
lxc.mount.entry = ${data_path}/cache cache none bind 0 0
lxc.cap.drop = sys_module mac_admin mac_override sys_time
lxc.seccomp.profile =
lxc.tty.max = 0
lxc.pty.max = 256
lxc.cgroup2.memory.max = 2048000000
lxc.init.cmd = /opt/start-jellyfin.sh
EOF
}
create_startup_script() {
cat > "$LXC_PATH/rootfs/opt/start-jellyfin.sh" << 'EOF'
#!/bin/bash
export PATH=/usr/lib/jellyfin-ffmpeg:$PATH
export LD_LIBRARY_PATH=/usr/lib/jellyfin-ffmpeg/lib:$LD_LIBRARY_PATH
echo "Starting Jellyfin..."
exec /jellyfin/jellyfin \
--datadir=/config \
--cachedir=/cache \
--webdir=/jellyfin/jellyfin-web
EOF
chmod +x "$LXC_PATH/rootfs/opt/start-jellyfin.sh"
}
# ============================================================================
# HAProxy Integration
# ============================================================================
configure_haproxy() {
local haproxy_enabled=$(uci_get network.haproxy)
[ "$haproxy_enabled" != "1" ] && { log "HAProxy integration disabled in UCI"; return 0; }
if ! command -v haproxyctl >/dev/null 2>&1; then
warn "haproxyctl not found, skipping HAProxy configuration"
return 0
fi
defaults
# Check if backend already exists
if uci -q get haproxy.jellyfin_web >/dev/null 2>&1; then
log "HAProxy backend jellyfin_web already exists, updating..."
uci set haproxy.jellyfin_web.server="media ${ip_address}:${port} weight 100 check"
else
log "Creating HAProxy backend..."
uci set haproxy.jellyfin_web=backend
uci set haproxy.jellyfin_web.name='jellyfin_web'
uci set haproxy.jellyfin_web.mode='http'
uci set haproxy.jellyfin_web.balance='roundrobin'
uci set haproxy.jellyfin_web.enabled='1'
uci set haproxy.jellyfin_web.server="media ${ip_address}:${port} weight 100 check"
fi
# Check if vhost already exists
local vhost_name=$(echo "$domain" | tr '.' '_')
if ! uci -q get haproxy.${vhost_name} >/dev/null 2>&1; then
log "Creating HAProxy vhost for $domain..."
uci set haproxy.${vhost_name}=vhost
uci set haproxy.${vhost_name}.domain="$domain"
uci set haproxy.${vhost_name}.backend='jellyfin_web'
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}.waf_bypass='1'
uci set haproxy.${vhost_name}.enabled='1'
fi
uci commit haproxy
haproxyctl generate 2>/dev/null
haproxyctl reload 2>/dev/null
log "HAProxy configured for $domain -> ${ip_address}:${port}"
}
# ============================================================================
# Installation
# ============================================================================
cmd_install() {
require_root
log "Installing Jellyfin LXC container..."
defaults
ensure_dir "$LXC_PATH"
ensure_dir "$LXC_PATH/rootfs/srv/SHARE"
ensure_dir "$LXC_PATH/rootfs/config"
ensure_dir "$LXC_PATH/rootfs/cache"
ensure_dir "$LXC_PATH/rootfs/opt"
ensure_dir "$data_path/config"
ensure_dir "$data_path/cache"
if [ ! -f "$LXC_PATH/rootfs/jellyfin/jellyfin" ]; then
log "Jellyfin rootfs not found. Please extract from Docker image:"
echo " docker pull jellyfin/jellyfin:latest"
echo " docker create --name temp-jellyfin jellyfin/jellyfin:latest"
echo " docker export temp-jellyfin > /tmp/jellyfin.tar"
echo " tar -xf /tmp/jellyfin.tar -C $LXC_PATH/rootfs/"
echo " docker rm temp-jellyfin"
return 1
fi
create_lxc_config
create_startup_script
# Symlink ffmpeg if needed
if [ -d "$LXC_PATH/rootfs/usr/lib/jellyfin-ffmpeg" ]; then
ln -sf /usr/lib/jellyfin-ffmpeg/ffmpeg "$LXC_PATH/rootfs/usr/bin/ffmpeg" 2>/dev/null
ln -sf /usr/lib/jellyfin-ffmpeg/ffprobe "$LXC_PATH/rootfs/usr/bin/ffprobe" 2>/dev/null
fi
uci_set main.enabled '1'
uci commit ${CONFIG}
configure_haproxy
log "Jellyfin LXC installed successfully!"
log "Start with: jellyfinctl start"
}
# ============================================================================
# Service Control
# ============================================================================
cmd_start() {
require_root
if lxc_running; then
log "Jellyfin already running"
return 0
fi
if ! lxc_exists; then
error "Jellyfin not installed. Run 'jellyfinctl install' first"
return 1
fi
defaults
create_lxc_config
log "Starting Jellyfin LXC..."
lxc-start -n "$CONTAINER" -d
sleep 5
if lxc_running; then
log "Jellyfin started at http://${ip_address}:${port}"
else
error "Failed to start Jellyfin"
return 1
fi
}
cmd_stop() {
require_root
if ! lxc_running; then
log "Jellyfin is not running"
return 0
fi
log "Stopping Jellyfin..."
lxc-stop -n "$CONTAINER"
log "Jellyfin stopped"
}
cmd_restart() {
cmd_stop
sleep 2
cmd_start
}
# ============================================================================
# Status
# ============================================================================
cmd_status() {
defaults
echo ""
echo "========================================"
echo " Jellyfin Media Server v$VERSION (LXC)"
echo "========================================"
echo ""
local enabled=$(uci_get main.enabled)
echo "Configuration:"
echo " Enabled: $([ "$enabled" = "1" ] && echo -e "${GREEN}Yes${NC}" || echo -e "${RED}No${NC}")"
echo " IP Address: $ip_address"
echo " Port: $port"
echo " Data: $data_path"
echo " Media: $media_path"
echo " Domain: $domain"
echo ""
echo "Container:"
if lxc_running; then
echo -e " Status: ${GREEN}Running${NC}"
local pid=$(lxc-info -n "$CONTAINER" 2>/dev/null | grep PID | awk '{print $2}')
echo " PID: $pid"
# Health check
local health=$(curl -s "http://${ip_address}:${port}/health" 2>/dev/null)
if [ "$health" = "Healthy" ]; then
echo -e " Health: ${GREEN}Healthy${NC}"
else
echo -e " Health: ${YELLOW}Starting...${NC}"
fi
elif lxc_exists; then
echo -e " Status: ${YELLOW}Stopped${NC}"
else
echo -e " Status: ${RED}Not installed${NC}"
fi
echo ""
# Storage
if [ -d "$data_path" ]; then
local disk=$(du -sh "$data_path" 2>/dev/null | cut -f1)
echo "Storage:"
echo " Data size: ${disk:-unknown}"
fi
echo ""
}
# ============================================================================
# Logs & Shell
# ============================================================================
cmd_logs() {
defaults
if [ -f "$data_path/config/log/log_$(date +%Y%m%d).log" ]; then
tail "${@:--100}" "$data_path/config/log/log_$(date +%Y%m%d).log"
else
ls -la "$data_path/config/log/" 2>/dev/null || echo "No logs found"
fi
}
cmd_shell() {
if ! lxc_running; then
error "Container not running"
return 1
fi
lxc-attach -n "$CONTAINER" -- /bin/bash 2>/dev/null || lxc-attach -n "$CONTAINER" -- /bin/sh
}
# ============================================================================
# Main
# ============================================================================
show_help() {
cat << EOF
Jellyfin Media Server Control v$VERSION (LXC)
Usage: jellyfinctl <command> [options]
Commands:
install Install LXC container
start Start Jellyfin
stop Stop Jellyfin
restart Restart Jellyfin
status Show status
logs [-f] [--tail N] Show logs
shell Open shell inside container
configure-haproxy Configure HAProxy vhost
Examples:
jellyfinctl install
jellyfinctl start
jellyfinctl status
jellyfinctl logs -100
EOF
}
case "${1:-}" in
install) shift; cmd_install "$@" ;;
start) shift; cmd_start "$@" ;;
stop) shift; cmd_stop "$@" ;;
restart) shift; cmd_restart "$@" ;;
status) shift; cmd_status "$@" ;;
logs) shift; cmd_logs "$@" ;;
shell) shift; cmd_shell "$@" ;;
configure-haproxy) configure_haproxy ;;
service-run) cmd_start ;;
service-stop) cmd_stop ;;
help|--help|-h|'') show_help ;;
*) error "Unknown command: $1"; show_help >&2; exit 1 ;;
esac
exit 0