feat: Enhance custom domain/subnet input

This commit is contained in:
Ivan K
2024-11-30 17:16:16 +03:00
parent 2fe12f3f4d
commit 82c7c290d9
6 changed files with 502 additions and 132 deletions

View File

@@ -115,14 +115,17 @@ return view.extend({
o.rmempty = false;
o.ucisection = 'main';
o = s.taboption('basic', form.Flag, 'custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing'));
o.default = '0';
o = s.taboption('basic', form.ListValue, 'custom_domains_list_enabled', _('User Domain List Type'), _('Select how to add your custom domains'));
o.value('disabled', _('Disabled'));
o.value('dynamic', _('Dynamic List'));
o.value('text', _('Text List'));
o.default = 'disabled';
o.rmempty = false;
o.ucisection = 'main';
o = s.taboption('basic', form.DynamicList, 'custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)'));
o.placeholder = 'Domains list';
o.depends('custom_domains_list_enabled', '1');
o.depends('custom_domains_list_enabled', 'dynamic');
o.rmempty = false;
o.ucisection = 'main';
o.validate = function (section_id, value) {
@@ -138,6 +141,31 @@ return view.extend({
return true;
};
o = s.taboption('basic', form.TextValue, 'custom_domains_text', _('User Domains List'), _('Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)'));
o.placeholder = 'example.com, sub.example.com\ndomain.com test.com\nsubdomain.domain.com another.com, third.com';
o.depends('custom_domains_list_enabled', 'text');
o.rows = 10;
o.rmempty = false;
o.ucisection = 'main';
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
const domains = value.split(/[,\s\n]/)
.map(d => d.trim())
.filter(d => d.length > 0);
const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
for (const domain of domains) {
if (!domainRegex.test(domain)) {
return _('Invalid domain format: ' + domain + '. Enter domain without protocol');
}
}
return true;
};
o = s.taboption('basic', form.Flag, 'custom_download_domains_list_enabled', _('Remote Domain Lists'), _('Download and use domain lists from remote URLs'));
o.default = '0';
o.rmempty = false;
@@ -164,14 +192,18 @@ return view.extend({
}
};
o = s.taboption('basic', form.Flag, 'custom_subnets_list_enabled', _('User Subnet List'), _('Enable and manage your custom list of IP subnets for selective routing'));
o.default = '0';
o = s.taboption('basic', form.ListValue, 'custom_subnets_list_enabled', _('User Subnet List Type'), _('Select how to add your 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.ucisection = 'main';
o = s.taboption('basic', form.DynamicList, 'custom_subnets', _('User Subnets'), _('Enter subnet in CIDR notation (example: 103.21.244.0/22)'));
o.placeholder = 'Subnets list';
o.depends('custom_subnets_list_enabled', '1');
o = s.taboption('basic', form.DynamicList, 'custom_subnets', _('User Subnets'), _('Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses'));
o.placeholder = 'IP or subnet';
o.depends('custom_subnets_list_enabled', 'dynamic');
o.rmempty = false;
o.ucisection = 'main';
o.validate = function (section_id, value) {
@@ -179,15 +211,15 @@ return view.extend({
return true;
}
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/;
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
if (!subnetRegex.test(value)) {
return _('Invalid subnet format. Use format: X.X.X.X/Y (like 103.21.244.0/22)');
return _('Invalid format. Use format: X.X.X.X or X.X.X.X/Y');
}
// Разбираем IP и маску
const [ip, cidr] = value.split('/');
const ipParts = ip.split('.');
const cidrNum = parseInt(cidr);
for (const part of ipParts) {
const num = parseInt(part);
@@ -196,13 +228,59 @@ return view.extend({
}
}
if (cidrNum < 0 || cidrNum > 32) {
return _('CIDR must be between 0 and 32');
if (cidr !== undefined) {
const cidrNum = parseInt(cidr);
if (cidrNum < 0 || cidrNum > 32) {
return _('CIDR must be between 0 and 32');
}
}
return true;
};
o = s.taboption('basic', form.TextValue, 'custom_subnets_text', _('User Subnets List'), _('Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline'));
o.placeholder = '103.21.244.0/22\n8.8.8.8\n1.1.1.1/32, 9.9.9.9 10.10.10.10';
o.depends('custom_subnets_list_enabled', 'text');
o.rows = 10;
o.rmempty = false;
o.ucisection = 'main';
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
// Split by commas, spaces and newlines
const subnets = value.split(/[,\s\n]/)
.map(s => s.trim())
.filter(s => s.length > 0);
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
for (const subnet of subnets) {
if (!subnetRegex.test(subnet)) {
return _('Invalid format: ' + subnet + '. Use format: X.X.X.X or X.X.X.X/Y');
}
const [ip, cidr] = subnet.split('/');
const ipParts = ip.split('.');
for (const part of ipParts) {
const num = parseInt(part);
if (num < 0 || num > 255) {
return _('IP parts must be between 0 and 255 in: ' + subnet);
}
}
if (cidr !== undefined) {
const cidrNum = parseInt(cidr);
if (cidrNum < 0 || cidrNum > 32) {
return _('CIDR must be between 0 and 32 in: ' + subnet);
}
}
}
return true;
};
o = s.taboption('basic', form.Flag, 'custom_download_subnets_list_enabled', _('Remote Subnet Lists'), _('Download and use subnet lists from remote URLs'));
o.default = '0';
o.rmempty = false;
@@ -409,15 +487,18 @@ return view.extend({
o.rmempty = false;
o.ucisection = 'second';
o = s.taboption('secondary_config', form.Flag, 'second_custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing'));
o.default = '0';
o = s.taboption('secondary_config', form.ListValue, 'second_custom_domains_list_enabled', _('User Domain List Type'), _('Select how to add your custom domains'));
o.value('disabled', _('Disabled'));
o.value('dynamic', _('Dynamic List'));
o.value('text', _('Text List'));
o.default = 'disabled';
o.rmempty = false;
o.depends('second_enable', '1');
o.ucisection = 'second';
o = s.taboption('secondary_config', form.DynamicList, 'second_custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)'));
o.placeholder = 'Domains list';
o.depends('second_custom_domains_list_enabled', '1');
o.depends('second_custom_domains_list_enabled', 'dynamic');
o.rmempty = false;
o.ucisection = 'second';
o.validate = function (section_id, value) {
@@ -433,15 +514,10 @@ return view.extend({
return true;
};
o = s.taboption('secondary_config', form.Flag, 'second_custom_subnets_list_enabled', _('User Subnet List'), _('Enable and manage your custom list of IP subnets for selective routing'));
o.default = '0';
o.rmempty = false;
o.depends('second_enable', '1');
o.ucisection = 'second';
o = s.taboption('secondary_config', form.DynamicList, 'second_custom_subnets', _('User Subnets'), _('Enter subnet in CIDR notation (example: 103.21.244.0/22)'));
o.placeholder = 'Subnets list';
o.depends('second_custom_subnets_list_enabled', '1');
o = s.taboption('secondary_config', form.TextValue, 'second_custom_domains_text', _('User Domains List'), _('Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)'));
o.placeholder = 'example.com, sub.example.com\ndomain.com test.com\nsubdomain.domain.com another.com, third.com';
o.depends('second_custom_domains_list_enabled', 'text');
o.rows = 10;
o.rmempty = false;
o.ucisection = 'second';
o.validate = function (section_id, value) {
@@ -449,15 +525,47 @@ return view.extend({
return true;
}
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/;
const domains = value.split(/[,\s\n]/)
.map(d => d.trim())
.filter(d => d.length > 0);
const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
for (const domain of domains) {
if (!domainRegex.test(domain)) {
return _('Invalid domain format: ' + domain + '. Enter domain without protocol');
}
}
return true;
};
o = s.taboption('secondary_config', form.ListValue, 'second_custom_subnets_list_enabled', _('User Subnet List Type'), _('Select how to add your custom subnets'));
o.value('disabled', _('Disabled'));
o.value('dynamic', _('Dynamic List'));
o.value('text', _('Text List'));
o.default = 'disabled';
o.rmempty = false;
o.depends('second_enable', '1');
o.ucisection = 'second';
o = s.taboption('secondary_config', form.DynamicList, 'second_custom_subnets', _('User Subnets'), _('Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses'));
o.placeholder = 'IP or subnet';
o.depends('second_custom_subnets_list_enabled', 'dynamic');
o.rmempty = false;
o.ucisection = 'second';
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
if (!subnetRegex.test(value)) {
return _('Invalid subnet format. Use format: X.X.X.X/Y (like 103.21.244.0/22)');
return _('Invalid format. Use format: X.X.X.X or X.X.X.X/Y');
}
const [ip, cidr] = value.split('/');
const ipParts = ip.split('.');
const cidrNum = parseInt(cidr);
for (const part of ipParts) {
const num = parseInt(part);
@@ -466,13 +574,58 @@ return view.extend({
}
}
if (cidrNum < 0 || cidrNum > 32) {
return _('CIDR must be between 0 and 32');
if (cidr !== undefined) {
const cidrNum = parseInt(cidr);
if (cidrNum < 0 || cidrNum > 32) {
return _('CIDR must be between 0 and 32');
}
}
return true;
};
o = s.taboption('secondary_config', form.TextValue, 'second_custom_subnets_text', _('User Subnets List'), _('Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline'));
o.placeholder = '103.21.244.0/22\n8.8.8.8\n1.1.1.1/32, 9.9.9.9 10.10.10.10';
o.depends('second_custom_subnets_list_enabled', 'text');
o.rows = 10;
o.rmempty = false;
o.ucisection = 'second';
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
}
const subnets = value.split(/[,\s\n]/)
.map(s => s.trim())
.filter(s => s.length > 0);
const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
for (const subnet of subnets) {
if (!subnetRegex.test(subnet)) {
return _('Invalid format: ' + subnet + '. Use format: X.X.X.X or X.X.X.X/Y');
}
const [ip, cidr] = subnet.split('/');
const ipParts = ip.split('.');
for (const part of ipParts) {
const num = parseInt(part);
if (num < 0 || num > 255) {
return _('IP parts must be between 0 and 255 in: ' + subnet);
}
}
if (cidr !== undefined) {
const cidrNum = parseInt(cidr);
if (cidrNum < 0 || cidrNum > 32) {
return _('CIDR must be between 0 and 32 in: ' + subnet);
}
}
}
return true;
};
return m.render();
}
});

View File

@@ -218,4 +218,76 @@ msgid "CIDR must be between 0 and 32"
msgstr "CIDR должен быть между 0 и 32"
msgid "Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)"
msgstr "Неверный формат IP. Используйте формат: X.X.X.X (например: 192.168.1.1)"
msgstr "Неверный формат IP. Используйте формат: X.X.X.X (например: 192.168.1.1)"
msgid "User Domain List Type"
msgstr "Тип пользовательского списка доменов"
msgid "Select how to add your custom domains"
msgstr "Выберите способ добавления пользовательских доменов"
msgid "Disabled"
msgstr "Отключено"
msgid "Dynamic List"
msgstr "Динамический список"
msgid "Text List"
msgstr "Текстовый список"
msgid "User Domains List"
msgstr "Список пользовательских доменов"
msgid "Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)"
msgstr "Введите имена доменов через запятую, пробел или новую строку (пример: sub.example.com, example.com или один домен на строку)"
msgid "Invalid domain format: %s. Enter domain without protocol"
msgstr "Неверный формат домена: %s. Введите домен без протокола"
msgid "User Subnet List Type"
msgstr "Тип пользовательского списка подсетей"
msgid "Select how to add your custom subnets"
msgstr "Выберите способ добавления пользовательских подсетей"
msgid "Text List (comma/space/newline separated)"
msgstr "Текстовый список (разделенный запятыми/пробелами/новыми строками)"
msgid "Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses"
msgstr "Введите подсети в нотации CIDR (пример: 103.21.244.0/22) или отдельные IP-адреса"
msgid "User Subnets List"
msgstr "Список пользовательских подсетей"
msgid "Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline"
msgstr "Введите подсети в нотации CIDR или отдельные IP-адреса через запятую, пробел или новую строку"
msgid "Invalid format. Use format: X.X.X.X or X.X.X.X/Y"
msgstr "Неверный формат. Используйте формат: X.X.X.X или X.X.X.X/Y"
msgid "IP parts must be between 0 and 255 in: %s"
msgstr "Части IP-адреса должны быть между 0 и 255 в: %s"
msgid "Configuration Type"
msgstr "Тип конфигурации"
msgid "Select how to configure the proxy"
msgstr "Выберите способ настройки прокси"
msgid "Connection URL"
msgstr "URL подключения"
msgid "Outbound Config"
msgstr "Конфигурация Outbound"
msgid "Outbound Configuration"
msgstr "Конфигурация исходящего соединения"
msgid "Enter complete outbound configuration in JSON format"
msgstr "Введите полную конфигурацию исходящего соединения в формате JSON"
msgid "JSON must contain at least type, server and server_port fields"
msgstr "JSON должен содержать как минимум поля type, server и server_port"
msgid "Invalid JSON format"
msgstr "Неверный формат JSON"

View File

@@ -218,4 +218,76 @@ msgid "CIDR must be between 0 and 32"
msgstr ""
msgid "Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)"
msgstr ""
msgid "User Domain List Type"
msgstr ""
msgid "Select how to add your custom domains"
msgstr ""
msgid "Disabled"
msgstr ""
msgid "Dynamic List"
msgstr ""
msgid "Text List"
msgstr ""
msgid "User Domains List"
msgstr ""
msgid "Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)"
msgstr ""
msgid "Invalid domain format: %s. Enter domain without protocol"
msgstr ""
msgid "User Subnet List Type"
msgstr ""
msgid "Select how to add your custom subnets"
msgstr ""
msgid "Text List (comma/space/newline separated)"
msgstr ""
msgid "Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses"
msgstr ""
msgid "User Subnets List"
msgstr ""
msgid "Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline"
msgstr ""
msgid "Invalid format. Use format: X.X.X.X or X.X.X.X/Y"
msgstr ""
msgid "IP parts must be between 0 and 255 in: %s"
msgstr ""
msgid "Configuration Type"
msgstr ""
msgid "Select how to configure the proxy"
msgstr ""
msgid "Connection URL"
msgstr ""
msgid "Outbound Config"
msgstr ""
msgid "Outbound Configuration"
msgstr ""
msgid "Enter complete outbound configuration in JSON format"
msgstr ""
msgid "JSON must contain at least type, server and server_port fields"
msgstr ""
msgid "Invalid JSON format"
msgstr ""