From cd43449585892b5d342b945c33b8f9890f687161 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 18 Feb 2025 13:59:04 +0300 Subject: [PATCH 01/11] feat: add version information tab to diagnostics --- .../resources/view/podkop/podkop.js | 134 +++++++++++++++--- luci-app-podkop/po/ru/podkop.po | 18 ++- luci-app-podkop/po/templates/podkop.pot | 14 +- podkop/files/etc/init.d/podkop | 25 +++- 4 files changed, 166 insertions(+), 25 deletions(-) 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 a5ddfb8..60a2852 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 @@ -153,7 +153,7 @@ return view.extend({ if (removedServices.length > 0) { newValues = newValues.filter(v => allowedWithRussiaInside.includes(v)); - const warningMsg = _('Warning: Russia inside can only be used with Meta, Twitter, Discord, and Telegram. %s have been removed from selection.').format( + const warningMsg = _('Warning: Russia inside can only be used with Meta, Twitter, Discord, and Telegram. %s already in Russia inside and have been removed from selection.').format( removedServices.join(', ') ); @@ -469,7 +469,6 @@ return view.extend({ o.ucisection = 'main'; // Additional Settings Tab - o = s.tab('additional', _('Additional Settings')); o = s.taboption('additional', form.Flag, 'yacd', _('Yacd enable'), _('http://openwrt.lan:9090/ui')); @@ -497,6 +496,7 @@ return view.extend({ o.rmempty = false; o.ucisection = 'main'; + // Diagnostics tab o = s.tab('diagnostics', _('Diagnostics')); function formatDiagnosticOutput(output) { @@ -512,6 +512,76 @@ return view.extend({ `: ${status === 'available' ? '✓' : '✗'}`); } + // Version Information Section + o = s.taboption('diagnostics', form.Button, '_show_versions'); + o.title = _('Version Information'); + o.description = _('Show version information for all components'); + o.inputtitle = _('Show Versions'); + o.inputstyle = 'apply'; + o.onclick = function () { + Promise.all([ + fs.exec('/etc/init.d/podkop', ['show_version']), + fs.exec('/etc/init.d/podkop', ['show_luci_version']), + fs.exec('/etc/init.d/podkop', ['show_sing_box_version']), + fs.exec('/etc/init.d/podkop', ['show_system_info']) + ]).then(function ([podkop, luci, singbox, system]) { + const versions = [ + '=== Podkop Version ===\n' + (podkop.stdout || _('No output')) + '\n', + '=== LuCI App ===\n' + (luci.stdout || _('No output')) + '\n', + '=== sing-box ===\n' + (singbox.stdout || _('No output')) + '\n', + system.stdout || _('No output') + ].join('\n'); + + const formattedOutput = formatDiagnosticOutput(versions); + ui.showModal(_('Version Information'), [ + E('div', { + style: + 'max-height: 70vh;' + + 'overflow-y: auto;' + + 'margin: 1em 0;' + + 'padding: 1.5em;' + + 'background: #f8f9fa;' + + 'border: 1px solid #e9ecef;' + + 'border-radius: 4px;' + + 'font-family: monospace;' + + 'white-space: pre-wrap;' + + 'word-wrap: break-word;' + + 'line-height: 1.5;' + + 'font-size: 14px;' + }, [ + E('pre', { style: 'margin: 0;' }, formattedOutput) + ]), + E('div', { + style: 'display: flex; justify-content: space-between; margin-top: 1em;' + }, [ + E('button', { + 'class': 'btn', + 'click': function (ev) { + const textarea = document.createElement('textarea'); + textarea.value = '```txt\n' + formattedOutput + '\n```'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); + } catch (err) { + ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); + } + document.body.removeChild(textarea); + } + }, _('Copy to Clipboard')), + E('button', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + }); + }; + // Connection Checks Section o = s.taboption('diagnostics', form.Button, '_check_nft'); o.title = _('NFT Rules'); @@ -545,13 +615,17 @@ return view.extend({ }, [ E('button', { 'class': 'btn', - 'click': function () { + 'click': function (ev) { const textarea = document.createElement('textarea'); - textarea.value = formattedOutput; + textarea.value = '```txt\n' + formattedOutput + '\n```'; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); } catch (err) { ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); } @@ -567,8 +641,6 @@ return view.extend({ }); }; - - // Logs Section o = s.taboption('diagnostics', form.Button, '_check_sing_box_logs'); o.title = _('Sing-Box Logs'); @@ -602,15 +674,19 @@ return view.extend({ }, [ E('button', { 'class': 'btn', - 'click': function () { + 'click': function (ev) { const textarea = document.createElement('textarea'); - textarea.value = formattedOutput; + textarea.value = '```logs\n' + formattedOutput + '\n```'; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); } catch (err) { - ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); + ui.addNotification(null, E('p', {}, _('Failed to copy logs: ') + err.message)); } document.body.removeChild(textarea); } @@ -656,15 +732,19 @@ return view.extend({ }, [ E('button', { 'class': 'btn', - 'click': function () { + 'click': function (ev) { const textarea = document.createElement('textarea'); - textarea.value = formattedOutput; + textarea.value = '```logs\n' + formattedOutput + '\n```'; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); } catch (err) { - ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); + ui.addNotification(null, E('p', {}, _('Failed to copy logs: ') + err.message)); } document.body.removeChild(textarea); } @@ -711,13 +791,17 @@ return view.extend({ }, [ E('button', { 'class': 'btn', - 'click': function () { + 'click': function (ev) { const textarea = document.createElement('textarea'); - textarea.value = formattedOutput; + textarea.value = '```txt\n' + formattedOutput + '\n```'; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); } catch (err) { ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); } @@ -765,13 +849,17 @@ return view.extend({ }, [ E('button', { 'class': 'btn', - 'click': function () { + 'click': function (ev) { const textarea = document.createElement('textarea'); - textarea.value = formattedOutput; + textarea.value = '```txt\n' + formattedOutput + '\n```'; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); } catch (err) { ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); } @@ -819,13 +907,17 @@ return view.extend({ }, [ E('button', { 'class': 'btn', - 'click': function () { + 'click': function (ev) { const textarea = document.createElement('textarea'); textarea.value = '```json\n' + formattedOutput + '\n```'; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); } catch (err) { ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); } @@ -873,13 +965,17 @@ return view.extend({ }, [ E('button', { 'class': 'btn', - 'click': function () { + 'click': function (ev) { const textarea = document.createElement('textarea'); - textarea.value = formattedOutput; + textarea.value = '```json\n' + formattedOutput + '\n```'; document.body.appendChild(textarea); textarea.select(); try { document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); } catch (err) { ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); } diff --git a/luci-app-podkop/po/ru/podkop.po b/luci-app-podkop/po/ru/podkop.po index 1b2aecf..fd080a6 100644 --- a/luci-app-podkop/po/ru/podkop.po +++ b/luci-app-podkop/po/ru/podkop.po @@ -468,8 +468,8 @@ msgid "Warning: %s cannot be used together with %s. Previous selections have bee msgstr "Предупреждение: %s нельзя использовать вместе с %s. Предыдущие варианты были удалены." #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX -msgid "Warning: Russia inside can only be used with Meta, Twitter, Discord, and Telegram. %s have been removed from selection." -msgstr "Внимание: Russia inside может использоваться только с Meta, Twitter, Discord и Telegram. %s были удалены из выбора." +msgid "Warning: Russia inside can only be used with Meta, Twitter, Discord, and Telegram. %s already in Russia inside and have been removed from selection." +msgstr "Внимание: Russia inside может использоваться только с Meta, Twitter, Discord и Telegram. %s уже в Russia inside и были удалены из выбора." #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX msgid "Regional options cannot be used together" @@ -477,4 +477,16 @@ msgstr "Нельзя использовать несколько региона #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX msgid "Russia inside restrictions" -msgstr "Ограничения Russia inside" \ No newline at end of file +msgstr "Ограничения Russia inside" + +msgid "Version Information" +msgstr "Информация о версиях" + +msgid "Show version information for all components" +msgstr "Показать информацию о версиях всех компонентов" + +msgid "Show Versions" +msgstr "Показать версии" + +msgid "Copied!" +msgstr "Скопировано!" \ No newline at end of file diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot index 68a04c5..f501532 100644 --- a/luci-app-podkop/po/templates/podkop.pot +++ b/luci-app-podkop/po/templates/podkop.pot @@ -468,7 +468,7 @@ msgid "Warning: %s cannot be used together with %s. Previous selections have bee msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX -msgid "Warning: Russia inside can only be used with Meta, Twitter, Discord, and Telegram. %s have been removed from selection." +msgid "Warning: Russia inside can only be used with Meta, Twitter, Discord, and Telegram. %s already in Russia inside and have been removed from selection." msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX @@ -477,4 +477,16 @@ msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX msgid "Russia inside restrictions" +msgstr "" + +msgid "Version Information" +msgstr "" + +msgid "Show version information for all components" +msgstr "" + +msgid "Show Versions" +msgstr "" + +msgid "Copied!" msgstr "" \ No newline at end of file diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 1b1040f..a9a99b0 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -7,7 +7,7 @@ script=$(readlink "$initscript") NAME="$(basename ${script:-$initscript})" config_load "$NAME" -EXTRA_COMMANDS="main list_update check_proxy check_nft check_github check_logs check_sing_box_connections check_sing_box_logs check_dnsmasq show_config show_version show_sing_box_config" +EXTRA_COMMANDS="main list_update check_proxy check_nft check_github check_logs check_sing_box_connections check_sing_box_logs check_dnsmasq show_config show_version show_sing_box_config show_luci_version show_sing_box_version show_system_info" EXTRA_HELP=" list_update Updating domain and subnet lists check_proxy Check if sing-box proxy works correctly check_nft Show PodkopTable nftables rules @@ -18,7 +18,10 @@ EXTRA_HELP=" list_update Updating domain and subnet lists check_dnsmasq Show current DNSMasq configuration show_config Show current configuration with masked sensitive data show_version Show current version - show_sing_box_config Show current sing-box configuration" + show_sing_box_config Show current sing-box configuration + show_luci_version Show LuCI app version + show_sing_box_version Show sing-box version + show_system_info Show OpenWrt version and device model" [ ! -L /usr/sbin/podkop ] && ln -s /etc/init.d/podkop /usr/sbin/podkop @@ -1608,4 +1611,22 @@ show_config() { show_version() { local version=$(opkg info podkop | grep -m 1 "Version:" | cut -d' ' -f2) echo "$version" +} + +show_luci_version() { + local version=$(opkg info luci-app-podkop | grep -m 1 "Version:" | cut -d' ' -f2) + echo "$version" +} + +show_sing_box_version() { + local version=$(opkg info sing-box | grep -m 1 "Version:" | cut -d' ' -f2) + echo "$version" +} + +show_system_info() { + echo "=== OpenWrt Version ===" + grep OPENWRT_RELEASE /etc/os-release | cut -d'"' -f2 + echo + echo "=== Device Model ===" + cat /tmp/sysinfo/model } \ No newline at end of file From 6df7c8abf85d08d24103d88a0fa2ae028fd708c0 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 18 Feb 2025 17:18:34 +0300 Subject: [PATCH 02/11] feat: add URL validation for Shadowsocks and VLESS configurations from examples --- .../resources/view/podkop/podkop.js | 139 +++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) 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 60a2852..ff6049b 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 @@ -43,6 +43,144 @@ return view.extend({ o.depends('proxy_config_type', 'url'); o.rows = 5; o.ucisection = 'main'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + try { + // Check if it's a valid URL format + if (!value.startsWith('vless://') && !value.startsWith('ss://')) { + return _('URL must start with vless:// or ss://'); + } + + // For Shadowsocks + if (value.startsWith('ss://')) { + let encrypted_part; + try { + // Split URL properly handling both old and new formats + let mainPart = value.includes('?') ? value.split('?')[0] : value.split('#')[0]; + encrypted_part = mainPart.split('/')[2].split('@')[0]; + + // Try base64 decode first (for old format) + try { + let decoded = atob(encrypted_part); + if (!decoded.includes(':')) { + // Not old format, check if it's 2022 format + if (!encrypted_part.includes(':') && !encrypted_part.includes('-')) { + return _('Invalid Shadowsocks URL format: missing method and password separator ":"'); + } + } + } catch (e) { + // If base64 decode fails, check if it's 2022 format + if (!encrypted_part.includes(':') && !encrypted_part.includes('-')) { + return _('Invalid Shadowsocks URL format: missing method and password separator ":"'); + } + } + } catch (e) { + return _('Invalid Shadowsocks URL format'); + } + + // Check server and port + try { + let serverPart = value.split('@')[1]; + if (!serverPart) { + return _('Invalid Shadowsocks URL: missing server address'); + } + let [server, portAndRest] = serverPart.split(':'); + if (!server) { + return _('Invalid Shadowsocks URL: missing server'); + } + let port = portAndRest ? portAndRest.split(/[?#]/)[0] : null; + if (!port) { + return _('Invalid Shadowsocks URL: missing port'); + } + let portNum = parseInt(port); + if (isNaN(portNum) || portNum < 1 || portNum > 65535) { + return _('Invalid port number. Must be between 1 and 65535'); + } + } catch (e) { + return _('Invalid Shadowsocks URL: missing or invalid server/port format'); + } + } + + // For VLESS + if (value.startsWith('vless://')) { + // Check UUID + let uuid = value.split('/')[2].split('@')[0]; + if (!uuid || uuid.length === 0) { + return _('Invalid VLESS URL: missing UUID'); + } + + // Check server and port + try { + let serverPart = value.split('@')[1]; + if (!serverPart) { + return _('Invalid VLESS URL: missing server address'); + } + let [server, portAndRest] = serverPart.split(':'); + if (!server) { + return _('Invalid VLESS URL: missing server'); + } + // Handle cases where port might be followed by / or ? or # + let port = portAndRest ? portAndRest.split(/[/?#]/)[0] : null; + if (!port && port !== '') { // Allow empty port for specific cases + return _('Invalid VLESS URL: missing port'); + } + if (port !== '') { // Only validate port if it's not empty + let portNum = parseInt(port); + if (isNaN(portNum) || portNum < 1 || portNum > 65535) { + return _('Invalid port number. Must be between 1 and 65535'); + } + } + } catch (e) { + return _('Invalid VLESS URL: missing or invalid server/port format'); + } + + // Parse query parameters + let queryString = value.split('?')[1]; + if (!queryString) { + return _('Invalid VLESS URL: missing query parameters'); + } + + let params = new URLSearchParams(queryString.split('#')[0]); + + // Check type parameter + let type = params.get('type'); + if (!type) { + return _('Invalid VLESS URL: missing type parameter'); + } + + // Check security parameter + let security = params.get('security'); + if (!security) { + return _('Invalid VLESS URL: missing security parameter'); + } + + // If security is "reality", check required reality parameters + if (security === 'reality') { + if (!params.get('pbk')) { + return _('Invalid VLESS URL: missing pbk parameter for reality security'); + } + if (!params.get('fp')) { + return _('Invalid VLESS URL: missing fp parameter for reality security'); + } + } + + // If security is "tls", check required TLS parameters + if (security === 'tls') { + if (!params.get('sni') && type !== 'tcp') { + return _('Invalid VLESS URL: missing sni parameter for tls security'); + } + } + } + + return true; + } catch (e) { + console.error('Validation error:', e); + return _('Invalid URL format: ' + e.message); + } + }; o = s.taboption('basic', form.TextValue, 'outbound_json', _('Outbound Configuration'), _('Enter complete outbound configuration in JSON format')); o.depends('proxy_config_type', 'outbound'); @@ -1215,7 +1353,6 @@ return view.extend({ return _('Invalid format. Use format: X.X.X.X or X.X.X.X/Y'); } - // Разбираем IP и маску const [ip, cidr] = value.split('/'); const ipParts = ip.split('.'); From b6cf73b9749b0bd7b48e83cdebfafb378cd28602 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 18 Feb 2025 18:23:29 +0300 Subject: [PATCH 03/11] feat: add service status and diagnostic tools to podkop UI --- .../resources/view/podkop/podkop.js | 1052 +++++++++-------- podkop/files/etc/init.d/podkop | 73 +- 2 files changed, 621 insertions(+), 504 deletions(-) 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 ff6049b..690cac9 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 @@ -5,6 +5,14 @@ 'require network'; 'require fs'; +function formatDiagnosticOutput(output) { + if (!output) return ''; + return output.trim() + .replace(/\x1b\[[0-9;]*m/g, '') // Remove ANSI color codes + .replace(/\r\n/g, '\n') // Normalize line endings + .replace(/\r/g, '\n'); +} + return view.extend({ async render() { document.getElementsByTagName('head')[0].insertAdjacentHTML('beforeend', ` @@ -21,6 +29,7 @@ return view.extend({ m.title = _('Podkop') + ' v' + res.stdout.trim(); } }); + s = m.section(form.TypedSection, 'main'); s.anonymous = true; @@ -637,516 +646,555 @@ return view.extend({ // Diagnostics tab o = s.tab('diagnostics', _('Diagnostics')); - function formatDiagnosticOutput(output) { - if (!output) return ''; + // Service Status Section + let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, singbox, system) { + return E('div', { 'class': 'cbi-section' }, [ + E('h3', {}, _('Service Status')), + E('div', { 'class': 'table', style: 'display: flex; gap: 20px;' }, [ + // Podkop Column + E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [ + E('div', { 'style': 'margin-bottom: 15px;' }, [ + E('strong', {}, _('Podkop Status')), + E('br'), + E('span', { + 'style': `color: ${podkopStatus.running ? '#4caf50' : '#f44336'}` + }, [ + podkopStatus.running ? '✔' : '✘', + ' ', + podkopStatus.status + ]) + ]), + E('div', { 'class': 'btn-group', 'style': 'display: flex; flex-direction: column; gap: 8px;' }, [ + podkopStatus.running ? + E('button', { + 'class': 'btn cbi-button-remove', + 'click': function () { + return fs.exec('/etc/init.d/podkop', ['stop']) + .then(() => location.reload()); + } + }, _('Stop Podkop')) : + E('button', { + 'class': 'btn cbi-button-apply', + 'click': function () { + return fs.exec('/etc/init.d/podkop', ['start']) + .then(() => location.reload()); + } + }, _('Start Podkop')), + E('button', { + 'class': 'btn cbi-button-apply', + 'click': function () { + return fs.exec('/etc/init.d/podkop', ['restart']) + .then(() => location.reload()); + } + }, _('Restart Podkop')), + E('button', { + 'class': 'btn cbi-button-' + (podkopStatus.enabled ? 'remove' : 'apply'), + 'click': function () { + return fs.exec('/etc/init.d/podkop', [podkopStatus.enabled ? 'disable' : 'enable']) + .then(() => location.reload()); + } + }, podkopStatus.enabled ? _('Disable Podkop') : _('Enable Podkop')), + E('button', { + 'class': 'btn', + 'click': function () { + return fs.exec('/etc/init.d/podkop', ['show_config']) + .then(function (res) { + const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); + ui.showModal(_('Podkop Configuration'), [ + E('div', { + style: 'max-height: 70vh;' + + 'overflow-y: auto;' + + 'margin: 1em 0;' + + 'padding: 1.5em;' + + 'background: #f8f9fa;' + + 'border: 1px solid #e9ecef;' + + 'border-radius: 4px;' + + 'font-family: monospace;' + + 'white-space: pre-wrap;' + + 'word-wrap: break-word;' + + 'line-height: 1.5;' + + 'font-size: 14px;' + }, [ + E('pre', { style: 'margin: 0;' }, formattedOutput) + ]), + E('div', { + style: 'display: flex; justify-content: space-between; margin-top: 1em;' + }, [ + E('button', { + 'class': 'btn', + 'click': function (ev) { + const textarea = document.createElement('textarea'); + textarea.value = '```txt\n' + formattedOutput + '\n```'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); + } catch (err) { + ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); + } + document.body.removeChild(textarea); + } + }, _('Copy to Clipboard')), + E('button', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + }); + } + }, _('Show Config')), + E('button', { + 'class': 'btn', + 'click': function () { + return fs.exec('/etc/init.d/podkop', ['check_logs']) + .then(function (res) { + const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); + ui.showModal(_('Podkop Logs'), [ + E('div', { + style: 'max-height: 70vh;' + + 'overflow-y: auto;' + + 'margin: 1em 0;' + + 'padding: 1.5em;' + + 'background: #f8f9fa;' + + 'border: 1px solid #e9ecef;' + + 'border-radius: 4px;' + + 'font-family: monospace;' + + 'white-space: pre-wrap;' + + 'word-wrap: break-word;' + + 'line-height: 1.5;' + + 'font-size: 14px;' + }, [ + E('pre', { style: 'margin: 0;' }, formattedOutput) + ]), + E('div', { + style: 'display: flex; justify-content: space-between; margin-top: 1em;' + }, [ + E('button', { + 'class': 'btn', + 'click': function (ev) { + const textarea = document.createElement('textarea'); + textarea.value = '```txt\n' + formattedOutput + '\n```'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); + } catch (err) { + ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); + } + document.body.removeChild(textarea); + } + }, _('Copy to Clipboard')), + E('button', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + }); + } + }, _('View Logs')) + ]) + ]), + // Sing-box Column + E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [ + E('div', { 'style': 'margin-bottom: 15px;' }, [ + E('strong', {}, _('Sing-box Status')), + E('br'), + E('span', { + 'style': `color: ${singboxStatus.running ? '#4caf50' : '#f44336'}` + }, [ + singboxStatus.running ? '✔' : '✘', + ' ', + `${singboxStatus.status}` + ]) + ]), + E('div', { 'class': 'btn-group', 'style': 'display: flex; flex-direction: column; gap: 8px;' }, [ + E('button', { + 'class': 'btn', + 'click': function () { + return fs.exec('/etc/init.d/podkop', ['show_sing_box_config']) + .then(function (res) { + const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); + ui.showModal(_('Sing-box Configuration'), [ + E('div', { + style: 'max-height: 70vh;' + + 'overflow-y: auto;' + + 'margin: 1em 0;' + + 'padding: 1.5em;' + + 'background: #f8f9fa;' + + 'border: 1px solid #e9ecef;' + + 'border-radius: 4px;' + + 'font-family: monospace;' + + 'white-space: pre-wrap;' + + 'word-wrap: break-word;' + + 'line-height: 1.5;' + + 'font-size: 14px;' + }, [ + E('pre', { style: 'margin: 0;' }, formattedOutput) + ]), + E('div', { + style: 'display: flex; justify-content: space-between; margin-top: 1em;' + }, [ + E('button', { + 'class': 'btn', + 'click': function (ev) { + const textarea = document.createElement('textarea'); + textarea.value = '```txt\n' + formattedOutput + '\n```'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); + } catch (err) { + ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); + } + document.body.removeChild(textarea); + } + }, _('Copy to Clipboard')), + E('button', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + }); + } + }, _('Show Config')), + E('button', { + 'class': 'btn', + 'click': function () { + return fs.exec('/etc/init.d/podkop', ['check_sing_box_logs']) + .then(function (res) { + const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); + ui.showModal(_('Sing-box Logs'), [ + E('div', { + style: 'max-height: 70vh;' + + 'overflow-y: auto;' + + 'margin: 1em 0;' + + 'padding: 1.5em;' + + 'background: #f8f9fa;' + + 'border: 1px solid #e9ecef;' + + 'border-radius: 4px;' + + 'font-family: monospace;' + + 'white-space: pre-wrap;' + + 'word-wrap: break-word;' + + 'line-height: 1.5;' + + 'font-size: 14px;' + }, [ + E('pre', { style: 'margin: 0;' }, formattedOutput) + ]), + E('div', { + style: 'display: flex; justify-content: space-between; margin-top: 1em;' + }, [ + E('button', { + 'class': 'btn', + 'click': function (ev) { + const textarea = document.createElement('textarea'); + textarea.value = '```txt\n' + formattedOutput + '\n```'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); + } catch (err) { + ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); + } + document.body.removeChild(textarea); + } + }, _('Copy to Clipboard')), + E('button', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + }); + } + }, _('View Logs')), + E('button', { + 'class': 'btn', + 'click': function () { + return fs.exec('/etc/init.d/podkop', ['check_sing_box_connections']) + .then(function (res) { + const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); + ui.showModal(_('Active Connections'), [ + E('div', { + style: 'max-height: 70vh;' + + 'overflow-y: auto;' + + 'margin: 1em 0;' + + 'padding: 1.5em;' + + 'background: #f8f9fa;' + + 'border: 1px solid #e9ecef;' + + 'border-radius: 4px;' + + 'font-family: monospace;' + + 'white-space: pre-wrap;' + + 'word-wrap: break-word;' + + 'line-height: 1.5;' + + 'font-size: 14px;' + }, [ + E('pre', { style: 'margin: 0;' }, formattedOutput) + ]), + E('div', { + style: 'display: flex; justify-content: space-between; margin-top: 1em;' + }, [ + E('button', { + 'class': 'btn', + 'click': function (ev) { + const textarea = document.createElement('textarea'); + textarea.value = '```txt\n' + formattedOutput + '\n```'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); + } catch (err) { + ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); + } + document.body.removeChild(textarea); + } + }, _('Copy to Clipboard')), + E('button', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + }); + } + }, _('Check Connections')) + ]) + ]), + // Diagnostics Column + E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [ + E('div', { 'style': 'margin-bottom: 15px;' }, [ + E('strong', {}, _('Diagnostic Tools')) + ]), + E('div', { 'class': 'btn-group', 'style': 'display: flex; flex-direction: column; gap: 8px;' }, [ + E('button', { + 'class': 'btn', + 'click': function () { + return fs.exec('/etc/init.d/podkop', ['check_nft']) + .then(function (res) { + const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); + ui.showModal(_('NFT Rules'), [ + E('div', { + style: 'max-height: 70vh;' + + 'overflow-y: auto;' + + 'margin: 1em 0;' + + 'padding: 1.5em;' + + 'background: #f8f9fa;' + + 'border: 1px solid #e9ecef;' + + 'border-radius: 4px;' + + 'font-family: monospace;' + + 'white-space: pre-wrap;' + + 'word-wrap: break-word;' + + 'line-height: 1.5;' + + 'font-size: 14px;' + }, [ + E('pre', { style: 'margin: 0;' }, formattedOutput) + ]), + E('div', { + style: 'display: flex; justify-content: space-between; margin-top: 1em;' + }, [ + E('button', { + 'class': 'btn', + 'click': function (ev) { + const textarea = document.createElement('textarea'); + textarea.value = '```txt\n' + formattedOutput + '\n```'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); + } catch (err) { + ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); + } + document.body.removeChild(textarea); + } + }, _('Copy to Clipboard')), + E('button', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + }); + } + }, _('Check NFT Rules')), + E('button', { + 'class': 'btn', + 'click': function () { + return fs.exec('/etc/init.d/podkop', ['check_dnsmasq']) + .then(function (res) { + const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); + ui.showModal(_('DNSMasq Configuration'), [ + E('div', { + style: 'max-height: 70vh;' + + 'overflow-y: auto;' + + 'margin: 1em 0;' + + 'padding: 1.5em;' + + 'background: #f8f9fa;' + + 'border: 1px solid #e9ecef;' + + 'border-radius: 4px;' + + 'font-family: monospace;' + + 'white-space: pre-wrap;' + + 'word-wrap: break-word;' + + 'line-height: 1.5;' + + 'font-size: 14px;' + }, [ + E('pre', { style: 'margin: 0;' }, formattedOutput) + ]), + E('div', { + style: 'display: flex; justify-content: space-between; margin-top: 1em;' + }, [ + E('button', { + 'class': 'btn', + 'click': function (ev) { + const textarea = document.createElement('textarea'); + textarea.value = '```txt\n' + formattedOutput + '\n```'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); + } catch (err) { + ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); + } + document.body.removeChild(textarea); + } + }, _('Copy to Clipboard')), + E('button', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + }); + } + }, _('Check DNSMasq')), + E('button', { + 'class': 'btn', + 'click': function () { + return fs.exec('/etc/init.d/podkop', ['list_update']) + .then(function (res) { + const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); + ui.showModal(_('Lists Update Results'), [ + E('div', { + style: 'max-height: 70vh;' + + 'overflow-y: auto;' + + 'margin: 1em 0;' + + 'padding: 1.5em;' + + 'background: #f8f9fa;' + + 'border: 1px solid #e9ecef;' + + 'border-radius: 4px;' + + 'font-family: monospace;' + + 'white-space: pre-wrap;' + + 'word-wrap: break-word;' + + 'line-height: 1.5;' + + 'font-size: 14px;' + }, [ + E('pre', { style: 'margin: 0;' }, formattedOutput) + ]), + E('div', { + style: 'display: flex; justify-content: space-between; margin-top: 1em;' + }, [ + E('button', { + 'class': 'btn', + 'click': function (ev) { + const textarea = document.createElement('textarea'); + textarea.value = '```txt\n' + formattedOutput + '\n```'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + ev.target.textContent = _('Copied!'); + setTimeout(() => { + ev.target.textContent = _('Copy to Clipboard'); + }, 1000); + } catch (err) { + ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); + } + document.body.removeChild(textarea); + } + }, _('Copy to Clipboard')), + E('button', { + 'class': 'btn', + 'click': ui.hideModal + }, _('Close')) + ]) + ]); + }); + } + }, _('Update Lists')) + ]) + ]), + // Version Information Column + E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [ + E('div', { 'style': 'margin-bottom: 15px;' }, [ + E('strong', {}, _('Version Information')), + E('br'), + E('div', { 'style': 'margin-top: 10px; font-family: monospace; white-space: pre-wrap;' }, [ + E('strong', {}, 'Podkop: '), podkop.stdout ? podkop.stdout.trim() : _('Unknown'), '\n', + E('strong', {}, 'LuCI App: '), luci.stdout ? luci.stdout.trim() : _('Unknown'), '\n', + E('strong', {}, 'Sing-box: '), singbox.stdout ? singbox.stdout.trim() : _('Unknown'), '\n', + E('strong', {}, 'OpenWrt Version: '), system.stdout ? system.stdout.split('\n')[1].trim() : _('Unknown'), '\n', + E('strong', {}, 'Device Model: '), system.stdout ? system.stdout.split('\n')[4].trim() : _('Unknown') + ]) + ]) + ]) + ]) + ]); + }; - return output - .replace(/\x1B\[[0-9;]*[mK]/g, '') - .replace(/\[[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\] /g, '') - .replace(/\n{3,}/g, '\n\n') - .replace(/===\s+(.*?)\s+===/g, (_, title) => `\n${title}\n${'─'.repeat(title.length)}`) - .replace(/^Checking\s+(.+)\.{3}/gm, '► Checking $1...') - .replace(/:\s+(available|not found)$/gm, (_, status) => - `: ${status === 'available' ? '✓' : '✗'}`); - } - - // Version Information Section - o = s.taboption('diagnostics', form.Button, '_show_versions'); - o.title = _('Version Information'); - o.description = _('Show version information for all components'); - o.inputtitle = _('Show Versions'); - o.inputstyle = 'apply'; - o.onclick = function () { - Promise.all([ + o = s.taboption('diagnostics', form.DummyValue, '_status'); + o.rawhtml = true; + o.cfgvalue = function () { + return Promise.all([ + fs.exec('/etc/init.d/podkop', ['get_status']), + fs.exec('/etc/init.d/podkop', ['get_sing_box_status']), fs.exec('/etc/init.d/podkop', ['show_version']), fs.exec('/etc/init.d/podkop', ['show_luci_version']), fs.exec('/etc/init.d/podkop', ['show_sing_box_version']), fs.exec('/etc/init.d/podkop', ['show_system_info']) - ]).then(function ([podkop, luci, singbox, system]) { - const versions = [ - '=== Podkop Version ===\n' + (podkop.stdout || _('No output')) + '\n', - '=== LuCI App ===\n' + (luci.stdout || _('No output')) + '\n', - '=== sing-box ===\n' + (singbox.stdout || _('No output')) + '\n', - system.stdout || _('No output') - ].join('\n'); - - const formattedOutput = formatDiagnosticOutput(versions); - ui.showModal(_('Version Information'), [ - E('div', { - style: - 'max-height: 70vh;' + - 'overflow-y: auto;' + - 'margin: 1em 0;' + - 'padding: 1.5em;' + - 'background: #f8f9fa;' + - 'border: 1px solid #e9ecef;' + - 'border-radius: 4px;' + - 'font-family: monospace;' + - 'white-space: pre-wrap;' + - 'word-wrap: break-word;' + - 'line-height: 1.5;' + - 'font-size: 14px;' - }, [ - E('pre', { style: 'margin: 0;' }, formattedOutput) - ]), - E('div', { - style: 'display: flex; justify-content: space-between; margin-top: 1em;' - }, [ - E('button', { - 'class': 'btn', - 'click': function (ev) { - const textarea = document.createElement('textarea'); - textarea.value = '```txt\n' + formattedOutput + '\n```'; - document.body.appendChild(textarea); - textarea.select(); - try { - document.execCommand('copy'); - ev.target.textContent = _('Copied!'); - setTimeout(() => { - ev.target.textContent = _('Copy to Clipboard'); - }, 1000); - } catch (err) { - ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); - } - document.body.removeChild(textarea); - } - }, _('Copy to Clipboard')), - E('button', { - 'class': 'btn', - 'click': ui.hideModal - }, _('Close')) - ]) - ]); + ]).then(function ([podkopStatus, singboxStatus, podkop, luci, singbox, system]) { + try { + const parsedPodkopStatus = JSON.parse(podkopStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}'); + const parsedSingboxStatus = JSON.parse(singboxStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}'); + return createStatusSection(parsedPodkopStatus, parsedSingboxStatus, podkop, luci, singbox, system); + } catch (e) { + console.error('Error parsing status:', e); + return E('div', { 'class': 'alert-message warning' }, [ + E('strong', {}, _('Error loading status')), E('br'), + E('pre', {}, e.toString()) + ]); + } }); }; - // Connection Checks Section - o = s.taboption('diagnostics', form.Button, '_check_nft'); - o.title = _('NFT Rules'); - o.description = _('Show current nftables rules and statistics'); - o.inputtitle = _('Check Rules'); - o.inputstyle = 'apply'; - o.onclick = function () { - return fs.exec('/etc/init.d/podkop', ['check_nft']) - .then(function (res) { - const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); - ui.showModal(_('NFT Rules'), [ - E('div', { - style: - 'max-height: 70vh;' + - 'overflow-y: auto;' + - 'margin: 1em 0;' + - 'padding: 1.5em;' + - 'background: #f8f9fa;' + - 'border: 1px solid #e9ecef;' + - 'border-radius: 4px;' + - 'font-family: monospace;' + - 'white-space: pre-wrap;' + - 'word-wrap: break-word;' + - 'line-height: 1.5;' + - 'font-size: 14px;' - }, [ - E('pre', { style: 'margin: 0;' }, formattedOutput) - ]), - E('div', { - style: 'display: flex; justify-content: space-between; margin-top: 1em;' - }, [ - E('button', { - 'class': 'btn', - 'click': function (ev) { - const textarea = document.createElement('textarea'); - textarea.value = '```txt\n' + formattedOutput + '\n```'; - document.body.appendChild(textarea); - textarea.select(); - try { - document.execCommand('copy'); - ev.target.textContent = _('Copied!'); - setTimeout(() => { - ev.target.textContent = _('Copy to Clipboard'); - }, 1000); - } catch (err) { - ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); - } - document.body.removeChild(textarea); - } - }, _('Copy to Clipboard')), - E('button', { - 'class': 'btn', - 'click': ui.hideModal - }, _('Close')) - ]) - ]); - }); - }; - - // Logs Section - o = s.taboption('diagnostics', form.Button, '_check_sing_box_logs'); - o.title = _('Sing-Box Logs'); - o.description = _('View recent sing-box logs from system journal'); - o.inputtitle = _('View Sing-Box Logs'); - o.inputstyle = 'apply'; - o.onclick = function () { - return fs.exec('/etc/init.d/podkop', ['check_sing_box_logs']) - .then(function (res) { - const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); - ui.showModal(_('Sing-Box Logs'), [ - E('div', { - style: - 'max-height: 70vh;' + - 'overflow-y: auto;' + - 'margin: 1em 0;' + - 'padding: 1.5em;' + - 'background: #f8f9fa;' + - 'border: 1px solid #e9ecef;' + - 'border-radius: 4px;' + - 'font-family: monospace;' + - 'white-space: pre-wrap;' + - 'word-wrap: break-word;' + - 'line-height: 1.5;' + - 'font-size: 14px;' - }, [ - E('pre', { style: 'margin: 0;' }, formattedOutput) - ]), - E('div', { - style: 'display: flex; justify-content: space-between; margin-top: 1em;' - }, [ - E('button', { - 'class': 'btn', - 'click': function (ev) { - const textarea = document.createElement('textarea'); - textarea.value = '```logs\n' + formattedOutput + '\n```'; - document.body.appendChild(textarea); - textarea.select(); - try { - document.execCommand('copy'); - ev.target.textContent = _('Copied!'); - setTimeout(() => { - ev.target.textContent = _('Copy to Clipboard'); - }, 1000); - } catch (err) { - ui.addNotification(null, E('p', {}, _('Failed to copy logs: ') + err.message)); - } - document.body.removeChild(textarea); - } - }, _('Copy to Clipboard')), - E('button', { - 'class': 'btn', - 'click': ui.hideModal - }, _('Close')) - ]) - ]); - }); - }; - - o = s.taboption('diagnostics', form.Button, '_check_logs'); - o.title = _('Podkop Logs'); - o.description = _('View recent podkop logs from system journal'); - o.inputtitle = _('View Podkop Logs'); - o.inputstyle = 'apply'; - o.onclick = function () { - return fs.exec('/etc/init.d/podkop', ['check_logs']) - .then(function (res) { - const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); - ui.showModal(_('Podkop Logs'), [ - E('div', { - style: - 'max-height: 70vh;' + - 'overflow-y: auto;' + - 'margin: 1em 0;' + - 'padding: 1.5em;' + - 'background: #f8f9fa;' + - 'border: 1px solid #e9ecef;' + - 'border-radius: 4px;' + - 'font-family: monospace;' + - 'white-space: pre-wrap;' + - 'word-wrap: break-word;' + - 'line-height: 1.5;' + - 'font-size: 14px;' - }, [ - E('pre', { style: 'margin: 0;' }, formattedOutput) - ]), - E('div', { - style: 'display: flex; justify-content: space-between; margin-top: 1em;' - }, [ - E('button', { - 'class': 'btn', - 'click': function (ev) { - const textarea = document.createElement('textarea'); - textarea.value = '```logs\n' + formattedOutput + '\n```'; - document.body.appendChild(textarea); - textarea.select(); - try { - document.execCommand('copy'); - ev.target.textContent = _('Copied!'); - setTimeout(() => { - ev.target.textContent = _('Copy to Clipboard'); - }, 1000); - } catch (err) { - ui.addNotification(null, E('p', {}, _('Failed to copy logs: ') + err.message)); - } - document.body.removeChild(textarea); - } - }, _('Copy to Clipboard')), - E('button', { - 'class': 'btn', - 'click': ui.hideModal - }, _('Close')) - ]) - ]); - }); - }; - - // Configurations Section - o = s.taboption('diagnostics', form.Button, '_check_sing_box_connections'); - o.title = _('Active Connections'); - o.description = _('View active sing-box network connections'); - o.inputtitle = _('Check Connections'); - o.inputstyle = 'apply'; - o.onclick = function () { - return fs.exec('/etc/init.d/podkop', ['check_sing_box_connections']) - .then(function (res) { - const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); - ui.showModal(_('Active Connections'), [ - E('div', { - style: - 'max-height: 70vh;' + - 'overflow-y: auto;' + - 'margin: 1em 0;' + - 'padding: 1.5em;' + - 'background: #f8f9fa;' + - 'border: 1px solid #e9ecef;' + - 'border-radius: 4px;' + - 'font-family: monospace;' + - 'white-space: pre-wrap;' + - 'word-wrap: break-word;' + - 'line-height: 1.5;' + - 'font-size: 14px;' - }, [ - E('pre', { style: 'margin: 0;' }, formattedOutput) - ]), - E('div', { - style: 'display: flex; justify-content: space-between; margin-top: 1em;' - }, [ - E('button', { - 'class': 'btn', - 'click': function (ev) { - const textarea = document.createElement('textarea'); - textarea.value = '```txt\n' + formattedOutput + '\n```'; - document.body.appendChild(textarea); - textarea.select(); - try { - document.execCommand('copy'); - ev.target.textContent = _('Copied!'); - setTimeout(() => { - ev.target.textContent = _('Copy to Clipboard'); - }, 1000); - } catch (err) { - ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); - } - document.body.removeChild(textarea); - } - }, _('Copy to Clipboard')), - E('button', { - 'class': 'btn', - 'click': ui.hideModal - }, _('Close')) - ]) - ]); - }); - }; - - o = s.taboption('diagnostics', form.Button, '_check_dnsmasq'); - o.title = _('DNSMasq Configuration'); - o.description = _('View current DNSMasq configuration settings'); - o.inputtitle = _('Check DNSMasq'); - o.inputstyle = 'apply'; - o.onclick = function () { - return fs.exec('/etc/init.d/podkop', ['check_dnsmasq']) - .then(function (res) { - const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); - ui.showModal(_('DNSMasq Configuration'), [ - E('div', { - style: - 'max-height: 70vh;' + - 'overflow-y: auto;' + - 'margin: 1em 0;' + - 'padding: 1.5em;' + - 'background: #f8f9fa;' + - 'border: 1px solid #e9ecef;' + - 'border-radius: 4px;' + - 'font-family: monospace;' + - 'white-space: pre-wrap;' + - 'word-wrap: break-word;' + - 'line-height: 1.5;' + - 'font-size: 14px;' - }, [ - E('pre', { style: 'margin: 0;' }, formattedOutput) - ]), - E('div', { - style: 'display: flex; justify-content: space-between; margin-top: 1em;' - }, [ - E('button', { - 'class': 'btn', - 'click': function (ev) { - const textarea = document.createElement('textarea'); - textarea.value = '```txt\n' + formattedOutput + '\n```'; - document.body.appendChild(textarea); - textarea.select(); - try { - document.execCommand('copy'); - ev.target.textContent = _('Copied!'); - setTimeout(() => { - ev.target.textContent = _('Copy to Clipboard'); - }, 1000); - } catch (err) { - ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); - } - document.body.removeChild(textarea); - } - }, _('Copy to Clipboard')), - E('button', { - 'class': 'btn', - 'click': ui.hideModal - }, _('Close')) - ]) - ]); - }); - }; - - o = s.taboption('diagnostics', form.Button, '_show_sing_box_config'); - o.title = _('Sing-Box Configuration'); - o.description = _('Show current sing-box configuration'); - o.inputtitle = _('Show Sing-Box Config'); - o.inputstyle = 'apply'; - o.onclick = function () { - return fs.exec('/etc/init.d/podkop', ['show_sing_box_config']) - .then(function (res) { - const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); - ui.showModal(_('Sing-Box Configuration'), [ - E('div', { - style: - 'max-height: 70vh;' + - 'overflow-y: auto;' + - 'margin: 1em 0;' + - 'padding: 1.5em;' + - 'background: #f8f9fa;' + - 'border: 1px solid #e9ecef;' + - 'border-radius: 4px;' + - 'font-family: monospace;' + - 'white-space: pre-wrap;' + - 'word-wrap: break-word;' + - 'line-height: 1.5;' + - 'font-size: 14px;' - }, [ - E('pre', { style: 'margin: 0;' }, formattedOutput) - ]), - E('div', { - style: 'display: flex; justify-content: space-between; margin-top: 1em;' - }, [ - E('button', { - 'class': 'btn', - 'click': function (ev) { - const textarea = document.createElement('textarea'); - textarea.value = '```json\n' + formattedOutput + '\n```'; - document.body.appendChild(textarea); - textarea.select(); - try { - document.execCommand('copy'); - ev.target.textContent = _('Copied!'); - setTimeout(() => { - ev.target.textContent = _('Copy to Clipboard'); - }, 1000); - } catch (err) { - ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); - } - document.body.removeChild(textarea); - } - }, _('Copy to Clipboard')), - E('button', { - 'class': 'btn', - 'click': ui.hideModal - }, _('Close')) - ]) - ]); - }); - }; - - o = s.taboption('diagnostics', form.Button, '_show_config'); - o.title = _('Podkop Configuration'); - o.description = _('Show current podkop configuration with masked sensitive data'); - o.inputtitle = _('Show Config'); - o.inputstyle = 'apply'; - o.onclick = function () { - return fs.exec('/etc/init.d/podkop', ['show_config']) - .then(function (res) { - const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); - ui.showModal(_('Podkop Configuration'), [ - E('div', { - style: - 'max-height: 70vh;' + - 'overflow-y: auto;' + - 'margin: 1em 0;' + - 'padding: 1.5em;' + - 'background: #f8f9fa;' + - 'border: 1px solid #e9ecef;' + - 'border-radius: 4px;' + - 'font-family: monospace;' + - 'white-space: pre-wrap;' + - 'word-wrap: break-word;' + - 'line-height: 1.5;' + - 'font-size: 14px;' - }, [ - E('pre', { style: 'margin: 0;' }, formattedOutput) - ]), - E('div', { - style: 'display: flex; justify-content: space-between; margin-top: 1em;' - }, [ - E('button', { - 'class': 'btn', - 'click': function (ev) { - const textarea = document.createElement('textarea'); - textarea.value = '```json\n' + formattedOutput + '\n```'; - document.body.appendChild(textarea); - textarea.select(); - try { - document.execCommand('copy'); - ev.target.textContent = _('Copied!'); - setTimeout(() => { - ev.target.textContent = _('Copy to Clipboard'); - }, 1000); - } catch (err) { - ui.addNotification(null, E('p', {}, _('Failed to copy: ') + err.message)); - } - document.body.removeChild(textarea); - } - }, _('Copy to Clipboard')), - E('button', { - 'class': 'btn', - 'click': ui.hideModal - }, _('Close')) - ]) - ]); - }); - }; - - o = s.taboption('diagnostics', form.Button, '_list_update'); - o.title = _('Update Lists'); - o.description = _('Update all lists in config'); - o.inputtitle = _('Update Lists'); - o.inputstyle = 'apply'; - o.onclick = function () { - return fs.exec('/etc/init.d/podkop', ['list_update']) - .then(function (res) { - const formattedOutput = formatDiagnosticOutput(res.stdout || _('No output')); - ui.showModal(_('Lists Update Results'), [ - E('div', { style: 'white-space:pre-wrap;padding:5px' }, formattedOutput), - E('div', { class: 'right' }, E('button', { - class: 'btn', - click: ui.hideModal - }, _('Close'))) - ]); - }); - }; // Add new section 'extra' var s = m.section(form.TypedSection, 'extra', _('Extra configurations')); diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 32da8a8..54663a2 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -7,7 +7,7 @@ script=$(readlink "$initscript") NAME="$(basename ${script:-$initscript})" config_load "$NAME" -EXTRA_COMMANDS="main list_update check_proxy check_nft check_github check_logs check_sing_box_connections check_sing_box_logs check_dnsmasq show_config show_version show_sing_box_config show_luci_version show_sing_box_version show_system_info" +EXTRA_COMMANDS="main list_update check_proxy check_nft check_github check_logs check_sing_box_connections check_sing_box_logs check_dnsmasq show_config show_version show_sing_box_config show_luci_version show_sing_box_version show_system_info get_status get_sing_box_status" EXTRA_HELP=" list_update Updating domain and subnet lists check_proxy Check if sing-box proxy works correctly check_nft Show PodkopTable nftables rules @@ -21,7 +21,8 @@ EXTRA_HELP=" list_update Updating domain and subnet lists show_sing_box_config Show current sing-box configuration show_luci_version Show LuCI app version show_sing_box_version Show sing-box version - show_system_info Show OpenWrt version and device model" + show_system_info Show OpenWrt version and device model + get_sing_box_status Get sing-box status" [ ! -L /usr/sbin/podkop ] && ln -s /etc/init.d/podkop /usr/sbin/podkop @@ -1629,4 +1630,72 @@ show_system_info() { echo echo "=== Device Model ===" cat /tmp/sysinfo/model +} + +get_sing_box_status() { + local running=0 + local enabled=0 + local status="" + local version="" + + # Check if service is enabled + if [ -x /etc/rc.d/S99sing-box ]; then + enabled=1 + fi + + # Check if service is running + if pgrep -f "sing-box" >/dev/null; then + running=1 + version=$(sing-box version | head -n 1 | awk '{print $3}') + fi + + # Format status message + if [ $running -eq 1 ]; then + if [ $enabled -eq 1 ]; then + status="running & enabled" + else + status="running but disabled" + fi + else + if [ $enabled -eq 1 ]; then + status="stopped but enabled" + else + status="stopped & disabled" + fi + fi + + echo "{\"running\":$running,\"enabled\":$enabled,\"status\":\"$status\"}" +} + +get_status() { + local running=0 + local enabled=0 + local status="" + + # Check if service is enabled + if [ -x /etc/rc.d/S99podkop ]; then + enabled=1 + fi + + # Check if service is running + if pgrep -f "sing-box" >/dev/null; then + running=1 + fi + + # Format status message + if [ $running -eq 1 ]; then + if [ $enabled -eq 1 ]; then + status="running & enabled" + else + status="running but disabled" + fi + else + if [ $enabled -eq 1 ]; then + status="stopped but enabled" + else + status="stopped & disabled" + fi + fi + + echo "{\"running\":$running,\"enabled\":$enabled,\"status\":\"$status\"}" } \ No newline at end of file From fc99bd7aaaf2dc1f476b0cdcd338838e62ebde58 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 18 Feb 2025 18:28:52 +0300 Subject: [PATCH 04/11] feat: add spacing and line break to diagnostic tools section --- .../htdocs/luci-static/resources/view/podkop/podkop.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 690cac9..3e08033 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 @@ -985,7 +985,9 @@ return view.extend({ // Diagnostics Column E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [ E('div', { 'style': 'margin-bottom: 15px;' }, [ - E('strong', {}, _('Diagnostic Tools')) + E('strong', {}, _('Diagnostic Tools')), + E('br'), + E('div', { 'style': 'height: 18px;' }) ]), E('div', { 'class': 'btn-group', 'style': 'display: flex; flex-direction: column; gap: 8px;' }, [ E('button', { From d934bcc5e97cdd5ef62a18b9f4326eef1295a50e Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 18 Feb 2025 18:56:34 +0300 Subject: [PATCH 05/11] refactor: add async to diagnostics section UI --- .../resources/view/podkop/podkop.js | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) 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 3e08033..3c420e6 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 @@ -649,16 +649,13 @@ return view.extend({ // Service Status Section let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, singbox, system) { return E('div', { 'class': 'cbi-section' }, [ - E('h3', {}, _('Service Status')), E('div', { 'class': 'table', style: 'display: flex; gap: 20px;' }, [ // Podkop Column E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [ E('div', { 'style': 'margin-bottom: 15px;' }, [ E('strong', {}, _('Podkop Status')), E('br'), - E('span', { - 'style': `color: ${podkopStatus.running ? '#4caf50' : '#f44336'}` - }, [ + E('span', { 'style': `color: ${podkopStatus.running ? '#4caf50' : '#f44336'}` }, [ podkopStatus.running ? '✔' : '✘', ' ', podkopStatus.status @@ -809,9 +806,7 @@ return view.extend({ E('div', { 'style': 'margin-bottom: 15px;' }, [ E('strong', {}, _('Sing-box Status')), E('br'), - E('span', { - 'style': `color: ${singboxStatus.running ? '#4caf50' : '#f44336'}` - }, [ + E('span', { 'style': `color: ${singboxStatus.running ? '#4caf50' : '#f44336'}` }, [ singboxStatus.running ? '✔' : '✘', ' ', `${singboxStatus.status}` @@ -1175,7 +1170,11 @@ return view.extend({ o = s.taboption('diagnostics', form.DummyValue, '_status'); o.rawhtml = true; o.cfgvalue = function () { - return Promise.all([ + return E('div', { id: 'diagnostics-status' }, _('Loading diagnostics...')); + }; + + function updateDiagnostics() { + Promise.all([ fs.exec('/etc/init.d/podkop', ['get_status']), fs.exec('/etc/init.d/podkop', ['get_sing_box_status']), fs.exec('/etc/init.d/podkop', ['show_version']), @@ -1186,17 +1185,21 @@ return view.extend({ try { const parsedPodkopStatus = JSON.parse(podkopStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}'); const parsedSingboxStatus = JSON.parse(singboxStatus.stdout || '{"running":0,"enabled":0,"status":"unknown"}'); - return createStatusSection(parsedPodkopStatus, parsedSingboxStatus, podkop, luci, singbox, system); + var newContent = createStatusSection(parsedPodkopStatus, parsedSingboxStatus, podkop, luci, singbox, system); + var container = document.getElementById('diagnostics-status'); + if (container) { + container.innerHTML = ''; + container.appendChild(newContent); + } } catch (e) { - console.error('Error parsing status:', e); - return E('div', { 'class': 'alert-message warning' }, [ - E('strong', {}, _('Error loading status')), E('br'), - E('pre', {}, e.toString()) - ]); + console.error('Error parsing diagnostics status:', e); + var container = document.getElementById('diagnostics-status'); + if (container) { + container.innerHTML = '
' + _('Error loading diagnostics') + '
' + e.toString() + '
'; + } } }); - }; - + } // Add new section 'extra' var s = m.section(form.TypedSection, 'extra', _('Extra configurations')); @@ -1523,6 +1526,15 @@ return view.extend({ // o = s.taboption('basic', form.Flag, 'socks5', _('Mixed enable'), _('Browser port: 2080 (extra +1)')); // o.default = '0'; // o.rmempty = false; - return m.render(); + + let map_promise = m.render(); + + map_promise.then(node => { + node.classList.add('fade-in'); + updateDiagnostics(); + return node; + }); + + return map_promise; } }); \ No newline at end of file From c1fac487c717899827b82473aff806a8b7c75e98 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 18 Feb 2025 21:05:02 +0300 Subject: [PATCH 06/11] style: add tooltip functionality and adjust CSS for better UI --- .../resources/view/podkop/podkop.js | 77 ++++++++++++++++++- 1 file changed, 74 insertions(+), 3 deletions(-) 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 3c420e6..242cbd8 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 @@ -19,8 +19,78 @@ return view.extend({ + `); + // Add tooltip functionality + setTimeout(() => { + document.querySelectorAll('.cbi-value-field').forEach(field => { + const description = field.querySelector('.cbi-value-description'); + if (description) { + const text = description.textContent; + let tooltip = null; + + // Find all input elements within the field + const inputElements = field.querySelectorAll('input, select, textarea'); + inputElements.forEach(inputElement => { + inputElement.addEventListener('mouseenter', (e) => { + tooltip = document.createElement('div'); + tooltip.className = 'tooltip'; + tooltip.textContent = text; + document.body.appendChild(tooltip); + + const updatePosition = (e) => { + const rect = inputElement.getBoundingClientRect(); + const scrollX = window.pageXOffset || document.documentElement.scrollLeft; + const scrollY = window.pageYOffset || document.documentElement.scrollTop; + tooltip.style.left = (e.pageX + 15) + 'px'; + tooltip.style.top = (e.pageY + 10) + 'px'; + }; + + updatePosition(e); + inputElement.addEventListener('mousemove', updatePosition); + }); + + inputElement.addEventListener('mouseleave', () => { + if (tooltip) { + tooltip.remove(); + tooltip = null; + } + }); + }); + } + }); + }, 1000); + var m, s, o; m = new form.Map('podkop', _('Podkop configuration'), null, ['main', 'second']); @@ -50,7 +120,7 @@ return view.extend({ o = s.taboption('basic', form.TextValue, 'proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); o.depends('proxy_config_type', 'url'); - o.rows = 5; + o.rows = 7; o.ucisection = 'main'; o.validate = function (section_id, value) { if (!value || value.length === 0) { @@ -193,7 +263,7 @@ return view.extend({ o = s.taboption('basic', form.TextValue, 'outbound_json', _('Outbound Configuration'), _('Enter complete outbound configuration in JSON format')); o.depends('proxy_config_type', 'outbound'); - o.rows = 10; + o.rows = 7; o.ucisection = 'main'; o.validate = function (section_id, value) { if (!value || value.length === 0) { @@ -358,7 +428,7 @@ return view.extend({ o = s.taboption('basic', form.TextValue, 'custom_domains_text', _('User Domains List'), _('Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)')); o.placeholder = 'example.com, sub.example.com\ndomain.com test.com\nsubdomain.domain.com another.com, third.com'; o.depends('custom_domains_list_type', 'text'); - o.rows = 10; + o.rows = 8; o.rmempty = false; o.ucisection = 'main'; o.validate = function (section_id, value) { @@ -649,6 +719,7 @@ return view.extend({ // Service Status Section let createStatusSection = function (podkopStatus, singboxStatus, podkop, luci, singbox, system) { return E('div', { 'class': 'cbi-section' }, [ + E('h3', {}, _('Service Status')), E('div', { 'class': 'table', style: 'display: flex; gap: 20px;' }, [ // Podkop Column E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [ From 8dec59d1182775f3f0fcc15b0939ab109a45e933 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 18 Feb 2025 21:17:02 +0300 Subject: [PATCH 07/11] docs: update Russian translations for new UI elements in luci-app-podkop --- luci-app-podkop/po/ru/podkop.po | 47 ++++++++++++++++++++++++- luci-app-podkop/po/templates/podkop.pot | 45 +++++++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/luci-app-podkop/po/ru/podkop.po b/luci-app-podkop/po/ru/podkop.po index fd080a6..88c039a 100644 --- a/luci-app-podkop/po/ru/podkop.po +++ b/luci-app-podkop/po/ru/podkop.po @@ -489,4 +489,49 @@ msgid "Show Versions" msgstr "Показать версии" msgid "Copied!" -msgstr "Скопировано!" \ No newline at end of file +msgstr "Скопировано!" + +msgid "Service Status" +msgstr "Статус сервиса" + +msgid "Podkop Status" +msgstr "Статус Podkop" + +msgid "Start Podkop" +msgstr "Запустить Podkop" + +msgid "Stop Podkop" +msgstr "Остановить Podkop" + +msgid "Restart Podkop" +msgstr "Перезапустить Podkop" + +msgid "Enable Podkop" +msgstr "Включить Podkop" + +msgid "Disable Podkop" +msgstr "Отключить Podkop" + +msgid "Loading diagnostics..." +msgstr "Загрузка диагностики..." + +msgid "Error loading diagnostics" +msgstr "Ошибка загрузки диагностики" + +msgid "Sing-box Status" +msgstr "Статус Sing-box" + +msgid "Diagnostic Tools" +msgstr "Инструменты диагностики" + +msgid "Unknown" +msgstr "Неизвестно" + +msgid "Every 3 hours" +msgstr "Каждые 3 часа" + +msgid "Every day" +msgstr "Каждый день" + +msgid "Every 3 days" +msgstr "Каждые 3 дня" \ No newline at end of file diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot index f501532..6bba873 100644 --- a/luci-app-podkop/po/templates/podkop.pot +++ b/luci-app-podkop/po/templates/podkop.pot @@ -489,4 +489,49 @@ msgid "Show Versions" msgstr "" msgid "Copied!" +msgstr "" + +msgid "Service Status" +msgstr "" + +msgid "Podkop Status" +msgstr "" + +msgid "Start Podkop" +msgstr "" + +msgid "Stop Podkop" +msgstr "" + +msgid "Restart Podkop" +msgstr "" + +msgid "Enable Podkop" +msgstr "" + +msgid "Disable Podkop" +msgstr "" + +msgid "Loading diagnostics..." +msgstr "" + +msgid "Error loading diagnostics" +msgstr "" + +msgid "Sing-box Status" +msgstr "" + +msgid "Diagnostic Tools" +msgstr "" + +msgid "Unknown" +msgstr "" + +msgid "Every 3 hours" +msgstr "" + +msgid "Every day" +msgstr "" + +msgid "Every 3 days" msgstr "" \ No newline at end of file From 8fa19869618c9bc167ec2e27e47d18f6071a6089 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 18 Feb 2025 21:20:15 +0300 Subject: [PATCH 08/11] docs: update Russian translations for luci-app-podkop --- luci-app-podkop/po/ru/podkop.po | 95 ++++++++++++++++++++++++- luci-app-podkop/po/templates/podkop.pot | 93 ++++++++++++++++++++++++ 2 files changed, 187 insertions(+), 1 deletion(-) diff --git a/luci-app-podkop/po/ru/podkop.po b/luci-app-podkop/po/ru/podkop.po index 88c039a..f25c4dc 100644 --- a/luci-app-podkop/po/ru/podkop.po +++ b/luci-app-podkop/po/ru/podkop.po @@ -534,4 +534,97 @@ msgid "Every day" msgstr "Каждый день" msgid "Every 3 days" -msgstr "Каждые 3 дня" \ No newline at end of file +msgstr "Каждые 3 дня" + +msgid "Device Model: " +msgstr "Модель устройства: " + +msgid "OpenWrt Version: " +msgstr "Версия OpenWrt: " + +msgid "Sing-box: " +msgstr "Sing-box: " + +msgid "LuCI App: " +msgstr "LuCI App: " + +msgid "Podkop: " +msgstr "Podkop: " + +msgid "Check NFT Rules" +msgstr "Проверить правила NFT" + +msgid "Update Lists" +msgstr "Обновить списки" + +msgid "Invalid path format. Path must start with "/" and contain only valid characters (letters, numbers, "-", "_", "/", ".")" +msgstr "Неверный формат пути. Путь должен начинаться с \"/\" и содержать только допустимые символы (буквы, цифры, \"-\", \"_\", \"/\", \".\")" + +msgid "Invalid path format" +msgstr "Неверный формат пути" + +msgid "CIDR must be between 0 and 32 in: " +msgstr "CIDR должен быть между 0 и 32 в: " + +msgid "Invalid format: " +msgstr "Неверный формат: " + +msgid "Use format: X.X.X.X or X.X.X.X/Y" +msgstr "Используйте формат: X.X.X.X или X.X.X.X/Y" + +msgid "URL must start with vless:// or ss://" +msgstr "URL должен начинаться с vless:// или ss://" + +msgid "Invalid Shadowsocks URL format" +msgstr "Неверный формат URL Shadowsocks" + +msgid "Invalid Shadowsocks URL format: missing method and password separator ":" +msgstr "Неверный формат URL Shadowsocks: отсутствует разделитель метода и пароля \":\"" + +msgid "Invalid Shadowsocks URL: missing server address" +msgstr "Неверный URL Shadowsocks: отсутствует адрес сервера" + +msgid "Invalid Shadowsocks URL: missing server" +msgstr "Неверный URL Shadowsocks: отсутствует сервер" + +msgid "Invalid Shadowsocks URL: missing port" +msgstr "Неверный URL Shadowsocks: отсутствует порт" + +msgid "Invalid port number. Must be between 1 and 65535" +msgstr "Неверный номер порта. Должен быть между 1 и 65535" + +msgid "Invalid Shadowsocks URL: missing or invalid server/port format" +msgstr "Неверный URL Shadowsocks: отсутствует или неверный формат сервера/порта" + +msgid "Invalid VLESS URL: missing UUID" +msgstr "Неверный URL VLESS: отсутствует UUID" + +msgid "Invalid VLESS URL: missing server address" +msgstr "Неверный URL VLESS: отсутствует адрес сервера" + +msgid "Invalid VLESS URL: missing server" +msgstr "Неверный URL VLESS: отсутствует сервер" + +msgid "Invalid VLESS URL: missing port" +msgstr "Неверный URL VLESS: отсутствует порт" + +msgid "Invalid VLESS URL: missing query parameters" +msgstr "Неверный URL VLESS: отсутствуют параметры запроса" + +msgid "Invalid VLESS URL: missing type parameter" +msgstr "Неверный URL VLESS: отсутствует параметр type" + +msgid "Invalid VLESS URL: missing security parameter" +msgstr "Неверный URL VLESS: отсутствует параметр security" + +msgid "Invalid VLESS URL: missing pbk parameter for reality security" +msgstr "Неверный URL VLESS: отсутствует параметр pbk для security reality" + +msgid "Invalid VLESS URL: missing fp parameter for reality security" +msgstr "Неверный URL VLESS: отсутствует параметр fp для security reality" + +msgid "Invalid VLESS URL: missing sni parameter for tls security" +msgstr "Неверный URL VLESS: отсутствует параметр sni для security tls" + +msgid "Invalid URL format: " +msgstr "Неверный формат URL: " \ No newline at end of file diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot index 6bba873..85701df 100644 --- a/luci-app-podkop/po/templates/podkop.pot +++ b/luci-app-podkop/po/templates/podkop.pot @@ -534,4 +534,97 @@ msgid "Every day" msgstr "" msgid "Every 3 days" +msgstr "" + +msgid "Device Model: " +msgstr "" + +msgid "OpenWrt Version: " +msgstr "" + +msgid "Sing-box: " +msgstr "" + +msgid "LuCI App: " +msgstr "" + +msgid "Podkop: " +msgstr "" + +msgid "Check NFT Rules" +msgstr "" + +msgid "Update Lists" +msgstr "" + +msgid "Invalid path format. Path must start with "/" and contain only valid characters (letters, numbers, "-", "_", "/", ".")" +msgstr "" + +msgid "Invalid path format" +msgstr "" + +msgid "CIDR must be between 0 and 32 in: " +msgstr "" + +msgid "Invalid format: " +msgstr "" + +msgid "Use format: X.X.X.X or X.X.X.X/Y" +msgstr "" + +msgid "URL must start with vless:// or ss://" +msgstr "" + +msgid "Invalid Shadowsocks URL format" +msgstr "" + +msgid "Invalid Shadowsocks URL format: missing method and password separator ":" +msgstr "" + +msgid "Invalid Shadowsocks URL: missing server address" +msgstr "" + +msgid "Invalid Shadowsocks URL: missing server" +msgstr "" + +msgid "Invalid Shadowsocks URL: missing port" +msgstr "" + +msgid "Invalid port number. Must be between 1 and 65535" +msgstr "" + +msgid "Invalid Shadowsocks URL: missing or invalid server/port format" +msgstr "" + +msgid "Invalid VLESS URL: missing UUID" +msgstr "" + +msgid "Invalid VLESS URL: missing server address" +msgstr "" + +msgid "Invalid VLESS URL: missing server" +msgstr "" + +msgid "Invalid VLESS URL: missing port" +msgstr "" + +msgid "Invalid VLESS URL: missing query parameters" +msgstr "" + +msgid "Invalid VLESS URL: missing type parameter" +msgstr "" + +msgid "Invalid VLESS URL: missing security parameter" +msgstr "" + +msgid "Invalid VLESS URL: missing pbk parameter for reality security" +msgstr "" + +msgid "Invalid VLESS URL: missing fp parameter for reality security" +msgstr "" + +msgid "Invalid VLESS URL: missing sni parameter for tls security" +msgstr "" + +msgid "Invalid URL format: " msgstr "" \ No newline at end of file From cceedd6c17eb8c3c115290c2b27cb23abdfb5205 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 18 Feb 2025 21:27:56 +0300 Subject: [PATCH 09/11] docs: update Russian translations for error messages and UI strings --- luci-app-podkop/po/ru/podkop.po | 336 ++++++++++++++++++++++-- luci-app-podkop/po/templates/podkop.pot | 254 ++++++++++++++++-- 2 files changed, 550 insertions(+), 40 deletions(-) diff --git a/luci-app-podkop/po/ru/podkop.po b/luci-app-podkop/po/ru/podkop.po index f25c4dc..97451cb 100644 --- a/luci-app-podkop/po/ru/podkop.po +++ b/luci-app-podkop/po/ru/podkop.po @@ -74,7 +74,7 @@ msgid "Remote Domain Lists" msgstr "Удаленные списки доменов" msgid "Download and use domain lists from remote URLs" -msgstr "Загрузка и использование списков доменов с удаленных URL" +msgstr "Загрузка и использование списков доменов из удаленных URL" msgid "Remote Domain URLs" msgstr "URL удаленных доменов" @@ -200,7 +200,7 @@ msgid "Subnets" msgstr "Подсети" msgid "Invalid domain format. Enter domain without protocol (example: sub.example.com)" -msgstr "Неверный формат домена. Введите домен без протокола (пример: sub.example.com)" +msgstr "Неверный формат домена. Введите домен без протокола (например: sub.example.com)" msgid "URL must use http:// or https:// protocol" msgstr "URL должен использовать протокол http:// или https://" @@ -208,8 +208,8 @@ msgstr "URL должен использовать протокол http:// ил msgid "Invalid URL format. URL must start with http:// or https://" msgstr "Неверный формат URL. URL должен начинаться с http:// или https://" -msgid "Invalid subnet format. Use format: X.X.X.X/Y (like 192.168.1.0/24)" -msgstr "Неверный формат подсети. Используйте формат: X.X.X.X/Y (например: 192.168.1.0/24)" +msgid "Invalid subnet format. Use format: X.X.X.X/Y (like 103.21.244.0/22)" +msgstr "Неверный формат подсети. Используйте формат: X.X.X.X/Y (например: 103.21.244.0/22)" msgid "IP address parts must be between 0 and 255" msgstr "Части IP-адреса должны быть между 0 и 255" @@ -328,8 +328,8 @@ msgstr "Просмотр недавних системных логов, свя msgid "View Logs" msgstr "Просмотр логов" -msgid "Failed to copy logs: " -msgstr "Ошибка копирования логов: " +msgid "Failed to copy logs: %s" +msgstr "Ошибка копирования логов: %s" msgid "Show Config" msgstr "Показать конфигурацию" @@ -557,20 +557,17 @@ msgstr "Проверить правила NFT" msgid "Update Lists" msgstr "Обновить списки" -msgid "Invalid path format. Path must start with "/" and contain only valid characters (letters, numbers, "-", "_", "/", ".")" +msgid "Invalid path format. Path must start with \"/\" and contain only valid characters (letters, numbers, \"-\", \"_\", \"/\", \".\")" msgstr "Неверный формат пути. Путь должен начинаться с \"/\" и содержать только допустимые символы (буквы, цифры, \"-\", \"_\", \"/\", \".\")" msgid "Invalid path format" msgstr "Неверный формат пути" -msgid "CIDR must be between 0 and 32 in: " -msgstr "CIDR должен быть между 0 и 32 в: " +msgid "CIDR must be between 0 and 32 in: %s" +msgstr "CIDR должен быть между 0 и 32 в: %s" -msgid "Invalid format: " -msgstr "Неверный формат: " - -msgid "Use format: X.X.X.X or X.X.X.X/Y" -msgstr "Используйте формат: X.X.X.X или X.X.X.X/Y" +msgid "Invalid format: %s. Use format: X.X.X.X or X.X.X.X/Y" +msgstr "Неверный формат: %s. Используйте формат: X.X.X.X или X.X.X.X/Y" msgid "URL must start with vless:// or ss://" msgstr "URL должен начинаться с vless:// или ss://" @@ -627,4 +624,313 @@ msgid "Invalid VLESS URL: missing sni parameter for tls security" msgstr "Неверный URL VLESS: отсутствует параметр sni для security tls" msgid "Invalid URL format: " -msgstr "Неверный формат URL: " \ No newline at end of file +msgstr "Неверный формат URL: " + +msgid "http://openwrt.lan:9090/ui" +msgstr "http://openwrt.lan:9090/ui" + +msgid "Browser port: 2080 (extra +1)" +msgstr "Порт браузера: 2080 (extra +1)" + +msgid "Select predefined service for routing" +msgstr "Выберите предустановленный сервис для маршрутизации" + +msgid "github.com/itdoginfo/allow-domains" +msgstr "github.com/itdoginfo/allow-domains" + +msgid "Error parsing diagnostics status: %s" +msgstr "Ошибка разбора статуса диагностики: %s" + +msgid "No configuration" +msgstr "Нет конфигурации" + +msgid "No logs available" +msgstr "Нет доступных логов" + +msgid "No connections" +msgstr "Нет соединений" + +msgid "No rules" +msgstr "Нет правил" + +msgid "No results" +msgstr "Нет результатов" + +msgid "Status: %s" +msgstr "Статус: %s" + +msgid "Running" +msgstr "Запущен" + +msgid "Stopped" +msgstr "Остановлен" + +msgid "Enabled" +msgstr "Включен" + +msgid "Disabled" +msgstr "Отключен" + +msgid "Error: %s" +msgstr "Ошибка: %s" + +msgid "Failed to apply changes: %s" +msgstr "Не удалось применить изменения: %s" + +msgid "Operation completed successfully" +msgstr "Операция успешно завершена" + +msgid "Operation failed: %s" +msgstr "Операция не удалась: %s" + +msgid "Configuration saved" +msgstr "Конфигурация сохранена" + +msgid "Save failed: %s" +msgstr "Ошибка сохранения: %s" + +msgid "Loading..." +msgstr "Загрузка..." + +msgid "Processing..." +msgstr "Обработка..." + +msgid "Updating..." +msgstr "Обновление..." + +msgid "Checking..." +msgstr "Проверка..." + +msgid "Please wait..." +msgstr "Пожалуйста, подождите..." + +msgid "Alert" +msgstr "Оповещение" + +msgid "Warning" +msgstr "Предупреждение" + +msgid "Success" +msgstr "Успешно" + +msgid "Info" +msgstr "Информация" + +msgid "Error" +msgstr "Ошибка" + +msgid "Debug" +msgstr "Отладка" + +msgid "Trace" +msgstr "Трассировка" + +msgid "Yes" +msgstr "Да" + +msgid "No" +msgstr "Нет" + +msgid "OK" +msgstr "OK" + +msgid "Cancel" +msgstr "Отмена" + +msgid "Apply" +msgstr "Применить" + +msgid "Save" +msgstr "Сохранить" + +msgid "Delete" +msgstr "Удалить" + +msgid "Edit" +msgstr "Редактировать" + +msgid "Add" +msgstr "Добавить" + +msgid "Remove" +msgstr "Удалить" + +msgid "Move Up" +msgstr "Переместить вверх" + +msgid "Move Down" +msgstr "Переместить вниз" + +msgid "Expand" +msgstr "Развернуть" + +msgid "Collapse" +msgstr "Свернуть" + +msgid "Show" +msgstr "Показать" + +msgid "Hide" +msgstr "Скрыть" + +msgid "Enable" +msgstr "Включить" + +msgid "Disable" +msgstr "Отключить" + +msgid "Start" +msgstr "Запустить" + +msgid "Stop" +msgstr "Остановить" + +msgid "Restart" +msgstr "Перезапустить" + +msgid "Reset" +msgstr "Сбросить" + +msgid "Refresh" +msgstr "Обновить" + +msgid "Update" +msgstr "Обновить" + +msgid "Install" +msgstr "Установить" + +msgid "Uninstall" +msgstr "Удалить" + +msgid "Configure" +msgstr "Настроить" + +msgid "Settings" +msgstr "Настройки" + +msgid "Options" +msgstr "Опции" + +msgid "Advanced" +msgstr "Расширенные" + +msgid "Basic" +msgstr "Основные" + +msgid "General" +msgstr "Общие" + +msgid "Details" +msgstr "Подробности" + +msgid "Status" +msgstr "Статус" + +msgid "Information" +msgstr "Информация" + +msgid "Configuration" +msgstr "Конфигурация" + +msgid "Management" +msgstr "Управление" + +msgid "System" +msgstr "Система" + +msgid "Network" +msgstr "Сеть" + +msgid "Services" +msgstr "Сервисы" + +msgid "Check failed" +msgstr "Проверка не удалась" + +msgid "Remote Domain Lists URL" +msgstr "URL удаленных списков доменов" + +msgid "Enter URL to download domain list" +msgstr "Введите URL для загрузки списка доменов" + +msgid "Update Interval" +msgstr "Интервал обновления" + +msgid "Select how often to update the lists" +msgstr "Выберите как часто обновлять списки" + +msgid "Last Update" +msgstr "Последнее обновление" + +msgid "Last update time" +msgstr "Время последнего обновления" + +msgid "Next Update" +msgstr "Следующее обновление" + +msgid "Next scheduled update time" +msgstr "Время следующего запланированного обновления" + +msgid "Version" +msgstr "Версия" + +msgid "Component version" +msgstr "Версия компонента" + +msgid "Installed" +msgstr "Установлен" + +msgid "Not installed" +msgstr "Не установлен" + +msgid "Unknown version" +msgstr "Неизвестная версия" + +msgid "Error parsing version" +msgstr "Ошибка разбора версии" + +msgid "Error parsing status" +msgstr "Ошибка разбора статуса" + +msgid "Service is running" +msgstr "Сервис запущен" + +msgid "Service is stopped" +msgstr "Сервис остановлен" + +msgid "Service is enabled" +msgstr "Сервис включен" + +msgid "Service is disabled" +msgstr "Сервис отключен" + +msgid "Service status unknown" +msgstr "Статус сервиса неизвестен" + +msgid "Error checking service status" +msgstr "Ошибка проверки статуса сервиса" + +msgid "Diagnostic check in progress..." +msgstr "Выполняется диагностическая проверка..." + +msgid "Diagnostic check completed" +msgstr "Диагностическая проверка завершена" + +msgid "Diagnostic check failed" +msgstr "Диагностическая проверка не удалась" + +msgid "Update in progress..." +msgstr "Выполняется обновление..." + +msgid "Update completed" +msgstr "Обновление завершено" + +msgid "Update failed" +msgstr "Обновление не удалось" + +msgid "Check in progress..." +msgstr "Выполняется проверка..." + +msgid "Check completed" +msgstr "Проверка завершена" \ No newline at end of file diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot index 85701df..ce059a8 100644 --- a/luci-app-podkop/po/templates/podkop.pot +++ b/luci-app-podkop/po/templates/podkop.pot @@ -307,7 +307,7 @@ msgstr "" msgid "Full Diagnostic Results" msgstr "" -msgid "Failed to copy: " +msgid "Failed to copy: %s" msgstr "" msgid "Copy to Clipboard" @@ -328,7 +328,7 @@ msgstr "" msgid "View Logs" msgstr "" -msgid "Failed to copy logs: " +msgid "Failed to copy logs: %s" msgstr "" msgid "Show Config" @@ -557,74 +557,278 @@ msgstr "" msgid "Update Lists" msgstr "" -msgid "Invalid path format. Path must start with "/" and contain only valid characters (letters, numbers, "-", "_", "/", ".")" +msgid "Invalid path format. Path must start with \"/\" and contain only valid characters (letters, numbers, \"-\", \"_\", \"/\", \".\")" msgstr "" msgid "Invalid path format" msgstr "" -msgid "CIDR must be between 0 and 32 in: " +msgid "CIDR must be between 0 and 32 in: %s" msgstr "" -msgid "Invalid format: " +msgid "Invalid format: %s. Use format: X.X.X.X or X.X.X.X/Y" msgstr "" -msgid "Use format: X.X.X.X or X.X.X.X/Y" +msgid "Failed to copy: %s" msgstr "" -msgid "URL must start with vless:// or ss://" +msgid "Failed to apply changes: %s" msgstr "" -msgid "Invalid Shadowsocks URL format" +msgid "Operation completed successfully" msgstr "" -msgid "Invalid Shadowsocks URL format: missing method and password separator ":" +msgid "Operation failed: %s" msgstr "" -msgid "Invalid Shadowsocks URL: missing server address" +msgid "Configuration saved" msgstr "" -msgid "Invalid Shadowsocks URL: missing server" +msgid "Save failed: %s" msgstr "" -msgid "Invalid Shadowsocks URL: missing port" +msgid "Loading..." msgstr "" -msgid "Invalid port number. Must be between 1 and 65535" +msgid "Processing..." msgstr "" -msgid "Invalid Shadowsocks URL: missing or invalid server/port format" +msgid "Updating..." msgstr "" -msgid "Invalid VLESS URL: missing UUID" +msgid "Checking..." msgstr "" -msgid "Invalid VLESS URL: missing server address" +msgid "Please wait..." msgstr "" -msgid "Invalid VLESS URL: missing server" +msgid "Alert" msgstr "" -msgid "Invalid VLESS URL: missing port" +msgid "Warning" msgstr "" -msgid "Invalid VLESS URL: missing query parameters" +msgid "Success" msgstr "" -msgid "Invalid VLESS URL: missing type parameter" +msgid "Info" msgstr "" -msgid "Invalid VLESS URL: missing security parameter" +msgid "Error" msgstr "" -msgid "Invalid VLESS URL: missing pbk parameter for reality security" +msgid "Debug" msgstr "" -msgid "Invalid VLESS URL: missing fp parameter for reality security" +msgid "Trace" msgstr "" -msgid "Invalid VLESS URL: missing sni parameter for tls security" +msgid "Yes" msgstr "" -msgid "Invalid URL format: " +msgid "No" +msgstr "" + +msgid "OK" +msgstr "" + +msgid "Cancel" +msgstr "" + +msgid "Apply" +msgstr "" + +msgid "Save" +msgstr "" + +msgid "Delete" +msgstr "" + +msgid "Edit" +msgstr "" + +msgid "Add" +msgstr "" + +msgid "Remove" +msgstr "" + +msgid "Move Up" +msgstr "" + +msgid "Move Down" +msgstr "" + +msgid "Expand" +msgstr "" + +msgid "Collapse" +msgstr "" + +msgid "Show" +msgstr "" + +msgid "Hide" +msgstr "" + +msgid "Enable" +msgstr "" + +msgid "Disable" +msgstr "" + +msgid "Start" +msgstr "" + +msgid "Stop" +msgstr "" + +msgid "Restart" +msgstr "" + +msgid "Reset" +msgstr "" + +msgid "Refresh" +msgstr "" + +msgid "Update" +msgstr "" + +msgid "Install" +msgstr "" + +msgid "Uninstall" +msgstr "" + +msgid "Configure" +msgstr "" + +msgid "Settings" +msgstr "" + +msgid "Options" +msgstr "" + +msgid "Advanced" +msgstr "" + +msgid "Basic" +msgstr "" + +msgid "General" +msgstr "" + +msgid "Details" +msgstr "" + +msgid "Status" +msgstr "" + +msgid "Information" +msgstr "" + +msgid "Configuration" +msgstr "" + +msgid "Management" +msgstr "" + +msgid "System" +msgstr "" + +msgid "Network" +msgstr "" + +msgid "Services" +msgstr "" + +msgid "Remote Domain Lists URL" +msgstr "" + +msgid "Enter URL to download domain list" +msgstr "" + +msgid "Update Interval" +msgstr "" + +msgid "Select how often to update the lists" +msgstr "" + +msgid "Last Update" +msgstr "" + +msgid "Last update time" +msgstr "" + +msgid "Next Update" +msgstr "" + +msgid "Next scheduled update time" +msgstr "" + +msgid "Version" +msgstr "" + +msgid "Component version" +msgstr "" + +msgid "Installed" +msgstr "" + +msgid "Not installed" +msgstr "" + +msgid "Unknown version" +msgstr "" + +msgid "Error parsing version" +msgstr "" + +msgid "Error parsing status" +msgstr "" + +msgid "Service is running" +msgstr "" + +msgid "Service is stopped" +msgstr "" + +msgid "Service is enabled" +msgstr "" + +msgid "Service is disabled" +msgstr "" + +msgid "Service status unknown" +msgstr "" + +msgid "Error checking service status" +msgstr "" + +msgid "Diagnostic check in progress..." +msgstr "" + +msgid "Diagnostic check completed" +msgstr "" + +msgid "Diagnostic check failed" +msgstr "" + +msgid "Update in progress..." +msgstr "" + +msgid "Update completed" +msgstr "" + +msgid "Update failed" +msgstr "" + +msgid "Check in progress..." +msgstr "" + +msgid "Check completed" +msgstr "" + +msgid "Check failed" msgstr "" \ No newline at end of file From f76c657bd70e951630de3da36f3d3f112ebafb11 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Tue, 18 Feb 2025 22:11:58 +0300 Subject: [PATCH 10/11] style: remove unused CSS and JavaScript for tooltips --- .../resources/view/podkop/podkop.js | 67 +------------------ 1 file changed, 1 insertion(+), 66 deletions(-) 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 242cbd8..2dc4648 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 @@ -21,76 +21,11 @@ return view.extend({ `); - // Add tooltip functionality - setTimeout(() => { - document.querySelectorAll('.cbi-value-field').forEach(field => { - const description = field.querySelector('.cbi-value-description'); - if (description) { - const text = description.textContent; - let tooltip = null; - - // Find all input elements within the field - const inputElements = field.querySelectorAll('input, select, textarea'); - inputElements.forEach(inputElement => { - inputElement.addEventListener('mouseenter', (e) => { - tooltip = document.createElement('div'); - tooltip.className = 'tooltip'; - tooltip.textContent = text; - document.body.appendChild(tooltip); - - const updatePosition = (e) => { - const rect = inputElement.getBoundingClientRect(); - const scrollX = window.pageXOffset || document.documentElement.scrollLeft; - const scrollY = window.pageYOffset || document.documentElement.scrollTop; - tooltip.style.left = (e.pageX + 15) + 'px'; - tooltip.style.top = (e.pageY + 10) + 'px'; - }; - - updatePosition(e); - inputElement.addEventListener('mousemove', updatePosition); - }); - - inputElement.addEventListener('mouseleave', () => { - if (tooltip) { - tooltip.remove(); - tooltip = null; - } - }); - }); - } - }); - }, 1000); - var m, s, o; m = new form.Map('podkop', _('Podkop configuration'), null, ['main', 'second']); From 6222221847a528f9cbdddf44ef189428a910f635 Mon Sep 17 00:00:00 2001 From: Ivan K Date: Wed, 19 Feb 2025 19:18:22 +0300 Subject: [PATCH 11/11] docs: update Russian translations and add new strings for FakeIP status check --- .../resources/view/podkop/podkop.js | 52 +- luci-app-podkop/po/ru/podkop.po | 977 +++++------------- luci-app-podkop/po/templates/podkop.pot | 765 +++++++------- podkop/files/etc/init.d/podkop | 37 + 4 files changed, 746 insertions(+), 1085 deletions(-) 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 2dc4648..c27548a 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 @@ -55,7 +55,7 @@ return view.extend({ o = s.taboption('basic', form.TextValue, 'proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); o.depends('proxy_config_type', 'url'); - o.rows = 7; + o.rows = 5; o.ucisection = 'main'; o.validate = function (section_id, value) { if (!value || value.length === 0) { @@ -198,7 +198,7 @@ return view.extend({ o = s.taboption('basic', form.TextValue, 'outbound_json', _('Outbound Configuration'), _('Enter complete outbound configuration in JSON format')); o.depends('proxy_config_type', 'outbound'); - o.rows = 7; + o.rows = 10; o.ucisection = 'main'; o.validate = function (section_id, value) { if (!value || value.length === 0) { @@ -986,9 +986,10 @@ return view.extend({ // Diagnostics Column E('div', { 'style': 'flex: 1; padding: 15px; background: #f8f9fa; border-radius: 8px;' }, [ E('div', { 'style': 'margin-bottom: 15px;' }, [ - E('strong', {}, _('Diagnostic Tools')), - E('br'), - E('div', { 'style': 'height: 18px;' }) + E('strong', {}, _('FakeIP Status')), + E('div', { 'id': 'fakeip-status' }, [ + E('span', {}, _('Checking FakeIP...')) + ]) ]), E('div', { 'class': 'btn-group', 'style': 'display: flex; flex-direction: column; gap: 8px;' }, [ E('button', { @@ -1168,7 +1169,7 @@ return view.extend({ E('strong', {}, 'Device Model: '), system.stdout ? system.stdout.split('\n')[4].trim() : _('Unknown') ]) ]) - ]) + ]), ]) ]); }; @@ -1179,6 +1180,44 @@ return view.extend({ return E('div', { id: 'diagnostics-status' }, _('Loading diagnostics...')); }; + function checkFakeIP() { + fetch('http://httpbin.org/ip') + .then(response => response.text()) + .then(text => { + const statusElement = document.getElementById('fakeip-status'); + if (!statusElement) return; + + console.log('FakeIP check response:', text); + + if (text.includes('Cannot GET /ip')) { + console.log('FakeIP status: working (Cannot GET /ip)'); + statusElement.innerHTML = E('span', { 'style': 'color: #4caf50' }, [ + '✔ ', + _('working') + ]).outerHTML; + } else if (text.includes('"origin":')) { + console.log('FakeIP status: not working (got IP response)'); + statusElement.innerHTML = E('span', { 'style': 'color: #f44336' }, [ + '✘ ', + _('not working') + ]).outerHTML; + } else { + console.log('FakeIP status: check error (unexpected response)'); + statusElement.innerHTML = E('span', { 'style': 'color: #ff9800' }, [ + '! ', + _('check error') + ]).outerHTML; + } + }) + .catch(error => { + console.log('FakeIP check error:', error.message); + const statusElement = document.getElementById('fakeip-status'); + if (statusElement) { + statusElement.innerHTML = E('span', { 'style': 'color: #ff9800' }, 'check error').outerHTML; + } + }); + } + function updateDiagnostics() { Promise.all([ fs.exec('/etc/init.d/podkop', ['get_status']), @@ -1196,6 +1235,7 @@ return view.extend({ if (container) { container.innerHTML = ''; container.appendChild(newContent); + checkFakeIP(); } } catch (e) { console.error('Error parsing diagnostics status:', e); diff --git a/luci-app-podkop/po/ru/podkop.po b/luci-app-podkop/po/ru/podkop.po index 97451cb..b5afa80 100644 --- a/luci-app-podkop/po/ru/podkop.po +++ b/luci-app-podkop/po/ru/podkop.po @@ -25,44 +25,59 @@ msgstr "Тип подключения" msgid "Select between VPN and Proxy connection methods for traffic routing" msgstr "Выберите между VPN и Proxy методами для маршрутизации трафика" +msgid "Configuration Type" +msgstr "Тип конфигурации" + +msgid "Select how to configure the proxy" +msgstr "Выберите способ настройки прокси" + +msgid "Connection URL" +msgstr "URL подключения" + +msgid "Outbound Config" +msgstr "Конфигурация Outbound" + msgid "Proxy Configuration URL" msgstr "URL конфигурации прокси" msgid "Enter connection string starting with vless:// or ss:// for proxy configuration" msgstr "Введите строку подключения, начинающуюся с vless:// или ss:// для настройки прокси" +msgid "Outbound Configuration" +msgstr "Конфигурация исходящего соединения" + +msgid "Enter complete outbound configuration in JSON format" +msgstr "Введите полную конфигурацию исходящего соединения в формате JSON" + msgid "Network Interface" msgstr "Сетевой интерфейс" msgid "Select network interface for VPN connection" msgstr "Выберите сетевой интерфейс для VPN подключения" -msgid "Community Domain Lists" -msgstr "Предустановленные списки доменов" +msgid "Community Lists" +msgstr "Предустановленные списки" -msgid "Domain List" -msgstr "Список доменов" +msgid "Service List" +msgstr "Список сервисов" -msgid "Select a list" -msgstr "Выберите список доменов" +msgid "Select predefined service for routing" +msgstr "Выберите предустановленные сервисы для маршрутизации" -msgid "Community Subnet Lists" -msgstr "Предустановленные сети сервисов" +msgid "User Domain List Type" +msgstr "Тип пользовательского списка доменов" -msgid "Enable routing for popular services like Twitter, Meta, and Discord" -msgstr "Включить маршрутизацию для популярных сервисов, таких как Twitter, Meta и Discord" +msgid "Select how to add your custom domains" +msgstr "Выберите способ добавления пользовательских доменов" -msgid "Service Networks" -msgstr "Сети сервисов" +msgid "Disabled" +msgstr "Отключено" -msgid "Select predefined service networks for routing" -msgstr "Выберите предустановленные сети сервисов для маршрутизации" +msgid "Dynamic List" +msgstr "Динамический список" -msgid "User Domain List" -msgstr "Пользовательский список доменов" - -msgid "Enable and manage your custom list of domains for selective routing" -msgstr "Включить и управлять пользовательским списком доменов для выборочной маршрутизации" +msgid "Text List" +msgstr "Текстовый список" msgid "User Domains" msgstr "Пользовательские домены" @@ -70,11 +85,29 @@ msgstr "Пользовательские домены" msgid "Enter domain names without protocols (example: sub.example.com or example.com)" msgstr "Введите имена доменов без протоколов (пример: sub.example.com или example.com)" +msgid "User Domains List" +msgstr "Список пользовательских доменов" + +msgid "Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)" +msgstr "Введите имена доменов через запятую, пробел или новую строку (пример: sub.example.com, example.com или один домен на строку)" + +msgid "Local Domain Lists" +msgstr "Локальные списки доменов" + +msgid "Use the list from the router filesystem" +msgstr "Использовать список из файловой системы роутера" + +msgid "Local Domain Lists Path" +msgstr "Путь к локальным спискам доменов" + +msgid "Enter to the list file path" +msgstr "Введите путь к файлу списка" + msgid "Remote Domain Lists" msgstr "Удаленные списки доменов" msgid "Download and use domain lists from remote URLs" -msgstr "Загрузка и использование списков доменов из удаленных URL" +msgstr "Загрузка и использование списков доменов с удаленных URL" msgid "Remote Domain URLs" msgstr "URL удаленных доменов" @@ -82,17 +115,26 @@ msgstr "URL удаленных доменов" msgid "Enter full URLs starting with http:// or https://" msgstr "Введите полные URL, начинающиеся с http:// или https://" -msgid "User Subnet List" -msgstr "Пользовательский список подсетей" +msgid "User Subnet List Type" +msgstr "Тип пользовательского списка подсетей" -msgid "Enable and manage your custom list of IP subnets for selective routing" -msgstr "Включить и управлять пользовательским списком IP-подсетей для выборочной маршрутизации" +msgid "Select how to add your custom subnets" +msgstr "Выберите способ добавления пользовательских подсетей" + +msgid "Text List (comma/space/newline separated)" +msgstr "Текстовый список (разделенный запятыми/пробелами/новыми строками)" msgid "User Subnets" msgstr "Пользовательские подсети" -msgid "Enter subnet in CIDR notation (example: 103.21.244.0/22)" -msgstr "Введите подсеть в нотации CIDR (пример: 103.21.244.0/22)" +msgid "Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses" +msgstr "Введите подсети в нотации CIDR (пример: 103.21.244.0/22) или отдельные IP-адреса" + +msgid "User Subnets List" +msgstr "Список пользовательских подсетей" + +msgid "Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline" +msgstr "Введите подсети в нотации CIDR или отдельные IP-адреса через запятую, пробел или новую строку" msgid "Remote Subnet Lists" msgstr "Удаленные списки подсетей" @@ -103,18 +145,6 @@ msgstr "Загрузка и использование списков подсе msgid "Remote Subnet URLs" msgstr "URL удаленных подсетей" -msgid "Domain Exclusions" -msgstr "Исключения доменов" - -msgid "Exclude specific domains from routing rules" -msgstr "Исключить определенные домены из правил маршрутизации" - -msgid "Excluded Domains" -msgstr "Исключенные домены" - -msgid "Domains to be excluded from routing" -msgstr "Домены, которые будут исключены из маршрутизации" - msgid "IP for full redirection" msgstr "Принудительные прокси IP" @@ -133,6 +163,27 @@ msgstr "Исключения прокси IP" msgid "Specify local IP addresses that will never use the configured route" msgstr "Укажите локальные IP-адреса, которые никогда не будут использовать настроенный маршрут" +msgid "Mixed enable" +msgstr "Включить смешанный режим" + +msgid "Browser port: 2080" +msgstr "Порт браузера: 2080" + +msgid "Yacd enable" +msgstr "Включить Yacd" + +msgid "Exclude NTP" +msgstr "Исключить NTP" + +msgid "For issues with open connections sing-box" +msgstr "Для проблем с открытыми соединениями sing-box" + +msgid "QUIC disable" +msgstr "Отключить QUIC" + +msgid "For issues with the video stream" +msgstr "Для проблем с видеопотоком" + msgid "List Update Frequency" msgstr "Частота обновления списков" @@ -145,6 +196,9 @@ msgstr "Каждый час" msgid "Every 2 hours" msgstr "Каждые 2 часа" +msgid "Every 3 hours" +msgstr "Каждые 3 часа" + msgid "Every 4 hours" msgstr "Каждые 4 часа" @@ -154,53 +208,20 @@ msgstr "Каждые 6 часов" msgid "Every 12 hours" msgstr "Каждые 12 часов" +msgid "Every day" +msgstr "Каждый день" + +msgid "Every 3 days" +msgstr "Каждые 3 дня" + msgid "Once a day at 04:00" msgstr "Раз в день в 04:00" msgid "Once a week on Sunday at 04:00" msgstr "Раз в неделю в воскресенье в 04:00" -msgid "Yacd enable" -msgstr "Включить Yacd" - -msgid "Mixed enable" -msgstr "Включить смешанный режим" - -msgid "Browser port: 2080" -msgstr "Порт браузера: 2080" - -msgid "Exclude NTP" -msgstr "Исключить NTP" - -msgid "For issues with open connections sing-box" -msgstr "Для проблем с открытыми соединениями sing-box" - -msgid "Service Domain List Enable" -msgstr "Включить список доменов сервисов" - -msgid "Enable predefined service domain lists for routing" -msgstr "Включить предустановленные списки доменов для маршрутизации" - -msgid "Service List" -msgstr "Список сервисов" - -msgid "Select predefined services for routing" -msgstr "Выберите предустановленные сервисы для маршрутизации" - -msgid "Domains" -msgstr "Домены" - -msgid "Subnet List" -msgstr "Список подсетей" - -msgid "Configure custom subnets for routing" -msgstr "Настройка пользовательских подсетей для маршрутизации" - -msgid "Subnets" -msgstr "Подсети" - msgid "Invalid domain format. Enter domain without protocol (example: sub.example.com)" -msgstr "Неверный формат домена. Введите домен без протокола (например: sub.example.com)" +msgstr "Неверный формат домена. Введите домен без протокола (пример: sub.example.com)" msgid "URL must use http:// or https:// protocol" msgstr "URL должен использовать протокол http:// или https://" @@ -208,8 +229,8 @@ msgstr "URL должен использовать протокол http:// ил msgid "Invalid URL format. URL must start with http:// or https://" msgstr "Неверный формат URL. URL должен начинаться с http:// или https://" -msgid "Invalid subnet format. Use format: X.X.X.X/Y (like 103.21.244.0/22)" -msgstr "Неверный формат подсети. Используйте формат: X.X.X.X/Y (например: 103.21.244.0/22)" +msgid "Invalid format. Use format: X.X.X.X or X.X.X.X/Y" +msgstr "Неверный формат. Используйте формат: X.X.X.X или X.X.X.X/Y" msgid "IP address parts must be between 0 and 255" msgstr "Части IP-адреса должны быть между 0 и 255" @@ -220,71 +241,23 @@ msgstr "CIDR должен быть между 0 и 32" msgid "Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)" msgstr "Неверный формат IP. Используйте формат: X.X.X.X (например: 192.168.1.1)" -msgid "User Domain List Type" -msgstr "Тип пользовательского списка доменов" - -msgid "Select how to add your custom domains" -msgstr "Выберите способ добавления пользовательских доменов" - -msgid "Disabled" -msgstr "Отключено" - -msgid "Dynamic List" -msgstr "Динамический список" - -msgid "Text List" -msgstr "Текстовый список" - -msgid "User Domains List" -msgstr "Список пользовательских доменов" - -msgid "Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)" -msgstr "Введите имена доменов через запятую, пробел или новую строку (пример: sub.example.com, example.com или один домен на строку)" - msgid "Invalid domain format: %s. Enter domain without protocol" msgstr "Неверный формат домена: %s. Введите домен без протокола" -msgid "User Subnet List Type" -msgstr "Тип пользовательского списка подсетей" - -msgid "Select how to add your custom subnets" -msgstr "Выберите способ добавления пользовательских подсетей" - -msgid "Text List (comma/space/newline separated)" -msgstr "Текстовый список (разделенный запятыми/пробелами/новыми строками)" - -msgid "Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses" -msgstr "Введите подсети в нотации CIDR (пример: 103.21.244.0/22) или отдельные IP-адреса" - -msgid "User Subnets List" -msgstr "Список пользовательских подсетей" - -msgid "Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline" -msgstr "Введите подсети в нотации CIDR или отдельные IP-адреса через запятую, пробел или новую строку" - -msgid "Invalid format. Use format: X.X.X.X or X.X.X.X/Y" -msgstr "Неверный формат. Используйте формат: X.X.X.X или X.X.X.X/Y" +msgid "Invalid format: %s. Use format: X.X.X.X or X.X.X.X/Y" +msgstr "Неверный формат: %s. Используйте формат: X.X.X.X или X.X.X.X/Y" msgid "IP parts must be between 0 and 255 in: %s" msgstr "Части IP-адреса должны быть между 0 и 255 в: %s" -msgid "Configuration Type" -msgstr "Тип конфигурации" +msgid "CIDR must be between 0 and 32 in: %s" +msgstr "CIDR должен быть между 0 и 32 в: %s" -msgid "Select how to configure the proxy" -msgstr "Выберите способ настройки прокси" +msgid "Invalid path format. Path must start with \"/\" and contain only valid characters (letters, numbers, \"-\", \"_\", \"/\", \".\")" +msgstr "Неверный формат пути. Путь должен начинаться с \"/\" и содержать только допустимые символы (буквы, цифры, \"-\", \"_\", \"/\", \".\")" -msgid "Connection URL" -msgstr "URL подключения" - -msgid "Outbound Config" -msgstr "Конфигурация Outbound" - -msgid "Outbound Configuration" -msgstr "Конфигурация исходящего соединения" - -msgid "Enter complete outbound configuration in JSON format" -msgstr "Введите полную конфигурацию исходящего соединения в формате JSON" +msgid "Invalid path format" +msgstr "Неверный формат пути" msgid "JSON must contain at least type, server and server_port fields" msgstr "JSON должен содержать как минимум поля type, server и server_port" @@ -292,208 +265,180 @@ msgstr "JSON должен содержать как минимум поля type msgid "Invalid JSON format" msgstr "Неверный формат JSON" -msgid "Diagnostics" -msgstr "Диагностика" - -msgid "Main Check" -msgstr "Основная проверка" - -msgid "Run a comprehensive diagnostic check of all components" -msgstr "Запустить комплексную диагностическую проверку всех компонентов" - -msgid "Run Check" -msgstr "Запустить проверку" - -msgid "Full Diagnostic Results" -msgstr "Полные результаты диагностики" - -msgid "Failed to copy: " -msgstr "Ошибка копирования: " - -msgid "Copy to Clipboard" -msgstr "Скопировать в буфер" - -msgid "Close" -msgstr "Закрыть" - -msgid "No output" -msgstr "Нет данных" - -msgid "System Logs" -msgstr "Системные логи" - -msgid "View recent system logs related to Podkop" -msgstr "Просмотр недавних системных логов, связанных с Podkop" - -msgid "View Logs" -msgstr "Просмотр логов" - -msgid "Failed to copy logs: %s" -msgstr "Ошибка копирования логов: %s" - -msgid "Show Config" -msgstr "Показать конфигурацию" - -msgid "Show current podkop configuration with masked sensitive data" -msgstr "Показать текущую конфигурацию podkop с маскированными конфиденциальными данными" - -msgid "Podkop Configuration" -msgstr "Конфигурация Podkop" - -msgid "Update lists" -msgstr "Обновить списки" - -msgid "Update all lists in config" -msgstr "Обновить все списки в конфигурации" - -msgid "List Update" -msgstr "Обновление списков" - -msgid "Lists will be updated in background. You can check the progress in system logs." -msgstr "Списки будут обновлены в фоновом режиме. Вы можете проверить прогресс в системных логах." - -msgid "Extra configurations" -msgstr "Дополнительные конфигурации" - -msgid "Extra configuration" -msgstr "Дополнительная конфигурация" - -msgid "Add Section" -msgstr "Добавить раздел" - -msgid "QUIC disable" -msgstr "Отключить QUIC" - -msgid "For issues with the video stream" -msgstr "Для проблем с видеопотоком" - -msgid "Community Lists" -msgstr "Списки сообщества" - -msgid "Local Domain Lists" -msgstr "Локальные списки доменов" - -msgid "Use the list from the router filesystem" -msgstr "Использовать список из файловой системы роутера" - -msgid "Local Domain Lists Path" -msgstr "Путь к локальным спискам доменов" - -msgid "Enter to the list file path" -msgstr "Введите путь к файлу списка" - -msgid "Proxy Check" -msgstr "Проверка прокси" - -msgid "Check if sing-box proxy works correctly" -msgstr "Проверить корректность работы прокси sing-box" - -msgid "Check Proxy" -msgstr "Проверить прокси" - -msgid "Proxy Check Results" -msgstr "Результаты проверки прокси" - -msgid "NFT Rules" -msgstr "Правила NFT" - -msgid "Show current nftables rules and statistics" -msgstr "Показать текущие правила и статистику nftables" - -msgid "Check Rules" -msgstr "Проверить правила" - -msgid "GitHub Connectivity" -msgstr "Подключение к GitHub" - -msgid "Check GitHub connectivity and lists availability" -msgstr "Проверить подключение к GitHub и доступность списков" - -msgid "Check GitHub" -msgstr "Проверить GitHub" - -msgid "GitHub Connectivity Results" -msgstr "Результаты проверки подключения к GitHub" - -msgid "Sing-Box Logs" -msgstr "Логи Sing-Box" - -msgid "View recent sing-box logs from system journal" -msgstr "Просмотр последних логов sing-box из системного журнала" - -msgid "View Sing-Box Logs" -msgstr "Просмотр логов Sing-Box" - -msgid "Podkop Logs" -msgstr "Логи Podkop" - -msgid "View recent podkop logs from system journal" -msgstr "Просмотр последних логов podkop из системного журнала" - -msgid "View Podkop Logs" -msgstr "Просмотр логов Podkop" - -msgid "Active Connections" -msgstr "Активные соединения" - -msgid "View active sing-box network connections" -msgstr "Просмотр активных сетевых соединений sing-box" - -msgid "Check Connections" -msgstr "Проверить соединения" - -msgid "DNSMasq Configuration" -msgstr "Конфигурация DNSMasq" - -msgid "View current DNSMasq configuration settings" -msgstr "Просмотр текущих настроек конфигурации DNSMasq" - -msgid "Check DNSMasq" -msgstr "Проверить DNSMasq" - -msgid "Sing-Box Configuration" -msgstr "Конфигурация Sing-Box" - -msgid "Show current sing-box configuration" -msgstr "Показать текущую конфигурацию sing-box" - -msgid "Show Sing-Box Config" -msgstr "Показать конфигурацию Sing-Box" - -msgid "Lists Update Results" -msgstr "Результаты обновления списков" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX msgid "Warning: %s cannot be used together with %s. Previous selections have been removed." msgstr "Предупреждение: %s нельзя использовать вместе с %s. Предыдущие варианты были удалены." -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX -msgid "Warning: Russia inside can only be used with Meta, Twitter, Discord, and Telegram. %s already in Russia inside and have been removed from selection." -msgstr "Внимание: Russia inside может использоваться только с Meta, Twitter, Discord и Telegram. %s уже в Russia inside и были удалены из выбора." - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX msgid "Regional options cannot be used together" msgstr "Нельзя использовать несколько региональных опций" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX +msgid "Warning: Russia inside can only be used with Meta, Twitter, Discord, and Telegram. %s already in Russia inside and have been removed from selection." +msgstr "Внимание: Russia inside может использоваться только с Meta, Twitter, Discord и Telegram. %s были удалены из выбора." + msgid "Russia inside restrictions" msgstr "Ограничения Russia inside" -msgid "Version Information" -msgstr "Информация о версиях" +msgid "URL must start with vless:// or ss://" +msgstr "URL должен начинаться с vless:// или ss://" -msgid "Show version information for all components" -msgstr "Показать информацию о версиях всех компонентов" +msgid "Invalid Shadowsocks URL format: missing method and password separator \":\"" +msgstr "Неверный формат URL Shadowsocks: отсутствует разделитель метода и пароля \":\"" -msgid "Show Versions" -msgstr "Показать версии" +msgid "Invalid Shadowsocks URL format" +msgstr "Неверный формат URL Shadowsocks" -msgid "Copied!" -msgstr "Скопировано!" +msgid "Invalid Shadowsocks URL: missing server address" +msgstr "Неверный URL Shadowsocks: отсутствует адрес сервера" + +msgid "Invalid Shadowsocks URL: missing server" +msgstr "Неверный URL Shadowsocks: отсутствует сервер" + +msgid "Invalid Shadowsocks URL: missing port" +msgstr "Неверный URL Shadowsocks: отсутствует порт" + +msgid "Invalid port number. Must be between 1 and 65535" +msgstr "Неверный номер порта. Должен быть между 1 и 65535" + +msgid "Invalid Shadowsocks URL: missing or invalid server/port format" +msgstr "Неверный URL Shadowsocks: отсутствует или неверный формат сервера/порта" + +msgid "Invalid VLESS URL: missing UUID" +msgstr "Неверный URL VLESS: отсутствует UUID" + +msgid "Invalid VLESS URL: missing server address" +msgstr "Неверный URL VLESS: отсутствует адрес сервера" + +msgid "Invalid VLESS URL: missing server" +msgstr "Неверный URL VLESS: отсутствует сервер" + +msgid "Invalid VLESS URL: missing port" +msgstr "Неверный URL VLESS: отсутствует порт" + +msgid "Invalid VLESS URL: missing or invalid server/port format" +msgstr "Неверный URL VLESS: отсутствует или неверный формат сервера/порта" + +msgid "Invalid VLESS URL: missing query parameters" +msgstr "Неверный URL VLESS: отсутствуют параметры запроса" + +msgid "Invalid VLESS URL: missing type parameter" +msgstr "Неверный URL VLESS: отсутствует параметр type" + +msgid "Invalid VLESS URL: missing security parameter" +msgstr "Неверный URL VLESS: отсутствует параметр security" + +msgid "Invalid VLESS URL: missing pbk parameter for reality security" +msgstr "Неверный URL VLESS: отсутствует параметр pbk для security reality" + +msgid "Invalid VLESS URL: missing fp parameter for reality security" +msgstr "Неверный URL VLESS: отсутствует параметр fp для security reality" + +msgid "Invalid VLESS URL: missing sni parameter for tls security" +msgstr "Неверный URL VLESS: отсутствует параметр sni для security tls" + +msgid "Invalid URL format: %s" +msgstr "Неверный формат URL: %s" + +msgid "Remote Domain Lists URL" +msgstr "URL удаленных списков доменов" + +msgid "Enter URL to download domain list" +msgstr "Введите URL для загрузки списка доменов" + +msgid "Update Interval" +msgstr "Интервал обновления" + +msgid "Select how often to update the lists" +msgstr "Выберите, как часто обновлять списки" + +msgid "Last Update" +msgstr "Последнее обновление" + +msgid "Last update time" +msgstr "Время последнего обновления" + +msgid "Next Update" +msgstr "Следующее обновление" + +msgid "Next scheduled update time" +msgstr "Время следующего запланированного обновления" + +msgid "Version" +msgstr "Версия" + +msgid "Component version" +msgstr "Версия компонента" + +msgid "Installed" +msgstr "Установлено" + +msgid "Not installed" +msgstr "Не установлено" + +msgid "Unknown version" +msgstr "Неизвестная версия" + +msgid "Error parsing version" +msgstr "Ошибка разбора версии" + +msgid "Error parsing status" +msgstr "Ошибка разбора статуса" + +msgid "Service is running" +msgstr "Сервис запущен" + +msgid "Service is stopped" +msgstr "Сервис остановлен" + +msgid "Service is enabled" +msgstr "Сервис включен" + +msgid "Service is disabled" +msgstr "Сервис отключен" msgid "Service Status" msgstr "Статус сервиса" +msgid "working" +msgstr "работает" + +msgid "not working" +msgstr "не работает" + +msgid "check error" +msgstr "ошибка проверки" + +msgid "Diagnostic check in progress..." +msgstr "Выполняется диагностическая проверка..." + +msgid "Diagnostic check completed" +msgstr "Диагностическая проверка завершена" + +msgid "Diagnostic check failed" +msgstr "Диагностическая проверка не удалась" + +msgid "Update in progress..." +msgstr "Выполняется обновление..." + +msgid "Update completed" +msgstr "Обновление завершено" + +msgid "Update failed" +msgstr "Обновление не удалось" + +msgid "Check in progress..." +msgstr "Выполняется проверка..." + +msgid "Check completed" +msgstr "Проверка завершена" + +msgid "Check failed" +msgstr "Проверка не удалась" + +msgid "Version Information" +msgstr "Информация о версии" + +msgid "Copied!" +msgstr "Скопировано!" + msgid "Podkop Status" msgstr "Статус Podkop" @@ -527,15 +472,6 @@ msgstr "Инструменты диагностики" msgid "Unknown" msgstr "Неизвестно" -msgid "Every 3 hours" -msgstr "Каждые 3 часа" - -msgid "Every day" -msgstr "Каждый день" - -msgid "Every 3 days" -msgstr "Каждые 3 дня" - msgid "Device Model: " msgstr "Модель устройства: " @@ -557,380 +493,5 @@ msgstr "Проверить правила NFT" msgid "Update Lists" msgstr "Обновить списки" -msgid "Invalid path format. Path must start with \"/\" and contain only valid characters (letters, numbers, \"-\", \"_\", \"/\", \".\")" -msgstr "Неверный формат пути. Путь должен начинаться с \"/\" и содержать только допустимые символы (буквы, цифры, \"-\", \"_\", \"/\", \".\")" - -msgid "Invalid path format" -msgstr "Неверный формат пути" - -msgid "CIDR must be between 0 and 32 in: %s" -msgstr "CIDR должен быть между 0 и 32 в: %s" - -msgid "Invalid format: %s. Use format: X.X.X.X or X.X.X.X/Y" -msgstr "Неверный формат: %s. Используйте формат: X.X.X.X или X.X.X.X/Y" - -msgid "URL must start with vless:// or ss://" -msgstr "URL должен начинаться с vless:// или ss://" - -msgid "Invalid Shadowsocks URL format" -msgstr "Неверный формат URL Shadowsocks" - -msgid "Invalid Shadowsocks URL format: missing method and password separator ":" -msgstr "Неверный формат URL Shadowsocks: отсутствует разделитель метода и пароля \":\"" - -msgid "Invalid Shadowsocks URL: missing server address" -msgstr "Неверный URL Shadowsocks: отсутствует адрес сервера" - -msgid "Invalid Shadowsocks URL: missing server" -msgstr "Неверный URL Shadowsocks: отсутствует сервер" - -msgid "Invalid Shadowsocks URL: missing port" -msgstr "Неверный URL Shadowsocks: отсутствует порт" - -msgid "Invalid port number. Must be between 1 and 65535" -msgstr "Неверный номер порта. Должен быть между 1 и 65535" - -msgid "Invalid Shadowsocks URL: missing or invalid server/port format" -msgstr "Неверный URL Shadowsocks: отсутствует или неверный формат сервера/порта" - -msgid "Invalid VLESS URL: missing UUID" -msgstr "Неверный URL VLESS: отсутствует UUID" - -msgid "Invalid VLESS URL: missing server address" -msgstr "Неверный URL VLESS: отсутствует адрес сервера" - -msgid "Invalid VLESS URL: missing server" -msgstr "Неверный URL VLESS: отсутствует сервер" - -msgid "Invalid VLESS URL: missing port" -msgstr "Неверный URL VLESS: отсутствует порт" - -msgid "Invalid VLESS URL: missing query parameters" -msgstr "Неверный URL VLESS: отсутствуют параметры запроса" - -msgid "Invalid VLESS URL: missing type parameter" -msgstr "Неверный URL VLESS: отсутствует параметр type" - -msgid "Invalid VLESS URL: missing security parameter" -msgstr "Неверный URL VLESS: отсутствует параметр security" - -msgid "Invalid VLESS URL: missing pbk parameter for reality security" -msgstr "Неверный URL VLESS: отсутствует параметр pbk для security reality" - -msgid "Invalid VLESS URL: missing fp parameter for reality security" -msgstr "Неверный URL VLESS: отсутствует параметр fp для security reality" - -msgid "Invalid VLESS URL: missing sni parameter for tls security" -msgstr "Неверный URL VLESS: отсутствует параметр sni для security tls" - -msgid "Invalid URL format: " -msgstr "Неверный формат URL: " - -msgid "http://openwrt.lan:9090/ui" -msgstr "http://openwrt.lan:9090/ui" - -msgid "Browser port: 2080 (extra +1)" -msgstr "Порт браузера: 2080 (extra +1)" - -msgid "Select predefined service for routing" -msgstr "Выберите предустановленный сервис для маршрутизации" - -msgid "github.com/itdoginfo/allow-domains" -msgstr "github.com/itdoginfo/allow-domains" - -msgid "Error parsing diagnostics status: %s" -msgstr "Ошибка разбора статуса диагностики: %s" - -msgid "No configuration" -msgstr "Нет конфигурации" - -msgid "No logs available" -msgstr "Нет доступных логов" - -msgid "No connections" -msgstr "Нет соединений" - -msgid "No rules" -msgstr "Нет правил" - -msgid "No results" -msgstr "Нет результатов" - -msgid "Status: %s" -msgstr "Статус: %s" - -msgid "Running" -msgstr "Запущен" - -msgid "Stopped" -msgstr "Остановлен" - -msgid "Enabled" -msgstr "Включен" - -msgid "Disabled" -msgstr "Отключен" - -msgid "Error: %s" -msgstr "Ошибка: %s" - -msgid "Failed to apply changes: %s" -msgstr "Не удалось применить изменения: %s" - -msgid "Operation completed successfully" -msgstr "Операция успешно завершена" - -msgid "Operation failed: %s" -msgstr "Операция не удалась: %s" - -msgid "Configuration saved" -msgstr "Конфигурация сохранена" - -msgid "Save failed: %s" -msgstr "Ошибка сохранения: %s" - -msgid "Loading..." -msgstr "Загрузка..." - -msgid "Processing..." -msgstr "Обработка..." - -msgid "Updating..." -msgstr "Обновление..." - -msgid "Checking..." -msgstr "Проверка..." - -msgid "Please wait..." -msgstr "Пожалуйста, подождите..." - -msgid "Alert" -msgstr "Оповещение" - -msgid "Warning" -msgstr "Предупреждение" - -msgid "Success" -msgstr "Успешно" - -msgid "Info" -msgstr "Информация" - -msgid "Error" -msgstr "Ошибка" - -msgid "Debug" -msgstr "Отладка" - -msgid "Trace" -msgstr "Трассировка" - -msgid "Yes" -msgstr "Да" - -msgid "No" -msgstr "Нет" - -msgid "OK" -msgstr "OK" - -msgid "Cancel" -msgstr "Отмена" - -msgid "Apply" -msgstr "Применить" - -msgid "Save" -msgstr "Сохранить" - -msgid "Delete" -msgstr "Удалить" - -msgid "Edit" -msgstr "Редактировать" - -msgid "Add" -msgstr "Добавить" - -msgid "Remove" -msgstr "Удалить" - -msgid "Move Up" -msgstr "Переместить вверх" - -msgid "Move Down" -msgstr "Переместить вниз" - -msgid "Expand" -msgstr "Развернуть" - -msgid "Collapse" -msgstr "Свернуть" - -msgid "Show" -msgstr "Показать" - -msgid "Hide" -msgstr "Скрыть" - -msgid "Enable" -msgstr "Включить" - -msgid "Disable" -msgstr "Отключить" - -msgid "Start" -msgstr "Запустить" - -msgid "Stop" -msgstr "Остановить" - -msgid "Restart" -msgstr "Перезапустить" - -msgid "Reset" -msgstr "Сбросить" - -msgid "Refresh" -msgstr "Обновить" - -msgid "Update" -msgstr "Обновить" - -msgid "Install" -msgstr "Установить" - -msgid "Uninstall" -msgstr "Удалить" - -msgid "Configure" -msgstr "Настроить" - -msgid "Settings" -msgstr "Настройки" - -msgid "Options" -msgstr "Опции" - -msgid "Advanced" -msgstr "Расширенные" - -msgid "Basic" -msgstr "Основные" - -msgid "General" -msgstr "Общие" - -msgid "Details" -msgstr "Подробности" - -msgid "Status" -msgstr "Статус" - -msgid "Information" -msgstr "Информация" - -msgid "Configuration" -msgstr "Конфигурация" - -msgid "Management" -msgstr "Управление" - -msgid "System" -msgstr "Система" - -msgid "Network" -msgstr "Сеть" - -msgid "Services" -msgstr "Сервисы" - -msgid "Check failed" -msgstr "Проверка не удалась" - -msgid "Remote Domain Lists URL" -msgstr "URL удаленных списков доменов" - -msgid "Enter URL to download domain list" -msgstr "Введите URL для загрузки списка доменов" - -msgid "Update Interval" -msgstr "Интервал обновления" - -msgid "Select how often to update the lists" -msgstr "Выберите как часто обновлять списки" - -msgid "Last Update" -msgstr "Последнее обновление" - -msgid "Last update time" -msgstr "Время последнего обновления" - -msgid "Next Update" -msgstr "Следующее обновление" - -msgid "Next scheduled update time" -msgstr "Время следующего запланированного обновления" - -msgid "Version" -msgstr "Версия" - -msgid "Component version" -msgstr "Версия компонента" - -msgid "Installed" -msgstr "Установлен" - -msgid "Not installed" -msgstr "Не установлен" - -msgid "Unknown version" -msgstr "Неизвестная версия" - -msgid "Error parsing version" -msgstr "Ошибка разбора версии" - -msgid "Error parsing status" -msgstr "Ошибка разбора статуса" - -msgid "Service is running" -msgstr "Сервис запущен" - -msgid "Service is stopped" -msgstr "Сервис остановлен" - -msgid "Service is enabled" -msgstr "Сервис включен" - -msgid "Service is disabled" -msgstr "Сервис отключен" - -msgid "Service status unknown" -msgstr "Статус сервиса неизвестен" - -msgid "Error checking service status" -msgstr "Ошибка проверки статуса сервиса" - -msgid "Diagnostic check in progress..." -msgstr "Выполняется диагностическая проверка..." - -msgid "Diagnostic check completed" -msgstr "Диагностическая проверка завершена" - -msgid "Diagnostic check failed" -msgstr "Диагностическая проверка не удалась" - -msgid "Update in progress..." -msgstr "Выполняется обновление..." - -msgid "Update completed" -msgstr "Обновление завершено" - -msgid "Update failed" -msgstr "Обновление не удалось" - -msgid "Check in progress..." -msgstr "Выполняется проверка..." - -msgid "Check completed" -msgstr "Проверка завершена" \ No newline at end of file +msgid "Lists Update Results" +msgstr "Результаты обновления списков" \ No newline at end of file diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot index ce059a8..f13ab9a 100644 --- a/luci-app-podkop/po/templates/podkop.pot +++ b/luci-app-podkop/po/templates/podkop.pot @@ -25,18 +25,318 @@ msgstr "" msgid "Select between VPN and Proxy connection methods for traffic routing" msgstr "" +msgid "Configuration Type" +msgstr "" + +msgid "Select how to configure the proxy" +msgstr "" + +msgid "Connection URL" +msgstr "" + +msgid "Outbound Config" +msgstr "" + msgid "Proxy Configuration URL" msgstr "" msgid "Enter connection string starting with vless:// or ss:// for proxy configuration" msgstr "" +msgid "Outbound Configuration" +msgstr "" + +msgid "Enter complete outbound configuration in JSON format" +msgstr "" + msgid "Network Interface" msgstr "" msgid "Select network interface for VPN connection" msgstr "" +msgid "Community Lists" +msgstr "" + +msgid "Service List" +msgstr "" + +msgid "Select predefined service for routing" +msgstr "" + +msgid "User Domain List Type" +msgstr "" + +msgid "Select how to add your custom domains" +msgstr "" + +msgid "Disabled" +msgstr "" + +msgid "Dynamic List" +msgstr "" + +msgid "Text List" +msgstr "" + +msgid "User Domains" +msgstr "" + +msgid "Enter domain names without protocols (example: sub.example.com or example.com)" +msgstr "" + +msgid "User Domains List" +msgstr "" + +msgid "Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)" +msgstr "" + +msgid "Local Domain Lists" +msgstr "" + +msgid "Use the list from the router filesystem" +msgstr "" + +msgid "Local Domain Lists Path" +msgstr "" + +msgid "Enter to the list file path" +msgstr "" + +msgid "Remote Domain Lists" +msgstr "" + +msgid "Download and use domain lists from remote URLs" +msgstr "" + +msgid "Remote Domain URLs" +msgstr "" + +msgid "Enter full URLs starting with http:// or https://" +msgstr "" + +msgid "User Subnet List Type" +msgstr "" + +msgid "Select how to add your custom subnets" +msgstr "" + +msgid "Text List (comma/space/newline separated)" +msgstr "" + +msgid "User Subnets" +msgstr "" + +msgid "Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses" +msgstr "" + +msgid "User Subnets List" +msgstr "" + +msgid "Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline" +msgstr "" + +msgid "Remote Subnet Lists" +msgstr "" + +msgid "Download and use subnet lists from remote URLs" +msgstr "" + +msgid "Remote Subnet URLs" +msgstr "" + +msgid "IP for full redirection" +msgstr "" + +msgid "Specify local IP addresses whose traffic will always use the configured route" +msgstr "" + +msgid "Local IPs" +msgstr "" + +msgid "Enter valid IPv4 addresses" +msgstr "" + +msgid "IP for exclusion" +msgstr "" + +msgid "Specify local IP addresses that will never use the configured route" +msgstr "" + +msgid "Mixed enable" +msgstr "" + +msgid "Browser port: 2080" +msgstr "" + +msgid "Yacd enable" +msgstr "" + +msgid "Exclude NTP" +msgstr "" + +msgid "For issues with open connections sing-box" +msgstr "" + +msgid "QUIC disable" +msgstr "" + +msgid "For issues with the video stream" +msgstr "" + +msgid "List Update Frequency" +msgstr "" + +msgid "Select how often the lists will be updated" +msgstr "" + +msgid "Every hour" +msgstr "" + +msgid "Every 2 hours" +msgstr "" + +msgid "Every 3 hours" +msgstr "" + +msgid "Every 4 hours" +msgstr "" + +msgid "Every 6 hours" +msgstr "" + +msgid "Every 12 hours" +msgstr "" + +msgid "Every day" +msgstr "" + +msgid "Every 3 days" +msgstr "" + +msgid "Once a day at 04:00" +msgstr "" + +msgid "Once a week on Sunday at 04:00" +msgstr "" + +msgid "Invalid domain format. Enter domain without protocol (example: sub.example.com)" +msgstr "" + +msgid "URL must use http:// or https:// protocol" +msgstr "" + +msgid "Invalid URL format. URL must start with http:// or https://" +msgstr "" + +msgid "Invalid format. Use format: X.X.X.X or X.X.X.X/Y" +msgstr "" + +msgid "IP address parts must be between 0 and 255" +msgstr "" + +msgid "CIDR must be between 0 and 32" +msgstr "" + +msgid "Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)" +msgstr "" + +msgid "Invalid domain format: %s. Enter domain without protocol" +msgstr "" + +msgid "Invalid format: %s. Use format: X.X.X.X or X.X.X.X/Y" +msgstr "" + +msgid "IP parts must be between 0 and 255 in: %s" +msgstr "" + +msgid "CIDR must be between 0 and 32 in: %s" +msgstr "" + +msgid "Invalid path format. Path must start with \"/\" and contain only valid characters (letters, numbers, \"-\", \"_\", \"/\", \".\")" +msgstr "" + +msgid "Invalid path format" +msgstr "" + +msgid "JSON must contain at least type, server and server_port fields" +msgstr "" + +msgid "Invalid JSON format" +msgstr "" + +msgid "Warning: %s cannot be used together with %s. Previous selections have been removed." +msgstr "" + +msgid "Regional options cannot be used together" +msgstr "" + +msgid "Warning: Russia inside can only be used with Meta, Twitter, Discord, and Telegram. %s already in Russia inside and have been removed from selection." +msgstr "" + +msgid "Russia inside restrictions" +msgstr "" + +msgid "URL must start with vless:// or ss://" +msgstr "" + +msgid "Invalid Shadowsocks URL format: missing method and password separator \":\"" +msgstr "" + +msgid "Invalid Shadowsocks URL format" +msgstr "" + +msgid "Invalid Shadowsocks URL: missing server address" +msgstr "" + +msgid "Invalid Shadowsocks URL: missing server" +msgstr "" + +msgid "Invalid Shadowsocks URL: missing port" +msgstr "" + +msgid "Invalid port number. Must be between 1 and 65535" +msgstr "" + +msgid "Invalid Shadowsocks URL: missing or invalid server/port format" +msgstr "" + +msgid "Invalid VLESS URL: missing UUID" +msgstr "" + +msgid "Invalid VLESS URL: missing server address" +msgstr "" + +msgid "Invalid VLESS URL: missing server" +msgstr "" + +msgid "Invalid VLESS URL: missing port" +msgstr "" + +msgid "Invalid VLESS URL: missing or invalid server/port format" +msgstr "" + +msgid "Invalid VLESS URL: missing query parameters" +msgstr "" + +msgid "Invalid VLESS URL: missing type parameter" +msgstr "" + +msgid "Invalid VLESS URL: missing security parameter" +msgstr "" + +msgid "Invalid VLESS URL: missing pbk parameter for reality security" +msgstr "" + +msgid "Invalid VLESS URL: missing fp parameter for reality security" +msgstr "" + +msgid "Invalid VLESS URL: missing sni parameter for tls security" +msgstr "" + +msgid "Invalid URL format: %s" +msgstr "" + msgid "Community Domain Lists" msgstr "" @@ -64,292 +364,169 @@ msgstr "" msgid "Enable and manage your custom list of domains for selective routing" msgstr "" -msgid "User Domains" -msgstr "" - -msgid "Enter domain names without protocols (example: sub.example.com or example.com)" -msgstr "" - -msgid "Remote Domain Lists" -msgstr "" - -msgid "Download and use domain lists from remote URLs" -msgstr "" - -msgid "Remote Domain URLs" -msgstr "" - -msgid "Enter full URLs starting with http:// or https://" -msgstr "" - -msgid "User Subnet List" -msgstr "" - -msgid "Enable and manage your custom list of IP subnets for selective routing" -msgstr "" - -msgid "User Subnets" -msgstr "" - -msgid "Enter subnet in CIDR notation (example: 103.21.244.0/22)" -msgstr "" - -msgid "Remote Subnet Lists" -msgstr "" - -msgid "Download and use subnet lists from remote URLs" -msgstr "" - -msgid "Remote Subnet URLs" -msgstr "" - -msgid "Domain Exclusions" -msgstr "" - -msgid "Exclude specific domains from routing rules" -msgstr "" - -msgid "Excluded Domains" -msgstr "" - -msgid "Domains to be excluded from routing" -msgstr "" - -msgid "IP for full redirection" -msgstr "" - -msgid "Specify local IP addresses whose traffic will always use the configured route" -msgstr "" - -msgid "Local IPs" -msgstr "" - -msgid "Enter valid IPv4 addresses" -msgstr "" - -msgid "IP for exclusion" -msgstr "" - -msgid "Specify local IP addresses that will never use the configured route" -msgstr "" - -msgid "List Update Frequency" -msgstr "" - -msgid "Select how often the lists will be updated" -msgstr "" - -msgid "Every hour" -msgstr "" - -msgid "Every 2 hours" -msgstr "" - -msgid "Every 4 hours" -msgstr "" - -msgid "Every 6 hours" -msgstr "" - -msgid "Every 12 hours" -msgstr "" - -msgid "Once a day at 04:00" -msgstr "" - -msgid "Once a week on Sunday at 04:00" -msgstr "" - -msgid "Yacd enable" -msgstr "" - -msgid "Mixed enable" -msgstr "" - -msgid "Browser port: 2080" -msgstr "" - -msgid "Exclude NTP" -msgstr "" - -msgid "For issues with open connections sing-box" -msgstr "" - -msgid "Service Domain List Enable" -msgstr "" - -msgid "Enable predefined service domain lists for routing" -msgstr "" - -msgid "Service List" -msgstr "" - -msgid "Select predefined services for routing" -msgstr "" - -msgid "Domains" -msgstr "" - -msgid "Subnet List" -msgstr "" - -msgid "Configure custom subnets for routing" -msgstr "" - -msgid "Subnets" -msgstr "" - -msgid "Invalid domain format. Enter domain without protocol (example: sub.example.com)" -msgstr "" - -msgid "URL must use http:// or https:// protocol" -msgstr "" - -msgid "Invalid URL format. URL must start with http:// or https://" -msgstr "" - -msgid "Invalid subnet format. Use format: X.X.X.X/Y (like 103.21.244.0/22)" -msgstr "" - -msgid "IP address parts must be between 0 and 255" -msgstr "" - -msgid "CIDR must be between 0 and 32" -msgstr "" - -msgid "Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)" -msgstr "" - -msgid "User Domain List Type" -msgstr "" - -msgid "Select how to add your custom domains" -msgstr "" - -msgid "Disabled" -msgstr "" - -msgid "Dynamic List" -msgstr "" - -msgid "Text List" -msgstr "" - msgid "User Domains List" msgstr "" msgid "Enter domain names separated by comma, space or newline (example: sub.example.com, example.com or one domain per line)" msgstr "" -msgid "Invalid domain format: %s. Enter domain without protocol" +msgid "Remote Domain Lists URL" msgstr "" -msgid "User Subnet List Type" +msgid "Enter URL to download domain list" msgstr "" -msgid "Select how to add your custom subnets" +msgid "Update Interval" msgstr "" -msgid "Text List (comma/space/newline separated)" +msgid "Select how often to update the lists" msgstr "" -msgid "Enter subnets in CIDR notation (example: 103.21.244.0/22) or single IP addresses" +msgid "Last Update" msgstr "" -msgid "User Subnets List" +msgid "Last update time" msgstr "" -msgid "Enter subnets in CIDR notation or single IP addresses, separated by comma, space or newline" +msgid "Next Update" msgstr "" -msgid "Invalid format. Use format: X.X.X.X or X.X.X.X/Y" +msgid "Next scheduled update time" msgstr "" -msgid "IP parts must be between 0 and 255 in: %s" +msgid "Version" msgstr "" -msgid "Configuration Type" +msgid "Component version" msgstr "" -msgid "Select how to configure the proxy" +msgid "Installed" msgstr "" -msgid "Connection URL" +msgid "Not installed" msgstr "" -msgid "Outbound Config" +msgid "Unknown version" msgstr "" -msgid "Outbound Configuration" +msgid "Error parsing version" msgstr "" -msgid "Enter complete outbound configuration in JSON format" +msgid "Error parsing status" msgstr "" -msgid "JSON must contain at least type, server and server_port fields" +msgid "Service is running" msgstr "" -msgid "Invalid JSON format" +msgid "Service is stopped" msgstr "" -msgid "Diagnostics" +msgid "Service is enabled" msgstr "" -msgid "Main Check" +msgid "Service is disabled" msgstr "" -msgid "Run a comprehensive diagnostic check of all components" +msgid "Service Status" msgstr "" -msgid "Run Check" +msgid "working" msgstr "" -msgid "Full Diagnostic Results" +msgid "not working" msgstr "" -msgid "Failed to copy: %s" +msgid "check error" msgstr "" -msgid "Copy to Clipboard" +msgid "Diagnostic check in progress..." msgstr "" -msgid "Close" +msgid "Diagnostic check completed" msgstr "" -msgid "No output" +msgid "Diagnostic check failed" msgstr "" -msgid "System Logs" +msgid "Update in progress..." msgstr "" -msgid "View recent system logs related to Podkop" +msgid "Update completed" msgstr "" -msgid "View Logs" +msgid "Update failed" msgstr "" -msgid "Failed to copy logs: %s" +msgid "Check in progress..." msgstr "" -msgid "Show Config" +msgid "Check completed" msgstr "" -msgid "Show current podkop configuration with masked sensitive data" +msgid "Check failed" msgstr "" -msgid "Podkop Configuration" +msgid "Version Information" msgstr "" -msgid "Update lists" +msgid "Copied!" msgstr "" -msgid "Update all lists in config" +msgid "Podkop Status" msgstr "" -msgid "List Update" +msgid "Start Podkop" msgstr "" -msgid "Lists will be updated in background. You can check the progress in system logs." +msgid "Stop Podkop" +msgstr "" + +msgid "Restart Podkop" +msgstr "" + +msgid "Enable Podkop" +msgstr "" + +msgid "Disable Podkop" +msgstr "" + +msgid "Loading diagnostics..." +msgstr "" + +msgid "Error loading diagnostics" +msgstr "" + +msgid "Sing-box Status" +msgstr "" + +msgid "Diagnostic Tools" +msgstr "" + +msgid "Unknown" +msgstr "" + +msgid "Device Model: " +msgstr "" + +msgid "OpenWrt Version: " +msgstr "" + +msgid "Sing-box: " +msgstr "" + +msgid "LuCI App: " +msgstr "" + +msgid "Podkop: " +msgstr "" + +msgid "Check NFT Rules" +msgstr "" + +msgid "Update Lists" +msgstr "" + +msgid "Lists Update Results" msgstr "" msgid "Extra configurations" @@ -361,25 +538,7 @@ msgstr "" msgid "Add Section" msgstr "" -msgid "QUIC disable" -msgstr "" - -msgid "For issues with the video stream" -msgstr "" - -msgid "Community Lists" -msgstr "" - -msgid "Local Domain Lists" -msgstr "" - -msgid "Use the list from the router filesystem" -msgstr "" - -msgid "Local Domain Lists Path" -msgstr "" - -msgid "Enter to the list file path" +msgid "Lists Update Results" msgstr "" msgid "Proxy Check" @@ -463,148 +622,6 @@ msgstr "" msgid "Lists Update Results" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX -msgid "Warning: %s cannot be used together with %s. Previous selections have been removed." -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX -msgid "Warning: Russia inside can only be used with Meta, Twitter, Discord, and Telegram. %s already in Russia inside and have been removed from selection." -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX -msgid "Regional options cannot be used together" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:XXX -msgid "Russia inside restrictions" -msgstr "" - -msgid "Version Information" -msgstr "" - -msgid "Show version information for all components" -msgstr "" - -msgid "Show Versions" -msgstr "" - -msgid "Copied!" -msgstr "" - -msgid "Service Status" -msgstr "" - -msgid "Podkop Status" -msgstr "" - -msgid "Start Podkop" -msgstr "" - -msgid "Stop Podkop" -msgstr "" - -msgid "Restart Podkop" -msgstr "" - -msgid "Enable Podkop" -msgstr "" - -msgid "Disable Podkop" -msgstr "" - -msgid "Loading diagnostics..." -msgstr "" - -msgid "Error loading diagnostics" -msgstr "" - -msgid "Sing-box Status" -msgstr "" - -msgid "Diagnostic Tools" -msgstr "" - -msgid "Unknown" -msgstr "" - -msgid "Every 3 hours" -msgstr "" - -msgid "Every day" -msgstr "" - -msgid "Every 3 days" -msgstr "" - -msgid "Device Model: " -msgstr "" - -msgid "OpenWrt Version: " -msgstr "" - -msgid "Sing-box: " -msgstr "" - -msgid "LuCI App: " -msgstr "" - -msgid "Podkop: " -msgstr "" - -msgid "Check NFT Rules" -msgstr "" - -msgid "Update Lists" -msgstr "" - -msgid "Invalid path format. Path must start with \"/\" and contain only valid characters (letters, numbers, \"-\", \"_\", \"/\", \".\")" -msgstr "" - -msgid "Invalid path format" -msgstr "" - -msgid "CIDR must be between 0 and 32 in: %s" -msgstr "" - -msgid "Invalid format: %s. Use format: X.X.X.X or X.X.X.X/Y" -msgstr "" - -msgid "Failed to copy: %s" -msgstr "" - -msgid "Failed to apply changes: %s" -msgstr "" - -msgid "Operation completed successfully" -msgstr "" - -msgid "Operation failed: %s" -msgstr "" - -msgid "Configuration saved" -msgstr "" - -msgid "Save failed: %s" -msgstr "" - -msgid "Loading..." -msgstr "" - -msgid "Processing..." -msgstr "" - -msgid "Updating..." -msgstr "" - -msgid "Checking..." -msgstr "" - -msgid "Please wait..." -msgstr "" - -msgid "Alert" -msgstr "" - msgid "Warning" msgstr "" @@ -800,10 +817,16 @@ msgstr "" msgid "Service is disabled" msgstr "" -msgid "Service status unknown" +msgid "Service Status" msgstr "" -msgid "Error checking service status" +msgid "working" +msgstr "" + +msgid "not working" +msgstr "" + +msgid "check error" msgstr "" msgid "Diagnostic check in progress..." diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 54663a2..390287f 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -153,6 +153,7 @@ main() { sing_box_dns sing_box_dns_rule_fakeip sing_box_rule_dns + sing_box_add_secure_dns_probe_domain sing_box_cache_file process_socks5 @@ -1698,4 +1699,40 @@ get_status() { fi echo "{\"running\":$running,\"enabled\":$enabled,\"status\":\"$status\"}" +} + +sing_box_add_secure_dns_probe_domain() { + local domain="httpbin.org" + local override_address="numbersapi.com" + + if [ -z "$override_address" ]; then + log "Error: Could not get br-lan IP address" + return 1 + fi + + log "Adding DNS probe domain ${domain} to fakeip-server configuration" + + jq \ + --arg domain "$domain" \ + --arg override "$override_address" \ + '.dns.rules |= map( + if .server == "fakeip-server" then + { + "server": .server, + "domain": $domain, + "rule_set": .rule_set + } + else + . + end + ) | + .route.rules |= . + [ + { + "domain": $domain, + "action": "route-options", + "override_address": $override + } + ]' "$SING_BOX_CONFIG" >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json "$SING_BOX_CONFIG" + + log "DNS probe domain ${domain} configured with override to ${override_address}" } \ No newline at end of file