From 9cd59b77ba4282144cc5a24f61b902c989c73398 Mon Sep 17 00:00:00 2001 From: CyberMind-FR Date: Wed, 18 Mar 2026 10:21:23 +0100 Subject: [PATCH] feat(repo): Add secubox-app-repo and luci-app-repo packages Backend package (secubox-app-repo): - repoctl CLI for managing local package repository - repo-sync script to download packages from GitHub releases - uhttpd-based server on port 8888 - UCI configuration at /etc/config/repo - RPCD handler for LuCI integration - Auto-sync cron support (configurable interval) Frontend package (luci-app-repo): - Dashboard showing repository status and package counts - Sync button to trigger package downloads - Log viewer for sync operations - Usage instructions for opkg configuration Supported architectures: - x86_64, aarch64_cortex-a72, aarch64_generic - mips_24kc, mipsel_24kc Co-Authored-By: Claude Opus 4.5 --- package/secubox/luci-app-repo/Makefile | 26 +++ .../resources/view/repo/dashboard.js | 203 ++++++++++++++++++ .../usr/share/luci/menu.d/luci-app-repo.json | 14 ++ .../usr/share/rpcd/acl.d/luci-app-repo.json | 17 ++ package/secubox/secubox-app-repo/Makefile | 60 ++++++ .../secubox-app-repo/root/etc/config/repo | 8 + .../root/etc/init.d/repo-server | 29 +++ .../root/usr/libexec/rpcd/luci.repo | 137 ++++++++++++ .../secubox-app-repo/root/usr/sbin/repo-sync | 131 +++++++++++ .../secubox-app-repo/root/usr/sbin/repoctl | 156 ++++++++++++++ .../usr/share/rpcd/acl.d/luci-app-repo.json | 17 ++ 11 files changed, 798 insertions(+) create mode 100644 package/secubox/luci-app-repo/Makefile create mode 100644 package/secubox/luci-app-repo/htdocs/luci-static/resources/view/repo/dashboard.js create mode 100644 package/secubox/luci-app-repo/root/usr/share/luci/menu.d/luci-app-repo.json create mode 100644 package/secubox/luci-app-repo/root/usr/share/rpcd/acl.d/luci-app-repo.json create mode 100644 package/secubox/secubox-app-repo/Makefile create mode 100644 package/secubox/secubox-app-repo/root/etc/config/repo create mode 100755 package/secubox/secubox-app-repo/root/etc/init.d/repo-server create mode 100755 package/secubox/secubox-app-repo/root/usr/libexec/rpcd/luci.repo create mode 100755 package/secubox/secubox-app-repo/root/usr/sbin/repo-sync create mode 100755 package/secubox/secubox-app-repo/root/usr/sbin/repoctl create mode 100644 package/secubox/secubox-app-repo/root/usr/share/rpcd/acl.d/luci-app-repo.json diff --git a/package/secubox/luci-app-repo/Makefile b/package/secubox/luci-app-repo/Makefile new file mode 100644 index 00000000..744df899 --- /dev/null +++ b/package/secubox/luci-app-repo/Makefile @@ -0,0 +1,26 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=luci-app-repo +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=SecuBox Team +PKG_LICENSE:=Apache-2.0 + +LUCI_TITLE:=LuCI Package Repository Dashboard +LUCI_DEPENDS:=+secubox-app-repo + +include $(INCLUDE_DIR)/package.mk + +define Package/luci-app-repo/install + $(INSTALL_DIR) $(1)/www/luci-static/resources/view/repo + $(INSTALL_DATA) ./htdocs/luci-static/resources/view/repo/*.js $(1)/www/luci-static/resources/view/repo/ + + $(INSTALL_DIR) $(1)/usr/share/luci/menu.d + $(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-repo.json $(1)/usr/share/luci/menu.d/ + + $(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d + $(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-repo.json $(1)/usr/share/rpcd/acl.d/ +endef + +$(eval $(call BuildPackage,luci-app-repo)) diff --git a/package/secubox/luci-app-repo/htdocs/luci-static/resources/view/repo/dashboard.js b/package/secubox/luci-app-repo/htdocs/luci-static/resources/view/repo/dashboard.js new file mode 100644 index 00000000..abc4a7d8 --- /dev/null +++ b/package/secubox/luci-app-repo/htdocs/luci-static/resources/view/repo/dashboard.js @@ -0,0 +1,203 @@ +'use strict'; +'require view'; +'require dom'; +'require poll'; +'require uci'; +'require rpc'; +'require ui'; + +var callStatus = rpc.declare({ + object: 'luci.repo', + method: 'status', + expect: {} +}); + +var callPackages = rpc.declare({ + object: 'luci.repo', + method: 'packages', + params: ['arch'], + expect: {} +}); + +var callSync = rpc.declare({ + object: 'luci.repo', + method: 'sync', + params: ['version'], + expect: {} +}); + +var callLogs = rpc.declare({ + object: 'luci.repo', + method: 'logs', + params: ['lines'], + expect: {} +}); + +return view.extend({ + load: function() { + return Promise.all([ + callStatus(), + uci.load('repo') + ]); + }, + + formatBytes: function(bytes) { + if (bytes === 0) return '0 B'; + var k = 1024; + var sizes = ['B', 'KB', 'MB', 'GB']; + var i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; + }, + + renderStatus: function(status) { + var statusClass = status.running ? 'success' : 'danger'; + var statusText = status.running ? _('Running') : _('Stopped'); + + return E('div', { 'class': 'cbi-section' }, [ + E('h3', {}, _('Repository Status')), + E('div', { 'class': 'table' }, [ + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td left', 'style': 'width:200px' }, _('Server Status')), + E('div', { 'class': 'td' }, [ + E('span', { 'class': 'label ' + statusClass }, statusText), + status.running ? E('span', {}, ' (port ' + status.port + ')') : '' + ]) + ]), + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td left' }, _('Version')), + E('div', { 'class': 'td' }, status.version || '-') + ]), + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td left' }, _('GitHub Repository')), + E('div', { 'class': 'td' }, status.github_repo || '-') + ]), + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td left' }, _('Last Sync')), + E('div', { 'class': 'td' }, status.last_sync || _('Never')) + ]), + E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td left' }, _('Auto Sync')), + E('div', { 'class': 'td' }, status.auto_sync ? + _('Every %d hours').format(status.sync_interval) : _('Disabled')) + ]) + ]) + ]); + }, + + renderArchitectures: function(status) { + var archs = status.architectures || {}; + var rows = []; + + Object.keys(archs).sort().forEach(function(arch) { + rows.push(E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td left' }, arch), + E('div', { 'class': 'td' }, archs[arch] + ' ' + _('packages')) + ])); + }); + + if (rows.length === 0) { + rows.push(E('div', { 'class': 'tr' }, [ + E('div', { 'class': 'td', 'colspan': 2 }, _('No packages synced yet')) + ])); + } + + return E('div', { 'class': 'cbi-section' }, [ + E('h3', {}, _('Available Architectures')), + E('div', { 'class': 'table' }, rows) + ]); + }, + + renderActions: function(status) { + var self = this; + + var syncBtn = E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': function() { + ui.showModal(_('Sync Packages'), [ + E('p', {}, _('Sync packages from GitHub release?')), + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'cbi-button', + 'click': ui.hideModal + }, _('Cancel')), + ' ', + E('button', { + 'class': 'cbi-button cbi-button-positive', + 'click': function() { + ui.hideModal(); + ui.showModal(_('Syncing...'), [ + E('p', { 'class': 'spinning' }, _('Downloading packages from GitHub...')) + ]); + callSync(status.version).then(function() { + setTimeout(function() { + ui.hideModal(); + window.location.reload(); + }, 3000); + }); + } + }, _('Sync Now')) + ]) + ]); + } + }, _('Sync Packages')); + + var logsBtn = E('button', { + 'class': 'cbi-button', + 'click': function() { + callLogs(100).then(function(result) { + var logs = (result.logs || '').split('|').join('\n'); + ui.showModal(_('Sync Logs'), [ + E('pre', { 'style': 'max-height:400px;overflow:auto;font-size:12px;' }, logs || _('No logs')), + E('div', { 'class': 'right' }, [ + E('button', { + 'class': 'cbi-button', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + }); + } + }, _('View Logs')); + + return E('div', { 'class': 'cbi-section' }, [ + E('h3', {}, _('Actions')), + E('div', { 'class': 'cbi-value' }, [ + syncBtn, ' ', logsBtn + ]) + ]); + }, + + renderUsage: function(status) { + var port = status.port || 8888; + + return E('div', { 'class': 'cbi-section' }, [ + E('h3', {}, _('Usage')), + E('p', {}, _('Add to /etc/opkg/customfeeds.conf:')), + E('pre', { 'style': 'background:#f5f5f5;padding:10px;border-radius:4px;' }, [ + '# Local repository\n', + 'src/gz secubox_luci http://127.0.0.1:' + port + '/luci/{ARCH}\n\n', + '# Or via HTTPS (external)\n', + 'src/gz secubox_luci https://repo.secubox.in/luci/{ARCH}' + ].join('')), + E('p', {}, _('Replace {ARCH} with your architecture: x86_64, aarch64_cortex-a72, aarch64_generic, etc.')) + ]); + }, + + render: function(data) { + var status = data[0] || {}; + + return E('div', { 'class': 'cbi-map' }, [ + E('h2', {}, _('Package Repository')), + E('div', { 'class': 'cbi-map-descr' }, + _('SecuBox package repository - serves OpenWrt packages locally for opkg installation.')), + this.renderStatus(status), + this.renderArchitectures(status), + this.renderActions(status), + this.renderUsage(status) + ]); + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null +}); diff --git a/package/secubox/luci-app-repo/root/usr/share/luci/menu.d/luci-app-repo.json b/package/secubox/luci-app-repo/root/usr/share/luci/menu.d/luci-app-repo.json new file mode 100644 index 00000000..c34b42b9 --- /dev/null +++ b/package/secubox/luci-app-repo/root/usr/share/luci/menu.d/luci-app-repo.json @@ -0,0 +1,14 @@ +{ + "admin/services/repo": { + "title": "Package Repository", + "order": 85, + "action": { + "type": "view", + "path": "repo/dashboard" + }, + "depends": { + "acl": ["luci-app-repo"], + "uci": {"repo": true} + } + } +} diff --git a/package/secubox/luci-app-repo/root/usr/share/rpcd/acl.d/luci-app-repo.json b/package/secubox/luci-app-repo/root/usr/share/rpcd/acl.d/luci-app-repo.json new file mode 100644 index 00000000..3e3a506c --- /dev/null +++ b/package/secubox/luci-app-repo/root/usr/share/rpcd/acl.d/luci-app-repo.json @@ -0,0 +1,17 @@ +{ + "luci-app-repo": { + "description": "SecuBox Package Repository Dashboard", + "read": { + "ubus": { + "luci.repo": ["status", "config", "packages", "logs"] + }, + "uci": ["repo"] + }, + "write": { + "ubus": { + "luci.repo": ["sync"] + }, + "uci": ["repo"] + } + } +} diff --git a/package/secubox/secubox-app-repo/Makefile b/package/secubox/secubox-app-repo/Makefile new file mode 100644 index 00000000..c0791009 --- /dev/null +++ b/package/secubox/secubox-app-repo/Makefile @@ -0,0 +1,60 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=secubox-app-repo +PKG_VERSION:=1.0.0 +PKG_RELEASE:=1 + +PKG_MAINTAINER:=SecuBox Team +PKG_LICENSE:=GPL-3.0 + +include $(INCLUDE_DIR)/package.mk + +define Package/secubox-app-repo + SECTION:=secubox + CATEGORY:=SecuBox + TITLE:=SecuBox Package Repository Manager + DEPENDS:=+uhttpd +wget +gzip +coreutils-stat + PKGARCH:=all +endef + +define Package/secubox-app-repo/description + SecuBox Package Repository Manager - hosts and syncs OpenWrt packages + from GitHub releases for local opkg installation. +endef + +define Package/secubox-app-repo/conffiles +/etc/config/repo +endef + +define Build/Compile +endef + +define Package/secubox-app-repo/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_BIN) ./root/usr/sbin/repoctl $(1)/usr/sbin/ + $(INSTALL_BIN) ./root/usr/sbin/repo-sync $(1)/usr/sbin/ + + $(INSTALL_DIR) $(1)/usr/libexec/rpcd + $(INSTALL_BIN) ./root/usr/libexec/rpcd/luci.repo $(1)/usr/libexec/rpcd/ + + $(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d + $(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-repo.json $(1)/usr/share/rpcd/acl.d/ + + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_BIN) ./root/etc/init.d/repo-server $(1)/etc/init.d/ + + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_CONF) ./root/etc/config/repo $(1)/etc/config/ +endef + +define Package/secubox-app-repo/postinst +#!/bin/sh +[ -n "$${IPKG_INSTROOT}" ] || { + /etc/init.d/repo-server enable + /etc/init.d/repo-server start + /etc/init.d/rpcd restart +} +exit 0 +endef + +$(eval $(call BuildPackage,secubox-app-repo)) diff --git a/package/secubox/secubox-app-repo/root/etc/config/repo b/package/secubox/secubox-app-repo/root/etc/config/repo new file mode 100644 index 00000000..a8e2f489 --- /dev/null +++ b/package/secubox/secubox-app-repo/root/etc/config/repo @@ -0,0 +1,8 @@ +config repo 'main' + option enabled '1' + option version 'v1.0.0-beta' + option github_repo 'gkerma/secubox-openwrt' + option port '8888' + option auto_sync '1' + option sync_interval '6' + option last_sync '' diff --git a/package/secubox/secubox-app-repo/root/etc/init.d/repo-server b/package/secubox/secubox-app-repo/root/etc/init.d/repo-server new file mode 100755 index 00000000..763b39df --- /dev/null +++ b/package/secubox/secubox-app-repo/root/etc/init.d/repo-server @@ -0,0 +1,29 @@ +#!/bin/sh /etc/rc.common + +START=99 +STOP=10 +USE_PROCD=1 + +REPO_DIR="/srv/repo.secubox.in" + +start_service() { + . /lib/functions.sh + config_load repo + config_get enabled main enabled "1" + config_get port main port "8888" + + [ "$enabled" = "1" ] || return 0 + + mkdir -p "$REPO_DIR" + + procd_open_instance + procd_set_param command /usr/sbin/uhttpd -f -h "$REPO_DIR" -p "0.0.0.0:$port" + procd_set_param respawn + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_close_instance +} + +service_triggers() { + procd_add_reload_trigger "repo" +} diff --git a/package/secubox/secubox-app-repo/root/usr/libexec/rpcd/luci.repo b/package/secubox/secubox-app-repo/root/usr/libexec/rpcd/luci.repo new file mode 100755 index 00000000..49b4c426 --- /dev/null +++ b/package/secubox/secubox-app-repo/root/usr/libexec/rpcd/luci.repo @@ -0,0 +1,137 @@ +#!/bin/sh + +. /lib/functions.sh +. /usr/share/libubox/jshn.sh + +REPO_DIR="/srv/repo.secubox.in" +LOG_FILE="/var/log/repo-sync.log" + +case "$1" in + list) + echo '{"status":{},"config":{},"packages":{"arch":"string"},"sync":{"version":"string"},"logs":{"lines":"number"}}' + ;; + call) + case "$2" in + status) + json_init + + # Load config + config_load repo + config_get enabled main enabled "1" + config_get version main version "unknown" + config_get github_repo main github_repo "" + config_get port main port "8888" + config_get last_sync main last_sync "" + config_get auto_sync main auto_sync "0" + config_get sync_interval main sync_interval "6" + + json_add_boolean "enabled" "$enabled" + json_add_string "version" "$version" + json_add_string "github_repo" "$github_repo" + json_add_int "port" "$port" + json_add_string "last_sync" "$last_sync" + json_add_boolean "auto_sync" "$auto_sync" + json_add_int "sync_interval" "$sync_interval" + + # Server status + if netstat -tln 2>/dev/null | grep -q ":$port "; then + json_add_boolean "running" 1 + else + json_add_boolean "running" 0 + fi + + # Package counts by architecture + json_add_object "architectures" + for dir in "$REPO_DIR/luci"/*; do + [ -d "$dir" ] || continue + arch=$(basename "$dir") + count=$(ls "$dir"/*.ipk 2>/dev/null | wc -l) + json_add_int "$arch" "$count" + done + json_close_object + + json_dump + ;; + + config) + json_init + config_load repo + + config_get enabled main enabled "1" + config_get version main version "" + config_get github_repo main github_repo "" + config_get port main port "8888" + config_get auto_sync main auto_sync "0" + config_get sync_interval main sync_interval "6" + + json_add_boolean "enabled" "$enabled" + json_add_string "version" "$version" + json_add_string "github_repo" "$github_repo" + json_add_int "port" "$port" + json_add_boolean "auto_sync" "$auto_sync" + json_add_int "sync_interval" "$sync_interval" + + json_dump + ;; + + packages) + read -r input + json_load "$input" + json_get_var arch arch "x86_64" + + json_init + json_add_string "arch" "$arch" + json_add_array "packages" + + dir="$REPO_DIR/luci/$arch" + if [ -d "$dir" ]; then + for ipk in "$dir"/*.ipk; do + [ -f "$ipk" ] || continue + name=$(basename "$ipk") + size=$(stat -c%s "$ipk" 2>/dev/null || echo 0) + json_add_object "" + json_add_string "name" "$name" + json_add_int "size" "$size" + json_close_object + done + fi + + json_close_array + json_dump + ;; + + sync) + read -r input + json_load "$input" + json_get_var version version "" + + if [ -n "$version" ]; then + uci set repo.main.version="$version" + uci commit repo + fi + + # Run sync in background + /usr/sbin/repo-sync & + + json_init + json_add_boolean "started" 1 + json_add_string "message" "Sync started in background" + json_dump + ;; + + logs) + read -r input + json_load "$input" + json_get_var lines lines 50 + + json_init + if [ -f "$LOG_FILE" ]; then + json_add_string "logs" "$(tail -n "$lines" "$LOG_FILE" | sed 's/"/\\"/g' | tr '\n' '|')" + else + json_add_string "logs" "" + fi + json_dump + ;; + esac + ;; +esac diff --git a/package/secubox/secubox-app-repo/root/usr/sbin/repo-sync b/package/secubox/secubox-app-repo/root/usr/sbin/repo-sync new file mode 100755 index 00000000..fbea1e5b --- /dev/null +++ b/package/secubox/secubox-app-repo/root/usr/sbin/repo-sync @@ -0,0 +1,131 @@ +#!/bin/sh +# SecuBox Package Repository Sync Script +# Syncs packages from GitHub releases to local repo + +. /lib/functions.sh + +REPO_DIR="/srv/repo.secubox.in" +CONFIG_FILE="/etc/config/repo" +LOG_FILE="/var/log/repo-sync.log" + +log() { + local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*" + echo "$msg" + echo "$msg" >> "$LOG_FILE" +} + +# Load config +config_load repo +config_get GITHUB_REPO main github_repo "gkerma/secubox-openwrt" +config_get VERSION main version "v1.0.0-beta" +config_get ENABLED main enabled "1" + +[ "$ENABLED" = "1" ] || { log "Repo sync disabled"; exit 0; } + +VERSION_NUM="${VERSION#v}" +TMP_DIR="/tmp/repo-sync-$$" + +log "Starting sync from $GITHUB_REPO $VERSION" + +mkdir -p "$TMP_DIR" +mkdir -p "$REPO_DIR/packages" "$REPO_DIR/luci" "$REPO_DIR/catalog" +cd "$TMP_DIR" + +# Architecture mappings: github-arch:opkg-arch +ARCHS="x86-64:x86_64 aarch64-generic:aarch64_generic aarch64-cortex-a72:aarch64_cortex-a72 rockchip-armv8:aarch64_generic mips-24kc:mips_24kc mipsel-24kc:mipsel_24kc" + +for arch_map in $ARCHS; do + ARCH="${arch_map%%:*}" + OPKG_ARCH="${arch_map##*:}" + TARBALL="secubox-${VERSION_NUM}-${ARCH}.tar.gz" + URL="https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/${TARBALL}" + + log "Downloading $TARBALL..." + if wget -q -O "$TARBALL" "$URL" 2>/dev/null; then + mkdir -p "$REPO_DIR/packages/$OPKG_ARCH" + mkdir -p "$REPO_DIR/luci/$OPKG_ARCH" + + # Extract + mkdir -p "extract-$ARCH" + tar -xzf "$TARBALL" -C "extract-$ARCH" 2>/dev/null + + # Sort packages + find "extract-$ARCH" -name '*.ipk' | while read pkg; do + PKG_NAME="$(basename "$pkg")" + if echo "$PKG_NAME" | grep -q '^luci-'; then + cp "$pkg" "$REPO_DIR/luci/$OPKG_ARCH/" + else + cp "$pkg" "$REPO_DIR/packages/$OPKG_ARCH/" + fi + done + + log " Extracted to $OPKG_ARCH" + else + log " Skipping $ARCH (not found)" + fi +done + +# Generate Packages index +log "Generating opkg indexes..." +for basedir in "$REPO_DIR/packages" "$REPO_DIR/luci"; do + for dir in "$basedir"/*; do + [ -d "$dir" ] || continue + cd "$dir" + + rm -f Packages Packages.gz + + for ipk in *.ipk 2>/dev/null; do + [ -f "$ipk" ] || continue + SIZE=$(stat -c%s "$ipk" 2>/dev/null || ls -l "$ipk" | awk '{print $5}') + MD5=$(md5sum "$ipk" | cut -d' ' -f1) + PKG=$(echo "$ipk" | sed 's/_.*//g') + + echo "Package: $PKG" + echo "Version: 0.0.0-r1" + echo "Architecture: all" + echo "Filename: $ipk" + echo "Size: $SIZE" + echo "MD5Sum: $MD5" + echo "" + done > Packages + + gzip -9c Packages > Packages.gz + log " $(basename "$dir"): $(grep -c '^Package:' Packages 2>/dev/null || echo 0) packages" + done +done + +# Create index.html +cat > "$REPO_DIR/index.html" << 'HTML' + +SecuBox Package Repository + + + +

SecuBox Package Repository

+

Add to /etc/opkg/customfeeds.conf:

+
src/gz secubox_packages https://repo.secubox.in/packages/{ARCH}
+src/gz secubox_luci https://repo.secubox.in/luci/{ARCH}
+

Architectures

+ + +HTML + +# Cleanup +cd / +rm -rf "$TMP_DIR" + +# Update last sync time +uci set repo.main.last_sync="$(date -Iseconds)" +uci commit repo + +log "Sync complete" diff --git a/package/secubox/secubox-app-repo/root/usr/sbin/repoctl b/package/secubox/secubox-app-repo/root/usr/sbin/repoctl new file mode 100755 index 00000000..4db907fe --- /dev/null +++ b/package/secubox/secubox-app-repo/root/usr/sbin/repoctl @@ -0,0 +1,156 @@ +#!/bin/sh +# SecuBox Package Repository Control Tool + +. /lib/functions.sh + +REPO_DIR="/srv/repo.secubox.in" +CONFIG="repo" + +usage() { + cat << EOF +Usage: repoctl [options] + +Commands: + status Show repository status + sync [version] Sync packages from GitHub release + start Start repository server + stop Stop repository server + restart Restart repository server + list [arch] List packages for architecture + config Show current configuration + set Set configuration value + +Configuration keys: + enabled Enable/disable repo (0/1) + version Release version to sync (e.g., v1.0.0-beta) + github_repo GitHub repository (e.g., gkerma/secubox-openwrt) + port Server port (default: 8888) + auto_sync Enable auto-sync cron (0/1) + sync_interval Sync interval in hours (default: 6) + +Examples: + repoctl status + repoctl sync v1.0.0-beta + repoctl list x86_64 + repoctl set version v1.0.1 +EOF + exit 1 +} + +cmd_status() { + config_load "$CONFIG" + config_get enabled main enabled "1" + config_get version main version "unknown" + config_get github_repo main github_repo "gkerma/secubox-openwrt" + config_get port main port "8888" + config_get last_sync main last_sync "never" + config_get auto_sync main auto_sync "0" + + echo "SecuBox Package Repository Status" + echo "==================================" + echo "Enabled: $enabled" + echo "Version: $version" + echo "GitHub Repo: $github_repo" + echo "Server Port: $port" + echo "Last Sync: $last_sync" + echo "Auto Sync: $auto_sync" + echo "" + + # Check server status + if netstat -tln 2>/dev/null | grep -q ":$port "; then + echo "Server: RUNNING (port $port)" + else + echo "Server: STOPPED" + fi + + # Package counts + echo "" + echo "Packages:" + for dir in "$REPO_DIR/luci"/*; do + [ -d "$dir" ] || continue + arch=$(basename "$dir") + count=$(ls "$dir"/*.ipk 2>/dev/null | wc -l) + echo " $arch: $count packages" + done +} + +cmd_sync() { + local version="${1:-}" + if [ -n "$version" ]; then + uci set "$CONFIG.main.version=$version" + uci commit "$CONFIG" + fi + /usr/sbin/repo-sync +} + +cmd_start() { + /etc/init.d/repo-server start + echo "Repository server started" +} + +cmd_stop() { + /etc/init.d/repo-server stop + echo "Repository server stopped" +} + +cmd_restart() { + /etc/init.d/repo-server restart + echo "Repository server restarted" +} + +cmd_list() { + local arch="${1:-x86_64}" + local dir="$REPO_DIR/luci/$arch" + + if [ ! -d "$dir" ]; then + echo "Architecture '$arch' not found" + echo "Available: $(ls "$REPO_DIR/luci" 2>/dev/null | tr '\n' ' ')" + exit 1 + fi + + echo "Packages for $arch:" + ls "$dir"/*.ipk 2>/dev/null | while read pkg; do + basename "$pkg" + done +} + +cmd_config() { + uci show "$CONFIG" +} + +cmd_set() { + local key="$1" + local value="$2" + + [ -z "$key" ] || [ -z "$value" ] && usage + + uci set "$CONFIG.main.$key=$value" + uci commit "$CONFIG" + echo "Set $key = $value" + + # Handle auto_sync changes + if [ "$key" = "auto_sync" ]; then + if [ "$value" = "1" ]; then + config_get interval main sync_interval "6" + echo "0 */$interval * * * /usr/sbin/repo-sync >/dev/null 2>&1" > /etc/cron.d/repo-sync + echo "Enabled auto-sync every $interval hours" + else + rm -f /etc/cron.d/repo-sync + echo "Disabled auto-sync" + fi + /etc/init.d/cron restart 2>/dev/null + fi +} + +# Main +case "$1" in + status) cmd_status ;; + sync) cmd_sync "$2" ;; + start) cmd_start ;; + stop) cmd_stop ;; + restart) cmd_restart ;; + list) cmd_list "$2" ;; + config) cmd_config ;; + set) cmd_set "$2" "$3" ;; + *) usage ;; +esac diff --git a/package/secubox/secubox-app-repo/root/usr/share/rpcd/acl.d/luci-app-repo.json b/package/secubox/secubox-app-repo/root/usr/share/rpcd/acl.d/luci-app-repo.json new file mode 100644 index 00000000..6b439b32 --- /dev/null +++ b/package/secubox/secubox-app-repo/root/usr/share/rpcd/acl.d/luci-app-repo.json @@ -0,0 +1,17 @@ +{ + "luci-app-repo": { + "description": "SecuBox Package Repository", + "read": { + "ubus": { + "luci.repo": ["status", "config", "packages", "logs"] + }, + "uci": ["repo"] + }, + "write": { + "ubus": { + "luci.repo": ["sync"] + }, + "uci": ["repo"] + } + } +}