first commit
commit
b0b6317b87
@ -0,0 +1,18 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2022 jjm2473 <jjm2473@gmail.com>
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the MIT License.
|
||||||
|
#
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_TITLE:=Task library
|
||||||
|
LUCI_DEPENDS:=+luci-lib-xterm +taskd
|
||||||
|
LUCI_EXTRA_DEPENDS:=taskd (>=1.0.3-1)
|
||||||
|
LUCI_PKGARCH:=all
|
||||||
|
|
||||||
|
PKG_MAINTAINER:=jjm2473 <jjm2473@gmail.com>
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
# call BuildPackage - OpenWrt buildroot signature
|
@ -0,0 +1,112 @@
|
|||||||
|
[hidden] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tasks_detail_container {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1000;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #0008;
|
||||||
|
}
|
||||||
|
#tasks_dialog {
|
||||||
|
position: absolute;
|
||||||
|
width: 770px;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background-color: #000;
|
||||||
|
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 2px 2px 6px #000a;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.dialog-title-bar {
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-right: -10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.dialog-title-bar .dialog-title {
|
||||||
|
word-break: break-all;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.dialog-content {
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
margin-right: -10px;
|
||||||
|
}
|
||||||
|
.dialog-icons {
|
||||||
|
align-self: center;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.dialog-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
border-radius: 50%;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
user-select: none;
|
||||||
|
margin-left: 10px;
|
||||||
|
line-height: 1;
|
||||||
|
font-family: sans-serif;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.dialog-icon.dialog-icon-min {
|
||||||
|
background-color: darkorange;
|
||||||
|
}
|
||||||
|
.dialog-icon.dialog-icon-close {
|
||||||
|
background-color: #ff5f56;
|
||||||
|
}
|
||||||
|
.dialog-icons:hover .dialog-icon.dialog-icon-min:before {
|
||||||
|
content: "_";
|
||||||
|
}
|
||||||
|
.dialog-icons:hover .dialog-icon.dialog-icon-close:before {
|
||||||
|
content: "X";
|
||||||
|
}
|
||||||
|
|
||||||
|
.tasks_stopped .dialog-icon.dialog-icon-close {
|
||||||
|
background-color: #27c840;
|
||||||
|
}
|
||||||
|
.tasks_stopped #tasks_dialog, .tasks_unknown #tasks_dialog {
|
||||||
|
padding: 19px;
|
||||||
|
border: 1px #27c840 solid;
|
||||||
|
|
||||||
|
animation: border-blink 1s;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tasks_failed #tasks_dialog {
|
||||||
|
border-color: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tasks_failed .dialog-icon.dialog-icon-close {
|
||||||
|
background-color: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tasks_unknown #tasks_dialog {
|
||||||
|
border-color: darkorange;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes border-blink { 50% { border-color:#fff ; } }
|
@ -0,0 +1,232 @@
|
|||||||
|
|
||||||
|
(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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
@ -0,0 +1,92 @@
|
|||||||
|
|
||||||
|
module("luci.controller.tasks-lib", package.seeall)
|
||||||
|
|
||||||
|
|
||||||
|
function index()
|
||||||
|
entry({"admin", "system", "tasks", "status"}, call("tasks_status")).dependent=false
|
||||||
|
entry({"admin", "system", "tasks", "log"}, call("tasks_log")).dependent=false
|
||||||
|
entry({"admin", "system", "tasks", "stop"}, post("tasks_stop")).dependent=false
|
||||||
|
end
|
||||||
|
|
||||||
|
local util = require "luci.util"
|
||||||
|
local jsonc = require "luci.jsonc"
|
||||||
|
local ltn12 = require "luci.ltn12"
|
||||||
|
|
||||||
|
local taskd = require "luci.model.tasks"
|
||||||
|
|
||||||
|
function tasks_status()
|
||||||
|
local data = taskd.status(luci.http.formvalue("task_id"))
|
||||||
|
luci.http.prepare_content("application/json")
|
||||||
|
luci.http.write_json(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
function tasks_log()
|
||||||
|
local wait = 107
|
||||||
|
local task_id = luci.http.formvalue("task_id")
|
||||||
|
local offset = luci.http.formvalue("offset")
|
||||||
|
offset = offset and tonumber(offset) or 0
|
||||||
|
local logpath = "/var/log/tasks/"..task_id..".log"
|
||||||
|
local i
|
||||||
|
local logfd = io.open(logpath, "rb")
|
||||||
|
if logfd == nil then
|
||||||
|
luci.http.status(404)
|
||||||
|
luci.http.write("log not found")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local size = logfd:seek("end")
|
||||||
|
|
||||||
|
if size < offset then
|
||||||
|
luci.http.status(205, "Reset Content")
|
||||||
|
luci.http.write("reset offset")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while (i < wait)
|
||||||
|
do
|
||||||
|
if size > offset then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
nixio.nanosleep(0, 10000000) -- sleep 10ms
|
||||||
|
size = logfd:seek("end")
|
||||||
|
i = i+1
|
||||||
|
end
|
||||||
|
if i == wait then
|
||||||
|
logfd:close()
|
||||||
|
luci.http.status(204)
|
||||||
|
luci.http.prepare_content("application/octet-stream")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
logfd:seek("set", offset)
|
||||||
|
|
||||||
|
local write_log = function()
|
||||||
|
local buffer = logfd:read(4096)
|
||||||
|
if buffer and #buffer > 0 then
|
||||||
|
return buffer
|
||||||
|
else
|
||||||
|
logfd:close()
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
luci.http.prepare_content("application/octet-stream")
|
||||||
|
|
||||||
|
if logfd then
|
||||||
|
ltn12.pump.all(write_log, luci.http.write)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function tasks_stop()
|
||||||
|
local sys = require("luci.sys")
|
||||||
|
local task_id = luci.http.formvalue("task_id") or ""
|
||||||
|
if task_id == "" then
|
||||||
|
luci.http.status(400)
|
||||||
|
luci.http.write("task_id is empty")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if sys.call("/etc/init.d/tasks task_del "..task_id.." >/dev/null 2>&1") ~= 0 then
|
||||||
|
nixio.nanosleep(2, 10000000)
|
||||||
|
end
|
||||||
|
luci.http.status(204)
|
||||||
|
end
|
@ -0,0 +1,100 @@
|
|||||||
|
local util = require "luci.util"
|
||||||
|
local jsonc = require "luci.jsonc"
|
||||||
|
|
||||||
|
local taskd = {}
|
||||||
|
|
||||||
|
local function output(data)
|
||||||
|
local ret={}
|
||||||
|
ret.running=data.running
|
||||||
|
if not data.running then
|
||||||
|
ret.exit_code=data.exit_code
|
||||||
|
if nil == ret.exit_code then
|
||||||
|
if data["data"] and data["data"]["exit_code"] and data["data"]["exit_code"] ~= "" then
|
||||||
|
ret.exit_code=tonumber(data["data"]["exit_code"])
|
||||||
|
else
|
||||||
|
ret.exit_code=143
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
ret.command=data["command"] and data["command"][4] or '#'
|
||||||
|
if data["data"] then
|
||||||
|
ret.start=tonumber(data["data"]["start"])
|
||||||
|
if not data.running and data["data"]["stop"] then
|
||||||
|
ret.stop=tonumber(data["data"]["stop"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
taskd.status = function (task_id)
|
||||||
|
task_id = task_id or ""
|
||||||
|
local data = util.trim(util.exec("/etc/init.d/tasks task_status "..task_id.." 2>/dev/null")) or ""
|
||||||
|
if data ~= "" then
|
||||||
|
data = jsonc.parse(data)
|
||||||
|
else
|
||||||
|
if task_id == "" then
|
||||||
|
data = {}
|
||||||
|
else
|
||||||
|
data = {running=false, exit_code=404}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if task_id ~= "" then
|
||||||
|
return output(data)
|
||||||
|
end
|
||||||
|
local ary={}
|
||||||
|
for k, v in pairs(data) do
|
||||||
|
ary[k] = output(v)
|
||||||
|
end
|
||||||
|
return ary
|
||||||
|
end
|
||||||
|
|
||||||
|
taskd.docker_map = function(config, task_id, script_path, title, desc)
|
||||||
|
require("luci.cbi")
|
||||||
|
require("luci.http")
|
||||||
|
require("luci.sys")
|
||||||
|
local translate = require("luci.i18n").translate
|
||||||
|
local m
|
||||||
|
m = luci.cbi.Map(config, title, desc)
|
||||||
|
m.template = "tasks/docker"
|
||||||
|
-- hide default buttons
|
||||||
|
m.pageaction = false
|
||||||
|
-- we want hook 'on_after_apply' works, 'apply_on_parse' can be true (rollback) or false (no rollback),
|
||||||
|
-- but 'apply_on_parse' must be true for luci 17.01 and below
|
||||||
|
m.apply_on_parse = true
|
||||||
|
m.script_path = script_path
|
||||||
|
m.task_id = task_id
|
||||||
|
m.auto_show_task = true
|
||||||
|
m.on_before_apply = function(self)
|
||||||
|
if self.uci.rollback then
|
||||||
|
-- luci 18.06+ has 'rollback' function
|
||||||
|
-- rollback dialog will show because 'apply_on_parse' is true,
|
||||||
|
-- hide rollback dialog by hook 'apply' function
|
||||||
|
local apply = self.uci.apply
|
||||||
|
self.uci.apply = function(uci, rollback)
|
||||||
|
apply(uci, false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
m.on_after_apply = function(self)
|
||||||
|
local cmd
|
||||||
|
local action = luci.http.formvalue("cbi.apply") or "null"
|
||||||
|
if "upgrade" == action or "install" == action
|
||||||
|
or "start" == action or "stop" == action or "restart" == action or "rm" == action then
|
||||||
|
cmd = string.format("\"%s\" %s", script_path, action)
|
||||||
|
end
|
||||||
|
if cmd then
|
||||||
|
if luci.sys.call("/etc/init.d/tasks task_add " .. task_id .. " " .. luci.util.shellquote(cmd) .. " >/dev/null 2>&1") ~= 0 then
|
||||||
|
self.task_start_failed = true
|
||||||
|
self.message = translate("Config saved, but apply failed")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.message = translate("Unknown command: ") .. action
|
||||||
|
end
|
||||||
|
if self.message then
|
||||||
|
self.auto_show_task = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return m
|
||||||
|
end
|
||||||
|
|
||||||
|
return taskd
|
@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
<% if self.task_start_failed then %>
|
||||||
|
<div class="alert-message warning"><%:Another task running, try again later.%> <a href="javascript:void(taskd.show_log('<%=self.task_id%>'))"><%:Click here to check running task%></a></div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%+cbi/map%>
|
||||||
|
<%
|
||||||
|
local task_running = false
|
||||||
|
local taskd = require "luci.model.tasks"
|
||||||
|
local status = taskd.status(self.task_id)
|
||||||
|
task_running = status.running
|
||||||
|
-%>
|
||||||
|
<div class="cbi-page-actions control-group">
|
||||||
|
<%
|
||||||
|
if not task_running then
|
||||||
|
%>
|
||||||
|
<%
|
||||||
|
local util = require "luci.util"
|
||||||
|
local container_status = util.trim(util.exec(self.script_path.." status"))
|
||||||
|
local container_install = (string.len(container_status) > 0)
|
||||||
|
local container_running = container_status == "running"
|
||||||
|
if container_install then
|
||||||
|
-%>
|
||||||
|
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Upgrade%>/<%:Apply%>" onclick="cbi_submit(this, 'cbi.apply', 'upgrade')" />
|
||||||
|
<%
|
||||||
|
if container_running then
|
||||||
|
-%>
|
||||||
|
<input class="btn cbi-button cbi-button-remove" type="button" value="<%:Stop%>" onclick="cbi_submit(this, 'cbi.apply', 'stop')" />
|
||||||
|
|
||||||
|
<input class="btn cbi-button cbi-button-reload" type="button" value="<%:Restart%>" onclick="cbi_submit(this, 'cbi.apply', 'restart')" />
|
||||||
|
<% else %>
|
||||||
|
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Start%>" onclick="cbi_submit(this, 'cbi.apply', 'start')" />
|
||||||
|
|
||||||
|
<input class="btn cbi-button cbi-button-remove" type="button" value="<%:Remove%>" onclick="cbi_submit(this, 'cbi.apply', 'rm')" />
|
||||||
|
<% end
|
||||||
|
else %>
|
||||||
|
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Install%>" onclick="cbi_submit(this, 'cbi.apply', 'install')" />
|
||||||
|
<% end
|
||||||
|
else
|
||||||
|
%>
|
||||||
|
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Task Running%>…" onclick="taskd.show_log('<%=self.task_id%>')" />
|
||||||
|
<%
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%+tasks/embed%>
|
||||||
|
<%
|
||||||
|
if self.auto_show_task and task_running then
|
||||||
|
-%>
|
||||||
|
<script>
|
||||||
|
taskd.show_log("<%=self.task_id%>");
|
||||||
|
</script>
|
||||||
|
<%
|
||||||
|
end
|
||||||
|
%>
|
@ -0,0 +1,34 @@
|
|||||||
|
<%+xterm/embed%>
|
||||||
|
<link rel="stylesheet" href="<%=resource%>/tasks/tasks.css<%# ?v=PKG_VERSION %>">
|
||||||
|
<script src="<%=resource%>/tasks/tasks.js<%# ?v=PKG_VERSION %>"></script>
|
||||||
|
<%
|
||||||
|
local i18n = {}
|
||||||
|
local function tr(str)
|
||||||
|
i18n[str]=translate(str)
|
||||||
|
end
|
||||||
|
tr("Stop running task?")
|
||||||
|
tr("Stopped at:")
|
||||||
|
tr("Finished at:")
|
||||||
|
tr("Failed at:")
|
||||||
|
tr("Lost login status")
|
||||||
|
tr("Fetch log failed, retrying...")
|
||||||
|
tr("Task does not exist or has been deleted")
|
||||||
|
-%>
|
||||||
|
<script>
|
||||||
|
window.taskd.csrfToken="<%=token%>";
|
||||||
|
window.taskd.i18n=<% luci.http.write_json(i18n) %>;
|
||||||
|
window.taskd.dialog_template=`
|
||||||
|
<div id="tasks_dialog">
|
||||||
|
<div class="dialog-title-bar">
|
||||||
|
<span class="dialog-title" id="tasks_id"></span>
|
||||||
|
<span class="dialog-icons">
|
||||||
|
<span class="dialog-icon dialog-icon-close" title="<%:Stop and Remove%>"></span>
|
||||||
|
<span class="dialog-icon dialog-icon-min" title="<%:Hide%>"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="dialog-content">
|
||||||
|
<div id="tasks_xterm_log"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
</script>
|
@ -0,0 +1,15 @@
|
|||||||
|
clean:
|
||||||
|
compile:
|
||||||
|
|
||||||
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
|
LUCI_NAME:=luci-lib-dummy
|
||||||
|
INCLUDE_DIR:=./dummy
|
||||||
|
|
||||||
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|
||||||
|
install:
|
||||||
|
mkdir -p "$(DESTDIR)$(LUCI_LIBRARYDIR)/i18n"
|
||||||
|
$(foreach lang,$(LUCI_LANGUAGES),$(foreach po,$(wildcard ${CURDIR}/po/$(lang)/*.po), \
|
||||||
|
po2lmo $(po) \
|
||||||
|
$(DESTDIR)$(LUCI_LIBRARYDIR)/i18n/$(basename $(notdir $(po))).$(lang).lmo;))
|
@ -0,0 +1,2 @@
|
|||||||
|
define BuildPackage
|
||||||
|
endef
|
@ -0,0 +1,41 @@
|
|||||||
|
msgid ""
|
||||||
|
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||||
|
|
||||||
|
msgid "Stop running task?"
|
||||||
|
msgstr "删除运行中的任务?"
|
||||||
|
|
||||||
|
msgid "Finished at:"
|
||||||
|
msgstr "完成于:"
|
||||||
|
|
||||||
|
msgid "Failed at:"
|
||||||
|
msgstr "失败于:"
|
||||||
|
|
||||||
|
msgid "Lost login status"
|
||||||
|
msgstr "丢失登陆状态"
|
||||||
|
|
||||||
|
msgid "Fetch log failed, retrying..."
|
||||||
|
msgstr "拉取日志失败,正在重试..."
|
||||||
|
|
||||||
|
msgid "Task does not exist or has been deleted"
|
||||||
|
msgstr "任务不存在或已删除"
|
||||||
|
|
||||||
|
msgid "Stop and Remove"
|
||||||
|
msgstr "停止并删除"
|
||||||
|
|
||||||
|
msgid "Hide"
|
||||||
|
msgstr "隐藏"
|
||||||
|
|
||||||
|
msgid "Config saved, but apply failed"
|
||||||
|
msgstr "配置已保存,但应用失败"
|
||||||
|
|
||||||
|
msgid "Unknown command: "
|
||||||
|
msgstr "未知命令:"
|
||||||
|
|
||||||
|
msgid "Another task running, try again later."
|
||||||
|
msgstr "已有后台任务运行中,请稍后重试。"
|
||||||
|
|
||||||
|
msgid "Click here to check running task"
|
||||||
|
msgstr "点此查看运行中的任务"
|
||||||
|
|
||||||
|
msgid "Task Running"
|
||||||
|
msgstr "任务执行中"
|
Loading…
Reference in New Issue