2023-10-12 17:38:51 +08:00
/ *
* SPDX - License - Identifier : GPL - 2.0 - only
*
* Copyright ( C ) 2022 - 2023 ImmortalWrt . org
* /
'use strict' ;
'require form' ;
'require poll' ;
'require rpc' ;
'require uci' ;
'require view' ;
'require homeproxy as hp' ;
var callServiceList = rpc . declare ( {
object : 'service' ,
method : 'list' ,
params : [ 'name' ] ,
expect : { '' : { } }
} ) ;
function getServiceStatus ( ) {
return L . resolveDefault ( callServiceList ( 'homeproxy' ) , { } ) . then ( ( res ) => {
var isRunning = false ;
try {
isRunning = res [ 'homeproxy' ] [ 'instances' ] [ 'sing-box-s' ] [ 'running' ] ;
} catch ( e ) { }
return isRunning ;
} ) ;
}
function renderStatus ( isRunning ) {
var spanTemp = '<em><span style="color:%s"><strong>%s %s</strong></span></em>' ;
var renderHTML ;
if ( isRunning )
renderHTML = spanTemp . format ( 'green' , _ ( 'HomeProxy Server' ) , _ ( 'RUNNING' ) ) ;
else
renderHTML = spanTemp . format ( 'red' , _ ( 'HomeProxy Server' ) , _ ( 'NOT RUNNING' ) ) ;
return renderHTML ;
}
return view . extend ( {
load : function ( ) {
return Promise . all ( [
uci . load ( 'homeproxy' ) ,
hp . getBuiltinFeatures ( )
] ) ;
} ,
render : function ( data ) {
var m , s , o ;
var features = data [ 1 ] ;
m = new form . Map ( 'homeproxy' , _ ( 'HomeProxy Server' ) ,
_ ( 'The modern ImmortalWrt proxy platform for ARM64/AMD64.' ) ) ;
s = m . section ( form . TypedSection ) ;
s . render = function ( ) {
poll . add ( function ( ) {
return L . resolveDefault ( getServiceStatus ( ) ) . then ( ( res ) => {
var view = document . getElementById ( 'service_status' ) ;
view . innerHTML = renderStatus ( res ) ;
} ) ;
} ) ;
return E ( 'div' , { class : 'cbi-section' , id : 'status_bar' } , [
E ( 'p' , { id : 'service_status' } , _ ( 'Collecting data...' ) )
] ) ;
}
s = m . section ( form . NamedSection , 'server' , 'homeproxy' , _ ( 'Global settings' ) ) ;
o = s . option ( form . Flag , 'enabled' , _ ( 'Enable' ) ) ;
o . default = o . disabled ;
o . rmempty = false ;
o = s . option ( form . Flag , 'auto_firewall' , _ ( 'Auto configure firewall' ) ) ;
o . default = o . disabled ;
o . rmempty = false ;
s = m . section ( form . GridSection , 'server' , _ ( 'Server settings' ) ) ;
s . addremove = true ;
s . rowcolors = true ;
s . sortable = true ;
s . nodescriptions = true ;
s . modaltitle = L . bind ( hp . loadModalTitle , this , _ ( 'Server' ) , _ ( 'Add a server' ) , data [ 0 ] ) ;
s . sectiontitle = L . bind ( hp . loadDefaultLabel , this , data [ 0 ] ) ;
s . renderSectionAdd = L . bind ( hp . renderSectionAdd , this , s ) ;
o = s . option ( form . Value , 'label' , _ ( 'Label' ) ) ;
o . load = L . bind ( hp . loadDefaultLabel , this , data [ 0 ] ) ;
o . validate = L . bind ( hp . validateUniqueValue , this , data [ 0 ] , 'server' , 'label' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Flag , 'enabled' , _ ( 'Enable' ) ) ;
o . default = o . enabled ;
o . rmempty = false ;
o . editable = true ;
o = s . option ( form . ListValue , 'type' , _ ( 'Type' ) ) ;
o . value ( 'http' , _ ( 'HTTP' ) ) ;
if ( features . with _quic ) {
o . value ( 'hysteria' , _ ( 'Hysteria' ) ) ;
o . value ( 'hysteria2' , _ ( 'Hysteria2' ) ) ;
o . value ( 'naive' , _ ( 'NaïveProxy' ) ) ;
}
o . value ( 'shadowsocks' , _ ( 'Shadowsocks' ) ) ;
o . value ( 'socks' , _ ( 'Socks' ) ) ;
o . value ( 'trojan' , _ ( 'Trojan' ) ) ;
if ( features . with _quic )
o . value ( 'tuic' , _ ( 'Tuic' ) ) ;
o . value ( 'vless' , _ ( 'VLESS' ) ) ;
o . value ( 'vmess' , _ ( 'VMess' ) ) ;
o . rmempty = false ;
2023-10-23 21:52:05 +08:00
o = s . option ( form . Value , 'address' , _ ( 'Listen address' ) ) ;
o . placeholder = '::' ;
o . datatype = 'ipaddr' ;
o . modalonly = true ;
o = s . option ( form . Value , 'port' , _ ( 'Listen port' ) ,
2023-10-12 17:38:51 +08:00
_ ( 'The port must be unique.' ) ) ;
o . datatype = 'port' ;
o . validate = L . bind ( hp . validateUniqueValue , this , data [ 0 ] , 'server' , 'port' ) ;
o = s . option ( form . Value , 'username' , _ ( 'Username' ) ) ;
o . depends ( 'type' , 'http' ) ;
o . depends ( 'type' , 'naive' ) ;
o . depends ( 'type' , 'socks' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'password' , _ ( 'Password' ) ) ;
o . password = true ;
o . depends ( { 'type' : /^(http|naive|socks)$/ , 'username' : /[\s\S]/ } ) ;
o . depends ( 'type' , 'hysteria2' ) ;
o . depends ( 'type' , 'shadowsocks' ) ;
o . depends ( 'type' , 'trojan' ) ;
o . depends ( 'type' , 'tuic' ) ;
o . validate = function ( section _id , value ) {
if ( section _id ) {
var type = this . map . lookupOption ( 'type' , section _id ) [ 0 ] . formvalue ( section _id ) ;
var required _type = [ 'http' , 'naive' , 'socks' , 'shadowsocks' ] ;
if ( required _type . includes ( type ) ) {
if ( type === 'shadowsocks' ) {
var encmode = this . map . lookupOption ( 'shadowsocks_encrypt_method' , section _id ) [ 0 ] . formvalue ( section _id ) ;
if ( encmode === 'none' )
return true ;
else if ( encmode === '2022-blake3-aes-128-gcm' )
return hp . validateBase64Key ( 24 , section _id , value ) ;
else if ( [ '2022-blake3-aes-256-gcm' , '2022-blake3-chacha20-poly1305' ] . includes ( encmode ) )
return hp . validateBase64Key ( 44 , section _id , value ) ;
}
if ( ! value )
return _ ( 'Expecting: %s' ) . format ( _ ( 'non-empty value' ) ) ;
}
}
return true ;
}
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 ;
o . modalonly = true ;
o = s . option ( form . Value , 'hysteria_down_mbps' , _ ( 'Max download speed' ) ,
_ ( 'Max download speed in Mbps.' ) ) ;
o . datatype = 'uinteger' ;
o . depends ( 'type' , 'hysteria' ) ;
o . depends ( 'type' , 'hysteria2' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'hysteria_up_mbps' , _ ( 'Max upload speed' ) ,
_ ( 'Max upload speed in Mbps.' ) ) ;
o . datatype = 'uinteger' ;
o . depends ( 'type' , 'hysteria' ) ;
o . depends ( 'type' , 'hysteria2' ) ;
o . modalonly = true ;
o = s . option ( form . ListValue , 'hysteria_auth_type' , _ ( 'Authentication type' ) ) ;
o . value ( '' , _ ( 'Disable' ) ) ;
o . value ( 'base64' , _ ( 'Base64' ) ) ;
o . value ( 'string' , _ ( 'String' ) ) ;
o . depends ( 'type' , 'hysteria' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'hysteria_auth_payload' , _ ( 'Authentication payload' ) ) ;
o . depends ( { 'type' : 'hysteria' , 'hysteria_auth_type' : /[\s\S]/ } ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . ListValue , 'hysteria_obfs_type' , _ ( 'Obfuscate type' ) ) ;
o . value ( '' , _ ( 'Disable' ) ) ;
o . value ( 'salamander' , _ ( 'Salamander' ) ) ;
o . depends ( 'type' , 'hysteria2' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'hysteria_obfs_password' , _ ( 'Obfuscate password' ) ) ;
o . depends ( 'type' , 'hysteria' ) ;
o . depends ( { 'type' : 'hysteria2' , 'hysteria_obfs_type' : /[\s\S]/ } ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'hysteria_recv_window_conn' , _ ( 'QUIC stream receive window' ) ,
_ ( 'The QUIC stream-level flow control window for receiving data.' ) ) ;
o . datatype = 'uinteger' ;
o . default = '67108864' ;
o . depends ( 'type' , 'hysteria' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'hysteria_recv_window_client' , _ ( 'QUIC connection receive window' ) ,
_ ( 'The QUIC connection-level flow control window for receiving data.' ) ) ;
o . datatype = 'uinteger' ;
o . default = '15728640' ;
o . depends ( 'type' , 'hysteria' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'hysteria_max_conn_client' , _ ( 'QUIC maximum concurrent bidirectional streams' ) ,
_ ( 'The maximum number of QUIC concurrent bidirectional streams that a peer is allowed to open.' ) ) ;
o . datatype = 'uinteger' ;
o . default = '1024' ;
o . depends ( 'type' , 'hysteria' ) ;
o . modalonly = true ;
o = s . option ( form . Flag , 'hysteria_disable_mtu_discovery' , _ ( 'Disable Path MTU discovery' ) ,
_ ( 'Disables Path MTU Discovery (RFC 8899). Packets will then be at most 1252 (IPv4) / 1232 (IPv6) bytes in size.' ) ) ;
o . default = o . disabled ;
o . depends ( 'type' , 'hysteria' ) ;
o . modalonly = true ;
o = s . option ( form . Flag , 'hysteria_ignore_client_bandwidth' , _ ( 'Ignore client bandwidth' ) ,
_ ( 'Tell the client to use the BBR flow control algorithm instead of Hysteria CC.' ) ) ;
o . default = o . disabled ;
o . depends ( { 'type' : 'hysteria2' , 'hysteria_down_mbps' : '' , 'hysteria_up_mbps' : '' } ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'hysteria_masquerade' , _ ( 'Masquerade' ) ,
_ ( '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 */
/* Shadowsocks config */
o = s . option ( form . ListValue , 'shadowsocks_encrypt_method' , _ ( 'Encrypt method' ) ) ;
for ( var i of hp . shadowsocks _encrypt _methods )
o . value ( i ) ;
o . default = 'aes-128-gcm' ;
o . depends ( 'type' , 'shadowsocks' ) ;
o . modalonly = true ;
/* Tuic config start */
o = s . option ( form . Value , 'uuid' , _ ( 'UUID' ) ) ;
o . depends ( 'type' , 'tuic' ) ;
o . depends ( 'type' , 'vless' ) ;
o . depends ( 'type' , 'vmess' ) ;
o . validate = hp . validateUUID ;
o . modalonly = true ;
o = s . option ( form . ListValue , 'tuic_congestion_control' , _ ( 'Congestion control algorithm' ) ,
_ ( 'QUIC congestion control algorithm.' ) ) ;
o . value ( 'cubic' ) ;
o . value ( 'new_reno' ) ;
o . value ( 'bbr' ) ;
o . default = 'cubic' ;
o . depends ( 'type' , 'tuic' ) ;
o . modalonly = true ;
o = s . option ( form . ListValue , 'tuic_auth_timeout' , _ ( 'Auth timeout' ) ,
_ ( 'How long the server should wait for the client to send the authentication command (in seconds).' ) ) ;
o . datatype = 'uinteger' ;
o . default = '3' ;
o . depends ( 'type' , 'tuic' ) ;
o . modalonly = true ;
o = s . option ( form . Flag , 'tuic_enable_zero_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.' ) ) ;
o . default = o . disabled ;
o . depends ( 'type' , 'tuic' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'tuic_heartbeat' , _ ( 'Heartbeat interval' ) ,
_ ( 'Interval for sending heartbeat packets for keeping the connection alive (in seconds).' ) ) ;
o . datatype = 'uinteger' ;
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' ) ;
o . depends ( 'type' , 'vless' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'vmess_alterid' , _ ( 'Alter ID' ) ,
_ ( 'Legacy protocol support (VMess MD5 Authentication) is provided for compatibility purposes only, use of alterId > 1 is not recommended.' ) ) ;
o . datatype = 'uinteger' ;
o . depends ( 'type' , 'vmess' ) ;
o . modalonly = true ;
/* VMess config end */
/* Transport config start */
o = s . option ( form . ListValue , 'transport' , _ ( 'Transport' ) ,
_ ( 'No TCP transport, plain HTTP is merged into the HTTP transport.' ) ) ;
o . value ( '' , _ ( 'None' ) ) ;
o . value ( 'grpc' , _ ( 'gRPC' ) ) ;
o . value ( 'http' , _ ( 'HTTP' ) ) ;
o . value ( 'quic' , _ ( 'QUIC' ) ) ;
o . value ( 'ws' , _ ( 'WebSocket' ) ) ;
o . onchange = function ( ev , section _id , value ) {
var desc = this . map . findElement ( 'id' , 'cbid.homeproxy.%s.transport' . format ( section _id ) ) . nextElementSibling ;
if ( value === 'http' )
desc . innerHTML = _ ( 'TLS is not enforced. If TLS is not configured, plain HTTP 1.1 is used.' ) ;
else if ( value === 'quic' )
desc . innerHTML = _ ( 'No additional encryption support: It\'s basically duplicate encryption.' ) ;
else
desc . innerHTML = _ ( 'No TCP transport, plain HTTP is merged into the HTTP transport.' ) ;
var tls _element = this . map . findElement ( 'id' , 'cbid.homeproxy.%s.tls' . format ( section _id ) ) . firstElementChild ;
if ( ( value === 'http' && tls _element . checked ) || ( value === 'grpc' && ! features . with _grpc ) )
this . map . findElement ( 'id' , 'cbid.homeproxy.%s.http_idle_timeout' . format ( section _id ) ) . nextElementSibling . innerHTML =
_ ( 'Specifies the time (in seconds) until idle clients should be closed with a GOAWAY frame. PING frames are not considered as activity.' ) ;
else if ( value === 'grpc' && features . with _grpc )
this . map . findElement ( 'id' , 'cbid.homeproxy.%s.http_idle_timeout' . format ( section _id ) ) . nextElementSibling . innerHTML =
_ ( 'If the transport doesn\'t see any activity after a duration of this time (in seconds), it pings the client to check if the connection is still active.' ) ;
}
o . depends ( 'type' , 'trojan' ) ;
o . depends ( 'type' , 'vless' ) ;
o . depends ( 'type' , 'vmess' ) ;
o . modalonly = true ;
/* gRPC config start */
o = s . option ( form . Value , 'grpc_servicename' , _ ( 'gRPC service name' ) ) ;
o . depends ( 'transport' , 'grpc' ) ;
o . modalonly = true ;
/* gRPC config end */
/* HTTP config start */
o = s . option ( form . DynamicList , 'http_host' , _ ( 'Host' ) ) ;
o . datatype = 'hostname' ;
o . depends ( 'transport' , 'http' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'http_path' , _ ( 'Path' ) ) ;
o . depends ( 'transport' , 'http' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'http_method' , _ ( 'Method' ) ) ;
o . depends ( 'transport' , 'http' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'http_idle_timeout' , _ ( 'Idle timeout' ) ,
_ ( 'Specifies the time (in seconds) until idle clients should be closed with a GOAWAY frame. PING frames are not considered as activity.' ) ) ;
o . datatype = 'uinteger' ;
o . depends ( 'transport' , 'grpc' ) ;
o . depends ( { 'transport' : 'http' , 'tls' : '1' } ) ;
o . modalonly = true ;
if ( features . with _grpc ) {
o = s . option ( form . Value , 'http_ping_timeout' , _ ( 'Ping timeout' ) ,
_ ( 'The timeout (in seconds) that after performing a keepalive check, the client will wait for activity. If no activity is detected, the connection will be closed.' ) ) ;
o . datatype = 'uinteger' ;
o . depends ( 'transport' , 'grpc' ) ;
o . modalonly = true ;
}
/* HTTP config end */
/* WebSocket config start */
o = s . option ( form . Value , 'ws_host' , _ ( 'Host' ) ) ;
o . depends ( 'transport' , 'ws' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'ws_path' , _ ( 'Path' ) ) ;
o . depends ( 'transport' , 'ws' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'websocket_early_data' , _ ( 'Early data' ) ,
_ ( 'Allowed payload size is in the request.' ) ) ;
o . datatype = 'uinteger' ;
o . value ( '2048' ) ;
o . depends ( 'transport' , 'ws' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'websocket_early_data_header' , _ ( 'Early data header name' ) ,
_ ( 'Early data is sent in path instead of header by default.' ) +
'<br/>' +
_ ( 'To be compatible with Xray-core, set this to <code>Sec-WebSocket-Protocol</code>.' ) ) ;
o . value ( 'Sec-WebSocket-Protocol' ) ;
o . depends ( 'transport' , 'ws' ) ;
o . modalonly = true ;
/* WebSocket config end */
/* Transport config end */
/* TLS config start */
o = s . option ( form . Flag , 'tls' , _ ( 'TLS' ) ) ;
o . default = o . disabled ;
o . depends ( 'type' , 'http' ) ;
o . depends ( 'type' , 'hysteria' ) ;
o . depends ( 'type' , 'hysteria2' ) ;
o . depends ( 'type' , 'naive' ) ;
o . depends ( 'type' , 'trojan' ) ;
o . depends ( 'type' , 'vless' ) ;
o . depends ( 'type' , 'vmess' ) ;
o . rmempty = false ;
o . validate = function ( section _id , value ) {
if ( section _id ) {
var type = this . map . lookupOption ( 'type' , section _id ) [ 0 ] . formvalue ( section _id ) ;
var tls = this . map . findElement ( 'id' , 'cbid.homeproxy.%s.tls' . format ( section _id ) ) . firstElementChild ;
if ( [ 'hysteria' , 'hysteria2' , 'tuic' ] . includes ( type ) ) {
tls . checked = true ;
tls . disabled = true ;
} else {
tls . disabled = null ;
}
}
return true ;
}
o . modalonly = true ;
o = s . option ( form . Value , 'tls_sni' , _ ( 'TLS SNI' ) ,
_ ( 'Used to verify the hostname on the returned certificates unless insecure is given.' ) ) ;
o . depends ( { 'tls' : '1' , 'tls_reality' : '0' } ) ;
o . depends ( { 'tls' : '1' , 'tls_reality' : null } ) ;
o . modalonly = true ;
o = s . option ( form . DynamicList , 'tls_alpn' , _ ( 'TLS ALPN' ) ,
_ ( 'List of supported application level protocols, in order of preference.' ) ) ;
o . depends ( 'tls' , '1' ) ;
o . modalonly = true ;
o = s . option ( form . ListValue , 'tls_min_version' , _ ( 'Minimum TLS version' ) ,
_ ( 'The minimum TLS version that is acceptable.' ) ) ;
o . value ( '' , _ ( 'default' ) ) ;
for ( var i of hp . tls _versions )
o . value ( i ) ;
o . depends ( 'tls' , '1' ) ;
o . modalonly = true ;
o = s . option ( form . ListValue , 'tls_max_version' , _ ( 'Maximum TLS version' ) ,
_ ( 'The maximum TLS version that is acceptable.' ) ) ;
o . value ( '' , _ ( 'default' ) ) ;
for ( var i of hp . tls _versions )
o . value ( i ) ;
o . depends ( 'tls' , '1' ) ;
o . modalonly = true ;
o = s . option ( form . MultiValue , 'tls_cipher_suites' , _ ( 'Cipher suites' ) ,
_ ( 'The elliptic curves that will be used in an ECDHE handshake, in preference order. If empty, the default will be used.' ) ) ;
for ( var i of hp . tls _cipher _suites )
o . value ( i ) ;
o . depends ( 'tls' , '1' ) ;
o . optional = true ;
o . modalonly = true ;
if ( features . with _acme ) {
o = s . option ( form . Flag , 'tls_acme' , _ ( 'Enable ACME' ) ,
_ ( 'Use ACME TLS certificate issuer.' ) ) ;
o . default = o . disabled ;
o . depends ( 'tls' , '1' ) ;
o . modalonly = true ;
o = s . option ( form . DynamicList , 'tls_acme_domain' , _ ( 'Domains' ) ) ;
o . datatype = 'hostname' ;
o . depends ( 'tls_acme' , '1' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_acme_dsn' , _ ( 'Default server name' ) ,
_ ( 'Server name to use when choosing a certificate if the ClientHello\'s ServerName field is empty.' ) ) ;
o . depends ( 'tls_acme' , '1' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_acme_email' , _ ( 'Email' ) ,
_ ( 'The email address to use when creating or selecting an existing ACME server account.' ) ) ;
o . depends ( 'tls_acme' , '1' ) ;
o . validate = function ( section _id , value ) {
if ( section _id ) {
if ( ! value )
return _ ( 'Expecting: %s' ) . format ( 'non-empty value' ) ;
else if ( ! value . match ( /^[^\s@]+@[^\s@]+\.[^\s@]+$/ ) )
return _ ( 'Expecting: %s' ) . format ( 'valid email address' ) ;
}
return true ;
}
o . modalonly = true ;
o = s . option ( form . Value , 'tls_acme_provider' , _ ( 'CA provider' ) ,
_ ( 'The ACME CA provider to use.' ) ) ;
o . value ( 'letsencrypt' , _ ( 'Let\'s Encrypt' ) ) ;
o . value ( 'zerossl' , _ ( 'ZeroSSL' ) ) ;
o . depends ( 'tls_acme' , '1' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Flag , 'tls_dns01_challenge' , _ ( 'DNS01 challenge' ) )
o . default = o . disabled ;
o . depends ( 'tls_acme' , '1' ) ;
o . modalonly = true ;
o = s . option ( form . ListValue , 'tls_dns01_provider' , _ ( 'DNS provider' ) ) ;
o . value ( 'alidns' , _ ( 'Alibaba Cloud DNS' ) ) ;
o . value ( 'cloudflare' , _ ( 'Cloudflare' ) ) ;
o . depends ( 'tls_dns01_challenge' , '1' ) ;
o . default = 'cloudflare' ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_dns01_ali_akid' , _ ( 'Access key ID' ) ) ;
o . depends ( 'tls_dns01_provider' , 'alidns' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_dns01_ali_aksec' , _ ( 'Access key secret' ) ) ;
o . depends ( 'tls_dns01_provider' , 'alidns' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_dns01_ali_rid' , _ ( 'Region ID' ) ) ;
o . depends ( 'tls_dns01_provider' , 'alidns' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_dns01_cf_api_token' , _ ( 'API token' ) ) ;
o . depends ( 'tls_dns01_provider' , 'cloudflare' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Flag , 'tls_acme_dhc' , _ ( 'Disable HTTP challenge' ) ) ;
o . default = o . disabled ;
o . depends ( 'tls_dns01_challenge' , '0' ) ;
o . modalonly = true ;
o = s . option ( form . Flag , 'tls_acme_dtac' , _ ( 'Disable TLS ALPN challenge' ) ) ;
o . default = o . disabled ;
o . depends ( 'tls_dns01_challenge' , '0' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_acme_ahp' , _ ( 'Alternative HTTP port' ) ,
_ ( 'The alternate port to use for the ACME HTTP challenge; if non-empty, this port will be used instead of 80 to spin up a listener for the HTTP challenge.' ) ) ;
o . datatype = 'port' ;
o . depends ( 'tls_dns01_challenge' , '0' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_acme_atp' , _ ( 'Alternative TLS port' ) ,
_ ( 'The alternate port to use for the ACME TLS-ALPN challenge; the system must forward 443 to this port for challenge to succeed.' ) ) ;
o . datatype = 'port' ;
o . depends ( 'tls_dns01_challenge' , '0' ) ;
o . modalonly = true ;
o = s . option ( form . Flag , 'tls_acme_external_account' , _ ( 'External Account Binding' ) ,
_ ( 'EAB (External Account Binding) contains information necessary to bind or map an ACME account to some other account known by the CA.' +
'<br/>External account bindings are "used to associate an ACME account with an existing account in a non-ACME system, such as a CA customer database.' ) ) ;
o . default = o . disabled ;
o . depends ( 'tls_acme' , '1' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_acme_ea_keyid' , _ ( 'External account key ID' ) ) ;
o . depends ( 'tls_acme_external_account' , '1' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_acme_ea_mackey' , _ ( 'External account MAC key' ) ) ;
o . depends ( 'tls_acme_external_account' , '1' ) ;
o . rmempty = false ;
o . modalonly = true ;
}
if ( features . with _reality _server ) {
o = s . option ( form . Flag , 'tls_reality' , _ ( 'REALITY' ) ) ;
o . default = o . disabled ;
o . depends ( { 'tls' : '1' , 'tls_acme' : '0' , 'type' : 'vless' } ) ;
o . depends ( { 'tls' : '1' , 'tls_acme' : null , 'type' : 'vless' } ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_reality_private_key' , _ ( 'REALITY private key' ) ) ;
o . depends ( 'tls_reality' , '1' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . DynamicList , 'tls_reality_short_id' , _ ( 'REALITY short ID' ) ) ;
o . depends ( 'tls_reality' , '1' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_reality_max_time_difference' , _ ( 'Max time difference' ) ,
_ ( 'The maximum time difference between the server and the client.' ) ) ;
o . depends ( 'tls_reality' , '1' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_reality_server_addr' , _ ( 'Handshake server address' ) ) ;
o . datatype = 'hostname' ;
o . depends ( 'tls_reality' , '1' ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_reality_server_port' , _ ( 'Handshake server port' ) ) ;
o . datatype = 'port' ;
o . depends ( 'tls_reality' , '1' ) ;
o . rmempty = false ;
o . modalonly = true ;
}
o = s . option ( form . Value , 'tls_cert_path' , _ ( 'Certificate path' ) ,
_ ( 'The server public key, in PEM format.' ) ) ;
o . value ( '/etc/homeproxy/certs/server_publickey.pem' ) ;
o . depends ( { 'tls' : '1' , 'tls_acme' : '0' , 'tls_reality' : null } ) ;
o . depends ( { 'tls' : '1' , 'tls_acme' : '0' , 'tls_reality' : '0' } ) ;
o . depends ( { 'tls' : '1' , 'tls_acme' : null , 'tls_reality' : '0' } ) ;
o . depends ( { 'tls' : '1' , 'tls_acme' : null , 'tls_reality' : null } ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Button , '_upload_cert' , _ ( 'Upload certificate' ) ,
_ ( '<strong>Save your configuration before uploading files!</strong>' ) ) ;
o . inputstyle = 'action' ;
o . inputtitle = _ ( 'Upload...' ) ;
o . depends ( { 'tls' : '1' , 'tls_cert_path' : '/etc/homeproxy/certs/server_publickey.pem' } ) ;
o . onclick = L . bind ( hp . uploadCertificate , this , _ ( 'certificate' ) , 'server_publickey' ) ;
o . modalonly = true ;
o = s . option ( form . Value , 'tls_key_path' , _ ( 'Key path' ) ,
_ ( 'The server private key, in PEM format.' ) ) ;
o . value ( '/etc/homeproxy/certs/server_privatekey.pem' ) ;
o . depends ( { 'tls' : '1' , 'tls_acme' : '0' , 'tls_reality' : '0' } ) ;
o . depends ( { 'tls' : '1' , 'tls_acme' : '0' , 'tls_reality' : null } ) ;
o . depends ( { 'tls' : '1' , 'tls_acme' : null , 'tls_reality' : '0' } ) ;
o . depends ( { 'tls' : '1' , 'tls_acme' : null , 'tls_reality' : null } ) ;
o . rmempty = false ;
o . modalonly = true ;
o = s . option ( form . Button , '_upload_key' , _ ( 'Upload key' ) ,
_ ( '<strong>Save your configuration before uploading files!</strong>' ) ) ;
o . inputstyle = 'action' ;
o . inputtitle = _ ( 'Upload...' ) ;
o . depends ( { 'tls' : '1' , 'tls_key_path' : '/etc/homeproxy/certs/server_privatekey.pem' } ) ;
o . onclick = L . bind ( hp . uploadCertificate , this , _ ( 'private key' ) , 'server_privatekey' ) ;
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 . default = o . disabled ;
o . depends ( { 'network' : 'udp' , '!reverse' : true } ) ;
o . modalonly = true ;
if ( features . has _mptcp ) {
o = s . option ( form . Flag , 'tcp_multi_path' , _ ( 'MultiPath TCP' ) ) ;
o . default = o . disabled ;
o . depends ( { 'network' : 'udp' , '!reverse' : true } ) ;
o . modalonly = true ;
}
o = s . option ( form . Flag , 'udp_fragment' , _ ( 'UDP Fragment' ) ,
_ ( 'Enable UDP fragmentation.' ) ) ;
o . default = o . disabled ;
o . depends ( { 'network' : 'tcp' , '!reverse' : true } ) ;
o . modalonly = true ;
o = s . option ( form . Flag , 'sniff_override' , _ ( 'Override destination' ) ,
_ ( 'Override the connection destination address with the sniffed domain.' ) ) ;
o . rmempty = false ;
o = s . option ( form . ListValue , 'domain_strategy' , _ ( 'Domain strategy' ) ,
_ ( 'If set, the requested domain name will be resolved to IP before routing.' ) ) ;
for ( var i in hp . dns _strategy )
o . value ( i , hp . dns _strategy [ i ] )
o . modalonly = true ;
o = s . option ( form . ListValue , 'network' , _ ( 'Network' ) ) ;
o . value ( 'tcp' , _ ( 'TCP' ) ) ;
o . value ( 'udp' , _ ( 'UDP' ) ) ;
o . value ( '' , _ ( 'Both' ) ) ;
o . depends ( 'type' , 'naive' ) ;
o . depends ( 'type' , 'shadowsocks' ) ;
o . modalonly = true ;
/* Extra settings end */
return m . render ( ) ;
}
} ) ;