mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-08-01 23:19:33 +08:00
182 lines
5.7 KiB
HTML
Executable File
182 lines
5.7 KiB
HTML
Executable File
<fieldset class="cbi-section">
|
||
<legend><%:Status%></legend>
|
||
|
||
<div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">
|
||
<div id="iperf3_status">-</div>
|
||
|
||
<button class="btn cbi-button cbi-button-apply" id="btn_start" type="button"><%:Start%></button>
|
||
<button class="btn cbi-button cbi-button-reset" id="btn_stop" type="button"><%:Stop%></button>
|
||
<button class="btn cbi-button cbi-button-action" id="btn_restart" type="button"><%:Restart%></button>
|
||
|
||
<span id="iperf3_busy" style="display:none; color:#999;">...</span>
|
||
</div>
|
||
|
||
<div id="iperf3_list" style="margin-top:8px;"></div>
|
||
</fieldset>
|
||
|
||
<script type="text/javascript">
|
||
//<![CDATA[
|
||
(function () {
|
||
var token = '<%=token%>';
|
||
|
||
function esc(s) {
|
||
if (s === null || s === undefined) return '';
|
||
return String(s).replace(/[&<>"']/g, function (c) {
|
||
return ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]);
|
||
});
|
||
}
|
||
|
||
function setBusy(b) {
|
||
document.getElementById('iperf3_busy').style.display = b ? '' : 'none';
|
||
document.getElementById('btn_start').disabled = b;
|
||
document.getElementById('btn_stop').disabled = b;
|
||
document.getElementById('btn_restart').disabled = b;
|
||
}
|
||
|
||
function badge(text, color) {
|
||
return '<span style="color:' + color + '">' + text + '</span>';
|
||
}
|
||
|
||
function stateBadge(it) {
|
||
// 新字段优先:it.state;旧字段兼容:it.running
|
||
var st = it.state;
|
||
|
||
if (!st) {
|
||
if (it.enable === false || it.enable === "0") st = "disabled";
|
||
else st = it.running ? "running" : "stopped";
|
||
}
|
||
|
||
if (st === "running") return badge('<%:Running%>', 'green');
|
||
if (st === "delay") return badge('<%:Delay%>', '#d67f00');
|
||
if (st === "conflict") return badge('<%:Conflict%>', 'red');
|
||
if (st === "disabled") return badge('<%:Disabled%>', '#999');
|
||
return badge('<%:Stopped%>', '#999');
|
||
}
|
||
|
||
function overallBadge(data) {
|
||
// controller 返回 data.running=true 表示至少一个 running
|
||
// 如果没有 running,但存在 delay/conflict,也给更准确的总体状态
|
||
if (data && data.running) {
|
||
return badge('<%:Running%>', 'green');
|
||
}
|
||
|
||
var hasDelay = false, hasConflict = false;
|
||
if (data && data.servers && data.servers.length) {
|
||
for (var i = 0; i < data.servers.length; i++) {
|
||
var st = data.servers[i].state;
|
||
if (st === "delay") hasDelay = true;
|
||
else if (st === "conflict") hasConflict = true;
|
||
}
|
||
}
|
||
|
||
if (hasConflict) return badge('<%:Conflict%>', 'red');
|
||
if (hasDelay) return badge('<%:Delay%>', '#d67f00');
|
||
return badge('<%:Not running%>', 'red');
|
||
}
|
||
|
||
function translateDetail(detail) {
|
||
// detail 来自 controller 的英文字符串,这里做映射为可翻译字符串
|
||
// 未命中映射时,直接原样显示
|
||
var map = {
|
||
"disabled in config": "<%:disabled in config%>",
|
||
"delay pending (sleep)": "<%:delay pending (sleep)%>",
|
||
"listening and iperf3 process found": "<%:listening and iperf3 process found%>",
|
||
"port is listening but not iperf3 (occupied)": "<%:port is listening but not iperf3 (occupied)%>",
|
||
"not listening": "<%:not listening%>"
|
||
};
|
||
return map[detail] || detail || "";
|
||
}
|
||
|
||
function renderStatus(data) {
|
||
var s = document.getElementById('iperf3_status');
|
||
s.innerHTML = overallBadge(data);
|
||
|
||
var list = document.getElementById('iperf3_list');
|
||
var html = '';
|
||
|
||
if (data && data.servers && data.servers.length) {
|
||
html += '<ul>';
|
||
|
||
for (var i = 0; i < data.servers.length; i++) {
|
||
var it = data.servers[i];
|
||
|
||
var port = esc(it.port);
|
||
var en = (it.enable !== false) && (it.enable !== "0");
|
||
|
||
// 旧字段 pid 兼容
|
||
var pidInfo = '';
|
||
if (it.pid) {
|
||
pidInfo = ' <span style="color:#999"><%:PID%> ' + esc(it.pid) + '</span>';
|
||
}
|
||
|
||
// 监听信息(新字段 listen)
|
||
var listenInfo = '';
|
||
if (typeof it.listen === "boolean") {
|
||
listenInfo = it.listen
|
||
? ' <span style="color:#999">[<%:LISTEN%>]</span>'
|
||
: ' <span style="color:#999">[<%:NO LISTEN%>]</span>';
|
||
}
|
||
|
||
// detail(来自 controller)
|
||
var dt = translateDetail(it.detail);
|
||
var detail = dt ? (' <span style="color:#999">(' + esc(dt) + ')</span>') : '';
|
||
|
||
html += '<li>'
|
||
+ '<%:Port%> ' + port + ' : '
|
||
+ stateBadge(it)
|
||
+ pidInfo
|
||
+ listenInfo
|
||
+ detail
|
||
+ (en ? '' : ' <span style="color:#999">(<%:disabled%>)</span>')
|
||
+ '</li>';
|
||
}
|
||
|
||
html += '</ul>';
|
||
|
||
// 状态说明(简短)
|
||
html += '<div style="margin-top:6px; color:#999;">'
|
||
+ '<span><%:Delay%></span>: <%:starting after configured delay%> '
|
||
+ '<span><%:Conflict%></span>: <%:port is occupied%>'
|
||
+ '</div>';
|
||
}
|
||
|
||
list.innerHTML = html;
|
||
}
|
||
|
||
function refresh() {
|
||
XHR.get('<%=luci.dispatcher.build_url("admin/services/iperf3-server/status")%>', null,
|
||
function (x, data) { renderStatus(data || {}); }
|
||
);
|
||
}
|
||
|
||
function doAction(url, confirmText) {
|
||
if (confirmText && !window.confirm(confirmText)) return;
|
||
|
||
setBusy(true);
|
||
XHR.post(url, { token: token }, function (x, data) {
|
||
setBusy(false);
|
||
refresh();
|
||
});
|
||
}
|
||
|
||
document.getElementById('btn_start').addEventListener('click', function () {
|
||
doAction('<%=luci.dispatcher.build_url("admin/services/iperf3-server/start")%>', '<%:Start iPerf3 servers?%>');
|
||
});
|
||
document.getElementById('btn_stop').addEventListener('click', function () {
|
||
doAction('<%=luci.dispatcher.build_url("admin/services/iperf3-server/stop")%>', '<%:Stop iPerf3 servers?%>');
|
||
});
|
||
document.getElementById('btn_restart').addEventListener('click', function () {
|
||
doAction('<%=luci.dispatcher.build_url("admin/services/iperf3-server/restart")%>', '<%:Restart iPerf3 servers?%>');
|
||
});
|
||
|
||
// 轮询刷新
|
||
XHR.poll(2, '<%=luci.dispatcher.build_url("admin/services/iperf3-server/status")%>', null,
|
||
function (x, data) { renderStatus(data || {}); }
|
||
);
|
||
|
||
// 首次刷新
|
||
refresh();
|
||
})();
|
||
//]]>
|
||
</script>
|