🍕 Sync 2026-09-17 11:06:12

This commit is contained in:
github-actions[bot] committed 2026-09-17 11:06:12 +08:00
1 parent 6bd796a758
commit 50871a0a18
10 files changed
+510 -48

No files matched your search

+1 -1
View File
@@ -12,7 +12,7 @@ LUCI_DEPENDS:=+luci-base +logd +jsonfilter
LUCI_PKGARCH:=all
PKG_VERSION:=0.1.41
PKG_RELEASE:=19
PKG_RELEASE:=20
# Reproducible build: honor SOURCE_DATE_EPOCH from the build environment (set by docker-sdk.sh / CI).
ifdef SOURCE_DATE_EPOCH
@@ -12,6 +12,10 @@ return baseclass.extend({
APP_VERSION: '0.1.41',
ROW_LIMIT_OPTIONS: [25, 50, 100, 250, 500, 1000, 2000],
DEFAULT_ROW_LIMIT: 100,
FETCH_MODE_OPTIONS: ['auto', 'manual'],
MANUAL_FETCH_LINES_OPTIONS: [25, 50, 100, 250, 500, 1000, 2000],
DEFAULT_FETCH_MODE: 'auto',
DEFAULT_MANUAL_FETCH_LINES: 100,
/* Row pass/deny tint: classic green/red default; accessible teal/orange */
ROW_TINT_OPTIONS: ['off', 'classic', 'accessible'],
DEFAULT_ROW_TINT: 'classic',
@@ -94,6 +94,10 @@ function optionNodes(pairs) {
return view.extend({
rowLimit: constants.DEFAULT_ROW_LIMIT,
fetchMode: constants.DEFAULT_FETCH_MODE,
manualFetchLines: constants.DEFAULT_MANUAL_FETCH_LINES,
manualFetchLinesExplicit: false,
rpcPreferencesResolved: false,
entries: [],
sessionSeen: null,
pauseBufferLoading: false,
@@ -108,14 +112,19 @@ return view.extend({
pollRequestQueued: false,
pollRequestWaiters: [],
pollRequestQueuedWaiters: [],
coordinatorDisposed: false,
/* Layer 2 — visibility / RTT cadence / shed surfacing. */
pollEpoch: 0,
pollCadenceSec: 1,
rttStreakKind: null,
rttStreakCount: 0,
serverAdaptive: 1,
serverAdaptive: undefined,
serverTruncated: 0,
serverShed: null,
lastPollRequestedLines: null,
lastPollEffectiveLimit: null,
lastPollReturnedMessages: null,
fillingBuffer: false,
weakDevice: false,
degradedSampling: false,
/* Layer 2 summary fallback — rows remain available behind an explicit toggle. */
@@ -194,27 +203,163 @@ return view.extend({
.map((k) => '%s=%s'.format(encodeURIComponent(k), encodeURIComponent(filters[k])));
if (this.rowLimit !== constants.DEFAULT_ROW_LIMIT)
parts.push('limit=%s'.format(encodeURIComponent(this.rowLimit)));
if (this.fetchMode === 'manual') {
parts.push('poll=manual');
parts.push('maxraw=%s'.format(encodeURIComponent(this.manualFetchLines)));
}
if (this.viewMode === 'detailed') parts.push('view=detailed');
location.hash = parts.join('&');
},
applyHash() {
if (!location.hash || location.hash.length < 2) return;
hashEntries() {
if (!location.hash || location.hash.length < 2) return [];
const entries = location.hash.substring(1).split('&');
const result = [];
for (let i = 0; i < entries.length; i++) {
const kv = entries[i].split('=');
if (kv.length !== 2) continue;
const key = decodeURIComponent(kv[0]);
const val = decodeURIComponent(kv[1]);
if (key === 'limit') {
const n = parseInt(val, 10);
if (isFinite(n) && constants.ROW_LIMIT_OPTIONS.indexOf(n) >= 0) {
this.applyRowLimit(n);
this.saveRowLimit();
}
let key;
let val;
try {
key = decodeURIComponent(kv[0]);
val = decodeURIComponent(kv[1]);
} catch (e) {
continue;
}
result.push({ key: key, val: val });
}
return result;
},
readFetchMode() {
const v = storedValue('fwlive-poll-mode', constants.DEFAULT_FETCH_MODE);
return constants.FETCH_MODE_OPTIONS.indexOf(v) >= 0 ? v : constants.DEFAULT_FETCH_MODE;
},
saveFetchMode() {
storeValue('fwlive-poll-mode', this.fetchMode);
},
readManualFetchLines() {
const raw = storedValue('fwlive-manual-lines', '');
const n = Number(raw);
return String(n) === raw && constants.MANUAL_FETCH_LINES_OPTIONS.indexOf(n) >= 0 ? n : null;
},
saveManualFetchLines() {
storeValue('fwlive-manual-lines', String(this.manualFetchLines));
},
autoFetchLines() {
return Math.min(Math.max(this.rowLimit * 4, 100), constants.FETCH_LINES_MAX);
},
snapManualFetchLines(lines) {
const options = constants.MANUAL_FETCH_LINES_OPTIONS;
let snapped = options[0];
for (let i = 0; i < options.length; i++) {
if (options[i] > lines) break;
snapped = options[i];
}
return snapped;
},
resolveRpcPreferences() {
if (this.rpcPreferencesResolved) return;
this.applyRowLimit(this.readRowLimit());
this.fetchMode = this.readFetchMode();
const storedManual = this.readManualFetchLines();
this.manualFetchLinesExplicit = storedManual !== null;
this.manualFetchLines =
storedManual === null ? this.snapManualFetchLines(this.autoFetchLines()) : storedManual;
let hashLimit = null;
let hashMode = null;
let hashManual = null;
const entries = this.hashEntries();
for (let i = 0; i < entries.length; i++) {
const key = entries[i].key;
const val = entries[i].val;
if (key === 'limit') {
const n = Number(val);
if (String(n) === val && constants.ROW_LIMIT_OPTIONS.indexOf(n) >= 0) hashLimit = n;
continue;
}
if (key === 'poll') {
if (constants.FETCH_MODE_OPTIONS.indexOf(val) >= 0) hashMode = val;
continue;
}
if (key === 'maxraw') {
const n = Number(val);
if (String(n) === val && constants.MANUAL_FETCH_LINES_OPTIONS.indexOf(n) >= 0)
hashManual = n;
}
}
if (hashLimit !== null) {
this.applyRowLimit(hashLimit);
this.saveRowLimit();
}
if (hashMode !== null) {
this.fetchMode = hashMode;
this.saveFetchMode();
}
if (hashManual !== null && this.fetchMode === 'manual') {
this.manualFetchLines = hashManual;
this.manualFetchLinesExplicit = true;
this.saveManualFetchLines();
}
if (storedManual === null && !(hashManual !== null && this.fetchMode === 'manual')) {
this.manualFetchLines = this.snapManualFetchLines(this.autoFetchLines());
}
this.rpcPreferencesResolved = true;
},
requestedFetchLines() {
this.resolveRpcPreferences();
if (this.paused)
return this.fetchMode === 'manual' ? this.manualFetchLines : constants.FETCH_LINES_MAX;
return this.fetchMode === 'manual' ? this.manualFetchLines : this.autoFetchLines();
},
updateFillingState(beforeLength, reply, requestedLines) {
if (!this.paused || this.resumeMerge) {
this.fillingBuffer = false;
return;
}
const cap = this.ingestCap();
const grew = this.entries.length > beforeLength;
if (this.entries.length >= cap || !grew) {
this.fillingBuffer = false;
return;
}
const rawCount =
reply &&
typeof reply.messages_received === 'number' &&
isFinite(reply.messages_received) &&
Math.floor(reply.messages_received) === reply.messages_received &&
reply.messages_received >= 0
? reply.messages_received
: null;
const effective =
this.lastPollEffectiveLimit !== null ? this.lastPollEffectiveLimit : requestedLines;
const shortRead = rawCount !== null && rawCount < effective;
this.fillingBuffer = !shortRead && (rawCount === null || rawCount >= effective || grew);
},
applyHash() {
this.resolveRpcPreferences();
const entries = this.hashEntries();
for (let i = 0; i < entries.length; i++) {
const key = entries[i].key;
const val = entries[i].val;
if (key === 'limit' || key === 'poll' || key === 'maxraw') continue;
if (key === 'view') {
if (val === 'advanced' || val === 'detailed') this.viewMode = 'detailed';
else if (val === 'simple') this.viewMode = 'simple';
@@ -732,6 +877,11 @@ return view.extend({
const epoch = this.pollEpoch;
const resumeMerge = !!this.resumeMerge;
const fetchLines = this.requestedFetchLines();
const beforeLength = this.entries.length;
this.lastPollRequestedLines = fetchLines;
this.lastPollReturnedMessages = null;
this.lastPollEffectiveLimit = null;
const t0 = this.nowMs();
let reply;
let errored = false;
@@ -739,9 +889,6 @@ return view.extend({
/* Raw logd lines, not post-filter rows. Fetch a multiple of the
* display limit so mixed syslog still fills the table; pause
* reads the ring cap so the buffer can catch up. */
const fetchLines = this.paused
? constants.FETCH_LINES_MAX
: Math.min(Math.max(this.rowLimit * 4, 100), constants.FETCH_LINES_MAX);
reply = await callFwlivePoll({
addresses: [String(fetchLines)]
});
@@ -756,12 +903,14 @@ return view.extend({
if (!reply || typeof reply !== 'object' || Array.isArray(reply)) {
this.lastPollError = true;
this.fillingBuffer = false;
this.notePollRtt(rtt, true);
this.updateAdaptiveBanner();
return;
}
if (reply.error) {
this.lastPollError = true;
this.fillingBuffer = false;
this.notePollRtt(rtt, true);
this.updateAdaptiveBanner();
return;
@@ -769,18 +918,32 @@ return view.extend({
const raw = reply.log;
if (!Array.isArray(raw)) {
this.lastPollError = true;
this.fillingBuffer = false;
this.notePollRtt(rtt, true);
this.updateAdaptiveBanner();
return;
}
this.lastPollError = false;
this.serverAdaptive = reply.adaptive === 0 || reply.adaptive === false ? 0 : 1;
if (reply.adaptive === 0 || reply.adaptive === false) this.serverAdaptive = 0;
else if (reply.adaptive === 1 || reply.adaptive === true) this.serverAdaptive = 1;
else this.serverAdaptive = undefined;
this.serverTruncated = reply.truncated ? 1 : 0;
this.serverShed =
reply.shed && typeof reply.shed === 'object' && !Array.isArray(reply.shed)
? reply.shed
: null;
this.lastPollReturnedMessages = raw.length;
if (
this.serverAdaptive === 1 &&
typeof reply.effective_limit === 'number' &&
isFinite(reply.effective_limit) &&
Math.floor(reply.effective_limit) === reply.effective_limit &&
reply.effective_limit >= 1 &&
reply.effective_limit <= constants.FETCH_LINES_MAX
) {
this.lastPollEffectiveLimit = reply.effective_limit;
}
this.notePollRtt(rtt, errored);
this.updateAdaptiveBanner();
@@ -803,6 +966,7 @@ return view.extend({
rowLimit: this.rowLimit,
fetchLinesMax: constants.FETCH_LINES_MAX
});
this.updateFillingState(beforeLength, reply, fetchLines);
/* A stale request returns above. Keep this obligation until a current
* request has actually applied the merged batch. */
if (resumeMerge) this.resumeMerge = false;
@@ -835,6 +999,7 @@ return view.extend({
const bits = [];
if (this.paused) {
if (this.pauseBufferLoading) bits.push(_('loading buffer'));
if (this.fillingBuffer) bits.push(_('buffer filling'));
}
const cap = this.ingestCap();
@@ -954,6 +1119,7 @@ return view.extend({
},
resumePollingAfterVisible() {
if (this.coordinatorDisposed) return;
this.bumpPollEpoch();
this.resetRttHistory();
if (!this.pollFn) this.pollFn = this.pollData.bind(this);
@@ -999,18 +1165,55 @@ return view.extend({
this.visibilityBound = false;
},
disposeCoordinator() {
if (this.coordinatorDisposed) return;
this.coordinatorDisposed = true;
this.bumpPollEpoch();
this.stopPollingForHidden();
this.unbindVisibility();
this.pollFn = null;
this.pollRequestQueued = false;
const queued = this.pollRequestQueuedWaiters;
this.pollRequestQueuedWaiters = [];
for (let i = 0; i < queued.length; i++) queued[i].resolve();
},
updateAdaptiveBanner() {
const el = document.getElementById('fwlive-adaptive');
if (!el) return;
if (!el.style) el.style = { display: '' };
if (!this.clientBackoffEnabled()) {
el.style.display = 'none';
el.textContent = '';
return;
}
const parts = [];
const mode = this.fetchMode === 'manual' ? _('Manual') : _('Auto');
if (this.lastPollRequestedLines !== null && this.lastPollReturnedMessages !== null) {
if (
this.serverAdaptive === 1 &&
this.lastPollEffectiveLimit !== null &&
this.lastPollEffectiveLimit < this.lastPollRequestedLines
) {
parts.push(
_(
'%s · requested up to %d raw lines · server limited fetch to %d · %d firewall messages returned'
).format(
mode,
this.lastPollRequestedLines,
this.lastPollEffectiveLimit,
this.lastPollReturnedMessages
)
);
} else {
parts.push(
_('%s · requested up to %d raw lines · %d firewall messages returned').format(
mode,
this.lastPollRequestedLines,
this.lastPollReturnedMessages
)
);
}
}
if (this.serverAdaptive === undefined) parts.push(_('Server protection state is unknown.'));
else if (this.serverAdaptive === 0)
parts.push(_('Server adaptive protection is disabled.'));
if (this.degradedSampling)
parts.push(_('Degraded — sampling (slow poll RTT; cadence reduced).'));
if (this.serverShed && this.serverShed.limit)
@@ -1030,6 +1233,22 @@ return view.extend({
}
},
fetchModeOptions() {
return optionNodes([
['auto', _('Auto')],
['manual', _('Manual')]
]);
},
manualFetchLinesOptions() {
const pairs = [];
for (let i = 0; i < constants.MANUAL_FETCH_LINES_OPTIONS.length; i++) {
const n = constants.MANUAL_FETCH_LINES_OPTIONS[i];
pairs.push([String(n), String(n)]);
}
return optionNodes(pairs);
},
summaryListText(label, values) {
const lines = [label + ':'];
if (!Array.isArray(values) || !values.length) {
@@ -1284,6 +1503,8 @@ return view.extend({
const label = document.getElementById('fwlive-watch-label');
const pauseBtn = document.getElementById('fwlive-pause');
const sel = document.getElementById('fwlive-limit');
const modeSel = document.getElementById('fwlive-fetch-mode');
const manualSel = document.getElementById('fwlive-manual-lines');
const hostCb = document.getElementById('fwlive-show-hostnames');
if (map) {
@@ -1297,6 +1518,11 @@ return view.extend({
if (label) label.textContent = this.paused ? _('Paused') : _('Watching');
if (pauseBtn) pauseBtn.textContent = this.paused ? _('Resume') : _('Pause');
if (sel) sel.value = String(this.rowLimit);
if (modeSel) modeSel.value = this.fetchMode;
if (manualSel) {
manualSel.value = String(this.manualFetchLines);
manualSel.disabled = this.fetchMode !== 'manual';
}
if (hostCb) hostCb.checked = !!this.showHostnames;
this.updateRowTintUi();
},
@@ -1312,6 +1538,29 @@ return view.extend({
else this.renderRows(true);
},
onFetchModeChange(ev) {
const mode = ev && ev.target ? ev.target.value : '';
if (constants.FETCH_MODE_OPTIONS.indexOf(mode) < 0) return;
this.fetchMode = mode;
if (mode === 'manual' && !this.manualFetchLinesExplicit) {
this.manualFetchLines = this.snapManualFetchLines(this.autoFetchLines());
this.manualFetchLinesExplicit = true;
}
this.saveFetchMode();
if (mode === 'manual') this.saveManualFetchLines();
this.updateStreamControlsUi();
this.updateHash(this.readFilters());
},
onManualFetchLinesChange(ev) {
const n = parseInt(ev && ev.target ? ev.target.value : '', 10);
if (constants.MANUAL_FETCH_LINES_OPTIONS.indexOf(n) < 0) return;
this.manualFetchLines = n;
this.manualFetchLinesExplicit = true;
this.saveManualFetchLines();
this.updateHash(this.readFilters());
},
onPauseClick() {
const wasPaused = this.paused;
this.paused = !this.paused;
@@ -1332,6 +1581,7 @@ return view.extend({
}
if (wasPaused && !this.paused) {
this.fillingBuffer = false;
this.followLive = true;
/* Merge pause buffer with the first live poll — do not replace. */
this.resumeMerge = true;
@@ -1761,6 +2011,13 @@ return view.extend({
const limitSel = document.getElementById('fwlive-limit');
if (limitSel) limitSel.addEventListener('change', this.onRowLimitChange.bind(this));
const modeSel = document.getElementById('fwlive-fetch-mode');
if (modeSel) modeSel.addEventListener('change', this.onFetchModeChange.bind(this));
const manualSel = document.getElementById('fwlive-manual-lines');
if (manualSel)
manualSel.addEventListener('change', this.onManualFetchLinesChange.bind(this));
const hostCb = document.getElementById('fwlive-show-hostnames');
if (hostCb) hostCb.addEventListener('change', this.onShowHostnamesChange.bind(this));
@@ -1779,7 +2036,7 @@ return view.extend({
},
requestPoll() {
if (this.isTabHidden()) return Promise.resolve();
if (this.coordinatorDisposed || this.isTabHidden()) return Promise.resolve();
const waiter = {};
const promise = new Promise(function (resolve) {
@@ -1836,7 +2093,7 @@ return view.extend({
/* If visibility changed while the request was active, retain the
* queued intent until the visible catch-up can start it. */
if (this.pollRequestQueued && !this.isTabHidden()) {
if (this.pollRequestQueued && !this.coordinatorDisposed && !this.isTabHidden()) {
const queued = this.pollRequestQueuedWaiters;
this.pollRequestQueuedWaiters = [];
this.pollRequestQueued = false;
@@ -1877,6 +2134,9 @@ return view.extend({
},
load() {
/* RPC-affecting preferences must precede poll registration and the first
* request; filter widgets still restore in addFooter after render. */
this.resolveRpcPreferences();
this.bindVisibility();
if (!this.pollFn) {
this.pollFn = this.pollData.bind(this);
@@ -1887,15 +2147,7 @@ return view.extend({
window.addEventListener(
'pagehide',
function () {
this.unbindVisibility();
if (this.pollFn) {
try {
poll.remove(this.pollFn);
} catch (e) {
/* poll gone */
}
this.pollFn = null;
}
this.disposeCoordinator();
if (this.filterInputTimer) {
clearTimeout(this.filterInputTimer);
this.filterInputTimer = null;
@@ -2077,6 +2329,36 @@ return view.extend({
this.limitSelectOptions()
)
]),
E('label', { 'class': 'fwlive-display-ctl', 'for': 'fwlive-fetch-mode' }, [
_('Fetch budget'),
E(
'select',
{
'id': 'fwlive-fetch-mode',
'class': 'cbi-input-select',
'aria-controls': 'fwlive-manual-lines'
},
this.fetchModeOptions()
)
]),
E(
'label',
{ 'class': 'fwlive-display-ctl', 'for': 'fwlive-manual-lines' },
[
_('Maximum raw lines'),
E(
'select',
{
'id': 'fwlive-manual-lines',
'class': 'cbi-input-select',
'title': _(
'Manual still uses server protection and poll cadence'
)
},
this.manualFetchLinesOptions()
)
]
),
E('label', { 'class': 'fwlive-display-ctl' }, [
E('input', {
'id': 'fwlive-row-tint-toggle',
@@ -2236,6 +2518,16 @@ return view.extend({
'Display options on the bar set Limit, row tint, palette, and hostnames.'
)
]),
E('li', {}, [
_(
'Fetch budget controls raw log lines per poll. Auto derives from Limit; Manual selects a bounded maximum.'
)
]),
E('li', {}, [
_(
'Manual still uses server protection and poll cadence; it changes the fetch budget, not the polling interval.'
)
]),
E('li', {}, [
_(
'For a responsive table on a weak device, keep Limit at 250 rows or below. The weak-device cap affects rendered rows; the buffer can still retain more.'
@@ -2306,7 +2598,6 @@ return view.extend({
this.hostnameFailed = new Map();
this.resolveGeneration = 0;
this.lastPollError = false;
this.applyRowLimit(this.readRowLimit());
this.applyHash();
this.attachHandlers();
this.applyRowTintMode();
+38
View File
@@ -790,3 +790,41 @@ msgstr "Häufigste Regeln"
msgid "Top talkers"
msgstr "Häufigste Absender"
msgid "Auto"
msgstr "Auto"
msgid "Fetch budget"
msgstr "Fetch budget"
msgid "Maximum raw lines"
msgstr "Maximum raw lines"
msgid "Manual"
msgstr "Manual"
msgid "Manual still uses server protection and poll cadence"
msgstr "Manual still uses server protection and poll cadence"
msgid "%s · requested up to %d raw lines · %d firewall messages returned"
msgstr "%s · requested up to %d raw lines · %d firewall messages returned"
msgid "%s · requested up to %d raw lines · server limited fetch to %d · %d firewall messages returned"
msgstr "%s · requested up to %d raw lines · server limited fetch to %d · %d firewall messages returned"
msgid "Server adaptive protection is disabled."
msgstr "Server adaptive protection is disabled."
msgid "Server protection state is unknown."
msgstr "Server protection state is unknown."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2250
msgid "Fetch budget controls raw log lines per poll. Auto derives from Limit; Manual selects a bounded maximum."
msgstr "Fetch budget controls raw log lines per poll. Auto derives from Limit; Manual selects a bounded maximum."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2255
msgid "Manual still uses server protection and poll cadence; it changes the fetch budget, not the polling interval."
msgstr "Manual still uses server protection and poll cadence; it changes the fetch budget, not the polling interval."
msgid "buffer filling"
msgstr "buffer filling"
+38
View File
@@ -795,3 +795,41 @@ msgstr "Частые правила"
msgid "Top talkers"
msgstr "Частые источники"
msgid "Auto"
msgstr "Auto"
msgid "Fetch budget"
msgstr "Fetch budget"
msgid "Maximum raw lines"
msgstr "Maximum raw lines"
msgid "Manual"
msgstr "Manual"
msgid "Manual still uses server protection and poll cadence"
msgstr "Manual still uses server protection and poll cadence"
msgid "%s · requested up to %d raw lines · %d firewall messages returned"
msgstr "%s · requested up to %d raw lines · %d firewall messages returned"
msgid "%s · requested up to %d raw lines · server limited fetch to %d · %d firewall messages returned"
msgstr "%s · requested up to %d raw lines · server limited fetch to %d · %d firewall messages returned"
msgid "Server adaptive protection is disabled."
msgstr "Server adaptive protection is disabled."
msgid "Server protection state is unknown."
msgstr "Server protection state is unknown."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2250
msgid "Fetch budget controls raw log lines per poll. Auto derives from Limit; Manual selects a bounded maximum."
msgstr "Fetch budget controls raw log lines per poll. Auto derives from Limit; Manual selects a bounded maximum."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2255
msgid "Manual still uses server protection and poll cadence; it changes the fetch budget, not the polling interval."
msgstr "Manual still uses server protection and poll cadence; it changes the fetch budget, not the polling interval."
msgid "buffer filling"
msgstr "buffer filling"
@@ -735,3 +735,53 @@ msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1959
msgid "Show rows"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1134
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1182
msgid "Auto"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2283
msgid "Fetch budget"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2295
msgid "Maximum raw lines"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1134
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1183
msgid "Manual"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2301
msgid "Manual still uses server protection and poll cadence"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1151
msgid "%s · requested up to %d raw lines · %d firewall messages returned"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1142
msgid "%s · requested up to %d raw lines · server limited fetch to %d · %d firewall messages returned"
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1160
msgid "Server adaptive protection is disabled."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:1159
msgid "Server protection state is unknown."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2250
msgid "Fetch budget controls raw log lines per poll. Auto derives from Limit; Manual selects a bounded maximum."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2255
msgid "Manual still uses server protection and poll cadence; it changes the fetch budget, not the polling interval."
msgstr ""
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:997
msgid "buffer filling"
msgstr ""
@@ -759,3 +759,41 @@ msgstr "主要规则"
msgid "Top talkers"
msgstr "主要通信方"
msgid "Auto"
msgstr "Auto"
msgid "Fetch budget"
msgstr "Fetch budget"
msgid "Maximum raw lines"
msgstr "Maximum raw lines"
msgid "Manual"
msgstr "Manual"
msgid "Manual still uses server protection and poll cadence"
msgstr "Manual still uses server protection and poll cadence"
msgid "%s · requested up to %d raw lines · %d firewall messages returned"
msgstr "%s · requested up to %d raw lines · %d firewall messages returned"
msgid "%s · requested up to %d raw lines · server limited fetch to %d · %d firewall messages returned"
msgstr "%s · requested up to %d raw lines · server limited fetch to %d · %d firewall messages returned"
msgid "Server adaptive protection is disabled."
msgstr "Server adaptive protection is disabled."
msgid "Server protection state is unknown."
msgstr "Server protection state is unknown."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2250
msgid "Fetch budget controls raw log lines per poll. Auto derives from Limit; Manual selects a bounded maximum."
msgstr "Fetch budget controls raw log lines per poll. Auto derives from Limit; Manual selects a bounded maximum."
#: openwrt-feed/luci-app-fwlive/htdocs/luci-static/resources/view/status/fwlive.js:2255
msgid "Manual still uses server protection and poll cadence; it changes the fetch budget, not the polling interval."
msgstr "Manual still uses server protection and poll cadence; it changes the fetch budget, not the polling interval."
msgid "buffer filling"
msgstr "buffer filling"
@@ -443,13 +443,14 @@ fwlive_adaptive_is_hot() {
# Merge adaptive siblings into a JSON object ending with }. A successful
# filter already carries the exact messages_received count; do not append a
# duplicate key in that case.
# Args: json_body shed_flag limit truncated messages_received
# Args: json_body shed_flag limit truncated messages_received success_flag
fwlive_adaptive_merge_reply() {
_body=$1
_shed=$2
_limit=$3
_trunc=$4
_msgs=$5
_success=${6:-0}
_adapt=1
fwlive_adaptive_enabled || _adapt=0
case "$_body" in
@@ -457,6 +458,8 @@ fwlive_adaptive_merge_reply() {
*) printf '%s' "$_body"; return 0 ;;
esac
_base=${_body%\}}
_effective=
[ "$_success" = 1 ] && _effective=",\"effective_limit\":$_limit"
_has_msgs=0
case "$_body" in
*',"messages_received":'*) _has_msgs=1 ;;
@@ -471,18 +474,18 @@ fwlive_adaptive_merge_reply() {
fi
if [ "$_shed" = 1 ]; then
if [ "$_has_msgs" = 1 ]; then
printf '%s,"adaptive":1,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
"$_base" "$_trunc" "$_limit"
printf '%s,"adaptive":1%s,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
"$_base" "$_effective" "$_trunc" "$_limit"
else
printf '%s,"adaptive":1,"messages_received":%s,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
"$_base" "$_msgs" "$_trunc" "$_limit"
printf '%s,"adaptive":1,"messages_received":%s%s,"truncated":%s,"shed":{"level":"hot","limit":%s}}' \
"$_base" "$_msgs" "$_effective" "$_trunc" "$_limit"
fi
else
if [ "$_has_msgs" = 1 ]; then
printf '%s,"adaptive":1,"truncated":%s}' "$_base" "$_trunc"
printf '%s,"adaptive":1%s,"truncated":%s}' "$_base" "$_effective" "$_trunc"
else
printf '%s,"adaptive":1,"messages_received":%s,"truncated":%s}' \
"$_base" "$_msgs" "$_trunc"
printf '%s,"adaptive":1,"messages_received":%s%s,"truncated":%s}' \
"$_base" "$_msgs" "$_effective" "$_trunc"
fi
fi
}
+2 -2
View File
@@ -440,7 +440,7 @@ fetch_firewall_logs() {
# Do not record ~0 ms as cold: failure is not healthy fast processing
# and must not clear an existing hot/shed cap (#329 Hermes Q1).
out='{"log":[],"error":"log_read_failed"}'
fwlive_adaptive_merge_reply "$out" "$shed_flag" "$lines" 0 0
fwlive_adaptive_merge_reply "$out" "$shed_flag" "$lines" 0 0 0
return 0
fi
@@ -486,7 +486,7 @@ fetch_firewall_logs() {
_trunc=0
[ "$shed_flag" = 1 ] && _trunc=1
[ "$lines" -lt "$requested" ] && _trunc=1
fwlive_adaptive_merge_reply "$out" "$shed_flag" "$lines" "$_trunc" 0
fwlive_adaptive_merge_reply "$out" "$shed_flag" "$lines" "$_trunc" 0 "$_filter_ok"
}
poll_logs() {
+4 -4
View File
@@ -1,14 +1,14 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=mihomo-meta
PKG_VERSION:=1.19.30
PKG_RELEASE:=14
PKG_VERSION:=1.19.31
PKG_RELEASE:=15
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/MetaCubeX/mihomo.git
PKG_SOURCE_VERSION:=v1.19.30
PKG_SOURCE_VERSION:=v1.19.31
PKG_MIRROR_HASH:=skip
PKG_LICENSE:=GPL3.0+
@@ -18,7 +18,7 @@ PKG_BUILD_DEPENDS:=golang/host
PKG_BUILD_PARALLEL:=1
PKG_BUILD_FLAGS:=no-mips16
PKG_BUILD_VERSION:=v1.19.30
PKG_BUILD_VERSION:=v1.19.31
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_BUILD_TIME:=$(shell date -u -Iseconds)