- Update Nextcloud version to 31.0.5 - Add auto-start (lxc.start.auto) for boot persistence - Add memory limit cgroup configuration - Fix nginx /apps/ path for static assets (CSS, JS, SVG) - Add Storage tab with disk usage visualization - Add delete backup functionality - Add RPCD methods: uninstall, get_storage, delete_backup - Update ACL permissions for new methods - Rewrite README.md with LXC architecture docs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
503 lines
14 KiB
Bash
Executable File
503 lines
14 KiB
Bash
Executable File
#!/bin/sh
|
|
# RPCD backend for Nextcloud LuCI app (LXC version)
|
|
# Copyright (C) 2025 CyberMind.fr
|
|
|
|
CONFIG="nextcloud"
|
|
LXC_NAME="nextcloud"
|
|
LXC_ROOTFS="/srv/lxc/nextcloud/rootfs"
|
|
|
|
uci_get() { uci -q get ${CONFIG}.$1; }
|
|
uci_set() { uci set ${CONFIG}.$1="$2" && uci commit ${CONFIG}; }
|
|
|
|
# Check if LXC container is running
|
|
lxc_running() {
|
|
lxc-info -n "$LXC_NAME" -s 2>/dev/null | grep -q "RUNNING"
|
|
}
|
|
|
|
# Check if container is installed
|
|
lxc_installed() {
|
|
[ -d "$LXC_ROOTFS" ] && [ -f "/srv/lxc/$LXC_NAME/config" ]
|
|
}
|
|
|
|
# Get service status
|
|
get_status() {
|
|
local enabled=$(uci_get main.enabled)
|
|
local http_port=$(uci_get main.http_port)
|
|
local data_path=$(uci_get main.data_path)
|
|
local domain=$(uci_get main.domain)
|
|
local ssl_enabled=$(uci_get ssl.enabled)
|
|
local ssl_domain=$(uci_get ssl.domain)
|
|
|
|
# Container status
|
|
local running=0
|
|
local installed=0
|
|
local version=""
|
|
local user_count=0
|
|
local disk_used="0"
|
|
|
|
lxc_installed && installed=1
|
|
lxc_running && running=1
|
|
|
|
# Get Nextcloud info if running
|
|
if [ "$running" = "1" ]; then
|
|
version=$(lxc-attach -n "$LXC_NAME" -- su -s /bin/bash www-data -c "php /var/www/nextcloud/occ -V" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "")
|
|
user_count=$(lxc-attach -n "$LXC_NAME" -- su -s /bin/bash www-data -c "php /var/www/nextcloud/occ user:list --output=json" 2>/dev/null | jsonfilter -e '@[*]' 2>/dev/null | wc -l || echo 0)
|
|
fi
|
|
|
|
# Get disk usage
|
|
if [ -d "${data_path:-/srv/nextcloud}/data" ]; then
|
|
disk_used=$(du -sh "${data_path:-/srv/nextcloud}/data" 2>/dev/null | awk '{print $1}' || echo "0")
|
|
fi
|
|
|
|
# Get LAN IP
|
|
local lan_ip
|
|
lan_ip=$(uci -q get network.lan.ipaddr || echo "192.168.255.1")
|
|
|
|
# Check web accessibility
|
|
local web_accessible=0
|
|
if [ "$running" = "1" ]; then
|
|
wget -q -O /dev/null --timeout=2 "http://127.0.0.1:${http_port:-8080}/status.php" 2>/dev/null && web_accessible=1
|
|
fi
|
|
|
|
cat <<EOF
|
|
{
|
|
"enabled": $([ "$enabled" = "1" ] && echo "true" || echo "false"),
|
|
"running": $([ "$running" = "1" ] && echo "true" || echo "false"),
|
|
"installed": $([ "$installed" = "1" ] && echo "true" || echo "false"),
|
|
"version": "$version",
|
|
"http_port": ${http_port:-8080},
|
|
"data_path": "${data_path:-/srv/nextcloud}",
|
|
"domain": "${domain:-cloud.local}",
|
|
"user_count": $user_count,
|
|
"disk_used": "$disk_used",
|
|
"web_url": "http://${lan_ip}:${http_port:-8080}",
|
|
"web_accessible": $([ "$web_accessible" = "1" ] && echo "true" || echo "false"),
|
|
"ssl_enabled": $([ "$ssl_enabled" = "1" ] && echo "true" || echo "false"),
|
|
"ssl_domain": "${ssl_domain:-}",
|
|
"container_name": "$LXC_NAME"
|
|
}
|
|
EOF
|
|
}
|
|
|
|
# Get configuration
|
|
get_config() {
|
|
local enabled=$(uci_get main.enabled)
|
|
local http_port=$(uci_get main.http_port)
|
|
local data_path=$(uci_get main.data_path)
|
|
local domain=$(uci_get main.domain)
|
|
local admin_user=$(uci_get main.admin_user)
|
|
local memory_limit=$(uci_get main.memory_limit)
|
|
local upload_max=$(uci_get main.upload_max)
|
|
local redis_enabled=$(uci_get redis.enabled)
|
|
local ssl_enabled=$(uci_get ssl.enabled)
|
|
local ssl_domain=$(uci_get ssl.domain)
|
|
local backup_enabled=$(uci_get backup.enabled)
|
|
local backup_keep=$(uci_get backup.keep)
|
|
|
|
cat <<EOF
|
|
{
|
|
"enabled": "${enabled:-0}",
|
|
"http_port": "${http_port:-8080}",
|
|
"data_path": "${data_path:-/srv/nextcloud}",
|
|
"domain": "${domain:-cloud.local}",
|
|
"admin_user": "${admin_user:-admin}",
|
|
"memory_limit": "${memory_limit:-1G}",
|
|
"upload_max": "${upload_max:-512M}",
|
|
"redis_enabled": "${redis_enabled:-1}",
|
|
"ssl_enabled": "${ssl_enabled:-0}",
|
|
"ssl_domain": "${ssl_domain:-}",
|
|
"backup_enabled": "${backup_enabled:-1}",
|
|
"backup_keep": "${backup_keep:-7}"
|
|
}
|
|
EOF
|
|
}
|
|
|
|
# Save configuration
|
|
save_config() {
|
|
local input
|
|
read -r input
|
|
|
|
local http_port=$(echo "$input" | jsonfilter -e '@.http_port' 2>/dev/null)
|
|
local data_path=$(echo "$input" | jsonfilter -e '@.data_path' 2>/dev/null)
|
|
local domain=$(echo "$input" | jsonfilter -e '@.domain' 2>/dev/null)
|
|
local admin_user=$(echo "$input" | jsonfilter -e '@.admin_user' 2>/dev/null)
|
|
local memory_limit=$(echo "$input" | jsonfilter -e '@.memory_limit' 2>/dev/null)
|
|
local upload_max=$(echo "$input" | jsonfilter -e '@.upload_max' 2>/dev/null)
|
|
|
|
[ -n "$http_port" ] && uci_set main.http_port "$http_port"
|
|
[ -n "$data_path" ] && uci_set main.data_path "$data_path"
|
|
[ -n "$domain" ] && uci_set main.domain "$domain"
|
|
[ -n "$admin_user" ] && uci_set main.admin_user "$admin_user"
|
|
[ -n "$memory_limit" ] && uci_set main.memory_limit "$memory_limit"
|
|
[ -n "$upload_max" ] && uci_set main.upload_max "$upload_max"
|
|
|
|
echo '{"success": true}'
|
|
}
|
|
|
|
# Install Nextcloud
|
|
do_install() {
|
|
if command -v nextcloudctl >/dev/null 2>&1; then
|
|
nextcloudctl install >/tmp/nextcloud-install.log 2>&1 &
|
|
echo '{"success": true, "message": "Installation started in background"}'
|
|
else
|
|
echo '{"success": false, "error": "nextcloudctl not found"}'
|
|
fi
|
|
}
|
|
|
|
# Start service
|
|
do_start() {
|
|
if [ -x /etc/init.d/nextcloud ]; then
|
|
uci_set main.enabled '1'
|
|
/etc/init.d/nextcloud start >/dev/null 2>&1
|
|
sleep 2
|
|
echo '{"success": true}'
|
|
else
|
|
echo '{"success": false, "error": "Service not installed"}'
|
|
fi
|
|
}
|
|
|
|
# Stop service
|
|
do_stop() {
|
|
if [ -x /etc/init.d/nextcloud ]; then
|
|
/etc/init.d/nextcloud stop >/dev/null 2>&1
|
|
echo '{"success": true}'
|
|
else
|
|
echo '{"success": false, "error": "Service not installed"}'
|
|
fi
|
|
}
|
|
|
|
# Restart service
|
|
do_restart() {
|
|
if [ -x /etc/init.d/nextcloud ]; then
|
|
/etc/init.d/nextcloud restart >/dev/null 2>&1
|
|
echo '{"success": true}'
|
|
else
|
|
echo '{"success": false, "error": "Service not installed"}'
|
|
fi
|
|
}
|
|
|
|
# Update container
|
|
do_update() {
|
|
if command -v nextcloudctl >/dev/null 2>&1; then
|
|
nextcloudctl update >/tmp/nextcloud-update.log 2>&1 &
|
|
echo '{"success": true, "message": "Update started in background"}'
|
|
else
|
|
echo '{"success": false, "error": "nextcloudctl not found"}'
|
|
fi
|
|
}
|
|
|
|
# Create backup
|
|
do_backup() {
|
|
local input
|
|
read -r input
|
|
local name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null)
|
|
|
|
if command -v nextcloudctl >/dev/null 2>&1; then
|
|
local result
|
|
if [ -n "$name" ]; then
|
|
result=$(nextcloudctl backup "$name" 2>&1)
|
|
else
|
|
result=$(nextcloudctl backup 2>&1)
|
|
fi
|
|
echo "{\"success\": true, \"backup_name\": \"$result\"}"
|
|
else
|
|
echo '{"success": false, "error": "nextcloudctl not found"}'
|
|
fi
|
|
}
|
|
|
|
# Restore backup
|
|
do_restore() {
|
|
local input
|
|
read -r input
|
|
local name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null)
|
|
|
|
if [ -z "$name" ]; then
|
|
echo '{"success": false, "error": "No backup name specified"}'
|
|
return
|
|
fi
|
|
|
|
if command -v nextcloudctl >/dev/null 2>&1; then
|
|
nextcloudctl restore "$name" >/tmp/nextcloud-restore.log 2>&1 &
|
|
echo '{"success": true, "message": "Restore started in background"}'
|
|
else
|
|
echo '{"success": false, "error": "nextcloudctl not found"}'
|
|
fi
|
|
}
|
|
|
|
# List backups
|
|
list_backups() {
|
|
local data_path=$(uci_get main.data_path)
|
|
local backup_dir="${data_path:-/srv/nextcloud}/backups"
|
|
local backups="[]"
|
|
|
|
if [ -d "$backup_dir" ]; then
|
|
backups=$(ls -1 "$backup_dir"/*-db.sql 2>/dev/null | while read f; do
|
|
local name=$(basename "$f" -db.sql)
|
|
local data_file="$backup_dir/$name-data.tar.gz"
|
|
local size="N/A"
|
|
local timestamp=0
|
|
|
|
if [ -f "$data_file" ]; then
|
|
size=$(ls -lh "$data_file" | awk '{print $5}')
|
|
fi
|
|
timestamp=$(stat -c %Y "$f" 2>/dev/null || echo 0)
|
|
|
|
printf '{"name":"%s","size":"%s","timestamp":%d},' "$name" "$size" "$timestamp"
|
|
done | sed 's/,$//' | sed 's/^/[/;s/$/]/')
|
|
fi
|
|
|
|
[ -z "$backups" ] && backups="[]"
|
|
echo "{\"backups\": $backups}"
|
|
}
|
|
|
|
# Enable SSL
|
|
do_ssl_enable() {
|
|
local input
|
|
read -r input
|
|
local domain=$(echo "$input" | jsonfilter -e '@.domain' 2>/dev/null)
|
|
|
|
if [ -z "$domain" ]; then
|
|
echo '{"success": false, "error": "No domain specified"}'
|
|
return
|
|
fi
|
|
|
|
if command -v nextcloudctl >/dev/null 2>&1; then
|
|
nextcloudctl ssl-enable "$domain" >/tmp/nextcloud-ssl.log 2>&1
|
|
echo '{"success": true, "message": "SSL enabled for '$domain'"}'
|
|
else
|
|
echo '{"success": false, "error": "nextcloudctl not found"}'
|
|
fi
|
|
}
|
|
|
|
# Disable SSL
|
|
do_ssl_disable() {
|
|
if command -v nextcloudctl >/dev/null 2>&1; then
|
|
nextcloudctl ssl-disable >/dev/null 2>&1
|
|
echo '{"success": true}'
|
|
else
|
|
echo '{"success": false, "error": "nextcloudctl not found"}'
|
|
fi
|
|
}
|
|
|
|
# Run OCC command
|
|
do_occ() {
|
|
local input
|
|
read -r input
|
|
local cmd=$(echo "$input" | jsonfilter -e '@.command' 2>/dev/null)
|
|
|
|
if [ -z "$cmd" ]; then
|
|
echo '{"success": false, "error": "No command specified"}'
|
|
return
|
|
fi
|
|
|
|
if command -v nextcloudctl >/dev/null 2>&1; then
|
|
local output=$(nextcloudctl occ $cmd 2>&1)
|
|
local escaped=$(echo "$output" | sed 's/"/\\"/g' | tr '\n' ' ')
|
|
echo "{\"success\": true, \"output\": \"$escaped\"}"
|
|
else
|
|
echo '{"success": false, "error": "nextcloudctl not found"}'
|
|
fi
|
|
}
|
|
|
|
# Get logs
|
|
get_logs() {
|
|
local lines=100
|
|
local log_content=""
|
|
|
|
# Check install log
|
|
if [ -f /tmp/nextcloud-install.log ]; then
|
|
log_content=$(tail -n $lines /tmp/nextcloud-install.log 2>/dev/null | sed 's/"/\\"/g' | tr '\n' '|')
|
|
fi
|
|
|
|
echo "{\"logs\": \"$log_content\"}"
|
|
}
|
|
|
|
# List users
|
|
list_users() {
|
|
if ! lxc_running; then
|
|
echo '{"users": []}'
|
|
return
|
|
fi
|
|
|
|
local users_json
|
|
users_json=$(lxc-attach -n "$LXC_NAME" -- su -s /bin/bash www-data -c "php /var/www/nextcloud/occ user:list --output=json" 2>/dev/null)
|
|
|
|
if [ -z "$users_json" ] || [ "$users_json" = "{}" ]; then
|
|
echo '{"users": []}'
|
|
return
|
|
fi
|
|
|
|
# Convert from {"uid":"displayname",...} to [{"uid":"x","displayname":"y"},...]
|
|
# Use sed to transform the JSON
|
|
local users_array
|
|
users_array=$(echo "$users_json" | sed 's/^{//;s/}$//' | tr ',' '\n' | while read -r line; do
|
|
uid=$(echo "$line" | cut -d'"' -f2)
|
|
displayname=$(echo "$line" | cut -d'"' -f4)
|
|
[ -n "$uid" ] && printf '{"uid":"%s","displayname":"%s"},' "$uid" "$displayname"
|
|
done | sed 's/,$//')
|
|
|
|
[ -z "$users_array" ] && users_array=""
|
|
echo "{\"users\": [$users_array]}"
|
|
}
|
|
|
|
# Reset user password
|
|
reset_password() {
|
|
local input
|
|
read -r input
|
|
local uid=$(echo "$input" | jsonfilter -e '@.uid' 2>/dev/null)
|
|
local password=$(echo "$input" | jsonfilter -e '@.password' 2>/dev/null)
|
|
|
|
if [ -z "$uid" ] || [ -z "$password" ]; then
|
|
echo '{"success": false, "error": "User ID and password required"}'
|
|
return
|
|
fi
|
|
|
|
if ! lxc_running; then
|
|
echo '{"success": false, "error": "Container not running"}'
|
|
return
|
|
fi
|
|
|
|
# Reset password via OCC
|
|
local result
|
|
result=$(lxc-attach -n "$LXC_NAME" -- su -s /bin/bash www-data -c "OC_PASS='$password' php /var/www/nextcloud/occ user:resetpassword --password-from-env '$uid'" 2>&1)
|
|
local rc=$?
|
|
|
|
if [ $rc -eq 0 ]; then
|
|
echo '{"success": true, "message": "Password reset for '"$uid"'"}'
|
|
else
|
|
local escaped=$(echo "$result" | sed 's/"/\\"/g' | tr '\n' ' ')
|
|
echo "{\"success\": false, \"error\": \"$escaped\"}"
|
|
fi
|
|
}
|
|
|
|
# Uninstall Nextcloud
|
|
do_uninstall() {
|
|
if command -v nextcloudctl >/dev/null 2>&1; then
|
|
nextcloudctl uninstall >/tmp/nextcloud-uninstall.log 2>&1
|
|
echo '{"success": true, "message": "Nextcloud uninstalled (data preserved)"}'
|
|
else
|
|
echo '{"success": false, "error": "nextcloudctl not found"}'
|
|
fi
|
|
}
|
|
|
|
# Get storage stats
|
|
get_storage() {
|
|
local data_path=$(uci_get main.data_path)
|
|
data_path="${data_path:-/srv/nextcloud}"
|
|
|
|
local total_size="0"
|
|
local data_size="0"
|
|
local backup_size="0"
|
|
|
|
if [ -d "$data_path" ]; then
|
|
total_size=$(du -sh "$data_path" 2>/dev/null | awk '{print $1}' || echo "0")
|
|
fi
|
|
if [ -d "$data_path/data" ]; then
|
|
data_size=$(du -sh "$data_path/data" 2>/dev/null | awk '{print $1}' || echo "0")
|
|
fi
|
|
if [ -d "$data_path/backups" ]; then
|
|
backup_size=$(du -sh "$data_path/backups" 2>/dev/null | awk '{print $1}' || echo "0")
|
|
fi
|
|
|
|
# Get disk free space
|
|
local disk_free=$(df -h "$data_path" 2>/dev/null | tail -1 | awk '{print $4}' || echo "0")
|
|
local disk_total=$(df -h "$data_path" 2>/dev/null | tail -1 | awk '{print $2}' || echo "0")
|
|
local disk_used_pct=$(df "$data_path" 2>/dev/null | tail -1 | awk '{print $5}' | tr -d '%' || echo "0")
|
|
|
|
cat <<EOF
|
|
{
|
|
"total_size": "$total_size",
|
|
"data_size": "$data_size",
|
|
"backup_size": "$backup_size",
|
|
"disk_free": "$disk_free",
|
|
"disk_total": "$disk_total",
|
|
"disk_used_percent": $disk_used_pct
|
|
}
|
|
EOF
|
|
}
|
|
|
|
# Delete a backup
|
|
delete_backup() {
|
|
local input
|
|
read -r input
|
|
local name=$(echo "$input" | jsonfilter -e '@.name' 2>/dev/null)
|
|
|
|
if [ -z "$name" ]; then
|
|
echo '{"success": false, "error": "No backup name specified"}'
|
|
return
|
|
fi
|
|
|
|
local data_path=$(uci_get main.data_path)
|
|
local backup_dir="${data_path:-/srv/nextcloud}/backups"
|
|
|
|
if [ -f "$backup_dir/$name-db.sql" ]; then
|
|
rm -f "$backup_dir/$name-db.sql" "$backup_dir/$name-data.tar.gz"
|
|
echo '{"success": true, "message": "Backup deleted: '"$name"'"}'
|
|
else
|
|
echo '{"success": false, "error": "Backup not found"}'
|
|
fi
|
|
}
|
|
|
|
# RPCD list method
|
|
list_methods() {
|
|
cat <<'EOF'
|
|
{
|
|
"status": {},
|
|
"get_config": {},
|
|
"save_config": {"http_port": "string", "data_path": "string", "domain": "string", "admin_user": "string", "memory_limit": "string", "upload_max": "string"},
|
|
"install": {},
|
|
"uninstall": {},
|
|
"start": {},
|
|
"stop": {},
|
|
"restart": {},
|
|
"update": {},
|
|
"backup": {"name": "string"},
|
|
"restore": {"name": "string"},
|
|
"list_backups": {},
|
|
"delete_backup": {"name": "string"},
|
|
"ssl_enable": {"domain": "string"},
|
|
"ssl_disable": {},
|
|
"occ": {"command": "string"},
|
|
"logs": {},
|
|
"list_users": {},
|
|
"reset_password": {"uid": "string", "password": "string"},
|
|
"get_storage": {}
|
|
}
|
|
EOF
|
|
}
|
|
|
|
# Main entry point
|
|
case "$1" in
|
|
list)
|
|
list_methods
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
status) get_status ;;
|
|
get_config) get_config ;;
|
|
save_config) save_config ;;
|
|
install) do_install ;;
|
|
uninstall) do_uninstall ;;
|
|
start) do_start ;;
|
|
stop) do_stop ;;
|
|
restart) do_restart ;;
|
|
update) do_update ;;
|
|
backup) do_backup ;;
|
|
restore) do_restore ;;
|
|
list_backups) list_backups ;;
|
|
delete_backup) delete_backup ;;
|
|
ssl_enable) do_ssl_enable ;;
|
|
ssl_disable) do_ssl_disable ;;
|
|
occ) do_occ ;;
|
|
logs) get_logs ;;
|
|
list_users) list_users ;;
|
|
reset_password) reset_password ;;
|
|
get_storage) get_storage ;;
|
|
*) echo '{"error": "Unknown method"}' ;;
|
|
esac
|
|
;;
|
|
*)
|
|
echo '{"error": "Unknown command"}'
|
|
;;
|
|
esac
|