You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

232 lines
8.4 KiB
JavaScript

(function(){
const taskd={};
const $gettext = function(str) {
return taskd.i18n[str] || str;
};
const retryPromise = function(fn) {
return new Promise((resolve, reject) => {
const retry = function() {
fn(resolve, reject, retry);
};
retry();
});
};
const retry403XHR = function(url, method, responseType) {
return retryPromise((resolve, reject, retry) => {
var oReq = new XMLHttpRequest();
oReq.onerror = reject;
oReq.open(method || 'GET', url, true);
if (responseType) {
oReq.responseType = responseType;
}
oReq.onload = function (oEvent) {
if (oReq.status == 403) {
alert($gettext("Lost login status"));
location.href = location.href;
} else if (oReq.status == 404) {
reject(oEvent);
} else {
resolve(oReq);
}
};
if (method=='POST') {
oReq.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
}
oReq.send(method=='POST'?("token="+taskd.csrfToken):null);
});
};
const request = function(url, method) {
return retry403XHR(url, method).then(oReq => oReq.responseText);
};
const getBin = function(url) {
return retry403XHR(url, null, "arraybuffer").then(oReq => {return {status: oReq.status, buffer: new Uint8Array(oReq.response)}});
};
const getTaskDetail = function(task_id) {
return request("/cgi-bin/luci/admin/system/tasks/status?task_id="+task_id).then(data=>JSON.parse(data));
};
const create_dialog = function(cfg) {
const container = document.createElement('div');
container.id = "tasks_detail_container";
container.innerHTML = taskd.dialog_template;
document.body.appendChild(container);
const title_view = container.querySelector(".dialog-title-bar .dialog-title");
title_view.innerText = cfg.title;
const term = new Terminal({convertEol: cfg.convertEol||false});
if (cfg.nohide) {
container.querySelector(".dialog-icon-min").hidden = true;
} else {
container.querySelector(".dialog-icon-min").onclick = function(){
container.hidden=true;
term.dispose();
document.body.removeChild(container);
cfg.onhide && cfg.onhide();
return false;
};
}
term.open(document.getElementById("tasks_xterm_log"));
return {term,container};
};
const show_log_txt = function(title, content, onclose) {
const dialog = create_dialog({title, convertEol:true, onhine:onclose});
const container = dialog.container;
const term = dialog.term;
container.querySelector(".dialog-icon-close").hidden = true;
term.write(content);
};
const show_log = function(task_id, nohide) {
let showing = true;
let running = true;
const dialog = create_dialog({title:task_id, nohide, onhide:function(){showing=false;}});
const container = dialog.container;
const term = dialog.term;
const title_view = container.querySelector(".dialog-title-bar .dialog-title");
container.querySelector(".dialog-icon-close").onclick = function(){
if (!running || confirm($gettext("Stop running task?"))) {
running=false;
showing=false;
del_task(task_id).then(()=>{
location.href = location.href;
});
}
return false;
};
const checkTask = function() {
return getTaskDetail(task_id).then(data=>{
if (!running) {
return false;
}
running = data.running;
let title = task_id;
if (!data.running && data.stop) {
title += " (" + (data.exit_code?$gettext("Failed at:"):$gettext("Finished at:")) + " " + new Date(data.stop * 1000).toLocaleString() + ")";
}
title += " > " + (data.command || '');
title_view.title = title;
title_view.innerText = title;
if (!data.running) {
container.classList.add('tasks_stopped');
if (data.exit_code) {
container.classList.add('tasks_failed');
}
}
// last pull
return showing;
});
};
let logoffset = 0;
const pulllog = function(check) {
let starter = Promise.resolve(showing);
if (check) {
starter = checkTask();
}
starter.then(again => {
if (again)
return getBin("/cgi-bin/luci/admin/system/tasks/log?task_id="+task_id+"&offset="+logoffset);
else
return {status: 204};
}).then(function(res){
if (!showing) {
return false;
}
switch(res.status){
case 205:
term.reset();
logoffset = 0;
return running;
break;
case 204:
return running && checkTask();
break;
case 200:
logoffset += res.buffer.byteLength;
term.write(res.buffer);
return running;
break;
}
}).then(again => {
if (again) {
setTimeout(pulllog, 0);
}
}).catch(err => {
if (showing) {
if (err.target.status == 0) {
title_view.innerText = task_id + ' (' + $gettext("Fetch log failed, retrying...") + ')';
setTimeout(()=>pulllog(true), 1000);
} else if (err.target.status == 403 || err.target.status == 404) {
title_view.innerText = task_id + ' (' + $gettext(err.target.status == 403?"Lost login status":"Task does not exist or has been deleted") + ')';
container.querySelector(".dialog-icon-close").hidden = true;
container.classList.add('tasks_unknown');
} else {
console.error(err);
}
}
});
};
pulllog(true);
};
const del_task = function(task_id) {
return request("/cgi-bin/luci/admin/system/tasks/stop?task_id="+task_id, "POST");
};
taskd.show_log = show_log;
taskd.remove = del_task;
taskd.show_log_txt = show_log_txt;
window.taskd=taskd;
})();
(function(){
// compat
if (typeof(window.findParent) !== 'function') {
const elem = function(e) {
return (e != null && typeof(e) == 'object' && 'nodeType' in e);
};
const matches = function(node, selector) {
var m = elem(node) ? node.matches || node.msMatchesSelector : null;
return m ? m.call(node, selector) : false;
};
window.findParent = function (node, selector) {
if (elem(node) && node.closest)
return node.closest(selector);
while (elem(node))
if (matches(node, selector))
return node;
else
node = node.parentNode;
return null;
};
}
if (typeof(window.cbi_submit) !== 'function') {
const makeHidden = function(name) {
const input = document.createElement('input');
input.type = 'hidden';
input.name = name;
return input;
};
window.cbi_submit = function(elem, name, value, action) {
var form = elem.form || findParent(elem, 'form');
if (!form)
return false;
if (action)
form.action = action;
if (name) {
var hidden = form.querySelector('input[type="hidden"][name="%s"]'.format(name)) ||
makeHidden(name);
hidden.value = value || '1';
form.appendChild(hidden);
}
form.submit();
return true;
};
}
})();