'use strict'; 'require form'; 'require baseclass'; 'require tools.widgets as widgets'; 'require view.podkop.main as main'; function createSectionContent(section) { let o = section.option( form.ListValue, 'connection_type', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing'), ); o.value('proxy', 'Proxy'); o.value('vpn', 'VPN'); o.value('block', 'Block'); o = section.option( form.ListValue, 'proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy'), ); o.value('url', _('Connection URL')); o.value('outbound', _('Outbound Config')); o.value('urltest', _('URLTest')); o.default = 'url'; o.depends('connection_type', 'proxy'); o = section.option( form.TextValue, 'proxy_string', _('Proxy Configuration URL'), '', ); o.depends('proxy_config_type', 'url'); o.rows = 5; // Enable soft wrapping for multi-line proxy URLs (e.g., for URLTest proxy links) o.wrap = 'soft'; // Render as a textarea to allow multiple proxy URLs/configs o.textarea = true; o.rmempty = false; o.sectionDescriptions = new Map(); o.placeholder = 'vless://uuid@server:port?type=tcp&security=tls#main\n// backup ss://method:pass@server:port\n// backup2 vless://uuid@server:port?type=grpc&security=reality#alt\n// backup3 trojan://04agAQapcl@127.0.0.1:33641?type=tcp&security=none#trojan-tcp-none'; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } try { const activeConfigs = main.splitProxyString(value); if (!activeConfigs.length) { return _('No active configuration found. One configuration is required.'); } if (activeConfigs.length > 1) { return _('Multiply active configurations found. Please leave one configuration.'); } const validation = main.validateProxyUrl(activeConfigs[0]); if (validation.valid) { return true; } return validation.message; } catch (e) { return `${_('Invalid URL format:')} ${e?.message}`; } }; o = section.option( form.TextValue, 'outbound_json', _('Outbound Configuration'), _('Enter complete outbound configuration in JSON format'), ); o.depends('proxy_config_type', 'outbound'); o.rows = 10; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } const validation = main.validateOutboundJson(value); if (validation.valid) { return true; } return validation.message; }; o = section.option( form.DynamicList, 'urltest_proxy_links', _('URLTest Proxy Links'), ); o.depends('proxy_config_type', 'urltest'); o.placeholder = 'vless://, ss://, trojan:// links'; o.rmempty = false; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } const validation = main.validateProxyUrl(value); if (validation.valid) { return true; } return validation.message; }; o = section.option( form.Flag, 'enable_shadowsocks_udp_over_tcp', _('Shadowsocks UDP over TCP'), _('Apply for SS2022'), ); o.default = '0'; o.depends('connection_type', 'proxy'); o.rmempty = false; o = section.option( widgets.DeviceSelect, 'interface', _('Network Interface'), _('Select network interface for VPN connection'), ); o.depends('connection_type', 'vpn'); o.noaliases = true; o.nobridges = false; o.noinactive = false; o.filter = function (section_id, value) { // Blocked interface names that should never be selectable const blockedInterfaces = [ 'br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0', 'pppoe-wan', 'lan', ]; // Reject immediately if the value matches any blocked interface if (blockedInterfaces.includes(value)) { return false; } // Try to find the device object with the given name const device = this.devices.find((dev) => dev.getName() === value); // If no device is found, allow the value if (!device) { return true; } // Get the device type (e.g., "wifi", "ethernet", etc.) const type = device.getType(); // Reject wireless-related devices const isWireless = type === 'wifi' || type === 'wireless' || type.includes('wlan'); return !isWireless; }; o = section.option( form.Flag, 'domain_resolver_enabled', _('Domain Resolver'), _('Enable built-in DNS resolver for domains handled by this section'), ); o.default = '0'; o.rmempty = false; o.depends('connection_type', 'vpn'); o = section.option( form.ListValue, 'domain_resolver_dns_type', _('DNS Protocol Type'), _('Select the DNS protocol type for the domain resolver'), ); o.value('doh', _('DNS over HTTPS (DoH)')); o.value('dot', _('DNS over TLS (DoT)')); o.value('udp', _('UDP (Unprotected DNS)')); o.default = 'udp'; o.rmempty = false; o.depends('domain_resolver_enabled', '1'); o = section.option( form.Value, 'domain_resolver_dns_server', _('DNS Server'), _('Select or enter DNS server address'), ); Object.entries(main.DNS_SERVER_OPTIONS).forEach(([key, label]) => { o.value(key, _(label)); }); o.default = '8.8.8.8'; o.rmempty = false; o.depends('domain_resolver_enabled', '1'); o.validate = function (section_id, value) { const validation = main.validateDNS(value); if (validation.valid) { return true; } return validation.message; }; o = section.option( form.DynamicList, 'community_lists', _('Community Lists'), _('Select a predefined list for routing') + ' github.com/itdoginfo/allow-domains', ); o.placeholder = 'Service list'; Object.entries(main.DOMAIN_LIST_OPTIONS).forEach(([key, label]) => { o.value(key, _(label)); }); o.rmempty = true; o = section.option( form.ListValue, 'user_domain_list_type', _('User Domain List Type'), _('Select the list type for adding custom domains'), ); o.value('disabled', _('Disabled')); o.value('dynamic', _('Dynamic List')); o.value('text', _('Text List')); o.default = 'disabled'; o.rmempty = false; o = section.option( form.DynamicList, 'user_domains', _('User Domains'), _('Enter domain names without protocols, e.g. example.com or sub.example.com'), ); o.placeholder = 'Domains list'; o.depends('user_domain_list_type', 'dynamic'); o.rmempty = false; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } const validation = main.validateDomain(value, true); if (validation.valid) { return true; } return validation.message; }; o = section.option( form.TextValue, 'user_domains_text', _('User Domains List'), _('Enter domain names separated by commas, spaces, or newlines. You can add comments using //'), ); o.placeholder = 'example.com, sub.example.com\n// Social networks\ndomain.com test.com // personal domains'; o.depends('user_domain_list_type', 'text'); o.rows = 8; o.rmempty = false; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } const domains = main.parseValueList(value); if (!domains.length) { return _('At least one valid domain must be specified. Comments-only content is not allowed.'); } const {valid, results} = main.bulkValidate(domains, row => main.validateDomain(row, true)); if (!valid) { const errors = results .filter((validation) => !validation.valid) // Leave only failed validations .map((validation) => _(`${validation.value}: ${validation.message}`)); // Collect validation errors return [_('Validation errors:'), ...errors].join('\n'); } return true; }; o = section.option( form.ListValue, 'user_subnet_list_type', _('User Subnet List Type'), _('Select the list type for adding custom subnets'), ); o.value('disabled', _('Disabled')); o.value('dynamic', _('Dynamic List')); o.value('text', _('Text List (comma/space/newline separated)')); o.default = 'disabled'; o.rmempty = false; o = section.option( form.DynamicList, 'user_subnets', _('User Subnets'), _('Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses'), ); o.placeholder = 'IP or subnet'; o.depends('user_subnet_list_type', 'dynamic'); o.rmempty = false; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } const validation = main.validateSubnet(value); if (validation.valid) { return true; } return validation.message; }; o = section.option( form.TextValue, 'user_subnets_text', _('User Subnets List'), _( 'Enter subnets in CIDR notation or single IP addresses, separated by commas, spaces, or newlines. ' + 'You can add comments using //' ), ); o.placeholder = '103.21.244.0/22\n// Google DNS\n8.8.8.8\n1.1.1.1/32, 9.9.9.9 // Cloudflare and Quad9'; o.depends('user_subnet_list_type', 'text'); o.rows = 10; o.rmempty = false; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } const subnets = main.parseValueList(value); if (!subnets.length) { return _('At least one valid subnet or IP must be specified. Comments-only content is not allowed.'); } const {valid, results} = main.bulkValidate(subnets, main.validateSubnet); if (!valid) { const errors = results .filter((validation) => !validation.valid) // Leave only failed validations .map((validation) => _(`${validation.value}: ${validation.message}`)); // Collect validation errors return [_('Validation errors:'), ...errors].join('\n'); } return true; }; o = section.option( form.DynamicList, 'local_domain_lists', _('Local Domain Lists'), _('Specify the path to the list file located on the router filesystem'), ); o.placeholder = '/path/file.lst'; o.rmempty = true; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } const validation = main.validatePath(value); if (validation.valid) { return true; } return validation.message; }; o = section.option( form.DynamicList, 'local_subnet_lists', _('Local Subnet Lists'), _('Specify the path to the list file located on the router filesystem'), ); o.placeholder = '/path/file.lst'; o.rmempty = true; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } const validation = main.validatePath(value); if (validation.valid) { return true; } return validation.message; }; o = section.option( form.DynamicList, 'remote_domain_lists', _('Remote Domain Lists'), _('Specify remote URLs to download and use domain lists'), ); o.placeholder = 'https://example.com/domains.srs'; o.rmempty = true; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } const validation = main.validateUrl(value); if (validation.valid) { return true; } return validation.message; }; o = section.option( form.DynamicList, 'remote_subnet_lists', _('Remote Subnet Lists'), _('Specify remote URLs to download and use subnet lists'), ); o.placeholder = 'https://example.com/subnets.srs'; o.rmempty = true; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } const validation = main.validateUrl(value); if (validation.valid) { return true; } return validation.message; }; o = section.option( form.DynamicList, 'fully_routed_ips', _('Fully Routed IPs'), _('Specify local IP addresses or subnets whose traffic will always be routed through the configured route'), ); o.placeholder = '192.168.1.2 or 192.168.1.0/24'; o.rmempty = true; o.validate = function (section_id, value) { // Optional if (!value || value.length === 0) { return true; } const validation = main.validateSubnet(value); if (validation.valid) { return true; } return validation.message; }; o = section.option( form.Flag, 'mixed_proxy_enabled', _('Enable Mixed Proxy'), _('Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies'), ); o.default = '0'; o.rmempty = false; o = section.option( form.Value, 'mixed_proxy_port', _('Mixed Proxy Port'), _( 'Specify the port number on which the mixed proxy will run for this section. ' + 'Make sure the selected port is not used by another service' ), ); o.rmempty = false; o.depends('mixed_proxy_enabled', '1'); } const EntryPoint = { createSectionContent, } return baseclass.extend(EntryPoint);