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'; "use strict";
'require baseclass'; "require baseclass";
'require form'; "require form";
'require ui'; "require ui";
'require uci'; "require uci";
'require fs'; "require fs";
'require view.podkop.main as main'; "require view.podkop.main as main";
function createDashboardContent(section) { function createDashboardContent(section) {
const o = section.option(form.DummyValue, '_mount_node'); const o = section.option(form.DummyValue, "_mount_node");
o.rawhtml = true; o.rawhtml = true;
o.cfgvalue = () => { o.cfgvalue = () => {
main.DashboardTab.initController(); main.DashboardTab.initController();

View File

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

View File

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

View File

@@ -1,49 +1,49 @@
'use strict'; "use strict";
'require form'; "require form";
'require baseclass'; "require baseclass";
'require ui'; "require ui";
'require tools.widgets as widgets'; "require tools.widgets as widgets";
'require view.podkop.main as main'; "require view.podkop.main as main";
function createSectionContent(section) { function createSectionContent(section) {
let o = section.option( let o = section.option(
form.ListValue, form.ListValue,
'connection_type', "connection_type",
_('Connection Type'), _("Connection Type"),
_('Select between VPN and Proxy connection methods for traffic routing'), _("Select between VPN and Proxy connection methods for traffic routing"),
); );
o.value('proxy', 'Proxy'); o.value("proxy", "Proxy");
o.value('vpn', 'VPN'); o.value("vpn", "VPN");
o.value('block', 'Block'); o.value("block", "Block");
o = section.option( o = section.option(
form.ListValue, form.ListValue,
'proxy_config_type', "proxy_config_type",
_('Configuration Type'), _("Configuration Type"),
_('Select how to configure the proxy'), _("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"));
o.default = 'url'; o.default = "url";
o.depends('connection_type', 'proxy'); o.depends("connection_type", "proxy");
o = section.option( o = section.option(
form.TextValue, form.TextValue,
'proxy_string', "proxy_string",
_('Proxy Configuration URL'), _("Proxy Configuration URL"),
'', "",
); );
o.depends('proxy_config_type', 'url'); o.depends("proxy_config_type", "url");
o.rows = 5; o.rows = 5;
// Enable soft wrapping for multi-line proxy URLs (e.g., for URLTest proxy links) // 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 // Render as a textarea to allow multiple proxy URLs/configs
o.textarea = true; o.textarea = true;
o.rmempty = false; o.rmempty = false;
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 \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) { o.validate = function (section_id, value) {
// Optional // Optional
if (!value || value.length === 0) { if (!value || value.length === 0) {
@@ -54,11 +54,15 @@ function createSectionContent(section) {
const activeConfigs = main.splitProxyString(value); const activeConfigs = main.splitProxyString(value);
if (!activeConfigs.length) { 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) { 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]); const validation = main.validateProxyUrl(activeConfigs[0]);
@@ -69,17 +73,17 @@ function createSectionContent(section) {
return validation.message; return validation.message;
} catch (e) { } catch (e) {
return `${_('Invalid URL format:')} ${e?.message}`; return `${_("Invalid URL format:")} ${e?.message}`;
} }
}; };
o = section.option( o = section.option(
form.TextValue, form.TextValue,
'outbound_json', "outbound_json",
_('Outbound Configuration'), _("Outbound Configuration"),
_('Enter complete outbound configuration in JSON format'), _("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.validate = function (section_id, value) { o.validate = function (section_id, value) {
// Optional // Optional
@@ -98,11 +102,11 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.DynamicList, form.DynamicList,
'urltest_proxy_links', "urltest_proxy_links",
_('URLTest Proxy Links'), _("URLTest Proxy Links"),
); );
o.depends('proxy_config_type', 'urltest'); o.depends("proxy_config_type", "urltest");
o.placeholder = 'vless://, ss://, trojan://, socks4/5:// links'; o.placeholder = "vless://, ss://, trojan://, socks4/5:// links";
o.rmempty = false; o.rmempty = false;
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
// Optional // Optional
@@ -121,35 +125,35 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.Flag, form.Flag,
'enable_udp_over_tcp', "enable_udp_over_tcp",
_('Shadowsocks/Socks UDP over TCP'), _("Shadowsocks/Socks UDP over TCP"),
_('Apply for socks/Shadowsocks 2022'), _("Apply for socks/Shadowsocks 2022"),
); );
o.default = '0'; o.default = "0";
o.depends('connection_type', 'proxy'); o.depends("connection_type", "proxy");
o.rmempty = false; o.rmempty = false;
o = section.option( o = section.option(
widgets.DeviceSelect, widgets.DeviceSelect,
'interface', "interface",
_('Network Interface'), _("Network Interface"),
_('Select network interface for VPN connection'), _("Select network interface for VPN connection"),
); );
o.depends('connection_type', 'vpn'); o.depends("connection_type", "vpn");
o.noaliases = true; o.noaliases = true;
o.nobridges = false; o.nobridges = false;
o.noinactive = false; o.noinactive = false;
o.filter = function (section_id, value) { o.filter = function (section_id, value) {
// Blocked interface names that should never be selectable // Blocked interface names that should never be selectable
const blockedInterfaces = [ const blockedInterfaces = [
'br-lan', "br-lan",
'eth0', "eth0",
'eth1', "eth1",
'wan', "wan",
'phy0-ap0', "phy0-ap0",
'phy1-ap0', "phy1-ap0",
'pppoe-wan', "pppoe-wan",
'lan', "lan",
]; ];
// Reject immediately if the value matches any blocked interface // Reject immediately if the value matches any blocked interface
@@ -170,46 +174,46 @@ function createSectionContent(section) {
// Reject wireless-related devices // Reject wireless-related devices
const isWireless = const isWireless =
type === 'wifi' || type === 'wireless' || type.includes('wlan'); type === "wifi" || type === "wireless" || type.includes("wlan");
return !isWireless; return !isWireless;
}; };
o = section.option( o = section.option(
form.Flag, form.Flag,
'domain_resolver_enabled', "domain_resolver_enabled",
_('Domain Resolver'), _("Domain Resolver"),
_('Enable built-in DNS resolver for domains handled by this section'), _("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('connection_type', 'vpn'); o.depends("connection_type", "vpn");
o = section.option( o = section.option(
form.ListValue, form.ListValue,
'domain_resolver_dns_type', "domain_resolver_dns_type",
_('DNS Protocol Type'), _("DNS Protocol Type"),
_('Select the DNS protocol type for the domain resolver'), _("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)"));
o.default = 'udp'; o.default = "udp";
o.rmempty = false; o.rmempty = false;
o.depends('domain_resolver_enabled', '1'); o.depends("domain_resolver_enabled", "1");
o = section.option( o = section.option(
form.Value, form.Value,
'domain_resolver_dns_server', "domain_resolver_dns_server",
_('DNS Server'), _("DNS Server"),
_('Select or enter DNS server address'), _("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));
}); });
o.default = '8.8.8.8'; o.default = "8.8.8.8";
o.rmempty = false; o.rmempty = false;
o.depends('domain_resolver_enabled', '1'); o.depends("domain_resolver_enabled", "1");
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
const validation = main.validateDNS(value); const validation = main.validateDNS(value);
@@ -222,12 +226,12 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.DynamicList, form.DynamicList,
'community_lists', "community_lists",
_('Community Lists'), _("Community Lists"),
_('Select a predefined list for routing') + _("Select a predefined list for routing") +
' <a href="https://github.com/itdoginfo/allow-domains" target="_blank">github.com/itdoginfo/allow-domains</a>', ' <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));
}); });
@@ -256,17 +260,17 @@ function createSectionContent(section) {
(v) => v === lastSelected || !main.REGIONAL_OPTIONS.includes(v), (v) => v === lastSelected || !main.REGIONAL_OPTIONS.includes(v),
); );
notifications.push( notifications.push(
E('p', { class: 'alert-message warning' }, [ E("p", { class: "alert-message warning" }, [
E('strong', {}, _('Regional options cannot be used together')), E("strong", {}, _("Regional options cannot be used together")),
E('br'), E("br"),
_( _(
'Warning: %s cannot be used together with %s. Previous selections have been removed.', "Warning: %s cannot be used together with %s. Previous selections have been removed.",
).format(removedRegions.join(', '), lastSelected), ).format(removedRegions.join(", "), lastSelected),
]), ]),
); );
} }
if (newValues.includes('russia_inside')) { if (newValues.includes("russia_inside")) {
const removedServices = newValues.filter( const removedServices = newValues.filter(
(v) => !main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v), (v) => !main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v),
); );
@@ -275,18 +279,18 @@ function createSectionContent(section) {
main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v), main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v),
); );
notifications.push( notifications.push(
E('p', { class: 'alert-message warning' }, [ E("p", { class: "alert-message warning" }, [
E('strong', {}, _('Russia inside restrictions')), E("strong", {}, _("Russia inside restrictions")),
E('br'), 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( ).format(
main.ALLOWED_WITH_RUSSIA_INSIDE.map( main.ALLOWED_WITH_RUSSIA_INSIDE.map(
(key) => main.DOMAIN_LIST_OPTIONS[key], (key) => main.DOMAIN_LIST_OPTIONS[key],
) )
.filter((label) => label !== 'Russia inside') .filter((label) => label !== "Russia inside")
.join(', '), .join(", "),
removedServices.join(', '), removedServices.join(", "),
), ),
]), ]),
); );
@@ -302,7 +306,7 @@ function createSectionContent(section) {
); );
lastValues = newValues; lastValues = newValues;
} catch (e) { } catch (e) {
console.error('Error in onchange handler:', e); console.error("Error in onchange handler:", e);
} finally { } finally {
isProcessing = false; isProcessing = false;
} }
@@ -310,24 +314,26 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.ListValue, form.ListValue,
'user_domain_list_type', "user_domain_list_type",
_('User Domain List Type'), _("User Domain List Type"),
_('Select the list type for adding custom domains'), _("Select the list type for adding 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"));
o.default = 'disabled'; o.default = "disabled";
o.rmempty = false; o.rmempty = false;
o = section.option( o = section.option(
form.DynamicList, form.DynamicList,
'user_domains', "user_domains",
_('User Domains'), _("User Domains"),
_('Enter domain names without protocols, e.g. example.com or sub.example.com'), _(
"Enter domain names without protocols, e.g. example.com or sub.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;
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
// Optional // Optional
@@ -346,12 +352,15 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.TextValue, form.TextValue,
'user_domains_text', "user_domains_text",
_('User Domains List'), _("User Domains List"),
_('Enter domain names separated by commas, spaces, or newlines. You can add comments using //'), _(
"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.placeholder =
o.depends('user_domain_list_type', 'text'); "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.rows = 8;
o.rmempty = false; o.rmempty = false;
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
@@ -363,17 +372,21 @@ function createSectionContent(section) {
const domains = main.parseValueList(value); const domains = main.parseValueList(value);
if (!domains.length) { 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) { 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");
} }
return true; return true;
@@ -381,24 +394,26 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.ListValue, form.ListValue,
'user_subnet_list_type', "user_subnet_list_type",
_('User Subnet List Type'), _("User Subnet List Type"),
_('Select the list type for adding custom subnets'), _("Select the list type for adding 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)"));
o.default = 'disabled'; o.default = "disabled";
o.rmempty = false; o.rmempty = false;
o = section.option( o = section.option(
form.DynamicList, form.DynamicList,
'user_subnets', "user_subnets",
_('User Subnets'), _("User Subnets"),
_('Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses'), _(
"Enter subnets in CIDR notation (e.g. 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;
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
// Optional // Optional
@@ -417,16 +432,16 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.TextValue, form.TextValue,
'user_subnets_text', "user_subnets_text",
_('User Subnets List'), _("User Subnets List"),
_( _(
'Enter subnets in CIDR notation or single IP addresses, separated by commas, spaces, or newlines. ' + "Enter subnets in CIDR notation or single IP addresses, separated by commas, spaces, or newlines. " +
'You can add comments using //' "You can add comments using //",
), ),
); );
o.placeholder = 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'; "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;
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
@@ -438,17 +453,19 @@ function createSectionContent(section) {
const subnets = main.parseValueList(value); const subnets = main.parseValueList(value);
if (!subnets.length) { 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) { 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");
} }
return true; return true;
@@ -456,11 +473,11 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.DynamicList, form.DynamicList,
'local_domain_lists', "local_domain_lists",
_('Local Domain Lists'), _("Local Domain Lists"),
_('Specify the path to the list file located on the router filesystem'), _("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.rmempty = true;
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
// Optional // Optional
@@ -479,11 +496,11 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.DynamicList, form.DynamicList,
'local_subnet_lists', "local_subnet_lists",
_('Local Subnet Lists'), _("Local Subnet Lists"),
_('Specify the path to the list file located on the router filesystem'), _("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.rmempty = true;
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
// Optional // Optional
@@ -502,11 +519,11 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.DynamicList, form.DynamicList,
'remote_domain_lists', "remote_domain_lists",
_('Remote Domain Lists'), _("Remote Domain Lists"),
_('Specify remote URLs to download and use 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.rmempty = true;
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
// Optional // Optional
@@ -525,11 +542,11 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.DynamicList, form.DynamicList,
'remote_subnet_lists', "remote_subnet_lists",
_('Remote Subnet Lists'), _("Remote Subnet Lists"),
_('Specify remote URLs to download and use 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.rmempty = true;
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
// Optional // Optional
@@ -548,11 +565,13 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.DynamicList, form.DynamicList,
'fully_routed_ips', "fully_routed_ips",
_('Fully Routed IPs'), _("Fully Routed IPs"),
_('Specify local IP addresses or subnets whose traffic will always be routed through the configured route'), _(
"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.rmempty = true;
o.validate = function (section_id, value) { o.validate = function (section_id, value) {
// Optional // Optional
@@ -571,28 +590,30 @@ function createSectionContent(section) {
o = section.option( o = section.option(
form.Flag, form.Flag,
'mixed_proxy_enabled', "mixed_proxy_enabled",
_('Enable Mixed Proxy'), _("Enable Mixed Proxy"),
_('Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies'), _(
"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.rmempty = false;
o = section.option( o = section.option(
form.Value, 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. ' + "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' "Make sure the selected port is not used by another service",
), ),
); );
o.rmempty = false; o.rmempty = false;
o.depends('mixed_proxy_enabled', '1'); o.depends("mixed_proxy_enabled", "1");
} }
const EntryPoint = { const EntryPoint = {
createSectionContent, createSectionContent,
} };
return baseclass.extend(EntryPoint); return baseclass.extend(EntryPoint);

View File

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