- Add embed_local_feed() to local-build.sh that copies built packages into bonus app as /www/secubox-feed/ for offline installation - Generate Packages index and apps-local.json manifest for opkg - Add RPCD backend (luci.secubox-store) for package install/remove - Add LuCI view for browsing and managing local packages - Fix OPENWRT_ONLY_PACKAGES to allow secubox-app-* wrappers in SDK build - Remove experimental python3-* packages (unfinished mitmproxy native plan) - Set rootfs partition size to 16GB for larger overlay - Bump luci-app-secubox-bonus to v0.2.0 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
275 lines
8.0 KiB
Bash
Executable File
275 lines
8.0 KiB
Bash
Executable File
#!/bin/sh
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
# SecuBox Local Package Store - RPCD Backend
|
|
# Manages installation/removal of packages from local feed
|
|
|
|
. /usr/share/libubox/jshn.sh
|
|
|
|
FEED_DIR="/www/secubox-feed"
|
|
APPS_JSON="$FEED_DIR/apps-local.json"
|
|
|
|
# List available packages from local feed
|
|
list_packages() {
|
|
if [ -f "$APPS_JSON" ]; then
|
|
# Read apps-local.json and add installation status
|
|
local packages=$(cat "$APPS_JSON")
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "feed_url" "/secubox-feed"
|
|
json_add_array "packages"
|
|
|
|
# Parse apps-local.json and check each package
|
|
local pkg_list=$(jsonfilter -s "$packages" -e '@.packages[*].name' 2>/dev/null)
|
|
|
|
for name in $pkg_list; do
|
|
local pkg_data=$(jsonfilter -s "$packages" -e "@.packages[@.name='$name']" 2>/dev/null)
|
|
local version=$(echo "$pkg_data" | jsonfilter -e '@.version' 2>/dev/null)
|
|
local filename=$(echo "$pkg_data" | jsonfilter -e '@.filename' 2>/dev/null)
|
|
local size=$(echo "$pkg_data" | jsonfilter -e '@.size' 2>/dev/null)
|
|
local category=$(echo "$pkg_data" | jsonfilter -e '@.category' 2>/dev/null)
|
|
local icon=$(echo "$pkg_data" | jsonfilter -e '@.icon' 2>/dev/null)
|
|
local description=$(echo "$pkg_data" | jsonfilter -e '@.description' 2>/dev/null)
|
|
|
|
# Check if installed
|
|
local installed=0
|
|
if opkg list-installed 2>/dev/null | grep -q "^${name} "; then
|
|
installed=1
|
|
fi
|
|
|
|
json_add_object ""
|
|
json_add_string "name" "$name"
|
|
json_add_string "version" "$version"
|
|
json_add_string "filename" "$filename"
|
|
json_add_int "size" "${size:-0}"
|
|
json_add_string "category" "$category"
|
|
json_add_string "icon" "$icon"
|
|
json_add_string "description" "$description"
|
|
json_add_boolean "installed" "$installed"
|
|
json_close_object
|
|
done
|
|
|
|
json_close_array
|
|
json_dump
|
|
else
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Local feed not found"
|
|
json_add_array "packages"
|
|
json_close_array
|
|
json_dump
|
|
fi
|
|
}
|
|
|
|
# Install a package from local feed
|
|
install_package() {
|
|
local pkg_name="$1"
|
|
|
|
if [ -z "$pkg_name" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Package name required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Find the package file
|
|
local pkg_file=$(ls "$FEED_DIR/${pkg_name}_"*.ipk 2>/dev/null | head -1)
|
|
|
|
if [ -z "$pkg_file" ] || [ ! -f "$pkg_file" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Package file not found: $pkg_name"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Update opkg lists first
|
|
opkg update 2>/dev/null || true
|
|
|
|
# Install the package
|
|
local output
|
|
output=$(opkg install "$pkg_file" 2>&1)
|
|
local result=$?
|
|
|
|
json_init
|
|
if [ $result -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Package installed successfully"
|
|
json_add_string "package" "$pkg_name"
|
|
|
|
# Clear LuCI cache
|
|
rm -rf /tmp/luci-modulecache /tmp/luci-indexcache 2>/dev/null
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_add_string "output" "$output"
|
|
json_dump
|
|
}
|
|
|
|
# Remove a package
|
|
remove_package() {
|
|
local pkg_name="$1"
|
|
|
|
if [ -z "$pkg_name" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Package name required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Check if installed
|
|
if ! opkg list-installed 2>/dev/null | grep -q "^${pkg_name} "; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Package not installed: $pkg_name"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
# Remove the package
|
|
local output
|
|
output=$(opkg remove "$pkg_name" 2>&1)
|
|
local result=$?
|
|
|
|
json_init
|
|
if [ $result -eq 0 ]; then
|
|
json_add_boolean "success" 1
|
|
json_add_string "message" "Package removed successfully"
|
|
json_add_string "package" "$pkg_name"
|
|
|
|
# Clear LuCI cache
|
|
rm -rf /tmp/luci-modulecache /tmp/luci-indexcache 2>/dev/null
|
|
else
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "$output"
|
|
fi
|
|
json_add_string "output" "$output"
|
|
json_dump
|
|
}
|
|
|
|
# Get package info
|
|
get_package_info() {
|
|
local pkg_name="$1"
|
|
|
|
if [ -z "$pkg_name" ]; then
|
|
json_init
|
|
json_add_boolean "success" 0
|
|
json_add_string "error" "Package name required"
|
|
json_dump
|
|
return
|
|
fi
|
|
|
|
json_init
|
|
json_add_boolean "success" 1
|
|
json_add_string "name" "$pkg_name"
|
|
|
|
# Check if installed
|
|
local installed_info=$(opkg list-installed "$pkg_name" 2>/dev/null)
|
|
if [ -n "$installed_info" ]; then
|
|
json_add_boolean "installed" 1
|
|
json_add_string "installed_version" "$(echo "$installed_info" | cut -d' ' -f3)"
|
|
else
|
|
json_add_boolean "installed" 0
|
|
fi
|
|
|
|
# Get info from feed
|
|
if [ -f "$APPS_JSON" ]; then
|
|
local pkg_data=$(cat "$APPS_JSON" | jsonfilter -e "@.packages[@.name='$pkg_name']" 2>/dev/null)
|
|
if [ -n "$pkg_data" ]; then
|
|
json_add_string "feed_version" "$(echo "$pkg_data" | jsonfilter -e '@.version' 2>/dev/null)"
|
|
json_add_string "description" "$(echo "$pkg_data" | jsonfilter -e '@.description' 2>/dev/null)"
|
|
json_add_string "category" "$(echo "$pkg_data" | jsonfilter -e '@.category' 2>/dev/null)"
|
|
json_add_int "size" "$(echo "$pkg_data" | jsonfilter -e '@.size' 2>/dev/null)"
|
|
fi
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Get feed status
|
|
get_feed_status() {
|
|
json_init
|
|
|
|
if [ -d "$FEED_DIR" ]; then
|
|
json_add_boolean "feed_exists" 1
|
|
json_add_string "feed_path" "$FEED_DIR"
|
|
|
|
local pkg_count=$(ls -1 "$FEED_DIR"/*.ipk 2>/dev/null | wc -l)
|
|
json_add_int "package_count" "$pkg_count"
|
|
|
|
if [ -f "$APPS_JSON" ]; then
|
|
json_add_boolean "index_exists" 1
|
|
local generated=$(jsonfilter -i "$APPS_JSON" -e '@.generated' 2>/dev/null)
|
|
json_add_string "generated" "$generated"
|
|
else
|
|
json_add_boolean "index_exists" 0
|
|
fi
|
|
|
|
# Check if feed is in opkg config
|
|
if grep -q "secubox-feed" /etc/opkg/customfeeds.conf 2>/dev/null; then
|
|
json_add_boolean "feed_configured" 1
|
|
else
|
|
json_add_boolean "feed_configured" 0
|
|
fi
|
|
else
|
|
json_add_boolean "feed_exists" 0
|
|
fi
|
|
|
|
json_dump
|
|
}
|
|
|
|
# Main dispatcher
|
|
case "$1" in
|
|
list)
|
|
json_init
|
|
json_add_object "list_packages"
|
|
json_close_object
|
|
json_add_object "install_package"
|
|
json_add_string "package" "str"
|
|
json_close_object
|
|
json_add_object "remove_package"
|
|
json_add_string "package" "str"
|
|
json_close_object
|
|
json_add_object "get_package_info"
|
|
json_add_string "package" "str"
|
|
json_close_object
|
|
json_add_object "get_feed_status"
|
|
json_close_object
|
|
json_dump
|
|
;;
|
|
call)
|
|
case "$2" in
|
|
list_packages)
|
|
list_packages
|
|
;;
|
|
install_package)
|
|
read -r input
|
|
pkg=$(echo "$input" | jsonfilter -e '@.package')
|
|
install_package "$pkg"
|
|
;;
|
|
remove_package)
|
|
read -r input
|
|
pkg=$(echo "$input" | jsonfilter -e '@.package')
|
|
remove_package "$pkg"
|
|
;;
|
|
get_package_info)
|
|
read -r input
|
|
pkg=$(echo "$input" | jsonfilter -e '@.package')
|
|
get_package_info "$pkg"
|
|
;;
|
|
get_feed_status)
|
|
get_feed_status
|
|
;;
|
|
*)
|
|
echo '{"error":"Unknown method"}'
|
|
;;
|
|
esac
|
|
;;
|
|
*)
|
|
echo '{"error":"Invalid action"}'
|
|
;;
|
|
esac
|