🌴 Sync 2026-03-05 23:51:42

This commit is contained in:
github-actions[bot] committed 2026-03-05 23:51:42 +08:00
1 parent 97c049ed0a
commit 0acff03804
14253 files changed
+3893239

No files matched your search

@@ -0,0 +1,181 @@
<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 ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[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%> &nbsp; '
+ '<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>