fix: run prettier for all js files

This commit is contained in:
divocat
2025-10-20 22:41:07 +03:00
parent 4186292aa7
commit f4be831b5e
5 changed files with 980 additions and 938 deletions

View File

@@ -1,13 +1,13 @@
'use strict';
'require baseclass';
'require form';
'require ui';
'require uci';
'require fs';
'require view.podkop.main as main';
"use strict";
"require baseclass";
"require form";
"require ui";
"require uci";
"require fs";
"require view.podkop.main as main";
function createDashboardContent(section) {
const o = section.option(form.DummyValue, '_mount_node');
const o = section.option(form.DummyValue, "_mount_node");
o.rawhtml = true;
o.cfgvalue = () => {
main.DashboardTab.initController();

View File

@@ -1,13 +1,13 @@
'use strict';
'require baseclass';
'require form';
'require ui';
'require uci';
'require fs';
'require view.podkop.main as main';
"use strict";
"require baseclass";
"require form";
"require ui";
"require uci";
"require fs";
"require view.podkop.main as main";
function createDiagnosticContent(section) {
const o = section.option(form.DummyValue, '_mount_node');
const o = section.option(form.DummyValue, "_mount_node");
o.rawhtml = true;
o.cfgvalue = () => {
main.DiagnosticTab.initController();

View File

@@ -1,77 +1,98 @@
'use strict';
'require view';
'require form';
'require baseclass';
'require network';
'require view.podkop.main as main';
"use strict";
"require view";
"require form";
"require baseclass";
"require network";
"require view.podkop.main as main";
// Settings content
'require view.podkop.settings as settings';
"require view.podkop.settings as settings";
// Sections content
'require view.podkop.section as section';
"require view.podkop.section as section";
// Dashboard content
'require view.podkop.dashboard as dashboard';
"require view.podkop.dashboard as dashboard";
// Diagnostic content
'require view.podkop.diagnostic as diagnostic';
"require view.podkop.diagnostic as diagnostic";
const EntryPoint = {
async render() {
main.injectGlobalStyles();
const podkopMap = new form.Map('podkop', _('Podkop Settings'), _('Configuration for Podkop service'));
const podkopMap = new form.Map(
"podkop",
_("Podkop Settings"),
_("Configuration for Podkop service"),
);
// Enable tab views
podkopMap.tabbed = true;
// Sections tab
const sectionsSection = podkopMap.section(form.TypedSection, 'section', _('Sections'));
const sectionsSection = podkopMap.section(
form.TypedSection,
"section",
_("Sections"),
);
sectionsSection.anonymous = false;
sectionsSection.addremove = true;
sectionsSection.template = 'cbi/simpleform';
sectionsSection.template = "cbi/simpleform";
// Render section content
section.createSectionContent(sectionsSection);
// Settings tab
const settingsSection = podkopMap.section(form.TypedSection, 'settings', _('Settings'));
const settingsSection = podkopMap.section(
form.TypedSection,
"settings",
_("Settings"),
);
settingsSection.anonymous = true;
settingsSection.addremove = false;
// Make it named [ config settings 'settings' ]
settingsSection.cfgsections = function () { return ['settings']; };
settingsSection.cfgsections = function () {
return ["settings"];
};
// Render settings content
settings.createSettingsContent(settingsSection);
// Diagnostic tab
const diagnosticSection = podkopMap.section(form.TypedSection, 'diagnostic', _('Diagnostics'));
const diagnosticSection = podkopMap.section(
form.TypedSection,
"diagnostic",
_("Diagnostics"),
);
diagnosticSection.anonymous = true;
diagnosticSection.addremove = false;
diagnosticSection.cfgsections = function () { return ['diagnostic']; };
diagnosticSection.cfgsections = function () {
return ["diagnostic"];
};
// Render diagnostic content
diagnostic.createDiagnosticContent(diagnosticSection);
// Dashboard tab
const dashboardSection = podkopMap.section(form.TypedSection, 'dashboard', _('Dashboard'));
const dashboardSection = podkopMap.section(
form.TypedSection,
"dashboard",
_("Dashboard"),
);
dashboardSection.anonymous = true;
dashboardSection.addremove = false;
dashboardSection.cfgsections = function () { return ['dashboard']; };
dashboardSection.cfgsections = function () {
return ["dashboard"];
};
// Render dashboard content
dashboard.createDashboardContent(dashboardSection);
// Inject core service
main.coreService();
return podkopMap.render();
}
}
},
};
return view.extend(EntryPoint);

View File

@@ -1,49 +1,49 @@
'use strict';
'require form';
'require baseclass';
'require ui';
'require tools.widgets as widgets';
'require view.podkop.main as main';
"use strict";
"require form";
"require baseclass";
"require ui";
"require tools.widgets as widgets";
"require view.podkop.main as main";
function createSectionContent(section) {
let o = section.option(
form.ListValue,
'connection_type',
_('Connection Type'),
_('Select between VPN and Proxy connection methods for traffic routing'),
"connection_type",
_("Connection Type"),
_("Select between VPN and Proxy connection methods for traffic routing"),
);
o.value('proxy', 'Proxy');
o.value('vpn', 'VPN');
o.value('block', 'Block');
o.value("proxy", "Proxy");
o.value("vpn", "VPN");
o.value("block", "Block");
o = section.option(
form.ListValue,
'proxy_config_type',
_('Configuration Type'),
_('Select how to configure the proxy'),
"proxy_config_type",
_("Configuration Type"),
_("Select how to configure the proxy"),
);
o.value('url', _('Connection URL'));
o.value('outbound', _('Outbound Config'));
o.value('urltest', _('URLTest'));
o.default = 'url';
o.depends('connection_type', 'proxy');
o.value("url", _("Connection URL"));
o.value("outbound", _("Outbound Config"));
o.value("urltest", _("URLTest"));
o.default = "url";
o.depends("connection_type", "proxy");
o = section.option(
form.TextValue,
'proxy_string',
_('Proxy Configuration URL'),
'',
"proxy_string",
_("Proxy Configuration URL"),
"",
);
o.depends('proxy_config_type', 'url');
o.depends("proxy_config_type", "url");
o.rows = 5;
// Enable soft wrapping for multi-line proxy URLs (e.g., for URLTest proxy links)
o.wrap = 'soft';
o.wrap = "soft";
// Render as a textarea to allow multiple proxy URLs/configs
o.textarea = true;
o.rmempty = false;
o.sectionDescriptions = new Map();
o.placeholder = 'vless://uuid@server:port?type=tcp&security=tls#main\n// backup ss://method:pass@server:port\n// backup2 vless://uuid@server:port?type=grpc&security=reality#alt\n// backup3 trojan://04agAQapcl@127.0.0.1:33641?type=tcp&security=none#trojan-tcp-none \n// socks5://127.0.0.1:1080';
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 \n// socks5://127.0.0.1:1080";
o.validate = function (section_id, value) {
// Optional
if (!value || value.length === 0) {
@@ -54,11 +54,15 @@ function createSectionContent(section) {
const activeConfigs = main.splitProxyString(value);
if (!activeConfigs.length) {
return _('No active configuration found. One configuration is required.');
return _(
"No active configuration found. One configuration is required.",
);
}
if (activeConfigs.length > 1) {
return _('Multiply active configurations found. Please leave one configuration.');
return _(
"Multiply active configurations found. Please leave one configuration.",
);
}
const validation = main.validateProxyUrl(activeConfigs[0]);
@@ -69,17 +73,17 @@ function createSectionContent(section) {
return validation.message;
} catch (e) {
return `${_('Invalid URL format:')} ${e?.message}`;
return `${_("Invalid URL format:")} ${e?.message}`;
}
};
o = section.option(
form.TextValue,
'outbound_json',
_('Outbound Configuration'),
_('Enter complete outbound configuration in JSON format'),
"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.validate = function (section_id, value) {
// Optional
@@ -98,11 +102,11 @@ function createSectionContent(section) {
o = section.option(
form.DynamicList,
'urltest_proxy_links',
_('URLTest Proxy Links'),
"urltest_proxy_links",
_("URLTest Proxy Links"),
);
o.depends('proxy_config_type', 'urltest');
o.placeholder = 'vless://, ss://, trojan://, socks4/5:// links';
o.depends("proxy_config_type", "urltest");
o.placeholder = "vless://, ss://, trojan://, socks4/5:// links";
o.rmempty = false;
o.validate = function (section_id, value) {
// Optional
@@ -121,35 +125,35 @@ function createSectionContent(section) {
o = section.option(
form.Flag,
'enable_udp_over_tcp',
_('Shadowsocks/Socks UDP over TCP'),
_('Apply for socks/Shadowsocks 2022'),
"enable_udp_over_tcp",
_("Shadowsocks/Socks UDP over TCP"),
_("Apply for socks/Shadowsocks 2022"),
);
o.default = '0';
o.depends('connection_type', 'proxy');
o.default = "0";
o.depends("connection_type", "proxy");
o.rmempty = false;
o = section.option(
widgets.DeviceSelect,
'interface',
_('Network Interface'),
_('Select network interface for VPN connection'),
"interface",
_("Network Interface"),
_("Select network interface for VPN connection"),
);
o.depends('connection_type', 'vpn');
o.depends("connection_type", "vpn");
o.noaliases = true;
o.nobridges = false;
o.noinactive = false;
o.filter = function (section_id, value) {
// Blocked interface names that should never be selectable
const blockedInterfaces = [
'br-lan',
'eth0',
'eth1',
'wan',
'phy0-ap0',
'phy1-ap0',
'pppoe-wan',
'lan',
"br-lan",
"eth0",
"eth1",
"wan",
"phy0-ap0",
"phy1-ap0",
"pppoe-wan",
"lan",
];
// Reject immediately if the value matches any blocked interface
@@ -170,46 +174,46 @@ function createSectionContent(section) {
// Reject wireless-related devices
const isWireless =
type === 'wifi' || type === 'wireless' || type.includes('wlan');
type === "wifi" || type === "wireless" || type.includes("wlan");
return !isWireless;
};
o = section.option(
form.Flag,
'domain_resolver_enabled',
_('Domain Resolver'),
_('Enable built-in DNS resolver for domains handled by this section'),
"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.depends('connection_type', 'vpn');
o.depends("connection_type", "vpn");
o = section.option(
form.ListValue,
'domain_resolver_dns_type',
_('DNS Protocol Type'),
_('Select the DNS protocol type for the domain resolver'),
"domain_resolver_dns_type",
_("DNS Protocol Type"),
_("Select the DNS protocol type for the domain resolver"),
);
o.value('doh', _('DNS over HTTPS (DoH)'));
o.value('dot', _('DNS over TLS (DoT)'));
o.value('udp', _('UDP (Unprotected DNS)'));
o.default = 'udp';
o.value("doh", _("DNS over HTTPS (DoH)"));
o.value("dot", _("DNS over TLS (DoT)"));
o.value("udp", _("UDP (Unprotected DNS)"));
o.default = "udp";
o.rmempty = false;
o.depends('domain_resolver_enabled', '1');
o.depends("domain_resolver_enabled", "1");
o = section.option(
form.Value,
'domain_resolver_dns_server',
_('DNS Server'),
_('Select or enter DNS server address'),
"domain_resolver_dns_server",
_("DNS Server"),
_("Select or enter DNS server address"),
);
Object.entries(main.DNS_SERVER_OPTIONS).forEach(([key, label]) => {
o.value(key, _(label));
});
o.default = '8.8.8.8';
o.default = "8.8.8.8";
o.rmempty = false;
o.depends('domain_resolver_enabled', '1');
o.depends("domain_resolver_enabled", "1");
o.validate = function (section_id, value) {
const validation = main.validateDNS(value);
@@ -222,12 +226,12 @@ function createSectionContent(section) {
o = section.option(
form.DynamicList,
'community_lists',
_('Community Lists'),
_('Select a predefined list for routing') +
"community_lists",
_("Community Lists"),
_("Select a predefined list for routing") +
' <a href="https://github.com/itdoginfo/allow-domains" target="_blank">github.com/itdoginfo/allow-domains</a>',
);
o.placeholder = 'Service list';
o.placeholder = "Service list";
Object.entries(main.DOMAIN_LIST_OPTIONS).forEach(([key, label]) => {
o.value(key, _(label));
});
@@ -256,17 +260,17 @@ function createSectionContent(section) {
(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'),
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),
"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),
);
@@ -275,18 +279,18 @@ function createSectionContent(section) {
main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v),
);
notifications.push(
E('p', { class: 'alert-message warning' }, [
E('strong', {}, _('Russia inside restrictions')),
E('br'),
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.',
"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(', '),
.filter((label) => label !== "Russia inside")
.join(", "),
removedServices.join(", "),
),
]),
);
@@ -302,7 +306,7 @@ function createSectionContent(section) {
);
lastValues = newValues;
} catch (e) {
console.error('Error in onchange handler:', e);
console.error("Error in onchange handler:", e);
} finally {
isProcessing = false;
}
@@ -310,24 +314,26 @@ function createSectionContent(section) {
o = section.option(
form.ListValue,
'user_domain_list_type',
_('User Domain List Type'),
_('Select the list type for adding custom domains'),
"user_domain_list_type",
_("User Domain List Type"),
_("Select the list type for adding custom domains"),
);
o.value('disabled', _('Disabled'));
o.value('dynamic', _('Dynamic List'));
o.value('text', _('Text List'));
o.default = 'disabled';
o.value("disabled", _("Disabled"));
o.value("dynamic", _("Dynamic List"));
o.value("text", _("Text List"));
o.default = "disabled";
o.rmempty = false;
o = section.option(
form.DynamicList,
'user_domains',
_('User Domains'),
_('Enter domain names without protocols, e.g. example.com or sub.example.com'),
"user_domains",
_("User Domains"),
_(
"Enter domain names without protocols, e.g. example.com or sub.example.com",
),
);
o.placeholder = 'Domains list';
o.depends('user_domain_list_type', 'dynamic');
o.placeholder = "Domains list";
o.depends("user_domain_list_type", "dynamic");
o.rmempty = false;
o.validate = function (section_id, value) {
// Optional
@@ -346,12 +352,15 @@ function createSectionContent(section) {
o = section.option(
form.TextValue,
'user_domains_text',
_('User Domains List'),
_('Enter domain names separated by commas, spaces, or newlines. You can add comments using //'),
"user_domains_text",
_("User Domains List"),
_(
"Enter domain names separated by commas, spaces, or newlines. You can add comments using //",
),
);
o.placeholder = 'example.com, sub.example.com\n// Social networks\ndomain.com test.com // personal domains';
o.depends('user_domain_list_type', 'text');
o.placeholder =
"example.com, sub.example.com\n// Social networks\ndomain.com test.com // personal domains";
o.depends("user_domain_list_type", "text");
o.rows = 8;
o.rmempty = false;
o.validate = function (section_id, value) {
@@ -363,17 +372,21 @@ function createSectionContent(section) {
const domains = main.parseValueList(value);
if (!domains.length) {
return _('At least one valid domain must be specified. Comments-only content is not allowed.');
return _(
"At least one valid domain must be specified. Comments-only content is not allowed.",
);
}
const {valid, results} = main.bulkValidate(domains, row => main.validateDomain(row, true));
const { valid, results } = main.bulkValidate(domains, (row) =>
main.validateDomain(row, true),
);
if (!valid) {
const errors = results
.filter((validation) => !validation.valid) // Leave only failed validations
.map((validation) => _(`${validation.value}: ${validation.message}`)); // Collect validation errors
return [_('Validation errors:'), ...errors].join('\n');
return [_("Validation errors:"), ...errors].join("\n");
}
return true;
@@ -381,24 +394,26 @@ function createSectionContent(section) {
o = section.option(
form.ListValue,
'user_subnet_list_type',
_('User Subnet List Type'),
_('Select the list type for adding custom subnets'),
"user_subnet_list_type",
_("User Subnet List Type"),
_("Select the list type for adding custom subnets"),
);
o.value('disabled', _('Disabled'));
o.value('dynamic', _('Dynamic List'));
o.value('text', _('Text List (comma/space/newline separated)'));
o.default = 'disabled';
o.value("disabled", _("Disabled"));
o.value("dynamic", _("Dynamic List"));
o.value("text", _("Text List (comma/space/newline separated)"));
o.default = "disabled";
o.rmempty = false;
o = section.option(
form.DynamicList,
'user_subnets',
_('User Subnets'),
_('Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses'),
"user_subnets",
_("User Subnets"),
_(
"Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses",
),
);
o.placeholder = 'IP or subnet';
o.depends('user_subnet_list_type', 'dynamic');
o.placeholder = "IP or subnet";
o.depends("user_subnet_list_type", "dynamic");
o.rmempty = false;
o.validate = function (section_id, value) {
// Optional
@@ -417,16 +432,16 @@ function createSectionContent(section) {
o = section.option(
form.TextValue,
'user_subnets_text',
_('User Subnets List'),
"user_subnets_text",
_("User Subnets List"),
_(
'Enter subnets in CIDR notation or single IP addresses, separated by commas, spaces, or newlines. ' +
'You can add comments using //'
"Enter subnets in CIDR notation or single IP addresses, separated by commas, spaces, or newlines. " +
"You can add comments using //",
),
);
o.placeholder =
'103.21.244.0/22\n// Google DNS\n8.8.8.8\n1.1.1.1/32, 9.9.9.9 // Cloudflare and Quad9';
o.depends('user_subnet_list_type', 'text');
"103.21.244.0/22\n// Google DNS\n8.8.8.8\n1.1.1.1/32, 9.9.9.9 // Cloudflare and Quad9";
o.depends("user_subnet_list_type", "text");
o.rows = 10;
o.rmempty = false;
o.validate = function (section_id, value) {
@@ -438,17 +453,19 @@ function createSectionContent(section) {
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.');
return _(
"At least one valid subnet or IP must be specified. Comments-only content is not allowed.",
);
}
const {valid, results} = main.bulkValidate(subnets, main.validateSubnet);
const { valid, results } = main.bulkValidate(subnets, main.validateSubnet);
if (!valid) {
const errors = results
.filter((validation) => !validation.valid) // Leave only failed validations
.map((validation) => _(`${validation.value}: ${validation.message}`)); // Collect validation errors
return [_('Validation errors:'), ...errors].join('\n');
return [_("Validation errors:"), ...errors].join("\n");
}
return true;
@@ -456,11 +473,11 @@ function createSectionContent(section) {
o = section.option(
form.DynamicList,
'local_domain_lists',
_('Local Domain Lists'),
_('Specify the path to the list file located on the router filesystem'),
"local_domain_lists",
_("Local Domain Lists"),
_("Specify the path to the list file located on the router filesystem"),
);
o.placeholder = '/path/file.lst';
o.placeholder = "/path/file.lst";
o.rmempty = true;
o.validate = function (section_id, value) {
// Optional
@@ -479,11 +496,11 @@ function createSectionContent(section) {
o = section.option(
form.DynamicList,
'local_subnet_lists',
_('Local Subnet Lists'),
_('Specify the path to the list file located on the router filesystem'),
"local_subnet_lists",
_("Local Subnet Lists"),
_("Specify the path to the list file located on the router filesystem"),
);
o.placeholder = '/path/file.lst';
o.placeholder = "/path/file.lst";
o.rmempty = true;
o.validate = function (section_id, value) {
// Optional
@@ -502,11 +519,11 @@ function createSectionContent(section) {
o = section.option(
form.DynamicList,
'remote_domain_lists',
_('Remote Domain Lists'),
_('Specify remote URLs to download and use domain lists'),
"remote_domain_lists",
_("Remote Domain Lists"),
_("Specify remote URLs to download and use domain lists"),
);
o.placeholder = 'https://example.com/domains.srs';
o.placeholder = "https://example.com/domains.srs";
o.rmempty = true;
o.validate = function (section_id, value) {
// Optional
@@ -525,11 +542,11 @@ function createSectionContent(section) {
o = section.option(
form.DynamicList,
'remote_subnet_lists',
_('Remote Subnet Lists'),
_('Specify remote URLs to download and use subnet lists'),
"remote_subnet_lists",
_("Remote Subnet Lists"),
_("Specify remote URLs to download and use subnet lists"),
);
o.placeholder = 'https://example.com/subnets.srs';
o.placeholder = "https://example.com/subnets.srs";
o.rmempty = true;
o.validate = function (section_id, value) {
// Optional
@@ -548,11 +565,13 @@ function createSectionContent(section) {
o = section.option(
form.DynamicList,
'fully_routed_ips',
_('Fully Routed IPs'),
_('Specify local IP addresses or subnets whose traffic will always be routed through the configured route'),
"fully_routed_ips",
_("Fully Routed IPs"),
_(
"Specify local IP addresses or subnets whose traffic will always be routed through the configured route",
),
);
o.placeholder = '192.168.1.2 or 192.168.1.0/24';
o.placeholder = "192.168.1.2 or 192.168.1.0/24";
o.rmempty = true;
o.validate = function (section_id, value) {
// Optional
@@ -571,28 +590,30 @@ function createSectionContent(section) {
o = section.option(
form.Flag,
'mixed_proxy_enabled',
_('Enable Mixed Proxy'),
_('Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies'),
"mixed_proxy_enabled",
_("Enable Mixed Proxy"),
_(
"Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies",
),
);
o.default = '0';
o.default = "0";
o.rmempty = false;
o = section.option(
form.Value,
'mixed_proxy_port',
_('Mixed Proxy Port'),
"mixed_proxy_port",
_("Mixed Proxy Port"),
_(
'Specify the port number on which the mixed proxy will run for this section. ' +
'Make sure the selected port is not used by another service'
"Specify the port number on which the mixed proxy will run for this section. " +
"Make sure the selected port is not used by another service",
),
);
o.rmempty = false;
o.depends('mixed_proxy_enabled', '1');
o.depends("mixed_proxy_enabled", "1");
}
const EntryPoint = {
createSectionContent,
}
};
return baseclass.extend(EntryPoint);

View File

@@ -1,33 +1,33 @@
'use strict';
'require form';
'require uci';
'require baseclass';
'require tools.widgets as widgets';
'require view.podkop.main as main';
"use strict";
"require form";
"require uci";
"require baseclass";
"require tools.widgets as widgets";
"require view.podkop.main as main";
function createSettingsContent(section) {
let o = section.option(
form.ListValue,
'dns_type',
_('DNS Protocol Type'),
_('Select DNS protocol to use'),
"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)'));
o.default = 'udp';
o.value("doh", _("DNS over HTTPS (DoH)"));
o.value("dot", _("DNS over TLS (DoT)"));
o.value("udp", _("UDP (Unprotected DNS)"));
o.default = "udp";
o.rmempty = false;
o = section.option(
form.Value,
'dns_server',
_('DNS Server'),
_('Select or enter DNS server address'),
"dns_server",
_("DNS Server"),
_("Select or enter DNS server address"),
);
Object.entries(main.DNS_SERVER_OPTIONS).forEach(([key, label]) => {
o.value(key, _(label));
});
o.default = '8.8.8.8';
o.default = "8.8.8.8";
o.rmempty = false;
o.validate = function (section_id, value) {
const validation = main.validateDNS(value);
@@ -41,16 +41,16 @@ function createSettingsContent(section) {
o = section.option(
form.Value,
'bootstrap_dns_server',
_('Bootstrap DNS server'),
"bootstrap_dns_server",
_("Bootstrap DNS server"),
_(
'The DNS server used to look up the IP address of an upstream 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));
});
o.default = '77.88.8.8';
o.default = "77.88.8.8";
o.rmempty = false;
o.validate = function (section_id, value) {
const validation = main.validateDNS(value);
@@ -64,20 +64,20 @@ function createSettingsContent(section) {
o = section.option(
form.Value,
'dns_rewrite_ttl',
_('DNS Rewrite TTL'),
_('Time in seconds for DNS record caching (default: 60)'),
"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.validate = function (section_id, value) {
if (!value) {
return _('TTL value cannot be empty');
return _("TTL value cannot be empty");
}
const ttl = parseInt(value);
if (isNaN(ttl) || ttl < 0) {
return _('TTL must be a positive number');
return _("TTL must be a positive number");
}
return true;
@@ -85,25 +85,25 @@ function createSettingsContent(section) {
o = section.option(
form.Flag,
'enable_output_network_interface',
_('Enable Output Network Interface'),
_('You can select Output Network Interface, by default autodetect'),
"enable_output_network_interface",
_("Enable Output Network Interface"),
_("You can select Output Network Interface, by default autodetect"),
);
o.default = '0';
o.default = "0";
o.rmempty = false;
o = section.option(
widgets.DeviceSelect,
'output_network_interface',
_('Output Network Interface'),
_('Select the network interface to which the traffic will originate'),
"output_network_interface",
_("Output Network Interface"),
_("Select the network interface to which the traffic will originate"),
);
o.noaliases = true;
o.multiple = false;
o.depends('enable_output_network_interface', '1');
o.depends("enable_output_network_interface", "1");
o.filter = function (section_id, value) {
// Blocked interface names that should never be selectable
const blockedInterfaces = ['br-lan'];
const blockedInterfaces = ["br-lan"];
// Reject immediately if the value matches any blocked interface
if (blockedInterfaces.includes(value)) {
@@ -112,11 +112,11 @@ function createSettingsContent(section) {
// Reject tun*, wg*, vpn*, awg*, oc*
if (
value.startsWith('tun') ||
value.startsWith('wg') ||
value.startsWith('vpn') ||
value.startsWith('awg') ||
value.startsWith('oc')
value.startsWith("tun") ||
value.startsWith("wg") ||
value.startsWith("vpn") ||
value.startsWith("awg") ||
value.startsWith("oc")
) {
return false;
}
@@ -134,25 +134,25 @@ function createSettingsContent(section) {
// Reject wireless-related devices
const isWireless =
type === 'wifi' || type === 'wireless' || type.includes('wlan');
type === "wifi" || type === "wireless" || type.includes("wlan");
return !isWireless;
};
o = section.option(
widgets.DeviceSelect,
'source_network_interfaces',
_('Source Network Interface'),
_('Select the network interface from which the traffic will originate'),
"source_network_interfaces",
_("Source Network Interface"),
_("Select the network interface from which the traffic will originate"),
);
o.default = 'br-lan';
o.default = "br-lan";
o.noaliases = true;
o.nobridges = false;
o.noinactive = false;
o.multiple = true;
o.filter = function (section_id, value) {
// Block specific interface names from being selectable
const blocked = ['wan', 'phy0-ap0', 'phy1-ap0', 'pppoe-wan'];
const blocked = ["wan", "phy0-ap0", "phy1-ap0", "pppoe-wan"];
if (blocked.includes(value)) {
return false;
}
@@ -170,7 +170,7 @@ function createSettingsContent(section) {
// Consider any Wi-Fi / wireless / wlan device as invalid
const isWireless =
type === 'wifi' || type === 'wireless' || type.includes('wlan');
type === "wifi" || type === "wireless" || type.includes("wlan");
// Allow only non-wireless devices
return !isWireless;
@@ -178,29 +178,29 @@ function createSettingsContent(section) {
o = section.option(
form.Flag,
'enable_badwan_interface_monitoring',
_('Interface Monitoring'),
_('Interface monitoring for Bad WAN'),
"enable_badwan_interface_monitoring",
_("Interface Monitoring"),
_("Interface monitoring for Bad WAN"),
);
o.default = '0';
o.default = "0";
o.rmempty = false;
o = section.option(
widgets.NetworkSelect,
'badwan_monitored_interfaces',
_('Monitored Interfaces'),
_('Select the WAN interfaces to be monitored'),
"badwan_monitored_interfaces",
_("Monitored Interfaces"),
_("Select the WAN interfaces to be monitored"),
);
o.depends('enable_badwan_interface_monitoring', '1');
o.depends("enable_badwan_interface_monitoring", "1");
o.multiple = true;
o.filter = function (section_id, value) {
// Reject if the value is in the blocked list ['lan', 'loopback']
if (['lan', 'loopback'].includes(value)) {
if (["lan", "loopback"].includes(value)) {
return false;
}
// Reject if the value starts with '@' (means it's an alias/reference)
if (value.startsWith('@')) {
if (value.startsWith("@")) {
return false;
}
@@ -210,72 +210,72 @@ function createSettingsContent(section) {
o = section.option(
form.Value,
'badwan_reload_delay',
_('Interface Monitoring Delay'),
_('Delay in milliseconds before reloading podkop after interface UP'),
"badwan_reload_delay",
_("Interface Monitoring Delay"),
_("Delay in milliseconds before reloading podkop after interface UP"),
);
o.depends('enable_badwan_interface_monitoring', '1');
o.default = '2000';
o.depends("enable_badwan_interface_monitoring", "1");
o.default = "2000";
o.rmempty = false;
o.validate = function (section_id, value) {
if (!value) {
return _('Delay value cannot be empty');
return _("Delay value cannot be empty");
}
return true;
};
o = section.option(
form.Flag,
'enable_yacd',
_('Enable YACD'),
"enable_yacd",
_("Enable YACD"),
`<a href="${main.getClashUIUrl()}" target="_blank">${main.getClashUIUrl()}</a>`,
);
o.default = '0';
o.default = "0";
o.rmempty = false;
o = section.option(
form.Flag,
'disable_quic',
_('Disable QUIC'),
"disable_quic",
_("Disable QUIC"),
_(
'Disable the QUIC protocol to improve compatibility or fix issues with video streaming',
"Disable the QUIC protocol to improve compatibility or fix issues with video streaming",
),
);
o.default = '0';
o.default = "0";
o.rmempty = false;
o = section.option(
form.ListValue,
'update_interval',
_('List Update Frequency'),
_('Select how often the domain or subnet lists are updated automatically'),
"update_interval",
_("List Update Frequency"),
_("Select how often the domain or subnet lists are updated automatically"),
);
Object.entries(main.UPDATE_INTERVAL_OPTIONS).forEach(([key, label]) => {
o.value(key, _(label));
});
o.default = '1d';
o.default = "1d";
o.rmempty = false;
o = section.option(
form.Flag,
'download_lists_via_proxy',
_('Download Lists via Proxy/VPN'),
_('Downloading all lists via main Proxy/VPN'),
"download_lists_via_proxy",
_("Download Lists via Proxy/VPN"),
_("Downloading all lists via main Proxy/VPN"),
);
o.default = '0';
o.default = "0";
o.rmempty = false;
o = section.option(
form.ListValue,
'download_lists_via_proxy_section',
_('Download Lists via specific proxy section'),
_('Downloading all lists via specific Proxy/VPN'),
"download_lists_via_proxy_section",
_("Download Lists via specific proxy section"),
_("Downloading all lists via specific Proxy/VPN"),
);
o.rmempty = false;
o.depends('download_lists_via_proxy', '1');
o.depends("download_lists_via_proxy", "1");
o.cfgvalue = function (section_id) {
return uci.get('podkop', section_id, 'download_lists_via_proxy_section');
return uci.get("podkop", section_id, "download_lists_via_proxy_section");
};
o.load = function () {
const sections = this.map?.data?.state?.values?.podkop ?? {};
@@ -285,7 +285,7 @@ function createSettingsContent(section) {
for (const secName in sections) {
const sec = sections[secName];
if (sec['.type'] === 'section') {
if (sec[".type"] === "section") {
this.keylist.push(secName);
this.vallist.push(secName);
}
@@ -296,57 +296,57 @@ function createSettingsContent(section) {
o = section.option(
form.Flag,
'dont_touch_dhcp',
_('Dont Touch My DHCP!'),
_('Podkop will not modify your DHCP configuration'),
"dont_touch_dhcp",
_("Dont Touch My DHCP!"),
_("Podkop will not modify your DHCP configuration"),
);
o.default = '0';
o.default = "0";
o.rmempty = false;
o = section.option(
form.ListValue,
'config_path',
_('Config File Path'),
"config_path",
_("Config File Path"),
_(
'Select path for sing-box config file. Change this ONLY if you know what you are doing',
"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.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 = section.option(
form.Value,
'cache_path',
_('Cache File Path'),
"cache_path",
_("Cache File Path"),
_(
'Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing',
"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)',
"/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.validate = function (section_id, value) {
if (!value) {
return _('Cache file path cannot be empty');
return _("Cache file path cannot be empty");
}
if (!value.startsWith('/')) {
return _('Path must be absolute (start with /)');
if (!value.startsWith("/")) {
return _("Path must be absolute (start with /)");
}
if (!value.endsWith('cache.db')) {
return _('Path must end with cache.db');
if (!value.endsWith("cache.db")) {
return _("Path must end with cache.db");
}
const parts = value.split('/').filter(Boolean);
const parts = value.split("/").filter(Boolean);
if (parts.length < 2) {
return _('Path must contain at least one directory (like /tmp/cache.db)');
return _("Path must contain at least one directory (like /tmp/cache.db)");
}
return true;
@@ -354,22 +354,22 @@ function createSettingsContent(section) {
o = section.option(
form.Flag,
'exclude_ntp',
_('Exclude NTP'),
"exclude_ntp",
_("Exclude NTP"),
_(
'Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN',
"Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN",
),
);
o.default = '0';
o.default = "0";
o.rmempty = false;
o = section.option(
form.DynamicList,
'routing_excluded_ips',
_('Routing Excluded IPs'),
_('Specify a local IP address to be excluded from routing'),
"routing_excluded_ips",
_("Routing Excluded IPs"),
_("Specify a local IP address to be excluded from routing"),
);
o.placeholder = 'IP';
o.placeholder = "IP";
o.rmempty = true;
o.validate = function (section_id, value) {
// Optional