mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-04 11:03:56 +08:00
254 lines
9.1 KiB
JavaScript
254 lines
9.1 KiB
JavaScript
/*
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*
|
|
* Copyright (C) 2022-2025 ImmortalWrt.org
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
'require baseclass';
|
|
'require form';
|
|
'require uci';
|
|
|
|
'require homeproxy-pro as hp';
|
|
'require view.homeproxy-pro.client.common as common';
|
|
|
|
/* Routing tab and the wrapping custom-routing NamedSection. The
|
|
* routing_node tab lives in nodes.js; the routing_rule sub-section is
|
|
* shared with dns and lives in common.js as renderRuleSection. */
|
|
function render(ctx) {
|
|
const { s, proxy_nodes, features, stubValidator } = ctx;
|
|
let o, ss, so;
|
|
|
|
s.tab('routing', _('Routing Settings'));
|
|
|
|
o = s.taboption('routing', form.ListValue, 'main_node', _('Main node'));
|
|
o.value('nil', _('Disable'));
|
|
o.value('urltest', _('URLTest'));
|
|
for (let i in proxy_nodes)
|
|
o.value(i, proxy_nodes[i]);
|
|
o.default = 'nil';
|
|
o.depends({'routing_mode': 'custom', '!reverse': true});
|
|
o.rmempty = false;
|
|
|
|
o = s.taboption('routing', hp.CBIStaticList, 'main_urltest_nodes', _('URLTest nodes'),
|
|
_('List of nodes to test.'));
|
|
for (let i in proxy_nodes)
|
|
o.value(i, proxy_nodes[i]);
|
|
o.depends('main_node', 'urltest');
|
|
o.rmempty = false;
|
|
|
|
o = s.taboption('routing', form.Value, 'main_urltest_interval', _('Test interval'),
|
|
_('The test interval in seconds.'));
|
|
o.datatype = 'uinteger';
|
|
o.placeholder = '180';
|
|
o.depends('main_node', 'urltest');
|
|
|
|
o = s.taboption('routing', form.Value, 'main_urltest_tolerance', _('Test tolerance'),
|
|
_('The test tolerance in milliseconds.'));
|
|
o.datatype = 'uinteger';
|
|
o.placeholder = '50';
|
|
o.depends('main_node', 'urltest');
|
|
|
|
o = s.taboption('routing', form.ListValue, 'main_udp_node', _('Main UDP node'));
|
|
o.value('nil', _('Disable'));
|
|
o.value('same', _('Same as main node'));
|
|
o.value('urltest', _('URLTest'));
|
|
for (let i in proxy_nodes)
|
|
o.value(i, proxy_nodes[i]);
|
|
o.default = 'nil';
|
|
o.depends({'routing_mode': /^((?!custom).)+$/, 'proxy_mode': /^((?!redirect$).)+$/});
|
|
o.rmempty = false;
|
|
|
|
o = s.taboption('routing', hp.CBIStaticList, 'main_udp_urltest_nodes', _('URLTest nodes'),
|
|
_('List of nodes to test.'));
|
|
for (let i in proxy_nodes)
|
|
o.value(i, proxy_nodes[i]);
|
|
o.depends('main_udp_node', 'urltest');
|
|
o.rmempty = false;
|
|
|
|
o = s.taboption('routing', form.Value, 'main_udp_urltest_interval', _('Test interval'),
|
|
_('The test interval in seconds.'));
|
|
o.datatype = 'uinteger';
|
|
o.placeholder = '180';
|
|
o.depends('main_udp_node', 'urltest');
|
|
|
|
o = s.taboption('routing', form.Value, 'main_udp_urltest_tolerance', _('Test tolerance'),
|
|
_('The test tolerance in milliseconds.'));
|
|
o.datatype = 'uinteger';
|
|
o.placeholder = '50';
|
|
o.depends('main_udp_node', 'urltest');
|
|
|
|
o = s.taboption('routing', form.ListValue, 'routing_mode', _('Routing mode'));
|
|
o.value('gfwlist', _('GFWList'));
|
|
o.value('bypass_mainland_china', _('Bypass mainland China'));
|
|
o.value('proxy_mainland_china', _('Only proxy mainland China'));
|
|
o.value('custom', _('Custom routing'));
|
|
o.value('global', _('Global'));
|
|
o.default = 'bypass_mainland_china';
|
|
o.rmempty = false;
|
|
o.onchange = function(ev, section_id, value) {
|
|
/* Save on every transition, not only into custom: leaving custom
|
|
* leaves the user's `routing.default_outbound`, `default_outbound_dns`
|
|
* and `bypass_cn_traffic` in UCI. The generator's read-only-in-custom
|
|
* gate (5573269) keeps the generation valid, but the residue stays in
|
|
* the file until the next time someone enters custom and saves
|
|
* again, which is invisible to the user. Triggering save on every
|
|
* change makes the UCI state track the mode the user actually picked. */
|
|
if (section_id && value)
|
|
this.map.save(null, true);
|
|
}
|
|
|
|
o = s.taboption('routing', form.Value, 'routing_port', _('Routing ports'),
|
|
_('Specify target ports to be proxied. Multiple ports must be separated by commas.'));
|
|
o.value('', _('All ports'));
|
|
o.value('common', _('Common ports only (bypass P2P traffic)'));
|
|
o.validate = function(section_id, value) {
|
|
if (section_id && value && value !== 'common') {
|
|
|
|
let ports = [];
|
|
for (let i of value.split(',')) {
|
|
if (!stubValidator.apply('port', i) && !stubValidator.apply('portrange', i))
|
|
return _('Expecting: %s').format(_('valid port value'));
|
|
if (ports.includes(i))
|
|
return _('Port %s alrealy exists!').format(i);
|
|
ports = ports.concat(i);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
o = s.taboption('routing', form.ListValue, 'proxy_mode', _('Proxy mode'));
|
|
o.value('redirect', _('Redirect TCP'));
|
|
if (features.hp_has_tproxy)
|
|
o.value('redirect_tproxy', _('Redirect TCP + TProxy UDP'));
|
|
if (features.hp_has_ip_full && features.hp_has_tun) {
|
|
o.value('redirect_tun', _('Redirect TCP + Tun UDP'));
|
|
o.value('tun', _('Tun TCP/UDP'));
|
|
} else {
|
|
o.description = _('To enable Tun support, you need to install <code>ip-full</code> and <code>kmod-tun</code>');
|
|
}
|
|
o.default = 'redirect_tproxy';
|
|
o.rmempty = false;
|
|
|
|
o = s.taboption('routing', form.Flag, 'ipv6_support', _('IPv6 support'));
|
|
o.default = o.enabled;
|
|
o.rmempty = false;
|
|
|
|
/* Opt-in switch to the universal sniffer list + 100ms timeout. Default
|
|
* off so an upgrade is invisible; arch-guard 39 pins the default '0'
|
|
* against drift. */
|
|
o = s.taboption('routing', form.Flag, 'sniffer_advanced_mode',
|
|
_('Sniffer: advanced mode (100ms, universal list)'),
|
|
_('Use the tutorial-recommended sniffer profile (100ms timeout, http/tls/stun/quic/dns). Default keeps the 300ms / unconstrained-list behaviour.'));
|
|
o.default = '0';
|
|
o.rmempty = false;
|
|
|
|
/* Custom routing settings start */
|
|
/* Routing settings start */
|
|
o = s.taboption('routing', form.SectionValue, '_routing', form.NamedSection, 'routing', 'homeproxy-pro');
|
|
o.depends('routing_mode', 'custom');
|
|
|
|
ss = o.subsection;
|
|
so = ss.option(form.ListValue, 'tcpip_stack', _('TCP/IP stack'),
|
|
_('TCP/IP stack.'));
|
|
if (features.with_gvisor) {
|
|
so.value('mixed', _('Mixed'));
|
|
so.value('gvisor', _('gVisor'));
|
|
}
|
|
so.value('system', _('System'));
|
|
so.default = 'system';
|
|
so.depends('homeproxy-pro.config.proxy_mode', 'redirect_tun');
|
|
so.depends('homeproxy-pro.config.proxy_mode', 'tun');
|
|
so.rmempty = false;
|
|
so.onchange = function(ev, section_id, value) {
|
|
let desc = ev.target.nextElementSibling;
|
|
if (value === 'mixed')
|
|
desc.innerHTML = _('Mixed <code>system</code> TCP stack and <code>gVisor</code> UDP stack.')
|
|
else if (value === 'gvisor')
|
|
desc.innerHTML = _('Based on google/gvisor.');
|
|
else if (value === 'system')
|
|
desc.innerHTML = _('Less compatibility and sometimes better performance.');
|
|
}
|
|
|
|
so = ss.option(form.Flag, 'endpoint_independent_nat', _('Enable endpoint-independent NAT'),
|
|
_('Performance may degrade slightly, so it is not recommended to enable on when it is not needed.'));
|
|
so.default = so.enabled;
|
|
so.depends('tcpip_stack', 'mixed');
|
|
so.depends('tcpip_stack', 'gvisor');
|
|
so.rmempty = false;
|
|
|
|
so = ss.option(form.Value, 'udp_timeout', _('UDP NAT expiration time'),
|
|
_('In seconds.'));
|
|
so.datatype = 'uinteger';
|
|
so.placeholder = '300';
|
|
so.depends('homeproxy-pro.config.proxy_mode', 'redirect_tproxy');
|
|
so.depends('homeproxy-pro.config.proxy_mode', 'redirect_tun');
|
|
so.depends('homeproxy-pro.config.proxy_mode', 'tun');
|
|
|
|
so = ss.option(form.Flag, 'bypass_cn_traffic', _('Bypass CN traffic'),
|
|
_('Bypass mainland China traffic via firewall rules by default.'));
|
|
so.rmempty = false;
|
|
|
|
so = ss.option(form.Flag, 'find_neighbor', _('Find neighbor hosts'),
|
|
_('Enable neighbor resolution so rules can match LAN devices by hostname (1.14).'));
|
|
so.rmempty = false;
|
|
|
|
so = ss.option(form.ListValue, 'domain_strategy', _('Domain strategy'),
|
|
_('If set, the requested domain name will be resolved to IP before routing.'));
|
|
for (let i in hp.dns_strategy)
|
|
so.value(i, hp.dns_strategy[i]);
|
|
|
|
so = ss.option(form.ListValue, 'default_outbound', _('Default outbound'),
|
|
_('Default outbound for connections not matched by any routing rules.'));
|
|
so.load = function(section_id) {
|
|
delete this.keylist;
|
|
delete this.vallist;
|
|
|
|
this.value('nil', _('Disable (the service)'));
|
|
this.value('direct-out', _('Direct'));
|
|
this.value('block-out', _('Block'));
|
|
uci.sections('homeproxy-pro', 'routing_node', (res) => {
|
|
if (res.enabled === '1')
|
|
this.value(res['.name'], res.label);
|
|
});
|
|
|
|
return this.super('load', section_id);
|
|
}
|
|
so.default = 'nil';
|
|
so.rmempty = false;
|
|
|
|
so = ss.option(form.ListValue, 'default_outbound_dns', _('Default outbound DNS'),
|
|
_('Default DNS server for resolving domain name in the server address.'));
|
|
so.load = function(section_id) {
|
|
delete this.keylist;
|
|
delete this.vallist;
|
|
|
|
this.value('default-dns', _('Default DNS (issued by WAN)'));
|
|
this.value('system-dns', _('System DNS'));
|
|
uci.sections('homeproxy-pro', 'dns_server', (res) => {
|
|
if (res.enabled === '1')
|
|
this.value(res['.name'], res.label);
|
|
});
|
|
|
|
return this.super('load', section_id);
|
|
}
|
|
so.default = 'default-dns';
|
|
so.rmempty = false;
|
|
/* Routing settings end */
|
|
}
|
|
|
|
/* Routing rule sub-section. Split out from render() so the orchestrator can
|
|
* call it after nodes.renderRoutingNodes() and preserve the original tab
|
|
* ordering (routing, routing_node, routing_rule). */
|
|
function renderRoutingRules(ctx) {
|
|
const { s, self } = ctx;
|
|
|
|
/* Routing rules start */
|
|
common.renderRuleSection(s, 'routing', self);
|
|
/* Routing rules end */
|
|
}
|
|
|
|
return baseclass.extend({ render, renderRoutingRules });
|