mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-05 18:44:03 +08:00
🎈 Sync 2026-11-05 03:32:18
This commit is contained in:
1 parent
fd75287766
commit
254dca6210
31 files changed
+509
-160
No files matched your search
+3
-3
@@ -5,14 +5,14 @@ PKG_UPSTREAM_VERSION:=1.19.33
|
||||
PKG_UPSTREAM_TAG:=v$(PKG_UPSTREAM_VERSION)
|
||||
PKG_SHORT_SHA:=3f27ef3
|
||||
|
||||
PKG_VERSION:=2026.10.27-r5
|
||||
PKG_RELEASE:=22
|
||||
PKG_VERSION:=2026.11.04.$(PKG_UPSTREAM_VERSION)
|
||||
PKG_RELEASE:=23
|
||||
|
||||
PKG_SOURCE:=$(PKG_NAME)-stable-$(PKG_UPSTREAM_TAG)-$(PKG_SHORT_SHA).tar.gz
|
||||
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-stable-$(PKG_UPSTREAM_TAG)-$(PKG_SHORT_SHA)
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
|
||||
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-clashoo/releases/download/mihomo-src
|
||||
PKG_SOURCE_VERSION:=aab968687034c10cd073a21293a3dc200a95171c
|
||||
PKG_SOURCE_VERSION:=2d264afc85d34b9a70e07c8505e7e94c235d79c9
|
||||
PKG_HASH:=skip
|
||||
PKG_BUILD_VERSION:=$(PKG_UPSTREAM_TAG)
|
||||
|
||||
|
||||
@@ -1551,6 +1551,34 @@ _ensure_cn_domain_rule_provider() {
|
||||
' "$CONFIG_YAML" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# fake-ip-filter 引用当前配置没定义的 rule-set 时,mihomo 会拒绝启动
|
||||
# (dns.fake-ip-filter[i] "RULE-SET,x,real-ip": rule-set 'x' not found)。
|
||||
# 切换配置后最常出现,启动前丢掉并记日志,免得整机莫名断网。
|
||||
prune_fakeip_missing_rulesets() {
|
||||
command -v yq >/dev/null 2>&1 || return 0
|
||||
[ -s "$CONFIG_YAML" ] || return 0
|
||||
|
||||
local providers entry _rs
|
||||
providers="$(yq -M e -r '.rule-providers // {} | keys | .[]' "$CONFIG_YAML" 2>/dev/null)"
|
||||
|
||||
yq -M e -r '.dns."fake-ip-filter" // [] | .[]' "$CONFIG_YAML" 2>/dev/null |
|
||||
while IFS= read -r entry; do
|
||||
case "$entry" in
|
||||
RULE-SET,*|rule-set:*) ;;
|
||||
*) continue ;;
|
||||
esac
|
||||
_rs="${entry#RULE-SET,}"
|
||||
_rs="${_rs#rule-set:}"
|
||||
_rs="${_rs%%,*}"
|
||||
[ -n "$_rs" ] || continue
|
||||
printf '%s\n' "$providers" | grep -Fxq "$_rs" && continue
|
||||
CLASHOO_DROP_ENTRY="$entry" yq -M e -i \
|
||||
'.dns."fake-ip-filter" = [.dns."fake-ip-filter"[] | select(. != strenv(CLASHOO_DROP_ENTRY))]' \
|
||||
"$CONFIG_YAML" 2>/dev/null || true
|
||||
log_msg "Skipped fake-ip-filter entry, rule-set not found: $entry" "已跳过 fake-ip-filter 条目,规则集不存在:$entry"
|
||||
done
|
||||
}
|
||||
|
||||
append_luci_fake_ip_filter_extras() {
|
||||
[ -n "${CLASHOO_KEEP_FAKEIP_FILTER:-}" ] || return 0
|
||||
command -v yq >/dev/null 2>&1 || return 0
|
||||
@@ -2252,6 +2280,7 @@ _start_prepare_config() {
|
||||
[ "$_reload_candidate" = "1" ] || _prepare_geo_bootstrap_mode
|
||||
_remap_dns_system_nameserver >/dev/null 2>&1
|
||||
ensure_dns_cn_policy_fallback >/dev/null 2>&1
|
||||
prune_fakeip_missing_rulesets
|
||||
|
||||
|
||||
if [ ! -f "$CONFIG_YAML" ];then
|
||||
|
||||
@@ -175,7 +175,7 @@ function dns_server(address, protocol, port) {
|
||||
|
||||
let dns_roles = {};
|
||||
uci.foreach('clashoo', 'dnsservers', function(sec) {
|
||||
if (b(sec.enabled) == false) return;
|
||||
if (sec.enabled != null && b(sec.enabled) == false) return;
|
||||
let role = s(sec.ser_type, 'nameserver');
|
||||
if (role == 'fallback' && cfg['dns']['enhanced-mode'] == 'fake-ip') return;
|
||||
let srv = dns_server(sec.ser_address, sec.protocol, sec.ser_port);
|
||||
@@ -202,7 +202,7 @@ if (length(bootstrap) && (!dns_present['default-nameserver'] || dns_force['defau
|
||||
let policies = {};
|
||||
let proxy_policies = {};
|
||||
uci.foreach('clashoo', 'dns_policy', function(sec) {
|
||||
if (b(sec.enabled) == false) return;
|
||||
if (sec.enabled != null && b(sec.enabled) == false) return;
|
||||
let role = s(sec.policy_type, 'nameserver-policy');
|
||||
if (role != 'nameserver-policy' && role != 'proxy-server-nameserver-policy') return;
|
||||
let matcher = trim(sec.matcher || '');
|
||||
@@ -252,7 +252,7 @@ if (need_cn_rs) {
|
||||
if (ab('authentication')) {
|
||||
cfg['authentication'] = [];
|
||||
uci.foreach('clashoo', 'authentication', function(sec) {
|
||||
if (b(sec.enabled) == false) return;
|
||||
if (sec.enabled != null && b(sec.enabled) == false) return;
|
||||
let user = trim(sec.username || '');
|
||||
let pass = trim(sec.password || '');
|
||||
if (user) push(cfg['authentication'], user + ':' + pass);
|
||||
@@ -262,7 +262,7 @@ if (ab('authentication')) {
|
||||
/* ── hosts ────────────────────────────────────────── */
|
||||
cfg['hosts'] = {};
|
||||
uci.foreach('clashoo', 'hosts', function(sec) {
|
||||
if (b(sec.enabled) == false) return;
|
||||
if (sec.enabled != null && b(sec.enabled) == false) return;
|
||||
let domain = trim(sec.adress || '');
|
||||
let ip = trim(sec.ip || '');
|
||||
if (domain && ip) cfg['hosts'][domain] = ip;
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=frpc-next
|
||||
PKG_VERSION:=0.71.1
|
||||
PKG_RELEASE:=2
|
||||
PKG_VERSION:=0.72.0
|
||||
PKG_RELEASE:=3
|
||||
|
||||
# Architecture mapping for pre-built binaries
|
||||
ifeq ($(ARCH),mipsel)
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=honk
|
||||
PKG_VERSION:=2026.11.03~88d470c
|
||||
PKG_RELEASE:=29
|
||||
PKG_VERSION:=2026.11.04~88d470c
|
||||
PKG_RELEASE:=30
|
||||
|
||||
HONK_SRC_URL:=https://github.com/daeuniverse/honk.git
|
||||
HONK_SRC_VERSION:=88d470c662e0ee7a3de3e67afc6129f67e66c1bf
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=lua-periphery
|
||||
PKG_VERSION:=2.5.1
|
||||
PKG_RELEASE:=2
|
||||
PKG_VERSION:=2.5.3
|
||||
PKG_RELEASE:=3
|
||||
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://github.com/vsergeev/lua-periphery.git
|
||||
PKG_SOURCE_VERSION:=6c966f76a1ba280d71871d737f4802c34d72ff55
|
||||
PKG_SOURCE_VERSION:=3c60e814022353157d1c47b5acc126bb6eb5d12f
|
||||
PKG_MIRROR_HASH:=skip
|
||||
|
||||
PKG_LICENSE:=MIT
|
||||
|
||||
@@ -35,8 +35,6 @@ const DANGEROUS_RW_PATHS = ['/', '/etc', '/root', '/usr', '/bin', '/sbin', '/lib
|
||||
|
||||
const POLL_INTERVAL = 5;
|
||||
|
||||
const RUNNING_SPAN = `<span style="color: green; font-weight: bold">${_('Running')}</span>`;
|
||||
const NOT_RUNNING_SPAN = `<span style="color: red; font-weight: bold">${_('Not running')}</span>`;
|
||||
|
||||
const STORAGE_KEY = 'luci-app-adguardhome';
|
||||
const STORAGE_KEY_CORE = 'luci-app-adguardhome_core_update';
|
||||
@@ -213,35 +211,71 @@ async function getLatestRelease(channel, force) {
|
||||
}
|
||||
}
|
||||
|
||||
// Hint appended to the "Current Version" row; onUpdate runs when "Update" is clicked.
|
||||
// Built from DOM nodes only (no HTML strings).
|
||||
// Hint appended to the "Current Version" row. The whole text is the button: a click
|
||||
// runs onUpdate (go to the Core Update tab). A span, not a link, so the theme's link
|
||||
// colour / underline never applies. Built from DOM nodes only (no HTML strings).
|
||||
function buildVersionHint(release, onUpdate) {
|
||||
return E('span', { 'id': 'agh-version-hint', 'style': 'margin-left: 12px;' }, [
|
||||
E('strong', { 'style': 'color: var(--warn-color-high, #d9822b);' },
|
||||
'→ ' + _('New version %s available').replace('%s', release.tag)),
|
||||
' ',
|
||||
E('a', { 'href': release.url, 'target': '_blank', 'rel': 'noopener noreferrer' }, _('Release notes')),
|
||||
' | ',
|
||||
E('a', {
|
||||
'href': '#',
|
||||
'click': function(ev) {
|
||||
return E('span', {
|
||||
'id': 'agh-version-hint',
|
||||
'role': 'button',
|
||||
'tabindex': '0',
|
||||
'title': _('Go to Core Update'),
|
||||
'style': 'margin-left: 12px; cursor: pointer;',
|
||||
'click': function(ev) {
|
||||
ev.preventDefault();
|
||||
onUpdate();
|
||||
},
|
||||
'keydown': function(ev) {
|
||||
if (ev.key === 'Enter' || ev.key === ' ') {
|
||||
ev.preventDefault();
|
||||
onUpdate();
|
||||
}
|
||||
}, _('Update'))
|
||||
]);
|
||||
}
|
||||
}, E('strong', { 'style': 'color: var(--warn-color-high, #d9822b);' },
|
||||
'\u2192 ' + _('New version %s available').replace('%s', release.tag)));
|
||||
}
|
||||
|
||||
// Tighter spacing for the "Current Version" / "Service Status" rows. The themes
|
||||
// give every row a lot of padding; !important is needed to win over them.
|
||||
const STATUS_COMPACT_CSS =
|
||||
'.agh-status-row { margin: 0 !important; padding-top: 2px !important; padding-bottom: 2px !important; min-height: 0 !important; }' +
|
||||
'.agh-status-row .cbi-value-title, .agh-status-row .cbi-value-field { margin: 0 !important; padding-top: 0 !important; padding-bottom: 0 !important; min-height: 0 !important; line-height: 1.5 !important; }';
|
||||
// Header card above the settings: one line with the Service Status pill (which is also
|
||||
// the Enable switch) and the Current Version. The pill follows the style of the Main
|
||||
// Switch of luci-app-bypass.
|
||||
const HEAD_CSS =
|
||||
'.agh-head { --agh-on-fg:#1a7f37; --agh-on-bg:#dafbe1; --agh-warn-fg:#9a6700; --agh-warn-bg:#fff8c5; --agh-off-fg:#82071e; --agh-off-bg:#ffebe9; display:flex; flex-direction:column; gap:.5rem; margin:.5rem 0 1rem; padding:.7rem 1rem; border-radius:var(--radius-card, 6px); background:var(--surface-raised, rgba(128,128,128,.06)); box-shadow:0 4px 8px rgba(0,0,0,.03); }' +
|
||||
':root[data-argon-mode="dark"] .agh-head, :root[data-darkmode="true"] .agh-head { --agh-on-fg:#7ee2a0; --agh-on-bg:rgba(46,160,67,.22); --agh-warn-fg:#e3b341; --agh-warn-bg:rgba(187,128,9,.22); --agh-off-fg:#ffa9ae; --agh-off-bg:rgba(248,81,73,.18); }' +
|
||||
'.agh-head-row { display:flex; align-items:center; flex-wrap:wrap; gap:.4rem 2rem; }' +
|
||||
'.agh-head-item { display:inline-flex; align-items:center; flex-wrap:wrap; gap:.25rem .75rem; line-height:1.5; }' +
|
||||
'.agh-head-note { display:flex; align-items:flex-start; gap:.5rem; line-height:1.5; padding:.45rem .75rem; border-left:4px solid var(--error-color-high, #d9534f); border-radius:4px; background:rgba(217,83,79,.12); color:var(--error-color-high, #d9534f); font-weight:600; }' +
|
||||
'.agh-head-note-icon { flex:none; font-size:1.15em; line-height:1.3; }' +
|
||||
'.agh-switch { display:inline-flex; align-items:center; gap:.4rem; height:auto; min-height:0; line-height:1.4; padding:.25rem .8rem; margin:0; border-radius:999px; font-weight:600; font-size:.9rem; border:0; box-shadow:none; cursor:pointer; transition:filter .15s; }' +
|
||||
'.agh-switch:hover { filter:brightness(.95); }' +
|
||||
'.agh-switch::before { content:""; width:.55rem; height:.55rem; border-radius:50%; background:currentColor; }' +
|
||||
'.agh-switch.on { color:var(--agh-on-fg); background:var(--agh-on-bg); }' +
|
||||
'.agh-switch.warn { color:var(--agh-warn-fg); background:var(--agh-warn-bg); }' +
|
||||
'.agh-switch.off { color:var(--agh-off-fg); background:var(--agh-off-bg); }';
|
||||
|
||||
// The clickable <a> of the Core Update tab in the tab menu (not the tab pane, which
|
||||
// carries the same data-tab value).
|
||||
function findCoreUpdateTabLink(root) {
|
||||
return root.querySelector('a[href="#cbi-tab-adguardhome-core_update"]') ||
|
||||
root.querySelector('a[href="#core_update"]') ||
|
||||
root.querySelector('[data-tab="core_update"]');
|
||||
return root.querySelector('.cbi-tabmenu li[data-tab="core_update"] a') ||
|
||||
root.querySelector('li[data-tab="core_update"] a') ||
|
||||
root.querySelector('a[href="#cbi-tab-adguardhome-core_update"]') ||
|
||||
root.querySelector('a[href="#core_update"]');
|
||||
}
|
||||
|
||||
// Click the tab as soon as it exists and is visible; the tab menu is built and
|
||||
// shown/hidden (depends) shortly after render, so retry for a couple of seconds.
|
||||
function activateCoreUpdateTab(root, tries) {
|
||||
const link = findCoreUpdateTabLink(root);
|
||||
const li = link ? link.parentNode : null;
|
||||
// LuCI marks every inactive tab 'cbi-tab-disabled', so that class means nothing here;
|
||||
// a tab that is not available (no visible option yet) has display:none.
|
||||
const hidden = li && li.style.display === 'none';
|
||||
if (link && !hidden) {
|
||||
link.click();
|
||||
return;
|
||||
}
|
||||
if ((tries || 0) < 40) {
|
||||
window.setTimeout(function() { activateCoreUpdateTab(root, (tries || 0) + 1); }, 50);
|
||||
}
|
||||
}
|
||||
|
||||
// The Core Update tab only exists while "Core Update" is ticked in General Settings.
|
||||
@@ -260,9 +294,8 @@ function isCoreUpdateTabEnabled(root) {
|
||||
// tab is created and selected on the next load.
|
||||
function goToCoreUpdateTab(root) {
|
||||
if (isCoreUpdateTabEnabled(root)) {
|
||||
const link = findCoreUpdateTabLink(root);
|
||||
if (link) {
|
||||
link.click();
|
||||
if (findCoreUpdateTabLink(root)) {
|
||||
activateCoreUpdateTab(root, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -299,10 +332,6 @@ async function getStatus() {
|
||||
}
|
||||
}
|
||||
|
||||
function getStatusValue(isRunning) {
|
||||
return isRunning ? RUNNING_SPAN : NOT_RUNNING_SPAN;
|
||||
}
|
||||
|
||||
async function getVersion() {
|
||||
try {
|
||||
const res = await fs.exec('/usr/bin/AdGuardHome', ['--version']);
|
||||
@@ -316,16 +345,6 @@ async function getVersion() {
|
||||
}
|
||||
}
|
||||
|
||||
function updateStatus(node) {
|
||||
const output = node ? node.querySelector('output') : null;
|
||||
return output
|
||||
? async function() {
|
||||
const isRunning = await getStatus();
|
||||
dom.content(output, getStatusValue(isRunning));
|
||||
}
|
||||
: function() {};
|
||||
}
|
||||
|
||||
function validateAbsolutePath(value) {
|
||||
if (!value.startsWith('/')) {
|
||||
return _('Path must be absolute.');
|
||||
@@ -465,22 +484,6 @@ return view.extend({
|
||||
|
||||
const map = new form.Map('adguardhome', _('AdGuard Home'));
|
||||
|
||||
const statusSect = map.section(form.TypedSection, 'status');
|
||||
statusSect.anonymous = true;
|
||||
statusSect.cfgsections = function() { return ['status_section']; };
|
||||
|
||||
const versionOpt = statusSect.option(form.DummyValue, '_version', _('Current Version'));
|
||||
versionOpt.cfgvalue = function() {
|
||||
return version
|
||||
? esc(version)
|
||||
: '<span style="color: var(--error-color-high); font-weight: bold;">' + esc(_('Not installed')) + '</span>';
|
||||
};
|
||||
versionOpt.rawhtml = true;
|
||||
|
||||
const statusOpt = statusSect.option(form.DummyValue, '_status', _('Service Status'));
|
||||
statusOpt.rawhtml = true;
|
||||
statusOpt.cfgvalue = function() { return getStatusValue(isRunning); };
|
||||
|
||||
const mainSect = map.section(form.TypedSection, 'adguardhome');
|
||||
mainSect.anonymous = true;
|
||||
|
||||
@@ -493,9 +496,6 @@ return view.extend({
|
||||
const enabledOpt = mainSect.taboption('general', form.Flag, 'enabled', _('Enable'));
|
||||
enabledOpt.default = '0';
|
||||
enabledOpt.rmempty = false;
|
||||
if (!coreExists) {
|
||||
enabledOpt.description = '<span style="color: var(--error-color-high); font-weight: bold;">' + esc(_('Core binary not found. Enable the service to trigger an automatic download.')) + '</span>';
|
||||
}
|
||||
|
||||
const configFileOpt = mainSect.taboption('general', form.Value, 'config_file', _('Configuration file'));
|
||||
configFileOpt.placeholder = DEFAULT_CONFIG_FILE;
|
||||
@@ -672,21 +672,146 @@ return view.extend({
|
||||
if (sessionStorage.getItem('agh_active_tab') === 'core_update') {
|
||||
sessionStorage.removeItem('agh_active_tab');
|
||||
window.location.hash = 'core_update';
|
||||
window.setTimeout(function() {
|
||||
const tabLink = findCoreUpdateTabLink(rendered);
|
||||
if (tabLink) tabLink.click();
|
||||
}, 50);
|
||||
activateCoreUpdateTab(rendered, 0);
|
||||
}
|
||||
|
||||
// Compact "Current Version" / "Service Status" rows.
|
||||
const versionRow = map.findElement('data-field', versionOpt.cbid('status_section'));
|
||||
const statusRow = map.findElement('data-field', statusOpt.cbid('status_section'));
|
||||
if (versionRow) versionRow.classList.add('agh-status-row');
|
||||
if (statusRow) statusRow.classList.add('agh-status-row');
|
||||
rendered.appendChild(E('style', {}, STATUS_COMPACT_CSS));
|
||||
// ---- Header: Service Status pill (= Enable switch) + Current Version -----------
|
||||
// The pill drives the (hidden) "Enable" checkbox of the form, so saving and applying
|
||||
// work exactly as before. LuCI re-renders the whole form after "Save" and throws away
|
||||
// everything we added, so decorate() builds the header again after every render.
|
||||
const enableSecId = sections.length > 0 ? sections[0]['.name'] : null;
|
||||
|
||||
const statusNode = map.findElement('data-field', statusOpt.cbid('status_section'));
|
||||
poll.add(updateStatus(statusNode), POLL_INTERVAL);
|
||||
function readEnabled() {
|
||||
const el = enableSecId ? enabledOpt.getUIElement(enableSecId) : null;
|
||||
if (el) {
|
||||
return el.getValue() === '1';
|
||||
}
|
||||
return sections.length > 0 && sections[0].enabled === '1';
|
||||
}
|
||||
|
||||
// The pill shows the live service state and, when clicked, flips the Enable setting.
|
||||
let running = Boolean(isRunning);
|
||||
let enableSwitch = null;
|
||||
let versionField = null;
|
||||
let head = null;
|
||||
|
||||
function paintEnableSwitch() {
|
||||
if (!enableSwitch) {
|
||||
return;
|
||||
}
|
||||
const enabled = readEnabled();
|
||||
let cls, text, hint;
|
||||
|
||||
if (running && enabled) {
|
||||
cls = 'on';
|
||||
text = _('Running');
|
||||
hint = _('Click to disable');
|
||||
} else if (running) {
|
||||
cls = 'warn';
|
||||
text = _('Running') + ' \u00b7 ' + _('Disabled');
|
||||
hint = _('Will stop after "Save & Apply". Click to keep it enabled.');
|
||||
} else if (enabled) {
|
||||
cls = 'warn';
|
||||
text = _('Not running');
|
||||
hint = _('Enabled but not running. Click "Save & Apply" to start it, or click to disable.');
|
||||
} else {
|
||||
cls = 'off';
|
||||
text = _('Disabled');
|
||||
hint = _('Click to enable');
|
||||
}
|
||||
enableSwitch.className = 'agh-switch ' + cls;
|
||||
dom.content(enableSwitch, text);
|
||||
enableSwitch.title = hint + ' - ' + _('Apply with "Save & Apply"');
|
||||
}
|
||||
|
||||
// Line the "Service Status" label up with the text of the first tab ("General
|
||||
// Settings") below. The offset depends on the theme, so measure it once the page
|
||||
// is laid out (and again when the window is resized).
|
||||
function alignHead(tries) {
|
||||
const box = head;
|
||||
const tab = rendered.querySelector('.cbi-tabmenu li a');
|
||||
let left = 0;
|
||||
if (!box) {
|
||||
return;
|
||||
}
|
||||
if (tab && tab.getBoundingClientRect().width > 0) {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(tab);
|
||||
left = range.getBoundingClientRect().left;
|
||||
}
|
||||
if (left > 0 && box.getBoundingClientRect().width > 0) {
|
||||
box.style.paddingLeft = '0px';
|
||||
const pad = left - box.getBoundingClientRect().left;
|
||||
box.style.paddingLeft = Math.max(0, Math.round(pad)) + 'px';
|
||||
} else if (tries < 40) {
|
||||
window.setTimeout(function() { if (box === head) alignHead(tries + 1); }, 50);
|
||||
}
|
||||
}
|
||||
|
||||
function decorate() {
|
||||
const enableRow = enableSecId ? map.findElement('data-field', enabledOpt.cbid(enableSecId)) : null;
|
||||
if (enableRow) {
|
||||
enableRow.style.display = 'none';
|
||||
}
|
||||
|
||||
enableSwitch = E('button', {
|
||||
'type': 'button',
|
||||
'click': function(ev) {
|
||||
ev.preventDefault();
|
||||
const el = enableSecId ? enabledOpt.getUIElement(enableSecId) : null;
|
||||
if (el) {
|
||||
el.setValue(readEnabled() ? '0' : '1');
|
||||
}
|
||||
paintEnableSwitch();
|
||||
}
|
||||
});
|
||||
paintEnableSwitch();
|
||||
|
||||
versionField = E('span', {},
|
||||
coreExists
|
||||
? version
|
||||
: E('span', { 'style': 'color: var(--error-color-high); font-weight: bold;' }, _('Not installed')));
|
||||
|
||||
const headRows = [
|
||||
E('div', { 'class': 'agh-head-row' }, [
|
||||
E('span', { 'class': 'agh-head-item' }, [ E('span', {}, _('Service Status')), enableSwitch ]),
|
||||
E('span', { 'class': 'agh-head-item' }, [ E('span', {}, _('Current Version')), versionField ])
|
||||
])
|
||||
];
|
||||
if (!coreExists) {
|
||||
headRows.push(E('div', { 'class': 'agh-head-note', 'role': 'alert' }, [
|
||||
E('span', { 'class': 'agh-head-note-icon', 'aria-hidden': 'true' }, '\u26a0\ufe0e'),
|
||||
E('span', {}, _('Core binary not found. Enable the service to trigger an automatic download.'))
|
||||
]));
|
||||
}
|
||||
head = E('div', { 'class': 'agh-head' }, headRows);
|
||||
|
||||
const title = rendered.querySelector('h2');
|
||||
rendered.insertBefore(head, title ? title.nextSibling : rendered.firstChild);
|
||||
rendered.appendChild(E('style', {}, HEAD_CSS));
|
||||
alignHead(0);
|
||||
}
|
||||
|
||||
decorate();
|
||||
window.addEventListener('resize', function() { alignHead(40); });
|
||||
|
||||
// After "Save" LuCI renders the form again into the same root element: add the
|
||||
// header back and bring the (also re-created) update controls up to date.
|
||||
let afterRerender = null;
|
||||
const origRenderContents = map.renderContents;
|
||||
map.renderContents = function() {
|
||||
return Promise.resolve(origRenderContents.apply(this, arguments)).then(function(res) {
|
||||
if (afterRerender) {
|
||||
afterRerender();
|
||||
}
|
||||
return res;
|
||||
});
|
||||
};
|
||||
|
||||
poll.add(async function() {
|
||||
running = Boolean(await getStatus());
|
||||
paintEnableSwitch();
|
||||
}, POLL_INTERVAL);
|
||||
|
||||
let updatePollId = null;
|
||||
let currentState = 'init';
|
||||
@@ -749,12 +874,7 @@ return view.extend({
|
||||
|
||||
// Show (release) or remove (null) the new-version hint after the version number.
|
||||
function setVersionHint(release) {
|
||||
const field = versionRow
|
||||
? (versionRow.querySelector('output') || versionRow.querySelector('.cbi-value-field'))
|
||||
: null;
|
||||
if (!field) {
|
||||
return;
|
||||
}
|
||||
const field = versionField;
|
||||
const old = field.querySelector('#agh-version-hint');
|
||||
if (old) {
|
||||
old.remove();
|
||||
@@ -1060,6 +1180,15 @@ return view.extend({
|
||||
// delays it, and stays silent if GitHub cannot be reached.
|
||||
detectNewRelease(detectedChannel);
|
||||
|
||||
afterRerender = function() {
|
||||
decorate();
|
||||
if (currentState !== 'updating') {
|
||||
currentState = 'init';
|
||||
detectedChannel = currentChannel();
|
||||
detectNewRelease(detectedChannel);
|
||||
}
|
||||
};
|
||||
|
||||
// Changing the Core Branch dropdown invalidates what the Action button shows.
|
||||
rendered.addEventListener('change', function() {
|
||||
if (['init', 'updatable', 'latest', 'check_failed'].indexOf(currentState) === -1) {
|
||||
|
||||
@@ -8,11 +8,11 @@ Hardens the LuCI page, replaces update_url with update_source, and shows
|
||||
a new-core-version hint with a one-click path to the Core Update tab.
|
||||
The Service Status pill doubles as the Enable switch, next to the version.
|
||||
---
|
||||
.../resources/view/adguardhome/config.js | 1207 +++++++++++++++--
|
||||
1 file changed, 1076 insertions(+), 131 deletions(-)
|
||||
.../resources/view/adguardhome/config.js | 1210 +++++++++++++++--
|
||||
1 file changed, 1079 insertions(+), 131 deletions(-)
|
||||
|
||||
diff --git a/htdocs/luci-static/resources/view/adguardhome/config.js b/htdocs/luci-static/resources/view/adguardhome/config.js
|
||||
index 6dc2d45..07aa5cc 100644
|
||||
index 6dc2d45..25c03d4 100644
|
||||
--- a/htdocs/luci-static/resources/view/adguardhome/config.js
|
||||
+++ b/htdocs/luci-static/resources/view/adguardhome/config.js
|
||||
@@ -5,6 +5,7 @@
|
||||
@@ -23,7 +23,7 @@ index 6dc2d45..07aa5cc 100644
|
||||
'require view';
|
||||
|
||||
const DEFAULT_CONFIG_FILE = '/etc/adguardhome/adguardhome.yaml';
|
||||
@@ -18,12 +19,292 @@ const DEFAULT_GOMEMLIMIT = '0';
|
||||
@@ -18,12 +19,293 @@ const DEFAULT_GOMEMLIMIT = '0';
|
||||
|
||||
const PATH_REGEX = new RegExp('^/etc(/[^/]+)?/?$');
|
||||
|
||||
@@ -253,7 +253,8 @@ index 6dc2d45..07aa5cc 100644
|
||||
+ ':root[data-argon-mode="dark"] .agh-head, :root[data-darkmode="true"] .agh-head { --agh-on-fg:#7ee2a0; --agh-on-bg:rgba(46,160,67,.22); --agh-warn-fg:#e3b341; --agh-warn-bg:rgba(187,128,9,.22); --agh-off-fg:#ffa9ae; --agh-off-bg:rgba(248,81,73,.18); }' +
|
||||
+ '.agh-head-row { display:flex; align-items:center; flex-wrap:wrap; gap:.4rem 2rem; }' +
|
||||
+ '.agh-head-item { display:inline-flex; align-items:center; flex-wrap:wrap; gap:.25rem .75rem; line-height:1.5; }' +
|
||||
+ '.agh-head-note { line-height:1.5; }' +
|
||||
+ '.agh-head-note { display:flex; align-items:flex-start; gap:.5rem; line-height:1.5; padding:.45rem .75rem; border-left:4px solid var(--error-color-high, #d9534f); border-radius:4px; background:rgba(217,83,79,.12); color:var(--error-color-high, #d9534f); font-weight:600; }' +
|
||||
+ '.agh-head-note-icon { flex:none; font-size:1.15em; line-height:1.3; }' +
|
||||
+ '.agh-switch { display:inline-flex; align-items:center; gap:.4rem; height:auto; min-height:0; line-height:1.4; padding:.25rem .8rem; margin:0; border-radius:999px; font-weight:600; font-size:.9rem; border:0; box-shadow:none; cursor:pointer; transition:filter .15s; }' +
|
||||
+ '.agh-switch:hover { filter:brightness(.95); }' +
|
||||
+ '.agh-switch::before { content:""; width:.55rem; height:.55rem; border-radius:50%; background:currentColor; }' +
|
||||
@@ -318,7 +319,7 @@ index 6dc2d45..07aa5cc 100644
|
||||
|
||||
function getServiceInfo(name) {
|
||||
const fn = rpc.declare({
|
||||
@@ -32,7 +313,9 @@ function getServiceInfo(name) {
|
||||
@@ -32,7 +314,9 @@ function getServiceInfo(name) {
|
||||
params: ['name'],
|
||||
expect: { [name]: { instances: { [name]: {} }}},
|
||||
});
|
||||
@@ -329,7 +330,7 @@ index 6dc2d45..07aa5cc 100644
|
||||
}
|
||||
|
||||
const getAGHServiceInfo = getServiceInfo('adguardhome');
|
||||
@@ -40,18 +323,14 @@ const getAGHServiceInfo = getServiceInfo('adguardhome');
|
||||
@@ -40,18 +324,14 @@ const getAGHServiceInfo = getServiceInfo('adguardhome');
|
||||
async function getStatus() {
|
||||
try {
|
||||
const res = await getAGHServiceInfo();
|
||||
@@ -350,7 +351,7 @@ index 6dc2d45..07aa5cc 100644
|
||||
async function getVersion() {
|
||||
try {
|
||||
const res = await fs.exec('/usr/bin/AdGuardHome', ['--version']);
|
||||
@@ -61,26 +340,30 @@ async function getVersion() {
|
||||
@@ -61,26 +341,30 @@ async function getVersion() {
|
||||
return version;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
@@ -392,7 +393,7 @@ index 6dc2d45..07aa5cc 100644
|
||||
}
|
||||
if (value.endsWith('/')) {
|
||||
return _('Path must not end with a slash.');
|
||||
@@ -95,167 +378,829 @@ function validateWorkDir(_unused, value) {
|
||||
@@ -95,167 +379,831 @@ function validateWorkDir(_unused, value) {
|
||||
if (value == null || value === '') {
|
||||
return true;
|
||||
}
|
||||
@@ -906,8 +907,10 @@ index 6dc2d45..07aa5cc 100644
|
||||
+ ])
|
||||
+ ];
|
||||
+ if (!coreExists) {
|
||||
+ headRows.push(E('div', { 'class': 'agh-head-note', 'style': 'color: var(--error-color-high); font-weight: bold;' },
|
||||
+ _('Core binary not found. Enable the service to trigger an automatic download.')));
|
||||
+ headRows.push(E('div', { 'class': 'agh-head-note', 'role': 'alert' }, [
|
||||
+ E('span', { 'class': 'agh-head-note-icon', 'aria-hidden': 'true' }, '\u26a0\ufe0e'),
|
||||
+ E('span', {}, _('Core binary not found. Enable the service to trigger an automatic download.'))
|
||||
+ ]));
|
||||
+ }
|
||||
+ head = E('div', { 'class': 'agh-head' }, headRows);
|
||||
+
|
||||
|
||||
@@ -1349,8 +1349,8 @@ return view.extend({
|
||||
o = s.option(form.ListValue, 'udp_mode', _("UDP Mode"));
|
||||
o.value('tun', 'TUN'); o.value('tproxy', 'TPROXY'); o.value('off', _("Off"));
|
||||
o = s.option(form.ListValue, 'stack', _("TUN Network Stack"));
|
||||
o.value('system', 'System'); o.value('gvisor', 'gVisor'); o.value('mixed', 'Mixed');
|
||||
o.description = _("TUN stack is used when either TCP or UDP mode is TUN.");
|
||||
o.value('system', 'System'); o.value('gvisor', 'gVisor'); o.value('mixed', 'Mixed'); o.value('mips', 'Mips');
|
||||
o.description = _("TUN stack is used when either TCP or UDP mode is TUN. System: kernel stack. gVisor / Mixed: userspace stack, needs a core built with <code>with_gvisor</code>. Mips: mihomo's own userspace stack (metacubex/mipstack), always available — pick it when gVisor is not in the core.");
|
||||
o = s.option(form.Flag, 'disable_quic_gso', _("Disable QUIC GSO"));
|
||||
o = s.option(form.Flag, 'block_quic', _("Block QUIC"));
|
||||
o.description = _("Reject proxied UDP 443 so some apps fall back to TCP. Try only when downloads or video stall.");
|
||||
@@ -1577,7 +1577,9 @@ return view.extend({
|
||||
|
||||
o = s.option(form.DynamicList, 'fake_ip_filter', _("Fake-IP Filter Domains"));
|
||||
o.placeholder = '+.lan / geosite:cn / RULE-SET,cn_domain,real-ip';
|
||||
o.description = _("Blocklist mode: enter domains or shorthand like <code>geosite:cn</code>. Rule mode: entries are converted to rule syntax automatically, such as <code>RULE-SET,xxx,real-ip</code> / <code>DOMAIN-SUFFIX,xxx,real-ip</code>; usually add <code>MATCH,fake-ip</code> at the end as fallback. <code>geosite:cn</code> becomes <code>RULE-SET,cn_domain,real-ip</code> via built-in cn.mrs acceleration (other <code>geosite:xxx</code> becomes <code>GEOSITE,xxx,real-ip</code>). Allowlist mode drops the shipped defaults (they mean the opposite there), so enter your own domains.");
|
||||
o.description = _(`Blocklist (default): listed domains get a real IP — <code>+.lan</code>, <code>rule-set:cn_domain</code>, <code>geosite:cn</code>.<br>
|
||||
Allowlist: only listed domains get fake-IP.<br>
|
||||
Rule mode: every entry needs a <code>fake-ip</code> / <code>real-ip</code> action; Clashoo adds <code>real-ip</code> for you.`);
|
||||
o.depends('enhanced_mode', 'fake-ip');
|
||||
o.remove = function () {};
|
||||
o = s.option(form.DynamicList, 'default_nameserver', 'Bootstrap DNS');
|
||||
@@ -1613,8 +1615,15 @@ return view.extend({
|
||||
o.value('nameserver', _("Domestic Upstream DNS"));
|
||||
o.value('direct-nameserver', _("Direct Domain Resolver"));
|
||||
o.value('proxy-server-nameserver', _("Proxy Node Domain Resolver"));
|
||||
o.value('fallback', _("Overseas Encrypted DNS (anti-pollution)"));
|
||||
/* fake-ip 先返回合成地址,fallback 这一角色会被 mixin 跳过,界面上要说清 */
|
||||
var dnsFakeIpMode = (uci.get('clashoo', 'config', 'enhanced_mode') || 'fake-ip') === 'fake-ip';
|
||||
o.value('fallback', dnsFakeIpMode
|
||||
? _("Overseas Encrypted DNS (no effect in fake-ip mode)")
|
||||
: _("Overseas Encrypted DNS"));
|
||||
o.default = 'nameserver';
|
||||
o.description = dnsFakeIpMode
|
||||
? _("Currently fake-ip: this role is skipped, the entry is saved but not used. Switch to redir-host to make it a secondary resolver.")
|
||||
: _("redir-host mode: used as a secondary encrypted resolver.");
|
||||
o = s.option(form.Value, 'ser_address', _("DNS Address"));
|
||||
o.placeholder = 'https://dns.alidns.com/dns-query / https://doh.pub/dns-query';
|
||||
o = s.option(form.ListValue, 'protocol', _("Protocol"));
|
||||
|
||||
@@ -876,11 +876,23 @@ return view.extend({
|
||||
|
||||
_stackLabel: function (stack) {
|
||||
var s = String(stack || '').toLowerCase();
|
||||
var map = { gvisor: 'gVisor', system: 'System', mixed: 'Mixed' };
|
||||
var map = { gvisor: 'gVisor', system: 'System', mixed: 'Mixed', mips: 'Mips' };
|
||||
return map[s] || (stack ? String(stack) : 'System');
|
||||
},
|
||||
|
||||
_tpModeRows: function (st) {
|
||||
/* eBPF 内核自己接管(cgroup + packet_rewrite),不跑 TUN/Redirect,也不建 tun 设备 */
|
||||
if (st && st.core_is_ebpf) {
|
||||
return {
|
||||
ebpf: true,
|
||||
rows: [
|
||||
{ proto: 'TCP', mode: 'eBPF', suffix: _("Mode") },
|
||||
{ proto: 'UDP', mode: 'eBPF', suffix: _("Mode") },
|
||||
{ proto: _("Network Stack"), mode: _("Core-managed"), suffix: '' }
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
var tcpRaw = st.tcp_node || st.effective_tcp_mode || st.tcp_mode || uci.get('clashoo', 'config', 'tcp_mode') || 'off';
|
||||
var udpRaw = st.udp_node || st.effective_udp_mode || st.udp_mode || uci.get('clashoo', 'config', 'udp_mode') || tcpRaw;
|
||||
var stackRaw = st.stack_type || st.stack || uci.get('clashoo', 'config', 'stack') || 'system';
|
||||
@@ -898,6 +910,8 @@ return view.extend({
|
||||
_isTpModeDegraded: function (st) {
|
||||
if (!st)
|
||||
return false;
|
||||
if (st.core_is_ebpf)
|
||||
return false;
|
||||
|
||||
if (st.runtime_degraded)
|
||||
return true;
|
||||
@@ -919,6 +933,8 @@ return view.extend({
|
||||
});
|
||||
if (this._isTpModeDegraded(st))
|
||||
rows.push(E('div', { 'class': 'cl-check-updated cl-mode-degraded' }, _("Degraded")));
|
||||
if (data.ebpf)
|
||||
rows.push(E('div', { 'class': 'cl-check-updated cl-mode-degraded' }, _("eBPF core captures traffic itself; turn off Clashoo forwarding rules")));
|
||||
|
||||
return E('div', { id: 'cl-tpmode', 'class': 'cl-mode-stack' }, rows);
|
||||
},
|
||||
@@ -1565,10 +1581,14 @@ return view.extend({
|
||||
var enhancedMode = String(st.enhanced_mode || 'fake-ip').toLowerCase();
|
||||
var tpMode = 'custom';
|
||||
|
||||
if (tcpMode === 'tun' && udpMode === 'tun' && stackMode === 'mixed')
|
||||
if (st.core_is_ebpf)
|
||||
tpMode = 'ebpf';
|
||||
else if (tcpMode === 'tun' && udpMode === 'tun' && stackMode === 'mixed')
|
||||
tpMode = 'tun-mixed';
|
||||
else if (tcpMode === 'tun' && udpMode === 'tun' && stackMode === 'gvisor')
|
||||
tpMode = 'tun-gvisor';
|
||||
else if (tcpMode === 'tun' && udpMode === 'tun' && stackMode === 'mips')
|
||||
tpMode = 'tun-mips';
|
||||
else if (tcpMode === 'tun' && udpMode === 'tun')
|
||||
tpMode = 'tun-system';
|
||||
else if (tcpMode === 'redirect' && udpMode === 'tproxy')
|
||||
@@ -1583,7 +1603,9 @@ return view.extend({
|
||||
return E('option', { value: o[0], selected: o[0] === val ? '' : null, disabled: o[2] ? '' : null }, o[1]);
|
||||
}));
|
||||
};
|
||||
var tpModeOptions = [['fake-ip','Fake-IP'],['redir-host','Redir-Host'],['tun-system','System'],['tun-gvisor','gVisor'],['tun-mixed','Mixed']];
|
||||
var tpModeOptions = [['fake-ip','Fake-IP'],['redir-host','Redir-Host'],['tun-system','System'],['tun-gvisor','gVisor'],['tun-mixed','Mixed'],['tun-mips','Mips']];
|
||||
if (tpMode === 'ebpf')
|
||||
tpModeOptions.unshift(['ebpf', 'eBPF (' + _("core-managed") + ')', true]);
|
||||
if (tpMode === 'custom')
|
||||
tpModeOptions.unshift(['custom',_("Custom")]);
|
||||
|
||||
@@ -1649,6 +1671,10 @@ return view.extend({
|
||||
self._lastSt.tcp_mode = 'tun';
|
||||
self._lastSt.udp_mode = 'tun';
|
||||
self._lastSt.stack = 'gvisor';
|
||||
} else if (mode === 'tun-mips') {
|
||||
self._lastSt.tcp_mode = 'tun';
|
||||
self._lastSt.udp_mode = 'tun';
|
||||
self._lastSt.stack = 'mips';
|
||||
} else if (mode === 'tun-system') {
|
||||
self._lastSt.tcp_mode = 'tun';
|
||||
self._lastSt.udp_mode = 'tun';
|
||||
|
||||
@@ -213,8 +213,8 @@ msgstr "黑名单"
|
||||
msgid "Blocklist (listed items use real IP, default)"
|
||||
msgstr "黑名单(列入走真 IP,默认)"
|
||||
|
||||
msgid "Blocklist mode: enter domains or shorthand like <code>geosite:cn</code>. Rule mode: entries are converted to rule syntax automatically, such as <code>RULE-SET,xxx,real-ip</code> / <code>DOMAIN-SUFFIX,xxx,real-ip</code>; usually add <code>MATCH,fake-ip</code> at the end as fallback. <code>geosite:cn</code> becomes <code>RULE-SET,cn_domain,real-ip</code> via built-in cn.mrs acceleration (other <code>geosite:xxx</code> becomes <code>GEOSITE,xxx,real-ip</code>). Allowlist mode drops the shipped defaults (they mean the opposite there), so enter your own domains."
|
||||
msgstr "黑名单模式:填域名或 <code>geosite:cn</code> 简写。规则模式:条目会自动转成 <code>RULE-SET,xxx,real-ip</code> / <code>DOMAIN-SUFFIX,xxx,real-ip</code> 这种与 rules 同语法的写法,末尾通常加一条 <code>MATCH,fake-ip</code> 兜底。<code>geosite:cn</code> 会转成 <code>RULE-SET,cn_domain,real-ip</code>,走内置 cn.mrs 加速(其它 <code>geosite:xxx</code> 则转成 <code>GEOSITE,xxx,real-ip</code>)。白名单模式下随包自带的默认项含义相反,会被跳过,请自行填写需要 Fake-IP 的域名。"
|
||||
msgid "Blocklist (default): listed domains get a real IP — <code>+.lan</code>, <code>rule-set:cn_domain</code>, <code>geosite:cn</code>.<br>\nAllowlist: only listed domains get fake-IP.<br>\nRule mode: every entry needs a <code>fake-ip</code> / <code>real-ip</code> action; Clashoo adds <code>real-ip</code> for you."
|
||||
msgstr "黑名单(默认):列出的域名返回真实 IP,可写 +.lan、rule-set:cn_domain、geosite:cn。<br>白名单:只有列出的域名走 Fake-IP。<br>规则模式:每条必须带 fake-ip / real-ip 动作,Clashoo 会自动补 real-ip。"
|
||||
|
||||
msgid "Bypass China IPv4"
|
||||
msgstr "大陆 IPv4 绕过"
|
||||
@@ -261,6 +261,9 @@ msgstr "清理失败(mihomo 可能未运行)"
|
||||
msgid "Cleanup interval (hours)"
|
||||
msgstr "清理间隔(小时)"
|
||||
|
||||
msgid "Currently fake-ip: this role is skipped, the entry is saved but not used. Switch to redir-host to make it a secondary resolver."
|
||||
msgstr "当前是 fake-ip:这一项会被跳过,配置会保存但不生效。改成 redir-host 后才会作为备用解析器。"
|
||||
|
||||
msgid "Clear"
|
||||
msgstr "清理"
|
||||
|
||||
@@ -273,6 +276,9 @@ msgstr "清理 Smart 缓存"
|
||||
msgid "Clear the current log file? This cannot be undone."
|
||||
msgstr "确定要清空当前日志文件吗?此操作不可恢复。"
|
||||
|
||||
msgid "redir-host mode: used as a secondary encrypted resolver."
|
||||
msgstr "redir-host 模式:作为备用加密解析器。"
|
||||
|
||||
msgid "Clear View"
|
||||
msgstr "清空显示"
|
||||
|
||||
@@ -927,8 +933,11 @@ msgstr "输出文件名(不含扩展名,留空自动填写)"
|
||||
msgid "Overseas"
|
||||
msgstr "境外"
|
||||
|
||||
msgid "Overseas Encrypted DNS (anti-pollution)"
|
||||
msgstr "国外加密 DNS(防污染)"
|
||||
msgid "Overseas Encrypted DNS"
|
||||
msgstr "国外加密 DNS"
|
||||
|
||||
msgid "Overseas Encrypted DNS (no effect in fake-ip mode)"
|
||||
msgstr "国外加密 DNS(fake-ip 下不生效)"
|
||||
|
||||
msgid "Overview"
|
||||
msgstr "概览"
|
||||
@@ -1616,3 +1625,15 @@ msgstr "仅在本机转换"
|
||||
|
||||
msgid "If local conversion fails, the selected external service receives the complete subscription URL, including its access token. Leave Local conversion only selected to keep the URL on this router."
|
||||
msgstr "如果本机转换失败,所选第三方服务会收到完整订阅链接,包括访问令牌。保持选择「仅在本机转换」可避免向第三方发送链接。"
|
||||
|
||||
msgid "TUN stack is used when either TCP or UDP mode is TUN. System: kernel stack. gVisor / Mixed: userspace stack, needs a core built with <code>with_gvisor</code>. Mips: mihomo's own userspace stack (metacubex/mipstack), always available — pick it when gVisor is not in the core."
|
||||
msgstr "TCP 或 UDP 模式为 TUN 时生效。System:系统协议栈。gVisor / Mixed:用户态栈,内核需要带 <code>with_gvisor</code> 编译。Mips:mihomo 自带用户态栈(metacubex/mipstack),始终可用,内核没有 gVisor 时选它。"
|
||||
|
||||
msgid "core-managed"
|
||||
msgstr "内核接管"
|
||||
|
||||
msgid "Core-managed"
|
||||
msgstr "内核接管"
|
||||
|
||||
msgid "eBPF core captures traffic itself; turn off Clashoo forwarding rules"
|
||||
msgstr "eBPF 内核自行接管流量,请关闭 Clashoo 的转发规则"
|
||||
@@ -361,6 +361,13 @@ p.close();
|
||||
return v;
|
||||
}
|
||||
|
||||
/* eBPF 入站只存在于第三方 mihomo 分支(如 alpha-smart-xxx-ebpf),
|
||||
* 官方 mihomo / smart / sing-box 都没有,这里只做识别。 */
|
||||
function core_is_ebpf_build(binary) {
|
||||
let v = get_core_version(binary);
|
||||
return !!match(v, /ebpf/i);
|
||||
}
|
||||
|
||||
function sync_legacy_core_fields(c) {
|
||||
let family = c.get('clashoo', 'config', 'core_type') || 'mihomo';
|
||||
let dcore = c.get('clashoo', 'config', 'dcore') || '2';
|
||||
@@ -2169,6 +2176,7 @@ return { running, pid, dash_port, dash_pass, proxy_mode, conf_path: conf_name, c
|
||||
runtime_degraded, runtime_degrade_reason,
|
||||
health_status, health_detail, has_tun_device,
|
||||
core_type: configured_core, dcore, local_ip,
|
||||
core_is_ebpf: core_is_ebpf_build(find_binary()),
|
||||
has_smart: !!find_smart_binary() };
|
||||
} catch (e) {
|
||||
return { success: false, error: 'status_failed', message: '' + e };
|
||||
@@ -2259,14 +2267,14 @@ set_mode: {
|
||||
args: { mode: 'mode' },
|
||||
call: function(req) {
|
||||
let mode = req.args?.mode || 'fake-ip';
|
||||
if (mode != 'fake-ip' && mode != 'redir-host' && mode != 'tun-system' && mode != 'tun-gvisor' && mode != 'tun-mixed')
|
||||
if (mode != 'fake-ip' && mode != 'redir-host' && mode != 'tun-system' && mode != 'tun-gvisor' && mode != 'tun-mixed' && mode != 'tun-mips')
|
||||
return { error: 'invalid mode' };
|
||||
let c = cursor(); c.load('clashoo');
|
||||
if (mode == 'tun-system' || mode == 'tun-gvisor' || mode == 'tun-mixed') {
|
||||
if (mode == 'tun-system' || mode == 'tun-gvisor' || mode == 'tun-mixed' || mode == 'tun-mips') {
|
||||
c.set('clashoo', 'config', 'tun_mode', '1');
|
||||
c.set('clashoo', 'config', 'tcp_mode', 'tun');
|
||||
c.set('clashoo', 'config', 'udp_mode', 'tun');
|
||||
c.set('clashoo', 'config', 'stack', mode == 'tun-mixed' ? 'mixed' : (mode == 'tun-gvisor' ? 'gvisor' : 'system'));
|
||||
c.set('clashoo', 'config', 'stack', mode == 'tun-mixed' ? 'mixed' : (mode == 'tun-gvisor' ? 'gvisor' : (mode == 'tun-mips' ? 'mips' : 'system')));
|
||||
c.set('clashoo', 'config', 'enable_udp', '1');
|
||||
c.set('clashoo', 'config', 'enhanced_mode', 'fake-ip');
|
||||
} else {
|
||||
|
||||
@@ -63,6 +63,10 @@ return L.view.extend({
|
||||
return kernelMajor > major || (kernelMajor == major && kernelMinor >= minor);
|
||||
}
|
||||
|
||||
// x86_64 routers aggregate up to 16 WANs, the others 8: the most the
|
||||
// mptcp init passes to "ip mptcp limits set"
|
||||
var mptcpLimitMax = (boardinfo.release && boardinfo.release.target == 'x86/64') ? 16 : 8;
|
||||
|
||||
function normalizeSchedulerValue(value) {
|
||||
if (value == null)
|
||||
return value;
|
||||
@@ -320,10 +324,10 @@ return L.view.extend({
|
||||
o.depends("mptcp_pm_type","1");
|
||||
o.retain = true;
|
||||
|
||||
// The kernel takes 8 at most for both limits, and one value above
|
||||
// it made "ip mptcp limits set" fail for both
|
||||
// One value above the kernel's maximum made "ip mptcp limits set"
|
||||
// fail for both limits
|
||||
o = s.option(form.Value, "mptcp_subflows", _("Max subflows"),_("specifies the maximum number of additional subflows allowed for each MPTCP connection"));
|
||||
o.datatype = "range(0,8)";
|
||||
o.datatype = "range(0," + mptcpLimitMax + ")";
|
||||
o.rmempty = false;
|
||||
o.default = 3;
|
||||
|
||||
@@ -333,7 +337,7 @@ return L.view.extend({
|
||||
o.default = 4;
|
||||
|
||||
o = s.option(form.Value, "mptcp_add_addr_accepted", _("Max add address"),_("specifies the maximum number of ADD_ADDR (add address) suboptions accepted for each MPTCP connection"));
|
||||
o.datatype = "range(0,8)";
|
||||
o.datatype = "range(0," + mptcpLimitMax + ")";
|
||||
o.rmempty = false;
|
||||
o.default = 1;
|
||||
|
||||
|
||||
@@ -595,13 +595,20 @@ end
|
||||
-- What set_settings / set_interface_settings accept for each field. They
|
||||
-- wrote any string: the init script builds a file path from mptcp_scheduler
|
||||
-- ("../" in it registered an arbitrary .o as the scheduler), passes the
|
||||
-- limits to "ip mptcp limits set" (a value above 8 made it fail for both)
|
||||
-- and the rest to sysctl.
|
||||
-- limits to "ip mptcp limits set" (a value above the kernel's maximum made
|
||||
-- it fail for both) and the rest to sysctl.
|
||||
local function valid_uint(v, max)
|
||||
local n = tonumber(v)
|
||||
return v:match("^%d+$") ~= nil and n ~= nil and (max == nil or n <= max)
|
||||
end
|
||||
|
||||
-- x86_64 routers aggregate up to 16 WANs, the others 8: the most the mptcp
|
||||
-- init passes to "ip mptcp limits set"
|
||||
local function valid_mptcp_limit(v)
|
||||
local max = exec_output("uname -m 2>/dev/null") == "x86_64" and 16 or 8
|
||||
return valid_uint(v, max)
|
||||
end
|
||||
|
||||
-- 1 to 256, as "ip route" takes it and the forms ask: the BPF schedulers
|
||||
-- read 0 as no weight (neutral 100) and add the weight to a signed counter
|
||||
local function valid_weight(v)
|
||||
@@ -671,9 +678,9 @@ local setting_checks = {
|
||||
mptcp_disable_initial_config = function(v) return v == "0" or v == "1" end,
|
||||
mptcp_force_multipath = function(v) return v == "0" or v == "1" end,
|
||||
mptcpd_enable = function(v) return v == "enable" or v == "disable" end,
|
||||
mptcp_subflows = function(v) return valid_uint(v, 8) end,
|
||||
mptcp_subflows = valid_mptcp_limit,
|
||||
mptcp_stale_loss_cnt = valid_uint,
|
||||
mptcp_add_addr_accepted = function(v) return valid_uint(v, 8) end,
|
||||
mptcp_add_addr_accepted = valid_mptcp_limit,
|
||||
mptcp_add_addr_timeout = valid_uint,
|
||||
mptcp_blackhole_timeout = valid_uint,
|
||||
mptcp_close_timeout = valid_uint,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-open-box
|
||||
PKG_VERSION:=0.1.397
|
||||
PKG_RELEASE:=62
|
||||
PKG_VERSION:=0.1.400
|
||||
PKG_RELEASE:=63
|
||||
|
||||
PKG_MAINTAINER:=liandu2024
|
||||
PKG_LICENSE:=GPL-3.0-or-later
|
||||
|
||||
@@ -159,6 +159,10 @@ body[data-bg] .login-card {
|
||||
|
||||
.login-error {
|
||||
@apply bg-danger-surface text-danger border-hairline rounded-2xl border px-3 py-2 text-center text-sm;
|
||||
|
||||
&.warning {
|
||||
@apply bg-warning-surface text-warning;
|
||||
}
|
||||
}
|
||||
|
||||
.login-actions {
|
||||
|
||||
@@ -19,7 +19,8 @@ test("production assets stay within raw-transfer budgets", () => {
|
||||
|
||||
// 187K: pseudo-elements are nested rules, one content declaration each.
|
||||
assert.ok(main <= 187_000, "main.css exceeds 187 KB");
|
||||
assert.ok(login <= 12_000, "login.css exceeds 12 KB");
|
||||
// 12.5K: auth_plugin warning tokens added ~0.35 KB.
|
||||
assert.ok(login <= 12_500, "login.css exceeds 12.5 KB");
|
||||
// 22K: palette recents (record on pick, pure-LRU browse order, storage
|
||||
// validation) added ~0.8 KB and the ">" logout command ~0.2 KB. The total
|
||||
// budget below moved by the same amount.
|
||||
|
||||
@@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
|
||||
LUCI_TITLE:=Aurora Theme (A modern browser theme built with Vite and Tailwind CSS)
|
||||
LUCI_DEPENDS:=+luci-base
|
||||
|
||||
PKG_VERSION:=1.4.2
|
||||
PKG_RELEASE:=86
|
||||
PKG_VERSION:=1.4.3
|
||||
PKG_RELEASE:=87
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
LUCI_MINIFY_CSS:=
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
|
||||
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-content:"";--tw-shadow:0 0 #0000;--tw-inset-shadow:0 0 #0000;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-shadow:0 0 #0000;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--spacing:var(--spacing);--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-widest:.1em;--radius-2xl:calc(var(--radius-base) * 2);--animate-spin:spin 1s linear infinite}}@layer base{*,:before,:after{box-sizing:border-box}body,hr,p,button,input{margin:0}button,input{font:inherit}a{text-decoration-line:none}[hidden]{display:none!important}}@layer components,utilities;:root{--bg:#f7fafc;--surface:#fff;--text:#121a22;--brand:#0085b7;--on-brand:#fff;--danger:#8d1925;--text-muted:#5f666d;--text-subtle:#7f858b;--hairline:#121a2221;--brand-subtle:#ddecf5;--danger-surface:#ffe8e6;--control-bg:#fff;--app-shadow-md:0 4px 16px #00000014, 0 1px 3px #0000000a;--font-sans:"Lato", ui-sans-serif, system-ui, sans-serif;--spacing:.25rem;--radius-base:.5rem}@supports (color:lab(0% 0 0)){:root{--bg:lab(98.1553% -.760227 -1.25223);--surface:lab(100% 0 0);--text:lab(8.89727% -1.68239 -6.93074);--brand:lab(51.3503% -19.237 -40.2116);--on-brand:lab(100% 0 0);--danger:lab(31.1495% 47.9256 25.8661);--text-muted:lab(42.8254% -1.57365 -4.8763);--text-subtle:lab(55.3248% -1.4126 -4.053);--hairline:lab(8.89727% -1.68239 -6.93074/.13);--brand-subtle:lab(92.5794% -3.9458 -6.17858);--danger-surface:lab(93.849% 11.2841 5.29771);--control-bg:lab(100.011% -.760317 -1.25225);--app-shadow-md:0 4px 16px lab(0% 0 0/.08), 0 1px 3px lab(0% 0 0/.04)}}[data-darkmode=true]{--bg:#05070e;--surface:#141822;--text:#f9fafb;--brand:#00958e;--on-brand:#fff;--danger:#f17070;--text-muted:#909297;--text-subtle:#5d6067;--hairline:#f9fafb1a;--brand-subtle:#061a20;--danger-surface:#541f20;--control-bg:#0a0e17;--app-shadow-md:0 10px 28px #0000006b}@supports (color:lab(0% 0 0)){[data-darkmode=true]{--bg:lab(1.93894% .0855997 -3.22397);--surface:lab(8.24713% -.0405461 -7.31381);--text:lab(98.252% -.0618994 -.756907);--brand:lab(55.0978% -44.5567 -7.62978);--on-brand:lab(100% 0 0);--danger:lab(63.5705% 50.8105 25.3639);--text-muted:lab(60.5379% -.225931 -3.05105);--text-subtle:lab(40.6865% -.278354 -4.23647);--hairline:lab(98.252% -.0618994 -.756907/.1);--brand-subtle:lab(7.95734% -6.34954 -6.72844);--danger-surface:lab(20.3014% 25.4342 12.827);--control-bg:lab(3.9802% .0812709 -5.64623);--app-shadow-md:0 10px 28px lab(0% 0 0/.42)}}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-property:none!important;animation:none!important}.spinning:before{animation:var(--animate-spin)!important}}html{background-color:var(--bg);min-height:100dvh;font-family:var(--font-sans);--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}html[data-darkmode=true]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}html body{background-color:var(--bg);min-height:100dvh}.page-bg{pointer-events:none;background-image:var(--page-bg-lqip,var(--page-bg));background-position:50%;background-repeat:no-repeat;background-size:cover;position:fixed;inset:0;transform:translateZ(0)}.page-bg:after{pointer-events:none;background-image:var(--page-bg);opacity:0;--tw-content:"";content:var(--tw-content);background-position:50%;background-repeat:no-repeat;background-size:cover;transition:opacity .5s;position:absolute;inset:0}.page-bg.full-loaded:after{opacity:1}@keyframes divider-in{0%{opacity:0;transform:scaleX(.15)}}.login-screen{min-height:100dvh;padding-inline:calc(var(--spacing) * 5);padding-bottom:calc(var(--spacing) * 14);flex-direction:column;justify-content:center;align-items:center;display:flex;position:relative}.login-shell{width:100%;max-width:var(--container-md)}.page-bg{--page-bg:var(--login-bg);--page-bg-lqip:var(--login-bg-lqip)}body[data-bg] .page-bg:before{pointer-events:none;z-index:1;--tw-content:"";content:var(--tw-content);background:var(--bg);position:absolute;inset:0}@supports (color:color-mix(in lab, red, red)){body[data-bg] .page-bg:before{background:color-mix(in srgb,var(--bg) var(--login-bg-scrim,0%),transparent)}}body[data-bg] .login-card{-webkit-backdrop-filter:blur(var(--login-bg-blur,0px)) saturate(150%);backdrop-filLine truncated
|
||||
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-content:"";--tw-shadow:0 0 #0000;--tw-inset-shadow:0 0 #0000;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-shadow:0 0 #0000;--tw-border-style:solid;--tw-gradient-position:initial;--tw-gradient-from:#0000;--tw-gradient-via:#0000;--tw-gradient-to:#0000;--tw-gradient-stops:initial;--tw-gradient-via-stops:initial;--tw-gradient-from-position:0%;--tw-gradient-via-position:50%;--tw-gradient-to-position:100%}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--spacing:var(--spacing);--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-widest:.1em;--radius-2xl:calc(var(--radius-base) * 2);--animate-spin:spin 1s linear infinite}}@layer base{*,:before,:after{box-sizing:border-box}body,hr,p,button,input{margin:0}button,input{font:inherit}a{text-decoration-line:none}[hidden]{display:none!important}}@layer components,utilities;:root{--bg:#f7fafc;--surface:#fff;--text:#121a22;--brand:#0085b7;--on-brand:#fff;--warning:#643e00;--danger:#8d1925;--text-muted:#5f666d;--text-subtle:#7f858b;--hairline:#121a2221;--brand-subtle:#ddecf5;--warning-surface:#ffedd6;--danger-surface:#ffe8e6;--control-bg:#fff;--app-shadow-md:0 4px 16px #00000014, 0 1px 3px #0000000a;--font-sans:"Lato", ui-sans-serif, system-ui, sans-serif;--spacing:.25rem;--radius-base:.5rem}@supports (color:lab(0% 0 0)){:root{--bg:lab(98.1553% -.760227 -1.25223);--surface:lab(100% 0 0);--text:lab(8.89727% -1.68239 -6.93074);--brand:lab(51.3503% -19.237 -40.2116);--on-brand:lab(100% 0 0);--warning:lab(30.2236% 14.0359 42.8654);--danger:lab(31.1495% 47.9256 25.8661);--text-muted:lab(42.8254% -1.57365 -4.8763);--text-subtle:lab(55.3248% -1.4126 -4.053);--hairline:lab(8.89727% -1.68239 -6.93074/.13);--brand-subtle:lab(92.5794% -3.9458 -6.17858);--warning-surface:lab(94.8007% 4.75499 15.6111);--danger-surface:lab(93.849% 11.2841 5.29771);--control-bg:lab(100.011% -.760317 -1.25225);--app-shadow-md:0 4px 16px lab(0% 0 0/.08), 0 1px 3px lab(0% 0 0/.04)}}[data-darkmode=true]{--bg:#05070e;--surface:#141822;--text:#f9fafb;--brand:#00958e;--on-brand:#fff;--warning:#f0ba59;--danger:#f17070;--text-muted:#909297;--text-subtle:#5d6067;--hairline:#f9fafb1a;--brand-subtle:#061a20;--warning-surface:#45310b;--danger-surface:#541f20;--control-bg:#0a0e17;--app-shadow-md:0 10px 28px #0000006b}@supports (color:lab(0% 0 0)){[data-darkmode=true]{--bg:lab(1.93894% .0855997 -3.22397);--surface:lab(8.24713% -.0405461 -7.31381);--text:lab(98.252% -.0618994 -.756907);--brand:lab(55.0978% -44.5567 -7.62978);--on-brand:lab(100% 0 0);--warning:lab(79.1996% 12.6808 55.9505);--danger:lab(63.5705% 50.8105 25.3639);--text-muted:lab(60.5379% -.225931 -3.05105);--text-subtle:lab(40.6865% -.278354 -4.23647);--hairline:lab(98.252% -.0618994 -.756907/.1);--brand-subtle:lab(7.95734% -6.34954 -6.72844);--warning-surface:lab(22.2973% 6.18184 26.7927);--danger-surface:lab(20.3014% 25.4342 12.827);--control-bg:lab(3.9802% .0812709 -5.64623);--app-shadow-md:0 10px 28px lab(0% 0 0/.42)}}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-property:none!important;animation:none!important}.spinning:before{animation:var(--animate-spin)!important}}html{background-color:var(--bg);min-height:100dvh;font-family:var(--font-sans);--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}html[data-darkmode=true]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}html body{background-color:var(--bg);min-height:100dvh}.page-bg{pointer-events:none;background-image:var(--page-bg-lqip,var(--page-bg));background-position:50%;background-repeat:no-repeat;background-size:cover;position:fixed;inset:0;transform:translateZ(0)}.page-bg:after{pointer-events:none;background-image:var(--page-bg);opacity:0;--tw-content:"";content:var(--tw-content);background-position:50%;background-repeat:no-repeat;background-size:cover;transition:opacity .5s;position:absolute;inset:0}.page-bg.full-loaded:after{opacity:1}@keyframes divider-in{0%{opacity:0;transform:scaleX(.15)}}.login-screen{min-height:100dvh;padding-inline:calc(var(--spacing) * 5);padding-bottom:calc(var(--spacing) * 14);flex-direction:column;justify-content:center;align-items:center;display:flex;position:relative}.login-shell{width:100%;max-width:var(--container-md)}.page-bg{--page-bg:var(--login-bg);--page-bg-lqip:var(--login-bg-lqip)}body[data-bg] .page-bg:before{pointer-events:none;z-index:1;--tw-content:"";content:var(--tw-content);background:var(--bg);position:absolute;inset:Line truncated
|
||||
@@ -54,12 +54,52 @@ const loginIconVer = themeTokens.icon_cache_version || '0';
|
||||
<input name="luci_password" id="luci_password" type="password" class="login-input" autocomplete="current-password">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if (auth_fields): %}
|
||||
{% for (let field in auth_fields): %}
|
||||
<div class="login-field">
|
||||
<label class="login-label" for="{{ field.name }}">{{ _(field.label ?? field.name) }}</label>
|
||||
<div class="input-wrap">
|
||||
<input
|
||||
name="{{ field.name }}"
|
||||
id="{{ field.name }}"
|
||||
type="{{ field.type ?? 'text' }}"
|
||||
class="login-input"
|
||||
{% if (field.placeholder): %}placeholder="{{ field.placeholder }}"{% endif %}
|
||||
{% if (field.inputmode): %}inputmode="{{ field.inputmode }}"{% endif %}
|
||||
{% if (field.pattern): %}pattern="{{ field.pattern }}"{% endif %}
|
||||
{% if (field.maxlength): %}maxlength="{{ field.maxlength }}"{% endif %}
|
||||
{% if (field.autocomplete): %}autocomplete="{{ field.autocomplete }}"{% endif %}
|
||||
{% if (field.required): %}required{% endif %}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if (auth_html): %}
|
||||
<div class="login-field">
|
||||
{{ auth_html }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if (auth_assets): %}
|
||||
{% for (let asset in auth_assets): %}
|
||||
{% if (asset.type == 'script'): %}
|
||||
<script src="{{ asset.src }}"></script>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if (fuser): %}
|
||||
<div class="login-error" role="alert">{{ _('Invalid username and/or password! Please try again.') }}</div>
|
||||
{% if (auth_message || fuser): %}
|
||||
<div class="login-error{% if (auth_plugin): %} warning{% endif %}" role="alert">
|
||||
{{ auth_message || _('Invalid username and/or password! Please try again.') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<input type="hidden" name="luci_magic" value="{{ luci_magic }}">
|
||||
|
||||
<div class="login-actions">
|
||||
<button type="submit" class="login-submit">{{ _('Log in') }}</button>
|
||||
</div>
|
||||
|
||||
@@ -140,8 +140,13 @@ html {
|
||||
|
||||
/* ── Form elements ── */
|
||||
|
||||
.login-error {
|
||||
.login-error,
|
||||
.alert-message {
|
||||
@apply border-destructive-a30 bg-destructive-a8 text-destructive mb-4 rounded-2xl border px-4 py-3 text-sm;
|
||||
|
||||
&.warning {
|
||||
@apply border-warning-a30 bg-warning-a10 text-warning-soft-fg;
|
||||
}
|
||||
}
|
||||
|
||||
.login-field {
|
||||
@@ -162,7 +167,11 @@ html {
|
||||
}
|
||||
|
||||
input {
|
||||
@apply text-foreground placeholder:text-muted-foreground w-full bg-transparent py-3.5 pr-10 pl-9 text-sm focus:outline-none;
|
||||
@apply text-foreground placeholder:text-muted-foreground w-full bg-transparent px-4 py-3.5 text-sm focus:outline-none;
|
||||
}
|
||||
|
||||
.login-input-icon ~ input {
|
||||
@apply pl-9;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
|
||||
LUCI_TITLE:=A modern sidebar LuCI theme for OpenWrt, built with shadcn/ui design language.
|
||||
LUCI_DEPENDS:=+luci-base
|
||||
|
||||
PKG_VERSION:=0.6.2
|
||||
PKG_RELEASE:=10
|
||||
PKG_VERSION:=0.6.3
|
||||
PKG_RELEASE:=11
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
LUCI_MINIFY_CSS:=
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
/*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
|
||||
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--spacing:.25rem;--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--radius-2xl:1rem;--radius-3xl:1.5rem;}}@layer base{*,:before,:after{box-sizing:border-box}body,p{margin:calc(var(--spacing) * 0)}button,input{margin:calc(var(--spacing) * 0);border-style:var(--tw-border-style);padding:calc(var(--spacing) * 0);color:inherit;font:inherit;background-color:#0000;border-width:0}::placeholder{opacity:1}[hidden]{display:none!important}}@layer components,utilities;@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-cyrillic-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-cyrillic-wght-normal.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-greek-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+1F??}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-greek-wght-normal.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-vietnamese-wght-normal.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--background:oklch(98.5% .004 275);--foreground:oklch(18% .02 275);--primary:oklch(56.7% .158 275);--primary-foreground:oklch(100% 0 0);--destructive:oklch(55% .2 25);--border:oklch(89.65% .0058 275);--muted-foreground:oklch(52.62% .0131 275);--primary-hover:oklch(62.7% .158 275);--primary-a10:oklch(56.7% .158 275/.1);--primary-a20:oklch(56.7% .158 275/.2);--primary-a70:oklch(56.7% .158 275/.7);--destructive-a8:oklch(55% .2 25/.08);--destructive-a30:oklch(55% .2 25/.3);--foreground-a5:oklch(18% .02 275/.05);--font-sans:"Inter Variable", ui-sans-serif, system-ui, sans-serif;}[data-darkmode=true]{--background:oklch(15.9% .005 264);--foreground:oklch(90% .006 264);--primary:oklch(56.7% .158 275);--primary-foreground:oklch(100% 0 0);--destructive:oklch(63% .16 25);--border:oklch(26.27% .0051 264);--muted-foreground:oklch(58.14% .0056 264);--primary-hover:oklch(62.7% .158 275);--primary-a10:oklch(56.7% .158 275/.1);--primary-a20:oklch(56.7% .158 275/.2);--primary-a70:oklch(56.7% .158 275/.7);--destructive-a8:oklch(63% .16 25/.08);--destructive-a30:oklch(63% .16 25/.3);--foreground-a5:oklch(90% .006 264/.05);}html{color-scheme:light}html[data-darkmode=true]{color-scheme:dark}.card-header img,.login-input-icon img,.login-pw-toggle img,img.shadcn-icon{filter:brightness(0)saturate()}html[data-darkmode=true] .card-header img,html[data-darkmode=true] .login-input-icon img,html[data-darkmode=true] .login-pw-toggle img,html[data-darkmode=true] img.shadcn-icon{filter:brightness(0)saturate()invert()}.card-header img,img.shadcn-icon,#sidebar img.shadcn-icon{opacity:.65}html[data-darkmode=true] #sidebar img.shadcn-icon{opacity:.7}.card-header button:hover img,#sidebar .sidebar-nav-item.active img,#sidebar .sidebar-accordion-item.active img,#sidebar .sidebar-nav-parent:hover img{opacity:1}html{background-color:var(--background);font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height));color:var(--foreground)}@keyframes fadeSlideIn{to{opacity:1;transform:translateY(0)}}@keyframes slideRightIn{to{opacity:1;transfLine truncated
|
||||
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;}}}@layer theme{:root,:host{--font-sans:var(--font-sans);--spacing:.25rem;--container-md:28rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--radius-2xl:1rem;--radius-3xl:1.5rem;}}@layer base{*,:before,:after{box-sizing:border-box}body,p{margin:calc(var(--spacing) * 0)}button,input{margin:calc(var(--spacing) * 0);border-style:var(--tw-border-style);padding:calc(var(--spacing) * 0);color:inherit;font:inherit;background-color:#0000;border-width:0}::placeholder{opacity:1}[hidden]{display:none!important}}@layer components,utilities;@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-cyrillic-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+460-52F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-cyrillic-wght-normal.woff2)format("woff2-variations");unicode-range:U+301,U+400-45F,U+490-491,U+4B0-4B1,U+2116}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-greek-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+1F??}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-greek-wght-normal.woff2)format("woff2-variations");unicode-range:U+370-377,U+37A-37F,U+384-38A,U+38C,U+38E-3A1,U+3A3-3FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-vietnamese-wght-normal.woff2)format("woff2-variations");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-latin-ext-wght-normal.woff2)format("woff2-variations");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Inter Variable;font-style:normal;font-display:swap;font-weight:100 900;src:url(/luci-static/shadcn/inter-latin-wght-normal.woff2)format("woff2-variations");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}:root{--background:oklch(98.5% .004 275);--foreground:oklch(18% .02 275);--primary:oklch(56.7% .158 275);--primary-foreground:oklch(100% 0 0);--destructive:oklch(55% .2 25);--border:oklch(89.65% .0058 275);--muted-foreground:oklch(52.62% .0131 275);--primary-hover:oklch(62.7% .158 275);--warning-soft-fg:oklch(42% .12 75);--primary-a10:oklch(56.7% .158 275/.1);--primary-a20:oklch(56.7% .158 275/.2);--primary-a70:oklch(56.7% .158 275/.7);--destructive-a8:oklch(55% .2 25/.08);--destructive-a30:oklch(55% .2 25/.3);--foreground-a5:oklch(18% .02 275/.05);--warning-a10:oklch(70% .13 75/.1);--warning-a30:oklch(70% .13 75/.3);--font-sans:"Inter Variable", ui-sans-serif, system-ui, sans-serif;}[data-darkmode=true]{--background:oklch(15.9% .005 264);--foreground:oklch(90% .006 264);--primary:oklch(56.7% .158 275);--primary-foreground:oklch(100% 0 0);--destructive:oklch(63% .16 25);--border:oklch(26.27% .0051 264);--muted-foreground:oklch(58.14% .0056 264);--primary-hover:oklch(62.7% .158 275);--warning-soft-fg:oklch(86% .1 85);--primary-a10:oklch(56.7% .158 275/.1);--primary-a20:oklch(56.7% .158 275/.2);--primary-a70:oklch(56.7% .158 275/.7);--destructive-a8:oklch(63% .16 25/.08);--destructive-a30:oklch(63% .16 25/.3);--foreground-a5:oklch(90% .006 264/.05);--warning-a10:oklch(80% .12 80/.1);--warning-a30:oklch(80% .12 80/.3);}html{color-scheme:light}html[data-darkmode=true]{color-scheme:dark}.card-header img,.login-input-icon img,.login-pw-toggle img,img.shadcn-icon{filter:brightness(0)saturate()}html[data-darkmode=true] .card-header img,html[data-darkmode=true] .login-input-icon img,html[data-darkmode=true] .login-pw-toggle img,html[data-darkmode=true] img.shadcn-icon{filter:brightness(0)saturate()invert()}.card-header img,img.shadcn-icon,#sidebar img.shadcn-icon{opacity:.65}html[data-darkmode=true] #sidebar img.shadcn-icon{opacity:.7}.card-header button:hover img,#sidebar .sidebar-nav-item.active img,#sidebar .sidebar-accordion-item.active img,#sidebar .sidebar-nav-parent:hover img{opacity:1}html{background-color:var(--background);font-family:var(--font-sans);Line truncated
|
||||
@@ -8,8 +8,8 @@
|
||||
import { getuid, getspnam } from 'luci.core';
|
||||
import { lsdir } from 'fs';
|
||||
|
||||
const boardinfo = ubus.call('system', 'board') ?? {};
|
||||
const rootsp = getspnam('root');
|
||||
const boardinfo = prefetched_boardinfo ?? (ubus.call('system', 'board') ?? {});
|
||||
const rootsp = !blank_page ? getspnam('root') : null;
|
||||
const root_pw_unset = (rootsp && rootsp.pwdp == '');
|
||||
|
||||
const page_segs = length(ctx?.request_path) ? ctx.request_path : (ctx?.path || []);
|
||||
@@ -82,8 +82,10 @@
|
||||
{% if (css): %}
|
||||
<style data-luci-shell>{{ css }}</style>
|
||||
{% endif %}
|
||||
{% if (!blank_page): %}
|
||||
<script src="{{ dispatcher.build_url('admin/translations', dispatcher.lang) }}"></script>
|
||||
<script src="{{ resource }}/cbi.js"></script>
|
||||
{% endif %}
|
||||
</head>
|
||||
|
||||
<body class="lang_{{ dispatcher.lang }}" data-page="{{ entityencode(page, true) }}" data-asset-version="{# PKG_VERSION #}"{% if (!blank_page): %} data-nav-stamp="{{ entityencode(join(':', [dispatcher.lang, ctx?.authuser ?? '', ctx?.authsession ?? '']), true) }}:{# PKG_VERSION #}"{% endif %}{% if (length(patch_all)): %} data-patches="{{ entityencode(join(' ', patch_all), true) }}"{% endif %}>
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
Licensed to the public under the Apache License 2.0.
|
||||
-#}
|
||||
|
||||
{%const boardinfo = ubus.call('system', 'board') ?? {};-%}
|
||||
{% include('header', { blank_page: true }) %}
|
||||
{% const boardinfo = ubus.call('system', 'board') ?? {}; -%}
|
||||
{% include('header', { blank_page: true, prefetched_boardinfo: boardinfo }) %}
|
||||
<style>:root{--login-bg-image:url('{{ media }}/images/login-bg.jpeg')}</style>
|
||||
|
||||
<main class="login-layout">
|
||||
@@ -24,9 +24,9 @@
|
||||
</div>
|
||||
|
||||
<form method="post" class="login-form">
|
||||
{% if (fuser): %}
|
||||
<div class="login-error animate-element animate-delay-200">
|
||||
{{ _('Invalid username and/or password! Please try again.') }}
|
||||
{% if (auth_message || fuser): %}
|
||||
<div class="login-error{% if (auth_plugin): %} warning{% endif %} animate-element animate-delay-200">
|
||||
{{ auth_message || _('Invalid username and/or password! Please try again.') }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
id="luci_username"
|
||||
placeholder="{{ _('Username') }}"
|
||||
autocomplete="username"
|
||||
{% if (duser): %}value="{{ entityencode(duser, true) }}"{% else %}value=""{% endif %}
|
||||
value="{{ entityencode(duser ?? '', true) }}"
|
||||
{% if (!duser): %}autofocus{% endif %}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,7 +60,7 @@
|
||||
id="luci_password"
|
||||
placeholder="{{ _('Password') }}"
|
||||
autocomplete="current-password"
|
||||
autofocus
|
||||
{% if (duser): %}autofocus{% endif %}
|
||||
>
|
||||
<button type="button" class="login-pw-toggle" id="pw-toggle" aria-label="{{ _('Reveal/hide password') }}">
|
||||
<img src="{{ media }}/icons/eye.svg" id="pw-toggle-icon" width="16" height="16" alt="">
|
||||
@@ -67,6 +68,39 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if (auth_fields): %}
|
||||
{% for (let field in auth_fields): %}
|
||||
<div class="login-field animate-element animate-delay-300">
|
||||
<label class="login-label" for="{{ field.name }}">{{ _(field.label ?? field.name) }}</label>
|
||||
<div class="login-input-glass">
|
||||
<input
|
||||
name="{{ field.name }}"
|
||||
id="{{ field.name }}"
|
||||
type="{{ field.type ?? 'text' }}"
|
||||
{% if (field.placeholder): %}placeholder="{{ field.placeholder }}"{% endif %}
|
||||
{% if (field.inputmode): %}inputmode="{{ field.inputmode }}"{% endif %}
|
||||
{% if (field.pattern): %}pattern="{{ field.pattern }}"{% endif %}
|
||||
{% if (field.maxlength): %}maxlength="{{ field.maxlength }}"{% endif %}
|
||||
{% if (field.autocomplete): %}autocomplete="{{ field.autocomplete }}"{% endif %}
|
||||
{% if (field.required): %}required{% endif %}
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if (auth_html): %}
|
||||
<div class="login-field animate-element animate-delay-300">
|
||||
{{ auth_html }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if (auth_assets): %}
|
||||
{% for (let asset in auth_assets): %}
|
||||
{% if (asset.type == 'script'): %}
|
||||
<script src="{{ asset.src }}"></script>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<input type="hidden" name="luci_magic" value="{{ luci_magic }}">
|
||||
|
||||
<button type="submit" class="login-submit animate-element animate-delay-400">
|
||||
|
||||
@@ -11,7 +11,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-theme-vantage
|
||||
PKG_VERSION:=1.0.14
|
||||
PKG_RELEASE:=29
|
||||
PKG_RELEASE:=30
|
||||
PKG_LICENSE:=GPL-3.0-or-later
|
||||
|
||||
LUCI_TITLE:=Vantage theme
|
||||
|
||||
@@ -3189,6 +3189,19 @@ select[id^="widget.cbid.firewall."] > option { color: var(--text); font-weight:
|
||||
}
|
||||
[id^="cbi-firewall-"] .table:has(.cbi-section-table-titles.named[data-title]) .cbi-section-table-row[data-title]:has(input[data-widget-id$=".enabled"]:not(:checked))::before { color: var(--text-3); }
|
||||
|
||||
/* File Manager (luci-app-filemanager) injects a light palette of its own: a
|
||||
#f9f9f9 status bar, #f0f0f0 row hover and editor gutter, #ccc rules. On the
|
||||
dark scheme that is a white band under the theme's light ink; repaint those
|
||||
surfaces with the theme's, and let the table's own hover show through the
|
||||
app's !important row fill. */
|
||||
body[data-page="admin-system-filemanager"] #status-bar { background: var(--s1); border-color: var(--line); }
|
||||
body[data-page="admin-system-filemanager"] #status-bar .cbi-progressbar { background: none; }
|
||||
body[data-page="admin-system-filemanager"] :is(#file-list-container, #content-help) { border-color: var(--line); }
|
||||
body[data-page="admin-system-filemanager"] #file-table :is(th, td) { border-right-color: var(--line); }
|
||||
body[data-page="admin-system-filemanager"] #file-table tr:hover { background: none !important; }
|
||||
body[data-page="admin-system-filemanager"] .line-numbers { background: var(--s3); border-right-color: var(--line); }
|
||||
body[data-page="admin-system-filemanager"] .sort-button { border-color: var(--line-2); }
|
||||
|
||||
/* ----------------------------------------------------- responsive */
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ MISE_HASH_x86_64:=3832f39c325e343f81fe3d92b2447c5d1a5eea1bc85092bb7b6c2580622264
|
||||
MISE_HASH_aarch64:=06186cfbfe947049b21d58575fb0ea800cc26ed1375f20f4b678cb3a9d679437
|
||||
|
||||
PKG_NAME:=mise
|
||||
PKG_VERSION:=2026.11.0
|
||||
PKG_RELEASE:=49
|
||||
PKG_VERSION:=2026.11.1
|
||||
PKG_RELEASE:=50
|
||||
PKG_SOURCE:=$(PKG_NAME)-v$(PKG_VERSION)-linux-$(MISE_ARCH)-musl.tar.gz
|
||||
PKG_SOURCE_URL:=https://github.com/jdx/mise/releases/download/v$(PKG_VERSION)/
|
||||
PKG_HASH:=skip
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=mptcp
|
||||
PKG_VERSION:=6.1
|
||||
PKG_RELEASE:=30
|
||||
PKG_RELEASE:=31
|
||||
|
||||
PKG_MAINTAINER:=Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
|
||||
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
@@ -27,13 +27,21 @@ valid_subnet4() {
|
||||
esac
|
||||
}
|
||||
|
||||
# An MPTCP limit as the kernel takes it: a number from 0 to 8, $2 when $1
|
||||
# is not a number
|
||||
# Most subflows and accepted ADD_ADDR per connection: x86_64 routers
|
||||
# aggregate up to 16 WANs, the others 8
|
||||
case "$(uname -m)" in
|
||||
x86_64) MPTCP_LIMIT_MAX=16 ;;
|
||||
*) MPTCP_LIMIT_MAX=8 ;;
|
||||
esac
|
||||
|
||||
# An MPTCP limit as the kernel takes it: a number from 0 to $3 (default
|
||||
# MPTCP_LIMIT_MAX), $2 when $1 is not a number
|
||||
_mptcp_limit() {
|
||||
local max="${3:-${MPTCP_LIMIT_MAX:-8}}"
|
||||
case "$1" in
|
||||
''|*[!0-9]*) echo "$2" ;;
|
||||
?|??) if [ "$1" -gt 8 ]; then echo 8; else echo "$1"; fi ;;
|
||||
*) echo 8 ;;
|
||||
?|??) if [ "$1" -gt "$max" ]; then echo "$max"; else echo "$1"; fi ;;
|
||||
*) echo "$max" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -182,12 +190,14 @@ global_multipath_settings() {
|
||||
elif [ -f /proc/sys/net/mptcp/enabled ]; then
|
||||
sysctl -qew net.mptcp.enabled="$multipath_status"
|
||||
_sysctl_conf_set net.mptcp.enabled "$multipath_status"
|
||||
# The kernel takes at most 8 for each limit (MPTCP_PM_ADDR_MAX) and
|
||||
# refuses the whole command when one is above, so a single value too
|
||||
# high left both limits at whatever they were
|
||||
# The kernel refuses the whole command when one limit is above its
|
||||
# maximum, so a single value too high left both limits at whatever
|
||||
# they were. That maximum is 8 on a kernel without the 64 limits
|
||||
# backport: retry within it there.
|
||||
mptcp_add_addr_accepted="$(_mptcp_limit "$mptcp_add_addr_accepted" 1)"
|
||||
mptcp_subflows="$(_mptcp_limit "$mptcp_subflows" 3)"
|
||||
ip mptcp limits set add_addr_accepted $mptcp_add_addr_accepted subflows $mptcp_subflows >/dev/null 2>&1
|
||||
ip mptcp limits set add_addr_accepted $mptcp_add_addr_accepted subflows $mptcp_subflows >/dev/null 2>&1 || \
|
||||
ip mptcp limits set add_addr_accepted "$(_mptcp_limit "$mptcp_add_addr_accepted" 1 8)" subflows "$(_mptcp_limit "$mptcp_subflows" 3 8)" >/dev/null 2>&1
|
||||
# The subflow limit decides how many socket buffers one MPTCP connection
|
||||
# can hold open at once, and they all draw on the same net.ipv4.tcp_mem
|
||||
# pool, so re-run the sizing now that the limit is known. Cheap and
|
||||
|
||||
+2
-2
@@ -8,8 +8,8 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=netdata
|
||||
PKG_VERSION:=2.12.2
|
||||
PKG_RELEASE:=23
|
||||
PKG_VERSION:=2.12.3
|
||||
PKG_RELEASE:=24
|
||||
|
||||
PKG_MAINTAINER:=Josef Schlehofer <pepe.schlehofer@gmail.com>, Daniel Engberg <daniel.engberg.lists@pyret.net>
|
||||
PKG_LICENSE:=GPL-3.0-or-later
|
||||
|
||||
Reference in new issue
Block a user