Merge pull request #6 from VizzleTF/main

Вкладки + cron + перевод + ci
This commit is contained in:
itdoginfo
2024-11-12 16:09:15 +03:00
committed by GitHub
8 changed files with 763 additions and 131 deletions

View File

@@ -1,15 +1,12 @@
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
@@ -27,10 +24,20 @@ jobs:
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: |
# Извлекаем версию из тега, убирая префикс 'v'
VERSION=${GITHUB_REF#refs/tags/v}
mkdir -p ./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/
- 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,6 +1,6 @@
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/
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

View File

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

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
@@ -10,10 +7,14 @@ PKG_RELEASE:=1
LUCI_TITLE:=LuCI podkop app
LUCI_DEPENDS:=+luci-base +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

@@ -8,29 +8,30 @@ 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;
o = s.tab('main', _('Main'));
// Basic Settings Tab
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.ucisection = 'main';
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;
o.ucisection = 'main';
// 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');
o.ucisection = 'main';
try {
const devices = await network.getDevices();
const excludeInterfaces = ['br-lan', 'eth0', 'eth1', 'wan', 'phy0-ap0', 'phy1-ap0'];
devices.forEach(function (device) {
@@ -41,157 +42,284 @@ 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'), _('<a href="https://github.com/itdoginfo/allow-domains" target="_blank">github.com/itdoginfo/allow-domains</a>'));
o.default = '0';
o.rmempty = false;
o.ucisection = 'main';
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');
o.value('ua', 'Ukraine');
o.depends('domain_list_enabled', '1');
o.rmempty = false;
o.ucisection = 'main';
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.ucisection = 'main';
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.ucisection = 'main';
o = s.taboption('main', form.Flag, 'custom_domains_list_enabled', _('Custom domains enable'));
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('main', form.DynamicList, 'custom_domains', _('Your domains'));
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;
o.validate = function(section_id, value) {
// Чтобы валидация не ругалась на пустое поле
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,}$/;
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('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('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;
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('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('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;
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', form.Flag, 'custom_download_domains_list_enabled', _('URL domains enable'));
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('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('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;
o.ucisection = 'main';
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.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('main', form.DynamicList, 'all_traffic_ip', _('Local IPs'));
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('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('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;
o.ucisection = 'main';
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('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('main', form.DynamicList, 'exclude_traffic_ip', _('Local IPs'));
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;
o.ucisection = 'main';
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.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');
o.rmempty = false;
o.ucisection = 'main';
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.ucisection = 'main';
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.ucisection = 'main';
// 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.ucisection = 'main';
o = s.tab('second', _('Second'));
o = s.tab('alternative_config', _('Alternative Config'));
o = s.taboption('second', form.Flag, 'second_enable', _('Second enable'));
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';
o = s.taboption('second', form.ListValue, 'mode', _('Mode'), _('Select VPN or Proxy'));
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', form.Value, 'proxy_string', _('Proxy String'), _('String vless:// or ss://'));
o.depends('mode', 'proxy');
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';
// Get all interface
o = s.taboption('second', form.ListValue, 'interface', _('Interface'), _('Specify the interface'));
o.depends('mode', 'vpn');
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) {
@@ -202,44 +330,88 @@ 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('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', form.ListValue, 'service_list', _('Service list'), _('Select a list'));
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', form.Flag, 'custom_domains_list_enabled', _('Custom domains enable'));
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('second', form.DynamicList, 'custom_domains', _('Your domains'));
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('custom_domains_list_enabled', '1');
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;
}
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('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('second', form.DynamicList, 'custom_subnets', _('Your subnet'));
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('custom_subnets_list_enabled', '1');
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();
}

View File

@@ -0,0 +1,221 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Podkop configuration"
msgstr "Настройка Podkop"
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 "Тип подключения"
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 "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 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 "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 "Yacd enable"
msgstr "Включить Yacd"
msgid "Mixed enable"
msgstr "Включить смешанный режим"
msgid "Browser port: 2080"
msgstr "Порт браузера: 2080"
msgid "Exclude NTP"
msgstr "Исключить NTP"
msgid "For issues with open connections sing-box"
msgstr "Для проблем с открытыми соединениями sing-box"
msgid "Service Domain List Enable"
msgstr "Включить список доменов сервисов"
msgid "Enable predefined service domain lists for routing"
msgstr "Включить предустановленные списки доменов для маршрутизации"
msgid "Service List"
msgstr "Список сервисов"
msgid "Select predefined services for routing"
msgstr "Выберите предустановленные сервисы для маршрутизации"
msgid "Domains"
msgstr "Домены"
msgid "Subnet List"
msgstr "Список подсетей"
msgid "Configure custom subnets for routing"
msgstr "Настройка пользовательских подсетей для маршрутизации"
msgid "Subnets"
msgstr "Подсети"
msgid "Invalid domain format. Enter domain without protocol (example: sub.example.com)"
msgstr "Неверный формат домена. Введите домен без протокола (пример: sub.example.com)"
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,221 @@
msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8"
msgid "Podkop configuration"
msgstr ""
msgid "Basic Settings"
msgstr ""
msgid "Additional Settings"
msgstr ""
msgid "Alternative Config"
msgstr ""
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 ""
msgid "Enable and manage your custom list of domains for selective routing"
msgstr ""
msgid "User Domains"
msgstr ""
msgid "Enter domain names without protocols (example: sub.example.com or example.com)"
msgstr ""
msgid "Remote Domain Lists"
msgstr ""
msgid "Download and use domain lists from remote URLs"
msgstr ""
msgid "Remote Domain URLs"
msgstr ""
msgid "Enter full URLs starting with http:// or https://"
msgstr ""
msgid "User Subnet List"
msgstr ""
msgid "Enable and manage your custom list of IP subnets for selective routing"
msgstr ""
msgid "User Subnets"
msgstr ""
msgid "Enter subnet in CIDR notation (example: 192.168.1.0/24)"
msgstr ""
msgid "Remote Subnet Lists"
msgstr ""
msgid "Download and use subnet lists from remote URLs"
msgstr ""
msgid "Remote Subnet URLs"
msgstr ""
msgid "Domain Exclusions"
msgstr ""
msgid "Exclude specific domains from routing rules"
msgstr ""
msgid "Excluded Domains"
msgstr ""
msgid "Domains to be excluded from routing"
msgstr ""
msgid "Force Proxy IPs"
msgstr ""
msgid "Specify local IP addresses whose traffic will always use the configured route"
msgstr ""
msgid "Local IPs"
msgstr ""
msgid "Enter valid IPv4 addresses"
msgstr ""
msgid "Bypass Proxy IPs"
msgstr ""
msgid "Specify local IP addresses that will never use the configured route"
msgstr ""
msgid "List Update Frequency"
msgstr ""
msgid "Select how often the lists will be updated"
msgstr ""
msgid "Every hour"
msgstr ""
msgid "Every 2 hours"
msgstr ""
msgid "Every 4 hours"
msgstr ""
msgid "Every 6 hours"
msgstr ""
msgid "Every 12 hours"
msgstr ""
msgid "Once a day at 04:00"
msgstr ""
msgid "Once a week on Sunday at 04:00"
msgstr ""
msgid "Yacd enable"
msgstr ""
msgid "Mixed enable"
msgstr ""
msgid "Browser port: 2080"
msgstr ""
msgid "Exclude NTP"
msgstr ""
msgid "For issues with open connections sing-box"
msgstr ""
msgid "Service Domain List Enable"
msgstr ""
msgid "Enable predefined service domain lists for routing"
msgstr ""
msgid "Service List"
msgstr ""
msgid "Select predefined services for routing"
msgstr ""
msgid "Domains"
msgstr ""
msgid "Subnet List"
msgstr ""
msgid "Configure custom subnets for routing"
msgstr ""
msgid "Subnets"
msgstr ""
msgid "Invalid domain format. Enter domain without protocol (example: sub.example.com)"
msgstr ""
msgid "URL must use http:// or https:// protocol"
msgstr ""
msgid "Invalid URL format. URL must start with http:// or https://"
msgstr ""
msgid "Invalid subnet format. Use format: X.X.X.X/Y (like 192.168.1.0/24)"
msgstr ""
msgid "IP address parts must be between 0 and 255"
msgstr ""
msgid "CIDR must be between 0 and 32"
msgstr ""
msgid "Invalid IP format. Use format: X.X.X.X (like 192.168.1.1)"
msgstr ""

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"
@@ -34,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
@@ -52,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
@@ -71,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)
@@ -87,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
@@ -108,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"
@@ -129,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
@@ -224,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
@@ -245,6 +246,11 @@ 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
add_cron_job
fi
}
log() {
@@ -259,18 +265,16 @@ log() {
}
add_cron_job() {
if ! crontab -l | grep -q "podkop"; then
#echo "$cron_job" >>/etc/crontabs/root
remove_cron_job
crontab -l | {
cat
echo "$cron_job"
} | crontab -
log "The cron job has been created"
fi
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"
}
@@ -311,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
@@ -327,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
@@ -358,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
}
@@ -391,7 +394,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\; }
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
@@ -532,7 +539,11 @@ lists_domains_download() {
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]"
@@ -840,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"