mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-08-01 23:19:33 +08:00
🍕 Sync 2026-04-25 20:15:58
This commit is contained in:
parent
80f09ddc21
commit
8a2eefa223
@ -1,8 +1,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=3ginfo
|
||||
PKG_VERSION:=20260404
|
||||
PKG_RELEASE:=2
|
||||
PKG_VERSION:=20260425
|
||||
PKG_RELEASE:=3
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
|
||||
@ -365,7 +365,16 @@ function showmodemparams() {
|
||||
}
|
||||
|
||||
if (data.cid_dec && data.cid_dec > 0 && data.operator_mcc == 260) {
|
||||
document.getElementById('btsearch' + idx).setAttribute("href", "https://btsearch.pl/stations?q=" + data.cid_dec);
|
||||
switch (data.operator_mnc) {
|
||||
case '01':
|
||||
case '02':
|
||||
case '03':
|
||||
case '06':
|
||||
document.getElementById('btsearch' + idx).setAttribute('href', 'https://btsearch.pl/stations?mnc=260' + data.operator_mnc + '&q=' + data.cid_dec);
|
||||
break;
|
||||
default:
|
||||
document.getElementById('btsearch' + idx).setAttribute('href', 'https://btsearch.pl/stations?q=' + data.cid_dec);
|
||||
}
|
||||
setDisplay('div_btsearch' + idx, true);
|
||||
} else {
|
||||
setDisplay('div_btsearch' + idx, false);
|
||||
|
||||
@ -10,8 +10,8 @@ LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix
|
||||
|
||||
PKG_MAINTAINER:=timsaya
|
||||
|
||||
PKG_VERSION:=0.12.6
|
||||
PKG_RELEASE:=4
|
||||
PKG_VERSION:=0.12.7
|
||||
PKG_RELEASE:=5
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
|
||||
@ -150,7 +150,7 @@ var callGetConnection = rpc.declare({
|
||||
var callGetConnectionFlows = rpc.declare({
|
||||
object: 'luci.bandix',
|
||||
method: 'getConnectionFlows',
|
||||
params: ['ip', 'protocol', 'state'],
|
||||
params: ['ip', 'protocol', 'state', 'page', 'page_size'],
|
||||
expect: {}
|
||||
});
|
||||
|
||||
@ -688,14 +688,16 @@ return view.extend({
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.flows-modal-content .flows-filters select {
|
||||
.flows-modal-content .flows-filters select,
|
||||
.flows-modal-content .flows-footer select {
|
||||
padding: 6px 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(0,0,0,0.2);
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.flows-modal-content.theme-dark .flows-filters select {
|
||||
.flows-modal-content.theme-dark .flows-filters select,
|
||||
.flows-modal-content.theme-dark .flows-footer select {
|
||||
background-color: #3a3a3a;
|
||||
border-color: rgba(255,255,255,0.2);
|
||||
color: #e5e5e5;
|
||||
@ -778,6 +780,13 @@ return view.extend({
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.flows-modal-content .flows-footer .flows-page-size-select {
|
||||
min-width: 92px;
|
||||
}
|
||||
|
||||
.flows-modal-content.theme-dark .flows-footer {
|
||||
@ -1051,6 +1060,11 @@ return view.extend({
|
||||
|
||||
function showFlowsModal(deviceIp, deviceInfo) {
|
||||
var currentTheme = getThemeMode();
|
||||
var currentPage = 1;
|
||||
var totalPages = 1;
|
||||
var totalItems = 0;
|
||||
var currentPageSize = 100;
|
||||
|
||||
var protocolSelect = E('select', { 'class': 'cbi-input-select', 'id': 'flows-protocol' }, [
|
||||
E('option', { 'value': '' }, _('All')),
|
||||
E('option', { 'value': 'tcp' }, 'TCP'),
|
||||
@ -1068,6 +1082,15 @@ return view.extend({
|
||||
E('option', { 'value': 'LAST_ACK' }, 'LAST_ACK'),
|
||||
E('option', { 'value': 'CLOSE' }, 'CLOSE')
|
||||
]);
|
||||
var pageSizeSelect = E('select', { 'class': 'cbi-input-select flows-page-size-select', 'id': 'flows-page-size' }, [
|
||||
E('option', { 'value': '50' }, '50'),
|
||||
E('option', { 'value': '100', 'selected': 'selected' }, '100'),
|
||||
E('option', { 'value': '200' }, '200'),
|
||||
E('option', { 'value': '500' }, '500')
|
||||
]);
|
||||
var pageInfo = E('span', { 'style': 'font-size: 0.85rem; opacity: 0.85; margin: 0 14px;' }, '');
|
||||
var prevPageBtn = E('button', { 'class': 'cbi-button cbi-button-action', 'style': 'margin-right: 8px;' }, _('Previous'));
|
||||
var nextPageBtn = E('button', { 'class': 'cbi-button cbi-button-action' }, _('Next'));
|
||||
var tableBody = E('tbody', { 'class': 'flows-table-body' });
|
||||
var tableWrap = E('div', { 'class': 'flows-table-wrap' }, [
|
||||
E('table', { 'class': 'flows-table' }, [
|
||||
@ -1088,22 +1111,75 @@ return view.extend({
|
||||
])
|
||||
]);
|
||||
|
||||
function loadFlows() {
|
||||
function updatePaginationUi() {
|
||||
if (pageInfo) {
|
||||
pageInfo.textContent = _('Page') + ' ' + currentPage + '/' + totalPages + ' · ' + _('Total') + ': ' + totalItems;
|
||||
}
|
||||
if (prevPageBtn) {
|
||||
prevPageBtn.disabled = currentPage <= 1;
|
||||
}
|
||||
if (nextPageBtn) {
|
||||
nextPageBtn.disabled = currentPage >= totalPages;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeFlowsResponse(res) {
|
||||
if (!res || res.status !== 'success') {
|
||||
return { items: [], total: 0, page: currentPage, page_size: currentPageSize, total_pages: 1 };
|
||||
}
|
||||
|
||||
if (Array.isArray(res.data)) {
|
||||
// 兼容旧后端:data 直接是数组
|
||||
return {
|
||||
items: res.data,
|
||||
total: res.data.length,
|
||||
page: 1,
|
||||
page_size: res.data.length || currentPageSize,
|
||||
total_pages: 1
|
||||
};
|
||||
}
|
||||
|
||||
if (res.data && Array.isArray(res.data.items)) {
|
||||
// 新后端:分页对象
|
||||
return {
|
||||
items: res.data.items,
|
||||
total: res.data.total || 0,
|
||||
page: res.data.page || currentPage,
|
||||
page_size: res.data.page_size || currentPageSize,
|
||||
total_pages: res.data.total_pages || 1
|
||||
};
|
||||
}
|
||||
|
||||
return { items: [], total: 0, page: currentPage, page_size: currentPageSize, total_pages: 1 };
|
||||
}
|
||||
|
||||
function loadFlows(resetPage) {
|
||||
if (resetPage) {
|
||||
currentPage = 1;
|
||||
}
|
||||
var protocol = protocolSelect.value || '';
|
||||
var state = stateSelect.value || '';
|
||||
currentPageSize = parseInt(pageSizeSelect.value, 10) || 100;
|
||||
tableBody.innerHTML = '';
|
||||
tableBody.appendChild(E('tr', {}, [
|
||||
E('td', { 'colspan': 9, 'class': 'loading-state' }, _('Loading…'))
|
||||
]));
|
||||
return callGetConnectionFlows(deviceIp, protocol || null, state || null).then(function (res) {
|
||||
return callGetConnectionFlows(deviceIp, protocol || null, state || null, currentPage, currentPageSize).then(function (res) {
|
||||
tableBody.innerHTML = '';
|
||||
if (!res || res.status !== 'success' || !res.data || res.data.length === 0) {
|
||||
var normalized = normalizeFlowsResponse(res);
|
||||
var flows = normalized.items;
|
||||
totalItems = normalized.total;
|
||||
currentPage = normalized.page;
|
||||
totalPages = Math.max(1, normalized.total_pages);
|
||||
updatePaginationUi();
|
||||
|
||||
if (!flows || flows.length === 0) {
|
||||
tableBody.appendChild(E('tr', {}, [
|
||||
E('td', { 'colspan': 9, 'class': 'loading-state' }, _('No connections'))
|
||||
]));
|
||||
return;
|
||||
}
|
||||
res.data.forEach(function (f) {
|
||||
flows.forEach(function (f) {
|
||||
var stateCls = '';
|
||||
if (f.state) {
|
||||
if (f.state.indexOf('ESTABLISHED') >= 0) stateCls = 'flows-state-est';
|
||||
@ -1131,15 +1207,19 @@ return view.extend({
|
||||
]));
|
||||
});
|
||||
}).catch(function (err) {
|
||||
totalItems = 0;
|
||||
totalPages = 1;
|
||||
currentPage = 1;
|
||||
updatePaginationUi();
|
||||
tableBody.innerHTML = '';
|
||||
tableBody.appendChild(E('tr', {}, [
|
||||
E('td', { 'colspan': 9, 'class': 'error-state' }, _('Failed to load') + ': ' + (err.message || err))
|
||||
E('td', { 'colspan': 9, 'class': 'error-state' }, _('Failed to load') + ': ' + (err.message || err) + ' · ' + _('Try using filters or smaller page size'))
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
function onFilterChange() {
|
||||
loadFlows();
|
||||
loadFlows(true);
|
||||
}
|
||||
|
||||
function hideFlowsModal() {
|
||||
@ -1147,6 +1227,22 @@ return view.extend({
|
||||
if (overlay) overlay.classList.remove('show');
|
||||
}
|
||||
|
||||
prevPageBtn.addEventListener('click', function () {
|
||||
if (currentPage > 1) {
|
||||
currentPage -= 1;
|
||||
loadFlows(false);
|
||||
}
|
||||
});
|
||||
nextPageBtn.addEventListener('click', function () {
|
||||
if (currentPage < totalPages) {
|
||||
currentPage += 1;
|
||||
loadFlows(false);
|
||||
}
|
||||
});
|
||||
pageSizeSelect.addEventListener('change', function () {
|
||||
loadFlows(true);
|
||||
});
|
||||
|
||||
var modalContent = E('div', { 'class': 'flows-modal-content theme-' + currentTheme }, [
|
||||
E('div', { 'class': 'flows-modal-header' }, [
|
||||
E('div', { 'class': 'flows-modal-title' }, _('Connection Details') + ' - ' + (deviceInfo.host || deviceInfo.ip4 || deviceIp)),
|
||||
@ -1161,6 +1257,10 @@ return view.extend({
|
||||
]),
|
||||
tableWrap,
|
||||
E('div', { 'class': 'flows-footer' }, [
|
||||
pageSizeSelect,
|
||||
prevPageBtn,
|
||||
nextPageBtn,
|
||||
pageInfo,
|
||||
E('button', { 'class': 'cbi-button cbi-button-reset', 'click': hideFlowsModal }, _('Close'))
|
||||
])
|
||||
]);
|
||||
@ -1181,7 +1281,8 @@ return view.extend({
|
||||
overlay.appendChild(modalContent);
|
||||
overlay.classList.add('show');
|
||||
modalContent.addEventListener('click', function (e) { e.stopPropagation(); });
|
||||
loadFlows();
|
||||
updatePaginationUi();
|
||||
loadFlows(true);
|
||||
}
|
||||
|
||||
container.addEventListener('click', function (ev) {
|
||||
@ -1260,4 +1361,4 @@ return view.extend({
|
||||
|
||||
return container;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -6535,6 +6535,99 @@ return view.extend({
|
||||
return increments.map(normalizeTrafficIncrementItem).filter(function (x) { return x; });
|
||||
}
|
||||
|
||||
// 为了规避 ubus 1MB 响应限制,长时间范围按块分段请求
|
||||
var TRAFFIC_INCREMENT_CHUNK_MS_HOURLY = 14 * 24 * 60 * 60 * 1000; // 14 天
|
||||
var TRAFFIC_INCREMENT_CHUNK_MS_DAILY = 60 * 24 * 60 * 60 * 1000; // 60 天
|
||||
|
||||
function mergeIncrementsResponse(base, part) {
|
||||
if (!part) return base;
|
||||
if (Array.isArray(part.inc)) {
|
||||
base.inc = base.inc.concat(part.inc);
|
||||
}
|
||||
|
||||
if (part.start && (!base.start || part.start < base.start)) {
|
||||
base.start = part.start;
|
||||
}
|
||||
if (part.end && (!base.end || part.end > base.end)) {
|
||||
base.end = part.end;
|
||||
}
|
||||
|
||||
base.t_rx_b = (base.t_rx_b || 0) + (part.t_rx_b || 0);
|
||||
base.t_tx_b = (base.t_tx_b || 0) + (part.t_tx_b || 0);
|
||||
base.t_b = (base.t_b || 0) + (part.t_b || 0);
|
||||
|
||||
if (!base.agg && part.agg) base.agg = part.agg;
|
||||
if (!base.mac && part.mac) base.mac = part.mac;
|
||||
if (!base.net && part.net) base.net = part.net;
|
||||
if (!base.network_type && part.network_type) base.network_type = part.network_type;
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
function buildTrafficIncrementsChunks(startMs, endMs, aggregation) {
|
||||
if (!startMs || !endMs || endMs <= startMs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var chunkMs = aggregation === 'daily' ? TRAFFIC_INCREMENT_CHUNK_MS_DAILY : TRAFFIC_INCREMENT_CHUNK_MS_HOURLY;
|
||||
var totalRange = endMs - startMs;
|
||||
if (totalRange <= chunkMs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var chunks = [];
|
||||
for (var s = startMs; s < endMs; s += chunkMs) {
|
||||
chunks.push({ start: s, end: Math.min(s + chunkMs, endMs) });
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
function fetchTrafficIncrementsChunked(startMs, endMs, aggregation, mac, networkType, onProgress) {
|
||||
var chunks = buildTrafficIncrementsChunks(startMs, endMs, aggregation);
|
||||
if (!chunks) {
|
||||
return callGetTrafficUsageIncrements(startMs, endMs, aggregation, mac, networkType);
|
||||
}
|
||||
|
||||
if (typeof onProgress === 'function') {
|
||||
onProgress(0, chunks.length);
|
||||
}
|
||||
|
||||
var merged = {
|
||||
start: startMs,
|
||||
end: endMs,
|
||||
agg: aggregation,
|
||||
inc: [],
|
||||
t_rx_b: 0,
|
||||
t_tx_b: 0,
|
||||
t_b: 0
|
||||
};
|
||||
|
||||
var chain = Promise.resolve(merged);
|
||||
var finished = 0;
|
||||
chunks.forEach(function (chunk) {
|
||||
chain = chain.then(function (acc) {
|
||||
return callGetTrafficUsageIncrements(chunk.start, chunk.end, aggregation, mac, networkType).then(function (part) {
|
||||
finished += 1;
|
||||
if (typeof onProgress === 'function') {
|
||||
onProgress(finished, chunks.length);
|
||||
}
|
||||
return mergeIncrementsResponse(acc, part || {});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return chain.then(function (result) {
|
||||
if (Array.isArray(result.inc)) {
|
||||
result.inc.sort(function (a, b) {
|
||||
var aTs = (a && (a.start || a.ts_ms || a.end)) || 0;
|
||||
var bTs = (b && (b.start || b.ts_ms || b.end)) || 0;
|
||||
return aTs - bTs;
|
||||
});
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// 更新时间序列增量数据(使用 Traffic Timeline 自己的时间范围)
|
||||
// Traffic Timeline 缩放变量
|
||||
var incrementsZoomEnabled = false;
|
||||
@ -6568,7 +6661,29 @@ return view.extend({
|
||||
selectedNetworkType = null;
|
||||
}
|
||||
|
||||
callGetTrafficUsageIncrements(startMs, endMs, selectedAggregation, selectedMac, selectedNetworkType).then(function (result) {
|
||||
var container = document.getElementById('traffic-increments-container');
|
||||
var chunks = buildTrafficIncrementsChunks(startMs, endMs, selectedAggregation);
|
||||
if (container) {
|
||||
if (chunks && chunks.length > 1) {
|
||||
container.innerHTML = '<div class="loading-state">' + _('Loading in chunks, please wait... (%d/%d)').format(0, chunks.length) + '</div>';
|
||||
} else {
|
||||
container.innerHTML = '<div class="loading-state">' + _('Loading...') + '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
fetchTrafficIncrementsChunked(
|
||||
startMs,
|
||||
endMs,
|
||||
selectedAggregation,
|
||||
selectedMac,
|
||||
selectedNetworkType,
|
||||
function (done, total) {
|
||||
var progressContainer = document.getElementById('traffic-increments-container');
|
||||
if (progressContainer && total > 1) {
|
||||
progressContainer.innerHTML = '<div class="loading-state">' + _('Loading in chunks, please wait... (%d/%d)').format(done, total) + '</div>';
|
||||
}
|
||||
}
|
||||
).then(function (result) {
|
||||
if (!result || !result.inc) {
|
||||
var container = document.getElementById('traffic-increments-container');
|
||||
if (container) {
|
||||
|
||||
@ -860,6 +860,9 @@ msgstr "Aún no hay reglas programadas, haga clic en \"Agregar regla\" para come
|
||||
msgid "Loading..."
|
||||
msgstr "Cargando..."
|
||||
|
||||
msgid "Loading in chunks, please wait... (%d/%d)"
|
||||
msgstr "Carga por fragmentos, espere... (%d/%d)"
|
||||
|
||||
msgid "No schedule rules"
|
||||
msgstr "No hay reglas programadas"
|
||||
|
||||
|
||||
@ -860,6 +860,9 @@ msgstr "Aucune règle programmée pour le moment, cliquez sur \"Ajouter une règ
|
||||
msgid "Loading..."
|
||||
msgstr "Chargement..."
|
||||
|
||||
msgid "Loading in chunks, please wait... (%d/%d)"
|
||||
msgstr "Chargement par morceaux, veuillez patienter... (%d/%d)"
|
||||
|
||||
msgid "No schedule rules"
|
||||
msgstr "Aucune règle programmée"
|
||||
|
||||
|
||||
@ -860,6 +860,9 @@ msgstr "スケジュール制限ルールがまだありません。「ルール
|
||||
msgid "Loading..."
|
||||
msgstr "読み込み中..."
|
||||
|
||||
msgid "Loading in chunks, please wait... (%d/%d)"
|
||||
msgstr "分割読み込み中です。しばらくお待ちください... (%d/%d)"
|
||||
|
||||
msgid "No schedule rules"
|
||||
msgstr "スケジュール制限ルールなし"
|
||||
|
||||
|
||||
@ -860,6 +860,9 @@ msgstr "Brak jeszcze reguł limitu czasu, kliknij \"Dodaj regułę\", aby rozpoc
|
||||
msgid "Loading..."
|
||||
msgstr "Ładowanie..."
|
||||
|
||||
msgid "Loading in chunks, please wait... (%d/%d)"
|
||||
msgstr "Ładowanie fragmentami, proszę czekać... (%d/%d)"
|
||||
|
||||
msgid "No schedule rules"
|
||||
msgstr "Brak reguł limitu czasu"
|
||||
|
||||
|
||||
@ -860,6 +860,9 @@ msgstr "Правил расписания пока нет, нажмите \"До
|
||||
msgid "Loading..."
|
||||
msgstr "Загрузка..."
|
||||
|
||||
msgid "Loading in chunks, please wait... (%d/%d)"
|
||||
msgstr "Поштучная загрузка, подождите... (%d/%d)"
|
||||
|
||||
msgid "No schedule rules"
|
||||
msgstr "Нет правил расписания"
|
||||
|
||||
|
||||
@ -857,6 +857,9 @@ msgstr "还没有定时限速规则,点击"添加规则"开始设置"
|
||||
msgid "Loading..."
|
||||
msgstr "加载中..."
|
||||
|
||||
msgid "Loading in chunks, please wait... (%d/%d)"
|
||||
msgstr "分段加载中,请稍等... (%d/%d)"
|
||||
|
||||
msgid "No schedule rules"
|
||||
msgstr "没有定时限速规则"
|
||||
|
||||
|
||||
@ -860,6 +860,9 @@ msgstr "還沒有定時限速規則,點擊"新增規則"開始設定"
|
||||
msgid "Loading..."
|
||||
msgstr "載入中..."
|
||||
|
||||
msgid "Loading in chunks, please wait... (%d/%d)"
|
||||
msgstr "分段載入中,請稍候... (%d/%d)"
|
||||
|
||||
msgid "No schedule rules"
|
||||
msgstr "沒有定時限速規則"
|
||||
|
||||
|
||||
@ -346,11 +346,15 @@ get_connection_flows() {
|
||||
local ip="$1"
|
||||
local protocol="$2"
|
||||
local state="$3"
|
||||
local page="$4"
|
||||
local page_size="$5"
|
||||
|
||||
local query_params=""
|
||||
[ -n "$ip" ] && query_params="${query_params}ip=$(printf '%s' "$ip" | sed 's/ /%20/g')&"
|
||||
[ -n "$protocol" ] && query_params="${query_params}protocol=$(printf '%s' "$protocol" | sed 's/ /%20/g')&"
|
||||
[ -n "$state" ] && query_params="${query_params}state=$(printf '%s' "$state" | sed 's/ /%20/g')&"
|
||||
[ -n "$page" ] && query_params="${query_params}page=$page&"
|
||||
[ -n "$page_size" ] && query_params="${query_params}page_size=$page_size&"
|
||||
query_params=$(echo "$query_params" | sed 's/&$//')
|
||||
|
||||
local url="$BANDIX_CONNECTION_FLOWS_API"
|
||||
@ -1248,6 +1252,8 @@ case "$1" in
|
||||
json_add_string "ip"
|
||||
json_add_string "protocol"
|
||||
json_add_string "state"
|
||||
json_add_int "page"
|
||||
json_add_int "page_size"
|
||||
json_close_object
|
||||
|
||||
json_add_object "getDnsQueries"
|
||||
@ -1444,6 +1450,8 @@ case "$1" in
|
||||
ip=""
|
||||
protocol=""
|
||||
state=""
|
||||
page=""
|
||||
page_size=""
|
||||
input=""
|
||||
if read -t 1 -r input; then
|
||||
:
|
||||
@ -1455,12 +1463,18 @@ case "$1" in
|
||||
[ -z "$protocol" ] && protocol="$(echo "$input" | jsonfilter -e '$.protocol' 2>/dev/null)"
|
||||
state="$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null)"
|
||||
[ -z "$state" ] && state="$(echo "$input" | jsonfilter -e '$.state' 2>/dev/null)"
|
||||
page="$(echo "$input" | jsonfilter -e '$[3]' 2>/dev/null)"
|
||||
[ -z "$page" ] && page="$(echo "$input" | jsonfilter -e '$.page' 2>/dev/null)"
|
||||
page_size="$(echo "$input" | jsonfilter -e '$[4]' 2>/dev/null)"
|
||||
[ -z "$page_size" ] && page_size="$(echo "$input" | jsonfilter -e '$.page_size' 2>/dev/null)"
|
||||
else
|
||||
[ -n "$3" ] && ip="$3"
|
||||
[ -n "$4" ] && protocol="$4"
|
||||
[ -n "$5" ] && state="$5"
|
||||
[ -n "$6" ] && page="$6"
|
||||
[ -n "$7" ] && page_size="$7"
|
||||
fi
|
||||
get_connection_flows "$ip" "$protocol" "$state"
|
||||
get_connection_flows "$ip" "$protocol" "$state" "$page" "$page_size"
|
||||
;;
|
||||
getDnsQueries)
|
||||
# logger "luci.bandix: getDnsQueries called"
|
||||
|
||||
@ -4,9 +4,31 @@
|
||||
-#}
|
||||
|
||||
{%
|
||||
import { access } from 'fs';
|
||||
import { cursor } from 'uci';
|
||||
|
||||
const wizard_request = ctx.request_path ?? [];
|
||||
let wizard_pending = false;
|
||||
|
||||
if (ctx.authsession &&
|
||||
wizard_request[0] == 'admin' &&
|
||||
wizard_request[1] != 'wizard' &&
|
||||
access('/etc/config/wizard') &&
|
||||
!access('/usr/sbin/quickstart') &&
|
||||
!access('/etc/config/finished')) {
|
||||
let wizard_uci = cursor();
|
||||
wizard_pending = (wizard_uci.get('wizard', 'config', 'finished') != '1');
|
||||
}
|
||||
|
||||
include(`themes/${theme}/header`);
|
||||
-%}
|
||||
|
||||
{% if (wizard_pending): %}
|
||||
<script>
|
||||
window.location.replace({{ sprintf('%J', dispatcher.build_url('admin/wizard')) }});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
<script src="{{ resource }}/luci.js?v={# PKG_VERSION #}-{{ pkgs_update_time }}"></script>
|
||||
<script>
|
||||
L = new LuCI({{ replace(`${ {
|
||||
|
||||
@ -81,9 +81,13 @@ function getLegacyIwinfoProbeTargets() {
|
||||
const targets = [];
|
||||
|
||||
for (const radio of uci.sections('wireless', 'wifi-device'))
|
||||
pushUnique(targets, radio['.name']);
|
||||
if (!isConfigWifiDeviceDisabled(radio['.name']))
|
||||
pushUnique(targets, radio['.name']);
|
||||
|
||||
for (const iface of uci.sections('wireless', 'wifi-iface')) {
|
||||
if (isConfigWifiIfaceDisabled(iface))
|
||||
continue;
|
||||
|
||||
const device = iface.device;
|
||||
const section = iface['.name'];
|
||||
const configuredIfname = iface.ifname;
|
||||
@ -98,6 +102,13 @@ function getLegacyIwinfoProbeTargets() {
|
||||
return targets;
|
||||
}
|
||||
|
||||
function parseIwDevInfoTxPower(stdout) {
|
||||
const match = String(stdout || '').match(/\btxpower\s+([0-9]+(?:\.[0-9]+)?)\s*dBm\b/i);
|
||||
const value = match ? parseFloat(match[1]) : NaN;
|
||||
|
||||
return (!isNaN(value) && value > 0) ? value : null;
|
||||
}
|
||||
|
||||
function buildIwinfoResolver(devices) {
|
||||
const deviceLookup = buildIwinfoDeviceLookup(devices);
|
||||
const aliasMap = Object.create(null);
|
||||
@ -115,6 +126,9 @@ function buildIwinfoResolver(devices) {
|
||||
}
|
||||
|
||||
for (const iface of uci.sections('wireless', 'wifi-iface')) {
|
||||
if (isConfigWifiIfaceDisabled(iface))
|
||||
continue;
|
||||
|
||||
const device = iface.device;
|
||||
const section = iface['.name'];
|
||||
const configuredIfname = iface.ifname;
|
||||
@ -142,6 +156,10 @@ function buildIwinfoResolver(devices) {
|
||||
|
||||
for (const radio of uci.sections('wireless', 'wifi-device')) {
|
||||
const name = radio['.name'];
|
||||
|
||||
if (isConfigWifiDeviceDisabled(name))
|
||||
continue;
|
||||
|
||||
const target = radioTargets[name] || (deviceLookup[name] ? name : null);
|
||||
|
||||
registerTarget(target);
|
||||
@ -209,18 +227,35 @@ function loadIwinfoInfoMap(force) {
|
||||
if (info != null)
|
||||
nextMap[name] = info;
|
||||
|
||||
for (const alias in resolver.aliasMap) {
|
||||
const target = resolver.aliasMap[alias];
|
||||
const missingTxPowerTargets = queryTargets.filter((name) => {
|
||||
const txpower = nextMap[name]?.txpower;
|
||||
|
||||
if (target && nextMap[target] != null)
|
||||
nextMap[alias] = nextMap[target];
|
||||
}
|
||||
return (txpower == null || isNaN(txpower) || txpower <= 0);
|
||||
});
|
||||
|
||||
if (Object.keys(nextMap).length > 0 || cachedIwinfoInfoMap == null)
|
||||
cachedIwinfoInfoMap = nextMap;
|
||||
return Promise.all(missingTxPowerTargets.map((name) =>
|
||||
L.resolveDefault(fs.exec_direct('/usr/sbin/iw', [ 'dev', name, 'info' ]), '').then((stdout) => [ name, parseIwDevInfoTxPower(stdout) ])
|
||||
)).then((fallbacks) => {
|
||||
for (const [ name, txpower ] of fallbacks) {
|
||||
if (txpower == null)
|
||||
continue;
|
||||
|
||||
cachedIwinfoInfoPromise = null;
|
||||
return cachedIwinfoInfoMap || nextMap;
|
||||
nextMap[name] = Object.assign({}, nextMap[name] || {}, { txpower });
|
||||
}
|
||||
|
||||
for (const alias in resolver.aliasMap) {
|
||||
const target = resolver.aliasMap[alias];
|
||||
|
||||
if (target && nextMap[target] != null)
|
||||
nextMap[alias] = nextMap[target];
|
||||
}
|
||||
|
||||
if (Object.keys(nextMap).length > 0 || cachedIwinfoInfoMap == null)
|
||||
cachedIwinfoInfoMap = nextMap;
|
||||
|
||||
cachedIwinfoInfoPromise = null;
|
||||
return cachedIwinfoInfoMap || nextMap;
|
||||
});
|
||||
});
|
||||
}).catch(() => {
|
||||
cachedIwinfoInfoPromise = null;
|
||||
@ -250,8 +285,19 @@ function isQcaWifiHwtype(hwtype) {
|
||||
return (hwtype == 'qcawifi' || hwtype == 'qcawificfg80211');
|
||||
}
|
||||
|
||||
function isConfigWifiDeviceDisabled(deviceName) {
|
||||
return (uci.get('wireless', deviceName, 'disabled') == '1');
|
||||
}
|
||||
|
||||
function isConfigWifiIfaceDisabled(ifaceSection) {
|
||||
const iface = L.isObject(ifaceSection) ? ifaceSection : uci.get('wireless', ifaceSection);
|
||||
|
||||
return (iface != null && iface['.type'] == 'wifi-iface' &&
|
||||
(iface.disabled == '1' || isConfigWifiDeviceDisabled(iface.device)));
|
||||
}
|
||||
|
||||
function isNetworkDisabled(radioNet) {
|
||||
return (radioNet.get('disabled') == '1' || uci.get('wireless', radioNet.getWifiDeviceName(), 'disabled') == '1');
|
||||
return (radioNet.get('disabled') == '1' || isConfigWifiDeviceDisabled(radioNet.getWifiDeviceName()));
|
||||
}
|
||||
|
||||
function isRadioDisplayUp(radioDev, wifiNets) {
|
||||
@ -997,6 +1043,9 @@ function probeAssocListCandidates(candidates, probeFn) {
|
||||
}
|
||||
|
||||
function getAssocListForNetwork(radioNet) {
|
||||
if (isNetworkDisabled(radioNet))
|
||||
return Promise.resolve([]);
|
||||
|
||||
const hwtype = uci.get('wireless', radioNet.getWifiDeviceName(), 'type');
|
||||
const candidates = getAssocListCandidates(radioNet);
|
||||
const resolvedIfname = candidates[0];
|
||||
@ -1494,11 +1543,15 @@ var CBIWifiFrequencyValue = form.Value.extend({
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
const has_he_modes = !!(htmodelist.HE20 || htmodelist.HE40 || htmodelist.HE80 || htmodelist.HE160);
|
||||
const has_eht_modes = !!(htmodelist.EHT20 || htmodelist.EHT40 || htmodelist.EHT80 || htmodelist.EHT160 || htmodelist.EHT320);
|
||||
const has_5g_channels = this.channels['5g'].length > (allow_auto ? 3 : 0);
|
||||
const has_ac = (hwmodelist.ac || ((hwmodelist.ax || hwmodelist.be || /^11ax|^11be/.test(hwval)) && has_5g_channels)) &&
|
||||
(L.hasSystemFeature('hostapd', '11ac') || htmodelist.VHT20 || htmodelist.VHT40 || htmodelist.VHT80 || htmodelist.VHT160);
|
||||
const has_ax = hwmodelist.ax && (L.hasSystemFeature('hostapd', '11ax') || htmodelist.HE20 || htmodelist.HE40 || htmodelist.HE80 || htmodelist.HE160);
|
||||
const has_be = hwmodelist.be && (L.hasSystemFeature('hostapd', '11be') || htmodelist.EHT20 || htmodelist.EHT40 || htmodelist.EHT80 || htmodelist.EHT160 || htmodelist.EHT320);
|
||||
const has_ax = (hwmodelist.ax || has_eht_modes) &&
|
||||
(L.hasSystemFeature('hostapd', '11ax') || has_he_modes || has_eht_modes);
|
||||
const has_be = (hwmodelist.be || has_eht_modes) &&
|
||||
(L.hasSystemFeature('hostapd', '11be') || has_eht_modes);
|
||||
|
||||
if (isQcaWifiHwtype(hwtype)) {
|
||||
const qca_has_be = has_be || /^11be/.test(hwval);
|
||||
@ -2034,6 +2087,15 @@ var CBIWifiTxPowerValue = form.ListValue.extend({
|
||||
}),
|
||||
|
||||
load: function(section_id) {
|
||||
const device_section = this.getDeviceSection ? this.getDeviceSection(section_id) : section_id;
|
||||
|
||||
if (isConfigWifiDeviceDisabled(device_section)) {
|
||||
this.powerval = this.wifiNetwork ? getDisplayTxPower(this.wifiNetwork) : null;
|
||||
this.poweroff = this.wifiNetwork ? this.wifiNetwork.getTXPowerOffset() : null;
|
||||
this.value('', _('driver default'));
|
||||
return form.ListValue.prototype.load.apply(this, [section_id]);
|
||||
}
|
||||
|
||||
return this.callTxPowerList(section_id).then(L.bind(function(pwrlist) {
|
||||
this.powerval = this.wifiNetwork ? getDisplayTxPower(this.wifiNetwork) : null;
|
||||
this.poweroff = this.wifiNetwork ? this.wifiNetwork.getTXPowerOffset() : null;
|
||||
@ -2071,6 +2133,11 @@ var CBIWifiCountryValue = form.Value.extend({
|
||||
}),
|
||||
|
||||
load: function(section_id) {
|
||||
const device_section = this.getDeviceSection ? this.getDeviceSection(section_id) : section_id;
|
||||
|
||||
if (isConfigWifiDeviceDisabled(device_section))
|
||||
return form.Value.prototype.load.apply(this, [section_id]);
|
||||
|
||||
return this.callCountryList(section_id).then(L.bind(function(countrylist) {
|
||||
if (Array.isArray(countrylist) && countrylist.length > 0) {
|
||||
this.value('', _('driver default'));
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=bandix
|
||||
PKG_VERSION:=0.12.7
|
||||
PKG_RELEASE:=2
|
||||
PKG_VERSION:=0.12.8
|
||||
PKG_RELEASE:=3
|
||||
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
PKG_MAINTAINER:=timsaya
|
||||
@ -13,7 +13,7 @@ include $(INCLUDE_DIR)/package.mk
|
||||
include $(TOPDIR)/feeds/packages/lang/rust/rust-values.mk
|
||||
|
||||
# 二进制文件的文件名和URL
|
||||
RUST_BANDIX_VERSION:=0.12.7
|
||||
RUST_BANDIX_VERSION:=0.12.8
|
||||
RUST_BINARY_FILENAME:=bandix-$(RUST_BANDIX_VERSION)-$(RUSTC_TARGET_ARCH).tar.gz
|
||||
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=teleproxy
|
||||
PKG_VERSION:=4.11.0
|
||||
PKG_RELEASE:=3
|
||||
PKG_RELEASE:=4
|
||||
|
||||
PKG_MAINTAINER:=Kosntantine Shevlakov <shevlakov@132lan.ru>
|
||||
PKG_LICENSE:=GPLv2
|
||||
@ -30,6 +30,7 @@ endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/usr/bin \
|
||||
$(1)/usr/share \
|
||||
$(1)/etc/config \
|
||||
$(1)/etc/init.d \
|
||||
$(1)/etc/uci-defaults
|
||||
@ -37,6 +38,7 @@ define Package/$(PKG_NAME)/install
|
||||
$(1)/usr/bin/
|
||||
$(INSTALL_BIN) ./files/$(PKG_NAME).init $(1)/etc/init.d/$(PKG_NAME)
|
||||
$(INSTALL_BIN) ./files/$(PKG_NAME).defaults $(1)/etc/uci-defaults/$(PKG_NAME)
|
||||
$(INSTALL_BIN) ./files/$(PKG_NAME)-config-refresh.sh $(1)/usr/share/
|
||||
$(INSTALL_CONF) ./files/$(PKG_NAME).config $(1)/etc/config/$(PKG_NAME)
|
||||
endef
|
||||
|
||||
|
||||
48
teleproxy/files/teleproxy-config-refresh.sh
Executable file
48
teleproxy/files/teleproxy-config-refresh.sh
Executable file
@ -0,0 +1,48 @@
|
||||
#!/bin/sh
|
||||
|
||||
# teleproxy config updater
|
||||
# by koshev-msk 2026
|
||||
|
||||
URL=https://core.telegram.org/
|
||||
|
||||
which curl || echo "Error. cURL not found."
|
||||
|
||||
# Check proxy connectrion
|
||||
SOCKS_PROXY=$(uci -q get teleproxy.default.socks)
|
||||
[ -n $SOCKS_PROXY ] && {
|
||||
# Check autentification proxy
|
||||
SOCKS_AUTH=$(uci -q get teleproxy.default.socks_auth)
|
||||
[ -n $SOCKS_AUTH ] && {
|
||||
CURL="curl -x socks5://${SOCKS_AUTH}@${SOCKS_PROXY}"
|
||||
} || {
|
||||
CURL="curl -x socks://${SOCKS_PROXY}"
|
||||
}
|
||||
} || {
|
||||
CURL="curl"
|
||||
}
|
||||
|
||||
[ -d /etc/teleproxy ] || mkdir -p /etc/teleproxy
|
||||
|
||||
# Download latest configs and check new sha256
|
||||
${CURL} -s --max-time 60 $URL/cidr.txt -o /tmp/cidr.txt && \
|
||||
SHA256_CIDR_NEW=$(sha256sum /tmp/cidr.txt | awk '{print $1}') || SHA256_CIDR_NEW="failed"
|
||||
${CURL} -s --max-time 60 $URL/getProxyConfig -o /tmp/proxy-multi.conf && \
|
||||
SHA256_MULTI_NEW=$(sha256sum /tmp/proxy-multi.conf | awk '{print $1}') || SHA256_MULTI_NEW="failed"
|
||||
${CURL} -s --max-time 60 $URL/getProxySecret -o /tmp/aes-secret && \
|
||||
SHA256_AES_NEW=$(sha256sum /tmp/aes-secret | awk '{print $1}') || SHA256_AES_NEW="failed"
|
||||
|
||||
# Check old sha256 config
|
||||
[ -f /etc/teleproxy/cidr.txt ] && \
|
||||
SHA256_CIDR_OLD=$(sha256sum /etc/teleproxy/cidr.txt | awk '{print $1}') || SHA256_CIDR_OLD="failed"
|
||||
[ -f /etc/teleproxy/proxy-multi.conf ] && \
|
||||
SHA256_MULTI_OLD=$(sha256sum /etc/teleproxy/proxy-multi.conf | awk '{print $1}') || SHA256_MULTI_OLD="failed"
|
||||
[ -f /etc/teleproxy/aes-secret ] && \
|
||||
SHA256_AES_OLD=$(sha256sum /etc/teleproxy/aes-secret | awk '{print $1}') || SHA256_MULTI_OLD="failed"
|
||||
|
||||
# Comapre hashes
|
||||
[ "$SHA256_CIDR_NEW" = "$SHA256_CIDR_OLD" ] || \
|
||||
cp /tmp/cidr.txt /etc/teleproxy/cidr.txt && rm /tmp/cidr.txt
|
||||
[ "$SHA256_MULTI_NEW" = "$SHA256_MULTI_OLD" ] || \
|
||||
cp /tmp/proxy-multi.conf /etc/teleproxy/proxy-multi.conf && rm /tmp/proxy-multi.conf
|
||||
[ "$SHA256_AES_NEW" = "$SHA256_AES_OLD" ] || \
|
||||
cp /tmp/aes-secret /etc/teleproxy/aes-secret && rm /tmp/aes-secret
|
||||
@ -6,7 +6,7 @@ BIN=/usr/bin/${NAME}
|
||||
PID=/var/run/${NAME}.pid
|
||||
|
||||
load_config(){
|
||||
for v in ipv6 address port secret direct aes_pwd auth_socks socks extra; do
|
||||
for v in ipv6 address port secret direct relay aes_pwd auth_socks socks extra; do
|
||||
config_get $v $1 $v
|
||||
done
|
||||
}
|
||||
@ -14,14 +14,14 @@ load_config(){
|
||||
|
||||
start() {
|
||||
config_load teleproxy
|
||||
local ipv6 address port secret direct aes_pwd auth_socks socks extra
|
||||
local ipv6 address port secret direct relay aes_pwd auth_socks socks extra
|
||||
config_foreach load_config
|
||||
[ $ipv6 ] && args="-6"
|
||||
[ $address ] && args="$args --address $address"
|
||||
[ $port ] && args="$args -H $port"
|
||||
[ $secret ] && args="$args -S $secret"
|
||||
[ $aes_pwd ] && args="$args --aes-pwd $aes-pwd"
|
||||
[ $socks ] && {
|
||||
[ $aes_pwd ] && args="$args --aes-pwd $aes_pwd"
|
||||
[ $socks -a "$direct" -eq "1" ] && {
|
||||
[ $auth_socks ] && {
|
||||
args="$args --socks socks5://${auth_socks}@${socks}"
|
||||
} || {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user