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 <noreply@anthropic.com>
This commit is contained in:
parent
276685f109
commit
9cd59b77ba
26
package/secubox/luci-app-repo/Makefile
Normal file
26
package/secubox/luci-app-repo/Makefile
Normal file
@ -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))
|
||||||
@ -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
|
||||||
|
});
|
||||||
@ -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}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
package/secubox/secubox-app-repo/Makefile
Normal file
60
package/secubox/secubox-app-repo/Makefile
Normal file
@ -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))
|
||||||
8
package/secubox/secubox-app-repo/root/etc/config/repo
Normal file
8
package/secubox/secubox-app-repo/root/etc/config/repo
Normal file
@ -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 ''
|
||||||
29
package/secubox/secubox-app-repo/root/etc/init.d/repo-server
Executable file
29
package/secubox/secubox-app-repo/root/etc/init.d/repo-server
Executable file
@ -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"
|
||||||
|
}
|
||||||
137
package/secubox/secubox-app-repo/root/usr/libexec/rpcd/luci.repo
Executable file
137
package/secubox/secubox-app-repo/root/usr/libexec/rpcd/luci.repo
Executable file
@ -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
|
||||||
131
package/secubox/secubox-app-repo/root/usr/sbin/repo-sync
Executable file
131
package/secubox/secubox-app-repo/root/usr/sbin/repo-sync
Executable file
@ -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'
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html><head><title>SecuBox Package Repository</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; max-width: 800px; margin: 2em auto; padding: 0 1em; }
|
||||||
|
code { background: #f0f0f0; padding: 2px 6px; border-radius: 3px; }
|
||||||
|
pre { background: #f0f0f0; padding: 1em; overflow-x: auto; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>SecuBox Package Repository</h1>
|
||||||
|
<p>Add to <code>/etc/opkg/customfeeds.conf</code>:</p>
|
||||||
|
<pre>src/gz secubox_packages https://repo.secubox.in/packages/{ARCH}
|
||||||
|
src/gz secubox_luci https://repo.secubox.in/luci/{ARCH}</pre>
|
||||||
|
<h2>Architectures</h2>
|
||||||
|
<ul>
|
||||||
|
<li><a href="luci/x86_64/">x86_64</a> - x86-64 VMs</li>
|
||||||
|
<li><a href="luci/aarch64_cortex-a72/">aarch64_cortex-a72</a> - Raspberry Pi 4</li>
|
||||||
|
<li><a href="luci/aarch64_generic/">aarch64_generic</a> - NanoPi R4S/R5S</li>
|
||||||
|
<li><a href="luci/mips_24kc/">mips_24kc</a> - Atheros/QCA</li>
|
||||||
|
<li><a href="luci/mipsel_24kc/">mipsel_24kc</a> - MT7621</li>
|
||||||
|
</ul>
|
||||||
|
</body></html>
|
||||||
|
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"
|
||||||
156
package/secubox/secubox-app-repo/root/usr/sbin/repoctl
Executable file
156
package/secubox/secubox-app-repo/root/usr/sbin/repoctl
Executable file
@ -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 <command> [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 <key> <val> 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
|
||||||
@ -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"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user