mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-08 05:04:21 +08:00
✨ Sync 2026-10-11 20:48:25
This commit is contained in:
1 parent
78f0ba8251
commit
5ce75c9f2b
45 files changed
+54
-879
No files matched your search
@@ -12,9 +12,6 @@
|
||||
'require uci';
|
||||
'require ui';
|
||||
|
||||
/* Shared wording for the TLS / transport form blocks. Keeping the strings here
|
||||
makes node.js and server.js consume a single definition instead of two
|
||||
copies that have to be edited in lockstep. */
|
||||
const TRANSPORT_NONE_HINT = _('No TCP transport, plain HTTP is merged into the HTTP transport.');
|
||||
const TRANSPORT_HTTP_HINT = _('TLS is not enforced. If TLS is not configured, plain HTTP 1.1 is used.');
|
||||
const TRANSPORT_QUIC_HINT = _('No additional encryption support: It\'s basically duplicate encryption.');
|
||||
@@ -38,28 +35,23 @@ return baseclass.extend({
|
||||
},
|
||||
|
||||
shadowsocks_encrypt_length: {
|
||||
/* AEAD */
|
||||
'aes-128-gcm': 0,
|
||||
'aes-192-gcm': 0,
|
||||
'aes-256-gcm': 0,
|
||||
'chacha20-ietf-poly1305': 0,
|
||||
'xchacha20-ietf-poly1305': 0,
|
||||
/* AEAD 2022 */
|
||||
'2022-blake3-aes-128-gcm': 16,
|
||||
'2022-blake3-aes-256-gcm': 32,
|
||||
'2022-blake3-chacha20-poly1305': 32
|
||||
},
|
||||
|
||||
shadowsocks_encrypt_methods: [
|
||||
/* Stream */
|
||||
'none',
|
||||
/* AEAD */
|
||||
'aes-128-gcm',
|
||||
'aes-192-gcm',
|
||||
'aes-256-gcm',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
/* AEAD 2022 */
|
||||
'2022-blake3-aes-128-gcm',
|
||||
'2022-blake3-aes-256-gcm',
|
||||
'2022-blake3-chacha20-poly1305'
|
||||
@@ -102,10 +94,6 @@ return baseclass.extend({
|
||||
}
|
||||
}),
|
||||
|
||||
/* Build the transport option group shared by the node (client) and server
|
||||
forms. `options.side` selects the client/server wording and the handful of
|
||||
fields that only exist on one side; option names, defaults and dependency
|
||||
sets are otherwise identical. */
|
||||
renderTransportOptions(section, options) {
|
||||
const features = options.features || {},
|
||||
side = options.side || 'client';
|
||||
@@ -236,10 +224,6 @@ return baseclass.extend({
|
||||
return o;
|
||||
},
|
||||
|
||||
/* Build the TLS option group shared by the node (client) and server forms.
|
||||
`options.type_depends` lists the node types the TLS flag applies to,
|
||||
`options.tls_forced_types` the types that force TLS on, and
|
||||
`options.oninsecurechange` the client-side confirm handler. */
|
||||
renderTlsOptions(section, options) {
|
||||
const side = options.side || 'client';
|
||||
let o;
|
||||
@@ -323,7 +307,6 @@ return baseclass.extend({
|
||||
},
|
||||
|
||||
calcStringMD5(e) {
|
||||
/* Thanks to https://stackoverflow.com/a/41602636 */
|
||||
let h = (a, b) => {
|
||||
let c, d, e, f, g;
|
||||
c = a & 2147483648;
|
||||
@@ -400,7 +383,6 @@ return baseclass.extend({
|
||||
if (!str)
|
||||
return null;
|
||||
|
||||
/* Thanks to luci-app-ssr-plus */
|
||||
str = str.replace(/-/g, '+').replace(/_/g, '/');
|
||||
let padding = (4 - str.length % 4) % 4;
|
||||
if (padding)
|
||||
@@ -427,14 +409,12 @@ return baseclass.extend({
|
||||
byteArr = crypto.getRandomValues(new Uint8Array(length));
|
||||
switch (type) {
|
||||
case 'base64':
|
||||
/* Thanks to https://stackoverflow.com/questions/9267899 */
|
||||
return btoa(String.fromCharCode.apply(null, byteArr));
|
||||
case 'hex':
|
||||
return Array.from(byteArr, (byte) =>
|
||||
(byte & 255).toString(16).padStart(2, '0')
|
||||
).join('');
|
||||
case 'uuid':
|
||||
/* Thanks to https://stackoverflow.com/a/2117523 */
|
||||
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, (c) =>
|
||||
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
|
||||
);
|
||||
@@ -501,7 +481,6 @@ return baseclass.extend({
|
||||
},
|
||||
|
||||
validateBase64Key(length, section_id, value) {
|
||||
/* Thanks to luci-proto-wireguard */
|
||||
if (section_id && value)
|
||||
if (value.length !== length || !value.match(/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/) || value[length-1] !== '=')
|
||||
return _('Expecting: %s').format(_('valid base64 key with %d characters').format(length));
|
||||
@@ -526,7 +505,6 @@ return baseclass.extend({
|
||||
else if (!value[2])
|
||||
value[2] = 65535;
|
||||
|
||||
/* numeric comparison: string '<' does lexicographic ordering */
|
||||
if (parseInt(value[1], 10) < parseInt(value[2], 10) && value[2] <= 65535)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@ return view.extend({
|
||||
let features = data[1],
|
||||
hosts = data[2]?.hosts;
|
||||
|
||||
/* Cache all configured proxy nodes, they will be called multiple times */
|
||||
let proxy_nodes = {};
|
||||
uci.sections(data[0], 'node', (res) => {
|
||||
let nodeaddr = ((res.type === 'direct') ? res.override_address : res.address) || '',
|
||||
@@ -352,8 +351,6 @@ return view.extend({
|
||||
o.depends('proxy_mode', 'redirect_tun');
|
||||
o.depends('proxy_mode', 'tun');
|
||||
|
||||
/* Custom routing settings start */
|
||||
/* Routing settings start */
|
||||
o = s.taboption('routing', form.SectionValue, '_routing', form.NamedSection, 'routing', 'homeproxy');
|
||||
o.depends('routing_mode', 'custom');
|
||||
|
||||
@@ -443,9 +440,7 @@ return view.extend({
|
||||
}
|
||||
so.default = 'default-dns';
|
||||
so.rmempty = false;
|
||||
/* Routing settings end */
|
||||
|
||||
/* Routing nodes start */
|
||||
s.tab('routing_node', _('Routing Nodes'));
|
||||
o = s.taboption('routing_node', form.SectionValue, '_routing_node', form.GridSection, 'routing_node');
|
||||
o.depends('routing_mode', 'custom');
|
||||
@@ -614,9 +609,7 @@ return view.extend({
|
||||
_('Interrupt existing connections when the selected outbound has changed.'));
|
||||
so.depends('node', 'urltest');
|
||||
so.modalonly = true;
|
||||
/* Routing nodes end */
|
||||
|
||||
/* Routing rules start */
|
||||
s.tab('routing_rule', _('Routing Rules'));
|
||||
o = s.taboption('routing_rule', form.SectionValue, '_routing_rule', form.GridSection, 'routing_rule');
|
||||
o.depends('routing_mode', 'custom');
|
||||
@@ -958,9 +951,7 @@ return view.extend({
|
||||
so = ss.taboption('fields_process', form.DynamicList, 'process_path_regex', _('Process path (regex)'),
|
||||
_('Match process path using regular expression.'));
|
||||
so.modalonly = true;
|
||||
/* Routing rules end */
|
||||
|
||||
/* DNS settings start */
|
||||
s.tab('dns', _('DNS Settings'));
|
||||
o = s.taboption('dns', form.SectionValue, '_dns', form.NamedSection, 'dns', 'homeproxy');
|
||||
o.depends('routing_mode', 'custom');
|
||||
@@ -1015,9 +1006,7 @@ return view.extend({
|
||||
_('Persist DNS cache across restarts (sing-box 1.14, replaces Store RDRC).'));
|
||||
so.depends('disable_cache', '0');
|
||||
so.rmempty = false;
|
||||
/* DNS settings end */
|
||||
|
||||
/* DNS servers start */
|
||||
s.tab('dns_server', _('DNS Servers'));
|
||||
o = s.taboption('dns_server', form.SectionValue, '_dns_server', form.GridSection, 'dns_server');
|
||||
o.depends('routing_mode', 'custom');
|
||||
@@ -1138,9 +1127,7 @@ return view.extend({
|
||||
so.default = 'direct-out';
|
||||
so.rmempty = false;
|
||||
so.editable = true;
|
||||
/* DNS servers end */
|
||||
|
||||
/* DNS rules start */
|
||||
s.tab('dns_rule', _('DNS Rules'));
|
||||
o = s.taboption('dns_rule', form.SectionValue, '_dns_rule', form.GridSection, 'dns_rule');
|
||||
o.depends('routing_mode', 'custom');
|
||||
@@ -1470,10 +1457,7 @@ return view.extend({
|
||||
so = ss.taboption('fields_process', form.DynamicList, 'process_path_regex', _('Process path (regex)'),
|
||||
_('Match process path using regular expression.'));
|
||||
so.modalonly = true;
|
||||
/* DNS rules end */
|
||||
/* Custom routing settings end */
|
||||
|
||||
/* Rule set settings start */
|
||||
s.tab('ruleset', _('Rule Set'));
|
||||
o = s.taboption('ruleset', form.SectionValue, '_ruleset', form.GridSection, 'ruleset');
|
||||
o.depends('routing_mode', 'custom');
|
||||
@@ -1584,15 +1568,12 @@ return view.extend({
|
||||
_('Update interval of rule set.'));
|
||||
so.placeholder = '1d';
|
||||
so.depends('type', 'remote');
|
||||
/* Rule set settings end */
|
||||
|
||||
/* ACL settings start */
|
||||
s.tab('control', _('Access Control'));
|
||||
|
||||
o = s.taboption('control', form.SectionValue, '_control', form.NamedSection, 'control', 'homeproxy');
|
||||
ss = o.subsection;
|
||||
|
||||
/* Interface control start */
|
||||
ss.tab('interface', _('Interface Control'));
|
||||
|
||||
so = ss.taboption('interface', widgets.DeviceSelect, 'listen_interfaces', _('Listen interfaces'),
|
||||
@@ -1604,9 +1585,7 @@ return view.extend({
|
||||
_('Bind outbound traffic to specific interface. Leave empty to auto detect.'));
|
||||
so.multiple = false;
|
||||
so.noaliases = true;
|
||||
/* Interface control end */
|
||||
|
||||
/* LAN IP policy start */
|
||||
ss.tab('lan_ip_policy', _('LAN IP Policy'));
|
||||
|
||||
so = ss.taboption('lan_ip_policy', form.ListValue, 'lan_proxy_mode', _('Proxy filter mode'));
|
||||
@@ -1649,9 +1628,7 @@ return view.extend({
|
||||
|
||||
so = fwtool.addMACOption(ss, 'lan_ip_policy', 'lan_global_proxy_mac_addrs', _('Global proxy MAC-s'), null, hosts);
|
||||
so.depends({'homeproxy.config.routing_mode': 'custom', '!reverse': true});
|
||||
/* LAN IP policy end */
|
||||
|
||||
/* WAN IP policy start */
|
||||
ss.tab('wan_ip_policy', _('WAN IP Policy'));
|
||||
|
||||
so = ss.taboption('wan_ip_policy', form.DynamicList, 'wan_proxy_ipv4_ips', _('Proxy IPv4 IP-s'));
|
||||
@@ -1667,9 +1644,7 @@ return view.extend({
|
||||
so = ss.taboption('wan_ip_policy', form.DynamicList, 'wan_direct_ipv6_ips', _('Direct IPv6 IP-s'));
|
||||
so.datatype = 'or(ip6addr, cidr6)';
|
||||
so.depends('homeproxy.config.ipv6_support', '1');
|
||||
/* WAN IP policy end */
|
||||
|
||||
/* Proxy domain list start */
|
||||
ss.tab('proxy_domain_list', _('Proxy Domain List'));
|
||||
|
||||
so = ss.taboption('proxy_domain_list', form.TextValue, '_proxy_domain_list');
|
||||
@@ -1699,9 +1674,7 @@ return view.extend({
|
||||
|
||||
return true;
|
||||
}
|
||||
/* Proxy domain list end */
|
||||
|
||||
/* Direct domain list start */
|
||||
ss.tab('direct_domain_list', _('Direct Domain List'));
|
||||
|
||||
so = ss.taboption('direct_domain_list', form.TextValue, '_direct_domain_list');
|
||||
@@ -1731,10 +1704,7 @@ return view.extend({
|
||||
|
||||
return true;
|
||||
}
|
||||
/* Direct domain list end */
|
||||
/* ACL settings end */
|
||||
|
||||
/* DNS cache / timeout for preset routing modes (rightmost tab, stored in the dns section) */
|
||||
s.tab('dns_cache', _('DNS Cache (1.14)'));
|
||||
o = s.taboption('dns_cache', form.SectionValue, '_dns_cache', form.NamedSection, 'dns', 'homeproxy');
|
||||
o.depends({'routing_mode': 'custom', '!reverse': true});
|
||||
|
||||
@@ -26,11 +26,9 @@ function parseShareLink(uri, features) {
|
||||
if (uri[0] && uri[1]) {
|
||||
switch (uri[0]) {
|
||||
case 'anytls':
|
||||
/* https://github.com/anytls/anytls-go/blob/v0.0.8/docs/uri_scheme.md */
|
||||
url = new URL('http://' + uri[1]);
|
||||
params = url.searchParams;
|
||||
|
||||
/* Check if password exists */
|
||||
if (!url.username)
|
||||
return null;
|
||||
|
||||
@@ -62,11 +60,9 @@ function parseShareLink(uri, features) {
|
||||
|
||||
break;
|
||||
case 'hysteria':
|
||||
/* https://github.com/HyNetwork/hysteria/wiki/URI-Scheme */
|
||||
url = new URL('http://' + uri[1]);
|
||||
params = url.searchParams;
|
||||
|
||||
/* WeChat-Video / FakeTCP are unsupported by sing-box currently */
|
||||
if (!features.with_quic || (params.get('protocol') && params.get('protocol') !== 'udp'))
|
||||
return null;
|
||||
|
||||
@@ -90,7 +86,6 @@ function parseShareLink(uri, features) {
|
||||
break;
|
||||
case 'hysteria2':
|
||||
case 'hy2':
|
||||
/* https://v2.hysteria.network/docs/developers/URI-Scheme/ */
|
||||
url = new URL('http://' + uri[1]);
|
||||
params = url.searchParams;
|
||||
|
||||
@@ -114,7 +109,6 @@ function parseShareLink(uri, features) {
|
||||
|
||||
break;
|
||||
case 'snell':
|
||||
/* Surge Snell share link: snell://host:port?psk=..&obfs=http&obfs-host=..#name */
|
||||
url = new URL('http://' + uri[1]);
|
||||
params = url.searchParams;
|
||||
|
||||
@@ -153,7 +147,6 @@ function parseShareLink(uri, features) {
|
||||
break;
|
||||
case 'ss':
|
||||
try {
|
||||
/* "Lovely" Shadowrocket format */
|
||||
try {
|
||||
let suri = uri[1].split('#'), slabel = '';
|
||||
if (suri.length <= 2) {
|
||||
@@ -163,15 +156,12 @@ function parseShareLink(uri, features) {
|
||||
}
|
||||
} catch(e) { }
|
||||
|
||||
/* SIP002 format https://shadowsocks.org/guide/sip002.html */
|
||||
url = new URL('http://' + uri[1]);
|
||||
|
||||
let userinfo;
|
||||
if (url.username && url.password) {
|
||||
/* User info encoded with URIComponent */
|
||||
userinfo = [url.username, decodeURIComponent(url.password)];
|
||||
} else if (url.username) {
|
||||
/* User info encoded with base64 */
|
||||
userinfo = hp.decodeBase64Str(decodeURIComponent(url.username)).split(':');
|
||||
if (userinfo.length > 1)
|
||||
userinfo = [userinfo[0], userinfo.slice(1).join(':')]
|
||||
@@ -184,9 +174,6 @@ function parseShareLink(uri, features) {
|
||||
if (url.search && url.searchParams.get('plugin')) {
|
||||
let plugin_info = url.searchParams.get('plugin').split(';');
|
||||
plugin = plugin_info[0];
|
||||
/* sing-box only knows obfs-local; simple-obfs (an old
|
||||
alias providers still emit) would otherwise be rejected
|
||||
as "plugin not found" and take the whole config with it */
|
||||
if (plugin === 'simple-obfs')
|
||||
plugin = 'obfs-local';
|
||||
plugin_opts = (plugin_info.length > 1) ? plugin_info.slice(1).join(';') : null;
|
||||
@@ -203,7 +190,6 @@ function parseShareLink(uri, features) {
|
||||
shadowsocks_plugin_opts: plugin_opts
|
||||
};
|
||||
} catch(e) {
|
||||
/* Legacy format https://github.com/shadowsocks/shadowsocks-org/commit/78ca46cd6859a4e9475953ed34a2d301454f579e */
|
||||
uri = uri[1].split('@');
|
||||
if (uri.length < 2)
|
||||
return null;
|
||||
@@ -221,11 +207,9 @@ function parseShareLink(uri, features) {
|
||||
|
||||
break;
|
||||
case 'trojan':
|
||||
/* https://p4gefau1t.github.io/trojan-go/developer/url/ */
|
||||
url = new URL('http://' + uri[1]);
|
||||
params = url.searchParams;
|
||||
|
||||
/* Check if password exists */
|
||||
if (!url.username)
|
||||
return null;
|
||||
|
||||
@@ -256,11 +240,9 @@ function parseShareLink(uri, features) {
|
||||
|
||||
break;
|
||||
case 'tuic':
|
||||
/* https://github.com/daeuniverse/dae/discussions/182 */
|
||||
url = new URL('http://' + uri[1]);
|
||||
params = url.searchParams;
|
||||
|
||||
/* Check if uuid exists */
|
||||
if (!url.username)
|
||||
return null;
|
||||
|
||||
@@ -280,16 +262,13 @@ function parseShareLink(uri, features) {
|
||||
|
||||
break;
|
||||
case 'vless':
|
||||
/* https://github.com/XTLS/Xray-core/discussions/716 */
|
||||
url = new URL('http://' + uri[1]);
|
||||
params = url.searchParams;
|
||||
|
||||
/* Unsupported protocol */
|
||||
if (params.get('type') === 'kcp')
|
||||
return null;
|
||||
else if (params.get('type') === 'quic' && ((params.get('quicSecurity') && params.get('quicSecurity') !== 'none') || !features.with_quic))
|
||||
return null;
|
||||
/* Check if uuid and type exist */
|
||||
if (!url.username || !params.get('type'))
|
||||
return null;
|
||||
|
||||
@@ -307,9 +286,6 @@ function parseShareLink(uri, features) {
|
||||
tls_reality_public_key: params.get('pbk') ? decodeURIComponent(params.get('pbk')) : null,
|
||||
tls_reality_short_id: params.get('sid'),
|
||||
tls_utls: features.with_utls ? params.get('fp') : null,
|
||||
/* only the value sing-box supports: any other flow is
|
||||
rejected with "unsupported flow" and one bad node breaks
|
||||
the whole generated config */
|
||||
vless_flow: (['tls', 'reality'].includes(params.get('security')) && params.get('flow') === 'xtls-rprx-vision') ? params.get('flow') : null
|
||||
};
|
||||
switch (params.get('type')) {
|
||||
@@ -340,26 +316,17 @@ function parseShareLink(uri, features) {
|
||||
|
||||
break;
|
||||
case 'vmess':
|
||||
/* "Lovely" shadowrocket format; uri is the split scheme/rest
|
||||
array, so this has to test the link body (testing the array
|
||||
itself compared '&' against its elements and never matched) */
|
||||
if (uri[1].includes('&'))
|
||||
return null;
|
||||
|
||||
/* https://github.com/2dust/v2rayN/wiki/Description-of-VMess-share-link */
|
||||
uri = JSON.parse(hp.decodeBase64Str(uri[1]));
|
||||
|
||||
if (uri.v != '2')
|
||||
return null;
|
||||
/* Unsupported protocols */
|
||||
else if (uri.net === 'kcp')
|
||||
return null;
|
||||
else if (uri.net === 'quic' && ((uri.type && uri.type !== 'none') || !features.with_quic))
|
||||
return null;
|
||||
/* https://www.v2fly.org/config/protocols/vmess.html#vmess-md5-%E8%AE%A4%E8%AF%81%E4%BF%A1%E6%81%AF-%E6%B7%98%E6%B1%B0%E6%9C%BA%E5%88%B6
|
||||
* else if (uri.aid && parseInt(uri.aid) !== 0)
|
||||
* return null;
|
||||
*/
|
||||
|
||||
config = {
|
||||
label: uri.ps,
|
||||
@@ -524,7 +491,6 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
}
|
||||
o.modalonly = true;
|
||||
|
||||
/* Direct config */
|
||||
o = s.option(form.ListValue, 'proxy_protocol', _('Proxy protocol'),
|
||||
_('Write proxy protocol in the connection header.'));
|
||||
o.value('', _('Disable'));
|
||||
@@ -533,7 +499,6 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.depends('type', 'direct');
|
||||
o.modalonly = true;
|
||||
|
||||
/* AnyTLS config start */
|
||||
o = s.option(form.Value, 'anytls_idle_session_check_interval', _('Idle session check interval'),
|
||||
_('Interval checking for idle sessions, in seconds.'));
|
||||
o.datatype = 'uinteger';
|
||||
@@ -554,9 +519,7 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.placeholder = '0';
|
||||
o.depends('type', 'anytls');
|
||||
o.modalonly = true;
|
||||
/* AnyTLS config end */
|
||||
|
||||
/* Hysteria (2) config start */
|
||||
o = s.option(form.DynamicList, 'hysteria_hopping_port', _('Hopping port'));
|
||||
o.depends('type', 'hysteria');
|
||||
o.depends('type', 'hysteria2');
|
||||
@@ -580,10 +543,6 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
|
||||
o = s.option(form.ListValue, 'hysteria_protocol', _('Protocol'));
|
||||
o.value('udp');
|
||||
/* WeChat-Video / FakeTCP are unsupported by sing-box currently
|
||||
* o.value('wechat-video');
|
||||
* o.value('faketcp');
|
||||
*/
|
||||
o.default = 'udp';
|
||||
o.depends('type', 'hysteria');
|
||||
o.rmempty = false;
|
||||
@@ -655,13 +614,10 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
_('Disable Chrome QUIC handshake parroting, which is enabled by default since sing-box 1.14. Turn this on only when the server uses an Ed25519 certificate or the handshake otherwise fails.'));
|
||||
o.depends('type', 'hysteria2');
|
||||
o.modalonly = true;
|
||||
/* Hysteria (2) config end */
|
||||
|
||||
/* Shadowsocks config start */
|
||||
o = s.option(form.ListValue, 'shadowsocks_encrypt_method', _('Encrypt method'));
|
||||
for (let i of hp.shadowsocks_encrypt_methods)
|
||||
o.value(i);
|
||||
/* Stream ciphers */
|
||||
o.value('aes-128-ctr');
|
||||
o.value('aes-192-ctr');
|
||||
o.value('aes-256-ctr');
|
||||
@@ -687,9 +643,7 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.depends('shadowsocks_plugin', 'obfs-local');
|
||||
o.depends('shadowsocks_plugin', 'v2ray-plugin');
|
||||
o.modalonly = true;
|
||||
/* Shadowsocks config end */
|
||||
|
||||
/* ShadowTLS config */
|
||||
o = s.option(form.ListValue, 'shadowtls_version', _('ShadowTLS version'));
|
||||
o.value('1', _('v1'));
|
||||
o.value('2', _('v2'));
|
||||
@@ -699,7 +653,6 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
/* Socks config */
|
||||
o = s.option(form.ListValue, 'socks_version', _('Socks version'));
|
||||
o.value('4', _('Socks4'));
|
||||
o.value('4a', _('Socks4A'));
|
||||
@@ -709,7 +662,6 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
/* SSH config start */
|
||||
o = s.option(form.Value, 'ssh_client_version', _('Client version'),
|
||||
_('Random version will be used if empty.'));
|
||||
o.depends('type', 'ssh');
|
||||
@@ -733,9 +685,7 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.password = true;
|
||||
o.depends('type', 'ssh');
|
||||
o.modalonly = true;
|
||||
/* SSH config end */
|
||||
|
||||
/* TUIC config start */
|
||||
o = s.option(form.Value, 'uuid', _('UUID'));
|
||||
o.password = true;
|
||||
o.depends('type', 'tuic');
|
||||
@@ -779,9 +729,7 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.default = '10';
|
||||
o.depends('type', 'tuic');
|
||||
o.modalonly = true;
|
||||
/* Tuic config end */
|
||||
|
||||
/* VMess / VLESS config start */
|
||||
o = s.option(form.ListValue, 'vless_flow', _('Flow'));
|
||||
o.value('', _('None'));
|
||||
o.value('xtls-rprx-vision');
|
||||
@@ -816,13 +764,9 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
_('Protocol parameter. Enable length block encryption.'));
|
||||
o.depends('type', 'vmess');
|
||||
o.modalonly = true;
|
||||
/* VMess config end */
|
||||
|
||||
/* Transport config start */
|
||||
hp.renderTransportOptions(s, { features: features, side: 'client' });
|
||||
/* Transport config end */
|
||||
|
||||
/* Wireguard config start */
|
||||
o = s.option(form.DynamicList, 'wireguard_local_address', _('Local address'),
|
||||
_('List of IP (v4 or v6) addresses prefixes to be assigned to the interface.'));
|
||||
o.datatype = 'cidr';
|
||||
@@ -868,9 +812,7 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.datatype = 'uinteger';
|
||||
o.depends('type', 'wireguard');
|
||||
o.modalonly = true;
|
||||
/* Wireguard config end */
|
||||
|
||||
/* Mux config start */
|
||||
o = s.option(form.Flag, 'multiplex', _('Multiplex'));
|
||||
o.depends('type', 'shadowsocks');
|
||||
o.depends('type', 'trojan');
|
||||
@@ -927,9 +869,7 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.datatype = 'uinteger';
|
||||
o.depends('multiplex_brutal', '1');
|
||||
o.modalonly = true;
|
||||
/* Mux config end */
|
||||
|
||||
/* Snell config start */
|
||||
o = s.option(form.ListValue, 'snell_version', _('Snell version'),
|
||||
_('sing-box implements Snell v4/v5 wire as v4 and v6. The pre-shared key (Password above) must be 12-255 bytes for v6.'));
|
||||
o.value('4', _('v4'));
|
||||
@@ -967,9 +907,7 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.value('unsafe-raw', _('unsafe-raw'));
|
||||
o.depends({'type': 'snell', 'snell_version': '6'});
|
||||
o.modalonly = true;
|
||||
/* Snell config end */
|
||||
|
||||
/* TLS config start */
|
||||
hp.renderTlsOptions(s, {
|
||||
side: 'client',
|
||||
type_depends: [ 'anytls', 'http', 'hysteria', 'hysteria2', 'shadowtls', 'trojan', 'tuic', 'vless', 'vmess' ],
|
||||
@@ -1063,9 +1001,7 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.depends('tls_reality', '1');
|
||||
o.modalonly = true;
|
||||
}
|
||||
/* TLS config end */
|
||||
|
||||
/* Extra settings start */
|
||||
o = s.option(form.Flag, 'tcp_fast_open', _('TCP fast open'));
|
||||
o.modalonly = true;
|
||||
|
||||
@@ -1088,7 +1024,6 @@ function renderNodeSettings(section, data, features, main_node, routing_mode) {
|
||||
o.default = '2';
|
||||
o.depends('udp_over_tcp', '1');
|
||||
o.modalonly = true;
|
||||
/* Extra settings end */
|
||||
|
||||
return s;
|
||||
}
|
||||
@@ -1107,7 +1042,6 @@ return view.extend({
|
||||
let routing_mode = uci.get(data[0], 'config', 'routing_mode');
|
||||
let features = data[1];
|
||||
|
||||
/* Cache subscription information, it will be called multiple times */
|
||||
let subinfo = [];
|
||||
for (let suburl of (uci.get(data[0], 'subscription', 'subscription_url') || [])) {
|
||||
const url = new URL(suburl);
|
||||
@@ -1120,8 +1054,6 @@ return view.extend({
|
||||
|
||||
s = m.section(form.NamedSection, 'subscription', 'homeproxy');
|
||||
|
||||
/* Node settings start */
|
||||
/* User nodes start */
|
||||
s.tab('node', _('Nodes'));
|
||||
o = s.taboption('node', form.SectionValue, '_node', form.GridSection, 'node');
|
||||
ss = renderNodeSettings(o.subsection, data, features, main_node, routing_mode);
|
||||
@@ -1133,8 +1065,6 @@ return view.extend({
|
||||
|
||||
return true;
|
||||
}
|
||||
/* Import subscription links start */
|
||||
/* Thanks to luci-app-shadowsocks-libev */
|
||||
ss.handleLinkImport = function() {
|
||||
let textarea = new ui.Textarea();
|
||||
ui.showModal(_('Import share links'), [
|
||||
@@ -1151,7 +1081,6 @@ return view.extend({
|
||||
click: ui.createHandlerFn(this, () => {
|
||||
let input_links = textarea.getValue().trim().split('\n');
|
||||
if (input_links && input_links[0]) {
|
||||
/* Remove duplicate lines */
|
||||
input_links = input_links.reduce((pre, cur) =>
|
||||
(!pre.includes(cur) && pre.push(cur), pre), []);
|
||||
|
||||
@@ -1163,9 +1092,6 @@ return view.extend({
|
||||
try {
|
||||
config = parseShareLink(l, features);
|
||||
} catch (e) {
|
||||
/* a single unparsable link must not abort
|
||||
the whole import (the changes made so
|
||||
far were left unsaved before) */
|
||||
config = null;
|
||||
}
|
||||
if (config) {
|
||||
@@ -1177,10 +1103,6 @@ return view.extend({
|
||||
let nameHash = hp.calcStringMD5(config.label);
|
||||
let previous = uci.get(data[0], nameHash) || {};
|
||||
let sid = uci.add(data[0], 'node', nameHash);
|
||||
/* drop options the node no longer has:
|
||||
uci.set() only writes the keys below,
|
||||
so re-importing a changed protocol
|
||||
used to leave the old fields behind */
|
||||
Object.keys(previous).forEach((k) => {
|
||||
if (k.charAt(0) !== '.' && !(k in config))
|
||||
uci.unset(data[0], sid, k);
|
||||
@@ -1239,10 +1161,7 @@ return view.extend({
|
||||
|
||||
return el;
|
||||
}
|
||||
/* Import subscription links end */
|
||||
/* User nodes end */
|
||||
|
||||
/* Subscription nodes start */
|
||||
for (const info of subinfo) {
|
||||
s.tab('sub_' + info.hash, _('Sub (%s)').format(info.title));
|
||||
o = s.taboption('sub_' + info.hash, form.SectionValue, '_sub_' + info.hash, form.GridSection, 'node');
|
||||
@@ -1251,10 +1170,7 @@ return view.extend({
|
||||
return (uci.get(data[0], section_id, 'grouphash') === info.hash);
|
||||
}
|
||||
}
|
||||
/* Subscription nodes end */
|
||||
/* Node settings end */
|
||||
|
||||
/* Subscriptions settings start */
|
||||
s.tab('subscription', _('Subscriptions'));
|
||||
|
||||
o = s.taboption('subscription', form.Flag, 'auto_update', _('Auto update'),
|
||||
@@ -1383,7 +1299,6 @@ return view.extend({
|
||||
|
||||
return this.map.save(null, true);
|
||||
}
|
||||
/* Subscriptions settings end */
|
||||
|
||||
return m.render();
|
||||
}
|
||||
|
||||
@@ -104,7 +104,6 @@ function handleGenKey(option) {
|
||||
password = hp.generateRand('hex', 16);
|
||||
break;
|
||||
}
|
||||
/* AEAD */
|
||||
((length) => {
|
||||
if (length && length > 0)
|
||||
password = hp.generateRand('base64', length);
|
||||
@@ -242,19 +241,13 @@ return view.extend({
|
||||
}
|
||||
o.modalonly = true;
|
||||
|
||||
/* AnyTLS config */
|
||||
o = s.option(form.DynamicList, 'anytls_padding_scheme', _('Padding scheme'),
|
||||
_('AnyTLS padding scheme in array.'));
|
||||
o.depends('type', 'anytls');
|
||||
o.modalonly = true;
|
||||
|
||||
/* Hysteria (2) config start */
|
||||
o = s.option(form.ListValue, 'hysteria_protocol', _('Protocol'));
|
||||
o.value('udp');
|
||||
/* WeChat-Video / FakeTCP are unsupported by sing-box currently
|
||||
o.value('wechat-video');
|
||||
o.value('faketcp');
|
||||
*/
|
||||
o.default = 'udp';
|
||||
o.depends('type', 'hysteria');
|
||||
o.rmempty = false;
|
||||
@@ -323,9 +316,7 @@ return view.extend({
|
||||
_('HTTP3 server behavior when authentication fails.<br/>A 404 page will be returned if empty.'));
|
||||
o.depends('type', 'hysteria2');
|
||||
o.modalonly = true;
|
||||
/* Hysteria (2) config end */
|
||||
|
||||
/* Snell config start */
|
||||
o = s.option(form.ListValue, 'snell_version', _('Snell version'),
|
||||
_('The pre-shared key (password) above must be 12-255 bytes for v6.'));
|
||||
o.value('5', _('v5'));
|
||||
@@ -348,9 +339,7 @@ return view.extend({
|
||||
o.value('unsafe-raw', _('unsafe-raw'));
|
||||
o.depends({'type': 'snell', 'snell_version': '6'});
|
||||
o.modalonly = true;
|
||||
/* Snell config end */
|
||||
|
||||
/* Shadowsocks config */
|
||||
o = s.option(form.ListValue, 'shadowsocks_encrypt_method', _('Encrypt method'));
|
||||
for (let i of hp.shadowsocks_encrypt_methods)
|
||||
o.value(i);
|
||||
@@ -358,7 +347,6 @@ return view.extend({
|
||||
o.depends('type', 'shadowsocks');
|
||||
o.modalonly = true;
|
||||
|
||||
/* Tuic config start */
|
||||
o = s.option(CBIGenValue, 'uuid', _('UUID'));
|
||||
o.password = true;
|
||||
o.depends('type', 'tuic');
|
||||
@@ -395,9 +383,7 @@ return view.extend({
|
||||
o.default = '10';
|
||||
o.depends('type', 'tuic');
|
||||
o.modalonly = true;
|
||||
/* Tuic config end */
|
||||
|
||||
/* VLESS / VMess config start */
|
||||
o = s.option(form.ListValue, 'vless_flow', _('Flow'));
|
||||
o.value('', _('None'));
|
||||
o.value('xtls-rprx-vision');
|
||||
@@ -409,13 +395,9 @@ return view.extend({
|
||||
o.datatype = 'uinteger';
|
||||
o.depends('type', 'vmess');
|
||||
o.modalonly = true;
|
||||
/* VMess config end */
|
||||
|
||||
/* Transport config start */
|
||||
hp.renderTransportOptions(s, { features: features, side: 'server' });
|
||||
/* Transport config end */
|
||||
|
||||
/* Mux config start */
|
||||
o = s.option(form.Flag, 'multiplex', _('Multiplex'));
|
||||
o.depends('type', 'shadowsocks');
|
||||
o.depends('type', 'trojan');
|
||||
@@ -445,9 +427,7 @@ return view.extend({
|
||||
o.depends('multiplex_brutal', '1');
|
||||
o.modalonly = true;
|
||||
}
|
||||
/* Mux config end */
|
||||
|
||||
/* TLS config start */
|
||||
hp.renderTlsOptions(s, {
|
||||
side: 'server',
|
||||
type_depends: [ 'anytls', 'http', 'hysteria', 'hysteria2', 'naive', 'trojan', 'tuic', 'vless', 'vmess' ],
|
||||
@@ -725,9 +705,7 @@ return view.extend({
|
||||
o.rows = 3;
|
||||
o.depends('tls', '1');
|
||||
o.modalonly = true;
|
||||
/* TLS config end */
|
||||
|
||||
/* Extra settings start */
|
||||
o = s.option(form.Flag, 'tcp_fast_open', _('TCP fast open'),
|
||||
_('Enable tcp fast open for listener.'));
|
||||
o.depends({'network': 'udp', '!reverse': true});
|
||||
@@ -766,7 +744,6 @@ return view.extend({
|
||||
o = s.option(form.Flag, 'reuse_addr', _('Reuse address'),
|
||||
_('Reuse listener address.'));
|
||||
o.modalonly = true;
|
||||
/* Extra settings end */
|
||||
|
||||
return m.render();
|
||||
}
|
||||
|
||||
@@ -14,13 +14,10 @@
|
||||
'require ui';
|
||||
'require view';
|
||||
|
||||
/* Thanks to luci-app-aria2 */
|
||||
const css = ' \
|
||||
#log_textarea { \
|
||||
padding: 10px; \
|
||||
text-align: left; \
|
||||
} \
|
||||
#log_textarea pre { \
|
||||
padding: .5rem; \
|
||||
word-break: break-all; \
|
||||
margin: 0; \
|
||||
@@ -257,7 +254,6 @@ return view.extend({
|
||||
o.cfgvalue = L.bind(getResVersion, this, o, 'gfw_list');
|
||||
o.rawhtml = true;
|
||||
|
||||
/* local rule-sets (used by bypass_mainland_china when present) */
|
||||
o = s.option(form.DummyValue, '_geoip_cn_version', _('GeoIP CN rule-set version'));
|
||||
o.cfgvalue = L.bind(getResVersion, this, o, 'geoip_cn');
|
||||
o.rawhtml = true;
|
||||
|
||||
Reference in new issue
Block a user