mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-04 12:03:56 +08:00
212 lines
8.8 KiB
JavaScript
212 lines
8.8 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 tools.firewall as fwtool';
|
|
'require tools.widgets as widgets';
|
|
|
|
/* Access Control tab. Five sub-tabs (interface control, LAN IP policy,
|
|
* WAN IP policy, proxy domain list, direct domain list). Each domain-list
|
|
* sub-tab reads/writes the proxy_list / direct_list files through an
|
|
* ubus RPC; the load callback is the only path that touches that RPC. */
|
|
function render(ctx) {
|
|
const { s, hosts, stubValidator } = ctx;
|
|
let o, ss, so;
|
|
|
|
s.tab('control', _('Access Control'));
|
|
|
|
o = s.taboption('control', form.SectionValue, '_control', form.NamedSection, 'control', 'homeproxy-pro');
|
|
ss = o.subsection;
|
|
|
|
/* Interface control start */
|
|
ss.tab('interface', _('Interface Control'));
|
|
|
|
so = ss.taboption('interface', widgets.DeviceSelect, 'listen_interfaces', _('Listen interfaces'),
|
|
_('Only process traffic from specific interfaces. Leave empty for all.'));
|
|
so.multiple = true;
|
|
so.noaliases = true;
|
|
|
|
so = ss.taboption('interface', widgets.DeviceSelect, 'bind_interface', _('Bind interface'),
|
|
_('Bind outbound traffic to specific interface. Leave empty to auto detect.'));
|
|
so.multiple = false;
|
|
so.noaliases = true;
|
|
/* Interface control end */
|
|
|
|
/* LAN IP policy start */
|
|
ss.tab('lan_ip_policy', _('LAN IP Policy'));
|
|
|
|
so = ss.taboption('lan_ip_policy', form.ListValue, 'lan_proxy_mode', _('Proxy filter mode'));
|
|
so.value('disabled', _('Disable'));
|
|
so.value('listed_only', _('Proxy listed only'));
|
|
so.value('except_listed', _('Proxy all except listed'));
|
|
so.default = 'disabled';
|
|
so.rmempty = false;
|
|
|
|
so = fwtool.addIPOption(ss, 'lan_ip_policy', 'lan_direct_ipv4_ips', _('Direct IPv4 IP-s'), null, 'ipv4', hosts, true);
|
|
so.depends('lan_proxy_mode', 'except_listed');
|
|
|
|
so = fwtool.addIPOption(ss, 'lan_ip_policy', 'lan_direct_ipv6_ips', _('Direct IPv6 IP-s'), null, 'ipv6', hosts, true);
|
|
so.depends({'lan_proxy_mode': 'except_listed', 'homeproxy-pro.config.ipv6_support': '1'});
|
|
|
|
so = fwtool.addMACOption(ss, 'lan_ip_policy', 'lan_direct_mac_addrs', _('Direct MAC-s'), null, hosts);
|
|
so.depends('lan_proxy_mode', 'except_listed');
|
|
|
|
so = fwtool.addIPOption(ss, 'lan_ip_policy', 'lan_proxy_ipv4_ips', _('Proxy IPv4 IP-s'), null, 'ipv4', hosts, true);
|
|
so.depends('lan_proxy_mode', 'listed_only');
|
|
|
|
so = fwtool.addIPOption(ss, 'lan_ip_policy', 'lan_proxy_ipv6_ips', _('Proxy IPv6 IP-s'), null, 'ipv6', hosts, true);
|
|
so.depends({'lan_proxy_mode': 'listed_only', 'homeproxy-pro.config.ipv6_support': '1'});
|
|
|
|
so = fwtool.addMACOption(ss, 'lan_ip_policy', 'lan_proxy_mac_addrs', _('Proxy MAC-s'), null, hosts);
|
|
so.depends('lan_proxy_mode', 'listed_only');
|
|
|
|
so = fwtool.addIPOption(ss, 'lan_ip_policy', 'lan_gaming_mode_ipv4_ips', _('Gaming mode IPv4 IP-s'), null, 'ipv4', hosts, true);
|
|
|
|
so = fwtool.addIPOption(ss, 'lan_ip_policy', 'lan_gaming_mode_ipv6_ips', _('Gaming mode IPv6 IP-s'), null, 'ipv6', hosts, true);
|
|
so.depends('homeproxy-pro.config.ipv6_support', '1');
|
|
|
|
so = fwtool.addMACOption(ss, 'lan_ip_policy', 'lan_gaming_mode_mac_addrs', _('Gaming mode MAC-s'), null, hosts);
|
|
|
|
so = fwtool.addIPOption(ss, 'lan_ip_policy', 'lan_global_proxy_ipv4_ips', _('Global proxy IPv4 IP-s'), null, 'ipv4', hosts, true);
|
|
so.depends({'homeproxy-pro.config.routing_mode': 'custom', '!reverse': true});
|
|
|
|
so = fwtool.addIPOption(ss, 'lan_ip_policy', 'lan_global_proxy_ipv6_ips', _('Global proxy IPv6 IP-s'), null, 'ipv6', hosts, true);
|
|
so.depends({'homeproxy-pro.config.routing_mode': /^((?!custom).)+$/, 'homeproxy-pro.config.ipv6_support': '1'});
|
|
|
|
so = fwtool.addMACOption(ss, 'lan_ip_policy', 'lan_global_proxy_mac_addrs', _('Global proxy MAC-s'), null, hosts);
|
|
so.depends({'homeproxy-pro.config.routing_mode': 'custom', '!reverse': true});
|
|
/* LAN IP policy end */
|
|
|
|
/* WAN IP policy start */
|
|
ss.tab('wan_ip_policy', _('WAN IP Policy'));
|
|
|
|
so = ss.taboption('wan_ip_policy', form.DynamicList, 'wan_proxy_ipv4_ips', _('Proxy IPv4 IP-s'));
|
|
so.datatype = 'or(ip4addr, cidr4)';
|
|
|
|
so = ss.taboption('wan_ip_policy', form.DynamicList, 'wan_proxy_ipv6_ips', _('Proxy IPv6 IP-s'));
|
|
so.datatype = 'or(ip6addr, cidr6)';
|
|
so.depends('homeproxy-pro.config.ipv6_support', '1');
|
|
|
|
so = ss.taboption('wan_ip_policy', form.DynamicList, 'wan_direct_ipv4_ips', _('Direct IPv4 IP-s'));
|
|
so.datatype = 'or(ip4addr, cidr4)';
|
|
|
|
so = ss.taboption('wan_ip_policy', form.DynamicList, 'wan_direct_ipv6_ips', _('Direct IPv6 IP-s'));
|
|
so.datatype = 'or(ip6addr, cidr6)';
|
|
so.depends('homeproxy-pro.config.ipv6_support', '1');
|
|
/* WAN IP policy end */
|
|
|
|
/* Proxy domain list start */
|
|
ss.tab('proxy_domain_list', _('Proxy Domain List'));
|
|
|
|
so = ss.taboption('proxy_domain_list', form.TextValue, '_proxy_domain_list');
|
|
so.rows = 10;
|
|
so.monospace = true;
|
|
so.datatype = 'hostname';
|
|
so.description = _('One domain per line. No spaces, no comments, no wildcards; the backend rejects invalid characters and tells you which line.');
|
|
so.depends({'homeproxy-pro.config.routing_mode': 'custom', '!reverse': true});
|
|
so.load = function(/* ... */) {
|
|
return hp.rpcCall('acllist_read', ['proxy_list'],
|
|
{ params: ['type'], expect: { '': {} } }).then((res) => {
|
|
/* acllist_read returns { content: null, error: '...' }
|
|
* when the backend rejected the request (bad type, file
|
|
* unreadable, ...). Surface the error instead of silently
|
|
* resolving to "" which makes the field look unchanged. */
|
|
if (res && res.error)
|
|
ui.addNotification(null, E('p', _('Failed to read domain list: %s.').format(res.error)));
|
|
return res ? res.content : '';
|
|
}, {});
|
|
}
|
|
so.write = function(_section_id, value) {
|
|
return hp.rpcCall('acllist_write', ['proxy_list', value], { params: ['type', 'content'], expect: { '': {} } }).then((ret) => {
|
|
/* Without this, a backend error like "UCI commit failed"
|
|
* is silently dropped and the user sees the old value
|
|
* reappear after refresh with no explanation. */
|
|
if (ret && ret.result === false)
|
|
throw (ret.error || 'unknown error');
|
|
return true;
|
|
});
|
|
}
|
|
so.remove = function(/* ... */) {
|
|
/* routing_mode is an option of the page's 'config' NamedSection, not
|
|
* of this nested 'control' one: `this.section.formvalue(...)` walks
|
|
* the children of `this.section`, never found it and returned null,
|
|
* so the test below was always true and a reset wiped the file even
|
|
* in custom mode, where the list is in use. Read the saved UCI value
|
|
* the backend will act on. */
|
|
let routing_mode = uci.get('homeproxy-pro', 'config', 'routing_mode');
|
|
if (routing_mode !== 'custom')
|
|
return hp.rpcCall('acllist_write', ['proxy_list', ''], { params: ['type', 'content'], expect: { '': {} } }).then((ret) => {
|
|
if (ret && ret.result === false)
|
|
throw (ret.error || 'unknown error');
|
|
return true;
|
|
});
|
|
return true;
|
|
}
|
|
so.validate = function(section_id, value) {
|
|
if (section_id && value)
|
|
for (let i of value.split('\n'))
|
|
if (i && !stubValidator.apply('hostname', i))
|
|
return _('Expecting: %s').format(_('valid hostname'));
|
|
|
|
return true;
|
|
}
|
|
/* Proxy domain list end */
|
|
|
|
/* Direct domain list start */
|
|
ss.tab('direct_domain_list', _('Direct Domain List'));
|
|
|
|
so = ss.taboption('direct_domain_list', form.TextValue, '_direct_domain_list');
|
|
so.rows = 10;
|
|
so.monospace = true;
|
|
so.datatype = 'hostname';
|
|
so.description = _('One domain per line. No spaces, no comments, no wildcards; the backend rejects invalid characters and tells you which line.');
|
|
so.depends({'homeproxy-pro.config.routing_mode': 'custom', '!reverse': true});
|
|
so.load = function(/* ... */) {
|
|
return hp.rpcCall('acllist_read', ['direct_list'],
|
|
{ params: ['type'], expect: { '': {} } }).then((res) => {
|
|
if (res && res.error)
|
|
ui.addNotification(null, E('p', _('Failed to read domain list: %s.').format(res.error)));
|
|
return res ? res.content : '';
|
|
}, {});
|
|
}
|
|
so.write = function(_section_id, value) {
|
|
return hp.rpcCall('acllist_write', ['direct_list', value], { params: ['type', 'content'], expect: { '': {} } }).then((ret) => {
|
|
if (ret && ret.result === false)
|
|
throw (ret.error || 'unknown error');
|
|
return true;
|
|
});
|
|
}
|
|
so.remove = function(/* ... */) {
|
|
/* Same `this.section` mistake as the proxy list above. */
|
|
let routing_mode = uci.get('homeproxy-pro', 'config', 'routing_mode');
|
|
if (routing_mode !== 'custom')
|
|
return hp.rpcCall('acllist_write', ['direct_list', ''], { params: ['type', 'content'], expect: { '': {} } }).then((ret) => {
|
|
if (ret && ret.result === false)
|
|
throw (ret.error || 'unknown error');
|
|
return true;
|
|
});
|
|
return true;
|
|
}
|
|
so.validate = function(section_id, value) {
|
|
if (section_id && value)
|
|
for (let i of value.split('\n'))
|
|
if (i && !stubValidator.apply('hostname', i))
|
|
return _('Expecting: %s').format(_('valid hostname'));
|
|
|
|
return true;
|
|
}
|
|
/* Direct domain list end */
|
|
/* ACL settings end */
|
|
}
|
|
|
|
return baseclass.extend({ render });
|