update 2026-08-30 11:09:43

This commit is contained in:
action committed 2026-08-30 11:09:43 +08:00
1 parent b09780b109
commit a8ecc43b8a
29 files changed
+1484 -49

No files matched your search

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 640 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 550 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 547 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 659 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 650 KiB

+3 -3
View File
@@ -9,9 +9,9 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://gn.googlesource.com/gn.git PKG_SOURCE_URL:=https://gn.googlesource.com/gn.git
PKG_SOURCE_DATE:=2026-08-19 PKG_SOURCE_DATE:=2026-08-28
PKG_SOURCE_VERSION:=58933a7cdbc90f70f2381f0c72e76d29be1d43a9 PKG_SOURCE_VERSION:=c1b663788ed1ecf2ffd53781c98ae2291ee002cf
PKG_MIRROR_HASH:=b4fd70abf2fe8e3017ac3c4e025fd25cd4364dbe2470035b3daf58a129f9f54f PKG_MIRROR_HASH:=aa635f1ce4b0cf2b7776dc44ad28671eb0fb79276097addca503ce4708ccdc5d
PKG_LICENSE:=BSD 3-Clause PKG_LICENSE:=BSD 3-Clause
PKG_LICENSE_FILES:=LICENSE PKG_LICENSE_FILES:=LICENSE
+2 -2
View File
@@ -3,7 +3,7 @@
#ifndef OUT_LAST_COMMIT_POSITION_H_ #ifndef OUT_LAST_COMMIT_POSITION_H_
#define OUT_LAST_COMMIT_POSITION_H_ #define OUT_LAST_COMMIT_POSITION_H_
#define LAST_COMMIT_POSITION_NUM 2528 #define LAST_COMMIT_POSITION_NUM 2539
#define LAST_COMMIT_POSITION "2528 (58933a7cdbc9)" #define LAST_COMMIT_POSITION "2539 (c1b663788ed1)"
#endif // OUT_LAST_COMMIT_POSITION_H_ #endif // OUT_LAST_COMMIT_POSITION_H_
+3 -2
View File
@@ -1,12 +1,12 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-mosdns PKG_NAME:=luci-app-mosdns
PKG_VERSION:=1.7.9 PKG_VERSION:=1.7.10
PKG_RELEASE:=1 PKG_RELEASE:=1
LUCI_TITLE:=LuCI Support for mosdns LUCI_TITLE:=LuCI Support for mosdns
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
LUCI_DEPENDS:=+mosdns +curl +v2ray-geoip +v2ray-geosite +geo2txt +ucode LUCI_DEPENDS:=+mosdns +uclient-fetch +v2ray-geoip +v2ray-geosite +geo2txt +ucode
PKG_MAINTAINER:=sbwml <admin@cooluc.com> PKG_MAINTAINER:=sbwml <admin@cooluc.com>
@@ -15,6 +15,7 @@ define Package/$(PKG_NAME)/conffiles
/etc/mosdns/cache.dump /etc/mosdns/cache.dump
/etc/mosdns/config_custom.yaml /etc/mosdns/config_custom.yaml
/etc/mosdns/rule /etc/mosdns/rule
/etc/mosdns/stats.dump
endef endef
include $(TOPDIR)/feeds/luci/luci.mk include $(TOPDIR)/feeds/luci/luci.mk
@@ -163,7 +163,7 @@ return view.extend({
o.value('info', _('Info')); o.value('info', _('Info'));
o.value('warn', _('Warning')); o.value('warn', _('Warning'));
o.value('error', _('Error')); o.value('error', _('Error'));
o.default = 'info'; o.default = 'error';
o.depends('configfile', '/var/etc/mosdns.json'); o.depends('configfile', '/var/etc/mosdns.json');
o = s.taboption('basic', form.Value, 'log_file', _('Log File')); o = s.taboption('basic', form.Value, 'log_file', _('Log File'));
@@ -426,6 +426,29 @@ return view.extend({
o.default = 52001; o.default = 52001;
o.depends('configfile', '/var/etc/mosdns.json'); o.depends('configfile', '/var/etc/mosdns.json');
o = s.taboption('api', form.Flag, 'stats_collector', _('Enable Stats Collector'));
o.rmempty = false;
o.default = o.enabled;
o.depends('configfile', '/var/etc/mosdns.json');
o = s.taboption('api', form.Value, 'stats_capacity', _('Ring Buffer Capacity'),
_('Query log ring buffer capacity (FIFO overwrite, default 2000, larger values consume more memory)'));
o.datatype = 'and(uinteger,min(1))';
o.default = 2000;
o.depends('stats_collector', '1');
o = s.taboption('api', form.Flag, 'stats_dump_file', _('Stats Dump'),
_('Save query statistics and logs locally and reload on next startup.'));
o.rmempty = false;
o.default = false;
o.depends('stats_collector', '1');
o = s.taboption('api', form.Value, 'stats_dump_interval',
_('Auto Save Stats Interval'));
o.datatype = 'and(uinteger,min(1))';
o.default = 600;
o.depends('stats_dump_file', '1');
o = s.taboption('api', form.Button, '_flush_cache', null, o = s.taboption('api', form.Button, '_flush_cache', null,
_('Flushing DNS Cache will clear any IP addresses or DNS records from MosDNS cache.')); _('Flushing DNS Cache will clear any IP addresses or DNS records from MosDNS cache.'));
o.title = '&#160;'; o.title = '&#160;';
@@ -0,0 +1,722 @@
'use strict';
'require dom';
'require poll';
'require rpc';
'require ui';
'require view';
const callGetStats = rpc.declare({
object: 'luci.mosdns',
method: 'get_stats',
expect: { '': {} }
});
const callGetHistory = rpc.declare({
object: 'luci.mosdns',
method: 'get_history',
params: ['points'],
expect: { '': {} }
});
const callGetTop = rpc.declare({
object: 'luci.mosdns',
method: 'get_top',
params: ['limit'],
expect: { '': {} }
});
const callGetLogs = rpc.declare({
object: 'luci.mosdns',
method: 'get_logs',
params: ['limit', 'offset', 'search', 'filter'],
expect: { '': {} }
});
const callClearQueryLogs = rpc.declare({
object: 'luci.mosdns',
method: 'clear_query_logs',
expect: { '': {} }
});
let filterVal = 'all';
let searchVal = '';
let pageIdx = 0;
const PAGE_SIZE = 20;
let isUserPaused = false;
let nodeStats;
let nodeTop;
let nodeLogs;
let autoStatusBadge;
const cleanIP = ip => {
if (!ip) return '-';
return ip.replace(/^::ffff:/i, '');
};
const injectStyles = () => {
if (document.getElementById('mosdns-statistics-styles'))
return;
/* HTML Styles provided by DeepSeek Chat */
const css = [
'.mosdns-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; margin-bottom: 1.25rem; }',
'.mosdns-rankings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; margin-bottom: 1.5rem; }',
'.mosdns-stat-card { background: var(--cbi-section-bg, #fff); border: 1px solid rgba(0,0,0,0.08); border-radius: 8px; padding: 1rem 1.2rem; box-shadow: 0 2px 6px rgba(0,0,0,0.03); display: flex; flex-direction: column; justify-content: space-between; position: relative; overflow: hidden; }',
'.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 { 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; }',
'.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; }',
'.mosdns-rank-bar { position: absolute; left: 0; top: 0; bottom: 0; opacity: 0.15; pointer-events: none; transition: width .3s ease; }',
'.mosdns-badge { display: inline-block; padding: 0.15em 0.55em; font-size: 0.75rem; font-weight: 600; border-radius: 4px; line-height: 1.25; text-align: center; white-space: nowrap; box-sizing: border-box; }',
'.mosdns-status-badge { min-width: 68px; }',
'.badge-danger { background: rgba(239, 68, 68, 0.12); color: #dc2626; border: 1px solid rgba(239, 68, 68, 0.25); }',
'.badge-teal { background: rgba(16, 185, 129, 0.12); color: #059669; border: 1px solid rgba(16, 185, 129, 0.25); }',
'.badge-primary { background: rgba(59, 130, 246, 0.12); color: #2563eb; border: 1px solid rgba(59, 130, 246, 0.25); }',
'.badge-neutral { background: rgba(107, 114, 128, 0.12); color: #4b5563; border: 1px solid rgba(107, 114, 128, 0.25); }',
'.badge-qtype { font-family: monospace; font-size: 0.72rem; padding: 0.1em 0.4em; background: rgba(125,125,125,0.1); border-radius: 3px; opacity: 0.8; margin-left: 0.4rem; }',
'.badge-pulse { animation: pulse 2s infinite; }',
'.dns-latency-fastest { color: #10b981; font-weight: 600; }',
'.dns-latency-fast { color: #059669; font-weight: 600; }',
'.dns-latency-normal { color: #3b82f6; font-weight: 600; }',
'.dns-latency-slow { color: #d97706; font-weight: 600; }',
'.dns-latency-slower { color: #ea580c; font-weight: 600; }',
'.dns-latency-timeout { color: #dc2626; font-weight: 600; }',
'.mosdns-table td { vertical-align: middle !important; padding: 0.45rem 0.6rem !important; }',
'.mosdns-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }',
'.mosdns-modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.85rem; padding-bottom: 0.75rem; border-bottom: 1px solid rgba(125,125,125,0.15); flex-wrap: wrap; gap: 0.5rem; }',
'.mosdns-modal-domain { font-size: 1.05rem; font-weight: 700; word-break: break-all; display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem; }',
'.mosdns-modal-meta-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 0.6rem; margin-bottom: 1rem; }',
'.mosdns-modal-meta-item { background: rgba(125,125,125,0.04); border: 1px solid rgba(125,125,125,0.08); border-radius: 6px; padding: 0.5rem 0.75rem; }',
'.mosdns-modal-meta-item .meta-label { font-size: 0.75rem; opacity: 0.6; margin-bottom: 0.2rem; font-weight: 600; }',
'.mosdns-modal-meta-item .meta-val { font-size: 0.85rem; font-weight: 600; }',
'.mosdns-modal-section-title { font-size: 0.9rem; font-weight: 700; margin: 0.85rem 0 0.45rem 0; display: flex; align-items: center; justify-content: space-between; }',
'.mosdns-answers-list { display: flex; flex-direction: column; gap: 0.35rem; max-height: 240px; overflow-y: auto; }',
'.mosdns-answer-row { display: flex; justify-content: space-between; align-items: center; background: rgba(125,125,125,0.04); border: 1px solid rgba(125,125,125,0.08); border-radius: 6px; padding: 0.4rem 0.65rem; gap: 0.5rem; font-size: 0.82rem; }',
'.mosdns-answer-data { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }',
'.mosdns-answer-ttl { font-size: 0.75rem; opacity: 0.65; white-space: nowrap; }',
'@keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }',
'@media (prefers-color-scheme: dark) {',
' .mosdns-stat-card, .mosdns-rank-panel, .mosdns-modal-meta-item, .mosdns-answer-row { background: rgba(255,255,255,0.03); border-color: rgba(255,255,255,0.08); box-shadow: none; }',
' .mosdns-sparkline-tooltip { background: #1e242b; border-color: rgba(255,255,255,0.15); box-shadow: 0 4px 12px rgba(0,0,0,0.5); }',
' .badge-danger { background: rgba(239, 68, 68, 0.2); color: #f87171; border-color: rgba(239, 68, 68, 0.35); }',
' .badge-teal { background: rgba(16, 185, 129, 0.2); color: #34d399; border-color: rgba(16, 185, 129, 0.35); }',
' .badge-primary { background: rgba(59, 130, 246, 0.2); color: #60a5fa; border-color: rgba(59, 130, 246, 0.35); }',
' .badge-neutral { background: rgba(156, 163, 175, 0.2); color: #9ca3af; border-color: rgba(156, 163, 175, 0.3); }',
' .dns-latency-fastest { color: #34d399; }',
' .dns-latency-normal { color: #60a5fa; }',
' .dns-latency-timeout { color: #f87171; }',
'}'
].join('\n');
document.head.appendChild(E('style', { id: 'mosdns-statistics-styles' }, css));
};
const debounce = (fn, delay = 300) => {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
};
const formatTimestamp = iso => {
if (!iso) return '-';
const d = new Date(iso);
return isNaN(d) ? iso : d.toTimeString().slice(0, 8);
};
const getLatencyClass = elapsedMs => {
const val = parseFloat(elapsedMs) || 0;
if (val < 5) return 'dns-latency-fastest';
if (val < 20) return 'dns-latency-fast';
if (val < 50) return 'dns-latency-normal';
if (val < 100) return 'dns-latency-slow';
if (val < 300) return 'dns-latency-slower';
return 'dns-latency-timeout';
};
const updateLiveStatusBadge = () => {
if (!autoStatusBadge) return;
if (pageIdx === 0 && !searchVal && !isUserPaused) {
dom.content(autoStatusBadge, [
E('span', { class: 'mosdns-badge badge-teal badge-pulse' }, _('● Live Auto-refresh'))
]);
} else {
dom.content(autoStatusBadge, [
E('span', { class: 'mosdns-badge badge-neutral' }, _('❚❚ Paused (Page %d)').format(pageIdx + 1))
]);
}
};
const createSparklineSVG = (dataItems, strokeColor, fillGradId, maxScale) => {
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';
const svgContainer = E('div', { class: 'mosdns-sparkline-wrap' });
svgContainer.innerHTML =
'<svg viewBox="0 0 ' + width + ' ' + height + '" class="mosdns-sparkline" preserveAspectRatio="none">' +
'<defs>' +
'<linearGradient id="' + fillGradId + '" x1="0" y1="0" x2="0" y2="1">' +
'<stop offset="0%" stop-color="' + strokeColor + '" stop-opacity="0.30" />' +
'<stop offset="100%" stop-color="' + strokeColor + '" stop-opacity="0.02" />' +
'</linearGradient>' +
'</defs>' +
'<path d="' + areaD + '" fill="url(#' + fillGradId + ')" />' +
'<path d="' + pathD + '" fill="none" stroke="' + strokeColor + '" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />' +
'<g class="spark-hover-group" style="display: none;">' +
'<line class="spark-hover-line" x1="0" y1="' + padTop + '" x2="0" y2="' + (height - padBottom) + '" stroke="' + strokeColor + '" stroke-width="1.2" stroke-dasharray="2 2" opacity="0.6" />' +
'<circle class="spark-hover-dot" cx="0" cy="0" r="3.5" fill="' + strokeColor + '" stroke="#fff" stroke-width="1.5" />' +
'</g>' +
'<rect width="' + width + '" height="' + height + '" fill="transparent" class="spark-hover-hitbox" style="cursor: crosshair;" />' +
'</svg>' +
'<div class="mosdns-sparkline-tooltip" style="display: none;"></div>';
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 coord = coords[idx];
if (!coord) return;
hoverLine.setAttribute('x1', coord.x);
hoverLine.setAttribute('x2', coord.x);
hoverDot.setAttribute('cx', coord.x);
hoverDot.setAttribute('cy', coord.y);
hoverGroup.style.display = 'block';
let timeStr = '-';
if (coord.time) {
const d = new Date(coord.time);
if (!isNaN(d)) {
const hh = String(d.getHours()).padStart(2, '0');
timeStr = hh + ':00';
} else {
timeStr = coord.time.slice(11, 16) || coord.time;
}
}
tooltip.innerHTML =
'<div style="font-weight: 700; color: ' + strokeColor + '; font-size: 0.82rem; line-height: 1.1;">' + coord.val.toLocaleString() + '</div>' +
'<div style="font-size: 0.72rem; opacity: 0.75; margin-top: 0.15rem;">' + timeStr + '</div>';
tooltip.style.display = 'block';
const tooltipX = (coord.x / width) * rect.width;
if (ratio > 0.65) {
tooltip.style.left = 'auto';
tooltip.style.right = (rect.width - tooltipX + 8) + 'px';
} else {
tooltip.style.left = (tooltipX + 8) + 'px';
tooltip.style.right = 'auto';
}
tooltip.style.top = '-6px';
};
const onLeave = () => {
hoverGroup.style.display = 'none';
tooltip.style.display = 'none';
};
hitbox.addEventListener('mousemove', onMove);
hitbox.addEventListener('mouseleave', onLeave);
return svgContainer;
};
const renderOverviewStats = (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.'));
}
const {
total_queries: total = 0,
blocked_queries: blocked = 0,
cached_queries: cached = 0,
blocked_percentage: blocked_pct = 0,
cached_percentage: cached_pct = 0,
avg_latency_ms: avg_ms = 0
} = stats;
const points = historyData?.points || [];
const totalItems = points.map(p => ({ time: p.time, val: Number(p.total) || 0 }));
const blockedItems = points.map(p => ({ time: p.time, val: Number(p.blocked) || 0 }));
const cachedItems = points.map(p => ({ time: p.time, val: Number(p.cached) || 0 }));
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)
]),
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)
]),
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)
]),
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'))
])
])
]);
};
const renderTopRankings = topData => {
if (!topData || topData.error) return E('div', {});
const { top_blocked = [], top_domains = [], top_clients = [] } = topData;
const renderList = (items, key, color, isClient = false) => {
if (!items || !items.length) {
return E('div', { style: 'padding: 1.5rem; text-align: center; opacity: 0.5; font-size: 0.85rem;' }, _('No data available'));
}
const maxCount = Math.max(...items.map(i => i.count || 1));
return E('div', { style: 'display: flex; flex-direction: column;' },
items.map(item => {
let val = item[key] || '-';
if (isClient) val = cleanIP(val);
const cnt = item.count || 0;
const pct = Math.round((cnt / maxCount) * 100);
return E('div', { class: 'mosdns-rank-item' }, [
E('div', { class: 'mosdns-rank-bar', style: 'width: ' + pct + '%; background-color: ' + color + ';' }),
E('span', {
class: 'mosdns-mono',
style: 'font-size: 0.82rem; z-index: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-right: 0.5rem;',
title: val
}, val),
E('span', { class: 'mosdns-badge badge-neutral mosdns-mono', style: 'z-index: 1;' }, cnt.toLocaleString())
]);
})
);
};
return E('div', { class: 'mosdns-rankings-grid' }, [
E('div', { class: 'mosdns-rank-panel' }, [
E('h4', { style: 'color: #2563eb;' }, [
E('span', {}, _('Top Queried Domains')),
E('span', { class: 'mosdns-badge badge-primary' }, top_domains.length)
]),
renderList(top_domains, 'domain', '#2563eb')
]),
E('div', { class: 'mosdns-rank-panel' }, [
E('h4', { style: 'color: #dc2626;' }, [
E('span', {}, _('Top Blocked Domains')),
E('span', { class: 'mosdns-badge badge-danger' }, top_blocked.length)
]),
renderList(top_blocked, 'domain', '#dc2626')
]),
E('div', { class: 'mosdns-rank-panel' }, [
E('h4', { style: 'color: #059669;' }, [
E('span', {}, _('Top Clients')),
E('span', { class: 'mosdns-badge badge-teal' }, top_clients.length)
]),
renderList(top_clients, 'client_ip', '#059669', true)
])
]);
};
const showLogDetailsModal = item => {
let statusBadge;
if (item.is_blocked) {
statusBadge = E('span', { class: 'mosdns-badge badge-danger mosdns-status-badge' }, 'BLOCKED');
} else if (item.is_cached) {
statusBadge = E('span', { class: 'mosdns-badge badge-teal mosdns-status-badge' }, 'CACHED');
} else if (item.status === 'NOERROR') {
statusBadge = E('span', { class: 'mosdns-badge badge-primary mosdns-status-badge' }, 'NOERROR');
} else {
statusBadge = E('span', { class: 'mosdns-badge badge-neutral mosdns-status-badge' }, item.status || 'NOERROR');
}
const answersCount = (item.answers && item.answers.length) || 0;
let answersContent;
if (answersCount > 0) {
answersContent = E('div', { class: 'mosdns-answers-list' },
item.answers.map(a => E('div', { class: 'mosdns-answer-row' }, [
E('div', { style: 'display: flex; align-items: center; gap: 0.5rem; overflow: hidden; flex: 1;' }, [
E('span', { class: 'badge-qtype', style: 'margin: 0;' }, a.type || 'A'),
E('span', { class: 'mosdns-mono mosdns-answer-data', title: a.data }, a.data)
]),
E('span', { class: 'mosdns-badge badge-neutral mosdns-mono mosdns-answer-ttl' }, 'TTL ' + a.ttl + 's')
]))
);
} else {
answersContent = E('div', {
style: 'text-align: center; padding: 1.25rem; background: rgba(125,125,125,0.03); border: 1px dashed rgba(125,125,125,0.15); border-radius: 6px; opacity: 0.6; font-size: 0.85rem;'
}, _('No DNS answer records returned.'));
}
const body = E('div', { style: 'padding: 0.25rem 0;' }, [
E('div', { class: 'mosdns-modal-header' }, [
E('div', { class: 'mosdns-modal-domain' }, [
E('span', { class: 'mosdns-mono' }, item.domain || '-'),
E('span', { class: 'badge-qtype' }, item.qtype || 'A')
]),
E('div', { style: 'display: flex; align-items: center; gap: 0.5rem;' }, [
statusBadge,
E('span', { class: 'mosdns-mono ' + getLatencyClass(item.elapsed_ms), style: 'font-size: 0.85rem;' }, item.elapsed_ms + ' ms')
])
]),
E('div', { class: 'mosdns-modal-meta-grid' }, [
E('div', { class: 'mosdns-modal-meta-item' }, [
E('div', { class: 'meta-label' }, _('Client IP')),
E('div', { class: 'meta-val mosdns-mono' }, cleanIP(item.client_ip))
]),
E('div', { class: 'mosdns-modal-meta-item' }, [
E('div', { class: 'meta-label' }, _('Time')),
E('div', { class: 'meta-val mosdns-mono' }, formatTimestamp(item.timestamp) + (item.timestamp ? ' (' + item.timestamp.slice(0, 10) + ')' : ''))
]),
E('div', { class: 'mosdns-modal-meta-item' }, [
E('div', { class: 'meta-label' }, _('Upstream')),
E('div', { class: 'meta-val mosdns-mono', style: 'word-break: break-all;' }, item.upstream || '-')
]),
E('div', { class: 'mosdns-modal-meta-item' }, [
E('div', { class: 'meta-label' }, _('Rule Hit')),
E('div', { class: 'meta-val mosdns-mono', style: 'word-break: break-all;' }, item.rule || '-')
])
]),
E('div', { class: 'mosdns-modal-section-title' }, [
E('span', {}, _('Answers')),
E('span', { class: 'mosdns-badge badge-neutral' }, answersCount)
]),
answersContent
]);
ui.showModal(_('Query Log Details'), [
body,
E('div', { class: 'right', style: 'margin-top: 1.25rem;' }, [
E('button', {
class: 'btn cbi-button cbi-button-action',
click: ui.hideModal
}, _('Close'))
])
]);
};
const renderLogsTable = logsData => {
const { total = 0, items = [] } = logsData || {};
const totalPages = Math.ceil(total / PAGE_SIZE) || 1;
const rows = items.map(item => {
let statusBadge;
if (item.is_blocked) {
statusBadge = E('span', { class: 'mosdns-badge badge-danger mosdns-status-badge' }, 'BLOCKED');
} else if (item.is_cached) {
statusBadge = E('span', { class: 'mosdns-badge badge-teal mosdns-status-badge' }, 'CACHED');
} else if (item.status === 'NOERROR') {
statusBadge = E('span', { class: 'mosdns-badge badge-primary mosdns-status-badge' }, 'NOERROR');
} else {
statusBadge = E('span', { class: 'mosdns-badge badge-neutral mosdns-status-badge' }, item.status || 'NOERROR');
}
const answersText = (item.answers && item.answers.length > 0)
? item.answers.map(a => a.data + ' (' + a.type + ')').join(', ')
: '-';
return E('tr', { class: 'tr' }, [
E('td', { class: 'td', style: 'font-size: 0.82rem; opacity: 0.7; white-space: nowrap;' }, formatTimestamp(item.timestamp)),
E('td', { class: 'td mosdns-mono', style: 'font-size: 0.82rem; white-space: nowrap;' }, cleanIP(item.client_ip)),
E('td', { class: 'td', style: 'max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;', title: item.domain || '-' }, [
E('span', { class: 'mosdns-mono', style: 'font-weight: 600;' }, item.domain || '-'),
E('span', { class: 'badge-qtype' }, item.qtype || 'A')
]),
E('td', { class: 'td' }, statusBadge),
E('td', {
class: 'td mosdns-mono',
style: 'font-size: 0.82rem; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer;',
title: _('Click to view full details'),
click: () => showLogDetailsModal(item)
}, answersText),
E('td', { class: 'td mosdns-mono ' + getLatencyClass(item.elapsed_ms), style: 'text-align: right; font-size: 0.82rem;' }, item.elapsed_ms + ' ms')
]);
});
if (!rows.length) {
rows.push(E('tr', { class: 'tr' }, [
E('td', { class: 'td', colspan: 6, style: 'text-align: center; opacity: 0.5; padding: 2rem;' }, _('No query log entries found.'))
]));
}
return E('div', {}, [
E('table', { class: 'table cbi-section-table mosdns-table', style: 'margin-top: 0.25rem;' }, [
E('tr', { class: 'tr table-titles' }, [
E('th', { class: 'th', style: 'width: 85px;' }, _('Time')),
E('th', { class: 'th', style: 'width: 125px;' }, _('Client IP')),
E('th', { class: 'th' }, _('Domain & Record')),
E('th', { class: 'th', style: 'width: 90px;' }, _('Status')),
E('th', { class: 'th' }, _('Answers')),
E('th', { class: 'th', style: 'width: 90px; text-align: right;' }, _('Elapsed'))
]),
...rows
]),
E('div', { style: 'display: flex; justify-content: space-between; align-items: center; margin-top: 0.75rem;' }, [
E('span', { style: 'font-size: 0.85rem; opacity: 0.7;' }, _('Page %d / %d (%d entries)').format(pageIdx + 1, totalPages, total)),
E('div', { style: 'display: flex; gap: 0.5rem;' }, [
E('button', {
class: 'btn cbi-button cbi-button-action',
disabled: pageIdx === 0 ? 'disabled' : null,
click: () => {
if (pageIdx > 0) {
pageIdx--;
updateLiveStatusBadge();
refreshLogs();
}
}
}, _('Previous')),
E('button', {
class: 'btn cbi-button cbi-button-action',
disabled: (pageIdx + 1) >= totalPages ? 'disabled' : null,
click: () => {
if ((pageIdx + 1) < totalPages) {
pageIdx++;
updateLiveStatusBadge();
refreshLogs();
}
}
}, _('Next'))
])
])
]);
};
const pollScheduler = async () => {
try {
const promises = [
callGetStats(),
callGetTop(10),
callGetHistory(24)
];
const shouldRefreshLogs = (pageIdx === 0 && !searchVal && !isUserPaused);
if (shouldRefreshLogs) {
promises.push(callGetLogs(PAGE_SIZE, 0, '', filterVal));
}
const results = await Promise.all(promises);
dom.content(nodeStats, renderOverviewStats(results[0], results[2]));
dom.content(nodeTop, renderTopRankings(results[1]));
if (shouldRefreshLogs && results[3]) {
dom.content(nodeLogs, renderLogsTable(results[3]));
}
updateLiveStatusBadge();
} catch (e) {
}
};
const refreshLogs = async () => {
try {
const logs = await callGetLogs(PAGE_SIZE, pageIdx * PAGE_SIZE, searchVal, filterVal);
dom.content(nodeLogs, renderLogsTable(logs));
updateLiveStatusBadge();
} catch (e) {
ui.addNotification(null, E('p', [_('Failed to update query logs: '), e.message]), 'error');
}
};
return view.extend({
async load() {
return Promise.all([
L.resolveDefault(callGetStats(), {}),
L.resolveDefault(callGetTop(10), {}),
L.resolveDefault(callGetLogs(PAGE_SIZE, 0, searchVal, filterVal), {}),
L.resolveDefault(callGetHistory(24), {})
]);
},
render(data) {
injectStyles();
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]));
dom.content(nodeLogs, renderLogsTable(data[2]));
updateLiveStatusBadge();
const searchInput = E('input', {
type: 'text',
class: 'cbi-input-text',
placeholder: _('Search domain or client IP...'),
style: 'min-width: 220px;'
});
searchInput.addEventListener('input', debounce(() => {
searchVal = searchInput.value.trim();
pageIdx = 0;
updateLiveStatusBadge();
refreshLogs();
}, 300));
const filterSelect = E('select', { class: 'cbi-input-select' }, [
E('option', { value: 'all', selected: filterVal === 'all' ? 'selected' : null }, _('All Queries')),
E('option', { value: 'blocked', selected: filterVal === 'blocked' ? 'selected' : null }, _('Blocked Only')),
E('option', { value: 'cached', selected: filterVal === 'cached' ? 'selected' : null }, _('Cached Only'))
]);
filterSelect.addEventListener('change', () => {
filterVal = filterSelect.value;
pageIdx = 0;
updateLiveStatusBadge();
refreshLogs();
});
const resetPageBtn = E('button', {
class: 'cbi-button cbi-button-apply',
click: () => {
pageIdx = 0;
updateLiveStatusBadge();
refreshLogs();
}
}, _('First Page / Resume'));
const clearBtn = E('button', {
class: 'btn cbi-button cbi-button-remove',
style: 'margin-left: auto;'
}, _('Clear query logs'));
clearBtn.addEventListener('click', () => {
ui.showModal(_('Clear query logs'), [
E('p', {}, _('Are you sure you want to clear all real-time query logs and top rankings?')),
E('div', { class: 'right', style: 'margin-top: 1rem;' }, [
E('button', {
class: 'btn cbi-button cbi-button-neutral',
click: ui.hideModal
}, _('Cancel')),
' ',
E('button', {
class: 'btn cbi-button cbi-button-remove',
click: async () => {
ui.hideModal();
try {
const res = await callClearQueryLogs();
if (res?.success) {
ui.addNotification(null, E('p', _('Query logs cleared successfully.')), 'info');
pageIdx = 0;
await pollScheduler();
await refreshLogs();
} else {
ui.addNotification(null, E('p', [_('Failed to clear query logs: '), res?.error || '']), 'error');
}
} catch (e) {
ui.addNotification(null, E('p', e.message), 'error');
}
}
}, _('Clear'))
])
]);
});
const controlBar = E('div', { style: 'display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 0.75rem;' }, [
searchInput,
filterSelect,
resetPageBtn,
autoStatusBadge,
clearBtn
]);
poll.add(pollScheduler);
return E('div', { class: 'cbi-map' }, [
E('h2', { name: 'content' }, '%s - %s'.format(_('MosDNS'), _('Statistics'))),
nodeStats,
nodeTop,
E('div', { class: 'cbi-section' }, [
E('h3', {}, _('Real-time Query Logs')),
controlBar,
nodeLogs
])
]);
},
handleSave: null,
handleSaveApply: null,
handleReset: null
});
+224 -12
View File
@@ -74,24 +74,50 @@ msgstr ""
msgid "Aliyun Public DNS (DNS over QUIC)" msgid "Aliyun Public DNS (DNS over QUIC)"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:640
msgid "All Queries"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:68 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:68
msgid "Another update is already in progress." msgid "Another update is already in progress."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:463
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:531
msgid "Answers"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:203 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:203
msgid "Apple domains optimization" msgid "Apple domains optimization"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:667
msgid ""
"Are you sure you want to clear all real-time query logs and top rankings?"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:359 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:359
msgid "Auto Save Cache Interval" msgid "Auto Save Cache Interval"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:447
msgid "Auto Save Stats Interval"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:106 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:106
msgid "" msgid ""
"Automatically update GeoIP and GeoSite databases as well as ad filtering " "Automatically update GeoIP and GeoSite databases as well as ad filtering "
"rules through scheduled tasks." "rules through scheduled tasks."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:336
msgid "Average Processing Time"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:306
msgid "Avg Processing"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:217 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:217
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:263 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:263
msgid "Baidu Public DNS (180.76.76.76)" msgid "Baidu Public DNS (180.76.76.76)"
@@ -119,6 +145,14 @@ msgstr ""
msgid "Block PTR" msgid "Block PTR"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:641
msgid "Blocked Only"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:314
msgid "Blocked by Filters"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:255 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:255
msgid "Bootstrap DNS servers" msgid "Bootstrap DNS servers"
msgstr "" msgstr ""
@@ -137,6 +171,18 @@ msgstr ""
msgid "Cache Prefetching" msgid "Cache Prefetching"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:642
msgid "Cached Only"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:325
msgid "Cached Queries"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:672
msgid "Cancel"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:150 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:150
msgid "Check And Update" msgid "Check And Update"
msgstr "" msgstr ""
@@ -163,10 +209,29 @@ msgstr ""
msgid "Cisco Public DNS (208.67.222.222)" msgid "Cisco Public DNS (208.67.222.222)"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:692
msgid "Clear"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:65 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:65
msgid "Clear logs" msgid "Clear logs"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:663
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:666
msgid "Clear query logs"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:511
msgid "Click to view full details"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:445
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:528
msgid "Client IP"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:475
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:35 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:35
msgid "Close" msgid "Close"
msgstr "" msgstr ""
@@ -202,7 +267,7 @@ msgstr ""
msgid "Config File" msgid "Config File"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:455 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:478
msgid "Configuration Editor" msgid "Configuration Editor"
msgstr "" msgstr ""
@@ -240,6 +305,10 @@ msgstr ""
msgid "DNS Forward" msgid "DNS Forward"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:302
msgid "DNS Queries Total"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:271 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:271
msgid "" msgid ""
"DNS query request concurrency, The number of upstream DNS servers that are " "DNS query request concurrency, The number of upstream DNS servers that are "
@@ -280,6 +349,14 @@ msgstr ""
msgid "DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)" msgid "DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:529
msgid "Domain & Record"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:532
msgid "Elapsed"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:111 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:111
msgid "Enable Auto Database Update" msgid "Enable Auto Database Update"
msgstr "" msgstr ""
@@ -296,6 +373,10 @@ msgstr ""
msgid "Enable EDNS client subnet" msgid "Enable EDNS client subnet"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:429
msgid "Enable Stats Collector"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:283 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:283
msgid "" msgid ""
"Enable TCP/DoT RFC 7766 new Query Pipelining connection multiplexing mode" "Enable TCP/DoT RFC 7766 new Query Pipelining connection multiplexing mode"
@@ -310,11 +391,11 @@ msgstr ""
msgid "Enabled" msgid "Enabled"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:482 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:505
msgid "Enter the GeoIP.dat category to be exported, Allow add multiple tags" msgid "Enter the GeoIP.dat category to be exported, Allow add multiple tags"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:477 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:500
msgid "Enter the GeoSite.dat category to be exported, Allow add multiple tags" msgid "Enter the GeoSite.dat category to be exported, Allow add multiple tags"
msgstr "" msgstr ""
@@ -354,8 +435,8 @@ msgstr ""
msgid "Every Wednesday" msgid "Every Wednesday"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:478 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:501
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:483 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:506
msgid "Export directory: /var/mosdns" msgid "Export directory: /var/mosdns"
msgstr "" msgstr ""
@@ -363,11 +444,23 @@ msgstr ""
msgid "Failed to clean logs." msgid "Failed to clean logs."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:686
msgid "Failed to clear query logs:"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:89 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:89
msgid "Failed to start update." msgid "Failed to start update."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:432 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:599
msgid "Failed to update query logs:"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:658
msgid "First Page / Resume"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:455
msgid "Flush DNS Cache" msgid "Flush DNS Cache"
msgstr "" msgstr ""
@@ -379,7 +472,7 @@ msgstr ""
msgid "Flushing DNS Cache Success." msgid "Flushing DNS Cache Success."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:430 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:453
msgid "" msgid ""
"Flushing DNS Cache will clear any IP addresses or DNS records from MosDNS " "Flushing DNS Cache will clear any IP addresses or DNS records from MosDNS "
"cache." "cache."
@@ -415,7 +508,7 @@ msgstr ""
msgid "GeoData Export" msgid "GeoData Export"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:481 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:504
msgid "GeoIP Tags" msgid "GeoIP Tags"
msgstr "" msgstr ""
@@ -423,7 +516,7 @@ msgstr ""
msgid "GeoIP Type" msgid "GeoIP Type"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:476 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:499
msgid "GeoSite Tags" msgid "GeoSite Tags"
msgstr "" msgstr ""
@@ -496,6 +589,10 @@ msgid ""
"seconds)." "seconds)."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:337
msgid "Latency"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:322 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:322
msgid "Lazy Cache TTL" msgid "Lazy Cache TTL"
msgstr "" msgstr ""
@@ -537,7 +634,7 @@ msgstr ""
msgid "Log Level" msgid "Log Level"
msgstr "" msgstr ""
#: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:38 #: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:46
msgid "Logs" msgid "Logs"
msgstr "" msgstr ""
@@ -577,10 +674,17 @@ msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:33 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:33
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:106 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:106
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:70 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:70
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:708
#: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:3 #: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:3
msgid "MosDNS" msgid "MosDNS"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:279
msgid ""
"MosDNS API is unreachable. Please ensure MosDNS is running and stats_api "
"plugin is enabled."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:107 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:107
msgid "MosDNS is a plugin-based DNS forwarder/traffic splitter." msgid "MosDNS is a plugin-based DNS forwarder/traffic splitter."
msgstr "" msgstr ""
@@ -593,10 +697,34 @@ msgstr ""
msgid "Netflix, Disney+, Hulu and streaming media rules list will use this DNS" msgid "Netflix, Disney+, Hulu and streaming media rules list will use this DNS"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:561
msgid "Next"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:428
msgid "No DNS answer records returned."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:353
msgid "No data available"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:27 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:27
msgid "No log data." msgid "No log data."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:520
msgid "No query log entries found."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:538
msgid "Page %d / %d (%d entries)"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:340
msgid "Per-query speed"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:301 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:301
msgid "" msgid ""
"Please provide the IP address you use when accessing foreign websites. This " "Please provide the IP address you use when accessing foreign websites. This "
@@ -628,6 +756,10 @@ msgstr ""
msgid "Prevent DNS Leaks" msgid "Prevent DNS Leaks"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:550
msgid "Previous"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:329 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:329
msgid "" msgid ""
"Proactively refresh hot cache entries in the background before they expire." "Proactively refresh hot cache entries in the background before they expire."
@@ -643,10 +775,28 @@ msgstr ""
msgid "Quad9 Public DNS (9.9.9.9)" msgid "Quad9 Public DNS (9.9.9.9)"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:469
msgid "Query Log Details"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:435
msgid ""
"Query log ring buffer capacity (FIFO overwrite, default 2000, larger values "
"consume more memory)"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:681
msgid "Query logs cleared successfully."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:31 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:31
msgid "RUNNING" msgid "RUNNING"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:712
msgid "Real-time Query Logs"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:23 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:23
msgid "Redirect" msgid "Redirect"
msgstr "" msgstr ""
@@ -669,6 +819,14 @@ msgstr ""
msgid "Remote DNS server" msgid "Remote DNS server"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:434
msgid "Ring Buffer Capacity"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:457
msgid "Rule Hit"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:11 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:11
msgid "Rule Settings" msgid "Rule Settings"
msgstr "" msgstr ""
@@ -677,10 +835,18 @@ msgstr ""
msgid "Rules" msgid "Rules"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:441
msgid "Save query statistics and logs locally and reload on next startup."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:353 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:353
msgid "Save the cache locally and reload the cache dump on the next startup" msgid "Save the cache locally and reload the cache dump on the next startup"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:629
msgid "Search domain or client IP..."
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:175 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:175
msgid "Set the maximum size of the log file (in MB)." msgid "Set the maximum size of the log file (in MB)."
msgstr "" msgstr ""
@@ -689,6 +855,19 @@ msgstr ""
msgid "Starting update..." msgid "Starting update..."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:708
#: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:38
msgid "Statistics"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:440
msgid "Stats Dump"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:530
msgid "Status"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:25 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:25
msgid "Streaming Media" msgid "Streaming Media"
msgstr "" msgstr ""
@@ -730,13 +909,30 @@ msgid ""
"server)" "server)"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:456 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:479
msgid "" msgid ""
"This is the content of the file '/etc/mosdns/config_custom.yaml' from which " "This is the content of the file '/etc/mosdns/config_custom.yaml' from which "
"your MosDNS configuration will be generated. Only accepts configuration " "your MosDNS configuration will be generated. Only accepts configuration "
"content in yaml format." "content in yaml format."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:449
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:527
msgid "Time"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:386
msgid "Top Blocked Domains"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:393
msgid "Top Clients"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:379
msgid "Top Queried Domains"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:213 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:213
msgid "TrafficRoute Public DNS (180.184.1.1)" msgid "TrafficRoute Public DNS (180.184.1.1)"
msgstr "" msgstr ""
@@ -745,7 +941,7 @@ msgstr ""
msgid "TrafficRoute Public DNS (180.184.2.2)" msgid "TrafficRoute Public DNS (180.184.2.2)"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:471 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:494
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:43 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:43
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:48 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:48
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:67 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:67
@@ -798,6 +994,10 @@ msgstr ""
msgid "Updating Database..." msgid "Updating Database..."
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:453
msgid "Upstream"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:164 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:164
msgid "Warning" msgid "Warning"
msgstr "" msgstr ""
@@ -832,3 +1032,15 @@ msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:143 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:143
msgid "https://gh-proxy.com" msgid "https://gh-proxy.com"
msgstr "" msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:303
msgid "● Live"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:148
msgid "● Live Auto-refresh"
msgstr ""
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:152
msgid "❚❚ Paused (Page %d)"
msgstr ""
+234 -16
View File
@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-03-20 09:23+0800\n" "POT-Creation-Date: 2026-03-20 09:23+0800\n"
"PO-Revision-Date: 2026-05-15 14:09+0800\n" "PO-Revision-Date: 2026-08-30 08:26+0800\n"
"Last-Translator: sbwml <admin@cooluc.com>\n" "Last-Translator: sbwml <admin@cooluc.com>\n"
"Language-Team: Chinese\n" "Language-Team: Chinese\n"
"Language: zh_Hans\n" "Language: zh_Hans\n"
@@ -89,24 +89,50 @@ msgstr "阿里云公共 DNS(DNS over HTTPS)"
msgid "Aliyun Public DNS (DNS over QUIC)" msgid "Aliyun Public DNS (DNS over QUIC)"
msgstr "阿里云公共 DNS(DNS over QUIC)" msgstr "阿里云公共 DNS(DNS over QUIC)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:640
msgid "All Queries"
msgstr "所有查询"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:68 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:68
msgid "Another update is already in progress." msgid "Another update is already in progress."
msgstr "另一个更新正在进行中。" msgstr "另一个更新正在进行中。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:463
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:531
msgid "Answers"
msgstr "应答结果"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:203 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:203
msgid "Apple domains optimization" msgid "Apple domains optimization"
msgstr "Apple 域名解析优化" msgstr "Apple 域名解析优化"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:667
msgid ""
"Are you sure you want to clear all real-time query logs and top rankings?"
msgstr "确定要清空所有实时查询日志和排行数据吗?"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:359 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:359
msgid "Auto Save Cache Interval" msgid "Auto Save Cache Interval"
msgstr "自动保存缓存间隔(秒)" msgstr "自动保存缓存间隔(秒)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:447
msgid "Auto Save Stats Interval"
msgstr "自动保存统计间隔"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:106 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:106
msgid "" msgid ""
"Automatically update GeoIP and GeoSite databases as well as ad filtering " "Automatically update GeoIP and GeoSite databases as well as ad filtering "
"rules through scheduled tasks." "rules through scheduled tasks."
msgstr "通过定时任务自动更新 GeoIP 和 GeoSite 数据库以及广告过滤规则。" msgstr "通过定时任务自动更新 GeoIP 和 GeoSite 数据库以及广告过滤规则。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:336
msgid "Average Processing Time"
msgstr "平均延迟"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:306
msgid "Avg Processing"
msgstr "平均处理"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:217 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:217
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:263 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:263
msgid "Baidu Public DNS (180.76.76.76)" msgid "Baidu Public DNS (180.76.76.76)"
@@ -136,6 +162,14 @@ msgstr "黑名单"
msgid "Block PTR" msgid "Block PTR"
msgstr "PTR 黑名单" msgstr "PTR 黑名单"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:641
msgid "Blocked Only"
msgstr "仅拦截"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:314
msgid "Blocked by Filters"
msgstr "拦截总数"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:255 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:255
msgid "Bootstrap DNS servers" msgid "Bootstrap DNS servers"
msgstr "Bootstrap DNS 服务器" msgstr "Bootstrap DNS 服务器"
@@ -154,6 +188,18 @@ msgstr "自动保存缓存"
msgid "Cache Prefetching" msgid "Cache Prefetching"
msgstr "缓存预取" msgstr "缓存预取"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:642
msgid "Cached Only"
msgstr "仅缓存"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:325
msgid "Cached Queries"
msgstr "缓存命中"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:672
msgid "Cancel"
msgstr "取消"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:150 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:150
msgid "Check And Update" msgid "Check And Update"
msgstr "检查并更新" msgstr "检查并更新"
@@ -180,10 +226,29 @@ msgstr "思科公共 DNS(208.67.220.220)"
msgid "Cisco Public DNS (208.67.222.222)" msgid "Cisco Public DNS (208.67.222.222)"
msgstr "思科公共 DNS(208.67.222.222)" msgstr "思科公共 DNS(208.67.222.222)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:692
msgid "Clear"
msgstr "清空"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:65 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:65
msgid "Clear logs" msgid "Clear logs"
msgstr "清空日志" msgstr "清空日志"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:663
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:666
msgid "Clear query logs"
msgstr "清除查询日志"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:511
msgid "Click to view full details"
msgstr "点击查看完整详情"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:445
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:528
msgid "Client IP"
msgstr "客户端 IP"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:475
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:35 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:35
msgid "Close" msgid "Close"
msgstr "关闭" msgstr "关闭"
@@ -219,7 +284,7 @@ msgstr "DNS 服务器并发数(默认 2)"
msgid "Config File" msgid "Config File"
msgstr "配置文件" msgstr "配置文件"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:455 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:478
msgid "Configuration Editor" msgid "Configuration Editor"
msgstr "配置编辑器" msgstr "配置编辑器"
@@ -258,6 +323,10 @@ msgstr "DNS 缓存大小"
msgid "DNS Forward" msgid "DNS Forward"
msgstr "DNS 转发" msgstr "DNS 转发"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:302
msgid "DNS Queries Total"
msgstr "DNS 查询总数"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:271 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:271
msgid "" msgid ""
"DNS query request concurrency, The number of upstream DNS servers that are " "DNS query request concurrency, The number of upstream DNS servers that are "
@@ -299,6 +368,14 @@ msgstr ""
msgid "DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)" msgid "DoH/TCP/DoT Connection Multiplexing idle timeout (default 30 seconds)"
msgstr "DoH/TCP/DoT 连接复用空闲保持时间(默认 30 秒)" msgstr "DoH/TCP/DoT 连接复用空闲保持时间(默认 30 秒)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:529
msgid "Domain & Record"
msgstr "域名与类型"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:532
msgid "Elapsed"
msgstr "耗时"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:111 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:111
msgid "Enable Auto Database Update" msgid "Enable Auto Database Update"
msgstr "启用自动更新" msgstr "启用自动更新"
@@ -315,6 +392,10 @@ msgstr "启用 DNS 缓存"
msgid "Enable EDNS client subnet" msgid "Enable EDNS client subnet"
msgstr "启用 EDNS 客户端子网" msgstr "启用 EDNS 客户端子网"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:429
msgid "Enable Stats Collector"
msgstr "启用统计收集器"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:283 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:283
msgid "" msgid ""
"Enable TCP/DoT RFC 7766 new Query Pipelining connection multiplexing mode" "Enable TCP/DoT RFC 7766 new Query Pipelining connection multiplexing mode"
@@ -329,11 +410,11 @@ msgstr "启用此选项 fallback 策略会强制转发到远程 DNS"
msgid "Enabled" msgid "Enabled"
msgstr "启用" msgstr "启用"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:482 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:505
msgid "Enter the GeoIP.dat category to be exported, Allow add multiple tags" msgid "Enter the GeoIP.dat category to be exported, Allow add multiple tags"
msgstr "输入需要导出的 GeoIP.dat 类别条目,允许添加多个标签" msgstr "输入需要导出的 GeoIP.dat 类别条目,允许添加多个标签"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:477 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:500
msgid "Enter the GeoSite.dat category to be exported, Allow add multiple tags" msgid "Enter the GeoSite.dat category to be exported, Allow add multiple tags"
msgstr "填写需要导出的 GeoSite.dat 类别条目,允许添加多个标签" msgstr "填写需要导出的 GeoSite.dat 类别条目,允许添加多个标签"
@@ -373,8 +454,8 @@ msgstr "每周二"
msgid "Every Wednesday" msgid "Every Wednesday"
msgstr "每周三" msgstr "每周三"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:478 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:501
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:483 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:506
msgid "Export directory: /var/mosdns" msgid "Export directory: /var/mosdns"
msgstr "导出目录:/var/mosdns" msgstr "导出目录:/var/mosdns"
@@ -382,11 +463,23 @@ msgstr "导出目录:/var/mosdns"
msgid "Failed to clean logs." msgid "Failed to clean logs."
msgstr "清理日志失败:%s" msgstr "清理日志失败:%s"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:686
msgid "Failed to clear query logs:"
msgstr "清除查询日志失败:"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:89 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:89
msgid "Failed to start update." msgid "Failed to start update."
msgstr "启动更新失败" msgstr "启动更新失败"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:432 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:599
msgid "Failed to update query logs:"
msgstr "更新查询日志失败:"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:658
msgid "First Page / Resume"
msgstr "重置并恢复"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:455
msgid "Flush DNS Cache" msgid "Flush DNS Cache"
msgstr "刷新 DNS 缓存" msgstr "刷新 DNS 缓存"
@@ -398,7 +491,7 @@ msgstr "刷新 DNS 缓存失败,请检查 MosDNS 状态是否在运行中。"
msgid "Flushing DNS Cache Success." msgid "Flushing DNS Cache Success."
msgstr "刷新 DNS 缓存成功" msgstr "刷新 DNS 缓存成功"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:430 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:453
msgid "" msgid ""
"Flushing DNS Cache will clear any IP addresses or DNS records from MosDNS " "Flushing DNS Cache will clear any IP addresses or DNS records from MosDNS "
"cache." "cache."
@@ -434,7 +527,7 @@ msgstr "全量:包含所有国家和私有 IP 地址。"
msgid "GeoData Export" msgid "GeoData Export"
msgstr "GeoData 导出" msgstr "GeoData 导出"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:481 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:504
msgid "GeoIP Tags" msgid "GeoIP Tags"
msgstr "GeoIP 标签" msgstr "GeoIP 标签"
@@ -442,7 +535,7 @@ msgstr "GeoIP 标签"
msgid "GeoIP Type" msgid "GeoIP Type"
msgstr "GeoIP 类型" msgstr "GeoIP 类型"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:476 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:499
msgid "GeoSite Tags" msgid "GeoSite Tags"
msgstr "GeoSite 标签" msgstr "GeoSite 标签"
@@ -475,7 +568,7 @@ msgstr "灰名单"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:22 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:22
msgid "Hosts" msgid "Hosts"
msgstr "" msgstr "Hosts 列表"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:300 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:300
msgid "IP Address" msgid "IP Address"
@@ -488,6 +581,10 @@ msgid ""
"www.cloudflare.com/ips-v6\" target=\"_blank\">https://www.cloudflare.com/ips-" "www.cloudflare.com/ips-v6\" target=\"_blank\">https://www.cloudflare.com/ips-"
"v6</a>" "v6</a>"
msgstr "" msgstr ""
"IPv4 CIDR: <a href=\"https://www.cloudflare.com/ips-v4\" target=\"_blank"
"\">https://www.cloudflare.com/ips-v4</a> <br /> IPv6 CIDR: <a href=\"https://"
"www.cloudflare.com/ips-v6\" target=\"_blank\">https://www.cloudflare.com/ips-"
"v6</a>"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:190 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:190
msgid "" msgid ""
@@ -515,6 +612,10 @@ msgid ""
"seconds)." "seconds)."
msgstr "后台线程扫描缓存以进行预取的时间间隔(单位:秒)" msgstr "后台线程扫描缓存以进行预取的时间间隔(单位:秒)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:337
msgid "Latency"
msgstr "延迟"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:322 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:322
msgid "Lazy Cache TTL" msgid "Lazy Cache TTL"
msgstr "乐观缓存 TTL" msgstr "乐观缓存 TTL"
@@ -556,7 +657,7 @@ msgstr "日志文件大小"
msgid "Log Level" msgid "Log Level"
msgstr "日志等级" msgstr "日志等级"
#: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:38 #: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:46
msgid "Logs" msgid "Logs"
msgstr "日志" msgstr "日志"
@@ -599,10 +700,19 @@ msgstr "修改 DNS 应答结果的最小 TTL 值 (秒),0 表示不修改"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:33 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:33
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:106 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:106
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:70 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:70
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:708
#: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:3 #: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:3
msgid "MosDNS" msgid "MosDNS"
msgstr "MosDNS" msgstr "MosDNS"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:279
msgid ""
"MosDNS API is unreachable. Please ensure MosDNS is running and stats_api "
"plugin is enabled."
msgstr ""
"MosDNS API 无法连接。请确保 MosDNS 正在运行且已启用统计收集器(stats_api)插"
"件。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:107 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:107
msgid "MosDNS is a plugin-based DNS forwarder/traffic splitter." msgid "MosDNS is a plugin-based DNS forwarder/traffic splitter."
msgstr "MosDNS 是一个插件化的 DNS 转发/分流器。" msgstr "MosDNS 是一个插件化的 DNS 转发/分流器。"
@@ -615,10 +725,34 @@ msgstr "未运行"
msgid "Netflix, Disney+, Hulu and streaming media rules list will use this DNS" msgid "Netflix, Disney+, Hulu and streaming media rules list will use this DNS"
msgstr "自定义 Netflix、Disney+、Hulu 以及 “流媒体” 规则列表的 DNS 服务器" msgstr "自定义 Netflix、Disney+、Hulu 以及 “流媒体” 规则列表的 DNS 服务器"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:561
msgid "Next"
msgstr "下一页"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:428
msgid "No DNS answer records returned."
msgstr "无 DNS 应答记录"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:353
msgid "No data available"
msgstr "暂无数据"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:27 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/logs.js:27
msgid "No log data." msgid "No log data."
msgstr "无日志数据。" msgstr "无日志数据。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:520
msgid "No query log entries found."
msgstr "未找到查询日志记录。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:538
msgid "Page %d / %d (%d entries)"
msgstr "第 %d / %d 页(%d 条记录)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:340
msgid "Per-query speed"
msgstr "单次查询速度"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:301 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:301
msgid "" msgid ""
"Please provide the IP address you use when accessing foreign websites. This " "Please provide the IP address you use when accessing foreign websites. This "
@@ -652,6 +786,10 @@ msgstr "当剩余 TTL 小于此值(秒)时触发预取"
msgid "Prevent DNS Leaks" msgid "Prevent DNS Leaks"
msgstr "防止 DNS 泄漏" msgstr "防止 DNS 泄漏"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:550
msgid "Previous"
msgstr "上一页"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:329 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:329
msgid "" msgid ""
"Proactively refresh hot cache entries in the background before they expire." "Proactively refresh hot cache entries in the background before they expire."
@@ -667,10 +805,28 @@ msgstr "Quad9 公共 DNS(149.112.112.112)"
msgid "Quad9 Public DNS (9.9.9.9)" msgid "Quad9 Public DNS (9.9.9.9)"
msgstr "Quad9 公共 DNS(9.9.9.9)" msgstr "Quad9 公共 DNS(9.9.9.9)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:469
msgid "Query Log Details"
msgstr "查询日志详情"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:435
msgid ""
"Query log ring buffer capacity (FIFO overwrite, default 2000, larger values "
"consume more memory)"
msgstr "查询日志环形缓冲区容量(FIFO 覆盖,默认 2000,数值越大占用内存越多)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:681
msgid "Query logs cleared successfully."
msgstr "查询日志清除成功。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:31 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:31
msgid "RUNNING" msgid "RUNNING"
msgstr "运行中" msgstr "运行中"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:712
msgid "Real-time Query Logs"
msgstr "实时查询日志"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:23 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:23
msgid "Redirect" msgid "Redirect"
msgstr "重定向" msgstr "重定向"
@@ -695,6 +851,14 @@ msgstr "远程 DNS 首选 IPv4"
msgid "Remote DNS server" msgid "Remote DNS server"
msgstr "远程 DNS 服务器" msgstr "远程 DNS 服务器"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:434
msgid "Ring Buffer Capacity"
msgstr "环形缓冲区容量"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:457
msgid "Rule Hit"
msgstr "命中规则"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:11 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:11
msgid "Rule Settings" msgid "Rule Settings"
msgstr "自定义规则列表" msgstr "自定义规则列表"
@@ -703,17 +867,38 @@ msgstr "自定义规则列表"
msgid "Rules" msgid "Rules"
msgstr "规则列表" msgstr "规则列表"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:441
msgid "Save query statistics and logs locally and reload on next startup."
msgstr "保存统计数据与查询日志在本地,并在下次启动时重新加载"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:353 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:353
msgid "Save the cache locally and reload the cache dump on the next startup" msgid "Save the cache locally and reload the cache dump on the next startup"
msgstr "保存缓存到本地文件,以供下次启动时重新载入使用" msgstr "保存缓存到本地文件,以供下次启动时重新载入使用"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:629
msgid "Search domain or client IP..."
msgstr "搜索域名或客户端 IP..."
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:175 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:175
msgid "Set the maximum size of the log file (in MB)." msgid "Set the maximum size of the log file (in MB)."
msgstr "设置日志文件的最大容量(单位:MB)。" msgstr "设置日志文件的最大容量(单位:MB)。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:28 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:28
msgid "Starting update..." msgid "Starting update..."
msgstr "" msgstr "正在启动更新..."
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:708
#: luci-app-mosdns/root/usr/share/luci/menu.d/luci-app-mosdns.json:38
msgid "Statistics"
msgstr "统计"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:440
msgid "Stats Dump"
msgstr "自动保存统计"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:530
msgid "Status"
msgstr "状态"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:25 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:25
msgid "Streaming Media" msgid "Streaming Media"
@@ -758,7 +943,7 @@ msgstr ""
"此功能通常在使用自建 DNS 服务器作为 远程 / 流媒体 DNS 上游时使用(需要上游服" "此功能通常在使用自建 DNS 服务器作为 远程 / 流媒体 DNS 上游时使用(需要上游服"
"务器的支持)" "务器的支持)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:456 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:479
msgid "" msgid ""
"This is the content of the file '/etc/mosdns/config_custom.yaml' from which " "This is the content of the file '/etc/mosdns/config_custom.yaml' from which "
"your MosDNS configuration will be generated. Only accepts configuration " "your MosDNS configuration will be generated. Only accepts configuration "
@@ -767,6 +952,23 @@ msgstr ""
"这是文件 “/etc/mosdns/config_custom.yaml” 的内容,您的 MosDNS 配置将从此文件" "这是文件 “/etc/mosdns/config_custom.yaml” 的内容,您的 MosDNS 配置将从此文件"
"生成。仅接受 yaml 格式的配置内容。" "生成。仅接受 yaml 格式的配置内容。"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:449
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:527
msgid "Time"
msgstr "时间"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:386
msgid "Top Blocked Domains"
msgstr "拦截域名 (Top 10)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:393
msgid "Top Clients"
msgstr "活跃客户端 (Top 10)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:379
msgid "Top Queried Domains"
msgstr "查询域名 (Top 10)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:213 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:213
msgid "TrafficRoute Public DNS (180.184.1.1)" msgid "TrafficRoute Public DNS (180.184.1.1)"
msgstr "火山引擎公共 DNS(180.184.1.1)" msgstr "火山引擎公共 DNS(180.184.1.1)"
@@ -775,7 +977,7 @@ msgstr "火山引擎公共 DNS(180.184.1.1)"
msgid "TrafficRoute Public DNS (180.184.2.2)" msgid "TrafficRoute Public DNS (180.184.2.2)"
msgstr "火山引擎公共 DNS(180.184.2.2)" msgstr "火山引擎公共 DNS(180.184.2.2)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:471 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:494
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:43 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:43
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:48 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:48
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:67 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/rules.js:67
@@ -828,6 +1030,10 @@ msgstr "更新成功"
msgid "Updating Database..." msgid "Updating Database..."
msgstr "更新数据库..." msgstr "更新数据库..."
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:453
msgid "Upstream"
msgstr "上游服务器"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:164 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/basic.js:164
msgid "Warning" msgid "Warning"
msgstr "警告" msgstr "警告"
@@ -865,4 +1071,16 @@ msgstr "信风公共 DNS(114.114.115.115)"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:143 #: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/update.js:143
msgid "https://gh-proxy.com" msgid "https://gh-proxy.com"
msgstr "" msgstr "https://gh-proxy.com"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:303
msgid "● Live"
msgstr "● 实时"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:148
msgid "● Live Auto-refresh"
msgstr "● 实时刷新"
#: luci-app-mosdns/htdocs/luci-static/resources/view/mosdns/statistics.js:152
msgid "❚❚ Paused (Page %d)"
msgstr "❚❚ 暂停 (第 %d 页)"
+6 -1
View File
@@ -8,7 +8,7 @@ config mosdns 'config'
option geo_update_day_time '2' option geo_update_day_time '2'
option geoip_type 'geoip-only-cn-private' option geoip_type 'geoip-only-cn-private'
option configfile '/var/etc/mosdns.json' option configfile '/var/etc/mosdns.json'
option log_level 'info' option log_level 'error'
option log_file '/var/log/mosdns.log' option log_file '/var/log/mosdns.log'
option log_size '1' option log_size '1'
option cache '1' option cache '1'
@@ -30,4 +30,9 @@ config mosdns 'config'
option lazy_cache_ttl '86400' option lazy_cache_ttl '86400'
option dump_file '0' option dump_file '0'
option reject_type65 '1' option reject_type65 '1'
option stats_collector '1'
option stats_capacity '2000'
option stats_dump_file '0'
option stats_dump_interval '600'
+24 -1
View File
@@ -34,7 +34,7 @@ get_config() {
config_get listen_address $1 listen_address "0.0.0.0" config_get listen_address $1 listen_address "0.0.0.0"
config_get log_file $1 log_file "/var/log/mosdns.log" config_get log_file $1 log_file "/var/log/mosdns.log"
config_get log_size $1 log_size "1" config_get log_size $1 log_size "1"
config_get log_level $1 log_level "info" config_get log_level $1 log_level "error"
config_get minimal_ttl $1 minimal_ttl 0 config_get minimal_ttl $1 minimal_ttl 0
config_get maximum_ttl $1 maximum_ttl 0 config_get maximum_ttl $1 maximum_ttl 0
config_get redirect $1 redirect 0 config_get redirect $1 redirect 0
@@ -57,6 +57,10 @@ get_config() {
config_get cloudflare $1 cloudflare 0 config_get cloudflare $1 cloudflare 0
config_get cloudflare_ip $1 cloudflare_ip "" config_get cloudflare_ip $1 cloudflare_ip ""
config_get reject_type65 $1 reject_type65 "0" config_get reject_type65 $1 reject_type65 "0"
config_get stats_collector $1 stats_collector 1
config_get stats_capacity $1 stats_capacity 2000
config_get stats_dump_file $1 stats_dump_file 0
config_get stats_dump_interval $1 stats_dump_interval 600
} }
generate_config() { generate_config() {
@@ -79,6 +83,20 @@ generate_config() {
json_close_array json_close_array
# plugins # plugins
json_add_array "plugins" json_add_array "plugins"
# plugin: stats_collector
[ "$stats_collector" -eq 1 ] && {
json_add_object
json_add_string "tag" "stats_collector"
json_add_string "type" "stats_api"
json_add_object "args"
json_add_int "capacity" "$stats_capacity"
[ "$stats_dump_file" -eq 1 ] && {
json_add_string "dump_file" "/etc/mosdns/stats.dump"
json_add_int "dump_interval" "$stats_dump_interval"
}
json_close_object
json_close_object
}
# plugin: geosite_cn # plugin: geosite_cn
json_add_object json_add_object
json_add_string "tag" "geosite_cn" json_add_string "tag" "geosite_cn"
@@ -589,6 +607,11 @@ generate_config() {
json_add_string "tag" "main_sequence" json_add_string "tag" "main_sequence"
json_add_string "type" "sequence" json_add_string "type" "sequence"
json_add_array "args" json_add_array "args"
[ "$stats_collector" -eq 1 ] && {
json_add_object
json_add_string "exec" "\$stats_collector"
json_close_object
}
json_add_object json_add_object
json_add_string "exec" "\$hosts" json_add_string "exec" "\$hosts"
json_close_object json_close_object
@@ -9,6 +9,14 @@ api:
include: [] include: []
plugins: plugins:
# 统计收集器
- tag: stats_collector
type: stats_api
args:
capacity: 2000 # 查询日志环缓冲区容量 (FIFO 覆盖)
dump_file: "/etc/mosdns/stats.dump" # 数据持久化转存文件路径,不设置则不转存
dump_interval: 600 # 周期性转储间隔(秒)
# 国内域名 # 国内域名
- tag: geosite_cn - tag: geosite_cn
type: domain_set type: domain_set
@@ -120,6 +128,7 @@ plugins:
- tag: main_sequence - tag: main_sequence
type: sequence type: sequence
args: args:
- exec: $stats_collector # 执行全局统计控制器
- exec: $lazy_cache - exec: $lazy_cache
- exec: jump has_resp_sequence - exec: jump has_resp_sequence
- exec: $query_is_local_domain - exec: $query_is_local_domain
@@ -34,9 +34,17 @@
"path": "mosdns/update" "path": "mosdns/update"
} }
}, },
"admin/services/mosdns/statistics": {
"title": "Statistics",
"order": 25,
"action": {
"type": "view",
"path": "mosdns/statistics"
}
},
"admin/services/mosdns/logs": { "admin/services/mosdns/logs": {
"title": "Logs", "title": "Logs",
"order": 25, "order": 30,
"action": { "action": {
"type": "view", "type": "view",
"path": "mosdns/logs" "path": "mosdns/logs"
@@ -149,8 +149,8 @@ function update_adlist() {
print(`Downloading ${mirror}${url}\n`); print(`Downloading ${mirror}${url}\n`);
stdout.flush(); stdout.flush();
let curl_res = exec_sys(`curl --connect-timeout 5 -m 90 --ipv4 -kfSLo "${ad_tmpdir}/${filename}" "${mirror}${url}"`); let dl_res = exec_sys(`wget -4 -q --no-check-certificate -T 90 -t 2 -O "${ad_tmpdir}/${filename}" "${mirror}${url}"`);
if (curl_res.code !== 0) download_failed = true; if (dl_res.code !== 0) download_failed = true;
} }
} }
@@ -192,7 +192,7 @@ function update_geodat() {
print(`Downloading ${geoip_url}.sha256sum\n`); print(`Downloading ${geoip_url}.sha256sum\n`);
stdout.flush(); stdout.flush();
if (exec_sys(`curl --connect-timeout 5 -m 20 --ipv4 -kfSLo "${tmpdir}/geoip.dat.sha256sum" "${geoip_url}.sha256sum"`).code !== 0) { if (exec_sys(`wget -4 -q --no-check-certificate -T 20 -t 2 -O "${tmpdir}/geoip.dat.sha256sum" "${geoip_url}.sha256sum"`).code !== 0) {
exec_sys(`rm -rf "${tmpdir}"`); exec_sys(`rm -rf "${tmpdir}"`);
die("Failed to download geoip.dat.sha256sum"); die("Failed to download geoip.dat.sha256sum");
} }
@@ -209,7 +209,7 @@ function update_geodat() {
} else { } else {
print(`Downloading ${geoip_url}\n`); print(`Downloading ${geoip_url}\n`);
stdout.flush(); stdout.flush();
if (exec_sys(`curl --connect-timeout 5 -m 120 --ipv4 -kfSLo "${tmpdir}/geoip.dat" "${geoip_url}"`).code !== 0) { if (exec_sys(`wget -4 -q --no-check-certificate -T 120 -t 2 -O "${tmpdir}/geoip.dat" "${geoip_url}"`).code !== 0) {
exec_sys(`rm -rf "${tmpdir}"`); exec_sys(`rm -rf "${tmpdir}"`);
die("Failed to download geoip.dat"); die("Failed to download geoip.dat");
} }
@@ -227,7 +227,7 @@ function update_geodat() {
print(`Downloading ${geosite_url}.sha256sum\n`); print(`Downloading ${geosite_url}.sha256sum\n`);
stdout.flush(); stdout.flush();
if (exec_sys(`curl --connect-timeout 5 -m 20 --ipv4 -kfSLo "${tmpdir}/geosite.dat.sha256sum" "${geosite_url}.sha256sum"`).code !== 0) { if (exec_sys(`wget -4 -q --no-check-certificate -T 20 -t 2 -O "${tmpdir}/geosite.dat.sha256sum" "${geosite_url}.sha256sum"`).code !== 0) {
exec_sys(`rm -rf "${tmpdir}"`); exec_sys(`rm -rf "${tmpdir}"`);
die("Failed to download geosite.dat.sha256sum"); die("Failed to download geosite.dat.sha256sum");
} }
@@ -244,7 +244,7 @@ function update_geodat() {
} else { } else {
print(`Downloading ${geosite_url}\n`); print(`Downloading ${geosite_url}\n`);
stdout.flush(); stdout.flush();
if (exec_sys(`curl --connect-timeout 5 -m 120 --ipv4 -kfSLo "${tmpdir}/geosite.dat" "${geosite_url}"`).code !== 0) { if (exec_sys(`wget -4 -q --no-check-certificate -T 120 -t 2 -O "${tmpdir}/geosite.dat" "${geosite_url}"`).code !== 0) {
exec_sys(`rm -rf "${tmpdir}"`); exec_sys(`rm -rf "${tmpdir}"`);
die("Failed to download geosite.dat"); die("Failed to download geosite.dat");
} }
@@ -16,10 +16,15 @@
"service": [ "list" ], "service": [ "list" ],
"luci.mosdns": [ "luci.mosdns": [
"clean_log", "clean_log",
"clear_query_logs",
"flush_cache", "flush_cache",
"get_history",
"get_logs",
"get_stats",
"get_top",
"get_update_log",
"get_version", "get_version",
"print_log", "print_log",
"get_update_log",
"start_update" "start_update"
] ]
}, },
@@ -35,7 +40,8 @@
"/var/mosdns/*": [ "write" ] "/var/mosdns/*": [ "write" ]
}, },
"ubus": { "ubus": {
"file": [ "write" ] "file": [ "write" ],
"luci.mosdns": [ "clear_query_logs" ]
}, },
"uci": [ "mosdns" ] "uci": [ "mosdns" ]
} }
@@ -58,16 +58,85 @@ function get_logfile_path_internal() {
} }
} }
function parse_json_safe(str) {
if (!str || str == "")
return null;
try {
return json(str);
} catch(e) {
return null;
}
}
function call_mosdns_api(endpoint, method) {
try {
let uci_cursor = cursor();
uci_cursor.load('mosdns');
let port = uci_cursor.get('mosdns', 'config', 'listen_port_api') || '9091';
let http_method = method ? method : 'GET';
let post_flag = (http_method === 'POST') ? '--post-data=""' : '';
let cmd = `wget -q -O - ${post_flag} "http://127.0.0.1:${port}/plugins/stats_collector${endpoint}"`;
let res = exec_sys(cmd);
if (res.code === 0 && res.stdout != "") {
let json_val = parse_json_safe(res.stdout);
if (json_val) return json_val;
}
return { error: "MosDNS API unreachable" };
} catch (e) {
return { error: String(e) };
}
}
const methods = { const methods = {
get_stats: {
call: function() {
return call_mosdns_api('/api/v1/stats', 'GET');
}
},
get_history: {
args: { points: 24 },
call: function(request) {
let points = request?.args?.points || 24;
return call_mosdns_api(`/api/v1/history?points=${points}`, 'GET');
}
},
get_top: {
args: { limit: 10 },
call: function(request) {
let limit = request?.args?.limit || 10;
return call_mosdns_api(`/api/v1/top?limit=${limit}`, 'GET');
}
},
get_logs: {
args: { limit: 50, offset: 0, search: '', filter: 'all' },
call: function(request) {
let limit = request?.args?.limit || 50;
let offset = request?.args?.offset || 0;
let search = request?.args?.search || '';
let filter = request?.args?.filter || 'all';
return call_mosdns_api(`/api/v1/logs?limit=${limit}&offset=${offset}&search=${search}&filter=${filter}`, 'GET');
}
},
clear_query_logs: {
call: function() {
return call_mosdns_api('/api/v1/logs/clear', 'POST');
}
},
flush_cache: { flush_cache: {
call: function() { call: function() {
try { try {
let uci_cursor = cursor(); let uci_cursor = cursor();
uci_cursor.load('mosdns'); uci_cursor.load('mosdns');
let port = uci_cursor.get('mosdns', 'config', 'listen_port_api'); let port = uci_cursor.get('mosdns', 'config', 'listen_port_api') || '9091';
if (!port) return { error: "API listen port not configured." }; if (!port) return { error: "API listen port not configured." };
let res = exec_sys(`curl -s 127.0.0.1:${port}/plugins/lazy_cache/flush`); let res = exec_sys(`wget -q -O - "http://127.0.0.1:${port}/plugins/lazy_cache/flush"`);
if (res.code === 0) { if (res.code === 0) {
return { success: true }; return { success: true };
} else { } else {
+22
View File
@@ -0,0 +1,22 @@
# Copyright (C) 2020 Gyj1109
# 适配 OpenWrt 25.12 修改版
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-syncthing
PKG_VERSION:=1.0
PKG_RELEASE:=3
LUCI_TITLE:=LuCI support for Syncthing
LUCI_PKGARCH:=all
# 适配 25.12:确保安装了 syncthing 后,LuCI 界面能正确拉起
LUCI_DEPENDS:=+syncthing
include $(TOPDIR)/feeds/luci/luci.mk
# 明确定义配置文件的归属,这对 25.12 的 apk 管理器非常重要
define Package/$(PKG_NAME)/conffiles
/etc/config/syncthing
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
+2
View File
@@ -0,0 +1,2 @@
# luci-app-syncthing
在Potat0000源码基础上进行了汉化优化,官方openwrt23.05.3编译后可使用
@@ -0,0 +1,17 @@
module("luci.controller.syncthing", package.seeall)
function index()
if not nixio.fs.access("/etc/config/syncthing") then
return
end
entry({"admin", "services", "syncthing"}, cbi("syncthing"), _("文件同步"), 10).dependent = true
entry({"admin", "services", "syncthing", "status"}, call("act_status")).leaf = true
end
function act_status()
local e = {}
e.running = luci.sys.call("pgrep syncthing >/dev/null") == 0
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
@@ -0,0 +1,48 @@
require("nixio.fs")
m = Map("syncthing", translate("Syncthing同步工具"))
m:section(SimpleSection).template = "syncthing/syncthing_status"
s = m:section(TypedSection, "syncthing")
s.anonymous = true
o = s:option(Flag, "enabled", translate("启用"))
o.default = 0
o.rmempty = false
gui_address = s:option(Value, "gui_address", translate("GUI访问地址"))
gui_address.description = translate("使用0.0.0.0以监控所有访问。")
gui_address.default = "http://0.0.0.0:8384"
gui_address.placeholder = "http://0.0.0.0:8384"
gui_address.rmempty = false
home = s:option(Value, "home", translate("配置文件目录"))
home.description = translate("只有保存在/etc/syncthing中的配置会自动备份!")
home.default = "/etc/syncthing"
home.placeholder = "/etc/syncthing"
home.rmempty = false
user = s:option(ListValue, "user", translate("用户"))
user.description = translate("默认是syncthing,但这可能会导致权限被拒绝。Syncthing官方不建议以root身份运行。")
user:value("", translate("syncthing"))
for u in luci.util.execi("cat /etc/passwd | cut -d ':' -f1") do
user:value(u)
end
macprocs = s:option(Value, "macprocs", translate("线程限制"))
macprocs.description = translate("0表示匹配CPU数量(默认),>0表示显式指定并发数。")
macprocs.default = "0"
macprocs.placeholder = "0"
macprocs.datatype = "range(0,32)"
macprocs.rmempty = false
nice = s:option(Value, "nice", translate("优先级"))
nice.description = translate("显式指定优先级值。0是最高,19是最低。(暂时不允许设置负值)")
nice.default = "19"
nice.placeholder = "19"
nice.datatype = "range(0,19)"
nice.rmempty = false
return m
@@ -0,0 +1,27 @@
<script type="text/javascript">//<![CDATA[
XHR.poll(1, '<%=url([[admin]], [[services]], [[syncthing]], [[status]])%>', null,
function (x, data) {
var tb = document.getElementById('syncthing_status');
if (data && tb) {
if (data.running) {
var links = '<em><b><font color="green">Syncthing <%:运行中%></font></b></em><input class="btn cbi-button mar-10" type="button" value="<%:打开Syncthing页面%>" onclick="openwebui();" />';
tb.innerHTML = links;
} else {
tb.innerHTML = '<em><b><font color="red">Syncthing <%:未运行%></font></b></em>';
}
}
}
);
function openwebui(){
var url = window.location.host+":<%=luci.sys.exec("uci -q get syncthing.syncthing.gui_address"):match(":[0-9]+"):gsub(":", "")%>";
window.open('http://'+url,'target','');
}
//]]>
</script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="syncthing_status">
<em><%:正在收集数据...%></em>
</p>
</fieldset>
@@ -0,0 +1,12 @@
#!/bin/sh
touch /etc/config/syncthing
uci -q batch <<-EOF >/dev/null
delete ucitrack.@syncthing[-1]
add ucitrack syncthing
set ucitrack.@syncthing[-1].exec='/etc/init.d/syncthing stop && /etc/init.d/syncthing start'
commit ucitrack
EOF
# remove LuCI cache
rm -f /tmp/luci*
exit 0
@@ -0,0 +1,11 @@
{
"luci-app-syncthing": {
"description": "Grant UCI access for luci-app-syncthing",
"read": {
"uci": [ "syncthing" ]
},
"write": {
"uci": [ "syncthing" ]
}
}
}