diff --git a/fe-app-podkop/src/validators/validateVlessUrl.ts b/fe-app-podkop/src/validators/validateVlessUrl.ts index e74ffa6..45ea65a 100644 --- a/fe-app-podkop/src/validators/validateVlessUrl.ts +++ b/fe-app-podkop/src/validators/validateVlessUrl.ts @@ -1,6 +1,5 @@ import { ValidationResult } from './types'; -// TODO refactor current validation and add tests export function validateVlessUrl(url: string): ValidationResult { try { const parsedUrl = new URL(url); diff --git a/fe-app-podkop/watch-upload.js b/fe-app-podkop/watch-upload.js index 9bdd821..db0b1ea 100644 --- a/fe-app-podkop/watch-upload.js +++ b/fe-app-podkop/watch-upload.js @@ -23,12 +23,12 @@ async function uploadFile(filePath) { const relativePath = path.relative(localDir, filePath); const remotePath = path.posix.join(remoteDir, relativePath); - console.log(`⬆️ Uploading: ${relativePath} -> ${remotePath}`); + console.log(`Uploading: ${relativePath} -> ${remotePath}`); try { await sftp.fastPut(filePath, remotePath); - console.log(`✅ Uploaded: ${relativePath}`); + console.log(`Uploaded: ${relativePath}`); } catch (err) { - console.error(`❌ Failed: ${relativePath}: ${err.message}`); + console.error(`Failed: ${relativePath}: ${err.message}`); } } @@ -36,34 +36,32 @@ async function deleteFile(filePath) { const relativePath = path.relative(localDir, filePath); const remotePath = path.posix.join(remoteDir, relativePath); - console.log(`🗑 Removing: ${relativePath}`); + console.log(`Removing: ${relativePath}`); try { await sftp.delete(remotePath); - console.log(`✅ Removed: ${relativePath}`); + console.log(`Removed: ${relativePath}`); } catch (err) { - console.warn(`⚠️ Could not delete ${relativePath}: ${err.message}`); + console.warn(`Could not delete ${relativePath}: ${err.message}`); } } async function uploadAllFiles() { - console.log('🚀 Uploading all files from', localDir); + console.log('Uploading all files from', localDir); const files = await glob(`${localDir}/**/*`, { nodir: true }); for (const file of files) { await uploadFile(file); } - console.log('✅ Initial upload complete!'); + console.log('Initial upload complete!'); } async function main() { await sftp.connect(config); - console.log(`✅ Connected to ${config.host}`); + console.log(`Connected to ${config.host}`); - // 🔹 Загрузить всё при старте await uploadAllFiles(); - // 🔹 Затем следить за изменениями chokidar .watch(localDir, { ignoreInitial: true }) .on('all', async (event, filePath) => { @@ -75,7 +73,7 @@ async function main() { }); process.on('SIGINT', async () => { - console.log('🔌 Disconnecting...'); + console.log('Disconnecting...'); await sftp.end(); process.exit(); }); diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/configSection.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/configSection.js index 6ce0f09..f1a225b 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/configSection.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/configSection.js @@ -12,11 +12,11 @@ function createConfigSection(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'), + 'basic', + form.ListValue, + 'mode', + _('Connection Type'), + _('Select between VPN and Proxy connection methods for traffic routing'), ); o.value('proxy', 'Proxy'); o.value('vpn', 'VPN'); @@ -24,11 +24,11 @@ function createConfigSection(section) { o.ucisection = s.section; o = s.taboption( - 'basic', - form.ListValue, - 'proxy_config_type', - _('Configuration Type'), - _('Select how to configure the proxy'), + 'basic', + form.ListValue, + 'proxy_config_type', + _('Configuration Type'), + _('Select how to configure the proxy'), ); o.value('url', _('Connection URL')); o.value('outbound', _('Outbound Config')); @@ -38,11 +38,11 @@ function createConfigSection(section) { o.ucisection = s.section; o = s.taboption( - 'basic', - form.TextValue, - 'proxy_string', - _('Proxy Configuration URL'), - '', + 'basic', + form.TextValue, + 'proxy_string', + _('Proxy Configuration URL'), + '', ); o.depends('proxy_config_type', 'url'); o.rows = 5; @@ -52,7 +52,7 @@ function createConfigSection(section) { 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'; + '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, [ @@ -66,9 +66,9 @@ function createConfigSection(section) { if (cfgvalue) { try { const activeConfig = cfgvalue - .split('\n') - .map((line) => line.trim()) - .find((line) => line && !line.startsWith('//')); + .split('\n') + .map((line) => line.trim()) + .find((line) => line && !line.startsWith('//')); if (activeConfig) { if (activeConfig.includes('#')) { @@ -76,24 +76,24 @@ function createConfigSection(section) { if (label && label.trim()) { const decodedLabel = decodeURIComponent(label); const descDiv = E( - 'div', - { class: 'cbi-value-description' }, - _('Current config: ') + decodedLabel, + 'div', + { class: 'cbi-value-description' }, + _('Current config: ') + decodedLabel, ); container.appendChild(descDiv); } else { const descDiv = E( - 'div', - { class: 'cbi-value-description' }, - _('Config without description'), + 'div', + { class: 'cbi-value-description' }, + _('Config without description'), ); container.appendChild(descDiv); } } else { const descDiv = E( - 'div', - { class: 'cbi-value-description' }, - _('Config without description'), + 'div', + { class: 'cbi-value-description' }, + _('Config without description'), ); container.appendChild(descDiv); } @@ -101,19 +101,19 @@ function createConfigSection(section) { } catch (e) { console.error('Error parsing config label:', e); const descDiv = E( - 'div', - { class: 'cbi-value-description' }, - _('Config without description'), + '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', - ), + '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); } @@ -129,20 +129,20 @@ function createConfigSection(section) { try { const activeConfigs = value - .split('\n') - .map((line) => line.trim()) - .filter((line) => !line.startsWith('//')) - .filter(Boolean); + .split('\n') + .map((line) => line.trim()) + .filter((line) => !line.startsWith('//')) + .filter(Boolean); if (!activeConfigs.length) { return _( - 'No active configuration found. One configuration is required.', + 'No active configuration found. One configuration is required.', ); } if (activeConfigs.length > 1) { return _( - 'Multiply active configurations found. Please leave one configuration.', + 'Multiply active configurations found. Please leave one configuration.', ); } @@ -159,11 +159,11 @@ function createConfigSection(section) { }; o = s.taboption( - 'basic', - form.TextValue, - 'outbound_json', - _('Outbound Configuration'), - _('Enter complete outbound configuration in JSON format'), + 'basic', + form.TextValue, + 'outbound_json', + _('Outbound Configuration'), + _('Enter complete outbound configuration in JSON format'), ); o.depends('proxy_config_type', 'outbound'); o.rows = 10; @@ -184,10 +184,10 @@ function createConfigSection(section) { }; o = s.taboption( - 'basic', - form.DynamicList, - 'urltest_proxy_links', - _('URLTest Proxy Links'), + 'basic', + form.DynamicList, + 'urltest_proxy_links', + _('URLTest Proxy Links'), ); o.depends('proxy_config_type', 'urltest'); o.placeholder = 'vless://, ss://, trojan:// links'; @@ -208,11 +208,11 @@ function createConfigSection(section) { }; o = s.taboption( - 'basic', - form.Flag, - 'ss_uot', - _('Shadowsocks UDP over TCP'), - _('Apply for SS2022'), + 'basic', + form.Flag, + 'ss_uot', + _('Shadowsocks UDP over TCP'), + _('Apply for SS2022'), ); o.default = '0'; o.depends('mode', 'proxy'); @@ -220,11 +220,11 @@ function createConfigSection(section) { o.ucisection = s.section; o = s.taboption( - 'basic', - widgets.DeviceSelect, - 'interface', - _('Network Interface'), - _('Select network interface for VPN connection'), + 'basic', + widgets.DeviceSelect, + 'interface', + _('Network Interface'), + _('Select network interface for VPN connection'), ); o.depends('mode', 'vpn'); o.ucisection = s.section; @@ -262,17 +262,17 @@ function createConfigSection(section) { // Reject wireless-related devices const isWireless = - type === 'wifi' || type === 'wireless' || type.includes('wlan'); + type === 'wifi' || type === 'wireless' || type.includes('wlan'); return !isWireless; }; o = s.taboption( - 'basic', - form.Flag, - 'domain_resolver_enabled', - _('Domain Resolver'), - _('Enable built-in DNS resolver for domains handled by this section'), + '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; @@ -280,11 +280,11 @@ function createConfigSection(section) { o.ucisection = s.section; o = s.taboption( - 'basic', - form.ListValue, - 'domain_resolver_dns_type', - _('DNS Protocol Type'), - _('Select the DNS protocol type for the domain resolver'), + '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)')); @@ -295,11 +295,11 @@ function createConfigSection(section) { o.ucisection = s.section; o = s.taboption( - 'basic', - form.Value, - 'domain_resolver_dns_server', - _('DNS Server'), - _('Select or enter DNS server address'), + '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)); @@ -319,21 +319,21 @@ function createConfigSection(section) { }; o = s.taboption( - 'basic', - form.Flag, - 'community_lists_enabled', - _('Community Lists'), + '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') + + 'basic', + form.DynamicList, + 'community_lists', + _('Service List'), + _('Select predefined service for routing') + ' github.com/itdoginfo/allow-domains', ); o.placeholder = 'Service list'; @@ -357,50 +357,50 @@ function createConfigSection(section) { let notifications = []; const selectedRegionalOptions = main.REGIONAL_OPTIONS.filter((opt) => - newValues.includes(opt), + newValues.includes(opt), ); if (selectedRegionalOptions.length > 1) { const lastSelected = - selectedRegionalOptions[selectedRegionalOptions.length - 1]; + selectedRegionalOptions[selectedRegionalOptions.length - 1]; const removedRegions = selectedRegionalOptions.slice(0, -1); newValues = newValues.filter( - (v) => v === lastSelected || !main.REGIONAL_OPTIONS.includes(v), + (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), - ]), + 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), + (v) => !main.ALLOWED_WITH_RUSSIA_INSIDE.includes(v), ); if (removedServices.length > 0) { newValues = newValues.filter((v) => - main.ALLOWED_WITH_RUSSIA_INSIDE.includes(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(', '), - ), - ]), + 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(', '), + ), + ]), ); } } @@ -410,7 +410,7 @@ function createConfigSection(section) { } notifications.forEach((notification) => - ui.addNotification(null, notification), + ui.addNotification(null, notification), ); lastValues = newValues; } catch (e) { @@ -421,11 +421,11 @@ function createConfigSection(section) { }; o = s.taboption( - 'basic', - form.ListValue, - 'user_domain_list_type', - _('User Domain List Type'), - _('Select how to add your custom domains'), + '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')); @@ -435,13 +435,13 @@ function createConfigSection(section) { o.ucisection = s.section; o = s.taboption( - 'basic', - form.DynamicList, - 'user_domains', - _('User Domains'), - _( - 'Enter domain names without protocols (example: sub.example.com or example.com)', - ), + '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'); @@ -463,16 +463,16 @@ function createConfigSection(section) { }; 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 //', - ), + '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'; + '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; @@ -487,7 +487,7 @@ function createConfigSection(section) { 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.', ); } @@ -495,8 +495,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'); } @@ -505,22 +505,22 @@ function createConfigSection(section) { }; o = s.taboption( - 'basic', - form.Flag, - 'local_domain_lists_enabled', - _('Local Domain Lists'), - _('Use the list from the router filesystem'), + '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'), + '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'); @@ -542,22 +542,22 @@ function createConfigSection(section) { }; o = s.taboption( - 'basic', - form.Flag, - 'remote_domain_lists_enabled', - _('Remote Domain Lists'), - _('Download and use domain lists from remote URLs'), + '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://'), + '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'); @@ -579,22 +579,22 @@ function createConfigSection(section) { }; o = s.taboption( - 'basic', - form.Flag, - 'local_subnet_lists_enabled', - _('Local Subnet Lists'), - _('Use the list from the router filesystem'), + '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'), + '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'); @@ -616,11 +616,11 @@ function createConfigSection(section) { }; o = s.taboption( - 'basic', - form.ListValue, - 'user_subnet_list_type', - _('User Subnet List Type'), - _('Select how to add your custom subnets'), + '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')); @@ -630,13 +630,13 @@ function createConfigSection(section) { o.ucisection = s.section; o = s.taboption( - 'basic', - form.DynamicList, - 'user_subnets', - _('User Subnets'), - _( - 'Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses', - ), + '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'); @@ -658,16 +658,16 @@ function createConfigSection(section) { }; 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 //', - ), + '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'; + '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; @@ -682,7 +682,7 @@ function createConfigSection(section) { 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.', ); } @@ -690,8 +690,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'); } @@ -700,22 +700,22 @@ function createConfigSection(section) { }; o = s.taboption( - 'basic', - form.Flag, - 'remote_subnet_lists_enabled', - _('Remote Subnet Lists'), - _('Download and use subnet lists from remote URLs'), + '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://'), + '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'); @@ -737,24 +737,24 @@ function createConfigSection(section) { }; 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', - ), + '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'), + 'basic', + form.DynamicList, + 'all_traffic_ip', + _('Local IPs'), + _('Enter valid IPv4 addresses'), ); o.placeholder = 'IP'; o.depends('all_traffic_from_ip_enabled', '1'); diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/dashboardTab.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/dashboardTab.js index f1659eb..a5056dc 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/dashboardTab.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/dashboardTab.js @@ -8,19 +8,19 @@ 'require view.podkop.main as main'; function createDashboardSection(mainSection) { - let o = mainSection.tab('dashboard', _('Dashboard')); + let o = mainSection.tab('dashboard', _('Dashboard')); - o = mainSection.taboption('dashboard', form.DummyValue, '_status'); - o.rawhtml = true; - o.cfgvalue = () => { - main.initDashboardController() + o = mainSection.taboption('dashboard', form.DummyValue, '_status'); + o.rawhtml = true; + o.cfgvalue = () => { + main.initDashboardController(); - return main.renderDashboard() - }; + return main.renderDashboard(); + }; } const EntryPoint = { - createDashboardSection, -} + createDashboardSection, +}; return baseclass.extend(EntryPoint); diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js index ce5222d..c84ff91 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js @@ -10,88 +10,73 @@ 'require view.podkop.main as main'; const EntryNode = { - async render() { - main.injectGlobalStyles(); + async render() { + main.injectGlobalStyles(); - // main.getClashVersion() - // .then(result => console.log('getClashVersion - then', result)) - // .catch(err => console.log('getClashVersion - err', err)) - // .finally(() => console.log('getClashVersion - finish')); - // - // main.getClashConfig() - // .then(result => console.log('getClashConfig - then', result)) - // .catch(err => console.log('getClashConfig - err', err)) - // .finally(() => console.log('getClashConfig - finish')); - // - // main.getClashProxies() - // .then(result => console.log('getClashProxies - then', result)) - // .catch(err => console.log('getClashProxies - err', err)) - // .finally(() => console.log('getClashProxies - finish')); + const podkopFormMap = new form.Map('podkop', '', null, ['main', 'extra']); - const podkopFormMap = new form.Map('podkop', '', null, ['main', 'extra']); + // Main Section + const mainSection = podkopFormMap.section(form.TypedSection, 'main'); + mainSection.anonymous = true; - // Main Section - const mainSection = podkopFormMap.section(form.TypedSection, 'main'); - mainSection.anonymous = true; + configSection.createConfigSection(mainSection); - configSection.createConfigSection(mainSection); + // Additional Settings Tab (main section) + additionalTab.createAdditionalSection(mainSection); - // Additional Settings Tab (main section) - additionalTab.createAdditionalSection(mainSection); + // Diagnostics Tab (main section) + diagnosticTab.createDiagnosticsSection(mainSection); + const podkopFormMapPromise = podkopFormMap.render().then((node) => { + // Set up diagnostics event handlers + diagnosticTab.setupDiagnosticsEventHandlers(node); - // Diagnostics Tab (main section) - diagnosticTab.createDiagnosticsSection(mainSection); - const podkopFormMapPromise = podkopFormMap.render().then(node => { - // Set up diagnostics event handlers - diagnosticTab.setupDiagnosticsEventHandlers(node); + // Start critical error polling for all tabs + utils.startErrorPolling(); - // Start critical error polling for all tabs + // Add event listener to keep error polling active when switching tabs + const tabs = node.querySelectorAll('.cbi-tabmenu'); + if (tabs.length > 0) { + tabs[0].addEventListener('click', function (e) { + const tab = e.target.closest('.cbi-tab'); + if (tab) { + // Ensure error polling continues when switching tabs utils.startErrorPolling(); - - // Add event listener to keep error polling active when switching tabs - const tabs = node.querySelectorAll('.cbi-tabmenu'); - if (tabs.length > 0) { - tabs[0].addEventListener('click', function (e) { - const tab = e.target.closest('.cbi-tab'); - if (tab) { - // Ensure error polling continues when switching tabs - utils.startErrorPolling(); - } - }); - } - - // Add visibility change handler to manage error polling - document.addEventListener('visibilitychange', function () { - if (document.hidden) { - utils.stopErrorPolling(); - } else { - utils.startErrorPolling(); - } - }); - - return node; + } }); + } - // Extra Section - const extraSection = podkopFormMap.section(form.TypedSection, 'extra', _('Extra configurations')); - extraSection.anonymous = false; - extraSection.addremove = true; - extraSection.addbtntitle = _('Add Section'); - extraSection.multiple = true; - configSection.createConfigSection(extraSection); + // Add visibility change handler to manage error polling + document.addEventListener('visibilitychange', function () { + if (document.hidden) { + utils.stopErrorPolling(); + } else { + utils.startErrorPolling(); + } + }); + return node; + }); - // Initial dashboard render - dashboardTab.createDashboardSection(mainSection); + // Extra Section + const extraSection = podkopFormMap.section( + form.TypedSection, + 'extra', + _('Extra configurations'), + ); + extraSection.anonymous = false; + extraSection.addremove = true; + extraSection.addbtntitle = _('Add Section'); + extraSection.multiple = true; + configSection.createConfigSection(extraSection); - // Inject dashboard actualizer logic - // main.initDashboardController(); + // Initial dashboard render + dashboardTab.createDashboardSection(mainSection); - // Inject core service - main.coreService(); + // Inject core service + main.coreService(); - return podkopFormMapPromise; - } -} + return podkopFormMapPromise; + }, +}; return view.extend(EntryNode);