update 2025-05-13 20:45:58
This commit is contained in:
parent
17918091de
commit
59e8d3b64a
|
@ -7,8 +7,8 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-ddns-go
|
||||
PKG_VERSION:=1.5.0
|
||||
PKG_RELEASE:=20250511
|
||||
PKG_VERSION:=1.5.1
|
||||
PKG_RELEASE:=20250513
|
||||
|
||||
PKG_MAINTAINER:=sirpdboy <herboy2008@gmail.com>
|
||||
PKG_CONFIG_DEPENDS:=
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
'require poll';
|
||||
|
||||
return view.extend({
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null,
|
||||
//handleSaveApply: null,
|
||||
//handleSave: null,
|
||||
//handleReset: null,
|
||||
load: function() {
|
||||
return uci.load('ddns-go');
|
||||
},
|
||||
|
|
|
@ -109,9 +109,9 @@ return view.extend({
|
|||
|
||||
|
||||
]);
|
||||
},
|
||||
}
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
//handleSaveApply: null,
|
||||
//handleSave: null,
|
||||
//handleReset: null
|
||||
});
|
||||
|
|
|
@ -113,6 +113,9 @@ start_service() {
|
|||
# Client
|
||||
if [ "$client_enabled" = "1" ]; then
|
||||
if [ -z "$1" -o "$1" = "mihomo-c" ]; then
|
||||
# Env variables
|
||||
export SAFE_PATHS="$RUN_DIR/"
|
||||
|
||||
# Generate/Validate client config
|
||||
ucode -S "$SDL_DIR/generate_client.uc" 2>>"$LOG_PATH" | yq -Poy | yq \
|
||||
'.sniffer["force-domain"][] style="double"
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2023 muink
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,20 +1,21 @@
|
|||
|
||||
# Copyright (C) 2020-2021 sirpdboy <herboy2008@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-3.0-only
|
||||
#
|
||||
# Copyright (C) 2021-2025 sirpdboy <herboy2008@gmail.com>
|
||||
# https://github.com/sirpdboy/luci-app-ddns-go
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v3.
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-netspeedtest
|
||||
|
||||
PKG_VERSION:=2.3.1
|
||||
PKG_RELEASE:=20250302
|
||||
PKG_VERSION:=5.0.1
|
||||
PKG_RELEASE:=20250512
|
||||
|
||||
LUCI_TITLE:=LuCI Support for netspeedtest
|
||||
LUCI_DEPENDS:=+python3 +iperf3-ssl +homebox
|
||||
LUCI_DEPENDS:=+speedtest-cli +homebox +iperf3-ssl
|
||||
LUCI_PKGARCH:=all
|
||||
PKG_MAINTAINER:=<https://github.com/sirpdboy/netspeedtest>
|
||||
PKG_MAINTAINER:=sirpdboy <herboy2008@gmail.com>
|
||||
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/* Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-netspeedtest */
|
||||
'use strict';
|
||||
'require view';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require uci';
|
||||
'require form';
|
||||
'require poll';
|
||||
|
||||
return view.extend({
|
||||
render: function() {
|
||||
var state = {
|
||||
running: false,
|
||||
port: 3300
|
||||
};
|
||||
var container = E('div');
|
||||
var statusSection = E('div', { 'class': 'cbi-section' });
|
||||
var statusIcon = E('span', { 'style': 'margin-right: 5px;' });
|
||||
var statusText = E('span');
|
||||
var toggleBtn = E('button', { 'class': 'btn cbi-button' });
|
||||
|
||||
var statusMessage = E('div', { style: 'text-align: center; padding: 2em;' }, [
|
||||
E('img', {
|
||||
src: 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMjQiIGhlaWdodD0iMTAyNCIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCI+PHBhdGggZmlsbD0iI2RmMDAwMCIgZD0iTTk0Mi40MjEgMjM0LjYyNGw4MC44MTEtODAuODExLTE1My4wNDUtMTUzLjA0NS04MC44MTEgODAuODExYy03OS45NTctNTEuNjI3LTE3NS4xNDctODEuNTc5LTI3Ny4zNzYtODEuNTc5LTI4Mi43NTIgMC01MTIgMjI5LjI0OC01MTIgNTEyIDAgMTAyLjIyOSAyOS45NTIgMTk3LjQxOSA4MS41NzkgMjc3LjM3NmwtODAuODExIDgwLjgxMSAxNTMuMDQ1IDE1My4wNDUgODAuODExLTgwLjgxMWM3OS45NTcgNTEuNjI3IDE3NS4xNDcgODEuNTc5IDI3Ny4zNzYgODEuNTc5IDI4Mi43NTIgMCA1MTItMjI5LjI0OCA1MTItNTEyIDAtMTAyLjIyOS0yOS45NTItMTk3LjQxOS04MS41NzktMjc3LjM3NnpNMTk0Ljk0NCA1MTJjMC0xNzUuMTA0IDE0MS45NTItMzE3LjA1NiAzMTcuMDU2LTMxNy4wNTYgNDggMCA5My40ODMgMTAuNjY3IDEzNC4yMjkgMjkuNzgxbC00MjEuNTQ3IDQyMS41NDdjLTE5LjA3Mi00MC43ODktMjkuNzM5LTg2LjI3Mi0yOS43MzktMTM0LjI3MnpNNTEyIDgyOS4wNTZjLTQ4IDAtOTMuNDgzLTEwLjY2Ny0xMzQuMjI5LTI5Ljc4MWw0MjEuNTQ3LTQyMS41NDdjMTkuMDcyIDQwLjc4OSAyOS43ODEgODYuMjcyIDI5Ljc4MSAxMzQuMjI5LTAuMDQzIDE3NS4xNDctMTQxLjk5NSAzMTcuMDk5LTMxNy4wOTkgMzE3LjA5OXoiLz48L3N2Zz4=',
|
||||
style: 'width: 100px; height: 100px; margin-bottom: 1em;'
|
||||
}),
|
||||
E('h2', {}, _('Homebox Service Not Running')),
|
||||
E('p', {}, _('Please enable the Homebox service'))
|
||||
]);
|
||||
|
||||
var iframe = E('iframe', {
|
||||
src: window.location.origin + ':' + state.port,
|
||||
style: 'width: 100%; min-height: 80vh; border: none; border-radius: 3px;'
|
||||
});
|
||||
|
||||
function checkProcess() {
|
||||
return fs.exec('/bin/pidof', ['homebox']).then(res => ({
|
||||
running: res.code === 0,
|
||||
pid: res.code === 0 ? res.stdout.trim() : null
|
||||
}));
|
||||
}
|
||||
|
||||
function controlService(action) {
|
||||
var command = action === 'start'
|
||||
? 'nohup /usr/bin/homebox > /tmp/homebox.log 2>&1 &'
|
||||
: '/usr/bin/killall homebox';
|
||||
return fs.exec('/bin/sh', ['-c', command]);
|
||||
}
|
||||
|
||||
function updateStatus() {
|
||||
statusIcon.textContent = state.running ? '✓' : '✗';
|
||||
statusIcon.style.color = state.running ? 'green' : 'red';
|
||||
statusText.textContent = _('Homebox Server') + (state.running ? _('RUNNING') : _('NOT RUNNING'));
|
||||
statusText.style.color = state.running ? 'green' : 'red';
|
||||
statusText.style.fontWeight = 'bold';
|
||||
statusText.style.fontSize = '0.92rem';
|
||||
|
||||
// 更新按钮状态
|
||||
toggleBtn.textContent = state.running ? _('Stop Server') : _('Start Server');
|
||||
toggleBtn.className = `btn cbi-button cbi-button-${state.running ? 'reset' : 'apply'}`;
|
||||
|
||||
// Update container content based on state
|
||||
container.textContent = '';
|
||||
if (state.running) {
|
||||
container.appendChild(iframe);
|
||||
} else {
|
||||
container.appendChild(statusMessage);
|
||||
}
|
||||
}
|
||||
|
||||
toggleBtn.addEventListener('click', ui.createHandlerFn(this, function() {
|
||||
var action = state.running ? 'stop' : 'start';
|
||||
return controlService(action)
|
||||
.then(checkProcess)
|
||||
.then(res => {
|
||||
state.running = res.running;
|
||||
updateStatus();
|
||||
});
|
||||
}));
|
||||
|
||||
// 开始
|
||||
statusSection.appendChild(E('div', { 'style': 'margin: 15px' }, [
|
||||
E('h3', {}, _('Lan Speedtest Homebox')),
|
||||
E('div', { 'class': 'cbi-map-descr' }, [statusIcon, statusText]),
|
||||
E('div', {'class': 'cbi-value', 'style': 'margin-top: 20px'}, [
|
||||
E('div', {'class': 'cbi-value-title'}, _('Homebox service control')),
|
||||
E('div', {'class': 'cbi-value-field'}, toggleBtn),
|
||||
E('div', { 'style': 'text-align: right; font-style: italic; margin-top: 20px;' }, [
|
||||
_('© github '),
|
||||
E('a', {
|
||||
'href': 'https://github.com/sirpdboy/luci-app-netspeedtest',
|
||||
'target': '_blank',
|
||||
'style': 'text-decoration: none;'
|
||||
}, 'by sirpdboy')
|
||||
])
|
||||
])
|
||||
]));
|
||||
|
||||
// Initial status check
|
||||
checkProcess().then(res => {
|
||||
state.running = res.running;
|
||||
updateStatus();
|
||||
toggleBtn.disabled = false;
|
||||
// Start polling
|
||||
poll.add(() => {
|
||||
return checkProcess().then(res => {
|
||||
if (res.running !== state.running) {
|
||||
state.running = res.running;
|
||||
updateStatus();
|
||||
toggleBtn.disabled = false;
|
||||
}
|
||||
});
|
||||
}, 5);
|
||||
|
||||
poll.start();
|
||||
});
|
||||
|
||||
return E('div', {}, [
|
||||
statusSection,
|
||||
container
|
||||
]);
|
||||
}
|
||||
|
||||
// handleSaveApply: null,
|
||||
// handleSave: null,
|
||||
// handleReset: null
|
||||
});
|
|
@ -0,0 +1,186 @@
|
|||
/* Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-netspeedtest */
|
||||
'use strict';
|
||||
'require view';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require uci';
|
||||
'require form';
|
||||
'require poll';
|
||||
|
||||
var state = {
|
||||
running: false,
|
||||
port: null
|
||||
};
|
||||
|
||||
const logPath = '/var/log/iperf3.log';
|
||||
|
||||
function checkProcess() {
|
||||
return fs.exec('/bin/pidof', ['iperf3']).then(res => ({
|
||||
running: res.code === 0,
|
||||
pid: res.code === 0 ? res.stdout.trim() : null
|
||||
}));
|
||||
}
|
||||
|
||||
function pollLog(textarea) {
|
||||
return fs.read(logPath).then(res => {
|
||||
const cleanedLog = res ? res.trim()
|
||||
.replace(/\u001b\[[0-9;]*m/g, '')
|
||||
.split('\n')
|
||||
.slice(-50)
|
||||
.join('\n') : _('Log file is empty');
|
||||
|
||||
if (textarea) {
|
||||
textarea.value = cleanedLog;
|
||||
textarea.scrollTop = textarea.scrollHeight;
|
||||
}
|
||||
return cleanedLog;
|
||||
}).catch(err => {
|
||||
console.error('Error reading log:', err);
|
||||
return _('Failed to read log: ') + err.message;
|
||||
});
|
||||
}
|
||||
|
||||
function controlService(action) {
|
||||
const commands = {
|
||||
start: `/usr/bin/iperf3 -s -D -p 5201 --logfile ${logPath}`,
|
||||
stop: '/usr/bin/killall iperf3'
|
||||
};
|
||||
|
||||
return (action === 'start'
|
||||
? fs.exec('/bin/sh', ['-c', `touch ${logPath} && chmod 644 ${logPath}`])
|
||||
: Promise.resolve()
|
||||
).then(() => fs.exec('/bin/sh', ['-c', commands[action]]));
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
// handleSaveApply: null,
|
||||
// handleSave: null,
|
||||
// handleReset: null,
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
uci.load('netspeedtest')
|
||||
]);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
|
||||
// 创建状态元素
|
||||
const statusIcon = E('span', { 'style': 'margin-right: 5px;' });
|
||||
const btnGroup = E('div', { 'class': 'cbi-value-field', 'style': 'display: flex; gap: 10px;' });
|
||||
const statusText = E('span');
|
||||
const toggleBtn = E('button', {
|
||||
'class': 'btn cbi-button',
|
||||
'click': ui.createHandlerFn(this, function() {
|
||||
const action = state.running ? 'stop' : 'start';
|
||||
return controlService(action)
|
||||
.then(() => checkProcess())
|
||||
.then(res => {
|
||||
state.running = res.running;
|
||||
updateStatus();
|
||||
if (logTextarea) {
|
||||
pollLog(logTextarea);
|
||||
}
|
||||
})
|
||||
.catch(err => ui.addNotification(null, E('p', _('Error: ') + err.message), 'error'));
|
||||
})
|
||||
});
|
||||
|
||||
function updateStatus() {
|
||||
statusIcon.textContent = state.running ? '✓' : '✗';
|
||||
statusIcon.style.color = state.running ? 'green' : 'red';
|
||||
statusText.textContent = _('Iperf3 Server ') + (state.running ? _('RUNNING') : _('NOT RUNNING'));
|
||||
statusText.style.color = state.running ? 'green' : 'red';
|
||||
statusText.style['font-weight'] = 'bold';
|
||||
statusText.style['font-size'] = '0.92rem';
|
||||
toggleBtn.textContent = state.running ? _('Stop Server') : _('Start Server');
|
||||
toggleBtn.className = `btn cbi-button cbi-button-${state.running ? 'reset' : 'apply'}`;
|
||||
}
|
||||
|
||||
// 初始化状态
|
||||
statusIcon.textContent = '...';
|
||||
statusText.textContent = _('Checking status...');
|
||||
toggleBtn.textContent = _('Loading...');
|
||||
toggleBtn.disabled = true;
|
||||
|
||||
// 创建日志区域
|
||||
let logTextarea;
|
||||
|
||||
logTextarea = E('textarea', {
|
||||
'class': 'cbi-input-textarea',
|
||||
'wrap': 'off',
|
||||
'readonly': 'readonly',
|
||||
'style': 'width: calc(100% - 20px);height: 535px;margin: 10px;overflow-y: scroll;',
|
||||
});
|
||||
// 构建UI
|
||||
const statusSection = E('div', { 'class': 'cbi-section' }, [
|
||||
E('div', { 'style': 'margin: 15px' }, [
|
||||
E('h3', {}, _('Lan Speedtest Iperf3')),
|
||||
E('div', { 'class': 'cbi-map-descr' }, [statusIcon, statusText]),
|
||||
E('div', {'class': 'cbi-value', 'style': 'margin-top: 20px'}, [
|
||||
E('div', {'class': 'cbi-value-title'}, _('Iperf3 service control')),
|
||||
E('div', {'class': 'cbi-value-field'}, toggleBtn),
|
||||
|
||||
E('div', {'class': 'cbi-value-title'}, _('Download iperf3 client')),
|
||||
E('div', {'class': 'cbi-value-field'}, [
|
||||
E('div', {
|
||||
'class': 'cbi-value-field',
|
||||
'style': 'display: flex;'
|
||||
}, [
|
||||
E('button', {
|
||||
'class': 'btn cbi-button cbi-button-save',
|
||||
'click': ui.createHandlerFn(this, () => window.open('https://iperf.fr/iperf-download.php', '_blank'))
|
||||
}, _('Official Website')),
|
||||
E('button', {
|
||||
'class': 'btn cbi-button cbi-button-save',
|
||||
'click': ui.createHandlerFn(this, () => window.open('https://github.com/sirpdboy/luci-app-netspeedtest/releases', '_blank'))
|
||||
}, _('GitHub'))
|
||||
])
|
||||
])
|
||||
]),
|
||||
E('div', { 'style': 'text-align: right; font-style: italic; margin-top: 20px;' }, [
|
||||
_('© github '),
|
||||
E('a', {
|
||||
'href': 'https://github.com/sirpdboy/luci-app-netspeedtest',
|
||||
'target': '_blank',
|
||||
'style': 'text-decoration: none;'
|
||||
}, 'by sirpdboy')
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
// 初始化状态检查
|
||||
checkProcess().then(res => {
|
||||
state.running = res.running;
|
||||
updateStatus();
|
||||
toggleBtn.disabled = false;
|
||||
|
||||
// 启动轮询
|
||||
poll.add(() => {
|
||||
return checkProcess().then(res => {
|
||||
if (res.running !== state.running) {
|
||||
state.running = res.running;
|
||||
updateStatus();
|
||||
}
|
||||
});
|
||||
}, 5);
|
||||
});
|
||||
|
||||
// 如果有日志,启动日志轮询
|
||||
poll.add(() => pollLog(logTextarea), 5);
|
||||
pollLog(logTextarea);
|
||||
|
||||
return E('div', [
|
||||
statusSection,
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('h3', {}, _('Iperf3 Run Log')),
|
||||
logTextarea,
|
||||
E('div', { 'style': 'text-align: right; font-size: small; margin-top: 5px;' },
|
||||
_('Refresh every 5 seconds.')
|
||||
)
|
||||
])
|
||||
]);
|
||||
|
||||
return render();
|
||||
}
|
||||
|
||||
});
|
|
@ -0,0 +1,86 @@
|
|||
'use strict';
|
||||
'require dom';
|
||||
'require fs';
|
||||
'require poll';
|
||||
'require uci';
|
||||
'require view';
|
||||
|
||||
var scrollPosition = 0;
|
||||
var userScrolled = false;
|
||||
var logTextarea;
|
||||
var log_path;
|
||||
|
||||
uci.load('netspeedtest').then(function() {
|
||||
log_path = '/var/log/netspeedtest.log';
|
||||
});
|
||||
|
||||
function pollLog() {
|
||||
return Promise.all([
|
||||
fs.read_direct(log_path, 'text').then(function (res) {
|
||||
return res.trim().split(/\n/).join('\n').replace(/\u001b\[33mWARN\u001b\[0m/g, '').replace(/\u001b\[36mINFO\u001b\[0m/g, '').replace(/\u001b\[31mERRO\u001b\[0m/g, '');
|
||||
}),
|
||||
]).then(function (data) {
|
||||
logTextarea.value = data[0] || _('No log data.');
|
||||
|
||||
if (!userScrolled) {
|
||||
logTextarea.scrollTop = logTextarea.scrollHeight;
|
||||
} else {
|
||||
logTextarea.scrollTop = scrollPosition;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return view.extend({
|
||||
handleCleanLogs: function () {
|
||||
return fs.write(log_path, '')
|
||||
.catch(function (e) { ui.addNotification(null, E('p', e.message)) });
|
||||
},
|
||||
|
||||
render: function () {
|
||||
logTextarea = E('textarea', {
|
||||
'class': 'cbi-input-textarea',
|
||||
'wrap': 'off',
|
||||
'readonly': 'readonly',
|
||||
'style': 'width: calc(100% - 20px);height: 535px;margin: 10px;overflow-y: scroll;',
|
||||
});
|
||||
|
||||
logTextarea.addEventListener('scroll', function () {
|
||||
userScrolled = true;
|
||||
scrollPosition = logTextarea.scrollTop;
|
||||
});
|
||||
|
||||
var log_textarea_wrapper = E('div', { 'id': 'log_textarea' }, logTextarea);
|
||||
|
||||
setTimeout(function () {
|
||||
poll.add(pollLog);
|
||||
}, 100);
|
||||
|
||||
var clear_logs_button = E('input', { 'class': 'btn cbi-button-action', 'type': 'button', 'style': 'margin-left: 10px; margin-top: 10px;', 'value': _('Clear logs') });
|
||||
clear_logs_button.addEventListener('click', this.handleCleanLogs.bind(this));
|
||||
|
||||
return E([
|
||||
E('div', { 'class': 'cbi-map' }, [
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
clear_logs_button,
|
||||
log_textarea_wrapper,
|
||||
E('div', { 'style': 'text-align:right' },
|
||||
E('small', {}, _('Refresh every %s seconds.').format(L.env.pollinterval)),
|
||||
E('div', { 'class': 'cbi-section-actions cbi-section-actions-right' })
|
||||
]),
|
||||
E('div', { 'style': 'text-align: right; font-style: italic;' }, [
|
||||
E('span', {}, [
|
||||
_('© github '),
|
||||
E('a', {
|
||||
'href': 'https://github.com/sirpdboy',
|
||||
'target': '_blank',
|
||||
'style': 'text-decoration: none;'
|
||||
}, 'by sirpdboy')
|
||||
])
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
// handleSaveApply: null,
|
||||
// handleSave: null,
|
||||
// handleReset: null
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
'use strict';
|
||||
'require view';
|
||||
'require uci';
|
||||
'require ui';
|
||||
'require form';
|
||||
|
||||
return view.extend({
|
||||
|
||||
load() {
|
||||
return Promise.all([
|
||||
uci.load('netspeedtest')
|
||||
]);
|
||||
},
|
||||
|
||||
render(res) {
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map('netspeedtest', _('OpenSpeedTest'));
|
||||
|
||||
s = m.section(form.NamedSection, '_iframe');
|
||||
s.anonymous = true;
|
||||
s.render = function (section_id) {
|
||||
return E('iframe', {
|
||||
src: '//openspeedtest.com/speedtest',
|
||||
style: 'border:none;width:100%;height:100%;min-height:360px;border:none;overflow:hidden !important;'
|
||||
});
|
||||
};
|
||||
|
||||
return m.render();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// handleSaveApply: null,
|
||||
// handleSave: null,
|
||||
// handleReset: null
|
||||
});
|
|
@ -0,0 +1,132 @@
|
|||
'use strict';
|
||||
'require view';
|
||||
'require poll';
|
||||
'require dom';
|
||||
'require fs';
|
||||
'require rpc';
|
||||
'require uci';
|
||||
'require ui';
|
||||
'require form';
|
||||
// 全局变量
|
||||
var TestTimeout = 240 * 1000; // 4 Minutes
|
||||
var ResultFile = '/tmp/speedtest_result';
|
||||
var SpeedtestCli = '/usr/bin/speedtest';
|
||||
var SpeedtestScript = '/usr/lib/netspeedtest/speedtest';
|
||||
|
||||
return view.extend({
|
||||
// handleSaveApply: null,
|
||||
// handleSave: null,
|
||||
// handleReset: null,
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
L.resolveDefault(fs.stat(SpeedtestCli), {}),
|
||||
L.resolveDefault(fs.read(ResultFile), null),
|
||||
L.resolveDefault(fs.stat(ResultFile), {}),
|
||||
uci.load('netspeedtest')
|
||||
]);
|
||||
},
|
||||
|
||||
poll_status: function (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');
|
||||
|
||||
// Update status indicators
|
||||
ookla_stat.style.color = has_ookla ? 'green' : 'red';
|
||||
dom.content(ookla_stat, [_(has_ookla ? 'Installed' : 'Not Installed')]);
|
||||
|
||||
// Update result display
|
||||
if (result_content.length) {
|
||||
if (result_content[0] == 'Testing') {
|
||||
result_stat.innerHTML = "<span style='color:green;font-weight:bold'>" +
|
||||
"<img src='/luci-static/resources/icons/loading.gif' height='17' style='vertical-align:middle'/> " +
|
||||
_('Testing in progress...') +
|
||||
"</span>";
|
||||
} else if (result_content[0].match(/https?:\S+/)) {
|
||||
result_stat.innerHTML = "<div style='max-width:500px'><a href='" +
|
||||
result_content[0] + "' target='_blank'><img src='" +
|
||||
result_content[0] + '.png' + "' style='max-width:100%'></a></div>";
|
||||
} else if (result_content[0] == 'Test failed') {
|
||||
result_stat.innerHTML = "<span style='color:red;font-weight:bold'>" +
|
||||
_('Test failed.') + "</span>";
|
||||
}
|
||||
} else {
|
||||
result_stat.innerHTML = "<span style='color:gray'>" +
|
||||
_('No test results yet.') + "</span>";
|
||||
}
|
||||
},
|
||||
|
||||
render: function (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();
|
||||
|
||||
var m, s, o;
|
||||
m = new form.Map('netspeedtest', _('WAN Ookla SpeedTest'));
|
||||
|
||||
// Result display section
|
||||
s = m.section(form.TypedSection, '_result');
|
||||
s.anonymous = true;
|
||||
s.render = function () {
|
||||
var content;
|
||||
if (result_content.length) {
|
||||
if (result_content[0] == 'Testing') {
|
||||
content = E('span', { style: 'color:green;font-weight:bold' }, [
|
||||
E('img', { src: '/luci-static/resources/icons/loading.gif', height: '20' }),
|
||||
' ', _('Testing in progress...')
|
||||
]);
|
||||
} else if (result_content[0].match(/https?:\S+/)) {
|
||||
content = E('div', { style: 'max-width:500px' }, [
|
||||
E('a', { href: result_content[0], target: '_blank' }, [
|
||||
E('img', { src: result_content[0] + '.png', style: 'max-width:100%' })
|
||||
])
|
||||
]);
|
||||
} else {
|
||||
content = E('span', { style: 'color:red;font-weight:bold' },
|
||||
_('Test failed.'));
|
||||
}
|
||||
} else {
|
||||
content = E('span', { style: 'color:gray' },
|
||||
_('No test results yet.'));
|
||||
}
|
||||
return E('div', { id: 'speedtest_result' }, content);
|
||||
};
|
||||
|
||||
// Configuration section
|
||||
s = m.section(form.NamedSection, 'config', 'netspeedtest');
|
||||
|
||||
// Start test button
|
||||
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)
|
||||
o.readonly = true;
|
||||
o.onclick = function() {
|
||||
return fs.exec_direct(SpeedtestScript)
|
||||
.then(function(res) { return window.location = window.location.href.split('#')[0] })
|
||||
.catch(function(e) { ui.addNotification(null, E('p', e.message), 'error') });
|
||||
};
|
||||
|
||||
o = s.option(form.DummyValue, '_ookla_status', _('Ookla® SpeedTest-CLI'));
|
||||
o.rawhtml = true;
|
||||
o.cfgvalue = function () {
|
||||
return E('span', {
|
||||
id: 'ookla_status',
|
||||
style: has_ookla ? 'color:green' : 'color:red'
|
||||
}, _(has_ookla ? 'Installed' : 'Not Installed'));
|
||||
};
|
||||
|
||||
return m.render()
|
||||
.then(L.bind(function(m, nodes) {
|
||||
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));
|
||||
}, this), 5);
|
||||
return nodes;
|
||||
}, this, m));
|
||||
}
|
||||
});
|
|
@ -1,133 +0,0 @@
|
|||
-- Copyright (C) 2020-2025 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/netspeedtest
|
||||
|
||||
module("luci.controller.netspeedtest", package.seeall)
|
||||
local http = require "luci.http"
|
||||
local fs=require"nixio.fs"
|
||||
local sys=require "luci.sys"
|
||||
local uci = luci.model.uci.cursor()
|
||||
name='netspeedtest'
|
||||
function index()
|
||||
|
||||
if not nixio.fs.access("/etc/config/netspeedtest") then return end
|
||||
|
||||
local e = entry({"admin","network","netspeedtest"},alias("admin", "network", "netspeedtest", "speedtestlan"),_("Net Speedtest"),90)
|
||||
e.dependent = false
|
||||
e.acl_depends = { "luci-app-netspeedtest" }
|
||||
|
||||
entry({"admin", "network", "netspeedtest", "speedtestlan"},cbi("netspeedtest/speedtestlan"),_("Lan Speedtest Web"),20).leaf = true
|
||||
entry({"admin", "network", "netspeedtest", "speedtestiperf3"},cbi("netspeedtest/speedtestiperf3", {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true}),_("Lan Speedtest Iperf3"),30).leaf = true
|
||||
entry({"admin", "network", "netspeedtest", "speedtestwan"},cbi("netspeedtest/speedtestwan", {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true}),_("Broadband speedtest"), 40).leaf = true
|
||||
entry({"admin", "network", "netspeedtest", "speedtestwanweb"},cbi("netspeedtest/speedtestwanweb", {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true}),_("Broadband OpenSpeedtest"), 41).leaf = true
|
||||
entry({"admin", "network", "netspeedtest", "speedtestport"},cbi("netspeedtest/speedtestport", {hideapplybtn=true, hidesavebtn=true, hideresetbtn=true}),_("Server Port Latency Test"), 50).leaf = true
|
||||
entry({"admin", "network", "netspeedtest", "log"}, form("netspeedtest/log"), _("Log"), 60).leaf = true
|
||||
entry({"admin", "network", "netspeedtest", "test_port"}, call("test_port"))
|
||||
entry({"admin", "network", "iperf3_status"}, call("iperf3_status"))
|
||||
entry({"admin", "network", "test_iperf0"}, post("test_iperf0"), nil).leaf = true
|
||||
entry({"admin", "network", "test_iperf1"}, post("test_iperf1"), nil).leaf = true
|
||||
entry({"admin", "network", "netspeedtest", "speedtestwanrun"}, call("speedtestwanrun"))
|
||||
entry({"admin", "network", "netspeedtest", "netcheck"}, call("netcheck"))
|
||||
entry({"admin", "network", "netspeedtest", "dellog"},call("dellog"))
|
||||
entry({"admin", "network", "netspeedtest", "getlog"},call("getlog"))
|
||||
end
|
||||
|
||||
function netcheck()
|
||||
http.prepare_content("text/plain; charset=utf-8")
|
||||
local f=io.open("/tmp/netspeedtest.log", "r+")
|
||||
local fdp=fs.readfile("/tmp/netspeedtestpos") or 0
|
||||
f:seek("set",fdp)
|
||||
local a=f:read(2048000) or ""
|
||||
fdp=f:seek()
|
||||
fs.writefile("/tmp/netspeedtestpos",tostring(fdp))
|
||||
f:close()
|
||||
http.write(a)
|
||||
end
|
||||
|
||||
function speedtestwanrun()
|
||||
local cli = luci.http.formvalue('cli')
|
||||
uci:set(name, 'speedtestwan', 'speedtest_cli', cli)
|
||||
uci:commit(name)
|
||||
fs.writefile("/tmp/netspeedtestpos","0")
|
||||
http.prepare_content("application/json")
|
||||
http.write('')
|
||||
sys.exec(string.format("/etc/init.d/netspeedtest wantest " ..cli.. " > /tmp/netspeedtest.log 2>&1 &" ))
|
||||
end
|
||||
|
||||
function test_port()
|
||||
local e = {}
|
||||
local domain = luci.http.formvalue('sdomain')
|
||||
local port = luci.http.formvalue('sport')
|
||||
local ip=sys.exec("echo "..domain.." | grep -E ^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$ || nslookup "..domain.." 2>/dev/null | grep Address | awk -F' ' '{print$NF}' | grep -E ^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$ | sed -n 1p")
|
||||
ip=sys.exec("echo -n "..ip)
|
||||
e.ping = luci.sys.exec(string.format("echo -n $(tcping -q -c 1 -i 1 -t 2 -p %s %s 2>&1 | grep -o 'time=[0-9]*.[0-9]*' | awk -F '=' '{print $2}') 2>/dev/null", port, ip))
|
||||
e.type = "tcping"
|
||||
if e.ping=="" then
|
||||
e.ping=sys.call("echo -n $(ping -c 1 -W 1 %q 2>&1 | grep -o 'time=[0-9]*.[0-9]*' | awk -F '=' '{print $2}') 2>/dev/null" % ip)
|
||||
e.type = "ping"
|
||||
end
|
||||
if e.ping=="" then e.ping="0" end
|
||||
sys.exec(string.format('echo -ne "\n 【$(date)】 服务器:%s -- 端口:%s -- TCP延时:%s ms \n">> /var/log/netspeedtest.log',domain,port,e.ping))
|
||||
uci:set(name, 'speedtestport', 'sdomain', domain)
|
||||
uci:set(name, 'speedtestport', 'sport', port)
|
||||
uci:set(name, 'speedtestport', 'tcpspeed', e.ping.." ms")
|
||||
uci:commit(name)
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
|
||||
function iperf3_status()
|
||||
local e={}
|
||||
e.run=sys.call("busybox ps -w | grep iperf3 | grep -v grep >/dev/null") == 0
|
||||
luci.http.prepare_content("application/json")
|
||||
luci.http.write_json(e)
|
||||
end
|
||||
|
||||
function testout(cmd, addr)
|
||||
luci.http.prepare_content("text/plain")
|
||||
local util = io.popen(cmd)
|
||||
if util then
|
||||
while true do
|
||||
local ln = util:read("*l")
|
||||
if not ln then break end
|
||||
luci.http.write(ln)
|
||||
luci.http.write("\n")
|
||||
end
|
||||
util:close()
|
||||
end
|
||||
end
|
||||
|
||||
function test_iperf0(addr)
|
||||
sys.call("pgrep -f unblockneteasemusic | xargs kill -9 >/dev/null 2>&1 ")
|
||||
sys.call("/etc/init.d/unblockneteasemusic stop ")
|
||||
sys.call("/etc/init.d/unblockmusic stop ")
|
||||
testout("iperf3 -s ", addr)
|
||||
end
|
||||
|
||||
function test_iperf1(addr)
|
||||
sys.call("pgrep -f iperf3 | xargs kill -9 >/dev/null 2>&1 ")
|
||||
sys.call("/etc/init.d/unblockneteasemusic restart")
|
||||
sys.call("/etc/init.d/unblockmusic restart")
|
||||
end
|
||||
|
||||
function dellog()
|
||||
fs.writefile("/var/log/netspeedtest.log","")
|
||||
http.prepare_content("application/json")
|
||||
http.write('')
|
||||
end
|
||||
|
||||
|
||||
|
||||
function getlog()
|
||||
logfile="/var/log/netspeedtest.log"
|
||||
if not fs.access(logfile) then
|
||||
http.write("")
|
||||
return
|
||||
end
|
||||
local f=io.open(logfile,"r")
|
||||
local a=f:read("*a") or ""
|
||||
f:close()
|
||||
a=string.gsub(a,"\n$","")
|
||||
http.prepare_content("text/plain; charset=utf-8")
|
||||
http.write(a)
|
||||
end
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
local fs = require "nixio.fs"
|
||||
local uci = require"luci.model.uci".cursor()
|
||||
local f, t
|
||||
f = SimpleForm("logview")
|
||||
f.reset = false
|
||||
f.submit = false
|
||||
t=f:field(TextValue,"conf")
|
||||
|
||||
t.rmempty=true
|
||||
t.rows=20
|
||||
t.template="netspeedtest/log"
|
||||
t.readonly="readonly"
|
||||
return f
|
|
@ -1,18 +0,0 @@
|
|||
-- Copyright (C) 2020-2025 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/netspeedtest
|
||||
require("luci.util")
|
||||
local o,s,e
|
||||
|
||||
|
||||
o = Map("netspeedtest", "<font color='green'>" .. translate("Net Speedtest") .."</font>",translate( "Network speed diagnosis test (including intranet and extranet)<br/>For specific usage, see:") ..translate("<a href=\'https://github.com/sirpdboy/netspeedtest.git' target=\'_blank\'>GitHub @sirpdboy/netspeedtest</a>") )
|
||||
o:section(SimpleSection).template = "netspeedtest/speedtestiperf3_status"
|
||||
|
||||
s = o:section(TypedSection, "speedtestiperf3", translate('Lan Speedtest Iperf3'))
|
||||
s.addremove=false
|
||||
s.anonymous=true
|
||||
|
||||
e = s:option(DummyValue, '', '')
|
||||
e.rawhtml = true
|
||||
e.template ='netspeedtest/speedtestiperf3'
|
||||
|
||||
|
||||
return o
|
|
@ -1,21 +0,0 @@
|
|||
-- Copyright (C) 2020-2025 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/netspeedtest
|
||||
local m, s ,o
|
||||
|
||||
m = Map("netspeedtest", "<font color='green'>" .. translate("Net Speedtest") .."</font>",translate( "Network speed diagnosis test (including intranet and extranet)<br/>For specific usage, see:") ..translate("<a href=\'https://github.com/sirpdboy/netspeedtest.git' target=\'_blank\'>GitHub @sirpdboy/netspeedtest</a>") )
|
||||
|
||||
s = m:section(TypedSection, "netspeedtest", translate('Lan Speedtest Web'))
|
||||
s.anonymous = true
|
||||
|
||||
o=s:option(Flag,"enabled",translate("Enable Homebox service"))
|
||||
o.default=0
|
||||
|
||||
o = s:option(DummyValue, '', '')
|
||||
o.rawhtml = true
|
||||
o.template ='netspeedtest/speedtestlan'
|
||||
|
||||
m.apply_on_parse = true
|
||||
m.on_after_apply = function(self,map)
|
||||
io.popen("/etc/init.d/netspeedtest restart")
|
||||
luci.http.redirect(luci.dispatcher.build_url("admin","network","netspeedtest","speedtestlan"))
|
||||
end
|
||||
return m
|
|
@ -1,25 +0,0 @@
|
|||
-- Copyright (C) 2020-2025 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/netspeedtest
|
||||
require("luci.util")
|
||||
local o,t,e
|
||||
|
||||
o = Map("netspeedtest", "<font color='green'>" .. translate("Net Speedtest") .."</font>",translate( "Network speed diagnosis test (including intranet and extranet)<br/>For specific usage, see:") ..translate("<a href=\'https://github.com/sirpdboy/netspeedtest.git' target=\'_blank\'>GitHub @sirpdboy/netspeedtest</a>") )
|
||||
|
||||
t = o:section(TypedSection, "speedtestport", translate('Server Port Latency Test'))
|
||||
t.addremove=false
|
||||
t.anonymous=true
|
||||
|
||||
e = t:option(Value, 'sdomain', translate('Test server address'))
|
||||
e.default = "www.baidu.com"
|
||||
e.description = translate('Enter the domain name or IP address of the server that needs to be tested')
|
||||
|
||||
e = t:option(Value, 'sport', translate('Test server port'))
|
||||
e.default = "443"
|
||||
|
||||
e = t:option(Value, 'tcpspeed', translate('Server Port Delay Value'))
|
||||
|
||||
e = t:option(DummyValue, '', '')
|
||||
e.rawhtml = true
|
||||
e.template ='netspeedtest/speedtestport'
|
||||
|
||||
|
||||
return o
|
|
@ -1,21 +0,0 @@
|
|||
-- Copyright (C) 2020-2025 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/netspeedtest
|
||||
|
||||
require("luci.util")
|
||||
local o,t,e
|
||||
|
||||
luci.sys.exec("echo '-' >/tmp/netspeedtest.log&&echo 1 > /tmp/netspeedtestpos" )
|
||||
o = Map("netspeedtest", "<font color='green'>" .. translate("Net Speedtest") .."</font>",translate( "Network speed diagnosis test (including intranet and extranet)<br/>For specific usage, see:") ..translate("<a href=\'https://github.com/sirpdboy/netspeedtest.git' target=\'_blank\'>GitHub @sirpdboy/netspeedtest</a>") )
|
||||
|
||||
t=o:section(TypedSection,"speedtestwan",translate("Broadband speedtest"))
|
||||
t.anonymous=true
|
||||
|
||||
e = t:option(ListValue, 'speedtest_cli', translate('client version selection'))
|
||||
e:value("0",translate("ookla-speedtest-cli"))
|
||||
e:value("1",translate("python3-speedtest-cli"))
|
||||
e.default = "1"
|
||||
|
||||
e=t:option(Button, "restart", translate("speedtest.net Broadband speed test"))
|
||||
e.inputtitle=translate("Click to execute")
|
||||
e.template ='netspeedtest/speedtestwan'
|
||||
|
||||
return o
|
|
@ -1,15 +0,0 @@
|
|||
-- Copyright (C) 2020-2025 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/netspeedtest
|
||||
require("luci.util")
|
||||
local o,t,e
|
||||
|
||||
luci.sys.exec("echo '-' >/tmp/netspeedtest.log&&echo 1 > /tmp/netspeedtestpos" )
|
||||
o = Map("netspeedtest", "<font color='green'>" .. translate("Net Speedtest") .."</font>",translate( "Network speed diagnosis test (including intranet and extranet)<br/>For specific usage, see:") ..translate("<a href=\'https://github.com/sirpdboy/netspeedtest.git' target=\'_blank\'>GitHub @sirpdboy/netspeedtest</a>") )
|
||||
|
||||
t=o:section(TypedSection,"speedtestwan",translate("Broadband OpenSpeedtest"))
|
||||
t.anonymous=true
|
||||
|
||||
e = t:option(DummyValue, '', '')
|
||||
e.rawhtml = true
|
||||
e.template ='netspeedtest/speedtestwanweb'
|
||||
|
||||
return o
|
|
@ -1,53 +0,0 @@
|
|||
<%+cbi/valueheader%>
|
||||
<input type="button" class="btn cbi-button cbi-button-apply" id="apply_update_button" value="<%:Clear Log%>" onclick="apply_del_log()"/>
|
||||
<input type="checkbox" name="NAME" value="reverse" onclick="reverselog()" style="vertical-align:middle;height:auto;"><%:Reverse%></input>
|
||||
<textarea id="cbid.logview.1.conf" class="cbi-input-textarea" style="width: 100%;display:inline" data-update="change" rows="32" cols="60" readonly="readonly" > </textarea>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
var islogreverse=false;
|
||||
|
||||
function createAndDownloadFile(fileName,content){
|
||||
var aTag=document.createElement('a');
|
||||
var blob=new Blob([content]);
|
||||
aTag.download=fileName;
|
||||
aTag.href=URL.createObjectURL(blob);
|
||||
aTag.click();
|
||||
URL.revokeObjectURL(blob);
|
||||
}
|
||||
function apply_del_log(){
|
||||
XHR.get('<%=url([[admin]],[[network]],[[netspeedtest]],[[dellog]])%>',null,function(x,data){
|
||||
var lv=document.getElementById('cbid.logview.1.conf');
|
||||
lv.innerHTML="";
|
||||
}
|
||||
);
|
||||
return
|
||||
}
|
||||
|
||||
function reverselog(){
|
||||
var lv=document.getElementById('cbid.logview.1.conf');
|
||||
lv.innerHTML=lv.innerHTML.split('\n').reverse().join('\n')
|
||||
if (islogreverse){
|
||||
islogreverse=false;
|
||||
}else{
|
||||
islogreverse=true;
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
XHR.poll(3,'<%=url([[admin]],[[network]],[[netspeedtest]],[[getlog]])%>',null,
|
||||
function(x,data){
|
||||
var lv=document.getElementById('cbid.logview.1.conf');
|
||||
lv.innerHTML=""
|
||||
if (x.responseText && lv){
|
||||
if (islogreverse){
|
||||
lv.innerHTML=x.responseText.split('\n').reverse().join('\n')+lv.innerHTML;
|
||||
}else{
|
||||
lv.innerHTML+=x.responseText;
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
<%+cbi/valuefooter%>
|
|
@ -1,93 +0,0 @@
|
|||
<%#
|
||||
Copyright 2020-2025 sirpdboy Wich <sirpdboy@qq.com>
|
||||
https://github.com/sirpdboy/netspeedtest
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<script type="text/javascript" src="<%=resource%>/cbi.js?v=1.1"></script>
|
||||
<%+cbi/valueheader%>
|
||||
<script type="text/javascript">
|
||||
var stxhr = new XHR();
|
||||
function update_status(btn,field, proto)
|
||||
{
|
||||
var tool = field.name;
|
||||
var addr = field.value;
|
||||
var protocol = proto ;
|
||||
var legend = document.getElementById('test-iperf-legend');
|
||||
var output = document.getElementById('test-iperf-output');
|
||||
if (legend && output)
|
||||
{
|
||||
output.innerHTML =
|
||||
'<img src="<%=resource%>/icons/loading.gif" alt="<%:Loading%>" style="vertical-align:middle" /> ' +
|
||||
'<%:Waiting for command to complete...%>';
|
||||
legend.parentNode.style.display = 'block';
|
||||
legend.style.display = 'inline';
|
||||
btn.value='<%:Waiting (executing)...%>';
|
||||
btn.disabled=true;
|
||||
stxhr.post('<%=url('admin/network')%>/test_' + tool + protocol + '/' + addr, { token: '<%=token%>' },
|
||||
function(x)
|
||||
{
|
||||
if (x.responseText)
|
||||
{
|
||||
legend.style.display = 'none';
|
||||
output.innerHTML = String.format('<pre>%h</pre>', x.responseText);
|
||||
}
|
||||
else
|
||||
{
|
||||
legend.style.display = 'none';
|
||||
output.innerHTML ='</p> <%:Operation execution complete%></p>';
|
||||
}
|
||||
|
||||
btn.disabled=false;
|
||||
btn.value='<%:Click to execute%>';
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<form method="post" class="cbi-map" action="<%=url('admin/network/netspeedtest')%>">
|
||||
<div class="cbi-value" >
|
||||
<label class="cbi-value-title" ><%:Select function%></label>
|
||||
<div class="cbi-value-field">
|
||||
<input style="margin: 5px 0" type="hidden" value="" name="iperf" />
|
||||
<select name="iperf_to" style="width:auto">
|
||||
<option value="0" selected="selected"><%:iperfstart%></option>
|
||||
<option value="1"><%:iperfstop%></option>
|
||||
</select>
|
||||
<div class="cbi-value-description">
|
||||
<%:The speed measurement terminal must be in the same LAN as the router that starts the speed measurement%><br />
|
||||
<%:Operation steps: start router speed measurement service download test client run speed measurement client input IP address of router speed measurement service%>
|
||||
</div></div></div>
|
||||
<div class="cbi-value" ><label class="cbi-value-title" ><%:Execute selected functions%></label>
|
||||
<div class="cbi-value-field"><input type="button" value="<%:Click to execute%>" class="cbi-button cbi-button-apply" onclick="return update_status(this,this.form.iperf,this.form.iperf_to.selectedIndex)" />
|
||||
</div></div>
|
||||
<div class="cbi-value" >
|
||||
<label class="cbi-value-title"><%:Iperf3 speed measurement software download%></label>
|
||||
<div class="cbi-value-field">
|
||||
<input type="button" class="cbi-button cbi-input-reload" value="<%:Github download iperf3%>" onclick="javascript:window.open('https://github.com/sirpdboy/netspeedtest/releases','target');" />
|
||||
<input type="button" class="cbi-button cbi-input-reload" value="<%:Download from foreign official websites%>" onclick="javascript:window.open('https://iperf.fr/iperf-download.php','target');" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="cbi-value" >
|
||||
<label class="cbi-value-title"><%:iperf3 commands reference%></label>
|
||||
<div class="cbi-value-field">
|
||||
<%:-c, --client host ................run in client mode, connecting to host%><br />
|
||||
<%:-s, --server .....................run in server mode%><br />
|
||||
<%:-u, --udp ........................use UDP rather than TCP%><br />
|
||||
<%:-b, --bandwidth ..<number>[KMG]...target bandwidth in bits/sec (0 for unlimited)%><br />
|
||||
<%:-t, --time .......<number>........time in seconds to transmit for (default 10 secs)%><br />
|
||||
<%:-i, --interval ...<number>........seconds between periodic bandwidth reports%><br />
|
||||
<%:-P, --parallel ...<number>........number of parallel client streams to run%><br />
|
||||
<%:-R, --reverse ....................run in reverse mode (server sends, client receives)%><br />
|
||||
</div>
|
||||
</div>
|
||||
<fieldset class="cbi-section" style="display:none">
|
||||
<legend id="test-iperf-legend">
|
||||
<%:Collecting data...%>
|
||||
</legend>
|
||||
<span id="test-iperf-output"></span>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<%+cbi/valuefooter%>
|
|
@ -1,27 +0,0 @@
|
|||
<script type="text/javascript">//<![CDATA[
|
||||
XHR.poll(3, '<%=url([[admin]], [[network]], [[iperf3_status]])%>', null,
|
||||
function(x, data) {
|
||||
var tb = document.getElementById('iperf3_status');
|
||||
if (data && tb)
|
||||
{
|
||||
if (data.run)
|
||||
{
|
||||
tb.innerHTML = '<br/><em style=\"color:green\"><%:The Iperf3 service is running.%></em>';
|
||||
}
|
||||
else
|
||||
{
|
||||
tb.innerHTML = '<br/><em style=\"color:red\"><%:The Iperf3 service is not running.%></em>';
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
//]]></script>
|
||||
|
||||
|
||||
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
|
||||
<fieldset class="cbi-section">
|
||||
<legend><%:Iperf3 Status%></legend>
|
||||
<p id="iperf3_status">
|
||||
<em><%:Collecting data...%></em>
|
||||
</p>
|
||||
</fieldset>
|
|
@ -1,32 +0,0 @@
|
|||
<%#
|
||||
Copyright 2020-2025 sirpdboy Wich <sirpdboy@qq.com>
|
||||
https://github.com/sirpdboy/netspeedtest
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
<%
|
||||
|
||||
local running = luci.sys.exec("busybox ps -w | grep homebox | grep -v grep >/dev/null && echo -ne '1' ")
|
||||
%>
|
||||
<%+cbi/valueheader%>
|
||||
|
||||
<div class="cbi-map">
|
||||
<% if running == "1" then %>
|
||||
<iframe id="homebox" style="width: 100%; min-height: 400px; height: 650px;border: none; border-radius: 3px;"></iframe>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById("homebox").src = window.location.protocol + "//" + window.location.hostname + ":3300";
|
||||
document.getElementById("homebox").height = document.documentElement.clientHeight;
|
||||
window.onresize = function(){
|
||||
document.getElementById("homebox").height = document.documentElement.clientHeight;
|
||||
}
|
||||
</script>
|
||||
<% else %>
|
||||
<style>.running{text-align: center;} .running>h1{font-size: 25px; color: #333; margin: 1rem;} .running>p{ font-size: 1.2rem; color: #8391a2; margin: 1rem;}</style>
|
||||
<div class="running">
|
||||
<img src="data:image/svg+xml;base64,PCEtLSBHZW5lcmF0ZWQgYnkgSWNvTW9vbi5pbyAtLT4KPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMjQiIGhlaWdodD0iMTAyNCIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCI+Cjx0aXRsZT48L3RpdGxlPgo8ZyBpZD0iaWNvbW9vbi1pZ25vcmUiPgo8L2c+CjxwYXRoIGZpbGw9IiNkZjAwMDAiIGQ9Ik05NDIuNDIxIDIzNC42MjRsODAuODExLTgwLjgxMS0xNTMuMDQ1LTE1My4wNDUtODAuODExIDgwLjgxMWMtNzkuOTU3LTUxLjYyNy0xNzUuMTQ3LTgxLjU3OS0yNzcuMzc2LTgxLjU3OS0yODIuNzUyIDAtNTEyIDIyOS4yNDgtNTEyIDUxMiAwIDEwMi4yMjkgMjkuOTUyIDE5Ny40MTkgODEuNTc5IDI3Ny4zNzZsLTgwLjgxMSA4MC44MTEgMTUzLjA0NSAxNTMuMDQ1IDgwLjgxMS04MC44MTFjNzkuOTU3IDUxLjYyNyAxNzUuMTQ3IDgxLjU3OSAyNzcuMzc2IDgxLjU3OSAyODIuNzUyIDAgNTEyLTIyOS4yNDggNTEyLTUxMiAwLTEwMi4yMjktMjkuOTUyLTE5Ny40MTktODEuNTc5LTI3Ny4zNzZ6TTE5NC45NDQgNTEyYzAtMTc1LjEwNCAxNDEuOTUyLTMxNy4wNTYgMzE3LjA1Ni0zMTcuMDU2IDQ4IDAgOTMuNDgzIDEwLjY2NyAxMzQuMjI5IDI5Ljc4MWwtNDIxLjU0NyA0MjEuNTQ3Yy0xOS4wNzItNDAuNzg5LTI5LjczOS04Ni4yNzItMjkuNzM5LTEzNC4yNzJ6TTUxMiA4MjkuMDU2Yy00OCAwLTkzLjQ4My0xMC42NjctMTM0LjIyOS0yOS43ODFsNDIxLjU0Ny00MjEuNTQ3YzE5LjA3MiA0MC43ODkgMjkuNzgxIDg2LjI3MiAyOS43ODEgMTM0LjIyOS0wLjA0MyAxNzUuMTQ3LTE0MS45OTUgMzE3LjA5OS0zMTcuMDk5IDMxNy4wOTl6Ij48L3BhdGg+Cjwvc3ZnPgo=" height="100">
|
||||
<h1><font color="color:red"><%:The homebox service is not running.%></font></h1>
|
||||
<p><%:Please enable the Homebox service%></p>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<%+cbi/valuefooter%>
|
|
@ -1,81 +0,0 @@
|
|||
<%#
|
||||
Copyright 2020-2025 sirpdboy Wich <sirpdboy@qq.com>
|
||||
https://github.com/sirpdboy/netspeedtest
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%+cbi/valueheader%>
|
||||
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
|
||||
function speedtestportrun(btn) {
|
||||
|
||||
var sid='speedtestport'
|
||||
var opt={
|
||||
base:"cbid.netspeedtest."+sid,
|
||||
get:function(opt){
|
||||
var id=this.base+'.'+opt;
|
||||
var obj=document.getElementsByName(id)[0] || document.getElementsByClassName(id)[0] || document.getElementById(id)
|
||||
if (obj){
|
||||
return obj;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getlist:function(opt){
|
||||
var id=this.base+'.'+opt;
|
||||
var objs=document.getElementsByName(id) || document.getElementsByClassName(id);
|
||||
var ret=[];
|
||||
if (objs){
|
||||
for (var i=0;i < objs.length;i++){
|
||||
ret[i]=objs[i].value;
|
||||
}
|
||||
}else{
|
||||
alert("<%:Fatal on get option,please help in debug%>:"+opt);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
const RUN_URL = '<%=luci.dispatcher.build_url("admin", "network", "netspeedtest","test_port")%>';
|
||||
const S_URL = '<%=luci.dispatcher.build_url("admin", "network", "netspeedtest","speedtestport")%>';
|
||||
|
||||
var output=document.getElementById("speedtestport-status");
|
||||
|
||||
btn.value='<%:Waiting (executing)...%>';
|
||||
btn.disabled=true;
|
||||
var sdomain=opt.get("sdomain").value;
|
||||
var sport=opt.get("sport").value;
|
||||
XHR.get('<%=luci.dispatcher.build_url("admin/network/netspeedtest/test_port")%>',{
|
||||
sdomain: sdomain,
|
||||
sport: sport
|
||||
},
|
||||
(x) =>{
|
||||
|
||||
if(x && x.status == 200) {
|
||||
output.innerHTML="<font style=\'color:green\'>"+"<%:Perform OK%>"+"</font>";
|
||||
|
||||
}
|
||||
else{
|
||||
output.innerHTML="<font style=\'color:green\'>"+"<%:Test failed%>"+"</font>";
|
||||
}
|
||||
setTimeout(function(){
|
||||
window.location = S_URL
|
||||
},1000);
|
||||
|
||||
btn.disabled=false;
|
||||
btn.value='<%:Click to execute%>';
|
||||
}
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//]]></script>
|
||||
|
||||
<label class="cbi-value-title"><%= translate("Test server port delay") %></label>
|
||||
<div class="cbi-value-field">
|
||||
<input type="button" class="btn cbi-button cbi-button-apply" value='<%:Click to execute%>' onclick="return speedtestportrun(this)" />
|
||||
<span id="speedtestport-status"></span>
|
||||
</div>
|
||||
<%+cbi/valuefooter%>
|
|
@ -1,99 +0,0 @@
|
|||
<%#
|
||||
Copyright 2020-2025 sirpdboy Wich <sirpdboy@qq.com>
|
||||
https://github.com/sirpdboy/netspeedtest
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%+cbi/valueheader%>
|
||||
<%local fs=require"nixio.fs"%>
|
||||
<input type="button" class="btn cbi-button cbi-button-apply" id="apply_run_button" value="<%:Click to execute%>" onclick=" return apply_run(this) "/>
|
||||
<div id="logview" style="display:none">
|
||||
<input type="checkbox" id="reversetag" value="reverse" onclick=" return reverselog()" style="vertical-align:middle;height: auto;"><%:reverse%></input>
|
||||
<textarea id="cbid.logview.test.conf" class="cbi-input-textarea" style="width: 100%;display:block;" data-update="change" rows="20" cols="80" readonly="readonly" > </textarea>
|
||||
</div>
|
||||
<script type="text/javascript">//<![CDATA[
|
||||
const RUN_URL = '<%=luci.dispatcher.build_url("admin", "network", "netspeedtest","speedtestwanrun")%>';
|
||||
const S_URL = '<%=luci.dispatcher.build_url("admin", "network", "netspeedtest","netcheck")%>';
|
||||
var checkbtn = document.getElementById('apply_run_button');
|
||||
|
||||
var islogreverse = false;
|
||||
function reverselog(){
|
||||
var lv = document.getElementById('cbid.logview.test.conf');
|
||||
lv.innerHTML=lv.innerHTML.split('\n').reverse().join('\n')
|
||||
if (islogreverse){
|
||||
islogreverse=false;
|
||||
}else{
|
||||
islogreverse=true;
|
||||
}
|
||||
return
|
||||
}
|
||||
function apply_run(btn) {
|
||||
|
||||
var sid='speedtestwan'
|
||||
var opt={
|
||||
base:"cbid.netspeedtest."+sid,
|
||||
get:function(opt){
|
||||
var id=this.base+'.'+opt;
|
||||
var obj=document.getElementsByName(id)[0] || document.getElementsByClassName(id)[0] || document.getElementById(id)
|
||||
if (obj){
|
||||
return obj;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
},
|
||||
getlist:function(opt){
|
||||
var id=this.base+'.'+opt;
|
||||
var objs=document.getElementsByName(id) || document.getElementsByClassName(id);
|
||||
var ret=[];
|
||||
if (objs){
|
||||
for (var i=0;i < objs.length;i++){
|
||||
ret[i]=objs[i].value;
|
||||
}
|
||||
}else{
|
||||
alert("<%:Fatal on get option,please help in debug%>:"+opt);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
btn.value='<%:Waiting (executing)...%>';
|
||||
btn.disabled=true;
|
||||
var cli=opt.get("speedtest_cli").value;
|
||||
console.log(cli);
|
||||
XHR.get(RUN_URL, { cli: cli },
|
||||
(x) =>{}
|
||||
);
|
||||
poll_check();
|
||||
return;
|
||||
}
|
||||
|
||||
function poll_check(){
|
||||
var tag = document.getElementById('logview');
|
||||
tag.style.display="block"
|
||||
XHR.poll(3, '<%=url([[admin]], [[network]], [[netspeedtest]], [[netcheck]])%>', null,
|
||||
function(x, data) {
|
||||
var lv = document.getElementById('cbid.logview.test.conf');
|
||||
|
||||
if (x.responseText && lv) {
|
||||
if (x.responseText=="\u0000"){
|
||||
for(j = 0,len=this.XHR._q.length; j < len; j++) {
|
||||
if (this.XHR._q[j].url == '<%=url([[admin]], [[network]], [[netspeedtest]], [[netcheck]])%>'){
|
||||
this.XHR._q.splice(j,1);
|
||||
checkbtn.disabled = false;
|
||||
checkbtn.value = '<%:Click to execute%>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if (islogreverse){
|
||||
lv.innerHTML = x.responseText.split('\n').reverse().join('\n')+lv.innerHTML;
|
||||
}else{
|
||||
lv.innerHTML += x.responseText;
|
||||
}
|
||||
}
|
||||
}
|
||||
);}
|
||||
|
||||
//]]>
|
||||
</script>
|
||||
<%+cbi/valuefooter%>
|
|
@ -1,10 +0,0 @@
|
|||
<%#
|
||||
Copyright 2020-2025 sirpdboy Wich <sirpdboy@qq.com>
|
||||
https://github.com/sirpdboy/netspeedtest
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-%>
|
||||
|
||||
<%+cbi/valueheader%>
|
||||
<!--OST Widget code start--><div style="text-align:right;"><div style="min-height:360px;"><div style="width:100%;height:0;padding-bottom:50%;position:relative;"><iframe style="border:none;position:absolute;top:0;left:0;width:100%;height:100%;min-height:360px;border:none;overflow:hidden !important;" src="//openspeedtest.com/speedtest"></iframe></div></div>Provided by OpenSpeedtest.com </div><!-- OST Widget code end -->
|
||||
|
||||
<%+cbi/valuefooter%>
|
|
@ -0,0 +1,122 @@
|
|||
#
|
||||
# Copyright (C) 2021-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-netspeedtest
|
||||
# This is free software, licensed under the GNU General Public License v3.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: LuCi Chinese Translation\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"Language: zh_CN\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Pootle 2.0.6\n"
|
||||
|
||||
msgid "NetSpeedtest"
|
||||
msgstr ""
|
||||
|
||||
msgid "A tool for testing network speed in multiple aspects"
|
||||
msgstr ""
|
||||
|
||||
msgid "Lan Speedtest Iperf3"
|
||||
msgstr ""
|
||||
|
||||
msgid "Lan Speedtest Homebox"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wan Ookla SpeedTest"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wan OpenSpeedTest"
|
||||
msgstr ""
|
||||
|
||||
msgid "Log"
|
||||
msgstr ""
|
||||
|
||||
msgid "Start Server"
|
||||
msgstr ""
|
||||
|
||||
msgid "Stop Server"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading..."
|
||||
msgstr ""
|
||||
|
||||
msgid "RUNNING"
|
||||
msgstr ""
|
||||
|
||||
msgid "NOT RUNNING"
|
||||
msgstr ""
|
||||
|
||||
msgid "Iperf3 Server"
|
||||
msgstr ""
|
||||
|
||||
msgid "Command failed"
|
||||
msgstr ""
|
||||
|
||||
msgid "Failed to read log file"
|
||||
msgstr ""
|
||||
|
||||
msgid "No log content available"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error: "
|
||||
msgstr ""
|
||||
|
||||
msgid "Iperf3 service control"
|
||||
msgstr ""
|
||||
|
||||
msgid "Listen Port"
|
||||
msgstr ""
|
||||
|
||||
msgid "Invalid format. Use [::]:port or ip:port"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable Log View"
|
||||
msgstr ""
|
||||
|
||||
msgid "Download iperf3 client"
|
||||
msgstr ""
|
||||
|
||||
msgid "Iperf3 Run Log"
|
||||
msgstr ""
|
||||
|
||||
msgid "Refresh Log"
|
||||
msgstr ""
|
||||
|
||||
msgid "Official Website"
|
||||
msgstr ""
|
||||
|
||||
msgid "Please enable the Homebox service"
|
||||
msgstr ""
|
||||
|
||||
msgid "Homebox service control"
|
||||
msgstr ""
|
||||
|
||||
msgid "Homebox Server"
|
||||
msgstr ""
|
||||
|
||||
msgid "Homebox Service Not Running"
|
||||
msgstr ""
|
||||
|
||||
msgid "Ookla® SpeedTest-CLI"
|
||||
msgstr ""
|
||||
|
||||
msgid "Start Ookla SpeedTest"
|
||||
msgstr ""
|
||||
|
||||
msgid "Click to execute"
|
||||
msgstr ""
|
||||
|
||||
msgid "Testing in progress..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Test failed."
|
||||
msgstr ""
|
||||
|
||||
msgid "No test results yet."
|
||||
msgstr ""
|
||||
|
||||
msgid "Test Script"
|
||||
msgstr ""
|
|
@ -0,0 +1 @@
|
|||
zh_Hans
|
|
@ -1,172 +0,0 @@
|
|||
#
|
||||
# Copyright (C) 2020-2022 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/netspeedtest
|
||||
# This is free software, licensed under the GNU General Public License v3.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: LuCi Chinese Translation\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"Language: zh_CN\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Pootle 2.0.6\n"
|
||||
|
||||
msgid "Net Speedtest"
|
||||
msgstr "网速测试"
|
||||
|
||||
msgid "Network speed diagnosis test (including intranet and extranet)<br/>For specific usage, see:"
|
||||
msgstr "网络速度诊断测试(包括内网、外网、特定端口服务器测速)<br/>使用说明见:"
|
||||
|
||||
msgid "Lan Speedtest Iperf3"
|
||||
msgstr "本地iperf3吞吐测速"
|
||||
|
||||
msgid "Lan Speedtest Web"
|
||||
msgstr "本地测速网页版"
|
||||
|
||||
msgid "Broadband speedtest"
|
||||
msgstr "宽带测速speedtest"
|
||||
|
||||
msgid "Broadband OpenSpeedtest"
|
||||
msgstr "宽带测速OpenSpeedtest"
|
||||
|
||||
msgid "Running state"
|
||||
msgstr "运行状态"
|
||||
|
||||
msgid "Enable Homebox service"
|
||||
msgstr "启用homebox服务"
|
||||
|
||||
msgid "The Iperf3 service is running."
|
||||
msgstr "iperf3服务已启动"
|
||||
|
||||
msgid "The Iperf3 service is not running."
|
||||
msgstr "iperf3服务未启动"
|
||||
|
||||
msgid "Iperf3 Status"
|
||||
msgstr "iperf3服务状态"
|
||||
|
||||
msgid "The homebox service is running."
|
||||
msgstr "homebox网页测速服务已启动"
|
||||
|
||||
msgid "The homebox service is not running."
|
||||
msgstr "homebox网页测速服务未启动"
|
||||
|
||||
msgid "Please enable the Homebox service"
|
||||
msgstr "请将homebox服务启用"
|
||||
|
||||
msgid "homebox Status"
|
||||
msgstr "homebox网页测速服务状态"
|
||||
|
||||
msgid "</br>For specific usage, see:"
|
||||
msgstr "</br>具体使用方法参见:"
|
||||
|
||||
msgid "iperfstart"
|
||||
msgstr "iperf服务启动"
|
||||
|
||||
msgid "iperfstop"
|
||||
msgstr "iperf服务停止"
|
||||
|
||||
msgid "Select function"
|
||||
msgstr "选择功能"
|
||||
|
||||
msgid "Execute selected functions"
|
||||
msgstr "执行选择的功能"
|
||||
|
||||
msgid "client version selection"
|
||||
msgstr "客户端版本"
|
||||
|
||||
msgid "python3-speedtest-cli"
|
||||
msgstr "python3网络测试客户端"
|
||||
|
||||
msgid "ookla-speedtest-cli"
|
||||
msgstr "ookla网络测试客户端"
|
||||
|
||||
msgid "iperf3 instructions"
|
||||
msgstr "iperf3使用说明"
|
||||
|
||||
msgid "Test speed service started"
|
||||
msgstr "测试速度服务已经启动"
|
||||
|
||||
msgid "The speed measurement terminal must be in the same LAN as the router that starts the speed measurement"
|
||||
msgstr "测速终端机必须与启动测速的路由器在同一局域网内"
|
||||
|
||||
msgid "Operation steps: start router speed measurement service download test client run speed measurement client input IP address of router speed measurement service"
|
||||
msgstr "使用步骤:A.启动路由器测速服务 B.下载测试客户端 C.运行测速客户端 D.输入路由器测速服务IP地址。 "
|
||||
|
||||
msgid "Github download iperf3"
|
||||
msgstr "Github下载iperf3"
|
||||
|
||||
msgid "Iperf3 speed measurement software download"
|
||||
msgstr "iperf3测速软件下载"
|
||||
|
||||
msgid "speedtest.net Broadband speed test"
|
||||
msgstr "speedtest.net宽带网速测试"
|
||||
|
||||
|
||||
msgid "Operation execution complete"
|
||||
msgstr "操作执行完毕"
|
||||
|
||||
msgid "Network speed test, please wait..."
|
||||
msgstr "网速测试中,请稍侯..."
|
||||
|
||||
msgid "Download from foreign official websites"
|
||||
msgstr "国外官网"
|
||||
|
||||
msgid "Server Port Latency Test"
|
||||
msgstr "服务器端口延迟测试"
|
||||
|
||||
msgid "Test server address"
|
||||
msgstr "测试服务器地址"
|
||||
|
||||
msgid "Test server port"
|
||||
msgstr "测试服务器端口"
|
||||
|
||||
msgid "Enter the domain name or IP address of the server that needs to be tested"
|
||||
msgstr "输入需要测试的服务器域名或者IP地址"
|
||||
|
||||
msgid "Test server port delay"
|
||||
msgstr "测试端口延时"
|
||||
|
||||
msgid "Click to execute"
|
||||
msgstr "点击执行"
|
||||
|
||||
msgid "Test failed"
|
||||
msgstr "测试失败"
|
||||
|
||||
msgid "Waiting (executing)..."
|
||||
msgstr "努力执行中"
|
||||
|
||||
msgid "Server Port Delay Value"
|
||||
msgstr "服务器端口延时值"
|
||||
|
||||
msgid "iperf3 commands reference"
|
||||
msgstr "iperf3命令参考"
|
||||
|
||||
msgid "-b, --bandwidth ..<number>[KMG]...target bandwidth in bits/sec (0 for unlimited)"
|
||||
msgstr "-b, --bandwidth ..<number>[KMG]...目标带宽(以位/秒为单位)(0表示无限制)"
|
||||
|
||||
msgid "-t, --time .......<number>........time in seconds to transmit for (default 10 secs)"
|
||||
msgstr "-t, --time .......<number>........传输时间(以秒为单位)(默认为10秒)"
|
||||
|
||||
msgid "-i, --interval ...<number>........seconds between periodic bandwidth reports"
|
||||
msgstr "-i, --interval ...<number>........测试报告之间的秒数"
|
||||
|
||||
msgid "-P, --parallel ...<number>........number of parallel client streams to run"
|
||||
msgstr "-P, --parallel ...<number>........多线程运行的数量"
|
||||
|
||||
msgid "-R, --reverse ....................run in reverse mode (server sends, client receives)"
|
||||
msgstr "-R, --reverse ....................反向模式运行(服务器发送,客户端接收)"
|
||||
|
||||
msgid "-c, --client host ................run in client mode, connecting to host"
|
||||
msgstr "-c, --client host ................客户端模式下运行,连接到host"
|
||||
|
||||
msgid "-s, --server .....................run in server mode"
|
||||
msgstr "-s, --server .....................服务器模式下运行"
|
||||
|
||||
msgid "-u, --udp ........................use UDP rather than TCP"
|
||||
msgstr "-u, --udp ........................测试传输使用UDP而不是TCP"
|
||||
|
||||
msgid "Perform OK"
|
||||
msgstr "执行完成"
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
#
|
||||
# Copyright (C) 2020-2022 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/netspeedtest
|
||||
# Copyright (C) 2020-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/netspeedtest
|
||||
# This is free software, licensed under the GNU General Public License v3.
|
||||
#
|
||||
msgid ""
|
||||
|
@ -13,160 +13,110 @@ msgstr ""
|
|||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Generator: Pootle 2.0.6\n"
|
||||
|
||||
msgid "Net Speedtest"
|
||||
msgid "NetSpeedtest"
|
||||
msgstr "网速测试"
|
||||
|
||||
msgid "Network speed diagnosis test (including intranet and extranet)<br/>For specific usage, see:"
|
||||
msgstr "网络速度诊断测试(包括内网、外网、特定端口服务器测速)<br/>使用说明见:"
|
||||
msgid "A tool for testing network speed in multiple aspects"
|
||||
msgstr "一个用于从多方面测试本地和宽带网络速度的工具"
|
||||
|
||||
msgid "Lan Speedtest Iperf3"
|
||||
msgstr "本地iperf3吞吐测速"
|
||||
|
||||
msgid "Lan Speedtest Web"
|
||||
msgstr "本地测速网页版"
|
||||
msgid "Lan Speedtest Homebox"
|
||||
msgstr "本地homebox网页测速"
|
||||
|
||||
msgid "Broadband speedtest"
|
||||
msgstr "宽带测速speedtest"
|
||||
msgid "Wan Ookla SpeedTest"
|
||||
msgstr "宽带Ookla网速测速"
|
||||
|
||||
msgid "Broadband OpenSpeedtest"
|
||||
msgstr "宽带测速OpenSpeedtest"
|
||||
msgid "Wan OpenSpeedTest"
|
||||
msgstr "宽带OpenSpeedTest测速"
|
||||
|
||||
msgid "Running state"
|
||||
msgstr "运行状态"
|
||||
msgid "Log"
|
||||
msgstr "日志"
|
||||
|
||||
msgid "Enable Homebox service"
|
||||
msgstr "启用homebox服务"
|
||||
msgid "Start Server"
|
||||
msgstr "启动服务"
|
||||
|
||||
msgid "The Iperf3 service is running."
|
||||
msgstr "iperf3服务已启动"
|
||||
msgid "Stop Server"
|
||||
msgstr "停止服务"
|
||||
|
||||
msgid "The Iperf3 service is not running."
|
||||
msgstr "iperf3服务未启动"
|
||||
msgid "Loading..."
|
||||
msgstr "装载中..."
|
||||
|
||||
msgid "Iperf3 Status"
|
||||
msgstr "iperf3服务状态"
|
||||
msgid "RUNNING"
|
||||
msgstr "运行中"
|
||||
|
||||
msgid "The homebox service is running."
|
||||
msgstr "homebox网页测速服务已启动"
|
||||
msgid "NOT RUNNING"
|
||||
msgstr "未运行"
|
||||
|
||||
msgid "The homebox service is not running."
|
||||
msgstr "homebox网页测速服务未启动"
|
||||
msgid "Iperf3 Server"
|
||||
msgstr "Iperf3服务端"
|
||||
|
||||
msgid "Command failed"
|
||||
msgstr "命令无效"
|
||||
|
||||
msgid "Failed to read log file"
|
||||
msgstr "读取日志失败"
|
||||
|
||||
msgid "No log content available"
|
||||
msgstr "没有日志记录"
|
||||
|
||||
msgid "Error: "
|
||||
msgstr "错误: "
|
||||
|
||||
msgid "Iperf3 service control"
|
||||
msgstr "Iperf3 服务控制"
|
||||
|
||||
msgid "Listen Port"
|
||||
msgstr "监听端口"
|
||||
|
||||
msgid "Invalid format. Use [::]:port or ip:port"
|
||||
msgstr "格式错误.如 [::]:端口 或 ip:端口"
|
||||
|
||||
msgid "Enable Log View"
|
||||
msgstr "开启日志显示"
|
||||
|
||||
msgid "Download iperf3 client"
|
||||
msgstr "下载iperf3客户端"
|
||||
|
||||
msgid "Iperf3 Run Log"
|
||||
msgstr "Iperf3运行日志"
|
||||
|
||||
msgid "Refresh Log"
|
||||
msgstr "刷新日志"
|
||||
|
||||
msgid "Official Website"
|
||||
msgstr "官方网站"
|
||||
|
||||
msgid "Please enable the Homebox service"
|
||||
msgstr "请将homebox服务启用"
|
||||
|
||||
msgid "homebox Status"
|
||||
msgstr "homebox网页测速服务状态"
|
||||
msgid "Homebox service control"
|
||||
msgstr "Homebox服务控制"
|
||||
|
||||
msgid "</br>For specific usage, see:"
|
||||
msgstr "</br>具体使用方法参见:"
|
||||
msgid "Homebox Server"
|
||||
msgstr "Homebox服务端"
|
||||
|
||||
msgid "iperfstart"
|
||||
msgstr "iperf服务启动"
|
||||
msgid "Homebox Service Not Running"
|
||||
msgstr "Homebox服务端未运行"
|
||||
|
||||
msgid "iperfstop"
|
||||
msgstr "iperf服务停止"
|
||||
msgid "Ookla® SpeedTest-CLI"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select function"
|
||||
msgstr "选择功能"
|
||||
|
||||
msgid "Execute selected functions"
|
||||
msgstr "执行选择的功能"
|
||||
|
||||
msgid "client version selection"
|
||||
msgstr "客户端版本"
|
||||
|
||||
msgid "python3-speedtest-cli"
|
||||
msgstr "python3网络测试客户端"
|
||||
|
||||
msgid "ookla-speedtest-cli"
|
||||
msgstr "ookla网络测试客户端"
|
||||
|
||||
msgid "iperf3 instructions"
|
||||
msgstr "iperf3使用说明"
|
||||
|
||||
msgid "Test speed service started"
|
||||
msgstr "测试速度服务已经启动"
|
||||
|
||||
msgid "The speed measurement terminal must be in the same LAN as the router that starts the speed measurement"
|
||||
msgstr "测速终端机必须与启动测速的路由器在同一局域网内"
|
||||
|
||||
msgid "Operation steps: start router speed measurement service download test client run speed measurement client input IP address of router speed measurement service"
|
||||
msgstr "使用步骤:A.启动路由器测速服务 B.下载测试客户端 C.运行测速客户端 D.输入路由器测速服务IP地址。 "
|
||||
|
||||
msgid "Github download iperf3"
|
||||
msgstr "Github下载iperf3"
|
||||
|
||||
msgid "Iperf3 speed measurement software download"
|
||||
msgstr "iperf3测速软件下载"
|
||||
|
||||
msgid "speedtest.net Broadband speed test"
|
||||
msgstr "speedtest.net宽带网速测试"
|
||||
|
||||
|
||||
msgid "Operation execution complete"
|
||||
msgstr "操作执行完毕"
|
||||
|
||||
msgid "Network speed test, please wait..."
|
||||
msgstr "网速测试中,请稍侯..."
|
||||
|
||||
msgid "Download from foreign official websites"
|
||||
msgstr "国外官网"
|
||||
|
||||
msgid "Server Port Latency Test"
|
||||
msgstr "服务器端口延迟测试"
|
||||
|
||||
msgid "Test server address"
|
||||
msgstr "测试服务器地址"
|
||||
|
||||
msgid "Test server port"
|
||||
msgstr "测试服务器端口"
|
||||
|
||||
msgid "Enter the domain name or IP address of the server that needs to be tested"
|
||||
msgstr "输入需要测试的服务器域名或者IP地址"
|
||||
|
||||
msgid "Test server port delay"
|
||||
msgstr "测试端口延时"
|
||||
msgid "Start Ookla SpeedTest"
|
||||
msgstr "开始宽带测速"
|
||||
|
||||
msgid "Click to execute"
|
||||
msgstr "点击执行"
|
||||
|
||||
msgid "Test failed"
|
||||
msgstr "测试失败"
|
||||
msgid "Testing in progress..."
|
||||
msgstr "测试正在进行中..."
|
||||
|
||||
msgid "Waiting (executing)..."
|
||||
msgstr "努力执行中"
|
||||
msgid "Test failed."
|
||||
msgstr "测速失败"
|
||||
|
||||
msgid "Server Port Delay Value"
|
||||
msgstr "服务器端口延时值"
|
||||
|
||||
msgid "iperf3 commands reference"
|
||||
msgstr "iperf3命令参考"
|
||||
|
||||
msgid "-b, --bandwidth ..<number>[KMG]...target bandwidth in bits/sec (0 for unlimited)"
|
||||
msgstr "-b, --bandwidth ..<number>[KMG]...目标带宽(以位/秒为单位)(0表示无限制)"
|
||||
|
||||
msgid "-t, --time .......<number>........time in seconds to transmit for (default 10 secs)"
|
||||
msgstr "-t, --time .......<number>........传输时间(以秒为单位)(默认为10秒)"
|
||||
|
||||
msgid "-i, --interval ...<number>........seconds between periodic bandwidth reports"
|
||||
msgstr "-i, --interval ...<number>........测试报告之间的秒数"
|
||||
|
||||
msgid "-P, --parallel ...<number>........number of parallel client streams to run"
|
||||
msgstr "-P, --parallel ...<number>........多线程运行的数量"
|
||||
|
||||
msgid "-R, --reverse ....................run in reverse mode (server sends, client receives)"
|
||||
msgstr "-R, --reverse ....................反向模式运行(服务器发送,客户端接收)"
|
||||
|
||||
msgid "-c, --client host ................run in client mode, connecting to host"
|
||||
msgstr "-c, --client host ................客户端模式下运行,连接到host"
|
||||
|
||||
msgid "-s, --server .....................run in server mode"
|
||||
msgstr "-s, --server .....................服务器模式下运行"
|
||||
|
||||
msgid "-u, --udp ........................use UDP rather than TCP"
|
||||
msgstr "-u, --udp ........................测试传输使用UDP而不是TCP"
|
||||
|
||||
msgid "Perform OK"
|
||||
msgstr "执行完成"
|
||||
msgid "No test results yet."
|
||||
msgstr "还没有测试结果"
|
||||
|
||||
msgid "Test Script"
|
||||
msgstr "测速脚本"
|
|
@ -1,15 +0,0 @@
|
|||
config netspeedtest 'netspeedtest'
|
||||
option enabled '0'
|
||||
option port '3300'
|
||||
|
||||
config homebox 'homebox'
|
||||
option enabled '0'
|
||||
option port '3300'
|
||||
|
||||
config speedtestiperf3 'speedtestiperf3'
|
||||
|
||||
config speedtestwan 'speedtestwan'
|
||||
|
||||
config speedtestport 'speedtestport'
|
||||
option sport '443'
|
||||
option sdomain 'www.taobao.com'
|
|
@ -1,161 +1,72 @@
|
|||
#!/bin/sh /etc/rc.common
|
||||
# Copyright (C) 2021-2025 sirpdboy
|
||||
|
||||
#
|
||||
# Copyright (C) 2020-2025 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/netspeedtest
|
||||
|
||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||
#
|
||||
. "${IPKG_INSTROOT}/lib/functions.sh"
|
||||
|
||||
START=99
|
||||
USE_PROCD=1
|
||||
|
||||
PROG=/usr/bin/homebox
|
||||
EXTRA_COMMANDS="wantest"
|
||||
EXTRA_COMMANDS="download_ookla ookla_verify"
|
||||
EXTRA_HELP=\
|
||||
" download_ookla Download Ookla Speedtest-CLI
|
||||
ookla_verify Verify Ookla Speedtest-CLI integrity"
|
||||
|
||||
TMP_T=/var/netspeedtest_nstest.tmp
|
||||
BINSPEEDTEST='/usr/bin/speedtest'
|
||||
LOCK=/var/lock/netspeedtest_nstest.lock
|
||||
LOGD=/etc/netspeedtest
|
||||
LOG=/var/log/netspeedtest.log
|
||||
LOGE=$LOGD/netspeedtest.log
|
||||
LOGT=$LOGD/netspeedtestpos
|
||||
[ -d "$LOGD" ] || mkdir -p $LOGD
|
||||
[ -f "$LOGE" ] || echo "start" > $LOGE
|
||||
[ -f "$LOGT" ] || echo "start" > $LOGT
|
||||
[ -f "$LOG" ] || echo "start" > $LOG
|
||||
|
||||
MAX_LOG=500
|
||||
limit_log() {
|
||||
local logf=$1
|
||||
local max=$2
|
||||
[ ! -f "$logf" ] && return
|
||||
sc=${max:-$MAX_LOG}
|
||||
local count=$(grep -c "" $logf)
|
||||
if [ $count -gt $sc ];then
|
||||
let count=count-$sc
|
||||
sed -i "1,$count d" $logf
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
echone() {
|
||||
echo -ne $* >> $LOG
|
||||
}
|
||||
|
||||
|
||||
wantest() {
|
||||
[ -f $LOCK ] && exit
|
||||
limit_log $LOG 500
|
||||
touch $LOCK
|
||||
TESTMODE=$1
|
||||
BINTEMP=$(git_bin_handle $TESTMODE)
|
||||
case $TESTMODE in
|
||||
0)
|
||||
echo -ne "\n —————ookla-speedtest测速—————\n" | tee -a $LOG
|
||||
$BINTEMP | tee $TMP_T
|
||||
RESULT=$(cat $TMP_T | grep 'URL' | cut -c15-)
|
||||
if [ -n "$RESULT" ] ;then
|
||||
echo -ne "\n 测服信息:`cat $TMP_T | grep 'Server'| cut -c10- | awk -F: '{printf $2$3}'` 线路:`cat $TMP_T | grep 'ISP' | awk -F: '{printf $2}' ` 延时:`cat $TMP_T | grep 'Latency' | awk -F: '{printf $2}' | awk -F '(' '{printf $1}'`" >> $LOG
|
||||
echo -ne "\n 下行速率:`cat $TMP_T | grep 'Download' |awk -F: '{printf $2}' | awk -F '(' '{printf $1}'` --" >> $LOG
|
||||
echo -ne "-- 上行速率:`cat $TMP_T | grep 'Upload' |awk -F: '{printf $2}' | awk -F '(' '{printf $1}'`" >> $LOG
|
||||
echo -ne "\n 测速结果图片链接:`cat $TMP_T | grep 'URL' | cut -c15-`" >> $LOG
|
||||
else
|
||||
echo -ne "\n 因客户端在LUCI下运行错误,测试失败!" | tee -a $LOG
|
||||
echo -ne "\n 请SSH运行:/etc/init.d/netspeedtest wantest 0 测试,完成后,在日志中有测试结果!" | tee -a $LOG
|
||||
fi
|
||||
echo -ne "\n 测试时间: `date +%Y-%m-%d' '%H:%M:%S`" | tee -a $LOG
|
||||
echo -ne "\n ————————————————————\n" | tee -a $LOG
|
||||
;;
|
||||
*)
|
||||
echo -ne "\n —————python3-speedtest测速—————\n" | tee -a $LOG
|
||||
$BINTEMP | tee $TMP_T
|
||||
echo -ne "\n 测服信息:$(cat $TMP_T | grep 'Hosted by'| cut -c10- | awk -F: '{printf $1}') 延时:$(cat $TMP_T | grep 'Hosted by' | awk -F: '{printf $2}')" >> $LOG
|
||||
echo -ne "\n 下行速率:$(cat $TMP_T | grep 'Download:' |awk -F: '{printf $2}' ) --" >> $LOG
|
||||
echo -ne "-- 上行速率:$(cat $TMP_T | grep 'Upload:' |awk -F: '{printf $2}' )" >> $LOG
|
||||
echo -ne "\n 测试结果图片链接:$(cat $TMP_T | grep 'results:' | cut -c16- )" >> $LOG
|
||||
echo -ne "\n 测试时间:`date +%Y-%m-%d" "%H:%M:%S`" | tee -a $LOG
|
||||
echo -ne "\n ————————————————————\n" | tee -a $LOG
|
||||
|
||||
;;
|
||||
esac
|
||||
rm -f $LOCK
|
||||
}
|
||||
getcpucore(){
|
||||
#i386, x86_64, arm32, arm32hf, and arm64.
|
||||
cputype=$(uname -ms | tr ' ' '_' | tr '[A-Z]' '[a-z]')
|
||||
[ -n "$(echo $cputype | grep -E "linux.*armv.*")" ] && cpucore="armel"
|
||||
[ -n "$(echo $cputype | grep -E "linux.*armv7.*")" ] && [ -n "$(cat /proc/cpuinfo | grep vfp)" ] && [ ! -d /jffs/clash ] && cpucore="armhf"
|
||||
[ -n "$(echo $cputype | grep -E "linux.*aarch64.*|linux.*armv8.*")" ] && cpucore="aarch64"
|
||||
[ -n "$(echo $cputype | grep -E "linux.*86.*")" ] && cpucore="i386"
|
||||
[ -n "$(echo $cputype | grep -E "linux.*86_64.*")" ] && cpucore="x86_64"
|
||||
echo $cpucore
|
||||
}
|
||||
|
||||
git_bin_handle()
|
||||
{
|
||||
case $1 in
|
||||
|
||||
0)
|
||||
TMPDIR=/var/netspeedtest
|
||||
BINSPEEDTEST=$TMPDIR/speedtest
|
||||
[ ! -d $TMPDIR ] && mkdir -p $TMPDIR 2>/dev/null
|
||||
if [ -x "$BINSPEEDTEST" ]; then
|
||||
chmod 755 $BINSPEEDTEST >/dev/null 2>&1
|
||||
echo "$BINSPEEDTEST --accept-gdpr --accept-license --progress=no"
|
||||
else
|
||||
ooklaurl=`curl --connect-timeout 5 -m 60 -sSL 'https://www.speedtest.net/apps/cli' | grep 'Download for Linux' | sed 's|<|\n<|g' | sed -n '/Download for Linux/,/<\/div>/p' | sed -En "s|.*<a href=\"([^\"]+)\">$(getcpucore)|\1|p" `
|
||||
[ -n "$ooklaurl" ] && curl -sSL $ooklaurl | tar -xvz -C /tmp >/dev/null 2>&1 || return
|
||||
mv /tmp/speedtest $BINSPEEDTEST >/dev/null 2>&1
|
||||
chmod 755 $BINSPEEDTEST >/dev/null 2>&1
|
||||
[ -x "$BINSPEEDTEST" ] && echo "$BINSPEEDTEST --accept-gdpr --accept-license --progress=no"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
BINSPEEDTEST='/usr/bin/speedtest'
|
||||
[ -x "$BINSPEEDTEST" ] && echo "$BINSPEEDTEST --share"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
speedtest_start(){
|
||||
case $1 in
|
||||
0)
|
||||
speedtest_start_ookla $2 ;;
|
||||
*)
|
||||
speedtest_start_cli $2 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
tcptest(){
|
||||
NDATA=`date +%Y-%m-%d' '%H:%M:%S`
|
||||
echo -ne "\n $NDATA 服务器:$1 ---- 端口:$2 ---- TCP延时:$3 ms \n" >> $LOG
|
||||
}
|
||||
#
|
||||
OOKLA_SPEEDTEST='/usr/bin/speedtest'
|
||||
# uci
|
||||
CONFIG='netspeedtest'
|
||||
NAMEDDSECTION='config'
|
||||
|
||||
get_config() {
|
||||
config_get_bool enabled $1 enabled 1
|
||||
config_get_bool logger $1 logger 1
|
||||
config_load netspeedtest
|
||||
config_get "proxy_enabled" "config" "proxy_enabled" "0"
|
||||
}
|
||||
|
||||
homebox_prepare() {
|
||||
pgrep -f homebox | xargs kill -9 >/dev/null 2>&1
|
||||
killall homebox
|
||||
killall homebox
|
||||
|
||||
download_ookla() {
|
||||
local url arch=$1
|
||||
[ -z "$arch" ] && return 1
|
||||
|
||||
[ "$(uci -q get $CONFIG.$NAMEDDSECTION.proxy_enabled)" == "1" ] && \
|
||||
export ALL_PROXY=$(uci -q get $CONFIG.$NAMEDDSECTION.proxy_protocol)://$(uci -q get $CONFIG.$NAMEDDSECTION.proxy_server)
|
||||
|
||||
UA='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36'
|
||||
url=$( \
|
||||
curl --connect-timeout 10 --retry 3 -sSL 'https://www.speedtest.net/apps/cli' \
|
||||
--user-agent "$UA" \
|
||||
| grep "Download for Linux" \
|
||||
| sed 's|<|\n<|g' \
|
||||
| sed -n '/Download for Linux/,/<\/div>/p' \
|
||||
| sed -En "s|.*<a href=\"([^\"]+)\">${arch}|\1|p" \
|
||||
)
|
||||
[ -z "$url" ] && return 1
|
||||
|
||||
[ -n "$url" ] && curl -sSL $url --user-agent "$UA" | tar -xvz -C /tmp
|
||||
mkdir -p ${OOKLA_SPEEDTEST%/*} 2>/dev/null
|
||||
cp -f /tmp/speedtest $OOKLA_SPEEDTEST
|
||||
chmod 755 $OOKLA_SPEEDTEST
|
||||
ookla_verify || rm -f $OOKLA_SPEEDTEST
|
||||
unset ALL_PROXY
|
||||
}
|
||||
|
||||
ookla_verify() {
|
||||
if [ -x "$OOKLA_SPEEDTEST" ]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
start_service() {
|
||||
config_load netspeedtest
|
||||
config_foreach get_config netspeedtest
|
||||
[ "x$enabled" != "x1" ] && {
|
||||
homebox_prepare
|
||||
exit
|
||||
}
|
||||
procd_open_instance
|
||||
procd_set_param command $PROG
|
||||
[ "x$logger" == x1 ] && procd_set_param stderr 1
|
||||
# procd_set_param respawn
|
||||
procd_close_instance
|
||||
ookla_verify
|
||||
|
||||
}
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "netspeedtest"
|
||||
}
|
||||
|
||||
service_triggers() {
|
||||
procd_add_reload_trigger "netspeedtest"
|
||||
reload_service() {
|
||||
stop
|
||||
sleep 1
|
||||
start
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
#!/bin/sh
|
||||
chmod +x /etc/init.d/netspeedtest >/dev/null 2>&1
|
||||
[ -f "/etc/config/netspeedtest" ] || {
|
||||
cat >/etc/config/netspeedtest <<-EOF
|
||||
config netspeedtest 'netspeedtest'
|
||||
option enabled '0'
|
||||
option port '3300'
|
||||
|
||||
config homebox 'homebox'
|
||||
option enabled '0'
|
||||
option port '3300'
|
||||
|
||||
config speedtestiperf3 'speedtestiperf3'
|
||||
|
||||
config speedtestwan 'speedtestwan'
|
||||
|
||||
config speedtestport 'speedtestport'
|
||||
option sport '443'
|
||||
option sdomain 'www.taobao.com'
|
||||
EOF
|
||||
}
|
||||
[ `uci -q get netspeedtest.global` ] && uci set netspeedtest.global=global
|
||||
[ `uci -q get netspeedtest.netspeedtest` ] || uci set netspeedtest.netspeedtest=netspeedtest
|
||||
[ `uci -q get netspeedtest.speedtestiperf3` ] || uci set netspeedtest.speedtestiperf3=speedtestiperf3
|
||||
[ `uci -q get netspeedtest.speedtestport` ] || uci set netspeedtest.speedtestport=speedtestport
|
||||
[ `uci -q get netspeedtest.speedtestwan` ] || uci set netspeedtest.speedtestport=speedtestwan
|
||||
LOGD=/etc/netspeedtest
|
||||
LOG=/var/log/netspeedtest.log
|
||||
LOGE=$LOGD/netspeedtest.log
|
||||
LOGT=$LOGD/netspeedtestpos
|
||||
[ -d "$LOGD" ] || mkdir -p $LOGD
|
||||
[ -f "$LOGE" ] || echo "start" > $LOGE
|
||||
[ -f "$LOGT" ] || echo "1" > $LOGT
|
||||
[ -f "$LOG" ] || echo "start" > $LOG
|
||||
|
||||
rm -rf /tmp/luci-modulecache /tmp/luci-indexcache*
|
||||
exit 0
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
touch /etc/config/netspeedtest
|
||||
uci -q batch <<-EOF >/dev/null
|
||||
set netspeedtest.config=netspeedtest
|
||||
commit netspeedtest
|
||||
EOF
|
|
@ -1,98 +0,0 @@
|
|||
################################################################################
|
||||
# (netspeedtest) functions.sh
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# Copyright (C) 2019-2025 The Sirpdboy Team <herboy2008@gmail.com>
|
||||
# . /lib/netspeedtest/functions.sh
|
||||
################################################################################
|
||||
|
||||
# tmp speedtest
|
||||
TMP_TEST=/var/netspeedtest.tmp
|
||||
MAX_LOG=200
|
||||
LOG=/var/log/netspeedtest.log
|
||||
limit_log() {
|
||||
local logf=$1
|
||||
local max=$2
|
||||
[ ! -f "$logf" ] && return
|
||||
sc=${max:-$MAX_LOG}
|
||||
local count=$(grep -c "" $logf)
|
||||
if [ $count -gt $sc ];then
|
||||
let count=count-$sc
|
||||
sed -i "1,$count d" $logf
|
||||
fi
|
||||
}
|
||||
|
||||
init_log() {
|
||||
local logf=$1
|
||||
[ ! -f "$logf" ] && echo "" > $logf
|
||||
}
|
||||
|
||||
echone() {
|
||||
echo -ne $* >> $LOG
|
||||
}
|
||||
|
||||
|
||||
git_bin_handle()
|
||||
{
|
||||
case $1 in
|
||||
|
||||
0)
|
||||
TMPDIR=/var/netspeedtest
|
||||
BINSPEEDTEST=$TMPDIR/speedtest
|
||||
[ ! -d $TMPDIR ] && mkdir -p $TMPDIR 2>/dev/null
|
||||
if [ -x "$BINSPEEDTEST" ]; then
|
||||
chmod 755 $BINSPEEDTEST >/dev/null 2>&1
|
||||
echo "$BINSPEEDTEST --accept-gdpr --accept-license --progress=no"
|
||||
else
|
||||
ooklaurl=`curl --connect-timeout 5 -m 60 -sSL 'https://www.speedtest.net/apps/cli' | grep 'Download for Linux' | sed 's|<|\n<|g' | sed -n '/Download for Linux/,/<\/div>/p' | sed -En "s|.*<a href=\"([^\"]+)\">x86_64|\1|p" `
|
||||
[ -n "$ooklaurl" ] && curl -sSL $ooklaurl | tar -xvz -C /tmp >/dev/null 2>&1 || return
|
||||
mv /tmp/speedtest $BINSPEEDTEST >/dev/null 2>&1
|
||||
chmod 755 $BINSPEEDTEST >/dev/null 2>&1
|
||||
[ -x "$BINSPEEDTEST" ] && echo "$BINSPEEDTEST --accept-gdpr --accept-license --progress=no"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
BINSPEEDTEST='/usr/bin/speedtest'
|
||||
[ -x "$BINSPEEDTEST" ] && echo "$BINSPEEDTEST --share"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
speedtest_start(){
|
||||
case $1 in
|
||||
0)
|
||||
speedtest_start_ookla $2 ;;
|
||||
*)
|
||||
speedtest_start_cli $2 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
speedtest_start_ookla(){
|
||||
echone "\n ookla-speedtest测速"
|
||||
info=$($1 > $TMP_TEST ) >/dev/null 2>&1
|
||||
echone "\n info:$info ----------------- "
|
||||
echone "\n 测服信息:`cat $TMP_TEST | grep 'Server'| cut -c10- | awk -F: '{printf $2$3}'` 线路:`cat $TMP_TEST | grep 'ISP' | awk -F: '{printf $2}' ` 延时:`cat $TMP_TEST | grep 'Latency' | awk -F: '{printf $2}' | awk -F '(' '{printf $1}'`"
|
||||
echone "\n 下行速率:`cat $TMP_TEST | grep 'Download' |awk -F: '{printf $2}' | awk -F '(' '{printf $1}'` --"
|
||||
echone "-- 上行速率:`cat $TMP_TEST | grep 'Upload' |awk -F: '{printf $2}' | awk -F '(' '{printf $1}'`"
|
||||
echo -ne "\n 测速结果图片链接:`cat $TMP_TEST | grep 'URL' | cut -c15-`" >> $LOG
|
||||
echone "\n 测试时间: `date +%Y-%m-%d' '%H:%M:%S`"
|
||||
echone "\n ————————————————————————————\n"
|
||||
cat $TMP_TEST | grep 'URL' | cut -c15- > /var/speedtesturl.tmp
|
||||
echo -ne "`cat $TMP_TEST | grep 'URL' | cut -c15- `"
|
||||
}
|
||||
|
||||
speedtest_start_cli(){
|
||||
echone "\n python3-speedtest测速"
|
||||
info=$($1 > $TMP_TEST ) >/dev/null 2>&1
|
||||
echone "\n 测服信息:`cat $TMP_TEST | grep 'Hosted by'| cut -c10- | awk -F: '{printf $1}'` 延时:`cat $TMP_TEST | grep 'Hosted by' | awk -F: '{printf $2}'`"
|
||||
echone "\n 下行速率:`cat $TMP_TEST | grep 'Download:' |awk -F: '{printf $2}' ` --"
|
||||
echone "-- 上行速率:`cat $TMP_TEST | grep 'Upload:' |awk -F: '{printf $2}' `"
|
||||
echo -ne "\n 测速结果图片链接:`cat $TMP_TEST | grep 'results' | cut -c16-`" >> $LOG
|
||||
echone "\n 测试时间: `date +%Y-%m-%d' '%H:%M:%S`"
|
||||
echone "\n ————————————————————————————\n"
|
||||
cat $TMP_TEST | grep 'results:' | cut -c16- > /var/speedtesturl.tmp
|
||||
echo -ne "`cat $TMP_TEST | grep 'results:' | cut -c16-`"
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,37 @@
|
|||
#!/bin/bash
|
||||
mkdir -p /etc/speedtest
|
||||
export HOME='/etc/speedtest'
|
||||
SPEEDTEST_CLI='/usr/bin/speedtest'
|
||||
SPEEDTEST_RESULT='/tmp/speedtest_result'
|
||||
|
||||
[ -n "$(pgrep -f "$SPEEDTEST_CLI")" ] && exit 1
|
||||
|
||||
echo "Start Testing" > "$SPEEDTEST_RESULT"
|
||||
LOCAL_IP=$(curl -s -4 --connect-timeout 3 http://ip.3322.net)
|
||||
|
||||
BAIDU_SK="LHHGlmhcb4ENvIXpR9QQ2tBYa6ooUowX hYCENCEx1nXO0Nt46ldexfG9oI49xBGh 0kKZnWWhXEPfzIkklmzAa3dZ"
|
||||
if [ -n "$LOCAL_IP" ]; then
|
||||
for SK in $BAIDU_SK
|
||||
do
|
||||
INFO=$(curl -sk --connect-timeout 3 "https://api.map.baidu.com/location/ip?ip="$LOCAL_IP"&coor=bd09ll&ak=$SK")
|
||||
if [ "$(echo $INFO | jsonfilter -e "@['status']")" = 0 ]; then
|
||||
status=0
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$status" = 0 ]; then
|
||||
lon=$(echo $INFO | jsonfilter -e "@['content']['point']['x']")
|
||||
lat=$(echo $INFO | jsonfilter -e "@['content']['point']['y']")
|
||||
server_id=$(curl -sk --connect-timeout 3 "https://www.speedtest.net/api/ios-config.php?lon=$lon&lat=$lat" | grep "server url" | head -n1 | sed 's/.*id="//;s/".*//')
|
||||
[ -n "$server_id" ] && ARG="-s $server_id"
|
||||
fi
|
||||
fi
|
||||
|
||||
RUNTEST=$($SPEEDTEST_CLI --accept-gdpr --accept-license --progress=no $ARG 2>&1)
|
||||
if [ $(echo $RUNTEST | grep -c "No servers defined") -ge 1 ] || [ $(echo $RUNTEST | grep -c "error") -ge 1 ]; then
|
||||
RUNTEST=$($SPEEDTEST_CLI --accept-gdpr --accept-license --progress=no 2>&1)
|
||||
fi
|
||||
|
||||
RESULT=$(echo "$RUNTEST" | grep "Result URL" | awk '{print $NF}')
|
||||
|
||||
[ -n "$RESULT" ] && echo "$RESULT" > "$SPEEDTEST_RESULT" || echo "Test failed" > "$SPEEDTEST_RESULT"
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"admin/network/netspeedtest": {
|
||||
"title": "NetSpeedtest",
|
||||
"order": 90,
|
||||
"action": {
|
||||
"type": "firstchild"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-netspeedtest" ],
|
||||
"uci": { "netspeedtest": true }
|
||||
}
|
||||
},
|
||||
"admin/network/netspeedtest/iperf3": {
|
||||
"title": "Lan Speedtest Iperf3",
|
||||
"order": 1,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "netspeedtest/iperf3"
|
||||
}
|
||||
},
|
||||
"admin/network/netspeedtest/homebox": {
|
||||
"title": "Lan Speedtest Homebox",
|
||||
"order": 2,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "netspeedtest/homebox"
|
||||
}
|
||||
},
|
||||
"admin/network/netspeedtest/speedtest": {
|
||||
"title": "Wan Ookla SpeedTest",
|
||||
"order": 4,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "netspeedtest/speedtest"
|
||||
}
|
||||
},
|
||||
"admin/network/netspeedtest/openspeedtest": {
|
||||
"title": "Wan OpenSpeedTest",
|
||||
"order": 5,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "netspeedtest/openspeedtest"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,19 @@
|
|||
{
|
||||
"luci-app-netspeedtest": {
|
||||
"description": "Grant UCI access for luci-app-netspeedtest",
|
||||
"read": {
|
||||
"uci": [ "netspeedtest" ]
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "netspeedtest" ]
|
||||
}
|
||||
}
|
||||
"luci-app-netspeedtest": {
|
||||
"description": "Grant access to netspeedtest procedures",
|
||||
"read": {
|
||||
"file": {
|
||||
"/etc/init.d/netspeedtest": [ "exec" ],
|
||||
"/usr/lib/netspeedtest/speedtest": [ "exec" ],
|
||||
"/tmp/speedtest_result": [ "read" ]
|
||||
},
|
||||
"ubus": {
|
||||
"service": [ "list" ]
|
||||
},
|
||||
"uci": [ "netspeedtest" ]
|
||||
},
|
||||
"write": {
|
||||
"uci": [ "netspeedtest" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -544,6 +544,7 @@ local hysteria2_type = map:get("@global_subscribe[0]", "hysteria2_type") or "sin
|
|||
params += opt.query("sni", dom_prefix + "tls_serverName");
|
||||
params += opt.query("alpn", dom_prefix + "tuic_alpn");
|
||||
params += opt.query("congestion_control", dom_prefix + "tuic_congestion_control");
|
||||
params += opt.query("udp_relay_mode", dom_prefix + "tuic_udp_relay_mode");
|
||||
params += opt.query("allowinsecure", dom_prefix + "tls_allowInsecure");
|
||||
|
||||
params += "#" + encodeURI(v_alias.value);
|
||||
|
@ -1437,6 +1438,7 @@ local hysteria2_type = map:get("@global_subscribe[0]", "hysteria2_type") or "sin
|
|||
}
|
||||
}
|
||||
opt.set(dom_prefix + 'tuic_congestion_control', queryParam.congestion_control || 'cubic');
|
||||
opt.set(dom_prefix + 'tuic_udp_relay_mode', queryParam.udp_relay_mode || 'native');
|
||||
opt.set(dom_prefix + 'tuic_alpn', queryParam.alpn || 'default');
|
||||
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
|
||||
opt.set(dom_prefix + 'tls_allowInsecure', true);
|
||||
|
|
|
@ -1323,6 +1323,7 @@ local function processData(szType, content, add_mode, add_from)
|
|||
result.tls_serverName = params.sni
|
||||
result.tuic_alpn = params.alpn or "default"
|
||||
result.tuic_congestion_control = params.congestion_control or "cubic"
|
||||
result.tuic_udp_relay_mode = params.udp_relay_mode or "native"
|
||||
params.allowinsecure = params.allowinsecure or params.insecure
|
||||
if params.allowinsecure then
|
||||
if params.allowinsecure == "1" or params.allowinsecure == "0" then
|
||||
|
|
|
@ -49,6 +49,10 @@ Some features are deprecated / unstable so they are placed in preview app. To en
|
|||
* Reboot your router
|
||||
* There will be a new menu option `Xray (preview)` in `Services`
|
||||
|
||||
## Changelog since 3.6.0
|
||||
|
||||
* 2025-05-13 feat: geodata reader
|
||||
|
||||
## Changelog since 3.5.0
|
||||
|
||||
* 2024-11-26 chore: bump status version
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-xray
|
||||
PKG_VERSION:=3.5.0
|
||||
PKG_VERSION:=3.6.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=MPLv2
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-xray-geodata
|
||||
PKG_VERSION:=3.6.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=MPLv2
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
PKG_MAINTAINER:=yichya <mail@yichya.dev>
|
||||
PKG_BUILD_PARALLEL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=Custom
|
||||
CATEGORY:=Extra packages
|
||||
TITLE:=LuCI Support for Xray (geodata page)
|
||||
DEPENDS:=luci-app-xray +xray-geodata
|
||||
PKGARCH:=all
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
LuCI Support for Xray (Client-side Rendered) (geodata page).
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
echo "luci-app-xray $(PKG_VERSION)-$(PKG_RELEASE) `git rev-parse HEAD` `date +%s`" > $(PKG_BUILD_DIR)/version.txt
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/www/luci-static/resources/view/xray
|
||||
$(INSTALL_DATA) ./root/www/luci-static/resources/view/xray/geodata.js $(1)/www/luci-static/resources/view/xray/geodata.js
|
||||
$(INSTALL_DIR) $(1)/usr/share/luci/menu.d
|
||||
$(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-xray-geodata.json $(1)/usr/share/luci/menu.d/luci-app-xray-geodata.json
|
||||
$(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d
|
||||
$(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-xray-geodata.json $(1)/usr/share/rpcd/acl.d/luci-app-xray-geodata.json
|
||||
$(INSTALL_DIR) $(1)/www/xray
|
||||
$(LN) /usr/share/xray/geoip.dat $(1)/www/xray/geoip.dat
|
||||
$(LN) /usr/share/xray/geosite.dat $(1)/www/xray/geosite.dat
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"admin/services/xray_geodata": {
|
||||
"title": "Xray (geodata)",
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "xray/geodata"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [
|
||||
"luci-app-xray-geodata"
|
||||
],
|
||||
"uci": {
|
||||
"xray_core": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"luci-app-xray-geodata": {
|
||||
"description": "Grant access to xray configurations",
|
||||
"read": {
|
||||
"uci": [
|
||||
"xray_core"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,460 @@
|
|||
'use strict';
|
||||
'require dom';
|
||||
'require uci';
|
||||
'require ui';
|
||||
'require view';
|
||||
'require view.xray.shared as shared';
|
||||
|
||||
const maxResults = 2048;
|
||||
|
||||
const WireType = {
|
||||
VARINT: 0,
|
||||
FIXED64: 1,
|
||||
LENGTH_DELIMITED: 2,
|
||||
FIXED32: 5
|
||||
};
|
||||
|
||||
class ProtoReader {
|
||||
constructor(buffer) {
|
||||
this.buffer = new Uint8Array(buffer);
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
readVarint() {
|
||||
let result = 0;
|
||||
let shift = 0;
|
||||
|
||||
while (this.pos < this.buffer.length) {
|
||||
const byte = this.buffer[this.pos++];
|
||||
result |= (byte & 0x7F) << shift;
|
||||
if ((byte & 0x80) === 0) {
|
||||
return result;
|
||||
}
|
||||
shift += 7;
|
||||
}
|
||||
throw new Error('Malformed varint');
|
||||
}
|
||||
|
||||
readString() {
|
||||
const length = this.readVarint();
|
||||
const value = new TextDecoder().decode(
|
||||
this.buffer.slice(this.pos, this.pos + length)
|
||||
);
|
||||
this.pos += length;
|
||||
return value;
|
||||
}
|
||||
|
||||
readBytes() {
|
||||
const length = this.readVarint();
|
||||
const bytes = this.buffer.slice(this.pos, this.pos + length);
|
||||
this.pos += length;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
readTag() {
|
||||
const tag = this.readVarint();
|
||||
return {
|
||||
fieldNumber: tag >>> 3,
|
||||
wireType: tag & 0x7
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Domain Type enum
|
||||
const DomainType = {
|
||||
Plain: 0,
|
||||
Regex: 1,
|
||||
Domain: 2,
|
||||
Full: 3
|
||||
};
|
||||
|
||||
function decodeDomainAttribute(reader) {
|
||||
const attribute = {
|
||||
key: '',
|
||||
typedValue: null
|
||||
};
|
||||
|
||||
while (reader.pos < reader.buffer.length) {
|
||||
const tag = reader.readTag();
|
||||
|
||||
switch (tag.fieldNumber) {
|
||||
case 1: // key
|
||||
attribute.key = reader.readString();
|
||||
break;
|
||||
case 2: // bool_value
|
||||
attribute.typedValue = reader.readVarint() !== 0;
|
||||
break;
|
||||
case 3: // int_value
|
||||
attribute.typedValue = reader.readVarint();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown field number: ${tag.fieldNumber}`);
|
||||
}
|
||||
}
|
||||
|
||||
return attribute;
|
||||
}
|
||||
|
||||
function decodeDomain(reader) {
|
||||
const domain = {
|
||||
type: DomainType.Plain,
|
||||
value: '',
|
||||
attribute: []
|
||||
};
|
||||
|
||||
while (reader.pos < reader.buffer.length) {
|
||||
const tag = reader.readTag();
|
||||
|
||||
switch (tag.fieldNumber) {
|
||||
case 1: // type
|
||||
domain.type = reader.readVarint();
|
||||
break;
|
||||
case 2: // value
|
||||
domain.value = reader.readString();
|
||||
break;
|
||||
case 3: // attribute
|
||||
const attrBytes = reader.readBytes();
|
||||
domain.attribute.push(
|
||||
decodeDomainAttribute(new ProtoReader(attrBytes))
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown field number: ${tag.fieldNumber}`);
|
||||
}
|
||||
}
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
function decodeCIDR(reader) {
|
||||
const cidr = {
|
||||
ip: new Uint8Array(),
|
||||
prefix: 0
|
||||
};
|
||||
|
||||
while (reader.pos < reader.buffer.length) {
|
||||
const tag = reader.readTag();
|
||||
|
||||
switch (tag.fieldNumber) {
|
||||
case 1: // ip
|
||||
cidr.ip = reader.readBytes();
|
||||
break;
|
||||
case 2: // prefix
|
||||
cidr.prefix = reader.readVarint();
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown field number: ${tag.fieldNumber}`);
|
||||
}
|
||||
}
|
||||
|
||||
return cidr;
|
||||
}
|
||||
|
||||
function decodeGeoIP(reader) {
|
||||
const geoIP = {
|
||||
countryCode: '',
|
||||
cidr: [],
|
||||
reverseMatch: false
|
||||
};
|
||||
|
||||
while (reader.pos < reader.buffer.length) {
|
||||
const tag = reader.readTag();
|
||||
|
||||
switch (tag.fieldNumber) {
|
||||
case 1: // country_code
|
||||
geoIP.countryCode = reader.readString();
|
||||
break;
|
||||
case 2: // cidr
|
||||
const cidrBytes = reader.readBytes();
|
||||
geoIP.cidr.push(
|
||||
decodeCIDR(new ProtoReader(cidrBytes))
|
||||
);
|
||||
break;
|
||||
case 3: // reverse_match
|
||||
geoIP.reverseMatch = reader.readVarint() !== 0;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown field number: ${tag.fieldNumber}`);
|
||||
}
|
||||
}
|
||||
|
||||
return geoIP;
|
||||
}
|
||||
|
||||
function decodeGeoSite(reader) {
|
||||
const geoSite = {
|
||||
countryCode: '',
|
||||
domain: []
|
||||
};
|
||||
|
||||
while (reader.pos < reader.buffer.length) {
|
||||
const tag = reader.readTag();
|
||||
|
||||
switch (tag.fieldNumber) {
|
||||
case 1: // country_code
|
||||
geoSite.countryCode = reader.readString();
|
||||
break;
|
||||
case 2: // domain
|
||||
const domainBytes = reader.readBytes();
|
||||
geoSite.domain.push(
|
||||
decodeDomain(new ProtoReader(domainBytes))
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown field number: ${tag.fieldNumber}`);
|
||||
}
|
||||
}
|
||||
|
||||
return geoSite;
|
||||
}
|
||||
|
||||
function decodeGeoSiteList(buffer) {
|
||||
const reader = new ProtoReader(buffer);
|
||||
const geoSiteList = {
|
||||
entry: []
|
||||
};
|
||||
|
||||
while (reader.pos < reader.buffer.length) {
|
||||
const tag = reader.readTag();
|
||||
|
||||
if (tag.fieldNumber === 1) { // entry
|
||||
const entryBytes = reader.readBytes();
|
||||
geoSiteList.entry.push(
|
||||
decodeGeoSite(new ProtoReader(entryBytes))
|
||||
);
|
||||
} else {
|
||||
throw new Error(`Unknown field number: ${tag.fieldNumber}`);
|
||||
}
|
||||
}
|
||||
|
||||
return geoSiteList;
|
||||
}
|
||||
|
||||
function decodeGeoIPList(buffer) {
|
||||
const reader = new ProtoReader(buffer);
|
||||
const geoIPList = {
|
||||
entry: []
|
||||
};
|
||||
|
||||
while (reader.pos < reader.buffer.length) {
|
||||
const tag = reader.readTag();
|
||||
|
||||
if (tag.fieldNumber === 1) { // entry
|
||||
const entryBytes = reader.readBytes();
|
||||
geoIPList.entry.push(
|
||||
decodeGeoIP(new ProtoReader(entryBytes))
|
||||
);
|
||||
} else {
|
||||
throw new Error(`Unknown field number: ${tag.fieldNumber}`);
|
||||
}
|
||||
}
|
||||
|
||||
return geoIPList;
|
||||
}
|
||||
|
||||
function matchesCIDR(cidrIp, prefix, queryIp) {
|
||||
// Check if input IP matches CIDR IP version
|
||||
if ((cidrIp.length === 4 && queryIp.length !== 4) || (cidrIp.length === 16 && queryIp.length !== 16)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare full bytes first
|
||||
const prefixBytes = Math.floor(prefix / 8);
|
||||
for (let i = 0; i < prefixBytes; i++) {
|
||||
if (cidrIp[i] !== queryIp[i]) return false;
|
||||
}
|
||||
|
||||
// Compare remaining bits if any
|
||||
const remainingBits = prefix % 8;
|
||||
if (remainingBits > 0) {
|
||||
const mask = 0xFF << (8 - remainingBits);
|
||||
return (cidrIp[prefixBytes] & mask) === (queryIp[prefixBytes] & mask);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function renderGeoIPResults(results) {
|
||||
const container = document.getElementById('geoip-results');
|
||||
container.innerHTML = '<br/>';
|
||||
|
||||
const flatResults = results.flatMap(entry => entry.cidr.map(cidr => ({ entry, cidr })));
|
||||
|
||||
const table = E('table', { 'class': 'table' }, [
|
||||
E('thead', {}, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, _('Country Code')),
|
||||
E('th', { 'class': 'th' }, _(`CIDR (${flatResults.length} total)`))
|
||||
])
|
||||
]),
|
||||
E('tbody', {}, flatResults.slice(0, maxResults).map(({ entry, cidr }, index) => {
|
||||
const ip = cidr.ip.length === 4 ? Array.from(cidr.ip).join('.') : Array.from(cidr.ip).reduce((arr, byte, i) => {
|
||||
if (i % 2 === 0) {
|
||||
arr.push((byte << 8) | cidr.ip[i + 1]);
|
||||
}
|
||||
return arr;
|
||||
}, []).map(part => part.toString(16).padStart(4, '0')).join(':').replace(/\b(?:0+:){2,}/, ':').split(':').map(octet => octet.replace(/\b0+/g, '')).join(':');
|
||||
return E('tr', { 'class': `tr cbi-rowstyle-${index % 2 + 1}` }, [
|
||||
E('td', { 'class': 'td' }, entry.countryCode),
|
||||
E('td', { 'class': 'td' }, `${ip}/${cidr.prefix}`)
|
||||
]);
|
||||
}))
|
||||
]);
|
||||
|
||||
container.appendChild(table);
|
||||
}
|
||||
|
||||
function renderGeoSiteResults(results) {
|
||||
const container = document.getElementById('geosite-results');
|
||||
container.innerHTML = '<br/>';
|
||||
|
||||
const flatResults = results.flatMap(entry => entry.domain.map(domain => ({ entry, domain })));
|
||||
|
||||
const table = E('table', { 'class': 'table' }, [
|
||||
E('thead', {}, [
|
||||
E('tr', { 'class': 'tr table-titles' }, [
|
||||
E('th', { 'class': 'th' }, _('Country Code')),
|
||||
E('th', { 'class': 'th' }, _(`Domain (${flatResults.length} total)`))
|
||||
])
|
||||
]),
|
||||
E('tbody', {}, flatResults.slice(0, maxResults).map(({ entry, domain }, index) =>
|
||||
E('tr', { 'class': `tr cbi-rowstyle-${index % 2 + 1}` }, [
|
||||
E('td', { 'class': 'td' }, entry.countryCode),
|
||||
E('td', { 'class': 'td' }, domain.value)
|
||||
])
|
||||
))
|
||||
]);
|
||||
|
||||
container.appendChild(table);
|
||||
}
|
||||
|
||||
return view.extend({
|
||||
load: function () {
|
||||
return Promise.all([
|
||||
new Date(),
|
||||
uci.load(shared.variant),
|
||||
fetch("/xray/geoip.dat").then(v => v.arrayBuffer()),
|
||||
fetch("/xray/geosite.dat").then(v => v.arrayBuffer()),
|
||||
]);
|
||||
},
|
||||
|
||||
render: function (load_result) {
|
||||
const geoip_result = decodeGeoIPList(load_result[2]);
|
||||
const geosite_result = decodeGeoSiteList(load_result[3]);
|
||||
const result = E([], {}, [
|
||||
E('div', {}, [
|
||||
E('div', { 'class': 'cbi-section', 'data-tab': 'geoip', 'data-tab-title': _('GeoIP') }, [
|
||||
E('select', {
|
||||
'id': 'geoip-select',
|
||||
'change': function () {
|
||||
const selectedCode = document.getElementById('geoip-select').value;
|
||||
const results = selectedCode ? geoip_result.entry.filter(entry => entry.countryCode === selectedCode) : [];
|
||||
renderGeoIPResults(results);
|
||||
}
|
||||
}, [
|
||||
E('option', { 'value': '' }, _('Filter GeoIP by Country Code')),
|
||||
...Array.from(new Set(geoip_result.entry.map(entry => entry.countryCode)))
|
||||
.sort()
|
||||
.map(code => {
|
||||
const count = geoip_result.entry
|
||||
.find(entry => entry.countryCode === code)
|
||||
.cidr.length;
|
||||
return E('option', { 'value': code }, `${code} (${count} items)`);
|
||||
})
|
||||
]),
|
||||
E('div', { 'class': 'cbi-section-create' }, [
|
||||
E('input', {
|
||||
'type': 'text',
|
||||
'id': 'geoip-search',
|
||||
'class': 'cbi-input-text',
|
||||
'placeholder': _('Search GeoIP...')
|
||||
}),
|
||||
E('button', {
|
||||
'class': 'cbi-button',
|
||||
'click': function () {
|
||||
const query = document.getElementById('geoip-search').value.trim();
|
||||
let queryIp = null;
|
||||
if (query.includes('.')) {
|
||||
queryIp = query.split('.').map(Number);
|
||||
} else if (query.includes(':')) {
|
||||
queryIp = query.split(':').reduce((acc, part, i, arr) => {
|
||||
if (part === '') {
|
||||
const padding = new Array((8 - arr.filter(x => x !== '').length) * 2).fill(0);
|
||||
return acc.concat(padding);
|
||||
}
|
||||
const hex = part.padStart(4, '0');
|
||||
return acc.concat([parseInt(hex.slice(0, 2), 16), parseInt(hex.slice(2, 4), 16)]);
|
||||
}, []);
|
||||
console.log(queryIp);
|
||||
}
|
||||
const selectedCode = document.getElementById('geoip-select').value;
|
||||
const results = geoip_result.entry.map(entry => ({
|
||||
...entry,
|
||||
cidr: entry.cidr.filter(cidr => {
|
||||
return queryIp && (selectedCode === '' || entry.countryCode === selectedCode) && matchesCIDR(cidr.ip, cidr.prefix, queryIp);
|
||||
})
|
||||
})).filter(entry => entry.cidr.length > 0);
|
||||
renderGeoIPResults(results);
|
||||
}
|
||||
}, _('Search GeoIP')),
|
||||
]),
|
||||
E('div', { 'id': 'geoip-results', 'class': 'results-container' }),
|
||||
]),
|
||||
E('div', { 'class': 'cbi-section', 'data-tab': 'geosite', 'data-tab-title': _('GeoSite') }, [
|
||||
E('select', {
|
||||
'id': 'geosite-select',
|
||||
'change': function () {
|
||||
const selectedCode = document.getElementById('geosite-select').value;
|
||||
const results = selectedCode ? geosite_result.entry.filter(entry => entry.countryCode === selectedCode) : [];
|
||||
renderGeoSiteResults(results);
|
||||
}
|
||||
}, [
|
||||
E('option', { 'value': '' }, _('Filter GeoSite by Country Code')),
|
||||
...Array.from(new Set(geosite_result.entry.map(entry => entry.countryCode)))
|
||||
.sort()
|
||||
.map(code => {
|
||||
const count = geosite_result.entry
|
||||
.find(entry => entry.countryCode === code)
|
||||
.domain.length;
|
||||
return E('option', { 'value': code }, `${code} (${count} items)`);
|
||||
})
|
||||
]),
|
||||
E('div', { 'class': 'cbi-section-create' }, [
|
||||
E('input', {
|
||||
'type': 'text',
|
||||
'id': 'geosite-search',
|
||||
'class': 'cbi-input-text',
|
||||
'placeholder': _('Search GeoSite...')
|
||||
}),
|
||||
E('button', {
|
||||
'class': 'cbi-button',
|
||||
'click': function () {
|
||||
const query = document.getElementById('geosite-search').value.toLowerCase();
|
||||
const selectedCode = document.getElementById('geosite-select').value;
|
||||
const results = geosite_result.entry.map(entry => ({
|
||||
...entry,
|
||||
domain: entry.domain.filter(domain => {
|
||||
return query && (selectedCode === '' || entry.countryCode === selectedCode) && domain.value.toLowerCase().includes(query);
|
||||
})
|
||||
})).filter(entry => entry.domain.length > 0);
|
||||
renderGeoSiteResults(results);
|
||||
}
|
||||
}, _('Search GeoSite')),
|
||||
]),
|
||||
E('div', { 'id': 'geosite-results', 'class': 'results-container' })
|
||||
]),
|
||||
])
|
||||
]);
|
||||
ui.tabs.initTabGroup(result.lastElementChild.childNodes);
|
||||
|
||||
return E([], [
|
||||
E('h2', _('Xray (geodata)')),
|
||||
E('p', { 'class': 'cbi-map-descr' }, `${_("Only first")} ${maxResults} ${_("results will be shown. Load GeoData files cost")} ${new Date().getTime() - load_result[0].getTime()} ${_("ms")}; ${geoip_result.entry.length} ${_("GeoIP entries")}, ${geosite_result.entry.length} ${_("GeoSite entries")}.`),
|
||||
result
|
||||
]);
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-xray-status
|
||||
PKG_VERSION:=3.5.0
|
||||
PKG_VERSION:=3.6.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=MPLv2
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=mihomo
|
||||
PKG_VERSION:=1.19.7
|
||||
PKG_VERSION:=1.19.8
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/metacubex/mihomo/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=b857289776f6ecfedaaca2cdfd9ed39746fda697bdcbbbb0b1bb172f1fc9462d
|
||||
PKG_HASH:=47e38ea4220f5b84485b06d980ff06cc9a48cd7bd9bdf1236168adf728a99835
|
||||
|
||||
PKG_MAINTAINER:=Anya Lin <hukk1996@gmail.com>
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=speedtest-cli
|
||||
PKG_VERSION:=1.2.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
ifeq ($(ARCH),aarch64)
|
||||
PKG_HASH:=3953d231da3783e2bf8904b6dd72767c5c6e533e163d3742fd0437affa431bd3
|
||||
else ifeq ($(ARCH),arm)
|
||||
ARM_CPU_FEATURES:=$(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE))))
|
||||
ifeq ($(ARM_CPU_FEATURES),)
|
||||
ARCH:=armel
|
||||
PKG_HASH:=629a455a2879224bd0dbd4b36d8c721dda540717937e4660b4d2c966029466bf
|
||||
else
|
||||
ARCH:=armhf
|
||||
PKG_HASH:=e45fcdebbd8a185553535533dd032d6b10bc8c64eee4139b1147b9c09835d08d
|
||||
endif
|
||||
else ifeq ($(ARCH),i386)
|
||||
PKG_HASH:=9ff7e18dbae7ee0e03c66108445a2fb6ceea6c86f66482e1392f55881b772fe8
|
||||
else ifeq ($(ARCH),x86_64)
|
||||
PKG_HASH:=5690596c54ff9bed63fa3732f818a05dbc2db19ad36ed68f21ca5f64d5cfeeb7
|
||||
endif
|
||||
|
||||
PKG_SOURCE:=ookla-speedtest-$(PKG_VERSION)-linux-$(ARCH).tgz
|
||||
PKG_SOURCE_URL:=https://install.speedtest.net/app/cli
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
|
||||
|
||||
PKG_MAINTAINER:=sbwml <admin@cooluc.com>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Speedtest CLI by Ookla
|
||||
DEPENDS:=@(aarch64||arm||i386||x86_64) +ca-bundle
|
||||
URL:=https://www.speedtest.net/
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
The Global Broadband Speed Test
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
( \
|
||||
pushd $(PKG_BUILD_DIR) ; \
|
||||
$(TAR) -zxf $(DL_DIR)/ookla-speedtest-$(PKG_VERSION)-linux-$(ARCH).tgz -C . ; \
|
||||
popd ; \
|
||||
)
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/speedtest $(1)/usr/bin
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
Loading…
Reference in New Issue