From 8cbf526882aef91bb48afca9e519fb251816aff4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 1 Sep 2026 02:40:52 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=90=20Sync=202026-09-01=2002:40:52?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- luci-app-mosdns/Makefile | 4 +- .../resources/view/mosdns/statistics.js | 359 +++++++++++++----- luci-app-passwall/Makefile | 2 +- .../luasrc/passwall/util_sing-box.lua | 11 - .../luasrc/passwall/util_xray.lua | 5 - luci-app-passwall2/Makefile | 2 +- .../luasrc/passwall2/util_sing-box.lua | 5 - luci-theme-footstrap/Makefile | 2 +- .../htdocs/luci-static/resources/fs-fit.js | 12 + mise/Makefile | 4 +- mosdns/Makefile | 2 +- .../100-mosdns-update-dependencies.patch | 241 +++++++----- ...xclude-blocked-queries-from-cache-hi.patch | 74 ++++ 13 files changed, 503 insertions(+), 220 deletions(-) create mode 100644 mosdns/patches/222-feat-stats_api-exclude-blocked-queries-from-cache-hi.patch diff --git a/luci-app-mosdns/Makefile b/luci-app-mosdns/Makefile index fe39983f..ae52b719 100644 --- a/luci-app-mosdns/Makefile +++ b/luci-app-mosdns/Makefile @@ -1,8 +1,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-mosdns -PKG_VERSION:=1.7.11 -PKG_RELEASE:=19 +PKG_VERSION:=1.7.12 +PKG_RELEASE:=20 LUCI_TITLE:=LuCI Support for mosdns LUCI_PKGARCH:=all diff --git a/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js b/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js index 9b398094..c08bbbda 100644 --- a/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js +++ b/luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js @@ -48,6 +48,10 @@ let nodeStats; let nodeTop; let nodeLogs; let autoStatusBadge; +let statsElements = null; +let currentBadgeState = null; +let lastTopJson = ''; +let lastLogsJson = ''; const cleanIP = ip => { if (!ip) return '-'; @@ -66,9 +70,10 @@ const injectStyles = () => { '.mosdns-stat-card .title-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; font-size: 0.85rem; opacity: 0.75; font-weight: 500; }', '.mosdns-stat-card .metric-val { font-size: 1.85rem; font-weight: 700; line-height: 1.1; letter-spacing: -0.02em; }', '.mosdns-stat-card .subtext { font-size: 0.8rem; opacity: 0.6; margin-top: 0.4rem; }', - '.mosdns-sparkline-wrap { margin-top: 0.5rem; height: 44px; position: relative; overflow: visible; display: flex; align-items: flex-end; }', + '.mosdns-sparkline-wrap { margin-top: 0.5rem; height: 44px; position: relative; overflow: visible; display: flex; align-items: flex-end; touch-action: none; -webkit-user-select: none; user-select: none; }', '.mosdns-sparkline { width: 100%; height: 100%; display: block; overflow: visible; }', '.mosdns-sparkline-tooltip { position: absolute; pointer-events: none; z-index: 20; padding: 0.25rem 0.5rem; border-radius: 5px; background: var(--cbi-section-bg, #fff); border: 1px solid rgba(0,0,0,0.12); box-shadow: 0 3px 10px rgba(0,0,0,0.12); line-height: 1.25; text-align: center; white-space: nowrap; transition: opacity 0.15s ease; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }', + '.spark-hover-hitbox { touch-action: none; -webkit-user-select: none; user-select: none; cursor: crosshair; }', '.mosdns-rank-panel { background: var(--cbi-section-bg, #fff); border: 1px solid rgba(0,0,0,0.08); border-radius: 8px; padding: 1rem 1.1rem; box-shadow: 0 2px 6px rgba(0,0,0,0.03); }', '.mosdns-rank-panel h4 { margin: 0 0 0.85rem 0; font-size: 0.95rem; font-weight: 600; display: flex; align-items: center; justify-content: space-between; }', '.mosdns-rank-item { position: relative; overflow: hidden; border-radius: 6px; padding: 0.35rem 0.65rem; display: flex; justify-content: space-between; align-items: center; background: rgba(125,125,125,0.03); border: 1px solid rgba(125,125,125,0.08); margin-bottom: 0.35rem; }', @@ -143,7 +148,15 @@ const getLatencyClass = elapsedMs => { const updateLiveStatusBadge = () => { if (!autoStatusBadge) return; - if (pageIdx === 0 && !searchVal && !isUserPaused) { + const isLive = (pageIdx === 0 && !searchVal && !isUserPaused); + const targetState = isLive ? 'live' : ('paused:' + pageIdx); + + if (currentBadgeState === targetState) + return; + + currentBadgeState = targetState; + + if (isLive) { dom.content(autoStatusBadge, [ E('span', { class: 'mosdns-badge badge-teal badge-pulse' }, _('● Live Auto-refresh')) ]); @@ -154,47 +167,17 @@ const updateLiveStatusBadge = () => { } }; -const createSparklineSVG = (dataItems, strokeColor, fillGradId, maxScale) => { +const createSparklineSVG = (strokeColor, fillGradId) => { const width = 300; const height = 44; const padTop = 4; const padBottom = 2; const drawHeight = height - padTop - padBottom; - let items = (dataItems && dataItems.length > 0) ? dataItems : []; - if (!items.length) { - items = new Array(24).fill(0).map(() => ({ time: '', val: 0 })); - } - if (items.length < 2) { - items = [items[0] || { time: '', val: 0 }, items[0] || { time: '', val: 0 }]; - } - - const vals = items.map(i => i.val || 0); - const maxVal = maxScale || Math.max(...vals, 1); - const len = items.length; - - const coords = items.map((item, idx) => { - const x = (idx / (len - 1)) * width; - const y = height - padBottom - ((item.val || 0) / maxVal) * drawHeight; - return { x, y, time: item.time, val: item.val || 0 }; - }); - - let pathD = 'M ' + coords[0].x.toFixed(1) + ',' + coords[0].y.toFixed(1); - for (let i = 0; i < coords.length - 1; i++) { - const p0 = coords[i === 0 ? 0 : i - 1]; - const p1 = coords[i]; - const p2 = coords[i + 1]; - const p3 = coords[i + 2 < coords.length ? i + 2 : i + 1]; - - const cp1x = p1.x + (p2.x - p0.x) / 6; - const cp1y = p1.y + (p2.y - p0.y) / 6; - const cp2x = p2.x - (p3.x - p1.x) / 6; - const cp2y = p2.y - (p3.y - p1.y) / 6; - - pathD += ' C ' + cp1x.toFixed(1) + ',' + cp1y.toFixed(1) + ' ' + cp2x.toFixed(1) + ',' + cp2y.toFixed(1) + ' ' + p2.x.toFixed(1) + ',' + p2.y.toFixed(1); - } - - const areaD = pathD + ' L ' + width + ',' + height + ' L 0,' + height + ' Z'; + let coords = []; + let isHovered = false; + let currentIdx = -1; + let hideTimer = null; const svgContainer = E('div', { class: 'mosdns-sparkline-wrap' }); svgContainer.innerHTML = @@ -205,27 +188,26 @@ const createSparklineSVG = (dataItems, strokeColor, fillGradId, maxScale) => { '' + '' + '' + - '' + - '' + + '' + + '' + '' + - '' + + '' + '' + ''; + const areaPath = svgContainer.querySelector('.spark-area-path'); + const linePath = svgContainer.querySelector('.spark-line-path'); const hoverGroup = svgContainer.querySelector('.spark-hover-group'); const hoverLine = svgContainer.querySelector('.spark-hover-line'); const hoverDot = svgContainer.querySelector('.spark-hover-dot'); const hitbox = svgContainer.querySelector('.spark-hover-hitbox'); const tooltip = svgContainer.querySelector('.mosdns-sparkline-tooltip'); - const onMove = e => { - const rect = svgContainer.getBoundingClientRect(); - const mouseX = e.clientX - rect.left; - const ratio = Math.max(0, Math.min(1, mouseX / rect.width)); - const idx = Math.round(ratio * (len - 1)); + const renderTooltipAndHover = idx => { + if (idx < 0 || idx >= coords.length) return; const coord = coords[idx]; if (!coord) return; @@ -251,10 +233,14 @@ const createSparklineSVG = (dataItems, strokeColor, fillGradId, maxScale) => { '
' + timeStr + '
'; tooltip.style.display = 'block'; - const tooltipX = (coord.x / width) * rect.width; + const rect = svgContainer.getBoundingClientRect(); + const containerWidth = rect.width || width; + const tooltipX = (coord.x / width) * containerWidth; + const ratio = coord.x / width; + if (ratio > 0.65) { tooltip.style.left = 'auto'; - tooltip.style.right = (rect.width - tooltipX + 8) + 'px'; + tooltip.style.right = (containerWidth - tooltipX + 8) + 'px'; } else { tooltip.style.left = (tooltipX + 8) + 'px'; tooltip.style.right = 'auto'; @@ -262,21 +248,203 @@ const createSparklineSVG = (dataItems, strokeColor, fillGradId, maxScale) => { tooltip.style.top = '-6px'; }; - const onLeave = () => { - hoverGroup.style.display = 'none'; - tooltip.style.display = 'none'; + const update = (dataItems, maxScale) => { + let items = (dataItems && dataItems.length > 0) ? dataItems : []; + if (!items.length) { + items = new Array(24).fill(0).map(() => ({ time: '', val: 0 })); + } + if (items.length < 2) { + items = [items[0] || { time: '', val: 0 }, items[0] || { time: '', val: 0 }]; + } + + const vals = items.map(i => i.val || 0); + const maxVal = maxScale || Math.max(...vals, 1); + const len = items.length; + + coords = items.map((item, idx) => { + const x = (idx / (len - 1)) * width; + const y = height - padBottom - ((item.val || 0) / maxVal) * drawHeight; + return { x, y, time: item.time, val: item.val || 0 }; + }); + + let pathD = 'M ' + coords[0].x.toFixed(1) + ',' + coords[0].y.toFixed(1); + for (let i = 0; i < coords.length - 1; i++) { + const p0 = coords[i === 0 ? 0 : i - 1]; + const p1 = coords[i]; + const p2 = coords[i + 1]; + const p3 = coords[i + 2 < coords.length ? i + 2 : i + 1]; + + const cp1x = p1.x + (p2.x - p0.x) / 6; + const cp1y = p1.y + (p2.y - p0.y) / 6; + const cp2x = p2.x - (p3.x - p1.x) / 6; + const cp2y = p2.y - (p3.y - p1.y) / 6; + + pathD += ' C ' + cp1x.toFixed(1) + ',' + cp1y.toFixed(1) + ' ' + cp2x.toFixed(1) + ',' + cp2y.toFixed(1) + ' ' + p2.x.toFixed(1) + ',' + p2.y.toFixed(1); + } + + const areaD = pathD + ' L ' + width + ',' + height + ' L 0,' + height + ' Z'; + + areaPath.setAttribute('d', areaD); + linePath.setAttribute('d', pathD); + + if (isHovered && currentIdx >= 0) { + renderTooltipAndHover(currentIdx); + } }; - hitbox.addEventListener('mousemove', onMove); - hitbox.addEventListener('mouseleave', onLeave); + const onPositionMove = clientX => { + if (hideTimer) { + clearTimeout(hideTimer); + hideTimer = null; + } + const rect = svgContainer.getBoundingClientRect(); + if (!rect.width || !coords.length) return; + const mouseX = clientX - rect.left; + const ratio = Math.max(0, Math.min(1, mouseX / rect.width)); + const idx = Math.round(ratio * (coords.length - 1)); + currentIdx = idx; + isHovered = true; + renderTooltipAndHover(idx); + }; + const onLeave = (delay = 0) => { + if (delay > 0) { + if (hideTimer) clearTimeout(hideTimer); + hideTimer = setTimeout(() => { + isHovered = false; + currentIdx = -1; + hoverGroup.style.display = 'none'; + tooltip.style.display = 'none'; + hideTimer = null; + }, delay); + } else { + if (hideTimer) { + clearTimeout(hideTimer); + hideTimer = null; + } + isHovered = false; + currentIdx = -1; + hoverGroup.style.display = 'none'; + tooltip.style.display = 'none'; + } + }; + + hitbox.addEventListener('mousemove', e => onPositionMove(e.clientX)); + hitbox.addEventListener('mouseenter', e => onPositionMove(e.clientX)); + hitbox.addEventListener('mouseleave', () => onLeave(0)); + + hitbox.addEventListener('touchstart', e => { + if (e.touches && e.touches.length > 0) { + onPositionMove(e.touches[0].clientX); + } + }, { passive: true }); + + hitbox.addEventListener('touchmove', e => { + if (e.touches && e.touches.length > 0) { + onPositionMove(e.touches[0].clientX); + } + }, { passive: true }); + + hitbox.addEventListener('touchend', () => onLeave(1500), { passive: true }); + hitbox.addEventListener('touchcancel', () => onLeave(0), { passive: true }); + + svgContainer.update = update; return svgContainer; }; -const renderOverviewStats = (stats, historyData) => { +const createOverviewStatsDOM = () => { + const metricTotal = E('div', { class: 'metric-val' }, '-'); + const subtextTotal = E('div', { class: 'subtext' }, '-'); + const sparklineTotal = createSparklineSVG('#3b82f6', 'spark-grad-total'); + + const badgeBlocked = E('span', { class: 'mosdns-badge badge-danger' }, '-'); + const metricBlocked = E('div', { class: 'metric-val', style: 'color: #dc2626;' }, '-'); + const sparklineBlocked = createSparklineSVG('#dc2626', 'spark-grad-blocked'); + + const badgeCached = E('span', { class: 'mosdns-badge badge-teal' }, '-'); + const metricCached = E('div', { class: 'metric-val', style: 'color: #059669;' }, '-'); + const sparklineCached = createSparklineSVG('#059669', 'spark-grad-cached'); + + const metricLatency = E('div', { class: 'metric-val', style: 'color: #2563eb;' }, '-'); + const subtextLatency = E('div', { class: 'subtext' }, _('Per-query speed')); + + const grid = E('div', { class: 'mosdns-grid' }, [ + E('div', { class: 'mosdns-stat-card' }, [ + E('div', {}, [ + E('div', { class: 'title-row' }, [ + E('span', {}, _('DNS Queries Total')), + E('span', { class: 'mosdns-badge badge-teal badge-pulse' }, _('● Live')) + ]), + metricTotal, + subtextTotal + ]), + sparklineTotal + ]), + + E('div', { class: 'mosdns-stat-card' }, [ + E('div', {}, [ + E('div', { class: 'title-row' }, [ + E('span', {}, _('Blocked by Filters')), + badgeBlocked + ]), + metricBlocked + ]), + sparklineBlocked + ]), + + E('div', { class: 'mosdns-stat-card' }, [ + E('div', {}, [ + E('div', { class: 'title-row' }, [ + E('span', {}, _('Cached Queries')), + badgeCached + ]), + metricCached + ]), + sparklineCached + ]), + + E('div', { class: 'mosdns-stat-card' }, [ + E('div', {}, [ + E('div', { class: 'title-row' }, [ + E('span', {}, _('Average Processing Time')), + E('span', { class: 'mosdns-badge badge-primary' }, _('Latency')) + ]), + metricLatency, + subtextLatency + ]) + ]) + ]); + + statsElements = { + grid, + metricTotal, + subtextTotal, + sparklineTotal, + badgeBlocked, + metricBlocked, + sparklineBlocked, + badgeCached, + metricCached, + sparklineCached, + metricLatency, + subtextLatency + }; + + return grid; +}; + +const updateOverviewStats = (stats, historyData) => { if (!stats || stats.error) { - return E('div', { class: 'alert-message warning' }, - _('MosDNS API is unreachable. Please ensure MosDNS is running and stats_api plugin is enabled.')); + if (nodeStats) { + dom.content(nodeStats, E('div', { class: 'alert-message warning' }, + _('MosDNS API is unreachable. Please ensure MosDNS is running and stats_api plugin is enabled.'))); + statsElements = null; + } + return; + } + + if (!statsElements || !nodeStats.contains(statsElements.grid)) { + dom.content(nodeStats, createOverviewStatsDOM()); } const { @@ -295,52 +463,19 @@ const renderOverviewStats = (stats, historyData) => { const baseMax = Math.max(...totalItems.map(i => i.val), 1); - return E('div', { class: 'mosdns-grid' }, [ - E('div', { class: 'mosdns-stat-card' }, [ - E('div', {}, [ - E('div', { class: 'title-row' }, [ - E('span', {}, _('DNS Queries Total')), - E('span', { class: 'mosdns-badge badge-teal badge-pulse' }, _('● Live')) - ]), - E('div', { class: 'metric-val' }, total.toLocaleString()), - E('div', { class: 'subtext' }, _('Avg Processing') + ': ' + avg_ms + ' ms') - ]), - createSparklineSVG(totalItems, '#3b82f6', 'spark-grad-total', baseMax) - ]), + statsElements.metricTotal.textContent = total.toLocaleString(); + statsElements.subtextTotal.textContent = _('Avg Processing') + ': ' + avg_ms + ' ms'; + statsElements.sparklineTotal.update(totalItems, baseMax); - E('div', { class: 'mosdns-stat-card' }, [ - E('div', {}, [ - E('div', { class: 'title-row' }, [ - E('span', {}, _('Blocked by Filters')), - E('span', { class: 'mosdns-badge badge-danger' }, blocked_pct + '%') - ]), - E('div', { class: 'metric-val', style: 'color: #dc2626;' }, blocked.toLocaleString()) - ]), - createSparklineSVG(blockedItems, '#dc2626', 'spark-grad-blocked', baseMax) - ]), + statsElements.badgeBlocked.textContent = blocked_pct + '%'; + statsElements.metricBlocked.textContent = blocked.toLocaleString(); + statsElements.sparklineBlocked.update(blockedItems, baseMax); - E('div', { class: 'mosdns-stat-card' }, [ - E('div', {}, [ - E('div', { class: 'title-row' }, [ - E('span', {}, _('Cached Queries')), - E('span', { class: 'mosdns-badge badge-teal' }, cached_pct + '%') - ]), - E('div', { class: 'metric-val', style: 'color: #059669;' }, cached.toLocaleString()) - ]), - createSparklineSVG(cachedItems, '#059669', 'spark-grad-cached', baseMax) - ]), + statsElements.badgeCached.textContent = cached_pct + '%'; + statsElements.metricCached.textContent = cached.toLocaleString(); + statsElements.sparklineCached.update(cachedItems, baseMax); - E('div', { class: 'mosdns-stat-card' }, [ - E('div', {}, [ - E('div', { class: 'title-row' }, [ - E('span', {}, _('Average Processing Time')), - E('span', { class: 'mosdns-badge badge-primary' }, _('Latency')) - ]), - E('div', { class: 'metric-val', style: 'color: #2563eb;' }, avg_ms + ' ms'), - E('div', { class: 'subtext' }, _('Per-query speed')) - ]) - ]) - ]); + statsElements.metricLatency.textContent = avg_ms + ' ms'; }; const renderTopRankings = topData => { @@ -398,6 +533,13 @@ const renderTopRankings = topData => { ]); }; +const updateTopRankings = topData => { + const json = JSON.stringify(topData || {}); + if (json === lastTopJson) return; + lastTopJson = json; + dom.content(nodeTop, renderTopRankings(topData)); +}; + const showLogDetailsModal = item => { let statusBadge; if (item.is_blocked) { @@ -579,11 +721,15 @@ const pollScheduler = async () => { const results = await Promise.all(promises); - dom.content(nodeStats, renderOverviewStats(results[0], results[2])); - dom.content(nodeTop, renderTopRankings(results[1])); + updateOverviewStats(results[0], results[2]); + updateTopRankings(results[1]); if (shouldRefreshLogs && results[3]) { - dom.content(nodeLogs, renderLogsTable(results[3])); + const json = JSON.stringify(results[3]); + if (json !== lastLogsJson) { + lastLogsJson = json; + dom.content(nodeLogs, renderLogsTable(results[3])); + } } updateLiveStatusBadge(); } catch (e) { @@ -593,6 +739,7 @@ const pollScheduler = async () => { const refreshLogs = async () => { try { const logs = await callGetLogs(PAGE_SIZE, pageIdx * PAGE_SIZE, searchVal, filterVal); + lastLogsJson = JSON.stringify(logs || {}); dom.content(nodeLogs, renderLogsTable(logs)); updateLiveStatusBadge(); } catch (e) { @@ -613,13 +760,19 @@ return view.extend({ render(data) { injectStyles(); + statsElements = null; + currentBadgeState = null; + lastTopJson = ''; + lastLogsJson = ''; + nodeStats = E('div', { id: 'overview-stats' }); nodeTop = E('div', { id: 'top-rankings' }); nodeLogs = E('div', { id: 'logs-table' }); autoStatusBadge = E('div', { style: 'display: inline-block;' }); - dom.content(nodeStats, renderOverviewStats(data[0], data[3])); - dom.content(nodeTop, renderTopRankings(data[1])); + updateOverviewStats(data[0], data[3]); + updateTopRankings(data[1]); + lastLogsJson = JSON.stringify(data[2] || {}); dom.content(nodeLogs, renderLogsTable(data[2])); updateLiveStatusBadge(); diff --git a/luci-app-passwall/Makefile b/luci-app-passwall/Makefile index 0ff01de1..51ad0225 100644 --- a/luci-app-passwall/Makefile +++ b/luci-app-passwall/Makefile @@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall PKG_VERSION:=26.8.26 -PKG_RELEASE:=254 +PKG_RELEASE:=255 PKG_PO_VERSION:=$(PKG_VERSION) PKG_CONFIG_DEPENDS:= \ diff --git a/luci-app-passwall/luasrc/passwall/util_sing-box.lua b/luci-app-passwall/luasrc/passwall/util_sing-box.lua index ad55a1da..9cecc8b4 100644 --- a/luci-app-passwall/luasrc/passwall/util_sing-box.lua +++ b/luci-app-passwall/luasrc/passwall/util_sing-box.lua @@ -1810,18 +1810,7 @@ function gen_config(var) end end - table.insert(route.rules, { - action = "route", - ip_is_private = true, - outbound = "direct" - }) - if COMMON.default_outbound_tag then - table.insert(route.rules, { - action = "route", - port_range = { "0:65535" }, - outbound = COMMON.default_outbound_tag - }) route.final = COMMON.default_outbound_tag end diff --git a/luci-app-passwall/luasrc/passwall/util_xray.lua b/luci-app-passwall/luasrc/passwall/util_xray.lua index 8162ade4..0bc35e4f 100644 --- a/luci-app-passwall/luasrc/passwall/util_xray.lua +++ b/luci-app-passwall/luasrc/passwall/util_xray.lua @@ -1540,11 +1540,6 @@ function gen_config(var) }) end - table.insert(rules, { - outboundTag = "direct", - ip = { "geoip:private" } - }) - if default_outboundTag then local rule = { _flag = "default", diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile index ddcbfd8c..a4c2eb4b 100644 --- a/luci-app-passwall2/Makefile +++ b/luci-app-passwall2/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall2 PKG_VERSION:=26.8.27 -PKG_RELEASE:=106 +PKG_RELEASE:=107 PKG_PO_VERSION:=$(PKG_VERSION) PKG_CONFIG_DEPENDS:= \ diff --git a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua index 7edf3221..d1a32027 100644 --- a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua +++ b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua @@ -1797,11 +1797,6 @@ function gen_config(var) end if COMMON.default_outbound_tag then - table.insert(route.rules, { - action = "route", - port_range = { "0:65535" }, - outbound = COMMON.default_outbound_tag - }) route.final = COMMON.default_outbound_tag end diff --git a/luci-theme-footstrap/Makefile b/luci-theme-footstrap/Makefile index cdfeabce..81b895ec 100644 --- a/luci-theme-footstrap/Makefile +++ b/luci-theme-footstrap/Makefile @@ -17,7 +17,7 @@ LUCI_NAME:=luci-theme-footstrap FOOTSTRAP_VERSION?= ifneq ($(FOOTSTRAP_VERSION),) PKG_VERSION:=$(FOOTSTRAP_VERSION) -PKG_RELEASE:=48 +PKG_RELEASE:=49 endif LUCI_TITLE:=Footstrap Theme diff --git a/luci-theme-footstrap/htdocs/luci-static/resources/fs-fit.js b/luci-theme-footstrap/htdocs/luci-static/resources/fs-fit.js index 9c8a7f87..8910b20e 100644 --- a/luci-theme-footstrap/htdocs/luci-static/resources/fs-fit.js +++ b/luci-theme-footstrap/htdocs/luci-static/resources/fs-fit.js @@ -130,6 +130,18 @@ function naturalHeight(el) { const last = el.lastElementChild; if (!last) return 0; const box = el.getBoundingClientRect(); + /* A BOX WITH NO HEIGHT OF ITS OWN HOLDS NOTHING UP, and asking its content how tall it would be + * gets an answer about a page that is not on screen. `visibility: hidden` leaves children in the + * layout with rects of their own, so an inactive tab pane — collapsed to height 0 by + * theme/30-tables.css — measures its full content here and the floor then pins that collapse + * open: on Network -> Interfaces the hidden `device` pane held 893px, and the active pane's + * content sat that much further down the page (issue #41, reported against a third-party page + * and reproduced on the stock one). The clear-and-remeasure shape this replaced read the + * collapsed height and wrote nothing, which is the behaviour restored here. + * + * A container a tick has just emptied also measures 0, and that is the same answer for the same + * reason: the floor it already wears is what holds it up, and holdFloor() leaves it alone. */ + if (!box.height) return 0; const end = last.getBoundingClientRect(); if (!end.height && !end.width) return 0; /* a last child out of the flow says nothing */ const cs = window.getComputedStyle(el); diff --git a/mise/Makefile b/mise/Makefile index 226031c4..b14a38a3 100644 --- a/mise/Makefile +++ b/mise/Makefile @@ -14,8 +14,8 @@ MISE_HASH_x86_64:=3832f39c325e343f81fe3d92b2447c5d1a5eea1bc85092bb7b6c2580622264 MISE_HASH_aarch64:=06186cfbfe947049b21d58575fb0ea800cc26ed1375f20f4b678cb3a9d679437 PKG_NAME:=mise -PKG_VERSION:=2026.8.15 -PKG_RELEASE:=2 +PKG_VERSION:=2026.8.16 +PKG_RELEASE:=3 PKG_SOURCE:=$(PKG_NAME)-v$(PKG_VERSION)-linux-$(MISE_ARCH)-musl.tar.gz PKG_SOURCE_URL:=https://github.com/jdx/mise/releases/download/v$(PKG_VERSION)/ PKG_HASH:=skip diff --git a/mosdns/Makefile b/mosdns/Makefile index bab8a1b7..84ad6296 100644 --- a/mosdns/Makefile +++ b/mosdns/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=mosdns PKG_VERSION:=5.3.4 -PKG_RELEASE:=19 +PKG_RELEASE:=20 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/IrineSistiana/mosdns/tar.gz/v$(PKG_VERSION)? diff --git a/mosdns/patches/100-mosdns-update-dependencies.patch b/mosdns/patches/100-mosdns-update-dependencies.patch index 15fbbdee..449ba9f1 100644 --- a/mosdns/patches/100-mosdns-update-dependencies.patch +++ b/mosdns/patches/100-mosdns-update-dependencies.patch @@ -1,16 +1,16 @@ -From 201408484de552cb927f6a50b76a34d65c8ac8c6 Mon Sep 17 00:00:00 2001 +From 4b76512586cd404855b717d6b8ac3b4d12ef7a54 Mon Sep 17 00:00:00 2001 From: sbwml Date: Fri, 20 Mar 2026 10:39:22 +0800 Subject: [PATCH] mosdns: update dependencies Signed-off-by: sbwml --- - .github/workflows/release.yml | 2 +- - .github/workflows/test.yml | 2 +- - go.mod | 36 +++++++++---------- - go.sum | 68 +++++++++++++++++------------------ - pkg/query_context/context.go | 1 - - 5 files changed, 54 insertions(+), 55 deletions(-) + .github/workflows/release.yml | 2 +- + .github/workflows/test.yml | 2 +- + go.mod | 60 ++++++++--------- + go.sum | 121 ++++++++++++++++------------------ + pkg/query_context/context.go | 1 - + 5 files changed, 85 insertions(+), 101 deletions(-) --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,107 +36,132 @@ Signed-off-by: sbwml --- a/go.mod +++ b/go.mod -@@ -1,30 +1,30 @@ +@@ -1,31 +1,31 @@ module github.com/IrineSistiana/mosdns/v5 -go 1.24.9 -+go 1.25.0 ++go 1.26.0 require ( github.com/IrineSistiana/go-bytes-pool v0.0.0-20230918115058-c72bd9761c57 - github.com/go-chi/chi/v5 v5.2.3 - github.com/go-viper/mapstructure/v2 v2.4.0 -+ github.com/go-chi/chi/v5 v5.2.5 ++ github.com/go-chi/chi/v5 v5.3.2 + github.com/go-viper/mapstructure/v2 v2.5.0 github.com/google/nftables v0.3.0 - github.com/kardianos/service v1.2.4 +- github.com/kardianos/service v1.2.4 - github.com/klauspost/compress v1.18.2 - github.com/miekg/dns v1.1.70 -+ github.com/klauspost/compress v1.18.4 -+ github.com/miekg/dns v1.1.72 ++ github.com/kardianos/service v1.3.0 ++ github.com/klauspost/compress v1.19.2 ++ github.com/miekg/dns v1.1.73 github.com/mitchellh/mapstructure v1.5.0 github.com/nadoo/ipset v0.5.0 - github.com/prometheus/client_golang v1.23.2 +- github.com/prometheus/client_golang v1.23.2 - github.com/quic-go/quic-go v0.58.1 -+ github.com/quic-go/quic-go v0.59.0 ++ github.com/prometheus/client_golang v1.24.1 ++ github.com/quic-go/quic-go v0.62.0 github.com/spf13/cobra v1.10.2 github.com/spf13/viper v1.21.0 - github.com/stretchr/testify v1.11.1 +- github.com/stretchr/testify v1.11.1 ++ github.com/stretchr/testify v1.12.1 github.com/vishvananda/netlink v1.3.0 - go.uber.org/zap v1.27.1 - go4.org/netipx v0.0.0-20231129151722-fdeea329fbba +- go.uber.org/zap v1.27.1 +- go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 - golang.org/x/net v0.48.0 - golang.org/x/sync v0.19.0 - golang.org/x/sys v0.40.0 - golang.org/x/time v0.14.0 -+ golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 -+ golang.org/x/net v0.52.0 -+ golang.org/x/sync v0.20.0 -+ golang.org/x/sys v0.42.0 +- google.golang.org/protobuf v1.36.11 ++ go.uber.org/zap v1.28.0 ++ go4.org/netipx v0.0.0-20260823151212-3075585bcbeb ++ golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa ++ golang.org/x/net v0.58.0 ++ golang.org/x/sync v0.22.0 ++ golang.org/x/sys v0.47.0 + golang.org/x/time v0.15.0 - google.golang.org/protobuf v1.36.11 ++ google.golang.org/protobuf v1.36.12 ) -@@ -37,14 +37,14 @@ require ( - github.com/fsnotify/fsnotify v1.9.0 // indirect + replace github.com/nadoo/ipset v0.5.0 => github.com/IrineSistiana/ipset v0.5.1-0.20220703061533-6e0fc3b04c0a +@@ -33,18 +33,16 @@ replace github.com/nadoo/ipset v0.5.0 => + require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect +- github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect +- github.com/fsnotify/fsnotify v1.9.0 // indirect ++ github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/mdlayher/netlink v1.8.0 // indirect -+ github.com/mdlayher/netlink v1.9.0 // indirect - github.com/mdlayher/socket v0.5.1 // indirect +- github.com/mdlayher/socket v0.5.1 // indirect ++ github.com/mdlayher/netlink v1.11.2 // indirect ++ github.com/mdlayher/socket v0.6.1 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.6.2 // indirect - github.com/prometheus/common v0.67.5 // indirect +- github.com/pelletier/go-toml/v2 v2.2.4 // indirect +- github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect +- github.com/prometheus/client_model v0.6.2 // indirect +- github.com/prometheus/common v0.67.5 // indirect - github.com/prometheus/procfs v0.19.2 // indirect -+ github.com/prometheus/procfs v0.20.1 // indirect ++ github.com/pelletier/go-toml/v2 v2.4.3 // indirect ++ github.com/prometheus/client_model v0.6.3 // indirect ++ github.com/prometheus/common v0.71.0 // indirect ++ github.com/prometheus/procfs v0.22.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect -@@ -55,11 +55,11 @@ require ( +@@ -55,11 +53,7 @@ require ( github.com/vishvananda/netns v0.0.4 // indirect go.uber.org/mock v0.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect -+ go.yaml.in/yaml/v2 v2.4.4 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect +- go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.46.0 // indirect - golang.org/x/mod v0.32.0 // indirect - golang.org/x/text v0.33.0 // indirect - golang.org/x/tools v0.40.0 // indirect -+ golang.org/x/crypto v0.49.0 // indirect -+ golang.org/x/mod v0.34.0 // indirect -+ golang.org/x/text v0.35.0 // indirect -+ golang.org/x/tools v0.43.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect +- gopkg.in/yaml.v3 v3.0.1 // indirect ++ go.yaml.in/yaml/v3 v3.0.5 // indirect ++ golang.org/x/crypto v0.55.0 // indirect ++ golang.org/x/text v0.41.0 // indirect ) --- a/go.sum +++ b/go.sum -@@ -13,10 +13,10 @@ github.com/frankban/quicktest v1.14.6 h1 +@@ -7,58 +7,56 @@ github.com/beorn7/perks v1.0.1/go.mod h1 + github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= + github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= + github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +-github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +-github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= + github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= - github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= - github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +-github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +-github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE= -github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -+github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug= -+github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0= ++github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= ++github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= ++github.com/go-chi/chi/v5 v5.3.2 h1:5YQkICvTCSZ25hoRsyJazN0scjzKGiu4VAUc7H1o1nY= ++github.com/go-chi/chi/v5 v5.3.2/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/nftables v0.3.0 h1:bkyZ0cbpVeMHXOrtlFc8ISmfVqq5gPJukoYieyVmITg= -@@ -25,20 +25,20 @@ github.com/inconshreveable/mousetrap v1. + github.com/google/nftables v0.3.0/go.mod h1:BCp9FsrbF1Fn/Yu6CLUc9GGZFw/+hsxfluNXXmxBfRM= + github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= - github.com/kardianos/service v1.2.4 h1:XNlGtZOYNx2u91urOdg/Kfmc+gfmuIo1Dd3rEi2OgBk= - github.com/kardianos/service v1.2.4/go.mod h1:E4V9ufUuY82F7Ztlu1eN9VXWIQxg8NoLQlmFe0MtrXc= +-github.com/kardianos/service v1.2.4 h1:XNlGtZOYNx2u91urOdg/Kfmc+gfmuIo1Dd3rEi2OgBk= +-github.com/kardianos/service v1.2.4/go.mod h1:E4V9ufUuY82F7Ztlu1eN9VXWIQxg8NoLQlmFe0MtrXc= -github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= -github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= -+github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= -+github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= ++github.com/kardianos/service v1.3.0 h1:/LGy+xPP2TM+GLTiCZ2di7cy0Jd/qrawlTUfqKYFdTI= ++github.com/kardianos/service v1.3.0/go.mod h1:E4V9ufUuY82F7Ztlu1eN9VXWIQxg8NoLQlmFe0MtrXc= ++github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8= ++github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -145,46 +170,80 @@ Signed-off-by: sbwml github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mdlayher/netlink v1.8.0 h1:e7XNIYJKD7hUct3Px04RuIGJbBxy1/c4nX7D5YyvvlM= -github.com/mdlayher/netlink v1.8.0/go.mod h1:UhgKXUlDQhzb09DrCl2GuRNEglHmhYoWAHid9HK3594= -+github.com/mdlayher/netlink v1.9.0 h1:G8+GLq2x3v4D4MVIqDdNUhTUC7TKiCy/6MDkmItfKco= -+github.com/mdlayher/netlink v1.9.0/go.mod h1:YBnl5BXsCoRuwBjKKlZ+aYmEoq0r12FDA/3JC+94KDg= - github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= - github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= +-github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= +-github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= -github.com/miekg/dns v1.1.70 h1:DZ4u2AV35VJxdD9Fo9fIWm119BsQL5cZU1cQ9s0LkqA= -github.com/miekg/dns v1.1.70/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= -+github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= -+github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= ++github.com/mdlayher/netlink v1.11.2 h1:HKh2jqe+omdSWcQ88nrT7INE61B0NXfiSPFdgL4YbNI= ++github.com/mdlayher/netlink v1.11.2/go.mod h1:uT2Yc/QLaZubzDpZIBi9d4GoeLwtp3x1AMeqSRrK2sA= ++github.com/mdlayher/socket v0.6.1 h1:M7uj2NtuujUY4mYr1C57NmfNiRHbkKpnBxO856lsc3A= ++github.com/mdlayher/socket v0.6.1/go.mod h1:+/SGtqc9V+5dAuRgQsU0fGBI+oRDiW7O2Obx10OIWfg= ++github.com/miekg/dns v1.1.73 h1:uhT8nJxmTrPJYClxVxTCX+CVn6qnzSiybRk72Z6DgrE= ++github.com/miekg/dns v1.1.73/go.mod h1:RW2Obtfd5NZHvOFe3zYG0W8koWOQtAzyHaLo8vASBuQ= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -@@ -53,12 +53,12 @@ github.com/prometheus/client_model v0.6. - github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= - github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= - github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +-github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +-github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +-github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +-github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +-github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +-github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +-github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +-github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +-github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +-github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= -github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= -github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= -+github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= -+github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= ++github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= ++github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= ++github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= ++github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= ++github.com/prometheus/client_model v0.6.3 h1:O0jaTVAYNxTHYInEPFJt5I3+sN8zqBtVMPTB1qyxiEo= ++github.com/prometheus/client_model v0.6.3/go.mod h1:gpN5P9S7Rr6Yr92PiQ+Ixvhf6JZEkF1dnxsYL2aPBEM= ++github.com/prometheus/common v0.71.0 h1:9KDAKb7Mj3HEVKyFCK6Dc/HIwlBzZIN2l7/lrHl3KK8= ++github.com/prometheus/common v0.71.0/go.mod h1:CLJ5H8TEsGX8bl31BdMkfhIZ+QmZ9tBPPotUxUbfcmk= ++github.com/prometheus/procfs v0.22.0 h1:6q9+/JL9IKAPbCmBrv9n5O5Ty3NKnciV5X7YGw0oics= ++github.com/prometheus/procfs v0.22.0/go.mod h1:CvmFr/GVhIjIvWJZW3tgkODBQMRIf0EyWMQLHCHab58= ++github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0= ++github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= -github.com/quic-go/quic-go v0.58.1 h1:J0s55TVauDbnCEY5tU2B8e7nYb3gnKMSez5Fwi1dl2s= -github.com/quic-go/quic-go v0.58.1/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= -+github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= -+github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= ++github.com/quic-go/quic-go v0.62.0 h1:ZHDjCk5OacATwGvs8PWE97CTvX7AqZiVoW7++ZOXTf8= ++github.com/quic-go/quic-go v0.62.0/go.mod h1:RAro2j2yN9a9EiPACLHT9IB2NXCvGQmmo/alT0yYI0w= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -@@ -91,32 +91,32 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSx +@@ -75,8 +73,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2K + github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= + github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= + github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +-github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +-github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= ++github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= ++github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg= + github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= + github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= + github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk= +@@ -89,38 +87,31 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5E + go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= + go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= - go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= - go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +-go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +-go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +-go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= ++go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= ++go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= - go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= - go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= - go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= +-go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= +-go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= -golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= -golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0= @@ -195,16 +254,18 @@ Signed-off-by: sbwml -golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -+golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -+golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -+golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= -+golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= -+golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -+golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= -+golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -+golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= -+golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -+golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= ++go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= ++go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= ++go4.org/netipx v0.0.0-20260823151212-3075585bcbeb h1:XBM4hvfwGAttkkiTIFfeigdfcL1xIfdKXqFdgiHGtDs= ++go4.org/netipx v0.0.0-20260823151212-3075585bcbeb/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= ++golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M= ++golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis= ++golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa h1:QSyA8ishJCyT21kER9KwNt0b7BM3iRK4x9QXhjN5Fdk= ++golang.org/x/exp v0.0.0-20260824195058-e88cd73687aa/go.mod h1:zeBbvyFKDaLwa7CH/zI8KXt7gTl14SF7sO08Pl5jBCM= ++golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To= ++golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU= ++golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= ++golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= @@ -215,17 +276,21 @@ Signed-off-by: sbwml -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= -golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= -+golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -+golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -+golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -+golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +-google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +-google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= ++golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= ++golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= ++golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8= ++golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= -+golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -+golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= - google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= - google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= ++google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= ++google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= --- a/pkg/query_context/context.go +++ b/pkg/query_context/context.go @@ -108,7 +108,6 @@ func (ctx *Context) QQuestion() dns.Ques diff --git a/mosdns/patches/222-feat-stats_api-exclude-blocked-queries-from-cache-hi.patch b/mosdns/patches/222-feat-stats_api-exclude-blocked-queries-from-cache-hi.patch new file mode 100644 index 00000000..9d115391 --- /dev/null +++ b/mosdns/patches/222-feat-stats_api-exclude-blocked-queries-from-cache-hi.patch @@ -0,0 +1,74 @@ +From 68799cf43340bf2e9e0b7145ea961d69ca45d6fe Mon Sep 17 00:00:00 2001 +From: sbwml +Date: Mon, 31 Aug 2026 22:49:01 +0800 +Subject: [PATCH] feat(stats_api): exclude blocked queries from cache hit + percentage + +Calculate cache hit rate based on unblocked queries (total - blocked) +instead of total queries to prevent ad blocking from skewing the stats. + +Signed-off-by: sbwml +--- + plugin/executable/stats_api/stats_api.go | 7 +++- + plugin/executable/stats_api/stats_api_test.go | 35 +++++++++++++++++++ + 2 files changed, 41 insertions(+), 1 deletion(-) + +--- a/plugin/executable/stats_api/stats_api.go ++++ b/plugin/executable/stats_api/stats_api.go +@@ -629,9 +629,14 @@ func (s *StatsAPI) handleStats(w http.Re + var blockedPct, cachedPct, avgLat float64 + if total > 0 { + blockedPct = float64(blocked) / float64(total) * 100.0 +- cachedPct = float64(cached) / float64(total) * 100.0 + avgLat = (float64(latUs) / float64(total)) / 1000.0 + } ++ if total > blocked { ++ cachedPct = float64(cached) / float64(total-blocked) * 100.0 ++ if cachedPct > 100.0 { ++ cachedPct = 100.0 ++ } ++ } + + blockedPct = math.Round(blockedPct*100) / 100 + cachedPct = math.Round(cachedPct*100) / 100 +--- a/plugin/executable/stats_api/stats_api_test.go ++++ b/plugin/executable/stats_api/stats_api_test.go +@@ -511,3 +511,38 @@ func TestStatsAPINonExistentDumpFile(t * + t.Errorf("expected 0 total queries, got %d", s.totalQueries.Load()) + } + } ++ ++func TestStatsAPICachedPercentageExcludesBlocked(t *testing.T) { ++ s := NewStatsAPI(&Args{Capacity: 100}, zap.NewNop()) ++ router := s.Router() ++ ++ // 100 total queries: 50 blocked, 25 cached, 25 forwarded to upstream ++ s.totalQueries.Store(100) ++ s.blockedQueries.Store(50) ++ s.cachedQueries.Store(25) ++ s.totalLatencyUs.Store(50000) ++ ++ req := httptest.NewRequest(http.MethodGet, "/api/v1/stats", nil) ++ w := httptest.NewRecorder() ++ router.ServeHTTP(w, req) ++ ++ if w.Code != http.StatusOK { ++ t.Fatalf("expected HTTP 200, got %d", w.Code) ++ } ++ ++ var statsResp map[string]any ++ if err := json.Unmarshal(w.Body.Bytes(), &statsResp); err != nil { ++ t.Fatalf("failed to unmarshal stats response: %v", err) ++ } ++ ++ // blocked_percentage = 50 / 100 = 50% ++ if blockedPct := statsResp["blocked_percentage"].(float64); blockedPct != 50.0 { ++ t.Errorf("expected blocked_percentage 50.0, got %v", blockedPct) ++ } ++ ++ // cached_percentage = 25 / (100 - 50) = 50% (previously 25%) ++ if cachedPct := statsResp["cached_percentage"].(float64); cachedPct != 50.0 { ++ t.Errorf("expected cached_percentage 50.0, got %v", cachedPct) ++ } ++} ++