2024-10-26 10:27:23 +08:00
'use strict' ;
'require form' ;
'require uci' ;
'require ui' ;
'require view' ;
'require fchomo as hm' ;
'require tools.widgets as widgets' ;
2025-03-10 16:22:21 +08:00
function parseProviderYaml ( field , name , cfg ) {
if ( ! cfg . type )
return null ;
// key mapping
let config = hm . removeBlankAttrs ( {
2025-03-12 00:27:48 +08:00
id : cfg . hm _id ,
label : cfg . hm _label ,
2025-03-10 16:22:21 +08:00
type : cfg . type ,
... ( cfg . type === 'inline' ? {
//dialer_proxy: cfg["dialer-proxy"],
payload : cfg . payload , // string: array
} : {
url : cfg . url ,
size _limit : cfg [ "size-limit" ] ,
interval : cfg . interval ,
2025-03-12 00:27:48 +08:00
proxy : cfg . proxy ? hm . preset _outbound . full . map ( ( [ key , label ] ) => key ) . includes ( cfg . proxy ) ? cfg . proxy : this . calcID ( hm . glossary [ "proxy_group" ] . field , cfg . proxy ) : null ,
2025-03-10 16:22:21 +08:00
header : cfg . header ? JSON . stringify ( cfg . header , null , 2 ) : null , // string: object
2025-03-12 00:27:48 +08:00
/* Health fields */
2025-03-10 16:22:21 +08:00
health _enable : hm . bool2str ( hm . getValue ( cfg , "health-check.enable" ) ) , // bool
health _url : hm . getValue ( cfg , "health-check.url" ) ,
health _interval : hm . getValue ( cfg , "health-check.interval" ) ,
health _timeout : hm . getValue ( cfg , "health-check.timeout" ) ,
health _lazy : hm . bool2str ( hm . getValue ( cfg , "health-check.lazy" ) ) , // bool
health _expected _status : hm . getValue ( cfg , "health-check.expected-status" ) ,
2025-03-12 00:27:48 +08:00
/* Override fields */
2025-03-10 16:22:21 +08:00
override _prefix : hm . getValue ( cfg , "override.additional-prefix" ) ,
override _suffix : hm . getValue ( cfg , "override.additional-suffix" ) ,
override _replace : ( hm . getValue ( cfg , "override.proxy-name" ) || [ ] ) . map ( ( obj ) => JSON . stringify ( obj ) ) , // array.string: array.object
2025-03-12 14:16:16 +08:00
// Configuration Items
2025-03-10 16:22:21 +08:00
override _tfo : hm . bool2str ( hm . getValue ( cfg , "override.tfo" ) ) , // bool
override _mptcp : hm . bool2str ( hm . getValue ( cfg , "override.mptcp" ) ) , // bool
override _udp : hm . bool2str ( hm . getValue ( cfg , "override.udp" ) ) , // bool
override _uot : hm . bool2str ( hm . getValue ( cfg , "override.udp-over-tcp" ) ) , // bool
override _up : hm . getValue ( cfg , "override.up" ) ,
override _down : hm . getValue ( cfg , "override.down" ) ,
override _skip _cert _verify : hm . bool2str ( hm . getValue ( cfg , "override.skip-cert-verify" ) ) , // bool
//override_dialer_proxy: hm.getValue(cfg, "override.dialer-proxy"),
override _interface _name : hm . getValue ( cfg , "override.interface-name" ) ,
override _routing _mark : hm . getValue ( cfg , "override.routing-mark" ) ,
override _ip _version : hm . getValue ( cfg , "override.ip-version" ) ,
2025-03-12 00:27:48 +08:00
/* General fields */
2025-03-12 14:16:16 +08:00
filter : [ cfg . filter ] , // array.string: string
2025-03-10 16:22:21 +08:00
exclude _filter : [ cfg [ "exclude-filter" ] ] , // array.string: string
exclude _type : [ cfg [ "exclude-type" ] ] // array.string: string
} )
} ) ;
return config ;
}
2024-10-26 10:27:23 +08:00
return view . extend ( {
2025-01-15 16:25:40 +08:00
load ( ) {
2024-10-26 10:27:23 +08:00
return Promise . all ( [
uci . load ( 'fchomo' )
] ) ;
} ,
2025-01-15 16:25:40 +08:00
render ( data ) {
2024-12-10 16:28:20 +08:00
let m , s , o , ss , so ;
2024-10-26 10:27:23 +08:00
m = new form . Map ( 'fchomo' , _ ( 'Edit node' ) ) ;
s = m . section ( form . NamedSection , 'global' , 'fchomo' ) ;
/* Proxy Node START */
s . tab ( 'node' , _ ( 'Proxy Node' ) ) ;
/* Proxy Node */
2025-03-09 00:20:30 +08:00
o = s . taboption ( 'node' , form . SectionValue , '_node' , hm . GridSection , 'node' , null ) ;
2024-10-26 10:27:23 +08:00
ss = o . subsection ;
ss . addremove = true ;
ss . rowcolors = true ;
ss . sortable = true ;
ss . nodescriptions = true ;
2025-03-09 00:20:30 +08:00
ss . hm _modaltitle = [ _ ( 'Node' ) , _ ( 'Add a Node' ) ] ;
2025-03-09 16:21:55 +08:00
ss . hm _prefmt = hm . glossary [ ss . sectiontype ] . prefmt ;
2025-03-10 04:19:32 +08:00
ss . hm _field = hm . glossary [ ss . sectiontype ] . field ;
2025-03-09 00:20:30 +08:00
ss . hm _lowcase _only = true ;
2024-10-26 10:27:23 +08:00
2024-11-16 20:36:29 +08:00
ss . tab ( 'field_general' , _ ( 'General fields' ) ) ;
ss . tab ( 'field_tls' , _ ( 'TLS fields' ) ) ;
ss . tab ( 'field_transport' , _ ( 'Transport fields' ) ) ;
ss . tab ( 'field_multiplex' , _ ( 'Multiplex fields' ) ) ;
ss . tab ( 'field_dial' , _ ( 'Dial fields' ) ) ;
so = ss . taboption ( 'field_general' , form . Value , 'label' , _ ( 'Label' ) ) ;
2024-10-26 10:27:23 +08:00
so . load = L . bind ( hm . loadDefaultLabel , so ) ;
so . validate = L . bind ( hm . validateUniqueValue , so ) ;
so . modalonly = true ;
2024-11-16 20:36:29 +08:00
so = ss . taboption ( 'field_general' , form . Flag , 'enabled' , _ ( 'Enable' ) ) ;
2024-10-26 10:27:23 +08:00
so . default = so . enabled ;
so . editable = true ;
2024-11-16 20:36:29 +08:00
so = ss . taboption ( 'field_general' , form . ListValue , 'type' , _ ( 'Type' ) ) ;
2024-10-26 10:27:23 +08:00
so . default = hm . outbound _type [ 0 ] [ 0 ] ;
hm . outbound _type . forEach ( ( res ) => {
so . value . apply ( so , res ) ;
} )
2024-11-16 20:36:29 +08:00
so = ss . taboption ( 'field_general' , form . Value , 'server' , _ ( 'Server address' ) ) ;
so . datatype = 'host' ;
so . rmempty = false ;
so . depends ( { type : 'direct' , '!reverse' : true } ) ;
so = ss . taboption ( 'field_general' , form . Value , 'port' , _ ( 'Port' ) ) ;
so . datatype = 'port' ;
so . rmempty = false ;
2024-12-21 14:15:01 +08:00
so . depends ( { type : /^(direct|mieru)$/ , '!reverse' : true } ) ;
2024-11-16 20:36:29 +08:00
/* HTTP / SOCKS fields */
/* hm.validateAuth */
so = ss . taboption ( 'field_general' , form . Value , 'username' , _ ( 'Username' ) ) ;
so . validate = L . bind ( hm . validateAuthUsername , so ) ;
2024-12-21 14:15:01 +08:00
so . depends ( { type : /^(http|socks5|mieru|ssh)$/ } ) ;
2024-11-16 20:36:29 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'password' , _ ( 'Password' ) ) ;
so . password = true ;
so . validate = L . bind ( hm . validateAuthPassword , so ) ;
2025-03-05 14:16:22 +08:00
so . depends ( { type : /^(http|socks5|mieru|trojan|anytls|hysteria2|tuic|ssh)$/ } ) ;
2024-11-16 20:36:29 +08:00
so . modalonly = true ;
2025-01-17 20:36:29 +08:00
so = ss . taboption ( 'field_general' , hm . TextValue , 'headers' , _ ( 'HTTP header' ) ) ;
2024-11-16 20:36:29 +08:00
so . placeholder = '{\n "User-Agent": [\n "Clash/v1.18.0",\n "mihomo/1.18.3"\n ],\n "Authorization": [\n //"token 1231231"\n ]\n}' ;
so . validate = L . bind ( hm . validateJson , so ) ;
so . depends ( 'type' , 'http' ) ;
so . modalonly = true ;
/* Hysteria / Hysteria2 fields */
so = ss . taboption ( 'field_general' , form . DynamicList , 'hysteria_ports' , _ ( 'Ports pool' ) ) ;
so . datatype = 'or(port, portrange)' ;
so . depends ( { type : /^(hysteria|hysteria2)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'hysteria_up_mbps' , _ ( 'Max upload speed' ) ,
_ ( 'In Mbps.' ) ) ;
so . datatype = 'uinteger' ;
so . depends ( { type : /^(hysteria|hysteria2)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'hysteria_down_mbps' , _ ( 'Max download speed' ) ,
_ ( 'In Mbps.' ) ) ;
so . datatype = 'uinteger' ;
so . depends ( { type : /^(hysteria|hysteria2)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'hysteria_obfs_type' , _ ( 'Obfuscate type' ) ) ;
so . value ( '' , _ ( 'Disable' ) ) ;
so . value ( 'salamander' , _ ( 'Salamander' ) ) ;
so . depends ( 'type' , 'hysteria2' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'hysteria_obfs_password' , _ ( 'Obfuscate password' ) ,
_ ( 'Enabling obfuscation will make the server incompatible with standard QUIC connections, losing the ability to masquerade with HTTP/3.' ) ) ;
so . password = true ;
so . rmempty = false ;
so . depends ( 'type' , 'hysteria' ) ;
so . depends ( { type : 'hysteria2' , hysteria _obfs _type : /.+/ } ) ;
so . modalonly = true ;
/* SSH fields */
so = ss . taboption ( 'field_general' , form . TextValue , 'ssh_priv_key' , _ ( 'Priv-key' ) ) ;
so . depends ( 'type' , 'ssh' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'ssh_priv_key_passphrase' , _ ( 'Priv-key passphrase' ) ) ;
so . password = true ;
so . depends ( { type : 'ssh' , ssh _priv _key : /.+/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . DynamicList , 'ssh_host_key_algorithms' , _ ( 'Host-key algorithms' ) ) ;
so . placeholder = 'rsa' ;
so . depends ( 'type' , 'ssh' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . DynamicList , 'ssh_host_key' , _ ( 'Host-key' ) ) ;
so . placeholder = 'ssh-rsa AAAAB3NzaC1yc2EAA...' ;
so . depends ( { type : 'ssh' , ssh _host _key _algorithms : /.+/ } ) ;
so . modalonly = true ;
/* Shadowsocks fields */
so = ss . taboption ( 'field_general' , form . ListValue , 'shadowsocks_chipher' , _ ( 'Chipher' ) ) ;
so . default = hm . shadowsocks _cipher _methods [ 1 ] [ 0 ] ;
hm . shadowsocks _cipher _methods . forEach ( ( res ) => {
so . value . apply ( so , res ) ;
} )
so . depends ( 'type' , 'ss' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'shadowsocks_password' , _ ( 'Password' ) ) ;
so . password = true ;
so . validate = function ( section _id , value ) {
2025-01-15 16:25:40 +08:00
const encmode = this . section . getOption ( 'shadowsocks_chipher' ) . formvalue ( section _id ) ;
2025-01-17 20:36:29 +08:00
return hm . validateShadowsocksPassword . call ( this , encmode , section _id , value ) ;
2024-11-16 20:36:29 +08:00
}
so . depends ( { type : 'ss' , shadowsocks _chipher : /.+/ } ) ;
so . modalonly = true ;
2024-12-21 14:15:01 +08:00
/* Mieru fields */
so = ss . taboption ( 'field_general' , form . Value , 'mieru_port_range' , _ ( 'Port range' ) ) ;
so . datatype = 'portrange' ;
so . depends ( 'type' , 'mieru' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'mieru_transport' , _ ( 'Transport' ) ) ;
so . value ( 'TCP' ) ;
so . default = 'TCP' ;
so . depends ( 'type' , 'mieru' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'mieru_multiplexing' , _ ( 'Multiplexing' ) ) ;
so . value ( 'MULTIPLEXING_OFF' ) ;
so . value ( 'MULTIPLEXING_LOW' ) ;
so . value ( 'MULTIPLEXING_MIDDLE' ) ;
so . value ( 'MULTIPLEXING_HIGH' ) ;
so . default = 'MULTIPLEXING_LOW' ;
so . depends ( 'type' , 'mieru' ) ;
so . modalonly = true ;
2024-11-16 20:36:29 +08:00
/* Snell fields */
so = ss . taboption ( 'field_general' , form . Value , 'snell_psk' , _ ( 'Pre-shared key' ) ) ;
so . password = true ;
so . rmempty = false ;
so . validate = L . bind ( hm . validateAuthPassword , so ) ;
so . depends ( 'type' , 'snell' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'snell_version' , _ ( 'Version' ) ) ;
so . value ( '1' , _ ( 'v1' ) ) ;
so . value ( '2' , _ ( 'v2' ) ) ;
so . value ( '3' , _ ( 'v3' ) ) ;
so . default = '3' ;
so . depends ( 'type' , 'snell' ) ;
so . modalonly = true ;
/* TUIC fields */
so = ss . taboption ( 'field_general' , form . Value , 'uuid' , _ ( 'UUID' ) ) ;
so . rmempty = false ;
so . validate = L . bind ( hm . validateUUID , so ) ;
so . depends ( 'type' , 'tuic' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'tuic_ip' , _ ( 'IP override' ) ,
_ ( 'Override the IP address of the server that DNS response.' ) ) ;
so . datatype = 'ipaddr(1)' ;
so . depends ( 'type' , 'tuic' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'tuic_congestion_controller' , _ ( 'Congestion controller' ) ,
_ ( 'QUIC congestion controller.' ) ) ;
so . default = 'cubic' ;
so . value ( 'cubic' , _ ( 'cubic' ) ) ;
so . value ( 'new_reno' , _ ( 'new_reno' ) ) ;
so . value ( 'bbr' , _ ( 'bbr' ) ) ;
so . depends ( 'type' , 'tuic' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'tuic_udp_relay_mode' , _ ( 'UDP relay mode' ) ,
_ ( 'UDP packet relay mode.' ) ) ;
so . value ( '' , _ ( 'Default' ) ) ;
so . value ( 'native' , _ ( 'Native' ) ) ;
so . value ( 'quic' , _ ( 'QUIC' ) ) ;
so . depends ( { type : 'tuic' , tuic _udp _over _stream : '0' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Flag , 'tuic_udp_over_stream' , _ ( 'UDP over stream' ) ,
_ ( 'This is the TUIC port of the SUoT protocol, designed to provide a QUIC stream based UDP relay mode that TUIC does not provide.' ) ) ;
so . default = so . disabled ;
so . depends ( 'type' , 'tuic' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'tuic_udp_over_stream_version' , _ ( 'UDP over stream version' ) ) ;
so . value ( '1' , _ ( 'v1' ) ) ;
so . depends ( { type : 'tuic' , tuic _udp _over _stream : '1' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'tuic_max_udp_relay_packet_size' , _ ( 'Max UDP relay packet size' ) ) ;
so . datatype = 'uinteger' ;
so . placeholder = '1500' ;
so . depends ( 'type' , 'tuic' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Flag , 'tuic_reduce_rtt' , _ ( 'Enable 0-RTT handshake' ) ,
_ ( 'Enable 0-RTT QUIC connection handshake on the client side. This is not impacting much on the performance, as the protocol is fully multiplexed.<br/>' +
'Disabling this is highly recommended, as it is vulnerable to replay attacks.' ) ) ;
so . default = so . disabled ;
so . depends ( 'type' , 'tuic' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'tuic_heartbeat' , _ ( 'Heartbeat interval' ) ,
_ ( 'In millisecond.' ) ) ;
so . datatype = 'uinteger' ;
so . placeholder = '10000' ;
so . depends ( 'type' , 'tuic' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'tuic_request_timeout' , _ ( 'Request timeout' ) ,
_ ( 'In millisecond.' ) ) ;
so . datatype = 'uinteger' ;
so . placeholder = '8000' ;
so . depends ( 'type' , 'tuic' ) ;
so . modalonly = true ;
/* Trojan fields */
so = ss . taboption ( 'field_general' , form . Flag , 'trojan_ss_enabled' , _ ( 'Shadowsocks encrypt' ) ) ;
so . default = so . disabled ;
so . depends ( 'type' , 'trojan' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'trojan_ss_chipher' , _ ( 'Shadowsocks chipher' ) ) ;
2025-03-03 20:39:58 +08:00
so . default = hm . trojan _cipher _methods [ 0 ] [ 0 ] ;
hm . trojan _cipher _methods . forEach ( ( res ) => {
so . value . apply ( so , res ) ;
} )
2024-11-16 20:36:29 +08:00
so . depends ( { type : 'trojan' , trojan _ss _enabled : '1' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'trojan_ss_password' , _ ( 'Shadowsocks password' ) ) ;
so . password = true ;
so . validate = function ( section _id , value ) {
2025-01-15 16:25:40 +08:00
const encmode = this . section . getOption ( 'trojan_ss_chipher' ) . formvalue ( section _id ) ;
2025-01-17 20:36:29 +08:00
return hm . validateShadowsocksPassword . call ( this , encmode , section _id , value ) ;
2024-11-16 20:36:29 +08:00
}
so . depends ( { type : 'trojan' , trojan _ss _enabled : '1' } ) ;
so . modalonly = true ;
2025-03-05 14:16:22 +08:00
/* AnyTLS fields */
so = ss . taboption ( 'field_general' , form . Value , 'anytls_idle_session_check_interval' , _ ( 'Idle session check interval' ) ,
_ ( 'In seconds.' ) ) ;
so . placeholder = '30' ;
so . validate = L . bind ( hm . validateTimeDuration , so ) ;
so . depends ( 'type' , 'anytls' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'anytls_idle_session_timeout' , _ ( 'Idle session timeout' ) ,
_ ( 'In seconds.' ) ) ;
so . placeholder = '30' ;
so . validate = L . bind ( hm . validateTimeDuration , so ) ;
so . depends ( 'type' , 'anytls' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'anytls_min_idle_session' , _ ( 'Min of idle sessions to keep' ) ) ;
so . datatype = 'uinteger' ;
so . placeholder = '0' ;
so . depends ( 'type' , 'anytls' ) ;
so . modalonly = true ;
2024-11-16 20:36:29 +08:00
/* VMess / VLESS fields */
so = ss . taboption ( 'field_general' , form . Value , 'vmess_uuid' , _ ( 'UUID' ) ) ;
so . rmempty = false ;
so . validate = L . bind ( hm . validateUUID , so ) ;
so . depends ( { type : /^(vmess|vless)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'vless_flow' , _ ( 'Flow' ) ) ;
2025-02-12 00:27:05 +08:00
so . default = hm . vless _flow [ 0 ] [ 0 ] ;
hm . vless _flow . forEach ( ( res ) => {
so . value . apply ( so , res ) ;
} )
2024-11-16 20:36:29 +08:00
so . depends ( 'type' , 'vless' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'vmess_alterid' , _ ( 'Alter ID' ) ) ;
so . datatype = 'uinteger' ;
so . default = '0' ;
so . depends ( 'type' , 'vmess' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'vmess_chipher' , _ ( 'Chipher' ) ) ;
so . default = 'auto' ;
so . value ( 'auto' , _ ( 'auto' ) ) ;
so . value ( 'none' , _ ( 'none' ) ) ;
so . value ( 'zero' , _ ( 'zero' ) ) ;
so . value ( 'aes-128-gcm' , _ ( 'aes-128-gcm' ) ) ;
so . value ( 'chacha20-poly1305' , _ ( 'chacha20-poly1305' ) ) ;
so . depends ( 'type' , 'vmess' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Flag , 'vmess_global_padding' , _ ( 'Global padding' ) ,
_ ( 'Protocol parameter. Will waste traffic randomly if enabled (enabled by default in v2ray and cannot be disabled).' ) ) ;
so . default = so . enabled ;
so . depends ( 'type' , 'vmess' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Flag , 'vmess_authenticated_length' , _ ( 'Authenticated length' ) ,
_ ( 'Protocol parameter. Enable length block encryption.' ) ) ;
so . default = so . disabled ;
so . depends ( 'type' , 'vmess' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'vmess_packet_encoding' , _ ( 'Packet encoding' ) ) ;
so . value ( '' , _ ( 'none' ) ) ;
so . value ( 'packetaddr' , _ ( 'packet addr (v2ray-core v5+)' ) ) ;
so . value ( 'xudp' , _ ( 'Xudp (Xray-core)' ) ) ;
so . depends ( { type : /^(vmess|vless)$/ } ) ;
so . modalonly = true ;
2024-11-27 11:29:22 +08:00
/* WireGuard fields */
so = ss . taboption ( 'field_general' , form . Value , 'wireguard_ip' , _ ( 'Local address' ) ,
_ ( 'The %s address used by local machine in the Wireguard network.' ) . format ( 'IPv4' ) ) ;
so . datatype = 'ip4addr(1)' ;
so . rmempty = false ;
so . depends ( 'type' , 'wireguard' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'wireguard_ipv6' , _ ( 'Local IPv6 address' ) ,
_ ( 'The %s address used by local machine in the Wireguard network.' ) . format ( 'IPv6' ) ) ;
so . datatype = 'ip6addr(1)' ;
so . depends ( 'type' , 'wireguard' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'wireguard_private_key' , _ ( 'Private key' ) ,
_ ( 'WireGuard requires base64-encoded private keys.' ) ) ;
so . password = true ;
so . validate = L . bind ( hm . validateBase64Key , so , 44 ) ;
so . rmempty = false ;
so . depends ( 'type' , 'wireguard' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'wireguard_peer_public_key' , _ ( 'Peer pubkic key' ) ,
_ ( 'WireGuard peer public key.' ) ) ;
so . validate = L . bind ( hm . validateBase64Key , so , 44 ) ;
so . rmempty = false ;
so . depends ( 'type' , 'wireguard' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'wireguard_pre_shared_key' , _ ( 'Pre-shared key' ) ,
_ ( 'WireGuard pre-shared key.' ) ) ;
so . password = true ;
so . validate = L . bind ( hm . validateBase64Key , so , 44 ) ;
so . depends ( 'type' , 'wireguard' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . DynamicList , 'wireguard_allowed_ips' , _ ( 'Allowed IPs' ) ,
_ ( 'Destination addresses allowed to be forwarded via Wireguard.' ) ) ;
so . datatype = 'cidr' ;
so . placeholder = '0.0.0.0/0' ;
so . depends ( 'type' , 'wireguard' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . DynamicList , 'wireguard_reserved' , _ ( 'Reserved field bytes' ) ) ;
so . datatype = 'integer' ;
so . depends ( 'type' , 'wireguard' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'wireguard_mtu' , _ ( 'MTU' ) ) ;
so . datatype = 'range(0,9000)' ;
so . placeholder = '1408' ;
so . depends ( 'type' , 'wireguard' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Flag , 'wireguard_remote_dns_resolve' , _ ( 'Remote DNS resolve' ) ,
_ ( 'Force DNS remote resolution.' ) ) ;
so . default = so . disabled ;
so . depends ( 'type' , 'wireguard' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . DynamicList , 'wireguard_dns' , _ ( 'DNS server' ) ) ;
so . datatype = 'or(host, hostport)' ;
so . depends ( 'wireguard_remote_dns_resolve' , '1' ) ;
so . modalonly = true ;
2024-11-16 20:36:29 +08:00
/* Plugin fields */
so = ss . taboption ( 'field_general' , form . ListValue , 'plugin' , _ ( 'Plugin' ) ) ;
so . value ( '' , _ ( 'none' ) ) ;
so . value ( 'obfs' , _ ( 'obfs-simple' ) ) ;
//so.value('v2ray-plugin', _('v2ray-plugin'));
2025-04-02 10:58:12 +08:00
//so.value('gost-plugin', _('gost-plugin'));
2024-11-16 20:36:29 +08:00
so . value ( 'shadow-tls' , _ ( 'shadow-tls' ) ) ;
so . value ( 'restls' , _ ( 'restls' ) ) ;
so . depends ( 'type' , 'ss' ) ;
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' ) ) ;
2025-05-10 20:38:54 +08:00
so . datatype = 'hostname' ;
2024-11-16 20:36:29 +08:00
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' ) ) ;
so . password = true ;
so . rmempty = false ;
so . depends ( { plugin : /^(shadow-tls|restls)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'plugin_opts_shadowtls_version' , _ ( 'Plugin: ' ) + _ ( 'Version' ) ) ;
so . value ( '1' , _ ( 'v1' ) ) ;
so . value ( '2' , _ ( 'v2' ) ) ;
so . value ( '3' , _ ( 'v3' ) ) ;
so . default = '2' ;
so . depends ( { plugin : 'shadow-tls' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'plugin_opts_restls_versionhint' , _ ( 'Plugin: ' ) + _ ( 'Version hint' ) ) ;
so . default = 'tls13' ;
so . rmempty = false ;
so . depends ( { plugin : 'restls' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Value , 'plugin_opts_restls_script' , _ ( 'Plugin: ' ) + _ ( 'Restls script' ) ) ;
so . default = '300?100<1,400~100,350~100,600~100,300~200,300~100' ;
so . rmempty = false ;
so . depends ( { plugin : 'restls' } ) ;
so . modalonly = true ;
/* Extra fields */
so = ss . taboption ( 'field_general' , form . Flag , 'udp' , _ ( 'UDP' ) ) ;
so . default = so . disabled ;
2025-04-02 10:58:12 +08:00
so . depends ( { type : /^(direct|socks5|ss|mieru|vmess|vless|trojan|anytls|wireguard)$/ } ) ;
2024-11-16 20:36:29 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Flag , 'uot' , _ ( 'UoT' ) ,
_ ( 'Enable the SUoT protocol, requires server support. Conflict with Multiplex.' ) ) ;
so . default = so . disabled ;
so . depends ( 'type' , 'ss' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'uot_version' , _ ( 'SUoT version' ) ) ;
so . value ( '1' , _ ( 'v1' ) ) ;
so . value ( '2' , _ ( 'v2' ) ) ;
so . default = '2' ;
so . depends ( 'uot' , '1' ) ;
so . modalonly = true ;
/* TLS fields */
so = ss . taboption ( 'field_general' , form . Flag , 'tls' , _ ( 'TLS' ) ) ;
so . default = so . disabled ;
so . validate = function ( section _id , value ) {
2025-01-15 16:25:40 +08:00
const type = this . section . getOption ( 'type' ) . formvalue ( section _id ) ;
let tls = this . section . getUIElement ( section _id , 'tls' ) . node . querySelector ( 'input' ) ;
2024-11-16 20:36:29 +08:00
// Force enabled
2025-03-05 14:16:22 +08:00
if ( [ 'trojan' , 'anytls' , 'hysteria' , 'hysteria2' , 'tuic' ] . includes ( type ) ) {
2024-11-16 20:36:29 +08:00
tls . checked = true ;
tls . disabled = true ;
} else {
tls . disabled = null ;
}
2025-05-10 20:38:54 +08:00
return true ;
}
so . depends ( { type : /^(http|socks5|vmess|vless|trojan|anytls|hysteria|hysteria2|tuic)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_tls' , form . Flag , 'tls_disable_sni' , _ ( 'Disable SNI' ) ,
_ ( 'Donot send server name in ClientHello.' ) ) ;
so . default = so . disabled ;
so . depends ( { tls : '1' , type : /^(tuic)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_tls' , form . Value , 'tls_sni' , _ ( 'TLS SNI' ) ,
_ ( 'Used to verify the hostname on the returned certificates.' ) ) ;
so . depends ( { tls : '1' , type : /^(http|vmess|vless|trojan|anytls|hysteria|hysteria2)$/ } ) ;
so . depends ( { tls : '1' , tls _disable _sni : '0' , type : /^(tuic)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_tls' , form . DynamicList , 'tls_alpn' , _ ( 'TLS ALPN' ) ,
_ ( 'List of supported application level protocols, in order of preference.' ) ) ;
so . validate = function ( section _id , value ) {
const type = this . section . getOption ( 'type' ) . formvalue ( section _id ) ;
//const plugin = this.section.getOption('plugin').formvalue(section_id);
let tls _alpn = this . section . getUIElement ( section _id , 'tls_alpn' ) ;
2024-11-16 20:36:29 +08:00
// Default alpn
if ( ! ` ${ tls _alpn . getValue ( ) } ` ) {
let def _alpn ;
switch ( type ) {
2025-05-10 20:38:54 +08:00
case 'ss' :
def _alpn = [ 'h2' , 'http/1.1' ] ; // when plugin === 'shadow-tls'
break ;
2024-11-16 20:36:29 +08:00
case 'hysteria' :
case 'hysteria2' :
case 'tuic' :
def _alpn = [ 'h3' ] ;
break ;
case 'vmess' :
case 'vless' :
case 'trojan' :
2025-03-05 14:16:22 +08:00
case 'anytls' :
2024-11-16 20:36:29 +08:00
def _alpn = [ 'h2' , 'http/1.1' ] ;
break ;
default :
def _alpn = [ ] ;
}
tls _alpn . setValue ( def _alpn ) ;
}
return true ;
}
2025-03-05 14:16:22 +08:00
so . depends ( { tls : '1' , type : /^(vmess|vless|trojan|anytls|hysteria|hysteria2|tuic)$/ } ) ;
2025-05-10 20:38:54 +08:00
so . depends ( { type : 'ss' , plugin : 'shadow-tls' } ) ;
2024-11-16 20:36:29 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_tls' , form . Value , 'tls_fingerprint' , _ ( 'Cert fingerprint' ) ,
_ ( 'Certificate fingerprint. Used to implement SSL Pinning and prevent MitM.' ) ) ;
so . validate = function ( section _id , value ) {
if ( ! value )
return true ;
if ( ! ( ( value . length === 64 ) && ( value . match ( /^[0-9a-fA-F]+$/ ) ) ) )
return _ ( 'Expecting: %s' ) . format ( _ ( 'valid SHA256 string with %d characters' ) . format ( 64 ) ) ;
return true ;
}
so . depends ( { tls : '1' , type : /^(http|socks5|vmess|vless|trojan|hysteria|hysteria2)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_tls' , form . Flag , 'tls_skip_cert_verify' , _ ( 'Skip cert verify' ) ,
_ ( 'Donot verifying server certificate.' ) +
'<br/>' +
_ ( 'This is <strong>DANGEROUS</strong>, your traffic is almost like <strong>PLAIN TEXT</strong>! Use at your own risk!' ) ) ;
so . default = so . disabled ;
2025-03-05 14:16:22 +08:00
so . depends ( { tls : '1' , type : /^(http|socks5|vmess|vless|trojan|anytls|hysteria|hysteria2|tuic)$/ } ) ;
2024-11-16 20:36:29 +08:00
so . modalonly = true ;
// uTLS fields
so = ss . taboption ( 'field_tls' , form . ListValue , 'tls_client_fingerprint' , _ ( 'Client fingerprint' ) ) ;
so . default = hm . tls _client _fingerprints [ 0 ] [ 0 ] ;
hm . tls _client _fingerprints . forEach ( ( res ) => {
so . value . apply ( so , res ) ;
} )
2025-03-05 14:16:22 +08:00
so . depends ( { tls : '1' , type : /^(vmess|vless|trojan|anytls)$/ } ) ;
2024-11-16 20:36:29 +08:00
so . depends ( { type : 'ss' , plugin : /^(shadow-tls|restls)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_tls' , form . Flag , 'tls_reality' , _ ( 'REALITY' ) ) ;
so . default = so . disabled ;
so . depends ( { tls : '1' , type : /^(vmess|vless|trojan)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_tls' , form . Value , 'tls_reality_public_key' , _ ( 'REALITY public key' ) ) ;
so . rmempty = false ;
so . depends ( 'tls_reality' , '1' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_tls' , form . Value , 'tls_reality_short_id' , _ ( 'REALITY short ID' ) ) ;
so . rmempty = false ;
so . depends ( 'tls_reality' , '1' ) ;
so . modalonly = true ;
/* Transport fields */
so = ss . taboption ( 'field_general' , form . Flag , 'transport_enabled' , _ ( 'Transport' ) ) ;
so . default = so . disabled ;
so . depends ( { type : /^(vmess|vless|trojan)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_transport' , form . ListValue , 'transport_type' , _ ( 'Transport type' ) ) ;
so . default = 'http' ;
so . value ( 'http' , _ ( 'HTTP' ) ) ;
so . value ( 'h2' , _ ( 'HTTPUpgrade' ) ) ;
so . value ( 'grpc' , _ ( 'gRPC' ) ) ;
so . value ( 'ws' , _ ( 'WebSocket' ) ) ;
so . validate = function ( section _id , value ) {
2025-01-15 16:25:40 +08:00
const type = this . section . getOption ( 'type' ) . formvalue ( section _id ) ;
2024-11-16 20:36:29 +08:00
switch ( type ) {
case 'vmess' :
case 'vless' :
if ( ! [ 'http' , 'h2' , 'grpc' , 'ws' ] . includes ( value ) )
return _ ( 'Expecting: only support %s.' ) . format ( _ ( 'HTTP' ) +
' / ' + _ ( 'HTTPUpgrade' ) +
' / ' + _ ( 'gRPC' ) +
' / ' + _ ( 'WebSocket' ) ) ;
break ;
case 'trojan' :
if ( ! [ 'grpc' , 'ws' ] . includes ( value ) )
return _ ( 'Expecting: only support %s.' ) . format ( _ ( 'gRPC' ) +
' / ' + _ ( 'WebSocket' ) ) ;
break ;
default :
break ;
}
return true ;
}
so . depends ( 'transport_enabled' , '1' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_transport' , form . DynamicList , 'transport_hosts' , _ ( 'Server hostname' ) ) ;
so . datatype = 'list(hostname)' ;
so . placeholder = 'example.com' ;
so . depends ( { transport _enabled : '1' , transport _type : 'h2' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_transport' , form . Value , 'transport_http_method' , _ ( 'HTTP request method' ) ) ;
so . value ( 'GET' , _ ( 'GET' ) ) ;
so . value ( 'POST' , _ ( 'POST' ) ) ;
so . value ( 'PUT' , _ ( 'PUT' ) ) ;
so . default = 'GET' ;
so . rmempty = false ;
so . depends ( { transport _enabled : '1' , transport _type : 'http' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_transport' , form . DynamicList , 'transport_paths' , _ ( 'Request path' ) ) ;
so . placeholder = '/video' ;
so . default = '/' ;
so . rmempty = false ;
so . depends ( { transport _enabled : '1' , transport _type : 'http' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_transport' , form . Value , 'transport_path' , _ ( 'Request path' ) ) ;
so . placeholder = '/' ;
so . default = '/' ;
so . rmempty = false ;
so . depends ( { transport _enabled : '1' , transport _type : /^(h2|ws)$/ } ) ;
so . modalonly = true ;
2025-01-17 20:36:29 +08:00
so = ss . taboption ( 'field_transport' , hm . TextValue , 'transport_http_headers' , _ ( 'HTTP header' ) ) ;
2024-11-16 20:36:29 +08:00
so . placeholder = '{\n "Host": "example.com",\n "Connection": [\n "keep-alive"\n ]\n}' ;
so . validate = L . bind ( hm . validateJson , so ) ;
so . depends ( { transport _enabled : '1' , transport _type : /^(http|ws)$/ } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_transport' , form . Value , 'transport_grpc_servicename' , _ ( 'gRPC service name' ) ) ;
so . depends ( { transport _enabled : '1' , transport _type : 'grpc' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_transport' , form . Value , 'transport_ws_max_early_data' , _ ( 'Max Early Data' ) ,
_ ( 'Early Data first packet length limit.' ) ) ;
so . datatype = 'uinteger' ;
so . value ( '2048' ) ;
so . depends ( { transport _enabled : '1' , transport _type : 'ws' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_transport' , form . Value , 'transport_ws_early_data_header' , _ ( 'Early Data header name' ) ) ;
so . value ( 'Sec-WebSocket-Protocol' ) ;
so . depends ( { transport _enabled : '1' , transport _type : 'ws' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_transport' , form . Flag , 'transport_ws_v2ray_http_upgrade' , _ ( 'V2ray HTTPUpgrade' ) ) ;
so . default = so . disabled ;
so . depends ( { transport _enabled : '1' , transport _type : 'ws' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_transport' , form . Flag , 'transport_ws_v2ray_http_upgrade_fast_open' , _ ( 'V2ray HTTPUpgrade fast open' ) ) ;
so . default = so . disabled ;
so . depends ( { transport _enabled : '1' , transport _type : 'ws' , transport _ws _v2ray _http _upgrade : '1' } ) ;
so . modalonly = true ;
/* Multiplex fields */ // TCP protocol only
so = ss . taboption ( 'field_general' , form . Flag , 'smux_enabled' , _ ( 'Multiplex' ) ) ;
so . default = so . disabled ;
so . depends ( { type : /^(vmess|vless|trojan)$/ } ) ;
so . depends ( { type : 'ss' , uot : '0' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_multiplex' , form . ListValue , 'smux_protocol' , _ ( 'Protocol' ) ) ;
so . default = 'h2mux' ;
so . value ( 'smux' , _ ( 'smux' ) ) ;
so . value ( 'yamux' , _ ( 'yamux' ) ) ;
so . value ( 'h2mux' , _ ( 'h2mux' ) ) ;
so . depends ( 'smux_enabled' , '1' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_multiplex' , form . Value , 'smux_max_connections' , _ ( 'Maximum connections' ) ) ;
so . datatype = 'uinteger' ;
so . placeholder = '4' ;
so . depends ( 'smux_enabled' , '1' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_multiplex' , form . Value , 'smux_min_streams' , _ ( 'Minimum streams' ) ,
_ ( 'Minimum multiplexed streams in a connection before opening a new connection.' ) ) ;
so . datatype = 'uinteger' ;
so . placeholder = '4' ;
so . depends ( 'smux_enabled' , '1' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_multiplex' , form . Value , 'smux_max_streams' , _ ( 'Maximum streams' ) ,
_ ( 'Maximum multiplexed streams in a connection before opening a new connection.<br/>' +
'Conflict with <code>%s</code> and <code>%s</code>.' )
. format ( _ ( 'Maximum connections' ) , _ ( 'Minimum streams' ) ) ) ;
so . datatype = 'uinteger' ;
so . placeholder = '0' ;
so . depends ( { smux _enabled : '1' , smux _max _connections : '' , smux _min _streams : '' } ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_multiplex' , form . Flag , 'smux_padding' , _ ( 'Enable padding' ) ) ;
so . default = so . disabled ;
so . depends ( 'smux_enabled' , '1' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_multiplex' , form . Flag , 'smux_only_tcp' , _ ( 'TCP only' ) ,
_ ( 'Enable multiplexing only for TCP.' ) ) ;
so . default = so . disabled ;
so . depends ( 'smux_enabled' , '1' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_multiplex' , form . Flag , 'smux_statistic' , _ ( 'Enable statistic' ) ,
_ ( 'Show connections in the dashboard for breaking connections easier.' ) ) ;
so . default = so . disabled ;
so . depends ( 'smux_enabled' , '1' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_multiplex' , form . Flag , 'smux_brutal' , _ ( 'Enable TCP Brutal' ) ,
_ ( 'Enable TCP Brutal congestion control algorithm' ) ) ;
so . default = so . disabled ;
so . depends ( 'smux_enabled' , '1' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_multiplex' , form . Value , 'smux_brutal_up' , _ ( 'Upload bandwidth' ) ,
_ ( 'Upload bandwidth in Mbps.' ) ) ;
so . datatype = 'uinteger' ;
so . depends ( 'smux_brutal' , '1' ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_multiplex' , form . Value , 'smux_brutal_down' , _ ( 'Download bandwidth' ) ,
_ ( 'Download bandwidth in Mbps.' ) ) ;
so . datatype = 'uinteger' ;
so . depends ( 'smux_brutal' , '1' ) ;
so . modalonly = true ;
/* Dial fields */
so = ss . taboption ( 'field_dial' , form . Flag , 'tfo' , _ ( 'TFO' ) ) ;
so . default = so . disabled ;
so . modalonly = true ;
so = ss . taboption ( 'field_dial' , form . Flag , 'mptcp' , _ ( 'mpTCP' ) ) ;
so . default = so . disabled ;
so . modalonly = true ;
2024-12-01 20:38:03 +08:00
/ * F e a t u r e s a r e i m p l e m e n t e d i n p r o x y c h a i n
2024-11-16 20:36:29 +08:00
so = ss . taboption ( 'field_dial' , form . Value , 'dialer_proxy' , _ ( 'dialer-proxy' ) ) ;
so . readonly = true ;
so . modalonly = true ;
2024-12-01 20:38:03 +08:00
* /
2024-11-16 20:36:29 +08:00
so = ss . taboption ( 'field_dial' , widgets . DeviceSelect , 'interface_name' , _ ( 'Bind interface' ) ,
_ ( 'Bind outbound interface.</br>' ) +
2025-02-12 00:27:05 +08:00
_ ( 'Priority: Proxy Node > Global.' ) ) ;
2024-11-16 20:36:29 +08:00
so . multiple = false ;
so . noaliases = true ;
so . modalonly = true ;
so = ss . taboption ( 'field_dial' , form . Value , 'routing_mark' , _ ( 'Routing mark' ) ,
2025-02-12 00:27:05 +08:00
_ ( 'Priority: Proxy Node > Global.' ) ) ;
2024-11-16 20:36:29 +08:00
so . datatype = 'uinteger' ;
so . modalonly = true ;
so = ss . taboption ( 'field_dial' , form . ListValue , 'ip_version' , _ ( 'IP version' ) ) ;
so . default = hm . ip _version [ 0 ] [ 0 ] ;
hm . ip _version . forEach ( ( res ) => {
so . value . apply ( so , res ) ;
} )
so . modalonly = true ;
2024-10-26 10:27:23 +08:00
/* Proxy Node END */
/* Provider START */
s . tab ( 'provider' , _ ( 'Provider' ) ) ;
/* Provider */
2025-03-09 00:20:30 +08:00
o = s . taboption ( 'provider' , form . SectionValue , '_provider' , hm . GridSection , 'provider' , null ) ;
2024-10-26 10:27:23 +08:00
ss = o . subsection ;
ss . addremove = true ;
ss . rowcolors = true ;
ss . sortable = true ;
ss . nodescriptions = true ;
2025-03-09 00:20:30 +08:00
ss . hm _modaltitle = [ _ ( 'Provider' ) , _ ( 'Add a provider' ) ] ;
2025-03-09 16:21:55 +08:00
ss . hm _prefmt = hm . glossary [ ss . sectiontype ] . prefmt ;
2025-03-10 04:19:32 +08:00
ss . hm _field = hm . glossary [ ss . sectiontype ] . field ;
2025-03-09 00:20:30 +08:00
ss . hm _lowcase _only = false ;
2025-03-10 16:22:21 +08:00
/* Import mihomo config and Remove idle files start */
ss . handleYamlImport = function ( ) {
const field = this . hm _field ;
const o = new hm . HandleImport ( this . map , this , _ ( 'Import mihomo config' ) ,
_ ( 'Please type <code>%s</code> fields of mihomo config.</br>' )
. format ( field ) ) ;
o . placeholder = 'proxy-providers:\n' +
' provider1:\n' +
' type: http\n' +
' url: "http://test.com"\n' +
' path: ./proxy_providers/provider1.yaml\n' +
' interval: 3600\n' +
' proxy: DIRECT\n' +
' size-limit: 0\n' +
' header:\n' +
' User-Agent:\n' +
' - "Clash/v1.18.0"\n' +
' - "mihomo/1.18.3"\n' +
' Accept:\n' +
" - 'application/vnd.github.v3.raw'\n" +
' Authorization:\n' +
" - 'token 1231231'\n" +
' health-check:\n' +
' enable: true\n' +
' interval: 600\n' +
' timeout: 5000\n' +
' lazy: true\n' +
' url: https://cp.cloudflare.com/generate_204\n' +
' expected-status: 204\n' +
' override:\n' +
' tfo: false\n' +
' mptcp: false\n' +
' udp: true\n' +
' udp-over-tcp: false\n' +
' down: "50 Mbps"\n' +
' up: "10 Mbps"\n' +
' skip-cert-verify: true\n' +
' dialer-proxy: proxy\n' +
' interface-name: tailscale0\n' +
' routing-mark: 233\n' +
' ip-version: ipv4-prefer\n' +
' additional-prefix: "[provider1]"\n' +
' additional-suffix: "test"\n' +
' proxy-name:\n' +
' - pattern: "test"\n' +
' target: "TEST"\n' +
' - pattern: "IPLC-(.*?)倍"\n' +
' target: "iplc x $1"\n' +
' filter: "(?i)港|hk|hongkong|hong kong"\n' +
' exclude-filter: "xxx"\n' +
' exclude-type: "ss|http"\n' +
' provider2:\n' +
' type: inline\n' +
' dialer-proxy: proxy\n' +
' payload:\n' +
' - name: "ss1"\n' +
' type: ss\n' +
' server: test.server.com\n' +
' port: 443\n' +
' cipher: chacha20-ietf-poly1305\n' +
' password: "password"\n' +
2025-03-12 00:27:48 +08:00
' provider3:\n' +
' type: http\n' +
' url: "http://test.com"\n' +
' path: ./proxy_providers/provider3.yaml\n' +
' proxy: proxy\n' +
2025-03-10 16:22:21 +08:00
' test:\n' +
' type: file\n' +
' path: /test.yaml\n' +
' health-check:\n' +
' enable: true\n' +
' interval: 36000\n' +
' url: https://cp.cloudflare.com/generate_204\n' +
' ...'
2025-03-12 00:27:48 +08:00
o . parseYaml = function ( field , name , cfg ) {
let config = hm . HandleImport . prototype . parseYaml . call ( this , field , name , cfg ) ;
2025-03-15 00:25:33 +08:00
return config ? parseProviderYaml . call ( this , field , name , config ) : null ;
2025-03-12 00:27:48 +08:00
} ;
2025-03-10 16:22:21 +08:00
return o . render ( ) ;
}
2024-10-26 10:27:23 +08:00
ss . renderSectionAdd = function ( /* ... */ ) {
2025-03-09 00:20:30 +08:00
let el = hm . GridSection . prototype . renderSectionAdd . apply ( this , arguments ) ;
2024-10-26 10:27:23 +08:00
2025-03-10 16:22:21 +08:00
el . appendChild ( E ( 'button' , {
'class' : 'cbi-button cbi-button-add' ,
'title' : _ ( 'mihomo config' ) ,
'click' : ui . createHandlerFn ( this , 'handleYamlImport' )
} , [ _ ( 'Import mihomo config' ) ] ) ) ;
2024-10-26 10:27:23 +08:00
el . appendChild ( E ( 'button' , {
'class' : 'cbi-button cbi-button-add' ,
'title' : _ ( 'Remove idles' ) ,
2025-01-17 20:36:29 +08:00
'click' : ui . createHandlerFn ( this , hm . handleRemoveIdles )
2024-10-26 10:27:23 +08:00
} , [ _ ( 'Remove idles' ) ] ) ) ;
return el ;
}
2025-03-10 16:22:21 +08:00
/* Import mihomo config and Remove idle files end */
2024-10-26 10:27:23 +08:00
ss . tab ( 'field_general' , _ ( 'General fields' ) ) ;
ss . tab ( 'field_override' , _ ( 'Override fields' ) ) ;
ss . tab ( 'field_health' , _ ( 'Health fields' ) ) ;
/* General fields */
so = ss . taboption ( 'field_general' , form . Value , 'label' , _ ( 'Label' ) ) ;
so . load = L . bind ( hm . loadDefaultLabel , so ) ;
so . validate = L . bind ( hm . validateUniqueValue , so ) ;
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . Flag , 'enabled' , _ ( 'Enable' ) ) ;
so . default = so . enabled ;
so . editable = true ;
so = ss . taboption ( 'field_general' , form . ListValue , 'type' , _ ( 'Type' ) ) ;
so . value ( 'file' , _ ( 'Local' ) ) ;
so . value ( 'http' , _ ( 'Remote' ) ) ;
2024-12-26 12:23:51 +08:00
so . value ( 'inline' , _ ( 'Inline' ) ) ;
2024-10-26 10:27:23 +08:00
so . default = 'http' ;
so = ss . option ( form . DummyValue , '_value' , _ ( 'Value' ) ) ;
so . load = function ( section _id ) {
2025-01-15 16:25:40 +08:00
const option = uci . get ( data [ 0 ] , section _id , 'type' ) ;
2024-10-26 10:27:23 +08:00
switch ( option ) {
case 'file' :
return uci . get ( data [ 0 ] , section _id , '.name' ) ;
case 'http' :
return uci . get ( data [ 0 ] , section _id , 'url' ) ;
2024-12-26 12:23:51 +08:00
case 'inline' :
return uci . get ( data [ 0 ] , section _id , '.name' ) ;
2024-10-26 10:27:23 +08:00
default :
return null ;
}
}
so . modalonly = false ;
2025-01-17 20:36:29 +08:00
so = ss . taboption ( 'field_general' , hm . TextValue , '_editer' , _ ( 'Editer' ) ,
2024-10-26 10:27:23 +08:00
_ ( 'Please type <a target="_blank" href="%s" rel="noreferrer noopener">%s</a>.' )
. format ( 'https://wiki.metacubex.one/config/proxy-providers/content/' , _ ( 'Contents' ) ) ) ;
so . placeholder = _ ( 'Content will not be verified, Please make sure you enter it correctly.' ) ;
so . load = function ( section _id ) {
2025-03-09 16:21:55 +08:00
return L . resolveDefault ( hm . readFile ( this . section . sectiontype , section _id ) , '' ) ;
2024-10-26 10:27:23 +08:00
}
2025-03-09 16:21:55 +08:00
so . write = L . bind ( hm . writeFile , so , so . section . sectiontype ) ;
so . remove = L . bind ( hm . writeFile , so , so . section . sectiontype ) ;
2024-10-26 10:27:23 +08:00
so . rmempty = false ;
so . retain = true ;
so . depends ( 'type' , 'file' ) ;
so . modalonly = true ;
2025-01-17 20:36:29 +08:00
so = ss . taboption ( 'field_general' , hm . TextValue , 'payload' , 'payload:' ,
2024-12-26 12:23:51 +08:00
_ ( 'Please type <a target="_blank" href="%s" rel="noreferrer noopener">%s</a>.' )
. format ( 'https://wiki.metacubex.one/config/proxy-providers/content/' , _ ( 'Payload' ) ) ) ;
so . placeholder = '- name: "ss1"\n type: ss\n server: server\n port: 443\n cipher: chacha20-ietf-poly1305\n password: "password"\n# ' + _ ( 'Content will not be verified, Please make sure you enter it correctly.' ) ;
so . rmempty = false ;
so . depends ( 'type' , 'inline' ) ;
so . modalonly = true ;
2024-10-26 10:27:23 +08:00
so = ss . taboption ( 'field_general' , form . Value , 'url' , _ ( 'Provider URL' ) ) ;
so . validate = L . bind ( hm . validateUrl , so ) ;
so . rmempty = false ;
so . depends ( 'type' , 'http' ) ;
so . modalonly = true ;
2024-12-21 14:15:01 +08:00
so = ss . taboption ( 'field_general' , form . Value , 'size_limit' , _ ( 'Size limit' ) ,
_ ( 'In bytes. <code>%s</code> will be used if empty.' ) . format ( '0' ) ) ;
so . placeholder = '0' ;
so . validate = L . bind ( hm . validateBytesize , so ) ;
so . depends ( 'type' , 'http' ) ;
2024-10-26 10:27:23 +08:00
so = ss . taboption ( 'field_general' , form . Value , 'interval' , _ ( 'Update interval' ) ,
_ ( 'In seconds. <code>%s</code> will be used if empty.' ) . format ( '86400' ) ) ;
so . placeholder = '86400' ;
so . validate = L . bind ( hm . validateTimeDuration , so ) ;
so . depends ( 'type' , 'http' ) ;
so = ss . taboption ( 'field_general' , form . ListValue , 'proxy' , _ ( 'Proxy group' ) ,
_ ( 'Name of the Proxy group to download provider.' ) ) ;
so . default = hm . preset _outbound . direct [ 0 ] [ 0 ] ;
hm . preset _outbound . direct . forEach ( ( res ) => {
so . value . apply ( so , res ) ;
} )
so . load = L . bind ( hm . loadProxyGroupLabel , so , hm . preset _outbound . direct ) ;
so . textvalue = L . bind ( hm . textvalue2Value , so ) ;
//so.editable = true;
so . depends ( 'type' , 'http' ) ;
2025-01-17 20:36:29 +08:00
so = ss . taboption ( 'field_general' , hm . TextValue , 'header' , _ ( 'HTTP header' ) ,
2024-10-26 10:27:23 +08:00
_ ( 'Custom HTTP header.' ) ) ;
so . placeholder = '{\n "User-Agent": [\n "Clash/v1.18.0",\n "mihomo/1.18.3"\n ],\n "Accept": [\n //"application/vnd.github.v3.raw"\n ],\n "Authorization": [\n //"token 1231231"\n ]\n}' ;
so . validate = L . bind ( hm . validateJson , so ) ;
so . depends ( 'type' , 'http' ) ;
so . modalonly = true ;
/* Override fields */
// https://github.com/muink/mihomo/blob/43f21c0b412b7a8701fe7a2ea6510c5b985a53d6/adapter/provider/parser.go#L30
so = ss . taboption ( 'field_override' , form . Value , 'override_prefix' , _ ( 'Add prefix' ) ) ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_override' , form . Value , 'override_suffix' , _ ( 'Add suffix' ) ) ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_override' , form . DynamicList , 'override_replace' , _ ( 'Replace name' ) ,
_ ( 'Replace node name. ' ) +
_ ( 'For format see <a target="_blank" href="%s" rel="noreferrer noopener">%s</a>.' )
. format ( 'https://wiki.metacubex.one/config/proxy-providers/#overrideproxy-name' , _ ( 'override.proxy-name' ) ) ) ;
so . placeholder = '{"pattern": "IPLC-(.*?)倍", "target": "iplc x $1"}' ;
so . validate = L . bind ( hm . validateJson , so ) ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_override' , form . DummyValue , '_config_items' , null ) ;
so . load = function ( ) {
return '<a target="_blank" href="%s" rel="noreferrer noopener">%s</a>'
. format ( 'https://wiki.metacubex.one/config/proxy-providers/#_2' , _ ( 'Configuration Items' ) ) ;
}
so . rawhtml = true ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_override' , form . Flag , 'override_tfo' , _ ( 'TFO' ) ) ;
so . default = so . disabled ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_override' , form . Flag , 'override_mptcp' , _ ( 'mpTCP' ) ) ;
so . default = so . disabled ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_override' , form . Flag , 'override_udp' , _ ( 'UDP' ) ) ;
so . default = so . enabled ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
2024-11-16 20:36:29 +08:00
so = ss . taboption ( 'field_override' , form . Flag , 'override_uot' , _ ( 'UoT' ) ,
_ ( 'Enable the SUoT protocol, requires server support. Conflict with Multiplex.' ) ) ;
2024-10-26 10:27:23 +08:00
so . default = so . disabled ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_override' , form . Value , 'override_up' , _ ( 'up' ) ,
_ ( 'In Mbps.' ) ) ;
so . datatype = 'uinteger' ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_override' , form . Value , 'override_down' , _ ( 'down' ) ,
_ ( 'In Mbps.' ) ) ;
so . datatype = 'uinteger' ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
2024-11-16 20:36:29 +08:00
so = ss . taboption ( 'field_override' , form . Flag , 'override_skip_cert_verify' , _ ( 'Skip cert verify' ) ,
_ ( 'Donot verifying server certificate.' ) +
'<br/>' +
_ ( 'This is <strong>DANGEROUS</strong>, your traffic is almost like <strong>PLAIN TEXT</strong>! Use at your own risk!' ) ) ;
2024-10-26 10:27:23 +08:00
so . default = so . disabled ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
2024-12-01 20:38:03 +08:00
/ * F e a t u r e s a r e i m p l e m e n t e d i n p r o x y c h a i n
2024-10-26 10:27:23 +08:00
so = ss . taboption ( 'field_override' , form . Value , 'override_dialer_proxy' , _ ( 'dialer-proxy' ) ) ;
so . readonly = true ;
so . modalonly = true ;
2024-12-01 20:38:03 +08:00
* /
2024-10-26 10:27:23 +08:00
so = ss . taboption ( 'field_override' , widgets . DeviceSelect , 'override_interface_name' , _ ( 'Bind interface' ) ,
_ ( 'Bind outbound interface.</br>' ) +
2025-02-12 00:27:05 +08:00
_ ( 'Priority: Proxy Node > Global.' ) ) ;
2024-10-26 10:27:23 +08:00
so . multiple = false ;
so . noaliases = true ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_override' , form . Value , 'override_routing_mark' , _ ( 'Routing mark' ) ,
2025-02-12 00:27:05 +08:00
_ ( 'Priority: Proxy Node > Global.' ) ) ;
2024-10-26 10:27:23 +08:00
so . datatype = 'uinteger' ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
2024-11-16 20:36:29 +08:00
so = ss . taboption ( 'field_override' , form . ListValue , 'override_ip_version' , _ ( 'IP version' ) ) ;
2024-10-26 10:27:23 +08:00
so . default = hm . ip _version [ 0 ] [ 0 ] ;
hm . ip _version . forEach ( ( res ) => {
so . value . apply ( so , res ) ;
} )
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
/* Health fields */
so = ss . taboption ( 'field_health' , form . Flag , 'health_enable' , _ ( 'Enable' ) ) ;
so . default = so . enabled ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_health' , form . Value , 'health_url' , _ ( 'Health check URL' ) ) ;
so . default = hm . health _checkurls [ 0 ] [ 0 ] ;
hm . health _checkurls . forEach ( ( res ) => {
so . value . apply ( so , res ) ;
} )
so . validate = L . bind ( hm . validateUrl , so ) ;
so . retain = true ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_health' , form . Value , 'health_interval' , _ ( 'Health check interval' ) ,
_ ( 'In seconds. <code>%s</code> will be used if empty.' ) . format ( '600' ) ) ;
so . placeholder = '600' ;
so . validate = L . bind ( hm . validateTimeDuration , so ) ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_health' , form . Value , 'health_timeout' , _ ( 'Health check timeout' ) ,
_ ( 'In millisecond. <code>%s</code> will be used if empty.' ) . format ( '5000' ) ) ;
so . datatype = 'uinteger' ;
so . placeholder = '5000' ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_health' , form . Flag , 'health_lazy' , _ ( 'Lazy' ) ,
_ ( 'No testing is performed when this provider node is not in use.' ) ) ;
so . default = so . enabled ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_health' , form . Value , 'health_expected_status' , _ ( 'Health check expected status' ) ,
_ ( 'Expected HTTP code. <code>204</code> will be used if empty. ' ) +
_ ( 'For format see <a target="_blank" href="%s" rel="noreferrer noopener">%s</a>.' )
. format ( 'https://wiki.metacubex.one/config/proxy-groups/#expected-status' , _ ( 'Expected status' ) ) ) ;
so . placeholder = '200/302/400-503' ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
/* General fields */
so = ss . taboption ( 'field_general' , form . DynamicList , 'filter' , _ ( 'Node filter' ) ,
_ ( 'Filter nodes that meet keywords or regexps.' ) ) ;
so . placeholder = '(?i)港|hk|hongkong|hong kong' ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . DynamicList , 'exclude_filter' , _ ( 'Node exclude filter' ) ,
_ ( 'Exclude nodes that meet keywords or regexps.' ) ) ;
so . default = '重置|到期|过期|剩余|套餐 海外用户|回国'
so . placeholder = 'xxx' ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . taboption ( 'field_general' , form . DynamicList , 'exclude_type' , _ ( 'Node exclude type' ) ,
_ ( 'Exclude matched node types.' ) ) ;
so . placeholder = 'ss|http' ;
2025-03-09 16:21:55 +08:00
so . depends ( { type : 'inline' , '!reverse' : true } ) ;
2024-10-26 10:27:23 +08:00
so . modalonly = true ;
so = ss . option ( form . DummyValue , '_update' ) ;
2025-01-17 20:36:29 +08:00
so . cfgvalue = L . bind ( hm . renderResDownload , so ) ;
2024-10-26 10:27:23 +08:00
so . editable = true ;
so . modalonly = false ;
/* Provider END */
2024-12-01 20:38:03 +08:00
/* Proxy chain START */
s . tab ( 'dialer_proxy' , _ ( 'Proxy chain' ) ) ;
/* Proxy chain */
2025-03-09 00:20:30 +08:00
o = s . taboption ( 'dialer_proxy' , form . SectionValue , '_dialer_proxy' , hm . GridSection , 'dialer_proxy' , null ) ;
2024-12-01 20:38:03 +08:00
ss = o . subsection ;
ss . addremove = true ;
ss . rowcolors = true ;
ss . sortable = true ;
ss . nodescriptions = true ;
2025-03-09 00:20:30 +08:00
ss . hm _modaltitle = [ _ ( 'Proxy chain' ) , _ ( 'Add a proxy chain' ) ] ;
2025-03-09 16:21:55 +08:00
ss . hm _prefmt = hm . glossary [ ss . sectiontype ] . prefmt ;
2025-03-10 04:19:32 +08:00
ss . hm _field = hm . glossary [ ss . sectiontype ] . field ;
2025-03-09 00:20:30 +08:00
ss . hm _lowcase _only = true ;
2024-12-01 20:38:03 +08:00
so = ss . option ( form . Value , 'label' , _ ( 'Label' ) ) ;
so . load = L . bind ( hm . loadDefaultLabel , so ) ;
so . validate = L . bind ( hm . validateUniqueValue , so ) ;
so . modalonly = true ;
so = ss . option ( form . Flag , 'enabled' , _ ( 'Enable' ) ) ;
so . default = so . enabled ;
so . editable = true ;
so = ss . option ( form . ListValue , 'type' , _ ( 'Type' ) ) ;
so . value ( 'node' , _ ( 'Proxy Node' ) ) ;
so . value ( 'provider' , _ ( 'Provider' ) ) ;
so . default = 'node' ;
so . textvalue = L . bind ( hm . textvalue2Value , so ) ;
so = ss . option ( form . DummyValue , '_value' , _ ( 'Value' ) ) ;
so . load = function ( section _id ) {
2025-01-15 16:25:40 +08:00
const type = uci . get ( data [ 0 ] , section _id , 'type' ) ;
const detour = uci . get ( data [ 0 ] , section _id , 'chain_tail_group' ) || uci . get ( data [ 0 ] , section _id , 'chain_tail' ) ;
2024-12-01 20:38:03 +08:00
switch ( type ) {
case 'node' :
return '%s » %s' . format (
uci . get ( data [ 0 ] , section _id , 'chain_head' ) ,
detour
) ;
case 'provider' :
return '%s » %s' . format (
uci . get ( data [ 0 ] , section _id , 'chain_head_sub' ) ,
detour
) ;
default :
return null ;
}
}
so . modalonly = false ;
so = ss . option ( form . ListValue , 'chain_head_sub' , _ ( 'Chain head' ) ) ;
2025-01-16 00:24:18 +08:00
so . load = L . bind ( hm . loadProviderLabel , so , [ [ '' , _ ( '-- Please choose --' ) ] ] ) ;
2024-12-01 20:38:03 +08:00
so . rmempty = false ;
so . depends ( 'type' , 'provider' ) ;
so . modalonly = true ;
so = ss . option ( form . ListValue , 'chain_head' , _ ( 'Chain head' ) ,
_ ( 'Recommended to use UoT node.</br>such as <code>%s</code>.' )
. format ( 'ss|ssr|vmess|vless|trojan|tuic' ) ) ;
2025-01-16 00:24:18 +08:00
so . load = L . bind ( hm . loadNodeLabel , so , [ [ '' , _ ( '-- Please choose --' ) ] ] ) ;
2024-12-01 20:38:03 +08:00
so . rmempty = false ;
so . validate = function ( section _id , value ) {
2025-01-15 16:25:40 +08:00
const chain _tail = this . section . getUIElement ( section _id , 'chain_tail' ) . getValue ( ) ;
2024-12-01 20:38:03 +08:00
if ( value === chain _tail )
return _ ( 'Expecting: %s' ) . format ( _ ( 'Different chain head/tail' ) ) ;
return true ;
}
so . depends ( 'type' , 'node' ) ;
so . modalonly = true ;
so = ss . option ( form . ListValue , 'chain_tail_group' , _ ( 'Chain tail' ) ) ;
so . load = L . bind ( hm . loadProxyGroupLabel , so , [ [ '' , _ ( '-- Please choose --' ) ] ] ) ;
so . rmempty = false ;
so . depends ( { chain _tail : /.+/ , '!reverse' : true } ) ;
so . modalonly = true ;
so = ss . option ( form . ListValue , 'chain_tail' , _ ( 'Chain tail' ) ,
_ ( 'Recommended to use UoT node.</br>such as <code>%s</code>.' )
. format ( 'ss|ssr|vmess|vless|trojan|tuic' ) ) ;
2025-01-16 00:24:18 +08:00
so . load = L . bind ( hm . loadNodeLabel , so , [ [ '' , _ ( '-- Please choose --' ) ] ] ) ;
2024-12-01 20:38:03 +08:00
so . rmempty = false ;
so . validate = function ( section _id , value ) {
2025-01-15 16:25:40 +08:00
const chain _head = this . section . getUIElement ( section _id , 'chain_head' ) . getValue ( ) ;
2024-12-01 20:38:03 +08:00
if ( value === chain _head )
return _ ( 'Expecting: %s' ) . format ( _ ( 'Different chain head/tail' ) ) ;
return true ;
}
so . depends ( { chain _tail _group : /.+/ , '!reverse' : true } ) ;
so . modalonly = true ;
/* Proxy chain END */
2024-10-26 10:27:23 +08:00
return m . render ( ) ;
}
} ) ;