mirror of
https://github.com/itdoginfo/podkop.git
synced 2025-12-09 13:06:52 +03:00
fix: run prettier for luci app js assets
This commit is contained in:
@@ -7,22 +7,46 @@
|
|||||||
function createAdditionalSection(mainSection) {
|
function createAdditionalSection(mainSection) {
|
||||||
let o = mainSection.tab('additional', _('Additional Settings'));
|
let o = mainSection.tab('additional', _('Additional Settings'));
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'yacd', _('Yacd enable'), `<a href="${main.getBaseUrl()}:9090/ui" target="_blank">${main.getBaseUrl()}:9090/ui</a>`);
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.Flag,
|
||||||
|
'yacd',
|
||||||
|
_('Yacd enable'),
|
||||||
|
`<a href="${main.getBaseUrl()}:9090/ui" target="_blank">${main.getBaseUrl()}:9090/ui</a>`,
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'exclude_ntp', _('Exclude NTP'), _('Allows you to exclude NTP protocol traffic from the tunnel'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.Flag,
|
||||||
|
'exclude_ntp',
|
||||||
|
_('Exclude NTP'),
|
||||||
|
_('Allows you to exclude NTP protocol traffic from the tunnel'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'quic_disable', _('QUIC disable'), _('For issues with the video stream'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.Flag,
|
||||||
|
'quic_disable',
|
||||||
|
_('QUIC disable'),
|
||||||
|
_('For issues with the video stream'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.ListValue, 'update_interval', _('List Update Frequency'), _('Select how often the lists will be updated'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.ListValue,
|
||||||
|
'update_interval',
|
||||||
|
_('List Update Frequency'),
|
||||||
|
_('Select how often the lists will be updated'),
|
||||||
|
);
|
||||||
Object.entries(main.UPDATE_INTERVAL_OPTIONS).forEach(([key, label]) => {
|
Object.entries(main.UPDATE_INTERVAL_OPTIONS).forEach(([key, label]) => {
|
||||||
o.value(key, _(label));
|
o.value(key, _(label));
|
||||||
});
|
});
|
||||||
@@ -30,7 +54,13 @@ function createAdditionalSection(mainSection) {
|
|||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.ListValue, 'dns_type', _('DNS Protocol Type'), _('Select DNS protocol to use'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.ListValue,
|
||||||
|
'dns_type',
|
||||||
|
_('DNS Protocol Type'),
|
||||||
|
_('Select DNS protocol to use'),
|
||||||
|
);
|
||||||
o.value('doh', _('DNS over HTTPS (DoH)'));
|
o.value('doh', _('DNS over HTTPS (DoH)'));
|
||||||
o.value('dot', _('DNS over TLS (DoT)'));
|
o.value('dot', _('DNS over TLS (DoT)'));
|
||||||
o.value('udp', _('UDP (Unprotected DNS)'));
|
o.value('udp', _('UDP (Unprotected DNS)'));
|
||||||
@@ -38,7 +68,13 @@ function createAdditionalSection(mainSection) {
|
|||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Value, 'dns_server', _('DNS Server'), _('Select or enter DNS server address'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.Value,
|
||||||
|
'dns_server',
|
||||||
|
_('DNS Server'),
|
||||||
|
_('Select or enter DNS server address'),
|
||||||
|
);
|
||||||
Object.entries(main.DNS_SERVER_OPTIONS).forEach(([key, label]) => {
|
Object.entries(main.DNS_SERVER_OPTIONS).forEach(([key, label]) => {
|
||||||
o.value(key, _(label));
|
o.value(key, _(label));
|
||||||
});
|
});
|
||||||
@@ -55,7 +91,15 @@ function createAdditionalSection(mainSection) {
|
|||||||
return _(validation.message);
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Value, 'bootstrap_dns_server', _('Bootstrap DNS server'), _('The DNS server used to look up the IP address of an upstream DNS server'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.Value,
|
||||||
|
'bootstrap_dns_server',
|
||||||
|
_('Bootstrap DNS server'),
|
||||||
|
_(
|
||||||
|
'The DNS server used to look up the IP address of an upstream DNS server',
|
||||||
|
),
|
||||||
|
);
|
||||||
Object.entries(main.BOOTSTRAP_DNS_SERVER_OPTIONS).forEach(([key, label]) => {
|
Object.entries(main.BOOTSTRAP_DNS_SERVER_OPTIONS).forEach(([key, label]) => {
|
||||||
o.value(key, _(label));
|
o.value(key, _(label));
|
||||||
});
|
});
|
||||||
@@ -72,7 +116,13 @@ function createAdditionalSection(mainSection) {
|
|||||||
return _(validation.message);
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Value, 'dns_rewrite_ttl', _('DNS Rewrite TTL'), _('Time in seconds for DNS record caching (default: 60)'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.Value,
|
||||||
|
'dns_rewrite_ttl',
|
||||||
|
_('DNS Rewrite TTL'),
|
||||||
|
_('Time in seconds for DNS record caching (default: 60)'),
|
||||||
|
);
|
||||||
o.default = '60';
|
o.default = '60';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
@@ -89,16 +139,35 @@ function createAdditionalSection(mainSection) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.ListValue, 'config_path', _('Config File Path'), _('Select path for sing-box config file. Change this ONLY if you know what you are doing'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.ListValue,
|
||||||
|
'config_path',
|
||||||
|
_('Config File Path'),
|
||||||
|
_(
|
||||||
|
'Select path for sing-box config file. Change this ONLY if you know what you are doing',
|
||||||
|
),
|
||||||
|
);
|
||||||
o.value('/etc/sing-box/config.json', 'Flash (/etc/sing-box/config.json)');
|
o.value('/etc/sing-box/config.json', 'Flash (/etc/sing-box/config.json)');
|
||||||
o.value('/tmp/sing-box/config.json', 'RAM (/tmp/sing-box/config.json)');
|
o.value('/tmp/sing-box/config.json', 'RAM (/tmp/sing-box/config.json)');
|
||||||
o.default = '/etc/sing-box/config.json';
|
o.default = '/etc/sing-box/config.json';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Value, 'cache_path', _('Cache File Path'), _('Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.Value,
|
||||||
|
'cache_path',
|
||||||
|
_('Cache File Path'),
|
||||||
|
_(
|
||||||
|
'Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing',
|
||||||
|
),
|
||||||
|
);
|
||||||
o.value('/tmp/sing-box/cache.db', 'RAM (/tmp/sing-box/cache.db)');
|
o.value('/tmp/sing-box/cache.db', 'RAM (/tmp/sing-box/cache.db)');
|
||||||
o.value('/usr/share/sing-box/cache.db', 'Flash (/usr/share/sing-box/cache.db)');
|
o.value(
|
||||||
|
'/usr/share/sing-box/cache.db',
|
||||||
|
'Flash (/usr/share/sing-box/cache.db)',
|
||||||
|
);
|
||||||
o.default = '/tmp/sing-box/cache.db';
|
o.default = '/tmp/sing-box/cache.db';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
@@ -123,7 +192,13 @@ function createAdditionalSection(mainSection) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', widgets.DeviceSelect, 'iface', _('Source Network Interface'), _('Select the network interface from which the traffic will originate'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
widgets.DeviceSelect,
|
||||||
|
'iface',
|
||||||
|
_('Source Network Interface'),
|
||||||
|
_('Select the network interface from which the traffic will originate'),
|
||||||
|
);
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
o.default = 'br-lan';
|
o.default = 'br-lan';
|
||||||
o.noaliases = true;
|
o.noaliases = true;
|
||||||
@@ -138,7 +213,7 @@ function createAdditionalSection(mainSection) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to find the device object by its name
|
// Try to find the device object by its name
|
||||||
const device = this.devices.find(dev => dev.getName() === value);
|
const device = this.devices.find((dev) => dev.getName() === value);
|
||||||
|
|
||||||
// If no device is found, allow the value
|
// If no device is found, allow the value
|
||||||
if (!device) {
|
if (!device) {
|
||||||
@@ -156,12 +231,24 @@ function createAdditionalSection(mainSection) {
|
|||||||
return !isWireless;
|
return !isWireless;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'mon_restart_ifaces', _('Interface monitoring'), _('Interface monitoring for bad WAN'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.Flag,
|
||||||
|
'mon_restart_ifaces',
|
||||||
|
_('Interface monitoring'),
|
||||||
|
_('Interface monitoring for bad WAN'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', widgets.NetworkSelect, 'restart_ifaces', _('Interface for monitoring'), _('Select the WAN interfaces to be monitored'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
widgets.NetworkSelect,
|
||||||
|
'restart_ifaces',
|
||||||
|
_('Interface for monitoring'),
|
||||||
|
_('Select the WAN interfaces to be monitored'),
|
||||||
|
);
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
o.depends('mon_restart_ifaces', '1');
|
o.depends('mon_restart_ifaces', '1');
|
||||||
o.multiple = true;
|
o.multiple = true;
|
||||||
@@ -180,7 +267,13 @@ function createAdditionalSection(mainSection) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Value, 'procd_reload_delay', _('Interface Monitoring Delay'), _('Delay in milliseconds before reloading podkop after interface UP'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.Value,
|
||||||
|
'procd_reload_delay',
|
||||||
|
_('Interface Monitoring Delay'),
|
||||||
|
_('Delay in milliseconds before reloading podkop after interface UP'),
|
||||||
|
);
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
o.depends('mon_restart_ifaces', '1');
|
o.depends('mon_restart_ifaces', '1');
|
||||||
o.default = '2000';
|
o.default = '2000';
|
||||||
@@ -192,23 +285,47 @@ function createAdditionalSection(mainSection) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'dont_touch_dhcp', _('Dont touch my DHCP!'), _('Podkop will not change the DHCP config'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.Flag,
|
||||||
|
'dont_touch_dhcp',
|
||||||
|
_('Dont touch my DHCP!'),
|
||||||
|
_('Podkop will not change the DHCP config'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('additional', form.Flag, 'detour', _('Proxy download of lists'), _('Downloading all lists via main Proxy/VPN'));
|
o = mainSection.taboption(
|
||||||
|
'additional',
|
||||||
|
form.Flag,
|
||||||
|
'detour',
|
||||||
|
_('Proxy download of lists'),
|
||||||
|
_('Downloading all lists via main Proxy/VPN'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
|
|
||||||
// Extra IPs and exclusions (main section)
|
// Extra IPs and exclusions (main section)
|
||||||
o = mainSection.taboption('basic', form.Flag, 'exclude_from_ip_enabled', _('IP for exclusion'), _('Specify local IP addresses that will never use the configured route'));
|
o = mainSection.taboption(
|
||||||
|
'basic',
|
||||||
|
form.Flag,
|
||||||
|
'exclude_from_ip_enabled',
|
||||||
|
_('IP for exclusion'),
|
||||||
|
_('Specify local IP addresses that will never use the configured route'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
|
|
||||||
o = mainSection.taboption('basic', form.DynamicList, 'exclude_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses'));
|
o = mainSection.taboption(
|
||||||
|
'basic',
|
||||||
|
form.DynamicList,
|
||||||
|
'exclude_traffic_ip',
|
||||||
|
_('Local IPs'),
|
||||||
|
_('Enter valid IPv4 addresses'),
|
||||||
|
);
|
||||||
o.placeholder = 'IP';
|
o.placeholder = 'IP';
|
||||||
o.depends('exclude_from_ip_enabled', '1');
|
o.depends('exclude_from_ip_enabled', '1');
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -216,7 +333,7 @@ function createAdditionalSection(mainSection) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = main.validateIPV4(value);
|
const validation = main.validateIPV4(value);
|
||||||
@@ -225,15 +342,21 @@ function createAdditionalSection(mainSection) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = mainSection.taboption('basic', form.Flag, 'socks5', _('Mixed enable'), _('Browser port: 2080'));
|
o = mainSection.taboption(
|
||||||
|
'basic',
|
||||||
|
form.Flag,
|
||||||
|
'socks5',
|
||||||
|
_('Mixed enable'),
|
||||||
|
_('Browser port: 2080'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = 'main';
|
o.ucisection = 'main';
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseclass.extend({
|
return baseclass.extend({
|
||||||
createAdditionalSection
|
createAdditionalSection,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,19 +6,30 @@
|
|||||||
'require view.podkop.main as main';
|
'require view.podkop.main as main';
|
||||||
'require tools.widgets as widgets';
|
'require tools.widgets as widgets';
|
||||||
|
|
||||||
|
|
||||||
function createConfigSection(section) {
|
function createConfigSection(section) {
|
||||||
const s = section;
|
const s = section;
|
||||||
|
|
||||||
let o = s.tab('basic', _('Basic Settings'));
|
let o = s.tab('basic', _('Basic Settings'));
|
||||||
|
|
||||||
o = s.taboption('basic', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing'));
|
o = s.taboption(
|
||||||
o.value('proxy', ('Proxy'));
|
'basic',
|
||||||
o.value('vpn', ('VPN'));
|
form.ListValue,
|
||||||
o.value('block', ('Block'));
|
'mode',
|
||||||
|
_('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.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.ListValue, 'proxy_config_type', _('Configuration Type'), _('Select how to configure the proxy'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.ListValue,
|
||||||
|
'proxy_config_type',
|
||||||
|
_('Configuration Type'),
|
||||||
|
_('Select how to configure the proxy'),
|
||||||
|
);
|
||||||
o.value('url', _('Connection URL'));
|
o.value('url', _('Connection URL'));
|
||||||
o.value('outbound', _('Outbound Config'));
|
o.value('outbound', _('Outbound Config'));
|
||||||
o.value('urltest', _('URLTest'));
|
o.value('urltest', _('URLTest'));
|
||||||
@@ -26,7 +37,13 @@ function createConfigSection(section) {
|
|||||||
o.depends('mode', 'proxy');
|
o.depends('mode', 'proxy');
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.TextValue, 'proxy_string', _('Proxy Configuration URL'), '');
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.TextValue,
|
||||||
|
'proxy_string',
|
||||||
|
_('Proxy Configuration URL'),
|
||||||
|
'',
|
||||||
|
);
|
||||||
o.depends('proxy_config_type', 'url');
|
o.depends('proxy_config_type', 'url');
|
||||||
o.rows = 5;
|
o.rows = 5;
|
||||||
o.wrap = 'soft';
|
o.wrap = 'soft';
|
||||||
@@ -34,43 +51,70 @@ function createConfigSection(section) {
|
|||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
o.sectionDescriptions = new Map();
|
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.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.renderWidget = function (section_id, option_index, cfgvalue) {
|
o.renderWidget = function (section_id, option_index, cfgvalue) {
|
||||||
const original = form.TextValue.prototype.renderWidget.apply(this, [section_id, option_index, cfgvalue]);
|
const original = form.TextValue.prototype.renderWidget.apply(this, [
|
||||||
|
section_id,
|
||||||
|
option_index,
|
||||||
|
cfgvalue,
|
||||||
|
]);
|
||||||
const container = E('div', {});
|
const container = E('div', {});
|
||||||
container.appendChild(original);
|
container.appendChild(original);
|
||||||
|
|
||||||
if (cfgvalue) {
|
if (cfgvalue) {
|
||||||
try {
|
try {
|
||||||
const activeConfig = cfgvalue.split('\n')
|
const activeConfig = cfgvalue
|
||||||
.map(line => line.trim())
|
.split('\n')
|
||||||
.find(line => line && !line.startsWith('//'));
|
.map((line) => line.trim())
|
||||||
|
.find((line) => line && !line.startsWith('//'));
|
||||||
|
|
||||||
if (activeConfig) {
|
if (activeConfig) {
|
||||||
if (activeConfig.includes('#')) {
|
if (activeConfig.includes('#')) {
|
||||||
const label = activeConfig.split('#').pop();
|
const label = activeConfig.split('#').pop();
|
||||||
if (label && label.trim()) {
|
if (label && label.trim()) {
|
||||||
const decodedLabel = decodeURIComponent(label);
|
const decodedLabel = decodeURIComponent(label);
|
||||||
const descDiv = E('div', { 'class': 'cbi-value-description' }, _('Current config: ') + decodedLabel);
|
const descDiv = E(
|
||||||
|
'div',
|
||||||
|
{ class: 'cbi-value-description' },
|
||||||
|
_('Current config: ') + decodedLabel,
|
||||||
|
);
|
||||||
container.appendChild(descDiv);
|
container.appendChild(descDiv);
|
||||||
} else {
|
} else {
|
||||||
const descDiv = E('div', { 'class': 'cbi-value-description' }, _('Config without description'));
|
const descDiv = E(
|
||||||
|
'div',
|
||||||
|
{ class: 'cbi-value-description' },
|
||||||
|
_('Config without description'),
|
||||||
|
);
|
||||||
container.appendChild(descDiv);
|
container.appendChild(descDiv);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const descDiv = E('div', { 'class': 'cbi-value-description' }, _('Config without description'));
|
const descDiv = E(
|
||||||
|
'div',
|
||||||
|
{ class: 'cbi-value-description' },
|
||||||
|
_('Config without description'),
|
||||||
|
);
|
||||||
container.appendChild(descDiv);
|
container.appendChild(descDiv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error parsing config label:', e);
|
console.error('Error parsing config label:', e);
|
||||||
const descDiv = E('div', { 'class': 'cbi-value-description' }, _('Config without description'));
|
const descDiv = E(
|
||||||
|
'div',
|
||||||
|
{ class: 'cbi-value-description' },
|
||||||
|
_('Config without description'),
|
||||||
|
);
|
||||||
container.appendChild(descDiv);
|
container.appendChild(descDiv);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const defaultDesc = E('div', { 'class': 'cbi-value-description' },
|
const defaultDesc = E(
|
||||||
_('Enter connection string starting with vless:// or ss:// for proxy configuration. Add comments with // for backup configs'));
|
'div',
|
||||||
|
{ class: 'cbi-value-description' },
|
||||||
|
_(
|
||||||
|
'Enter connection string starting with vless:// or ss:// for proxy configuration. Add comments with // for backup configs',
|
||||||
|
),
|
||||||
|
);
|
||||||
container.appendChild(defaultDesc);
|
container.appendChild(defaultDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,16 +124,19 @@ function createConfigSection(section) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const activeConfig = value.split('\n')
|
const activeConfig = value
|
||||||
.map(line => line.trim())
|
.split('\n')
|
||||||
.find(line => line && !line.startsWith('//'));
|
.map((line) => line.trim())
|
||||||
|
.find((line) => line && !line.startsWith('//'));
|
||||||
|
|
||||||
if (!activeConfig) {
|
if (!activeConfig) {
|
||||||
return _('No active configuration found. At least one non-commented line is required.');
|
return _(
|
||||||
|
'No active configuration found. At least one non-commented line is required.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = main.validateProxyUrl(activeConfig);
|
const validation = main.validateProxyUrl(activeConfig);
|
||||||
@@ -98,20 +145,26 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return `${_('Invalid URL format:')} ${e?.message}`;
|
return `${_('Invalid URL format:')} ${e?.message}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.TextValue, 'outbound_json', _('Outbound Configuration'), _('Enter complete outbound configuration in JSON format'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.TextValue,
|
||||||
|
'outbound_json',
|
||||||
|
_('Outbound Configuration'),
|
||||||
|
_('Enter complete outbound configuration in JSON format'),
|
||||||
|
);
|
||||||
o.depends('proxy_config_type', 'outbound');
|
o.depends('proxy_config_type', 'outbound');
|
||||||
o.rows = 10;
|
o.rows = 10;
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = main.validateOutboundJson(value);
|
const validation = main.validateOutboundJson(value);
|
||||||
@@ -120,10 +173,15 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.DynamicList, 'urltest_proxy_links', _('URLTest Proxy Links'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.DynamicList,
|
||||||
|
'urltest_proxy_links',
|
||||||
|
_('URLTest Proxy Links'),
|
||||||
|
);
|
||||||
o.depends('proxy_config_type', 'urltest');
|
o.depends('proxy_config_type', 'urltest');
|
||||||
o.placeholder = 'vless://, ss://, trojan:// links';
|
o.placeholder = 'vless://, ss://, trojan:// links';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -133,7 +191,7 @@ function createConfigSection(section) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = main.validateProxyUrl(value);
|
const validation = main.validateProxyUrl(value);
|
||||||
@@ -142,16 +200,28 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.Flag, 'ss_uot', _('Shadowsocks UDP over TCP'), _('Apply for SS2022'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.Flag,
|
||||||
|
'ss_uot',
|
||||||
|
_('Shadowsocks UDP over TCP'),
|
||||||
|
_('Apply for SS2022'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.depends('mode', 'proxy');
|
o.depends('mode', 'proxy');
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', widgets.DeviceSelect, 'interface', _('Network Interface'), _('Select network interface for VPN connection'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
widgets.DeviceSelect,
|
||||||
|
'interface',
|
||||||
|
_('Network Interface'),
|
||||||
|
_('Select network interface for VPN connection'),
|
||||||
|
);
|
||||||
o.depends('mode', 'vpn');
|
o.depends('mode', 'vpn');
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
o.noaliases = true;
|
o.noaliases = true;
|
||||||
@@ -176,7 +246,7 @@ function createConfigSection(section) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to find the device object with the given name
|
// Try to find the device object with the given name
|
||||||
const device = this.devices.find(dev => dev.getName() === value);
|
const device = this.devices.find((dev) => dev.getName() === value);
|
||||||
|
|
||||||
// If no device is found, allow the value
|
// If no device is found, allow the value
|
||||||
if (!device) {
|
if (!device) {
|
||||||
@@ -193,13 +263,25 @@ function createConfigSection(section) {
|
|||||||
return !isWireless;
|
return !isWireless;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.Flag, 'domain_resolver_enabled', _('Domain Resolver'), _('Enable built-in DNS resolver for domains handled by this section'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.Flag,
|
||||||
|
'domain_resolver_enabled',
|
||||||
|
_('Domain Resolver'),
|
||||||
|
_('Enable built-in DNS resolver for domains handled by this section'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.depends('mode', 'vpn');
|
o.depends('mode', 'vpn');
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.ListValue, 'domain_resolver_dns_type', _('DNS Protocol Type'), _('Select the DNS protocol type for the domain resolver'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
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('doh', _('DNS over HTTPS (DoH)'));
|
||||||
o.value('dot', _('DNS over TLS (DoT)'));
|
o.value('dot', _('DNS over TLS (DoT)'));
|
||||||
o.value('udp', _('UDP (Unprotected DNS)'));
|
o.value('udp', _('UDP (Unprotected DNS)'));
|
||||||
@@ -208,7 +290,13 @@ function createConfigSection(section) {
|
|||||||
o.depends('domain_resolver_enabled', '1');
|
o.depends('domain_resolver_enabled', '1');
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.Value, 'domain_resolver_dns_server', _('DNS Server'), _('Select or enter DNS server address'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.Value,
|
||||||
|
'domain_resolver_dns_server',
|
||||||
|
_('DNS Server'),
|
||||||
|
_('Select or enter DNS server address'),
|
||||||
|
);
|
||||||
Object.entries(main.DNS_SERVER_OPTIONS).forEach(([key, label]) => {
|
Object.entries(main.DNS_SERVER_OPTIONS).forEach(([key, label]) => {
|
||||||
o.value(key, _(label));
|
o.value(key, _(label));
|
||||||
});
|
});
|
||||||
@@ -223,15 +311,27 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.Flag, 'community_lists_enabled', _('Community Lists'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.Flag,
|
||||||
|
'community_lists_enabled',
|
||||||
|
_('Community Lists'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.DynamicList, 'community_lists', _('Service List'), _('Select predefined service for routing') + ' <a href="https://github.com/itdoginfo/allow-domains" target="_blank">github.com/itdoginfo/allow-domains</a>');
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.DynamicList,
|
||||||
|
'community_lists',
|
||||||
|
_('Service List'),
|
||||||
|
_('Select predefined service for routing') +
|
||||||
|
' <a href="https://github.com/itdoginfo/allow-domains" target="_blank">github.com/itdoginfo/allow-domains</a>',
|
||||||
|
);
|
||||||
o.placeholder = 'Service list';
|
o.placeholder = 'Service list';
|
||||||
Object.entries(main.DOMAIN_LIST_OPTIONS).forEach(([key, label]) => {
|
Object.entries(main.DOMAIN_LIST_OPTIONS).forEach(([key, label]) => {
|
||||||
o.value(key, _(label));
|
o.value(key, _(label));
|
||||||
@@ -252,31 +352,52 @@ function createConfigSection(section) {
|
|||||||
let newValues = [...values];
|
let newValues = [...values];
|
||||||
let notifications = [];
|
let notifications = [];
|
||||||
|
|
||||||
const selectedRegionalOptions = main.REGIONAL_OPTIONS.filter(opt => newValues.includes(opt));
|
const selectedRegionalOptions = main.REGIONAL_OPTIONS.filter((opt) =>
|
||||||
|
newValues.includes(opt),
|
||||||
|
);
|
||||||
|
|
||||||
if (selectedRegionalOptions.length > 1) {
|
if (selectedRegionalOptions.length > 1) {
|
||||||
const lastSelected = selectedRegionalOptions[selectedRegionalOptions.length - 1];
|
const lastSelected =
|
||||||
|
selectedRegionalOptions[selectedRegionalOptions.length - 1];
|
||||||
const removedRegions = selectedRegionalOptions.slice(0, -1);
|
const removedRegions = selectedRegionalOptions.slice(0, -1);
|
||||||
newValues = newValues.filter(v => v === lastSelected || !main.REGIONAL_OPTIONS.includes(v));
|
newValues = newValues.filter(
|
||||||
notifications.push(E('p', { class: 'alert-message warning' }, [
|
(v) => v === lastSelected || !main.REGIONAL_OPTIONS.includes(v),
|
||||||
E('strong', {}, _('Regional options cannot be used together')), E('br'),
|
);
|
||||||
_('Warning: %s cannot be used together with %s. Previous selections have been removed.')
|
notifications.push(
|
||||||
.format(removedRegions.join(', '), lastSelected)
|
E('p', { class: 'alert-message warning' }, [
|
||||||
]));
|
E('strong', {}, _('Regional options cannot be used together')),
|
||||||
|
E('br'),
|
||||||
|
_(
|
||||||
|
'Warning: %s cannot be used together with %s. Previous selections have been removed.',
|
||||||
|
).format(removedRegions.join(', '), lastSelected),
|
||||||
|
]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newValues.includes('russia_inside')) {
|
if (newValues.includes('russia_inside')) {
|
||||||
const removedServices = newValues.filter(v => !main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v));
|
const removedServices = newValues.filter(
|
||||||
|
(v) => !main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v),
|
||||||
|
);
|
||||||
if (removedServices.length > 0) {
|
if (removedServices.length > 0) {
|
||||||
newValues = newValues.filter(v => main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v));
|
newValues = newValues.filter((v) =>
|
||||||
notifications.push(E('p', { class: 'alert-message warning' }, [
|
main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v),
|
||||||
E('strong', {}, _('Russia inside restrictions')), E('br'),
|
);
|
||||||
_('Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection.')
|
notifications.push(
|
||||||
.format(
|
E('p', { class: 'alert-message warning' }, [
|
||||||
main.ALLOWED_WITH_RUSSIA_INSIDE.map(key => main.DOMAIN_LIST_OPTIONS[key]).filter(label => label !== 'Russia inside').join(', '),
|
E('strong', {}, _('Russia inside restrictions')),
|
||||||
removedServices.join(', ')
|
E('br'),
|
||||||
|
_(
|
||||||
|
'Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection.',
|
||||||
|
).format(
|
||||||
|
main.ALLOWED_WITH_RUSSIA_INSIDE.map(
|
||||||
|
(key) => main.DOMAIN_LIST_OPTIONS[key],
|
||||||
)
|
)
|
||||||
]));
|
.filter((label) => label !== 'Russia inside')
|
||||||
|
.join(', '),
|
||||||
|
removedServices.join(', '),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +405,9 @@ function createConfigSection(section) {
|
|||||||
this.getUIElement(section_id).setValue(newValues);
|
this.getUIElement(section_id).setValue(newValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifications.forEach(notification => ui.addNotification(null, notification));
|
notifications.forEach((notification) =>
|
||||||
|
ui.addNotification(null, notification),
|
||||||
|
);
|
||||||
lastValues = newValues;
|
lastValues = newValues;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Error in onchange handler:', e);
|
console.error('Error in onchange handler:', e);
|
||||||
@@ -293,7 +416,13 @@ function createConfigSection(section) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.ListValue, 'user_domain_list_type', _('User Domain List Type'), _('Select how to add your custom domains'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.ListValue,
|
||||||
|
'user_domain_list_type',
|
||||||
|
_('User Domain List Type'),
|
||||||
|
_('Select how to add your custom domains'),
|
||||||
|
);
|
||||||
o.value('disabled', _('Disabled'));
|
o.value('disabled', _('Disabled'));
|
||||||
o.value('dynamic', _('Dynamic List'));
|
o.value('dynamic', _('Dynamic List'));
|
||||||
o.value('text', _('Text List'));
|
o.value('text', _('Text List'));
|
||||||
@@ -301,7 +430,15 @@ function createConfigSection(section) {
|
|||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.DynamicList, 'user_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.DynamicList,
|
||||||
|
'user_domains',
|
||||||
|
_('User Domains'),
|
||||||
|
_(
|
||||||
|
'Enter domain names without protocols (example: sub.example.com or example.com)',
|
||||||
|
),
|
||||||
|
);
|
||||||
o.placeholder = 'Domains list';
|
o.placeholder = 'Domains list';
|
||||||
o.depends('user_domain_list_type', 'dynamic');
|
o.depends('user_domain_list_type', 'dynamic');
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -309,7 +446,7 @@ function createConfigSection(section) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = main.validateDomain(value);
|
const validation = main.validateDomain(value);
|
||||||
@@ -318,11 +455,20 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.TextValue, 'user_domains_text', _('User Domains List'), _('Enter domain names separated by comma, space or newline. You can add comments after //'));
|
o = s.taboption(
|
||||||
o.placeholder = 'example.com, sub.example.com\n// Social networks\ndomain.com test.com // personal domains';
|
'basic',
|
||||||
|
form.TextValue,
|
||||||
|
'user_domains_text',
|
||||||
|
_('User Domains List'),
|
||||||
|
_(
|
||||||
|
'Enter domain names separated by comma, space or newline. You can add comments after //',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
o.placeholder =
|
||||||
|
'example.com, sub.example.com\n// Social networks\ndomain.com test.com // personal domains';
|
||||||
o.depends('user_domain_list_type', 'text');
|
o.depends('user_domain_list_type', 'text');
|
||||||
o.rows = 8;
|
o.rows = 8;
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -330,14 +476,14 @@ function createConfigSection(section) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const domains = main.parseValueList(value);
|
const domains = main.parseValueList(value);
|
||||||
|
|
||||||
if (!domains.length) {
|
if (!domains.length) {
|
||||||
return _(
|
return _(
|
||||||
'At least one valid domain must be specified. Comments-only content is not allowed.'
|
'At least one valid domain must be specified. Comments-only content is not allowed.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,8 +491,8 @@ function createConfigSection(section) {
|
|||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
const errors = results
|
const errors = results
|
||||||
.filter(validation => !validation.valid) // Leave only failed validations
|
.filter((validation) => !validation.valid) // Leave only failed validations
|
||||||
.map((validation) => _(`${validation.value}: ${validation.message}`)) // Collect validation errors
|
.map((validation) => _(`${validation.value}: ${validation.message}`)); // Collect validation errors
|
||||||
|
|
||||||
return [_('Validation errors:'), ...errors].join('\n');
|
return [_('Validation errors:'), ...errors].join('\n');
|
||||||
}
|
}
|
||||||
@@ -354,12 +500,24 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.Flag, 'local_domain_lists_enabled', _('Local Domain Lists'), _('Use the list from the router filesystem'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.Flag,
|
||||||
|
'local_domain_lists_enabled',
|
||||||
|
_('Local Domain Lists'),
|
||||||
|
_('Use the list from the router filesystem'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.DynamicList, 'local_domain_lists', _('Local Domain List Paths'), _('Enter the list file path'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.DynamicList,
|
||||||
|
'local_domain_lists',
|
||||||
|
_('Local Domain List Paths'),
|
||||||
|
_('Enter the list file path'),
|
||||||
|
);
|
||||||
o.placeholder = '/path/file.lst';
|
o.placeholder = '/path/file.lst';
|
||||||
o.depends('local_domain_lists_enabled', '1');
|
o.depends('local_domain_lists_enabled', '1');
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -367,7 +525,7 @@ function createConfigSection(section) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = main.validatePath(value);
|
const validation = main.validatePath(value);
|
||||||
@@ -376,15 +534,27 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.Flag, 'remote_domain_lists_enabled', _('Remote Domain Lists'), _('Download and use domain lists from remote URLs'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.Flag,
|
||||||
|
'remote_domain_lists_enabled',
|
||||||
|
_('Remote Domain Lists'),
|
||||||
|
_('Download and use domain lists from remote URLs'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.DynamicList, 'remote_domain_lists', _('Remote Domain URLs'), _('Enter full URLs starting with http:// or https://'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.DynamicList,
|
||||||
|
'remote_domain_lists',
|
||||||
|
_('Remote Domain URLs'),
|
||||||
|
_('Enter full URLs starting with http:// or https://'),
|
||||||
|
);
|
||||||
o.placeholder = 'URL';
|
o.placeholder = 'URL';
|
||||||
o.depends('remote_domain_lists_enabled', '1');
|
o.depends('remote_domain_lists_enabled', '1');
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -392,7 +562,7 @@ function createConfigSection(section) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = main.validateUrl(value);
|
const validation = main.validateUrl(value);
|
||||||
@@ -401,15 +571,27 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.Flag, 'local_subnet_lists_enabled', _('Local Subnet Lists'), _('Use the list from the router filesystem'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.Flag,
|
||||||
|
'local_subnet_lists_enabled',
|
||||||
|
_('Local Subnet Lists'),
|
||||||
|
_('Use the list from the router filesystem'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.DynamicList, 'local_subnet_lists', _('Local Subnet List Paths'), _('Enter the list file path'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.DynamicList,
|
||||||
|
'local_subnet_lists',
|
||||||
|
_('Local Subnet List Paths'),
|
||||||
|
_('Enter the list file path'),
|
||||||
|
);
|
||||||
o.placeholder = '/path/file.lst';
|
o.placeholder = '/path/file.lst';
|
||||||
o.depends('local_subnet_lists_enabled', '1');
|
o.depends('local_subnet_lists_enabled', '1');
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -417,7 +599,7 @@ function createConfigSection(section) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = main.validatePath(value);
|
const validation = main.validatePath(value);
|
||||||
@@ -426,10 +608,16 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.ListValue, 'user_subnet_list_type', _('User Subnet List Type'), _('Select how to add your custom subnets'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.ListValue,
|
||||||
|
'user_subnet_list_type',
|
||||||
|
_('User Subnet List Type'),
|
||||||
|
_('Select how to add your custom subnets'),
|
||||||
|
);
|
||||||
o.value('disabled', _('Disabled'));
|
o.value('disabled', _('Disabled'));
|
||||||
o.value('dynamic', _('Dynamic List'));
|
o.value('dynamic', _('Dynamic List'));
|
||||||
o.value('text', _('Text List (comma/space/newline separated)'));
|
o.value('text', _('Text List (comma/space/newline separated)'));
|
||||||
@@ -437,7 +625,15 @@ function createConfigSection(section) {
|
|||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.DynamicList, 'user_subnets', _('User Subnets'), _('Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.DynamicList,
|
||||||
|
'user_subnets',
|
||||||
|
_('User Subnets'),
|
||||||
|
_(
|
||||||
|
'Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses',
|
||||||
|
),
|
||||||
|
);
|
||||||
o.placeholder = 'IP or subnet';
|
o.placeholder = 'IP or subnet';
|
||||||
o.depends('user_subnet_list_type', 'dynamic');
|
o.depends('user_subnet_list_type', 'dynamic');
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -445,7 +641,7 @@ function createConfigSection(section) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = main.validateSubnet(value);
|
const validation = main.validateSubnet(value);
|
||||||
@@ -454,11 +650,20 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.TextValue, 'user_subnets_text', _('User Subnets List'), _('Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline. You can add comments after //'));
|
o = s.taboption(
|
||||||
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';
|
'basic',
|
||||||
|
form.TextValue,
|
||||||
|
'user_subnets_text',
|
||||||
|
_('User Subnets List'),
|
||||||
|
_(
|
||||||
|
'Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline. You can add comments after //',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
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.depends('user_subnet_list_type', 'text');
|
||||||
o.rows = 10;
|
o.rows = 10;
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -466,14 +671,14 @@ function createConfigSection(section) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const subnets = main.parseValueList(value);
|
const subnets = main.parseValueList(value);
|
||||||
|
|
||||||
if (!subnets.length) {
|
if (!subnets.length) {
|
||||||
return _(
|
return _(
|
||||||
'At least one valid subnet or IP must be specified. Comments-only content is not allowed.'
|
'At least one valid subnet or IP must be specified. Comments-only content is not allowed.',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,8 +686,8 @@ function createConfigSection(section) {
|
|||||||
|
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
const errors = results
|
const errors = results
|
||||||
.filter(validation => !validation.valid) // Leave only failed validations
|
.filter((validation) => !validation.valid) // Leave only failed validations
|
||||||
.map((validation) => _(`${validation.value}: ${validation.message}`)) // Collect validation errors
|
.map((validation) => _(`${validation.value}: ${validation.message}`)); // Collect validation errors
|
||||||
|
|
||||||
return [_('Validation errors:'), ...errors].join('\n');
|
return [_('Validation errors:'), ...errors].join('\n');
|
||||||
}
|
}
|
||||||
@@ -490,12 +695,24 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.Flag, 'remote_subnet_lists_enabled', _('Remote Subnet Lists'), _('Download and use subnet lists from remote URLs'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.Flag,
|
||||||
|
'remote_subnet_lists_enabled',
|
||||||
|
_('Remote Subnet Lists'),
|
||||||
|
_('Download and use subnet lists from remote URLs'),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.DynamicList, 'remote_subnet_lists', _('Remote Subnet URLs'), _('Enter full URLs starting with http:// or https://'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.DynamicList,
|
||||||
|
'remote_subnet_lists',
|
||||||
|
_('Remote Subnet URLs'),
|
||||||
|
_('Enter full URLs starting with http:// or https://'),
|
||||||
|
);
|
||||||
o.placeholder = 'URL';
|
o.placeholder = 'URL';
|
||||||
o.depends('remote_subnet_lists_enabled', '1');
|
o.depends('remote_subnet_lists_enabled', '1');
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -503,7 +720,7 @@ function createConfigSection(section) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = main.validateUrl(value);
|
const validation = main.validateUrl(value);
|
||||||
@@ -512,15 +729,29 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
|
|
||||||
o = s.taboption('basic', form.Flag, 'all_traffic_from_ip_enabled', _('IP for full redirection'), _('Specify local IP addresses whose traffic will always use the configured route'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.Flag,
|
||||||
|
'all_traffic_from_ip_enabled',
|
||||||
|
_('IP for full redirection'),
|
||||||
|
_(
|
||||||
|
'Specify local IP addresses whose traffic will always use the configured route',
|
||||||
|
),
|
||||||
|
);
|
||||||
o.default = '0';
|
o.default = '0';
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
o.ucisection = s.section;
|
o.ucisection = s.section;
|
||||||
|
|
||||||
o = s.taboption('basic', form.DynamicList, 'all_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses'));
|
o = s.taboption(
|
||||||
|
'basic',
|
||||||
|
form.DynamicList,
|
||||||
|
'all_traffic_ip',
|
||||||
|
_('Local IPs'),
|
||||||
|
_('Enter valid IPv4 addresses'),
|
||||||
|
);
|
||||||
o.placeholder = 'IP';
|
o.placeholder = 'IP';
|
||||||
o.depends('all_traffic_from_ip_enabled', '1');
|
o.depends('all_traffic_from_ip_enabled', '1');
|
||||||
o.rmempty = false;
|
o.rmempty = false;
|
||||||
@@ -528,7 +759,7 @@ function createConfigSection(section) {
|
|||||||
o.validate = function (section_id, value) {
|
o.validate = function (section_id, value) {
|
||||||
// Optional
|
// Optional
|
||||||
if (!value || value.length === 0) {
|
if (!value || value.length === 0) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const validation = main.validateIPV4(value);
|
const validation = main.validateIPV4(value);
|
||||||
@@ -537,10 +768,10 @@ function createConfigSection(section) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _(validation.message)
|
return _(validation.message);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseclass.extend({
|
return baseclass.extend({
|
||||||
createConfigSection
|
createConfigSection,
|
||||||
});
|
});
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,14 +15,12 @@ let errorPollTimer = null;
|
|||||||
|
|
||||||
// Helper function to fetch errors from the podkop command
|
// Helper function to fetch errors from the podkop command
|
||||||
async function getPodkopErrors() {
|
async function getPodkopErrors() {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
safeExec('/usr/bin/podkop', ['check_logs'], 'P0_PRIORITY', result => {
|
safeExec('/usr/bin/podkop', ['check_logs'], 'P0_PRIORITY', (result) => {
|
||||||
if (!result || !result.stdout) return resolve([]);
|
if (!result || !result.stdout) return resolve([]);
|
||||||
|
|
||||||
const logs = result.stdout.split('\n');
|
const logs = result.stdout.split('\n');
|
||||||
const errors = logs.filter(log =>
|
const errors = logs.filter((log) => log.includes('[critical]'));
|
||||||
log.includes('[critical]')
|
|
||||||
);
|
|
||||||
|
|
||||||
resolve(errors);
|
resolve(errors);
|
||||||
});
|
});
|
||||||
@@ -31,20 +29,29 @@ async function getPodkopErrors() {
|
|||||||
|
|
||||||
// Show error notification to the user
|
// Show error notification to the user
|
||||||
function showErrorNotification(error, isMultiple = false) {
|
function showErrorNotification(error, isMultiple = false) {
|
||||||
const notificationContent = E('div', { 'class': 'alert-message error' }, [
|
const notificationContent = E('div', { class: 'alert-message error' }, [
|
||||||
E('pre', { 'class': 'error-log' }, error)
|
E('pre', { class: 'error-log' }, error),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
ui.addNotification(null, notificationContent);
|
ui.addNotification(null, notificationContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function for command execution with prioritization
|
// Helper function for command execution with prioritization
|
||||||
function safeExec(command, args, priority, callback, timeout = main.COMMAND_TIMEOUT) {
|
function safeExec(
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
priority,
|
||||||
|
callback,
|
||||||
|
timeout = main.COMMAND_TIMEOUT,
|
||||||
|
) {
|
||||||
// Default to highest priority execution if priority is not provided or invalid
|
// Default to highest priority execution if priority is not provided or invalid
|
||||||
let schedulingDelay = main.COMMAND_SCHEDULING.P0_PRIORITY;
|
let schedulingDelay = main.COMMAND_SCHEDULING.P0_PRIORITY;
|
||||||
|
|
||||||
// If priority is a string, try to get the corresponding delay value
|
// If priority is a string, try to get the corresponding delay value
|
||||||
if (typeof priority === 'string' && main.COMMAND_SCHEDULING[priority] !== undefined) {
|
if (
|
||||||
|
typeof priority === 'string' &&
|
||||||
|
main.COMMAND_SCHEDULING[priority] !== undefined
|
||||||
|
) {
|
||||||
schedulingDelay = main.COMMAND_SCHEDULING[priority];
|
schedulingDelay = main.COMMAND_SCHEDULING[priority];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +66,7 @@ function safeExec(command, args, priority, callback, timeout = main.COMMAND_TIME
|
|||||||
controller.signal.addEventListener('abort', () => {
|
controller.signal.addEventListener('abort', () => {
|
||||||
reject(new Error('Command execution timed out'));
|
reject(new Error('Command execution timed out'));
|
||||||
});
|
});
|
||||||
})
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
@@ -70,7 +77,9 @@ function safeExec(command, args, priority, callback, timeout = main.COMMAND_TIME
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Command execution failed or timed out: ${command} ${args.join(' ')}`);
|
console.warn(
|
||||||
|
`Command execution failed or timed out: ${command} ${args.join(' ')}`,
|
||||||
|
);
|
||||||
const errorResult = { stdout: '', stderr: error.message, error: error };
|
const errorResult = { stdout: '', stderr: error.message, error: error };
|
||||||
|
|
||||||
if (callback && typeof callback === 'function') {
|
if (callback && typeof callback === 'function') {
|
||||||
@@ -84,8 +93,7 @@ function safeExec(command, args, priority, callback, timeout = main.COMMAND_TIME
|
|||||||
if (callback && typeof callback === 'function') {
|
if (callback && typeof callback === 'function') {
|
||||||
setTimeout(executeCommand, schedulingDelay);
|
setTimeout(executeCommand, schedulingDelay);
|
||||||
return;
|
return;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return executeCommand();
|
return executeCommand();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,19 +105,19 @@ async function checkForCriticalErrors() {
|
|||||||
|
|
||||||
if (errors && errors.length > 0) {
|
if (errors && errors.length > 0) {
|
||||||
// Filter out errors we've already seen
|
// Filter out errors we've already seen
|
||||||
const newErrors = errors.filter(error => !lastErrorsSet.has(error));
|
const newErrors = errors.filter((error) => !lastErrorsSet.has(error));
|
||||||
|
|
||||||
if (newErrors.length > 0) {
|
if (newErrors.length > 0) {
|
||||||
// On initial check, just store errors without showing notifications
|
// On initial check, just store errors without showing notifications
|
||||||
if (!isInitialCheck) {
|
if (!isInitialCheck) {
|
||||||
// Show each new error as a notification
|
// Show each new error as a notification
|
||||||
newErrors.forEach(error => {
|
newErrors.forEach((error) => {
|
||||||
showErrorNotification(error, newErrors.length > 1);
|
showErrorNotification(error, newErrors.length > 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new errors to our set of seen errors
|
// Add new errors to our set of seen errors
|
||||||
newErrors.forEach(error => lastErrorsSet.add(error));
|
newErrors.forEach((error) => lastErrorsSet.add(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +141,10 @@ function startErrorPolling() {
|
|||||||
checkForCriticalErrors();
|
checkForCriticalErrors();
|
||||||
|
|
||||||
// Then set up periodic checks
|
// Then set up periodic checks
|
||||||
errorPollTimer = setInterval(checkForCriticalErrors, main.ERROR_POLL_INTERVAL);
|
errorPollTimer = setInterval(
|
||||||
|
checkForCriticalErrors,
|
||||||
|
main.ERROR_POLL_INTERVAL,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop polling for errors
|
// Stop polling for errors
|
||||||
@@ -148,5 +159,5 @@ return baseclass.extend({
|
|||||||
startErrorPolling,
|
startErrorPolling,
|
||||||
stopErrorPolling,
|
stopErrorPolling,
|
||||||
checkForCriticalErrors,
|
checkForCriticalErrors,
|
||||||
safeExec
|
safeExec,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user