fix: run prettier for luci app js assets

This commit is contained in:
divocat
2025-10-05 16:12:56 +03:00
parent d9a4f50f62
commit eb60e6edec
4 changed files with 2216 additions and 1484 deletions

View File

@@ -7,22 +7,46 @@
function createAdditionalSection(mainSection) {
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.rmempty = false;
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.rmempty = false;
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.rmempty = false;
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]) => {
o.value(key, _(label));
});
@@ -30,7 +54,13 @@ function createAdditionalSection(mainSection) {
o.rmempty = false;
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('dot', _('DNS over TLS (DoT)'));
o.value('udp', _('UDP (Unprotected DNS)'));
@@ -38,7 +68,13 @@ function createAdditionalSection(mainSection) {
o.rmempty = false;
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]) => {
o.value(key, _(label));
});
@@ -55,7 +91,15 @@ function createAdditionalSection(mainSection) {
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]) => {
o.value(key, _(label));
});
@@ -72,7 +116,13 @@ function createAdditionalSection(mainSection) {
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.rmempty = false;
o.ucisection = 'main';
@@ -89,16 +139,35 @@ function createAdditionalSection(mainSection) {
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('/tmp/sing-box/config.json', 'RAM (/tmp/sing-box/config.json)');
o.default = '/etc/sing-box/config.json';
o.rmempty = false;
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('/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.rmempty = false;
o.ucisection = 'main';
@@ -123,7 +192,13 @@ function createAdditionalSection(mainSection) {
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.default = 'br-lan';
o.noaliases = true;
@@ -138,7 +213,7 @@ function createAdditionalSection(mainSection) {
}
// 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 (!device) {
@@ -156,12 +231,24 @@ function createAdditionalSection(mainSection) {
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.rmempty = false;
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.depends('mon_restart_ifaces', '1');
o.multiple = true;
@@ -180,7 +267,13 @@ function createAdditionalSection(mainSection) {
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.depends('mon_restart_ifaces', '1');
o.default = '2000';
@@ -192,23 +285,47 @@ function createAdditionalSection(mainSection) {
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.rmempty = false;
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.rmempty = false;
o.ucisection = 'main';
// 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.rmempty = false;
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.depends('exclude_from_ip_enabled', '1');
o.rmempty = false;
@@ -216,7 +333,7 @@ function createAdditionalSection(mainSection) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
return true;
}
const validation = main.validateIPV4(value);
@@ -225,15 +342,21 @@ function createAdditionalSection(mainSection) {
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.rmempty = false;
o.ucisection = 'main';
}
return baseclass.extend({
createAdditionalSection
createAdditionalSection,
});

View File

@@ -6,19 +6,30 @@
'require view.podkop.main as main';
'require tools.widgets as widgets';
function createConfigSection(section) {
const s = section;
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.value('proxy', ('Proxy'));
o.value('vpn', ('VPN'));
o.value('block', ('Block'));
o = s.taboption(
'basic',
form.ListValue,
'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 = 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('outbound', _('Outbound Config'));
o.value('urltest', _('URLTest'));
@@ -26,7 +37,13 @@ function createConfigSection(section) {
o.depends('mode', 'proxy');
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.rows = 5;
o.wrap = 'soft';
@@ -34,43 +51,70 @@ function createConfigSection(section) {
o.rmempty = false;
o.ucisection = s.section;
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) {
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', {});
container.appendChild(original);
if (cfgvalue) {
try {
const activeConfig = cfgvalue.split('\n')
.map(line => line.trim())
.find(line => line && !line.startsWith('//'));
const activeConfig = cfgvalue
.split('\n')
.map((line) => line.trim())
.find((line) => line && !line.startsWith('//'));
if (activeConfig) {
if (activeConfig.includes('#')) {
const label = activeConfig.split('#').pop();
if (label && label.trim()) {
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);
} 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);
}
} 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);
}
}
} catch (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);
}
} else {
const defaultDesc = E('div', { 'class': 'cbi-value-description' },
_('Enter connection string starting with vless:// or ss:// for proxy configuration. Add comments with // for backup configs'));
const defaultDesc = E(
'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);
}
@@ -80,16 +124,19 @@ function createConfigSection(section) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
return true;
}
try {
const activeConfig = value.split('\n')
.map(line => line.trim())
.find(line => line && !line.startsWith('//'));
const activeConfig = value
.split('\n')
.map((line) => line.trim())
.find((line) => line && !line.startsWith('//'));
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);
@@ -98,20 +145,26 @@ function createConfigSection(section) {
return true;
}
return _(validation.message)
return _(validation.message);
} catch (e) {
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.rows = 10;
o.ucisection = s.section;
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
return true;
}
const validation = main.validateOutboundJson(value);
@@ -120,10 +173,15 @@ function createConfigSection(section) {
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.placeholder = 'vless://, ss://, trojan:// links';
o.rmempty = false;
@@ -133,7 +191,7 @@ function createConfigSection(section) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
return true;
}
const validation = main.validateProxyUrl(value);
@@ -142,16 +200,28 @@ function createConfigSection(section) {
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.depends('mode', 'proxy');
o.rmempty = false;
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.ucisection = s.section;
o.noaliases = true;
@@ -176,7 +246,7 @@ function createConfigSection(section) {
}
// 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 (!device) {
@@ -193,13 +263,25 @@ function createConfigSection(section) {
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.rmempty = false;
o.depends('mode', 'vpn');
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('dot', _('DNS over TLS (DoT)'));
o.value('udp', _('UDP (Unprotected DNS)'));
@@ -208,7 +290,13 @@ function createConfigSection(section) {
o.depends('domain_resolver_enabled', '1');
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]) => {
o.value(key, _(label));
});
@@ -223,15 +311,27 @@ function createConfigSection(section) {
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.rmempty = false;
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';
Object.entries(main.DOMAIN_LIST_OPTIONS).forEach(([key, label]) => {
o.value(key, _(label));
@@ -252,31 +352,52 @@ function createConfigSection(section) {
let newValues = [...values];
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) {
const lastSelected = selectedRegionalOptions[selectedRegionalOptions.length - 1];
const lastSelected =
selectedRegionalOptions[selectedRegionalOptions.length - 1];
const removedRegions = selectedRegionalOptions.slice(0, -1);
newValues = newValues.filter(v => v === lastSelected || !main.REGIONAL_OPTIONS.includes(v));
notifications.push(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)
]));
newValues = newValues.filter(
(v) => v === lastSelected || !main.REGIONAL_OPTIONS.includes(v),
);
notifications.push(
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')) {
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) {
newValues = newValues.filter(v => main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v));
notifications.push(E('p', { class: 'alert-message warning' }, [
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.')
.format(
main.ALLOWED_WITH_RUSSIA_INSIDE.map(key => main.DOMAIN_LIST_OPTIONS[key]).filter(label => label !== 'Russia inside').join(', '),
removedServices.join(', ')
newValues = newValues.filter((v) =>
main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v),
);
notifications.push(
E('p', { class: 'alert-message warning' }, [
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.',
).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);
}
notifications.forEach(notification => ui.addNotification(null, notification));
notifications.forEach((notification) =>
ui.addNotification(null, notification),
);
lastValues = newValues;
} catch (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('dynamic', _('Dynamic List'));
o.value('text', _('Text List'));
@@ -301,7 +430,15 @@ function createConfigSection(section) {
o.rmempty = false;
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.depends('user_domain_list_type', 'dynamic');
o.rmempty = false;
@@ -309,7 +446,7 @@ function createConfigSection(section) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
return true;
}
const validation = main.validateDomain(value);
@@ -318,11 +455,20 @@ function createConfigSection(section) {
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.placeholder = 'example.com, sub.example.com\n// Social networks\ndomain.com test.com // personal domains';
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.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;
@@ -330,14 +476,14 @@ function createConfigSection(section) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
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.'
'At least one valid domain must be specified. Comments-only content is not allowed.',
);
}
@@ -345,8 +491,8 @@ function createConfigSection(section) {
if (!valid) {
const errors = results
.filter(validation => !validation.valid) // Leave only failed validations
.map((validation) => _(`${validation.value}: ${validation.message}`)) // Collect validation errors
.filter((validation) => !validation.valid) // Leave only failed validations
.map((validation) => _(`${validation.value}: ${validation.message}`)); // Collect validation errors
return [_('Validation errors:'), ...errors].join('\n');
}
@@ -354,12 +500,24 @@ function createConfigSection(section) {
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.rmempty = false;
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.depends('local_domain_lists_enabled', '1');
o.rmempty = false;
@@ -367,7 +525,7 @@ function createConfigSection(section) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
return true;
}
const validation = main.validatePath(value);
@@ -376,15 +534,27 @@ function createConfigSection(section) {
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.rmempty = false;
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.depends('remote_domain_lists_enabled', '1');
o.rmempty = false;
@@ -392,7 +562,7 @@ function createConfigSection(section) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
return true;
}
const validation = main.validateUrl(value);
@@ -401,15 +571,27 @@ function createConfigSection(section) {
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.rmempty = false;
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.depends('local_subnet_lists_enabled', '1');
o.rmempty = false;
@@ -417,7 +599,7 @@ function createConfigSection(section) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
return true;
}
const validation = main.validatePath(value);
@@ -426,10 +608,16 @@ function createConfigSection(section) {
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('dynamic', _('Dynamic List'));
o.value('text', _('Text List (comma/space/newline separated)'));
@@ -437,7 +625,15 @@ function createConfigSection(section) {
o.rmempty = false;
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.depends('user_subnet_list_type', 'dynamic');
o.rmempty = false;
@@ -445,7 +641,7 @@ function createConfigSection(section) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
return true;
}
const validation = main.validateSubnet(value);
@@ -454,11 +650,20 @@ function createConfigSection(section) {
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.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 = 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.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;
@@ -466,14 +671,14 @@ function createConfigSection(section) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
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.'
'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) {
const errors = results
.filter(validation => !validation.valid) // Leave only failed validations
.map((validation) => _(`${validation.value}: ${validation.message}`)) // Collect validation errors
.filter((validation) => !validation.valid) // Leave only failed validations
.map((validation) => _(`${validation.value}: ${validation.message}`)); // Collect validation errors
return [_('Validation errors:'), ...errors].join('\n');
}
@@ -490,12 +695,24 @@ function createConfigSection(section) {
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.rmempty = false;
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.depends('remote_subnet_lists_enabled', '1');
o.rmempty = false;
@@ -503,7 +720,7 @@ function createConfigSection(section) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
return true;
}
const validation = main.validateUrl(value);
@@ -512,15 +729,29 @@ function createConfigSection(section) {
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.rmempty = false;
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.depends('all_traffic_from_ip_enabled', '1');
o.rmempty = false;
@@ -528,7 +759,7 @@ function createConfigSection(section) {
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
return true
return true;
}
const validation = main.validateIPV4(value);
@@ -537,10 +768,10 @@ function createConfigSection(section) {
return true;
}
return _(validation.message)
return _(validation.message);
};
}
return baseclass.extend({
createConfigSection
createConfigSection,
});

View File

@@ -15,14 +15,12 @@ let errorPollTimer = null;
// Helper function to fetch errors from the podkop command
async function getPodkopErrors() {
return new Promise(resolve => {
safeExec('/usr/bin/podkop', ['check_logs'], 'P0_PRIORITY', result => {
return new Promise((resolve) => {
safeExec('/usr/bin/podkop', ['check_logs'], 'P0_PRIORITY', (result) => {
if (!result || !result.stdout) return resolve([]);
const logs = result.stdout.split('\n');
const errors = logs.filter(log =>
log.includes('[critical]')
);
const errors = logs.filter((log) => log.includes('[critical]'));
resolve(errors);
});
@@ -31,20 +29,29 @@ async function getPodkopErrors() {
// Show error notification to the user
function showErrorNotification(error, isMultiple = false) {
const notificationContent = E('div', { 'class': 'alert-message error' }, [
E('pre', { 'class': 'error-log' }, error)
const notificationContent = E('div', { class: 'alert-message error' }, [
E('pre', { class: 'error-log' }, error),
]);
ui.addNotification(null, notificationContent);
}
// 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
let schedulingDelay = main.COMMAND_SCHEDULING.P0_PRIORITY;
// 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];
}
@@ -59,7 +66,7 @@ function safeExec(command, args, priority, callback, timeout = main.COMMAND_TIME
controller.signal.addEventListener('abort', () => {
reject(new Error('Command execution timed out'));
});
})
}),
]);
clearTimeout(timeoutId);
@@ -70,7 +77,9 @@ function safeExec(command, args, priority, callback, timeout = main.COMMAND_TIME
return result;
} 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 };
if (callback && typeof callback === 'function') {
@@ -84,8 +93,7 @@ function safeExec(command, args, priority, callback, timeout = main.COMMAND_TIME
if (callback && typeof callback === 'function') {
setTimeout(executeCommand, schedulingDelay);
return;
}
else {
} else {
return executeCommand();
}
}
@@ -97,19 +105,19 @@ async function checkForCriticalErrors() {
if (errors && errors.length > 0) {
// 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) {
// On initial check, just store errors without showing notifications
if (!isInitialCheck) {
// Show each new error as a notification
newErrors.forEach(error => {
newErrors.forEach((error) => {
showErrorNotification(error, newErrors.length > 1);
});
}
// 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();
// Then set up periodic checks
errorPollTimer = setInterval(checkForCriticalErrors, main.ERROR_POLL_INTERVAL);
errorPollTimer = setInterval(
checkForCriticalErrors,
main.ERROR_POLL_INTERVAL,
);
}
// Stop polling for errors
@@ -148,5 +159,5 @@ return baseclass.extend({
startErrorPolling,
stopErrorPolling,
checkForCriticalErrors,
safeExec
safeExec,
});