From 9f5e99ab523930e3611a2e65873704812249d29d Mon Sep 17 00:00:00 2001 From: Ivan K <54704529+VizzleTF@users.noreply.github.com> Date: Sat, 9 Nov 2024 00:04:17 +0300 Subject: [PATCH 01/14] Streamline build (#1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: Добавлена группировка по вкладкам feat: Выбор частоты обновления списков по cron на вкладке Дополнительные настройки feat: Перевод на русский язык cicd: Добавлена поддержка пакета luci-i18n-podkop-ru --- .github/workflows/build.yml | 22 +- Dockerfile | 6 +- luci-app-podkop/Makefile | 9 +- .../resources/view/podkop/podkop.js | 306 +++++++++++++----- luci-app-podkop/po/ru/podkop.po | 236 ++++++++++++++ luci-app-podkop/po/templates/podkop.pot | 150 +++++++++ podkop/files/etc/init.d/podkop | 25 +- 7 files changed, 645 insertions(+), 109 deletions(-) create mode 100644 luci-app-podkop/po/ru/podkop.po create mode 100644 luci-app-podkop/po/templates/podkop.pot diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eb22712..8868663 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,36 +1,40 @@ name: Build packages - on: push: tags: - v* - jobs: build: name: Build podkop and luci-app-podkop runs-on: ubuntu-latest - steps: - uses: actions/checkout@v4.2.1 - + - name: Build and push uses: docker/build-push-action@v6.9.0 with: context: . tags: podkop:ci - + - name: Create Docker container run: docker create --name podkop podkop:ci - + - name: Copy file from Docker container run: | docker cp podkop:/builder/bin/packages/x86_64/utilites/. ./bin/ docker cp podkop:/builder/bin/packages/x86_64/luci/. ./bin/ - + + - name: Filter IPK files + run: | + mkdir -p ./filtered-bin + cp ./bin/luci-i18n-podkop-ru_*.ipk ./filtered-bin/ + cp ./bin/podkop_*.ipk ./filtered-bin/ + cp ./bin/luci-app-podkop_*.ipk ./filtered-bin/ + - name: Remove Docker container run: docker rm podkop - + - name: Release uses: softprops/action-gh-release@v2.0.8 with: - files: ./bin/*.ipk + files: ./filtered-bin/*.ipk \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 8012f38..35ab0d2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,10 @@ FROM openwrt/sdk:x86_64-v23.05.5 -RUN ./scripts/feeds update -a && mkdir -p /builder/package/feeds/utilites/ && mkdir -p /builder/package/feeds/luci/ +FROM openwrt/sdk:x86_64-v23.05.5 + +RUN ./scripts/feeds update -a && ./scripts/feeds install luci-base && mkdir -p /builder/package/feeds/utilites/ && mkdir -p /builder/package/feeds/luci/ COPY ./podkop /builder/package/feeds/utilites/podkop COPY ./luci-app-podkop /builder/package/feeds/luci/luci-app-podkop -RUN make defconfig && make package/podkop/compile && make package/luci-app-podkop/compile V=s -j4 \ No newline at end of file +RUN make defconfig && make package/podkop/compile && make package/luci-app-podkop/compile V=s \ No newline at end of file diff --git a/luci-app-podkop/Makefile b/luci-app-podkop/Makefile index ea35ab3..2e9a711 100644 --- a/luci-app-podkop/Makefile +++ b/luci-app-podkop/Makefile @@ -1,6 +1,3 @@ -# See /LICENSE for more information. -# This is free software, licensed under the GNU General Public License v2. - include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-podkop @@ -8,12 +5,16 @@ PKG_VERSION:=0.2.3 PKG_RELEASE:=1 LUCI_TITLE:=LuCI podkop app -LUCI_DEPENDS:=+luci-base +podkop +LUCI_DEPENDS:=+podkop LUCI_PKGARCH:=all +LUCI_LANG.ru:=Русский (Russian) +LUCI_LANG.en:=English PKG_LICENSE:=GPL-2.0-or-later PKG_MAINTAINER:=ITDog +LUCI_LANGUAGES:=en ru + include $(TOPDIR)/feeds/luci/luci.mk # call BuildPackage - OpenWrt buildroot signature \ No newline at end of file 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 500e88b..fd3ef2f 100644 --- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js +++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js @@ -10,27 +10,24 @@ return view.extend({ m = new form.Map('podkop', _('Podkop configuration')); - s = m.section(form.TypedSection, 'main'); s.anonymous = true; - o = s.tab('main', _('Main')); + o = s.tab('basic', _('Basic Settings')); - o = s.taboption('main', form.ListValue, 'mode', _('Mode'), _('Select VPN or Proxy')); + o = s.taboption('basic', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing')); o.value('vpn', ('VPN')); o.value('proxy', ('Proxy')); - o = s.taboption('main', form.TextValue, 'proxy_string', _('Proxy String'), _('String vless:// or ss://')); + o = s.taboption('basic', form.TextValue, 'proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); o.depends('mode', 'proxy'); - o .rows = 5; + o.rows = 5; - // Get all interface - o = s.taboption('main', form.ListValue, 'interface', _('Interface'), _('Specify the interface')); + o = s.taboption('basic', form.ListValue, 'interface', _('Network Interface'), _('Select network interface for VPN connection')); o.depends('mode', 'vpn'); try { const devices = await network.getDevices(); - const excludeInterfaces = ['br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0']; devices.forEach(function (device) { @@ -41,19 +38,17 @@ return view.extend({ if (!isExcluded) { o.value(deviceName, deviceName); } - } else { - console.warn('Device name is undefined or empty'); } }); } catch (error) { console.error('Error fetching devices:', error); } - o = s.taboption('main', form.Flag, 'domain_list_enabled', _('Domain list enable'), _('github.com/itdoginfo/allow-domains')); + o = s.taboption('basic', form.Flag, 'domain_list_enabled', _('Predefined Domain Lists'), _('Enable routing based on predefined domain lists for specific regions\ngithub.com/itdoginfo/allow-domains')); o.default = '0'; o.rmempty = false; - o = s.taboption('main', form.ListValue, 'domain_list', _('Domain list'), _('Select a list')); + o = s.taboption('basic', form.ListValue, 'domain_list', _('Domain List'), _('Select a predefined domain list')); o.placeholder = 'placeholder'; o.value('ru_inside', 'Russia inside'); o.value('ru_outside', 'Russia outside'); @@ -61,137 +56,242 @@ return view.extend({ o.depends('domain_list_enabled', '1'); o.rmempty = false; - o = s.taboption('main', form.Flag, 'delist_domains_enabled', _('Delist domains from main list enable')); + o = s.taboption('basic', form.Flag, 'subnets_list_enabled', _('Predefined Service Networks'), _('Enable routing for popular services like Twitter, Meta, and Discord')); o.default = '0'; o.rmempty = false; - o = s.taboption('main', form.DynamicList, 'delist_domains', _('Delist domains'), _('Domains to be excluded')); - o.placeholder = 'Delist domains'; - o.depends('delist_domains_enabled', '1'); - o.rmempty = false; - - o = s.taboption('main', form.Flag, 'subnets_list_enabled', _('Subnets list enable')); - o.default = '0'; - o.rmempty = false; - - o = s.taboption('main', form.DynamicList, 'subnets', _('Subnets specify option')); - o.placeholder = 'Subnet list'; + o = s.taboption('basic', form.DynamicList, 'subnets', _('Service Networks'), _('Select predefined service networks for routing')); + o.placeholder = 'Service network list'; o.value('twitter', 'Twitter(x.com)'); o.value('meta', 'Meta'); o.value('discord', 'Discord(voice)'); o.depends('subnets_list_enabled', '1'); o.rmempty = false; - o = s.taboption('main', form.Flag, 'custom_domains_list_enabled', _('Custom domains enable')); + o = s.tab('custom', _('User Settings')); + + o = s.taboption('custom', form.Flag, 'custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing')); o.default = '0'; o.rmempty = false; - o = s.taboption('main', form.DynamicList, 'custom_domains', _('Your domains')); + o = s.taboption('custom', form.DynamicList, 'custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o.placeholder = 'Domains list'; o.depends('custom_domains_list_enabled', '1'); o.rmempty = false; - o.validate = function(section_id, value) { - // Чтобы валидация не ругалась на пустое поле + o.validate = function (section_id, value) { if (!value || value.length === 0) { return true; } - // Регулярное выражение для проверки доменов и субдоменов (без порта, протокола, пути) - // Домен должен соответствовать правилам именования доменов. Только для латиницы - const domainRegex = /^(?!:\/\/)([a-zA-Z0-9]+(-[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,}$/; + const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/; if (!domainRegex.test(value)) { - return `Invalid domain format: ${value}. Enter only valid domain, without protocol, port or path`; + return _('Invalid domain format. Enter domain without protocol (example: sub.example.com)'); + } + return true; + }; + + o = s.taboption('custom', form.Flag, 'custom_download_domains_list_enabled', _('Remote Domain Lists'), _('Download and use domain lists from remote URLs')); + o.default = '0'; + o.rmempty = false; + + o = s.taboption('custom', form.DynamicList, 'custom_download_domains', _('Remote Domain URLs'), _('Enter full URLs starting with http:// or https://')); + o.placeholder = 'URL'; + o.depends('custom_download_domains_list_enabled', '1'); + o.rmempty = false; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + try { + const url = new URL(value); + if (!['http:', 'https:'].includes(url.protocol)) { + return _('URL must use http:// or https:// protocol'); + } + return true; + } catch (e) { + return _('Invalid URL format. URL must start with http:// or https://'); + } + }; + + o = s.taboption('custom', form.Flag, 'custom_subnets_list_enabled', _('User Subnet List'), _('Enable and manage your custom list of IP subnets for selective routing')); + o.default = '0'; + o.rmempty = false; + + o = s.taboption('custom', form.DynamicList, 'custom_subnets', _('User Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); + o.placeholder = 'Subnets list'; + o.depends('custom_subnets_list_enabled', '1'); + o.rmempty = false; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/; + + if (!subnetRegex.test(value)) { + return _('Invalid subnet format. Use format: X.X.X.X/Y (like 192.168.1.0/24)'); + } + + const [ip, cidr] = value.split('/'); + const ipParts = ip.split('.'); + const cidrNum = parseInt(cidr); + + for (const part of ipParts) { + const num = parseInt(part); + if (num < 0 || num > 255) { + return _('IP address parts must be between 0 and 255'); + } + } + + if (cidrNum < 0 || cidrNum > 32) { + return _('CIDR must be between 0 and 32'); } return true; }; - o = s.taboption('main', form.Flag, 'custom_download_domains_list_enabled', _('URL domains enable')); + o = s.taboption('custom', form.Flag, 'custom_download_subnets_list_enabled', _('Remote Subnet Lists'), _('Download and use subnet lists from remote URLs')); o.default = '0'; o.rmempty = false; - o = s.taboption('main', form.DynamicList, 'custom_download_domains', _('Your URL domains')); - o.placeholder = 'URL'; - o.depends('custom_download_domains_list_enabled', '1'); - o.rmempty = false; - - o = s.taboption('main', form.Flag, 'custom_subnets_list_enabled', _('Custom subnets enable')); - o.default = '0'; - o.rmempty = false; - - o = s.taboption('main', form.DynamicList, 'custom_subnets', _('Your subnet')); - o.placeholder = 'Subnets list'; - o.depends('custom_subnets_list_enabled', '1'); - o.rmempty = false; - - o = s.taboption('main', form.Flag, 'custom_download_subnets_list_enabled', _('URL subnets enable')); - o.default = '0'; - o.rmempty = false; - - o = s.taboption('main', form.DynamicList, 'custom_download_subnets', _('Your URL subnet')); + o = s.taboption('custom', form.DynamicList, 'custom_download_subnets', _('Remote Subnet URLs'), _('Enter full URLs starting with http:// or https://')); o.placeholder = 'URL'; o.depends('custom_download_subnets_list_enabled', '1'); o.rmempty = false; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } - o = s.taboption('main', form.Flag, 'all_traffic_from_ip_enabled', _('IP for full redirection')); + try { + const url = new URL(value); + if (!['http:', 'https:'].includes(url.protocol)) { + return _('URL must use http:// or https:// protocol'); + } + return true; + } catch (e) { + return _('Invalid URL format. URL must start with http:// or https://'); + } + }; + + o = s.tab('additional', _('Additional Settings')); + + o = s.taboption('additional', form.Flag, 'delist_domains_enabled', _('Domain Exclusions'), _('Exclude specific domains from routing rules')); o.default = '0'; o.rmempty = false; - o = s.taboption('main', form.DynamicList, 'all_traffic_ip', _('Local IPs')); + o = s.taboption('additional', form.DynamicList, 'delist_domains', _('Excluded Domains'), _('Domains to be excluded from routing')); + o.placeholder = 'Delist domains'; + o.depends('delist_domains_enabled', '1'); + o.rmempty = false; + + o = s.taboption('additional', form.Flag, 'all_traffic_from_ip_enabled', _('Force Proxy IPs'), _('Specify local IP addresses whose traffic will always use the configured route')); + o.default = '0'; + o.rmempty = false; + + o = s.taboption('additional', form.DynamicList, 'all_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); o.placeholder = 'IP'; o.depends('all_traffic_from_ip_enabled', '1'); o.rmempty = false; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } - o = s.taboption('main', form.Flag, 'exclude_from_ip_enabled', _('IP for full exclude')); + const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/; + + if (!ipRegex.test(value)) { + return _('Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)'); + } + + const ipParts = value.split('.'); + for (const part of ipParts) { + const num = parseInt(part); + if (num < 0 || num > 255) { + return _('IP address parts must be between 0 and 255'); + } + } + + return true; + }; + + o = s.taboption('additional', form.Flag, 'exclude_from_ip_enabled', _('Bypass Proxy IPs'), _('Specify local IP addresses that will never use the configured route')); o.default = '0'; o.rmempty = false; - o = s.taboption('main', form.DynamicList, 'exclude_traffic_ip', _('Local IPs')); + o = s.taboption('additional', form.DynamicList, 'exclude_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); o.placeholder = 'IP'; o.depends('exclude_from_ip_enabled', '1'); o.rmempty = false; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } - o = s.taboption('main', form.Flag, 'yacd', _('Yacd enable'), _('http://openwrt.lan:9090/ui')); + const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/; + + if (!ipRegex.test(value)) { + return _('Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)'); + } + + const ipParts = value.split('.'); + for (const part of ipParts) { + const num = parseInt(part); + if (num < 0 || num > 255) { + return _('IP address parts must be between 0 and 255'); + } + } + + return true; + }; + + o = s.taboption('additional', form.Flag, 'yacd', _('Yacd enable'), _('http://openwrt.lan:9090/ui')); o.default = '0'; o.depends('mode', 'proxy'); o.rmempty = false; - o = s.taboption('main', form.Flag, 'socks5', _('Mixed enable'), _('Browser port: 2080')); + o = s.taboption('additional', form.Flag, 'socks5', _('Mixed enable'), _('Browser port: 2080')); o.default = '0'; o.depends('mode', 'proxy'); o.rmempty = false; - - o = s.taboption('main', form.Flag, 'exclude_ntp', _('Exclude NTP'), _('For issues with open connections sing-box')); + + o = s.taboption('additional', form.Flag, 'exclude_ntp', _('Exclude NTP'), _('For issues with open connections sing-box')); o.default = '0'; o.depends('mode', 'proxy'); - o.rmempty = false; + o.rmempty = false; - // Second section - s = m.section(form.TypedSection, 'second'); - s.anonymous = true; + o = s.taboption('additional', form.ListValue, 'update_interval', _('List Update Frequency'), _('Select how often the lists will be updated')); + o.value('0 */1 * * *', _('Every hour')); + o.value('0 */2 * * *', _('Every 2 hours')); + o.value('0 */4 * * *', _('Every 4 hours')); + o.value('0 */6 * * *', _('Every 6 hours')); + o.value('0 */12 * * *', _('Every 12 hours')); + o.value('0 4 * * *', _('Once a day at 04:00')); + o.value('0 4 * * 0', _('Once a week on Sunday at 04:00')); + o.default = '0 4 * * *'; + o.rmempty = false; - o = s.tab('second', _('Second')); + o = s.tab('second_settings', _('Alternative Route')); - o = s.taboption('second', form.Flag, 'second_enable', _('Second enable')); + o = s.taboption('second_settings', form.Flag, 'second_enable', _('Alternative Route Enable'), _('Enable secondary routing configuration')); o.default = '0'; o.rmempty = false; - o = s.taboption('second', form.ListValue, 'mode', _('Mode'), _('Select VPN or Proxy')); + o = s.taboption('second_settings', form.ListValue, 'second_mode', _('Connection Type'), _('Select between VPN and Proxy for alternative route')); o.value('vpn', ('VPN')); o.value('proxy', ('Proxy')); o.depends('second_enable', '1'); - o = s.taboption('second', form.Value, 'proxy_string', _('Proxy String'), _('String vless:// or ss://')); - o.depends('mode', 'proxy'); + o = s.taboption('second_settings', form.Value, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); + o.depends('second_mode', 'proxy'); - // Get all interface - o = s.taboption('second', form.ListValue, 'interface', _('Interface'), _('Specify the interface')); - o.depends('mode', 'vpn'); + o = s.taboption('second_settings', form.ListValue, 'second_interface', _('Network Interface'), _('Select network interface for VPN connection')); + o.depends('second_mode', 'vpn'); try { const devices = await network.getDevices(); - const excludeInterfaces = ['br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0']; devices.forEach(function (device) { @@ -202,45 +302,83 @@ return view.extend({ if (!isExcluded) { o.value(deviceName, deviceName); } - } else { - console.warn('Device name is undefined or empty'); } }); } catch (error) { console.error('Error fetching devices:', error); } - o = s.taboption('second', form.Flag, 'domain_service_enabled', _('Domain service enable')); + o = s.taboption('second_settings', form.Flag, 'domain_service_enabled', _('Service List Enable'), _('Enable predefined service lists for alternative routing')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); - o = s.taboption('second', form.ListValue, 'service_list', _('Service list'), _('Select a list')); + o = s.taboption('second_settings', form.ListValue, 'service_list', _('Service List'), _('Select predefined services for alternative routing')); o.placeholder = 'placeholder'; o.value('youtube', 'Youtube'); o.depends('domain_service_enabled', '1'); o.rmempty = false; - o = s.taboption('second', form.Flag, 'custom_domains_list_enabled', _('Custom domains enable')); + o = s.taboption('second_settings', form.Flag, 'second_custom_domains_list_enabled', _('Alternative Domain List'), _('Configure custom domains for alternative routing path')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); - o = s.taboption('second', form.DynamicList, 'custom_domains', _('Your domains')); + o = s.taboption('second_settings', form.DynamicList, 'second_custom_domains', _('Alternative Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o.placeholder = 'Domains list'; - o.depends('custom_domains_list_enabled', '1'); + o.depends('second_custom_domains_list_enabled', '1'); o.rmempty = false; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } - o = s.taboption('second', form.Flag, 'custom_subnets_list_enabled', _('Custom subnets enable')); + const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/; + + if (!domainRegex.test(value)) { + return _('Invalid domain format. Enter domain without protocol (example: sub.example.com)'); + } + return true; + }; + + o = s.taboption('second_settings', form.Flag, 'second_custom_subnets_list_enabled', _('Alternative Subnet List'), _('Configure custom subnets for alternative routing path')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); - o = s.taboption('second', form.DynamicList, 'custom_subnets', _('Your subnet')); + o = s.taboption('second_settings', form.DynamicList, 'second_custom_subnets', _('Alternative Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); o.placeholder = 'Subnets list'; - o.depends('custom_subnets_list_enabled', '1'); + o.depends('second_custom_subnets_list_enabled', '1'); o.rmempty = false; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/; + + if (!subnetRegex.test(value)) { + return _('Invalid subnet format. Use format: X.X.X.X/Y (like 192.168.1.0/24)'); + } + + const [ip, cidr] = value.split('/'); + const ipParts = ip.split('.'); + const cidrNum = parseInt(cidr); + + for (const part of ipParts) { + const num = parseInt(part); + if (num < 0 || num > 255) { + return _('IP address parts must be between 0 and 255'); + } + } + + if (cidrNum < 0 || cidrNum > 32) { + return _('CIDR must be between 0 and 32'); + } + + return true; + }; return m.render(); } -}); \ No newline at end of file +}); diff --git a/luci-app-podkop/po/ru/podkop.po b/luci-app-podkop/po/ru/podkop.po new file mode 100644 index 0000000..0b9a39a --- /dev/null +++ b/luci-app-podkop/po/ru/podkop.po @@ -0,0 +1,236 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +msgid "Podkop configuration" +msgstr "Настройка Podkop" + +msgid "Basic Settings" +msgstr "Основные настройки" + +msgid "Connection Type" +msgstr "Тип подключения" + +msgid "Select between VPN and Proxy connection methods for traffic routing" +msgstr "Выберите между VPN и Proxy методами для маршрутизации трафика" + +msgid "Proxy Configuration URL" +msgstr "URL конфигурации прокси" + +msgid "Enter connection string starting with vless:// or ss:// for proxy configuration" +msgstr "Введите строку подключения, начинающуюся с vless:// или ss:// для настройки прокси" + +msgid "Network Interface" +msgstr "Сетевой интерфейс" + +msgid "Select network interface for VPN connection" +msgstr "Выберите сетевой интерфейс для VPN подключения" + +msgid "Predefined Domain Lists" +msgstr "Предустановленные списки доменов" + +msgid "Enable routing based on predefined domain lists for specific regions" +msgstr "Включить маршрутизацию на основе предустановленных списков доменов для определенных регионов" + +msgid "Domain List" +msgstr "Список доменов" + +msgid "Select a predefined domain list" +msgstr "Выберите предустановленный список доменов" + +msgid "Predefined Service Networks" +msgstr "Предустановленные сети сервисов" + +msgid "Enable routing for popular services like Twitter, Meta, and Discord" +msgstr "Включить маршрутизацию для популярных сервисов, таких как Twitter, Meta и Discord" + +msgid "Service Networks" +msgstr "Сети сервисов" + +msgid "Select predefined service networks for routing" +msgstr "Выберите предустановленные сети сервисов для маршрутизации" + +msgid "User Settings" +msgstr "Пользовательские настройки" + +msgid "User Domain List" +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 "Введите имена доменов без протоколов (пример: sub.example.com или example.com)" + +msgid "Remote Domain Lists" +msgstr "Удаленные списки доменов" + +msgid "Download and use domain lists from remote URLs" +msgstr "Загрузка и использование списков доменов с удаленных URL" + +msgid "Remote Domain URLs" +msgstr "URL удаленных доменов" + +msgid "Enter full URLs starting with http:// or https://" +msgstr "Введите полные URL, начинающиеся с http:// или https://" + +msgid "User Subnet List" +msgstr "Пользовательский список подсетей" + +msgid "Enable and manage your custom list of IP subnets for selective routing" +msgstr "Включить и управлять пользовательским списком IP-подсетей для выборочной маршрутизации" + +msgid "User Subnets" +msgstr "Пользовательские подсети" + +msgid "Enter subnet in CIDR notation (example: 192.168.1.0/24)" +msgstr "Введите подсеть в нотации CIDR (пример: 192.168.1.0/24)" + +msgid "Remote Subnet Lists" +msgstr "Удаленные списки подсетей" + +msgid "Download and use subnet lists from remote URLs" +msgstr "Загрузка и использование списков подсетей с удаленных URL" + +msgid "Remote Subnet URLs" +msgstr "URL удаленных подсетей" + +msgid "Additional Settings" +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 "Force Proxy IPs" +msgstr "Принудительные прокси IP" + +msgid "Specify local IP addresses whose traffic will always use the configured route" +msgstr "Укажите локальные IP-адреса, трафик которых всегда будет использовать настроенный маршрут" + +msgid "Local IPs" +msgstr "Локальные IP" + +msgid "Enter valid IPv4 addresses" +msgstr "Введите действительные IPv4 адреса" + +msgid "Bypass Proxy IPs" +msgstr "Исключения прокси IP" + +msgid "Specify local IP addresses that will never use the configured route" +msgstr "Укажите локальные IP-адреса, которые никогда не будут использовать настроенный маршрут" + +msgid "List Update Frequency" +msgstr "Частота обновления списков" + +msgid "Select how often the lists will be updated" +msgstr "Выберите, как часто будут обновляться списки" + +msgid "Every hour" +msgstr "Каждый час" + +msgid "Every 2 hours" +msgstr "Каждые 2 часа" + +msgid "Every 4 hours" +msgstr "Каждые 4 часа" + +msgid "Every 6 hours" +msgstr "Каждые 6 часов" + +msgid "Every 12 hours" +msgstr "Каждые 12 часов" + +msgid "Once a day at 04:00" +msgstr "Раз в день в 04:00" + +msgid "Once a week on Sunday at 04:00" +msgstr "Раз в неделю в воскресенье в 04:00" + +msgid "Once a week on Monday 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 "Alternative Route" +msgstr "Альтернативный маршрут" + +msgid "Alternative Route Enable" +msgstr "Включить альтернативный маршрут" + +msgid "Enable secondary routing configuration" +msgstr "Включить вторичную конфигурацию маршрутизации" + +msgid "Service List Enable" +msgstr "Включить список сервисов" + +msgid "Enable predefined service lists for alternative routing" +msgstr "Включить предустановленные списки сервисов для альтернативной маршрутизации" + +msgid "Service List" +msgstr "Список сервисов" + +msgid "Select predefined services for alternative routing" +msgstr "Выберите предустановленные сервисы для альтернативной маршрутизации" + +msgid "Alternative Domain List" +msgstr "Альтернативный список доменов" + +msgid "Configure custom domains for alternative routing path" +msgstr "Настройте пользовательские домены для альтернативного маршрута" + +msgid "Alternative Domains" +msgstr "Альтернативные домены" + +msgid "Alternative Subnet List" +msgstr "Альтернативный список подсетей" + +msgid "Configure custom subnets for alternative routing path" +msgstr "Настройте пользовательские подсети для альтернативного маршрута" + +msgid "Alternative Subnets" +msgstr "Альтернативные подсети" + +msgid "Invalid domain format. Enter domain without protocol (example: sub.example.com)" +msgstr "Неверный формат домена. Введите домен без протокола (пример: sub.example.com)" + +msgid "URL must use http:// or https:// protocol" +msgstr "URL должен использовать протокол http:// или https://" + +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 "IP address parts must be between 0 and 255" +msgstr "Части IP-адреса должны быть между 0 и 255" + +msgid "CIDR must be between 0 and 32" +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)" \ No newline at end of file diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot new file mode 100644 index 0000000..10d386f --- /dev/null +++ b/luci-app-podkop/po/templates/podkop.pot @@ -0,0 +1,150 @@ +msgid "" +msgstr "Content-Type: text/plain; charset=UTF-8" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Podkop configuration" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Basic Settings" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Connection Type" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Select between VPN and Proxy connection methods for traffic routing" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Proxy Configuration URL" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Enter connection string starting with vless:// or ss:// for proxy configuration" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Network Interface" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Select network interface for VPN connection" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "List Update Frequency" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Select how often the lists will be updated" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Every hour" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Every 2 hours" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Every 4 hours" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Every 6 hours" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Every 12 hours" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Once a day at 04:00" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Once a week on Sunday at 04:00" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Once a week on Monday at 04:00" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Predefined Domain Lists" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Enable routing based on predefined domain lists for specific regions" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Domain List" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Select a predefined domain list" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Predefined Service Networks" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Enable routing for popular services like Twitter, Meta, and Discord" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "User Settings" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Alternative Route" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Alternative Route Enable" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Enable secondary routing configuration" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Invalid domain format. Enter domain without protocol (example: sub.example.com)" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "URL must use http:// or https:// protocol" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Invalid URL format. URL must start with http:// or https://" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Invalid subnet format. Use format: X.X.X.X/Y (like 192.168.1.0/24)" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Yacd enable" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Mixed enable" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Browser port: 2080" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Exclude NTP" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "For issues with open connections sing-box" +msgstr "" \ No newline at end of file diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 13a6edf..55a3057 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -12,7 +12,8 @@ EXTRA_HELP=" list_update Updating domain and subnet lists add_route_interface Adding route for interface sing_box_config_vless For test vless string" -cron_job="0 4 * * * /etc/init.d/podkop list_update" +config_get update_interval "main" "update_interval" "0 4 * * *" +cron_job="${update_interval} /etc/init.d/podkop list_update" start_service() { log "Start podkop" @@ -245,6 +246,12 @@ reload_service() { service_triggers() { log "service_triggers start" procd_add_config_trigger "config.change" "$NAME" "$initscript" reload 'on_config_change' + + config_get update_interval "main" "update_interval" + if [ -n "$update_interval" ]; then + remove_cron_job + add_cron_job + fi } log() { @@ -259,18 +266,16 @@ log() { } add_cron_job() { - if ! crontab -l | grep -q "podkop"; then - #echo "$cron_job" >>/etc/crontabs/root - crontab -l | { - cat - echo "$cron_job" - } | crontab - - log "The cron job has been created" - fi + remove_cron_job + crontab -l | { + cat + echo "$cron_job" + } | crontab - + log "The cron job has been created: $cron_job" } remove_cron_job() { - sed -i "\|podkop|d" /etc/crontabs/root + (crontab -l | grep -v "/etc/init.d/podkop list_update") | crontab - log "The cron job removed" } From 20d0d00620c31cfea2d439d928ffffb031c51f3a Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Sat, 9 Nov 2024 00:41:44 +0300 Subject: [PATCH 02/14] docs: Remove GitHub link from description --- .../htdocs/luci-static/resources/view/podkop/podkop.js | 2 +- 1 file changed, 1 insertion(+), 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 fd3ef2f..1091cc2 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 @@ -44,7 +44,7 @@ return view.extend({ console.error('Error fetching devices:', error); } - o = s.taboption('basic', form.Flag, 'domain_list_enabled', _('Predefined Domain Lists'), _('Enable routing based on predefined domain lists for specific regions\ngithub.com/itdoginfo/allow-domains')); + o = s.taboption('basic', form.Flag, 'domain_list_enabled', _('Predefined Domain Lists'), _('Enable routing based on predefined domain lists for specific regions')); o.default = '0'; o.rmempty = false; From 8794fc72edfd24d56f9b6a5a6977eedd0c4eb5f3 Mon Sep 17 00:00:00 2001 From: Ivan K <54704529+VizzleTF@users.noreply.github.com> Date: Sat, 9 Nov 2024 20:29:13 +0300 Subject: [PATCH 03/14] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a375f2a..22dce36 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ opkg update && opkg install sing-box Приоритет 2 - [ ] Списки доменов и подсетей с роутера -- [ ] Кнопка обновления списка доменов и подсетей +- [x] Кнопка обновления списка доменов и подсетей - [ ] IPv6 - [ ] Придумать автонастройку DNS через stubby итд. Как лучше это реализовать. - [ ] Удаление подсетей CF из domain sets раз в N часов @@ -120,7 +120,7 @@ Wiki - [ ] Формирование json для sing-box на уровне jq, а не шаблонов - [ ] Unit тесты (BATS) - [ ] Интеграционые тесты бекенда (OpenWrt rootfs + BATS) -- [ ] RU перевод +- [x] RU перевод Хз как сделать - [ ] Добавить label от конфига vless\ss\etc в luci. @@ -195,4 +195,4 @@ sudo apt update sudo apt install build-essential clang flex bison g++ gawk \ gcc-multilib g++-multilib gettext git libncurses-dev libssl-dev \ python3-distutils rsync unzip zlib1g-dev file wget -``` \ No newline at end of file +``` From 6179306da9d434ec13c3b2bb52060efb3d1e14f5 Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Sun, 10 Nov 2024 16:07:59 +0300 Subject: [PATCH 04/14] fix: Fix secondary route configuration --- .github/workflows/build.yml | 10 +- Dockerfile | 4 +- luci-app-podkop/Makefile | 2 +- .../resources/view/podkop/podkop.js | 26 ++-- luci-app-podkop/po/ru/podkop.po | 47 +++--- luci-app-podkop/po/templates/podkop.pot | 140 +++++++++++++----- podkop/files/etc/init.d/podkop | 41 ++--- 7 files changed, 172 insertions(+), 98 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8868663..ca07f73 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,13 +9,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4.2.1 - + - name: Build and push uses: docker/build-push-action@v6.9.0 with: context: . tags: podkop:ci - + - name: Create Docker container run: docker create --name podkop podkop:ci @@ -23,17 +23,17 @@ jobs: run: | docker cp podkop:/builder/bin/packages/x86_64/utilites/. ./bin/ docker cp podkop:/builder/bin/packages/x86_64/luci/. ./bin/ - + - name: Filter IPK files run: | mkdir -p ./filtered-bin cp ./bin/luci-i18n-podkop-ru_*.ipk ./filtered-bin/ cp ./bin/podkop_*.ipk ./filtered-bin/ cp ./bin/luci-app-podkop_*.ipk ./filtered-bin/ - + - name: Remove Docker container run: docker rm podkop - + - name: Release uses: softprops/action-gh-release@v2.0.8 with: diff --git a/Dockerfile b/Dockerfile index 35ab0d2..2b427fe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,8 @@ FROM openwrt/sdk:x86_64-v23.05.5 -FROM openwrt/sdk:x86_64-v23.05.5 - RUN ./scripts/feeds update -a && ./scripts/feeds install luci-base && mkdir -p /builder/package/feeds/utilites/ && mkdir -p /builder/package/feeds/luci/ COPY ./podkop /builder/package/feeds/utilites/podkop COPY ./luci-app-podkop /builder/package/feeds/luci/luci-app-podkop -RUN make defconfig && make package/podkop/compile && make package/luci-app-podkop/compile V=s \ No newline at end of file +RUN make defconfig && make package/podkop/compile && make package/luci-app-podkop/compile V=s -j4 \ No newline at end of file diff --git a/luci-app-podkop/Makefile b/luci-app-podkop/Makefile index 2e9a711..5ec9d55 100644 --- a/luci-app-podkop/Makefile +++ b/luci-app-podkop/Makefile @@ -5,7 +5,7 @@ PKG_VERSION:=0.2.3 PKG_RELEASE:=1 LUCI_TITLE:=LuCI podkop app -LUCI_DEPENDS:=+podkop +LUCI_DEPENDS:=+luci-base +podkop LUCI_PKGARCH:=all LUCI_LANG.ru:=Русский (Russian) LUCI_LANG.en:=English 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 1091cc2..70404d4 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 @@ -13,6 +13,7 @@ return view.extend({ s = m.section(form.TypedSection, 'main'); s.anonymous = true; + // Basic Settings tab o = s.tab('basic', _('Basic Settings')); o = s.taboption('basic', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing')); @@ -44,7 +45,7 @@ return view.extend({ console.error('Error fetching devices:', error); } - o = s.taboption('basic', form.Flag, 'domain_list_enabled', _('Predefined Domain Lists'), _('Enable routing based on predefined domain lists for specific regions')); + o = s.taboption('basic', form.Flag, 'domain_list_enabled', _('Predefined Domain Lists'), _('github.com/itdoginfo/allow-domains')); o.default = '0'; o.rmempty = false; @@ -68,6 +69,7 @@ return view.extend({ o.depends('subnets_list_enabled', '1'); o.rmempty = false; + // User Settings tab o = s.tab('custom', _('User Settings')); o = s.taboption('custom', form.Flag, 'custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing')); @@ -176,6 +178,7 @@ return view.extend({ } }; + // Additional Settings tab o = s.tab('additional', _('Additional Settings')); o = s.taboption('additional', form.Flag, 'delist_domains_enabled', _('Domain Exclusions'), _('Exclude specific domains from routing rules')); @@ -273,13 +276,14 @@ return view.extend({ o.default = '0 4 * * *'; o.rmempty = false; - o = s.tab('second_settings', _('Alternative Route')); + // Secondary Route tab + o = s.tab('second_settings', _('Secondary Route')); - o = s.taboption('second_settings', form.Flag, 'second_enable', _('Alternative Route Enable'), _('Enable secondary routing configuration')); + o = s.taboption('second_settings', form.Flag, 'second_enable', _('Secondary Route Enable'), _('Enable secondary routing configuration')); o.default = '0'; o.rmempty = false; - o = s.taboption('second_settings', form.ListValue, 'second_mode', _('Connection Type'), _('Select between VPN and Proxy for alternative route')); + o = s.taboption('second_settings', form.ListValue, 'second_mode', _('Connection Type'), _('Select between VPN and Proxy for secondary route')); o.value('vpn', ('VPN')); o.value('proxy', ('Proxy')); o.depends('second_enable', '1'); @@ -308,23 +312,23 @@ return view.extend({ console.error('Error fetching devices:', error); } - o = s.taboption('second_settings', form.Flag, 'domain_service_enabled', _('Service List Enable'), _('Enable predefined service lists for alternative routing')); + o = s.taboption('second_settings', form.Flag, 'domain_service_enabled', _('Service Domain List Enable'), _('Enable predefined service domain lists for secondary routing')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); - o = s.taboption('second_settings', form.ListValue, 'service_list', _('Service List'), _('Select predefined services for alternative routing')); + o = s.taboption('second_settings', form.ListValue, 'service_list', _('Service List'), _('Select predefined services for secondary routing')); o.placeholder = 'placeholder'; o.value('youtube', 'Youtube'); o.depends('domain_service_enabled', '1'); o.rmempty = false; - o = s.taboption('second_settings', form.Flag, 'second_custom_domains_list_enabled', _('Alternative Domain List'), _('Configure custom domains for alternative routing path')); + o = s.taboption('second_settings', form.Flag, 'second_custom_domains_list_enabled', _('Secondary Domain List'), _('Configure custom domains for secondary routing path')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); - o = s.taboption('second_settings', form.DynamicList, 'second_custom_domains', _('Alternative Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); + o = s.taboption('second_settings', form.DynamicList, 'second_custom_domains', _('Secondary Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o.placeholder = 'Domains list'; o.depends('second_custom_domains_list_enabled', '1'); o.rmempty = false; @@ -341,12 +345,12 @@ return view.extend({ return true; }; - o = s.taboption('second_settings', form.Flag, 'second_custom_subnets_list_enabled', _('Alternative Subnet List'), _('Configure custom subnets for alternative routing path')); + o = s.taboption('second_settings', form.Flag, 'second_custom_subnets_list_enabled', _('Secondary Subnet List'), _('Configure custom subnets for secondary routing path')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); - o = s.taboption('second_settings', form.DynamicList, 'second_custom_subnets', _('Alternative Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); + o = s.taboption('second_settings', form.DynamicList, 'second_custom_subnets', _('Secondary Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); o.placeholder = 'Subnets list'; o.depends('second_custom_subnets_list_enabled', '1'); o.rmempty = false; @@ -381,4 +385,4 @@ return view.extend({ return m.render(); } -}); +}); \ No newline at end of file diff --git a/luci-app-podkop/po/ru/podkop.po b/luci-app-podkop/po/ru/podkop.po index 0b9a39a..1d74662 100644 --- a/luci-app-podkop/po/ru/podkop.po +++ b/luci-app-podkop/po/ru/podkop.po @@ -28,9 +28,6 @@ msgstr "Выберите сетевой интерфейс для VPN подкл msgid "Predefined Domain Lists" msgstr "Предустановленные списки доменов" -msgid "Enable routing based on predefined domain lists for specific regions" -msgstr "Включить маршрутизацию на основе предустановленных списков доменов для определенных регионов" - msgid "Domain List" msgstr "Список доменов" @@ -175,44 +172,44 @@ msgstr "Исключить NTP" msgid "For issues with open connections sing-box" msgstr "Для проблем с открытыми соединениями sing-box" -msgid "Alternative Route" -msgstr "Альтернативный маршрут" +msgid "Secondary Route" +msgstr "Вторичный маршрут" -msgid "Alternative Route Enable" -msgstr "Включить альтернативный маршрут" +msgid "Secondary Route Enable" +msgstr "Включить вторичный маршрут" msgid "Enable secondary routing configuration" msgstr "Включить вторичную конфигурацию маршрутизации" -msgid "Service List Enable" -msgstr "Включить список сервисов" +msgid "Service Domain List Enable" +msgstr "Включить список доменов сервисов" -msgid "Enable predefined service lists for alternative routing" -msgstr "Включить предустановленные списки сервисов для альтернативной маршрутизации" +msgid "Enable predefined service domain lists for secondary routing" +msgstr "Включить предустановленные списки доменов сервисов для вторичной маршрутизации" msgid "Service List" msgstr "Список сервисов" -msgid "Select predefined services for alternative routing" -msgstr "Выберите предустановленные сервисы для альтернативной маршрутизации" +msgid "Select predefined services for secondary routing" +msgstr "Выберите предустановленные сервисы для вторичной маршрутизации" -msgid "Alternative Domain List" -msgstr "Альтернативный список доменов" +msgid "Secondary Domain List" +msgstr "Вторичный список доменов" -msgid "Configure custom domains for alternative routing path" -msgstr "Настройте пользовательские домены для альтернативного маршрута" +msgid "Configure custom domains for secondary routing path" +msgstr "Настройте пользовательские домены для вторичного маршрута" -msgid "Alternative Domains" -msgstr "Альтернативные домены" +msgid "Secondary Domains" +msgstr "Вторичные домены" -msgid "Alternative Subnet List" -msgstr "Альтернативный список подсетей" +msgid "Secondary Subnet List" +msgstr "Вторичный список подсетей" -msgid "Configure custom subnets for alternative routing path" -msgstr "Настройте пользовательские подсети для альтернативного маршрута" +msgid "Configure custom subnets for secondary routing path" +msgstr "Настройте пользовательские подсети для вторичного маршрута" -msgid "Alternative Subnets" -msgstr "Альтернативные подсети" +msgid "Secondary Subnets" +msgstr "Вторичные подсети" msgid "Invalid domain format. Enter domain without protocol (example: sub.example.com)" msgstr "Неверный формат домена. Введите домен без протокола (пример: sub.example.com)" diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot index 10d386f..5b3bfa5 100644 --- a/luci-app-podkop/po/templates/podkop.pot +++ b/luci-app-podkop/po/templates/podkop.pot @@ -33,6 +33,102 @@ msgstr "" msgid "Select network interface for VPN connection" msgstr "" +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Predefined Domain Lists" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Domain List" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Select a predefined domain list" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Predefined Service Networks" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Enable routing for popular services like Twitter, Meta, and Discord" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Service Networks" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Select predefined service networks for routing" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "User Settings" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Secondary Route" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Secondary Route Enable" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Enable secondary routing configuration" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Service Domain List Enable" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Enable predefined service domain lists for secondary routing" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Service List" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Select predefined services for secondary routing" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Secondary Domain List" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Configure custom domains for secondary routing path" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Secondary Domains" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Secondary Subnet List" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Configure custom subnets for secondary routing path" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Secondary Subnets" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Additional Settings" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Domain Exclusions" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Excluded Domains" +msgstr "" + #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "List Update Frequency" msgstr "" @@ -74,43 +170,23 @@ msgid "Once a week on Monday at 04:00" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Predefined Domain Lists" +msgid "Yacd enable" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Enable routing based on predefined domain lists for specific regions" +msgid "Mixed enable" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Domain List" +msgid "Browser port: 2080" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Select a predefined domain list" +msgid "Exclude NTP" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Predefined Service Networks" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Enable routing for popular services like Twitter, Meta, and Discord" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "User Settings" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Alternative Route" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Alternative Route Enable" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Enable secondary routing configuration" +msgid "For issues with open connections sing-box" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 @@ -130,21 +206,13 @@ msgid "Invalid subnet format. Use format: X.X.X.X/Y (like 192.168.1.0/24)" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Yacd enable" +msgid "IP address parts must be between 0 and 255" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Mixed enable" +msgid "CIDR must be between 0 and 32" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Browser port: 2080" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Exclude NTP" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "For issues with open connections sing-box" +msgid "Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)" msgstr "" \ No newline at end of file diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 55a3057..b0bdc4b 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -35,9 +35,9 @@ start_service() { fi config_get_bool second_enable "second" "second_enable" "0" - config_get mode "second" "mode" "0" - if [ "$second_enable" -eq "1" ] && [ "$mode" = "proxy" ]; then - config_get proxy_string second "proxy_string" + config_get second_mode "second" "second_mode" "0" + if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "proxy" ]; then + config_get proxy_string "second" "second_proxy_string" if [[ "$proxy_string" =~ ^ss:// ]]; then sing_box_config_shadowsocks "$proxy_string" "1603" elif [[ "$proxy_string" =~ ^vless:// ]]; then @@ -53,9 +53,9 @@ start_service() { /etc/init.d/sing-box enable fi - if [ "$second_enable" -eq "1" ] && [ "$mode" = "vpn" ]; then + if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "vpn" ]; then log "VPN mode for second" - config_get interface "second" "interface" "0" + config_get interface "second" "second_interface" "0" if [ -n "$interface" ]; then add_route_interface "$interface" "podkop2" else @@ -72,8 +72,8 @@ start_service() { # Main - proxy, Second - proxy config_get_bool second_enable "second" "second_enable" "0" - config_get mode "second" "mode" "0" - if [ "$second_enable" -eq "1" ] && [ "$mode" = "proxy" ]; then + config_get second_mode "second" "second_mode" "0" + if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "proxy" ]; then log "Two proxy enable" outbound_main=$(mktemp) outbound_second=$(mktemp) @@ -88,7 +88,7 @@ start_service() { return fi - config_get proxy_string second "proxy_string" + config_get proxy_string "second" "second_proxy_string" if [[ "$proxy_string" =~ ^ss:// ]]; then sing_box_config_outbound_shadowsocks "$proxy_string" "$outbound_second" second elif [[ "$proxy_string" =~ ^vless:// ]]; then @@ -109,8 +109,8 @@ start_service() { # Main proxy, second disable/vpn config_get_bool second_enable "second" "second_enable" "0" - config_get mode "second" "mode" "0" - if [ "$second_enable" -eq "0" ] || [ "$mode" = "vpn" ]; then + config_get second_mode "second" "second_mode" "0" + if [ "$second_enable" -eq "0" ] || [ "$second_mode" = "vpn" ]; then config_get proxy_string main "proxy_string" if [[ "$proxy_string" =~ ^ss:// ]]; then sing_box_config_shadowsocks "$proxy_string" "1602" @@ -130,11 +130,11 @@ start_service() { # Main proxy, Second VPN config_get_bool second_enable "second" "second_enable" "0" - config_get mode "second" "mode" "0" - if [ "$second_enable" -eq "1" ] && [ "$mode" = "vpn" ]; then + config_get second_mode "second" "second_mode" "0" + if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "vpn" ]; then log "VPN mode for seconds" log "You are using VPN mode, make sure you have installed all the necessary packages, configured, created the zone and forwarding." - config_get interface "second" "interface" "0" + config_get interface "second" "second_interface" "0" if [ -n "$interface" ]; then add_route_interface "$interface" "podkop2" else @@ -225,7 +225,7 @@ stop_service() { log "Stop sing-box" config_get mode_main "main" "mode" "0" - config_get mode_second "second" "mode" "0" + config_get mode_second "second" "second_mode" "0" if [ "$mode_main" = "proxy" ] || [ "$mode_second" = "proxy" ]; then /etc/init.d/sing-box stop @@ -396,7 +396,11 @@ add_set() { log "Create set $set_name" nft add chain inet PodkopTable mangle { type filter hook prerouting priority -150 \; policy accept \;} nft add set inet PodkopTable "$set_name" { type ipv4_addr\; flags interval\; auto-merge\; } - config_get mode "$connect" "mode" + if [ "$connect" = "main" ]; then + config_get mode "$connect" "mode" + else + config_get mode "$connect" "second_mode" + fi case "$mode" in "vpn") if ! nft list chain inet PodkopTable mangle | grep -q "ip daddr @"$set_name" meta mark set"; then @@ -537,8 +541,11 @@ lists_domains_download() { while true; do if curl -m 3 github.com; then curl -f $URL --output /tmp/dnsmasq.d/podkop-domains.lst - sed -i 's/fw4#vpn_domains/PodkopTable#podkop_domains/g' /tmp/dnsmasq.d/podkop-domains.lst - return 0 + if [ "$connect" = "second" ]; then + sed -i 's/fw4#vpn_domains/PodkopTable#podkop2_domains/g' /tmp/dnsmasq.d/podkop-domains.lst + else + sed -i 's/fw4#vpn_domains/PodkopTable#podkop_domains/g' /tmp/dnsmasq.d/podkop-domains.lst + fi return 0 else log "GitHub is not available. Check the internet availability [$count sec]" count=$((count + 1)) From 1ac1aa8f749a4ed543c39a022cec95982a9a061e Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Sun, 10 Nov 2024 16:11:03 +0300 Subject: [PATCH 05/14] ci: Update CI build configuration --- .github/workflows/build.yml | 2 +- podkop/files/etc/init.d/podkop | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ca07f73..2cd624a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - name: Create Docker container run: docker create --name podkop podkop:ci - + - name: Copy file from Docker container run: | docker cp podkop:/builder/bin/packages/x86_64/utilites/. ./bin/ diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index b0bdc4b..1abf9c2 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -249,7 +249,6 @@ service_triggers() { config_get update_interval "main" "update_interval" if [ -n "$update_interval" ]; then - remove_cron_job add_cron_job fi } From 56e93a3d5ff5b59458f67c6b6a9c611539c51b02 Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Sun, 10 Nov 2024 17:40:23 +0300 Subject: [PATCH 06/14] refactor: Add ucisection attribute to taboptions --- .../resources/view/podkop/podkop.js | 41 ++++++++++++++++--- podkop/files/etc/init.d/podkop | 3 +- 2 files changed, 38 insertions(+), 6 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 70404d4..dcfbf92 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 @@ -8,24 +8,26 @@ return view.extend({ async render() { var m, s, o; - m = new form.Map('podkop', _('Podkop configuration')); + m = new form.Map('podkop', _('Podkop configuration'), null, ['main', 'second']); s = m.section(form.TypedSection, 'main'); s.anonymous = true; - // Basic Settings tab o = s.tab('basic', _('Basic Settings')); o = s.taboption('basic', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing')); o.value('vpn', ('VPN')); o.value('proxy', ('Proxy')); + o.ucisection = 'main'; o = s.taboption('basic', form.TextValue, 'proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); o.depends('mode', 'proxy'); o.rows = 5; + o.ucisection = 'main'; o = s.taboption('basic', form.ListValue, 'interface', _('Network Interface'), _('Select network interface for VPN connection')); o.depends('mode', 'vpn'); + o.ucisection = 'main'; try { const devices = await network.getDevices(); @@ -48,6 +50,7 @@ return view.extend({ o = s.taboption('basic', form.Flag, 'domain_list_enabled', _('Predefined Domain Lists'), _('github.com/itdoginfo/allow-domains')); o.default = '0'; o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('basic', form.ListValue, 'domain_list', _('Domain List'), _('Select a predefined domain list')); o.placeholder = 'placeholder'; @@ -56,10 +59,12 @@ return view.extend({ o.value('ua', 'Ukraine'); o.depends('domain_list_enabled', '1'); o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('basic', form.Flag, 'subnets_list_enabled', _('Predefined Service Networks'), _('Enable routing for popular services like Twitter, Meta, and Discord')); o.default = '0'; o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('basic', form.DynamicList, 'subnets', _('Service Networks'), _('Select predefined service networks for routing')); o.placeholder = 'Service network list'; @@ -68,18 +73,20 @@ return view.extend({ o.value('discord', 'Discord(voice)'); o.depends('subnets_list_enabled', '1'); o.rmempty = false; + o.ucisection = 'main'; - // User Settings tab o = s.tab('custom', _('User Settings')); o = s.taboption('custom', form.Flag, 'custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing')); o.default = '0'; o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('custom', form.DynamicList, 'custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o.placeholder = 'Domains list'; o.depends('custom_domains_list_enabled', '1'); o.rmempty = false; + o.ucisection = 'main'; o.validate = function (section_id, value) { if (!value || value.length === 0) { return true; @@ -96,11 +103,13 @@ return view.extend({ o = s.taboption('custom', form.Flag, 'custom_download_domains_list_enabled', _('Remote Domain Lists'), _('Download and use domain lists from remote URLs')); o.default = '0'; o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('custom', form.DynamicList, 'custom_download_domains', _('Remote Domain URLs'), _('Enter full URLs starting with http:// or https://')); o.placeholder = 'URL'; o.depends('custom_download_domains_list_enabled', '1'); o.rmempty = false; + o.ucisection = 'main'; o.validate = function (section_id, value) { if (!value || value.length === 0) { return true; @@ -120,11 +129,13 @@ return view.extend({ o = s.taboption('custom', form.Flag, 'custom_subnets_list_enabled', _('User Subnet List'), _('Enable and manage your custom list of IP subnets for selective routing')); o.default = '0'; o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('custom', form.DynamicList, 'custom_subnets', _('User Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); o.placeholder = 'Subnets list'; o.depends('custom_subnets_list_enabled', '1'); o.rmempty = false; + o.ucisection = 'main'; o.validate = function (section_id, value) { if (!value || value.length === 0) { return true; @@ -157,11 +168,13 @@ return view.extend({ o = s.taboption('custom', form.Flag, 'custom_download_subnets_list_enabled', _('Remote Subnet Lists'), _('Download and use subnet lists from remote URLs')); o.default = '0'; o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('custom', form.DynamicList, 'custom_download_subnets', _('Remote Subnet URLs'), _('Enter full URLs starting with http:// or https://')); o.placeholder = 'URL'; o.depends('custom_download_subnets_list_enabled', '1'); o.rmempty = false; + o.ucisection = 'main'; o.validate = function (section_id, value) { if (!value || value.length === 0) { return true; @@ -178,26 +191,29 @@ return view.extend({ } }; - // Additional Settings tab o = s.tab('additional', _('Additional Settings')); o = s.taboption('additional', form.Flag, 'delist_domains_enabled', _('Domain Exclusions'), _('Exclude specific domains from routing rules')); o.default = '0'; o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('additional', form.DynamicList, 'delist_domains', _('Excluded Domains'), _('Domains to be excluded from routing')); o.placeholder = 'Delist domains'; o.depends('delist_domains_enabled', '1'); o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('additional', form.Flag, 'all_traffic_from_ip_enabled', _('Force Proxy IPs'), _('Specify local IP addresses whose traffic will always use the configured route')); o.default = '0'; o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('additional', form.DynamicList, 'all_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); o.placeholder = 'IP'; o.depends('all_traffic_from_ip_enabled', '1'); o.rmempty = false; + o.ucisection = 'main'; o.validate = function (section_id, value) { if (!value || value.length === 0) { return true; @@ -223,11 +239,13 @@ return view.extend({ o = s.taboption('additional', form.Flag, 'exclude_from_ip_enabled', _('Bypass Proxy IPs'), _('Specify local IP addresses that will never use the configured route')); o.default = '0'; o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('additional', form.DynamicList, 'exclude_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); o.placeholder = 'IP'; o.depends('exclude_from_ip_enabled', '1'); o.rmempty = false; + o.ucisection = 'main'; o.validate = function (section_id, value) { if (!value || value.length === 0) { return true; @@ -254,16 +272,19 @@ return view.extend({ o.default = '0'; o.depends('mode', 'proxy'); o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('additional', form.Flag, 'socks5', _('Mixed enable'), _('Browser port: 2080')); o.default = '0'; o.depends('mode', 'proxy'); o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('additional', form.Flag, 'exclude_ntp', _('Exclude NTP'), _('For issues with open connections sing-box')); o.default = '0'; o.depends('mode', 'proxy'); o.rmempty = false; + o.ucisection = 'main'; o = s.taboption('additional', form.ListValue, 'update_interval', _('List Update Frequency'), _('Select how often the lists will be updated')); o.value('0 */1 * * *', _('Every hour')); @@ -275,24 +296,28 @@ return view.extend({ o.value('0 4 * * 0', _('Once a week on Sunday at 04:00')); o.default = '0 4 * * *'; o.rmempty = false; + o.ucisection = 'main'; - // Secondary Route tab o = s.tab('second_settings', _('Secondary Route')); o = s.taboption('second_settings', form.Flag, 'second_enable', _('Secondary Route Enable'), _('Enable secondary routing configuration')); o.default = '0'; o.rmempty = false; + o.ucisection = 'second'; // This is correct o = s.taboption('second_settings', form.ListValue, 'second_mode', _('Connection Type'), _('Select between VPN and Proxy for secondary route')); o.value('vpn', ('VPN')); o.value('proxy', ('Proxy')); o.depends('second_enable', '1'); + o.ucisection = 'second'; // Changed to 'second' o = s.taboption('second_settings', form.Value, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); o.depends('second_mode', 'proxy'); + o.ucisection = 'second'; // Changed to 'second' o = s.taboption('second_settings', form.ListValue, 'second_interface', _('Network Interface'), _('Select network interface for VPN connection')); o.depends('second_mode', 'vpn'); + o.ucisection = 'second'; // Changed to 'second' try { const devices = await network.getDevices(); @@ -316,22 +341,26 @@ return view.extend({ o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); + o.ucisection = 'second'; // Changed to 'second' o = s.taboption('second_settings', form.ListValue, 'service_list', _('Service List'), _('Select predefined services for secondary routing')); o.placeholder = 'placeholder'; o.value('youtube', 'Youtube'); o.depends('domain_service_enabled', '1'); o.rmempty = false; + o.ucisection = 'second'; // Changed to 'second' o = s.taboption('second_settings', form.Flag, 'second_custom_domains_list_enabled', _('Secondary Domain List'), _('Configure custom domains for secondary routing path')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); + o.ucisection = 'second'; // Changed to 'second' o = s.taboption('second_settings', form.DynamicList, 'second_custom_domains', _('Secondary Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o.placeholder = 'Domains list'; o.depends('second_custom_domains_list_enabled', '1'); o.rmempty = false; + o.ucisection = 'second'; // Changed to 'second' o.validate = function (section_id, value) { if (!value || value.length === 0) { return true; @@ -349,11 +378,13 @@ return view.extend({ o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); + o.ucisection = 'second'; // Changed to 'second' o = s.taboption('second_settings', form.DynamicList, 'second_custom_subnets', _('Secondary Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); o.placeholder = 'Subnets list'; o.depends('second_custom_subnets_list_enabled', '1'); o.rmempty = false; + o.ucisection = 'second'; // Changed to 'second' o.validate = function (section_id, value) { if (!value || value.length === 0) { return true; diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 1abf9c2..4fd3f7c 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -544,7 +544,8 @@ lists_domains_download() { sed -i 's/fw4#vpn_domains/PodkopTable#podkop2_domains/g' /tmp/dnsmasq.d/podkop-domains.lst else sed -i 's/fw4#vpn_domains/PodkopTable#podkop_domains/g' /tmp/dnsmasq.d/podkop-domains.lst - fi return 0 + fi + return 0 else log "GitHub is not available. Check the internet availability [$count sec]" count=$((count + 1)) From d8a860fb2fc1cc6fd818b380209386e59f379fad Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Sun, 10 Nov 2024 18:39:14 +0300 Subject: [PATCH 07/14] fix: second proxy configuration fixing --- .../resources/view/podkop/podkop.js | 2 +- podkop/files/etc/init.d/podkop | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 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 dcfbf92..59fb75e 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 @@ -311,7 +311,7 @@ return view.extend({ o.depends('second_enable', '1'); o.ucisection = 'second'; // Changed to 'second' - o = s.taboption('second_settings', form.Value, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); + o = s.taboption('second_settings', form.TextValue, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); o.depends('second_mode', 'proxy'); o.ucisection = 'second'; // Changed to 'second' diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 4fd3f7c..3300594 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -315,13 +315,12 @@ list_update() { /etc/init.d/dnsmasq restart fi - config_get_bool custom_domains_list_enabled "second" "custom_domains_list_enabled" "0" - if [ "$custom_domains_list_enabled" -eq 1 ]; then + config_get_bool second_custom_domains_list_enabled "second" "second_custom_domains_list_enabled" "0" + if [ "$second_custom_domains_list_enabled" -eq 1 ]; then log "Adding a custom domains list. Second podkop" add_set "podkop2_domains" "second" rm -f /tmp/dnsmasq.d/podkop2-custom-domains.lst - config_list_foreach second custom_domains "list_delist_domains" - config_list_foreach second custom_domains "list_custom_domains_create" "podkop2" + config_list_foreach second second_custom_domains "list_custom_domains_create" "podkop2" dnsmasq_config_check podkop2-custom-domains.lst fi @@ -331,11 +330,11 @@ list_update() { add_set "podkop2_domains" "second" config_get service_list second "service_list" lists_services_download "$service_list" - config_list_foreach second custom_domains "list_delist_domains" + config_list_foreach second second_custom_domains "list_delist_domains" dnsmasq_config_check podkop2-domains.lst fi - if [ "$custom_domains_list_enabled" -eq 1 ] || [ "$domain_service_enabled" -eq 1 ]; then + if [ "$second_custom_domains_list_enabled" -eq 1 ] || [ "$domain_service_enabled" -eq 1 ]; then /etc/init.d/dnsmasq restart fi @@ -362,11 +361,11 @@ list_update() { config_list_foreach main custom_subnets "list_custom_subnets_create" "podkop" fi - config_get_bool custom_subnets_list_enabled "second" "custom_subnets_list_enabled" "0" - if [ "$custom_subnets_list_enabled" -eq 1 ]; then + config_get_bool second_custom_subnets_list_enabled "second" "second_custom_subnets_list_enabled" "0" + if [ "$second_custom_subnets_list_enabled" -eq 1 ]; then log "Adding a custom subnets list. Second" add_set "podkop2_subnets" "second" - config_list_foreach second custom_subnets "list_custom_subnets_create" "podkop2" + config_list_foreach second second_custom_subnets "list_custom_subnets_create" "podkop2" fi } @@ -852,7 +851,6 @@ sing_box_config_vless() { end)' "$template_config" >/etc/sing-box/config.json } -# make one function for full and outbound only sing_box_config_outbound_shadowsocks() { local STRING="$1" local outbound="$2" From b3678323ca2059ee84a70c71e4640c70c36fcb60 Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Mon, 11 Nov 2024 18:28:58 +0300 Subject: [PATCH 08/14] feat: Consolidate settings into basic tab --- .../resources/view/podkop/podkop.js | 59 +- luci-app-podkop/po/ru/podkop.po | 20 +- luci-app-podkop/po/templates/podkop.pot | 60 +- podkop/files/etc/init.d/podkop | 1376 +++++------------ 4 files changed, 496 insertions(+), 1019 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 59fb75e..9cafcb1 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 @@ -13,6 +13,7 @@ return view.extend({ s = m.section(form.TypedSection, 'main'); s.anonymous = true; + // Basic Settings Tab o = s.tab('basic', _('Basic Settings')); o = s.taboption('basic', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing')); @@ -75,14 +76,12 @@ return view.extend({ o.rmempty = false; o.ucisection = 'main'; - o = s.tab('custom', _('User Settings')); - - o = s.taboption('custom', form.Flag, 'custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing')); + o = s.taboption('basic', form.Flag, 'custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing')); o.default = '0'; o.rmempty = false; o.ucisection = 'main'; - o = s.taboption('custom', form.DynamicList, 'custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); + o = s.taboption('basic', form.DynamicList, 'custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o.placeholder = 'Domains list'; o.depends('custom_domains_list_enabled', '1'); o.rmempty = false; @@ -100,12 +99,12 @@ return view.extend({ return true; }; - o = s.taboption('custom', form.Flag, 'custom_download_domains_list_enabled', _('Remote Domain Lists'), _('Download and use domain lists from remote URLs')); + o = s.taboption('basic', form.Flag, 'custom_download_domains_list_enabled', _('Remote Domain Lists'), _('Download and use domain lists from remote URLs')); o.default = '0'; o.rmempty = false; o.ucisection = 'main'; - o = s.taboption('custom', form.DynamicList, 'custom_download_domains', _('Remote Domain URLs'), _('Enter full URLs starting with http:// or https://')); + o = s.taboption('basic', form.DynamicList, 'custom_download_domains', _('Remote Domain URLs'), _('Enter full URLs starting with http:// or https://')); o.placeholder = 'URL'; o.depends('custom_download_domains_list_enabled', '1'); o.rmempty = false; @@ -126,12 +125,12 @@ return view.extend({ } }; - o = s.taboption('custom', form.Flag, 'custom_subnets_list_enabled', _('User Subnet List'), _('Enable and manage your custom list of IP subnets for selective routing')); + o = s.taboption('basic', form.Flag, 'custom_subnets_list_enabled', _('User Subnet List'), _('Enable and manage your custom list of IP subnets for selective routing')); o.default = '0'; o.rmempty = false; o.ucisection = 'main'; - o = s.taboption('custom', form.DynamicList, 'custom_subnets', _('User Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); + o = s.taboption('basic', form.DynamicList, 'custom_subnets', _('User Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); o.placeholder = 'Subnets list'; o.depends('custom_subnets_list_enabled', '1'); o.rmempty = false; @@ -165,12 +164,12 @@ return view.extend({ return true; }; - o = s.taboption('custom', form.Flag, 'custom_download_subnets_list_enabled', _('Remote Subnet Lists'), _('Download and use subnet lists from remote URLs')); + o = s.taboption('basic', form.Flag, 'custom_download_subnets_list_enabled', _('Remote Subnet Lists'), _('Download and use subnet lists from remote URLs')); o.default = '0'; o.rmempty = false; o.ucisection = 'main'; - o = s.taboption('custom', form.DynamicList, 'custom_download_subnets', _('Remote Subnet URLs'), _('Enter full URLs starting with http:// or https://')); + o = s.taboption('basic', form.DynamicList, 'custom_download_subnets', _('Remote Subnet URLs'), _('Enter full URLs starting with http:// or https://')); o.placeholder = 'URL'; o.depends('custom_download_subnets_list_enabled', '1'); o.rmempty = false; @@ -191,25 +190,23 @@ return view.extend({ } }; - o = s.tab('additional', _('Additional Settings')); - - o = s.taboption('additional', form.Flag, 'delist_domains_enabled', _('Domain Exclusions'), _('Exclude specific domains from routing rules')); + o = s.taboption('basic', form.Flag, 'delist_domains_enabled', _('Domain Exclusions'), _('Exclude specific domains from routing rules')); o.default = '0'; o.rmempty = false; o.ucisection = 'main'; - o = s.taboption('additional', form.DynamicList, 'delist_domains', _('Excluded Domains'), _('Domains to be excluded from routing')); + o = s.taboption('basic', form.DynamicList, 'delist_domains', _('Excluded Domains'), _('Domains to be excluded from routing')); o.placeholder = 'Delist domains'; o.depends('delist_domains_enabled', '1'); o.rmempty = false; o.ucisection = 'main'; - o = s.taboption('additional', form.Flag, 'all_traffic_from_ip_enabled', _('Force Proxy IPs'), _('Specify local IP addresses whose traffic will always use the configured route')); + o = s.taboption('basic', form.Flag, 'all_traffic_from_ip_enabled', _('Force Proxy IPs'), _('Specify local IP addresses whose traffic will always use the configured route')); o.default = '0'; o.rmempty = false; o.ucisection = 'main'; - o = s.taboption('additional', form.DynamicList, 'all_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); + o = s.taboption('basic', form.DynamicList, 'all_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); o.placeholder = 'IP'; o.depends('all_traffic_from_ip_enabled', '1'); o.rmempty = false; @@ -236,12 +233,12 @@ return view.extend({ return true; }; - o = s.taboption('additional', form.Flag, 'exclude_from_ip_enabled', _('Bypass Proxy IPs'), _('Specify local IP addresses that will never use the configured route')); + o = s.taboption('basic', form.Flag, 'exclude_from_ip_enabled', _('Bypass Proxy IPs'), _('Specify local IP addresses that will never use the configured route')); o.default = '0'; o.rmempty = false; o.ucisection = 'main'; - o = s.taboption('additional', form.DynamicList, 'exclude_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); + o = s.taboption('basic', form.DynamicList, 'exclude_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); o.placeholder = 'IP'; o.depends('exclude_from_ip_enabled', '1'); o.rmempty = false; @@ -268,6 +265,9 @@ return view.extend({ return true; }; + // Additional Settings Tab + o = s.tab('additional', _('Additional Settings')); + o = s.taboption('additional', form.Flag, 'yacd', _('Yacd enable'), _('http://openwrt.lan:9090/ui')); o.default = '0'; o.depends('mode', 'proxy'); @@ -298,26 +298,27 @@ return view.extend({ o.rmempty = false; o.ucisection = 'main'; - o = s.tab('second_settings', _('Secondary Route')); + // Secondary Settings Tab + o = s.tab('second_settings', _('Secondary Settings')); o = s.taboption('second_settings', form.Flag, 'second_enable', _('Secondary Route Enable'), _('Enable secondary routing configuration')); o.default = '0'; o.rmempty = false; - o.ucisection = 'second'; // This is correct + o.ucisection = 'second'; o = s.taboption('second_settings', form.ListValue, 'second_mode', _('Connection Type'), _('Select between VPN and Proxy for secondary route')); o.value('vpn', ('VPN')); o.value('proxy', ('Proxy')); o.depends('second_enable', '1'); - o.ucisection = 'second'; // Changed to 'second' + o.ucisection = 'second'; o = s.taboption('second_settings', form.TextValue, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); o.depends('second_mode', 'proxy'); - o.ucisection = 'second'; // Changed to 'second' + o.ucisection = 'second'; o = s.taboption('second_settings', form.ListValue, 'second_interface', _('Network Interface'), _('Select network interface for VPN connection')); o.depends('second_mode', 'vpn'); - o.ucisection = 'second'; // Changed to 'second' + o.ucisection = 'second'; try { const devices = await network.getDevices(); @@ -341,26 +342,26 @@ return view.extend({ o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); - o.ucisection = 'second'; // Changed to 'second' + o.ucisection = 'second'; o = s.taboption('second_settings', form.ListValue, 'service_list', _('Service List'), _('Select predefined services for secondary routing')); o.placeholder = 'placeholder'; o.value('youtube', 'Youtube'); o.depends('domain_service_enabled', '1'); o.rmempty = false; - o.ucisection = 'second'; // Changed to 'second' + o.ucisection = 'second'; o = s.taboption('second_settings', form.Flag, 'second_custom_domains_list_enabled', _('Secondary Domain List'), _('Configure custom domains for secondary routing path')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); - o.ucisection = 'second'; // Changed to 'second' + o.ucisection = 'second'; o = s.taboption('second_settings', form.DynamicList, 'second_custom_domains', _('Secondary Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o.placeholder = 'Domains list'; o.depends('second_custom_domains_list_enabled', '1'); o.rmempty = false; - o.ucisection = 'second'; // Changed to 'second' + o.ucisection = 'second'; o.validate = function (section_id, value) { if (!value || value.length === 0) { return true; @@ -378,13 +379,13 @@ return view.extend({ o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); - o.ucisection = 'second'; // Changed to 'second' + o.ucisection = 'second'; o = s.taboption('second_settings', form.DynamicList, 'second_custom_subnets', _('Secondary Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); o.placeholder = 'Subnets list'; o.depends('second_custom_subnets_list_enabled', '1'); o.rmempty = false; - o.ucisection = 'second'; // Changed to 'second' + o.ucisection = 'second'; o.validate = function (section_id, value) { if (!value || value.length === 0) { return true; diff --git a/luci-app-podkop/po/ru/podkop.po b/luci-app-podkop/po/ru/podkop.po index 1d74662..814737f 100644 --- a/luci-app-podkop/po/ru/podkop.po +++ b/luci-app-podkop/po/ru/podkop.po @@ -4,8 +4,8 @@ msgstr "Content-Type: text/plain; charset=UTF-8" msgid "Podkop configuration" msgstr "Настройка Podkop" -msgid "Basic Settings" -msgstr "Основные настройки" +msgid "Main Config" +msgstr "Основная конфигурация" msgid "Connection Type" msgstr "Тип подключения" @@ -46,8 +46,11 @@ msgstr "Сети сервисов" msgid "Select predefined service networks for routing" msgstr "Выберите предустановленные сети сервисов для маршрутизации" -msgid "User Settings" -msgstr "Пользовательские настройки" +msgid "Add-ons" +msgstr "Дополнения" + +msgid "Alternative Config" +msgstr "Альтернативная конфигурация" msgid "User Domain List" msgstr "Пользовательский список доменов" @@ -58,9 +61,6 @@ msgstr "Включить и управлять пользовательским msgid "User Domains" msgstr "Пользовательские домены" -msgid "Enter domain names without protocols (example: sub.example.com or example.com)" -msgstr "Введите имена доменов без протоколов (пример: sub.example.com или example.com)" - msgid "Remote Domain Lists" msgstr "Удаленные списки доменов" @@ -94,9 +94,6 @@ msgstr "Загрузка и использование списков подсе msgid "Remote Subnet URLs" msgstr "URL удаленных подсетей" -msgid "Additional Settings" -msgstr "Дополнительные настройки" - msgid "Domain Exclusions" msgstr "Исключения доменов" @@ -172,9 +169,6 @@ msgstr "Исключить NTP" msgid "For issues with open connections sing-box" msgstr "Для проблем с открытыми соединениями sing-box" -msgid "Secondary Route" -msgstr "Вторичный маршрут" - msgid "Secondary Route Enable" msgstr "Включить вторичный маршрут" diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot index 5b3bfa5..34938fe 100644 --- a/luci-app-podkop/po/templates/podkop.pot +++ b/luci-app-podkop/po/templates/podkop.pot @@ -6,7 +6,7 @@ msgid "Podkop configuration" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Basic Settings" +msgid "Main Config" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 @@ -62,73 +62,97 @@ msgid "Select predefined service networks for routing" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "User Settings" +msgid "Add-ons" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Secondary Route" +msgid "Alternative Config" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Secondary Route Enable" +msgid "User Domain List" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Enable secondary routing configuration" +msgid "Enable and manage your custom list of domains for selective routing" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Service Domain List Enable" +msgid "User Domains" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Enable predefined service domain lists for secondary routing" +msgid "Remote Domain Lists" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Service List" +msgid "Download and use domain lists from remote URLs" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Select predefined services for secondary routing" +msgid "Remote Domain URLs" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Secondary Domain List" +msgid "Enter full URLs starting with http:// or https://" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Configure custom domains for secondary routing path" +msgid "User Subnet List" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Secondary Domains" +msgid "Enable and manage your custom list of IP subnets for selective routing" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Secondary Subnet List" +msgid "User Subnets" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Configure custom subnets for secondary routing path" +msgid "Remote Subnet Lists" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Secondary Subnets" +msgid "Download and use subnet lists from remote URLs" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Additional Settings" +msgid "Remote Subnet URLs" msgstr "" #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Domain Exclusions" msgstr "" +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Exclude specific domains from routing rules" +msgstr "" + #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Excluded Domains" msgstr "" +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Force Proxy IPs" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Specify local IP addresses whose traffic will always use the configured route" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Local IPs" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Bypass Proxy IPs" +msgstr "" + +#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Specify local IP addresses that will never use the configured route" +msgstr "" + #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "List Update Frequency" msgstr "" @@ -165,10 +189,6 @@ msgstr "" msgid "Once a week on Sunday at 04:00" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Once a week on Monday at 04:00" -msgstr "" - #: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Yacd enable" msgstr "" diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 3300594..4ba0b96 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -1,958 +1,420 @@ -#!/bin/sh /etc/rc.common - -START=99 -USE_PROCD=1 - -script=$(readlink "$initscript") -NAME="$(basename ${script:-$initscript})" -config_load "$NAME" - -EXTRA_COMMANDS="list_update add_route_interface" -EXTRA_HELP=" list_update Updating domain and subnet lists - add_route_interface Adding route for interface - sing_box_config_vless For test vless string" - -config_get update_interval "main" "update_interval" "0 4 * * *" -cron_job="${update_interval} /etc/init.d/podkop list_update" - -start_service() { - log "Start podkop" - - dnsmasqfull - routing_table_create - add_mark - - config_get mode "main" "mode" - case "$mode" in - "vpn") - log "VPN mode" - log "You are using VPN mode, make sure you have installed all the necessary packages, configured, created the zone and forwarding." - config_get interface "main" "interface" "0" - if [ -n "$interface" ]; then - add_route_interface "$interface" "podkop" - else - log "Interface undefined" - fi - - config_get_bool second_enable "second" "second_enable" "0" - config_get second_mode "second" "second_mode" "0" - if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "proxy" ]; then - config_get proxy_string "second" "second_proxy_string" - if [[ "$proxy_string" =~ ^ss:// ]]; then - sing_box_config_shadowsocks "$proxy_string" "1603" - elif [[ "$proxy_string" =~ ^vless:// ]]; then - sing_box_config_vless "$proxy_string" "1603" - else - log "Unsupported proxy type: $proxy_string" - return - fi - add_route_tproxy podkop2 - sing_box_config_check - sing_box_uci - /etc/init.d/sing-box restart - /etc/init.d/sing-box enable - fi - - if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "vpn" ]; then - log "VPN mode for second" - config_get interface "second" "second_interface" "0" - if [ -n "$interface" ]; then - add_route_interface "$interface" "podkop2" - else - log "Interface undefined" - fi - fi - ;; - "proxy") - log "Proxy mode" - if ! command -v sing-box >/dev/null 2>&1; then - log "Sing-box isn't installed. Proxy mode works with sing-box" - return - fi - - # Main - proxy, Second - proxy - config_get_bool second_enable "second" "second_enable" "0" - config_get second_mode "second" "second_mode" "0" - if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "proxy" ]; then - log "Two proxy enable" - outbound_main=$(mktemp) - outbound_second=$(mktemp) - - config_get proxy_string main "proxy_string" - if [[ "$proxy_string" =~ ^ss:// ]]; then - sing_box_config_outbound_shadowsocks "$proxy_string" "$outbound_main" main - elif [[ "$proxy_string" =~ ^vless:// ]]; then - sing_box_config_outbound_vless "$proxy_string" "$outbound_main" main - else - log "Unsupported proxy type: $proxy_string" - return - fi - - config_get proxy_string "second" "second_proxy_string" - if [[ "$proxy_string" =~ ^ss:// ]]; then - sing_box_config_outbound_shadowsocks "$proxy_string" "$outbound_second" second - elif [[ "$proxy_string" =~ ^vless:// ]]; then - sing_box_config_outbound_vless "$proxy_string" "$outbound_second" second - else - log "Unsupported proxy type: $proxy_string" - return - fi - - jq --argjson outbounds "$(jq -s '{"outbounds": (.[0].outbounds + .[1].outbounds)}' "$outbound_main" "$outbound_second")" \ - '.outbounds += $outbounds.outbounds' /etc/podkop/sing-box-two-proxy-template.json >/etc/sing-box/config.json - - rm -f "$outbound_main" "$outbound_second" - - add_route_tproxy podkop - add_route_tproxy podkop2 - fi - - # Main proxy, second disable/vpn - config_get_bool second_enable "second" "second_enable" "0" - config_get second_mode "second" "second_mode" "0" - if [ "$second_enable" -eq "0" ] || [ "$second_mode" = "vpn" ]; then - config_get proxy_string main "proxy_string" - if [[ "$proxy_string" =~ ^ss:// ]]; then - sing_box_config_shadowsocks "$proxy_string" "1602" - elif [[ "$proxy_string" =~ ^vless:// ]]; then - sing_box_config_vless "$proxy_string" "1602" - else - log "Unsupported proxy type: $proxy_string" - return - fi - add_route_tproxy podkop - fi - - sing_box_config_check - sing_box_uci - /etc/init.d/sing-box restart - /etc/init.d/sing-box enable - - # Main proxy, Second VPN - config_get_bool second_enable "second" "second_enable" "0" - config_get second_mode "second" "second_mode" "0" - if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "vpn" ]; then - log "VPN mode for seconds" - log "You are using VPN mode, make sure you have installed all the necessary packages, configured, created the zone and forwarding." - config_get interface "second" "second_interface" "0" - if [ -n "$interface" ]; then - add_route_interface "$interface" "podkop2" - else - log "Interface undefined" - fi - fi - ;; - *) - log "Requires *vpn* or *proxy* value" - return - ;; - esac - - list_update - - if [ "$domain_list_enabled" -eq 1 ] || [ "$subnets_list_enabled" -eq 1 ]; then - add_cron_job - fi - - config_get_bool all_traffic_from_ip_enabled "main" "all_traffic_from_ip_enabled" "0" - if [ "$all_traffic_from_ip_enabled" -eq 1 ]; then - log "Adding an IP to redirect all traffic" - config_list_foreach main all_traffic_ip list_all_traffic_from_ip - fi - - config_get_bool exclude_from_ip_enabled "main" "exclude_from_ip_enabled" "0" - if [ "$exclude_from_ip_enabled" -eq 1 ]; then - log "Adding an IP for exclusion" - config_list_foreach main exclude_traffic_ip list_exclude_traffic_from_ip - fi - - config_get_bool yacd "main" "yacd" "0" - if [ "$yacd" -eq 1 ]; then - log "Yacd enable" - jq '.experimental.clash_api = { - "external_ui": "ui", - "external_controller": "0.0.0.0:9090" - }' /etc/sing-box/config.json >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json /etc/sing-box/config.json - /etc/init.d/sing-box restart - fi - - config_get_bool socks5 "main" "socks5" "0" - if [ "$socks5" -eq 1 ]; then - log "Socks5 local enable port 2080" - jq '.inbounds += [{ - "type": "mixed", - "listen": "0.0.0.0", - "listen_port": 2080, - "set_system_proxy": false - }]' /etc/sing-box/config.json >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json /etc/sing-box/config.json - /etc/init.d/sing-box restart - fi - - config_get_bool exclude_ntp "main" "exclude_ntp" "0" - if [ "$exclude_ntp" -eq 1 ]; then - log "NTP traffic exclude for proxy" - nft insert rule inet PodkopTable mangle udp dport 123 return - fi -} - -stop_service() { - log "Stopping the podkop" - rm -f /tmp/dnsmasq.d/podkop* - remove_cron_job - - log "Flush nft" - if nft list table inet PodkopTable >/dev/null 2>&1; then - nft delete table inet PodkopTable - fi - - log "Flush ip rule" - if ip rule list | grep -q "podkop"; then - ip rule del fwmark 0x105 table podkop priority 105 - fi - - if ip rule list | grep -q "podkop2"; then - ip rule del fwmark 0x106 table podkop2 priority 106 - fi - - log "Flush ip route" - if ip route list table podkop; then - ip route flush table podkop - fi - - if ip route list table podkop2; then - ip route flush table podkop2 - fi - - log "Stop sing-box" - config_get mode_main "main" "mode" "0" - config_get mode_second "second" "second_mode" "0" - - if [ "$mode_main" = "proxy" ] || [ "$mode_second" = "proxy" ]; then - /etc/init.d/sing-box stop - /etc/init.d/sing-box disable - fi -} - -restart_service() { - stop - start -} - -reload_service() { - stop - start -} - -service_triggers() { - log "service_triggers start" - procd_add_config_trigger "config.change" "$NAME" "$initscript" reload 'on_config_change' - - config_get update_interval "main" "update_interval" - if [ -n "$update_interval" ]; then - add_cron_job - fi -} - -log() { - local message="$1" - local timestamp=$(date +"%Y-%m-%d %H:%M:%S") - local CYAN="\033[0;36m" - local GREEN="\033[0;32m" - local RESET="\033[0m" - - echo -e "${CYAN}[$timestamp]${RESET} ${GREEN}$message${RESET}" - logger -t "podkop" "$timestamp $message" -} - -add_cron_job() { - remove_cron_job - crontab -l | { - cat - echo "$cron_job" - } | crontab - - log "The cron job has been created: $cron_job" -} - -remove_cron_job() { - (crontab -l | grep -v "/etc/init.d/podkop list_update") | crontab - - log "The cron job removed" -} - -list_update() { - config_get_bool domain_list_enabled "main" "domain_list_enabled" "0" - if [ "$domain_list_enabled" -eq 1 ]; then - log "Adding a common domains list" - add_set "podkop_domains" "main" - config_get domain_list main "domain_list" - lists_domains_download "$domain_list" - dnsmasq_config_check podkop-domains.lst - fi - - config_get_bool custom_domains_list_enabled "main" "custom_domains_list_enabled" "0" - if [ "$custom_domains_list_enabled" -eq 1 ]; then - log "Adding a custom domains list" - add_set "podkop_domains" "main" - rm -f /tmp/dnsmasq.d/podkop-custom-domains.lst - config_list_foreach main custom_domains "list_custom_domains_create" "podkop" - dnsmasq_config_check podkop-custom-domains.lst - fi - - config_get_bool custom_download_domains_list_enabled "main" "custom_download_domains_list_enabled" "0" - if [ "$custom_download_domains_list_enabled" -eq 1 ]; then - log "Adding a custom domains list from URL" - add_set "podkop_domains" "main" - config_list_foreach main custom_download_domains "list_custom_download_domains_create" "podkop" - fi - - config_get_bool delist_domains_enabled "main" "delist_domains_enabled" "0" - if [ "$delist_domains_enabled" -eq 1 ] && [ "$domain_list_enabled" -eq 1 ]; then - log "Exclude domains from the common list" - config_list_foreach main delist_domains "list_delist_domains" - dnsmasq_config_check podkop-domains.lst - fi - - if [ "$domain_list_enabled" -eq 1 ] || [ "$custom_domains_list_enabled" -eq 1 ]; then - /etc/init.d/dnsmasq restart - fi - - config_get_bool second_custom_domains_list_enabled "second" "second_custom_domains_list_enabled" "0" - if [ "$second_custom_domains_list_enabled" -eq 1 ]; then - log "Adding a custom domains list. Second podkop" - add_set "podkop2_domains" "second" - rm -f /tmp/dnsmasq.d/podkop2-custom-domains.lst - config_list_foreach second second_custom_domains "list_custom_domains_create" "podkop2" - dnsmasq_config_check podkop2-custom-domains.lst - fi - - config_get_bool domain_service_enabled "second" "domain_service_enabled" "0" - if [ "$domain_service_enabled" -eq 1 ]; then - log "Adding a service for podkop2" - add_set "podkop2_domains" "second" - config_get service_list second "service_list" - lists_services_download "$service_list" - config_list_foreach second second_custom_domains "list_delist_domains" - dnsmasq_config_check podkop2-domains.lst - fi - - if [ "$second_custom_domains_list_enabled" -eq 1 ] || [ "$domain_service_enabled" -eq 1 ]; then - /etc/init.d/dnsmasq restart - fi - - config_get_bool subnets_list_enabled "main" "subnets_list_enabled" "0" - if [ "$subnets_list_enabled" -eq 1 ]; then - log "Adding a subnets from list" - mkdir -p /tmp/podkop - add_set "podkop_subnets" "main" - config_list_foreach main subnets "list_subnets_download" - fi - - config_get_bool custom_download_subnets_list_enabled "main" "custom_download_subnets_list_enabled" "0" - if [ "$custom_download_subnets_list_enabled" -eq 1 ]; then - log "Adding a subnets from URL" - mkdir -p /tmp/podkop - add_set "podkop_subnets" "main" - config_list_foreach main custom_download_subnets "list_subnets_download" - fi - - config_get_bool custom_subnets_list_enabled "main" "custom_subnets_list_enabled" "0" - if [ "$custom_subnets_list_enabled" -eq 1 ]; then - log "Adding a custom subnets list" - add_set "podkop_subnets" "main" - config_list_foreach main custom_subnets "list_custom_subnets_create" "podkop" - fi - - config_get_bool second_custom_subnets_list_enabled "second" "second_custom_subnets_list_enabled" "0" - if [ "$second_custom_subnets_list_enabled" -eq 1 ]; then - log "Adding a custom subnets list. Second" - add_set "podkop2_subnets" "second" - config_list_foreach second second_custom_subnets "list_custom_subnets_create" "podkop2" - fi -} - -dnsmasqfull() { - if /usr/sbin/dnsmasq -v | grep -q "no-nftset"; then - log "Dnsmasq-full is not installed. Future: link only" - log "Use script or:" - log "cd /tmp/ && /bin/opkg download dnsmasq-full && /bin/opkg remove dnsmasq && /bin/opkg install dnsmasq-full --cache /tmp/ && cp /etc/config/dhcp /etc/config/dhcp-old && mv /etc/config/dhcp-opkg /etc/config/dhcp" - return - fi -} - -routing_table_create() { - grep -q "105 podkop" /etc/iproute2/rt_tables || echo '105 podkop' >>/etc/iproute2/rt_tables - config_get_bool second_enable "second" "second_enable" "0" - if [ "$second_enable" -eq 1 ]; then - grep -q "106 podkop2" /etc/iproute2/rt_tables || echo '106 podkop2' >>/etc/iproute2/rt_tables - fi -} - -add_set() { - local set_name="$1" - local connect="$2" - - nft add table inet PodkopTable - log "Create set $set_name" - nft add chain inet PodkopTable mangle { type filter hook prerouting priority -150 \; policy accept \;} - nft add set inet PodkopTable "$set_name" { type ipv4_addr\; flags interval\; auto-merge\; } - if [ "$connect" = "main" ]; then - config_get mode "$connect" "mode" - else - config_get mode "$connect" "second_mode" - fi - case "$mode" in - "vpn") - if ! nft list chain inet PodkopTable mangle | grep -q "ip daddr @"$set_name" meta mark set"; then - if [ "$connect" = "main" ]; then - nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta mark set 0x105 counter - elif [ "$connect" = "second" ]; then - nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta mark set 0x106 counter - fi - fi - ;; - - "proxy") - nft add chain inet PodkopTable proxy { type filter hook prerouting priority -100 \; } - if nft list table inet PodkopTable | grep -q "ip daddr @"$set_name" meta l4proto"; then - log "Nft rule tproxy exists" - else - log "Added nft rule tproxy" - if [ "$connect" = "main" ]; then - nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta l4proto tcp meta mark set 0x105 counter - nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta l4proto udp meta mark set 0x105 counter - if ! ( nft list table inet PodkopTable | grep -q "meta mark 0x00000105 meta l4proto tcp tproxy" ); then - nft add rule inet PodkopTable proxy iifname "br-lan" meta mark 0x105 meta l4proto tcp tproxy ip to :1602 counter - nft add rule inet PodkopTable proxy iifname "br-lan" meta mark 0x105 meta l4proto udp tproxy ip to :1602 counter - fi - elif [ "$connect" = "second" ]; then - nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta l4proto tcp meta mark set 0x106 counter - nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta l4proto udp meta mark set 0x106 counter - if ! ( nft list table inet PodkopTable | grep -q "meta mark 0x00000106 meta l4proto tcp tproxy" ); then - nft add rule inet PodkopTable proxy iifname "br-lan" meta mark 0x106 meta l4proto tcp tproxy ip to :1603 counter - nft add rule inet PodkopTable proxy iifname "br-lan" meta mark 0x106 meta l4proto udp tproxy ip to :1603 counter - fi - fi - fi - ;; - - *) - log "Requires *vpn* or *proxy* value" - return - ;; - esac -} - -add_route_interface() { - local interface="$1" - local table="$2" - local retry_count_route=0 - local max_retries=10 - - if ! ip link show "$interface" >/dev/null 2>&1; then - log "Interface "$interface" undetected, wait 10 sec..." - sleep 10 - - if ! ip link show "$interface" >/dev/null 2>&1; then - log "Interface "$interface" undetected. exit" - return - fi - fi - - if ! ip link show "$interface" >/dev/null 2>&1; then - log "Interface "$interface" does not exist, not possible to create a route" - return - fi - - if ip route show table $table | grep -q "^default dev"; then - log "Route for "$interface" exists" - return 0 - fi - - log "Added route for "$interface"" - while [ $retry_count_route -lt $max_retries ]; do - if ip route add table $table default dev "$interface" 2>&1 | grep -q "Network is down"; then - log "Attempt $retry_count_route: Interface "$interface" is down, retrying in 3 seconds..." - sleep 3 - retry_count_route=$((retry_count_route + 1)) - else - log "Route for "$interface" added" - return 0 - fi - done - - log "The maximum number of attempts has been exceeded. Failed to add a route." - return -} - -add_route_tproxy() { - local table=$1 - if ! ip route list table $table | grep -q "local default dev lo scope host"; then - log "Added route for tproxy" - ip route add local 0.0.0.0/0 dev lo table $table - else - log "Route for tproxy exists" - fi -} - -add_mark() { - if ! ip rule list | grep -q "from all fwmark 0x105 lookup podkop"; then - log "Create marking rule" - ip -4 rule add fwmark 0x105 table podkop priority 105 - else - log "Marking rule exist" - fi - - config_get_bool second_enable "second" "second_enable" "0" - if [ "$second_enable" -eq 1 ]; then - if ! ip rule list | grep -q "from all fwmark 0x106 lookup podkop2"; then - log "Create marking rule for podkop second" - ip -4 rule add fwmark 0x106 table podkop2 priority 106 - else - log "Podkop second marking rule exist" - fi - fi -} - -lists_domains_download() { - local URL="$1" - - RU_INSIDE_DOMAINS=https://raw.githubusercontent.com/itdoginfo/allow-domains/main/Russia/inside-dnsmasq-nfset.lst - RU_OUTSIDE_DOMAINS=https://raw.githubusercontent.com/itdoginfo/allow-domains/main/Russia/outside-dnsmasq-nfset.lst - UA_DOMAINS=https://raw.githubusercontent.com/itdoginfo/allow-domains/main/Ukraine/inside-dnsmasq-nfset.lst - - case "$URL" in - "ru_inside") - URL=$RU_INSIDE_DOMAINS - ;; - "ru_outside") - URL=$RU_OUTSIDE_DOMAINS - ;; - "ua") - URL=$UA_DOMAINS - ;; - *) - log "Unidentified list of domains" - return - ;; - esac - - count=0 - while true; do - if curl -m 3 github.com; then - curl -f $URL --output /tmp/dnsmasq.d/podkop-domains.lst - if [ "$connect" = "second" ]; then - sed -i 's/fw4#vpn_domains/PodkopTable#podkop2_domains/g' /tmp/dnsmasq.d/podkop-domains.lst - else - sed -i 's/fw4#vpn_domains/PodkopTable#podkop_domains/g' /tmp/dnsmasq.d/podkop-domains.lst - fi - return 0 - else - log "GitHub is not available. Check the internet availability [$count sec]" - count=$((count + 1)) - fi - - if [ $count -lt 30 ]; then - sleep_interval=1 - elif [ $count -ge 30 ] && [ $count -lt 60 ]; then - sleep_interval=5 - elif [ $count -ge 60 ] && [ $count -lt 90 ]; then - sleep_interval=10 - else - sleep_interval=30 - fi - - sleep $sleep_interval - done -} - -lists_services_download() { - local URL="$1" - - YOUTUBE=https://raw.githubusercontent.com/itdoginfo/allow-domains/refs/heads/main/Services/youtube.lst - - case "$URL" in - "youtube") - URL=$YOUTUBE - ;; - *) - log "Unidentified list of domains" - return - ;; - esac - - count=0 - while true; do - if curl -m 3 github.com; then - curl -f $URL --output /tmp/dnsmasq.d/podkop2-domains.lst - delist_downloaded_domains - sed -i 's/.*/nftset=\/&\/4#inet#PodkopTable#podkop2_domains/g' /tmp/dnsmasq.d/podkop2-domains.lst - return 0 - else - log "GitHub is not available. Check the internet availability [$count sec]" - count=$((count + 1)) - fi - - if [ $count -lt 30 ]; then - sleep_interval=1 - elif [ $count -ge 30 ] && [ $count -lt 60 ]; then - sleep_interval=5 - elif [ $count -ge 60 ] && [ $count -lt 90 ]; then - sleep_interval=10 - else - sleep_interval=30 - fi - - sleep $sleep_interval - done -} - -list_subnets_download() { - TWITTER_SUBNETS=https://raw.githubusercontent.com/itdoginfo/allow-domains/main/Subnets/IPv4/Twitter.lst - META_SUBNETS=https://raw.githubusercontent.com/itdoginfo/allow-domains/main/Subnets/IPv4/Meta.lst - DISCORD_SUBNETS=https://raw.githubusercontent.com/itdoginfo/allow-domains/refs/heads/main/Subnets/IPv4/Discord.lst - local URL="$1" - - case "$URL" in - "twitter") - URL=$TWITTER_SUBNETS - ;; - "meta") - URL=$META_SUBNETS - ;; - "discord") - URL=$DISCORD_SUBNETS - ;; - *) - log "Custom URL for subnet" - if curl --output /dev/null --silent --head --fail "$URL"; then - log "URL is valid" - else - log "URL $URL is not valid" - fi - ;; - esac - - local filename=$(basename "$URL") - curl -f "$URL" --output "/tmp/podkop/$filename" - while IFS= read -r subnet; do - nft add element inet PodkopTable podkop_subnets { $subnet } - done <"/tmp/podkop/$filename" -} - -list_custom_domains_create() { - local domain="$1" - local name="$2" - echo "nftset=/$domain/4#inet#PodkopTable#${name}_domains" >>"/tmp/dnsmasq.d/${name}-custom-domains.lst" - log "$domain added to the list" -} - -list_custom_download_domains_create() { - local URL="$1" - local name="$2" - local filename=$(basename "$URL") - local config="/tmp/dnsmasq.d/${name}-${filename}.lst" - - rm -f $config - curl -f "$URL" --output "/tmp/podkop/${filename}" - while IFS= read -r domain; do - echo "nftset=/$domain/4#inet#PodkopTable#${name}_domains" >>$config - done <"/tmp/podkop/$filename" - dnsmasq_config_check ${name}-${filename}.lst -} - -list_custom_subnets_create() { - local subnet="$1" - local name="$2" - nft add element inet PodkopTable ${name}_subnets { $subnet } -} - -list_all_traffic_from_ip() { - local ip="$1" - if ! nft list chain inet PodkopTable mangle | grep -q "ip saddr $ip"; then - config_get mode "main" "mode" "0" - if [ "$mode" = "vpn" ]; then - nft insert rule inet PodkopTable mangle ip saddr $ip meta mark set 0x105 counter - elif [ "$mode" = "proxy" ]; then - nft add set inet PodkopTable localv4 { type ipv4_addr\; flags interval\; } - nft add element inet PodkopTable localv4 { \ - 0.0.0.0/8, \ - 10.0.0.0/8, \ - 127.0.0.0/8, \ - 169.254.0.0/16, \ - 172.16.0.0/12, \ - 192.0.0.0/24, \ - 192.0.2.0/24, \ - 192.88.99.0/24, \ - 192.168.0.0/16, \ - 198.18.0.0/15, \ - 198.51.100.0/24, \ - 203.0.113.0/24, \ - 224.0.0.0/4, \ - 240.0.0.0-255.255.255.255 } - nft insert rule inet PodkopTable mangle ip saddr $ip meta l4proto { tcp, udp } meta mark set 0x105 counter - nft insert rule inet PodkopTable mangle ip saddr $ip ip daddr @localv4 return - fi - fi -} - -list_exclude_traffic_from_ip() { - local ip="$1" - if ! nft list chain inet PodkopTable mangle | grep -q "ip saddr $ip"; then - nft insert rule inet PodkopTable mangle ip saddr $ip return - fi -} - -list_delist_domains() { - local domain="$1" - - if [ -f "/tmp/dnsmasq.d/podkop-domains.lst" ]; then - sed -i "/$domain/d" /tmp/dnsmasq.d/podkop-domains.lst - nft flush set inet PodkopTable podkop_domains - log "Strings containing '$domain' have been excluded from the list" - else - log "Config /tmp/dnsmasq.d/podkop-domains.lst not exists" - fi -} - -delist_downloaded_domains() { - local domains="/tmp/dnsmasq.d/podkop2-domains.lst" - - if [ -f "$domains" ]; then - while IFS= read -r line; do - list_delist_domains "$line" - done <"$domains" - else - log "$domains not found" - fi -} - -dnsmasq_config_check() { - local config="$1" - if ! /usr/sbin/dnsmasq --conf-file=/tmp/dnsmasq.d/$config --test 2>&1 | grep -q "syntax check OK"; then - log "Dnsmasq config $config contains errors. Break" - return - fi -} - -sing_box_uci() { - local config="/etc/config/sing-box" - if grep -q "option enabled '0'" "$config" || - grep -q "option user 'sing-box'" "$config"; then - sed -i \ - -e "s/option enabled '0'/option enabled '1'/" \ - -e "s/option user 'sing-box'/option user 'root'/" $config - log "Change sing-box UCI config" - else - log "Sing-box UCI config OK" - fi -} - -sing_box_config_shadowsocks() { - local STRING="$1" - local listen_port="$2" - - local encrypted_part=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | base64 --decode) - local method=$(echo "$encrypted_part" | cut -d':' -f1) - local password=$(echo "$encrypted_part" | cut -d':' -f2-) - - local server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) - local port=$(echo "$STRING" | sed -n 's|.*:\([0-9]\+\).*|\1|p') - local label=$(echo "$STRING" | cut -d'#' -f2) - - template_config="/etc/podkop/sing-box-shadowsocks-template.json" - - jq --arg server "$server" \ - --arg port "$port" \ - --arg method "$method" \ - --arg password "$password" \ - --arg listen_port "$listen_port" \ - '.inbounds[] |= - if .type == "tproxy" then - .listen_port = ($listen_port | tonumber) - else - . - end | - .outbounds[] |= - if .type == "shadowsocks" then - .server = $server | - .server_port = ($port | tonumber) | - .method = $method | - .password = $password - else - . - end' "$template_config" >/etc/sing-box/config.json -} - -sing_box_config_vless() { - local STRING="$1" - local listen_port="$2" - - get_param() { - echo "$STRING" | sed -n "s/.*[?&]$1=\([^&?#]*\).*/\1/p" +'use strict'; +'require view'; +'require form'; +'require ui'; +'require network'; + +return view.extend({ + async render() { + var m, s, o; + + m = new form.Map('podkop', _('Podkop configuration'), null, ['main', 'second']); + + s = m.section(form.TypedSection, 'main'); + s.anonymous = true; + + // Main Config Tab + o = s.tab('main_config', _('Main Config')); + + o = s.taboption('main_config', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing')); + o.value('vpn', ('VPN')); + o.value('proxy', ('Proxy')); + o.ucisection = 'main'; + + o = s.taboption('main_config', form.TextValue, 'proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); + o.depends('mode', 'proxy'); + o.rows = 5; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.ListValue, 'interface', _('Network Interface'), _('Select network interface for VPN connection')); + o.depends('mode', 'vpn'); + o.ucisection = 'main'; + + try { + const devices = await network.getDevices(); + const excludeInterfaces = ['br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0']; + + devices.forEach(function (device) { + if (device.dev && device.dev.name) { + const deviceName = device.dev.name; + const isExcluded = excludeInterfaces.includes(deviceName) || /^lan\d+$/.test(deviceName); + + if (!isExcluded) { + o.value(deviceName, deviceName); + } + } + }); + } catch (error) { + console.error('Error fetching devices:', error); + } + + o = s.taboption('main_config', form.Flag, 'domain_list_enabled', _('Predefined Domain Lists'), _('github.com/itdoginfo/allow-domains')); + o.default = '0'; + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.ListValue, 'domain_list', _('Domain List'), _('Select a predefined domain list')); + o.placeholder = 'placeholder'; + o.value('ru_inside', 'Russia inside'); + o.value('ru_outside', 'Russia outside'); + o.value('ua', 'Ukraine'); + o.depends('domain_list_enabled', '1'); + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.Flag, 'subnets_list_enabled', _('Predefined Service Networks'), _('Enable routing for popular services like Twitter, Meta, and Discord')); + o.default = '0'; + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.DynamicList, 'subnets', _('Service Networks'), _('Select predefined service networks for routing')); + o.placeholder = 'Service network list'; + o.value('twitter', 'Twitter(x.com)'); + o.value('meta', 'Meta'); + o.value('discord', 'Discord(voice)'); + o.depends('subnets_list_enabled', '1'); + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.Flag, 'custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing')); + o.default = '0'; + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.DynamicList, 'custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); + o.placeholder = 'Domains list'; + o.depends('custom_domains_list_enabled', '1'); + o.rmempty = false; + o.ucisection = 'main'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/; + + if (!domainRegex.test(value)) { + return _('Invalid domain format. Enter domain without protocol (example: sub.example.com)'); + } + return true; + }; + + o = s.taboption('main_config', form.Flag, 'custom_download_domains_list_enabled', _('Remote Domain Lists'), _('Download and use domain lists from remote URLs')); + o.default = '0'; + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.DynamicList, 'custom_download_domains', _('Remote Domain URLs'), _('Enter full URLs starting with http:// or https://')); + o.placeholder = 'URL'; + o.depends('custom_download_domains_list_enabled', '1'); + o.rmempty = false; + o.ucisection = 'main'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + try { + const url = new URL(value); + if (!['http:', 'https:'].includes(url.protocol)) { + return _('URL must use http:// or https:// protocol'); + } + return true; + } catch (e) { + return _('Invalid URL format. URL must start with http:// or https://'); + } + }; + + o = s.taboption('main_config', form.Flag, 'custom_subnets_list_enabled', _('User Subnet List'), _('Enable and manage your custom list of IP subnets for selective routing')); + o.default = '0'; + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.DynamicList, 'custom_subnets', _('User Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); + o.placeholder = 'Subnets list'; + o.depends('custom_subnets_list_enabled', '1'); + o.rmempty = false; + o.ucisection = 'main'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/; + + if (!subnetRegex.test(value)) { + return _('Invalid subnet format. Use format: X.X.X.X/Y (like 192.168.1.0/24)'); + } + + const [ip, cidr] = value.split('/'); + const ipParts = ip.split('.'); + const cidrNum = parseInt(cidr); + + for (const part of ipParts) { + const num = parseInt(part); + if (num < 0 || num > 255) { + return _('IP address parts must be between 0 and 255'); + } + } + + if (cidrNum < 0 || cidrNum > 32) { + return _('CIDR must be between 0 and 32'); + } + + return true; + }; + + o = s.taboption('main_config', form.Flag, 'custom_download_subnets_list_enabled', _('Remote Subnet Lists'), _('Download and use subnet lists from remote URLs')); + o.default = '0'; + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.DynamicList, 'custom_download_subnets', _('Remote Subnet URLs'), _('Enter full URLs starting with http:// or https://')); + o.placeholder = 'URL'; + o.depends('custom_download_subnets_list_enabled', '1'); + o.rmempty = false; + o.ucisection = 'main'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + try { + const url = new URL(value); + if (!['http:', 'https:'].includes(url.protocol)) { + return _('URL must use http:// or https:// protocol'); + } + return true; + } catch (e) { + return _('Invalid URL format. URL must start with http:// or https://'); + } + }; + + o = s.taboption('main_config', form.Flag, 'delist_domains_enabled', _('Domain Exclusions'), _('Exclude specific domains from routing rules')); + o.default = '0'; + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.DynamicList, 'delist_domains', _('Excluded Domains'), _('Domains to be excluded from routing')); + o.placeholder = 'Delist domains'; + o.depends('delist_domains_enabled', '1'); + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.Flag, 'all_traffic_from_ip_enabled', _('Force Proxy IPs'), _('Specify local IP addresses whose traffic will always use the configured route')); + o.default = '0'; + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.DynamicList, 'all_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); + o.placeholder = 'IP'; + o.depends('all_traffic_from_ip_enabled', '1'); + o.rmempty = false; + o.ucisection = 'main'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/; + + if (!ipRegex.test(value)) { + return _('Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)'); + } + + const ipParts = value.split('.'); + for (const part of ipParts) { + const num = parseInt(part); + if (num < 0 || num > 255) { + return _('IP address parts must be between 0 and 255'); + } + } + + return true; + }; + + o = s.taboption('main_config', form.Flag, 'exclude_from_ip_enabled', _('Bypass Proxy IPs'), _('Specify local IP addresses that will never use the configured route')); + o.default = '0'; + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('main_config', form.DynamicList, 'exclude_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); + o.placeholder = 'IP'; + o.depends('exclude_from_ip_enabled', '1'); + o.rmempty = false; + o.ucisection = 'main'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/; + + if (!ipRegex.test(value)) { + return _('Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)'); + } + + const ipParts = value.split('.'); + for (const part of ipParts) { + const num = parseInt(part); + if (num < 0 || num > 255) { + return _('IP address parts must be between 0 and 255'); + } + } + + return true; + }; + + // Add-ons Tab + o = s.tab('addons', _('Add-ons')); + + o = s.taboption('addons', form.Flag, 'yacd', _('Yacd enable'), _('http://openwrt.lan:9090/ui')); + o.default = '0'; + o.depends('mode', 'proxy'); + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('addons', form.Flag, 'socks5', _('Mixed enable'), _('Browser port: 2080')); + o.default = '0'; + o.depends('mode', 'proxy'); + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('addons', form.Flag, 'exclude_ntp', _('Exclude NTP'), _('For issues with open connections sing-box')); + o.default = '0'; + o.depends('mode', 'proxy'); + o.rmempty = false; + o.ucisection = 'main'; + + o = s.taboption('addons', form.ListValue, 'update_interval', _('List Update Frequency'), _('Select how often the lists will be updated')); + o.value('0 */1 * * *', _('Every hour')); + o.value('0 */2 * * *', _('Every 2 hours')); + o.value('0 */4 * * *', _('Every 4 hours')); + o.value('0 */6 * * *', _('Every 6 hours')); + o.value('0 */12 * * *', _('Every 12 hours')); + o.value('0 4 * * *', _('Once a day at 04:00')); + o.value('0 4 * * 0', _('Once a week on Sunday at 04:00')); + o.default = '0 4 * * *'; + o.rmempty = false; + o.ucisection = 'main'; + + // Alternative Config Tab + o = s.tab('alternative_config', _('Alternative Config')); + + o = s.taboption('alternative_config', form.Flag, 'second_enable', _('Secondary Route Enable'), _('Enable secondary routing configuration')); + o.default = '0'; + o.rmempty = false; + o.ucisection = 'second'; + + o = s.taboption('alternative_config', form.ListValue, 'second_mode', _('Connection Type'), _('Select between VPN and Proxy for secondary route')); + o.value('vpn', ('VPN')); + o.value('proxy', ('Proxy')); + o.depends('second_enable', '1'); + o.ucisection = 'second'; + + o = s.taboption('alternative_config', form.TextValue, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); + o.depends('second_mode', 'proxy'); + o.ucisection = 'second'; + + o = s.taboption('alternative_config', form.ListValue, 'second_interface', _('Network Interface'), _('Select network interface for VPN connection')); + o.depends('second_mode', 'vpn'); + o.ucisection = 'second'; + + try { + const devices = await network.getDevices(); + const excludeInterfaces = ['br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0']; + + devices.forEach(function (device) { + if (device.dev && device.dev.name) { + const deviceName = device.dev.name; + const isExcluded = excludeInterfaces.includes(deviceName) || /^lan\d+$/.test(deviceName); + + if (!isExcluded) { + o.value(deviceName, deviceName); + } + } + }); + } catch (error) { + console.error('Error fetching devices:', error); + } + + o = s.taboption('alternative_config', form.Flag, 'domain_service_enabled', _('Service Domain List Enable'), _('Enable predefined service domain lists for secondary routing')); + o.default = '0'; + o.rmempty = false; + o.depends('second_enable', '1'); + o.ucisection = 'second'; + + o = s.taboption('alternative_config', form.ListValue, 'service_list', _('Service List'), _('Select predefined services for secondary routing')); + o.placeholder = 'placeholder'; + o.value('youtube', 'Youtube'); + o.depends('domain_service_enabled', '1'); + o.rmempty = false; + o.ucisection = 'second'; + + o = s.taboption('alternative_config', form.Flag, 'second_custom_domains_list_enabled', _('Secondary Domain List'), _('Configure custom domains for secondary routing path')); + o.default = '0'; + o.rmempty = false; + o.depends('second_enable', '1'); + o.ucisection = 'second'; + + o = s.taboption('alternative_config', form.DynamicList, 'second_custom_domains', _('Secondary Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); + o.placeholder = 'Domains list'; + o.depends('second_custom_domains_list_enabled', '1'); + o.rmempty = false; + o.ucisection = 'second'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/; + + if (!domainRegex.test(value)) { + return _('Invalid domain format. Enter domain without protocol (example: sub.example.com)'); + } + return true; + }; + + o = s.taboption('alternative_config', form.Flag, 'second_custom_subnets_list_enabled', _('Secondary Subnet List'), _('Configure custom subnets for secondary routing path')); + o.default = '0'; + o.rmempty = false; + o.depends('second_enable', '1'); + o.ucisection = 'second'; + + o = s.taboption('alternative_config', form.DynamicList, 'second_custom_subnets', _('Secondary Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); + o.placeholder = 'Subnets list'; + o.depends('second_custom_subnets_list_enabled', '1'); + o.rmempty = false; + o.ucisection = 'second'; + o.validate = function (section_id, value) { + if (!value || value.length === 0) { + return true; + } + + const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/; + + if (!subnetRegex.test(value)) { + return _('Invalid subnet format. Use format: X.X.X.X/Y (like 192.168.1.0/24)'); + } + + const [ip, cidr] = value.split('/'); + const ipParts = ip.split('.'); + const cidrNum = parseInt(cidr); + + for (const part of ipParts) { + const num = parseInt(part); + if (num < 0 || num > 255) { + return _('IP address parts must be between 0 and 255'); + } + } + + if (cidrNum < 0 || cidrNum > 32) { + return _('CIDR must be between 0 and 32'); + } + + return true; + }; + + return m.render(); } - - uuid=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1) - server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) - port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | awk -F'/' '{print $1}') - - type=$(get_param "type") - flow=$(get_param "flow") - sni=$(get_param "sni") - fp=$(get_param "fp") - security=$(get_param "security") - pbk=$(get_param "pbk") - sid=$(get_param "sid") - encoding=$(get_param "packetEncoding") - alpn=$(echo "$(get_param "alpn" | sed 's/%2C/,/g; s/%2F/\//g')" | jq -R -s -c 'split(",")' | sed 's/\\n//g') - label=$(echo "$STRING" | cut -d'#' -f2) - - template_config="/etc/podkop/sing-box-vless-template.json" - - jq --arg server "$server" \ - --arg port "$port" \ - --arg uuid "$uuid" \ - --arg type "$type" \ - --arg flow "$flow" \ - --arg sni "$sni" \ - --arg fp "$fp" \ - --arg security "$security" \ - --arg pbk "$pbk" \ - --arg sid "$sid" \ - --argjson alpn "$alpn" \ - --arg encoding "$encoding" \ - --arg listen_port "$listen_port" \ - '.inbounds[] |= - if .type == "tproxy" then - .listen_port = ($listen_port | tonumber) - else - . - end | - .outbounds[] |= - (.server = $server | - .server_port = ($port | tonumber) | - .uuid = $uuid | - if $security == "reality" then - if $flow == "" then del(.flow) else .flow = $flow end | - if $encoding == "" then del(.packet_encoding) else .packet_encoding = $encoding end | - .tls.server_name = $sni | - .tls.utls.fingerprint = $fp | - .tls.reality.public_key = $pbk | - .tls.reality.short_id = $sid - elif $security == "tls" then - .tls.alpn = $alpn | - .tls.server_name = $sni | - del(.flow) | - del(.tls.utls) | - del(.tls.reality) - elif $security == "" or $security == "none" then - del(.flow) | - del(.tls) - else - . - end)' "$template_config" >/etc/sing-box/config.json -} - -sing_box_config_outbound_shadowsocks() { - local STRING="$1" - local outbound="$2" - local name="$3" - - local encrypted_part=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | base64 --decode) - local method=$(echo "$encrypted_part" | cut -d':' -f1) - local password=$(echo "$encrypted_part" | cut -d':' -f2-) - - local server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) - local port=$(echo "$STRING" | cut -d':' -f3 | cut -d'#' -f1) - label=$(echo "$STRING" | cut -d'#' -f2) - - template_config="/etc/podkop/sing-box-shadowsocks-outbound-template.json" - - jq --arg server "$server" \ - --arg port "$port" \ - --arg method "$method" \ - --arg password "$password" \ - --arg tag "$name" \ - '.outbounds[] |= - if .type == "shadowsocks" then - .server = $server | - .server_port = ($port | tonumber) | - .method = $method | - .password = $password | - .tag = $tag - else - . - end' "$template_config" >$outbound -} - -sing_box_config_outbound_vless() { - local STRING="$1" - local outbound="$2" - local name="$3" - - get_param() { - echo "$STRING" | sed -n "s/.*[?&]$1=\([^&?#]*\).*/\1/p" - } - - uuid=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1) - server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) - port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | awk -F'/' '{print $1}') - - type=$(get_param "type") - flow=$(get_param "flow") - sni=$(get_param "sni") - fp=$(get_param "fp") - security=$(get_param "security") - pbk=$(get_param "pbk") - sid=$(get_param "sid") - alpn=$(echo "$(get_param "alpn" | sed 's/%2C/,/g; s/%2F/\//g')" | jq -R -s -c 'split(",")' | sed 's/\\n//g') - encoding=$(get_param "packetEncoding") - label=$(echo "$STRING" | cut -d'#' -f2) - - template_config="/etc/podkop/sing-box-vless-outbound-template.json" - - jq --arg server "$server" \ - --arg port "$port" \ - --arg uuid "$uuid" \ - --arg type "$type" \ - --arg flow "$flow" \ - --arg sni "$sni" \ - --arg fp "$fp" \ - --arg security "$security" \ - --arg pbk "$pbk" \ - --arg sid "$sid" \ - --argjson alpn "$alpn" \ - --arg encoding "$encoding" \ - --arg tag "$name" \ - '.outbounds[] |= - (.server = $server | - .server_port = ($port | tonumber) | - .uuid = $uuid | - if $security == "reality" then - if $flow == "" then del(.flow) else .flow = $flow end | - if $encoding == "" then del(.packet_encoding) else .packet_encoding = $encoding end | - .tls.server_name = $sni | - .tls.utls.fingerprint = $fp | - .tls.reality.public_key = $pbk | - .tls.reality.short_id = $sid | - .tag = $tag - elif $security == "tls" then - .tls.alpn = $alpn | - .tls.server_name = $sni | - del(.flow) | - del(.tls.utls) | - del(.tls.reality) | - .tag = $tag - elif $security == "" or $security == "none" then - del(.flow) | - del(.tls) | - .tag = $tag - else - . - end)' "$template_config" >$outbound -} - -sing_box_config_check() { - if ! sing-box -c /etc/sing-box/config.json check >/dev/null 2>&1; then - log "Sing-box configuration is invalid" - return - fi -} \ No newline at end of file +}); \ No newline at end of file From b6f1c4e747157a7748cdd8c0bcd0218ec67ee431 Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Mon, 11 Nov 2024 18:38:05 +0300 Subject: [PATCH 09/14] refactor: Rename and update settings tab --- .../resources/view/podkop/podkop.js | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 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 9cafcb1..df98351 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 @@ -265,7 +265,6 @@ return view.extend({ return true; }; - // Additional Settings Tab o = s.tab('additional', _('Additional Settings')); o = s.taboption('additional', form.Flag, 'yacd', _('Yacd enable'), _('http://openwrt.lan:9090/ui')); @@ -298,25 +297,24 @@ return view.extend({ o.rmempty = false; o.ucisection = 'main'; - // Secondary Settings Tab - o = s.tab('second_settings', _('Secondary Settings')); + o = s.tab('alternative_config', _('Alternative Config')); - o = s.taboption('second_settings', form.Flag, 'second_enable', _('Secondary Route Enable'), _('Enable secondary routing configuration')); + o = s.taboption('alternative_config', form.Flag, 'second_enable', _('Alternative VPN/Proxy Enable'), _('Enable routing configuration')); o.default = '0'; o.rmempty = false; o.ucisection = 'second'; - o = s.taboption('second_settings', form.ListValue, 'second_mode', _('Connection Type'), _('Select between VPN and Proxy for secondary route')); + o = s.taboption('alternative_config', form.ListValue, 'second_mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing')); o.value('vpn', ('VPN')); o.value('proxy', ('Proxy')); o.depends('second_enable', '1'); o.ucisection = 'second'; - o = s.taboption('second_settings', form.TextValue, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); + o = s.taboption('alternative_config', form.TextValue, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); o.depends('second_mode', 'proxy'); o.ucisection = 'second'; - o = s.taboption('second_settings', form.ListValue, 'second_interface', _('Network Interface'), _('Select network interface for VPN connection')); + o = s.taboption('alternative_config', form.ListValue, 'second_interface', _('Network Interface'), _('Select network interface for VPN connection')); o.depends('second_mode', 'vpn'); o.ucisection = 'second'; @@ -338,26 +336,26 @@ return view.extend({ console.error('Error fetching devices:', error); } - o = s.taboption('second_settings', form.Flag, 'domain_service_enabled', _('Service Domain List Enable'), _('Enable predefined service domain lists for secondary routing')); + o = s.taboption('alternative_config', form.Flag, 'domain_service_enabled', _('Service Domain List Enable'), _('Enable predefined service domain lists for routing')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); o.ucisection = 'second'; - o = s.taboption('second_settings', form.ListValue, 'service_list', _('Service List'), _('Select predefined services for secondary routing')); + o = s.taboption('alternative_config', form.ListValue, 'service_list', _('Service List'), _('Select predefined services for routing')); o.placeholder = 'placeholder'; o.value('youtube', 'Youtube'); o.depends('domain_service_enabled', '1'); o.rmempty = false; o.ucisection = 'second'; - o = s.taboption('second_settings', form.Flag, 'second_custom_domains_list_enabled', _('Secondary Domain List'), _('Configure custom domains for secondary routing path')); + o = s.taboption('alternative_config', form.Flag, 'second_custom_domains_list_enabled', _('Domain List'), _('Configure custom domains for routing')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); o.ucisection = 'second'; - o = s.taboption('second_settings', form.DynamicList, 'second_custom_domains', _('Secondary Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); + o = s.taboption('alternative_config', form.DynamicList, 'second_custom_domains', _('Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o.placeholder = 'Domains list'; o.depends('second_custom_domains_list_enabled', '1'); o.rmempty = false; @@ -375,13 +373,13 @@ return view.extend({ return true; }; - o = s.taboption('second_settings', form.Flag, 'second_custom_subnets_list_enabled', _('Secondary Subnet List'), _('Configure custom subnets for secondary routing path')); + o = s.taboption('alternative_config', form.Flag, 'second_custom_subnets_list_enabled', _('Subnet List'), _('Configure custom subnets for routing')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); o.ucisection = 'second'; - o = s.taboption('second_settings', form.DynamicList, 'second_custom_subnets', _('Secondary Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); + o = s.taboption('alternative_config', form.DynamicList, 'second_custom_subnets', _('Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); o.placeholder = 'Subnets list'; o.depends('second_custom_subnets_list_enabled', '1'); o.rmempty = false; From 47699ee0d62e0b3dcb7813fa9efb4868ece63af7 Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Mon, 11 Nov 2024 18:46:52 +0300 Subject: [PATCH 10/14] refactor: Optimize VPN/Proxy configuration messages --- .../resources/view/podkop/podkop.js | 2 +- luci-app-podkop/po/ru/podkop.po | 64 +++--- luci-app-podkop/po/templates/podkop.pot | 183 ++++++++---------- 3 files changed, 113 insertions(+), 136 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 df98351..afbb04b 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 @@ -299,7 +299,7 @@ return view.extend({ o = s.tab('alternative_config', _('Alternative Config')); - o = s.taboption('alternative_config', form.Flag, 'second_enable', _('Alternative VPN/Proxy Enable'), _('Enable routing configuration')); + o = s.taboption('alternative_config', form.Flag, 'second_enable', _('Alternative VPN/Proxy Enable'), _('Enable alternative VPN/Proxy configuration')); o.default = '0'; o.rmempty = false; o.ucisection = 'second'; diff --git a/luci-app-podkop/po/ru/podkop.po b/luci-app-podkop/po/ru/podkop.po index 814737f..ac7713a 100644 --- a/luci-app-podkop/po/ru/podkop.po +++ b/luci-app-podkop/po/ru/podkop.po @@ -4,8 +4,20 @@ msgstr "Content-Type: text/plain; charset=UTF-8" msgid "Podkop configuration" msgstr "Настройка Podkop" -msgid "Main Config" -msgstr "Основная конфигурация" +msgid "Basic Settings" +msgstr "Основные настройки" + +msgid "Additional Settings" +msgstr "Дополнительные настройки" + +msgid "Alternative Config" +msgstr "Альтернативная конфигурация" + +msgid "Alternative VPN/Proxy Enable" +msgstr "Включить альтернативный VPN/Proxy" + +msgid "Enable alternative VPN/Proxy configuration" +msgstr "Включить конфигурацию альтернативного VPN/Proxy" msgid "Connection Type" msgstr "Тип подключения" @@ -46,12 +58,6 @@ msgstr "Сети сервисов" msgid "Select predefined service networks for routing" msgstr "Выберите предустановленные сети сервисов для маршрутизации" -msgid "Add-ons" -msgstr "Дополнения" - -msgid "Alternative Config" -msgstr "Альтернативная конфигурация" - msgid "User Domain List" msgstr "Пользовательский список доменов" @@ -61,6 +67,9 @@ msgstr "Включить и управлять пользовательским msgid "User Domains" msgstr "Пользовательские домены" +msgid "Enter domain names without protocols (example: sub.example.com or example.com)" +msgstr "Введите имена доменов без протоколов (пример: sub.example.com или example.com)" + msgid "Remote Domain Lists" msgstr "Удаленные списки доменов" @@ -151,9 +160,6 @@ msgstr "Раз в день в 04:00" msgid "Once a week on Sunday at 04:00" msgstr "Раз в неделю в воскресенье в 04:00" -msgid "Once a week on Monday at 04:00" -msgstr "Раз в неделю в понедельник в 04:00" - msgid "Yacd enable" msgstr "Включить Yacd" @@ -169,41 +175,29 @@ msgstr "Исключить NTP" msgid "For issues with open connections sing-box" msgstr "Для проблем с открытыми соединениями sing-box" -msgid "Secondary Route Enable" -msgstr "Включить вторичный маршрут" - -msgid "Enable secondary routing configuration" -msgstr "Включить вторичную конфигурацию маршрутизации" - msgid "Service Domain List Enable" msgstr "Включить список доменов сервисов" -msgid "Enable predefined service domain lists for secondary routing" -msgstr "Включить предустановленные списки доменов сервисов для вторичной маршрутизации" +msgid "Enable predefined service domain lists for routing" +msgstr "Включить предустановленные списки доменов для маршрутизации" msgid "Service List" msgstr "Список сервисов" -msgid "Select predefined services for secondary routing" -msgstr "Выберите предустановленные сервисы для вторичной маршрутизации" +msgid "Select predefined services for routing" +msgstr "Выберите предустановленные сервисы для маршрутизации" -msgid "Secondary Domain List" -msgstr "Вторичный список доменов" +msgid "Domains" +msgstr "Домены" -msgid "Configure custom domains for secondary routing path" -msgstr "Настройте пользовательские домены для вторичного маршрута" +msgid "Subnet List" +msgstr "Список подсетей" -msgid "Secondary Domains" -msgstr "Вторичные домены" +msgid "Configure custom subnets for routing" +msgstr "Настройка пользовательских подсетей для маршрутизации" -msgid "Secondary Subnet List" -msgstr "Вторичный список подсетей" - -msgid "Configure custom subnets for secondary routing path" -msgstr "Настройте пользовательские подсети для вторичного маршрута" - -msgid "Secondary Subnets" -msgstr "Вторичные подсети" +msgid "Subnets" +msgstr "Подсети" msgid "Invalid domain format. Enter domain without protocol (example: sub.example.com)" msgstr "Неверный формат домена. Введите домен без протокола (пример: sub.example.com)" diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot index 34938fe..fe3d6b7 100644 --- a/luci-app-podkop/po/templates/podkop.pot +++ b/luci-app-podkop/po/templates/podkop.pot @@ -1,238 +1,221 @@ msgid "" msgstr "Content-Type: text/plain; charset=UTF-8" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Podkop configuration" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Main Config" +msgid "Basic Settings" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Connection Type" +msgid "Additional Settings" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Select between VPN and Proxy connection methods for traffic routing" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Proxy Configuration URL" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Enter connection string starting with vless:// or ss:// for proxy configuration" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Network Interface" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Select network interface for VPN connection" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Predefined Domain Lists" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Domain List" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Select a predefined domain list" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Predefined Service Networks" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Enable routing for popular services like Twitter, Meta, and Discord" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Service Networks" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Select predefined service networks for routing" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 -msgid "Add-ons" -msgstr "" - -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Alternative Config" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Alternative VPN/Proxy Enable" +msgstr "" + +msgid "Enable alternative VPN/Proxy configuration" +msgstr "" + +msgid "Connection Type" +msgstr "" + +msgid "Select between VPN and Proxy connection methods for traffic routing" +msgstr "" + +msgid "Proxy Configuration URL" +msgstr "" + +msgid "Enter connection string starting with vless:// or ss:// for proxy configuration" +msgstr "" + +msgid "Network Interface" +msgstr "" + +msgid "Select network interface for VPN connection" +msgstr "" + +msgid "Predefined Domain Lists" +msgstr "" + +msgid "Domain List" +msgstr "" + +msgid "Select a predefined domain list" +msgstr "" + +msgid "Predefined Service Networks" +msgstr "" + +msgid "Enable routing for popular services like Twitter, Meta, and Discord" +msgstr "" + +msgid "Service Networks" +msgstr "" + +msgid "Select predefined service networks for routing" +msgstr "" + msgid "User Domain List" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Enable and manage your custom list of domains for selective routing" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "User Domains" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Enter domain names without protocols (example: sub.example.com or example.com)" +msgstr "" + msgid "Remote Domain Lists" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Download and use domain lists from remote URLs" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Remote Domain URLs" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Enter full URLs starting with http:// or https://" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "User Subnet List" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Enable and manage your custom list of IP subnets for selective routing" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "User Subnets" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Enter subnet in CIDR notation (example: 192.168.1.0/24)" +msgstr "" + msgid "Remote Subnet Lists" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Download and use subnet lists from remote URLs" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Remote Subnet URLs" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Domain Exclusions" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Exclude specific domains from routing rules" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Excluded Domains" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Domains to be excluded from routing" +msgstr "" + msgid "Force Proxy IPs" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Specify local IP addresses whose traffic will always use the configured route" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Local IPs" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +msgid "Enter valid IPv4 addresses" +msgstr "" + msgid "Bypass Proxy IPs" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Specify local IP addresses that will never use the configured route" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "List Update Frequency" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Select how often the lists will be updated" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Every hour" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Every 2 hours" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Every 4 hours" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Every 6 hours" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Every 12 hours" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Once a day at 04:00" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Once a week on Sunday at 04:00" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Yacd enable" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Mixed enable" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Browser port: 2080" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Exclude NTP" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "For issues with open connections sing-box" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 +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 "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "URL must use http:// or https:// protocol" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Invalid URL format. URL must start with http:// or https://" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Invalid subnet format. Use format: X.X.X.X/Y (like 192.168.1.0/24)" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "IP address parts must be between 0 and 255" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "CIDR must be between 0 and 32" msgstr "" -#: applications/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:0 msgid "Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)" msgstr "" \ No newline at end of file From 5195dfa715ec1cb1f65478db583242c9e466e0c1 Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Mon, 11 Nov 2024 18:51:34 +0300 Subject: [PATCH 11/14] feat: Add language translation package correct version --- luci-app-podkop/Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/luci-app-podkop/Makefile b/luci-app-podkop/Makefile index 5ec9d55..470371f 100644 --- a/luci-app-podkop/Makefile +++ b/luci-app-podkop/Makefile @@ -17,4 +17,14 @@ LUCI_LANGUAGES:=en ru include $(TOPDIR)/feeds/luci/luci.mk +define Package/luci-i18n-$(PKG_NAME)-$(1) + SECTION:=luci + CATEGORY:=LuCI + TITLE:=$(PKG_NAME) - $(1) translation + HIDDEN:=1 + DEFAULT:=LUCI_LANG_$(1)||ALL + VERSION:=$(PKG_VERSION_TRANS) + DEPENDS:=$(PKG_NAME) +endef + # call BuildPackage - OpenWrt buildroot signature \ No newline at end of file From 52483887f4792c59dc879a2982f431faba3f1991 Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Mon, 11 Nov 2024 19:08:06 +0300 Subject: [PATCH 12/14] fix: Rollout podkop init.d configuration --- podkop/files/etc/init.d/podkop | 1376 ++++++++++++++++++++++---------- 1 file changed, 957 insertions(+), 419 deletions(-) diff --git a/podkop/files/etc/init.d/podkop b/podkop/files/etc/init.d/podkop index 4ba0b96..3300594 100755 --- a/podkop/files/etc/init.d/podkop +++ b/podkop/files/etc/init.d/podkop @@ -1,420 +1,958 @@ -'use strict'; -'require view'; -'require form'; -'require ui'; -'require network'; - -return view.extend({ - async render() { - var m, s, o; - - m = new form.Map('podkop', _('Podkop configuration'), null, ['main', 'second']); - - s = m.section(form.TypedSection, 'main'); - s.anonymous = true; - - // Main Config Tab - o = s.tab('main_config', _('Main Config')); - - o = s.taboption('main_config', form.ListValue, 'mode', _('Connection Type'), _('Select between VPN and Proxy connection methods for traffic routing')); - o.value('vpn', ('VPN')); - o.value('proxy', ('Proxy')); - o.ucisection = 'main'; - - o = s.taboption('main_config', form.TextValue, 'proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); - o.depends('mode', 'proxy'); - o.rows = 5; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.ListValue, 'interface', _('Network Interface'), _('Select network interface for VPN connection')); - o.depends('mode', 'vpn'); - o.ucisection = 'main'; - - try { - const devices = await network.getDevices(); - const excludeInterfaces = ['br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0']; - - devices.forEach(function (device) { - if (device.dev && device.dev.name) { - const deviceName = device.dev.name; - const isExcluded = excludeInterfaces.includes(deviceName) || /^lan\d+$/.test(deviceName); - - if (!isExcluded) { - o.value(deviceName, deviceName); - } - } - }); - } catch (error) { - console.error('Error fetching devices:', error); - } - - o = s.taboption('main_config', form.Flag, 'domain_list_enabled', _('Predefined Domain Lists'), _('github.com/itdoginfo/allow-domains')); - o.default = '0'; - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.ListValue, 'domain_list', _('Domain List'), _('Select a predefined domain list')); - o.placeholder = 'placeholder'; - o.value('ru_inside', 'Russia inside'); - o.value('ru_outside', 'Russia outside'); - o.value('ua', 'Ukraine'); - o.depends('domain_list_enabled', '1'); - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.Flag, 'subnets_list_enabled', _('Predefined Service Networks'), _('Enable routing for popular services like Twitter, Meta, and Discord')); - o.default = '0'; - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.DynamicList, 'subnets', _('Service Networks'), _('Select predefined service networks for routing')); - o.placeholder = 'Service network list'; - o.value('twitter', 'Twitter(x.com)'); - o.value('meta', 'Meta'); - o.value('discord', 'Discord(voice)'); - o.depends('subnets_list_enabled', '1'); - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.Flag, 'custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing')); - o.default = '0'; - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.DynamicList, 'custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); - o.placeholder = 'Domains list'; - o.depends('custom_domains_list_enabled', '1'); - o.rmempty = false; - o.ucisection = 'main'; - o.validate = function (section_id, value) { - if (!value || value.length === 0) { - return true; - } - - const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/; - - if (!domainRegex.test(value)) { - return _('Invalid domain format. Enter domain without protocol (example: sub.example.com)'); - } - return true; - }; - - o = s.taboption('main_config', form.Flag, 'custom_download_domains_list_enabled', _('Remote Domain Lists'), _('Download and use domain lists from remote URLs')); - o.default = '0'; - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.DynamicList, 'custom_download_domains', _('Remote Domain URLs'), _('Enter full URLs starting with http:// or https://')); - o.placeholder = 'URL'; - o.depends('custom_download_domains_list_enabled', '1'); - o.rmempty = false; - o.ucisection = 'main'; - o.validate = function (section_id, value) { - if (!value || value.length === 0) { - return true; - } - - try { - const url = new URL(value); - if (!['http:', 'https:'].includes(url.protocol)) { - return _('URL must use http:// or https:// protocol'); - } - return true; - } catch (e) { - return _('Invalid URL format. URL must start with http:// or https://'); - } - }; - - o = s.taboption('main_config', form.Flag, 'custom_subnets_list_enabled', _('User Subnet List'), _('Enable and manage your custom list of IP subnets for selective routing')); - o.default = '0'; - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.DynamicList, 'custom_subnets', _('User Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); - o.placeholder = 'Subnets list'; - o.depends('custom_subnets_list_enabled', '1'); - o.rmempty = false; - o.ucisection = 'main'; - o.validate = function (section_id, value) { - if (!value || value.length === 0) { - return true; - } - - const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/; - - if (!subnetRegex.test(value)) { - return _('Invalid subnet format. Use format: X.X.X.X/Y (like 192.168.1.0/24)'); - } - - const [ip, cidr] = value.split('/'); - const ipParts = ip.split('.'); - const cidrNum = parseInt(cidr); - - for (const part of ipParts) { - const num = parseInt(part); - if (num < 0 || num > 255) { - return _('IP address parts must be between 0 and 255'); - } - } - - if (cidrNum < 0 || cidrNum > 32) { - return _('CIDR must be between 0 and 32'); - } - - return true; - }; - - o = s.taboption('main_config', form.Flag, 'custom_download_subnets_list_enabled', _('Remote Subnet Lists'), _('Download and use subnet lists from remote URLs')); - o.default = '0'; - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.DynamicList, 'custom_download_subnets', _('Remote Subnet URLs'), _('Enter full URLs starting with http:// or https://')); - o.placeholder = 'URL'; - o.depends('custom_download_subnets_list_enabled', '1'); - o.rmempty = false; - o.ucisection = 'main'; - o.validate = function (section_id, value) { - if (!value || value.length === 0) { - return true; - } - - try { - const url = new URL(value); - if (!['http:', 'https:'].includes(url.protocol)) { - return _('URL must use http:// or https:// protocol'); - } - return true; - } catch (e) { - return _('Invalid URL format. URL must start with http:// or https://'); - } - }; - - o = s.taboption('main_config', form.Flag, 'delist_domains_enabled', _('Domain Exclusions'), _('Exclude specific domains from routing rules')); - o.default = '0'; - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.DynamicList, 'delist_domains', _('Excluded Domains'), _('Domains to be excluded from routing')); - o.placeholder = 'Delist domains'; - o.depends('delist_domains_enabled', '1'); - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.Flag, 'all_traffic_from_ip_enabled', _('Force Proxy IPs'), _('Specify local IP addresses whose traffic will always use the configured route')); - o.default = '0'; - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.DynamicList, 'all_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); - o.placeholder = 'IP'; - o.depends('all_traffic_from_ip_enabled', '1'); - o.rmempty = false; - o.ucisection = 'main'; - o.validate = function (section_id, value) { - if (!value || value.length === 0) { - return true; - } - - const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/; - - if (!ipRegex.test(value)) { - return _('Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)'); - } - - const ipParts = value.split('.'); - for (const part of ipParts) { - const num = parseInt(part); - if (num < 0 || num > 255) { - return _('IP address parts must be between 0 and 255'); - } - } - - return true; - }; - - o = s.taboption('main_config', form.Flag, 'exclude_from_ip_enabled', _('Bypass Proxy IPs'), _('Specify local IP addresses that will never use the configured route')); - o.default = '0'; - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('main_config', form.DynamicList, 'exclude_traffic_ip', _('Local IPs'), _('Enter valid IPv4 addresses')); - o.placeholder = 'IP'; - o.depends('exclude_from_ip_enabled', '1'); - o.rmempty = false; - o.ucisection = 'main'; - o.validate = function (section_id, value) { - if (!value || value.length === 0) { - return true; - } - - const ipRegex = /^(\d{1,3}\.){3}\d{1,3}$/; - - if (!ipRegex.test(value)) { - return _('Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)'); - } - - const ipParts = value.split('.'); - for (const part of ipParts) { - const num = parseInt(part); - if (num < 0 || num > 255) { - return _('IP address parts must be between 0 and 255'); - } - } - - return true; - }; - - // Add-ons Tab - o = s.tab('addons', _('Add-ons')); - - o = s.taboption('addons', form.Flag, 'yacd', _('Yacd enable'), _('http://openwrt.lan:9090/ui')); - o.default = '0'; - o.depends('mode', 'proxy'); - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('addons', form.Flag, 'socks5', _('Mixed enable'), _('Browser port: 2080')); - o.default = '0'; - o.depends('mode', 'proxy'); - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('addons', form.Flag, 'exclude_ntp', _('Exclude NTP'), _('For issues with open connections sing-box')); - o.default = '0'; - o.depends('mode', 'proxy'); - o.rmempty = false; - o.ucisection = 'main'; - - o = s.taboption('addons', form.ListValue, 'update_interval', _('List Update Frequency'), _('Select how often the lists will be updated')); - o.value('0 */1 * * *', _('Every hour')); - o.value('0 */2 * * *', _('Every 2 hours')); - o.value('0 */4 * * *', _('Every 4 hours')); - o.value('0 */6 * * *', _('Every 6 hours')); - o.value('0 */12 * * *', _('Every 12 hours')); - o.value('0 4 * * *', _('Once a day at 04:00')); - o.value('0 4 * * 0', _('Once a week on Sunday at 04:00')); - o.default = '0 4 * * *'; - o.rmempty = false; - o.ucisection = 'main'; - - // Alternative Config Tab - o = s.tab('alternative_config', _('Alternative Config')); - - o = s.taboption('alternative_config', form.Flag, 'second_enable', _('Secondary Route Enable'), _('Enable secondary routing configuration')); - o.default = '0'; - o.rmempty = false; - o.ucisection = 'second'; - - o = s.taboption('alternative_config', form.ListValue, 'second_mode', _('Connection Type'), _('Select between VPN and Proxy for secondary route')); - o.value('vpn', ('VPN')); - o.value('proxy', ('Proxy')); - o.depends('second_enable', '1'); - o.ucisection = 'second'; - - o = s.taboption('alternative_config', form.TextValue, 'second_proxy_string', _('Proxy Configuration URL'), _('Enter connection string starting with vless:// or ss:// for proxy configuration')); - o.depends('second_mode', 'proxy'); - o.ucisection = 'second'; - - o = s.taboption('alternative_config', form.ListValue, 'second_interface', _('Network Interface'), _('Select network interface for VPN connection')); - o.depends('second_mode', 'vpn'); - o.ucisection = 'second'; - - try { - const devices = await network.getDevices(); - const excludeInterfaces = ['br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0']; - - devices.forEach(function (device) { - if (device.dev && device.dev.name) { - const deviceName = device.dev.name; - const isExcluded = excludeInterfaces.includes(deviceName) || /^lan\d+$/.test(deviceName); - - if (!isExcluded) { - o.value(deviceName, deviceName); - } - } - }); - } catch (error) { - console.error('Error fetching devices:', error); - } - - o = s.taboption('alternative_config', form.Flag, 'domain_service_enabled', _('Service Domain List Enable'), _('Enable predefined service domain lists for secondary routing')); - o.default = '0'; - o.rmempty = false; - o.depends('second_enable', '1'); - o.ucisection = 'second'; - - o = s.taboption('alternative_config', form.ListValue, 'service_list', _('Service List'), _('Select predefined services for secondary routing')); - o.placeholder = 'placeholder'; - o.value('youtube', 'Youtube'); - o.depends('domain_service_enabled', '1'); - o.rmempty = false; - o.ucisection = 'second'; - - o = s.taboption('alternative_config', form.Flag, 'second_custom_domains_list_enabled', _('Secondary Domain List'), _('Configure custom domains for secondary routing path')); - o.default = '0'; - o.rmempty = false; - o.depends('second_enable', '1'); - o.ucisection = 'second'; - - o = s.taboption('alternative_config', form.DynamicList, 'second_custom_domains', _('Secondary Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); - o.placeholder = 'Domains list'; - o.depends('second_custom_domains_list_enabled', '1'); - o.rmempty = false; - o.ucisection = 'second'; - o.validate = function (section_id, value) { - if (!value || value.length === 0) { - return true; - } - - const domainRegex = /^(?!-)[A-Za-z0-9-]+([-.][A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/; - - if (!domainRegex.test(value)) { - return _('Invalid domain format. Enter domain without protocol (example: sub.example.com)'); - } - return true; - }; - - o = s.taboption('alternative_config', form.Flag, 'second_custom_subnets_list_enabled', _('Secondary Subnet List'), _('Configure custom subnets for secondary routing path')); - o.default = '0'; - o.rmempty = false; - o.depends('second_enable', '1'); - o.ucisection = 'second'; - - o = s.taboption('alternative_config', form.DynamicList, 'second_custom_subnets', _('Secondary Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); - o.placeholder = 'Subnets list'; - o.depends('second_custom_subnets_list_enabled', '1'); - o.rmempty = false; - o.ucisection = 'second'; - o.validate = function (section_id, value) { - if (!value || value.length === 0) { - return true; - } - - const subnetRegex = /^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$/; - - if (!subnetRegex.test(value)) { - return _('Invalid subnet format. Use format: X.X.X.X/Y (like 192.168.1.0/24)'); - } - - const [ip, cidr] = value.split('/'); - const ipParts = ip.split('.'); - const cidrNum = parseInt(cidr); - - for (const part of ipParts) { - const num = parseInt(part); - if (num < 0 || num > 255) { - return _('IP address parts must be between 0 and 255'); - } - } - - if (cidrNum < 0 || cidrNum > 32) { - return _('CIDR must be between 0 and 32'); - } - - return true; - }; - - return m.render(); +#!/bin/sh /etc/rc.common + +START=99 +USE_PROCD=1 + +script=$(readlink "$initscript") +NAME="$(basename ${script:-$initscript})" +config_load "$NAME" + +EXTRA_COMMANDS="list_update add_route_interface" +EXTRA_HELP=" list_update Updating domain and subnet lists + add_route_interface Adding route for interface + sing_box_config_vless For test vless string" + +config_get update_interval "main" "update_interval" "0 4 * * *" +cron_job="${update_interval} /etc/init.d/podkop list_update" + +start_service() { + log "Start podkop" + + dnsmasqfull + routing_table_create + add_mark + + config_get mode "main" "mode" + case "$mode" in + "vpn") + log "VPN mode" + log "You are using VPN mode, make sure you have installed all the necessary packages, configured, created the zone and forwarding." + config_get interface "main" "interface" "0" + if [ -n "$interface" ]; then + add_route_interface "$interface" "podkop" + else + log "Interface undefined" + fi + + config_get_bool second_enable "second" "second_enable" "0" + config_get second_mode "second" "second_mode" "0" + if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "proxy" ]; then + config_get proxy_string "second" "second_proxy_string" + if [[ "$proxy_string" =~ ^ss:// ]]; then + sing_box_config_shadowsocks "$proxy_string" "1603" + elif [[ "$proxy_string" =~ ^vless:// ]]; then + sing_box_config_vless "$proxy_string" "1603" + else + log "Unsupported proxy type: $proxy_string" + return + fi + add_route_tproxy podkop2 + sing_box_config_check + sing_box_uci + /etc/init.d/sing-box restart + /etc/init.d/sing-box enable + fi + + if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "vpn" ]; then + log "VPN mode for second" + config_get interface "second" "second_interface" "0" + if [ -n "$interface" ]; then + add_route_interface "$interface" "podkop2" + else + log "Interface undefined" + fi + fi + ;; + "proxy") + log "Proxy mode" + if ! command -v sing-box >/dev/null 2>&1; then + log "Sing-box isn't installed. Proxy mode works with sing-box" + return + fi + + # Main - proxy, Second - proxy + config_get_bool second_enable "second" "second_enable" "0" + config_get second_mode "second" "second_mode" "0" + if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "proxy" ]; then + log "Two proxy enable" + outbound_main=$(mktemp) + outbound_second=$(mktemp) + + config_get proxy_string main "proxy_string" + if [[ "$proxy_string" =~ ^ss:// ]]; then + sing_box_config_outbound_shadowsocks "$proxy_string" "$outbound_main" main + elif [[ "$proxy_string" =~ ^vless:// ]]; then + sing_box_config_outbound_vless "$proxy_string" "$outbound_main" main + else + log "Unsupported proxy type: $proxy_string" + return + fi + + config_get proxy_string "second" "second_proxy_string" + if [[ "$proxy_string" =~ ^ss:// ]]; then + sing_box_config_outbound_shadowsocks "$proxy_string" "$outbound_second" second + elif [[ "$proxy_string" =~ ^vless:// ]]; then + sing_box_config_outbound_vless "$proxy_string" "$outbound_second" second + else + log "Unsupported proxy type: $proxy_string" + return + fi + + jq --argjson outbounds "$(jq -s '{"outbounds": (.[0].outbounds + .[1].outbounds)}' "$outbound_main" "$outbound_second")" \ + '.outbounds += $outbounds.outbounds' /etc/podkop/sing-box-two-proxy-template.json >/etc/sing-box/config.json + + rm -f "$outbound_main" "$outbound_second" + + add_route_tproxy podkop + add_route_tproxy podkop2 + fi + + # Main proxy, second disable/vpn + config_get_bool second_enable "second" "second_enable" "0" + config_get second_mode "second" "second_mode" "0" + if [ "$second_enable" -eq "0" ] || [ "$second_mode" = "vpn" ]; then + config_get proxy_string main "proxy_string" + if [[ "$proxy_string" =~ ^ss:// ]]; then + sing_box_config_shadowsocks "$proxy_string" "1602" + elif [[ "$proxy_string" =~ ^vless:// ]]; then + sing_box_config_vless "$proxy_string" "1602" + else + log "Unsupported proxy type: $proxy_string" + return + fi + add_route_tproxy podkop + fi + + sing_box_config_check + sing_box_uci + /etc/init.d/sing-box restart + /etc/init.d/sing-box enable + + # Main proxy, Second VPN + config_get_bool second_enable "second" "second_enable" "0" + config_get second_mode "second" "second_mode" "0" + if [ "$second_enable" -eq "1" ] && [ "$second_mode" = "vpn" ]; then + log "VPN mode for seconds" + log "You are using VPN mode, make sure you have installed all the necessary packages, configured, created the zone and forwarding." + config_get interface "second" "second_interface" "0" + if [ -n "$interface" ]; then + add_route_interface "$interface" "podkop2" + else + log "Interface undefined" + fi + fi + ;; + *) + log "Requires *vpn* or *proxy* value" + return + ;; + esac + + list_update + + if [ "$domain_list_enabled" -eq 1 ] || [ "$subnets_list_enabled" -eq 1 ]; then + add_cron_job + fi + + config_get_bool all_traffic_from_ip_enabled "main" "all_traffic_from_ip_enabled" "0" + if [ "$all_traffic_from_ip_enabled" -eq 1 ]; then + log "Adding an IP to redirect all traffic" + config_list_foreach main all_traffic_ip list_all_traffic_from_ip + fi + + config_get_bool exclude_from_ip_enabled "main" "exclude_from_ip_enabled" "0" + if [ "$exclude_from_ip_enabled" -eq 1 ]; then + log "Adding an IP for exclusion" + config_list_foreach main exclude_traffic_ip list_exclude_traffic_from_ip + fi + + config_get_bool yacd "main" "yacd" "0" + if [ "$yacd" -eq 1 ]; then + log "Yacd enable" + jq '.experimental.clash_api = { + "external_ui": "ui", + "external_controller": "0.0.0.0:9090" + }' /etc/sing-box/config.json >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json /etc/sing-box/config.json + /etc/init.d/sing-box restart + fi + + config_get_bool socks5 "main" "socks5" "0" + if [ "$socks5" -eq 1 ]; then + log "Socks5 local enable port 2080" + jq '.inbounds += [{ + "type": "mixed", + "listen": "0.0.0.0", + "listen_port": 2080, + "set_system_proxy": false + }]' /etc/sing-box/config.json >/tmp/sing-box-config-tmp.json && mv /tmp/sing-box-config-tmp.json /etc/sing-box/config.json + /etc/init.d/sing-box restart + fi + + config_get_bool exclude_ntp "main" "exclude_ntp" "0" + if [ "$exclude_ntp" -eq 1 ]; then + log "NTP traffic exclude for proxy" + nft insert rule inet PodkopTable mangle udp dport 123 return + fi +} + +stop_service() { + log "Stopping the podkop" + rm -f /tmp/dnsmasq.d/podkop* + remove_cron_job + + log "Flush nft" + if nft list table inet PodkopTable >/dev/null 2>&1; then + nft delete table inet PodkopTable + fi + + log "Flush ip rule" + if ip rule list | grep -q "podkop"; then + ip rule del fwmark 0x105 table podkop priority 105 + fi + + if ip rule list | grep -q "podkop2"; then + ip rule del fwmark 0x106 table podkop2 priority 106 + fi + + log "Flush ip route" + if ip route list table podkop; then + ip route flush table podkop + fi + + if ip route list table podkop2; then + ip route flush table podkop2 + fi + + log "Stop sing-box" + config_get mode_main "main" "mode" "0" + config_get mode_second "second" "second_mode" "0" + + if [ "$mode_main" = "proxy" ] || [ "$mode_second" = "proxy" ]; then + /etc/init.d/sing-box stop + /etc/init.d/sing-box disable + fi +} + +restart_service() { + stop + start +} + +reload_service() { + stop + start +} + +service_triggers() { + log "service_triggers start" + procd_add_config_trigger "config.change" "$NAME" "$initscript" reload 'on_config_change' + + config_get update_interval "main" "update_interval" + if [ -n "$update_interval" ]; then + add_cron_job + fi +} + +log() { + local message="$1" + local timestamp=$(date +"%Y-%m-%d %H:%M:%S") + local CYAN="\033[0;36m" + local GREEN="\033[0;32m" + local RESET="\033[0m" + + echo -e "${CYAN}[$timestamp]${RESET} ${GREEN}$message${RESET}" + logger -t "podkop" "$timestamp $message" +} + +add_cron_job() { + remove_cron_job + crontab -l | { + cat + echo "$cron_job" + } | crontab - + log "The cron job has been created: $cron_job" +} + +remove_cron_job() { + (crontab -l | grep -v "/etc/init.d/podkop list_update") | crontab - + log "The cron job removed" +} + +list_update() { + config_get_bool domain_list_enabled "main" "domain_list_enabled" "0" + if [ "$domain_list_enabled" -eq 1 ]; then + log "Adding a common domains list" + add_set "podkop_domains" "main" + config_get domain_list main "domain_list" + lists_domains_download "$domain_list" + dnsmasq_config_check podkop-domains.lst + fi + + config_get_bool custom_domains_list_enabled "main" "custom_domains_list_enabled" "0" + if [ "$custom_domains_list_enabled" -eq 1 ]; then + log "Adding a custom domains list" + add_set "podkop_domains" "main" + rm -f /tmp/dnsmasq.d/podkop-custom-domains.lst + config_list_foreach main custom_domains "list_custom_domains_create" "podkop" + dnsmasq_config_check podkop-custom-domains.lst + fi + + config_get_bool custom_download_domains_list_enabled "main" "custom_download_domains_list_enabled" "0" + if [ "$custom_download_domains_list_enabled" -eq 1 ]; then + log "Adding a custom domains list from URL" + add_set "podkop_domains" "main" + config_list_foreach main custom_download_domains "list_custom_download_domains_create" "podkop" + fi + + config_get_bool delist_domains_enabled "main" "delist_domains_enabled" "0" + if [ "$delist_domains_enabled" -eq 1 ] && [ "$domain_list_enabled" -eq 1 ]; then + log "Exclude domains from the common list" + config_list_foreach main delist_domains "list_delist_domains" + dnsmasq_config_check podkop-domains.lst + fi + + if [ "$domain_list_enabled" -eq 1 ] || [ "$custom_domains_list_enabled" -eq 1 ]; then + /etc/init.d/dnsmasq restart + fi + + config_get_bool second_custom_domains_list_enabled "second" "second_custom_domains_list_enabled" "0" + if [ "$second_custom_domains_list_enabled" -eq 1 ]; then + log "Adding a custom domains list. Second podkop" + add_set "podkop2_domains" "second" + rm -f /tmp/dnsmasq.d/podkop2-custom-domains.lst + config_list_foreach second second_custom_domains "list_custom_domains_create" "podkop2" + dnsmasq_config_check podkop2-custom-domains.lst + fi + + config_get_bool domain_service_enabled "second" "domain_service_enabled" "0" + if [ "$domain_service_enabled" -eq 1 ]; then + log "Adding a service for podkop2" + add_set "podkop2_domains" "second" + config_get service_list second "service_list" + lists_services_download "$service_list" + config_list_foreach second second_custom_domains "list_delist_domains" + dnsmasq_config_check podkop2-domains.lst + fi + + if [ "$second_custom_domains_list_enabled" -eq 1 ] || [ "$domain_service_enabled" -eq 1 ]; then + /etc/init.d/dnsmasq restart + fi + + config_get_bool subnets_list_enabled "main" "subnets_list_enabled" "0" + if [ "$subnets_list_enabled" -eq 1 ]; then + log "Adding a subnets from list" + mkdir -p /tmp/podkop + add_set "podkop_subnets" "main" + config_list_foreach main subnets "list_subnets_download" + fi + + config_get_bool custom_download_subnets_list_enabled "main" "custom_download_subnets_list_enabled" "0" + if [ "$custom_download_subnets_list_enabled" -eq 1 ]; then + log "Adding a subnets from URL" + mkdir -p /tmp/podkop + add_set "podkop_subnets" "main" + config_list_foreach main custom_download_subnets "list_subnets_download" + fi + + config_get_bool custom_subnets_list_enabled "main" "custom_subnets_list_enabled" "0" + if [ "$custom_subnets_list_enabled" -eq 1 ]; then + log "Adding a custom subnets list" + add_set "podkop_subnets" "main" + config_list_foreach main custom_subnets "list_custom_subnets_create" "podkop" + fi + + config_get_bool second_custom_subnets_list_enabled "second" "second_custom_subnets_list_enabled" "0" + if [ "$second_custom_subnets_list_enabled" -eq 1 ]; then + log "Adding a custom subnets list. Second" + add_set "podkop2_subnets" "second" + config_list_foreach second second_custom_subnets "list_custom_subnets_create" "podkop2" + fi +} + +dnsmasqfull() { + if /usr/sbin/dnsmasq -v | grep -q "no-nftset"; then + log "Dnsmasq-full is not installed. Future: link only" + log "Use script or:" + log "cd /tmp/ && /bin/opkg download dnsmasq-full && /bin/opkg remove dnsmasq && /bin/opkg install dnsmasq-full --cache /tmp/ && cp /etc/config/dhcp /etc/config/dhcp-old && mv /etc/config/dhcp-opkg /etc/config/dhcp" + return + fi +} + +routing_table_create() { + grep -q "105 podkop" /etc/iproute2/rt_tables || echo '105 podkop' >>/etc/iproute2/rt_tables + config_get_bool second_enable "second" "second_enable" "0" + if [ "$second_enable" -eq 1 ]; then + grep -q "106 podkop2" /etc/iproute2/rt_tables || echo '106 podkop2' >>/etc/iproute2/rt_tables + fi +} + +add_set() { + local set_name="$1" + local connect="$2" + + nft add table inet PodkopTable + log "Create set $set_name" + nft add chain inet PodkopTable mangle { type filter hook prerouting priority -150 \; policy accept \;} + nft add set inet PodkopTable "$set_name" { type ipv4_addr\; flags interval\; auto-merge\; } + if [ "$connect" = "main" ]; then + config_get mode "$connect" "mode" + else + config_get mode "$connect" "second_mode" + fi + case "$mode" in + "vpn") + if ! nft list chain inet PodkopTable mangle | grep -q "ip daddr @"$set_name" meta mark set"; then + if [ "$connect" = "main" ]; then + nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta mark set 0x105 counter + elif [ "$connect" = "second" ]; then + nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta mark set 0x106 counter + fi + fi + ;; + + "proxy") + nft add chain inet PodkopTable proxy { type filter hook prerouting priority -100 \; } + if nft list table inet PodkopTable | grep -q "ip daddr @"$set_name" meta l4proto"; then + log "Nft rule tproxy exists" + else + log "Added nft rule tproxy" + if [ "$connect" = "main" ]; then + nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta l4proto tcp meta mark set 0x105 counter + nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta l4proto udp meta mark set 0x105 counter + if ! ( nft list table inet PodkopTable | grep -q "meta mark 0x00000105 meta l4proto tcp tproxy" ); then + nft add rule inet PodkopTable proxy iifname "br-lan" meta mark 0x105 meta l4proto tcp tproxy ip to :1602 counter + nft add rule inet PodkopTable proxy iifname "br-lan" meta mark 0x105 meta l4proto udp tproxy ip to :1602 counter + fi + elif [ "$connect" = "second" ]; then + nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta l4proto tcp meta mark set 0x106 counter + nft add rule inet PodkopTable mangle ip daddr @"$set_name" meta l4proto udp meta mark set 0x106 counter + if ! ( nft list table inet PodkopTable | grep -q "meta mark 0x00000106 meta l4proto tcp tproxy" ); then + nft add rule inet PodkopTable proxy iifname "br-lan" meta mark 0x106 meta l4proto tcp tproxy ip to :1603 counter + nft add rule inet PodkopTable proxy iifname "br-lan" meta mark 0x106 meta l4proto udp tproxy ip to :1603 counter + fi + fi + fi + ;; + + *) + log "Requires *vpn* or *proxy* value" + return + ;; + esac +} + +add_route_interface() { + local interface="$1" + local table="$2" + local retry_count_route=0 + local max_retries=10 + + if ! ip link show "$interface" >/dev/null 2>&1; then + log "Interface "$interface" undetected, wait 10 sec..." + sleep 10 + + if ! ip link show "$interface" >/dev/null 2>&1; then + log "Interface "$interface" undetected. exit" + return + fi + fi + + if ! ip link show "$interface" >/dev/null 2>&1; then + log "Interface "$interface" does not exist, not possible to create a route" + return + fi + + if ip route show table $table | grep -q "^default dev"; then + log "Route for "$interface" exists" + return 0 + fi + + log "Added route for "$interface"" + while [ $retry_count_route -lt $max_retries ]; do + if ip route add table $table default dev "$interface" 2>&1 | grep -q "Network is down"; then + log "Attempt $retry_count_route: Interface "$interface" is down, retrying in 3 seconds..." + sleep 3 + retry_count_route=$((retry_count_route + 1)) + else + log "Route for "$interface" added" + return 0 + fi + done + + log "The maximum number of attempts has been exceeded. Failed to add a route." + return +} + +add_route_tproxy() { + local table=$1 + if ! ip route list table $table | grep -q "local default dev lo scope host"; then + log "Added route for tproxy" + ip route add local 0.0.0.0/0 dev lo table $table + else + log "Route for tproxy exists" + fi +} + +add_mark() { + if ! ip rule list | grep -q "from all fwmark 0x105 lookup podkop"; then + log "Create marking rule" + ip -4 rule add fwmark 0x105 table podkop priority 105 + else + log "Marking rule exist" + fi + + config_get_bool second_enable "second" "second_enable" "0" + if [ "$second_enable" -eq 1 ]; then + if ! ip rule list | grep -q "from all fwmark 0x106 lookup podkop2"; then + log "Create marking rule for podkop second" + ip -4 rule add fwmark 0x106 table podkop2 priority 106 + else + log "Podkop second marking rule exist" + fi + fi +} + +lists_domains_download() { + local URL="$1" + + RU_INSIDE_DOMAINS=https://raw.githubusercontent.com/itdoginfo/allow-domains/main/Russia/inside-dnsmasq-nfset.lst + RU_OUTSIDE_DOMAINS=https://raw.githubusercontent.com/itdoginfo/allow-domains/main/Russia/outside-dnsmasq-nfset.lst + UA_DOMAINS=https://raw.githubusercontent.com/itdoginfo/allow-domains/main/Ukraine/inside-dnsmasq-nfset.lst + + case "$URL" in + "ru_inside") + URL=$RU_INSIDE_DOMAINS + ;; + "ru_outside") + URL=$RU_OUTSIDE_DOMAINS + ;; + "ua") + URL=$UA_DOMAINS + ;; + *) + log "Unidentified list of domains" + return + ;; + esac + + count=0 + while true; do + if curl -m 3 github.com; then + curl -f $URL --output /tmp/dnsmasq.d/podkop-domains.lst + if [ "$connect" = "second" ]; then + sed -i 's/fw4#vpn_domains/PodkopTable#podkop2_domains/g' /tmp/dnsmasq.d/podkop-domains.lst + else + sed -i 's/fw4#vpn_domains/PodkopTable#podkop_domains/g' /tmp/dnsmasq.d/podkop-domains.lst + fi + return 0 + else + log "GitHub is not available. Check the internet availability [$count sec]" + count=$((count + 1)) + fi + + if [ $count -lt 30 ]; then + sleep_interval=1 + elif [ $count -ge 30 ] && [ $count -lt 60 ]; then + sleep_interval=5 + elif [ $count -ge 60 ] && [ $count -lt 90 ]; then + sleep_interval=10 + else + sleep_interval=30 + fi + + sleep $sleep_interval + done +} + +lists_services_download() { + local URL="$1" + + YOUTUBE=https://raw.githubusercontent.com/itdoginfo/allow-domains/refs/heads/main/Services/youtube.lst + + case "$URL" in + "youtube") + URL=$YOUTUBE + ;; + *) + log "Unidentified list of domains" + return + ;; + esac + + count=0 + while true; do + if curl -m 3 github.com; then + curl -f $URL --output /tmp/dnsmasq.d/podkop2-domains.lst + delist_downloaded_domains + sed -i 's/.*/nftset=\/&\/4#inet#PodkopTable#podkop2_domains/g' /tmp/dnsmasq.d/podkop2-domains.lst + return 0 + else + log "GitHub is not available. Check the internet availability [$count sec]" + count=$((count + 1)) + fi + + if [ $count -lt 30 ]; then + sleep_interval=1 + elif [ $count -ge 30 ] && [ $count -lt 60 ]; then + sleep_interval=5 + elif [ $count -ge 60 ] && [ $count -lt 90 ]; then + sleep_interval=10 + else + sleep_interval=30 + fi + + sleep $sleep_interval + done +} + +list_subnets_download() { + TWITTER_SUBNETS=https://raw.githubusercontent.com/itdoginfo/allow-domains/main/Subnets/IPv4/Twitter.lst + META_SUBNETS=https://raw.githubusercontent.com/itdoginfo/allow-domains/main/Subnets/IPv4/Meta.lst + DISCORD_SUBNETS=https://raw.githubusercontent.com/itdoginfo/allow-domains/refs/heads/main/Subnets/IPv4/Discord.lst + local URL="$1" + + case "$URL" in + "twitter") + URL=$TWITTER_SUBNETS + ;; + "meta") + URL=$META_SUBNETS + ;; + "discord") + URL=$DISCORD_SUBNETS + ;; + *) + log "Custom URL for subnet" + if curl --output /dev/null --silent --head --fail "$URL"; then + log "URL is valid" + else + log "URL $URL is not valid" + fi + ;; + esac + + local filename=$(basename "$URL") + curl -f "$URL" --output "/tmp/podkop/$filename" + while IFS= read -r subnet; do + nft add element inet PodkopTable podkop_subnets { $subnet } + done <"/tmp/podkop/$filename" +} + +list_custom_domains_create() { + local domain="$1" + local name="$2" + echo "nftset=/$domain/4#inet#PodkopTable#${name}_domains" >>"/tmp/dnsmasq.d/${name}-custom-domains.lst" + log "$domain added to the list" +} + +list_custom_download_domains_create() { + local URL="$1" + local name="$2" + local filename=$(basename "$URL") + local config="/tmp/dnsmasq.d/${name}-${filename}.lst" + + rm -f $config + curl -f "$URL" --output "/tmp/podkop/${filename}" + while IFS= read -r domain; do + echo "nftset=/$domain/4#inet#PodkopTable#${name}_domains" >>$config + done <"/tmp/podkop/$filename" + dnsmasq_config_check ${name}-${filename}.lst +} + +list_custom_subnets_create() { + local subnet="$1" + local name="$2" + nft add element inet PodkopTable ${name}_subnets { $subnet } +} + +list_all_traffic_from_ip() { + local ip="$1" + if ! nft list chain inet PodkopTable mangle | grep -q "ip saddr $ip"; then + config_get mode "main" "mode" "0" + if [ "$mode" = "vpn" ]; then + nft insert rule inet PodkopTable mangle ip saddr $ip meta mark set 0x105 counter + elif [ "$mode" = "proxy" ]; then + nft add set inet PodkopTable localv4 { type ipv4_addr\; flags interval\; } + nft add element inet PodkopTable localv4 { \ + 0.0.0.0/8, \ + 10.0.0.0/8, \ + 127.0.0.0/8, \ + 169.254.0.0/16, \ + 172.16.0.0/12, \ + 192.0.0.0/24, \ + 192.0.2.0/24, \ + 192.88.99.0/24, \ + 192.168.0.0/16, \ + 198.18.0.0/15, \ + 198.51.100.0/24, \ + 203.0.113.0/24, \ + 224.0.0.0/4, \ + 240.0.0.0-255.255.255.255 } + nft insert rule inet PodkopTable mangle ip saddr $ip meta l4proto { tcp, udp } meta mark set 0x105 counter + nft insert rule inet PodkopTable mangle ip saddr $ip ip daddr @localv4 return + fi + fi +} + +list_exclude_traffic_from_ip() { + local ip="$1" + if ! nft list chain inet PodkopTable mangle | grep -q "ip saddr $ip"; then + nft insert rule inet PodkopTable mangle ip saddr $ip return + fi +} + +list_delist_domains() { + local domain="$1" + + if [ -f "/tmp/dnsmasq.d/podkop-domains.lst" ]; then + sed -i "/$domain/d" /tmp/dnsmasq.d/podkop-domains.lst + nft flush set inet PodkopTable podkop_domains + log "Strings containing '$domain' have been excluded from the list" + else + log "Config /tmp/dnsmasq.d/podkop-domains.lst not exists" + fi +} + +delist_downloaded_domains() { + local domains="/tmp/dnsmasq.d/podkop2-domains.lst" + + if [ -f "$domains" ]; then + while IFS= read -r line; do + list_delist_domains "$line" + done <"$domains" + else + log "$domains not found" + fi +} + +dnsmasq_config_check() { + local config="$1" + if ! /usr/sbin/dnsmasq --conf-file=/tmp/dnsmasq.d/$config --test 2>&1 | grep -q "syntax check OK"; then + log "Dnsmasq config $config contains errors. Break" + return + fi +} + +sing_box_uci() { + local config="/etc/config/sing-box" + if grep -q "option enabled '0'" "$config" || + grep -q "option user 'sing-box'" "$config"; then + sed -i \ + -e "s/option enabled '0'/option enabled '1'/" \ + -e "s/option user 'sing-box'/option user 'root'/" $config + log "Change sing-box UCI config" + else + log "Sing-box UCI config OK" + fi +} + +sing_box_config_shadowsocks() { + local STRING="$1" + local listen_port="$2" + + local encrypted_part=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | base64 --decode) + local method=$(echo "$encrypted_part" | cut -d':' -f1) + local password=$(echo "$encrypted_part" | cut -d':' -f2-) + + local server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) + local port=$(echo "$STRING" | sed -n 's|.*:\([0-9]\+\).*|\1|p') + local label=$(echo "$STRING" | cut -d'#' -f2) + + template_config="/etc/podkop/sing-box-shadowsocks-template.json" + + jq --arg server "$server" \ + --arg port "$port" \ + --arg method "$method" \ + --arg password "$password" \ + --arg listen_port "$listen_port" \ + '.inbounds[] |= + if .type == "tproxy" then + .listen_port = ($listen_port | tonumber) + else + . + end | + .outbounds[] |= + if .type == "shadowsocks" then + .server = $server | + .server_port = ($port | tonumber) | + .method = $method | + .password = $password + else + . + end' "$template_config" >/etc/sing-box/config.json +} + +sing_box_config_vless() { + local STRING="$1" + local listen_port="$2" + + get_param() { + echo "$STRING" | sed -n "s/.*[?&]$1=\([^&?#]*\).*/\1/p" } -}); \ No newline at end of file + + uuid=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1) + server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) + port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | awk -F'/' '{print $1}') + + type=$(get_param "type") + flow=$(get_param "flow") + sni=$(get_param "sni") + fp=$(get_param "fp") + security=$(get_param "security") + pbk=$(get_param "pbk") + sid=$(get_param "sid") + encoding=$(get_param "packetEncoding") + alpn=$(echo "$(get_param "alpn" | sed 's/%2C/,/g; s/%2F/\//g')" | jq -R -s -c 'split(",")' | sed 's/\\n//g') + label=$(echo "$STRING" | cut -d'#' -f2) + + template_config="/etc/podkop/sing-box-vless-template.json" + + jq --arg server "$server" \ + --arg port "$port" \ + --arg uuid "$uuid" \ + --arg type "$type" \ + --arg flow "$flow" \ + --arg sni "$sni" \ + --arg fp "$fp" \ + --arg security "$security" \ + --arg pbk "$pbk" \ + --arg sid "$sid" \ + --argjson alpn "$alpn" \ + --arg encoding "$encoding" \ + --arg listen_port "$listen_port" \ + '.inbounds[] |= + if .type == "tproxy" then + .listen_port = ($listen_port | tonumber) + else + . + end | + .outbounds[] |= + (.server = $server | + .server_port = ($port | tonumber) | + .uuid = $uuid | + if $security == "reality" then + if $flow == "" then del(.flow) else .flow = $flow end | + if $encoding == "" then del(.packet_encoding) else .packet_encoding = $encoding end | + .tls.server_name = $sni | + .tls.utls.fingerprint = $fp | + .tls.reality.public_key = $pbk | + .tls.reality.short_id = $sid + elif $security == "tls" then + .tls.alpn = $alpn | + .tls.server_name = $sni | + del(.flow) | + del(.tls.utls) | + del(.tls.reality) + elif $security == "" or $security == "none" then + del(.flow) | + del(.tls) + else + . + end)' "$template_config" >/etc/sing-box/config.json +} + +sing_box_config_outbound_shadowsocks() { + local STRING="$1" + local outbound="$2" + local name="$3" + + local encrypted_part=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1 | base64 --decode) + local method=$(echo "$encrypted_part" | cut -d':' -f1) + local password=$(echo "$encrypted_part" | cut -d':' -f2-) + + local server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) + local port=$(echo "$STRING" | cut -d':' -f3 | cut -d'#' -f1) + label=$(echo "$STRING" | cut -d'#' -f2) + + template_config="/etc/podkop/sing-box-shadowsocks-outbound-template.json" + + jq --arg server "$server" \ + --arg port "$port" \ + --arg method "$method" \ + --arg password "$password" \ + --arg tag "$name" \ + '.outbounds[] |= + if .type == "shadowsocks" then + .server = $server | + .server_port = ($port | tonumber) | + .method = $method | + .password = $password | + .tag = $tag + else + . + end' "$template_config" >$outbound +} + +sing_box_config_outbound_vless() { + local STRING="$1" + local outbound="$2" + local name="$3" + + get_param() { + echo "$STRING" | sed -n "s/.*[?&]$1=\([^&?#]*\).*/\1/p" + } + + uuid=$(echo "$STRING" | cut -d'/' -f3 | cut -d'@' -f1) + server=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f1) + port=$(echo "$STRING" | cut -d'@' -f2 | cut -d':' -f2 | cut -d'?' -f1 | awk -F'/' '{print $1}') + + type=$(get_param "type") + flow=$(get_param "flow") + sni=$(get_param "sni") + fp=$(get_param "fp") + security=$(get_param "security") + pbk=$(get_param "pbk") + sid=$(get_param "sid") + alpn=$(echo "$(get_param "alpn" | sed 's/%2C/,/g; s/%2F/\//g')" | jq -R -s -c 'split(",")' | sed 's/\\n//g') + encoding=$(get_param "packetEncoding") + label=$(echo "$STRING" | cut -d'#' -f2) + + template_config="/etc/podkop/sing-box-vless-outbound-template.json" + + jq --arg server "$server" \ + --arg port "$port" \ + --arg uuid "$uuid" \ + --arg type "$type" \ + --arg flow "$flow" \ + --arg sni "$sni" \ + --arg fp "$fp" \ + --arg security "$security" \ + --arg pbk "$pbk" \ + --arg sid "$sid" \ + --argjson alpn "$alpn" \ + --arg encoding "$encoding" \ + --arg tag "$name" \ + '.outbounds[] |= + (.server = $server | + .server_port = ($port | tonumber) | + .uuid = $uuid | + if $security == "reality" then + if $flow == "" then del(.flow) else .flow = $flow end | + if $encoding == "" then del(.packet_encoding) else .packet_encoding = $encoding end | + .tls.server_name = $sni | + .tls.utls.fingerprint = $fp | + .tls.reality.public_key = $pbk | + .tls.reality.short_id = $sid | + .tag = $tag + elif $security == "tls" then + .tls.alpn = $alpn | + .tls.server_name = $sni | + del(.flow) | + del(.tls.utls) | + del(.tls.reality) | + .tag = $tag + elif $security == "" or $security == "none" then + del(.flow) | + del(.tls) | + .tag = $tag + else + . + end)' "$template_config" >$outbound +} + +sing_box_config_check() { + if ! sing-box -c /etc/sing-box/config.json check >/dev/null 2>&1; then + log "Sing-box configuration is invalid" + return + fi +} \ No newline at end of file From 66c7eb0ccb9cdf4715ff4602d53479ea769e5a53 Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Mon, 11 Nov 2024 19:59:04 +0300 Subject: [PATCH 13/14] feat: Improve custom routing options --- .../htdocs/luci-static/resources/view/podkop/podkop.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 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 afbb04b..32168e6 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 @@ -349,13 +349,13 @@ return view.extend({ o.rmempty = false; o.ucisection = 'second'; - o = s.taboption('alternative_config', form.Flag, 'second_custom_domains_list_enabled', _('Domain List'), _('Configure custom domains for routing')); + o = s.taboption('alternative_config', form.Flag, 'second_custom_domains_list_enabled', _('User Domain List'), _('Enable and manage your custom list of domains for selective routing')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); o.ucisection = 'second'; - o = s.taboption('alternative_config', form.DynamicList, 'second_custom_domains', _('Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); + o = s.taboption('alternative_config', form.DynamicList, 'second_custom_domains', _('User Domains'), _('Enter domain names without protocols (example: sub.example.com or example.com)')); o.placeholder = 'Domains list'; o.depends('second_custom_domains_list_enabled', '1'); o.rmempty = false; @@ -373,13 +373,13 @@ return view.extend({ return true; }; - o = s.taboption('alternative_config', form.Flag, 'second_custom_subnets_list_enabled', _('Subnet List'), _('Configure custom subnets for routing')); + o = s.taboption('alternative_config', form.Flag, 'second_custom_subnets_list_enabled', _('User Subnet List'), _('Enable and manage your custom list of IP subnets for selective routing')); o.default = '0'; o.rmempty = false; o.depends('second_enable', '1'); o.ucisection = 'second'; - o = s.taboption('alternative_config', form.DynamicList, 'second_custom_subnets', _('Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); + o = s.taboption('alternative_config', form.DynamicList, 'second_custom_subnets', _('User Subnets'), _('Enter subnet in CIDR notation (example: 192.168.1.0/24)')); o.placeholder = 'Subnets list'; o.depends('second_custom_subnets_list_enabled', '1'); o.rmempty = false; From f520270864611c814a2059102038b351e614a4f4 Mon Sep 17 00:00:00 2001 From: Ivan Kvashonkin Date: Mon, 11 Nov 2024 21:23:25 +0300 Subject: [PATCH 14/14] refactor: Extract version from tag and add to translate ipk file --- .github/workflows/build.yml | 5 ++++- luci-app-podkop/Makefile | 10 ---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2cd624a..4509dc9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,8 +26,11 @@ jobs: - name: Filter IPK files run: | + # Извлекаем версию из тега, убирая префикс 'v' + VERSION=${GITHUB_REF#refs/tags/v} + mkdir -p ./filtered-bin - cp ./bin/luci-i18n-podkop-ru_*.ipk ./filtered-bin/ + cp ./bin/luci-i18n-podkop-ru_*.ipk "./filtered-bin/luci-i18n-podkop-ru_${VERSION}.ipk" cp ./bin/podkop_*.ipk ./filtered-bin/ cp ./bin/luci-app-podkop_*.ipk ./filtered-bin/ diff --git a/luci-app-podkop/Makefile b/luci-app-podkop/Makefile index 470371f..5ec9d55 100644 --- a/luci-app-podkop/Makefile +++ b/luci-app-podkop/Makefile @@ -17,14 +17,4 @@ LUCI_LANGUAGES:=en ru include $(TOPDIR)/feeds/luci/luci.mk -define Package/luci-i18n-$(PKG_NAME)-$(1) - SECTION:=luci - CATEGORY:=LuCI - TITLE:=$(PKG_NAME) - $(1) translation - HIDDEN:=1 - DEFAULT:=LUCI_LANG_$(1)||ALL - VERSION:=$(PKG_VERSION_TRANS) - DEPENDS:=$(PKG_NAME) -endef - # call BuildPackage - OpenWrt buildroot signature \ No newline at end of file