🍕 Sync 2025-11-24 00:10:28
This commit is contained in:
@@ -10,7 +10,7 @@ LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix
|
||||
|
||||
PKG_MAINTAINER:=timsaya
|
||||
|
||||
PKG_VERSION:=0.8.2
|
||||
PKG_VERSION:=0.8.3
|
||||
PKG_RELEASE:=1
|
||||
|
||||
include $(TOPDIR)/feeds/luci/luci.mk
|
||||
|
||||
@@ -8,6 +8,40 @@
|
||||
|
||||
// 暗色模式检测已改为使用 CSS 媒体查询 @media (prefers-color-scheme: dark)
|
||||
|
||||
// 检测主题类型:返回 'wide'(宽主题,如 Argon)或 'narrow'(窄主题,如 Bootstrap)
|
||||
function getThemeType() {
|
||||
// 获取 LuCI 主题设置
|
||||
var mediaUrlBase = uci.get('luci', 'main', 'mediaurlbase');
|
||||
|
||||
if (!mediaUrlBase) {
|
||||
// 如果无法获取,尝试从 DOM 中检测
|
||||
var linkTags = document.querySelectorAll('link[rel="stylesheet"]');
|
||||
for (var i = 0; i < linkTags.length; i++) {
|
||||
var href = linkTags[i].getAttribute('href') || '';
|
||||
if (href.toLowerCase().includes('argon')) {
|
||||
return 'wide';
|
||||
}
|
||||
}
|
||||
// 默认返回窄主题
|
||||
return 'narrow';
|
||||
}
|
||||
|
||||
var mediaUrlBaseLower = mediaUrlBase.toLowerCase();
|
||||
|
||||
// 宽主题关键词列表(可以根据需要扩展)
|
||||
var wideThemeKeywords = ['argon', 'material', 'design', 'edge'];
|
||||
|
||||
// 检查是否是宽主题
|
||||
for (var i = 0; i < wideThemeKeywords.length; i++) {
|
||||
if (mediaUrlBaseLower.includes(wideThemeKeywords[i])) {
|
||||
return 'wide';
|
||||
}
|
||||
}
|
||||
|
||||
// 默认是窄主题(Bootstrap 等)
|
||||
return 'narrow';
|
||||
}
|
||||
|
||||
// 格式化时间戳
|
||||
function formatTimestamp(timestamp) {
|
||||
if (!timestamp) return _('Never Online');
|
||||
@@ -97,6 +131,21 @@ return view.extend({
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* 只在宽模式下应用警告样式 */
|
||||
.bandix-alert.wide-theme {
|
||||
background-color: rgba(251, 191, 36, 0.1);
|
||||
border: 1px solid rgba(251, 191, 36, 0.3);
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bandix-alert.wide-theme {
|
||||
background-color: rgba(251, 191, 36, 0.15);
|
||||
border-color: rgba(251, 191, 36, 0.4);
|
||||
color: #fbbf24;
|
||||
}
|
||||
}
|
||||
|
||||
.bandix-alert-icon {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
@@ -116,6 +165,189 @@ return view.extend({
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* 移动端统计卡片布局 */
|
||||
@media (max-width: 768px) {
|
||||
.stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.stats-grid .cbi-section {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.stats-card-main-value {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
/* 移动端设备列表卡片式布局 */
|
||||
.bandix-table {
|
||||
display: none; /* 移动端隐藏表格 */
|
||||
}
|
||||
|
||||
.device-list-cards {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.device-card {
|
||||
background-color: var(--cbi-section-bg, #fff);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 12px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.device-card {
|
||||
background-color: var(--cbi-section-bg, rgba(30, 30, 30, 0.98));
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.device-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.device-card-header {
|
||||
border-bottom-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.device-card-name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.device-card-name .device-name {
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.device-card-name .device-ip {
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.device-card-name .device-mac {
|
||||
font-size: 0.7rem;
|
||||
opacity: 0.6;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.device-card-stats {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.device-card-stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.device-card-stat-label {
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.7;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.device-card-stat-value {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.device-card-tcp-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
margin-bottom: 12px;
|
||||
padding: 12px;
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.device-card-tcp-details {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
.device-card-tcp-details-label {
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.7;
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.device-card-tcp-status-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.device-card-tcp-status-label {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
min-width: 45px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.device-card-tcp-status-label.established {
|
||||
background-color: #10b981;
|
||||
}
|
||||
|
||||
.device-card-tcp-status-label.time-wait {
|
||||
background-color: #f59e0b;
|
||||
}
|
||||
|
||||
.device-card-tcp-status-label.closed {
|
||||
background-color: #6b7280;
|
||||
}
|
||||
|
||||
.device-card-tcp-status-value {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.device-card-total {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.device-card-total {
|
||||
border-top-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* PC端显示表格,隐藏卡片 */
|
||||
@media (min-width: 769px) {
|
||||
.bandix-table {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.device-list-cards {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.bandix-connection-container > .cbi-section:first-of-type {
|
||||
margin-top: 0;
|
||||
@@ -326,11 +558,16 @@ return view.extend({
|
||||
|
||||
// 检查连接监控是否启用
|
||||
if (!connectionEnabled) {
|
||||
var alertDiv = E('div', { 'class': 'bandix-alert' }, [
|
||||
E('div', {}, [
|
||||
E('strong', {}, _('Connection Monitor Disabled')),
|
||||
E('p', { 'style': 'margin: 4px 0 0 0;' },
|
||||
_('Please enable connection monitoring in settings'))
|
||||
var alertDiv = E('div', {
|
||||
'class': 'bandix-alert' + (getThemeType() === 'wide' ? ' wide-theme' : '')
|
||||
}, [
|
||||
E('div', { 'style': 'display: flex; align-items: center; gap: 8px;' }, [
|
||||
E('span', { 'style': 'font-size: 1rem;' }, '⚠'),
|
||||
E('div', {}, [
|
||||
E('strong', {}, _('Connection Monitor Disabled')),
|
||||
E('p', { 'style': 'margin: 4px 0 0 0;' },
|
||||
_('Please enable connection monitoring in settings'))
|
||||
])
|
||||
])
|
||||
]);
|
||||
container.appendChild(alertDiv);
|
||||
@@ -348,8 +585,13 @@ return view.extend({
|
||||
}
|
||||
|
||||
// 添加提示信息
|
||||
var infoAlert = E('div', { 'class': 'bandix-alert' }, [
|
||||
E('span', {}, _('List only shows LAN device connections, data may differ from total connections.'))
|
||||
var infoAlert = E('div', {
|
||||
'class': 'bandix-alert' + (getThemeType() === 'wide' ? ' wide-theme' : '')
|
||||
}, [
|
||||
E('div', { 'style': 'display: flex; align-items: center; gap: 8px;' }, [
|
||||
E('span', { 'style': 'font-size: 1rem;' }, '⚠'),
|
||||
E('span', {}, _('List only shows LAN device connections, data may differ from total connections.'))
|
||||
])
|
||||
]);
|
||||
container.appendChild(infoAlert);
|
||||
|
||||
@@ -429,6 +671,7 @@ return view.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建表格(PC端)
|
||||
var table = E('table', { 'class': 'bandix-table' }, [
|
||||
E('thead', {}, [
|
||||
E('tr', {}, [
|
||||
@@ -474,8 +717,60 @@ return view.extend({
|
||||
}))
|
||||
]);
|
||||
|
||||
// 创建卡片容器(移动端)
|
||||
var cardsContainer = E('div', { 'class': 'device-list-cards' });
|
||||
|
||||
devices.forEach(function (device) {
|
||||
var card = E('div', { 'class': 'device-card' }, [
|
||||
// 卡片头部:设备信息
|
||||
E('div', { 'class': 'device-card-header' }, [
|
||||
E('div', { 'class': 'device-status online' }),
|
||||
E('div', { 'class': 'device-card-name' }, [
|
||||
E('div', { 'class': 'device-name' }, formatDeviceName(device)),
|
||||
E('div', { 'class': 'device-ip' }, device.ip_address || '-'),
|
||||
E('div', { 'class': 'device-mac' }, device.mac_address || '-')
|
||||
])
|
||||
]),
|
||||
// 统计信息:TCP 和 UDP
|
||||
E('div', { 'class': 'device-card-stats' }, [
|
||||
E('div', { 'class': 'device-card-stat-item' }, [
|
||||
E('div', { 'class': 'device-card-stat-label' }, 'TCP'),
|
||||
E('div', { 'class': 'device-card-stat-value' }, device.tcp_connections || 0)
|
||||
]),
|
||||
E('div', { 'class': 'device-card-stat-item' }, [
|
||||
E('div', { 'class': 'device-card-stat-label' }, 'UDP'),
|
||||
E('div', { 'class': 'device-card-stat-value' }, device.udp_connections || 0)
|
||||
])
|
||||
]),
|
||||
// TCP 状态详情
|
||||
E('div', { 'class': 'device-card-tcp-details' }, [
|
||||
E('div', { 'class': 'device-card-tcp-details-label' }, _('TCP Status Details')),
|
||||
E('div', { 'class': 'device-card-tcp-status-row' }, [
|
||||
E('span', { 'class': 'device-card-tcp-status-label established' }, 'EST'),
|
||||
E('span', { 'class': 'device-card-tcp-status-value' }, device.established_tcp || 0)
|
||||
]),
|
||||
E('div', { 'class': 'device-card-tcp-status-row' }, [
|
||||
E('span', { 'class': 'device-card-tcp-status-label time-wait' }, 'WAIT'),
|
||||
E('span', { 'class': 'device-card-tcp-status-value' }, device.time_wait_tcp || 0)
|
||||
]),
|
||||
E('div', { 'class': 'device-card-tcp-status-row' }, [
|
||||
E('span', { 'class': 'device-card-tcp-status-label closed' }, 'CLOSE'),
|
||||
E('span', { 'class': 'device-card-tcp-status-value' }, device.close_wait_tcp || 0)
|
||||
])
|
||||
]),
|
||||
// 总连接数
|
||||
E('div', { 'class': 'device-card-total' }, [
|
||||
E('div', { 'style': 'font-size: 0.875rem; opacity: 0.7; font-weight: 500;' }, _('Total Connections')),
|
||||
E('div', { 'style': 'font-size: 1.25rem; font-weight: 700;' }, device.total_connections || 0)
|
||||
])
|
||||
]);
|
||||
|
||||
cardsContainer.appendChild(card);
|
||||
});
|
||||
|
||||
container.innerHTML = '';
|
||||
container.appendChild(table);
|
||||
container.appendChild(cardsContainer);
|
||||
}
|
||||
|
||||
// 显示错误信息
|
||||
|
||||
@@ -8,6 +8,40 @@
|
||||
|
||||
// 暗色模式检测已改为使用 CSS 媒体查询 @media (prefers-color-scheme: dark)
|
||||
|
||||
// 检测主题类型:返回 'wide'(宽主题,如 Argon)或 'narrow'(窄主题,如 Bootstrap)
|
||||
function getThemeType() {
|
||||
// 获取 LuCI 主题设置
|
||||
var mediaUrlBase = uci.get('luci', 'main', 'mediaurlbase');
|
||||
|
||||
if (!mediaUrlBase) {
|
||||
// 如果无法获取,尝试从 DOM 中检测
|
||||
var linkTags = document.querySelectorAll('link[rel="stylesheet"]');
|
||||
for (var i = 0; i < linkTags.length; i++) {
|
||||
var href = linkTags[i].getAttribute('href') || '';
|
||||
if (href.toLowerCase().includes('argon')) {
|
||||
return 'wide';
|
||||
}
|
||||
}
|
||||
// 默认返回窄主题
|
||||
return 'narrow';
|
||||
}
|
||||
|
||||
var mediaUrlBaseLower = mediaUrlBase.toLowerCase();
|
||||
|
||||
// 宽主题关键词列表(可以根据需要扩展)
|
||||
var wideThemeKeywords = ['argon', 'material', 'design', 'edge'];
|
||||
|
||||
// 检查是否是宽主题
|
||||
for (var i = 0; i < wideThemeKeywords.length; i++) {
|
||||
if (mediaUrlBaseLower.includes(wideThemeKeywords[i])) {
|
||||
return 'wide';
|
||||
}
|
||||
}
|
||||
|
||||
// 默认是窄主题(Bootstrap 等)
|
||||
return 'narrow';
|
||||
}
|
||||
|
||||
function formatTimestamp(timestamp) {
|
||||
if (!timestamp) return '-';
|
||||
var date = new Date(timestamp);
|
||||
@@ -88,7 +122,7 @@ function formatResponseResult(query) {
|
||||
var callGetDnsQueries = rpc.declare({
|
||||
object: 'luci.bandix',
|
||||
method: 'getDnsQueries',
|
||||
params: ['domain', 'device', 'is_query', 'dns_server', 'page', 'page_size'],
|
||||
params: ['domain', 'device', 'is_query', 'dns_server', 'query_type', 'page', 'page_size'],
|
||||
expect: {}
|
||||
});
|
||||
|
||||
@@ -138,6 +172,21 @@ return view.extend({
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* 只在宽模式下应用警告样式 */
|
||||
.bandix-alert.wide-theme {
|
||||
background-color: rgba(251, 191, 36, 0.1);
|
||||
border: 1px solid rgba(251, 191, 36, 0.3);
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bandix-alert.wide-theme {
|
||||
background-color: rgba(251, 191, 36, 0.15);
|
||||
border-color: rgba(251, 191, 36, 0.4);
|
||||
color: #fbbf24;
|
||||
}
|
||||
}
|
||||
|
||||
.bandix-alert-icon {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
@@ -175,10 +224,18 @@ return view.extend({
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.875rem;
|
||||
min-width: 150px;
|
||||
min-width: 120px;
|
||||
max-width: 200px;
|
||||
width: 120px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.filter-section .cbi-select {
|
||||
min-width: 120px;
|
||||
max-width: 200px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.bandix-table {
|
||||
width: 100%;
|
||||
font-size: 0.875rem;
|
||||
@@ -258,6 +315,35 @@ return view.extend({
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* 刷新按钮加载状态 */
|
||||
.refresh-btn-loading {
|
||||
position: relative;
|
||||
pointer-events: none;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.refresh-btn-loading::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 2px solid currentColor;
|
||||
border-top-color: transparent;
|
||||
border-right-color: transparent;
|
||||
border-radius: 50%;
|
||||
animation: refresh-btn-spin 0.6s linear infinite;
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@keyframes refresh-btn-spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.loading-state {
|
||||
text-align: center;
|
||||
@@ -402,6 +488,183 @@ return view.extend({
|
||||
font-weight: 600;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 移动端优化 */
|
||||
@media (max-width: 768px) {
|
||||
/* 移动端隐藏表格,显示卡片 */
|
||||
.bandix-table {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dns-query-cards {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 移动端卡片样式 */
|
||||
.dns-query-card {
|
||||
background-color: var(--cbi-section-bg, #fff);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 12px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.dns-query-card {
|
||||
background-color: var(--cbi-section-bg, rgba(30, 30, 30, 0.98));
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.dns-query-card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.dns-query-card-header {
|
||||
border-bottom-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
}
|
||||
|
||||
.dns-query-card-time {
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.7;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dns-query-card-type {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.dns-query-card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.dns-query-card-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.dns-query-card-label {
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.7;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dns-query-card-value {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.dns-query-card-domain {
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 600;
|
||||
color: #3b82f6;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.dns-query-card-response-result {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.dns-query-card-response-badge {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.7rem;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.dns-query-card-response-badge {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 移动端过滤器优化 */
|
||||
.filter-section {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
padding: 12px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filter-group {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.filter-input,
|
||||
.filter-section .cbi-select {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
/* 移动端分页优化 */
|
||||
.pagination {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
text-align: center;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.pagination-controls {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.pagination-controls .cbi-select {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.pagination-controls button {
|
||||
flex: 1;
|
||||
min-width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
/* PC端显示表格,隐藏卡片 */
|
||||
@media (min-width: 769px) {
|
||||
.bandix-table {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.dns-query-cards {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`);
|
||||
document.head.appendChild(style);
|
||||
|
||||
@@ -413,11 +676,16 @@ return view.extend({
|
||||
container.appendChild(header);
|
||||
|
||||
if (!dnsEnabled) {
|
||||
var alertDiv = E('div', { 'class': 'bandix-alert' }, [
|
||||
E('div', {}, [
|
||||
E('strong', {}, _('DNS Monitoring Disabled')),
|
||||
E('p', { 'style': 'margin: 4px 0 0 0;' },
|
||||
_('Please enable DNS monitoring in settings'))
|
||||
var alertDiv = E('div', {
|
||||
'class': 'bandix-alert' + (getThemeType() === 'wide' ? ' wide-theme' : '')
|
||||
}, [
|
||||
E('div', { 'style': 'display: flex; align-items: center; gap: 8px;' }, [
|
||||
E('span', { 'style': 'font-size: 1rem;' }, '⚠'),
|
||||
E('div', {}, [
|
||||
E('strong', {}, _('DNS Monitoring Disabled')),
|
||||
E('p', { 'style': 'margin: 4px 0 0 0;' },
|
||||
_('Please enable DNS monitoring in settings'))
|
||||
])
|
||||
])
|
||||
]);
|
||||
container.appendChild(alertDiv);
|
||||
@@ -435,8 +703,13 @@ return view.extend({
|
||||
}
|
||||
|
||||
// 添加提示信息
|
||||
var infoAlert = E('div', { 'class': 'bandix-alert' }, [
|
||||
E('span', {}, _('Does not include DoH and DoT'))
|
||||
var infoAlert = E('div', {
|
||||
'class': 'bandix-alert' + (getThemeType() === 'wide' ? ' wide-theme' : '')
|
||||
}, [
|
||||
E('div', { 'style': 'display: flex; align-items: center; gap: 8px;' }, [
|
||||
E('span', { 'style': 'font-size: 1rem;' }, '⚠'),
|
||||
E('span', {}, _('Does not include DoH and DoT'))
|
||||
])
|
||||
]);
|
||||
container.appendChild(infoAlert);
|
||||
|
||||
@@ -463,16 +736,32 @@ return view.extend({
|
||||
'type': 'text',
|
||||
'class': 'filter-input',
|
||||
'id': 'domain-filter',
|
||||
'placeholder': _('Search Domain')
|
||||
'placeholder': _('Domain')
|
||||
})
|
||||
]),
|
||||
E('div', { 'class': 'filter-group' }, [
|
||||
E('label', { 'class': 'filter-label' }, _('Query Type') + ':'),
|
||||
E('select', { 'class': 'cbi-select', 'id': 'query-type-filter' }, [
|
||||
E('option', { 'value': '' }, _('All')),
|
||||
E('option', { 'value': 'A' }, 'A'),
|
||||
E('option', { 'value': 'AAAA' }, 'AAAA'),
|
||||
E('option', { 'value': 'CNAME' }, 'CNAME'),
|
||||
E('option', { 'value': 'MX' }, 'MX'),
|
||||
E('option', { 'value': 'TXT' }, 'TXT'),
|
||||
E('option', { 'value': 'NS' }, 'NS'),
|
||||
E('option', { 'value': 'PTR' }, 'PTR'),
|
||||
E('option', { 'value': 'SOA' }, 'SOA'),
|
||||
E('option', { 'value': 'HTTPS' }, 'HTTPS'),
|
||||
E('option', { 'value': 'SVCB' }, 'SVCB')
|
||||
])
|
||||
]),
|
||||
E('div', { 'class': 'filter-group' }, [
|
||||
E('label', { 'class': 'filter-label' }, _('Device Filter') + ':'),
|
||||
E('input', {
|
||||
'type': 'text',
|
||||
'class': 'filter-input',
|
||||
'id': 'device-filter',
|
||||
'placeholder': _('Search Device')
|
||||
'placeholder': _('Device')
|
||||
})
|
||||
]),
|
||||
E('div', { 'class': 'filter-group' }, [
|
||||
@@ -481,7 +770,7 @@ return view.extend({
|
||||
'type': 'text',
|
||||
'class': 'filter-input',
|
||||
'id': 'dns-server-filter',
|
||||
'placeholder': _('Search DNS Server')
|
||||
'placeholder': _('DNS Server')
|
||||
})
|
||||
]),
|
||||
E('div', { 'class': 'filter-group', 'style': 'margin-left: auto;' }, [
|
||||
@@ -505,7 +794,8 @@ return view.extend({
|
||||
domain: '',
|
||||
device: '',
|
||||
is_query: '',
|
||||
dns_server: ''
|
||||
dns_server: '',
|
||||
query_type: ''
|
||||
};
|
||||
|
||||
|
||||
@@ -556,6 +846,7 @@ return view.extend({
|
||||
currentFilters.device,
|
||||
currentFilters.is_query,
|
||||
currentFilters.dns_server,
|
||||
currentFilters.query_type,
|
||||
currentPage,
|
||||
pageSize
|
||||
).then(function (result) {
|
||||
@@ -602,17 +893,20 @@ return view.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
// 移除旧的表格和分页
|
||||
// 移除旧的表格、卡片和分页
|
||||
var oldTable = container.querySelector('.bandix-table');
|
||||
var oldCards = container.querySelector('.dns-query-cards');
|
||||
var oldPagination = container.querySelector('.pagination');
|
||||
var oldLoadingState = container.querySelector('.loading-state');
|
||||
if (oldTable) oldTable.remove();
|
||||
if (oldCards) oldCards.remove();
|
||||
if (oldPagination) oldPagination.remove();
|
||||
if (oldLoadingState) oldLoadingState.remove();
|
||||
|
||||
// 确保容器是相对定位(用于遮罩层)
|
||||
container.style.position = 'relative';
|
||||
|
||||
// 创建表格(PC端)
|
||||
var table = E('table', { 'class': 'bandix-table' }, [
|
||||
E('thead', {}, [
|
||||
E('tr', {}, [
|
||||
@@ -666,6 +960,78 @@ return view.extend({
|
||||
]);
|
||||
}))
|
||||
]);
|
||||
|
||||
// 创建移动端卡片容器
|
||||
var cardsContainer = E('div', { 'class': 'dns-query-cards' });
|
||||
|
||||
// 为每个查询创建卡片
|
||||
queries.forEach(function (query) {
|
||||
var result = formatResponseResult(query);
|
||||
var responseResultBadges = [];
|
||||
if (result.display.length === 0) {
|
||||
responseResultBadges.push(E('span', { 'class': 'dns-query-card-response-badge' }, '-'));
|
||||
} else {
|
||||
result.display.forEach(function (item) {
|
||||
responseResultBadges.push(E('span', { 'class': 'dns-query-card-response-badge' }, item));
|
||||
});
|
||||
if (result.hasMore) {
|
||||
responseResultBadges.push(E('span', {
|
||||
'class': 'dns-query-card-response-badge',
|
||||
'style': 'opacity: 0.7;'
|
||||
}, '...'));
|
||||
}
|
||||
}
|
||||
|
||||
var card = E('div', { 'class': 'dns-query-card' }, [
|
||||
// 卡片头部:时间和类型
|
||||
E('div', { 'class': 'dns-query-card-header' }, [
|
||||
E('div', { 'class': 'dns-query-card-time' }, formatTimestamp(query.timestamp)),
|
||||
E('span', {
|
||||
'class': 'query-badge ' + (query.is_query ? 'query' : 'response')
|
||||
}, query.is_query ? _('Query') : _('Response'))
|
||||
]),
|
||||
// 卡片主体
|
||||
E('div', { 'class': 'dns-query-card-body' }, [
|
||||
// 域名
|
||||
E('div', { 'class': 'dns-query-card-row' }, [
|
||||
E('div', { 'class': 'dns-query-card-label' }, _('Domain')),
|
||||
E('div', { 'class': 'dns-query-card-value dns-query-card-domain' }, query.domain || '-')
|
||||
]),
|
||||
// 查询类型
|
||||
E('div', { 'class': 'dns-query-card-row' }, [
|
||||
E('div', { 'class': 'dns-query-card-label' }, _('Query Type')),
|
||||
E('div', { 'class': 'dns-query-card-value' }, query.query_type || '-')
|
||||
]),
|
||||
// 响应时间
|
||||
E('div', { 'class': 'dns-query-card-row' }, [
|
||||
E('div', { 'class': 'dns-query-card-label' }, _('Response Time')),
|
||||
E('div', { 'class': 'dns-query-card-value' }, query.response_time_ms ? query.response_time_ms + ' ' + _('ms') : '-')
|
||||
]),
|
||||
// 设备
|
||||
E('div', { 'class': 'dns-query-card-row' }, [
|
||||
E('div', { 'class': 'dns-query-card-label' }, _('Device')),
|
||||
E('div', { 'class': 'dns-query-card-value' }, formatDeviceName(query))
|
||||
]),
|
||||
// DNS服务器
|
||||
E('div', { 'class': 'dns-query-card-row' }, [
|
||||
E('div', { 'class': 'dns-query-card-label' }, _('DNS Server')),
|
||||
E('div', { 'class': 'dns-query-card-value' }, formatDnsServer(query))
|
||||
]),
|
||||
// 响应结果
|
||||
E('div', { 'class': 'dns-query-card-row' }, [
|
||||
E('div', { 'class': 'dns-query-card-label' }, _('Response Result')),
|
||||
E('div', {
|
||||
'class': 'dns-query-card-value',
|
||||
'title': result.full.length > 0 ? result.full.join('\n') : ''
|
||||
}, [
|
||||
E('div', { 'class': 'dns-query-card-response-result' }, responseResultBadges)
|
||||
])
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
cardsContainer.appendChild(card);
|
||||
});
|
||||
|
||||
var pagination = E('div', { 'class': 'pagination' }, [
|
||||
E('div', { 'class': 'pagination-info' },
|
||||
@@ -696,6 +1062,7 @@ return view.extend({
|
||||
]);
|
||||
|
||||
container.appendChild(table);
|
||||
container.appendChild(cardsContainer);
|
||||
container.appendChild(pagination);
|
||||
|
||||
// 绑定分页事件
|
||||
@@ -943,9 +1310,10 @@ return view.extend({
|
||||
var deviceFilter = document.getElementById('device-filter');
|
||||
var dnsServerFilter = document.getElementById('dns-server-filter');
|
||||
var typeFilter = document.getElementById('type-filter');
|
||||
var queryTypeFilter = document.getElementById('query-type-filter');
|
||||
var refreshBtn = document.getElementById('refresh-queries-btn');
|
||||
|
||||
if (domainFilter && deviceFilter && dnsServerFilter && typeFilter) {
|
||||
if (domainFilter && deviceFilter && dnsServerFilter && typeFilter && queryTypeFilter) {
|
||||
var searchTimer = null;
|
||||
|
||||
function performSearch() {
|
||||
@@ -953,6 +1321,7 @@ return view.extend({
|
||||
currentFilters.device = deviceFilter.value.trim();
|
||||
currentFilters.dns_server = dnsServerFilter.value.trim();
|
||||
currentFilters.is_query = typeFilter.value;
|
||||
currentFilters.query_type = queryTypeFilter.value;
|
||||
currentPage = 1;
|
||||
updateQueries();
|
||||
}
|
||||
@@ -972,16 +1341,34 @@ return view.extend({
|
||||
|
||||
// 下拉框立即搜索(不需要防抖)
|
||||
typeFilter.addEventListener('change', performSearch);
|
||||
queryTypeFilter.addEventListener('change', performSearch);
|
||||
|
||||
// 刷新按钮
|
||||
if (refreshBtn) {
|
||||
var originalBtnText = refreshBtn.textContent || refreshBtn.innerText || _('Refresh');
|
||||
|
||||
refreshBtn.addEventListener('click', function () {
|
||||
// 保存原始按钮文本(如果还没有保存)
|
||||
if (!originalBtnText) {
|
||||
originalBtnText = refreshBtn.textContent || refreshBtn.innerText || _('Refresh');
|
||||
}
|
||||
|
||||
// 设置按钮为加载状态
|
||||
refreshBtn.classList.add('refresh-btn-loading');
|
||||
refreshBtn.textContent = _('Loading...');
|
||||
refreshBtn.disabled = true;
|
||||
|
||||
// 同时刷新统计数据和查询记录
|
||||
updateStats();
|
||||
|
||||
var container = document.getElementById('dns-queries-container');
|
||||
if (!container) {
|
||||
updateQueries();
|
||||
updateQueries().finally(function() {
|
||||
// 恢复按钮状态
|
||||
refreshBtn.classList.remove('refresh-btn-loading');
|
||||
refreshBtn.textContent = originalBtnText;
|
||||
refreshBtn.disabled = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1006,7 +1393,12 @@ return view.extend({
|
||||
}, 50);
|
||||
|
||||
// 刷新数据(蒙版会在 updateQueries 中自动移除)
|
||||
updateQueries();
|
||||
updateQueries().finally(function() {
|
||||
// 恢复按钮状态
|
||||
refreshBtn.classList.remove('refresh-btn-loading');
|
||||
refreshBtn.textContent = originalBtnText;
|
||||
refreshBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1051,6 +1443,12 @@ return view.extend({
|
||||
card.style.backgroundColor = bgColor;
|
||||
});
|
||||
|
||||
// 应用到 DNS 查询卡片(移动端)
|
||||
var dnsQueryCards = document.querySelectorAll('.dns-query-card');
|
||||
dnsQueryCards.forEach(function(card) {
|
||||
card.style.backgroundColor = bgColor;
|
||||
});
|
||||
|
||||
// 应用到加载状态和错误状态
|
||||
var loadingStates = document.querySelectorAll('.loading-state');
|
||||
loadingStates.forEach(function(el) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -745,3 +745,9 @@ msgstr "Respuestas"
|
||||
|
||||
msgid "Average Response Time"
|
||||
msgstr "Tiempo de Respuesta Promedio"
|
||||
|
||||
msgid "Show More"
|
||||
msgstr "Mostrar más"
|
||||
|
||||
msgid "Show Less"
|
||||
msgstr "Mostrar menos"
|
||||
|
||||
@@ -744,4 +744,10 @@ msgid "Responses"
|
||||
msgstr "Réponses"
|
||||
|
||||
msgid "Average Response Time"
|
||||
msgstr "Temps de Réponse Moyen"
|
||||
msgstr "Temps de Réponse Moyen"
|
||||
|
||||
msgid "Show More"
|
||||
msgstr "Afficher plus"
|
||||
|
||||
msgid "Show Less"
|
||||
msgstr "Afficher moins"
|
||||
@@ -745,3 +745,9 @@ msgstr "Respons"
|
||||
|
||||
msgid "Average Response Time"
|
||||
msgstr "Waktu Respons Rata-rata"
|
||||
|
||||
msgid "Show More"
|
||||
msgstr "Tampilkan lebih banyak"
|
||||
|
||||
msgid "Show Less"
|
||||
msgstr "Tampilkan lebih sedikit"
|
||||
|
||||
@@ -745,3 +745,9 @@ msgstr "レスポンス"
|
||||
|
||||
msgid "Average Response Time"
|
||||
msgstr "平均応答時間"
|
||||
|
||||
msgid "Show More"
|
||||
msgstr "もっと見る"
|
||||
|
||||
msgid "Show Less"
|
||||
msgstr "閉じる"
|
||||
|
||||
@@ -746,3 +746,9 @@ msgstr "Odpowiedzi"
|
||||
msgid "Average Response Time"
|
||||
msgstr "Średni Czas Odpowiedzi"
|
||||
|
||||
msgid "Show More"
|
||||
msgstr "Pokaż więcej"
|
||||
|
||||
msgid "Show Less"
|
||||
msgstr "Pokaż mniej"
|
||||
|
||||
|
||||
@@ -744,4 +744,10 @@ msgid "Responses"
|
||||
msgstr "Ответы"
|
||||
|
||||
msgid "Average Response Time"
|
||||
msgstr "Среднее Время Ответа"
|
||||
msgstr "Среднее Время Ответа"
|
||||
|
||||
msgid "Show More"
|
||||
msgstr "Показать больше"
|
||||
|
||||
msgid "Show Less"
|
||||
msgstr "Показать меньше"
|
||||
@@ -745,3 +745,9 @@ msgstr "คำตอบ"
|
||||
|
||||
msgid "Average Response Time"
|
||||
msgstr "เวลาเฉลี่ยในการตอบสนอง"
|
||||
|
||||
msgid "Show More"
|
||||
msgstr "แสดงเพิ่มเติม"
|
||||
|
||||
msgid "Show Less"
|
||||
msgstr "แสดงน้อยลง"
|
||||
|
||||
@@ -744,4 +744,10 @@ msgid "Responses"
|
||||
msgstr "响应"
|
||||
|
||||
msgid "Average Response Time"
|
||||
msgstr "平均响应时间"
|
||||
msgstr "平均响应时间"
|
||||
|
||||
msgid "Show More"
|
||||
msgstr "显示更多"
|
||||
|
||||
msgid "Show Less"
|
||||
msgstr "显示更少"
|
||||
@@ -744,4 +744,10 @@ msgid "Responses"
|
||||
msgstr "響應"
|
||||
|
||||
msgid "Average Response Time"
|
||||
msgstr "平均響應時間"
|
||||
msgstr "平均響應時間"
|
||||
|
||||
msgid "Show More"
|
||||
msgstr "顯示更多"
|
||||
|
||||
msgid "Show Less"
|
||||
msgstr "顯示更少"
|
||||
@@ -240,13 +240,15 @@ get_dns_queries() {
|
||||
local device="$2"
|
||||
local is_query="$3"
|
||||
local dns_server="$4"
|
||||
local page="$5"
|
||||
local page_size="$6"
|
||||
local query_type="$5"
|
||||
local page="$6"
|
||||
local page_size="$7"
|
||||
|
||||
[ -n "$domain" ] && query_params="${query_params}domain=$(printf '%s' "$domain" | sed 's/ /%20/g')&"
|
||||
[ -n "$device" ] && query_params="${query_params}device=$(printf '%s' "$device" | sed 's/ /%20/g')&"
|
||||
[ -n "$is_query" ] && query_params="${query_params}is_query=$is_query&"
|
||||
[ -n "$dns_server" ] && query_params="${query_params}dns_server=$(printf '%s' "$dns_server" | sed 's/ /%20/g')&"
|
||||
[ -n "$query_type" ] && query_params="${query_params}query_type=$(printf '%s' "$query_type" | sed 's/ /%20/g')&"
|
||||
[ -n "$page" ] && query_params="${query_params}page=$page&"
|
||||
[ -n "$page_size" ] && query_params="${query_params}page_size=$page_size&"
|
||||
|
||||
@@ -320,6 +322,7 @@ case "$1" in
|
||||
json_add_string "device"
|
||||
json_add_string "is_query"
|
||||
json_add_string "dns_server"
|
||||
json_add_string "query_type"
|
||||
json_add_int "page"
|
||||
json_add_int "page_size"
|
||||
json_close_object
|
||||
@@ -450,15 +453,18 @@ case "$1" in
|
||||
dns_server=$(echo "$input" | jsonfilter -e '$[3]' 2>/dev/null)
|
||||
[ -z "$dns_server" ] && dns_server=$(echo "$input" | jsonfilter -e '$.dns_server' 2>/dev/null)
|
||||
|
||||
page=$(echo "$input" | jsonfilter -e '$[4]' 2>/dev/null)
|
||||
query_type=$(echo "$input" | jsonfilter -e '$[4]' 2>/dev/null)
|
||||
[ -z "$query_type" ] && query_type=$(echo "$input" | jsonfilter -e '$.query_type' 2>/dev/null)
|
||||
|
||||
page=$(echo "$input" | jsonfilter -e '$[5]' 2>/dev/null)
|
||||
[ -z "$page" ] && page=$(echo "$input" | jsonfilter -e '$.page' 2>/dev/null)
|
||||
|
||||
page_size=$(echo "$input" | jsonfilter -e '$[5]' 2>/dev/null)
|
||||
page_size=$(echo "$input" | jsonfilter -e '$[6]' 2>/dev/null)
|
||||
[ -z "$page_size" ] && page_size=$(echo "$input" | jsonfilter -e '$.page_size' 2>/dev/null)
|
||||
|
||||
get_dns_queries "$domain" "$device" "$is_query" "$dns_server" "$page" "$page_size"
|
||||
get_dns_queries "$domain" "$device" "$is_query" "$dns_server" "$query_type" "$page" "$page_size"
|
||||
else
|
||||
get_dns_queries "" "" "" "" "" ""
|
||||
get_dns_queries "" "" "" "" "" "" ""
|
||||
fi
|
||||
;;
|
||||
getDnsStats)
|
||||
|
||||
@@ -16,52 +16,52 @@ o.rmempty = false
|
||||
---- gfwlist URL
|
||||
o = s:option(DynamicList, "gfwlist_url", translate("GFW domains(gfwlist) Update URL"))
|
||||
o:depends("geo2rule", false)
|
||||
o:value("https://fastly.jsdelivr.net/gh/YW5vbnltb3Vz/domain-list-community@release/gfwlist.txt", translate("v2fly/domain-list-community"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt", translate("Loyalsoldier/v2ray-rules-dat"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/Loukky/gfwlist-by-loukky/gfwlist.txt", translate("Loukky/gfwlist-by-loukky"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt", translate("gfwlist/gfwlist"))
|
||||
o.default = "https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt"
|
||||
o:value("https://cdn.jsdelivr.net/gh/YW5vbnltb3Vz/domain-list-community@release/gfwlist.txt", translate("v2fly/domain-list-community"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt", translate("Loyalsoldier/v2ray-rules-dat"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Loukky/gfwlist-by-loukky/gfwlist.txt", translate("Loukky/gfwlist-by-loukky"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/gfwlist/gfwlist/gfwlist.txt", translate("gfwlist/gfwlist"))
|
||||
o.default = "https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt"
|
||||
|
||||
----chnroute URL
|
||||
o = s:option(DynamicList, "chnroute_url", translate("China IPs(chnroute) Update URL"))
|
||||
o:depends("geo2rule", false)
|
||||
o:value("https://fastly.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china.txt", translate("gaoyifan/china-operator-ip/china"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china.txt", translate("gaoyifan/china-operator-ip/china"))
|
||||
o:value("https://ispip.clang.cn/all_cn.txt", translate("Clang.CN"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/soffchen/GeoIP2-CN@release/CN-ip-cidr.txt", translate("soffchen/GeoIP2-CN"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/Hackl0us/GeoIP2-CN@release/CN-ip-cidr.txt", translate("Hackl0us/GeoIP2-CN"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/blackmatrix7/ios_rule_script@master/rule/Clash/ChinaMax/ChinaMax_IP_No_IPv6.txt", translate("ios_rule_script/ChinaMax_IP_No_IPv6"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/soffchen/GeoIP2-CN@release/CN-ip-cidr.txt", translate("soffchen/GeoIP2-CN"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Hackl0us/GeoIP2-CN@release/CN-ip-cidr.txt", translate("Hackl0us/GeoIP2-CN"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/blackmatrix7/ios_rule_script@master/rule/Clash/ChinaMax/ChinaMax_IP_No_IPv6.txt", translate("ios_rule_script/ChinaMax_IP_No_IPv6"))
|
||||
|
||||
----chnroute6 URL
|
||||
o = s:option(DynamicList, "chnroute6_url", translate("China IPv6s(chnroute6) Update URL"))
|
||||
o:depends("geo2rule", false)
|
||||
o:value("https://fastly.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china6.txt", translate("gaoyifan/china-operator-ip/china6"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china6.txt", translate("gaoyifan/china-operator-ip/china6"))
|
||||
o:value("https://ispip.clang.cn/all_cn_ipv6.txt", translate("Clang.CN.IPv6"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/blackmatrix7/ios_rule_script@master/rule/Clash/ChinaMax/ChinaMax_IP.txt", translate("ios_rule_script/ChinaMax_IP"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/blackmatrix7/ios_rule_script@master/rule/Clash/ChinaMax/ChinaMax_IP.txt", translate("ios_rule_script/ChinaMax_IP"))
|
||||
|
||||
----chnlist URL
|
||||
o = s:option(DynamicList, "chnlist_url", translate("China List(Chnlist) Update URL"))
|
||||
o:depends("geo2rule", false)
|
||||
o:value("https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf", translate("felixonmars/domains.china"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf", translate("felixonmars/apple.china"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/google.china.conf", translate("felixonmars/google.china"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/china-list.txt", translate("Loyalsoldier/china-list"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/apple-cn.txt", translate("Loyalsoldier/apple-cn"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/google-cn.txt", translate("Loyalsoldier/google-cn"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/blackmatrix7/ios_rule_script@master/rule/Clash/ChinaMax/ChinaMax_Domain.txt", translate("ios_rule_script/ChinaMax_Domain"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf", translate("felixonmars/domains.china"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf", translate("felixonmars/apple.china"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/google.china.conf", translate("felixonmars/google.china"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/china-list.txt", translate("Loyalsoldier/china-list"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/apple-cn.txt", translate("Loyalsoldier/apple-cn"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/google-cn.txt", translate("Loyalsoldier/google-cn"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/blackmatrix7/ios_rule_script@master/rule/Clash/ChinaMax/ChinaMax_Domain.txt", translate("ios_rule_script/ChinaMax_Domain"))
|
||||
|
||||
if has_xray or has_singbox then
|
||||
o = s:option(ListValue, "geoip_url", translate("GeoIP Update URL"))
|
||||
o:value("https://github.com/Loyalsoldier/geoip/releases/latest/download/geoip.dat", translate("Loyalsoldier/geoip"))
|
||||
o:value("https://github.com/MetaCubeX/meta-rules-dat/releases/latest/download/geoip.dat", translate("MetaCubeX/geoip"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/Loyalsoldier/geoip@release/geoip.dat", translate("Loyalsoldier/geoip (CDN)"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat", translate("MetaCubeX/geoip (CDN)"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Loyalsoldier/geoip@release/geoip.dat", translate("Loyalsoldier/geoip (CDN)"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat", translate("MetaCubeX/geoip (CDN)"))
|
||||
o.default = "https://github.com/Loyalsoldier/geoip/releases/latest/download/geoip.dat"
|
||||
|
||||
o = s:option(ListValue, "geosite_url", translate("Geosite Update URL"))
|
||||
o:value("https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat", translate("Loyalsoldier/geosite"))
|
||||
o:value("https://github.com/MetaCubeX/meta-rules-dat/releases/latest/download/geosite.dat", translate("MetaCubeX/geosite"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat", translate("Loyalsoldier/geosite (CDN)"))
|
||||
o:value("https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat", translate("MetaCubeX/geosite (CDN)"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/geosite.dat", translate("Loyalsoldier/geosite (CDN)"))
|
||||
o:value("https://cdn.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat", translate("MetaCubeX/geosite (CDN)"))
|
||||
o.default = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat"
|
||||
|
||||
o = s:option(Value, "v2ray_location_asset", translate("Location of Geo rule files"), translate("This variable specifies a directory where geoip.dat and geosite.dat files are."))
|
||||
|
||||
@@ -226,9 +226,13 @@ function url(...)
|
||||
return require "luci.dispatcher".build_url(url)
|
||||
end
|
||||
|
||||
function trim(text)
|
||||
if not text or text == "" then return "" end
|
||||
return text:match("^%s*(.-)%s*$")
|
||||
function trim(s)
|
||||
local len = #s
|
||||
local i, j = 1, len
|
||||
while i <= len and s:byte(i) <= 32 do i = i + 1 end
|
||||
while j >= i and s:byte(j) <= 32 do j = j - 1 end
|
||||
if i > j then return "" end
|
||||
return s:sub(i, j)
|
||||
end
|
||||
|
||||
-- 分割字符串
|
||||
@@ -1258,20 +1262,49 @@ end
|
||||
|
||||
function get_std_domain(domain)
|
||||
domain = trim(domain)
|
||||
if domain == "" or domain:find("#") then return "" end
|
||||
-- 删除首尾所有的 .
|
||||
domain = domain:gsub("^[%.]+", ""):gsub("[%.]+$", "")
|
||||
-- 如果 domain 包含 '*',则分割并删除包含 '*' 的部分及其前面的部分
|
||||
if domain:find("%*") then
|
||||
local parts = {}
|
||||
for part in domain:gmatch("[^%.]+") do
|
||||
table.insert(parts, part)
|
||||
if domain == "" then return "" end
|
||||
-- 含 # → ""
|
||||
for i = 1, #domain do
|
||||
if domain:byte(i) == 35 then return "" end -- '#'
|
||||
end
|
||||
local len = #domain
|
||||
local si, ei = 1, len
|
||||
-- 去前缀 '.'
|
||||
while si <= len and domain:byte(si) == 46 do si = si + 1 end
|
||||
-- 去后缀 '.'
|
||||
while ei >= si and domain:byte(ei) == 46 do ei = ei - 1 end
|
||||
if si > ei then return "" end
|
||||
domain = domain:sub(si, ei)
|
||||
len = #domain
|
||||
-- 是否有 '*'
|
||||
local star = false
|
||||
for i = 1, len do
|
||||
if domain:byte(i) == 42 then star = true break end
|
||||
end
|
||||
if not star then return domain end
|
||||
-- 切割 label
|
||||
local parts, pstart = {}, 1
|
||||
for i = 1, len + 1 do
|
||||
local b = (i <= len) and domain:byte(i) or 46 -- '.' 作为结束
|
||||
if b == 46 then
|
||||
parts[#parts + 1] = domain:sub(pstart, i - 1)
|
||||
pstart = i + 1
|
||||
end
|
||||
for i = #parts, 1, -1 do
|
||||
if parts[i]:find("%*") then
|
||||
-- 删除包含 '*' 的部分及其前面的部分
|
||||
return parts[i + 1] and parts[i + 1] .. "." .. table.concat(parts, ".", i + 2) or ""
|
||||
end
|
||||
-- 从右向左找含 '*' ,并删除包含 '*' 的部分及其左边部分
|
||||
for i = #parts, 1, -1 do
|
||||
local s = parts[i]
|
||||
local has = false
|
||||
for j = 1, #s do
|
||||
if s:byte(j) == 42 then has = true break end
|
||||
end
|
||||
if has then
|
||||
if i == #parts then return "" end
|
||||
local out = parts[i + 1]
|
||||
for k = i + 2, #parts do
|
||||
out = out .. "." .. parts[k]
|
||||
end
|
||||
return out
|
||||
end
|
||||
end
|
||||
return domain
|
||||
|
||||
@@ -64,13 +64,13 @@ config global_rules
|
||||
option gfwlist_update '1'
|
||||
option geosite_update '0'
|
||||
option geoip_update '0'
|
||||
list gfwlist_url 'https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt'
|
||||
list gfwlist_url 'https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt'
|
||||
list chnroute_url 'https://ispip.clang.cn/all_cn.txt'
|
||||
list chnroute_url 'https://fastly.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china.txt'
|
||||
list chnroute_url 'https://cdn.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china.txt'
|
||||
list chnroute6_url 'https://ispip.clang.cn/all_cn_ipv6.txt'
|
||||
list chnroute6_url 'https://fastly.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china6.txt'
|
||||
list chnlist_url 'https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf'
|
||||
list chnlist_url 'https://fastly.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf'
|
||||
list chnroute6_url 'https://cdn.jsdelivr.net/gh/gaoyifan/china-operator-ip@ip-lists/china6.txt'
|
||||
list chnlist_url 'https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/accelerated-domains.china.conf'
|
||||
list chnlist_url 'https://cdn.jsdelivr.net/gh/felixonmars/dnsmasq-china-list/apple.china.conf'
|
||||
option v2ray_location_asset '/usr/share/v2ray/'
|
||||
option geoip_url 'https://github.com/Loyalsoldier/geoip/releases/latest/download/geoip.dat'
|
||||
option geosite_url 'https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/geosite.dat'
|
||||
|
||||
@@ -21,12 +21,6 @@ local chnlist_update = "0"
|
||||
local geoip_update = "0"
|
||||
local geosite_update = "0"
|
||||
|
||||
-- match comments/title/whitelist/ip address/excluded_domain
|
||||
local comment_pattern = "^[#!\\[@]+"
|
||||
local ip_pattern = "^%d+%.%d+%.%d+%.%d+"
|
||||
local ip4_ipset_pattern = "^%d+%.%d+%.%d+%.%d+[%/][%d]+$"
|
||||
local ip6_ipset_pattern = ":-[%x]+%:+[%x]-[%/][%d]+$"
|
||||
local domain_pattern = "([%w%-]+%.[%w%.%-]+)[%/%*]*"
|
||||
local excluded_domain = {"apple.com","sina.cn","sina.com.cn","baidu.com","byr.cn","jlike.com","weibo.com","zhongsou.com","youdao.com","sogou.com","so.com","soso.com","aliyun.com","taobao.com","jd.com","qq.com","bing.com"}
|
||||
|
||||
local gfwlist_url = uci:get(name, "@global_rules[0]", "gfwlist_url") or {"https://fastly.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt"}
|
||||
@@ -87,7 +81,8 @@ end
|
||||
-- curl
|
||||
local function curl(url, file, valifile)
|
||||
local args = {
|
||||
"-skL", "-w %{http_code}", "--retry 3", "--connect-timeout 3", "--max-time 300", "--speed-limit 51200 --speed-time 15"
|
||||
"-skL", "-w %{http_code}", "--retry 3", "--connect-timeout 3", "--max-time 300", "--speed-limit 51200 --speed-time 15",
|
||||
'-A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"'
|
||||
}
|
||||
if file then
|
||||
args[#args + 1] = "-o " .. file
|
||||
@@ -100,14 +95,43 @@ local function curl(url, file, valifile)
|
||||
end
|
||||
|
||||
--check excluded domain
|
||||
local excluded_map = {}
|
||||
for _, d in ipairs(excluded_domain) do
|
||||
excluded_map[d] = true
|
||||
end
|
||||
local function check_excluded_domain(value)
|
||||
value = value and value:lower() or ""
|
||||
for _, domain in ipairs(excluded_domain) do
|
||||
local pattern = "^[a-z0-9-]+%.(" .. domain .. ")$"
|
||||
if value:match(pattern) then
|
||||
return true
|
||||
end
|
||||
if not value or value == "" then return false end
|
||||
value = value:lower()
|
||||
local eq_pos = value:find("=", 1, true)
|
||||
if eq_pos then
|
||||
value = value:sub(eq_pos + 1)
|
||||
end
|
||||
if value:sub(1,1) == "/" then
|
||||
value = value:sub(2)
|
||||
end
|
||||
local slash_pos = value:find("/", 1, true)
|
||||
local colon_pos = value:find(":", 1, true)
|
||||
local cut_pos
|
||||
if slash_pos and colon_pos then
|
||||
cut_pos = (slash_pos < colon_pos) and slash_pos or colon_pos
|
||||
else
|
||||
cut_pos = slash_pos or colon_pos
|
||||
end
|
||||
if cut_pos then
|
||||
value = value:sub(1, cut_pos - 1)
|
||||
end
|
||||
value = value:gsub("^%.*", ""):gsub("%.*$", "")
|
||||
while value do
|
||||
if excluded_map[value] then
|
||||
return true
|
||||
end
|
||||
local dot_pos = value:find(".", 1, true)
|
||||
if not dot_pos then
|
||||
break
|
||||
end
|
||||
value = value:sub(dot_pos + 1)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function line_count(file_path)
|
||||
@@ -118,6 +142,185 @@ local function line_count(file_path)
|
||||
return num;
|
||||
end
|
||||
|
||||
-- 替代 string.find 查找 "^[#!\\[@]+"
|
||||
local function is_comment_line(s)
|
||||
if not s or s == "" then return false end
|
||||
local b = s:byte(1)
|
||||
-- '#' = 35, '!' = 33, '\' = 92, '[' = 91, '@' = 64
|
||||
if b == 35 or b == 33 or b == 92 or b == 91 or b == 64 then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- IPv4 检测,替代 string.find "^%d+%.%d+%.%d+%.%d+"
|
||||
-- IPv4 cidr检测,替代 string.find "^%d+%.%d+%.%d+%.%d+[%/][%d]+$"
|
||||
local function is_ipv4(s, check_cidr)
|
||||
local dot = 0
|
||||
local seg_start = 1
|
||||
local len = #s
|
||||
local mask_start = nil
|
||||
local i = 1
|
||||
while i <= len do
|
||||
local b = s:byte(i)
|
||||
if b >= 48 and b <= 57 then
|
||||
-- 数字,继续
|
||||
elseif b == 46 then -- "."
|
||||
dot = dot + 1
|
||||
if dot > 3 or i == seg_start then return false end
|
||||
local seg = tonumber(s:sub(seg_start, i - 1))
|
||||
if not seg or seg > 255 then return false end
|
||||
seg_start = i + 1
|
||||
elseif b == 47 then -- "/"
|
||||
if not check_cidr then return false end
|
||||
if dot ~= 3 or i == seg_start then return false end
|
||||
local seg = tonumber(s:sub(seg_start, i - 1))
|
||||
if not seg or seg > 255 then return false end
|
||||
mask_start = i + 1
|
||||
break
|
||||
else
|
||||
return false
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
-- 如果没有 CIDR,则检查最后一段即可
|
||||
if not check_cidr or not mask_start then
|
||||
if dot ~= 3 or seg_start > len then return false end
|
||||
local seg = tonumber(s:sub(seg_start))
|
||||
return seg and seg <= 255 or false
|
||||
end
|
||||
-- CIDR 掩码检查
|
||||
if mask_start > len then return false end
|
||||
local mask = tonumber(s:sub(mask_start))
|
||||
return mask and mask >= 0 and mask <= 32 or false
|
||||
end
|
||||
|
||||
local function is_ipv4_cidr(s)
|
||||
return is_ipv4(s, true)
|
||||
end
|
||||
|
||||
local function is_ipv6(s, check_cidr)
|
||||
local first = s:byte(1)
|
||||
local last = s:byte(#s)
|
||||
if first == 91 and last == 93 then -- "[" and "]"
|
||||
s = s:sub(2, -2)
|
||||
end
|
||||
local len = #s
|
||||
local i = 1
|
||||
local seg_len = 0
|
||||
local segs = 0
|
||||
local saw_dc = false -- 是否出现 "::"
|
||||
local b
|
||||
while i <= len do
|
||||
b = s:byte(i)
|
||||
-- CIDR 部分
|
||||
if b == 47 then -- '/'
|
||||
if not check_cidr then
|
||||
return false
|
||||
end
|
||||
-- 处理 "/" 之前的段
|
||||
if seg_len > 0 then segs = segs + 1 end
|
||||
if (not saw_dc and segs ~= 8) or (saw_dc and segs > 8) then return false end
|
||||
-- 解析掩码
|
||||
i = i + 1
|
||||
if i > len then return false end
|
||||
local mask = 0
|
||||
while i <= len do
|
||||
b = s:byte(i)
|
||||
if b < 48 or b > 57 then return false end
|
||||
mask = mask * 10 + (b - 48)
|
||||
if mask > 128 then return false end
|
||||
i = i + 1
|
||||
end
|
||||
-- CIDR 解析成功
|
||||
return true
|
||||
end
|
||||
-- 冒号处理(: 或 ::)
|
||||
if b == 58 then
|
||||
local nextb = (i+1 <= len) and s:byte(i+1) or 0
|
||||
-- "::"
|
||||
if nextb == 58 then
|
||||
if saw_dc then return false end
|
||||
saw_dc = true
|
||||
if seg_len > 0 then segs = segs + 1 end
|
||||
seg_len = 0
|
||||
i = i + 2
|
||||
else
|
||||
-- 普通 ":"
|
||||
if seg_len == 0 then return false end
|
||||
segs = segs + 1
|
||||
seg_len = 0
|
||||
i = i + 1
|
||||
end
|
||||
else
|
||||
-- hex 数字
|
||||
local is_hex =
|
||||
(b >= 48 and b <= 57) or -- 0-9
|
||||
(b >= 65 and b <= 70) or -- A-F
|
||||
(b >= 97 and b <= 102) -- a-f
|
||||
if not is_hex then return false end
|
||||
seg_len = seg_len + 1
|
||||
if seg_len > 4 then return false end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
if seg_len > 0 then segs = segs + 1 end
|
||||
if not saw_dc then return segs == 8 end
|
||||
return segs <= 8
|
||||
end
|
||||
|
||||
-- IPv6 cidr检测,替代 string.find ":-[%x]+%:+[%x]-[%/][%d]+$"
|
||||
local function is_ipv6_cidr(s)
|
||||
return is_ipv6(s, true)
|
||||
end
|
||||
|
||||
-- 检测是否含有冒号,替代 string.find(line, ":")
|
||||
local function has_colon(s)
|
||||
for i = 1, #s do
|
||||
if s:byte(i) == 58 then -- ':'
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- 域名提取,替代 string.match "([%w%-]+%.[%w%.%-]+)[%/%*]*"
|
||||
local function extract_domain(s)
|
||||
if not s or s == "" then return nil end
|
||||
local len = #s
|
||||
local start = nil
|
||||
local last_dot = nil
|
||||
for i = 1, len do
|
||||
local b = s:byte(i)
|
||||
-- 允许的域名字符:a-zA-Z0-9.-
|
||||
if (b >= 48 and b <= 57) or (b >= 65 and b <= 90) or (b >= 97 and b <= 122) or b == 45 or b == 46 then
|
||||
if not start then start = i end
|
||||
if b == 46 then last_dot = i end
|
||||
else
|
||||
if start then
|
||||
if last_dot and last_dot > start then
|
||||
local domain = s:sub(start, i - 1)
|
||||
while domain:byte(1) == 46 do
|
||||
domain = domain:sub(2)
|
||||
end
|
||||
return domain
|
||||
else
|
||||
start = nil
|
||||
last_dot = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if start and last_dot and last_dot > start then
|
||||
local domain = s:sub(start)
|
||||
while domain:byte(1) == 46 do
|
||||
domain = domain:sub(2)
|
||||
end
|
||||
return domain
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function non_file_check(file_path, vali_file)
|
||||
if fs.readfile(file_path, 10) then
|
||||
local size_str = sys.exec("grep -i 'Content-Length' " .. vali_file .. " | tail -n1 | sed 's/[^0-9]//g'")
|
||||
@@ -201,7 +404,6 @@ local function fetch_rule(rule_name,rule_type,url,exclude_domain)
|
||||
|
||||
if sret_tmp == 200 then
|
||||
if rule_name == "gfwlist" and geo2rule == "0" then
|
||||
local domains = {}
|
||||
local gfwlist = io.open(download_file_tmp..k, "r")
|
||||
local decode = api.base64Decode(gfwlist:read("*all"))
|
||||
gfwlist:close()
|
||||
@@ -214,8 +416,8 @@ local function fetch_rule(rule_name,rule_type,url,exclude_domain)
|
||||
if rule_type == "domain" and exclude_domain == true then
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
line = line:gsub("full:", "")
|
||||
if not (string.find(line, comment_pattern) or string.find(line, ip_pattern) or check_excluded_domain(line) or string.find(line, ":")) then
|
||||
local match = string.match(line, domain_pattern)
|
||||
if not (is_comment_line(line) or is_ipv4(line) or check_excluded_domain(line) or has_colon(line)) then
|
||||
local match = extract_domain(line)
|
||||
if match then
|
||||
domains[match] = true
|
||||
end
|
||||
@@ -225,8 +427,8 @@ local function fetch_rule(rule_name,rule_type,url,exclude_domain)
|
||||
elseif rule_type == "domain" then
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
line = line:gsub("full:", "")
|
||||
if not (string.find(line, comment_pattern) or string.find(line, ip_pattern) or string.find(line, ":")) then
|
||||
local match = string.match(line, domain_pattern)
|
||||
if not (is_comment_line(line) or is_ipv4(line) or has_colon(line)) then
|
||||
local match = extract_domain(line)
|
||||
if match then
|
||||
domains[match] = true
|
||||
end
|
||||
@@ -235,22 +437,35 @@ local function fetch_rule(rule_name,rule_type,url,exclude_domain)
|
||||
|
||||
elseif rule_type == "ip4" then
|
||||
local out = io.open(unsort_file_tmp, "a")
|
||||
local function is_0dot(s) -- "^0%..*"
|
||||
return s and s:byte(1)==48 and s:byte(2)==46
|
||||
end
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
if string.match(line, ip4_ipset_pattern) and not string.match(line, "^0%..*") then
|
||||
out:write(string.format("%s\n", line))
|
||||
if is_ipv4_cidr(line) and not is_0dot(line) then
|
||||
out:write(line .. "\n")
|
||||
end
|
||||
end
|
||||
out:close()
|
||||
|
||||
elseif rule_type == "ip6" then
|
||||
local out = io.open(unsort_file_tmp, "a")
|
||||
local function is_double_colon_cidr(s) -- "^::(/%d+)?$"
|
||||
if not s or s:byte(1)~=58 or s:byte(2)~=58 then return false end
|
||||
local l = #s
|
||||
if l==2 then return true end
|
||||
if l==3 or s:byte(3)~=47 then return false end
|
||||
for i=4,l do
|
||||
local b=s:byte(i)
|
||||
if b<48 or b>57 then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
for line in io.lines(download_file_tmp..k) do
|
||||
if string.match(line, ip6_ipset_pattern) and not string.match(line, "^::(/%d+)?$") then
|
||||
out:write(string.format("%s\n", line))
|
||||
if is_ipv6_cidr(line) and not is_double_colon_cidr(line) then
|
||||
out:write(line .. "\n")
|
||||
end
|
||||
end
|
||||
out:close()
|
||||
|
||||
end
|
||||
else
|
||||
sret = 0
|
||||
@@ -268,7 +483,7 @@ local function fetch_rule(rule_name,rule_type,url,exclude_domain)
|
||||
end
|
||||
out:close()
|
||||
end
|
||||
sys.call("LC_ALL=C sort -u " .. unsort_file_tmp .. " > " .. file_tmp)
|
||||
os.execute("LC_ALL=C /bin/busybox sort -u " .. unsort_file_tmp .. " > " .. file_tmp)
|
||||
os.remove(unsort_file_tmp)
|
||||
|
||||
local old_md5 = sys.exec("echo -n $(md5sum " .. rule_path .. "/" ..rule_name.. " | awk '{print $1}')"):gsub("\n", "")
|
||||
@@ -290,10 +505,10 @@ local function fetch_rule(rule_name,rule_type,url,exclude_domain)
|
||||
end
|
||||
gen_cache(set_name, "ipv6_addr", file_tmp, output_file)
|
||||
end
|
||||
sys.exec(string.format('mv -f %s %s', output_file, rule_path .. "/" ..rule_name.. ".nft"))
|
||||
sys.call(string.format('mv -f %s %s', output_file, rule_path .. "/" ..rule_name.. ".nft"))
|
||||
os.remove(output_file)
|
||||
end
|
||||
sys.exec("mv -f "..file_tmp .. " " ..rule_path .. "/" ..rule_name)
|
||||
sys.call("mv -f "..file_tmp .. " " ..rule_path .. "/" ..rule_name)
|
||||
reboot = 1
|
||||
log(rule_name.. " 更新成功,总规则数 " ..count.. " 条。")
|
||||
else
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=v2ray-core
|
||||
PKG_VERSION:=5.41.0
|
||||
PKG_VERSION:=5.42.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
|
||||
PKG_SOURCE_URL:=https://codeload.github.com/v2fly/v2ray-core/tar.gz/v$(PKG_VERSION)?
|
||||
PKG_HASH:=c67caa2d73f35a9562ecaeb5184733c943c9dafb47e8f1cfeacb892a9247e9b5
|
||||
PKG_HASH:=24e2182bf77342165511150db014668723ac4a0c9e72269b0590b0be72875b49
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
Reference in New Issue
Block a user