luci-base: add responseProgress callback and stderr option

for LuCI.request and `fs.exec_direct()`.

This commit adds XHR response progress event option in luci.js and fs.js.
And diagnostics.js will use this new event to update command output.

This commit also adds a `stderr` option in `fs.exec_direct()`, and
requires a patch for `cgi-io`.

Currently `cgi-io` redirects stderr to /dev/null, which makes user
unable to see error messages. The patch adds a `stderr` option,
and if it's set to `1`, redirects stderr to stdout.

Signed-off-by: Richard Yu <yurichard3839@gmail.com>
Link: https://github.com/openwrt/luci/pull/7920
This commit is contained in:
Richard Yu
2025-09-01 22:39:47 +08:00
committed by Paul Donald
parent fdef3c0d0c
commit ece28ab5a4
2 changed files with 21 additions and 4 deletions

View File

@@ -397,12 +397,22 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
* is usually not needed but can be useful for programs that cannot * is usually not needed but can be useful for programs that cannot
* handle UTF-8 input. * handle UTF-8 input.
* *
* @param {boolean} [stderr=false]
* Whether to include stderr output in command output. This is usually
* not needed but can be useful to execute a command and get full output.
*
* @param {function} [responseProgress=null]
* An optional request callback function which receives ProgressEvent
* instances as sole argument during the HTTP response transfer. This is
* usually not needed but can be useful to receive realtime command
* output before command exit.
*
* @returns {Promise<*>} * @returns {Promise<*>}
* Returns a promise resolving with the command stdout output interpreted * Returns a promise resolving with the command stdout output interpreted
* according to the specified type or rejecting with an error stating the * according to the specified type or rejecting with an error stating the
* failure reason. * failure reason.
*/ */
exec_direct: function(command, params, type, latin1) { exec_direct: function(command, params, type, latin1, stderr, responseProgress) {
var cmdstr = String(command) var cmdstr = String(command)
.replace(/\\/g, '\\\\').replace(/(\s)/g, '\\$1'); .replace(/\\/g, '\\\\').replace(/(\s)/g, '\\$1');
@@ -416,12 +426,12 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
else else
cmdstr = encodeURIComponent(cmdstr); cmdstr = encodeURIComponent(cmdstr);
var postdata = 'sessionid=%s&command=%s' var postdata = 'sessionid=%s&command=%s&stderr=%d'
.format(encodeURIComponent(L.env.sessionid), cmdstr); .format(encodeURIComponent(L.env.sessionid), cmdstr, stderr ? 1 : 0);
return request.post(L.env.cgi_base + '/cgi-exec', postdata, { return request.post(L.env.cgi_base + '/cgi-exec', postdata, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: (type == 'blob') ? 'blob' : 'text' responseType: (type == 'blob') ? 'blob' : 'text', responseProgress
}).then(handleCgiIoReply.bind({ type: type })); }).then(handleCgiIoReply.bind({ type: type }));
} }
}); });

View File

@@ -634,6 +634,10 @@
* @property {function} [progress] * @property {function} [progress]
* An optional request callback function which receives ProgressEvent * An optional request callback function which receives ProgressEvent
* instances as sole argument during the HTTP request transfer. * instances as sole argument during the HTTP request transfer.
*
* @property {function} [responseProgress]
* An optional request callback function which receives ProgressEvent
* instances as sole argument during the HTTP response transfer.
*/ */
/** /**
@@ -752,6 +756,9 @@
if ('progress' in opt && 'upload' in opt.xhr) if ('progress' in opt && 'upload' in opt.xhr)
opt.xhr.upload.addEventListener('progress', opt.progress); opt.xhr.upload.addEventListener('progress', opt.progress);
if (opt.responseProgress != null)
opt.xhr.addEventListener('progress', opt.responseProgress);
if (contenttype != null) if (contenttype != null)
opt.xhr.setRequestHeader('Content-Type', contenttype); opt.xhr.setRequestHeader('Content-Type', contenttype);