update 2025-05-16 00:28:34

This commit is contained in:
kenzok8 2025-05-16 00:28:35 +08:00
parent fc1910ae82
commit c07983cf1c
6 changed files with 193 additions and 119 deletions

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# Copyright (C) 2021-2025 sirpdboy <herboy2008@gmail.com>
# https://github.com/sirpdboy/luci-app-ddns-go
# https://github.com/sirpdboy/luci-app-netspeedtest
# This is free software, licensed under the Apache License, Version 2.0 .
#
@ -9,11 +9,11 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-netspeedtest
PKG_VERSION:=5.0.2
PKG_RELEASE:=20250513
PKG_VERSION:=5.0.3
PKG_RELEASE:=20250515
LUCI_TITLE:=LuCI Support for netspeedtest
LUCI_DEPENDS:=+speedtest-cli +homebox +iperf3-ssl
LUCI_DEPENDS:=+speedtest-cli +homebox $(if $(find_package iperf3-ssl),+iperf3-ssl,+iperf3)
LUCI_PKGARCH:=all
PKG_MAINTAINER:=sirpdboy <herboy2008@gmail.com>
@ -23,9 +23,17 @@ define Package/$(PKG_NAME)/conffiles
endef
define Package/$(PKG_NAME)/postinst
#!/bin/sh
if [ -z "$${IPKG_INSTROOT}" ]; then
if ! uci show netspeedtest.config >/dev/null 2>&1; then
uci set netspeedtest.config=netspeedtest
uci set netspeedtest.config.enabled='1'
uci commit netspeedtest
fi
fi
exit 0
endef
include $(TOPDIR)/feeds/luci/luci.mk

View File

@ -6,7 +6,6 @@
'require uci';
'require form';
'require poll';
return view.extend({
render: function() {
var state = {
@ -33,12 +32,28 @@ return view.extend({
style: 'border:none;width: 100%; min-height: 80vh; border: none; border-radius: 3px;overflow:hidden !important;'
});
function checkProcess() {
return fs.exec('/bin/pidof', ['homebox']).then(res => ({
async function checkProcess() {
try {
// 尝试使用pgrep
const res = await fs.exec('/usr/bin/pgrep', ['homebox']);
return {
running: res.code === 0,
pid: res.code === 0 ? res.stdout.trim() : null
}));
pid: res.stdout.trim() || null
};
} catch (err) {
// 回退到ps方法
try {
const psRes = await fs.exec('/bin/ps', ['-w', '-C', 'homebox', '-o', 'pid=']);
const pid = psRes.stdout.trim();
return {
running: pid !== '',
pid: pid || null
};
} catch (err) {
return { running: false, pid: null };
}
}
}
function controlService(action) {
var command = action === 'start'

View File

@ -14,16 +14,34 @@ var state = {
const logPath = '/tmp/netspeedtest.log';
function checkProcess() {
return fs.exec('/bin/pidof', ['iperf3']).then(res => ({
async function checkProcess() {
try {
// 尝试使用pgrep
const res = await fs.exec('/usr/bin/pgrep', ['iperf3']);
return {
running: res.code === 0,
pid: res.code === 0 ? res.stdout.trim() : null
}));
pid: res.stdout.trim() || null
};
} catch (err) {
// 回退到ps方法
try {
const psRes = await fs.exec('/bin/ps', ['-w', '-C', 'iperf3', '-o', 'pid=']);
const pid = psRes.stdout.trim();
return {
running: pid !== '',
pid: pid || null
};
} catch (err) {
return { running: false, pid: null };
}
}
}
function pollLog(textarea) {
return fs.read(logPath).then(res => {
const cleanedLog = res ? res.trim()
return fs.exec('/bin/cat', [logPath])
.then(res => {
if (res.code !== 0) throw new Error(res.stderr);
const cleanedLog = res.stdout ? res.stdout.trim()
.replace(/\u001b\[[0-9;]*m/g, '')
.split('\n')
.slice(-50)
@ -40,18 +58,24 @@ function pollLog(textarea) {
});
}
function controlService(action) {
const commands = {
start: `/usr/bin/iperf3 -s -D -p 5201 --logfile ${logPath}`,
stop: '/usr/bin/killall iperf3'
start: `/usr/bin/iperf3 -s -D -p 5201 --logfile ${logPath} 2>&1`,
stop: '/usr/bin/killall -q iperf3'
};
return (action === 'start'
? fs.exec('/bin/sh', ['-c', `touch ${logPath} && chmod 644 ${logPath}`])
? fs.exec('/bin/sh', ['-c', `mkdir -p /tmp/netspeedtest && touch ${logPath} && chmod 644 ${logPath}`])
: Promise.resolve()
).then(() => fs.exec('/bin/sh', ['-c', commands[action]]));
).then(() => fs.exec('/bin/sh', ['-c', commands[action]]))
.catch(err => {
console.error('Service control error:', err);
throw err;
});
}
return view.extend({
// handleSaveApply: null,
// handleSave: null,

View File

@ -15,9 +15,6 @@ var SpeedtestCli = '/usr/bin/speedtest';
var SpeedtestScript = '/usr/lib/netspeedtest/speedtest';
return view.extend({
// handleSaveApply: null,
// handleSave: null,
// handleReset: null,
load() {
return Promise.all([
L.resolveDefault(fs.stat(SpeedtestCli), {}),
@ -27,21 +24,40 @@ return view.extend({
]);
},
poll_status(nodes, res) {
poll_status(nodes, res) {
var has_ookla = res[0].path,
result_content = res[1] ? res[1].trim().split("\n") : [];
var ookla_stat = nodes.querySelector('#ookla_status'),
result_stat = nodes.querySelector('#speedtest_result');
result_stat = nodes.querySelector('#speedtest_result'),
start_btn = nodes.querySelector('.cbi-button-apply');
// 获取版本号(新增部分)
// 更新测试按钮状态
if (start_btn) {
if (result_content.length && result_content[0] == 'Testing' &&
(Date.now() - (nodes.result_mtime || 0)) < TestTimeout) {
start_btn.disabled = true;
} else {
start_btn.disabled = false;
}
}
// 获取版本号
var version_info = '';
if (has_ookla) {
fs.exec_direct('/usr/bin/speedtest', ['--version'])
fs.exec(SpeedtestCli, ['--version'])
.then(function(res) {
if (res.stdout) {
var version_match = res.stdout.match(/Speedtest (\d+\.\d+\.\d+)/);
// 匹配 "Speedtest by Ookla 1.2.0.84" 这样的格式
var version_match = res.stdout.match(/Speedtest by Ookla (\d+\.\d+\.\d+\.\d+)/) ||
res.stdout.match(/Speedtest (\d+\.\d+\.\d+\.\d+)/);
if (version_match) {
version_info = ' ver:' + version_match[1];
} else {
// 如果上面没匹配到,尝试匹配更简单的版本号格式
version_match = res.stdout.match(/(\d+\.\d+\.\d+)/);
if (version_match) {
version_info = ' ver:' + version_match[1];
}
}
}
// 更新状态显示(包含版本号)
@ -54,10 +70,10 @@ poll_status(nodes, res) {
dom.content(ookla_stat, [_(has_ookla ? 'Installed' : 'Not Installed')]);
});
} else {
// 未安装时的显示保持不变
ookla_stat.style.color = 'red';
dom.content(ookla_stat, [_('Not Installed')]);
}
if (result_content.length) {
if (result_content[0] == 'Testing') {
result_stat.innerHTML = "<span style='color:green;font-weight:bold'>" +
@ -76,13 +92,12 @@ poll_status(nodes, res) {
result_stat.innerHTML = "<span style='color:gray;margin-left:20px'>" +
_('No test results yet.') + "</span>";
}
},
},
render(res) {
var has_ookla = res[0].path,
result_content = res[1] ? res[1].trim().split("\n") : [],
result_mtime = res[2] ? res[2].mtime * 1000 : 0,
date = new Date();
result_mtime = res[2] ? res[2].mtime * 1000 : 0;
var m, s, o;
m = new form.Map('netspeedtest', _('Wan Ookla SpeedTest'));
@ -120,8 +135,9 @@ poll_status(nodes, res) {
o = s.option(form.Button, '_start', _('Start Ookla SpeedTest'));
o.inputtitle = _('Click to execute');
o.inputstyle = 'apply';
if (result_content.length && result_content[0] == 'Testing' && (date.getTime() - result_mtime) < TestTimeout)
if (result_content.length && result_content[0] == 'Testing' && (Date.now() - result_mtime) < TestTimeout) {
o.readonly = true;
}
o.onclick = function() {
return fs.exec_direct(SpeedtestScript)
.then(function(res) { return window.location = window.location.href.split('#')[0] })
@ -139,11 +155,16 @@ poll_status(nodes, res) {
return m.render()
.then(L.bind(function(m, nodes) {
nodes.result_mtime = result_mtime; // 保存结果文件修改时间
poll.add(L.bind(function () {
return Promise.all([
L.resolveDefault(fs.stat(SpeedtestCli), {}),
L.resolveDefault(fs.read(ResultFile), null)
]).then(L.bind(this.poll_status, this, nodes));
L.resolveDefault(fs.read(ResultFile), null),
L.resolveDefault(fs.stat(ResultFile), {})
]).then(L.bind(function(res) {
nodes.result_mtime = res[2] ? res[2].mtime * 1000 : 0;
this.poll_status(nodes, res);
}, this));
}, this), 5);
return nodes;
}, this, m));

View File

@ -1,6 +1,9 @@
#!/bin/sh
[ ! -s "/etc/config/netspeedtest" ] && {
touch /etc/config/netspeedtest
uci -q batch <<-EOF >/dev/null
set netspeedtest.config=netspeedtest
commit netspeedtest
EOF
}

View File

@ -459,6 +459,9 @@ function gen_outbound(flag, node, tag, proxy_table)
if node.protocol == "anytls" then
protocol_table = {
password = (node.password and node.password ~= "") and node.password or "",
idle_session_check_interval = "30s",
idle_session_timeout = "30s",
min_idle_session = 5,
tls = tls
}
end