mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-30 15:45:13 +08:00
💐 Sync 2026-06-25 20:51:16
This commit is contained in:
1 parent
3232f1d574
commit
7a3941cfa5
18 files changed
+734
-55
No files matched your search
@@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-run
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=6
|
||||
PKG_RELEASE:=7
|
||||
|
||||
LUCI_TITLE:=LuCI support for running installers and packages
|
||||
LUCI_DESCRIPTION:=Upload and execute .run, .sh, .ipk and .apk files from LuCI.
|
||||
|
||||
@@ -28,7 +28,11 @@
|
||||
"args_hint": "您可以输入脚本参数或留空",
|
||||
"cancel": "取消",
|
||||
"confirm": "确定",
|
||||
"auto_cleaned": "执行完毕,已自动清理临时文件。"
|
||||
"auto_cleaned": "执行完毕,已自动清理临时文件。",
|
||||
"download_run": "下载并执行 .run",
|
||||
"download_url": "请输入 .run 文件的下载地址",
|
||||
"downloading": "正在下载...",
|
||||
"only_run": "仅支持 .run 文件下载"
|
||||
},
|
||||
"en": {
|
||||
"title": "Run Installer",
|
||||
@@ -59,6 +63,10 @@
|
||||
"args_hint": "You can enter script arguments or leave it blank",
|
||||
"cancel": "Cancel",
|
||||
"confirm": "OK",
|
||||
"auto_cleaned": "Execution complete, temporary files cleaned automatically."
|
||||
"auto_cleaned": "Execution complete, temporary files cleaned automatically.",
|
||||
"download_run": "Download and execute .run",
|
||||
"download_url": "Enter .run file download URL",
|
||||
"downloading": "Downloading...",
|
||||
"only_run": "Only .run files are supported"
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,11 @@ function getDefaultText(key) {
|
||||
'args_hint': '您可以输入脚本参数或留空',
|
||||
'cancel': '取消',
|
||||
'confirm': '确定',
|
||||
'auto_cleaned': '执行完毕,已自动清理临时文件。'
|
||||
'auto_cleaned': '执行完毕,已自动清理临时文件。',
|
||||
'download_run': '下载并执行 .run',
|
||||
'download_url': '请输入 .run 文件的下载地址',
|
||||
'downloading': '正在下载...',
|
||||
'only_run': '仅支持 .run 文件下载'
|
||||
};
|
||||
return defaults[key] || null;
|
||||
}
|
||||
@@ -144,6 +148,12 @@ var cleanup = rpc.declare({
|
||||
method: 'cleanup'
|
||||
});
|
||||
|
||||
var downloadRun = rpc.declare({
|
||||
object: 'luci-app-run',
|
||||
method: 'download_run',
|
||||
params: ['url']
|
||||
});
|
||||
|
||||
function formatBytes(size) {
|
||||
if (size >= 1024 * 1024)
|
||||
return '%.1f MiB'.format(size / 1024 / 1024);
|
||||
@@ -290,6 +300,20 @@ return view.extend({
|
||||
}
|
||||
}, [_('choose_apk')]);
|
||||
|
||||
var downloadButton = E('button', {
|
||||
class: 'cbi-button cbi-button-action run-btn',
|
||||
style: 'margin-left:10px;background:#E65100!important;background-color:#E65100!important;background-image:none!important;color:#fff!important;border-color:#E65100!important;box-shadow:none!important;text-shadow:none!important;opacity:1!important',
|
||||
click: function (ev) {
|
||||
ev.preventDefault();
|
||||
self.showDownloadDialog(function (url) {
|
||||
if (url !== null) {
|
||||
log.textContent = '';
|
||||
self.startDownload(runButton, state, url.trim());
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [_('download_run')]);
|
||||
|
||||
var runButton = E('button', {
|
||||
class: 'cbi-button cbi-button-action run-btn',
|
||||
disabled: true,
|
||||
@@ -354,7 +378,7 @@ return view.extend({
|
||||
E('h3', [_('upload_title')]),
|
||||
E('p', [state]),
|
||||
E('p', [pickButton, ipkButton, apkButton]),
|
||||
E('p', { style: 'margin-top:10px' }, [runButton, cleanButton]),
|
||||
E('p', { style: 'margin-top:10px' }, [downloadButton, runButton, cleanButton]),
|
||||
progress,
|
||||
fileInput,
|
||||
ipkInput,
|
||||
@@ -543,6 +567,81 @@ return view.extend({
|
||||
document.getElementById('run-args-dialog-input').focus();
|
||||
},
|
||||
|
||||
showDownloadDialog: function (callback) {
|
||||
var dialog = E('div', {
|
||||
style: 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:9999'
|
||||
}, [
|
||||
E('div', {
|
||||
style: 'background:#fff;border-radius:8px;padding:20px;width:400px;box-shadow:0 4px 20px rgba(0,0,0,0.3)'
|
||||
}, [
|
||||
E('input', {
|
||||
type: 'text',
|
||||
placeholder: _('download_url'),
|
||||
style: 'width:100%;padding:10px;margin-bottom:15px;border:1px solid #ccc;border-radius:4px;box-sizing:border-box;font-size:14px',
|
||||
id: 'run-download-url-input'
|
||||
}),
|
||||
E('div', {
|
||||
style: 'display:flex;justify-content:flex-end;gap:10px'
|
||||
}, [
|
||||
E('button', {
|
||||
class: 'cbi-button cbi-button-reset',
|
||||
style: 'padding:8px 20px;text-transform:none',
|
||||
click: function () {
|
||||
document.body.removeChild(dialog);
|
||||
callback(null);
|
||||
}
|
||||
}, [_('cancel')]),
|
||||
E('button', {
|
||||
class: 'cbi-button cbi-button-action',
|
||||
style: 'padding:8px 20px;text-transform:none',
|
||||
click: function () {
|
||||
var url = document.getElementById('run-download-url-input').value.trim();
|
||||
if (!url) {
|
||||
document.body.removeChild(dialog);
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
var filename = url.split('/').pop().split('?')[0];
|
||||
if (!filename.match(/\.run$/i)) {
|
||||
ui.addNotification(null, E('p', [_('only_run')]), 'danger');
|
||||
return;
|
||||
}
|
||||
|
||||
document.body.removeChild(dialog);
|
||||
callback(url);
|
||||
}
|
||||
}, [_('confirm')])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
document.body.appendChild(dialog);
|
||||
document.getElementById('run-download-url-input').focus();
|
||||
},
|
||||
|
||||
startDownload: function (runButton, state, url) {
|
||||
var self = this;
|
||||
|
||||
runButton.disabled = true;
|
||||
state.textContent = _('downloading');
|
||||
self.prevRunning = false;
|
||||
self.autoCleanType = null;
|
||||
|
||||
return downloadRun(url).then(function (res) {
|
||||
if (res && res.error) {
|
||||
throw new Error(res.error);
|
||||
}
|
||||
|
||||
self.logOffset = 0;
|
||||
self.prevRunning = true;
|
||||
state.textContent = _('started', res.pid);
|
||||
}).catch(function (err) {
|
||||
runButton.disabled = false;
|
||||
ui.addNotification(null, E('p', [err.message || err]), 'danger');
|
||||
});
|
||||
},
|
||||
|
||||
refreshLog: function (log, state) {
|
||||
var self = this;
|
||||
|
||||
|
||||
@@ -75,11 +75,13 @@ write_state() {
|
||||
local file="$2"
|
||||
local log="$3"
|
||||
local started="$4"
|
||||
local type="$5"
|
||||
{
|
||||
printf 'PID=%s\n' "$pid"
|
||||
printf 'FILE=%s\n' "$file"
|
||||
printf 'LOG=%s\n' "$log"
|
||||
printf 'STARTED=%s\n' "$started"
|
||||
printf 'TYPE=%s\n' "$type"
|
||||
} > "$STATE_FILE"
|
||||
}
|
||||
|
||||
@@ -136,6 +138,9 @@ method_list() {
|
||||
"read_log": {
|
||||
"offset": "Integer"
|
||||
},
|
||||
"download_run": {
|
||||
"url": "String"
|
||||
},
|
||||
"cleanup": {},
|
||||
"version": {},
|
||||
"capabilities": {}
|
||||
@@ -236,6 +241,86 @@ upload_finish() {
|
||||
json_dump
|
||||
}
|
||||
|
||||
download_run() {
|
||||
load_input
|
||||
get_string url
|
||||
|
||||
[ -n "$url" ] || { json_error "Missing URL"; return; }
|
||||
|
||||
local filename id dir file log work pid now
|
||||
|
||||
# Extract filename from URL - get everything after the last /
|
||||
filename="${url##*/}"
|
||||
# Remove query parameters if any
|
||||
filename="${filename%%\?*}"
|
||||
|
||||
case "$filename" in
|
||||
*.run) ;;
|
||||
*) json_error "Only .run files are supported for download"; return ;;
|
||||
esac
|
||||
|
||||
read_state
|
||||
if [ -n "$PID" ] && ! is_running "$PID"; then
|
||||
rm -f "$STATE_FILE"
|
||||
unset PID
|
||||
fi
|
||||
|
||||
if is_running "$PID"; then
|
||||
json_error "Another installer is already running"
|
||||
return
|
||||
fi
|
||||
|
||||
id="download_$(date +%s)"
|
||||
dir="$RUN_DIR/$id"
|
||||
file="$dir/$filename"
|
||||
log="$dir/output.log"
|
||||
|
||||
mkdir -p "$dir" || { json_error "Unable to create download directory"; return; }
|
||||
|
||||
now="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
|
||||
(
|
||||
cd "$dir" || exit 1
|
||||
printf 'luci-app-run: started %s\n' "$now"
|
||||
printf 'luci-app-run: downloading %s\n\n' "$url"
|
||||
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -L -o "$filename" "$url"
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -O "$filename" "$url"
|
||||
else
|
||||
printf 'luci-app-run: error: neither curl nor wget is available\n'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rc=$?
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
printf '\nluci-app-run: download failed with status %s at %s\n' "$rc" "$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
exit "$rc"
|
||||
fi
|
||||
|
||||
printf '\nluci-app-run: download completed at %s\n' "$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
printf '\nluci-app-run: executing %s\n\n' "$file"
|
||||
|
||||
chmod 700 "$file" 2>/dev/null || true
|
||||
|
||||
"$file"
|
||||
rc=$?
|
||||
printf '\nluci-app-run: exited with status %s at %s\n' "$rc" "$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
exit "$rc"
|
||||
) > "$log" 2>&1 &
|
||||
|
||||
pid="$!"
|
||||
write_state "$pid" "$file" "$log" "$now" "download"
|
||||
|
||||
json_init
|
||||
json_add_int code 0
|
||||
json_add_int pid "$pid"
|
||||
json_add_string log "$log"
|
||||
json_add_string filename "$filename"
|
||||
json_dump
|
||||
}
|
||||
|
||||
run_installer() {
|
||||
load_input
|
||||
get_string id
|
||||
@@ -317,7 +402,7 @@ run_installer() {
|
||||
esac
|
||||
|
||||
pid="$!"
|
||||
write_state "$pid" "$file" "$log" "$now"
|
||||
write_state "$pid" "$file" "$log" "$now" "upload"
|
||||
|
||||
json_init
|
||||
json_add_int code 0
|
||||
@@ -433,6 +518,7 @@ case "$1" in
|
||||
upload_chunk) upload_chunk ;;
|
||||
upload_finish) upload_finish ;;
|
||||
run) run_installer ;;
|
||||
download_run) download_run ;;
|
||||
status) status ;;
|
||||
read_log) read_log ;;
|
||||
cleanup) cleanup ;;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"upload_chunk",
|
||||
"upload_finish",
|
||||
"run",
|
||||
"download_run",
|
||||
"cleanup"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user