Streamline build (#1)

feat: Добавлена группировка по вкладкам
feat: Выбор частоты обновления списков по cron на вкладке Дополнительные настройки
feat: Перевод на русский язык
cicd: Добавлена поддержка пакета luci-i18n-podkop-ru
This commit is contained in:
Ivan K
2024-11-09 00:04:17 +03:00
committed by GitHub
parent f4485ba0b5
commit 9f5e99ab52
7 changed files with 645 additions and 109 deletions

View File

@@ -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

View File

@@ -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
RUN make defconfig && make package/podkop/compile && make package/luci-app-podkop/compile V=s

View File

@@ -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 <podkop@itdog.info>
LUCI_LANGUAGES:=en ru
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@@ -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'), _('<a href="https://github.com/itdoginfo/allow-domains" target="_blank">github.com/itdoginfo/allow-domains</a>'));
o = s.taboption('basic', form.Flag, 'domain_list_enabled', _('Predefined Domain Lists'), _('Enable routing based on predefined domain lists for specific regions\n<a href="https://github.com/itdoginfo/allow-domains" target="_blank">github.com/itdoginfo/allow-domains</a>'));
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();
}
});
});

View File

@@ -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)"

View File

@@ -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 ""

View File

@@ -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"
}