secubox-openwrt/package/secubox/secubox-app-bonus/root/usr/sbin/secubox-feed
CyberMind-FR 066bedafef feat(appstore): Implement KISS Evolution - feeds, profiles, skills, feedback
Add four major features to enhance SecuBox AppStore:

1. Feed Source Management:
   - Feed types: published, unpublished, development
   - Share tokens for private feed access
   - CLI: secubox feed list/add/share/import
   - LuCI: Feed type badges and share URLs in catalog-sources

2. Profile Export/Import:
   - Export configurations with feed sources embedded
   - Import from URL or file with merge/replace modes
   - CLI: secubox profile export/import/share
   - LuCI: New profiles.js view with export/import dialogs

3. Skill System:
   - Capability discovery from module catalogs
   - Quality indicators based on provider count
   - CLI: secubox skill list/providers/install/check
   - LuCI: New skills.js view with provider browser

4. Feedback Loop:
   - Issue reporting and resolution tracking
   - Search existing resolutions
   - CLI: secubox feedback report/resolve/search/list
   - LuCI: New feedback.js view for knowledge base

Technical changes:
- RPCD backend with 17 new API methods
- POSIX shell compatibility fixes (ESC via printf, tr A-Z a-z)
- LuCI menu entries for new views

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 10:13:54 +01:00

366 lines
9.7 KiB
Bash

#!/bin/sh
# SecuBox Feed Manager
# Manages local and remote package feeds for SecuBox appstore
# Copyright 2026 CyberMind <contact@cybermind.fr>
FEED_DIR="/www/secubox-feed"
OPKG_LIST="/var/opkg-lists/secubox"
REMOTE_FEED_URL="${SECUBOX_FEED_URL:-https://feed.maegia.tv/secubox-feed}"
GITEA_API="${SECUBOX_GITEA_API:-}"
GITHUB_REPO="${SECUBOX_GITHUB_REPO:-}"
usage() {
cat << EOF
SecuBox Feed Manager - Manage local and remote package feeds
Usage: secubox-feed <command> [options]
Commands:
update Update local feed from installed IPKs
sync Sync feed to /var/opkg-lists for opkg
fetch <url> Fetch IPK from URL and add to local feed
fetch-release <pkg> Fetch latest release from GitHub/Gitea
list List packages in local feed
info <pkg> Show package info
install <pkg> Install package from local feed (force-depends)
clean Remove old package versions
serve Start simple HTTP server for WAN access
export Export feed URL for sharing
Options:
-h, --help Show this help
-v, --verbose Verbose output
-q, --quiet Quiet mode
Environment:
SECUBOX_FEED_URL Remote feed URL (default: $REMOTE_FEED_URL)
SECUBOX_GITEA_API Gitea API URL for releases
SECUBOX_GITHUB_REPO GitHub repo (owner/repo) for releases
EOF
}
log() {
[ "$QUIET" = "1" ] && return
echo "$@"
}
verbose() {
[ "$VERBOSE" = "1" ] && echo "$@"
}
error() {
echo "Error: $@" >&2
}
# Regenerate Packages index from IPK files
regenerate_packages() {
local feed_dir="${1:-$FEED_DIR}"
log "Regenerating Packages index..."
rm -f "$feed_dir/Packages" "$feed_dir/Packages.gz"
local count=0
for pkg in "$feed_dir"/*.ipk; do
[ -f "$pkg" ] || continue
local tmp_dir=$(mktemp -d)
(
cd "$tmp_dir"
tar -xzf "$pkg" 2>/dev/null || return 1
tar -xzf control.tar.gz 2>/dev/null || return 1
if [ -f control ]; then
# Strip libc and blank lines
sed -e 's/^Depends: libc$//' \
-e 's/^Depends: libc, /Depends: /' \
-e '/^$/d' control
echo "Filename: $(basename "$pkg")"
echo "Size: $(stat -c%s "$pkg" 2>/dev/null || wc -c < "$pkg")"
echo "SHA256sum: $(sha256sum "$pkg" | cut -d' ' -f1)"
echo ""
fi
) >> "$feed_dir/Packages"
rm -rf "$tmp_dir"
count=$((count + 1))
done
gzip -kf "$feed_dir/Packages" 2>/dev/null
log "Generated index for $count packages"
}
# Sync feed to opkg lists
sync_feed() {
if [ -f "$FEED_DIR/Packages" ]; then
cp "$FEED_DIR/Packages" "$OPKG_LIST"
log "Feed synced to $OPKG_LIST"
log "$(grep -c '^Package:' "$OPKG_LIST") packages available"
else
error "No Packages file found. Run 'secubox-feed update' first."
return 1
fi
}
# Fetch IPK from URL
fetch_ipk() {
local url="$1"
local filename=$(basename "$url")
log "Fetching $filename..."
if command -v curl >/dev/null 2>&1; then
curl -fsSL -o "$FEED_DIR/$filename" "$url"
elif command -v wget >/dev/null 2>&1; then
wget -q -O "$FEED_DIR/$filename" "$url"
else
error "Neither curl nor wget available"
return 1
fi
if [ -f "$FEED_DIR/$filename" ]; then
log "Downloaded: $filename"
regenerate_packages
sync_feed
else
error "Download failed"
return 1
fi
}
# Fetch latest release from GitHub
fetch_github_release() {
local pkg="$1"
local repo="${GITHUB_REPO:-secubox/secubox-openwrt}"
log "Fetching latest release for $pkg from GitHub..."
local api_url="https://api.github.com/repos/$repo/releases/latest"
local release_json=$(curl -fsSL "$api_url" 2>/dev/null)
if [ -z "$release_json" ]; then
error "Failed to fetch release info"
return 1
fi
# Find IPK matching package name
local ipk_url=$(echo "$release_json" | jsonfilter -e "@.assets[*].browser_download_url" 2>/dev/null | grep -i "${pkg}.*\.ipk" | head -1)
if [ -n "$ipk_url" ]; then
fetch_ipk "$ipk_url"
else
error "No IPK found for $pkg in latest release"
return 1
fi
}
# Fetch from Gitea releases
fetch_gitea_release() {
local pkg="$1"
if [ -z "$GITEA_API" ]; then
error "SECUBOX_GITEA_API not set"
return 1
fi
log "Fetching latest release for $pkg from Gitea..."
local api_url="$GITEA_API/repos/secubox/secubox-openwrt/releases/latest"
local release_json=$(curl -fsSL "$api_url" 2>/dev/null)
if [ -z "$release_json" ]; then
error "Failed to fetch release info from Gitea"
return 1
fi
local ipk_url=$(echo "$release_json" | jsonfilter -e "@.assets[*].browser_download_url" 2>/dev/null | grep -i "${pkg}.*\.ipk" | head -1)
if [ -n "$ipk_url" ]; then
fetch_ipk "$ipk_url"
else
error "No IPK found for $pkg in Gitea release"
return 1
fi
}
# List packages in feed
list_packages() {
if [ -f "$FEED_DIR/Packages" ]; then
grep "^Package:" "$FEED_DIR/Packages" | sed 's/Package: //' | sort
else
error "No Packages file found"
return 1
fi
}
# Show package info
package_info() {
local pkg="$1"
if [ -f "$FEED_DIR/Packages" ]; then
awk -v pkg="$pkg" '
/^Package:/ { found = ($2 == pkg) }
found { print }
found && /^$/ { exit }
' "$FEED_DIR/Packages"
else
error "No Packages file found"
return 1
fi
}
# Install package with force-depends
install_package() {
local pkg="$1"
local ipk_file="$FEED_DIR/${pkg}_"*.ipk
# Find the IPK file
local found=$(ls $ipk_file 2>/dev/null | head -1)
if [ -n "$found" ]; then
log "Installing $pkg..."
opkg install --force-depends "$found"
else
error "Package $pkg not found in local feed"
return 1
fi
}
# Clean old versions
clean_old_versions() {
log "Cleaning old package versions..."
local removed=0
for name in $(ls "$FEED_DIR"/*.ipk 2>/dev/null | sed 's/_[0-9].*$//' | sort -u); do
local basename=$(basename "$name")
local versions=$(ls -t "$FEED_DIR/${basename}_"*.ipk 2>/dev/null)
local count=$(echo "$versions" | wc -l)
if [ "$count" -gt 1 ]; then
echo "$versions" | tail -n +2 | while read old; do
[ -f "$old" ] || continue
verbose "Removing: $(basename "$old")"
rm -f "$old"
removed=$((removed + 1))
done
fi
done
log "Removed $removed old versions"
regenerate_packages
}
# Start simple HTTP server
serve_feed() {
local port="${1:-8080}"
if command -v uhttpd >/dev/null 2>&1; then
log "Feed available at: http://$(uci get network.lan.ipaddr 2>/dev/null || echo '<router-ip>'):$port/secubox-feed/"
log "Add to remote opkg: src/gz secubox http://<router-ip>:$port/secubox-feed"
# uhttpd is already running for LuCI, feed is at /www/secubox-feed/
else
log "uhttpd not available. Feed is at file:///www/secubox-feed/"
fi
}
# Export feed URL
export_feed() {
local ip=$(uci get network.lan.ipaddr 2>/dev/null || echo "192.168.1.1")
local wan_ip=$(curl -s ifconfig.me 2>/dev/null || echo "")
echo "Local feed configuration:"
echo " src secubox file:///www/secubox-feed"
echo ""
echo "LAN access (for other OpenWrt devices):"
echo " src/gz secubox http://$ip/secubox-feed"
echo ""
if [ -n "$wan_ip" ]; then
echo "WAN access (if port 80 forwarded):"
echo " src/gz secubox http://$wan_ip/secubox-feed"
fi
echo ""
echo "Remote hosted feed:"
echo " src/gz secubox $REMOTE_FEED_URL"
}
# Main
main() {
local cmd=""
VERBOSE=0
QUIET=0
while [ $# -gt 0 ]; do
case "$1" in
-h|--help) usage; exit 0 ;;
-v|--verbose) VERBOSE=1; shift ;;
-q|--quiet) QUIET=1; shift ;;
*)
if [ -z "$cmd" ]; then
cmd="$1"
else
break
fi
shift
;;
esac
done
# Ensure feed directory exists
mkdir -p "$FEED_DIR"
case "$cmd" in
update|regenerate)
regenerate_packages
sync_feed
;;
sync)
sync_feed
;;
fetch)
[ -z "$1" ] && { error "URL required"; exit 1; }
fetch_ipk "$1"
;;
fetch-release|release)
[ -z "$1" ] && { error "Package name required"; exit 1; }
if [ -n "$GITEA_API" ]; then
fetch_gitea_release "$1"
elif [ -n "$GITHUB_REPO" ]; then
fetch_github_release "$1"
else
error "Set SECUBOX_GITEA_API or SECUBOX_GITHUB_REPO"
exit 1
fi
;;
list|ls)
list_packages
;;
info|show)
[ -z "$1" ] && { error "Package name required"; exit 1; }
package_info "$1"
;;
install)
[ -z "$1" ] && { error "Package name required"; exit 1; }
install_package "$1"
;;
clean)
clean_old_versions
;;
serve)
serve_feed "$1"
;;
export|url)
export_feed
;;
"")
usage
exit 1
;;
*)
error "Unknown command: $cmd"
usage
exit 1
;;
esac
}
main "$@"