mirror of
https://github.com/itdoginfo/podkop.git
synced 2025-12-15 16:06:53 +03:00
523 lines
15 KiB
JavaScript
523 lines
15 KiB
JavaScript
'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') +
|
|
' <a href="https://github.com/itdoginfo/allow-domains" target="_blank">github.com/itdoginfo/allow-domains</a>',
|
|
);
|
|
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);
|