🍓 Sync 2026-07-05 20:30:31

This commit is contained in:
github-actions[bot] 2026-07-05 20:30:31 +08:00
parent 5378a4f7c1
commit a4f37aefb9
30 changed files with 3430 additions and 3012 deletions

View File

@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=daed
PKG_VERSION:=2026.06.14
PKG_RELEASE:=17
PKG_RELEASE:=18
PKG_SOURCE:=daed-src-2026.06.14-4e215048068a.tar.gz
PKG_SOURCE_URL:=https://github.com/kenzok8/openwrt-daede/releases/download/daed-src
@ -40,7 +40,7 @@ GO_PKG_LDFLAGS:= \
GO_PKG_LDFLAGS_X:= \
$(GO_PKG)/db.AppName=$(PKG_NAME) \
$(GO_PKG)/db.AppVersion=$(PKG_VERSION)
GO_PKG_TAGS:=embedallowed,trace
GO_PKG_TAGS:=embedallowed,trace,timetzdata
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/bpf.mk

View File

@ -24,6 +24,7 @@ start_service() {
procd_open_instance "$CONF"
procd_set_param env DAE_LOCATION_ASSET="/usr/share/v2ray"
procd_set_param env TZ="$(uci -q get system.@system[0].zonename)"
procd_set_param command "$PROG" run
procd_append_param command --config "/etc/daed/"
procd_append_param command --listen "$listen_addr"

View File

@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-daede
PKG_VERSION:=1.14.7
PKG_RELEASE:=27
PKG_RELEASE:=28
PKG_MAINTAINER:=kenzok8
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)

View File

@ -662,6 +662,7 @@ return view.extend({
let before;
let newSubId = '';
let createdGroupId = '';
const createdNodeIds = [];
let groupReady = false;
const oldSubId = existingAirport ? existingAirport.subscription_id : '';
const oldNodeIds = existingAirport ? existingAirport.node_ids : [];
@ -671,7 +672,113 @@ return view.extend({
return requestDaedToken(endpoint, forceLogin).then(function(auth) {
token = auth.token;
usedCachedToken = auth.cached;
return graphQL(endpoint, 'query State{groups{id name nodes{id}}}', {}, token);
return graphQL(endpoint, 'query State{nodes(first:10000){edges{id link tag}} groups{id name nodes{id}}}', {}, token);
});
};
const importDirectNodes = function() {
const existing = {};
(((before || {}).nodes || {}).edges || []).forEach(function(node) {
existing[clashConverter.normalizeLink(node.link)] = node;
});
const fresh = items.filter(function(item) {
const duplicate = existing[clashConverter.normalizeLink(item.link)];
if (duplicate)
item.duplicate = true;
return !duplicate;
});
const imported = fresh.length
? graphQL(endpoint,
'mutation ImportNodes($args:[ImportArgument!]!){importNodes(rollbackError:false,args:$args){link error node{id}}}',
{ args: fresh.map(function(item, index) { return { link: item.link, tag: airportSync.backendId(airportId) + Date.now().toString(36) + (index + 1) }; }) }, token)
: Promise.resolve({ importNodes: [] });
return imported.then(function(result) {
const rows = result.importNodes || [];
const nodeIds = [];
const ownedIds = [];
const rowByLink = {};
rows.forEach(function(row) {
rowByLink[clashConverter.normalizeLink(row.link)] = row;
});
let failed = rows.filter(function(row) { return !!row.error && row.error !== 'node already exists'; }).length;
items.forEach(function(item) {
const key = clashConverter.normalizeLink(item.link);
const row = rowByLink[key];
const old = existing[key];
const id = row && row.node && row.node.id || old && old.id;
if (id)
nodeIds.push(id);
if (row && !row.error && row.node) {
ownedIds.push(row.node.id);
createdNodeIds.push(row.node.id);
}
else if (existingAirport && oldNodeIds.indexOf(id) >= 0)
ownedIds.push(id);
});
if (!nodeIds.length) {
const details = rows.filter(function(row) { return !!row.error; }).map(function(row) {
return row.error;
}).filter(function(error, index, errors) {
return errors.indexOf(error) === index;
}).join('; ');
throw new Error(details || _('No usable nodes were imported'));
}
const oldManagedGroup = airportSync.findManagedGroup(before.groups, existingAirport);
const ensureGroup = oldManagedGroup
? Promise.resolve(oldManagedGroup.id)
: graphQL(endpoint,
'mutation CreateGroup($name:String!,$policy:Policy!){createGroup(name:$name,policy:$policy){id}}',
{ name: groupName, policy: 'min_moving_avg' }, token).then(function(value) {
createdGroupId = value.createGroup.id;
return createdGroupId;
});
return ensureGroup.then(function(groupId) {
const rename = oldManagedGroup && oldManagedGroup.name !== groupName
? graphQL(endpoint, 'mutation RenameGroup($id:ID!,$name:String!){renameGroup(id:$id,name:$name)}', { id: groupId, name: groupName }, token)
: Promise.resolve();
return rename.then(function() {
return graphQL(endpoint, 'mutation SetPolicy($id:ID!,$policy:Policy!){groupSetPolicy(id:$id,policy:$policy)}', { id: groupId, policy: 'min_moving_avg' }, token);
}).then(function() {
return graphQL(endpoint, 'mutation AddNodes($id:ID!,$nodeIDs:[ID!]!){groupAddNodes(id:$id,nodeIDs:$nodeIDs)}', { id: groupId, nodeIDs: nodeIds }, token);
}).then(function() {
groupReady = true;
const oldMembers = oldManagedGroup ? oldManagedGroup.nodes.map(function(node) { return node.id; }) : [];
const removedMembers = oldMembers.filter(function(id) { return nodeIds.indexOf(id) < 0; });
return (removedMembers.length
? graphQL(endpoint, 'mutation DelNodes($id:ID!,$nodeIDs:[ID!]!){groupDelNodes(id:$id,nodeIDs:$nodeIDs)}', { id: groupId, nodeIDs: removedMembers }, token)
: Promise.resolve()).catch(function() {
failed++;
}).then(function() {
const otherAirportNodes = airportRecords().filter(function(record) {
return record.backend === 'daed' && (!existingAirport || record.id !== existingAirport.id);
}).map(function(record) { return record.node_ids; });
const otherGroupNodes = before.groups.filter(function(other) {
return other.id !== groupId;
}).map(function(other) { return other.nodes.map(function(node) { return node.id; }); });
const stale = airportSync.safeOldNodeIds(oldNodeIds, ownedIds, otherAirportNodes, otherGroupNodes);
return (stale.length
? graphQL(endpoint, 'mutation RemoveNodes($ids:[ID!]!){removeNodes(ids:$ids)}', { ids: stale }, token)
: Promise.resolve()).catch(function() {
failed++;
}).then(function() {
writeAirportRecord(existingAirport, {
id: airportId,
backend: 'daed',
name: name,
sourceHash: state.sourceHash,
groupId: groupId,
subscriptionId: '',
nodeIds: ownedIds
});
return applyUciChanges().then(function() {
items.forEach(function(item) { item.duplicate = true; item.selected = false; });
return { added: ownedIds.length, duplicates: items.length - ownedIds.length, failed: failed };
});
});
});
});
});
});
};
@ -693,14 +800,35 @@ return view.extend({
// import the whole converted batch as one subscription
return graphQL(endpoint,
'mutation Import($a:ImportArgument!){importSubscription(rollbackError:false,arg:$a){sub{id} nodeImportResult{error}}}',
{ a: { link: subUrl, tag: groupName } }, token);
{ a: { link: subUrl, tag: groupName } }, token).then(function(result) {
const sub = result.importSubscription && result.importSubscription.sub;
const rows = (result.importSubscription && result.importSubscription.nodeImportResult) || [];
const usable = rows.length
? rows.some(function(r) { return !r.error || r.error === 'node already exists'; })
: !!(sub && sub.id);
if (!sub || !sub.id || !usable) {
const details = rows.map(function(r) { return r.error; }).filter(Boolean).join('; ');
if (sub && sub.id)
newSubId = sub.id;
throw new Error(details || _('No usable nodes were imported'));
}
return result;
}).catch(function(error) {
if (daedSession.isAccessDenied(error))
throw error;
const cleanupSub = newSubId
? graphQL(endpoint, 'mutation Rm($ids:[ID!]!){removeSubscriptions(ids:$ids)}', { ids: [ newSubId ] }, token).catch(function() {})
: Promise.resolve();
newSubId = '';
return cleanupSub.then(importDirectNodes).then(function(result) {
return { directResult: result };
});
});
}).then(function(result) {
if (result.directResult)
return result.directResult;
const sub = result.importSubscription && result.importSubscription.sub;
if (!sub || !sub.id) {
const rows = (result.importSubscription && result.importSubscription.nodeImportResult) || [];
const details = rows.map(function(r) { return r.error; }).filter(Boolean).join('; ');
throw new Error(details || _('No usable nodes were imported'));
}
newSubId = sub.id;
const rows = result.importSubscription.nodeImportResult || [];
const failed = rows.filter(function(r) { return !!r.error && r.error !== 'node already exists'; }).length;
@ -745,11 +873,13 @@ return view.extend({
}).catch(function(error) {
if (daedSession.isAccessDenied(error))
daedSession.clear(window.localStorage);
if (groupReady || !token || (!newSubId && !createdGroupId))
if (groupReady || !token || (!newSubId && !createdGroupId && !createdNodeIds.length))
throw error;
const cleanup = [];
if (newSubId)
cleanup.push(graphQL(endpoint, 'mutation Rm($ids:[ID!]!){removeSubscriptions(ids:$ids)}', { ids: [ newSubId ] }, token));
if (createdNodeIds.length)
cleanup.push(graphQL(endpoint, 'mutation Rm($ids:[ID!]!){removeNodes(ids:$ids)}', { ids: createdNodeIds }, token));
if (createdGroupId)
cleanup.push(graphQL(endpoint, 'mutation RmG($id:ID!){removeGroup(id:$id)}', { id: createdGroupId }, token));
return Promise.all(cleanup).catch(function() {}).then(function() { throw error; });

View File

@ -17,6 +17,7 @@ const DEFAULT_TEMPLATE =
'global {\n' +
' tproxy_port: 12345\n' +
' tproxy_port_protect: true\n' +
' so_mark_from_dae: 0\n' +
' log_level: info\n' +
' lan_interface: br-lan\n' +
' wan_interface: auto\n' +

View File

@ -234,6 +234,7 @@ generate() {
echo "global {"
echo " tproxy_port: 12345"
echo " tproxy_port_protect: true"
echo " so_mark_from_dae: 0"
echo " log_level: ${log_level}"
[ -n "$lan_interface" ] && echo " lan_interface: ${lan_interface}"
echo " wan_interface: ${wan_interface}"

View File

@ -242,7 +242,6 @@ const proxy_group_type = [
['fallback', _('Fallback')],
['url-test', _('URL test')],
['load-balance', _('Load balance')],
//['relay', _('Relay')], // Deprecated
];
const routing_port_type = [
@ -1763,7 +1762,7 @@ function textvalue2Value(section_id) {
let cval = this.cfgvalue(section_id);
let i = this.keylist.indexOf(cval);
return this.vallist[i];
return this.vallist[i] ?? cval;
}
function validateAuth(section_id, value) {

View File

@ -461,9 +461,6 @@ function renderListeners(s, uciconfig, isClient) {
o.modalonly = true;
o = s.taboption('field_general', form.ListValue, 'snell_version', _('Version'));
o.value('1', _('v1'));
o.value('2', _('v2'));
o.value('3', _('v3'));
o.value('4', _('v4'));
o.value('5', _('v5'));
o.default = '4';
@ -562,21 +559,32 @@ function renderListeners(s, uciconfig, isClient) {
o.value('obfs', _('obfs-simple'));
o.value('shadow-tls', _('shadow-tls'));
//o.value('kcp-tun', _('kcp-tun'));
o.depends('type', 'shadowsocks');
o.validate = function(section_id, value) {
const type = this.section.getOption('type').formvalue(section_id);
if (value) {
if (type === 'snell' && !['obfs', 'shadow-tls'].includes(value)) {
return _('Expecting: only support %s.').format(_('obfs-simple') +
' / ' + _('shadow-tls'));
}
}
return true;
}
o.depends({type: /^(shadowsocks|snell)$/});
o.modalonly = true;
o = s.taboption('field_general', form.ListValue, 'plugin_opts_obfsmode', _('Plugin: ') + _('Obfs Mode'));
o.value('http', _('HTTP'));
o.value('tls', _('TLS'));
o.depends('plugin', 'obfs');
o.depends('type', 'snell');
o.modalonly = true;
o = s.taboption('field_general', form.Value, 'plugin_opts_host', _('Plugin: ') + _('Host that supports TLS 1.3'));
o.datatype = 'hostname';
o.placeholder = 'cloud.tencent.com';
o.rmempty = false;
o.depends('type', 'snell');
o.depends({plugin: 'obfs', type: 'snell'});
o.modalonly = true;
o = s.taboption('field_general', form.Value, 'plugin_opts_handshake_dest', _('Plugin: ') + _('Handshake target that supports TLS 1.3'));

View File

@ -13,7 +13,7 @@ const parseProxyGroupYaml = hm.parseYaml.extend({
if (!cfg.type)
return null;
// key mapping // 2026/06/10
// key mapping // 2026/07/05
let config = hm.removeBlankAttrs({
id: this.id,
label: this.label,
@ -23,7 +23,9 @@ const parseProxyGroupYaml = hm.parseYaml.extend({
include_all: this.bool2str(cfg["include-all"]), // bool
include_all_proxies: this.bool2str(cfg["include-all-proxies"]), // bool
include_all_providers: this.bool2str(cfg["include-all-providers"]), // bool
empty_fallback: cfg["empty-fallback"] ? hm.preset_outbound.proxy.map(([key, label]) => key).includes(cfg["empty-fallback"]) ? cfg["empty-fallback"] : this.calcID(hm.glossary["proxy_group"].field, cfg["empty-fallback"]) : null, // string
empty_fallback: cfg["empty-fallback"], // string
// Select fields
default_selected: cfg["default-selected"], // string
// Url-test fields
tolerance: cfg.tolerance,
// Load-balance fields
@ -683,10 +685,10 @@ function renderPayload(s, total, uciconfig) {
initDynamicPayload(o, n, 'factor', uciconfig);
o.load = L.bind(function(n, key, uciconfig, section_id) {
let fusedval = [
['NETWORK', '-- NETWORK --'],
['NETWORK', _('-- NETWORK --')],
['udp', _('UDP')],
['tcp', _('TCP')],
['RULESET', '-- RULE-SET --']
['RULESET', _('-- RULE-SET --')]
];
hm.loadLabel.call(this, [
@ -1011,6 +1013,7 @@ return view.extend({
'- name: AllProvider\n' +
' type: select\n' +
' include-all-providers: true\n' +
' default-selected: proxy1\n' +
' filter: "(?i)港|hk|hongkong|hong kong"\n' +
' exclude-filter: "美|日"\n' +
' exclude-type: "Shadowsocks|Http"\n' +
@ -1133,6 +1136,7 @@ return view.extend({
so.load = function(section_id) {
return hm.loadLabel.call(this, [
...hm.preset_outbound.proxy,
['NODE', _('-- PROXY-NODE --')],
...hm.loadLabelValues(this.config, 'node')
], section_id);
}
@ -1189,6 +1193,23 @@ return view.extend({
so.depends({type: 'select', '!reverse': true});
so.modalonly = true;
/* Select fields */
so = ss.taboption('field_general', form.Value, 'default_selected', _('Default selected'));
hm.preset_outbound.proxy.forEach((res) => {
so.value.apply(so, res);
})
so.load = function(section_id) {
return hm.loadLabel.call(this, [
...hm.preset_outbound.proxy,
['GROUP', _('-- PROXY-GROUP --')],
...hm.loadLabelValues(this.config, 'proxy_group'),
['NODE', _('-- PROXY-NODE --')],
...hm.loadLabelValues(this.config, 'node')
], section_id);
}
so.depends('type', 'select');
so.textvalue = hm.textvalue2Value;
/* Url-test fields */
so = ss.taboption('field_general', form.Value, 'tolerance', _('Node switch tolerance'),
_('In millisecond. <code>%s</code> will be used if empty.').format('150'));
@ -1888,6 +1909,23 @@ return view.extend({
so = ss.option(form.DynamicList, 'fallback_filter_domain', _('Domain'),
_('Match domain. Support wildcards.</br>') +
_('The matching <code>%s</code> will be deemed as poisoned.').format(_('Domain')));
so = ss.option(form.Flag, 'fallback_lazy_query', _('Lazy query'),
_('Lazy query.'));
so.default = so.disabled;
so.validate = function(section_id, value) {
let desc = this.getUIElement(section_id).node.nextSibling;
value = this.formvalue(section_id);
if (value == 1)
desc.innerHTML = _('Check the response from the <code>%s</code>, only initiate query if the <code>%s</code> is satisfied.')
.format(_('Default DNS server'), _('Fallback filter'));
else
desc.innerHTML = _('Send queries to both the <code>%s</code> and the <code>%s</code>.')
.format(_('Default DNS server'), _('Fallback DNS server'));
return true;
}
/* Fallback filter END */
return m.render();

View File

@ -886,14 +886,25 @@ return view.extend({
so.value('shadow-tls', _('shadow-tls'));
so.value('restls', _('restls'));
//so.value('kcptun', _('kcptun'));
so.depends('type', 'ss');
so.validate = function(section_id, value) {
const type = this.section.getOption('type').formvalue(section_id);
if (value) {
if (type === 'snell' && !['obfs', 'shadow-tls'].includes(value)) {
return _('Expecting: only support %s.').format(_('obfs-simple') +
' / ' + _('shadow-tls'));
}
}
return true;
}
so.depends({type: /^(ss|snell)$/});
so.modalonly = true;
so = ss.taboption('field_general', form.ListValue, 'plugin_opts_obfsmode', _('Plugin: ') + _('Obfs Mode'));
so.value('http', _('HTTP'));
so.value('tls', _('TLS'));
so.depends('plugin', 'obfs');
so.depends('type', 'snell');
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'plugin_opts_host', _('Plugin: ') + _('Host that supports TLS 1.3'));
@ -901,7 +912,6 @@ return view.extend({
so.placeholder = 'cloud.tencent.com';
so.rmempty = false;
so.depends({plugin: /^(obfs|v2ray-plugin|shadow-tls|restls)$/});
so.depends('type', 'snell');
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'plugin_opts_thetlspassword', _('Plugin: ') + _('Password'));
@ -949,6 +959,14 @@ return view.extend({
so.depends({type: 'hysteria2'});
so.modalonly = true;
so = ss.taboption('field_general', form.Value, 'handshake_timeout', _('Handshake timeout'),
_('In seconds. After configuration, the handshake is not affected by the outer connection timeout.') + '</br>' +
_('The default value is <code>%s</code>, indicating that only the outer connection timeout is used.').format('0'));
so.datatype = 'uinteger';
so.placeholder = '30';
so.depends({type: /^(masque|openvpn)$/});
so.modalonly = true;
so = ss.taboption('field_general', form.Flag, 'udp', _('UDP'));
so.default = so.disabled;
so.depends({type: /^(rematch|direct|socks5|ss|mieru|vmess|vless|trojan|anytls|trusttunnel|masque|wireguard)$/});
@ -1114,6 +1132,7 @@ return view.extend({
switch (type) {
case 'ss':
case 'snell':
def_alpn = ['h2', 'http/1.1']; // when plugin === 'shadow-tls'
break;
case 'hysteria':
@ -1142,7 +1161,7 @@ return view.extend({
return true;
}
so.depends({tls: '1', type: /^(vmess|vless|trojan|anytls|hysteria|hysteria2|tuic|trusttunnel)$/});
so.depends({type: 'ss', plugin: 'shadow-tls'});
so.depends({type: /^(ss|snell)$/, plugin: 'shadow-tls'});
so.modalonly = true;
so = ss.taboption('field_tls', form.Value, 'tls_fingerprint', _('Cert fingerprint'),
@ -1197,7 +1216,7 @@ return view.extend({
so = ss.taboption('field_tls', form.Flag, 'tls_ech', _('Enable ECH'));
so.default = so.disabled;
so.depends({tls: '1', type: /^(vmess|vless|trojan|anytls|hysteria|hysteria2|tuic)$/});
so.depends({type: 'ss', plugin: /^(shadow-tls|restls)$/});
so.depends({type: 'ss', plugin: /^(v2ray-plugin|gost-plugin)$/});
so.modalonly = true;
so = ss.taboption('field_tls', form.Value, 'tls_ech_config', _('ECH config'),
@ -1218,7 +1237,7 @@ return view.extend({
so.value.apply(so, res);
})
so.depends({tls: '1', type: /^(vmess|vless|trojan|anytls|trusttunnel)$/});
so.depends({type: 'ss', plugin: /^(shadow-tls|restls)$/});
so.depends({type: /^(ss|snell)$/, plugin: /^(shadow-tls|restls)$/});
so.modalonly = true;
so = ss.taboption('field_tls', form.Flag, 'tls_reality', _('REALITY'));

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -121,7 +121,7 @@ function parse_filter(cfg) {
return cfg;
}
function get_proxy(cfg) {
function get_proxy(cfg, pass_undefined) {
if (isEmpty(cfg))
return null;
@ -129,9 +129,12 @@ function get_proxy(cfg) {
return cfg;
const label = uci.get(uciconf, cfg, 'label');
if (isEmpty(label))
die(sprintf("%s's label is missing, please check your configuration.", cfg));
else
if (isEmpty(label)) {
if (pass_undefined)
return cfg;
else
die(sprintf("%s's label is missing, please check your configuration.", cfg));
} else
return label;
}
@ -440,6 +443,7 @@ if (!isEmpty(config.dns.fallback))
ipcidr: uci.get(uciconf, ucidns, 'fallback_filter_ipcidr') || [],
domain: uci.get(uciconf, ucidns, 'fallback_filter_domain') || [],
};
config.dns["fallback-lazy-query"] = strToBool(uci.get(uciconf, ucidns, 'fallback_lazy_query'));
/* DNS END */
/* Hosts START */
@ -559,10 +563,6 @@ uci.foreach(uciconf, ucinode, (cfg) => {
psk: cfg.snell_psk,
version: cfg.snell_version,
reuse: strToBool(cfg.snell_reuse),
"obfs-opts": cfg.type === 'snell' ? {
mode: cfg.plugin_opts_obfsmode,
host: cfg.plugin_opts_host,
} : null,
/* TUIC */
ip: cfg.tuic_ip,
@ -610,19 +610,35 @@ uci.foreach(uciconf, ucinode, (cfg) => {
"persistent-keepalive": strToInt(cfg.wireguard_persistent_keepalive),
/* Plugin fields */
plugin: cfg.plugin,
"plugin-opts": cfg.plugin ? {
mode: cfg.plugin_opts_obfsmode,
host: cfg.plugin_opts_host,
password: cfg.plugin_opts_thetlspassword,
version: strToInt(cfg.plugin_opts_shadowtls_version),
"version-hint": cfg.plugin_opts_restls_versionhint,
"restls-script": cfg.plugin_opts_restls_script
} : null,
...(cfg.plugin ? (
cfg.type === 'snell' ? {
// snell
"obfs-opts": {
mode: cfg.plugin in ['shadow-tls'] ? cfg.plugin : cfg.plugin_opts_obfsmode,
host: cfg.plugin_opts_host,
password: cfg.plugin_opts_thetlspassword,
version: strToInt(cfg.plugin_opts_shadowtls_version),
alpn: cfg.tls_alpn // Array
}
} : {
// others
plugin: cfg.plugin,
"plugin-opts": {
mode: cfg.plugin_opts_obfsmode,
host: cfg.plugin_opts_host,
password: cfg.plugin_opts_thetlspassword,
version: strToInt(cfg.plugin_opts_shadowtls_version),
alpn: cfg.tls_alpn, // Array
"version-hint": cfg.plugin_opts_restls_versionhint,
"restls-script": cfg.plugin_opts_restls_script
}
}
) : {}),
/* Extra fields */
"congestion-controller": cfg.congestion_controller,
"bbr-profile": cfg.bbr_profile,
"handshake-timeout": strToInt(cfg.handshake_timeout),
udp: strToBool(cfg.udp),
"udp-over-tcp": strToBool(cfg.uot),
"udp-over-tcp-version": cfg.uot_version,
@ -633,7 +649,7 @@ uci.foreach(uciconf, ucinode, (cfg) => {
"disable-sni": strToBool(cfg.tls_disable_sni),
...arrToObj([[(cfg.type in ['vmess', 'vless']) ? 'servername' : 'sni', cfg.tls_sni]]),
fingerprint: cfg.tls_fingerprint,
alpn: cfg.tls_alpn, // Array
alpn: cfg.plugin in ['shadow-tls'] ? null : cfg.tls_alpn, // Array
"skip-cert-verify": strToBool(cfg.tls_skip_cert_verify),
certificate: cfg.tls_cert_path, // mTLS
"private-key": cfg.masque_private_key || cfg.wireguard_private_key || cfg.ssh_priv_key || cfg.tls_key_path, // mTLS/SSH/WireGuard/Masque
@ -744,7 +760,9 @@ uci.foreach(uciconf, ucipgrp, (cfg) => {
"include-all": strToBool(cfg.include_all),
"include-all-proxies": strToBool(cfg.include_all_proxies),
"include-all-providers": strToBool(cfg.include_all_providers),
"empty-fallback": cfg.empty_fallback ? get_proxy(cfg.empty_fallback) : null,
"empty-fallback": cfg.empty_fallback ? get_proxy(cfg.empty_fallback, true) : null,
// Select fields
"default-selected": cfg.default_selected ? get_proxy(cfg.default_selected, true) : null,
// Url-test fields
tolerance: (cfg.type === 'url-test') ? strToInt(cfg.tolerance) ?? 150 : null,
// Load-balance fields

View File

@ -307,10 +307,6 @@ export function parseListener(cfg, isClient, label) {
/* Snell */
psk: cfg.snell_psk,
version: cfg.snell_version,
"obfs-opts": cfg.type === 'snell' ? {
mode: cfg.plugin_opts_obfsmode,
host: cfg.plugin_opts_host,
} : null,
/* Tuic */
"max-idle-time": durationToSecond(cfg.tuic_max_idle_time),
@ -334,29 +330,41 @@ export function parseListener(cfg, isClient, label) {
target: cfg.tunnel_target,
/* Plugin fields */
...(cfg.plugin ? {
...(cfg.plugin ? (
cfg.plugin === 'obfs' ? (
// obfs-simple
"simple-obfs": cfg.plugin === 'obfs' ? {
enable: true,
mode: cfg.plugin_opts_obfsmode
} : null,
cfg.type === 'snell' ? {
// snell
"obfs-opts": {
mode: cfg.plugin_opts_obfsmode,
host: cfg.plugin_opts_host
}
} : {
// shadowsocks
"simple-obfs": {
enable: true,
mode: cfg.plugin_opts_obfsmode
}
}
) : cfg.plugin === 'shadow-tls' ? {
// shadow-tls
"shadow-tls": cfg.plugin === 'shadow-tls' ? {
enable: true,
version: strToInt(cfg.plugin_opts_shadowtls_version),
...(strToInt(cfg.plugin_opts_shadowtls_version) >= 3 ? {
users: [
{
name: 1,
password: cfg.plugin_opts_thetlspassword
}
],
} : { password: cfg.plugin_opts_thetlspassword }),
handshake: {
dest: cfg.plugin_opts_handshake_dest
},
} : null
} : {}),
"shadow-tls": {
enable: true,
version: strToInt(cfg.plugin_opts_shadowtls_version),
...(strToInt(cfg.plugin_opts_shadowtls_version) >= 3 ? {
users: [
{
name: 1,
password: cfg.plugin_opts_thetlspassword
}
],
} : { password: cfg.plugin_opts_thetlspassword }),
handshake: {
dest: cfg.plugin_opts_handshake_dest
}
}
} : {}
) : {}),
/* Extra fields */
"congestion-controller": cfg.congestion_controller,

View File

@ -12,8 +12,8 @@ PKG_MAINTAINER:=Rafał Wabik <4Rafal@gmail.com>
LUCI_DESCRIPTION:=LuCI JS interface for the lite-watchdog scripts.
LUCI_DEPENDS:=+sms-tool +kmod-usb-serial +kmod-usb-serial-option +comgt
LUCI_PKGARCH:=all
PKG_VERSION:=1.0.17
PKG_RELEASE:=2
PKG_VERSION:=1.0.18
PKG_RELEASE:=3
define Package/luci-app-lite-watchdog/conffiles
/etc/modem/log.txt

View File

@ -241,6 +241,7 @@ return view.extend({
o.value('wan', _('Connection restart'));
o.value('reboot', _('Reboot'));
o.default = action || "wan";
o.rmempty = false;
}
return m.render();

View File

@ -9,7 +9,7 @@
--bg: oklch(0.967 0.003 264);
--surface: oklch(1 0 0);
--text: oklch(0.21 0.02 264);
--brand: oklch(0.68 0.11 233);
--brand: oklch(0.58 0.14 233);
--on-brand: oklch(1 0 0);
--link: oklch(0.74 0.238 322.16);
--info: oklch(0.45 0.12 255);
@ -18,16 +18,16 @@
--danger: oklch(0.35 0.12 25);
--text-muted: oklch(49.77% 0.0135 264);
--text-subtle: oklch(60.36% 0.0112 264);
--surface-sunken: oklch(95.7% 0.003 264);
--surface-sunken: oklch(95.5% 0.003 264);
--surface-overlay: oklch(98.3% 0.003 264);
--hairline: oklch(21% 0.02 264 / 0.08);
--hairline: oklch(21% 0.02 264 / 0.13);
--hover-faint: oklch(92.7% 0.003 264);
--brand-hover: oklch(62% 0.11 233);
--brand-subtle: oklch(93.26% 0.0155 238);
--brand-subtle-hover: oklch(89.26% 0.0155 238);
--focus-ring: oklch(68% 0.11 233 / 0.6);
--progress-start: oklch(77.7% 0.0724 233.4);
--progress-end: oklch(0.68 0.11 233);
--brand-hover: oklch(52% 0.14 233);
--brand-subtle: oklch(92.06% 0.0191 237.1);
--brand-subtle-hover: oklch(88.06% 0.0191 237.1);
--focus-ring: oklch(58% 0.14 233 / 0.6);
--progress-start: oklch(71.13% 0.0919 233.3);
--progress-end: oklch(0.58 0.14 233);
--info-surface: oklch(94% 0.05 255);
--warning-surface: oklch(95% 0.05 60);
--success-surface: oklch(94% 0.05 165);

View File

@ -6,7 +6,7 @@ input[type="text"],
input[type="password"],
.cbi-input-text,
.cbi-input {
@apply text-text border-hairline bg-surface-sunken placeholder-text-muted focus:border-brand focus:ring-focus-ring relative rounded-2xl border px-3 py-1.5 text-sm font-normal shadow-sm transition-[border-color,box-shadow] duration-150 focus:ring-2 focus:outline-none;
@apply text-text border-hairline bg-surface-overlay dark:bg-surface-sunken placeholder-text-muted focus:border-brand focus:ring-focus-ring relative rounded-2xl border px-3 py-1.5 text-sm font-normal shadow-sm transition-[border-color,box-shadow] duration-150 focus:ring-2 focus:outline-none;
.table.cbi-section-table & {
@apply w-full;
@ -23,7 +23,7 @@ input[type="password"],
input[type="radio"],
input[type="checkbox"] {
@apply focus:before:border-brand focus:before:ring-focus-ring checked:before:border-brand checked:before:bg-brand before:border-hairline before:bg-surface-sunken after:bg-on-brand hover:before:border-hairline relative mr-3 inline-block h-4 w-4 cursor-pointer appearance-none before:absolute before:top-0 before:left-0 before:h-4 before:w-4 before:border before:transition-[background-color,border-color,box-shadow] before:duration-150 after:absolute after:top-0.5 after:left-0.5 after:h-3 after:w-3 after:opacity-0 after:transition-opacity after:duration-150 checked:after:opacity-100 focus:before:ring-2 focus:before:outline-none disabled:cursor-not-allowed;
@apply focus:before:border-brand focus:before:ring-focus-ring checked:before:border-brand checked:before:bg-brand before:border-hairline before:bg-surface-overlay dark:before:bg-surface-sunken after:bg-on-brand hover:before:border-hairline relative mr-3 inline-block h-4 w-4 cursor-pointer appearance-none before:absolute before:top-0 before:left-0 before:h-4 before:w-4 before:border before:transition-[background-color,border-color,box-shadow] before:duration-150 after:absolute after:top-0.5 after:left-0.5 after:h-3 after:w-3 after:opacity-0 after:transition-opacity after:duration-150 checked:after:opacity-100 focus:before:ring-2 focus:before:outline-none disabled:cursor-not-allowed;
}
input[type="radio"] {

View File

@ -1,5 +1,5 @@
select {
@apply text-text border-hairline bg-surface-sunken focus:border-brand focus:ring-focus-ring appearance-none rounded-2xl border px-3 py-1.5 pr-10 text-sm font-normal shadow-sm transition-[border-color,box-shadow] duration-150 focus:ring-2 focus:outline-none;
@apply text-text border-hairline bg-surface-overlay dark:bg-surface-sunken focus:border-brand focus:ring-focus-ring appearance-none rounded-2xl border px-3 py-1.5 pr-10 text-sm font-normal shadow-sm transition-[border-color,box-shadow] duration-150 focus:ring-2 focus:outline-none;
@apply bg-[url('@assets/icons/arrow-down.svg')] bg-size-[16px] bg-position-[right_0.75rem_center] bg-no-repeat dark:bg-[url('@assets/icons/arrow-down-dark.svg')];
&[disabled] {
@apply cursor-not-allowed opacity-40 dark:opacity-30;

View File

@ -1,5 +1,5 @@
textarea {
@apply text-text border-hairline bg-surface-sunken placeholder-text-muted focus:border-brand focus:ring-focus-ring min-h-24 w-full resize-y rounded-2xl border px-3 py-2 text-sm font-normal shadow-sm transition-[border-color,box-shadow] duration-150 focus:ring-2 focus:outline-none;
@apply text-text border-hairline bg-surface-overlay dark:bg-surface-sunken placeholder-text-muted focus:border-brand focus:ring-focus-ring min-h-24 w-full resize-y rounded-2xl border px-3 py-2 text-sm font-normal shadow-sm transition-[border-color,box-shadow] duration-150 focus:ring-2 focus:outline-none;
&[disabled] {
@apply cursor-not-allowed opacity-40 dark:opacity-30;
}

View File

@ -92,7 +92,7 @@
}
.input-wrap {
@apply border-hairline bg-surface-sunken relative rounded-2xl border [contain:layout_style];
@apply border-hairline bg-surface-overlay dark:bg-surface-sunken relative rounded-2xl border [contain:layout_style];
}
/* Focus ring: opacity-only transition runs on compositor thread, not main thread

View File

@ -4,7 +4,7 @@ export const DEFAULTS = {
bg: "oklch(0.967 0.003 264)",
surface: "oklch(1 0 0)",
text: "oklch(0.21 0.02 264)",
brand: "oklch(0.68 0.11 233)",
brand: "oklch(0.58 0.14 233)",
on_brand: "oklch(1 0 0)",
link: "oklch(0.74 0.238 322.16)",
info: "oklch(0.45 0.12 255)",

View File

@ -4,9 +4,9 @@ export const DERIVATIONS = {
light: {
text_muted: ["mix", "text", "bg", 0.62],
text_subtle: ["mix", "text", "bg", 0.48],
surface_sunken: ["shade", "bg", -0.010],
surface_sunken: ["shade", "bg", -0.012],
surface_overlay: ["shade", "bg", 0.016],
hairline: ["alpha", "text", 0.08],
hairline: ["alpha", "text", 0.13],
hover_faint: ["shade", "bg", -0.04],
brand_hover: ["shade", "brand", -0.06],
brand_subtle: ["mix", "brand", "bg", 0.12],

View File

@ -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.0.6
PKG_RELEASE:=40
PKG_VERSION:=1.0.7
PKG_RELEASE:=41
PKG_LICENSE:=Apache-2.0
LUCI_MINIFY_CSS:=

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -10,12 +10,12 @@ include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=natflow
PKG_VERSION:=20260531
PKG_RELEASE:=33
PKG_RELEASE:=34
PKG_SOURCE:=$(PKG_VERSION).tar.xz
PKG_SOURCE_URL:=https://github.com/ptpt52/natflow.git
PKG_SOURCE_PROTO:=git
PKG_SOURCE_VERSION:=0a039fc3e686237efa6f89cd593731a039965b14
PKG_SOURCE_VERSION:=50b9d608a30193f71f8831ee0d6a1a17a3aac456
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_MAINTAINER:=Chen Minqiang <ptpt52@gmail.com>
PKG_LICENSE:=GPL-2.0

View File

@ -5,8 +5,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=nps
PKG_VERSION:=0.26.34
PKG_RELEASE:=9
PKG_VERSION:=0.26.35
PKG_RELEASE:=10
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/yisier/nps/tar.gz/v$(PKG_VERSION)?

2
quectel_MHI/Makefile Normal file → Executable file
View File

@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=pcie_mhi
PKG_VERSION:=1.3.8
PKG_RELEASE:=33
PKG_RELEASE:=34
include $(INCLUDE_DIR)/kernel.mk
include $(INCLUDE_DIR)/package.mk