From 4202530585e4e26681184699c1a03a7fb439d65f Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sat, 7 Mar 2026 14:55:11 +0300
Subject: [PATCH 01/75] add HWID and subscription support
---
README.md | 55 +++++-
fe-app-podkop/src/constants.ts | 9 +
.../methods/custom/getDashboardSections.ts | 35 ++++
.../src/podkop/methods/shell/index.ts | 2 +
fe-app-podkop/src/podkop/types.ts | 9 +
install.sh | 10 +-
.../luci-static/resources/view/podkop/main.js | 42 ++++-
.../resources/view/podkop/section.js | 48 ++++-
podkop/files/etc/config/podkop | 12 +-
podkop/files/usr/bin/podkop | 174 +++++++++++++++++-
podkop/files/usr/lib/constants.sh | 1 +
podkop/files/usr/lib/helpers.sh | 91 +++++++++
.../files/usr/lib/sing_box_config_facade.sh | 96 ++++++++++
13 files changed, 563 insertions(+), 21 deletions(-)
diff --git a/README.md b/README.md
index a938bc1f..2b8ac9ca 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,11 @@
+# Podkop Evolution
+
+> **Podkop's fork with HWID and Subscription URL support**
+>
+> Этот форк добавляет поддержку ссылок подписки (subscription URL) с кастомными заголовками (HWID, Device-OS, Device-Model) и автоматическим обновлением. Основан на [itdoginfo/podkop](https://github.com/itdoginfo/podkop).
+
+---
+
# Вещи, которые вам нужно знать перед установкой
- Это бета-версия, которая находится в активной разработке. Из версии в версию что-то может меняться.
@@ -16,12 +24,45 @@
# Документация
https://podkop.net/
-# Установка Podkop
+# Установка Podkop Evolution
Полная информация в [документации](https://podkop.net/docs/install/)
Вкратце, достаточно одного скрипта для установки и обновления:
```
-sh <(wget -O - https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh)
+sh <(wget -O - https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/install.sh)
+```
+
+## Новое в этом форке: Подписки (Subscription)
+
+Добавлена поддержка subscription URL — ссылки подписки от провайдера прокси. При выборе типа конфигурации **Subscription** в LuCI:
+
+- Введите URL подписки от вашего провайдера
+- Выберите интервал автообновления (от 30 минут до 1 дня)
+- Все серверы из подписки автоматически появятся в дашборде
+- Автоматический выбор лучшего сервера по задержке (URLTest)
+- Ручное переключение между серверами через дашборд
+
+При скачивании подписки отправляются заголовки:
+- `User-Agent: singbox/<версия>`
+- `X-HWID` — уникальный идентификатор роутера
+- `X-Device-OS: OpenWrt Linux`
+- `X-Device-Model` — модель роутера
+- `X-Ver-OS` — версия ядра
+
+Пример конфигурации через UCI:
+```
+uci set podkop.my_sub=section
+uci set podkop.my_sub.connection_type='proxy'
+uci set podkop.my_sub.proxy_config_type='subscription'
+uci set podkop.my_sub.subscription_url='https://your-provider.com/api/sub'
+uci set podkop.my_sub.subscription_update_interval='1h'
+uci add_list podkop.my_sub.community_lists='russia_inside'
+uci commit podkop
+```
+
+Ручное обновление подписки:
+```
+/usr/bin/podkop subscription_update
```
## Изменения 0.7.0
@@ -38,7 +79,7 @@ mv /etc/config/podkop /etc/config/podkop-070
```
2. Стянуть новый дефолтный конфиг:
```
-wget -O /etc/config/podkop https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/podkop/files/etc/config/podkop
+wget -O /etc/config/podkop https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/podkop/files/etc/config/podkop
```
3. Настроить заново ваш Podkop через Luci или UCI.
@@ -48,14 +89,12 @@ wget -O /etc/config/podkop https://raw.githubusercontent.com/itdoginfo/podkop/re
> PR принимаются только по issues, у которых стоит label "enhancement". Либо по согласованию с авторами в ТГ-чате. Остальные PR на данный момент не рассматриваются.
## Будущее
-- [ ] [Подписка](https://github.com/itdoginfo/podkop/issues/118). Здесь нужна реализация, чтоб для каждой секции помимо ручного выбора, был выбор фильтрации по тегу. Например, для main выбираем ключевые слова NL, DE, FI. А для extra секции фильтруем по RU. И создаётся outbound c urltest в которых перечислены outbound из фильтров.
+- [x] [Подписка](https://github.com/itdoginfo/podkop/issues/118) — **реализовано в этом форке!**
- [ ] Весь трафик в sing-box и маршрутизация полностью на его уровне.
-- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. Вопрос в том, как это искусственно провернуть. Попробовать положить прокси и посмотреть, останется ли работать DNS в этом случае. И здесь, вероятно, можно обойтись триггером в init.d. [Issue](https://github.com/itdoginfo/podkop/issues/111)
+- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. [Issue](https://github.com/itdoginfo/podkop/issues/111)
- [ ] Галочка, которая режет доступ к doh серверам.
- [ ] IPv6. Только после наполнения Wiki.
## Тесты
- [ ] Unit тесты (BATS)
-- [ ] Интеграционные тесты бекенда (OpenWrt rootfs + BATS)
-
-[](https://deepwiki.com/itdoginfo/podkop)
\ No newline at end of file
+- [ ] Интеграционные тесты бекенда (OpenWrt rootfs + BATS)
\ No newline at end of file
diff --git a/fe-app-podkop/src/constants.ts b/fe-app-podkop/src/constants.ts
index 6f840d14..19e0907c 100644
--- a/fe-app-podkop/src/constants.ts
+++ b/fe-app-podkop/src/constants.ts
@@ -66,6 +66,15 @@ export const UPDATE_INTERVAL_OPTIONS = {
'3d': 'Every 3 days',
};
+export const SUBSCRIPTION_UPDATE_INTERVAL_OPTIONS = {
+ '30m': 'Every 30 minutes',
+ '1h': 'Every hour',
+ '3h': 'Every 3 hours',
+ '6h': 'Every 6 hours',
+ '12h': 'Every 12 hours',
+ '1d': 'Every day',
+};
+
export const DNS_SERVER_OPTIONS = {
'1.1.1.1': '1.1.1.1 (Cloudflare)',
'8.8.8.8': '8.8.8.8 (Google)',
diff --git a/fe-app-podkop/src/podkop/methods/custom/getDashboardSections.ts b/fe-app-podkop/src/podkop/methods/custom/getDashboardSections.ts
index 6696d66b..554caa6d 100644
--- a/fe-app-podkop/src/podkop/methods/custom/getDashboardSections.ts
+++ b/fe-app-podkop/src/podkop/methods/custom/getDashboardSections.ts
@@ -154,6 +154,41 @@ export async function getDashboardSections(): Promise proxy.code === `${section['.name']}-out`,
+ );
+ const outbound = proxies.find(
+ (proxy) => proxy.code === `${section['.name']}-urltest-out`,
+ );
+
+ const outbounds = (outbound?.value?.all ?? [])
+ .map((code) => proxies.find((item) => item.code === code))
+ .map((item) => ({
+ code: item?.code || '',
+ displayName: item?.value?.name || '',
+ latency: item?.value?.history?.[0]?.delay || 0,
+ type: item?.value?.type || '',
+ selected: selector?.value?.now === item?.code,
+ }));
+
+ return {
+ withTagSelect: true,
+ code: selector?.code || section['.name'],
+ displayName: section['.name'],
+ outbounds: [
+ {
+ code: outbound?.code || '',
+ displayName: _('Fastest'),
+ latency: outbound?.value?.history?.[0]?.delay || 0,
+ type: outbound?.value?.type || '',
+ selected: selector?.value?.now === outbound?.code,
+ },
+ ...outbounds,
+ ],
+ };
+ }
}
if (section.connection_type === 'vpn') {
diff --git a/fe-app-podkop/src/podkop/methods/shell/index.ts b/fe-app-podkop/src/podkop/methods/shell/index.ts
index e9c29406..6b0337a4 100644
--- a/fe-app-podkop/src/podkop/methods/shell/index.ts
+++ b/fe-app-podkop/src/podkop/methods/shell/index.ts
@@ -84,4 +84,6 @@ export const PodkopShellMethods = {
callBaseMethod(
Podkop.AvailableMethods.GET_SYSTEM_INFO,
),
+ subscriptionUpdate: async () =>
+ callBaseMethod(Podkop.AvailableMethods.SUBSCRIPTION_UPDATE),
};
diff --git a/fe-app-podkop/src/podkop/types.ts b/fe-app-podkop/src/podkop/types.ts
index 672ec0cd..e93ab032 100644
--- a/fe-app-podkop/src/podkop/types.ts
+++ b/fe-app-podkop/src/podkop/types.ts
@@ -65,6 +65,7 @@ export namespace Podkop {
SHOW_SING_BOX_CONFIG = 'show_sing_box_config',
CHECK_LOGS = 'check_logs',
GET_SYSTEM_INFO = 'get_system_info',
+ SUBSCRIPTION_UPDATE = 'subscription_update',
}
export enum AvailableClashAPIMethods {
@@ -113,6 +114,13 @@ export namespace Podkop {
outbound_json: string;
}
+ export interface ConfigProxySubscriptionSection {
+ connection_type: 'proxy';
+ proxy_config_type: 'subscription';
+ subscription_url: string;
+ subscription_update_interval?: string;
+ }
+
export interface ConfigVpnSection {
connection_type: 'vpn';
interface: string;
@@ -127,6 +135,7 @@ export namespace Podkop {
| ConfigProxySelectorSection
| ConfigProxyUrlSection
| ConfigProxyOutboundSection
+ | ConfigProxySubscriptionSection
| ConfigVpnSection
| ConfigBlockSection;
diff --git a/install.sh b/install.sh
index 6376d7c2..6fde5176 100755
--- a/install.sh
+++ b/install.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# shellcheck shell=dash
-REPO="https://api.github.com/repos/itdoginfo/podkop/releases/latest"
+REPO="https://api.github.com/repos/yandexru45/podkop-evolution/releases/latest"
DOWNLOAD_DIR="/tmp/podkop"
COUNT=3
@@ -66,7 +66,7 @@ update_config() {
printf "\033[48;5;196m\033[1m║ ! Обнаружена старая версия podkop. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Если продолжите обновление, вам потребуется настроить Podkop заново. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Старая конфигурация будет сохранена в /etc/config/podkop-070 ║\033[0m\n"
- printf "\033[48;5;196m\033[1m║ Подробности: https://github.com/itdoginfo/podkop ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Подробности: https://github.com/yandexru45/podkop-evolution ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Точно хотите продолжить? ║\033[0m\n"
printf "\033[48;5;196m\033[1m╚══════════════════════════════════════════════════════════════════════╝\033[0m\n"
@@ -76,7 +76,7 @@ update_config() {
printf "\033[48;5;196m\033[1m║ ! Detected old podkop version. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ If you continue the update, you will need to RECONFIGURE podkop. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Your old configuration will be saved to /etc/config/podkop-070 ║\033[0m\n"
- printf "\033[48;5;196m\033[1m║ Details: https://github.com/itdoginfo/podkop ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Details: https://github.com/yandexru45/podkop-evolution ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Are you sure you want to continue? ║\033[0m\n"
printf "\033[48;5;196m\033[1m╚══════════════════════════════════════════════════════════════════════╝\033[0m\n"
@@ -88,7 +88,7 @@ update_config() {
yes|y|Y)
mv /etc/config/podkop /etc/config/podkop-070
- wget -O /etc/config/podkop https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/podkop/files/etc/config/podkop
+ wget -O /etc/config/podkop https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/podkop/files/etc/config/podkop
msg "Podkop config has been reset to default. Your old config saved in /etc/config/podkop-070"
break
;;
@@ -115,7 +115,7 @@ main() {
fi
if command -v curl >/dev/null 2>&1; then
- check_response=$(curl -s "https://api.github.com/repos/itdoginfo/podkop/releases/latest")
+ check_response=$(curl -s "https://api.github.com/repos/yandexru45/podkop-evolution/releases/latest")
if echo "$check_response" | grep -q 'API rate limit '; then
msg "You've reached the GitHub rate limit. Repeat in five minutes."
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
index c8fae381..6ff47115 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
+++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
@@ -617,6 +617,7 @@ var Podkop;
AvailableMethods2["SHOW_SING_BOX_CONFIG"] = "show_sing_box_config";
AvailableMethods2["CHECK_LOGS"] = "check_logs";
AvailableMethods2["GET_SYSTEM_INFO"] = "get_system_info";
+ AvailableMethods2["SUBSCRIPTION_UPDATE"] = "subscription_update";
})(AvailableMethods = Podkop2.AvailableMethods || (Podkop2.AvailableMethods = {}));
let AvailableClashAPIMethods;
((AvailableClashAPIMethods2) => {
@@ -691,7 +692,8 @@ var PodkopShellMethods = {
checkLogs: async () => callBaseMethod(Podkop.AvailableMethods.CHECK_LOGS),
getSystemInfo: async () => callBaseMethod(
Podkop.AvailableMethods.GET_SYSTEM_INFO
- )
+ ),
+ subscriptionUpdate: async () => callBaseMethod(Podkop.AvailableMethods.SUBSCRIPTION_UPDATE)
};
// src/podkop/methods/custom/getDashboardSections.ts
@@ -811,6 +813,36 @@ async function getDashboardSections() {
]
};
}
+ if (section.proxy_config_type === "subscription") {
+ const selector = proxies.find(
+ (proxy) => proxy.code === `${section[".name"]}-out`
+ );
+ const outbound = proxies.find(
+ (proxy) => proxy.code === `${section[".name"]}-urltest-out`
+ );
+ const outbounds = (outbound?.value?.all ?? []).map((code) => proxies.find((item) => item.code === code)).map((item) => ({
+ code: item?.code || "",
+ displayName: item?.value?.name || "",
+ latency: item?.value?.history?.[0]?.delay || 0,
+ type: item?.value?.type || "",
+ selected: selector?.value?.now === item?.code
+ }));
+ return {
+ withTagSelect: true,
+ code: selector?.code || section[".name"],
+ displayName: section[".name"],
+ outbounds: [
+ {
+ code: outbound?.code || "",
+ displayName: _("Fastest"),
+ latency: outbound?.value?.history?.[0]?.delay || 0,
+ type: outbound?.value?.type || "",
+ selected: selector?.value?.now === outbound?.code
+ },
+ ...outbounds
+ ]
+ };
+ }
}
if (section.connection_type === "vpn") {
const outbound = proxies.find(
@@ -921,6 +953,14 @@ var UPDATE_INTERVAL_OPTIONS = {
"1d": "Every day",
"3d": "Every 3 days"
};
+var SUBSCRIPTION_UPDATE_INTERVAL_OPTIONS = {
+ "30m": "Every 30 minutes",
+ "1h": "Every hour",
+ "3h": "Every 3 hours",
+ "6h": "Every 6 hours",
+ "12h": "Every 12 hours",
+ "1d": "Every day"
+};
var DNS_SERVER_OPTIONS = {
"1.1.1.1": "1.1.1.1 (Cloudflare)",
"8.8.8.8": "8.8.8.8 (Google)",
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js
index dc619bcb..16f71fd4 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js
+++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js
@@ -26,6 +26,7 @@ function createSectionContent(section) {
o.value("url", _("Connection URL"));
o.value("selector", _("Selector"));
o.value("urltest", _("URLTest"));
+ o.value("subscription", _("Subscription"));
o.value("outbound", _("Outbound Config"));
o.default = "url";
o.depends("connection_type", "proxy");
@@ -82,6 +83,44 @@ function createSectionContent(section) {
return validation.message;
};
+ o = section.option(
+ form.Value,
+ "subscription_url",
+ _("Subscription URL"),
+ _("Enter the subscription URL to fetch proxy configurations from your provider"),
+ );
+ o.depends("proxy_config_type", "subscription");
+ o.placeholder = "https://example.com/api/sub";
+ o.rmempty = false;
+ o.validate = function (section_id, value) {
+ if (!value || value.length === 0) {
+ return true;
+ }
+
+ const validation = main.validateUrl(value);
+
+ if (validation.valid) {
+ return true;
+ }
+
+ return validation.message;
+ };
+
+ o = section.option(
+ form.ListValue,
+ "subscription_update_interval",
+ _("Subscription Update Interval"),
+ _("How often to automatically update the subscription"),
+ );
+ o.value("30m", _("Every 30 minutes"));
+ o.value("1h", _("Every hour"));
+ o.value("3h", _("Every 3 hours"));
+ o.value("6h", _("Every 6 hours"));
+ o.value("12h", _("Every 12 hours"));
+ o.value("1d", _("Every day"));
+ o.default = "1h";
+ o.depends("proxy_config_type", "subscription");
+
o = section.option(
form.DynamicList,
"selector_proxy_links",
@@ -140,6 +179,7 @@ function createSectionContent(section) {
o.value("5m", _("Every 5 minutes"));
o.default = "3m";
o.depends("proxy_config_type", "urltest");
+ o.depends("proxy_config_type", "subscription");
o = section.option(
form.Value,
@@ -150,6 +190,7 @@ function createSectionContent(section) {
o.default = "50";
o.rmempty = false;
o.depends("proxy_config_type", "urltest");
+ o.depends("proxy_config_type", "subscription");
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
@@ -177,6 +218,7 @@ function createSectionContent(section) {
o.default = "https://www.gstatic.com/generate_204";
o.rmempty = false;
o.depends("proxy_config_type", "urltest");
+ o.depends("proxy_config_type", "subscription");
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
@@ -298,7 +340,7 @@ function createSectionContent(section) {
"community_lists",
_("Community Lists"),
_("Select a predefined list for routing") +
- ' github.com/itdoginfo/allow-domains',
+ ' github.com/itdoginfo/allow-domains',
);
o.placeholder = "Service list";
Object.entries(main.DOMAIN_LIST_OPTIONS).forEach(([key, label]) => {
@@ -505,7 +547,7 @@ function createSectionContent(section) {
_("User Subnets List"),
_(
"Enter subnets in CIDR notation or single IP addresses, separated by commas, spaces, or newlines. " +
- "You can add comments using //",
+ "You can add comments using //",
),
);
o.placeholder =
@@ -678,7 +720,7 @@ function createSectionContent(section) {
_("Mixed Proxy Port"),
_(
"Specify the port number on which the mixed proxy will run for this section. " +
- "Make sure the selected port is not used by another service",
+ "Make sure the selected port is not used by another service",
),
);
o.rmempty = false;
diff --git a/podkop/files/etc/config/podkop b/podkop/files/etc/config/podkop
index 27003cfa..ff8d9766 100644
--- a/podkop/files/etc/config/podkop
+++ b/podkop/files/etc/config/podkop
@@ -37,4 +37,14 @@ config section 'main'
#list remote_subnet_lists 'https://example.com/subnets.srs'
#list fully_routed_ips '192.168.1.2'
#option mixed_proxy_enabled '1'
- #option mixed_proxy_port '2080'
\ No newline at end of file
+ #option mixed_proxy_port '2080'
+
+#config section 'subscription_example'
+# option connection_type 'proxy'
+# option proxy_config_type 'subscription'
+# option subscription_url 'https://example.com/api/sub'
+# option subscription_update_interval '1h'
+# #option urltest_check_interval '3m'
+# #option urltest_tolerance '50'
+# #option urltest_testing_url 'https://www.gstatic.com/generate_204'
+# list community_lists 'russia_inside'
\ No newline at end of file
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index e26164a0..7dd2c579 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -125,6 +125,7 @@ start_main() {
mkdir -p "$TMP_SING_BOX_FOLDER"
mkdir -p "$TMP_RULESET_FOLDER"
+ mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
# base
route_table_rule_mark
@@ -134,6 +135,7 @@ start_main() {
# sing-box
sing_box_init_config
config_foreach add_cron_job "section"
+ config_foreach add_subscription_cron_job "section"
/etc/init.d/sing-box start
log "Nice"
@@ -474,10 +476,54 @@ add_cron_job() {
}
remove_cron_job() {
- (crontab -l | grep -v "/usr/bin/podkop list_update") | crontab -
+ (crontab -l | grep -v "/usr/bin/podkop list_update" | grep -v "/usr/bin/podkop subscription_update") | crontab -
log "The cron job removed"
}
+add_subscription_cron_job() {
+ local section="$1"
+ local proxy_config_type subscription_update_interval cron_job
+
+ config_get proxy_config_type "$section" "proxy_config_type"
+ if [ "$proxy_config_type" != "subscription" ]; then
+ return
+ fi
+
+ config_get subscription_update_interval "$section" "subscription_update_interval" "1h"
+
+ case "$subscription_update_interval" in
+ "30m")
+ cron_job="*/30 * * * * /usr/bin/podkop subscription_update"
+ ;;
+ "1h")
+ cron_job="17 * * * * /usr/bin/podkop subscription_update"
+ ;;
+ "3h")
+ cron_job="17 */3 * * * /usr/bin/podkop subscription_update"
+ ;;
+ "6h")
+ cron_job="17 */6 * * * /usr/bin/podkop subscription_update"
+ ;;
+ "12h")
+ cron_job="17 */12 * * * /usr/bin/podkop subscription_update"
+ ;;
+ "1d")
+ cron_job="17 9 * * * /usr/bin/podkop subscription_update"
+ ;;
+ *)
+ log "Invalid subscription_update_interval value: $subscription_update_interval"
+ return
+ ;;
+ esac
+
+ # Avoid duplicate subscription cron
+ (crontab -l | grep -v "/usr/bin/podkop subscription_update") | {
+ cat
+ echo "$cron_job"
+ } | crontab -
+ log "The subscription cron job has been created: $cron_job"
+}
+
list_update() {
echolog "🔄 Starting lists update..."
@@ -546,6 +592,76 @@ list_update() {
fi
}
+subscription_update() {
+ echolog "🔄 Starting subscription update..."
+
+ local has_subscription=0
+
+ _check_subscription_section() {
+ local section="$1"
+ local proxy_config_type
+ config_get proxy_config_type "$section" "proxy_config_type"
+ if [ "$proxy_config_type" = "subscription" ]; then
+ has_subscription=1
+ fi
+ }
+ config_foreach _check_subscription_section "section"
+
+ if [ "$has_subscription" -eq 0 ]; then
+ echolog "ℹ️ No subscription sections found, nothing to update"
+ return 0
+ fi
+
+ _update_subscription_for_section() {
+ local section="$1"
+ local proxy_config_type subscription_url subscription_json_path
+
+ config_get proxy_config_type "$section" "proxy_config_type"
+ if [ "$proxy_config_type" != "subscription" ]; then
+ return
+ fi
+
+ config_get subscription_url "$section" "subscription_url"
+ if [ -z "$subscription_url" ]; then
+ echolog "❌ Subscription URL not set for section '$section'"
+ return
+ fi
+
+ mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
+ subscription_json_path="$TMP_SUBSCRIPTION_FOLDER/${section}.json"
+
+ echolog "📥 Updating subscription for section '$section'..."
+
+ local service_proxy_address
+ service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
+
+ # Remove old cached file to force re-download
+ rm -f "$subscription_json_path"
+ download_subscription "$subscription_url" "$subscription_json_path" "$service_proxy_address"
+
+ if [ ! -f "$subscription_json_path" ] || [ ! -s "$subscription_json_path" ]; then
+ echolog "❌ Failed to download subscription for section '$section'"
+ return
+ fi
+
+ local outbounds_count
+ outbounds_count=$(jq -r '[.outbounds[] | select(
+ .type != "selector" and
+ .type != "urltest" and
+ .type != "direct" and
+ .type != "dns" and
+ .type != "block"
+ )] | length' "$subscription_json_path" 2>/dev/null)
+
+ echolog "✅ Subscription updated for section '$section': $outbounds_count outbounds"
+ }
+ config_foreach _update_subscription_for_section "section"
+
+ echolog "🔄 Restarting podkop to apply updated subscriptions..."
+ restart
+ echolog "✅ Subscription update completed"
+}
+
# sing-box funcs
sing_box_configure_service() {
local sing_box_enabled sing_box_user sing_box_config_path sing_box_conffile
@@ -712,6 +828,54 @@ configure_outbound_handler() {
"$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag")"
;;
+ subscription)
+ log "Detected proxy configuration type: subscription" "debug"
+ local subscription_url subscription_json_path urltest_tag selector_tag \
+ urltest_outbounds selector_outbounds urltest_check_interval urltest_tolerance urltest_testing_url
+
+ config_get subscription_url "$section" "subscription_url"
+ config_get urltest_check_interval "$section" "urltest_check_interval" "3m"
+ config_get urltest_tolerance "$section" "urltest_tolerance" 50
+ config_get urltest_testing_url "$section" "urltest_testing_url" "https://www.gstatic.com/generate_204"
+
+ if [ -z "$subscription_url" ]; then
+ log "Subscription URL is not set. Aborted." "fatal"
+ exit 1
+ fi
+
+ mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
+ subscription_json_path="$TMP_SUBSCRIPTION_FOLDER/${section}.json"
+
+ # Download subscription if not cached or force update
+ if [ ! -f "$subscription_json_path" ]; then
+ log "Downloading subscription for section '$section'"
+ local service_proxy_address
+ service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
+ download_subscription "$subscription_url" "$subscription_json_path" "$service_proxy_address"
+
+ if [ ! -f "$subscription_json_path" ] || [ ! -s "$subscription_json_path" ]; then
+ log "Failed to download subscription for section '$section'. Aborted." "fatal"
+ exit 1
+ fi
+ fi
+
+ # Parse subscription outbounds
+ config="$(sing_box_cf_add_subscription_outbounds "$config" "$section" "$subscription_json_path")"
+
+ if [ -z "$SUBSCRIPTION_OUTBOUND_TAGS" ]; then
+ log "No proxy outbounds found in subscription for section '$section'. Aborted." "fatal"
+ exit 1
+ fi
+
+ # Create urltest + selector (like urltest proxy_config_type)
+ urltest_tag="$(get_outbound_tag_by_section "$section-urltest")"
+ selector_tag="$(get_outbound_tag_by_section "$section")"
+ urltest_outbounds="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS")"
+ selector_outbounds="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS,$urltest_tag")"
+ config="$(sing_box_cm_add_urltest_outbound "$config" "$urltest_tag" "$urltest_outbounds" \
+ "$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
+ config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag")"
+ ;;
*)
log "Unknown proxy configuration type: '$proxy_config_type'. Aborted." "fatal"
exit 1
@@ -1594,7 +1758,7 @@ get_service_listen_address() {
network_get_ipaddr service_listen_address "$interface"
if [ -z "$service_listen_address" ]; then
- log "Failed to determine the listening IP address. Please open an issue to report this problem: https://github.com/itdoginfo/podkop/issues" "error"
+ log "Failed to determine the listening IP address. Please open an issue to report this problem: https://github.com/yandexru45/podkop-evolution/issues" "error"
return 1
fi
@@ -1867,7 +2031,7 @@ get_system_info() {
podkop_version="$PODKOP_VERSION"
- podkop_latest_version=$(curl -m 3 -s https://api.github.com/repos/itdoginfo/podkop/releases/latest | grep '"tag_name":' | cut -d'"' -f4)
+ podkop_latest_version=$(curl -m 3 -s https://api.github.com/repos/yandexru45/podkop-evolution/releases/latest | grep '"tag_name":' | cut -d'"' -f4)
[ -z "$podkop_latest_version" ] && podkop_latest_version="unknown"
if [ -f /www/luci-static/resources/view/podkop/main.js ]; then
@@ -2662,6 +2826,7 @@ Available commands:
restart Restart podkop service
main Run main podkop process
list_update Update domain lists
+ subscription_update Update subscription proxies
check_proxy Check proxy connectivity
check_nft Check NFT rules
check_nft_rules Check NFT rules status
@@ -2702,6 +2867,9 @@ main)
list_update)
list_update
;;
+subscription_update)
+ subscription_update
+ ;;
check_proxy)
check_proxy
;;
diff --git a/podkop/files/usr/lib/constants.sh b/podkop/files/usr/lib/constants.sh
index 563ad0c5..68bc3b51 100644
--- a/podkop/files/usr/lib/constants.sh
+++ b/podkop/files/usr/lib/constants.sh
@@ -9,6 +9,7 @@ CHECK_PROXY_IP_DOMAIN="ip.podkop.fyi"
FAKEIP_TEST_DOMAIN="fakeip.podkop.fyi"
TMP_SING_BOX_FOLDER="/tmp/sing-box"
TMP_RULESET_FOLDER="$TMP_SING_BOX_FOLDER/rulesets"
+TMP_SUBSCRIPTION_FOLDER="$TMP_SING_BOX_FOLDER/subscriptions"
CLOUDFLARE_OCTETS="8.47 162.159 188.114" # Endpoints https://github.com/ampetelin/warp-endpoint-checker
JQ_REQUIRED_VERSION="1.7.1"
COREUTILS_BASE64_REQUIRED_VERSION="9.7"
diff --git a/podkop/files/usr/lib/helpers.sh b/podkop/files/usr/lib/helpers.sh
index c25edb89..24f656c9 100644
--- a/podkop/files/usr/lib/helpers.sh
+++ b/podkop/files/usr/lib/helpers.sh
@@ -353,4 +353,95 @@ parse_domain_or_subnet_file_to_comma_string() {
done < "$filepath"
echo "$result"
+}
+
+# Returns the device model from OpenWrt sysinfo, or "OpenWrt Router" as fallback
+get_device_model() {
+ local model=""
+ if [ -f /tmp/sysinfo/model ]; then
+ model="$(cat /tmp/sysinfo/model 2>/dev/null)"
+ fi
+ echo "${model:-OpenWrt Router}"
+}
+
+# Returns the Linux kernel version
+get_kernel_version() {
+ uname -r
+}
+
+# Returns the sing-box version number (e.g. "1.12.0")
+get_sing_box_version() {
+ local version=""
+ if command -v sing-box >/dev/null 2>&1; then
+ version="$(sing-box version 2>/dev/null | head -1 | awk '{print $NF}')"
+ fi
+ echo "${version:-1.0}"
+}
+
+# Generates a deterministic HWID based on WAN MAC address and device model
+# Format: xxxx-xxxx-xxxx-xxxx
+# Same router always produces the same HWID
+generate_hwid() {
+ local mac="" model="" raw_hash=""
+
+ # Try to get WAN MAC address
+ if [ -f /sys/class/net/eth0/address ]; then
+ mac="$(cat /sys/class/net/eth0/address 2>/dev/null)"
+ elif [ -f /sys/class/net/br-lan/address ]; then
+ mac="$(cat /sys/class/net/br-lan/address 2>/dev/null)"
+ fi
+
+ model="$(get_device_model)"
+
+ # Generate hash from MAC + model
+ raw_hash="$(printf '%s-%s' "$mac" "$model" | md5sum | cut -c1-16)"
+
+ # Format as xxxx-xxxx-xxxx-xxxx
+ printf '%s-%s-%s-%s' \
+ "$(echo "$raw_hash" | cut -c1-4)" \
+ "$(echo "$raw_hash" | cut -c5-8)" \
+ "$(echo "$raw_hash" | cut -c9-12)" \
+ "$(echo "$raw_hash" | cut -c13-16)"
+}
+
+# Downloads a subscription JSON from the given URL with custom headers
+# Arguments:
+# $1 - subscription URL
+# $2 - output file path
+# $3 - http proxy address (optional)
+# $4 - retries (optional, default 3)
+# $5 - wait between retries (optional, default 2)
+download_subscription() {
+ local url="$1"
+ local filepath="$2"
+ local http_proxy_address="$3"
+ local retries="${4:-3}"
+ local wait="${5:-2}"
+
+ local sb_version device_model kernel_version hwid
+ sb_version="$(get_sing_box_version)"
+ device_model="$(get_device_model)"
+ kernel_version="$(get_kernel_version)"
+ hwid="$(generate_hwid)"
+
+ local header_args=""
+ header_args="--header='User-Agent: singbox/$sb_version'"
+ header_args="$header_args --header='X-HWID: $hwid'"
+ header_args="$header_args --header='X-Device-OS: OpenWrt Linux'"
+ header_args="$header_args --header='X-Device-Model: $device_model'"
+ header_args="$header_args --header='X-Ver-OS: $kernel_version'"
+ header_args="$header_args --header='Accept-Language: ru-RU,en,*'"
+ header_args="$header_args --header='X-Device-Locale: EN'"
+
+ for attempt in $(seq 1 "$retries"); do
+ if [ -n "$http_proxy_address" ]; then
+ http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
+ eval wget -O "$filepath" $header_args "$url" && break
+ else
+ eval wget -O "$filepath" $header_args "$url" && break
+ fi
+
+ log "Attempt $attempt/$retries to download subscription from $url failed" "warn"
+ sleep "$wait"
+ done
}
\ No newline at end of file
diff --git a/podkop/files/usr/lib/sing_box_config_facade.sh b/podkop/files/usr/lib/sing_box_config_facade.sh
index 6887e207..7315696d 100644
--- a/podkop/files/usr/lib/sing_box_config_facade.sh
+++ b/podkop/files/usr/lib/sing_box_config_facade.sh
@@ -328,3 +328,99 @@ sing_box_cf_add_single_key_reject_rule() {
echo "$config"
}
+
+#######################################
+# Parse a sing-box subscription JSON and add all proxy outbounds to the configuration.
+# Filters out non-proxy types (selector, urltest, direct, dns, block).
+# Uses 'tag' field (or 'remark' if present) as display name for each outbound.
+# Arguments:
+# config: string (JSON), sing-box configuration to modify
+# section: string, the UCI section name
+# subscription_json_path: string, path to the downloaded subscription JSON file
+# Outputs:
+# Writes updated JSON configuration to stdout
+# Sets global variable SUBSCRIPTION_OUTBOUND_TAGS (comma-separated list of tags)
+# Sets global variable SUBSCRIPTION_OUTBOUND_NAMES (newline-separated list of display names)
+#######################################
+sing_box_cf_add_subscription_outbounds() {
+ local config="$1"
+ local section="$2"
+ local subscription_json_path="$3"
+
+ SUBSCRIPTION_OUTBOUND_TAGS=""
+ SUBSCRIPTION_OUTBOUND_NAMES=""
+
+ if [ ! -f "$subscription_json_path" ]; then
+ log "Subscription JSON file not found: $subscription_json_path" "error"
+ echo "$config"
+ return 1
+ fi
+
+ # Extract proxy outbounds from subscription JSON
+ # Filter out non-proxy types: selector, urltest, direct, dns, block
+ local outbounds_count
+ outbounds_count=$(jq -r '[.outbounds[] | select(
+ .type != "selector" and
+ .type != "urltest" and
+ .type != "direct" and
+ .type != "dns" and
+ .type != "block"
+ )] | length' "$subscription_json_path" 2>/dev/null)
+
+ if [ -z "$outbounds_count" ] || [ "$outbounds_count" -eq 0 ]; then
+ log "No proxy outbounds found in subscription JSON" "error"
+ echo "$config"
+ return 1
+ fi
+
+ log "Found $outbounds_count proxy outbounds in subscription" "info"
+
+ local i=1
+ local outbound_json display_name outbound_tag
+
+ while [ "$i" -le "$outbounds_count" ]; do
+ # Extract the i-th proxy outbound as raw JSON
+ outbound_json=$(jq -c "[.outbounds[] | select(
+ .type != \"selector\" and
+ .type != \"urltest\" and
+ .type != \"direct\" and
+ .type != \"dns\" and
+ .type != \"block\"
+ )][$i - 1]" "$subscription_json_path" 2>/dev/null)
+
+ if [ -z "$outbound_json" ] || [ "$outbound_json" = "null" ]; then
+ i=$((i + 1))
+ continue
+ fi
+
+ # Get display name: prefer remark, then tag, then fallback
+ display_name=$(echo "$outbound_json" | jq -r '.remark // .tag // "server-'"$i"'"' 2>/dev/null)
+
+ # Create the tag in podkop format
+ outbound_tag="$(get_outbound_tag_by_section "$section-$i")"
+
+ # Remove tag from raw outbound (it will be set by sing_box_cm_add_raw_outbound)
+ local clean_outbound
+ clean_outbound=$(echo "$outbound_json" | jq -c 'del(.tag) | del(.remark)' 2>/dev/null)
+
+ config=$(sing_box_cm_add_raw_outbound "$config" "$outbound_tag" "$clean_outbound")
+
+ if [ -z "$SUBSCRIPTION_OUTBOUND_TAGS" ]; then
+ SUBSCRIPTION_OUTBOUND_TAGS="$outbound_tag"
+ else
+ SUBSCRIPTION_OUTBOUND_TAGS="$SUBSCRIPTION_OUTBOUND_TAGS,$outbound_tag"
+ fi
+
+ if [ -z "$SUBSCRIPTION_OUTBOUND_NAMES" ]; then
+ SUBSCRIPTION_OUTBOUND_NAMES="$display_name"
+ else
+ SUBSCRIPTION_OUTBOUND_NAMES="$(printf '%s\n%s' "$SUBSCRIPTION_OUTBOUND_NAMES" "$display_name")"
+ fi
+
+ i=$((i + 1))
+ done
+
+ log "Added $((i - 1)) subscription outbounds for section '$section'" "info"
+
+ echo "$config"
+}
From e8a764bbb34783f4e4f66329eefe3fd89e1dcd15 Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sat, 7 Mar 2026 16:09:55 +0300
Subject: [PATCH 02/75] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?=
=?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC?=
=?UTF-8?q?=D0=BE=D1=81=D1=82=D1=8C=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3?=
=?UTF-8?q?=D1=83=D1=80=D0=B0=D1=86=D0=B8=D0=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/view/podkop/section.js | 29 ++---
podkop/files/usr/bin/podkop | 104 ++++++++++++++----
2 files changed, 101 insertions(+), 32 deletions(-)
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js
index 16f71fd4..d5ddc196 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js
+++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js
@@ -37,7 +37,7 @@ function createSectionContent(section) {
_("Proxy Configuration URL"),
_("vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links")
);
- o.depends("proxy_config_type", "url");
+ o.depends({ connection_type: "proxy", proxy_config_type: "url" });
o.rows = 5;
// Enable soft wrapping for multi-line proxy URLs (e.g., for URLTest proxy links)
o.wrap = "soft";
@@ -66,7 +66,7 @@ function createSectionContent(section) {
_("Outbound Configuration"),
_("Enter complete outbound configuration in JSON format"),
);
- o.depends("proxy_config_type", "outbound");
+ o.depends({ connection_type: "proxy", proxy_config_type: "outbound" });
o.rows = 10;
o.validate = function (section_id, value) {
// Optional
@@ -89,7 +89,7 @@ function createSectionContent(section) {
_("Subscription URL"),
_("Enter the subscription URL to fetch proxy configurations from your provider"),
);
- o.depends("proxy_config_type", "subscription");
+ o.depends({ connection_type: "proxy", proxy_config_type: "subscription" });
o.placeholder = "https://example.com/api/sub";
o.rmempty = false;
o.validate = function (section_id, value) {
@@ -119,7 +119,7 @@ function createSectionContent(section) {
o.value("12h", _("Every 12 hours"));
o.value("1d", _("Every day"));
o.default = "1h";
- o.depends("proxy_config_type", "subscription");
+ o.depends({ connection_type: "proxy", proxy_config_type: "subscription" });
o = section.option(
form.DynamicList,
@@ -127,7 +127,7 @@ function createSectionContent(section) {
_("Selector Proxy Links"),
_("vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links")
);
- o.depends("proxy_config_type", "selector");
+ o.depends({ connection_type: "proxy", proxy_config_type: "selector" });
o.rmempty = false;
o.validate = function (section_id, value) {
// Optional
@@ -150,7 +150,7 @@ function createSectionContent(section) {
_("URLTest Proxy Links"),
_("vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links")
);
- o.depends("proxy_config_type", "urltest");
+ o.depends({ connection_type: "proxy", proxy_config_type: "urltest" });
o.rmempty = false;
o.validate = function (section_id, value) {
// Optional
@@ -178,8 +178,8 @@ function createSectionContent(section) {
o.value("3m", _("Every 3 minutes"));
o.value("5m", _("Every 5 minutes"));
o.default = "3m";
- o.depends("proxy_config_type", "urltest");
- o.depends("proxy_config_type", "subscription");
+ o.depends({ connection_type: "proxy", proxy_config_type: "urltest" });
+ o.depends({ connection_type: "proxy", proxy_config_type: "subscription" });
o = section.option(
form.Value,
@@ -189,8 +189,8 @@ function createSectionContent(section) {
);
o.default = "50";
o.rmempty = false;
- o.depends("proxy_config_type", "urltest");
- o.depends("proxy_config_type", "subscription");
+ o.depends({ connection_type: "proxy", proxy_config_type: "urltest" });
+ o.depends({ connection_type: "proxy", proxy_config_type: "subscription" });
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
return true;
@@ -217,8 +217,8 @@ function createSectionContent(section) {
o.value("https://connectivity-check.ubuntu.com", "https://connectivity-check.ubuntu.com (Ubuntu)")
o.default = "https://www.gstatic.com/generate_204";
o.rmempty = false;
- o.depends("proxy_config_type", "urltest");
- o.depends("proxy_config_type", "subscription");
+ o.depends({ connection_type: "proxy", proxy_config_type: "urltest" });
+ o.depends({ connection_type: "proxy", proxy_config_type: "subscription" });
o.validate = function (section_id, value) {
if (!value || value.length === 0) {
@@ -723,7 +723,10 @@ function createSectionContent(section) {
"Make sure the selected port is not used by another service",
),
);
- o.rmempty = false;
+ o.default = "2080";
+ o.placeholder = "2080";
+ o.datatype = "port";
+ o.rmempty = true;
o.depends("mixed_proxy_enabled", "1");
}
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index 7dd2c579..3e07bb17 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -78,23 +78,63 @@ check_requirements() {
if has_outbound_section; then
log "Outbound section found" "debug"
else
- log "Outbound section not found. Please check your configuration file (missing proxy_string, selector_proxy_links, urltest_proxy_links, outbound_json, or interface). Aborted." "error"
+ log "Outbound section not found. Please check your configuration file (missing proxy_string, selector_proxy_links, urltest_proxy_links, subscription_url, outbound_json, or interface according to connection_type/proxy_config_type). Aborted." "error"
exit 1
fi
}
-_check_outbound_section() {
+section_has_configured_outbound() {
local section="$1"
- local proxy_string interface outbound_json urltest_proxy_links
+ local connection_type proxy_config_type
+
+ config_get connection_type "$section" "connection_type"
+
+ case "$connection_type" in
+ proxy)
+ config_get proxy_config_type "$section" "proxy_config_type" "url"
+
+ case "$proxy_config_type" in
+ url)
+ local proxy_string
+ config_get proxy_string "$section" "proxy_string"
+ [ -n "$proxy_string" ] && return 0
+ ;;
+ selector)
+ local selector_proxy_links
+ config_get selector_proxy_links "$section" "selector_proxy_links"
+ [ -n "$selector_proxy_links" ] && return 0
+ ;;
+ urltest)
+ local urltest_proxy_links
+ config_get urltest_proxy_links "$section" "urltest_proxy_links"
+ [ -n "$urltest_proxy_links" ] && return 0
+ ;;
+ outbound)
+ local outbound_json
+ config_get outbound_json "$section" "outbound_json"
+ [ -n "$outbound_json" ] && return 0
+ ;;
+ subscription)
+ local subscription_url
+ config_get subscription_url "$section" "subscription_url"
+ [ -n "$subscription_url" ] && return 0
+ ;;
+ esac
+ ;;
+ vpn)
+ local interface
+ config_get interface "$section" "interface"
+ [ -n "$interface" ] && return 0
+ ;;
+ esac
- config_get proxy_string "$section" "proxy_string"
- config_get selector_proxy_links "$section" "selector_proxy_links"
- config_get urltest_proxy_links "$section" "urltest_proxy_links"
- config_get outbound_json "$section" "outbound_json"
- config_get interface "$section" "interface"
+ return 1
+}
- if [ -n "$proxy_string" ] || [ -n "$selector_proxy_links" ] || [ -n "$urltest_proxy_links" ] ||
- [ -n "$outbound_json" ] || [ -n "$interface" ]; then
+_check_outbound_section() {
+ local section="$1"
+
+ if section_has_configured_outbound "$section"; then
section_exists=0
fi
}
@@ -482,7 +522,12 @@ remove_cron_job() {
add_subscription_cron_job() {
local section="$1"
- local proxy_config_type subscription_update_interval cron_job
+ local connection_type proxy_config_type subscription_update_interval cron_job
+
+ config_get connection_type "$section" "connection_type"
+ if [ "$connection_type" != "proxy" ]; then
+ return
+ fi
config_get proxy_config_type "$section" "proxy_config_type"
if [ "$proxy_config_type" != "subscription" ]; then
@@ -599,7 +644,13 @@ subscription_update() {
_check_subscription_section() {
local section="$1"
- local proxy_config_type
+ local connection_type proxy_config_type
+
+ config_get connection_type "$section" "connection_type"
+ if [ "$connection_type" != "proxy" ]; then
+ return
+ fi
+
config_get proxy_config_type "$section" "proxy_config_type"
if [ "$proxy_config_type" = "subscription" ]; then
has_subscription=1
@@ -614,7 +665,13 @@ subscription_update() {
_update_subscription_for_section() {
local section="$1"
- local proxy_config_type subscription_url subscription_json_path
+ local connection_type proxy_config_type subscription_url subscription_json_path
+
+ config_get connection_type "$section" "connection_type"
+ if [ "$connection_type" != "proxy" ]; then
+ return
+ fi
+
config_get proxy_config_type "$section" "proxy_config_type"
if [ "$proxy_config_type" != "subscription" ]; then
@@ -1385,7 +1442,19 @@ configure_section_mixed_proxy() {
log "Could not determine the listening IP address for the Mixed Proxy. The proxy will not be created." "warn"
return 1
fi
- config_get mixed_proxy_port "$section" "mixed_proxy_port"
+ config_get mixed_proxy_port "$section" "mixed_proxy_port" "2080"
+
+ case "$mixed_proxy_port" in
+ '' | *[!0-9]*)
+ log "Invalid mixed_proxy_port '$mixed_proxy_port' for section '$section'. Falling back to 2080." "warn"
+ mixed_proxy_port="2080"
+ ;;
+ esac
+
+ if [ "$mixed_proxy_port" -lt 1 ] || [ "$mixed_proxy_port" -gt 65535 ]; then
+ log "mixed_proxy_port '$mixed_proxy_port' for section '$section' is out of range (1-65535). Falling back to 2080." "warn"
+ mixed_proxy_port="2080"
+ fi
if [ "$mixed_inbound_enabled" -eq 1 ]; then
mixed_inbound_tag="$(get_inbound_tag_by_section "$section-mixed")"
mixed_outbound_tag="$(get_outbound_tag_by_section "$section")"
@@ -1686,11 +1755,8 @@ get_download_detour_tag() {
_determine_first_outbound_section() {
local section="$1"
- local connection_type
- config_get connection_type "$section" "connection_type"
-
- if [ "$connection_type" = "proxy" ] || [ "$connection_type" = "vpn" ]; then
- [ -z "$first_section" ] && first_section="$1"
+ if section_has_configured_outbound "$section"; then
+ [ -z "$first_section" ] && first_section="$section"
fi
}
From 676936650d46663b8446e9487a29130555043912 Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sat, 7 Mar 2026 16:39:34 +0300
Subject: [PATCH 03/75] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?=
=?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D1=83=20out?=
=?UTF-8?q?bound=20=D0=B2=20podkop?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
podkop/files/usr/bin/podkop | 6 +++++-
podkop/files/usr/lib/sing_box_config_facade.sh | 2 ++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index 3e07bb17..8e9c1107 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -917,7 +917,11 @@ configure_outbound_handler() {
fi
# Parse subscription outbounds
- config="$(sing_box_cf_add_subscription_outbounds "$config" "$section" "$subscription_json_path")"
+ if ! sing_box_cf_add_subscription_outbounds "$config" "$section" "$subscription_json_path" > /dev/null; then
+ log "No proxy outbounds found in subscription for section '$section'. Aborted." "fatal"
+ exit 1
+ fi
+ config="$SING_BOX_CF_LAST_CONFIG"
if [ -z "$SUBSCRIPTION_OUTBOUND_TAGS" ]; then
log "No proxy outbounds found in subscription for section '$section'. Aborted." "fatal"
diff --git a/podkop/files/usr/lib/sing_box_config_facade.sh b/podkop/files/usr/lib/sing_box_config_facade.sh
index 7315696d..a4e7b390 100644
--- a/podkop/files/usr/lib/sing_box_config_facade.sh
+++ b/podkop/files/usr/lib/sing_box_config_facade.sh
@@ -349,6 +349,7 @@ sing_box_cf_add_subscription_outbounds() {
SUBSCRIPTION_OUTBOUND_TAGS=""
SUBSCRIPTION_OUTBOUND_NAMES=""
+ SING_BOX_CF_LAST_CONFIG="$config"
if [ ! -f "$subscription_json_path" ]; then
log "Subscription JSON file not found: $subscription_json_path" "error"
@@ -421,6 +422,7 @@ sing_box_cf_add_subscription_outbounds() {
done
log "Added $((i - 1)) subscription outbounds for section '$section'" "info"
+ SING_BOX_CF_LAST_CONFIG="$config"
echo "$config"
}
From d7e39316bca98437981ab745b7563bd773ffffdf Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sat, 7 Mar 2026 17:09:27 +0300
Subject: [PATCH 04/75] =?UTF-8?q?Fix=20vpn=20=D0=BF=D0=BE=D0=B4=D0=BF?=
=?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=B8=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3?=
=?UTF-8?q?=20luci?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../files/usr/lib/sing_box_config_facade.sh | 25 ++++++++++++++++---
1 file changed, 22 insertions(+), 3 deletions(-)
diff --git a/podkop/files/usr/lib/sing_box_config_facade.sh b/podkop/files/usr/lib/sing_box_config_facade.sh
index a4e7b390..89f6d208 100644
--- a/podkop/files/usr/lib/sing_box_config_facade.sh
+++ b/podkop/files/usr/lib/sing_box_config_facade.sh
@@ -377,7 +377,8 @@ sing_box_cf_add_subscription_outbounds() {
log "Found $outbounds_count proxy outbounds in subscription" "info"
local i=1
- local outbound_json display_name outbound_tag
+ local added_count=0
+ local outbound_json display_name outbound_tag outbound_type outbound_tls_enabled
while [ "$i" -le "$outbounds_count" ]; do
# Extract the i-th proxy outbound as raw JSON
@@ -397,6 +398,16 @@ sing_box_cf_add_subscription_outbounds() {
# Get display name: prefer remark, then tag, then fallback
display_name=$(echo "$outbound_json" | jq -r '.remark // .tag // "server-'"$i"'"' 2>/dev/null)
+ outbound_type=$(echo "$outbound_json" | jq -r '.type // ""' 2>/dev/null)
+ outbound_tls_enabled=$(echo "$outbound_json" | jq -r '.tls.enabled // false' 2>/dev/null)
+
+ # sing-box does not support top-level tls field for shadowsocks outbound.
+ if [ "$outbound_type" = "shadowsocks" ] && [ "$outbound_tls_enabled" = "true" ]; then
+ log "Skip unsupported Shadowsocks outbound with tls: '$display_name'" "warn"
+ i=$((i + 1))
+ continue
+ fi
+
# Create the tag in podkop format
outbound_tag="$(get_outbound_tag_by_section "$section-$i")"
@@ -404,7 +415,14 @@ sing_box_cf_add_subscription_outbounds() {
local clean_outbound
clean_outbound=$(echo "$outbound_json" | jq -c 'del(.tag) | del(.remark)' 2>/dev/null)
- config=$(sing_box_cm_add_raw_outbound "$config" "$outbound_tag" "$clean_outbound")
+ local updated_config
+ updated_config=$(sing_box_cm_add_raw_outbound "$config" "$outbound_tag" "$clean_outbound" 2>/dev/null)
+ if [ -z "$updated_config" ]; then
+ log "Skip invalid outbound from subscription: '$display_name'" "warn"
+ i=$((i + 1))
+ continue
+ fi
+ config="$updated_config"
if [ -z "$SUBSCRIPTION_OUTBOUND_TAGS" ]; then
SUBSCRIPTION_OUTBOUND_TAGS="$outbound_tag"
@@ -418,10 +436,11 @@ sing_box_cf_add_subscription_outbounds() {
SUBSCRIPTION_OUTBOUND_NAMES="$(printf '%s\n%s' "$SUBSCRIPTION_OUTBOUND_NAMES" "$display_name")"
fi
+ added_count=$((added_count + 1))
i=$((i + 1))
done
- log "Added $((i - 1)) subscription outbounds for section '$section'" "info"
+ log "Added $added_count subscription outbounds for section '$section'" "info"
SING_BOX_CF_LAST_CONFIG="$config"
echo "$config"
From eb3250776038aadce56779e5aab0654ff0f1fd86 Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sat, 7 Mar 2026 17:38:22 +0300
Subject: [PATCH 05/75] Fix podkop VPN subscription UI
---
.../files/usr/lib/sing_box_config_facade.sh | 30 +++++++++++++++++--
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/podkop/files/usr/lib/sing_box_config_facade.sh b/podkop/files/usr/lib/sing_box_config_facade.sh
index 89f6d208..da5285fd 100644
--- a/podkop/files/usr/lib/sing_box_config_facade.sh
+++ b/podkop/files/usr/lib/sing_box_config_facade.sh
@@ -378,7 +378,7 @@ sing_box_cf_add_subscription_outbounds() {
local i=1
local added_count=0
- local outbound_json display_name outbound_tag outbound_type outbound_tls_enabled
+ local outbound_json display_name outbound_tag outbound_type outbound_tls_enabled preferred_tag base_tag tag_suffix
while [ "$i" -le "$outbounds_count" ]; do
# Extract the i-th proxy outbound as raw JSON
@@ -408,8 +408,19 @@ sing_box_cf_add_subscription_outbounds() {
continue
fi
- # Create the tag in podkop format
- outbound_tag="$(get_outbound_tag_by_section "$section-$i")"
+ # Keep original tag from the subscription for dashboard readability.
+ preferred_tag=$(echo "$outbound_json" | jq -r '.tag // .remark // "server-'"$i"'"' 2>/dev/null)
+ if [ -z "$preferred_tag" ] || [ "$preferred_tag" = "null" ]; then
+ preferred_tag="server-$i"
+ fi
+
+ base_tag="$preferred_tag"
+ outbound_tag="$base_tag"
+ tag_suffix=1
+ while printf '%s' "$config" | jq -e --arg tag "$outbound_tag" '.outbounds[]? | select(.tag == $tag)' > /dev/null 2>&1; do
+ outbound_tag="${base_tag}-$tag_suffix"
+ tag_suffix=$((tag_suffix + 1))
+ done
# Remove tag from raw outbound (it will be set by sing_box_cm_add_raw_outbound)
local clean_outbound
@@ -422,6 +433,19 @@ sing_box_cf_add_subscription_outbounds() {
i=$((i + 1))
continue
fi
+
+ # Validate against current sing-box version and skip unsupported outbounds.
+ local validation_tmp
+ validation_tmp="$(mktemp)"
+ sing_box_cm_save_config_to_file "$updated_config" "$validation_tmp"
+ if ! sing-box -c "$validation_tmp" check > /dev/null 2>&1; then
+ rm -f "$validation_tmp"
+ log "Skip unsupported outbound for current sing-box: '$display_name'" "warn"
+ i=$((i + 1))
+ continue
+ fi
+ rm -f "$validation_tmp"
+
config="$updated_config"
if [ -z "$SUBSCRIPTION_OUTBOUND_TAGS" ]; then
From 8d091b84754820d0cd61f72f8c2b300103e0bdda Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sat, 7 Mar 2026 18:25:43 +0300
Subject: [PATCH 06/75] Fix podkop startup errors
---
.../luci-static/resources/view/podkop/main.js | 21 ++++++--
podkop/files/usr/bin/podkop | 54 +++++++++++++++----
2 files changed, 61 insertions(+), 14 deletions(-)
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
index 6ff47115..dfeb8976 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
+++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
@@ -579,9 +579,17 @@ async function callBaseMethod(method, args = [], command = "/usr/bin/podkop") {
});
if (response.stdout) {
try {
+ const data = JSON.parse(response.stdout);
+ if (data && typeof data === "object" && data.success === false) {
+ return {
+ success: false,
+ data,
+ error: data.message || data.error || ""
+ };
+ }
return {
success: true,
- data: JSON.parse(response.stdout)
+ data
};
} catch (_e) {
return {
@@ -778,7 +786,7 @@ async function getDashboardSections() {
}));
return {
withTagSelect: true,
- code: selector?.code || section[".name"],
+ code: selector?.code || section[".name"] + "-out",
displayName: section[".name"],
outbounds
};
@@ -799,7 +807,7 @@ async function getDashboardSections() {
}));
return {
withTagSelect: true,
- code: selector?.code || section[".name"],
+ code: selector?.code || section[".name"] + "-out",
displayName: section[".name"],
outbounds: [
{
@@ -829,7 +837,7 @@ async function getDashboardSections() {
}));
return {
withTagSelect: true,
- code: selector?.code || section[".name"],
+ code: selector?.code || section[".name"] + "-out",
displayName: section[".name"],
outbounds: [
{
@@ -2115,7 +2123,10 @@ async function connectToClashSockets() {
);
}
async function handleChooseOutbound(selector, tag) {
- await PodkopShellMethods.setClashApiGroupProxy(selector, tag);
+ const response = await PodkopShellMethods.setClashApiGroupProxy(selector, tag);
+ if (!response.success || response.data?.success === false) {
+ showToast(response.data?.message || _("Failed to switch proxy"), "error");
+ }
await fetchDashboardSections();
}
async function handleTestGroupLatency(tag) {
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index 8e9c1107..33af0318 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -686,6 +686,8 @@ subscription_update() {
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
subscription_json_path="$TMP_SUBSCRIPTION_FOLDER/${section}.json"
+ local subscription_url_cache_path
+ subscription_url_cache_path="$TMP_SUBSCRIPTION_FOLDER/${section}.url"
echolog "📥 Updating subscription for section '$section'..."
@@ -701,6 +703,7 @@ subscription_update() {
return
fi
+ printf '%s' "$subscription_url" > "$subscription_url_cache_path"
local outbounds_count
outbounds_count=$(jq -r '[.outbounds[] | select(
.type != "selector" and
@@ -902,9 +905,29 @@ configure_outbound_handler() {
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
subscription_json_path="$TMP_SUBSCRIPTION_FOLDER/${section}.json"
+ local subscription_url_cache_path cached_subscription_url should_download
+ subscription_url_cache_path="$TMP_SUBSCRIPTION_FOLDER/${section}.url"
+ should_download=0
- # Download subscription if not cached or force update
- if [ ! -f "$subscription_json_path" ]; then
+ if [ ! -f "$subscription_json_path" ] || [ ! -s "$subscription_json_path" ]; then
+ should_download=1
+ fi
+
+ if [ -f "$subscription_url_cache_path" ]; then
+ cached_subscription_url="$(cat "$subscription_url_cache_path" 2>/dev/null)"
+ else
+ cached_subscription_url=""
+ fi
+
+ if [ "$cached_subscription_url" != "$subscription_url" ]; then
+ if [ -n "$cached_subscription_url" ]; then
+ log "Subscription URL changed for section '$section', refreshing cache" "debug"
+ fi
+ should_download=1
+ rm -f "$subscription_json_path"
+ fi
+
+ if [ "$should_download" -eq 1 ]; then
log "Downloading subscription for section '$section'"
local service_proxy_address
service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
@@ -914,6 +937,8 @@ configure_outbound_handler() {
log "Failed to download subscription for section '$section'. Aborted." "fatal"
exit 1
fi
+
+ printf '%s' "$subscription_url" > "$subscription_url_cache_path"
fi
# Parse subscription outbounds
@@ -2477,6 +2502,7 @@ clash_api() {
get_proxy_latency)
local proxy_tag="$2"
+ local encoded_proxy_tag
local timeout="${3:-2000}"
if [ -z "$proxy_tag" ]; then
@@ -2484,7 +2510,9 @@ clash_api() {
return 1
fi
- curl -G -s "$CLASH_URL/proxies/$proxy_tag/delay" \
+ encoded_proxy_tag=$(printf '%s' "$proxy_tag" | jq -sRr @uri)
+
+ curl -G -s "$CLASH_URL/proxies/$encoded_proxy_tag/delay" \
--header "$auth_header" \
--data-urlencode "url=$TEST_URL" \
--data-urlencode "timeout=$timeout" | jq .
@@ -2492,6 +2520,7 @@ clash_api() {
get_group_latency)
local group_tag="$2"
+ local encoded_group_tag
local timeout="${3:-5000}"
if [ -z "$group_tag" ]; then
@@ -2499,7 +2528,9 @@ clash_api() {
return 1
fi
- curl -G -s "$CLASH_URL/group/$group_tag/delay" \
+ encoded_group_tag=$(printf '%s' "$group_tag" | jq -sRr @uri)
+
+ curl -G -s "$CLASH_URL/group/$encoded_group_tag/delay" \
--header "$auth_header" \
--data-urlencode "url=$TEST_URL" \
--data-urlencode "timeout=$timeout" | jq .
@@ -2509,6 +2540,10 @@ clash_api() {
local group_tag="$2"
local proxy_tag="$3"
+ local encoded_group_tag payload
+ encoded_group_tag=$(printf '%s' "$group_tag" | jq -sRr @uri)
+ payload=$(jq -cn --arg name "$proxy_tag" '{name:$name}')
+
if [ -z "$group_tag" ] || [ -z "$proxy_tag" ]; then
echo '{"error":"group_tag and proxy_tag required"}' | jq .
return 1
@@ -2516,9 +2551,10 @@ clash_api() {
local response
response=$(
- curl -X PUT -s -w "\n%{http_code}" "$CLASH_URL/proxies/$group_tag" \
+ curl -X PUT -s -w "\n%{http_code}" "$CLASH_URL/proxies/$encoded_group_tag" \
--header "$auth_header" \
- --data-raw "{\"name\":\"$proxy_tag\"}"
+ --header "Content-Type: application/json" \
+ --data-raw "$payload"
)
local http_code
@@ -2528,15 +2564,15 @@ clash_api() {
case "$http_code" in
204)
- echo "{\"success\":true,\"group\":\"$group_tag\",\"proxy\":\"$proxy_tag\"}" | jq .
+ jq -n --arg group "$group_tag" --arg proxy "$proxy_tag" '{success:true,group:$group,proxy:$proxy}'
;;
404)
- echo "{\"success\":false,\"error\":\"group_not_found\",\"message\":\"$group_tag does not exist\"}" | jq .
+ jq -n --arg group "$group_tag" '{success:false,error:"group_not_found",message:($group + " does not exist")}'
return 1
;;
400)
if echo "$body" | grep -q "not found"; then
- echo "{\"success\":false,\"error\":\"proxy_not_found\",\"message\":\"$proxy_tag not found in group $group_tag\"}" | jq .
+ jq -n --arg proxy "$proxy_tag" --arg group "$group_tag" '{success:false,error:"proxy_not_found",message:($proxy + " not found in group " + $group)}'
else
echo '{"success":false,"error":"bad_request","message":"Invalid request"}' | jq .
fi
From 357fa269873b26b08874cc9f8490eb438f816ba0 Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sat, 7 Mar 2026 19:06:46 +0300
Subject: [PATCH 07/75] Review podkop startup logs
---
podkop/files/usr/bin/podkop | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index 33af0318..79b2fd37 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -851,7 +851,7 @@ configure_outbound_handler() {
selector_tag="$(get_outbound_tag_by_section "$section")"
selector_outbounds="$(comma_string_to_json_array "$outbound_tags")"
config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" \
- "$default_outbound")"
+ "$default_outbound" "true")"
;;
urltest)
log "Detected proxy configuration type: urltest" "debug"
@@ -886,7 +886,7 @@ configure_outbound_handler() {
selector_outbounds="$(comma_string_to_json_array "$outbound_tags,$urltest_tag")"
config="$(sing_box_cm_add_urltest_outbound "$config" "$urltest_tag" "$urltest_outbounds" \
"$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
- config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag")"
+ config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag" "true")"
;;
subscription)
log "Detected proxy configuration type: subscription" "debug"
@@ -960,7 +960,7 @@ configure_outbound_handler() {
selector_outbounds="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS,$urltest_tag")"
config="$(sing_box_cm_add_urltest_outbound "$config" "$urltest_tag" "$urltest_outbounds" \
"$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
- config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag")"
+ config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag" "true")"
;;
*)
log "Unknown proxy configuration type: '$proxy_config_type'. Aborted." "fatal"
From 617080e0bcf476e48cb8ba7c11567bccbd1837d9 Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sat, 7 Mar 2026 19:28:33 +0300
Subject: [PATCH 08/75] =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=BD=D1=83=D0=BB=20?=
=?UTF-8?q?=D1=81=D1=81=D1=8B=D0=BB=D0=BA=D0=B8,=20=D0=BA=D0=B0=D0=BA=20?=
=?UTF-8?q?=D0=B1=D1=8B=D0=BB=D0=BE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 55 ++++++++----------------------------------------------
install.sh | 10 +++++-----
2 files changed, 13 insertions(+), 52 deletions(-)
diff --git a/README.md b/README.md
index 2b8ac9ca..a938bc1f 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,3 @@
-# Podkop Evolution
-
-> **Podkop's fork with HWID and Subscription URL support**
->
-> Этот форк добавляет поддержку ссылок подписки (subscription URL) с кастомными заголовками (HWID, Device-OS, Device-Model) и автоматическим обновлением. Основан на [itdoginfo/podkop](https://github.com/itdoginfo/podkop).
-
----
-
# Вещи, которые вам нужно знать перед установкой
- Это бета-версия, которая находится в активной разработке. Из версии в версию что-то может меняться.
@@ -24,45 +16,12 @@
# Документация
https://podkop.net/
-# Установка Podkop Evolution
+# Установка Podkop
Полная информация в [документации](https://podkop.net/docs/install/)
Вкратце, достаточно одного скрипта для установки и обновления:
```
-sh <(wget -O - https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/install.sh)
-```
-
-## Новое в этом форке: Подписки (Subscription)
-
-Добавлена поддержка subscription URL — ссылки подписки от провайдера прокси. При выборе типа конфигурации **Subscription** в LuCI:
-
-- Введите URL подписки от вашего провайдера
-- Выберите интервал автообновления (от 30 минут до 1 дня)
-- Все серверы из подписки автоматически появятся в дашборде
-- Автоматический выбор лучшего сервера по задержке (URLTest)
-- Ручное переключение между серверами через дашборд
-
-При скачивании подписки отправляются заголовки:
-- `User-Agent: singbox/<версия>`
-- `X-HWID` — уникальный идентификатор роутера
-- `X-Device-OS: OpenWrt Linux`
-- `X-Device-Model` — модель роутера
-- `X-Ver-OS` — версия ядра
-
-Пример конфигурации через UCI:
-```
-uci set podkop.my_sub=section
-uci set podkop.my_sub.connection_type='proxy'
-uci set podkop.my_sub.proxy_config_type='subscription'
-uci set podkop.my_sub.subscription_url='https://your-provider.com/api/sub'
-uci set podkop.my_sub.subscription_update_interval='1h'
-uci add_list podkop.my_sub.community_lists='russia_inside'
-uci commit podkop
-```
-
-Ручное обновление подписки:
-```
-/usr/bin/podkop subscription_update
+sh <(wget -O - https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh)
```
## Изменения 0.7.0
@@ -79,7 +38,7 @@ mv /etc/config/podkop /etc/config/podkop-070
```
2. Стянуть новый дефолтный конфиг:
```
-wget -O /etc/config/podkop https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/podkop/files/etc/config/podkop
+wget -O /etc/config/podkop https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/podkop/files/etc/config/podkop
```
3. Настроить заново ваш Podkop через Luci или UCI.
@@ -89,12 +48,14 @@ wget -O /etc/config/podkop https://raw.githubusercontent.com/yandexru45/podkop-e
> PR принимаются только по issues, у которых стоит label "enhancement". Либо по согласованию с авторами в ТГ-чате. Остальные PR на данный момент не рассматриваются.
## Будущее
-- [x] [Подписка](https://github.com/itdoginfo/podkop/issues/118) — **реализовано в этом форке!**
+- [ ] [Подписка](https://github.com/itdoginfo/podkop/issues/118). Здесь нужна реализация, чтоб для каждой секции помимо ручного выбора, был выбор фильтрации по тегу. Например, для main выбираем ключевые слова NL, DE, FI. А для extra секции фильтруем по RU. И создаётся outbound c urltest в которых перечислены outbound из фильтров.
- [ ] Весь трафик в sing-box и маршрутизация полностью на его уровне.
-- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. [Issue](https://github.com/itdoginfo/podkop/issues/111)
+- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. Вопрос в том, как это искусственно провернуть. Попробовать положить прокси и посмотреть, останется ли работать DNS в этом случае. И здесь, вероятно, можно обойтись триггером в init.d. [Issue](https://github.com/itdoginfo/podkop/issues/111)
- [ ] Галочка, которая режет доступ к doh серверам.
- [ ] IPv6. Только после наполнения Wiki.
## Тесты
- [ ] Unit тесты (BATS)
-- [ ] Интеграционные тесты бекенда (OpenWrt rootfs + BATS)
\ No newline at end of file
+- [ ] Интеграционные тесты бекенда (OpenWrt rootfs + BATS)
+
+[](https://deepwiki.com/itdoginfo/podkop)
\ No newline at end of file
diff --git a/install.sh b/install.sh
index 6fde5176..6376d7c2 100755
--- a/install.sh
+++ b/install.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# shellcheck shell=dash
-REPO="https://api.github.com/repos/yandexru45/podkop-evolution/releases/latest"
+REPO="https://api.github.com/repos/itdoginfo/podkop/releases/latest"
DOWNLOAD_DIR="/tmp/podkop"
COUNT=3
@@ -66,7 +66,7 @@ update_config() {
printf "\033[48;5;196m\033[1m║ ! Обнаружена старая версия podkop. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Если продолжите обновление, вам потребуется настроить Podkop заново. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Старая конфигурация будет сохранена в /etc/config/podkop-070 ║\033[0m\n"
- printf "\033[48;5;196m\033[1m║ Подробности: https://github.com/yandexru45/podkop-evolution ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Подробности: https://github.com/itdoginfo/podkop ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Точно хотите продолжить? ║\033[0m\n"
printf "\033[48;5;196m\033[1m╚══════════════════════════════════════════════════════════════════════╝\033[0m\n"
@@ -76,7 +76,7 @@ update_config() {
printf "\033[48;5;196m\033[1m║ ! Detected old podkop version. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ If you continue the update, you will need to RECONFIGURE podkop. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Your old configuration will be saved to /etc/config/podkop-070 ║\033[0m\n"
- printf "\033[48;5;196m\033[1m║ Details: https://github.com/yandexru45/podkop-evolution ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Details: https://github.com/itdoginfo/podkop ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Are you sure you want to continue? ║\033[0m\n"
printf "\033[48;5;196m\033[1m╚══════════════════════════════════════════════════════════════════════╝\033[0m\n"
@@ -88,7 +88,7 @@ update_config() {
yes|y|Y)
mv /etc/config/podkop /etc/config/podkop-070
- wget -O /etc/config/podkop https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/podkop/files/etc/config/podkop
+ wget -O /etc/config/podkop https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/podkop/files/etc/config/podkop
msg "Podkop config has been reset to default. Your old config saved in /etc/config/podkop-070"
break
;;
@@ -115,7 +115,7 @@ main() {
fi
if command -v curl >/dev/null 2>&1; then
- check_response=$(curl -s "https://api.github.com/repos/yandexru45/podkop-evolution/releases/latest")
+ check_response=$(curl -s "https://api.github.com/repos/itdoginfo/podkop/releases/latest")
if echo "$check_response" | grep -q 'API rate limit '; then
msg "You've reached the GitHub rate limit. Repeat in five minutes."
From 5c7e3dce316de27842cbc8ed374a89b9e094cd99 Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sun, 8 Mar 2026 13:04:47 +0300
Subject: [PATCH 09/75] =?UTF-8?q?=D0=BF=D0=BE=D1=87=D0=B8=D0=BD=D0=B8?=
=?UTF-8?q?=D0=BB=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=83=20=D0=BF=D1=80?=
=?UTF-8?q?=D0=B8=20=D0=BC=D1=83=D0=BB=D1=8C=D1=82=D0=B8=D1=81=D0=BF=D0=B8?=
=?UTF-8?q?=D1=81=D0=BA=D0=B0=D1=85=20+=20=D0=B2=D0=B5=D1=80=D0=BD=D1=83?=
=?UTF-8?q?=D0=BB=20readme=20&=20install=20script?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 55 +++++++++++++++++++++++++++++++------
install.sh | 10 +++----
podkop/files/usr/bin/podkop | 45 ++++++++++++++++++++++++------
3 files changed, 89 insertions(+), 21 deletions(-)
diff --git a/README.md b/README.md
index a938bc1f..2b8ac9ca 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,11 @@
+# Podkop Evolution
+
+> **Podkop's fork with HWID and Subscription URL support**
+>
+> Этот форк добавляет поддержку ссылок подписки (subscription URL) с кастомными заголовками (HWID, Device-OS, Device-Model) и автоматическим обновлением. Основан на [itdoginfo/podkop](https://github.com/itdoginfo/podkop).
+
+---
+
# Вещи, которые вам нужно знать перед установкой
- Это бета-версия, которая находится в активной разработке. Из версии в версию что-то может меняться.
@@ -16,12 +24,45 @@
# Документация
https://podkop.net/
-# Установка Podkop
+# Установка Podkop Evolution
Полная информация в [документации](https://podkop.net/docs/install/)
Вкратце, достаточно одного скрипта для установки и обновления:
```
-sh <(wget -O - https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/install.sh)
+sh <(wget -O - https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/install.sh)
+```
+
+## Новое в этом форке: Подписки (Subscription)
+
+Добавлена поддержка subscription URL — ссылки подписки от провайдера прокси. При выборе типа конфигурации **Subscription** в LuCI:
+
+- Введите URL подписки от вашего провайдера
+- Выберите интервал автообновления (от 30 минут до 1 дня)
+- Все серверы из подписки автоматически появятся в дашборде
+- Автоматический выбор лучшего сервера по задержке (URLTest)
+- Ручное переключение между серверами через дашборд
+
+При скачивании подписки отправляются заголовки:
+- `User-Agent: singbox/<версия>`
+- `X-HWID` — уникальный идентификатор роутера
+- `X-Device-OS: OpenWrt Linux`
+- `X-Device-Model` — модель роутера
+- `X-Ver-OS` — версия ядра
+
+Пример конфигурации через UCI:
+```
+uci set podkop.my_sub=section
+uci set podkop.my_sub.connection_type='proxy'
+uci set podkop.my_sub.proxy_config_type='subscription'
+uci set podkop.my_sub.subscription_url='https://your-provider.com/api/sub'
+uci set podkop.my_sub.subscription_update_interval='1h'
+uci add_list podkop.my_sub.community_lists='russia_inside'
+uci commit podkop
+```
+
+Ручное обновление подписки:
+```
+/usr/bin/podkop subscription_update
```
## Изменения 0.7.0
@@ -38,7 +79,7 @@ mv /etc/config/podkop /etc/config/podkop-070
```
2. Стянуть новый дефолтный конфиг:
```
-wget -O /etc/config/podkop https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/podkop/files/etc/config/podkop
+wget -O /etc/config/podkop https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/podkop/files/etc/config/podkop
```
3. Настроить заново ваш Podkop через Luci или UCI.
@@ -48,14 +89,12 @@ wget -O /etc/config/podkop https://raw.githubusercontent.com/itdoginfo/podkop/re
> PR принимаются только по issues, у которых стоит label "enhancement". Либо по согласованию с авторами в ТГ-чате. Остальные PR на данный момент не рассматриваются.
## Будущее
-- [ ] [Подписка](https://github.com/itdoginfo/podkop/issues/118). Здесь нужна реализация, чтоб для каждой секции помимо ручного выбора, был выбор фильтрации по тегу. Например, для main выбираем ключевые слова NL, DE, FI. А для extra секции фильтруем по RU. И создаётся outbound c urltest в которых перечислены outbound из фильтров.
+- [x] [Подписка](https://github.com/itdoginfo/podkop/issues/118) — **реализовано в этом форке!**
- [ ] Весь трафик в sing-box и маршрутизация полностью на его уровне.
-- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. Вопрос в том, как это искусственно провернуть. Попробовать положить прокси и посмотреть, останется ли работать DNS в этом случае. И здесь, вероятно, можно обойтись триггером в init.d. [Issue](https://github.com/itdoginfo/podkop/issues/111)
+- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. [Issue](https://github.com/itdoginfo/podkop/issues/111)
- [ ] Галочка, которая режет доступ к doh серверам.
- [ ] IPv6. Только после наполнения Wiki.
## Тесты
- [ ] Unit тесты (BATS)
-- [ ] Интеграционные тесты бекенда (OpenWrt rootfs + BATS)
-
-[](https://deepwiki.com/itdoginfo/podkop)
\ No newline at end of file
+- [ ] Интеграционные тесты бекенда (OpenWrt rootfs + BATS)
\ No newline at end of file
diff --git a/install.sh b/install.sh
index 6376d7c2..6fde5176 100755
--- a/install.sh
+++ b/install.sh
@@ -1,7 +1,7 @@
#!/bin/sh
# shellcheck shell=dash
-REPO="https://api.github.com/repos/itdoginfo/podkop/releases/latest"
+REPO="https://api.github.com/repos/yandexru45/podkop-evolution/releases/latest"
DOWNLOAD_DIR="/tmp/podkop"
COUNT=3
@@ -66,7 +66,7 @@ update_config() {
printf "\033[48;5;196m\033[1m║ ! Обнаружена старая версия podkop. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Если продолжите обновление, вам потребуется настроить Podkop заново. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Старая конфигурация будет сохранена в /etc/config/podkop-070 ║\033[0m\n"
- printf "\033[48;5;196m\033[1m║ Подробности: https://github.com/itdoginfo/podkop ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Подробности: https://github.com/yandexru45/podkop-evolution ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Точно хотите продолжить? ║\033[0m\n"
printf "\033[48;5;196m\033[1m╚══════════════════════════════════════════════════════════════════════╝\033[0m\n"
@@ -76,7 +76,7 @@ update_config() {
printf "\033[48;5;196m\033[1m║ ! Detected old podkop version. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ If you continue the update, you will need to RECONFIGURE podkop. ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Your old configuration will be saved to /etc/config/podkop-070 ║\033[0m\n"
- printf "\033[48;5;196m\033[1m║ Details: https://github.com/itdoginfo/podkop ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Details: https://github.com/yandexru45/podkop-evolution ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Are you sure you want to continue? ║\033[0m\n"
printf "\033[48;5;196m\033[1m╚══════════════════════════════════════════════════════════════════════╝\033[0m\n"
@@ -88,7 +88,7 @@ update_config() {
yes|y|Y)
mv /etc/config/podkop /etc/config/podkop-070
- wget -O /etc/config/podkop https://raw.githubusercontent.com/itdoginfo/podkop/refs/heads/main/podkop/files/etc/config/podkop
+ wget -O /etc/config/podkop https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/podkop/files/etc/config/podkop
msg "Podkop config has been reset to default. Your old config saved in /etc/config/podkop-070"
break
;;
@@ -115,7 +115,7 @@ main() {
fi
if command -v curl >/dev/null 2>&1; then
- check_response=$(curl -s "https://api.github.com/repos/itdoginfo/podkop/releases/latest")
+ check_response=$(curl -s "https://api.github.com/repos/yandexru45/podkop-evolution/releases/latest")
if echo "$check_response" | grep -q 'API rate limit '; then
msg "You've reached the GitHub rate limit. Repeat in five minutes."
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index 79b2fd37..3d1a2141 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -568,6 +568,23 @@ add_subscription_cron_job() {
} | crontab -
log "The subscription cron job has been created: $cron_job"
}
+ensure_nft_ready_for_list_update() {
+ if nft list table inet "$NFT_TABLE_NAME" > /dev/null 2>&1; then
+ return 0
+ fi
+
+ log "NFT table '$NFT_TABLE_NAME' is missing before lists update, recreating nft rules" "warn"
+ route_table_rule_mark
+ create_nft_rules
+
+ if ! nft list table inet "$NFT_TABLE_NAME" > /dev/null 2>&1; then
+ log "Failed to recreate NFT table '$NFT_TABLE_NAME'" "error"
+ return 1
+ fi
+
+ return 0
+}
+
list_update() {
echolog "🔄 Starting lists update..."
@@ -600,7 +617,7 @@ list_update() {
local service_proxy_address
service_proxy_address="$(get_service_proxy_address)"
- if [ -n "$http_proxy_address" ]; then
+ if [ -n "$service_proxy_address" ]; then
if curl -s -x "http://$service_proxy_address" -m $curl_timeout https://github.com > /dev/null; then
echolog "✅ GitHub connection check passed (via proxy)"
break
@@ -624,16 +641,23 @@ list_update() {
return 1
fi
+ if ! ensure_nft_ready_for_list_update; then
+ echolog "❌ NFT table is unavailable, cannot update lists"
+ return 1
+ fi
+
echolog "📥 Downloading and processing lists..."
- config_foreach import_community_subnet_lists "section"
- config_foreach import_domains_from_remote_domain_lists "section"
- config_foreach import_subnets_from_remote_subnet_lists "section"
+ local update_failed=0
+ config_foreach import_community_subnet_lists "section" || update_failed=1
+ config_foreach import_domains_from_remote_domain_lists "section" || update_failed=1
+ config_foreach import_subnets_from_remote_subnet_lists "section" || update_failed=1
- if [ $? -eq 0 ]; then
+ if [ "$update_failed" -eq 0 ]; then
echolog "✅ Lists update completed successfully"
else
echolog "❌ Lists update failed"
+ return 1
fi
}
@@ -1570,9 +1594,14 @@ import_community_service_subnet_list_handler() {
;;
"discord")
URL=$SUBNETS_DISCORD
- nft_create_ipv4_set "$NFT_TABLE_NAME" "$NFT_DISCORD_SET_NAME"
- nft add rule inet "$NFT_TABLE_NAME" mangle iifname "@$NFT_INTERFACE_SET_NAME" ip daddr \
- "@$NFT_DISCORD_SET_NAME" udp dport '{ 50000-65535 }' meta mark set "$NFT_FAKEIP_MARK" counter
+ if ! nft list set inet "$NFT_TABLE_NAME" "$NFT_DISCORD_SET_NAME" > /dev/null 2>&1; then
+ nft_create_ipv4_set "$NFT_TABLE_NAME" "$NFT_DISCORD_SET_NAME"
+ fi
+ if ! nft list chain inet "$NFT_TABLE_NAME" mangle 2> /dev/null | \
+ grep -Fq "@$NFT_DISCORD_SET_NAME udp dport { 50000-65535 }"; then
+ nft add rule inet "$NFT_TABLE_NAME" mangle iifname "@$NFT_INTERFACE_SET_NAME" ip daddr \
+ "@$NFT_DISCORD_SET_NAME" udp dport '{ 50000-65535 }' meta mark set "$NFT_FAKEIP_MARK" counter
+ fi
;;
"roblox")
URL=$SUBNETS_ROBLOX
From d02ee70f30fa4e5bd0b1833543838a750204ccea Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Fri, 13 Mar 2026 19:29:37 +0300
Subject: [PATCH 10/75] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?=
=?UTF-8?q?=D0=BB=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D0=B8=D1=80=D0=BE=D0=B2?=
=?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=20=D1=81=D1=82=D1=80?=
=?UTF-8?q?=D0=B0=D0=BD=D0=B0=D0=BC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../methods/custom/getDashboardSections.ts | 78 ++++++++----
fe-app-podkop/src/podkop/types.ts | 1 +
.../luci-static/resources/view/podkop/main.js | 66 ++++++++---
.../resources/view/podkop/section.js | 10 ++
podkop/files/etc/config/podkop | 3 +-
podkop/files/usr/bin/podkop | 112 ++++++++++++++++--
6 files changed, 223 insertions(+), 47 deletions(-)
diff --git a/fe-app-podkop/src/podkop/methods/custom/getDashboardSections.ts b/fe-app-podkop/src/podkop/methods/custom/getDashboardSections.ts
index 554caa6d..771ae8a4 100644
--- a/fe-app-podkop/src/podkop/methods/custom/getDashboardSections.ts
+++ b/fe-app-podkop/src/podkop/methods/custom/getDashboardSections.ts
@@ -159,34 +159,72 @@ export async function getDashboardSections(): Promise proxy.code === `${section['.name']}-out`,
);
- const outbound = proxies.find(
+ const fallbackUrltest = proxies.find(
(proxy) => proxy.code === `${section['.name']}-urltest-out`,
);
+ const selectorOutbounds = (selector?.value?.all ?? []).flatMap((code) => {
+ const item = proxies.find((proxy) => proxy.code === code);
+ if (!item) {
+ return [];
+ }
- const outbounds = (outbound?.value?.all ?? [])
- .map((code) => proxies.find((item) => item.code === code))
- .map((item) => ({
- code: item?.code || '',
- displayName: item?.value?.name || '',
- latency: item?.value?.history?.[0]?.delay || 0,
- type: item?.value?.type || '',
- selected: selector?.value?.now === item?.code,
- }));
+ const isLegacyFastest = item.code === `${section['.name']}-urltest-out`;
+
+ return [
+ {
+ code: item.code,
+ displayName: isLegacyFastest
+ ? _('Fastest')
+ : item?.value?.name || '',
+ latency: item?.value?.history?.[0]?.delay || 0,
+ type: item?.value?.type || '',
+ selected: selector?.value?.now === item.code,
+ },
+ ];
+ });
+
+ const outbounds = [
+ ...selectorOutbounds.filter(
+ (item) => item.type?.toLowerCase() === 'urltest',
+ ),
+ ...selectorOutbounds.filter(
+ (item) => item.type?.toLowerCase() !== 'urltest',
+ ),
+ ];
+
+ if (outbounds.length === 0 && fallbackUrltest) {
+ const fallbackOutbounds = (fallbackUrltest?.value?.all ?? [])
+ .map((code) => proxies.find((item) => item.code === code))
+ .map((item) => ({
+ code: item?.code || '',
+ displayName: item?.value?.name || '',
+ latency: item?.value?.history?.[0]?.delay || 0,
+ type: item?.value?.type || '',
+ selected: selector?.value?.now === item?.code,
+ }));
+
+ return {
+ withTagSelect: true,
+ code: selector?.code || section['.name'],
+ displayName: section['.name'],
+ outbounds: [
+ {
+ code: fallbackUrltest?.code || '',
+ displayName: _('Fastest'),
+ latency: fallbackUrltest?.value?.history?.[0]?.delay || 0,
+ type: fallbackUrltest?.value?.type || '',
+ selected: selector?.value?.now === fallbackUrltest?.code,
+ },
+ ...fallbackOutbounds,
+ ],
+ };
+ }
return {
withTagSelect: true,
code: selector?.code || section['.name'],
displayName: section['.name'],
- outbounds: [
- {
- code: outbound?.code || '',
- displayName: _('Fastest'),
- latency: outbound?.value?.history?.[0]?.delay || 0,
- type: outbound?.value?.type || '',
- selected: selector?.value?.now === outbound?.code,
- },
- ...outbounds,
- ],
+ outbounds,
};
}
}
diff --git a/fe-app-podkop/src/podkop/types.ts b/fe-app-podkop/src/podkop/types.ts
index e93ab032..98673bd8 100644
--- a/fe-app-podkop/src/podkop/types.ts
+++ b/fe-app-podkop/src/podkop/types.ts
@@ -119,6 +119,7 @@ export namespace Podkop {
proxy_config_type: 'subscription';
subscription_url: string;
subscription_update_interval?: string;
+ subscription_group_by_countries?: '0' | '1';
}
export interface ConfigVpnSection {
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
index dfeb8976..21ca9153 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
+++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
@@ -825,30 +825,60 @@ async function getDashboardSections() {
const selector = proxies.find(
(proxy) => proxy.code === `${section[".name"]}-out`
);
- const outbound = proxies.find(
+ const fallbackUrltest = proxies.find(
(proxy) => proxy.code === `${section[".name"]}-urltest-out`
);
- const outbounds = (outbound?.value?.all ?? []).map((code) => proxies.find((item) => item.code === code)).map((item) => ({
- code: item?.code || "",
- displayName: item?.value?.name || "",
- latency: item?.value?.history?.[0]?.delay || 0,
- type: item?.value?.type || "",
- selected: selector?.value?.now === item?.code
- }));
+ const selectorOutbounds = (selector?.value?.all ?? []).flatMap((code) => {
+ const item = proxies.find((proxy) => proxy.code === code);
+ if (!item) {
+ return [];
+ }
+ const isLegacyFastest = item.code === `${section[".name"]}-urltest-out`;
+ return [{
+ code: item.code,
+ displayName: isLegacyFastest ? _("Fastest") : item?.value?.name || "",
+ latency: item?.value?.history?.[0]?.delay || 0,
+ type: item?.value?.type || "",
+ selected: selector?.value?.now === item.code
+ }];
+ });
+ const outbounds = [
+ ...selectorOutbounds.filter(
+ (item) => item.type?.toLowerCase() === "urltest"
+ ),
+ ...selectorOutbounds.filter(
+ (item) => item.type?.toLowerCase() !== "urltest"
+ )
+ ];
+ if (outbounds.length === 0 && fallbackUrltest) {
+ const fallbackOutbounds = (fallbackUrltest?.value?.all ?? []).map((code) => proxies.find((item) => item.code === code)).map((item) => ({
+ code: item?.code || "",
+ displayName: item?.value?.name || "",
+ latency: item?.value?.history?.[0]?.delay || 0,
+ type: item?.value?.type || "",
+ selected: selector?.value?.now === item?.code
+ }));
+ return {
+ withTagSelect: true,
+ code: selector?.code || section[".name"] + "-out",
+ displayName: section[".name"],
+ outbounds: [
+ {
+ code: fallbackUrltest?.code || "",
+ displayName: _("Fastest"),
+ latency: fallbackUrltest?.value?.history?.[0]?.delay || 0,
+ type: fallbackUrltest?.value?.type || "",
+ selected: selector?.value?.now === fallbackUrltest?.code
+ },
+ ...fallbackOutbounds
+ ]
+ };
+ }
return {
withTagSelect: true,
code: selector?.code || section[".name"] + "-out",
displayName: section[".name"],
- outbounds: [
- {
- code: outbound?.code || "",
- displayName: _("Fastest"),
- latency: outbound?.value?.history?.[0]?.delay || 0,
- type: outbound?.value?.type || "",
- selected: selector?.value?.now === outbound?.code
- },
- ...outbounds
- ]
+ outbounds
};
}
}
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js
index d5ddc196..b09bed6f 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js
+++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js
@@ -121,6 +121,16 @@ function createSectionContent(section) {
o.default = "1h";
o.depends({ connection_type: "proxy", proxy_config_type: "subscription" });
+ o = section.option(
+ form.Flag,
+ "subscription_group_by_countries",
+ _("Группировать по странам"),
+ _("Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы"),
+ );
+ o.default = "0";
+ o.rmempty = false;
+ o.depends({ connection_type: "proxy", proxy_config_type: "subscription" });
+
o = section.option(
form.DynamicList,
"selector_proxy_links",
diff --git a/podkop/files/etc/config/podkop b/podkop/files/etc/config/podkop
index ff8d9766..53e2ed95 100644
--- a/podkop/files/etc/config/podkop
+++ b/podkop/files/etc/config/podkop
@@ -44,7 +44,8 @@ config section 'main'
# option proxy_config_type 'subscription'
# option subscription_url 'https://example.com/api/sub'
# option subscription_update_interval '1h'
+# #option subscription_group_by_countries '0'
# #option urltest_check_interval '3m'
# #option urltest_tolerance '50'
# #option urltest_testing_url 'https://www.gstatic.com/generate_204'
-# list community_lists 'russia_inside'
\ No newline at end of file
+# list community_lists 'russia_inside'
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index 3d1a2141..b88fa6ba 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -818,6 +818,52 @@ sing_box_configure_outbounds() {
config_foreach configure_outbound_handler "section"
}
+sing_box_get_unique_outbound_tag() {
+ local config="$1"
+ local base_tag="$2"
+ local candidate="$base_tag"
+ local tag_suffix=1
+
+ while printf '%s' "$config" | jq -e --arg tag "$candidate" '.outbounds[]? | select(.tag == $tag)' > /dev/null 2>&1; do
+ candidate="${base_tag}-${tag_suffix}"
+ tag_suffix=$((tag_suffix + 1))
+ done
+
+ echo "$candidate"
+}
+
+sing_box_build_subscription_country_groups() {
+ local subscription_outbound_tags="$1"
+
+ printf '%s' "$subscription_outbound_tags" | jq -Rrc '
+ def is_regional_indicator: . >= 127462 and . <= 127487;
+ def extract_country_flag:
+ (. | explode) as $codepoints
+ | if ($codepoints | length) >= 2
+ and ($codepoints[0] | is_regional_indicator)
+ and ($codepoints[1] | is_regional_indicator)
+ then ($codepoints[0:2] | implode)
+ else ""
+ end;
+
+ (split(",") | map(select(length > 0))) as $tags
+ | reduce $tags[] as $tag (
+ {country_order: [], country_groups: {}, ungrouped: []};
+ ($tag | extract_country_flag) as $country_flag
+ | if $country_flag == "" then
+ .ungrouped += [$tag]
+ else
+ .country_groups[$country_flag] = ((.country_groups[$country_flag] // []) + [$tag])
+ | if (.country_order | index($country_flag)) == null then
+ .country_order += [$country_flag]
+ else
+ .
+ end
+ end
+ )
+ ' 2>/dev/null
+}
+
configure_outbound_handler() {
local section="$1"
@@ -915,12 +961,14 @@ configure_outbound_handler() {
subscription)
log "Detected proxy configuration type: subscription" "debug"
local subscription_url subscription_json_path urltest_tag selector_tag \
- urltest_outbounds selector_outbounds urltest_check_interval urltest_tolerance urltest_testing_url
+ urltest_outbounds selector_outbounds urltest_check_interval urltest_tolerance \
+ urltest_testing_url subscription_group_by_countries
config_get subscription_url "$section" "subscription_url"
config_get urltest_check_interval "$section" "urltest_check_interval" "3m"
config_get urltest_tolerance "$section" "urltest_tolerance" 50
config_get urltest_testing_url "$section" "urltest_testing_url" "https://www.gstatic.com/generate_204"
+ config_get_bool subscription_group_by_countries "$section" "subscription_group_by_countries" 0
if [ -z "$subscription_url" ]; then
log "Subscription URL is not set. Aborted." "fatal"
@@ -977,14 +1025,62 @@ configure_outbound_handler() {
exit 1
fi
- # Create urltest + selector (like urltest proxy_config_type)
- urltest_tag="$(get_outbound_tag_by_section "$section-urltest")"
selector_tag="$(get_outbound_tag_by_section "$section")"
- urltest_outbounds="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS")"
- selector_outbounds="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS,$urltest_tag")"
- config="$(sing_box_cm_add_urltest_outbound "$config" "$urltest_tag" "$urltest_outbounds" \
- "$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
- config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag" "true")"
+
+ if [ "$subscription_group_by_countries" -eq 1 ]; then
+ local grouping_json country_flag country_group_outbounds country_group_tag \
+ selector_outbound_tags selector_default ungrouped_outbound_tags
+
+ grouping_json="$(sing_box_build_subscription_country_groups "$SUBSCRIPTION_OUTBOUND_TAGS")"
+ if [ -z "$grouping_json" ]; then
+ log "Failed to build grouped subscription outbounds for section '$section'. Aborted." "fatal"
+ exit 1
+ fi
+
+ for country_flag in $(echo "$grouping_json" | jq -r '.country_order[]' 2>/dev/null); do
+ country_group_outbounds="$(echo "$grouping_json" | jq -c --arg country_flag "$country_flag" '.country_groups[$country_flag] // []' 2>/dev/null)"
+ if [ -z "$country_group_outbounds" ] || [ "$country_group_outbounds" = "[]" ]; then
+ continue
+ fi
+
+ country_group_tag="$(sing_box_get_unique_outbound_tag "$config" "$country_flag Fastest")"
+ config="$(sing_box_cm_add_urltest_outbound "$config" "$country_group_tag" "$country_group_outbounds" \
+ "$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
+
+ if [ -z "$selector_outbound_tags" ]; then
+ selector_outbound_tags="$country_group_tag"
+ selector_default="$country_group_tag"
+ else
+ selector_outbound_tags="$selector_outbound_tags,$country_group_tag"
+ fi
+ done
+
+ ungrouped_outbound_tags="$(echo "$grouping_json" | jq -r '.ungrouped | join(",")' 2>/dev/null)"
+ if [ -n "$ungrouped_outbound_tags" ]; then
+ if [ -z "$selector_outbound_tags" ]; then
+ selector_outbound_tags="$ungrouped_outbound_tags"
+ selector_default="${ungrouped_outbound_tags%%,*}"
+ else
+ selector_outbound_tags="$selector_outbound_tags,$ungrouped_outbound_tags"
+ fi
+ fi
+
+ if [ -z "$selector_outbound_tags" ]; then
+ log "No selector outbounds available after grouping subscription outbounds for section '$section'. Aborted." "fatal"
+ exit 1
+ fi
+
+ selector_outbounds="$(comma_string_to_json_array "$selector_outbound_tags")"
+ config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$selector_default" "true")"
+ else
+ # Create urltest + selector (default subscription behaviour)
+ urltest_tag="$(get_outbound_tag_by_section "$section-urltest")"
+ urltest_outbounds="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS")"
+ selector_outbounds="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS,$urltest_tag")"
+ config="$(sing_box_cm_add_urltest_outbound "$config" "$urltest_tag" "$urltest_outbounds" \
+ "$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
+ config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag" "true")"
+ fi
;;
*)
log "Unknown proxy configuration type: '$proxy_config_type'. Aborted." "fatal"
From 8f2b11f672c3d8387938bfab930951c17f8efdcc Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sat, 14 Mar 2026 14:56:04 +0300
Subject: [PATCH 11/75] =?UTF-8?q?=D0=BF=D0=BE=D1=87=D0=B8=D0=BD=D0=B8?=
=?UTF-8?q?=D0=BB=20=D0=B3=D1=80=D1=83=D0=BF=D0=BF=D1=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
podkop/files/usr/bin/podkop | 35 ++++++++++++++++++++++++++++++++---
1 file changed, 32 insertions(+), 3 deletions(-)
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index b88fa6ba..d3d2f30b 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -864,6 +864,19 @@ sing_box_build_subscription_country_groups() {
' 2>/dev/null
}
+is_truthy_option() {
+ local value="$1"
+
+ case "$value" in
+ 1|true|TRUE|True|on|ON|yes|YES|enabled|ENABLED)
+ return 0
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+}
+
configure_outbound_handler() {
local section="$1"
@@ -962,13 +975,25 @@ configure_outbound_handler() {
log "Detected proxy configuration type: subscription" "debug"
local subscription_url subscription_json_path urltest_tag selector_tag \
urltest_outbounds selector_outbounds urltest_check_interval urltest_tolerance \
- urltest_testing_url subscription_group_by_countries
+ urltest_testing_url subscription_group_by_countries subscription_group_by_countries_raw
config_get subscription_url "$section" "subscription_url"
config_get urltest_check_interval "$section" "urltest_check_interval" "3m"
config_get urltest_tolerance "$section" "urltest_tolerance" 50
config_get urltest_testing_url "$section" "urltest_testing_url" "https://www.gstatic.com/generate_204"
- config_get_bool subscription_group_by_countries "$section" "subscription_group_by_countries" 0
+ config_get subscription_group_by_countries_raw "$section" "subscription_group_by_countries" ""
+ if [ -z "$subscription_group_by_countries_raw" ]; then
+ # Backward-compatible alias in case custom builds used another key
+ config_get subscription_group_by_countries_raw "$section" "group_by_countries" ""
+ fi
+
+ if is_truthy_option "$subscription_group_by_countries_raw"; then
+ subscription_group_by_countries=1
+ else
+ subscription_group_by_countries=0
+ fi
+
+ log "Subscription country grouping for section '$section': raw='${subscription_group_by_countries_raw:-}', enabled=$subscription_group_by_countries" "debug"
if [ -z "$subscription_url" ]; then
log "Subscription URL is not set. Aborted." "fatal"
@@ -1029,7 +1054,7 @@ configure_outbound_handler() {
if [ "$subscription_group_by_countries" -eq 1 ]; then
local grouping_json country_flag country_group_outbounds country_group_tag \
- selector_outbound_tags selector_default ungrouped_outbound_tags
+ selector_outbound_tags selector_default ungrouped_outbound_tags grouped_count ungrouped_count
grouping_json="$(sing_box_build_subscription_country_groups "$SUBSCRIPTION_OUTBOUND_TAGS")"
if [ -z "$grouping_json" ]; then
@@ -1037,6 +1062,10 @@ configure_outbound_handler() {
exit 1
fi
+ grouped_count="$(echo "$grouping_json" | jq -r '.country_order | length' 2>/dev/null)"
+ ungrouped_count="$(echo "$grouping_json" | jq -r '.ungrouped | length' 2>/dev/null)"
+ log "Country grouping prepared for section '$section': groups=$grouped_count, ungrouped=$ungrouped_count" "debug"
+
for country_flag in $(echo "$grouping_json" | jq -r '.country_order[]' 2>/dev/null); do
country_group_outbounds="$(echo "$grouping_json" | jq -c --arg country_flag "$country_flag" '.country_groups[$country_flag] // []' 2>/dev/null)"
if [ -z "$country_group_outbounds" ] || [ "$country_group_outbounds" = "[]" ]; then
From 078aecc9a113e806d3f2199fd1a5f9d194847496 Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sat, 14 Mar 2026 15:52:28 +0300
Subject: [PATCH 12/75] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=20=D0=BF=D0=BE?=
=?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=B0=20=D0=B0=D1=83=D1=82=D0=B1=D0=B0=D1=83?=
=?UTF-8?q?=D0=BD=D0=B4=D0=BE=D0=B2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
podkop/files/usr/bin/podkop | 69 ++++++++++++-------
.../files/usr/lib/sing_box_config_facade.sh | 10 +++
2 files changed, 55 insertions(+), 24 deletions(-)
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index d3d2f30b..b23481ec 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -833,9 +833,9 @@ sing_box_get_unique_outbound_tag() {
}
sing_box_build_subscription_country_groups() {
- local subscription_outbound_tags="$1"
+ local subscription_outbound_tags_json="$1"
- printf '%s' "$subscription_outbound_tags" | jq -Rrc '
+ printf '%s' "$subscription_outbound_tags_json" | jq -c '
def is_regional_indicator: . >= 127462 and . <= 127487;
def extract_country_flag:
(. | explode) as $codepoints
@@ -846,7 +846,7 @@ sing_box_build_subscription_country_groups() {
else ""
end;
- (split(",") | map(select(length > 0))) as $tags
+ (if type == "array" then . else [] end) as $tags
| reduce $tags[] as $tag (
{country_order: [], country_groups: {}, ungrouped: []};
($tag | extract_country_flag) as $country_flag
@@ -975,7 +975,8 @@ configure_outbound_handler() {
log "Detected proxy configuration type: subscription" "debug"
local subscription_url subscription_json_path urltest_tag selector_tag \
urltest_outbounds selector_outbounds urltest_check_interval urltest_tolerance \
- urltest_testing_url subscription_group_by_countries subscription_group_by_countries_raw
+ urltest_testing_url subscription_group_by_countries subscription_group_by_countries_raw \
+ subscription_outbound_tags_json
config_get subscription_url "$section" "subscription_url"
config_get urltest_check_interval "$section" "urltest_check_interval" "3m"
@@ -1050,13 +1051,19 @@ configure_outbound_handler() {
exit 1
fi
+ subscription_outbound_tags_json="$SUBSCRIPTION_OUTBOUND_TAGS_JSON"
+ if [ -z "$subscription_outbound_tags_json" ] || [ "$subscription_outbound_tags_json" = "[]" ]; then
+ # Fallback for backward compatibility with older facade versions.
+ subscription_outbound_tags_json="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS")"
+ fi
+
selector_tag="$(get_outbound_tag_by_section "$section")"
if [ "$subscription_group_by_countries" -eq 1 ]; then
local grouping_json country_flag country_group_outbounds country_group_tag \
- selector_outbound_tags selector_default ungrouped_outbound_tags grouped_count ungrouped_count
+ selector_outbounds_json selector_default ungrouped_outbounds_json grouped_count ungrouped_count
- grouping_json="$(sing_box_build_subscription_country_groups "$SUBSCRIPTION_OUTBOUND_TAGS")"
+ grouping_json="$(sing_box_build_subscription_country_groups "$subscription_outbound_tags_json")"
if [ -z "$grouping_json" ]; then
log "Failed to build grouped subscription outbounds for section '$section'. Aborted." "fatal"
exit 1
@@ -1066,6 +1073,8 @@ configure_outbound_handler() {
ungrouped_count="$(echo "$grouping_json" | jq -r '.ungrouped | length' 2>/dev/null)"
log "Country grouping prepared for section '$section': groups=$grouped_count, ungrouped=$ungrouped_count" "debug"
+ selector_outbounds_json="[]"
+
for country_flag in $(echo "$grouping_json" | jq -r '.country_order[]' 2>/dev/null); do
country_group_outbounds="$(echo "$grouping_json" | jq -c --arg country_flag "$country_flag" '.country_groups[$country_flag] // []' 2>/dev/null)"
if [ -z "$country_group_outbounds" ] || [ "$country_group_outbounds" = "[]" ]; then
@@ -1076,36 +1085,48 @@ configure_outbound_handler() {
config="$(sing_box_cm_add_urltest_outbound "$config" "$country_group_tag" "$country_group_outbounds" \
"$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
- if [ -z "$selector_outbound_tags" ]; then
- selector_outbound_tags="$country_group_tag"
- selector_default="$country_group_tag"
- else
- selector_outbound_tags="$selector_outbound_tags,$country_group_tag"
- fi
+ selector_outbounds_json=$(
+ printf '%s' "$selector_outbounds_json" | jq -ac --arg tag "$country_group_tag" '. + [$tag]' 2>/dev/null
+ )
done
- ungrouped_outbound_tags="$(echo "$grouping_json" | jq -r '.ungrouped | join(",")' 2>/dev/null)"
- if [ -n "$ungrouped_outbound_tags" ]; then
- if [ -z "$selector_outbound_tags" ]; then
- selector_outbound_tags="$ungrouped_outbound_tags"
- selector_default="${ungrouped_outbound_tags%%,*}"
- else
- selector_outbound_tags="$selector_outbound_tags,$ungrouped_outbound_tags"
- fi
+ if [ -z "$selector_outbounds_json" ]; then
+ selector_outbounds_json="[]"
+ fi
+
+ ungrouped_outbounds_json="$(echo "$grouping_json" | jq -c '.ungrouped // []' 2>/dev/null)"
+ if [ -n "$ungrouped_outbounds_json" ] && [ "$ungrouped_outbounds_json" != "[]" ]; then
+ selector_outbounds_json=$(
+ jq -acn --argjson selector "$selector_outbounds_json" --argjson ungrouped "$ungrouped_outbounds_json" \
+ '$selector + $ungrouped' 2>/dev/null
+ )
fi
- if [ -z "$selector_outbound_tags" ]; then
+ if [ -z "$selector_outbounds_json" ] || [ "$selector_outbounds_json" = "[]" ]; then
log "No selector outbounds available after grouping subscription outbounds for section '$section'. Aborted." "fatal"
exit 1
fi
- selector_outbounds="$(comma_string_to_json_array "$selector_outbound_tags")"
+ selector_default="$(echo "$selector_outbounds_json" | jq -r '.[0] // ""' 2>/dev/null)"
+ if [ -z "$selector_default" ] || [ "$selector_default" = "null" ]; then
+ log "Unable to determine default selector outbound for section '$section'. Aborted." "fatal"
+ exit 1
+ fi
+
+ selector_outbounds="$selector_outbounds_json"
config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$selector_default" "true")"
else
# Create urltest + selector (default subscription behaviour)
urltest_tag="$(get_outbound_tag_by_section "$section-urltest")"
- urltest_outbounds="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS")"
- selector_outbounds="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS,$urltest_tag")"
+ urltest_outbounds="$subscription_outbound_tags_json"
+ selector_outbounds=$(
+ jq -acn --argjson outbounds "$subscription_outbound_tags_json" --arg tag "$urltest_tag" \
+ '$outbounds + [$tag]' 2>/dev/null
+ )
+ if [ -z "$selector_outbounds" ]; then
+ log "Failed to build selector outbounds for subscription section '$section'. Aborted." "fatal"
+ exit 1
+ fi
config="$(sing_box_cm_add_urltest_outbound "$config" "$urltest_tag" "$urltest_outbounds" \
"$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag" "true")"
diff --git a/podkop/files/usr/lib/sing_box_config_facade.sh b/podkop/files/usr/lib/sing_box_config_facade.sh
index da5285fd..c27f97fc 100644
--- a/podkop/files/usr/lib/sing_box_config_facade.sh
+++ b/podkop/files/usr/lib/sing_box_config_facade.sh
@@ -340,6 +340,7 @@ sing_box_cf_add_single_key_reject_rule() {
# Outputs:
# Writes updated JSON configuration to stdout
# Sets global variable SUBSCRIPTION_OUTBOUND_TAGS (comma-separated list of tags)
+# Sets global variable SUBSCRIPTION_OUTBOUND_TAGS_JSON (JSON array of tags, ASCII-escaped)
# Sets global variable SUBSCRIPTION_OUTBOUND_NAMES (newline-separated list of display names)
#######################################
sing_box_cf_add_subscription_outbounds() {
@@ -348,6 +349,7 @@ sing_box_cf_add_subscription_outbounds() {
local subscription_json_path="$3"
SUBSCRIPTION_OUTBOUND_TAGS=""
+ SUBSCRIPTION_OUTBOUND_TAGS_JSON="[]"
SUBSCRIPTION_OUTBOUND_NAMES=""
SING_BOX_CF_LAST_CONFIG="$config"
@@ -454,6 +456,14 @@ sing_box_cf_add_subscription_outbounds() {
SUBSCRIPTION_OUTBOUND_TAGS="$SUBSCRIPTION_OUTBOUND_TAGS,$outbound_tag"
fi
+ # Keep a JSON representation to avoid Unicode corruption in shell string processing.
+ SUBSCRIPTION_OUTBOUND_TAGS_JSON=$(
+ printf '%s' "$SUBSCRIPTION_OUTBOUND_TAGS_JSON" | jq -ac --arg tag "$outbound_tag" '. + [$tag]' 2>/dev/null
+ )
+ if [ -z "$SUBSCRIPTION_OUTBOUND_TAGS_JSON" ]; then
+ SUBSCRIPTION_OUTBOUND_TAGS_JSON="[]"
+ fi
+
if [ -z "$SUBSCRIPTION_OUTBOUND_NAMES" ]; then
SUBSCRIPTION_OUTBOUND_NAMES="$display_name"
else
From c9cf5cb6244f20490ae0bade4631350c6e234732 Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sun, 5 Apr 2026 15:09:38 +0300
Subject: [PATCH 13/75] =?UTF-8?q?=D0=A3=D1=81=D0=B8=D0=BB=D0=B8=D1=82?=
=?UTF-8?q?=D1=8C=20=D0=BE=D1=82=D0=BA=D0=B0=D0=B7=D0=BE=D1=83=D1=81=D1=82?=
=?UTF-8?q?=D0=BE=D0=B9=D1=87=D0=B8=D0=B2=D0=BE=D1=81=D1=82=D1=8C=20=D0=B7?=
=?UTF-8?q?=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20=D0=BF=D0=BE=D0=B4?=
=?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=BE=D0=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
podkop/files/usr/bin/podkop | 526 ++++++++++++++++++++++++--------
podkop/files/usr/lib/helpers.sh | 99 +++++-
2 files changed, 487 insertions(+), 138 deletions(-)
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index b23481ec..8abf2529 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -147,6 +147,213 @@ has_outbound_section() {
return $section_exists
}
+get_subscription_json_path() {
+ local section="$1"
+
+ echo "$TMP_SUBSCRIPTION_FOLDER/${section}.json"
+}
+
+get_subscription_url_cache_path() {
+ local section="$1"
+
+ echo "$TMP_SUBSCRIPTION_FOLDER/${section}.url"
+}
+
+subscription_cache_is_usable() {
+ local subscription_json_path="$1"
+
+ [ -s "$subscription_json_path" ] || return 1
+
+ validate_subscription_file "$subscription_json_path"
+}
+
+wait_for_subscription_connectivity() {
+ local section="$1"
+ local subscription_url="$2"
+ local service_proxy_address="$3"
+ local attempts="${4:-12}"
+ local wait="${5:-5}"
+ local timeout="${6:-5}"
+ local attempt
+
+ for attempt in $(seq 1 "$attempts"); do
+ if check_subscription_connectivity "$subscription_url" "$service_proxy_address" 1 0 "$timeout"; then
+ log "Subscription connectivity check passed for section '$section'" "info"
+ return 0
+ fi
+
+ log "Subscription source is unavailable for section '$section' [$attempt/$attempts]" "warn"
+ [ "$attempt" -lt "$attempts" ] && sleep "$wait"
+ done
+
+ log "Subscription connectivity check failed for section '$section' after $attempts attempts" "error"
+ return 1
+}
+
+download_subscription_into_cache() {
+ local section="$1"
+ local subscription_url="$2"
+ local subscription_json_path="$3"
+ local subscription_url_cache_path="$4"
+ local service_proxy_address="$5"
+ local tmpfile
+
+ mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
+ tmpfile="$(mktemp "$TMP_SUBSCRIPTION_FOLDER/${section}.download.XXXXXX")" || return 1
+
+ if ! download_subscription "$subscription_url" "$tmpfile" "$service_proxy_address" 3 2 10; then
+ rm -f "$tmpfile"
+ return 1
+ fi
+
+ if ! validate_subscription_file "$tmpfile"; then
+ log "Downloaded subscription for section '$section' is invalid" "error"
+ rm -f "$tmpfile"
+ return 1
+ fi
+
+ if [ -f "$subscription_json_path" ] && cmp -s "$tmpfile" "$subscription_json_path"; then
+ rm -f "$tmpfile"
+ printf '%s' "$subscription_url" > "$subscription_url_cache_path"
+ log "Subscription for section '$section' is unchanged" "info"
+ return 2
+ fi
+
+ mv "$tmpfile" "$subscription_json_path" || {
+ rm -f "$tmpfile"
+ return 1
+ }
+
+ printf '%s' "$subscription_url" > "$subscription_url_cache_path"
+ return 0
+}
+
+prepare_subscription_cache_for_startup() {
+ local section="$1"
+ local connection_type proxy_config_type subscription_url subscription_json_path subscription_url_cache_path
+ local cached_subscription_url service_proxy_address had_usable_cache cache_needs_refresh
+
+ config_get connection_type "$section" "connection_type"
+ [ "$connection_type" = "proxy" ] || return 0
+
+ config_get proxy_config_type "$section" "proxy_config_type"
+ [ "$proxy_config_type" = "subscription" ] || return 0
+
+ config_get subscription_url "$section" "subscription_url"
+ if [ -z "$subscription_url" ]; then
+ log "Subscription URL is not set for section '$section'. Aborted." "fatal"
+ exit 1
+ fi
+
+ subscription_json_path="$(get_subscription_json_path "$section")"
+ subscription_url_cache_path="$(get_subscription_url_cache_path "$section")"
+ cached_subscription_url=""
+ had_usable_cache=0
+ cache_needs_refresh=0
+
+ if subscription_cache_is_usable "$subscription_json_path"; then
+ had_usable_cache=1
+ else
+ rm -f "$subscription_json_path"
+ fi
+
+ if [ -f "$subscription_url_cache_path" ]; then
+ cached_subscription_url="$(cat "$subscription_url_cache_path" 2> /dev/null)"
+ fi
+
+ if [ "$had_usable_cache" -eq 0 ] || [ "$cached_subscription_url" != "$subscription_url" ]; then
+ cache_needs_refresh=1
+ fi
+
+ if [ "$cache_needs_refresh" -eq 0 ]; then
+ return 0
+ fi
+
+ service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
+
+ if wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address"; then
+ if download_subscription_into_cache \
+ "$section" "$subscription_url" "$subscription_json_path" "$subscription_url_cache_path" "$service_proxy_address"; then
+ return 0
+ fi
+ fi
+
+ if [ "$had_usable_cache" -eq 1 ]; then
+ log "Keeping cached subscription for section '$section' until a fresh download succeeds" "warn"
+ return 0
+ fi
+
+ log "No usable subscription cache for section '$section'; podkop startup will wait for internet connectivity" "warn"
+ subscription_startup_blocked=1
+ return 1
+}
+
+prepare_subscription_caches_for_startup() {
+ subscription_startup_blocked=0
+ config_foreach prepare_subscription_cache_for_startup "section"
+
+ [ "$subscription_startup_blocked" -eq 0 ]
+}
+
+stop_subscription_startup_retry_worker() {
+ local pidfile="/var/run/podkop_subscription_retry.pid"
+
+ if [ -f "$pidfile" ]; then
+ pid="$(cat "$pidfile" 2> /dev/null)"
+ if [ -n "$pid" ] && kill -0 "$pid" 2> /dev/null; then
+ kill "$pid" 2> /dev/null
+ log "Stopped deferred startup recovery worker"
+ fi
+ rm -f "$pidfile"
+ fi
+}
+
+start_subscription_startup_retry_worker() {
+ local pidfile="/var/run/podkop_subscription_retry.pid"
+
+ if [ -f "$pidfile" ]; then
+ pid="$(cat "$pidfile" 2> /dev/null)"
+ if [ -n "$pid" ] && kill -0 "$pid" 2> /dev/null; then
+ log "Deferred startup recovery worker is already running" "debug"
+ return 0
+ fi
+ rm -f "$pidfile"
+ fi
+
+ (
+ trap 'rm -f "'"$pidfile"'"' EXIT INT TERM
+
+ while true; do
+ config_load "$PODKOP_CONFIG"
+
+ if prepare_subscription_caches_for_startup; then
+ log "Subscription cache is ready, resuming deferred podkop startup" "info"
+ rm -f "$pidfile"
+ start_main
+ start_rc=$?
+
+ if [ "$start_rc" -eq 0 ]; then
+ config_get_bool dont_touch_dhcp "settings" "dont_touch_dhcp" 0
+ if [ "$dont_touch_dhcp" -eq 0 ]; then
+ dnsmasq_configure
+ fi
+
+ uci_set "podkop" "settings" "shutdown_correctly" 0
+ uci commit "podkop" && config_load "$PODKOP_CONFIG"
+ fi
+
+ exit "$start_rc"
+ fi
+
+ log "Deferred podkop startup is still waiting for subscription connectivity" "warn"
+ sleep 10
+ done
+ ) &
+
+ echo $! > "$pidfile"
+ log "Started deferred startup recovery worker with PID $!" "warn"
+}
+
start_main() {
log "Starting podkop"
@@ -167,6 +374,14 @@ start_main() {
mkdir -p "$TMP_RULESET_FOLDER"
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
+ if ! prepare_subscription_caches_for_startup; then
+ log "Podkop startup is deferred until the subscription source becomes reachable" "warn"
+ start_subscription_startup_retry_worker
+ return 2
+ fi
+
+ stop_subscription_startup_retry_worker
+
# base
route_table_rule_mark
create_nft_rules
@@ -186,6 +401,8 @@ start_main() {
stop_main() {
log "Stopping the podkop"
+ stop_subscription_startup_retry_worker
+
if [ -f /var/run/podkop_list_update.pid ]; then
pid=$(cat /var/run/podkop_list_update.pid)
if kill -0 "$pid" 2> /dev/null; then
@@ -220,6 +437,15 @@ stop_main() {
start() {
start_main
+ start_rc=$?
+
+ if [ "$start_rc" -eq 2 ]; then
+ return 0
+ fi
+
+ if [ "$start_rc" -ne 0 ]; then
+ return "$start_rc"
+ fi
config_get_bool dont_touch_dhcp "settings" "dont_touch_dhcp" 0
if [ "$dont_touch_dhcp" -eq 0 ]; then
@@ -245,8 +471,8 @@ stop() {
reload() {
log "Podkop reload"
- stop_main
- start_main
+ stop
+ start
}
restart() {
@@ -587,7 +813,7 @@ ensure_nft_ready_for_list_update() {
list_update() {
- echolog "🔄 Starting lists update..."
+ echolog "рџ”„ Starting lists update..."
local nslookup_timeout=3
local nslookup_attempts=10
@@ -600,7 +826,7 @@ list_update() {
# DNS Check
for i in $(seq 1 $nslookup_attempts); do
if nslookup -timeout=$nslookup_timeout openwrt.org > /dev/null 2>&1; then
- echolog "✅ DNS check passed"
+ echolog "вњ… DNS check passed"
break
fi
echolog "DNS is unavailable [$i/$nslookup_attempts]"
@@ -608,7 +834,7 @@ list_update() {
done
if [ "$i" -eq $nslookup_attempts ]; then
- echolog "❌ DNS check failed after $nslookup_attempts attempts"
+ echolog "вќЊ DNS check failed after $nslookup_attempts attempts"
return 1
fi
@@ -619,12 +845,12 @@ list_update() {
if [ -n "$service_proxy_address" ]; then
if curl -s -x "http://$service_proxy_address" -m $curl_timeout https://github.com > /dev/null; then
- echolog "✅ GitHub connection check passed (via proxy)"
+ echolog "вњ… GitHub connection check passed (via proxy)"
break
fi
else
if curl -s -m $curl_timeout https://github.com > /dev/null; then
- echolog "✅ GitHub connection check passed"
+ echolog "вњ… GitHub connection check passed"
break
fi
fi
@@ -637,16 +863,16 @@ list_update() {
done
if [ "$i" -eq $curl_attempts ]; then
- echolog "❌ GitHub connection check failed after $curl_attempts attempts"
+ echolog "вќЊ GitHub connection check failed after $curl_attempts attempts"
return 1
fi
if ! ensure_nft_ready_for_list_update; then
- echolog "❌ NFT table is unavailable, cannot update lists"
+ echolog "вќЊ NFT table is unavailable, cannot update lists"
return 1
fi
- echolog "📥 Downloading and processing lists..."
+ echolog "рџ“Ґ Downloading and processing lists..."
local update_failed=0
config_foreach import_community_subnet_lists "section" || update_failed=1
@@ -654,17 +880,19 @@ list_update() {
config_foreach import_subnets_from_remote_subnet_lists "section" || update_failed=1
if [ "$update_failed" -eq 0 ]; then
- echolog "✅ Lists update completed successfully"
+ echolog "вњ… Lists update completed successfully"
else
- echolog "❌ Lists update failed"
+ echolog "вќЊ Lists update failed"
return 1
fi
}
subscription_update() {
- echolog "🔄 Starting subscription update..."
+ echolog "рџ”„ Starting subscription update..."
local has_subscription=0
+ local updated_sections=0
+ local failed_sections=0
_check_subscription_section() {
local section="$1"
@@ -683,13 +911,14 @@ subscription_update() {
config_foreach _check_subscription_section "section"
if [ "$has_subscription" -eq 0 ]; then
- echolog "ℹ️ No subscription sections found, nothing to update"
+ echolog "в„№пёЏ No subscription sections found, nothing to update"
return 0
fi
_update_subscription_for_section() {
local section="$1"
local connection_type proxy_config_type subscription_url subscription_json_path
+ local subscription_url_cache_path service_proxy_address update_result outbounds_count
config_get connection_type "$section" "connection_type"
if [ "$connection_type" != "proxy" ]; then
@@ -704,31 +933,44 @@ subscription_update() {
config_get subscription_url "$section" "subscription_url"
if [ -z "$subscription_url" ]; then
- echolog "❌ Subscription URL not set for section '$section'"
+ echolog "вќЊ Subscription URL not set for section '$section'"
+ failed_sections=$((failed_sections + 1))
return
fi
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
- subscription_json_path="$TMP_SUBSCRIPTION_FOLDER/${section}.json"
- local subscription_url_cache_path
- subscription_url_cache_path="$TMP_SUBSCRIPTION_FOLDER/${section}.url"
+ subscription_json_path="$(get_subscription_json_path "$section")"
+ subscription_url_cache_path="$(get_subscription_url_cache_path "$section")"
- echolog "📥 Updating subscription for section '$section'..."
+ echolog "рџ“Ґ Updating subscription for section '$section'..."
- local service_proxy_address
service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
- # Remove old cached file to force re-download
- rm -f "$subscription_json_path"
- download_subscription "$subscription_url" "$subscription_json_path" "$service_proxy_address"
-
- if [ ! -f "$subscription_json_path" ] || [ ! -s "$subscription_json_path" ]; then
- echolog "❌ Failed to download subscription for section '$section'"
+ if ! wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address" 6 5 5; then
+ echolog "вќЊ Failed to download subscription for section '$section'"
+ failed_sections=$((failed_sections + 1))
return
fi
- printf '%s' "$subscription_url" > "$subscription_url_cache_path"
- local outbounds_count
+ download_subscription_into_cache \
+ "$section" "$subscription_url" "$subscription_json_path" "$subscription_url_cache_path" "$service_proxy_address"
+ update_result=$?
+
+ case "$update_result" in
+ 0)
+ updated_sections=$((updated_sections + 1))
+ ;;
+ 2)
+ echolog "в„№пёЏ Subscription for section '$section' is unchanged"
+ return
+ ;;
+ *)
+ echolog "вќЊ Failed to download subscription for section '$section'"
+ failed_sections=$((failed_sections + 1))
+ return
+ ;;
+ esac
+
outbounds_count=$(jq -r '[.outbounds[] | select(
.type != "selector" and
.type != "urltest" and
@@ -737,13 +979,33 @@ subscription_update() {
.type != "block"
)] | length' "$subscription_json_path" 2>/dev/null)
- echolog "✅ Subscription updated for section '$section': $outbounds_count outbounds"
+ echolog "вњ… Subscription updated for section '$section': $outbounds_count outbounds"
}
config_foreach _update_subscription_for_section "section"
- echolog "🔄 Restarting podkop to apply updated subscriptions..."
+ if [ "$updated_sections" -eq 0 ]; then
+ if [ "$failed_sections" -gt 0 ]; then
+ echolog "вќЊ Subscription update finished with errors; keeping the last working cache"
+ return 1
+ fi
+
+ echolog "в„№пёЏ Subscription update completed: no changes detected"
+ return 0
+ fi
+
+ echolog "рџ”„ Restarting podkop to apply updated subscriptions..."
restart
- echolog "✅ Subscription update completed"
+ restart_rc=$?
+ if [ "$restart_rc" -ne 0 ]; then
+ echolog "вќЊ Subscription was downloaded, but podkop restart failed"
+ return "$restart_rc"
+ fi
+
+ if [ "$failed_sections" -gt 0 ]; then
+ echolog "вњ… Subscription update applied for changed sections; failed sections kept their previous cache"
+ else
+ echolog "вњ… Subscription update completed"
+ fi
}
# sing-box funcs
@@ -1002,12 +1264,16 @@ configure_outbound_handler() {
fi
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
- subscription_json_path="$TMP_SUBSCRIPTION_FOLDER/${section}.json"
- local subscription_url_cache_path cached_subscription_url should_download
- subscription_url_cache_path="$TMP_SUBSCRIPTION_FOLDER/${section}.url"
+ subscription_json_path="$(get_subscription_json_path "$section")"
+ local subscription_url_cache_path cached_subscription_url should_download had_usable_cache
+ subscription_url_cache_path="$(get_subscription_url_cache_path "$section")"
should_download=0
+ had_usable_cache=0
- if [ ! -f "$subscription_json_path" ] || [ ! -s "$subscription_json_path" ]; then
+ if subscription_cache_is_usable "$subscription_json_path"; then
+ had_usable_cache=1
+ else
+ rm -f "$subscription_json_path"
should_download=1
fi
@@ -1019,24 +1285,30 @@ configure_outbound_handler() {
if [ "$cached_subscription_url" != "$subscription_url" ]; then
if [ -n "$cached_subscription_url" ]; then
- log "Subscription URL changed for section '$section', refreshing cache" "debug"
+ log "Subscription URL changed for section '$section'" "warn"
+ fi
+ if [ "$had_usable_cache" -eq 0 ]; then
+ should_download=1
+ else
+ log "Using cached subscription for section '$section' until a fresh download succeeds" "warn"
fi
- should_download=1
- rm -f "$subscription_json_path"
fi
if [ "$should_download" -eq 1 ]; then
log "Downloading subscription for section '$section'"
local service_proxy_address
service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
- download_subscription "$subscription_url" "$subscription_json_path" "$service_proxy_address"
- if [ ! -f "$subscription_json_path" ] || [ ! -s "$subscription_json_path" ]; then
- log "Failed to download subscription for section '$section'. Aborted." "fatal"
- exit 1
+ if ! wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address" 6 5 5 ||
+ ! download_subscription_into_cache \
+ "$section" "$subscription_url" "$subscription_json_path" "$subscription_url_cache_path" "$service_proxy_address"; then
+ if [ "$had_usable_cache" -eq 1 ]; then
+ log "Failed to refresh subscription for section '$section', continuing with cached data" "warn"
+ else
+ log "Failed to download subscription for section '$section'. Aborted." "fatal"
+ exit 1
+ fi
fi
-
- printf '%s' "$subscription_url" > "$subscription_url_cache_path"
fi
# Parse subscription outbounds
@@ -1169,10 +1441,10 @@ configure_outbound_handler() {
config=$(sing_box_cm_add_interface_outbound "$config" "$outbound_tag" "$interface_name" "$domain_resolver_tag")
;;
block)
- log "Connection type 'block' detected for the $section section – no outbound will be created (handled via reject route rules)"
+ log "Connection type 'block' detected for the $section section – no outbound will be created (handled via reject route rules)"
;;
exclusion)
- log "Connection type 'exclusion' detected for the $section section – no outbound will be created (handled via route rules)"
+ log "Connection type 'exclusion' detected for the $section section – no outbound will be created (handled via route rules)"
;;
*)
log "Unknown connection type '$connection_type' for the $section section. Aborted." "fatal"
@@ -2133,7 +2405,7 @@ check_nft() {
# Check if table exists
if ! nft list table inet "$NFT_TABLE_NAME" > /dev/null 2>&1; then
- nolog "❌ $NFT_TABLE_NAME not found"
+ nolog "вќЊ $NFT_TABLE_NAME not found"
return 1
fi
@@ -2782,9 +3054,9 @@ global_check() {
local PODKOP_LUCI_VERSION="Unknown"
[ -n "$1" ] && PODKOP_LUCI_VERSION="$1"
- print_global "📡 Global check run!"
- print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- print_global "🛠️ System info"
+ print_global "рџ“Ў Global check run!"
+ print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ print_global "рџ› пёЏ System info"
local system_info_json
system_info_json=$(get_system_info)
@@ -2799,17 +3071,17 @@ global_check() {
openwrt_version=$(echo "$system_info_json" | jq -r '.openwrt_version // "unknown"')
device_model=$(echo "$system_info_json" | jq -r '.device_model // "unknown"')
- print_global "🕳️ Podkop: $podkop_version (latest: $podkop_latest_version)"
- print_global "🕳️ LuCI App: $luci_app_version"
- print_global "📦 Sing-box: $sing_box_version"
- print_global "🛜 OpenWrt: $openwrt_version"
- print_global "🛜 Device: $device_model"
+ print_global "рџ•іпёЏ Podkop: $podkop_version (latest: $podkop_latest_version)"
+ print_global "рџ•іпёЏ LuCI App: $luci_app_version"
+ print_global "📦 Sing-box: $sing_box_version"
+ print_global "рџ›њ OpenWrt: $openwrt_version"
+ print_global "рџ›њ Device: $device_model"
else
- print_global "❌ Failed to get system info"
+ print_global "вќЊ Failed to get system info"
fi
- print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- print_global "➡️ DNS status"
+ print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ print_global "вћЎпёЏ DNS status"
local dns_check_json
dns_check_json=$(check_dns_available)
@@ -2828,24 +3100,24 @@ global_check() {
# Bootstrap DNS
if [ -n "$bootstrap_dns_server" ]; then
if [ "$bootstrap_dns_status" -eq 1 ]; then
- print_global "✅ Bootstrap DNS: $bootstrap_dns_server"
+ print_global "вњ… Bootstrap DNS: $bootstrap_dns_server"
else
- print_global "❌ Bootstrap DNS: $bootstrap_dns_server"
+ print_global "вќЊ Bootstrap DNS: $bootstrap_dns_server"
fi
fi
# DNS server status
if [ "$dns_status" -eq 1 ]; then
- print_global "✅ Main DNS: $dns_server [$dns_type]"
+ print_global "вњ… Main DNS: $dns_server [$dns_type]"
else
- print_global "❌ Main DNS: $dns_server [$dns_type]"
+ print_global "вќЊ Main DNS: $dns_server [$dns_type]"
fi
# DNS on router
if [ "$dns_on_router" -eq 1 ]; then
- print_global "✅ DNS on router"
+ print_global "вњ… DNS on router"
else
- print_global "❌ DNS on router"
+ print_global "вќЊ DNS on router"
fi
# DHCP configuration check
@@ -2853,20 +3125,20 @@ global_check() {
config_get dont_touch_dhcp "settings" "dont_touch_dhcp"
if [ "$dont_touch_dhcp" = "1" ]; then
- print_global "⚠️ dont_touch_dhcp is enabled. 📄 DHCP config:"
+ print_global "вљ пёЏ dont_touch_dhcp is enabled. рџ“„ DHCP config:"
awk '/^config /{p=($2=="dnsmasq")} p' /etc/config/dhcp
elif [ "$dhcp_config_status" -eq 0 ]; then
- print_global "❌ DHCP configuration differs from template. 📄 DHCP config:"
+ print_global "вќЊ DHCP configuration differs from template. рџ“„ DHCP config:"
awk '/^config /{p=($2=="dnsmasq")} p' /etc/config/dhcp
else
- print_global "✅ /etc/config/dhcp"
+ print_global "вњ… /etc/config/dhcp"
fi
else
- print_global "❌ Failed to get DNS info"
+ print_global "вќЊ Failed to get DNS info"
fi
- print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- print_global "📦 Sing-box status"
+ print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ print_global "📦 Sing-box status"
local singbox_check_json
singbox_check_json=$(check_sing_box)
@@ -2882,46 +3154,46 @@ global_check() {
sing_box_ports_listening=$(echo "$singbox_check_json" | jq -r '.sing_box_ports_listening // 0')
if [ "$sing_box_installed" -eq 1 ]; then
- print_global "✅ Sing-box installed"
+ print_global "вњ… Sing-box installed"
else
- print_global "❌ Sing-box installed"
+ print_global "вќЊ Sing-box installed"
fi
if [ "$sing_box_version_ok" -eq 1 ]; then
- print_global "✅ Sing-box version is compatible (newer than 1.12.4)"
+ print_global "вњ… Sing-box version is compatible (newer than 1.12.4)"
else
- print_global "❌ Sing-box version is not compatible (older than 1.12.4)"
+ print_global "вќЊ Sing-box version is not compatible (older than 1.12.4)"
fi
if [ "$sing_box_service_exist" -eq 1 ]; then
- print_global "✅ Sing-box service exist"
+ print_global "вњ… Sing-box service exist"
else
- print_global "❌ Sing-box service exist"
+ print_global "вќЊ Sing-box service exist"
fi
if [ "$sing_box_autostart_disabled" -eq 1 ]; then
- print_global "✅ Sing-box autostart disabled"
+ print_global "вњ… Sing-box autostart disabled"
else
- print_global "❌ Sing-box autostart disabled"
+ print_global "вќЊ Sing-box autostart disabled"
fi
if [ "$sing_box_process_running" -eq 1 ]; then
- print_global "✅ Sing-box process running"
+ print_global "вњ… Sing-box process running"
else
- print_global "❌ Sing-box process running"
+ print_global "вќЊ Sing-box process running"
fi
if [ "$sing_box_ports_listening" -eq 1 ]; then
- print_global "✅ Sing-box listening ports"
+ print_global "вњ… Sing-box listening ports"
else
- print_global "❌ Sing-box listening ports"
+ print_global "вќЊ Sing-box listening ports"
fi
else
- print_global "❌ Failed to get sing-box info"
+ print_global "вќЊ Failed to get sing-box info"
fi
- print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- print_global "🧱 NFT rules status"
+ print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ print_global "рџ§± NFT rules status"
local nft_check_json
nft_check_json=$(check_nft_rules)
@@ -2939,78 +3211,78 @@ global_check() {
rules_other_mark_exist=$(echo "$nft_check_json" | jq -r '.rules_other_mark_exist // 0')
if [ "$table_exist" -eq 1 ]; then
- print_global "✅ Table exist"
+ print_global "вњ… Table exist"
else
- print_global "❌ Table exist"
+ print_global "вќЊ Table exist"
fi
if [ "$rules_mangle_exist" -eq 1 ]; then
- print_global "✅ Rules mangle exist"
+ print_global "вњ… Rules mangle exist"
else
- print_global "❌ Rules mangle exist"
+ print_global "вќЊ Rules mangle exist"
fi
if [ "$rules_mangle_counters" -eq 1 ]; then
- print_global "✅ Rules mangle counters"
+ print_global "вњ… Rules mangle counters"
else
- print_global "⚠️ Rules mangle counters"
+ print_global "вљ пёЏ Rules mangle counters"
fi
if [ "$rules_mangle_output_exist" -eq 1 ]; then
- print_global "✅ Rules mangle output exist"
+ print_global "вњ… Rules mangle output exist"
else
- print_global "❌ Rules mangle output exist"
+ print_global "вќЊ Rules mangle output exist"
fi
if [ "$rules_mangle_output_counters" -eq 1 ]; then
- print_global "✅ Rules mangle output counters"
+ print_global "вњ… Rules mangle output counters"
else
- print_global "⚠️ Rules mangle output counters"
+ print_global "вљ пёЏ Rules mangle output counters"
fi
if [ "$rules_proxy_exist" -eq 1 ]; then
- print_global "✅ Rules proxy exist"
+ print_global "вњ… Rules proxy exist"
else
- print_global "❌ Rules proxy exist"
+ print_global "вќЊ Rules proxy exist"
fi
if [ "$rules_proxy_counters" -eq 1 ]; then
- print_global "✅ Rules proxy counters"
+ print_global "вњ… Rules proxy counters"
else
- print_global "⚠️ Rules proxy counters"
+ print_global "вљ пёЏ Rules proxy counters"
fi
if [ "$rules_other_mark_exist" -eq 1 ]; then
- print_global "⚠️ Additional marking rules found:"
+ print_global "вљ пёЏ Additional marking rules found:"
nft list ruleset | awk '/table inet '"$NFT_TABLE_NAME"'/{flag=1; next} /^table/{flag=0} !flag' | grep -E "mark set|meta mark"
else
- print_global "✅ Additional marking rules found"
+ print_global "вњ… Additional marking rules found"
fi
else
- print_global "❌ Failed to get NFT rules info"
+ print_global "вќЊ Failed to get NFT rules info"
fi
- print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- print_global "📄 Podkop config"
+ print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ print_global "рџ“„ Podkop config"
show_config
- # print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- # print_global "🔧 System check"
+ # print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ # print_global "рџ”§ System check"
# if grep -E "^nameserver\s+([0-9]{1,3}\.){3}[0-9]{1,3}" "$RESOLV_CONF" | grep -vqE "127\.0\.0\.1|0\.0\.0\.0"; then
- # print_global "❌ /etc/resolv.conf contains external nameserver:"
+ # print_global "вќЊ /etc/resolv.conf contains external nameserver:"
# cat /etc/resolv.conf
# echo ""
# else
- # print_global "✅ /etc/resolv.conf"
+ # print_global "вњ… /etc/resolv.conf"
# fi
- # print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- # print_global "🧱 NFT table"
+ # print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ # print_global "рџ§± NFT table"
# check_nft
- print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- print_global "📄 WAN config"
+ print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ print_global "рџ“„ WAN config"
if uci show network.wan > /dev/null 2>&1; then
awk '
/^config / {
@@ -3031,20 +3303,20 @@ global_check() {
}
' /etc/config/network
else
- print_global "❌ WAN configuration not found"
+ print_global "вќЊ WAN configuration not found"
fi
if uci show network | grep -q endpoint_host; then
uci show network | grep endpoint_host | cut -d'=' -f2 | tr -d "'\" " | while read -r host; do
if [ "$host" = "engage.cloudflareclient.com" ]; then
- print_global "⚠️ WARP detected: $host"
+ print_global "вљ пёЏ WARP detected: $host"
continue
fi
ip_prefix=$(echo "$host" | cut -d'.' -f1,2)
if echo "$CLOUDFLARE_OCTETS" | grep -wq "$ip_prefix"; then
- print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- print_global "⚠️ WARP detected: $host"
+ print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ print_global "вљ пёЏ WARP detected: $host"
fi
done
fi
@@ -3055,19 +3327,19 @@ global_check() {
allowed_ips=$(uci get "${peer_section}.allowed_ips" 2> /dev/null)
if [ "$allowed_ips" = "0.0.0.0/0" ]; then
- print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- print_global "⚠️ WG Route allowed IP enabled with 0.0.0.0/0"
+ print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ print_global "вљ пёЏ WG Route allowed IP enabled with 0.0.0.0/0"
fi
done
fi
if [ -f "/etc/init.d/zapret" ]; then
- print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- print_global "⚠️ Zapret detected"
+ print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ print_global "вљ пёЏ Zapret detected"
fi
- print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- print_global "🥸 FakeIP status"
+ print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
+ print_global "🥸 FakeIP status"
local fakeip_check_json
fakeip_check_json=$(check_fakeip)
@@ -3078,21 +3350,21 @@ global_check() {
fakeip_status=$(echo "$fakeip_check_json" | jq -r '.fakeip // false')
if [ "$fakeip_status" = "true" ]; then
- print_global "✅ Router DNS is routed through sing-box"
+ print_global "вњ… Router DNS is routed through sing-box"
else
- print_global "⚠️ Router DNS is NOT routed through sing-box"
+ print_global "вљ пёЏ Router DNS is NOT routed through sing-box"
fi
else
- print_global "❌ Failed to get FakeIP info"
+ print_global "вќЊ Failed to get FakeIP info"
fi
local fakeip_address
fakeip_address=$(dig +short @127.0.0.42 $FAKEIP_TEST_DOMAIN)
if echo "$fakeip_address" | grep -q "^198\.18\."; then
- print_global "✅ Sing-box works with FakeIP: $fakeip_address"
+ print_global "вњ… Sing-box works with FakeIP: $fakeip_address"
else
- print_global "❌ Sing-box does NOT work with FakeIP: $fakeip_address"
+ print_global "вќЊ Sing-box does NOT work with FakeIP: $fakeip_address"
fi
}
diff --git a/podkop/files/usr/lib/helpers.sh b/podkop/files/usr/lib/helpers.sh
index 24f656c9..dda8ea80 100644
--- a/podkop/files/usr/lib/helpers.sh
+++ b/podkop/files/usr/lib/helpers.sh
@@ -417,6 +417,7 @@ download_subscription() {
local http_proxy_address="$3"
local retries="${4:-3}"
local wait="${5:-2}"
+ local timeout="${6:-10}"
local sb_version device_model kernel_version hwid
sb_version="$(get_sing_box_version)"
@@ -424,24 +425,100 @@ download_subscription() {
kernel_version="$(get_kernel_version)"
hwid="$(generate_hwid)"
- local header_args=""
- header_args="--header='User-Agent: singbox/$sb_version'"
- header_args="$header_args --header='X-HWID: $hwid'"
- header_args="$header_args --header='X-Device-OS: OpenWrt Linux'"
- header_args="$header_args --header='X-Device-Model: $device_model'"
- header_args="$header_args --header='X-Ver-OS: $kernel_version'"
- header_args="$header_args --header='Accept-Language: ru-RU,en,*'"
- header_args="$header_args --header='X-Device-Locale: EN'"
+ local tmpfile
+ tmpfile="${filepath}.part.$$"
+ rm -f "$tmpfile"
for attempt in $(seq 1 "$retries"); do
if [ -n "$http_proxy_address" ]; then
http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
- eval wget -O "$filepath" $header_args "$url" && break
+ wget -T "$timeout" -t 1 -O "$tmpfile" \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url"
else
- eval wget -O "$filepath" $header_args "$url" && break
+ wget -T "$timeout" -t 1 -O "$tmpfile" \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url"
+ fi
+
+ if [ $? -eq 0 ] && [ -s "$tmpfile" ]; then
+ mv "$tmpfile" "$filepath"
+ return 0
fi
+ rm -f "$tmpfile"
log "Attempt $attempt/$retries to download subscription from $url failed" "warn"
sleep "$wait"
done
-}
\ No newline at end of file
+
+ rm -f "$tmpfile"
+ return 1
+}
+
+check_subscription_connectivity() {
+ local url="$1"
+ local http_proxy_address="$2"
+ local retries="${3:-3}"
+ local wait="${4:-2}"
+ local timeout="${5:-5}"
+
+ local sb_version device_model kernel_version hwid
+ sb_version="$(get_sing_box_version)"
+ device_model="$(get_device_model)"
+ kernel_version="$(get_kernel_version)"
+ hwid="$(generate_hwid)"
+
+ local attempt
+ for attempt in $(seq 1 "$retries"); do
+ if [ -n "$http_proxy_address" ]; then
+ http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
+ wget -q -T "$timeout" -t 1 -O /dev/null \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" && return 0
+ else
+ wget -q -T "$timeout" -t 1 -O /dev/null \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" && return 0
+ fi
+
+ [ "$attempt" -lt "$retries" ] && sleep "$wait"
+ done
+
+ return 1
+}
+
+validate_subscription_file() {
+ local filepath="$1"
+
+ [ -s "$filepath" ] || return 1
+
+ jq -e '
+ type == "object" and
+ (.outbounds | type == "array") and
+ ((.outbounds | length) > 0)
+ ' "$filepath" > /dev/null 2>&1
+}
From bec1f4c10e40ae88f586412d28e63c4d7d3d1457 Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Sun, 5 Apr 2026 15:51:24 +0300
Subject: [PATCH 14/75] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?=
=?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D1=81=D0=BE=D0=B2=D0=BC=D0=B5=D1=81=D1=82?=
=?UTF-8?q?=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D1=8C=20wget=20=D0=B2=20=D0=BF?=
=?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B5=20=D0=BF=D0=BE=D0=B4?=
=?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=BE=D0=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
podkop/files/usr/lib/helpers.sh | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/podkop/files/usr/lib/helpers.sh b/podkop/files/usr/lib/helpers.sh
index dda8ea80..c16b9382 100644
--- a/podkop/files/usr/lib/helpers.sh
+++ b/podkop/files/usr/lib/helpers.sh
@@ -432,7 +432,7 @@ download_subscription() {
for attempt in $(seq 1 "$retries"); do
if [ -n "$http_proxy_address" ]; then
http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
- wget -T "$timeout" -t 1 -O "$tmpfile" \
+ wget -T "$timeout" -O "$tmpfile" \
--header "User-Agent: singbox/$sb_version" \
--header "X-HWID: $hwid" \
--header "X-Device-OS: OpenWrt Linux" \
@@ -442,7 +442,7 @@ download_subscription() {
--header "X-Device-Locale: EN" \
"$url"
else
- wget -T "$timeout" -t 1 -O "$tmpfile" \
+ wget -T "$timeout" -O "$tmpfile" \
--header "User-Agent: singbox/$sb_version" \
--header "X-HWID: $hwid" \
--header "X-Device-OS: OpenWrt Linux" \
@@ -484,7 +484,7 @@ check_subscription_connectivity() {
for attempt in $(seq 1 "$retries"); do
if [ -n "$http_proxy_address" ]; then
http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
- wget -q -T "$timeout" -t 1 -O /dev/null \
+ wget -q -T "$timeout" -O /dev/null \
--header "User-Agent: singbox/$sb_version" \
--header "X-HWID: $hwid" \
--header "X-Device-OS: OpenWrt Linux" \
@@ -494,7 +494,7 @@ check_subscription_connectivity() {
--header "X-Device-Locale: EN" \
"$url" && return 0
else
- wget -q -T "$timeout" -t 1 -O /dev/null \
+ wget -q -T "$timeout" -O /dev/null \
--header "User-Agent: singbox/$sb_version" \
--header "X-HWID: $hwid" \
--header "X-Device-OS: OpenWrt Linux" \
From 7d551f9bfdb498fb2ce3279051cda971af1c5313 Mon Sep 17 00:00:00 2001
From: Artem Kireev
Date: Wed, 13 May 2026 10:32:48 +0300
Subject: [PATCH 15/75] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?=
=?UTF-8?q?=D0=B8=D1=82=D1=8C=20deadlock=20=D0=BF=D1=80=D0=B8=20=D1=85?=
=?UTF-8?q?=D0=BE=D0=BB=D0=BE=D0=B4=D0=BD=D0=BE=D0=BC=20=D1=81=D1=82=D0=B0?=
=?UTF-8?q?=D1=80=D1=82=D0=B5=20=D1=81=20download=5Flists=5Fvia=5Fproxy=3D?=
=?UTF-8?q?1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
При холодном старте подкоп вызывает prepare_subscription_caches_for_startup
до запуска sing-box. Если включена опция download_lists_via_proxy=1,
функция пытается скачать подписку через прокси 127.0.0.1:4534
(SB_SERVICE_MIXED_INBOUND), который ещё не существует, потому что sing-box
запускается только после prepare_subscription_caches_for_startup.
В результате wait_for_subscription_connectivity безуспешно делает 12 попыток
по 5 секунд (60 секунд ожидания), после чего стартует retry worker, который
крутится в цикле каждые 10 секунд и тоже использует прокси — но sing-box
никогда не запустится без подписки. Получается deadlock: подписка нужна для
старта sing-box, а sing-box нужен для скачивания подписки через прокси.
Ситуация усугубляется тем, что /tmp/sing-box/subscriptions лежит в tmpfs,
поэтому на холодном старте кэш всегда пустой и cache_needs_refresh=1.
На этапе bootstrap скачиваем подписку напрямую, игнорируя
download_lists_via_proxy. После старта sing-box subscription_update и
list_update продолжают использовать прокси согласно настройке.
---
podkop/files/usr/bin/podkop | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index 8abf2529..d1125c65 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -269,7 +269,14 @@ prepare_subscription_cache_for_startup() {
return 0
fi
- service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
+ # Bootstrap subscription directly: sing-box (and its service proxy) is not
+ # running yet at this stage, so download_lists_via_proxy would deadlock here.
+ service_proxy_address=""
+ local _download_lists_via_proxy
+ config_get_bool _download_lists_via_proxy "settings" "download_lists_via_proxy" 0
+ if [ "$_download_lists_via_proxy" -eq 1 ]; then
+ log "download_lists_via_proxy is set, but sing-box is not running yet during startup. Bootstrapping subscription for section '$section' over a direct connection" "info"
+ fi
if wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address"; then
if download_subscription_into_cache \
From 0f44d09b5e9b46bb08eb85bed60793f881e343df Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Tue, 26 May 2026 22:56:45 +0300
Subject: [PATCH 16/75] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?=
=?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=B2=D0=BE=D1=81=D1=81=D1=82=D0=B0=D0=BD?=
=?UTF-8?q?=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=D0=B4?=
=?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=BE=D0=BA=20=D0=BF=D1=80=D0=B8=20=D1=85?=
=?UTF-8?q?=D0=BE=D0=BB=D0=BE=D0=B4=D0=BD=D0=BE=D0=BC=20=D1=81=D1=82=D0=B0?=
=?UTF-8?q?=D1=80=D1=82=D0=B5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
podkop/files/usr/bin/podkop | 476 +++++++++++++++++++++---------
podkop/files/usr/lib/constants.sh | 5 +-
podkop/files/usr/lib/helpers.sh | 396 ++++++++++++++++++++++---
3 files changed, 692 insertions(+), 185 deletions(-)
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index d1125c65..f0d75a2c 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -150,21 +150,148 @@ has_outbound_section() {
get_subscription_json_path() {
local section="$1"
- echo "$TMP_SUBSCRIPTION_FOLDER/${section}.json"
+ echo "$SUBSCRIPTION_CACHE_FOLDER/${section}.json"
}
get_subscription_url_cache_path() {
local section="$1"
- echo "$TMP_SUBSCRIPTION_FOLDER/${section}.url"
+ echo "$SUBSCRIPTION_CACHE_FOLDER/${section}.url"
+}
+
+get_subscription_rejected_cache_path() {
+ local section="$1"
+
+ echo "$SUBSCRIPTION_CACHE_FOLDER/${section}.rejected"
+}
+
+ensure_subscription_cache_dir() {
+ local state_dir_created=0 cache_dir_created=0
+
+ [ -d "$PODKOP_STATE_DIR" ] || state_dir_created=1
+ [ -d "$SUBSCRIPTION_CACHE_FOLDER" ] || cache_dir_created=1
+ mkdir -p "$SUBSCRIPTION_CACHE_FOLDER" || return 1
+ [ "$state_dir_created" -eq 1 ] && chmod 700 "$PODKOP_STATE_DIR" 2>/dev/null
+ [ "$cache_dir_created" -eq 1 ] && chmod 700 "$SUBSCRIPTION_CACHE_FOLDER" 2>/dev/null
+}
+
+migrate_subscription_cache_from_tmp() {
+ local src_json src_url dst_json dst_url
+
+ [ -d "$TMP_SUBSCRIPTION_FOLDER" ] || return 0
+ ensure_subscription_cache_dir || return 0
+
+ for src_json in "$TMP_SUBSCRIPTION_FOLDER"/*.json; do
+ [ -e "$src_json" ] || continue
+ src_url="${src_json%.json}.url"
+ dst_json="$(get_subscription_json_path "$(basename "${src_json%.json}")")"
+ dst_url="$(get_subscription_url_cache_path "$(basename "${src_json%.json}")")"
+
+ if [ ! -e "$dst_json" ] && subscription_cache_is_usable "$src_json"; then
+ cp "$src_json" "$dst_json" 2>/dev/null
+ [ -f "$src_url" ] && cp "$src_url" "$dst_url" 2>/dev/null
+ chmod 600 "$dst_json" "$dst_url" 2>/dev/null
+ log "Migrated subscription cache for section '$(basename "${src_json%.json}")' to persistent storage" "info"
+ fi
+ done
+}
+
+mark_subscription_outbound_unavailable() {
+ local section="$1"
+ local subscription_json_path rejected_cache_path rejected_hash
+
+ case " $SUBSCRIPTION_UNAVAILABLE_SECTIONS " in
+ *" $section "*) ;;
+ *) SUBSCRIPTION_UNAVAILABLE_SECTIONS="$SUBSCRIPTION_UNAVAILABLE_SECTIONS $section" ;;
+ esac
+
+ log "Subscription cache for section '$section' is unavailable; matching traffic for this section will be rejected until refresh succeeds" "warn"
+ # A structurally valid subscription can still contain no sing-box usable
+ # proxy outbounds. Remember its hash so the retry worker does not persist,
+ # restart and reject exactly the same unusable feed in a flash-writing loop.
+ subscription_json_path="$(get_subscription_json_path "$section")"
+ rejected_cache_path="$(get_subscription_rejected_cache_path "$section")"
+ rejected_hash="$(md5sum "$subscription_json_path" 2>/dev/null | awk '{print $1}')"
+ if [ -n "$rejected_hash" ] && [ "$(cat "$rejected_cache_path" 2>/dev/null)" != "$rejected_hash" ]; then
+ printf '%s' "$rejected_hash" > "${rejected_cache_path}.tmp.$$" && mv "${rejected_cache_path}.tmp.$$" "$rejected_cache_path"
+ chmod 600 "$rejected_cache_path" 2>/dev/null
+ fi
+ subscription_startup_blocked=1
+}
+
+subscription_outbound_is_unavailable() {
+ local section="$1"
+
+ case " $SUBSCRIPTION_UNAVAILABLE_SECTIONS " in
+ *" $section "*) return 0 ;;
+ esac
+ return 1
+}
+
+get_subscription_download_proxy_address() {
+ local section="$1"
+ local phase="$2"
+ local download_lists_via_proxy download_lists_via_proxy_section
+
+ config_get_bool download_lists_via_proxy "settings" "download_lists_via_proxy" 0
+ [ "$download_lists_via_proxy" -eq 1 ] || return 0
+
+ config_get download_lists_via_proxy_section "settings" "download_lists_via_proxy_section"
+ if [ -z "$download_lists_via_proxy_section" ]; then
+ log "download_lists_via_proxy is enabled but no proxy section is selected; using direct mode for $phase subscription download" "warn"
+ return 0
+ fi
+
+ if [ "$phase" = "bootstrap" ]; then
+ if [ "$download_lists_via_proxy_section" = "$section" ]; then
+ log "Cannot bootstrap subscription for section '$section' through itself before cache exists; using direct mode" "warn"
+ else
+ log "download_lists_via_proxy is configured, but bootstrap subscription download for section '$section' must use direct mode until sing-box starts" "info"
+ fi
+ return 0
+ fi
+
+ if [ "$download_lists_via_proxy_section" = "$section" ]; then
+ if subscription_cache_is_usable "$(get_subscription_json_path "$section")"; then
+ log "Updating subscription for section '$section' through its currently active cached proxy" "info"
+ echo "$(get_service_proxy_address 2>/dev/null || echo '')"
+ return 0
+ fi
+
+ log "Subscription section '$section' is selected as its own download proxy, but no usable cache exists yet; falling back to direct mode for bootstrap" "warn"
+ return 0
+ fi
+
+ local selected_connection_type selected_proxy_config_type
+ config_get selected_connection_type "$download_lists_via_proxy_section" "connection_type"
+ config_get selected_proxy_config_type "$download_lists_via_proxy_section" "proxy_config_type"
+ if [ "$selected_connection_type" = "proxy" ] && [ "$selected_proxy_config_type" = "subscription" ] && \
+ ! subscription_cache_is_usable "$(get_subscription_json_path "$download_lists_via_proxy_section")"; then
+ log "Selected download proxy section '$download_lists_via_proxy_section' has no usable subscription cache; using direct mode for recovery of '$section'" "warn"
+ return 0
+ fi
+
+ echo "$(get_service_proxy_address 2>/dev/null || echo '')"
}
subscription_cache_is_usable() {
local subscription_json_path="$1"
+ local rejected_cache_path current_hash rejected_hash
[ -s "$subscription_json_path" ] || return 1
- validate_subscription_file "$subscription_json_path"
+ validate_subscription_file "$subscription_json_path" || return 1
+
+ rejected_cache_path="${subscription_json_path%.json}.rejected"
+ if [ -s "$rejected_cache_path" ]; then
+ current_hash="$(md5sum "$subscription_json_path" 2>/dev/null | awk '{print $1}')"
+ rejected_hash="$(cat "$rejected_cache_path" 2>/dev/null)"
+ if [ -n "$current_hash" ] && [ "$current_hash" = "$rejected_hash" ]; then
+ return 1
+ fi
+ fi
+
+ return 0
}
wait_for_subscription_connectivity() {
@@ -196,10 +323,11 @@ download_subscription_into_cache() {
local subscription_json_path="$3"
local subscription_url_cache_path="$4"
local service_proxy_address="$5"
- local tmpfile
+ local tmpfile persist_tmpfile url_tmpfile rejected_cache_path tmp_hash rejected_hash
- mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
- tmpfile="$(mktemp "$TMP_SUBSCRIPTION_FOLDER/${section}.download.XXXXXX")" || return 1
+ ensure_subscription_cache_dir || return 1
+ mkdir -p "$TMP_SUBSCRIPTION_DOWNLOAD_FOLDER" || return 1
+ tmpfile="$(mktemp "$TMP_SUBSCRIPTION_DOWNLOAD_FOLDER/${section}.download.XXXXXX")" || return 1
if ! download_subscription "$subscription_url" "$tmpfile" "$service_proxy_address" 3 2 10; then
rm -f "$tmpfile"
@@ -212,19 +340,37 @@ download_subscription_into_cache() {
return 1
fi
+ rejected_cache_path="$(get_subscription_rejected_cache_path "$section")"
+ tmp_hash="$(md5sum "$tmpfile" 2>/dev/null | awk '{print $1}')"
+ rejected_hash="$(cat "$rejected_cache_path" 2>/dev/null)"
+ if [ -n "$tmp_hash" ] && [ "$tmp_hash" = "$rejected_hash" ]; then
+ log "Downloaded subscription for section '$section' is unchanged and was previously rejected because it contains no usable sing-box outbounds" "warn"
+ rm -f "$tmpfile"
+ return 1
+ fi
+
if [ -f "$subscription_json_path" ] && cmp -s "$tmpfile" "$subscription_json_path"; then
rm -f "$tmpfile"
- printf '%s' "$subscription_url" > "$subscription_url_cache_path"
+ if [ "$(cat "$subscription_url_cache_path" 2>/dev/null)" != "$subscription_url" ]; then
+ url_tmpfile="${subscription_url_cache_path}.tmp.$$"
+ printf '%s' "$subscription_url" > "$url_tmpfile" && mv "$url_tmpfile" "$subscription_url_cache_path"
+ chmod 600 "$subscription_url_cache_path" 2>/dev/null
+ fi
log "Subscription for section '$section' is unchanged" "info"
return 2
fi
- mv "$tmpfile" "$subscription_json_path" || {
- rm -f "$tmpfile"
+ persist_tmpfile="${subscription_json_path}.tmp.$$"
+ cp "$tmpfile" "$persist_tmpfile" && mv "$persist_tmpfile" "$subscription_json_path" || {
+ rm -f "$tmpfile" "$persist_tmpfile"
return 1
}
+ rm -f "$tmpfile"
+ rm -f "$rejected_cache_path"
- printf '%s' "$subscription_url" > "$subscription_url_cache_path"
+ url_tmpfile="${subscription_url_cache_path}.tmp.$$"
+ printf '%s' "$subscription_url" > "$url_tmpfile" && mv "$url_tmpfile" "$subscription_url_cache_path"
+ chmod 600 "$subscription_json_path" "$subscription_url_cache_path" 2>/dev/null
return 0
}
@@ -251,6 +397,9 @@ prepare_subscription_cache_for_startup() {
had_usable_cache=0
cache_needs_refresh=0
+ ensure_subscription_cache_dir || true
+ migrate_subscription_cache_from_tmp
+
if subscription_cache_is_usable "$subscription_json_path"; then
had_usable_cache=1
else
@@ -270,7 +419,7 @@ prepare_subscription_cache_for_startup() {
fi
# Bootstrap subscription directly: sing-box (and its service proxy) is not
- # running yet at this stage, so download_lists_via_proxy would deadlock here.
+ # running yet at this stage, so proxy-based bootstrap would deadlock here.
service_proxy_address=""
local _download_lists_via_proxy
config_get_bool _download_lists_via_proxy "settings" "download_lists_via_proxy" 0
@@ -278,7 +427,7 @@ prepare_subscription_cache_for_startup() {
log "download_lists_via_proxy is set, but sing-box is not running yet during startup. Bootstrapping subscription for section '$section' over a direct connection" "info"
fi
- if wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address"; then
+ if wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address" 2 2 5; then
if download_subscription_into_cache \
"$section" "$subscription_url" "$subscription_json_path" "$subscription_url_cache_path" "$service_proxy_address"; then
return 0
@@ -287,12 +436,12 @@ prepare_subscription_cache_for_startup() {
if [ "$had_usable_cache" -eq 1 ]; then
log "Keeping cached subscription for section '$section' until a fresh download succeeds" "warn"
- return 0
+ else
+ log "No usable subscription cache for section '$section'; startup will continue with a temporary blocked outbound until subscription becomes reachable" "warn"
+ subscription_startup_blocked=1
fi
- log "No usable subscription cache for section '$section'; podkop startup will wait for internet connectivity" "warn"
- subscription_startup_blocked=1
- return 1
+ return 0
}
prepare_subscription_caches_for_startup() {
@@ -330,30 +479,23 @@ start_subscription_startup_retry_worker() {
(
trap 'rm -f "'"$pidfile"'"' EXIT INT TERM
+ # Let the primary sing-box instance come up first, so a configured
+ # service proxy can be used for the post-bootstrap refresh.
+ sleep 10
+
while true; do
config_load "$PODKOP_CONFIG"
- if prepare_subscription_caches_for_startup; then
- log "Subscription cache is ready, resuming deferred podkop startup" "info"
+ # Run in a child process: a successful subscription_update performs
+ # its own restart, which stops this worker via the pidfile safely.
+ if /usr/bin/podkop subscription_update >/dev/null 2>&1; then
+ log "Deferred subscription refresh succeeded; updated configuration is being applied" "info"
rm -f "$pidfile"
- start_main
- start_rc=$?
-
- if [ "$start_rc" -eq 0 ]; then
- config_get_bool dont_touch_dhcp "settings" "dont_touch_dhcp" 0
- if [ "$dont_touch_dhcp" -eq 0 ]; then
- dnsmasq_configure
- fi
-
- uci_set "podkop" "settings" "shutdown_correctly" 0
- uci commit "podkop" && config_load "$PODKOP_CONFIG"
- fi
-
- exit "$start_rc"
+ exit 0
fi
- log "Deferred podkop startup is still waiting for subscription connectivity" "warn"
- sleep 10
+ log "Deferred subscription refresh is still waiting for connectivity" "warn"
+ sleep 30
done
) &
@@ -380,13 +522,13 @@ start_main() {
mkdir -p "$TMP_SING_BOX_FOLDER"
mkdir -p "$TMP_RULESET_FOLDER"
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
+ ensure_subscription_cache_dir
+ migrate_subscription_cache_from_tmp
- if ! prepare_subscription_caches_for_startup; then
- log "Podkop startup is deferred until the subscription source becomes reachable" "warn"
- start_subscription_startup_retry_worker
- return 2
+ prepare_subscription_caches_for_startup
+ if [ "$subscription_startup_blocked" -ne 0 ]; then
+ log "Podkop startup continues with temporary blocked subscription outbounds until sources become reachable" "warn"
fi
-
stop_subscription_startup_retry_worker
# base
@@ -400,6 +542,10 @@ start_main() {
config_foreach add_subscription_cron_job "section"
/etc/init.d/sing-box start
+ if [ "$subscription_startup_blocked" -ne 0 ]; then
+ start_subscription_startup_retry_worker
+ fi
+
log "Nice"
list_update &
echo $! > /var/run/podkop_list_update.pid
@@ -946,12 +1092,13 @@ subscription_update() {
fi
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
+ ensure_subscription_cache_dir
subscription_json_path="$(get_subscription_json_path "$section")"
subscription_url_cache_path="$(get_subscription_url_cache_path "$section")"
echolog "рџ“Ґ Updating subscription for section '$section'..."
- service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
+ service_proxy_address="$(get_subscription_download_proxy_address "$section" "runtime" 2>/dev/null || echo '')"
if ! wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address" 6 5 5; then
echolog "вќЊ Failed to download subscription for section '$section'"
@@ -1082,6 +1229,7 @@ sing_box_configure_inbounds() {
sing_box_configure_outbounds() {
log "Configure the outbounds section of a sing-box JSON configuration"
+ SUBSCRIPTION_UNAVAILABLE_SECTIONS=""
config=$(sing_box_cm_add_direct_outbound "$config" "$SB_DIRECT_OUTBOUND_TAG")
config_foreach configure_outbound_handler "section"
@@ -1245,7 +1393,7 @@ configure_outbound_handler() {
local subscription_url subscription_json_path urltest_tag selector_tag \
urltest_outbounds selector_outbounds urltest_check_interval urltest_tolerance \
urltest_testing_url subscription_group_by_countries subscription_group_by_countries_raw \
- subscription_outbound_tags_json
+ subscription_outbound_tags_json service_proxy_address subscription_ready
config_get subscription_url "$section" "subscription_url"
config_get urltest_check_interval "$section" "urltest_check_interval" "3m"
@@ -1303,112 +1451,113 @@ configure_outbound_handler() {
if [ "$should_download" -eq 1 ]; then
log "Downloading subscription for section '$section'"
- local service_proxy_address
- service_proxy_address="$(get_service_proxy_address 2>/dev/null || echo '')"
+ # Config generation runs before sing-box is started. Never use
+ # its local download proxy here; runtime refresh will use it.
+ service_proxy_address="$(get_subscription_download_proxy_address "$section" "bootstrap" 2>/dev/null || echo '')"
- if ! wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address" 6 5 5 ||
+ if ! wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address" 1 0 5 ||
! download_subscription_into_cache \
"$section" "$subscription_url" "$subscription_json_path" "$subscription_url_cache_path" "$service_proxy_address"; then
if [ "$had_usable_cache" -eq 1 ]; then
log "Failed to refresh subscription for section '$section', continuing with cached data" "warn"
else
- log "Failed to download subscription for section '$section'. Aborted." "fatal"
- exit 1
+ log "Failed to download subscription for section '$section'; using a temporary blocked outbound" "warn"
fi
fi
fi
- # Parse subscription outbounds
- if ! sing_box_cf_add_subscription_outbounds "$config" "$section" "$subscription_json_path" > /dev/null; then
- log "No proxy outbounds found in subscription for section '$section'. Aborted." "fatal"
- exit 1
+ subscription_ready=0
+ if subscription_cache_is_usable "$subscription_json_path"; then
+ if sing_box_cf_add_subscription_outbounds "$config" "$section" "$subscription_json_path" > /dev/null; then
+ if [ -n "$SUBSCRIPTION_OUTBOUND_TAGS" ]; then
+ config="$SING_BOX_CF_LAST_CONFIG"
+ subscription_ready=1
+ fi
+ fi
fi
- config="$SING_BOX_CF_LAST_CONFIG"
- if [ -z "$SUBSCRIPTION_OUTBOUND_TAGS" ]; then
- log "No proxy outbounds found in subscription for section '$section'. Aborted." "fatal"
- exit 1
- fi
+ if [ "$subscription_ready" -eq 0 ]; then
+ log "Subscription cache for section '$section' is unavailable or empty; using a temporary blocked outbound" "warn"
+ mark_subscription_outbound_unavailable "$section"
+ else
+ selector_tag="$(get_outbound_tag_by_section "$section")"
+ subscription_outbound_tags_json="$SUBSCRIPTION_OUTBOUND_TAGS_JSON"
+ if [ -z "$subscription_outbound_tags_json" ] || [ "$subscription_outbound_tags_json" = "[]" ]; then
+ subscription_outbound_tags_json="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS")"
+ fi
- subscription_outbound_tags_json="$SUBSCRIPTION_OUTBOUND_TAGS_JSON"
- if [ -z "$subscription_outbound_tags_json" ] || [ "$subscription_outbound_tags_json" = "[]" ]; then
- # Fallback for backward compatibility with older facade versions.
- subscription_outbound_tags_json="$(comma_string_to_json_array "$SUBSCRIPTION_OUTBOUND_TAGS")"
- fi
+ if [ "$subscription_group_by_countries" -eq 1 ]; then
+ local grouping_json country_flag country_group_outbounds country_group_tag \
+ selector_outbounds_json selector_default ungrouped_outbounds_json grouped_count ungrouped_count
- selector_tag="$(get_outbound_tag_by_section "$section")"
+ grouping_json="$(sing_box_build_subscription_country_groups "$subscription_outbound_tags_json")"
+ if [ -z "$grouping_json" ]; then
+ log "Failed to build grouped subscription outbounds for section '$section'. Aborted." "fatal"
+ exit 1
+ fi
- if [ "$subscription_group_by_countries" -eq 1 ]; then
- local grouping_json country_flag country_group_outbounds country_group_tag \
- selector_outbounds_json selector_default ungrouped_outbounds_json grouped_count ungrouped_count
+ grouped_count="$(echo "$grouping_json" | jq -r '.country_order | length' 2>/dev/null)"
+ ungrouped_count="$(echo "$grouping_json" | jq -r '.ungrouped | length' 2>/dev/null)"
+ log "Country grouping prepared for section '$section': groups=$grouped_count, ungrouped=$ungrouped_count" "debug"
- grouping_json="$(sing_box_build_subscription_country_groups "$subscription_outbound_tags_json")"
- if [ -z "$grouping_json" ]; then
- log "Failed to build grouped subscription outbounds for section '$section'. Aborted." "fatal"
- exit 1
- fi
+ selector_outbounds_json="[]"
- grouped_count="$(echo "$grouping_json" | jq -r '.country_order | length' 2>/dev/null)"
- ungrouped_count="$(echo "$grouping_json" | jq -r '.ungrouped | length' 2>/dev/null)"
- log "Country grouping prepared for section '$section': groups=$grouped_count, ungrouped=$ungrouped_count" "debug"
+ for country_flag in $(echo "$grouping_json" | jq -r '.country_order[]' 2>/dev/null); do
+ country_group_outbounds="$(echo "$grouping_json" | jq -c --arg country_flag "$country_flag" '.country_groups[$country_flag] // []' 2>/dev/null)"
+ if [ -z "$country_group_outbounds" ] || [ "$country_group_outbounds" = "[]" ]; then
+ continue
+ fi
- selector_outbounds_json="[]"
+ country_group_tag="$(sing_box_get_unique_outbound_tag "$config" "$country_flag Fastest")"
+ config="$(sing_box_cm_add_urltest_outbound "$config" "$country_group_tag" "$country_group_outbounds" \
+ "$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
- for country_flag in $(echo "$grouping_json" | jq -r '.country_order[]' 2>/dev/null); do
- country_group_outbounds="$(echo "$grouping_json" | jq -c --arg country_flag "$country_flag" '.country_groups[$country_flag] // []' 2>/dev/null)"
- if [ -z "$country_group_outbounds" ] || [ "$country_group_outbounds" = "[]" ]; then
- continue
+ selector_outbounds_json=$(
+ printf '%s' "$selector_outbounds_json" | jq -ac --arg tag "$country_group_tag" '. + [$tag]' 2>/dev/null
+ )
+ done
+
+ if [ -z "$selector_outbounds_json" ]; then
+ selector_outbounds_json="[]"
fi
- country_group_tag="$(sing_box_get_unique_outbound_tag "$config" "$country_flag Fastest")"
- config="$(sing_box_cm_add_urltest_outbound "$config" "$country_group_tag" "$country_group_outbounds" \
- "$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
+ ungrouped_outbounds_json="$(echo "$grouping_json" | jq -c '.ungrouped // []' 2>/dev/null)"
+ if [ -n "$ungrouped_outbounds_json" ] && [ "$ungrouped_outbounds_json" != "[]" ]; then
+ selector_outbounds_json=$(
+ jq -acn --argjson selector "$selector_outbounds_json" --argjson ungrouped "$ungrouped_outbounds_json" \
+ '$selector + $ungrouped' 2>/dev/null
+ )
+ fi
- selector_outbounds_json=$(
- printf '%s' "$selector_outbounds_json" | jq -ac --arg tag "$country_group_tag" '. + [$tag]' 2>/dev/null
- )
- done
+ if [ -z "$selector_outbounds_json" ] || [ "$selector_outbounds_json" = "[]" ]; then
+ log "No selector outbounds available after grouping subscription outbounds for section '$section'. Aborted." "fatal"
+ exit 1
+ fi
- if [ -z "$selector_outbounds_json" ]; then
- selector_outbounds_json="[]"
- fi
+ selector_default="$(echo "$selector_outbounds_json" | jq -r '.[0] // ""' 2>/dev/null)"
+ if [ -z "$selector_default" ] || [ "$selector_default" = "null" ]; then
+ log "Unable to determine default selector outbound for section '$section'. Aborted." "fatal"
+ exit 1
+ fi
- ungrouped_outbounds_json="$(echo "$grouping_json" | jq -c '.ungrouped // []' 2>/dev/null)"
- if [ -n "$ungrouped_outbounds_json" ] && [ "$ungrouped_outbounds_json" != "[]" ]; then
- selector_outbounds_json=$(
- jq -acn --argjson selector "$selector_outbounds_json" --argjson ungrouped "$ungrouped_outbounds_json" \
- '$selector + $ungrouped' 2>/dev/null
+ selector_outbounds="$selector_outbounds_json"
+ config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$selector_default" "true")"
+ else
+ # Create urltest + selector (default subscription behaviour)
+ urltest_tag="$(get_outbound_tag_by_section "$section-urltest")"
+ urltest_outbounds="$subscription_outbound_tags_json"
+ selector_outbounds=$(
+ jq -acn --argjson outbounds "$subscription_outbound_tags_json" --arg tag "$urltest_tag" \
+ '$outbounds + [$tag]' 2>/dev/null
)
+ if [ -z "$selector_outbounds" ]; then
+ log "Failed to build selector outbounds for subscription section '$section'. Aborted." "fatal"
+ exit 1
+ fi
+ config="$(sing_box_cm_add_urltest_outbound "$config" "$urltest_tag" "$urltest_outbounds" \
+ "$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
+ config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag" "true")"
fi
-
- if [ -z "$selector_outbounds_json" ] || [ "$selector_outbounds_json" = "[]" ]; then
- log "No selector outbounds available after grouping subscription outbounds for section '$section'. Aborted." "fatal"
- exit 1
- fi
-
- selector_default="$(echo "$selector_outbounds_json" | jq -r '.[0] // ""' 2>/dev/null)"
- if [ -z "$selector_default" ] || [ "$selector_default" = "null" ]; then
- log "Unable to determine default selector outbound for section '$section'. Aborted." "fatal"
- exit 1
- fi
-
- selector_outbounds="$selector_outbounds_json"
- config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$selector_default" "true")"
- else
- # Create urltest + selector (default subscription behaviour)
- urltest_tag="$(get_outbound_tag_by_section "$section-urltest")"
- urltest_outbounds="$subscription_outbound_tags_json"
- selector_outbounds=$(
- jq -acn --argjson outbounds "$subscription_outbound_tags_json" --arg tag "$urltest_tag" \
- '$outbounds + [$tag]' 2>/dev/null
- )
- if [ -z "$selector_outbounds" ]; then
- log "Failed to build selector outbounds for subscription section '$section'. Aborted." "fatal"
- exit 1
- fi
- config="$(sing_box_cm_add_urltest_outbound "$config" "$urltest_tag" "$urltest_outbounds" \
- "$urltest_testing_url" "$urltest_check_interval" "$urltest_tolerance")"
- config="$(sing_box_cm_add_selector_outbound "$config" "$selector_tag" "$selector_outbounds" "$urltest_tag" "true")"
fi
;;
*)
@@ -1517,8 +1666,17 @@ sing_box_configure_route() {
local first_outbound_section
first_outbound_section="$(get_first_outbound_section)"
- first_outbound_tag="$(get_outbound_tag_by_section "$first_outbound_section")"
- config=$(sing_box_cf_proxy_domain "$config" "$SB_TPROXY_INBOUND_TAG" "$CHECK_PROXY_IP_DOMAIN" "$first_outbound_tag")
+ if subscription_outbound_is_unavailable "$first_outbound_section"; then
+ log "First configured outbound section '$first_outbound_section' is unavailable; proxy test traffic will be rejected until its subscription refresh succeeds" "warn"
+ first_outbound_tag=""
+ else
+ first_outbound_tag="$(get_outbound_tag_by_section "$first_outbound_section")"
+ fi
+ if [ -n "$first_outbound_tag" ]; then
+ config=$(sing_box_cf_proxy_domain "$config" "$SB_TPROXY_INBOUND_TAG" "$CHECK_PROXY_IP_DOMAIN" "$first_outbound_tag")
+ else
+ config=$(sing_box_cf_add_single_key_reject_rule "$config" "$SB_TPROXY_INBOUND_TAG" "domain" "$CHECK_PROXY_IP_DOMAIN")
+ fi
config=$(sing_box_cf_override_domain_port "$config" "$FAKEIP_TEST_DOMAIN" 8443)
configure_common_reject_route_rule
@@ -1544,10 +1702,14 @@ include_source_ips_in_routing_handler() {
config_get fully_routed_ips "$section" "fully_routed_ips"
if [ -n "$fully_routed_ips" ]; then
rule_tag="$(gen_id)"
- config=$(
- sing_box_cm_add_route_rule \
- "$config" "$rule_tag" "$SB_TPROXY_INBOUND_TAG" "$(get_outbound_tag_by_section "$section")"
- )
+ if subscription_outbound_is_unavailable "$section"; then
+ config="$(sing_box_cm_add_reject_route_rule "$config" "$rule_tag" "$SB_TPROXY_INBOUND_TAG")"
+ else
+ config=$(
+ sing_box_cm_add_route_rule \
+ "$config" "$rule_tag" "$SB_TPROXY_INBOUND_TAG" "$(get_outbound_tag_by_section "$section")"
+ )
+ fi
config_list_foreach "$section" "fully_routed_ips" include_source_ip_in_routing_handler "$rule_tag"
fi
}
@@ -1630,8 +1792,12 @@ configure_routing_for_section_lists() {
case "$section_connection_type" in
proxy | vpn)
route_rule_tag="$(gen_id)"
- outbound_tag=$(get_outbound_tag_by_section "$section")
- config=$(sing_box_cm_add_route_rule "$config" "$route_rule_tag" "$SB_TPROXY_INBOUND_TAG" "$outbound_tag")
+ if subscription_outbound_is_unavailable "$section"; then
+ config="$(sing_box_cm_add_reject_route_rule "$config" "$route_rule_tag" "$SB_TPROXY_INBOUND_TAG")"
+ else
+ outbound_tag=$(get_outbound_tag_by_section "$section")
+ config=$(sing_box_cm_add_route_rule "$config" "$route_rule_tag" "$SB_TPROXY_INBOUND_TAG" "$outbound_tag")
+ fi
;;
block)
route_rule_tag="$SB_REJECT_RULE_TAG"
@@ -1896,15 +2062,21 @@ sing_box_additional_inbounds() {
if [ "$download_lists_via_proxy" -eq 1 ]; then
local download_lists_via_proxy_section section_outbound_tag
config_get download_lists_via_proxy_section "settings" "download_lists_via_proxy_section"
- section_outbound_tag="$(get_outbound_tag_by_section "$download_lists_via_proxy_section")"
- config=$(
- sing_box_cf_add_mixed_inbound_and_route_rule \
- "$config" \
- "$SB_SERVICE_MIXED_INBOUND_TAG" \
- "$SB_SERVICE_MIXED_INBOUND_ADDRESS" \
- "$SB_SERVICE_MIXED_INBOUND_PORT" \
- "$section_outbound_tag"
- )
+ if subscription_outbound_is_unavailable "$download_lists_via_proxy_section"; then
+ log "Service download proxy section '$download_lists_via_proxy_section' is unavailable; rejecting proxy requests until its subscription refresh succeeds" "warn"
+ config="$(sing_box_cm_add_mixed_inbound "$config" "$SB_SERVICE_MIXED_INBOUND_TAG" "$SB_SERVICE_MIXED_INBOUND_ADDRESS" "$SB_SERVICE_MIXED_INBOUND_PORT")"
+ config="$(sing_box_cm_add_reject_route_rule "$config" "$(gen_id)" "$SB_SERVICE_MIXED_INBOUND_TAG")"
+ else
+ section_outbound_tag="$(get_outbound_tag_by_section "$download_lists_via_proxy_section")"
+ config=$(
+ sing_box_cf_add_mixed_inbound_and_route_rule \
+ "$config" \
+ "$SB_SERVICE_MIXED_INBOUND_TAG" \
+ "$SB_SERVICE_MIXED_INBOUND_ADDRESS" \
+ "$SB_SERVICE_MIXED_INBOUND_PORT" \
+ "$section_outbound_tag"
+ )
+ fi
fi
config_foreach configure_section_mixed_proxy "section"
@@ -1935,15 +2107,20 @@ configure_section_mixed_proxy() {
fi
if [ "$mixed_inbound_enabled" -eq 1 ]; then
mixed_inbound_tag="$(get_inbound_tag_by_section "$section-mixed")"
- mixed_outbound_tag="$(get_outbound_tag_by_section "$section")"
- config=$(
- sing_box_cf_add_mixed_inbound_and_route_rule \
- "$config" \
- "$mixed_inbound_tag" \
- "$mixed_proxy_address" \
- "$mixed_proxy_port" \
- "$mixed_outbound_tag"
- )
+ if subscription_outbound_is_unavailable "$section"; then
+ config="$(sing_box_cm_add_mixed_inbound "$config" "$mixed_inbound_tag" "$mixed_proxy_address" "$mixed_proxy_port")"
+ config="$(sing_box_cm_add_reject_route_rule "$config" "$(gen_id)" "$mixed_inbound_tag")"
+ else
+ mixed_outbound_tag="$(get_outbound_tag_by_section "$section")"
+ config=$(
+ sing_box_cf_add_mixed_inbound_and_route_rule \
+ "$config" \
+ "$mixed_inbound_tag" \
+ "$mixed_proxy_address" \
+ "$mixed_proxy_port" \
+ "$mixed_outbound_tag"
+ )
+ fi
fi
}
@@ -2228,6 +2405,11 @@ get_download_detour_tag() {
if [ "$download_lists_via_proxy" -eq 1 ]; then
local download_lists_via_proxy_section section_outbound_tag
config_get download_lists_via_proxy_section "settings" "download_lists_via_proxy_section"
+ if subscription_outbound_is_unavailable "$download_lists_via_proxy_section"; then
+ log "Download detour section '$download_lists_via_proxy_section' is unavailable; using direct detour until subscription refresh succeeds" "warn"
+ echo ""
+ return 0
+ fi
section_outbound_tag="$(get_outbound_tag_by_section "$download_lists_via_proxy_section")"
echo "$section_outbound_tag"
else
diff --git a/podkop/files/usr/lib/constants.sh b/podkop/files/usr/lib/constants.sh
index 68bc3b51..db40974e 100644
--- a/podkop/files/usr/lib/constants.sh
+++ b/podkop/files/usr/lib/constants.sh
@@ -3,6 +3,7 @@
PODKOP_VERSION="__COMPILED_VERSION_VARIABLE__"
## Common
PODKOP_CONFIG="/etc/config/podkop"
+PODKOP_STATE_DIR="/etc/podkop"
RESOLV_CONF="/etc/resolv.conf"
DNS_RESOLVERS="1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 9.9.9.9 9.9.9.11 94.140.14.14 94.140.15.15 208.67.220.220 208.67.222.222 77.88.8.1 77.88.8.8"
CHECK_PROXY_IP_DOMAIN="ip.podkop.fyi"
@@ -10,6 +11,8 @@ FAKEIP_TEST_DOMAIN="fakeip.podkop.fyi"
TMP_SING_BOX_FOLDER="/tmp/sing-box"
TMP_RULESET_FOLDER="$TMP_SING_BOX_FOLDER/rulesets"
TMP_SUBSCRIPTION_FOLDER="$TMP_SING_BOX_FOLDER/subscriptions"
+SUBSCRIPTION_CACHE_FOLDER="$PODKOP_STATE_DIR/subscriptions"
+TMP_SUBSCRIPTION_DOWNLOAD_FOLDER="$TMP_SING_BOX_FOLDER/subscription-downloads"
CLOUDFLARE_OCTETS="8.47 162.159 188.114" # Endpoints https://github.com/ampetelin/warp-endpoint-checker
JQ_REQUIRED_VERSION="1.7.1"
COREUTILS_BASE64_REQUIRED_VERSION="9.7"
@@ -64,4 +67,4 @@ SUBNETS_HETZNER="${GITHUB_RAW_URL}/Subnets/IPv4/hetzner.lst"
SUBNETS_OVH="${GITHUB_RAW_URL}/Subnets/IPv4/ovh.lst"
SUBNETS_DIGITALOCEAN="${GITHUB_RAW_URL}/Subnets/IPv4/digitalocean.lst"
SUBNETS_CLOUDFRONT="${GITHUB_RAW_URL}/Subnets/IPv4/cloudfront.lst"
-COMMUNITY_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube hdrezka tiktok google_ai google_play hodca discord meta twitter cloudflare cloudfront digitalocean hetzner ovh telegram roblox"
\ No newline at end of file
+COMMUNITY_SERVICES="russia_inside russia_outside ukraine_inside geoblock block porn news anime youtube hdrezka tiktok google_ai google_play hodca discord meta twitter cloudflare cloudfront digitalocean hetzner ovh telegram roblox"
diff --git a/podkop/files/usr/lib/helpers.sh b/podkop/files/usr/lib/helpers.sh
index c16b9382..77eb9195 100644
--- a/podkop/files/usr/lib/helpers.sh
+++ b/podkop/files/usr/lib/helpers.sh
@@ -254,23 +254,203 @@ migration_rename_config_key() {
}
# Download URL to file
+redact_url_for_log() {
+ local url="$1"
+ local sanitized
+
+ sanitized="${url%%#*}"
+ sanitized="${sanitized%%\?*}"
+
+ case "$sanitized" in
+ *://*@*)
+ sanitized="${sanitized%%://*}://***@${sanitized#*@}"
+ ;;
+ esac
+
+ case "$url" in
+ *\?*) sanitized="$sanitized?" ;;
+ esac
+
+ printf '%s\n' "$sanitized"
+}
+
+url_host_for_log() {
+ local url="$1"
+ local host
+
+ host="${url#*://}"
+ host="${host%%/*}"
+ host="${host%%\?*}"
+ host="${host%%#*}"
+ host="${host##*@}"
+
+ case "$host" in
+ \[*\]*)
+ host="${host#\[}"
+ host="${host%%\]*}"
+ ;;
+ *)
+ host="${host%%:*}"
+ ;;
+ esac
+
+ printf '%s\n' "$host"
+}
+
+url_is_ipv6_literal() {
+ case "$1" in
+ *://\[*\]*) return 0 ;;
+ esac
+ return 1
+}
+
+wget_supports_ipv4_flag() {
+ wget --help 2>&1 | grep -Eq -- 'Use IPv4 only|(^|[[:space:]])-4([[:space:],]|$)'
+}
+
+has_ipv4_default_route() {
+ ip -4 route show default 2>/dev/null | grep -q '^default'
+}
+
+has_ipv6_default_route() {
+ ip -6 route show default 2>/dev/null | grep -q '^default'
+}
+
+has_global_ipv6_addr() {
+ ip -6 addr show scope global 2>/dev/null | grep -q 'inet6 '
+}
+
+ipv6_route_usable() {
+ ip -6 route get 2606:4700:4700::1111 >/dev/null 2>&1
+}
+
+ipv6_appears_usable() {
+ has_ipv6_default_route && has_global_ipv6_addr && ipv6_route_usable
+}
+
+get_wget_ipv4_mode() {
+ local mode
+ config_get mode "settings" "wget_ipv4_mode" "auto" 2>/dev/null
+ case "$mode" in
+ off | force | auto) echo "$mode" ;;
+ *) echo "auto" ;;
+ esac
+}
+
+should_force_wget_ipv4() {
+ local url="$1"
+ local mode
+
+ url_is_ipv6_literal "$url" && return 1
+ wget_supports_ipv4_flag || return 1
+
+ mode="$(get_wget_ipv4_mode)"
+ case "$mode" in
+ off)
+ return 1
+ ;;
+ force)
+ has_ipv4_default_route
+ return $?
+ ;;
+ auto | *)
+ has_ipv4_default_route || return 1
+ ipv6_appears_usable && return 1
+ return 0
+ ;;
+ esac
+}
+
+format_wget_error() {
+ local errfile="$1"
+ local message
+
+ message="$(tr '\n' ' ' < "$errfile" 2>/dev/null | sed 's/[[:space:]][[:space:]]*/ /g; s/^ //; s/ $//' | cut -c1-220)"
+ [ -n "$message" ] || message="no stderr from wget"
+ printf '%s\n' "$message"
+}
+
+log_wget_failure() {
+ local operation="$1"
+ local url="$2"
+ local errfile="$3"
+ local rc="$4"
+ local attempt="$5"
+ local retries="$6"
+ local timeout="$7"
+ local http_proxy_address="$8"
+ local family="$9"
+ local mode err host
+
+ if [ -n "$http_proxy_address" ]; then
+ mode="proxy $http_proxy_address"
+ else
+ mode="direct"
+ fi
+
+ err="$(format_wget_error "$errfile")"
+ host="$(url_host_for_log "$url")"
+
+ log "$operation failed [$attempt/$retries]: wget rc=$rc, mode=$mode, family=$family, timeout=${timeout}s, host=${host:-unknown}, url=$(redact_url_for_log "$url"), error=\"$err\"" "warn"
+ if echo "$err" | grep -qi 'Operation not permitted'; then
+ log "$operation got 'Operation not permitted'. On OpenWrt this often means broken IPv6/default route/firewall while WAN is IPv4-only; podkop will prefer IPv4 when wget supports -4." "warn"
+ fi
+}
+
download_to_file() {
local url="$1"
local filepath="$2"
local http_proxy_address="$3"
local retries="${4:-3}"
local wait="${5:-2}"
+ local timeout="${6:-10}"
+ local attempt errfile rc family
for attempt in $(seq 1 "$retries"); do
- if [ -n "$http_proxy_address" ]; then
- http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" wget -O "$filepath" "$url" && break
+ errfile="${filepath}.wget.err.$$"
+ family="any"
+ if should_force_wget_ipv4 "$url"; then
+ family="ipv4"
+ if [ -n "$http_proxy_address" ]; then
+ http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" wget -4 -T "$timeout" -O "$filepath" "$url" 2>"$errfile"
+ else
+ wget -4 -T "$timeout" -O "$filepath" "$url" 2>"$errfile"
+ fi
+ elif [ -n "$http_proxy_address" ]; then
+ http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" wget -T "$timeout" -O "$filepath" "$url" 2>"$errfile"
else
- wget -O "$filepath" "$url" && break
+ wget -T "$timeout" -O "$filepath" "$url" 2>"$errfile"
+ fi
+ rc=$?
+ if [ "$rc" -eq 0 ]; then
+ rm -f "$errfile"
+ return 0
fi
- log "Attempt $attempt/$retries to download $url failed" "warn"
- sleep "$wait"
+ log_wget_failure "Download" "$url" "$errfile" "$rc" "$attempt" "$retries" "$timeout" "$http_proxy_address" "$family"
+ rm -f "$errfile"
+
+ if [ "$family" != "ipv4" ] && has_ipv4_default_route && wget_supports_ipv4_flag; then
+ errfile="${filepath}.wget.err.$$"
+ log "Retrying download over IPv4-only after generic wget failure" "warn"
+ if [ -n "$http_proxy_address" ]; then
+ http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" wget -4 -T "$timeout" -O "$filepath" "$url" 2>"$errfile"
+ else
+ wget -4 -T "$timeout" -O "$filepath" "$url" 2>"$errfile"
+ fi
+ rc=$?
+ if [ "$rc" -eq 0 ]; then
+ rm -f "$errfile"
+ return 0
+ fi
+ log_wget_failure "Download IPv4 retry" "$url" "$errfile" "$rc" "$attempt" "$retries" "$timeout" "$http_proxy_address" "ipv4"
+ rm -f "$errfile"
+ fi
+
+ [ "$attempt" -lt "$retries" ] && sleep "$wait"
done
+
+ return 1
}
# Converts Windows-style line endings (CRLF) to Unix-style (LF)
@@ -425,14 +605,28 @@ download_subscription() {
kernel_version="$(get_kernel_version)"
hwid="$(generate_hwid)"
- local tmpfile
+ local tmpfile errfile rc family
tmpfile="${filepath}.part.$$"
- rm -f "$tmpfile"
+ errfile="${filepath}.err.$$"
+ rm -f "$tmpfile" "$errfile"
for attempt in $(seq 1 "$retries"); do
- if [ -n "$http_proxy_address" ]; then
- http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
- wget -T "$timeout" -O "$tmpfile" \
+ family="any"
+ if should_force_wget_ipv4 "$url"; then
+ family="ipv4"
+ if [ -n "$http_proxy_address" ]; then
+ http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
+ wget -4 -T "$timeout" -O "$tmpfile" \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" 2>"$errfile"
+ else
+ wget -4 -T "$timeout" -O "$tmpfile" \
--header "User-Agent: singbox/$sb_version" \
--header "X-HWID: $hwid" \
--header "X-Device-OS: OpenWrt Linux" \
@@ -440,30 +634,82 @@ download_subscription() {
--header "X-Ver-OS: $kernel_version" \
--header "Accept-Language: ru-RU,en,*" \
--header "X-Device-Locale: EN" \
- "$url"
+ "$url" 2>"$errfile"
+ fi
else
- wget -T "$timeout" -O "$tmpfile" \
- --header "User-Agent: singbox/$sb_version" \
- --header "X-HWID: $hwid" \
- --header "X-Device-OS: OpenWrt Linux" \
- --header "X-Device-Model: $device_model" \
- --header "X-Ver-OS: $kernel_version" \
- --header "Accept-Language: ru-RU,en,*" \
- --header "X-Device-Locale: EN" \
- "$url"
+ if [ -n "$http_proxy_address" ]; then
+ http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
+ wget -T "$timeout" -O "$tmpfile" \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" 2>"$errfile"
+ else
+ wget -T "$timeout" -O "$tmpfile" \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" 2>"$errfile"
+ fi
fi
- if [ $? -eq 0 ] && [ -s "$tmpfile" ]; then
+ rc=$?
+ if [ "$rc" -eq 0 ] && [ -s "$tmpfile" ]; then
mv "$tmpfile" "$filepath"
+ rm -f "$errfile"
return 0
fi
rm -f "$tmpfile"
- log "Attempt $attempt/$retries to download subscription from $url failed" "warn"
+ log_wget_failure "Subscription download" "$url" "$errfile" "$rc" "$attempt" "$retries" "$timeout" "$http_proxy_address" "$family"
+
+ if [ "$family" != "ipv4" ] && has_ipv4_default_route && wget_supports_ipv4_flag; then
+ family="ipv4"
+ log "Retrying subscription download over IPv4-only" "warn"
+ if [ -n "$http_proxy_address" ]; then
+ http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
+ wget -4 -T "$timeout" -O "$tmpfile" \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" 2>"$errfile"
+ else
+ wget -4 -T "$timeout" -O "$tmpfile" \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" 2>"$errfile"
+ fi
+ rc=$?
+ if [ "$rc" -eq 0 ] && [ -s "$tmpfile" ]; then
+ mv "$tmpfile" "$filepath"
+ rm -f "$errfile"
+ return 0
+ fi
+ log_wget_failure "Subscription download IPv4 retry" "$url" "$errfile" "$rc" "$attempt" "$retries" "$timeout" "$http_proxy_address" "$family"
+ fi
+
sleep "$wait"
done
rm -f "$tmpfile"
+ rm -f "$errfile"
return 1
}
@@ -480,11 +726,26 @@ check_subscription_connectivity() {
kernel_version="$(get_kernel_version)"
hwid="$(generate_hwid)"
- local attempt
+ local attempt errfile rc family
+ errfile="/tmp/podkop-subscription-check.$$"
+ rm -f "$errfile"
for attempt in $(seq 1 "$retries"); do
- if [ -n "$http_proxy_address" ]; then
- http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
- wget -q -T "$timeout" -O /dev/null \
+ family="any"
+ if should_force_wget_ipv4 "$url"; then
+ family="ipv4"
+ if [ -n "$http_proxy_address" ]; then
+ http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
+ wget -q -4 -T "$timeout" -O /dev/null \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" 2>"$errfile"
+ else
+ wget -q -4 -T "$timeout" -O /dev/null \
--header "User-Agent: singbox/$sb_version" \
--header "X-HWID: $hwid" \
--header "X-Device-OS: OpenWrt Linux" \
@@ -492,22 +753,77 @@ check_subscription_connectivity() {
--header "X-Ver-OS: $kernel_version" \
--header "Accept-Language: ru-RU,en,*" \
--header "X-Device-Locale: EN" \
- "$url" && return 0
+ "$url" 2>"$errfile"
+ fi
else
- wget -q -T "$timeout" -O /dev/null \
- --header "User-Agent: singbox/$sb_version" \
- --header "X-HWID: $hwid" \
- --header "X-Device-OS: OpenWrt Linux" \
- --header "X-Device-Model: $device_model" \
- --header "X-Ver-OS: $kernel_version" \
- --header "Accept-Language: ru-RU,en,*" \
- --header "X-Device-Locale: EN" \
- "$url" && return 0
+ if [ -n "$http_proxy_address" ]; then
+ http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
+ wget -q -T "$timeout" -O /dev/null \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" 2>"$errfile"
+ else
+ wget -q -T "$timeout" -O /dev/null \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" 2>"$errfile"
+ fi
+ fi
+
+ rc=$?
+ if [ "$rc" -eq 0 ]; then
+ rm -f "$errfile"
+ return 0
+ fi
+
+ log_wget_failure "Subscription connectivity" "$url" "$errfile" "$rc" "$attempt" "$retries" "$timeout" "$http_proxy_address" "$family"
+
+ if [ "$family" != "ipv4" ] && has_ipv4_default_route && wget_supports_ipv4_flag; then
+ family="ipv4"
+ if [ -n "$http_proxy_address" ]; then
+ http_proxy="http://$http_proxy_address" https_proxy="http://$http_proxy_address" \
+ wget -q -4 -T "$timeout" -O /dev/null \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" 2>"$errfile"
+ else
+ wget -q -4 -T "$timeout" -O /dev/null \
+ --header "User-Agent: singbox/$sb_version" \
+ --header "X-HWID: $hwid" \
+ --header "X-Device-OS: OpenWrt Linux" \
+ --header "X-Device-Model: $device_model" \
+ --header "X-Ver-OS: $kernel_version" \
+ --header "Accept-Language: ru-RU,en,*" \
+ --header "X-Device-Locale: EN" \
+ "$url" 2>"$errfile"
+ fi
+ rc=$?
+ if [ "$rc" -eq 0 ]; then
+ rm -f "$errfile"
+ return 0
+ fi
+ log_wget_failure "Subscription connectivity IPv4 retry" "$url" "$errfile" "$rc" "$attempt" "$retries" "$timeout" "$http_proxy_address" "$family"
fi
[ "$attempt" -lt "$retries" ] && sleep "$wait"
done
+ rm -f "$errfile"
return 1
}
@@ -519,6 +835,12 @@ validate_subscription_file() {
jq -e '
type == "object" and
(.outbounds | type == "array") and
- ((.outbounds | length) > 0)
+ ([.outbounds[] | select(
+ .type != "selector" and
+ .type != "urltest" and
+ .type != "direct" and
+ .type != "dns" and
+ .type != "block"
+ )] | length > 0)
' "$filepath" > /dev/null 2>&1
}
From 2de71647f015713456a754dcc1e0dbe7227b5b6c Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Tue, 26 May 2026 23:56:56 +0300
Subject: [PATCH 17/75] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B8?=
=?UTF-8?q?=D1=82=D1=8C=20=D0=B4=D0=B8=D0=B0=D0=B3=D0=BD=D0=BE=D1=81=D1=82?=
=?UTF-8?q?=D0=B8=D0=BA=D1=83=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?=
=?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BF=D0=BE=D0=B4=D0=BF=D0=B8=D1=81=D0=BE?=
=?UTF-8?q?=D0=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 1 +
podkop/files/usr/bin/podkop | 108 ++++++++++++++++++-------
podkop/files/usr/lib/helpers.sh | 137 ++++++++++++++++++++++++++++----
3 files changed, 203 insertions(+), 43 deletions(-)
diff --git a/.gitignore b/.gitignore
index 21b50a0f..4a470951 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
fe-app-podkop/node_modules
fe-app-podkop/.env
.DS_Store
+*.txt
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index f0d75a2c..05dc894b 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -323,21 +323,35 @@ download_subscription_into_cache() {
local subscription_json_path="$3"
local subscription_url_cache_path="$4"
local service_proxy_address="$5"
- local tmpfile persist_tmpfile url_tmpfile rejected_cache_path tmp_hash rejected_hash
+ local tmpfile persist_tmpfile url_tmpfile rejected_cache_path tmp_hash rejected_hash validation_reason file_size
- ensure_subscription_cache_dir || return 1
- mkdir -p "$TMP_SUBSCRIPTION_DOWNLOAD_FOLDER" || return 1
- tmpfile="$(mktemp "$TMP_SUBSCRIPTION_DOWNLOAD_FOLDER/${section}.download.XXXXXX")" || return 1
+ ensure_subscription_cache_dir || {
+ log "Failed to prepare persistent subscription cache directory '$SUBSCRIPTION_CACHE_FOLDER' for section '$section'" "error"
+ return 10
+ }
+ mkdir -p "$TMP_SUBSCRIPTION_DOWNLOAD_FOLDER" || {
+ log "Failed to create temporary subscription download directory '$TMP_SUBSCRIPTION_DOWNLOAD_FOLDER' for section '$section'" "error"
+ return 11
+ }
+ tmpfile="$(mktemp "$TMP_SUBSCRIPTION_DOWNLOAD_FOLDER/${section}.download.XXXXXX")" || {
+ log "Failed to create temporary subscription download file in '$TMP_SUBSCRIPTION_DOWNLOAD_FOLDER' for section '$section'" "error"
+ return 11
+ }
if ! download_subscription "$subscription_url" "$tmpfile" "$service_proxy_address" 3 2 10; then
+ log "Subscription body download failed for section '$section' after retries" "error"
rm -f "$tmpfile"
- return 1
+ return 12
fi
+ file_size="$(wc -c < "$tmpfile" 2>/dev/null | tr -d ' ')"
+ log "Downloaded subscription body for section '$section': bytes=${file_size:-unknown}" "debug"
+
if ! validate_subscription_file "$tmpfile"; then
- log "Downloaded subscription for section '$section' is invalid" "error"
+ validation_reason="$(describe_subscription_validation_failure "$tmpfile")"
+ log "Downloaded subscription for section '$section' is invalid: ${validation_reason:-unknown validation error}" "error"
rm -f "$tmpfile"
- return 1
+ return 13
fi
rejected_cache_path="$(get_subscription_rejected_cache_path "$section")"
@@ -346,15 +360,19 @@ download_subscription_into_cache() {
if [ -n "$tmp_hash" ] && [ "$tmp_hash" = "$rejected_hash" ]; then
log "Downloaded subscription for section '$section' is unchanged and was previously rejected because it contains no usable sing-box outbounds" "warn"
rm -f "$tmpfile"
- return 1
+ return 14
fi
if [ -f "$subscription_json_path" ] && cmp -s "$tmpfile" "$subscription_json_path"; then
rm -f "$tmpfile"
if [ "$(cat "$subscription_url_cache_path" 2>/dev/null)" != "$subscription_url" ]; then
url_tmpfile="${subscription_url_cache_path}.tmp.$$"
- printf '%s' "$subscription_url" > "$url_tmpfile" && mv "$url_tmpfile" "$subscription_url_cache_path"
- chmod 600 "$subscription_url_cache_path" 2>/dev/null
+ if printf '%s' "$subscription_url" > "$url_tmpfile" && mv "$url_tmpfile" "$subscription_url_cache_path"; then
+ chmod 600 "$subscription_url_cache_path" 2>/dev/null
+ else
+ log "Subscription content for section '$section' is unchanged, but URL metadata could not be persisted; the next refresh may download it again" "warn"
+ rm -f "$url_tmpfile"
+ fi
fi
log "Subscription for section '$section' is unchanged" "info"
return 2
@@ -362,14 +380,21 @@ download_subscription_into_cache() {
persist_tmpfile="${subscription_json_path}.tmp.$$"
cp "$tmpfile" "$persist_tmpfile" && mv "$persist_tmpfile" "$subscription_json_path" || {
+ log "Failed to persist subscription cache for section '$section' to '$subscription_json_path'" "error"
rm -f "$tmpfile" "$persist_tmpfile"
- return 1
+ return 15
}
rm -f "$tmpfile"
rm -f "$rejected_cache_path"
url_tmpfile="${subscription_url_cache_path}.tmp.$$"
- printf '%s' "$subscription_url" > "$url_tmpfile" && mv "$url_tmpfile" "$subscription_url_cache_path"
+ printf '%s' "$subscription_url" > "$url_tmpfile" && mv "$url_tmpfile" "$subscription_url_cache_path" || {
+ # The valid JSON cache has already been atomically installed. Treat
+ # metadata failure as non-fatal so recovery reloads and applies it;
+ # only refresh deduplication is degraded until metadata can be written.
+ log "Subscription cache for section '$section' was updated, but URL metadata could not be persisted; applying the cache and retrying metadata on a later refresh" "warn"
+ rm -f "$url_tmpfile"
+ }
chmod 600 "$subscription_json_path" "$subscription_url_cache_path" 2>/dev/null
return 0
}
@@ -488,13 +513,15 @@ start_subscription_startup_retry_worker() {
# Run in a child process: a successful subscription_update performs
# its own restart, which stops this worker via the pidfile safely.
- if /usr/bin/podkop subscription_update >/dev/null 2>&1; then
+ # Keep diagnostics visible in syslog; subscription URLs are redacted
+ # in the lower-level download helpers.
+ if /usr/bin/podkop subscription_update; then
log "Deferred subscription refresh succeeded; updated configuration is being applied" "info"
rm -f "$pidfile"
exit 0
fi
- log "Deferred subscription refresh is still waiting for connectivity" "warn"
+ log "Deferred subscription refresh has not completed yet; retrying in background" "warn"
sleep 30
done
) &
@@ -1098,10 +1125,15 @@ subscription_update() {
echolog "рџ“Ґ Updating subscription for section '$section'..."
- service_proxy_address="$(get_subscription_download_proxy_address "$section" "runtime" 2>/dev/null || echo '')"
+ service_proxy_address="$(get_subscription_download_proxy_address "$section" "runtime" || echo '')"
+ if [ -n "$service_proxy_address" ]; then
+ log "Updating subscription for section '$section' via service proxy $service_proxy_address" "info"
+ else
+ log "Updating subscription for section '$section' directly" "info"
+ fi
if ! wait_for_subscription_connectivity "$section" "$subscription_url" "$service_proxy_address" 6 5 5; then
- echolog "вќЊ Failed to download subscription for section '$section'"
+ echolog "вќЊ Subscription source is not reachable for section '$section'"
failed_sections=$((failed_sections + 1))
return
fi
@@ -1113,27 +1145,47 @@ subscription_update() {
case "$update_result" in
0)
updated_sections=$((updated_sections + 1))
+ outbounds_count=$(jq -r '[.outbounds[] | select(
+ .type != "selector" and
+ .type != "urltest" and
+ .type != "direct" and
+ .type != "dns" and
+ .type != "block"
+ )] | length' "$subscription_json_path" 2>/dev/null)
+
+ echolog "вњ… Subscription updated for section '$section': $outbounds_count outbounds"
;;
2)
echolog "в„№пёЏ Subscription for section '$section' is unchanged"
return
;;
+ 10)
+ echolog "вќЊ Failed to prepare subscription cache for section '$section'"
+ ;;
+ 11)
+ echolog "вќЊ Failed to create temporary subscription download file for section '$section'"
+ ;;
+ 12)
+ echolog "вќЊ Failed to download subscription body for section '$section'"
+ ;;
+ 13)
+ echolog "вќЊ Downloaded subscription for section '$section' is invalid"
+ ;;
+ 14)
+ echolog "вќЊ Downloaded subscription for section '$section' is unchanged and still has no usable outbounds"
+ ;;
+ 15)
+ echolog "вќЊ Failed to persist subscription cache for section '$section'"
+ ;;
*)
- echolog "вќЊ Failed to download subscription for section '$section'"
- failed_sections=$((failed_sections + 1))
- return
+ echolog "вќЊ Failed to update subscription for section '$section': internal error rc=$update_result"
;;
esac
- outbounds_count=$(jq -r '[.outbounds[] | select(
- .type != "selector" and
- .type != "urltest" and
- .type != "direct" and
- .type != "dns" and
- .type != "block"
- )] | length' "$subscription_json_path" 2>/dev/null)
-
- echolog "вњ… Subscription updated for section '$section': $outbounds_count outbounds"
+ if [ "$update_result" -ne 0 ]; then
+ failed_sections=$((failed_sections + 1))
+ return
+ fi
}
config_foreach _update_subscription_for_section "section"
diff --git a/podkop/files/usr/lib/helpers.sh b/podkop/files/usr/lib/helpers.sh
index 77eb9195..3ce3d3e5 100644
--- a/podkop/files/usr/lib/helpers.sh
+++ b/podkop/files/usr/lib/helpers.sh
@@ -256,22 +256,57 @@ migration_rename_config_key() {
# Download URL to file
redact_url_for_log() {
local url="$1"
- local sanitized
+ local scheme rest authority suffix userinfo_flag path_flag query_flag fragment_flag
- sanitized="${url%%#*}"
- sanitized="${sanitized%%\?*}"
+ scheme=""
+ rest="$url"
+ userinfo_flag=0
+ path_flag=0
+ query_flag=0
+ fragment_flag=0
- case "$sanitized" in
- *://*@*)
- sanitized="${sanitized%%://*}://***@${sanitized#*@}"
- ;;
+ case "$url" in
+ *'#'*) fragment_flag=1 ;;
esac
+ rest="${rest%%#*}"
case "$url" in
- *\?*) sanitized="$sanitized?" ;;
+ *\?*) query_flag=1 ;;
+ esac
+ rest="${rest%%\?*}"
+
+ case "$rest" in
+ *://*)
+ scheme="${rest%%://*}://"
+ rest="${rest#*://}"
+ ;;
esac
- printf '%s\n' "$sanitized"
+ authority="${rest%%/*}"
+ if [ "$authority" != "$rest" ]; then
+ path_flag=1
+ fi
+
+ case "$authority" in
+ *@*)
+ userinfo_flag=1
+ authority="${authority##*@}"
+ ;;
+ esac
+
+ suffix=""
+ [ "$path_flag" -eq 1 ] && suffix="$suffix/"
+ [ "$query_flag" -eq 1 ] && suffix="$suffix?"
+ [ "$fragment_flag" -eq 1 ] && suffix="$suffix#"
+
+ if [ -z "$authority" ]; then
+ printf 'redacted-url(has_path=%s,has_query=%s,has_userinfo=%s,has_fragment=%s)\n' \
+ "$path_flag" "$query_flag" "$userinfo_flag" "$fragment_flag"
+ return 0
+ fi
+
+ printf '%s%s%s(has_path=%s,has_query=%s,has_userinfo=%s,has_fragment=%s)\n' \
+ "$scheme" "$authority" "$suffix" "$path_flag" "$query_flag" "$userinfo_flag" "$fragment_flag"
}
url_host_for_log() {
@@ -363,13 +398,33 @@ should_force_wget_ipv4() {
format_wget_error() {
local errfile="$1"
+ local url="$2"
local message
message="$(tr '\n' ' ' < "$errfile" 2>/dev/null | sed 's/[[:space:]][[:space:]]*/ /g; s/^ //; s/ $//' | cut -c1-220)"
+ message="$(printf '%s' "$message" | sed 's#[Hh][Tt][Tt][Pp][Ss]\{0,1\}://[^[:space:]]*##g')"
[ -n "$message" ] || message="no stderr from wget"
printf '%s\n' "$message"
}
+wget_error_class() {
+ local err="$1"
+
+ if echo "$err" | grep -qi 'Operation not permitted'; then
+ echo "operation_not_permitted"
+ elif echo "$err" | grep -qi 'not an http or ftp url\|bad address\|unable to resolve\|Name or service not known'; then
+ echo "dns_or_bad_url"
+ elif echo "$err" | grep -qi 'timed out\|timeout'; then
+ echo "timeout"
+ elif echo "$err" | grep -qi 'certificate\|SSL\|TLS'; then
+ echo "tls"
+ elif echo "$err" | grep -qi '404\|403\|401\|500\|502\|503\|HTTP'; then
+ echo "http"
+ else
+ echo "unknown"
+ fi
+}
+
log_wget_failure() {
local operation="$1"
local url="$2"
@@ -380,7 +435,7 @@ log_wget_failure() {
local timeout="$7"
local http_proxy_address="$8"
local family="$9"
- local mode err host
+ local mode err host err_class
if [ -n "$http_proxy_address" ]; then
mode="proxy $http_proxy_address"
@@ -388,12 +443,13 @@ log_wget_failure() {
mode="direct"
fi
- err="$(format_wget_error "$errfile")"
+ err="$(format_wget_error "$errfile" "$url")"
host="$(url_host_for_log "$url")"
+ err_class="$(wget_error_class "$err")"
- log "$operation failed [$attempt/$retries]: wget rc=$rc, mode=$mode, family=$family, timeout=${timeout}s, host=${host:-unknown}, url=$(redact_url_for_log "$url"), error=\"$err\"" "warn"
+ log "$operation failed [$attempt/$retries]: wget rc=$rc, mode=$mode, family=$family, timeout=${timeout}s, host=${host:-unknown}, url=$(redact_url_for_log "$url"), error_class=$err_class, error=\"$err\"" "warn"
if echo "$err" | grep -qi 'Operation not permitted'; then
- log "$operation got 'Operation not permitted'. On OpenWrt this often means broken IPv6/default route/firewall while WAN is IPv4-only; podkop will prefer IPv4 when wget supports -4." "warn"
+ log "$operation got 'Operation not permitted'. On OpenWrt this can indicate firewall, routing, or IPv6 preference issues; podkop will retry with IPv4 when supported." "warn"
fi
}
@@ -663,11 +719,19 @@ download_subscription() {
rc=$?
if [ "$rc" -eq 0 ] && [ -s "$tmpfile" ]; then
- mv "$tmpfile" "$filepath"
+ if ! mv "$tmpfile" "$filepath"; then
+ log "Subscription download succeeded but failed to move temporary file to destination" "error"
+ rm -f "$tmpfile" "$errfile"
+ return 1
+ fi
rm -f "$errfile"
return 0
fi
+ if [ "$rc" -eq 0 ] && [ ! -s "$tmpfile" ]; then
+ log "Subscription download returned success but produced an empty file: host=$(url_host_for_log "$url"), url=$(redact_url_for_log "$url")" "warn"
+ fi
+
rm -f "$tmpfile"
log_wget_failure "Subscription download" "$url" "$errfile" "$rc" "$attempt" "$retries" "$timeout" "$http_proxy_address" "$family"
@@ -698,10 +762,17 @@ download_subscription() {
fi
rc=$?
if [ "$rc" -eq 0 ] && [ -s "$tmpfile" ]; then
- mv "$tmpfile" "$filepath"
+ if ! mv "$tmpfile" "$filepath"; then
+ log "Subscription download IPv4 retry succeeded but failed to move temporary file to destination" "error"
+ rm -f "$tmpfile" "$errfile"
+ return 1
+ fi
rm -f "$errfile"
return 0
fi
+ if [ "$rc" -eq 0 ] && [ ! -s "$tmpfile" ]; then
+ log "Subscription download IPv4 retry returned success but produced an empty file: host=$(url_host_for_log "$url"), url=$(redact_url_for_log "$url")" "warn"
+ fi
log_wget_failure "Subscription download IPv4 retry" "$url" "$errfile" "$rc" "$attempt" "$retries" "$timeout" "$http_proxy_address" "$family"
fi
@@ -710,6 +781,7 @@ download_subscription() {
rm -f "$tmpfile"
rm -f "$errfile"
+ log "Subscription download failed after $retries attempts: host=$(url_host_for_log "$url"), url=$(redact_url_for_log "$url")" "error"
return 1
}
@@ -844,3 +916,38 @@ validate_subscription_file() {
)] | length > 0)
' "$filepath" > /dev/null 2>&1
}
+
+describe_subscription_validation_failure() {
+ local filepath="$1"
+ local total usable
+
+ if [ ! -s "$filepath" ]; then
+ echo "downloaded file is empty"
+ return 0
+ fi
+
+ if ! jq -e '.' "$filepath" >/dev/null 2>&1; then
+ echo "downloaded file is not valid JSON"
+ return 0
+ fi
+
+ if ! jq -e 'type == "object"' "$filepath" >/dev/null 2>&1; then
+ echo "subscription root is not a JSON object"
+ return 0
+ fi
+
+ if ! jq -e '.outbounds | type == "array"' "$filepath" >/dev/null 2>&1; then
+ echo "subscription has no outbounds array"
+ return 0
+ fi
+
+ total="$(jq -r '.outbounds | length' "$filepath" 2>/dev/null)"
+ usable="$(jq -r '[.outbounds[] | select(
+ .type != "selector" and
+ .type != "urltest" and
+ .type != "direct" and
+ .type != "dns" and
+ .type != "block"
+ )] | length' "$filepath" 2>/dev/null)"
+ echo "subscription contains no usable proxy outbounds: total=${total:-unknown}, usable=${usable:-unknown}"
+}
From b6a0f8ea7113e90e7d5f6b6c86201f98d67e075b Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Wed, 27 May 2026 10:32:31 +0300
Subject: [PATCH 18/75] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B8?=
=?UTF-8?q?=D1=82=D1=8C=20=D0=B4=D0=B8=D0=B0=D0=B3=D0=BD=D0=BE=D1=81=D1=82?=
=?UTF-8?q?=D0=B8=D0=BA=D1=83=20=D0=BA=D0=B5=D1=88=D0=B0=20=D0=BF=D0=BE?=
=?UTF-8?q?=D0=B4=D0=BF=D0=B8=D1=81=D0=BE=D0=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
podkop/files/usr/bin/podkop | 31 +++++++++++++++++++++++++++----
1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index 05dc894b..a3c74e3b 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -167,12 +167,31 @@ get_subscription_rejected_cache_path() {
ensure_subscription_cache_dir() {
local state_dir_created=0 cache_dir_created=0
+ local mkdir_errfile mkdir_rc
[ -d "$PODKOP_STATE_DIR" ] || state_dir_created=1
[ -d "$SUBSCRIPTION_CACHE_FOLDER" ] || cache_dir_created=1
- mkdir -p "$SUBSCRIPTION_CACHE_FOLDER" || return 1
- [ "$state_dir_created" -eq 1 ] && chmod 700 "$PODKOP_STATE_DIR" 2>/dev/null
- [ "$cache_dir_created" -eq 1 ] && chmod 700 "$SUBSCRIPTION_CACHE_FOLDER" 2>/dev/null
+ mkdir_errfile="/tmp/podkop-subscription-cache-mkdir.$$"
+ if mkdir -p "$SUBSCRIPTION_CACHE_FOLDER" 2>"$mkdir_errfile"; then
+ rm -f "$mkdir_errfile"
+ else
+ mkdir_rc=$?
+ if [ -d "$SUBSCRIPTION_CACHE_FOLDER" ]; then
+ log "Subscription cache directory '$SUBSCRIPTION_CACHE_FOLDER' already exists; continuing after mkdir rc=$mkdir_rc" "warn"
+ rm -f "$mkdir_errfile"
+ else
+ local mkdir_err
+ mkdir_err="$(tr '\n' ' ' < "$mkdir_errfile" 2>/dev/null | sed 's/[[:space:]][[:space:]]*/ /g; s/^ //; s/ $//' | cut -c1-220)"
+ rm -f "$mkdir_errfile"
+ log "Failed to prepare subscription cache directory '$SUBSCRIPTION_CACHE_FOLDER': mkdir rc=$mkdir_rc, state_dir_created=$state_dir_created, cache_dir_created=$cache_dir_created, error=\"${mkdir_err:-no stderr}\"" "error"
+ return 1
+ fi
+ fi
+
+ if [ "$state_dir_created" -eq 1 ] || [ "$cache_dir_created" -eq 1 ]; then
+ chmod 700 "$PODKOP_STATE_DIR" 2>/dev/null
+ chmod 700 "$SUBSCRIPTION_CACHE_FOLDER" 2>/dev/null
+ fi
}
migrate_subscription_cache_from_tmp() {
@@ -1119,7 +1138,11 @@ subscription_update() {
fi
mkdir -p "$TMP_SUBSCRIPTION_FOLDER"
- ensure_subscription_cache_dir
+ if ! ensure_subscription_cache_dir; then
+ echolog "вќЊ Subscription cache directory is unavailable for section '$section'"
+ failed_sections=$((failed_sections + 1))
+ return
+ fi
subscription_json_path="$(get_subscription_json_path "$section")"
subscription_url_cache_path="$(get_subscription_url_cache_path "$section")"
From 3cf321a78bd5762b3383c50a0a09a818ed8372f1 Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Tue, 2 Jun 2026 16:15:30 +0300
Subject: [PATCH 19/75] feat: sing-box-extended core switching + xhttp client
transport
Add runtime sing-box core switching (stable <-> extended) and xhttp
client outbound support, reimplemented on the fork's jq stack (no ucode).
Backend:
- helpers.sh: is_sing_box_extended() / get_sing_box_version()
- get_system_info exposes sing_box_extended flag
- updater.sh (new): download sing-box-extended from shtorm-7/sing-box-extended
with arch/musl detection, jq release parsing, backup/extract/validate/rollback;
install_stable reverts to packaged sing-box; component_action dispatch
- sing_box_config_facade.sh: xhttp transport branch gated on extended
(path/host/sni/mode), default ALPN h2,http/1.1; subscription xhttp skipped
when core not extended
- sing_box_config_manager.sh: sing_box_cm_set_xhttp_transport_for_outbound (jq)
Frontend:
- types: sing_box_extended + component action method
- async componentAction via executeShellCommand (long timeout)
- Diagnostics tab: Install extended / Install stable button
- RU locales for new strings
Default install keeps stock sing-box; extended is opt-in via UI.
---
fe-app-podkop/locales/calls.json | 758 ++++++++++--------
fe-app-podkop/locales/podkop.pot | 718 +++++++++--------
fe-app-podkop/locales/podkop.ru.po | 56 +-
.../src/podkop/methods/shell/index.ts | 40 +
.../src/podkop/services/store.service.ts | 2 +
.../tabs/diagnostic/diagnostic.store.ts | 4 +
.../podkop/tabs/diagnostic/initController.ts | 56 +-
.../partials/renderAvailableActions.ts | 13 +
fe-app-podkop/src/podkop/types.ts | 1 +
.../luci-static/resources/view/podkop/main.js | 101 ++-
luci-app-podkop/po/ru/podkop.po | 56 +-
luci-app-podkop/po/templates/podkop.pot | 718 +++++++++--------
podkop/files/usr/bin/podkop | 23 +-
podkop/files/usr/lib/helpers.sh | 17 +-
.../files/usr/lib/sing_box_config_facade.sh | 31 +-
.../files/usr/lib/sing_box_config_manager.sh | 55 ++
podkop/files/usr/lib/updater.sh | 368 +++++++++
17 files changed, 2025 insertions(+), 992 deletions(-)
create mode 100644 podkop/files/usr/lib/updater.sh
diff --git a/fe-app-podkop/locales/calls.json b/fe-app-podkop/locales/calls.json
index 742d92b9..033db57f 100644
--- a/fe-app-podkop/locales/calls.json
+++ b/fe-app-podkop/locales/calls.json
@@ -3,1875 +3,1991 @@
"call": "✔ Enabled",
"key": "✔ Enabled",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:345"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:345"
]
},
{
"call": "✔ Running",
"key": "✔ Running",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:356"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:356"
]
},
{
"call": "✘ Disabled",
"key": "✘ Disabled",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:346"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:346"
]
},
{
"call": "✘ Stopped",
"key": "✘ Stopped",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:357"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:357"
+ ]
+ },
+ {
+ "call": "Группировать по странам",
+ "key": "Группировать по странам",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:127"
+ ]
+ },
+ {
+ "call": "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы",
+ "key": "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:128"
]
},
{
"call": "Active Connections",
"key": "Active Connections",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:307"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:307"
]
},
{
"call": "Additional marking rules found",
"key": "Additional marking rules found",
"places": [
- "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:106"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:106"
]
},
{
"call": "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall.",
"key": "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall.",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:247"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:247"
]
},
{
"call": "Applicable for SOCKS and Shadowsocks proxy",
"key": "Applicable for SOCKS and Shadowsocks proxy",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:199"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:251"
]
},
{
"call": "At least one valid domain must be specified. Comments-only content is not allowed.",
"key": "At least one valid domain must be specified. Comments-only content is not allowed.",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:444"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:496"
]
},
{
"call": "At least one valid subnet or IP must be specified. Comments-only content is not allowed.",
"key": "At least one valid subnet or IP must be specified. Comments-only content is not allowed.",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:525"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:577"
]
},
{
"call": "Available actions",
"key": "Available actions",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:43"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:47"
]
},
{
"call": "Bootsrap DNS",
"key": "Bootsrap DNS",
"places": [
- "src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:65"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:65"
]
},
{
"call": "Bootstrap DNS server",
"key": "Bootstrap DNS server",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:45"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:45"
]
},
{
"call": "Browser is not using FakeIP",
"key": "Browser is not using FakeIP",
"places": [
- "src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:58"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:58"
]
},
{
"call": "Browser is using FakeIP correctly",
"key": "Browser is using FakeIP correctly",
"places": [
- "src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:57"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:57"
]
},
{
"call": "Cache File Path",
"key": "Cache File Path",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:348"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:348"
]
},
{
"call": "Cache file path cannot be empty",
"key": "Cache file path cannot be empty",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:362"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:362"
]
},
{
"call": "Cannot receive checks result",
"key": "Cannot receive checks result",
"places": [
- "src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:27",
- "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:28",
- "src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:27",
- "src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:25"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:27",
+ "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:28",
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:27",
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:25"
]
},
{
"call": "Checking, please wait",
"key": "Checking, please wait",
"places": [
- "src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:15",
- "src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:15",
- "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:13",
- "src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:15",
- "src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:13"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:15",
+ "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:15",
+ "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:13",
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:15",
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:13"
]
},
{
"call": "checks",
"key": "checks",
"places": [
- "src/podkop/tabs/diagnostic/helpers/getCheckTitle.ts:2"
+ "src\\podkop\\tabs\\diagnostic\\helpers\\getCheckTitle.ts:2"
]
},
{
"call": "Checks failed",
"key": "Checks failed",
"places": [
- "src/podkop/tabs/diagnostic/helpers/getMeta.ts:26"
+ "src\\podkop\\tabs\\diagnostic\\helpers\\getMeta.ts:26"
]
},
{
"call": "Checks passed",
"key": "Checks passed",
"places": [
- "src/podkop/tabs/diagnostic/helpers/getMeta.ts:13"
+ "src\\podkop\\tabs\\diagnostic\\helpers\\getMeta.ts:13"
]
},
{
"call": "CIDR must be between 0 and 32",
"key": "CIDR must be between 0 and 32",
"places": [
- "src/validators/validateSubnet.ts:33"
+ "src\\validators\\validateSubnet.ts:33"
]
},
{
"call": "Close",
"key": "Close",
"places": [
- "src/partials/modal/renderModal.ts:26"
+ "src\\partials\\modal\\renderModal.ts:26"
]
},
{
"call": "Community Lists",
"key": "Community Lists",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:299"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:351"
]
},
{
"call": "Config File Path",
"key": "Config File Path",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:335"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:335"
]
},
{
"call": "Configuration for Podkop service",
"key": "Configuration for Podkop service",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:27"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:27"
]
},
{
"call": "Configuration Type",
"key": "Configuration Type",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:23"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:23"
]
},
{
"call": "Connection Type",
"key": "Connection Type",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:12"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:12"
]
},
{
"call": "Connection URL",
"key": "Connection URL",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:26"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:26"
]
},
{
"call": "Copy",
"key": "Copy",
"places": [
- "src/partials/modal/renderModal.ts:20"
+ "src\\partials\\modal\\renderModal.ts:20"
]
},
{
"call": "Currently unavailable",
"key": "Currently unavailable",
"places": [
- "src/podkop/tabs/dashboard/partials/renderWidget.ts:22"
+ "src\\podkop\\tabs\\dashboard\\partials\\renderWidget.ts:22"
]
},
{
"call": "Dashboard",
"key": "Dashboard",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:80"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:80"
]
},
{
"call": "Dashboard currently unavailable",
"key": "Dashboard currently unavailable",
"places": [
- "src/podkop/tabs/dashboard/partials/renderSections.ts:19"
+ "src\\podkop\\tabs\\dashboard\\partials\\renderSections.ts:19"
]
},
{
"call": "Delay in milliseconds before reloading podkop after interface UP",
"key": "Delay in milliseconds before reloading podkop after interface UP",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:222"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:222"
]
},
{
"call": "Delay value cannot be empty",
"key": "Delay value cannot be empty",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:229"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:229"
]
},
{
"call": "DHCP has DNS server",
"key": "DHCP has DNS server",
"places": [
- "src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:82"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:82"
]
},
{
"call": "Diagnostics",
"key": "Diagnostics",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:65"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:65"
]
},
{
"call": "Disable autostart",
"key": "Disable autostart",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:79"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:83"
]
},
{
"call": "Disable QUIC",
"key": "Disable QUIC",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:265"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:265"
]
},
{
"call": "Disable the QUIC protocol to improve compatibility or fix issues with video streaming",
"key": "Disable the QUIC protocol to improve compatibility or fix issues with video streaming",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:266"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:266"
]
},
{
"call": "Disabled",
"key": "Disabled",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:390",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:470"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:442",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:522"
]
},
{
"call": "DNS on router",
"key": "DNS on router",
"places": [
- "src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:77"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:77"
]
},
{
"call": "DNS over HTTPS (DoH)",
"key": "DNS over HTTPS (DoH)",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:267",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:15"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:319",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:15"
]
},
{
"call": "DNS over TLS (DoT)",
"key": "DNS over TLS (DoT)",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:268",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:16"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:320",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:16"
]
},
{
"call": "DNS Protocol Type",
"key": "DNS Protocol Type",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:264",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:12"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:316",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:12"
]
},
{
"call": "DNS Rewrite TTL",
"key": "DNS Rewrite TTL",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:68"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:68"
]
},
{
"call": "DNS Server",
"key": "DNS Server",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:277",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:24"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:329",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:24"
]
},
{
"call": "DNS server address cannot be empty",
"key": "DNS server address cannot be empty",
"places": [
- "src/validators/validateDns.ts:7"
+ "src\\validators\\validateDns.ts:7"
]
},
{
"call": "Do not panic, everything can be fixed, just...",
"key": "Do not panic, everything can be fixed, just...",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts:26"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderWikiDisclaimer.ts:26"
]
},
{
"call": "Domain Resolver",
"key": "Domain Resolver",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:254"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:306"
]
},
{
"call": "Dont Touch My DHCP!",
"key": "Dont Touch My DHCP!",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:326"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:326"
]
},
{
"call": "Downlink",
"key": "Downlink",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:241",
- "src/podkop/tabs/dashboard/initController.ts:275"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:241",
+ "src\\podkop\\tabs\\dashboard\\initController.ts:275"
]
},
{
"call": "Download",
"key": "Download",
"places": [
- "src/partials/modal/renderModal.ts:15"
+ "src\\partials\\modal\\renderModal.ts:15"
]
},
{
"call": "Download Lists via Proxy/VPN",
"key": "Download Lists via Proxy/VPN",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:288"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:288"
]
},
{
"call": "Download Lists via specific proxy section",
"key": "Download Lists via specific proxy section",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:297"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:297"
]
},
{
"call": "Downloading all lists via specific Proxy/VPN",
"key": "Downloading all lists via specific Proxy/VPN",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:289",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:298"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:289",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:298"
]
},
{
"call": "Dynamic List",
"key": "Dynamic List",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:391",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:471"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:443",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:523"
]
},
{
"call": "Enable autostart",
"key": "Enable autostart",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:89"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:93"
]
},
{
"call": "Enable built-in DNS resolver for domains handled by this section",
"key": "Enable built-in DNS resolver for domains handled by this section",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:255"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:307"
]
},
{
"call": "Enable DNS resolve to get real IP when routing",
"key": "Enable DNS resolve to get real IP when routing",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:691"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:746"
]
},
{
"call": "Enable Mixed Proxy",
"key": "Enable Mixed Proxy",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:665"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:717"
]
},
{
"call": "Enable Output Network Interface",
"key": "Enable Output Network Interface",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:126"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:126"
]
},
{
"call": "Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies",
"key": "Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:666"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:718"
]
},
{
"call": "Enable YACD",
"key": "Enable YACD",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:237"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:237"
]
},
{
"call": "Enable YACD WAN Access",
"key": "Enable YACD WAN Access",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:246"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:246"
]
},
{
"call": "Enter complete outbound configuration in JSON format",
"key": "Enter complete outbound configuration in JSON format",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:66"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:67"
]
},
{
"call": "Enter domain names separated by commas, spaces, or newlines. You can add comments using //",
"key": "Enter domain names separated by commas, spaces, or newlines. You can add comments using //",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:426"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:478"
]
},
{
"call": "Enter domain names without protocols, e.g. example.com or sub.example.com",
"key": "Enter domain names without protocols, e.g. example.com or sub.example.com",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:400"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:452"
]
},
{
"call": "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses",
"key": "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:480"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:532"
+ ]
+ },
+ {
+ "call": "Enter the subscription URL to fetch proxy configurations from your provider",
+ "key": "Enter the subscription URL to fetch proxy configurations from your provider",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:90"
]
},
{
"call": "Every 1 minute",
"key": "Every 1 minute",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:138"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:187"
+ ]
+ },
+ {
+ "call": "Every 12 hours",
+ "key": "Every 12 hours",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:119"
+ ]
+ },
+ {
+ "call": "Every 3 hours",
+ "key": "Every 3 hours",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:117"
]
},
{
"call": "Every 3 minutes",
"key": "Every 3 minutes",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:139"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:188"
+ ]
+ },
+ {
+ "call": "Every 30 minutes",
+ "key": "Every 30 minutes",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:115"
]
},
{
"call": "Every 30 seconds",
"key": "Every 30 seconds",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:137"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:186"
]
},
{
"call": "Every 5 minutes",
"key": "Every 5 minutes",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:140"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:189"
+ ]
+ },
+ {
+ "call": "Every 6 hours",
+ "key": "Every 6 hours",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:118"
+ ]
+ },
+ {
+ "call": "Every day",
+ "key": "Every day",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:120"
+ ]
+ },
+ {
+ "call": "Every hour",
+ "key": "Every hour",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:116"
]
},
{
"call": "Exclude NTP",
"key": "Exclude NTP",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:402"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:402"
]
},
{
"call": "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN",
"key": "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:403"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:403"
]
},
{
"call": "Failed to copy!",
"key": "Failed to copy!",
"places": [
- "src/helpers/copyToClipboard.ts:12"
+ "src\\helpers\\copyToClipboard.ts:12"
]
},
{
"call": "Failed to execute!",
"key": "Failed to execute!",
"places": [
- "src/podkop/tabs/diagnostic/initController.ts:227",
- "src/podkop/tabs/diagnostic/initController.ts:231",
- "src/podkop/tabs/diagnostic/initController.ts:261",
- "src/podkop/tabs/diagnostic/initController.ts:265",
- "src/podkop/tabs/diagnostic/initController.ts:302",
- "src/podkop/tabs/diagnostic/initController.ts:306"
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:229",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:233",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:263",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:267",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:304",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:308",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:342",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:346"
]
},
{
"call": "Fastest",
"key": "Fastest",
"places": [
- "src/podkop/methods/custom/getDashboardSections.ts:148",
- "src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:59"
+ "src\\podkop\\methods\\custom\\getDashboardSections.ts:150",
+ "src\\podkop\\methods\\custom\\getDashboardSections.ts:181",
+ "src\\podkop\\methods\\custom\\getDashboardSections.ts:218",
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:59"
]
},
{
"call": "Fully Routed IPs",
"key": "Fully Routed IPs",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:638"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:690"
]
},
{
"call": "Get global check",
"key": "Get global check",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:98"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:102"
]
},
{
"call": "Global check",
"key": "Global check",
"places": [
- "src/podkop/tabs/diagnostic/initController.ts:222"
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:224"
+ ]
+ },
+ {
+ "call": "How often to automatically update the subscription",
+ "key": "How often to automatically update the subscription",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:113"
]
},
{
"call": "HTTP error",
"key": "HTTP error",
"places": [
- "src/podkop/api.ts:27"
+ "src\\podkop\\api.ts:27"
+ ]
+ },
+ {
+ "call": "Install extended",
+ "key": "Install extended",
+ "places": [
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:129"
+ ]
+ },
+ {
+ "call": "Install stable",
+ "key": "Install stable",
+ "places": [
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:129"
]
},
{
"call": "Interface Monitoring",
"key": "Interface Monitoring",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:189"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:189"
]
},
{
"call": "Interface Monitoring Delay",
"key": "Interface Monitoring Delay",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:221"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:221"
]
},
{
"call": "Interface monitoring for Bad WAN",
"key": "Interface monitoring for Bad WAN",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:190"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:190"
]
},
{
"call": "Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH",
"key": "Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH",
"places": [
- "src/validators/validateDns.ts:23"
+ "src\\validators\\validateDns.ts:23"
]
},
{
"call": "Invalid domain address",
"key": "Invalid domain address",
"places": [
- "src/validators/validateDomain.ts:18",
- "src/validators/validateDomain.ts:27"
+ "src\\validators\\validateDomain.ts:18",
+ "src\\validators\\validateDomain.ts:27"
]
},
{
"call": "Invalid format. Use X.X.X.X or X.X.X.X/Y",
"key": "Invalid format. Use X.X.X.X or X.X.X.X/Y",
"places": [
- "src/validators/validateSubnet.ts:11"
+ "src\\validators\\validateSubnet.ts:11"
]
},
{
"call": "Invalid HY2 URL: insecure must be 0 or 1",
"key": "Invalid HY2 URL: insecure must be 0 or 1",
"places": [
- "src/validators/validateHysteriaUrl.ts:90"
+ "src\\validators\\validateHysteriaUrl.ts:90"
]
},
{
"call": "Invalid HY2 URL: invalid port number",
"key": "Invalid HY2 URL: invalid port number",
"places": [
- "src/validators/validateHysteriaUrl.ts:77"
+ "src\\validators\\validateHysteriaUrl.ts:77"
]
},
{
"call": "Invalid HY2 URL: missing credentials/server",
"key": "Invalid HY2 URL: missing credentials/server",
"places": [
- "src/validators/validateHysteriaUrl.ts:30"
+ "src\\validators\\validateHysteriaUrl.ts:30"
]
},
{
"call": "Invalid HY2 URL: missing host",
"key": "Invalid HY2 URL: missing host",
"places": [
- "src/validators/validateHysteriaUrl.ts:47"
+ "src\\validators\\validateHysteriaUrl.ts:47"
]
},
{
"call": "Invalid HY2 URL: missing host & port",
"key": "Invalid HY2 URL: missing host & port",
"places": [
- "src/validators/validateHysteriaUrl.ts:41"
+ "src\\validators\\validateHysteriaUrl.ts:41"
]
},
{
"call": "Invalid HY2 URL: missing password",
"key": "Invalid HY2 URL: missing password",
"places": [
- "src/validators/validateHysteriaUrl.ts:36"
+ "src\\validators\\validateHysteriaUrl.ts:36"
]
},
{
"call": "Invalid HY2 URL: missing port",
"key": "Invalid HY2 URL: missing port",
"places": [
- "src/validators/validateHysteriaUrl.ts:50"
+ "src\\validators\\validateHysteriaUrl.ts:50"
]
},
{
"call": "Invalid HY2 URL: must not contain spaces",
"key": "Invalid HY2 URL: must not contain spaces",
"places": [
- "src/validators/validateHysteriaUrl.ts:18"
+ "src\\validators\\validateHysteriaUrl.ts:18"
]
},
{
"call": "Invalid HY2 URL: must start with hysteria2:// or hy2://",
"key": "Invalid HY2 URL: must start with hysteria2:// or hy2://",
"places": [
- "src/validators/validateHysteriaUrl.ts:12"
+ "src\\validators\\validateHysteriaUrl.ts:12"
]
},
{
"call": "Invalid HY2 URL: obfs-password required when obfs is set",
"key": "Invalid HY2 URL: obfs-password required when obfs is set",
"places": [
- "src/validators/validateHysteriaUrl.ts:108"
+ "src\\validators\\validateHysteriaUrl.ts:108"
]
},
{
"call": "Invalid HY2 URL: parsing failed",
"key": "Invalid HY2 URL: parsing failed",
"places": [
- "src/validators/validateHysteriaUrl.ts:122"
+ "src\\validators\\validateHysteriaUrl.ts:122"
]
},
{
"call": "Invalid HY2 URL: sni cannot be empty",
"key": "Invalid HY2 URL: sni cannot be empty",
"places": [
- "src/validators/validateHysteriaUrl.ts:116"
+ "src\\validators\\validateHysteriaUrl.ts:116"
]
},
{
"call": "Invalid HY2 URL: unsupported obfs type",
"key": "Invalid HY2 URL: unsupported obfs type",
"places": [
- "src/validators/validateHysteriaUrl.ts:98"
+ "src\\validators\\validateHysteriaUrl.ts:98"
]
},
{
"call": "Invalid IP address",
"key": "Invalid IP address",
"places": [
- "src/validators/validateIp.ts:11"
+ "src\\validators\\validateIp.ts:11"
]
},
{
"call": "Invalid JSON format",
"key": "Invalid JSON format",
"places": [
- "src/validators/validateOutboundJson.ts:9"
+ "src\\validators\\validateOutboundJson.ts:9"
]
},
{
"call": "Invalid path format. Path must start with \"/\" and contain valid characters",
"key": "Invalid path format. Path must start with \"/\" and contain valid characters",
"places": [
- "src/validators/validatePath.ts:22"
+ "src\\validators\\validatePath.ts:22"
]
},
{
"call": "Invalid port number. Must be between 1 and 65535",
"key": "Invalid port number. Must be between 1 and 65535",
"places": [
- "src/validators/validateShadowsocksUrl.ts:85"
+ "src\\validators\\validateShadowsocksUrl.ts:85"
]
},
{
"call": "Invalid Shadowsocks URL: decoded credentials must contain method:password",
"key": "Invalid Shadowsocks URL: decoded credentials must contain method:password",
"places": [
- "src/validators/validateShadowsocksUrl.ts:37"
+ "src\\validators\\validateShadowsocksUrl.ts:37"
]
},
{
"call": "Invalid Shadowsocks URL: missing credentials",
"key": "Invalid Shadowsocks URL: missing credentials",
"places": [
- "src/validators/validateShadowsocksUrl.ts:27"
+ "src\\validators\\validateShadowsocksUrl.ts:27"
]
},
{
"call": "Invalid Shadowsocks URL: missing method and password separator \":\"",
"key": "Invalid Shadowsocks URL: missing method and password separator \":\"",
"places": [
- "src/validators/validateShadowsocksUrl.ts:46"
+ "src\\validators\\validateShadowsocksUrl.ts:46"
]
},
{
"call": "Invalid Shadowsocks URL: missing port",
"key": "Invalid Shadowsocks URL: missing port",
"places": [
- "src/validators/validateShadowsocksUrl.ts:76"
+ "src\\validators\\validateShadowsocksUrl.ts:76"
]
},
{
"call": "Invalid Shadowsocks URL: missing server",
"key": "Invalid Shadowsocks URL: missing server",
"places": [
- "src/validators/validateShadowsocksUrl.ts:67"
+ "src\\validators\\validateShadowsocksUrl.ts:67"
]
},
{
"call": "Invalid Shadowsocks URL: missing server address",
"key": "Invalid Shadowsocks URL: missing server address",
"places": [
- "src/validators/validateShadowsocksUrl.ts:58"
+ "src\\validators\\validateShadowsocksUrl.ts:58"
]
},
{
"call": "Invalid Shadowsocks URL: must not contain spaces",
"key": "Invalid Shadowsocks URL: must not contain spaces",
"places": [
- "src/validators/validateShadowsocksUrl.ts:16"
+ "src\\validators\\validateShadowsocksUrl.ts:16"
]
},
{
"call": "Invalid Shadowsocks URL: must start with ss://",
"key": "Invalid Shadowsocks URL: must start with ss://",
"places": [
- "src/validators/validateShadowsocksUrl.ts:8"
+ "src\\validators\\validateShadowsocksUrl.ts:8"
]
},
{
"call": "Invalid Shadowsocks URL: parsing failed",
"key": "Invalid Shadowsocks URL: parsing failed",
"places": [
- "src/validators/validateShadowsocksUrl.ts:91"
+ "src\\validators\\validateShadowsocksUrl.ts:91"
]
},
{
"call": "Invalid SOCKS URL: invalid host format",
"key": "Invalid SOCKS URL: invalid host format",
"places": [
- "src/validators/validateSocksUrl.ts:73"
+ "src\\validators\\validateSocksUrl.ts:73"
]
},
{
"call": "Invalid SOCKS URL: invalid port number",
"key": "Invalid SOCKS URL: invalid port number",
"places": [
- "src/validators/validateSocksUrl.ts:63"
+ "src\\validators\\validateSocksUrl.ts:63"
]
},
{
"call": "Invalid SOCKS URL: missing host and port",
"key": "Invalid SOCKS URL: missing host and port",
"places": [
- "src/validators/validateSocksUrl.ts:42"
+ "src\\validators\\validateSocksUrl.ts:42"
]
},
{
"call": "Invalid SOCKS URL: missing hostname or IP",
"key": "Invalid SOCKS URL: missing hostname or IP",
"places": [
- "src/validators/validateSocksUrl.ts:51"
+ "src\\validators\\validateSocksUrl.ts:51"
]
},
{
"call": "Invalid SOCKS URL: missing port",
"key": "Invalid SOCKS URL: missing port",
"places": [
- "src/validators/validateSocksUrl.ts:56"
+ "src\\validators\\validateSocksUrl.ts:56"
]
},
{
"call": "Invalid SOCKS URL: missing username",
"key": "Invalid SOCKS URL: missing username",
"places": [
- "src/validators/validateSocksUrl.ts:34"
+ "src\\validators\\validateSocksUrl.ts:34"
]
},
{
"call": "Invalid SOCKS URL: must not contain spaces",
"key": "Invalid SOCKS URL: must not contain spaces",
"places": [
- "src/validators/validateSocksUrl.ts:19"
+ "src\\validators\\validateSocksUrl.ts:19"
]
},
{
"call": "Invalid SOCKS URL: must start with socks4://, socks4a://, or socks5://",
"key": "Invalid SOCKS URL: must start with socks4://, socks4a://, or socks5://",
"places": [
- "src/validators/validateSocksUrl.ts:10"
+ "src\\validators\\validateSocksUrl.ts:10"
]
},
{
"call": "Invalid SOCKS URL: parsing failed",
"key": "Invalid SOCKS URL: parsing failed",
"places": [
- "src/validators/validateSocksUrl.ts:77"
+ "src\\validators\\validateSocksUrl.ts:77"
]
},
{
"call": "Invalid Trojan URL: must not contain spaces",
"key": "Invalid Trojan URL: must not contain spaces",
"places": [
- "src/validators/validateTrojanUrl.ts:15"
+ "src\\validators\\validateTrojanUrl.ts:15"
]
},
{
"call": "Invalid Trojan URL: must start with trojan://",
"key": "Invalid Trojan URL: must start with trojan://",
"places": [
- "src/validators/validateTrojanUrl.ts:8"
+ "src\\validators\\validateTrojanUrl.ts:8"
]
},
{
"call": "Invalid Trojan URL: parsing failed",
"key": "Invalid Trojan URL: parsing failed",
"places": [
- "src/validators/validateTrojanUrl.ts:56"
+ "src\\validators\\validateTrojanUrl.ts:56"
]
},
{
"call": "Invalid URL format",
"key": "Invalid URL format",
"places": [
- "src/validators/validateUrl.ts:8",
- "src/validators/validateUrl.ts:31"
+ "src\\validators\\validateUrl.ts:8",
+ "src\\validators\\validateUrl.ts:31"
]
},
{
"call": "Invalid VLESS URL: parsing failed",
"key": "Invalid VLESS URL: parsing failed",
"places": [
- "src/validators/validateVlessUrl.ts:110"
+ "src\\validators\\validateVlessUrl.ts:110"
]
},
{
"call": "IP address 0.0.0.0 is not allowed",
"key": "IP address 0.0.0.0 is not allowed",
"places": [
- "src/validators/validateSubnet.ts:18"
+ "src\\validators\\validateSubnet.ts:18"
]
},
{
"call": "Issues detected",
"key": "Issues detected",
"places": [
- "src/podkop/tabs/diagnostic/helpers/getMeta.ts:20"
+ "src\\podkop\\tabs\\diagnostic\\helpers\\getMeta.ts:20"
]
},
{
"call": "Latest",
"key": "Latest",
"places": [
- "src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts:48"
+ "src\\podkop\\tabs\\diagnostic\\helpers\\getPodkopVersionRow.ts:48"
]
},
{
"call": "List Update Frequency",
"key": "List Update Frequency",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:276"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:276"
]
},
{
"call": "Local Domain Lists",
"key": "Local Domain Lists",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:546"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:598"
]
},
{
"call": "Local Subnet Lists",
"key": "Local Subnet Lists",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:569"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:621"
]
},
{
"call": "Log Level",
"key": "Log Level",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:384"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:384"
]
},
{
"call": "Main DNS",
"key": "Main DNS",
"places": [
- "src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:72"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:72"
]
},
{
"call": "Memory Usage",
"key": "Memory Usage",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:311"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:311"
]
},
{
"call": "Mixed Proxy Port",
"key": "Mixed Proxy Port",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:678"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:730"
]
},
{
"call": "Monitored Interfaces",
"key": "Monitored Interfaces",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:198"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:198"
]
},
{
"call": "Must be a number in the range of 50 - 1000",
"key": "Must be a number in the range of 50 - 1000",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:164"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:215"
]
},
{
"call": "Network Interface",
"key": "Network Interface",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:208"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:260"
]
},
{
"call": "No other marking rules found",
"key": "No other marking rules found",
"places": [
- "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:105"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:105"
]
},
{
"call": "Not implement yet",
"key": "Not implement yet",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderCheckSection.ts:189"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderCheckSection.ts:189"
]
},
{
"call": "Not responding",
"key": "Not responding",
"places": [
- "src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:75",
- "src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:81",
- "src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:100"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:75",
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:81",
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:100"
]
},
{
"call": "Not running",
"key": "Not running",
"places": [
- "src/podkop/tabs/diagnostic/diagnostic.store.ts:55",
- "src/podkop/tabs/diagnostic/diagnostic.store.ts:63",
- "src/podkop/tabs/diagnostic/diagnostic.store.ts:71",
- "src/podkop/tabs/diagnostic/diagnostic.store.ts:79",
- "src/podkop/tabs/diagnostic/diagnostic.store.ts:87"
+ "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:59",
+ "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:67",
+ "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:75",
+ "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:83",
+ "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:91"
]
},
{
"call": "Operation timed out",
"key": "Operation timed out",
"places": [
- "src/helpers/withTimeout.ts:7"
+ "src\\helpers\\withTimeout.ts:7"
]
},
{
"call": "Outbound Config",
"key": "Outbound Config",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:29"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:30"
]
},
{
"call": "Outbound Configuration",
"key": "Outbound Configuration",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:65"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:66"
]
},
{
"call": "Outdated",
"key": "Outdated",
"places": [
- "src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts:38"
+ "src\\podkop\\tabs\\diagnostic\\helpers\\getPodkopVersionRow.ts:38"
]
},
{
"call": "Output Network Interface",
"key": "Output Network Interface",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:135"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:135"
]
},
{
"call": "Path cannot be empty",
"key": "Path cannot be empty",
"places": [
- "src/validators/validatePath.ts:7"
+ "src\\validators\\validatePath.ts:7"
]
},
{
"call": "Path must be absolute (start with /)",
"key": "Path must be absolute (start with /)",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:366"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:366"
]
},
{
"call": "Path must contain at least one directory (like /tmp/cache.db)",
"key": "Path must contain at least one directory (like /tmp/cache.db)",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:375"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:375"
]
},
{
"call": "Path must end with cache.db",
"key": "Path must end with cache.db",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:370"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:370"
]
},
{
"call": "Pending",
"key": "Pending",
"places": [
- "src/podkop/tabs/diagnostic/diagnostic.store.ts:103",
- "src/podkop/tabs/diagnostic/diagnostic.store.ts:111",
- "src/podkop/tabs/diagnostic/diagnostic.store.ts:119",
- "src/podkop/tabs/diagnostic/diagnostic.store.ts:127",
- "src/podkop/tabs/diagnostic/diagnostic.store.ts:135"
+ "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:107",
+ "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:115",
+ "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:123",
+ "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:131",
+ "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:139"
]
},
{
"call": "Podkop",
"key": "Podkop",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:343"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:343"
]
},
{
"call": "Podkop Settings",
"key": "Podkop Settings",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:26"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:26"
]
},
{
"call": "Podkop will not modify your DHCP configuration",
"key": "Podkop will not modify your DHCP configuration",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:327"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:327"
]
},
{
"call": "Proxy Configuration URL",
"key": "Proxy Configuration URL",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:36"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:37"
]
},
{
"call": "Proxy traffic is not routed via FakeIP",
"key": "Proxy traffic is not routed via FakeIP",
"places": [
- "src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:66"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:66"
]
},
{
"call": "Proxy traffic is routed via FakeIP",
"key": "Proxy traffic is routed via FakeIP",
"places": [
- "src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:65"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:65"
]
},
{
"call": "Regional options cannot be used together",
"key": "Regional options cannot be used together",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:333"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:385"
]
},
{
"call": "Remote Domain Lists",
"key": "Remote Domain Lists",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:592"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:644"
]
},
{
"call": "Remote Subnet Lists",
"key": "Remote Subnet Lists",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:615"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:667"
]
},
{
"call": "Resolve real IP for routing",
"key": "Resolve real IP for routing",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:690"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:745"
]
},
{
"call": "Restart podkop",
"key": "Restart podkop",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:49"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:53"
]
},
{
"call": "Router DNS is not routed through sing-box",
"key": "Router DNS is not routed through sing-box",
"places": [
- "src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:51"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:51"
]
},
{
"call": "Router DNS is routed through sing-box",
"key": "Router DNS is routed through sing-box",
"places": [
- "src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:50"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:50"
]
},
{
"call": "Routing Excluded IPs",
"key": "Routing Excluded IPs",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:413"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:413"
]
},
{
"call": "Rules mangle counters",
"key": "Rules mangle counters",
"places": [
- "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:79"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:79"
]
},
{
"call": "Rules mangle exist",
"key": "Rules mangle exist",
"places": [
- "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:74"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:74"
]
},
{
"call": "Rules mangle output counters",
"key": "Rules mangle output counters",
"places": [
- "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:89"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:89"
]
},
{
"call": "Rules mangle output exist",
"key": "Rules mangle output exist",
"places": [
- "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:84"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:84"
]
},
{
"call": "Rules proxy counters",
"key": "Rules proxy counters",
"places": [
- "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:99"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:99"
]
},
{
"call": "Rules proxy exist",
"key": "Rules proxy exist",
"places": [
- "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:94"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:94"
]
},
{
"call": "Run Diagnostic",
"key": "Run Diagnostic",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderRunAction.ts:15"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderRunAction.ts:15"
]
},
{
"call": "Russia inside restrictions",
"key": "Russia inside restrictions",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:352"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:404"
]
},
{
"call": "Secret key for authenticating remote access to YACD when WAN access is enabled.",
"key": "Secret key for authenticating remote access to YACD when WAN access is enabled.",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:257"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:257"
]
},
{
"call": "Sections",
"key": "Sections",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:36"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:36"
]
},
{
"call": "Select a predefined list for routing",
"key": "Select a predefined list for routing",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:300"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:352"
]
},
{
"call": "Select between VPN and Proxy connection methods for traffic routing",
"key": "Select between VPN and Proxy connection methods for traffic routing",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:13"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:13"
]
},
{
"call": "Select DNS protocol to use",
"key": "Select DNS protocol to use",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:13"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:13"
]
},
{
"call": "Select how often the domain or subnet lists are updated automatically",
"key": "Select how often the domain or subnet lists are updated automatically",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:277"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:277"
]
},
{
"call": "Select how to configure the proxy",
"key": "Select how to configure the proxy",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:24"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:24"
]
},
{
"call": "Select network interface for VPN connection",
"key": "Select network interface for VPN connection",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:209"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:261"
]
},
{
"call": "Select or enter DNS server address",
"key": "Select or enter DNS server address",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:278",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:25"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:330",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:25"
]
},
{
"call": "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing",
"key": "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:349"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:349"
]
},
{
"call": "Select path for sing-box config file. Change this ONLY if you know what you are doing",
"key": "Select path for sing-box config file. Change this ONLY if you know what you are doing",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:336"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:336"
]
},
{
"call": "Select the DNS protocol type for the domain resolver",
"key": "Select the DNS protocol type for the domain resolver",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:265"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:317"
]
},
{
"call": "Select the list type for adding custom domains",
"key": "Select the list type for adding custom domains",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:388"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:440"
]
},
{
"call": "Select the list type for adding custom subnets",
"key": "Select the list type for adding custom subnets",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:468"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:520"
]
},
{
"call": "Select the log level for sing-box",
"key": "Select the log level for sing-box",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:385"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:385"
]
},
{
"call": "Select the network interface from which the traffic will originate",
"key": "Select the network interface from which the traffic will originate",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:90"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:90"
]
},
{
"call": "Select the network interface to which the traffic will originate",
"key": "Select the network interface to which the traffic will originate",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:136"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:136"
]
},
{
"call": "Select the WAN interfaces to be monitored",
"key": "Select the WAN interfaces to be monitored",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:199"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:199"
]
},
{
"call": "Selector",
"key": "Selector",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:27"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:27"
]
},
{
"call": "Selector Proxy Links",
"key": "Selector Proxy Links",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:88"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:137"
]
},
{
"call": "Services info",
"key": "Services info",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:340"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:340"
]
},
{
"call": "Settings",
"key": "Settings",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:49"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:49"
]
},
{
"call": "Show sing-box config",
"key": "Show sing-box config",
"places": [
- "src/podkop/tabs/diagnostic/initController.ts:290",
- "src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:116"
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:292",
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:120"
]
},
{
"call": "Sing-box",
"key": "Sing-box",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:354"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:354"
]
},
{
"call": "Sing-box autostart disabled",
"key": "Sing-box autostart disabled",
"places": [
- "src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:77"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:77"
+ ]
+ },
+ {
+ "call": "Sing-box core changed, version:",
+ "key": "Sing-box core changed, version:",
+ "places": [
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:337"
]
},
{
"call": "Sing-box installed",
"key": "Sing-box installed",
"places": [
- "src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:62"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:62"
]
},
{
"call": "Sing-box listening ports",
"key": "Sing-box listening ports",
"places": [
- "src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:87"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:87"
]
},
{
"call": "Sing-box process running",
"key": "Sing-box process running",
"places": [
- "src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:82"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:82"
]
},
{
"call": "Sing-box service exist",
"key": "Sing-box service exist",
"places": [
- "src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:72"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:72"
]
},
{
"call": "Sing-box version is compatible (newer than 1.12.4)",
"key": "Sing-box version is compatible (newer than 1.12.4)",
"places": [
- "src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:67"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:67"
]
},
{
"call": "Source Network Interface",
"key": "Source Network Interface",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:89"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:89"
]
},
{
"call": "Specify a local IP address to be excluded from routing",
"key": "Specify a local IP address to be excluded from routing",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:414"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:414"
]
},
{
"call": "Specify local IP addresses or subnets whose traffic will always be routed through the configured route",
"key": "Specify local IP addresses or subnets whose traffic will always be routed through the configured route",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:639"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:691"
]
},
{
"call": "Specify remote URLs to download and use domain lists",
"key": "Specify remote URLs to download and use domain lists",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:593"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:645"
]
},
{
"call": "Specify remote URLs to download and use subnet lists",
"key": "Specify remote URLs to download and use subnet lists",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:616"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:668"
]
},
{
"call": "Specify the path to the list file located on the router filesystem",
"key": "Specify the path to the list file located on the router filesystem",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:547",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:570"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:599",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:622"
]
},
{
"call": "Start podkop",
"key": "Start podkop",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:69"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:73"
]
},
{
"call": "Stop podkop",
"key": "Stop podkop",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:59"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:63"
+ ]
+ },
+ {
+ "call": "Subscription",
+ "key": "Subscription",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:29"
+ ]
+ },
+ {
+ "call": "Subscription Update Interval",
+ "key": "Subscription Update Interval",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:112"
+ ]
+ },
+ {
+ "call": "Subscription URL",
+ "key": "Subscription URL",
+ "places": [
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:89"
]
},
{
"call": "Successfully copied!",
"key": "Successfully copied!",
"places": [
- "src/helpers/copyToClipboard.ts:10"
+ "src\\helpers\\copyToClipboard.ts:10"
]
},
{
"call": "System info",
"key": "System info",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:304"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:304"
]
},
{
"call": "System information",
"key": "System information",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderSystemInfo.ts:21"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderSystemInfo.ts:21"
]
},
{
"call": "Table exist",
"key": "Table exist",
"places": [
- "src/podkop/tabs/diagnostic/checks/runNftCheck.ts:69"
+ "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:69"
]
},
{
"call": "Test latency",
"key": "Test latency",
"places": [
- "src/podkop/tabs/dashboard/partials/renderSections.ts:108"
+ "src\\podkop\\tabs\\dashboard\\partials\\renderSections.ts:108"
]
},
{
"call": "Text List",
"key": "Text List",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:392",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:472"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:444",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:524"
]
},
{
"call": "The DNS server used to look up the IP address of an upstream DNS server",
"key": "The DNS server used to look up the IP address of an upstream DNS server",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:46"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:46"
]
},
{
"call": "The interval between connectivity tests",
"key": "The interval between connectivity tests",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:135"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:184"
]
},
{
"call": "The maximum difference in response times (ms) allowed when comparing servers",
"key": "The maximum difference in response times (ms) allowed when comparing servers",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:148"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:198"
]
},
{
"call": "The URL used to test server connectivity",
"key": "The URL used to test server connectivity",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:171"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:222"
]
},
{
"call": "Time in seconds for DNS record caching (default: 60)",
"key": "Time in seconds for DNS record caching (default: 60)",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:69"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:69"
]
},
{
"call": "Traffic",
"key": "Traffic",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:238"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:238"
]
},
{
"call": "Traffic Total",
"key": "Traffic Total",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:268"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:268"
]
},
{
"call": "Troubleshooting",
"key": "Troubleshooting",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts:25"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderWikiDisclaimer.ts:25"
]
},
{
"call": "TTL must be a positive number",
"key": "TTL must be a positive number",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:80"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:80"
]
},
{
"call": "TTL value cannot be empty",
"key": "TTL value cannot be empty",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:75"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:75"
]
},
{
"call": "UDP (Unprotected DNS)",
"key": "UDP (Unprotected DNS)",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:269",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:17"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:321",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:17"
]
},
{
"call": "UDP over TCP",
"key": "UDP over TCP",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:198"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:250"
]
},
{
"call": "unknown",
"key": "unknown",
"places": [
- "src/podkop/tabs/diagnostic/initController.ts:38",
- "src/podkop/tabs/diagnostic/initController.ts:39",
- "src/podkop/tabs/diagnostic/initController.ts:40",
- "src/podkop/tabs/diagnostic/initController.ts:41",
- "src/podkop/tabs/diagnostic/initController.ts:42",
- "src/podkop/tabs/diagnostic/initController.ts:43",
- "src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts:7"
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:39",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:40",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:41",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:42",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:43",
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:44",
+ "src\\podkop\\tabs\\diagnostic\\helpers\\getPodkopVersionRow.ts:7"
]
},
{
"call": "Unknown error",
"key": "Unknown error",
"places": [
- "src/podkop/api.ts:40"
+ "src\\podkop\\api.ts:40"
]
},
{
"call": "Uplink",
"key": "Uplink",
"places": [
- "src/podkop/tabs/dashboard/initController.ts:240",
- "src/podkop/tabs/dashboard/initController.ts:271"
+ "src\\podkop\\tabs\\dashboard\\initController.ts:240",
+ "src\\podkop\\tabs\\dashboard\\initController.ts:271"
]
},
{
"call": "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://",
"key": "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://",
"places": [
- "src/validators/validateProxyUrl.ts:37"
+ "src\\validators\\validateProxyUrl.ts:37"
]
},
{
"call": "URL must use one of the following protocols:",
"key": "URL must use one of the following protocols:",
"places": [
- "src/validators/validateUrl.ts:17"
+ "src\\validators\\validateUrl.ts:17"
]
},
{
"call": "URLTest",
"key": "URLTest",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:28"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:28"
]
},
{
"call": "URLTest Check Interval",
"key": "URLTest Check Interval",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:134"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:183"
]
},
{
"call": "URLTest Proxy Links",
"key": "URLTest Proxy Links",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:111"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:160"
]
},
{
"call": "URLTest Testing URL",
"key": "URLTest Testing URL",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:170"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:221"
]
},
{
"call": "URLTest Tolerance",
"key": "URLTest Tolerance",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:147"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:197"
]
},
{
"call": "User Domain List Type",
"key": "User Domain List Type",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:387"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:439"
]
},
{
"call": "User Domains",
"key": "User Domains",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:399"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:451"
]
},
{
"call": "User Domains List",
"key": "User Domains List",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:425"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:477"
]
},
{
"call": "User Subnet List Type",
"key": "User Subnet List Type",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:467"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:519"
]
},
{
"call": "User Subnets",
"key": "User Subnets",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:479"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:531"
]
},
{
"call": "User Subnets List",
"key": "User Subnets List",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:505"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:557"
]
},
{
"call": "Valid",
"key": "Valid",
"places": [
- "src/validators/validateDns.ts:14",
- "src/validators/validateDns.ts:18",
- "src/validators/validateDomain.ts:13",
- "src/validators/validateDomain.ts:30",
- "src/validators/validateHysteriaUrl.ts:120",
- "src/validators/validateIp.ts:8",
- "src/validators/validateOutboundJson.ts:7",
- "src/validators/validatePath.ts:16",
- "src/validators/validateShadowsocksUrl.ts:95",
- "src/validators/validateSocksUrl.ts:80",
- "src/validators/validateSubnet.ts:38",
- "src/validators/validateTrojanUrl.ts:59",
- "src/validators/validateUrl.ts:28",
- "src/validators/validateVlessUrl.ts:108"
+ "src\\validators\\validateDns.ts:14",
+ "src\\validators\\validateDns.ts:18",
+ "src\\validators\\validateDomain.ts:13",
+ "src\\validators\\validateDomain.ts:30",
+ "src\\validators\\validateHysteriaUrl.ts:120",
+ "src\\validators\\validateIp.ts:8",
+ "src\\validators\\validateOutboundJson.ts:7",
+ "src\\validators\\validatePath.ts:16",
+ "src\\validators\\validateShadowsocksUrl.ts:95",
+ "src\\validators\\validateSocksUrl.ts:80",
+ "src\\validators\\validateSubnet.ts:38",
+ "src\\validators\\validateTrojanUrl.ts:59",
+ "src\\validators\\validateUrl.ts:28",
+ "src\\validators\\validateVlessUrl.ts:108"
]
},
{
"call": "Validation errors:",
"key": "Validation errors:",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:458",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:537"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:510",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:589"
]
},
{
"call": "View logs",
"key": "View logs",
"places": [
- "src/podkop/tabs/diagnostic/initController.ts:256",
- "src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:107"
+ "src\\podkop\\tabs\\diagnostic\\initController.ts:258",
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:111"
]
},
{
"call": "Visit Wiki",
"key": "Visit Wiki",
"places": [
- "src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts:31"
+ "src\\podkop\\tabs\\diagnostic\\partials\\renderWikiDisclaimer.ts:31"
]
},
{
"call": "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links",
"key": "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:37",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:89",
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:112"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:38",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:138",
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:161"
]
},
{
"call": "Warning: %s cannot be used together with %s. Previous selections have been removed.",
"key": "Warning: %s cannot be used together with %s. Previous selections have been removed.",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:335"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:387"
]
},
{
"call": "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection.",
"key": "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection.",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:354"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:406"
]
},
{
"call": "YACD Secret Key",
"key": "YACD Secret Key",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:256"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:256"
]
},
{
"call": "You can select Output Network Interface, by default autodetect",
"key": "You can select Output Network Interface, by default autodetect",
"places": [
- "../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:127"
+ "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:127"
]
}
]
\ No newline at end of file
diff --git a/fe-app-podkop/locales/podkop.pot b/fe-app-podkop/locales/podkop.pot
index d96b948e..a722424c 100644
--- a/fe-app-podkop/locales/podkop.pot
+++ b/fe-app-podkop/locales/podkop.pot
@@ -1,1115 +1,1183 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2026 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PODKOP package.
-# divocatt <210179590+divocatt@users.noreply.github.com>, 2026.
+# yandexru45 , 2026.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PODKOP\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-05-29 13:40+0300\n"
-"PO-Revision-Date: 2026-05-29 13:40+0300\n"
-"Last-Translator: divocatt <210179590+divocatt@users.noreply.github.com>\n"
+"POT-Creation-Date: 2026-06-02 11:25+0300\n"
+"PO-Revision-Date: 2026-06-02 11:25+0300\n"
+"Last-Translator: yandexru45 \n"
"Language-Team: LANGUAGE \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: src/podkop/tabs/dashboard/initController.ts:345
+#: src\podkop\tabs\dashboard\initController.ts:345
msgid "✔ Enabled"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:356
+#: src\podkop\tabs\dashboard\initController.ts:356
msgid "✔ Running"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:346
+#: src\podkop\tabs\dashboard\initController.ts:346
msgid "✘ Disabled"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:357
+#: src\podkop\tabs\dashboard\initController.ts:357
msgid "✘ Stopped"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:307
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:127
+msgid "Группировать по странам"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:128
+msgid "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы"
+msgstr ""
+
+#: src\podkop\tabs\dashboard\initController.ts:307
msgid "Active Connections"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:106
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:106
msgid "Additional marking rules found"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:247
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:247
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:199
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:251
msgid "Applicable for SOCKS and Shadowsocks proxy"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:444
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:496
msgid "At least one valid domain must be specified. Comments-only content is not allowed."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:525
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:577
msgid "At least one valid subnet or IP must be specified. Comments-only content is not allowed."
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:43
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:47
msgid "Available actions"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:65
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:65
msgid "Bootsrap DNS"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:45
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:45
msgid "Bootstrap DNS server"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:58
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:58
msgid "Browser is not using FakeIP"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:57
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:57
msgid "Browser is using FakeIP correctly"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:348
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:348
msgid "Cache File Path"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:362
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:362
msgid "Cache file path cannot be empty"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:27
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:28
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:27
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:25
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:27
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:28
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:27
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:25
msgid "Cannot receive checks result"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:15
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:15
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:13
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:15
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:13
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:15
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:15
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:13
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:15
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:13
msgid "Checking, please wait"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getCheckTitle.ts:2
+#: src\podkop\tabs\diagnostic\helpers\getCheckTitle.ts:2
msgid "checks"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getMeta.ts:26
+#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:26
msgid "Checks failed"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getMeta.ts:13
+#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:13
msgid "Checks passed"
msgstr ""
-#: src/validators/validateSubnet.ts:33
+#: src\validators\validateSubnet.ts:33
msgid "CIDR must be between 0 and 32"
msgstr ""
-#: src/partials/modal/renderModal.ts:26
+#: src\partials\modal\renderModal.ts:26
msgid "Close"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:299
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:351
msgid "Community Lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:335
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:335
msgid "Config File Path"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:27
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:27
msgid "Configuration for Podkop service"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:23
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:23
msgid "Configuration Type"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:12
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:12
msgid "Connection Type"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:26
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:26
msgid "Connection URL"
msgstr ""
-#: src/partials/modal/renderModal.ts:20
+#: src\partials\modal\renderModal.ts:20
msgid "Copy"
msgstr ""
-#: src/podkop/tabs/dashboard/partials/renderWidget.ts:22
+#: src\podkop\tabs\dashboard\partials\renderWidget.ts:22
msgid "Currently unavailable"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:80
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:80
msgid "Dashboard"
msgstr ""
-#: src/podkop/tabs/dashboard/partials/renderSections.ts:19
+#: src\podkop\tabs\dashboard\partials\renderSections.ts:19
msgid "Dashboard currently unavailable"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:222
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:222
msgid "Delay in milliseconds before reloading podkop after interface UP"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:229
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:229
msgid "Delay value cannot be empty"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:82
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:82
msgid "DHCP has DNS server"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:65
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:65
msgid "Diagnostics"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:79
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:83
msgid "Disable autostart"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:265
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:265
msgid "Disable QUIC"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:266
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:266
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:390
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:470
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:442
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:522
msgid "Disabled"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:77
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:77
msgid "DNS on router"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:267
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:15
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:319
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:15
msgid "DNS over HTTPS (DoH)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:268
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:16
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:320
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:16
msgid "DNS over TLS (DoT)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:264
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:12
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:316
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:12
msgid "DNS Protocol Type"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:68
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:68
msgid "DNS Rewrite TTL"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:277
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:24
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:329
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:24
msgid "DNS Server"
msgstr ""
-#: src/validators/validateDns.ts:7
+#: src\validators\validateDns.ts:7
msgid "DNS server address cannot be empty"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts:26
+#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:26
msgid "Do not panic, everything can be fixed, just..."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:254
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:306
msgid "Domain Resolver"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:326
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:326
msgid "Dont Touch My DHCP!"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:241
-#: src/podkop/tabs/dashboard/initController.ts:275
+#: src\podkop\tabs\dashboard\initController.ts:241
+#: src\podkop\tabs\dashboard\initController.ts:275
msgid "Downlink"
msgstr ""
-#: src/partials/modal/renderModal.ts:15
+#: src\partials\modal\renderModal.ts:15
msgid "Download"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:288
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:288
msgid "Download Lists via Proxy/VPN"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:297
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:297
msgid "Download Lists via specific proxy section"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:289
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:298
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:289
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:298
msgid "Downloading all lists via specific Proxy/VPN"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:391
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:471
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:443
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:523
msgid "Dynamic List"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:89
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:93
msgid "Enable autostart"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:255
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:307
msgid "Enable built-in DNS resolver for domains handled by this section"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:691
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:746
msgid "Enable DNS resolve to get real IP when routing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:665
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:717
msgid "Enable Mixed Proxy"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:126
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:126
msgid "Enable Output Network Interface"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:666
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:718
msgid "Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:237
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:237
msgid "Enable YACD"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:246
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:246
msgid "Enable YACD WAN Access"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:66
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:67
msgid "Enter complete outbound configuration in JSON format"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:426
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:478
msgid "Enter domain names separated by commas, spaces, or newlines. You can add comments using //"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:400
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:452
msgid "Enter domain names without protocols, e.g. example.com or sub.example.com"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:480
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:532
msgid "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:138
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:90
+msgid "Enter the subscription URL to fetch proxy configurations from your provider"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:187
msgid "Every 1 minute"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:139
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:119
+msgid "Every 12 hours"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:117
+msgid "Every 3 hours"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:188
msgid "Every 3 minutes"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:137
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:115
+msgid "Every 30 minutes"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:186
msgid "Every 30 seconds"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:140
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:189
msgid "Every 5 minutes"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:402
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:118
+msgid "Every 6 hours"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:120
+msgid "Every day"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:116
+msgid "Every hour"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:402
msgid "Exclude NTP"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:403
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:403
msgid "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN"
msgstr ""
-#: src/helpers/copyToClipboard.ts:12
+#: src\helpers\copyToClipboard.ts:12
msgid "Failed to copy!"
msgstr ""
-#: src/podkop/tabs/diagnostic/initController.ts:227
-#: src/podkop/tabs/diagnostic/initController.ts:231
-#: src/podkop/tabs/diagnostic/initController.ts:261
-#: src/podkop/tabs/diagnostic/initController.ts:265
-#: src/podkop/tabs/diagnostic/initController.ts:302
-#: src/podkop/tabs/diagnostic/initController.ts:306
+#: src\podkop\tabs\diagnostic\initController.ts:229
+#: src\podkop\tabs\diagnostic\initController.ts:233
+#: src\podkop\tabs\diagnostic\initController.ts:263
+#: src\podkop\tabs\diagnostic\initController.ts:267
+#: src\podkop\tabs\diagnostic\initController.ts:304
+#: src\podkop\tabs\diagnostic\initController.ts:308
+#: src\podkop\tabs\diagnostic\initController.ts:342
+#: src\podkop\tabs\diagnostic\initController.ts:346
msgid "Failed to execute!"
msgstr ""
-#: src/podkop/methods/custom/getDashboardSections.ts:148
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:59
+#: src\podkop\methods\custom\getDashboardSections.ts:150
+#: src\podkop\methods\custom\getDashboardSections.ts:181
+#: src\podkop\methods\custom\getDashboardSections.ts:218
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:59
msgid "Fastest"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:638
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:690
msgid "Fully Routed IPs"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:98
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:102
msgid "Get global check"
msgstr ""
-#: src/podkop/tabs/diagnostic/initController.ts:222
+#: src\podkop\tabs\diagnostic\initController.ts:224
msgid "Global check"
msgstr ""
-#: src/podkop/api.ts:27
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:113
+msgid "How often to automatically update the subscription"
+msgstr ""
+
+#: src\podkop\api.ts:27
msgid "HTTP error"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:189
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:129
+msgid "Install extended"
+msgstr ""
+
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:129
+msgid "Install stable"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:189
msgid "Interface Monitoring"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:221
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:221
msgid "Interface Monitoring Delay"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:190
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:190
msgid "Interface monitoring for Bad WAN"
msgstr ""
-#: src/validators/validateDns.ts:23
+#: src\validators\validateDns.ts:23
msgid "Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH"
msgstr ""
-#: src/validators/validateDomain.ts:18
-#: src/validators/validateDomain.ts:27
+#: src\validators\validateDomain.ts:18
+#: src\validators\validateDomain.ts:27
msgid "Invalid domain address"
msgstr ""
-#: src/validators/validateSubnet.ts:11
+#: src\validators\validateSubnet.ts:11
msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:90
+#: src\validators\validateHysteriaUrl.ts:90
msgid "Invalid HY2 URL: insecure must be 0 or 1"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:77
+#: src\validators\validateHysteriaUrl.ts:77
msgid "Invalid HY2 URL: invalid port number"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:30
+#: src\validators\validateHysteriaUrl.ts:30
msgid "Invalid HY2 URL: missing credentials/server"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:47
+#: src\validators\validateHysteriaUrl.ts:47
msgid "Invalid HY2 URL: missing host"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:41
+#: src\validators\validateHysteriaUrl.ts:41
msgid "Invalid HY2 URL: missing host & port"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:36
+#: src\validators\validateHysteriaUrl.ts:36
msgid "Invalid HY2 URL: missing password"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:50
+#: src\validators\validateHysteriaUrl.ts:50
msgid "Invalid HY2 URL: missing port"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:18
+#: src\validators\validateHysteriaUrl.ts:18
msgid "Invalid HY2 URL: must not contain spaces"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:12
+#: src\validators\validateHysteriaUrl.ts:12
msgid "Invalid HY2 URL: must start with hysteria2:// or hy2://"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:108
+#: src\validators\validateHysteriaUrl.ts:108
msgid "Invalid HY2 URL: obfs-password required when obfs is set"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:122
+#: src\validators\validateHysteriaUrl.ts:122
msgid "Invalid HY2 URL: parsing failed"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:116
+#: src\validators\validateHysteriaUrl.ts:116
msgid "Invalid HY2 URL: sni cannot be empty"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:98
+#: src\validators\validateHysteriaUrl.ts:98
msgid "Invalid HY2 URL: unsupported obfs type"
msgstr ""
-#: src/validators/validateIp.ts:11
+#: src\validators\validateIp.ts:11
msgid "Invalid IP address"
msgstr ""
-#: src/validators/validateOutboundJson.ts:9
+#: src\validators\validateOutboundJson.ts:9
msgid "Invalid JSON format"
msgstr ""
-#: src/validators/validatePath.ts:22
+#: src\validators\validatePath.ts:22
msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:85
+#: src\validators\validateShadowsocksUrl.ts:85
msgid "Invalid port number. Must be between 1 and 65535"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:37
+#: src\validators\validateShadowsocksUrl.ts:37
msgid "Invalid Shadowsocks URL: decoded credentials must contain method:password"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:27
+#: src\validators\validateShadowsocksUrl.ts:27
msgid "Invalid Shadowsocks URL: missing credentials"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:46
+#: src\validators\validateShadowsocksUrl.ts:46
msgid "Invalid Shadowsocks URL: missing method and password separator \":\""
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:76
+#: src\validators\validateShadowsocksUrl.ts:76
msgid "Invalid Shadowsocks URL: missing port"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:67
+#: src\validators\validateShadowsocksUrl.ts:67
msgid "Invalid Shadowsocks URL: missing server"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:58
+#: src\validators\validateShadowsocksUrl.ts:58
msgid "Invalid Shadowsocks URL: missing server address"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:16
+#: src\validators\validateShadowsocksUrl.ts:16
msgid "Invalid Shadowsocks URL: must not contain spaces"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:8
+#: src\validators\validateShadowsocksUrl.ts:8
msgid "Invalid Shadowsocks URL: must start with ss://"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:91
+#: src\validators\validateShadowsocksUrl.ts:91
msgid "Invalid Shadowsocks URL: parsing failed"
msgstr ""
-#: src/validators/validateSocksUrl.ts:73
+#: src\validators\validateSocksUrl.ts:73
msgid "Invalid SOCKS URL: invalid host format"
msgstr ""
-#: src/validators/validateSocksUrl.ts:63
+#: src\validators\validateSocksUrl.ts:63
msgid "Invalid SOCKS URL: invalid port number"
msgstr ""
-#: src/validators/validateSocksUrl.ts:42
+#: src\validators\validateSocksUrl.ts:42
msgid "Invalid SOCKS URL: missing host and port"
msgstr ""
-#: src/validators/validateSocksUrl.ts:51
+#: src\validators\validateSocksUrl.ts:51
msgid "Invalid SOCKS URL: missing hostname or IP"
msgstr ""
-#: src/validators/validateSocksUrl.ts:56
+#: src\validators\validateSocksUrl.ts:56
msgid "Invalid SOCKS URL: missing port"
msgstr ""
-#: src/validators/validateSocksUrl.ts:34
+#: src\validators\validateSocksUrl.ts:34
msgid "Invalid SOCKS URL: missing username"
msgstr ""
-#: src/validators/validateSocksUrl.ts:19
+#: src\validators\validateSocksUrl.ts:19
msgid "Invalid SOCKS URL: must not contain spaces"
msgstr ""
-#: src/validators/validateSocksUrl.ts:10
+#: src\validators\validateSocksUrl.ts:10
msgid "Invalid SOCKS URL: must start with socks4://, socks4a://, or socks5://"
msgstr ""
-#: src/validators/validateSocksUrl.ts:77
+#: src\validators\validateSocksUrl.ts:77
msgid "Invalid SOCKS URL: parsing failed"
msgstr ""
-#: src/validators/validateTrojanUrl.ts:15
+#: src\validators\validateTrojanUrl.ts:15
msgid "Invalid Trojan URL: must not contain spaces"
msgstr ""
-#: src/validators/validateTrojanUrl.ts:8
+#: src\validators\validateTrojanUrl.ts:8
msgid "Invalid Trojan URL: must start with trojan://"
msgstr ""
-#: src/validators/validateTrojanUrl.ts:56
+#: src\validators\validateTrojanUrl.ts:56
msgid "Invalid Trojan URL: parsing failed"
msgstr ""
-#: src/validators/validateUrl.ts:8
-#: src/validators/validateUrl.ts:31
+#: src\validators\validateUrl.ts:8
+#: src\validators\validateUrl.ts:31
msgid "Invalid URL format"
msgstr ""
-#: src/validators/validateVlessUrl.ts:110
+#: src\validators\validateVlessUrl.ts:110
msgid "Invalid VLESS URL: parsing failed"
msgstr ""
-#: src/validators/validateSubnet.ts:18
+#: src\validators\validateSubnet.ts:18
msgid "IP address 0.0.0.0 is not allowed"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getMeta.ts:20
+#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:20
msgid "Issues detected"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts:48
+#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:48
msgid "Latest"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:276
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:276
msgid "List Update Frequency"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:546
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:598
msgid "Local Domain Lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:569
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:621
msgid "Local Subnet Lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:384
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:384
msgid "Log Level"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:72
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:72
msgid "Main DNS"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:311
+#: src\podkop\tabs\dashboard\initController.ts:311
msgid "Memory Usage"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:678
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:730
msgid "Mixed Proxy Port"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:198
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:198
msgid "Monitored Interfaces"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:164
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:215
msgid "Must be a number in the range of 50 - 1000"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:208
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:260
msgid "Network Interface"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:105
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:105
msgid "No other marking rules found"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderCheckSection.ts:189
+#: src\podkop\tabs\diagnostic\partials\renderCheckSection.ts:189
msgid "Not implement yet"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:75
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:81
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:100
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:75
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:81
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:100
msgid "Not responding"
msgstr ""
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:55
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:63
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:71
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:79
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:87
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:59
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:67
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:75
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:83
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:91
msgid "Not running"
msgstr ""
-#: src/helpers/withTimeout.ts:7
+#: src\helpers\withTimeout.ts:7
msgid "Operation timed out"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:29
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:30
msgid "Outbound Config"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:65
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:66
msgid "Outbound Configuration"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts:38
+#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:38
msgid "Outdated"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:135
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:135
msgid "Output Network Interface"
msgstr ""
-#: src/validators/validatePath.ts:7
+#: src\validators\validatePath.ts:7
msgid "Path cannot be empty"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:366
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:366
msgid "Path must be absolute (start with /)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:375
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:375
msgid "Path must contain at least one directory (like /tmp/cache.db)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:370
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:370
msgid "Path must end with cache.db"
msgstr ""
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:103
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:111
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:119
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:127
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:135
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:107
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:115
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:123
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:131
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:139
msgid "Pending"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:343
+#: src\podkop\tabs\dashboard\initController.ts:343
msgid "Podkop"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:26
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:26
msgid "Podkop Settings"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:327
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:327
msgid "Podkop will not modify your DHCP configuration"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:36
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:37
msgid "Proxy Configuration URL"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:66
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:66
msgid "Proxy traffic is not routed via FakeIP"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:65
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:65
msgid "Proxy traffic is routed via FakeIP"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:333
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:385
msgid "Regional options cannot be used together"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:592
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:644
msgid "Remote Domain Lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:615
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:667
msgid "Remote Subnet Lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:690
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:745
msgid "Resolve real IP for routing"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:49
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:53
msgid "Restart podkop"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:51
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:51
msgid "Router DNS is not routed through sing-box"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:50
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:50
msgid "Router DNS is routed through sing-box"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:413
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:413
msgid "Routing Excluded IPs"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:79
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:79
msgid "Rules mangle counters"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:74
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:74
msgid "Rules mangle exist"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:89
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:89
msgid "Rules mangle output counters"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:84
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:84
msgid "Rules mangle output exist"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:99
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:99
msgid "Rules proxy counters"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:94
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:94
msgid "Rules proxy exist"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderRunAction.ts:15
+#: src\podkop\tabs\diagnostic\partials\renderRunAction.ts:15
msgid "Run Diagnostic"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:352
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:404
msgid "Russia inside restrictions"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:257
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:257
msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:36
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:36
msgid "Sections"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:300
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:352
msgid "Select a predefined list for routing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:13
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:13
msgid "Select between VPN and Proxy connection methods for traffic routing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:13
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:13
msgid "Select DNS protocol to use"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:277
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:277
msgid "Select how often the domain or subnet lists are updated automatically"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:24
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:24
msgid "Select how to configure the proxy"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:209
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:261
msgid "Select network interface for VPN connection"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:278
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:25
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:330
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:25
msgid "Select or enter DNS server address"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:349
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:349
msgid "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:336
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:336
msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:265
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:317
msgid "Select the DNS protocol type for the domain resolver"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:388
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:440
msgid "Select the list type for adding custom domains"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:468
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:520
msgid "Select the list type for adding custom subnets"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:385
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:385
msgid "Select the log level for sing-box"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:90
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:90
msgid "Select the network interface from which the traffic will originate"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:136
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:136
msgid "Select the network interface to which the traffic will originate"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:199
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:199
msgid "Select the WAN interfaces to be monitored"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:27
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:27
msgid "Selector"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:88
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:137
msgid "Selector Proxy Links"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:340
+#: src\podkop\tabs\dashboard\initController.ts:340
msgid "Services info"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:49
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:49
msgid "Settings"
msgstr ""
-#: src/podkop/tabs/diagnostic/initController.ts:290
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:116
+#: src\podkop\tabs\diagnostic\initController.ts:292
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:120
msgid "Show sing-box config"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:354
+#: src\podkop\tabs\dashboard\initController.ts:354
msgid "Sing-box"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:77
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:77
msgid "Sing-box autostart disabled"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:62
+#: src\podkop\tabs\diagnostic\initController.ts:337
+msgid "Sing-box core changed, version:"
+msgstr ""
+
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:62
msgid "Sing-box installed"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:87
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:87
msgid "Sing-box listening ports"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:82
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:82
msgid "Sing-box process running"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:72
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:72
msgid "Sing-box service exist"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:67
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:67
msgid "Sing-box version is compatible (newer than 1.12.4)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:89
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:89
msgid "Source Network Interface"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:414
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:414
msgid "Specify a local IP address to be excluded from routing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:639
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:691
msgid "Specify local IP addresses or subnets whose traffic will always be routed through the configured route"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:593
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:645
msgid "Specify remote URLs to download and use domain lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:616
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:668
msgid "Specify remote URLs to download and use subnet lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:547
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:570
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:599
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:622
msgid "Specify the path to the list file located on the router filesystem"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:69
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:73
msgid "Start podkop"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:59
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:63
msgid "Stop podkop"
msgstr ""
-#: src/helpers/copyToClipboard.ts:10
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:29
+msgid "Subscription"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:112
+msgid "Subscription Update Interval"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:89
+msgid "Subscription URL"
+msgstr ""
+
+#: src\helpers\copyToClipboard.ts:10
msgid "Successfully copied!"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:304
+#: src\podkop\tabs\dashboard\initController.ts:304
msgid "System info"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderSystemInfo.ts:21
+#: src\podkop\tabs\diagnostic\partials\renderSystemInfo.ts:21
msgid "System information"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:69
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:69
msgid "Table exist"
msgstr ""
-#: src/podkop/tabs/dashboard/partials/renderSections.ts:108
+#: src\podkop\tabs\dashboard\partials\renderSections.ts:108
msgid "Test latency"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:392
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:472
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:444
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:524
msgid "Text List"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:46
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:46
msgid "The DNS server used to look up the IP address of an upstream DNS server"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:135
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:184
msgid "The interval between connectivity tests"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:148
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:198
msgid "The maximum difference in response times (ms) allowed when comparing servers"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:171
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:222
msgid "The URL used to test server connectivity"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:69
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:69
msgid "Time in seconds for DNS record caching (default: 60)"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:238
+#: src\podkop\tabs\dashboard\initController.ts:238
msgid "Traffic"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:268
+#: src\podkop\tabs\dashboard\initController.ts:268
msgid "Traffic Total"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts:25
+#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:25
msgid "Troubleshooting"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:80
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:80
msgid "TTL must be a positive number"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:75
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:75
msgid "TTL value cannot be empty"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:269
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:17
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:321
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:17
msgid "UDP (Unprotected DNS)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:198
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:250
msgid "UDP over TCP"
msgstr ""
-#: src/podkop/tabs/diagnostic/initController.ts:38
-#: src/podkop/tabs/diagnostic/initController.ts:39
-#: src/podkop/tabs/diagnostic/initController.ts:40
-#: src/podkop/tabs/diagnostic/initController.ts:41
-#: src/podkop/tabs/diagnostic/initController.ts:42
-#: src/podkop/tabs/diagnostic/initController.ts:43
-#: src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts:7
+#: src\podkop\tabs\diagnostic\initController.ts:39
+#: src\podkop\tabs\diagnostic\initController.ts:40
+#: src\podkop\tabs\diagnostic\initController.ts:41
+#: src\podkop\tabs\diagnostic\initController.ts:42
+#: src\podkop\tabs\diagnostic\initController.ts:43
+#: src\podkop\tabs\diagnostic\initController.ts:44
+#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:7
msgid "unknown"
msgstr ""
-#: src/podkop/api.ts:40
+#: src\podkop\api.ts:40
msgid "Unknown error"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:240
-#: src/podkop/tabs/dashboard/initController.ts:271
+#: src\podkop\tabs\dashboard\initController.ts:240
+#: src\podkop\tabs\dashboard\initController.ts:271
msgid "Uplink"
msgstr ""
-#: src/validators/validateProxyUrl.ts:37
+#: src\validators\validateProxyUrl.ts:37
msgid "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://"
msgstr ""
-#: src/validators/validateUrl.ts:17
+#: src\validators\validateUrl.ts:17
msgid "URL must use one of the following protocols:"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:28
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:28
msgid "URLTest"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:134
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:183
msgid "URLTest Check Interval"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:111
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:160
msgid "URLTest Proxy Links"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:170
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:221
msgid "URLTest Testing URL"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:147
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:197
msgid "URLTest Tolerance"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:387
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:439
msgid "User Domain List Type"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:399
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:451
msgid "User Domains"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:425
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:477
msgid "User Domains List"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:467
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:519
msgid "User Subnet List Type"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:479
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:531
msgid "User Subnets"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:505
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:557
msgid "User Subnets List"
msgstr ""
-#: src/validators/validateDns.ts:14
-#: src/validators/validateDns.ts:18
-#: src/validators/validateDomain.ts:13
-#: src/validators/validateDomain.ts:30
-#: src/validators/validateHysteriaUrl.ts:120
-#: src/validators/validateIp.ts:8
-#: src/validators/validateOutboundJson.ts:7
-#: src/validators/validatePath.ts:16
-#: src/validators/validateShadowsocksUrl.ts:95
-#: src/validators/validateSocksUrl.ts:80
-#: src/validators/validateSubnet.ts:38
-#: src/validators/validateTrojanUrl.ts:59
-#: src/validators/validateUrl.ts:28
-#: src/validators/validateVlessUrl.ts:108
+#: src\validators\validateDns.ts:14
+#: src\validators\validateDns.ts:18
+#: src\validators\validateDomain.ts:13
+#: src\validators\validateDomain.ts:30
+#: src\validators\validateHysteriaUrl.ts:120
+#: src\validators\validateIp.ts:8
+#: src\validators\validateOutboundJson.ts:7
+#: src\validators\validatePath.ts:16
+#: src\validators\validateShadowsocksUrl.ts:95
+#: src\validators\validateSocksUrl.ts:80
+#: src\validators\validateSubnet.ts:38
+#: src\validators\validateTrojanUrl.ts:59
+#: src\validators\validateUrl.ts:28
+#: src\validators\validateVlessUrl.ts:108
msgid "Valid"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:458
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:537
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:510
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:589
msgid "Validation errors:"
msgstr ""
-#: src/podkop/tabs/diagnostic/initController.ts:256
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:107
+#: src\podkop\tabs\diagnostic\initController.ts:258
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:111
msgid "View logs"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts:31
+#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:31
msgid "Visit Wiki"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:37
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:89
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:112
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:38
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:138
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:161
msgid "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:335
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:387
msgid "Warning: %s cannot be used together with %s. Previous selections have been removed."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:354
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:406
msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:256
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:256
msgid "YACD Secret Key"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:127
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:127
msgid "You can select Output Network Interface, by default autodetect"
msgstr ""
diff --git a/fe-app-podkop/locales/podkop.ru.po b/fe-app-podkop/locales/podkop.ru.po
index 77a33a4f..8bd2c236 100644
--- a/fe-app-podkop/locales/podkop.ru.po
+++ b/fe-app-podkop/locales/podkop.ru.po
@@ -1,15 +1,15 @@
# RU translations for PODKOP package.
# Copyright (C) 2026 THE PODKOP'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PODKOP package.
-# divocatt, 2026.
+# yandexru45, 2026.
#
msgid ""
msgstr ""
"Project-Id-Version: PODKOP\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-05-29 16:40+0300\n"
-"PO-Revision-Date: 2026-05-29 16:40+0300\n"
-"Last-Translator: divocatt\n"
+"POT-Creation-Date: 2026-06-02 14:25+0300\n"
+"PO-Revision-Date: 2026-06-02 14:25+0300\n"
+"Last-Translator: yandexru45\n"
"Language-Team: none\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
@@ -29,6 +29,12 @@ msgstr "✘ Отключено"
msgid "✘ Stopped"
msgstr "✘ Остановлен"
+msgid "Группировать по странам"
+msgstr ""
+
+msgid "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы"
+msgstr ""
+
msgid "Active Connections"
msgstr "Активные соединения"
@@ -227,18 +233,39 @@ msgstr "Введите доменные имена без протоколов,
msgid "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses"
msgstr "Введите подсети в нотации CIDR (например, 103.21.244.0/22) или отдельные IP-адреса"
+msgid "Enter the subscription URL to fetch proxy configurations from your provider"
+msgstr ""
+
msgid "Every 1 minute"
msgstr "Каждую минуту"
+msgid "Every 12 hours"
+msgstr ""
+
+msgid "Every 3 hours"
+msgstr ""
+
msgid "Every 3 minutes"
msgstr "Каждые 3 минуты"
+msgid "Every 30 minutes"
+msgstr ""
+
msgid "Every 30 seconds"
msgstr "Каждые 30 секунд"
msgid "Every 5 minutes"
msgstr "Каждые 5 минут"
+msgid "Every 6 hours"
+msgstr ""
+
+msgid "Every day"
+msgstr ""
+
+msgid "Every hour"
+msgstr ""
+
msgid "Exclude NTP"
msgstr "Исключить NTP"
@@ -263,9 +290,18 @@ msgstr "Получить глобальную проверку"
msgid "Global check"
msgstr "Глобальная проверка"
+msgid "How often to automatically update the subscription"
+msgstr ""
+
msgid "HTTP error"
msgstr "Ошибка HTTP"
+msgid "Install extended"
+msgstr "Установить extended"
+
+msgid "Install stable"
+msgstr "Установить stable"
+
msgid "Interface Monitoring"
msgstr "Мониторинг интерфейса"
@@ -626,6 +662,9 @@ msgstr "Sing-box"
msgid "Sing-box autostart disabled"
msgstr "Автостарт sing-box отключен"
+msgid "Sing-box core changed, version:"
+msgstr "Ядро sing-box изменено, версия:"
+
msgid "Sing-box installed"
msgstr "Sing-box установлен"
@@ -665,6 +704,15 @@ msgstr "Запустить podkop"
msgid "Stop podkop"
msgstr "Остановить podkop"
+msgid "Subscription"
+msgstr ""
+
+msgid "Subscription Update Interval"
+msgstr ""
+
+msgid "Subscription URL"
+msgstr ""
+
msgid "Successfully copied!"
msgstr "Успешно скопировано!"
diff --git a/fe-app-podkop/src/podkop/methods/shell/index.ts b/fe-app-podkop/src/podkop/methods/shell/index.ts
index 6b0337a4..24038743 100644
--- a/fe-app-podkop/src/podkop/methods/shell/index.ts
+++ b/fe-app-podkop/src/podkop/methods/shell/index.ts
@@ -1,5 +1,12 @@
import { callBaseMethod } from './callBaseMethod';
import { ClashAPI, Podkop } from '../../types';
+import { executeShellCommand } from '../../../helpers';
+
+interface SingBoxComponentActionResult {
+ success: boolean;
+ version?: string;
+ message?: string;
+}
export const PodkopShellMethods = {
checkDNSAvailable: async () =>
@@ -86,4 +93,37 @@ export const PodkopShellMethods = {
),
subscriptionUpdate: async () =>
callBaseMethod(Podkop.AvailableMethods.SUBSCRIPTION_UPDATE),
+ singBoxComponentAction: async (
+ action: 'install_extended' | 'install_stable' | 'check_update',
+ ): Promise => {
+ const response = await executeShellCommand({
+ command: '/usr/bin/podkop',
+ args: ['component_action', 'sing_box', action],
+ timeout: 600000,
+ });
+
+ if (response.stdout) {
+ try {
+ const parsed = JSON.parse(
+ response.stdout,
+ ) as SingBoxComponentActionResult;
+
+ return {
+ success: Boolean(parsed.success),
+ version: parsed.version,
+ message: parsed.message,
+ };
+ } catch (_e) {
+ return {
+ success: false,
+ message: response.stdout,
+ };
+ }
+ }
+
+ return {
+ success: false,
+ message: response.stderr || '',
+ };
+ },
};
diff --git a/fe-app-podkop/src/podkop/services/store.service.ts b/fe-app-podkop/src/podkop/services/store.service.ts
index 4069240c..ca9db829 100644
--- a/fe-app-podkop/src/podkop/services/store.service.ts
+++ b/fe-app-podkop/src/podkop/services/store.service.ts
@@ -180,6 +180,7 @@ export interface StoreType {
globalCheck: { loading: boolean };
viewLogs: { loading: boolean };
showSingBoxConfig: { loading: boolean };
+ singBoxInstall: { loading: boolean };
};
diagnosticsSystemInfo: {
loading: boolean;
@@ -189,6 +190,7 @@ export interface StoreType {
sing_box_version: string;
openwrt_version: string;
device_model: string;
+ sing_box_extended: 0 | 1;
};
}
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/diagnostic.store.ts b/fe-app-podkop/src/podkop/tabs/diagnostic/diagnostic.store.ts
index c6ea5626..261f1cdf 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/diagnostic.store.ts
+++ b/fe-app-podkop/src/podkop/tabs/diagnostic/diagnostic.store.ts
@@ -19,6 +19,7 @@ export const initialDiagnosticStore: Pick<
sing_box_version: 'loading',
openwrt_version: 'loading',
device_model: 'loading',
+ sing_box_extended: 0,
},
diagnosticsActions: {
restart: {
@@ -45,6 +46,9 @@ export const initialDiagnosticStore: Pick<
showSingBoxConfig: {
loading: false,
},
+ singBoxInstall: {
+ loading: false,
+ },
},
diagnosticsRunAction: { loading: false },
diagnosticsChecks: [
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts b/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts
index 7ef08cdd..3e90e9f9 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts
+++ b/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts
@@ -29,6 +29,7 @@ async function fetchSystemInfo() {
diagnosticsSystemInfo: {
loading: false,
...systemInfo.data,
+ sing_box_extended: systemInfo.data.sing_box_extended === 1 ? 1 : 0,
},
});
} else {
@@ -41,6 +42,7 @@ async function fetchSystemInfo() {
sing_box_version: _('unknown'),
openwrt_version: _('unknown'),
device_model: _('unknown'),
+ sing_box_extended: 0,
},
});
}
@@ -314,6 +316,45 @@ async function handleShowSingBoxConfig() {
}
}
+async function handleInstallSingBox() {
+ const diagnosticsActions = store.get().diagnosticsActions;
+ store.set({
+ diagnosticsActions: {
+ ...diagnosticsActions,
+ singBoxInstall: { loading: true },
+ },
+ });
+
+ const isExtended = store.get().diagnosticsSystemInfo.sing_box_extended === 1;
+
+ try {
+ const result = await PodkopShellMethods.singBoxComponentAction(
+ isExtended ? 'install_stable' : 'install_extended',
+ );
+
+ if (result.success) {
+ showToast(
+ _('Sing-box core changed, version: ') + (result.version || ''),
+ 'success',
+ );
+ } else {
+ logger.error('[DIAGNOSTIC]', 'handleInstallSingBox - e', result);
+ showToast(result.message || _('Failed to execute!'), 'error');
+ }
+ } catch (e) {
+ logger.error('[DIAGNOSTIC]', 'handleInstallSingBox - e', e);
+ showToast(_('Failed to execute!'), 'error');
+ } finally {
+ store.set({
+ diagnosticsActions: {
+ ...diagnosticsActions,
+ singBoxInstall: { loading: false },
+ },
+ });
+ await fetchSystemInfo();
+ }
+}
+
function renderWikiDisclaimerWidget() {
const diagnosticsChecks = store.get().diagnosticsChecks;
@@ -402,6 +443,15 @@ function renderDiagnosticAvailableActionsWidget() {
onClick: handleShowSingBoxConfig,
disabled: atLeastOneServiceCommandLoading,
},
+ singBoxInstall: {
+ loading: diagnosticsActions.singBoxInstall.loading,
+ visible: true,
+ onClick: handleInstallSingBox,
+ disabled:
+ atLeastOneServiceCommandLoading ||
+ diagnosticsActions.singBoxInstall.loading,
+ },
+ singBoxExtended: store.get().diagnosticsSystemInfo.sing_box_extended,
});
return preserveScrollForPage(() => {
@@ -456,7 +506,11 @@ async function onStoreUpdate(
renderDiagnosticRunActionWidget();
}
- if (diff.diagnosticsActions || diff.servicesInfoWidget) {
+ if (
+ diff.diagnosticsActions ||
+ diff.servicesInfoWidget ||
+ diff.diagnosticsSystemInfo
+ ) {
renderDiagnosticAvailableActionsWidget();
}
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts b/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts
index f54fd7f3..65ac61c8 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts
+++ b/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts
@@ -27,6 +27,8 @@ interface IRenderAvailableActionsProps {
globalCheck: ActionProps;
viewLogs: ActionProps;
showSingBoxConfig: ActionProps;
+ singBoxInstall: ActionProps;
+ singBoxExtended: 0 | 1;
}
export function renderAvailableActions({
@@ -38,6 +40,8 @@ export function renderAvailableActions({
globalCheck,
viewLogs,
showSingBoxConfig,
+ singBoxInstall,
+ singBoxExtended,
}: IRenderAvailableActionsProps) {
return E('div', { class: 'pdk_diagnostic-page__right-bar__actions' }, [
E('b', {}, _('Available actions')),
@@ -118,5 +122,14 @@ export function renderAvailableActions({
disabled: showSingBoxConfig.disabled,
}),
]),
+ ...insertIf(singBoxInstall.visible, [
+ renderButton({
+ onClick: singBoxInstall.onClick,
+ icon: renderRotateCcwIcon24,
+ text: singBoxExtended ? _('Install stable') : _('Install extended'),
+ loading: singBoxInstall.loading,
+ disabled: singBoxInstall.disabled,
+ }),
+ ]),
]);
}
diff --git a/fe-app-podkop/src/podkop/types.ts b/fe-app-podkop/src/podkop/types.ts
index c3f8afc7..f336ab6f 100644
--- a/fe-app-podkop/src/podkop/types.ts
+++ b/fe-app-podkop/src/podkop/types.ts
@@ -218,6 +218,7 @@ export namespace Podkop {
sing_box_version: string;
openwrt_version: string;
device_model: string;
+ sing_box_extended: 0 | 1;
}
export interface GetClashApiProxyLatency {
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
index 83fdcb65..11453b9d 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
+++ b/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
@@ -698,7 +698,35 @@ var PodkopShellMethods = {
getSystemInfo: async () => callBaseMethod(
Podkop.AvailableMethods.GET_SYSTEM_INFO
),
- subscriptionUpdate: async () => callBaseMethod(Podkop.AvailableMethods.SUBSCRIPTION_UPDATE)
+ subscriptionUpdate: async () => callBaseMethod(Podkop.AvailableMethods.SUBSCRIPTION_UPDATE),
+ singBoxComponentAction: async (action) => {
+ const response = await executeShellCommand({
+ command: "/usr/bin/podkop",
+ args: ["component_action", "sing_box", action],
+ timeout: 6e5
+ });
+ if (response.stdout) {
+ try {
+ const parsed = JSON.parse(
+ response.stdout
+ );
+ return {
+ success: Boolean(parsed.success),
+ version: parsed.version,
+ message: parsed.message
+ };
+ } catch (_e) {
+ return {
+ success: false,
+ message: response.stdout
+ };
+ }
+ }
+ return {
+ success: false,
+ message: response.stderr || ""
+ };
+ }
};
// src/podkop/methods/custom/getDashboardSections.ts
@@ -1223,7 +1251,8 @@ var initialDiagnosticStore = {
luci_app_version: "loading",
sing_box_version: "loading",
openwrt_version: "loading",
- device_model: "loading"
+ device_model: "loading",
+ sing_box_extended: 0
},
diagnosticsActions: {
restart: {
@@ -1249,6 +1278,9 @@ var initialDiagnosticStore = {
},
showSingBoxConfig: {
loading: false
+ },
+ singBoxInstall: {
+ loading: false
}
},
diagnosticsRunAction: { loading: false },
@@ -3574,7 +3606,9 @@ function renderAvailableActions({
disable,
globalCheck,
viewLogs,
- showSingBoxConfig
+ showSingBoxConfig,
+ singBoxInstall,
+ singBoxExtended
}) {
return E("div", { class: "pdk_diagnostic-page__right-bar__actions" }, [
E("b", {}, _("Available actions")),
@@ -3654,6 +3688,15 @@ function renderAvailableActions({
loading: showSingBoxConfig.loading,
disabled: showSingBoxConfig.disabled
})
+ ]),
+ ...insertIf(singBoxInstall.visible, [
+ renderButton({
+ onClick: singBoxInstall.onClick,
+ icon: renderRotateCcwIcon24,
+ text: singBoxExtended ? _("Install stable") : _("Install extended"),
+ loading: singBoxInstall.loading,
+ disabled: singBoxInstall.disabled
+ })
])
]);
}
@@ -4054,7 +4097,8 @@ async function fetchSystemInfo() {
store.set({
diagnosticsSystemInfo: {
loading: false,
- ...systemInfo.data
+ ...systemInfo.data,
+ sing_box_extended: systemInfo.data.sing_box_extended === 1 ? 1 : 0
}
});
} else {
@@ -4066,7 +4110,8 @@ async function fetchSystemInfo() {
luci_app_version: _("unknown"),
sing_box_version: _("unknown"),
openwrt_version: _("unknown"),
- device_model: _("unknown")
+ device_model: _("unknown"),
+ sing_box_extended: 0
}
});
}
@@ -4311,6 +4356,41 @@ async function handleShowSingBoxConfig() {
});
}
}
+async function handleInstallSingBox() {
+ const diagnosticsActions = store.get().diagnosticsActions;
+ store.set({
+ diagnosticsActions: {
+ ...diagnosticsActions,
+ singBoxInstall: { loading: true }
+ }
+ });
+ const isExtended = store.get().diagnosticsSystemInfo.sing_box_extended === 1;
+ try {
+ const result = await PodkopShellMethods.singBoxComponentAction(
+ isExtended ? "install_stable" : "install_extended"
+ );
+ if (result.success) {
+ showToast(
+ _("Sing-box core changed, version: ") + (result.version || ""),
+ "success"
+ );
+ } else {
+ logger.error("[DIAGNOSTIC]", "handleInstallSingBox - e", result);
+ showToast(result.message || _("Failed to execute!"), "error");
+ }
+ } catch (e) {
+ logger.error("[DIAGNOSTIC]", "handleInstallSingBox - e", e);
+ showToast(_("Failed to execute!"), "error");
+ } finally {
+ store.set({
+ diagnosticsActions: {
+ ...diagnosticsActions,
+ singBoxInstall: { loading: false }
+ }
+ });
+ await fetchSystemInfo();
+ }
+}
function renderWikiDisclaimerWidget() {
const diagnosticsChecks = store.get().diagnosticsChecks;
function getWikiKind() {
@@ -4384,7 +4464,14 @@ function renderDiagnosticAvailableActionsWidget() {
visible: true,
onClick: handleShowSingBoxConfig,
disabled: atLeastOneServiceCommandLoading
- }
+ },
+ singBoxInstall: {
+ loading: diagnosticsActions.singBoxInstall.loading,
+ visible: true,
+ onClick: handleInstallSingBox,
+ disabled: atLeastOneServiceCommandLoading || diagnosticsActions.singBoxInstall.loading
+ },
+ singBoxExtended: store.get().diagnosticsSystemInfo.sing_box_extended
});
return preserveScrollForPage(() => {
container.replaceChildren(renderedActions);
@@ -4427,7 +4514,7 @@ async function onStoreUpdate2(next, prev, diff) {
if (diff.diagnosticsRunAction) {
renderDiagnosticRunActionWidget();
}
- if (diff.diagnosticsActions || diff.servicesInfoWidget) {
+ if (diff.diagnosticsActions || diff.servicesInfoWidget || diff.diagnosticsSystemInfo) {
renderDiagnosticAvailableActionsWidget();
}
if (diff.diagnosticsSystemInfo) {
diff --git a/luci-app-podkop/po/ru/podkop.po b/luci-app-podkop/po/ru/podkop.po
index 77a33a4f..8bd2c236 100644
--- a/luci-app-podkop/po/ru/podkop.po
+++ b/luci-app-podkop/po/ru/podkop.po
@@ -1,15 +1,15 @@
# RU translations for PODKOP package.
# Copyright (C) 2026 THE PODKOP'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PODKOP package.
-# divocatt, 2026.
+# yandexru45, 2026.
#
msgid ""
msgstr ""
"Project-Id-Version: PODKOP\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-05-29 16:40+0300\n"
-"PO-Revision-Date: 2026-05-29 16:40+0300\n"
-"Last-Translator: divocatt\n"
+"POT-Creation-Date: 2026-06-02 14:25+0300\n"
+"PO-Revision-Date: 2026-06-02 14:25+0300\n"
+"Last-Translator: yandexru45\n"
"Language-Team: none\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
@@ -29,6 +29,12 @@ msgstr "✘ Отключено"
msgid "✘ Stopped"
msgstr "✘ Остановлен"
+msgid "Группировать по странам"
+msgstr ""
+
+msgid "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы"
+msgstr ""
+
msgid "Active Connections"
msgstr "Активные соединения"
@@ -227,18 +233,39 @@ msgstr "Введите доменные имена без протоколов,
msgid "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses"
msgstr "Введите подсети в нотации CIDR (например, 103.21.244.0/22) или отдельные IP-адреса"
+msgid "Enter the subscription URL to fetch proxy configurations from your provider"
+msgstr ""
+
msgid "Every 1 minute"
msgstr "Каждую минуту"
+msgid "Every 12 hours"
+msgstr ""
+
+msgid "Every 3 hours"
+msgstr ""
+
msgid "Every 3 minutes"
msgstr "Каждые 3 минуты"
+msgid "Every 30 minutes"
+msgstr ""
+
msgid "Every 30 seconds"
msgstr "Каждые 30 секунд"
msgid "Every 5 minutes"
msgstr "Каждые 5 минут"
+msgid "Every 6 hours"
+msgstr ""
+
+msgid "Every day"
+msgstr ""
+
+msgid "Every hour"
+msgstr ""
+
msgid "Exclude NTP"
msgstr "Исключить NTP"
@@ -263,9 +290,18 @@ msgstr "Получить глобальную проверку"
msgid "Global check"
msgstr "Глобальная проверка"
+msgid "How often to automatically update the subscription"
+msgstr ""
+
msgid "HTTP error"
msgstr "Ошибка HTTP"
+msgid "Install extended"
+msgstr "Установить extended"
+
+msgid "Install stable"
+msgstr "Установить stable"
+
msgid "Interface Monitoring"
msgstr "Мониторинг интерфейса"
@@ -626,6 +662,9 @@ msgstr "Sing-box"
msgid "Sing-box autostart disabled"
msgstr "Автостарт sing-box отключен"
+msgid "Sing-box core changed, version:"
+msgstr "Ядро sing-box изменено, версия:"
+
msgid "Sing-box installed"
msgstr "Sing-box установлен"
@@ -665,6 +704,15 @@ msgstr "Запустить podkop"
msgid "Stop podkop"
msgstr "Остановить podkop"
+msgid "Subscription"
+msgstr ""
+
+msgid "Subscription Update Interval"
+msgstr ""
+
+msgid "Subscription URL"
+msgstr ""
+
msgid "Successfully copied!"
msgstr "Успешно скопировано!"
diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot
index d96b948e..a722424c 100644
--- a/luci-app-podkop/po/templates/podkop.pot
+++ b/luci-app-podkop/po/templates/podkop.pot
@@ -1,1115 +1,1183 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) 2026 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PODKOP package.
-# divocatt <210179590+divocatt@users.noreply.github.com>, 2026.
+# yandexru45 , 2026.
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PODKOP\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-05-29 13:40+0300\n"
-"PO-Revision-Date: 2026-05-29 13:40+0300\n"
-"Last-Translator: divocatt <210179590+divocatt@users.noreply.github.com>\n"
+"POT-Creation-Date: 2026-06-02 11:25+0300\n"
+"PO-Revision-Date: 2026-06-02 11:25+0300\n"
+"Last-Translator: yandexru45 \n"
"Language-Team: LANGUAGE \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: src/podkop/tabs/dashboard/initController.ts:345
+#: src\podkop\tabs\dashboard\initController.ts:345
msgid "✔ Enabled"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:356
+#: src\podkop\tabs\dashboard\initController.ts:356
msgid "✔ Running"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:346
+#: src\podkop\tabs\dashboard\initController.ts:346
msgid "✘ Disabled"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:357
+#: src\podkop\tabs\dashboard\initController.ts:357
msgid "✘ Stopped"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:307
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:127
+msgid "Группировать по странам"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:128
+msgid "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы"
+msgstr ""
+
+#: src\podkop\tabs\dashboard\initController.ts:307
msgid "Active Connections"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:106
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:106
msgid "Additional marking rules found"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:247
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:247
msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:199
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:251
msgid "Applicable for SOCKS and Shadowsocks proxy"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:444
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:496
msgid "At least one valid domain must be specified. Comments-only content is not allowed."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:525
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:577
msgid "At least one valid subnet or IP must be specified. Comments-only content is not allowed."
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:43
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:47
msgid "Available actions"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:65
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:65
msgid "Bootsrap DNS"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:45
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:45
msgid "Bootstrap DNS server"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:58
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:58
msgid "Browser is not using FakeIP"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:57
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:57
msgid "Browser is using FakeIP correctly"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:348
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:348
msgid "Cache File Path"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:362
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:362
msgid "Cache file path cannot be empty"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:27
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:28
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:27
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:25
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:27
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:28
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:27
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:25
msgid "Cannot receive checks result"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:15
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:15
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:13
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:15
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:13
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:15
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:15
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:13
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:15
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:13
msgid "Checking, please wait"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getCheckTitle.ts:2
+#: src\podkop\tabs\diagnostic\helpers\getCheckTitle.ts:2
msgid "checks"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getMeta.ts:26
+#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:26
msgid "Checks failed"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getMeta.ts:13
+#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:13
msgid "Checks passed"
msgstr ""
-#: src/validators/validateSubnet.ts:33
+#: src\validators\validateSubnet.ts:33
msgid "CIDR must be between 0 and 32"
msgstr ""
-#: src/partials/modal/renderModal.ts:26
+#: src\partials\modal\renderModal.ts:26
msgid "Close"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:299
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:351
msgid "Community Lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:335
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:335
msgid "Config File Path"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:27
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:27
msgid "Configuration for Podkop service"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:23
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:23
msgid "Configuration Type"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:12
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:12
msgid "Connection Type"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:26
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:26
msgid "Connection URL"
msgstr ""
-#: src/partials/modal/renderModal.ts:20
+#: src\partials\modal\renderModal.ts:20
msgid "Copy"
msgstr ""
-#: src/podkop/tabs/dashboard/partials/renderWidget.ts:22
+#: src\podkop\tabs\dashboard\partials\renderWidget.ts:22
msgid "Currently unavailable"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:80
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:80
msgid "Dashboard"
msgstr ""
-#: src/podkop/tabs/dashboard/partials/renderSections.ts:19
+#: src\podkop\tabs\dashboard\partials\renderSections.ts:19
msgid "Dashboard currently unavailable"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:222
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:222
msgid "Delay in milliseconds before reloading podkop after interface UP"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:229
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:229
msgid "Delay value cannot be empty"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:82
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:82
msgid "DHCP has DNS server"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:65
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:65
msgid "Diagnostics"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:79
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:83
msgid "Disable autostart"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:265
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:265
msgid "Disable QUIC"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:266
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:266
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:390
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:470
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:442
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:522
msgid "Disabled"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:77
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:77
msgid "DNS on router"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:267
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:15
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:319
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:15
msgid "DNS over HTTPS (DoH)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:268
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:16
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:320
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:16
msgid "DNS over TLS (DoT)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:264
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:12
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:316
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:12
msgid "DNS Protocol Type"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:68
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:68
msgid "DNS Rewrite TTL"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:277
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:24
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:329
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:24
msgid "DNS Server"
msgstr ""
-#: src/validators/validateDns.ts:7
+#: src\validators\validateDns.ts:7
msgid "DNS server address cannot be empty"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts:26
+#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:26
msgid "Do not panic, everything can be fixed, just..."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:254
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:306
msgid "Domain Resolver"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:326
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:326
msgid "Dont Touch My DHCP!"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:241
-#: src/podkop/tabs/dashboard/initController.ts:275
+#: src\podkop\tabs\dashboard\initController.ts:241
+#: src\podkop\tabs\dashboard\initController.ts:275
msgid "Downlink"
msgstr ""
-#: src/partials/modal/renderModal.ts:15
+#: src\partials\modal\renderModal.ts:15
msgid "Download"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:288
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:288
msgid "Download Lists via Proxy/VPN"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:297
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:297
msgid "Download Lists via specific proxy section"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:289
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:298
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:289
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:298
msgid "Downloading all lists via specific Proxy/VPN"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:391
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:471
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:443
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:523
msgid "Dynamic List"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:89
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:93
msgid "Enable autostart"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:255
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:307
msgid "Enable built-in DNS resolver for domains handled by this section"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:691
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:746
msgid "Enable DNS resolve to get real IP when routing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:665
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:717
msgid "Enable Mixed Proxy"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:126
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:126
msgid "Enable Output Network Interface"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:666
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:718
msgid "Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:237
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:237
msgid "Enable YACD"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:246
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:246
msgid "Enable YACD WAN Access"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:66
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:67
msgid "Enter complete outbound configuration in JSON format"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:426
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:478
msgid "Enter domain names separated by commas, spaces, or newlines. You can add comments using //"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:400
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:452
msgid "Enter domain names without protocols, e.g. example.com or sub.example.com"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:480
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:532
msgid "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:138
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:90
+msgid "Enter the subscription URL to fetch proxy configurations from your provider"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:187
msgid "Every 1 minute"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:139
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:119
+msgid "Every 12 hours"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:117
+msgid "Every 3 hours"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:188
msgid "Every 3 minutes"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:137
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:115
+msgid "Every 30 minutes"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:186
msgid "Every 30 seconds"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:140
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:189
msgid "Every 5 minutes"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:402
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:118
+msgid "Every 6 hours"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:120
+msgid "Every day"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:116
+msgid "Every hour"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:402
msgid "Exclude NTP"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:403
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:403
msgid "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN"
msgstr ""
-#: src/helpers/copyToClipboard.ts:12
+#: src\helpers\copyToClipboard.ts:12
msgid "Failed to copy!"
msgstr ""
-#: src/podkop/tabs/diagnostic/initController.ts:227
-#: src/podkop/tabs/diagnostic/initController.ts:231
-#: src/podkop/tabs/diagnostic/initController.ts:261
-#: src/podkop/tabs/diagnostic/initController.ts:265
-#: src/podkop/tabs/diagnostic/initController.ts:302
-#: src/podkop/tabs/diagnostic/initController.ts:306
+#: src\podkop\tabs\diagnostic\initController.ts:229
+#: src\podkop\tabs\diagnostic\initController.ts:233
+#: src\podkop\tabs\diagnostic\initController.ts:263
+#: src\podkop\tabs\diagnostic\initController.ts:267
+#: src\podkop\tabs\diagnostic\initController.ts:304
+#: src\podkop\tabs\diagnostic\initController.ts:308
+#: src\podkop\tabs\diagnostic\initController.ts:342
+#: src\podkop\tabs\diagnostic\initController.ts:346
msgid "Failed to execute!"
msgstr ""
-#: src/podkop/methods/custom/getDashboardSections.ts:148
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:59
+#: src\podkop\methods\custom\getDashboardSections.ts:150
+#: src\podkop\methods\custom\getDashboardSections.ts:181
+#: src\podkop\methods\custom\getDashboardSections.ts:218
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:59
msgid "Fastest"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:638
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:690
msgid "Fully Routed IPs"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:98
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:102
msgid "Get global check"
msgstr ""
-#: src/podkop/tabs/diagnostic/initController.ts:222
+#: src\podkop\tabs\diagnostic\initController.ts:224
msgid "Global check"
msgstr ""
-#: src/podkop/api.ts:27
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:113
+msgid "How often to automatically update the subscription"
+msgstr ""
+
+#: src\podkop\api.ts:27
msgid "HTTP error"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:189
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:129
+msgid "Install extended"
+msgstr ""
+
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:129
+msgid "Install stable"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:189
msgid "Interface Monitoring"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:221
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:221
msgid "Interface Monitoring Delay"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:190
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:190
msgid "Interface monitoring for Bad WAN"
msgstr ""
-#: src/validators/validateDns.ts:23
+#: src\validators\validateDns.ts:23
msgid "Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH"
msgstr ""
-#: src/validators/validateDomain.ts:18
-#: src/validators/validateDomain.ts:27
+#: src\validators\validateDomain.ts:18
+#: src\validators\validateDomain.ts:27
msgid "Invalid domain address"
msgstr ""
-#: src/validators/validateSubnet.ts:11
+#: src\validators\validateSubnet.ts:11
msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:90
+#: src\validators\validateHysteriaUrl.ts:90
msgid "Invalid HY2 URL: insecure must be 0 or 1"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:77
+#: src\validators\validateHysteriaUrl.ts:77
msgid "Invalid HY2 URL: invalid port number"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:30
+#: src\validators\validateHysteriaUrl.ts:30
msgid "Invalid HY2 URL: missing credentials/server"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:47
+#: src\validators\validateHysteriaUrl.ts:47
msgid "Invalid HY2 URL: missing host"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:41
+#: src\validators\validateHysteriaUrl.ts:41
msgid "Invalid HY2 URL: missing host & port"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:36
+#: src\validators\validateHysteriaUrl.ts:36
msgid "Invalid HY2 URL: missing password"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:50
+#: src\validators\validateHysteriaUrl.ts:50
msgid "Invalid HY2 URL: missing port"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:18
+#: src\validators\validateHysteriaUrl.ts:18
msgid "Invalid HY2 URL: must not contain spaces"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:12
+#: src\validators\validateHysteriaUrl.ts:12
msgid "Invalid HY2 URL: must start with hysteria2:// or hy2://"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:108
+#: src\validators\validateHysteriaUrl.ts:108
msgid "Invalid HY2 URL: obfs-password required when obfs is set"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:122
+#: src\validators\validateHysteriaUrl.ts:122
msgid "Invalid HY2 URL: parsing failed"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:116
+#: src\validators\validateHysteriaUrl.ts:116
msgid "Invalid HY2 URL: sni cannot be empty"
msgstr ""
-#: src/validators/validateHysteriaUrl.ts:98
+#: src\validators\validateHysteriaUrl.ts:98
msgid "Invalid HY2 URL: unsupported obfs type"
msgstr ""
-#: src/validators/validateIp.ts:11
+#: src\validators\validateIp.ts:11
msgid "Invalid IP address"
msgstr ""
-#: src/validators/validateOutboundJson.ts:9
+#: src\validators\validateOutboundJson.ts:9
msgid "Invalid JSON format"
msgstr ""
-#: src/validators/validatePath.ts:22
+#: src\validators\validatePath.ts:22
msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:85
+#: src\validators\validateShadowsocksUrl.ts:85
msgid "Invalid port number. Must be between 1 and 65535"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:37
+#: src\validators\validateShadowsocksUrl.ts:37
msgid "Invalid Shadowsocks URL: decoded credentials must contain method:password"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:27
+#: src\validators\validateShadowsocksUrl.ts:27
msgid "Invalid Shadowsocks URL: missing credentials"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:46
+#: src\validators\validateShadowsocksUrl.ts:46
msgid "Invalid Shadowsocks URL: missing method and password separator \":\""
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:76
+#: src\validators\validateShadowsocksUrl.ts:76
msgid "Invalid Shadowsocks URL: missing port"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:67
+#: src\validators\validateShadowsocksUrl.ts:67
msgid "Invalid Shadowsocks URL: missing server"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:58
+#: src\validators\validateShadowsocksUrl.ts:58
msgid "Invalid Shadowsocks URL: missing server address"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:16
+#: src\validators\validateShadowsocksUrl.ts:16
msgid "Invalid Shadowsocks URL: must not contain spaces"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:8
+#: src\validators\validateShadowsocksUrl.ts:8
msgid "Invalid Shadowsocks URL: must start with ss://"
msgstr ""
-#: src/validators/validateShadowsocksUrl.ts:91
+#: src\validators\validateShadowsocksUrl.ts:91
msgid "Invalid Shadowsocks URL: parsing failed"
msgstr ""
-#: src/validators/validateSocksUrl.ts:73
+#: src\validators\validateSocksUrl.ts:73
msgid "Invalid SOCKS URL: invalid host format"
msgstr ""
-#: src/validators/validateSocksUrl.ts:63
+#: src\validators\validateSocksUrl.ts:63
msgid "Invalid SOCKS URL: invalid port number"
msgstr ""
-#: src/validators/validateSocksUrl.ts:42
+#: src\validators\validateSocksUrl.ts:42
msgid "Invalid SOCKS URL: missing host and port"
msgstr ""
-#: src/validators/validateSocksUrl.ts:51
+#: src\validators\validateSocksUrl.ts:51
msgid "Invalid SOCKS URL: missing hostname or IP"
msgstr ""
-#: src/validators/validateSocksUrl.ts:56
+#: src\validators\validateSocksUrl.ts:56
msgid "Invalid SOCKS URL: missing port"
msgstr ""
-#: src/validators/validateSocksUrl.ts:34
+#: src\validators\validateSocksUrl.ts:34
msgid "Invalid SOCKS URL: missing username"
msgstr ""
-#: src/validators/validateSocksUrl.ts:19
+#: src\validators\validateSocksUrl.ts:19
msgid "Invalid SOCKS URL: must not contain spaces"
msgstr ""
-#: src/validators/validateSocksUrl.ts:10
+#: src\validators\validateSocksUrl.ts:10
msgid "Invalid SOCKS URL: must start with socks4://, socks4a://, or socks5://"
msgstr ""
-#: src/validators/validateSocksUrl.ts:77
+#: src\validators\validateSocksUrl.ts:77
msgid "Invalid SOCKS URL: parsing failed"
msgstr ""
-#: src/validators/validateTrojanUrl.ts:15
+#: src\validators\validateTrojanUrl.ts:15
msgid "Invalid Trojan URL: must not contain spaces"
msgstr ""
-#: src/validators/validateTrojanUrl.ts:8
+#: src\validators\validateTrojanUrl.ts:8
msgid "Invalid Trojan URL: must start with trojan://"
msgstr ""
-#: src/validators/validateTrojanUrl.ts:56
+#: src\validators\validateTrojanUrl.ts:56
msgid "Invalid Trojan URL: parsing failed"
msgstr ""
-#: src/validators/validateUrl.ts:8
-#: src/validators/validateUrl.ts:31
+#: src\validators\validateUrl.ts:8
+#: src\validators\validateUrl.ts:31
msgid "Invalid URL format"
msgstr ""
-#: src/validators/validateVlessUrl.ts:110
+#: src\validators\validateVlessUrl.ts:110
msgid "Invalid VLESS URL: parsing failed"
msgstr ""
-#: src/validators/validateSubnet.ts:18
+#: src\validators\validateSubnet.ts:18
msgid "IP address 0.0.0.0 is not allowed"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getMeta.ts:20
+#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:20
msgid "Issues detected"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts:48
+#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:48
msgid "Latest"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:276
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:276
msgid "List Update Frequency"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:546
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:598
msgid "Local Domain Lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:569
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:621
msgid "Local Subnet Lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:384
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:384
msgid "Log Level"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runDnsCheck.ts:72
+#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:72
msgid "Main DNS"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:311
+#: src\podkop\tabs\dashboard\initController.ts:311
msgid "Memory Usage"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:678
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:730
msgid "Mixed Proxy Port"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:198
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:198
msgid "Monitored Interfaces"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:164
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:215
msgid "Must be a number in the range of 50 - 1000"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:208
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:260
msgid "Network Interface"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:105
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:105
msgid "No other marking rules found"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderCheckSection.ts:189
+#: src\podkop\tabs\diagnostic\partials\renderCheckSection.ts:189
msgid "Not implement yet"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:75
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:81
-#: src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts:100
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:75
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:81
+#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:100
msgid "Not responding"
msgstr ""
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:55
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:63
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:71
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:79
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:87
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:59
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:67
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:75
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:83
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:91
msgid "Not running"
msgstr ""
-#: src/helpers/withTimeout.ts:7
+#: src\helpers\withTimeout.ts:7
msgid "Operation timed out"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:29
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:30
msgid "Outbound Config"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:65
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:66
msgid "Outbound Configuration"
msgstr ""
-#: src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts:38
+#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:38
msgid "Outdated"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:135
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:135
msgid "Output Network Interface"
msgstr ""
-#: src/validators/validatePath.ts:7
+#: src\validators\validatePath.ts:7
msgid "Path cannot be empty"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:366
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:366
msgid "Path must be absolute (start with /)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:375
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:375
msgid "Path must contain at least one directory (like /tmp/cache.db)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:370
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:370
msgid "Path must end with cache.db"
msgstr ""
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:103
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:111
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:119
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:127
-#: src/podkop/tabs/diagnostic/diagnostic.store.ts:135
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:107
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:115
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:123
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:131
+#: src\podkop\tabs\diagnostic\diagnostic.store.ts:139
msgid "Pending"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:343
+#: src\podkop\tabs\dashboard\initController.ts:343
msgid "Podkop"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:26
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:26
msgid "Podkop Settings"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:327
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:327
msgid "Podkop will not modify your DHCP configuration"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:36
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:37
msgid "Proxy Configuration URL"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:66
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:66
msgid "Proxy traffic is not routed via FakeIP"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:65
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:65
msgid "Proxy traffic is routed via FakeIP"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:333
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:385
msgid "Regional options cannot be used together"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:592
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:644
msgid "Remote Domain Lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:615
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:667
msgid "Remote Subnet Lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:690
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:745
msgid "Resolve real IP for routing"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:49
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:53
msgid "Restart podkop"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:51
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:51
msgid "Router DNS is not routed through sing-box"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts:50
+#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:50
msgid "Router DNS is routed through sing-box"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:413
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:413
msgid "Routing Excluded IPs"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:79
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:79
msgid "Rules mangle counters"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:74
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:74
msgid "Rules mangle exist"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:89
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:89
msgid "Rules mangle output counters"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:84
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:84
msgid "Rules mangle output exist"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:99
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:99
msgid "Rules proxy counters"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:94
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:94
msgid "Rules proxy exist"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderRunAction.ts:15
+#: src\podkop\tabs\diagnostic\partials\renderRunAction.ts:15
msgid "Run Diagnostic"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:352
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:404
msgid "Russia inside restrictions"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:257
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:257
msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:36
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:36
msgid "Sections"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:300
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:352
msgid "Select a predefined list for routing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:13
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:13
msgid "Select between VPN and Proxy connection methods for traffic routing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:13
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:13
msgid "Select DNS protocol to use"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:277
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:277
msgid "Select how often the domain or subnet lists are updated automatically"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:24
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:24
msgid "Select how to configure the proxy"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:209
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:261
msgid "Select network interface for VPN connection"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:278
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:25
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:330
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:25
msgid "Select or enter DNS server address"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:349
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:349
msgid "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:336
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:336
msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:265
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:317
msgid "Select the DNS protocol type for the domain resolver"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:388
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:440
msgid "Select the list type for adding custom domains"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:468
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:520
msgid "Select the list type for adding custom subnets"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:385
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:385
msgid "Select the log level for sing-box"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:90
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:90
msgid "Select the network interface from which the traffic will originate"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:136
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:136
msgid "Select the network interface to which the traffic will originate"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:199
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:199
msgid "Select the WAN interfaces to be monitored"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:27
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:27
msgid "Selector"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:88
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:137
msgid "Selector Proxy Links"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:340
+#: src\podkop\tabs\dashboard\initController.ts:340
msgid "Services info"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js:49
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:49
msgid "Settings"
msgstr ""
-#: src/podkop/tabs/diagnostic/initController.ts:290
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:116
+#: src\podkop\tabs\diagnostic\initController.ts:292
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:120
msgid "Show sing-box config"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:354
+#: src\podkop\tabs\dashboard\initController.ts:354
msgid "Sing-box"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:77
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:77
msgid "Sing-box autostart disabled"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:62
+#: src\podkop\tabs\diagnostic\initController.ts:337
+msgid "Sing-box core changed, version:"
+msgstr ""
+
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:62
msgid "Sing-box installed"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:87
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:87
msgid "Sing-box listening ports"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:82
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:82
msgid "Sing-box process running"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:72
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:72
msgid "Sing-box service exist"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts:67
+#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:67
msgid "Sing-box version is compatible (newer than 1.12.4)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:89
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:89
msgid "Source Network Interface"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:414
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:414
msgid "Specify a local IP address to be excluded from routing"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:639
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:691
msgid "Specify local IP addresses or subnets whose traffic will always be routed through the configured route"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:593
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:645
msgid "Specify remote URLs to download and use domain lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:616
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:668
msgid "Specify remote URLs to download and use subnet lists"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:547
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:570
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:599
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:622
msgid "Specify the path to the list file located on the router filesystem"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:69
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:73
msgid "Start podkop"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:59
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:63
msgid "Stop podkop"
msgstr ""
-#: src/helpers/copyToClipboard.ts:10
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:29
+msgid "Subscription"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:112
+msgid "Subscription Update Interval"
+msgstr ""
+
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:89
+msgid "Subscription URL"
+msgstr ""
+
+#: src\helpers\copyToClipboard.ts:10
msgid "Successfully copied!"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:304
+#: src\podkop\tabs\dashboard\initController.ts:304
msgid "System info"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderSystemInfo.ts:21
+#: src\podkop\tabs\diagnostic\partials\renderSystemInfo.ts:21
msgid "System information"
msgstr ""
-#: src/podkop/tabs/diagnostic/checks/runNftCheck.ts:69
+#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:69
msgid "Table exist"
msgstr ""
-#: src/podkop/tabs/dashboard/partials/renderSections.ts:108
+#: src\podkop\tabs\dashboard\partials\renderSections.ts:108
msgid "Test latency"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:392
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:472
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:444
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:524
msgid "Text List"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:46
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:46
msgid "The DNS server used to look up the IP address of an upstream DNS server"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:135
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:184
msgid "The interval between connectivity tests"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:148
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:198
msgid "The maximum difference in response times (ms) allowed when comparing servers"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:171
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:222
msgid "The URL used to test server connectivity"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:69
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:69
msgid "Time in seconds for DNS record caching (default: 60)"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:238
+#: src\podkop\tabs\dashboard\initController.ts:238
msgid "Traffic"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:268
+#: src\podkop\tabs\dashboard\initController.ts:268
msgid "Traffic Total"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts:25
+#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:25
msgid "Troubleshooting"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:80
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:80
msgid "TTL must be a positive number"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:75
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:75
msgid "TTL value cannot be empty"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:269
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:17
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:321
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:17
msgid "UDP (Unprotected DNS)"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:198
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:250
msgid "UDP over TCP"
msgstr ""
-#: src/podkop/tabs/diagnostic/initController.ts:38
-#: src/podkop/tabs/diagnostic/initController.ts:39
-#: src/podkop/tabs/diagnostic/initController.ts:40
-#: src/podkop/tabs/diagnostic/initController.ts:41
-#: src/podkop/tabs/diagnostic/initController.ts:42
-#: src/podkop/tabs/diagnostic/initController.ts:43
-#: src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts:7
+#: src\podkop\tabs\diagnostic\initController.ts:39
+#: src\podkop\tabs\diagnostic\initController.ts:40
+#: src\podkop\tabs\diagnostic\initController.ts:41
+#: src\podkop\tabs\diagnostic\initController.ts:42
+#: src\podkop\tabs\diagnostic\initController.ts:43
+#: src\podkop\tabs\diagnostic\initController.ts:44
+#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:7
msgid "unknown"
msgstr ""
-#: src/podkop/api.ts:40
+#: src\podkop\api.ts:40
msgid "Unknown error"
msgstr ""
-#: src/podkop/tabs/dashboard/initController.ts:240
-#: src/podkop/tabs/dashboard/initController.ts:271
+#: src\podkop\tabs\dashboard\initController.ts:240
+#: src\podkop\tabs\dashboard\initController.ts:271
msgid "Uplink"
msgstr ""
-#: src/validators/validateProxyUrl.ts:37
+#: src\validators\validateProxyUrl.ts:37
msgid "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://"
msgstr ""
-#: src/validators/validateUrl.ts:17
+#: src\validators\validateUrl.ts:17
msgid "URL must use one of the following protocols:"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:28
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:28
msgid "URLTest"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:134
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:183
msgid "URLTest Check Interval"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:111
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:160
msgid "URLTest Proxy Links"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:170
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:221
msgid "URLTest Testing URL"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:147
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:197
msgid "URLTest Tolerance"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:387
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:439
msgid "User Domain List Type"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:399
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:451
msgid "User Domains"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:425
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:477
msgid "User Domains List"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:467
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:519
msgid "User Subnet List Type"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:479
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:531
msgid "User Subnets"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:505
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:557
msgid "User Subnets List"
msgstr ""
-#: src/validators/validateDns.ts:14
-#: src/validators/validateDns.ts:18
-#: src/validators/validateDomain.ts:13
-#: src/validators/validateDomain.ts:30
-#: src/validators/validateHysteriaUrl.ts:120
-#: src/validators/validateIp.ts:8
-#: src/validators/validateOutboundJson.ts:7
-#: src/validators/validatePath.ts:16
-#: src/validators/validateShadowsocksUrl.ts:95
-#: src/validators/validateSocksUrl.ts:80
-#: src/validators/validateSubnet.ts:38
-#: src/validators/validateTrojanUrl.ts:59
-#: src/validators/validateUrl.ts:28
-#: src/validators/validateVlessUrl.ts:108
+#: src\validators\validateDns.ts:14
+#: src\validators\validateDns.ts:18
+#: src\validators\validateDomain.ts:13
+#: src\validators\validateDomain.ts:30
+#: src\validators\validateHysteriaUrl.ts:120
+#: src\validators\validateIp.ts:8
+#: src\validators\validateOutboundJson.ts:7
+#: src\validators\validatePath.ts:16
+#: src\validators\validateShadowsocksUrl.ts:95
+#: src\validators\validateSocksUrl.ts:80
+#: src\validators\validateSubnet.ts:38
+#: src\validators\validateTrojanUrl.ts:59
+#: src\validators\validateUrl.ts:28
+#: src\validators\validateVlessUrl.ts:108
msgid "Valid"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:458
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:537
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:510
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:589
msgid "Validation errors:"
msgstr ""
-#: src/podkop/tabs/diagnostic/initController.ts:256
-#: src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts:107
+#: src\podkop\tabs\diagnostic\initController.ts:258
+#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:111
msgid "View logs"
msgstr ""
-#: src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts:31
+#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:31
msgid "Visit Wiki"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:37
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:89
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:112
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:38
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:138
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:161
msgid "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:335
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:387
msgid "Warning: %s cannot be used together with %s. Previous selections have been removed."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js:354
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:406
msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:256
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:256
msgid "YACD Secret Key"
msgstr ""
-#: ../luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js:127
+#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:127
msgid "You can select Output Network Interface, by default autodetect"
msgstr ""
diff --git a/podkop/files/usr/bin/podkop b/podkop/files/usr/bin/podkop
index 0254b42e..736a516f 100755
--- a/podkop/files/usr/bin/podkop
+++ b/podkop/files/usr/bin/podkop
@@ -20,6 +20,7 @@ check_required_file "$PODKOP_LIB/sing_box_config_manager.sh"
check_required_file "$PODKOP_LIB/sing_box_config_facade.sh"
check_required_file "$PODKOP_LIB/logging.sh"
check_required_file "$PODKOP_LIB/rulesets.sh"
+check_required_file "$PODKOP_LIB/updater.sh"
. /lib/functions.sh
. /lib/config/uci.sh
. /lib/functions/network.sh
@@ -30,6 +31,7 @@ check_required_file "$PODKOP_LIB/rulesets.sh"
. "$PODKOP_LIB/sing_box_config_facade.sh"
. "$PODKOP_LIB/logging.sh"
. "$PODKOP_LIB/rulesets.sh"
+. "$PODKOP_LIB/updater.sh"
config_load "$PODKOP_CONFIG"
@@ -2840,7 +2842,7 @@ show_system_info() {
}
get_system_info() {
- local podkop_version podkop_latest_version luci_app_version sing_box_version openwrt_version device_model
+ local podkop_version podkop_latest_version luci_app_version sing_box_version openwrt_version device_model sing_box_extended
podkop_version="$PODKOP_VERSION"
@@ -2860,6 +2862,11 @@ get_system_info() {
sing_box_version="not installed"
fi
+ sing_box_extended=0
+ if command -v sing-box > /dev/null 2>&1 && is_sing_box_extended "$sing_box_version"; then
+ sing_box_extended=1
+ fi
+
if [ -f /etc/os-release ]; then
openwrt_version=$(grep OPENWRT_RELEASE /etc/os-release | cut -d'"' -f2)
[ -z "$openwrt_version" ] && openwrt_version="unknown"
@@ -2874,7 +2881,7 @@ get_system_info() {
device_model="unknown"
fi
- echo "{\"podkop_version\": \"$podkop_version\", \"podkop_latest_version\": \"$podkop_latest_version\", \"luci_app_version\": \"$luci_app_version\", \"sing_box_version\": \"$sing_box_version\", \"openwrt_version\": \"$openwrt_version\", \"device_model\": \"$device_model\"}" | jq .
+ echo "{\"podkop_version\": \"$podkop_version\", \"podkop_latest_version\": \"$podkop_latest_version\", \"luci_app_version\": \"$luci_app_version\", \"sing_box_version\": \"$sing_box_version\", \"sing_box_extended\": $sing_box_extended, \"openwrt_version\": \"$openwrt_version\", \"device_model\": \"$device_model\"}" | jq .
}
get_sing_box_status() {
@@ -3333,7 +3340,7 @@ global_check() {
system_info_json=$(get_system_info)
if [ -n "$system_info_json" ]; then
- local podkop_version podkop_latest_version luci_app_version sing_box_version openwrt_version device_model
+ local podkop_version podkop_latest_version luci_app_version sing_box_version openwrt_version device_model sing_box_extended
podkop_version=$(echo "$system_info_json" | jq -r '.podkop_version // "unknown"')
podkop_latest_version=$(echo "$system_info_json" | jq -r '.podkop_latest_version // "unknown"')
@@ -3669,6 +3676,9 @@ Available commands:
get_system_info Get system information in JSON format
check_dns_available Check DNS server availability
global_check Run global system check
+ component_action Run component action:
+ (e.g. sing_box install_extended|install_stable|check_update)
+ component_action_async Run component_action in background, returns job file path
EOF
}
@@ -3748,6 +3758,13 @@ check_dns_available)
global_check)
global_check "${2:-}"
;;
+component_action)
+ component_action "$2" "$3"
+ ;;
+component_action_async)
+ "$0" component_action "$2" "$3" > "/tmp/podkop-component-$$.json" 2>&1 &
+ echo "{\"success\":true,\"job\":\"/tmp/podkop-component-$$.json\"}"
+ ;;
*)
show_help
exit 1
diff --git a/podkop/files/usr/lib/helpers.sh b/podkop/files/usr/lib/helpers.sh
index 3ce3d3e5..7d1e8660 100644
--- a/podkop/files/usr/lib/helpers.sh
+++ b/podkop/files/usr/lib/helpers.sh
@@ -609,11 +609,26 @@ get_kernel_version() {
get_sing_box_version() {
local version=""
if command -v sing-box >/dev/null 2>&1; then
- version="$(sing-box version 2>/dev/null | head -1 | awk '{print $NF}')"
+ version="$(sing-box version 2>/dev/null | head -n1 | awk '{print $NF}')"
fi
echo "${version:-1.0}"
}
+# Returns 0 if the given (or detected) sing-box version is an "extended" build
+# Arguments:
+# $1 - optional sing-box version string (defaults to get_sing_box_version)
+is_sing_box_extended() {
+ local version="${1:-}"
+
+ [ -n "$version" ] || version="$(get_sing_box_version)"
+
+ case "$version" in
+ *extended*) return 0 ;;
+ esac
+
+ return 1
+}
+
# Generates a deterministic HWID based on WAN MAC address and device model
# Format: xxxx-xxxx-xxxx-xxxx
# Same router always produces the same HWID
diff --git a/podkop/files/usr/lib/sing_box_config_facade.sh b/podkop/files/usr/lib/sing_box_config_facade.sh
index e54da74a..a0884a7c 100644
--- a/podkop/files/usr/lib/sing_box_config_facade.sh
+++ b/podkop/files/usr/lib/sing_box_config_facade.sh
@@ -188,7 +188,7 @@ _add_outbound_security() {
case "$security" in
tls | reality)
- local sni insecure alpn fingerprint public_key short_id
+ local sni insecure alpn fingerprint public_key short_id transport_type
sni=$(url_get_query_param "$url" "sni")
insecure=$(_get_insecure_query_param_from_url "$url")
alpn=$(comma_string_to_json_array "$(url_get_query_param "$url" "alpn")")
@@ -196,6 +196,12 @@ _add_outbound_security() {
public_key=$(url_get_query_param "$url" "pbk")
short_id=$(url_get_query_param "$url" "sid")
+ # XHTTP transport defaults its ALPN to h2/http/1.1 when none is provided.
+ transport_type=$(url_get_query_param "$url" "type")
+ if [ "$transport_type" = "xhttp" ] && [ "$alpn" = "[]" ]; then
+ alpn='["h2","http/1.1"]'
+ fi
+
if [ "$scheme" = "hysteria2" ] || [ "$scheme" = "hy2" ]; then
fingerprint=""
fi
@@ -261,6 +267,20 @@ _add_outbound_transport() {
sing_box_cm_set_grpc_transport_for_outbound "$config" "$outbound_tag" "$grpc_service_name"
)
;;
+ xhttp)
+ if ! is_sing_box_extended; then
+ log "XHTTP transport requires sing-box-extended. Install sing-box-extended and retry." "error"
+ echo "$config"
+ return 0
+ fi
+ local xhttp_path xhttp_host xhttp_sni xhttp_mode
+ xhttp_path=$(url_get_query_param "$url" "path")
+ xhttp_host=$(url_get_query_param "$url" "host")
+ xhttp_sni=$(url_get_query_param "$url" "sni")
+ [ -n "$xhttp_host" ] || xhttp_host="$xhttp_sni"
+ xhttp_mode=$(url_get_query_param "$url" "mode")
+ config=$(sing_box_cm_set_xhttp_transport_for_outbound "$config" "$outbound_tag" "$xhttp_path" "$xhttp_host" "$xhttp_mode")
+ ;;
*)
log "Unknown transport '$transport' detected." "error"
;;
@@ -415,6 +435,15 @@ sing_box_cf_add_subscription_outbounds() {
continue
fi
+ # XHTTP transport requires sing-box-extended; skip such outbounds otherwise.
+ local outbound_transport_type
+ outbound_transport_type=$(echo "$outbound_json" | jq -r '.transport.type // ""' 2>/dev/null)
+ if [ "$outbound_transport_type" = "xhttp" ] && ! is_sing_box_extended; then
+ log "Skip unsupported XHTTP outbound (requires sing-box-extended): '$display_name'" "warn"
+ i=$((i + 1))
+ continue
+ fi
+
# Keep original tag from the subscription for dashboard readability.
preferred_tag=$(echo "$outbound_json" | jq -r '.tag // .remark // "server-'"$i"'"' 2>/dev/null)
if [ -z "$preferred_tag" ] || [ "$preferred_tag" = "null" ]; then
diff --git a/podkop/files/usr/lib/sing_box_config_manager.sh b/podkop/files/usr/lib/sing_box_config_manager.sh
index c1a2943b..635b4312 100644
--- a/podkop/files/usr/lib/sing_box_config_manager.sh
+++ b/podkop/files/usr/lib/sing_box_config_manager.sh
@@ -833,6 +833,61 @@ sing_box_cm_set_ws_transport_for_outbound() {
)'
}
+#######################################
+# Set XHTTP transport settings for an outbound in a sing-box JSON configuration.
+# Requires sing-box-extended on the router.
+# Arguments:
+# config: string (JSON), sing-box configuration to modify
+# tag: string, identifier of the outbound to modify
+# path: string, XHTTP path (defaults to "/" if empty)
+# host: string, Host header for XHTTP (optional)
+# mode: string, XHTTP mode (auto|packet-up|stream-up|stream-one; defaults to "auto")
+# Outputs:
+# Writes updated JSON configuration to stdout
+# Example:
+# CONFIG=$(sing_box_cm_set_xhttp_transport_for_outbound "$CONFIG" "vless-xhttp-out" "/path" "example.com" "auto")
+#######################################
+sing_box_cm_set_xhttp_transport_for_outbound() {
+ local config="$1"
+ local tag="$2"
+ local path="$3"
+ local host="$4"
+ local mode="$5"
+
+ case "$mode" in
+ auto | packet-up | stream-up | stream-one) ;;
+ *) mode="auto" ;;
+ esac
+
+ [ -n "$path" ] || path="/"
+
+ echo "$config" | jq \
+ --arg tag "$tag" \
+ --arg path "$path" \
+ --arg host "$host" \
+ --arg mode "$mode" \
+ '.outbounds |= map(
+ if .tag == $tag then
+ . + {
+ transport: (
+ {
+ type: "xhttp",
+ mode: $mode,
+ path: $path,
+ x_padding_bytes: "100-1000",
+ no_grpc_header: false,
+ sc_max_each_post_bytes: 1000000,
+ sc_min_posts_interval_ms: 30
+ }
+ + (if $host != "" then {host: $host} else {} end)
+ )
+ }
+ else
+ .
+ end
+ )'
+}
+
#######################################
# Set TLS settings for an outbound in a sing-box JSON configuration.
# Arguments:
diff --git a/podkop/files/usr/lib/updater.sh b/podkop/files/usr/lib/updater.sh
new file mode 100644
index 00000000..fd8f6fbf
--- /dev/null
+++ b/podkop/files/usr/lib/updater.sh
@@ -0,0 +1,368 @@
+# shellcheck shell=ash
+
+# Runtime updater for sing-box-extended and stock sing-box.
+# JSON parsing is done with jq (no ucode, no extra package deps).
+# This file is sourced from /usr/bin/podkop, so log() is available.
+
+SB_EXT_ARCH_SUFFIX=""
+UPDATES_SING_BOX_EXTENDED_REPO="shtorm-7/sing-box-extended"
+
+updates_log() {
+ local message="$1"
+ local level="${2:-info}"
+
+ log "Updater: $message" "$level"
+}
+
+# Returns 0 if the system uses musl libc.
+updates_system_uses_musl() {
+ ls /lib/ld-musl-*.so* >/dev/null 2>&1 && return 0
+
+ ldd --version 2>&1 | grep -qi 'musl'
+}
+
+# Reads a value from /etc/openwrt_release (e.g. DISTRIB_ARCH).
+updates_read_openwrt_release_value() {
+ local key="$1"
+
+ [ -f /etc/openwrt_release ] || return 0
+ sed -n "s/^${key}='\(.*\)'/\1/p" /etc/openwrt_release 2>/dev/null | head -n 1
+}
+
+# Resolves the sing-box-extended release asset arch suffix into SB_EXT_ARCH_SUFFIX.
+# Returns 1 if the architecture is unsupported.
+updates_resolve_sing_box_extended_arch_suffix() {
+ local host_arch distrib_arch
+
+ host_arch="$(uname -m 2>/dev/null || true)"
+ distrib_arch="$(updates_read_openwrt_release_value "DISTRIB_ARCH")"
+
+ case "$distrib_arch" in
+ *mipsel* | *mipsle*) host_arch="mipsel" ;;
+ *mips64el* | *mips64le*) host_arch="mips64el" ;;
+ esac
+
+ case "$host_arch" in
+ aarch64) SB_EXT_ARCH_SUFFIX="arm64" ;;
+ armv7*) SB_EXT_ARCH_SUFFIX="armv7" ;;
+ armv6*) SB_EXT_ARCH_SUFFIX="armv6" ;;
+ x86_64) SB_EXT_ARCH_SUFFIX="amd64" ;;
+ i386 | i686) SB_EXT_ARCH_SUFFIX="386" ;;
+ mips) SB_EXT_ARCH_SUFFIX="mips-softfloat" ;;
+ mipsel | mipsle) SB_EXT_ARCH_SUFFIX="mipsle-softfloat" ;;
+ mips64) SB_EXT_ARCH_SUFFIX="mips64" ;;
+ mips64el | mips64le) SB_EXT_ARCH_SUFFIX="mips64le" ;;
+ riscv64) SB_EXT_ARCH_SUFFIX="riscv64" ;;
+ s390x) SB_EXT_ARCH_SUFFIX="s390x" ;;
+ *) return 1 ;;
+ esac
+}
+
+# Fetches the sing-box-extended GitHub releases JSON (echoes to stdout).
+updates_fetch_sing_box_extended_releases() {
+ local url response
+ url="https://api.github.com/repos/${UPDATES_SING_BOX_EXTENDED_REPO}/releases?per_page=30"
+
+ if command -v curl >/dev/null 2>&1; then
+ response="$(curl -m 15 -sL "$url" 2>/dev/null)"
+ fi
+ if [ -z "$response" ] && command -v wget >/dev/null 2>&1; then
+ response="$(wget -q -O- "$url" 2>/dev/null)"
+ fi
+
+ [ -n "$response" ] || return 1
+ printf '%s' "$response"
+}
+
+# Picks the newest non-draft, non-prerelease, stable (no alpha/beta/rc) tag.
+updates_extended_release_tag() {
+ local json="$1"
+
+ printf '%s' "$json" | jq -r '
+ map(select((.draft != true) and (.prerelease != true)))
+ | map(.tag_name)
+ | map(select(. != null and . != ""))
+ | map(select((ascii_downcase | test("alpha|beta|rc")) | not))
+ | .[0] // empty
+ ' 2>/dev/null
+}
+
+# Extracts the release object matching the given tag.
+updates_extended_release_object() {
+ local json="$1"
+ local tag="$2"
+
+ printf '%s' "$json" | jq -c --arg t "$tag" '
+ map(select((.draft != true) and (.prerelease != true) and (.tag_name == $t)))
+ | .[0] // empty
+ ' 2>/dev/null
+}
+
+# Resolves the download URL for the matching asset of a release object.
+updates_extended_asset_url() {
+ local rel="$1"
+ local suffix url
+
+ if updates_system_uses_musl; then
+ suffix="linux-${SB_EXT_ARCH_SUFFIX}-musl.tar.gz"
+ url="$(printf '%s' "$rel" | jq -r --arg s "$suffix" '
+ .assets // []
+ | map(select(.name != null and (.name | endswith($s))))
+ | .[0].browser_download_url // empty
+ ' 2>/dev/null)"
+ if [ -n "$url" ]; then
+ printf '%s' "$url"
+ return 0
+ fi
+ fi
+
+ suffix="linux-${SB_EXT_ARCH_SUFFIX}.tar.gz"
+ url="$(printf '%s' "$rel" | jq -r --arg s "$suffix" '
+ .assets // []
+ | map(select(.name != null and (.name | endswith($s))))
+ | .[0].browser_download_url // empty
+ ' 2>/dev/null)"
+ if [ -n "$url" ]; then
+ printf '%s' "$url"
+ return 0
+ fi
+
+ return 1
+}
+
+# Downloads a URL to a file path (curl, fall back to wget). Returns 0 on success.
+updates_download_to_file() {
+ local url="$1"
+ local dest="$2"
+
+ if command -v curl >/dev/null 2>&1; then
+ curl -m 120 -fsSL "$url" -o "$dest" && [ -s "$dest" ] && return 0
+ fi
+ if command -v wget >/dev/null 2>&1; then
+ wget -q -O "$dest" "$url" && [ -s "$dest" ] && return 0
+ fi
+
+ return 1
+}
+
+# Restarts podkop if its init script is present (best-effort).
+updates_restart_podkop() {
+ if [ -x /etc/init.d/podkop ]; then
+ updates_log "Restarting podkop after component change"
+ /etc/init.d/podkop restart >/dev/null 2>&1 || true
+ fi
+}
+
+# Downloads and installs sing-box-extended, replacing /usr/bin/sing-box.
+# Echoes a JSON result on stdout.
+updates_install_sing_box_extended() {
+ local tmp_dir archive releases tag rel asset_url
+ local binary_path cronet_path
+ local backup_binary backup_cronet new_version
+
+ if ! updates_resolve_sing_box_extended_arch_suffix; then
+ updates_log "Unsupported architecture for sing-box-extended" "error"
+ echo "{\"success\":false,\"message\":\"Unsupported architecture for sing-box-extended\"}"
+ return 1
+ fi
+
+ releases="$(updates_fetch_sing_box_extended_releases)"
+ if [ -z "$releases" ]; then
+ updates_log "Failed to fetch sing-box-extended releases" "error"
+ echo "{\"success\":false,\"message\":\"Failed to fetch sing-box-extended releases\"}"
+ return 1
+ fi
+
+ tag="$(updates_extended_release_tag "$releases")"
+ if [ -z "$tag" ]; then
+ updates_log "Failed to resolve sing-box-extended release tag" "error"
+ echo "{\"success\":false,\"message\":\"Failed to resolve sing-box-extended release tag\"}"
+ return 1
+ fi
+
+ rel="$(updates_extended_release_object "$releases" "$tag")"
+ asset_url="$(updates_extended_asset_url "$rel")"
+ if [ -z "$asset_url" ]; then
+ updates_log "Failed to resolve sing-box-extended asset for arch $SB_EXT_ARCH_SUFFIX" "error"
+ echo "{\"success\":false,\"message\":\"Failed to resolve sing-box-extended asset\"}"
+ return 1
+ fi
+
+ tmp_dir="$(mktemp -d /tmp/podkop-sbext.XXXXXX 2>/dev/null)"
+ if [ -z "$tmp_dir" ]; then
+ updates_log "Failed to create temporary directory" "error"
+ echo "{\"success\":false,\"message\":\"Failed to create temporary directory\"}"
+ return 1
+ fi
+
+ archive="$tmp_dir/sing-box-extended.tar.gz"
+ updates_log "Downloading sing-box-extended $tag ($SB_EXT_ARCH_SUFFIX)"
+ if ! updates_download_to_file "$asset_url" "$archive"; then
+ rm -rf "$tmp_dir"
+ updates_log "Failed to download sing-box-extended" "error"
+ echo "{\"success\":false,\"message\":\"Failed to download sing-box-extended\"}"
+ return 1
+ fi
+
+ binary_path="$(tar -tzf "$archive" 2>/dev/null | grep -E '(^|/)sing-box$' | sed -n '1p')"
+ if [ -z "$binary_path" ]; then
+ rm -rf "$tmp_dir"
+ updates_log "sing-box binary not found in archive" "error"
+ echo "{\"success\":false,\"message\":\"sing-box binary not found in archive\"}"
+ return 1
+ fi
+ cronet_path="$(tar -tzf "$archive" 2>/dev/null | grep -E '(^|/)libcronet\.so$' | sed -n '1p')"
+
+ backup_binary=""
+ if [ -e /usr/bin/sing-box ]; then
+ backup_binary="$tmp_dir/sing-box.backup"
+ if ! cp -p /usr/bin/sing-box "$backup_binary"; then
+ rm -rf "$tmp_dir"
+ updates_log "Failed to backup current sing-box binary" "error"
+ echo "{\"success\":false,\"message\":\"Failed to backup current sing-box binary\"}"
+ return 1
+ fi
+ rm -f /usr/bin/sing-box
+ fi
+
+ backup_cronet=""
+ if [ -n "$cronet_path" ] && [ -e /usr/lib/libcronet.so ]; then
+ backup_cronet="$tmp_dir/libcronet.so.backup"
+ if ! cp -p /usr/lib/libcronet.so "$backup_cronet"; then
+ [ -n "$backup_binary" ] && mv -f "$backup_binary" /usr/bin/sing-box
+ rm -rf "$tmp_dir"
+ updates_log "Failed to backup current libcronet.so" "error"
+ echo "{\"success\":false,\"message\":\"Failed to backup current libcronet.so\"}"
+ return 1
+ fi
+ rm -f /usr/lib/libcronet.so
+ fi
+
+ if ! tar -xzf "$archive" -O "$binary_path" > /usr/bin/sing-box 2>/dev/null || [ ! -s /usr/bin/sing-box ]; then
+ rm -f /usr/bin/sing-box
+ [ -n "$backup_binary" ] && mv -f "$backup_binary" /usr/bin/sing-box
+ [ -n "$backup_cronet" ] && mv -f "$backup_cronet" /usr/lib/libcronet.so
+ rm -rf "$tmp_dir"
+ updates_log "Failed to extract sing-box-extended binary" "error"
+ echo "{\"success\":false,\"message\":\"Failed to extract sing-box-extended binary\"}"
+ return 1
+ fi
+ chmod 0755 /usr/bin/sing-box
+
+ if [ -n "$cronet_path" ]; then
+ if ! tar -xzf "$archive" -O "$cronet_path" > /usr/lib/libcronet.so 2>/dev/null || [ ! -s /usr/lib/libcronet.so ]; then
+ rm -f /usr/bin/sing-box /usr/lib/libcronet.so
+ [ -n "$backup_binary" ] && mv -f "$backup_binary" /usr/bin/sing-box
+ [ -n "$backup_cronet" ] && mv -f "$backup_cronet" /usr/lib/libcronet.so
+ rm -rf "$tmp_dir"
+ updates_log "Failed to extract libcronet.so" "error"
+ echo "{\"success\":false,\"message\":\"Failed to extract libcronet.so\"}"
+ return 1
+ fi
+ chmod 0644 /usr/lib/libcronet.so
+ fi
+
+ new_version="$(LD_LIBRARY_PATH=/usr/lib /usr/bin/sing-box version 2>/dev/null | head -1 | awk '{print $NF}')"
+ case "$new_version" in
+ *extended*) ;;
+ *)
+ rm -f /usr/bin/sing-box
+ [ -n "$backup_binary" ] && mv -f "$backup_binary" /usr/bin/sing-box
+ [ -n "$cronet_path" ] && rm -f /usr/lib/libcronet.so
+ [ -n "$backup_cronet" ] && mv -f "$backup_cronet" /usr/lib/libcronet.so
+ rm -rf "$tmp_dir"
+ updates_log "Installed sing-box failed extended validation; previous binary restored" "error"
+ echo "{\"success\":false,\"message\":\"Installed sing-box failed extended validation; previous binary restored\"}"
+ return 1
+ ;;
+ esac
+
+ rm -f "$backup_binary" "$backup_cronet"
+ rm -rf "$tmp_dir"
+ updates_restart_podkop
+ updates_log "Installed sing-box-extended $new_version"
+ echo "{\"success\":true,\"version\":\"$new_version\"}"
+ return 0
+}
+
+# Reinstalls the stock (stable) sing-box via the system package manager.
+# Echoes a JSON result on stdout.
+updates_install_sing_box_stable() {
+ local new_version
+
+ if command -v apk >/dev/null 2>&1; then
+ updates_log "Updating apk package lists"
+ apk update /dev/null 2>&1 || true
+ updates_log "Installing stable sing-box via apk"
+ if ! apk add --allow-downgrade sing-box /dev/null 2>&1; then
+ apk fix sing-box /dev/null 2>&1 || true
+ fi
+ elif command -v opkg >/dev/null 2>&1; then
+ updates_log "Updating opkg package lists"
+ opkg update /dev/null 2>&1 || true
+ updates_log "Installing stable sing-box via opkg"
+ if ! opkg install --force-reinstall --force-downgrade sing-box /dev/null 2>&1; then
+ opkg install --force-downgrade sing-box /dev/null 2>&1 || true
+ fi
+ else
+ updates_log "No supported package manager (apk/opkg) found" "error"
+ echo "{\"success\":false,\"message\":\"No supported package manager found\"}"
+ return 1
+ fi
+
+ updates_restart_podkop
+ new_version="$(get_sing_box_version)"
+ updates_log "Stable sing-box installed: ${new_version:-unknown}"
+ echo "{\"success\":true,\"version\":\"$new_version\"}"
+ return 0
+}
+
+# Checks whether a newer sing-box-extended release is available.
+# Echoes a JSON status (latest|outdated) on stdout.
+updates_check_sing_box_extended() {
+ local current_version releases tag status
+
+ current_version="$(get_sing_box_version)"
+
+ releases="$(updates_fetch_sing_box_extended_releases)"
+ if [ -z "$releases" ]; then
+ echo "{\"success\":false,\"message\":\"Failed to fetch sing-box-extended releases\"}"
+ return 1
+ fi
+
+ tag="$(updates_extended_release_tag "$releases")"
+ if [ -z "$tag" ]; then
+ echo "{\"success\":false,\"message\":\"Failed to resolve sing-box-extended release tag\"}"
+ return 1
+ fi
+
+ status="outdated"
+ case "$current_version" in
+ *"$tag"*) status="latest" ;;
+ esac
+
+ echo "{\"success\":true,\"current_version\":\"$current_version\",\"latest_version\":\"$tag\",\"status\":\"$status\"}"
+ return 0
+}
+
+# Dispatcher for component-related actions.
+component_action() {
+ local component="$1"
+ local action="$2"
+
+ case "$component:$action" in
+ sing_box:install_extended)
+ updates_install_sing_box_extended
+ ;;
+ sing_box:install_stable)
+ updates_install_sing_box_stable
+ ;;
+ sing_box:check_update)
+ updates_check_sing_box_extended
+ ;;
+ *)
+ echo '{"success":false,"message":"Unknown component action"}'
+ return 1
+ ;;
+ esac
+}
From 897b93664be5f9eca67c346c903009a52e92e73d Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Tue, 2 Jun 2026 17:34:44 +0300
Subject: [PATCH 20/75] rebrand: podkop -> NetShift (v0.8.0) with migration
Full project rebrand to reduce association with upstream podkop, per
upstream maintainers' request.
Renames (git mv, history preserved):
- package podkop -> netshift (/usr/bin/netshift, /etc/config/netshift,
/etc/init.d/netshift, /etc/netshift state dir, /usr/lib/netshift)
- luci-app-podkop -> luci-app-netshift; view namespace view/podkop -> view/netshift
- fe-app-podkop -> fe-app-netshift; TS namespace Podkop -> NetShift
- nft table PodkopTable -> NetShiftTable; rt_table podkop -> netshift
- nft sets, dnsmasq backup keys, cron self-calls, PID/tmp paths
- i18n podkop.pot/po -> netshift.pot/po; web title Podkop -> NetShift
- build-arg PODKOP_VERSION -> NETSHIFT_VERSION; Dockerfiles + CI workflows
Migration (install.sh migrate_from_podkop): on detecting an old podkop
install, stops the old service (clean teardown of nft/dnsmasq/rt_tables),
copies /etc/config/podkop -> /etc/config/netshift (backup at
/etc/config/podkop.bak.pre-netshift), migrates state dir, strips old
cron/rt_tables entries, removes old packages, then installs NetShift.
Config schema is compatible so the VPN keeps working after migration.
Docs: README + TRADEMARK rewritten for NetShift.
Intentionally kept: upstream attribution (itdoginfo/podkop), podkop.net
docs links, *.podkop.fyi check domains, yandexru45/podkop-evolution repo
URL (GitHub repo not renamed yet), maintainer email.
Verified: bash -n clean, yarn lint/test (283) pass, main.js idempotent,
Docker ipk build produces netshift_v0.8.0 / luci-app-netshift_v0.8.0.
---
.github/ISSUE_TEMPLATE/bug_report.yml | 6 +-
.github/ISSUE_TEMPLATE/config.yml | 4 +-
.github/ISSUE_TEMPLATE/feature_request.yml | 6 +-
.github/workflows/build.yml | 14 +-
.github/workflows/frontend-ci.yml | 8 +-
.github/workflows/shellcheck.yml | 12 +-
.gitignore | 4 +-
Dockerfile-apk | 12 +-
Dockerfile-ipk | 12 +-
README.md | 62 +-
TRADEMARK.md | 38 +-
TRADEMARK_RU.md | 38 +-
fe-app-netshift/.env.example | 16 +
.../.prettierrc | 0
.../distribute-locales.js | 10 +-
.../eslint.config.js | 0
.../extract-calls.js | 4 +-
.../generate-po.js | 10 +-
.../generate-pot.js | 4 +-
.../locales/calls.json | 596 ++++-----
fe-app-netshift/locales/netshift.pot | 1183 +++++++++++++++++
.../locales/netshift.ru.po | 50 +-
.../package.json | 4 +-
.../src/constants.ts | 2 +-
.../src/helpers/copyToClipboard.ts | 0
.../src/helpers/downloadAsTxt.ts | 0
.../src/helpers/executeShellCommand.ts | 0
.../src/helpers/getClashApiUrl.ts | 0
.../src/helpers/getProxyUrlName.ts | 0
.../src/helpers/index.ts | 0
.../src/helpers/injectGlobalStyles.ts | 0
.../src/helpers/insertIf.ts | 0
.../src/helpers/maskIP.ts | 0
.../src/helpers/normalizeCompiledVersion.ts | 0
.../src/helpers/onMount.ts | 0
.../src/helpers/parseQueryString.ts | 0
.../src/helpers/parseValueList.ts | 0
.../src/helpers/preserveScrollForPage.ts | 0
.../src/helpers/prettyBytes.ts | 0
.../src/helpers/removeVersionPrefix.ts | 0
.../src/helpers/showToast.ts | 0
.../src/helpers/splitProxyString.ts | 0
.../src/helpers/svgEl.ts | 0
.../src/helpers/tests/maskIp.test.js | 0
.../src/helpers/withTimeout.ts | 2 +-
.../src/icons/index.ts | 0
.../src/icons/renderBookOpenTextIcon24.ts | 0
.../src/icons/renderCheckIcon24.ts | 0
.../src/icons/renderCircleAlertIcon24.ts | 0
.../src/icons/renderCircleCheckBigIcon24.ts | 0
.../src/icons/renderCircleCheckIcon24.ts | 0
.../src/icons/renderCirclePlayIcon24.ts | 0
.../src/icons/renderCircleSlashIcon24.ts | 0
.../src/icons/renderCircleStopIcon24.ts | 0
.../src/icons/renderCircleXIcon24.ts | 0
.../src/icons/renderCogIcon24.ts | 0
.../src/icons/renderLoaderCircleIcon24.ts | 0
.../src/icons/renderPauseIcon24.ts | 0
.../src/icons/renderPlayIcon24.ts | 0
.../src/icons/renderRotateCcwIcon24.ts | 0
.../src/icons/renderSearchIcon24.ts | 0
.../src/icons/renderSquareChartGanttIcon24.ts | 0
.../src/icons/renderTriangleAlertIcon24.ts | 0
.../src/icons/renderXIcon24.ts | 0
.../src/luci.d.ts | 0
.../src/main.ts | 2 +-
.../src/netshift}/api.ts | 0
.../netshift/fetchers/fetchServicesInfo.ts | 32 +
.../src/netshift}/fetchers/index.ts | 0
.../src/netshift}/index.ts | 0
.../methods/custom/getClashApiSecret.ts | 0
.../methods/custom/getConfigSections.ts | 5 +
.../methods/custom/getDashboardSections.ts | 8 +-
.../src/netshift}/methods/custom/index.ts | 2 +-
.../methods/fakeip/getFakeIpCheck.ts | 0
.../netshift}/methods/fakeip/getIpCheck.ts | 0
.../src/netshift}/methods/fakeip/index.ts | 0
.../src/netshift}/methods/index.ts | 0
.../netshift}/methods/shell/callBaseMethod.ts | 8 +-
.../src/netshift/methods/shell/index.ts | 129 ++
.../src/netshift}/services/core.service.ts | 10 +-
.../src/netshift}/services/index.ts | 0
.../src/netshift}/services/logger.service.ts | 0
.../services/netshiftLogWatcher.service.ts | 36 +-
.../src/netshift}/services/socket.service.ts | 0
.../src/netshift}/services/store.service.ts | 12 +-
.../src/netshift}/services/tab.service.ts | 0
.../src/netshift}/tabs/dashboard/index.ts | 0
.../tabs/dashboard/initController.ts | 16 +-
.../tabs/dashboard/partials/index.ts | 0
.../tabs/dashboard/partials/renderSections.ts | 6 +-
.../tabs/dashboard/partials/renderWidget.ts | 0
.../src/netshift}/tabs/dashboard/render.ts | 0
.../src/netshift}/tabs/dashboard/styles.ts | 4 +-
.../tabs/diagnostic/checks/contstants.ts | 0
.../tabs/diagnostic/checks/runDnsCheck.ts | 4 +-
.../tabs/diagnostic/checks/runFakeIPCheck.ts | 4 +-
.../tabs/diagnostic/checks/runNftCheck.ts | 4 +-
.../diagnostic/checks/runSectionsCheck.ts | 9 +-
.../tabs/diagnostic/checks/runSingBoxCheck.ts | 4 +-
.../diagnostic/checks/updateCheckStore.ts | 0
.../tabs/diagnostic/diagnostic.store.ts | 4 +-
.../tabs/diagnostic/helpers/getCheckTitle.ts | 0
.../tabs/diagnostic/helpers/getMeta.ts | 0
.../helpers/getNetshiftVersionRow.ts | 18 +-
.../src/netshift}/tabs/diagnostic/index.ts | 0
.../tabs/diagnostic/initController.ts | 40 +-
.../tabs/diagnostic/partials/index.ts | 0
.../partials/renderAvailableActions.ts | 6 +-
.../diagnostic/partials/renderCheckSection.ts | 0
.../diagnostic/partials/renderRunAction.ts | 0
.../diagnostic/partials/renderSystemInfo.ts | 0
.../partials/renderWikiDisclaimer.ts | 2 +-
.../tabs/diagnostic/renderDiagnostic.ts | 0
.../src/netshift}/tabs/diagnostic/styles.ts | 4 +-
.../tests/getNetshiftVersionRow.test.ts | 30 +-
.../src/netshift}/tabs/index.ts | 0
.../src/netshift}/types.ts | 28 +-
.../src/partials/button/renderButton.ts | 0
.../src/partials/button/styles.ts | 0
.../src/partials/index.ts | 0
.../src/partials/modal/renderModal.ts | 0
.../src/partials/modal/styles.ts | 0
.../src/styles.ts | 8 +-
.../src/validators/bulkValidate.ts | 0
.../src/validators/index.ts | 0
.../src/validators/tests/validateDns.test.js | 0
.../validators/tests/validateDomain.test.js | 0
.../tests/validateHysteriaUrl.test.js | 0
.../src/validators/tests/validateIp.test.js | 0
.../src/validators/tests/validatePath.test.js | 0
.../tests/validateShadowsocksUrl.test.js | 0
.../validators/tests/validateSocksUrl.test.js | 0
.../validators/tests/validateSubnet.test.js | 0
.../tests/validateTrojanUrl.test.js | 0
.../src/validators/tests/validateUrl.test.js | 0
.../validators/tests/validateVlessUrl.test.js | 0
.../src/validators/types.ts | 0
.../src/validators/validateDns.ts | 0
.../src/validators/validateDomain.ts | 0
.../src/validators/validateHysteriaUrl.ts | 0
.../src/validators/validateIp.ts | 0
.../src/validators/validateOutboundJson.ts | 0
.../src/validators/validatePath.ts | 0
.../src/validators/validateProxyUrl.ts | 0
.../src/validators/validateShadowsocksUrl.ts | 0
.../src/validators/validateSocksUrl.ts | 0
.../src/validators/validateSubnet.ts | 0
.../src/validators/validateTrojanUrl.ts | 0
.../src/validators/validateUrl.ts | 0
.../src/validators/validateVlessUrl.ts | 0
.../tests/setup/global-mocks.ts | 0
.../tsconfig.json | 0
.../tsup.config.ts | 4 +-
.../vitest.config.js | 0
.../watch-upload.js | 12 +-
{fe-app-podkop => fe-app-netshift}/yarn.lock | 0
fe-app-podkop/.env.example | 16 -
fe-app-podkop/locales/podkop.pot | 1183 -----------------
.../src/podkop/fetchers/fetchServicesInfo.ts | 29 -
.../methods/custom/getConfigSections.ts | 5 -
.../src/podkop/methods/shell/index.ts | 129 --
install.sh | 204 ++-
.../Makefile | 10 +-
.../resources/view/netshift}/dashboard.js | 2 +-
.../resources/view/netshift}/diagnostic.js | 2 +-
.../resources/view/netshift}/main.js | 333 ++---
.../resources/view/netshift/netshift.js | 30 +-
.../resources/view/netshift}/section.js | 2 +-
.../resources/view/netshift}/settings.js | 10 +-
.../msgmerge.sh | 4 +-
.../po/ru/netshift.po | 50 +-
luci-app-netshift/po/templates/netshift.pot | 1183 +++++++++++++++++
.../root/etc/uci-defaults/50_luci-netshift | 2 +-
.../share/luci/menu.d/luci-app-netshift.json | 14 +
.../share/rpcd/acl.d/luci-app-netshift.json | 14 +-
.../xgettext.sh | 6 +-
luci-app-podkop/po/templates/podkop.pot | 1183 -----------------
.../share/luci/menu.d/luci-app-podkop.json | 14 -
netshift/Makefile | 64 +
.../files/etc/config/netshift | 0
.../files/etc/init.d/netshift | 10 +-
.../podkop => netshift/files/usr/bin/netshift | 220 +--
.../files/usr/lib/constants.sh | 16 +-
{podkop => netshift}/files/usr/lib/helpers.jq | 0
{podkop => netshift}/files/usr/lib/helpers.sh | 4 +-
{podkop => netshift}/files/usr/lib/logging.sh | 2 +-
{podkop => netshift}/files/usr/lib/nft.sh | 0
.../files/usr/lib/rulesets.sh | 0
.../files/usr/lib/sing_box_config_facade.sh | 6 +-
.../files/usr/lib/sing_box_config_manager.sh | 10 +-
{podkop => netshift}/files/usr/lib/updater.sh | 18 +-
podkop/Makefile | 64 -
193 files changed, 3775 insertions(+), 3612 deletions(-)
create mode 100644 fe-app-netshift/.env.example
rename {fe-app-podkop => fe-app-netshift}/.prettierrc (100%)
rename {fe-app-podkop => fe-app-netshift}/distribute-locales.js (80%)
rename {fe-app-podkop => fe-app-netshift}/eslint.config.js (100%)
rename {fe-app-podkop => fe-app-netshift}/extract-calls.js (93%)
rename {fe-app-podkop => fe-app-netshift}/generate-po.js (92%)
rename {fe-app-podkop => fe-app-netshift}/generate-pot.js (96%)
rename {fe-app-podkop => fe-app-netshift}/locales/calls.json (61%)
create mode 100644 fe-app-netshift/locales/netshift.pot
rename luci-app-podkop/po/ru/podkop.po => fe-app-netshift/locales/netshift.ru.po (96%)
rename {fe-app-podkop => fe-app-netshift}/package.json (90%)
rename {fe-app-podkop => fe-app-netshift}/src/constants.ts (97%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/copyToClipboard.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/downloadAsTxt.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/executeShellCommand.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/getClashApiUrl.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/getProxyUrlName.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/index.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/injectGlobalStyles.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/insertIf.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/maskIP.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/normalizeCompiledVersion.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/onMount.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/parseQueryString.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/parseValueList.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/preserveScrollForPage.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/prettyBytes.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/removeVersionPrefix.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/showToast.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/splitProxyString.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/svgEl.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/tests/maskIp.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/helpers/withTimeout.ts (94%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/index.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderBookOpenTextIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderCheckIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderCircleAlertIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderCircleCheckBigIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderCircleCheckIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderCirclePlayIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderCircleSlashIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderCircleStopIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderCircleXIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderCogIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderLoaderCircleIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderPauseIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderPlayIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderRotateCcwIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderSearchIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderSquareChartGanttIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderTriangleAlertIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/icons/renderXIcon24.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/luci.d.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/main.ts (90%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/api.ts (100%)
create mode 100644 fe-app-netshift/src/netshift/fetchers/fetchServicesInfo.ts
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/fetchers/index.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/index.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/methods/custom/getClashApiSecret.ts (100%)
create mode 100644 fe-app-netshift/src/netshift/methods/custom/getConfigSections.ts
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/methods/custom/getDashboardSections.ts (97%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/methods/custom/index.ts (86%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/methods/fakeip/getFakeIpCheck.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/methods/fakeip/getIpCheck.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/methods/fakeip/index.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/methods/index.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/methods/shell/callBaseMethod.ts (77%)
create mode 100644 fe-app-netshift/src/netshift/methods/shell/index.ts
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/services/core.service.ts (71%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/services/index.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/services/logger.service.ts (100%)
rename fe-app-podkop/src/podkop/services/podkopLogWatcher.service.ts => fe-app-netshift/src/netshift/services/netshiftLogWatcher.service.ts (68%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/services/socket.service.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/services/store.service.ts (95%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/services/tab.service.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/dashboard/index.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/dashboard/initController.ts (95%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/dashboard/partials/index.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/dashboard/partials/renderSections.ts (96%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/dashboard/partials/renderWidget.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/dashboard/render.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/dashboard/styles.ts (97%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/checks/contstants.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/checks/runDnsCheck.ts (94%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/checks/runFakeIPCheck.ts (93%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/checks/runNftCheck.ts (95%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/checks/runSectionsCheck.ts (92%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/checks/runSingBoxCheck.ts (95%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/checks/updateCheckStore.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/diagnostic.store.ts (97%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/helpers/getCheckTitle.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/helpers/getMeta.ts (100%)
rename fe-app-podkop/src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts => fe-app-netshift/src/netshift/tabs/diagnostic/helpers/getNetshiftVersionRow.ts (71%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/index.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/initController.ts (93%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/partials/index.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/partials/renderAvailableActions.ts (97%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/partials/renderCheckSection.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/partials/renderRunAction.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/partials/renderSystemInfo.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/partials/renderWikiDisclaimer.ts (94%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/renderDiagnostic.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/diagnostic/styles.ts (98%)
rename fe-app-podkop/src/podkop/tabs/diagnostic/tests/getPodkopVersionRow.test.ts => fe-app-netshift/src/netshift/tabs/diagnostic/tests/getNetshiftVersionRow.test.ts (66%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/tabs/index.ts (100%)
rename {fe-app-podkop/src/podkop => fe-app-netshift/src/netshift}/types.ts (88%)
rename {fe-app-podkop => fe-app-netshift}/src/partials/button/renderButton.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/partials/button/styles.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/partials/index.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/partials/modal/renderModal.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/partials/modal/styles.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/styles.ts (91%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/bulkValidate.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/index.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/tests/validateDns.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/tests/validateDomain.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/tests/validateHysteriaUrl.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/tests/validateIp.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/tests/validatePath.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/tests/validateShadowsocksUrl.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/tests/validateSocksUrl.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/tests/validateSubnet.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/tests/validateTrojanUrl.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/tests/validateUrl.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/tests/validateVlessUrl.test.js (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/types.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateDns.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateDomain.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateHysteriaUrl.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateIp.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateOutboundJson.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validatePath.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateProxyUrl.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateShadowsocksUrl.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateSocksUrl.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateSubnet.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateTrojanUrl.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateUrl.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/src/validators/validateVlessUrl.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/tests/setup/global-mocks.ts (100%)
rename {fe-app-podkop => fe-app-netshift}/tsconfig.json (100%)
rename {fe-app-podkop => fe-app-netshift}/tsup.config.ts (85%)
rename {fe-app-podkop => fe-app-netshift}/vitest.config.js (100%)
rename {fe-app-podkop => fe-app-netshift}/watch-upload.js (87%)
rename {fe-app-podkop => fe-app-netshift}/yarn.lock (100%)
delete mode 100644 fe-app-podkop/.env.example
delete mode 100644 fe-app-podkop/locales/podkop.pot
delete mode 100644 fe-app-podkop/src/podkop/fetchers/fetchServicesInfo.ts
delete mode 100644 fe-app-podkop/src/podkop/methods/custom/getConfigSections.ts
delete mode 100644 fe-app-podkop/src/podkop/methods/shell/index.ts
rename {luci-app-podkop => luci-app-netshift}/Makefile (69%)
rename {luci-app-podkop/htdocs/luci-static/resources/view/podkop => luci-app-netshift/htdocs/luci-static/resources/view/netshift}/dashboard.js (91%)
rename {luci-app-podkop/htdocs/luci-static/resources/view/podkop => luci-app-netshift/htdocs/luci-static/resources/view/netshift}/diagnostic.js (91%)
rename {luci-app-podkop/htdocs/luci-static/resources/view/podkop => luci-app-netshift/htdocs/luci-static/resources/view/netshift}/main.js (93%)
rename luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js => luci-app-netshift/htdocs/luci-static/resources/view/netshift/netshift.js (73%)
rename {luci-app-podkop/htdocs/luci-static/resources/view/podkop => luci-app-netshift/htdocs/luci-static/resources/view/netshift}/section.js (99%)
rename {luci-app-podkop/htdocs/luci-static/resources/view/podkop => luci-app-netshift/htdocs/luci-static/resources/view/netshift}/settings.js (97%)
rename {luci-app-podkop => luci-app-netshift}/msgmerge.sh (89%)
rename fe-app-podkop/locales/podkop.ru.po => luci-app-netshift/po/ru/netshift.po (96%)
create mode 100644 luci-app-netshift/po/templates/netshift.pot
rename luci-app-podkop/root/etc/uci-defaults/50_luci-podkop => luci-app-netshift/root/etc/uci-defaults/50_luci-netshift (66%)
create mode 100644 luci-app-netshift/root/usr/share/luci/menu.d/luci-app-netshift.json
rename luci-app-podkop/root/usr/share/rpcd/acl.d/luci-app-podkop.json => luci-app-netshift/root/usr/share/rpcd/acl.d/luci-app-netshift.json (55%)
rename {luci-app-podkop => luci-app-netshift}/xgettext.sh (82%)
delete mode 100644 luci-app-podkop/po/templates/podkop.pot
delete mode 100644 luci-app-podkop/root/usr/share/luci/menu.d/luci-app-podkop.json
create mode 100644 netshift/Makefile
rename podkop/files/etc/config/podkop => netshift/files/etc/config/netshift (100%)
rename podkop/files/etc/init.d/podkop => netshift/files/etc/init.d/netshift (88%)
rename podkop/files/usr/bin/podkop => netshift/files/usr/bin/netshift (95%)
rename {podkop => netshift}/files/usr/lib/constants.sh (89%)
rename {podkop => netshift}/files/usr/lib/helpers.jq (100%)
rename {podkop => netshift}/files/usr/lib/helpers.sh (99%)
rename {podkop => netshift}/files/usr/lib/logging.sh (92%)
rename {podkop => netshift}/files/usr/lib/nft.sh (100%)
rename {podkop => netshift}/files/usr/lib/rulesets.sh (100%)
rename {podkop => netshift}/files/usr/lib/sing_box_config_facade.sh (99%)
rename {podkop => netshift}/files/usr/lib/sing_box_config_manager.sh (99%)
rename {podkop => netshift}/files/usr/lib/updater.sh (96%)
delete mode 100644 podkop/Makefile
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index d5df5cc7..e68028f6 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -11,7 +11,7 @@ body:
Спасибо за создание отчета об ошибке!
Перед отправкой, пожалуйста:
- - Проверьте [существующие issues](https://github.com/itdoginfo/podkop/issues)
+ - Проверьте [существующие issues](https://github.com/yandexru45/podkop-evolution/issues)
- Просмотрите [документацию](https://podkop.net)
- type: textarea
@@ -53,7 +53,7 @@ body:
Информация о вашей системе (заполните всё применимое)
value: |
- **OpenWrt версия**:
- - **Podkop версия**:
+ - **NetShift версия**:
- **Роутер модель**:
- **Sing-box версия**:
render: markdown
@@ -68,7 +68,7 @@ body:
Релевантные части конфигурации (удалите чувствительную информацию!)
placeholder: |
Например:
- - Содержимое /etc/config/podkop
+ - Содержимое /etc/config/netshift
- Конфигурация sing-box (если релевантно)
- Дополнительные конфиги, которые потребуются wireless/network/dhcp и т.д.
render: shell
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 8b68badd..578d5bb3 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 💬 Если у вас что-то не работает, прежде всего прочитайте README проекта
- url: https://github.com/itdoginfo/podkop
+ url: https://github.com/yandexru45/podkop-evolution
about: README проекта
- name: 📚 Если вы не нашли в README документацию, то вот ссылка на неё
url: https://podkop.net
- about: Официальная документация PodKop
\ No newline at end of file
+ about: Официальная документация NetShift
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index d2d15f2a..b9196b95 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -1,6 +1,6 @@
---
name: ✨ Запрос новой функции
-description: Предложите новую функцию или улучшение для Podkop
+description: Предложите новую функцию или улучшение для NetShift
title: "[FEATURE] "
labels: ["enhancement", "needs-discussion"]
assignees: []
@@ -11,7 +11,7 @@ body:
Спасибо за предложение новой функции!
Перед отправкой, пожалуйста:
- - Проверьте [существующие запросы](https://github.com/itdoginfo/podkop/issues?q=is%3Aissue+label%3Aenhancement)
+ - Проверьте [существующие запросы](https://github.com/yandexru45/podkop-evolution/issues?q=is%3Aissue+label%3Aenhancement)
- Убедитесь, что функции не существует в [документации](https://podkop.net)
- type: textarea
@@ -40,7 +40,7 @@ body:
label: 💡 Предлагаемое решение
description: Четкое и краткое описание того, что вы хотите реализовать
placeholder: |
- Я хочу, чтобы Podkop мог [...]
+ Я хочу, чтобы NetShift мог [...]
Предлагаю добавить функцию, которая [...]
Можно было бы улучшить [...] путем [...]
validations:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d754a949..fc3c705d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -23,7 +23,7 @@ jobs:
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
build:
- name: Builder for ${{ matrix.package_type }} podkop and luci-app-podkop
+ name: Builder for ${{ matrix.package_type }} netshift and luci-app-netshift
runs-on: ubuntu-latest
needs: preparation
strategy:
@@ -41,12 +41,12 @@ jobs:
with:
file: ./Dockerfile-${{ matrix.package_type }}
context: .
- tags: podkop:ci-${{ matrix.package_type }}
+ tags: netshift:ci-${{ matrix.package_type }}
build-args: |
- PODKOP_VERSION=${{ needs.preparation.outputs.version }}
+ NETSHIFT_VERSION=${{ needs.preparation.outputs.version }}
- name: Create ${{ matrix.package_type }} Docker container
- run: docker create --name ${{ matrix.package_type }} podkop:ci-${{ matrix.package_type }}
+ run: docker create --name ${{ matrix.package_type }} netshift:ci-${{ matrix.package_type }}
- name: Copy files from ${{ matrix.package_type }} Docker container
run: |
@@ -73,9 +73,9 @@ jobs:
VERSION="${{ needs.preparation.outputs.version }}"
mkdir -p ./filtered-bin/${{ matrix.package_type }}
- cp ./bin/${{ matrix.package_type }}/luci-i18n-podkop-ru-*.${{ matrix.package_type }} "./filtered-bin/${{ matrix.package_type }}/luci-i18n-podkop-ru-${VERSION}.${{ matrix.package_type }}"
- cp ./bin/${{ matrix.package_type }}/podkop-*.${{ matrix.package_type }} ./filtered-bin/${{ matrix.package_type }}/
- cp ./bin/${{ matrix.package_type }}/luci-app-podkop-*.${{ matrix.package_type }} ./filtered-bin/${{ matrix.package_type }}/
+ cp ./bin/${{ matrix.package_type }}/luci-i18n-netshift-ru-*.${{ matrix.package_type }} "./filtered-bin/${{ matrix.package_type }}/luci-i18n-netshift-ru-${VERSION}.${{ matrix.package_type }}"
+ cp ./bin/${{ matrix.package_type }}/netshift-*.${{ matrix.package_type }} ./filtered-bin/${{ matrix.package_type }}/
+ cp ./bin/${{ matrix.package_type }}/luci-app-netshift-*.${{ matrix.package_type }} ./filtered-bin/${{ matrix.package_type }}/
- name: Remove Docker container
run: docker rm ${{ matrix.package_type }}
diff --git a/.github/workflows/frontend-ci.yml b/.github/workflows/frontend-ci.yml
index e1d1f39b..fb154de1 100644
--- a/.github/workflows/frontend-ci.yml
+++ b/.github/workflows/frontend-ci.yml
@@ -3,7 +3,7 @@ name: Frontend CI
on:
pull_request:
paths:
- - 'fe-app-podkop/**'
+ - 'fe-app-netshift/**'
- '.github/workflows/frontend-ci.yml'
jobs:
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-24.04
defaults:
run:
- working-directory: fe-app-podkop
+ working-directory: fe-app-netshift
steps:
- name: Checkout code
@@ -28,14 +28,14 @@ jobs:
- name: Get yarn cache directory path
id: yarn-cache-dir-path
- working-directory: fe-app-podkop
+ working-directory: fe-app-netshift
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
- name: Cache yarn dependencies
uses: actions/cache@v4.3.0
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
- key: ${{ runner.os }}-yarn-${{ hashFiles('fe-app-podkop/yarn.lock') }}
+ key: ${{ runner.os }}-yarn-${{ hashFiles('fe-app-netshift/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml
index 41cbebc8..6c0b785e 100644
--- a/.github/workflows/shellcheck.yml
+++ b/.github/workflows/shellcheck.yml
@@ -7,8 +7,8 @@ on:
- 'rc/**'
paths:
- 'install.sh'
- - 'podkop/files/usr/bin/**'
- - 'podkop/files/usr/lib/**'
+ - 'netshift/files/usr/bin/**'
+ - 'netshift/files/usr/lib/**'
- '.github/workflows/shellcheck.yml'
pull_request:
branches:
@@ -16,8 +16,8 @@ on:
- 'rc/**'
paths:
- 'install.sh'
- - 'podkop/files/usr/bin/**'
- - 'podkop/files/usr/lib/**'
+ - 'netshift/files/usr/bin/**'
+ - 'netshift/files/usr/lib/**'
- '.github/workflows/shellcheck.yml'
permissions:
@@ -43,7 +43,7 @@ jobs:
with:
severity: error
include-path: |
- podkop/files/usr/bin/podkop
- podkop/files/usr/lib/**.sh
+ netshift/files/usr/bin/netshift
+ netshift/files/usr/lib/**.sh
install.sh
token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 4a470951..1c415c9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
.idea
-fe-app-podkop/node_modules
-fe-app-podkop/.env
+fe-app-netshift/node_modules
+fe-app-netshift/.env
.DS_Store
*.txt
diff --git a/Dockerfile-apk b/Dockerfile-apk
index 9c880a4a..f96b34d6 100644
--- a/Dockerfile-apk
+++ b/Dockerfile-apk
@@ -1,11 +1,11 @@
FROM itdoginfo/openwrt-sdk-apk:25.12.3
-ARG PODKOP_VERSION
-ENV PODKOP_VERSION=${PODKOP_VERSION}
+ARG NETSHIFT_VERSION
+ENV NETSHIFT_VERSION=${NETSHIFT_VERSION}
-COPY ./podkop /builder/package/feeds/utilities/podkop
-COPY ./luci-app-podkop /builder/package/feeds/luci/luci-app-podkop
+COPY ./netshift /builder/package/feeds/utilities/netshift
+COPY ./luci-app-netshift /builder/package/feeds/luci/luci-app-netshift
RUN make defconfig && \
- make package/podkop/compile -j4 V=s && \
- make package/luci-app-podkop/compile -j4 V=s
\ No newline at end of file
+ make package/netshift/compile -j4 V=s && \
+ make package/luci-app-netshift/compile -j4 V=s
diff --git a/Dockerfile-ipk b/Dockerfile-ipk
index 3dfac2ab..9b5c38a4 100644
--- a/Dockerfile-ipk
+++ b/Dockerfile-ipk
@@ -1,11 +1,11 @@
FROM itdoginfo/openwrt-sdk-ipk:24.10.6
-ARG PODKOP_VERSION
+ARG NETSHIFT_VERSION
-COPY ./podkop /builder/package/feeds/utilities/podkop
-COPY ./luci-app-podkop /builder/package/feeds/luci/luci-app-podkop
+COPY ./netshift /builder/package/feeds/utilities/netshift
+COPY ./luci-app-netshift /builder/package/feeds/luci/luci-app-netshift
-RUN export PODKOP_VERSION="v${PODKOP_VERSION}" && \
+RUN export NETSHIFT_VERSION="v${NETSHIFT_VERSION}" && \
make defconfig && \
- make package/podkop/compile V=s -j4 && \
- make package/luci-app-podkop/compile V=s -j4
\ No newline at end of file
+ make package/netshift/compile V=s -j4 && \
+ make package/luci-app-netshift/compile V=s -j4
diff --git a/README.md b/README.md
index ccdbb5a6..db3976a9 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-# Podkop Evolution
+# NetShift
-> **Podkop's fork with HWID and Subscription URL support**
+> **Форк с поддержкой Subscription URL + HWID и переключаемым ядром sing-box-extended (xhttp)**
>
-> Этот форк добавляет поддержку ссылок подписки (subscription URL) с кастомными заголовками (HWID, Device-OS, Device-Model) и автоматическим обновлением. Основан на [itdoginfo/podkop](https://github.com/itdoginfo/podkop).
+> NetShift добавляет поддержку ссылок подписки (subscription URL) с кастомными заголовками (HWID, Device-OS, Device-Model) и автоматическим обновлением, а также переключение ядра на sing-box-extended с поддержкой клиентского транспорта xhttp. Основан на [itdoginfo/podkop](https://github.com/itdoginfo/podkop).
Маршрутизация трафика для OpenWrt.
@@ -18,8 +18,8 @@
### Обновления и конфигурация
- При обновлении **обязательно** [очищайте кэш LuCI](https://podkop.net/docs/clear-browser-cache/).
- После обновления проверяйте конфигурацию — она может изменяться между версиями.
-- При старте Podkop модифицируется конфигурация Dnsmasq.
-- Podkop изменяет конфигурацию sing-box. Если вы используете собственную конфигурацию, заранее сохраните её.
+- При старте NetShift модифицируется конфигурация Dnsmasq.
+- NetShift изменяет конфигурацию sing-box. Если вы используете собственную конфигурацию, заранее сохраните её.
### Системные требования
- Требуется OpenWrt 24.10 или выше.
@@ -38,7 +38,7 @@
# Документация
https://podkop.net/
-# Установка Podkop Evolution
+# Установка NetShift
Полная информация в [документации](https://podkop.net/docs/install/)
Для установки и обновления достаточно выполнить один скрипт:
@@ -46,7 +46,7 @@ https://podkop.net/
sh <(wget -O - https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/install.sh)
```
-## Новое в этом форке: Подписки (Subscription)
+## Новое в NetShift: Подписки (Subscription)
Добавлена поддержка subscription URL — ссылки подписки от провайдера прокси. При выборе типа конфигурации **Subscription** в LuCI:
@@ -65,22 +65,44 @@ sh <(wget -O - https://raw.githubusercontent.com/yandexru45/podkop-evolution/ref
Пример конфигурации через UCI:
```
-uci set podkop.my_sub=section
-uci set podkop.my_sub.connection_type='proxy'
-uci set podkop.my_sub.proxy_config_type='subscription'
-uci set podkop.my_sub.subscription_url='https://your-provider.com/api/sub'
-uci set podkop.my_sub.subscription_update_interval='1h'
-uci add_list podkop.my_sub.community_lists='russia_inside'
-uci commit podkop
+uci set netshift.my_sub=section
+uci set netshift.my_sub.connection_type='proxy'
+uci set netshift.my_sub.proxy_config_type='subscription'
+uci set netshift.my_sub.subscription_url='https://your-provider.com/api/sub'
+uci set netshift.my_sub.subscription_update_interval='1h'
+uci add_list netshift.my_sub.community_lists='russia_inside'
+uci commit netshift
```
Ручное обновление подписки:
```
-/usr/bin/podkop subscription_update
+/usr/bin/netshift subscription_update
```
+## Новое в NetShift: ядро sing-box-extended (xhttp)
+
+NetShift позволяет переключать ядро между стабильным sing-box и сборкой
+sing-box-extended прямо из вкладки **Diagnostics** в LuCI:
+
+- **Install extended** — установить расширенное ядро sing-box-extended.
+- **Install stable** — вернуться на стабильное ядро sing-box.
+
+После установки расширенного ядра становится доступен клиентский транспорт
+**xhttp**. Поддерживается только клиентский режим xhttp (не серверный).
+
+## Изменения 0.8.0 — переименование в NetShift
+Начиная с версии 0.8.0 проект переименован из `podkop` в **NetShift**. Пакет
+теперь называется `netshift` (бинарь `/usr/bin/netshift`), а конфигурация
+переехала на `/etc/config/netshift`. LuCI-приложение — `luci-app-netshift`.
+
+При обновлении старый конфиг `/etc/config/podkop` автоматически мигрируется в
+`/etc/config/netshift`, а резервная копия сохраняется в
+`/etc/config/podkop.bak.pre-netshift`.
+
## Изменения 0.7.0
-Начиная с версии 0.7.0 изменена структура конфига `/etc/config/podkop`. Старые значения несовместимы с новыми. Нужно заново настроить Podkop.
+Начиная с версии 0.7.0 изменена структура конфига `/etc/config/netshift`
+(на тот момент — `/etc/config/podkop`). Старые значения несовместимы с новыми.
+Нужно заново настроить NetShift.
Скрипт установки обнаружит старую версию и предупредит вас об этом. Если вы согласитесь, то он сделает автоматически написанное ниже.
@@ -89,13 +111,13 @@ uci commit podkop
0. Не ныть в issue и чатик.
1. Забэкапить старый конфиг:
```
-mv /etc/config/podkop /etc/config/podkop-070
+mv /etc/config/netshift /etc/config/netshift-070
```
2. Стянуть новый дефолтный конфиг:
```
-wget -O /etc/config/podkop https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/podkop/files/etc/config/podkop
+wget -O /etc/config/netshift https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/netshift/files/etc/config/netshift
```
-3. Настроить заново ваш Podkop через Luci или UCI.
+3. Настроить заново ваш NetShift через Luci или UCI.
# ToDo
@@ -103,7 +125,7 @@ wget -O /etc/config/podkop https://raw.githubusercontent.com/yandexru45/podkop-e
> Pull Request принимаются только после согласования с авторами в Telegram-чате. На данный момент PR без предварительного обсуждения не рассматриваются.
## Будущее
-- [x] [Подписка](https://github.com/itdoginfo/podkop/issues/118) — **реализовано в этом форке!**
+- [x] [Подписка](https://github.com/itdoginfo/podkop/issues/118) — **реализовано в NetShift!**
- [ ] Весь трафик в sing-box и маршрутизация полностью на его уровне.
- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. [Issue](https://github.com/itdoginfo/podkop/issues/111)
- [ ] Галочка, которая режет доступ к doh серверам.
diff --git a/TRADEMARK.md b/TRADEMARK.md
index f7fbf71b..b949d1a4 100644
--- a/TRADEMARK.md
+++ b/TRADEMARK.md
@@ -1,51 +1,51 @@
Trademark Guidelines
-Version 1.0 dated May 28, 2026
+Version 1.0 dated 2026
-This trademark policy was prepared to help you understand how to use the Podkop trademarks, service marks, and logos in connection with the Podkop open source project and related software.
+This trademark policy was prepared to help you understand how to use the NetShift trademarks, service marks, and logos in connection with the NetShift open source project and related software.
-While the Podkop software is available under an open source license, that license does not grant permission to use the Podkop trademarks, service marks, or logos. This policy explains acceptable use of the Podkop brand and related marks.
+While the NetShift software is available under an open source license, that license does not grant permission to use the NetShift trademarks, service marks, or logos. This policy explains acceptable use of the NetShift brand and related marks.
This Policy covers:
-1. Our word trademarks and service marks: Podkop
-2. Our logos, icons, and other Podkop brand assets
+1. Our word trademarks and service marks: NetShift
+2. Our logos, icons, and other NetShift brand assets
This policy encompasses all trademarks and service marks, whether they are registered or not.
## 1. General Guidelines
-Whenever you use one of our marks, you must always do so in a way that does not mislead anyone about what they are getting and from whom. For example, you cannot say you are distributing Podkop software when you are distributing a modified version of it, because recipients may not understand the differences between your modified versions and our own.
+Whenever you use one of our marks, you must always do so in a way that does not mislead anyone about what they are getting and from whom. For example, you cannot say you are distributing NetShift software when you are distributing a modified version of it, because recipients may not understand the differences between your modified versions and our own.
You also cannot use our logo on your website in a way that suggests that your website is an official website or that we endorse your website.
-You can, however, say that you like the Podkop project, that you participate in the Podkop community, or that you are providing an unmodified version of the Podkop software.
+You can, however, say that you like the NetShift project, that you participate in the NetShift community, or that you are providing an unmodified version of the NetShift software.
You may not use or register our marks, or variations of them as part of your own trademark, service mark, domain name, company name, trade name, product name or service name.
Trademark law does not allow your use of names or trademarks that are too similar to ours. You therefore may not use an obvious variation of any of our marks or any phonetic equivalent, foreign language equivalent, takeoff, or abbreviation for a similar or compatible product or service. For example, we would consider the following too similar to one of our Marks:
-- MyPodkop
-- Open-Podkop
-- PodkopX
-- Podkop Lite
-- Podkop Pro
+- MyNetShift
+- Open-NetShift
+- NetShiftX
+- NetShift Lite
+- NetShift Pro
## 2. Acceptable Uses
### Unmodified Code
-When you redistribute an unmodified copy of Podkop software, you must not remove any Podkop trademarks, notices, or branding included in the original distribution.
+When you redistribute an unmodified copy of NetShift software, you must not remove any NetShift trademarks, notices, or branding included in the original distribution.
### Modified Code
-If you distribute a modified version of Podkop software, you may not use the Podkop name, trademarks, or logos in connection with your modified version, except to accurately describe the origin of the software in factual statements.
+If you distribute a modified version of NetShift software, you may not use the NetShift name, trademarks, or logos in connection with your modified version, except to accurately describe the origin of the software in factual statements.
-You must replace any Podkop branding, including names displayed in user interfaces, logs, documentation, and other user-facing elements, with your own distinct name and branding, so that your modified version is clearly distinguishable from the original Podkop software.
+You must replace any NetShift branding, including names displayed in user interfaces, logs, documentation, and other user-facing elements, with your own distinct name and branding, so that your modified version is clearly distinguishable from the original NetShift software.
-You must remove all Podkop logos and any other brand assets from the modified version.
+You must remove all NetShift logos and any other brand assets from the modified version.
-You may not present your modified version as Podkop or as an official Podkop release, nor may you use the Podkop name in a way that suggests endorsement, affiliation, or official status.
+You may not present your modified version as NetShift or as an official NetShift release, nor may you use the NetShift name in a way that suggests endorsement, affiliation, or official status.
-You may only refer to Podkop in a factual and descriptive manner, for example: “This software is derived from Podkop open-source software.”
+You may only refer to NetShift in a factual and descriptive manner, for example: “This software is derived from NetShift open-source software.”
### Statements about Compatibility
@@ -60,4 +60,4 @@ You must not register any domain that includes our word marks or any variant or
Always use trademarks in their exact form with correct spelling. They must not be abbreviated, modified, hyphenated, or combined with other words in a way that creates a new product or service name.
-Unacceptable: Podcop
\ No newline at end of file
+Unacceptable: NetShfit
diff --git a/TRADEMARK_RU.md b/TRADEMARK_RU.md
index f5181578..64186381 100644
--- a/TRADEMARK_RU.md
+++ b/TRADEMARK_RU.md
@@ -1,52 +1,52 @@
Руководство по использованию товарных знаков
-Версия 1.0 от 28 мая 2026 года
+Версия 1.0 от 2026 года
-Настоящая политика в отношении товарных знаков подготовлена для того, чтобы помочь вам понять, как использовать товарные знаки, знаки обслуживания и логотипы Podkop в связи с открытым исходным кодом проекта Podkop и связанным программным обеспечением.
+Настоящая политика в отношении товарных знаков подготовлена для того, чтобы помочь вам понять, как использовать товарные знаки, знаки обслуживания и логотипы NetShift в связи с открытым исходным кодом проекта NetShift и связанным программным обеспечением.
-Хотя программное обеспечение Podkop распространяется под лицензией с открытым исходным кодом, эта лицензия не предоставляет разрешения на использование товарных знаков Podkop, знаков обслуживания или логотипов. Данная политика объясняет допустимое использование бренда Podkop и связанных обозначений.
+Хотя программное обеспечение NetShift распространяется под лицензией с открытым исходным кодом, эта лицензия не предоставляет разрешения на использование товарных знаков NetShift, знаков обслуживания или логотипов. Данная политика объясняет допустимое использование бренда NetShift и связанных обозначений.
Настоящая Политика охватывает:
-1. Наши словесные товарные знаки и знаки обслуживания: Podkop
-2. Наши логотипы, иконки и другие бренд-активы Podkop
+1. Наши словесные товарные знаки и знаки обслуживания: NetShift
+2. Наши логотипы, иконки и другие бренд-активы NetShift
Данная политика распространяется на все товарные знаки и знаки обслуживания, независимо от того, зарегистрированы они или нет.
## 1. Общие рекомендации
-При использовании любого из наших знаков вы всегда должны делать это таким образом, чтобы никого не вводить в заблуждение относительно того, что именно они получают и от кого. Например, вы не можете утверждать, что распространяете программное обеспечение Podkop, если вы распространяете его модифицированную версию, поскольку получатели могут не понимать различий между вашей модифицированной версией и нашей оригинальной.
+При использовании любого из наших знаков вы всегда должны делать это таким образом, чтобы никого не вводить в заблуждение относительно того, что именно они получают и от кого. Например, вы не можете утверждать, что распространяете программное обеспечение NetShift, если вы распространяете его модифицированную версию, поскольку получатели могут не понимать различий между вашей модифицированной версией и нашей оригинальной.
Вы также не можете использовать наш логотип на своём сайте таким образом, чтобы это создавало впечатление, что ваш сайт является официальным сайтом или что мы одобряем ваш сайт.
-Однако вы можете указывать, что вам нравится проект Podkop, что вы участвуете в сообществе Podkop или что вы распространяете немодифицированную версию программного обеспечения Podkop.
+Однако вы можете указывать, что вам нравится проект NetShift, что вы участвуете в сообществе NetShift или что вы распространяете немодифицированную версию программного обеспечения NetShift.
Вы не имеете права использовать или регистрировать наши знаки, а также их вариации, как часть вашего собственного товарного знака, знака обслуживания, доменного имени, названия компании, коммерческого наименования, названия продукта или услуги.
Закон о товарных знаках не допускает использование названий или знаков, которые слишком похожи на наши. Поэтому вы не можете использовать очевидные вариации наших знаков или любые фонетически, иностранно-языковые эквиваленты, производные, аббревиатуры для похожего или совместимого продукта или услуги. Например, мы считаем слишком похожими на наши знаки следующие варианты:
-- MyPodkop
-- Open-Podkop
-- PodkopX
-- Podkop Lite
-- Podkop Pro
+- MyNetShift
+- Open-NetShift
+- NetShiftX
+- NetShift Lite
+- NetShift Pro
## 2. Допустимое использование
### Немодифицированный код
-При распространении немодифицированной копии программного обеспечения Podkop вы не должны удалять товарные знаки, уведомления или брендинг Podkop, включённые в исходное распространение.
+При распространении немодифицированной копии программного обеспечения NetShift вы не должны удалять товарные знаки, уведомления или брендинг NetShift, включённые в исходное распространение.
### Модифицированный код
-Если вы распространяете модифицированную версию программного обеспечения Podkop, вы не можете использовать название Podkop, товарные знаки или логотипы в связи с вашей модифицированной версией, за исключением точного описания происхождения программного обеспечения в фактических утверждениях.
+Если вы распространяете модифицированную версию программного обеспечения NetShift, вы не можете использовать название NetShift, товарные знаки или логотипы в связи с вашей модифицированной версией, за исключением точного описания происхождения программного обеспечения в фактических утверждениях.
-Вы обязаны заменить все элементы брендинга Podkop, включая названия, отображаемые в пользовательском интерфейсе, логах, документации и других пользовательских элементах, на собственное отличительное название и брендинг, чтобы ваша модифицированная версия была явно отличима от оригинального программного обеспечения Podkop.
+Вы обязаны заменить все элементы брендинга NetShift, включая названия, отображаемые в пользовательском интерфейсе, логах, документации и других пользовательских элементах, на собственное отличительное название и брендинг, чтобы ваша модифицированная версия была явно отличима от оригинального программного обеспечения NetShift.
-Вы должны удалить все логотипы Podkop и любые другие бренд-материалы из модифицированной версии.
+Вы должны удалить все логотипы NetShift и любые другие бренд-материалы из модифицированной версии.
-Вы не можете представлять вашу модифицированную версию как Podkop или как официальную версию Podkop, а также использовать название Podkop таким образом, чтобы это подразумевало одобрение, аффилированность или официальный статус.
+Вы не можете представлять вашу модифицированную версию как NetShift или как официальную версию NetShift, а также использовать название NetShift таким образом, чтобы это подразумевало одобрение, аффилированность или официальный статус.
-Вы можете ссылаться на Podkop только в фактическом и описательном контексте, например: «Это программное обеспечение основано на программном обеспечении Podkop с открытым исходным кодом».
+Вы можете ссылаться на NetShift только в фактическом и описательном контексте, например: «Это программное обеспечение основано на программном обеспечении NetShift с открытым исходным кодом».
### Упоминания о совместимости
@@ -62,4 +62,4 @@
Всегда используйте товарные знаки в их точной форме с корректным написанием. Их нельзя сокращать, изменять, соединять дефисами или объединять с другими словами таким образом, чтобы это создавало новое название продукта или услуги.
-Недопустимо: Podcop
\ No newline at end of file
+Недопустимо: NetShfit
diff --git a/fe-app-netshift/.env.example b/fe-app-netshift/.env.example
new file mode 100644
index 00000000..4cf8dfa0
--- /dev/null
+++ b/fe-app-netshift/.env.example
@@ -0,0 +1,16 @@
+SFTP_HOST=192.168.160.129
+SFTP_PORT=22
+SFTP_USER=root
+SFTP_PASS=
+
+# you can use key if needed
+# SFTP_PRIVATE_KEY=~/.ssh/id_rsa
+
+LOCAL_DIR_FE=../luci-app-netshift/htdocs/luci-static/resources/view/netshift
+REMOTE_DIR_FE=/www/luci-static/resources/view/netshift
+
+LOCAL_DIR_BIN=../netshift/files/usr/bin/
+REMOTE_DIR_BIN=/usr/bin/
+
+LOCAL_DIR_LIB=../netshift/files/usr/lib/
+REMOTE_DIR_LIB=/usr/lib/netshift/
diff --git a/fe-app-podkop/.prettierrc b/fe-app-netshift/.prettierrc
similarity index 100%
rename from fe-app-podkop/.prettierrc
rename to fe-app-netshift/.prettierrc
diff --git a/fe-app-podkop/distribute-locales.js b/fe-app-netshift/distribute-locales.js
similarity index 80%
rename from fe-app-podkop/distribute-locales.js
rename to fe-app-netshift/distribute-locales.js
index 0bc27b23..48c303cc 100644
--- a/fe-app-podkop/distribute-locales.js
+++ b/fe-app-netshift/distribute-locales.js
@@ -6,7 +6,7 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const sourceDir = path.resolve(__dirname, 'locales');
-const targetRoot = path.resolve(__dirname, '../luci-app-podkop/po');
+const targetRoot = path.resolve(__dirname, '../luci-app-netshift/po');
async function main() {
const files = await fs.readdir(sourceDir);
@@ -14,17 +14,17 @@ async function main() {
for (const file of files) {
const filePath = path.join(sourceDir, file);
- if (file === 'podkop.pot') {
- const potTarget = path.join(targetRoot, 'templates', 'podkop.pot');
+ if (file === 'netshift.pot') {
+ const potTarget = path.join(targetRoot, 'templates', 'netshift.pot');
await fs.mkdir(path.dirname(potTarget), { recursive: true });
await fs.copyFile(filePath, potTarget);
console.log(`✅ Copied POT: ${filePath} → ${potTarget}`);
}
- const match = file.match(/^podkop\.([a-zA-Z_]+)\.po$/);
+ const match = file.match(/^netshift\.([a-zA-Z_]+)\.po$/);
if (match) {
const lang = match[1];
- const poTarget = path.join(targetRoot, lang, 'podkop.po');
+ const poTarget = path.join(targetRoot, lang, 'netshift.po');
await fs.mkdir(path.dirname(poTarget), { recursive: true });
await fs.copyFile(filePath, poTarget);
console.log(`✅ Copied ${lang.toUpperCase()}: ${filePath} → ${poTarget}`);
diff --git a/fe-app-podkop/eslint.config.js b/fe-app-netshift/eslint.config.js
similarity index 100%
rename from fe-app-podkop/eslint.config.js
rename to fe-app-netshift/eslint.config.js
diff --git a/fe-app-podkop/extract-calls.js b/fe-app-netshift/extract-calls.js
similarity index 93%
rename from fe-app-podkop/extract-calls.js
rename to fe-app-netshift/extract-calls.js
index 6b7d765e..0bab3b73 100644
--- a/fe-app-podkop/extract-calls.js
+++ b/fe-app-netshift/extract-calls.js
@@ -19,12 +19,12 @@ function stripIllegalReturn(code) {
const files = await glob([
'src/**/*.ts',
- '../luci-app-podkop/htdocs/luci-static/resources/view/podkop/**/*.js',
+ '../luci-app-netshift/htdocs/luci-static/resources/view/netshift/**/*.js',
], {
ignore: [
'**/*.test.ts',
'**/main.js',
- '../luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js',
+ '../luci-app-netshift/htdocs/luci-static/resources/view/netshift/main.js',
],
absolute: true,
});
diff --git a/fe-app-podkop/generate-po.js b/fe-app-netshift/generate-po.js
similarity index 92%
rename from fe-app-podkop/generate-po.js
rename to fe-app-netshift/generate-po.js
index 234d6b8e..1859666a 100644
--- a/fe-app-podkop/generate-po.js
+++ b/fe-app-netshift/generate-po.js
@@ -8,7 +8,7 @@ if (!lang) {
}
const callsPath = 'locales/calls.json';
-const poPath = `locales/podkop.${lang}.po`;
+const poPath = `locales/netshift.${lang}.po`;
function getGitUser() {
try {
@@ -36,14 +36,14 @@ function getHeader(lang) {
: 'nplurals=2; plural=(n != 1);';
return [
- `# ${lang.toUpperCase()} translations for PODKOP package.`,
- `# Copyright (C) ${now.getFullYear()} THE PODKOP'S COPYRIGHT HOLDER`,
- `# This file is distributed under the same license as the PODKOP package.`,
+ `# ${lang.toUpperCase()} translations for NETSHIFT package.`,
+ `# Copyright (C) ${now.getFullYear()} THE NETSHIFT'S COPYRIGHT HOLDER`,
+ `# This file is distributed under the same license as the NETSHIFT package.`,
`# ${translator}, ${now.getFullYear()}.`,
'#',
'msgid ""',
'msgstr ""',
- `"Project-Id-Version: PODKOP\\n"`,
+ `"Project-Id-Version: NETSHIFT\\n"`,
`"Report-Msgid-Bugs-To: \\n"`,
`"POT-Creation-Date: ${date} ${time}${tzOffset}\\n"`,
`"PO-Revision-Date: ${date} ${time}${tzOffset}\\n"`,
diff --git a/fe-app-podkop/generate-pot.js b/fe-app-netshift/generate-pot.js
similarity index 96%
rename from fe-app-podkop/generate-pot.js
rename to fe-app-netshift/generate-pot.js
index b7390440..3af0f84a 100644
--- a/fe-app-podkop/generate-pot.js
+++ b/fe-app-netshift/generate-pot.js
@@ -2,8 +2,8 @@ import fs from 'fs/promises';
import { execSync } from 'child_process';
const inputFile = 'locales/calls.json';
-const outputFile = 'locales/podkop.pot';
-const projectId = 'PODKOP';
+const outputFile = 'locales/netshift.pot';
+const projectId = 'NETSHIFT';
function getGitUser() {
const name = execSync('git config user.name').toString().trim();
diff --git a/fe-app-podkop/locales/calls.json b/fe-app-netshift/locales/calls.json
similarity index 61%
rename from fe-app-podkop/locales/calls.json
rename to fe-app-netshift/locales/calls.json
index 033db57f..8a57d517 100644
--- a/fe-app-podkop/locales/calls.json
+++ b/fe-app-netshift/locales/calls.json
@@ -3,175 +3,175 @@
"call": "✔ Enabled",
"key": "✔ Enabled",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:345"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:345"
]
},
{
"call": "✔ Running",
"key": "✔ Running",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:356"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:356"
]
},
{
"call": "✘ Disabled",
"key": "✘ Disabled",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:346"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:346"
]
},
{
"call": "✘ Stopped",
"key": "✘ Stopped",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:357"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:357"
]
},
{
"call": "Группировать по странам",
"key": "Группировать по странам",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:127"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:127"
]
},
{
"call": "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы",
"key": "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:128"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:128"
]
},
{
"call": "Active Connections",
"key": "Active Connections",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:307"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:307"
]
},
{
"call": "Additional marking rules found",
"key": "Additional marking rules found",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:106"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runNftCheck.ts:106"
]
},
{
"call": "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall.",
"key": "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall.",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:247"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:247"
]
},
{
"call": "Applicable for SOCKS and Shadowsocks proxy",
"key": "Applicable for SOCKS and Shadowsocks proxy",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:251"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:251"
]
},
{
"call": "At least one valid domain must be specified. Comments-only content is not allowed.",
"key": "At least one valid domain must be specified. Comments-only content is not allowed.",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:496"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:496"
]
},
{
"call": "At least one valid subnet or IP must be specified. Comments-only content is not allowed.",
"key": "At least one valid subnet or IP must be specified. Comments-only content is not allowed.",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:577"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:577"
]
},
{
"call": "Available actions",
"key": "Available actions",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:47"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:47"
]
},
{
"call": "Bootsrap DNS",
"key": "Bootsrap DNS",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:65"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runDnsCheck.ts:65"
]
},
{
"call": "Bootstrap DNS server",
"key": "Bootstrap DNS server",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:45"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:45"
]
},
{
"call": "Browser is not using FakeIP",
"key": "Browser is not using FakeIP",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:58"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:58"
]
},
{
"call": "Browser is using FakeIP correctly",
"key": "Browser is using FakeIP correctly",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:57"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:57"
]
},
{
"call": "Cache File Path",
"key": "Cache File Path",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:348"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:348"
]
},
{
"call": "Cache file path cannot be empty",
"key": "Cache file path cannot be empty",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:362"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:362"
]
},
{
"call": "Cannot receive checks result",
"key": "Cannot receive checks result",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:27",
- "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:28",
- "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:27",
- "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:25"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runDnsCheck.ts:27",
+ "src\\netshift\\tabs\\diagnostic\\checks\\runNftCheck.ts:28",
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:27",
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:25"
]
},
{
"call": "Checking, please wait",
"key": "Checking, please wait",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:15",
- "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:15",
- "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:13",
- "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:15",
- "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:13"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runDnsCheck.ts:15",
+ "src\\netshift\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:15",
+ "src\\netshift\\tabs\\diagnostic\\checks\\runNftCheck.ts:13",
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:15",
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:13"
]
},
{
"call": "checks",
"key": "checks",
"places": [
- "src\\podkop\\tabs\\diagnostic\\helpers\\getCheckTitle.ts:2"
+ "src\\netshift\\tabs\\diagnostic\\helpers\\getCheckTitle.ts:2"
]
},
{
"call": "Checks failed",
"key": "Checks failed",
"places": [
- "src\\podkop\\tabs\\diagnostic\\helpers\\getMeta.ts:26"
+ "src\\netshift\\tabs\\diagnostic\\helpers\\getMeta.ts:26"
]
},
{
"call": "Checks passed",
"key": "Checks passed",
"places": [
- "src\\podkop\\tabs\\diagnostic\\helpers\\getMeta.ts:13"
+ "src\\netshift\\tabs\\diagnostic\\helpers\\getMeta.ts:13"
]
},
{
@@ -192,42 +192,42 @@
"call": "Community Lists",
"key": "Community Lists",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:351"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:351"
]
},
{
"call": "Config File Path",
"key": "Config File Path",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:335"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:335"
]
},
{
- "call": "Configuration for Podkop service",
- "key": "Configuration for Podkop service",
+ "call": "Configuration for NetShift service",
+ "key": "Configuration for NetShift service",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:27"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\netshift.js:27"
]
},
{
"call": "Configuration Type",
"key": "Configuration Type",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:23"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:23"
]
},
{
"call": "Connection Type",
"key": "Connection Type",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:12"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:12"
]
},
{
"call": "Connection URL",
"key": "Connection URL",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:26"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:26"
]
},
{
@@ -241,124 +241,124 @@
"call": "Currently unavailable",
"key": "Currently unavailable",
"places": [
- "src\\podkop\\tabs\\dashboard\\partials\\renderWidget.ts:22"
+ "src\\netshift\\tabs\\dashboard\\partials\\renderWidget.ts:22"
]
},
{
"call": "Dashboard",
"key": "Dashboard",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:80"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\netshift.js:80"
]
},
{
"call": "Dashboard currently unavailable",
"key": "Dashboard currently unavailable",
"places": [
- "src\\podkop\\tabs\\dashboard\\partials\\renderSections.ts:19"
+ "src\\netshift\\tabs\\dashboard\\partials\\renderSections.ts:19"
]
},
{
- "call": "Delay in milliseconds before reloading podkop after interface UP",
- "key": "Delay in milliseconds before reloading podkop after interface UP",
+ "call": "Delay in milliseconds before reloading NetShift after interface UP",
+ "key": "Delay in milliseconds before reloading NetShift after interface UP",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:222"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:222"
]
},
{
"call": "Delay value cannot be empty",
"key": "Delay value cannot be empty",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:229"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:229"
]
},
{
"call": "DHCP has DNS server",
"key": "DHCP has DNS server",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:82"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runDnsCheck.ts:82"
]
},
{
"call": "Diagnostics",
"key": "Diagnostics",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:65"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\netshift.js:65"
]
},
{
"call": "Disable autostart",
"key": "Disable autostart",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:83"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:83"
]
},
{
"call": "Disable QUIC",
"key": "Disable QUIC",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:265"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:265"
]
},
{
"call": "Disable the QUIC protocol to improve compatibility or fix issues with video streaming",
"key": "Disable the QUIC protocol to improve compatibility or fix issues with video streaming",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:266"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:266"
]
},
{
"call": "Disabled",
"key": "Disabled",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:442",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:522"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:442",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:522"
]
},
{
"call": "DNS on router",
"key": "DNS on router",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:77"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runDnsCheck.ts:77"
]
},
{
"call": "DNS over HTTPS (DoH)",
"key": "DNS over HTTPS (DoH)",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:319",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:15"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:319",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:15"
]
},
{
"call": "DNS over TLS (DoT)",
"key": "DNS over TLS (DoT)",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:320",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:16"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:320",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:16"
]
},
{
"call": "DNS Protocol Type",
"key": "DNS Protocol Type",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:316",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:12"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:316",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:12"
]
},
{
"call": "DNS Rewrite TTL",
"key": "DNS Rewrite TTL",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:68"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:68"
]
},
{
"call": "DNS Server",
"key": "DNS Server",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:329",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:24"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:329",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:24"
]
},
{
@@ -372,29 +372,29 @@
"call": "Do not panic, everything can be fixed, just...",
"key": "Do not panic, everything can be fixed, just...",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderWikiDisclaimer.ts:26"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderWikiDisclaimer.ts:26"
]
},
{
"call": "Domain Resolver",
"key": "Domain Resolver",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:306"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:306"
]
},
{
"call": "Dont Touch My DHCP!",
"key": "Dont Touch My DHCP!",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:326"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:326"
]
},
{
"call": "Downlink",
"key": "Downlink",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:241",
- "src\\podkop\\tabs\\dashboard\\initController.ts:275"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:241",
+ "src\\netshift\\tabs\\dashboard\\initController.ts:275"
]
},
{
@@ -408,205 +408,205 @@
"call": "Download Lists via Proxy/VPN",
"key": "Download Lists via Proxy/VPN",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:288"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:288"
]
},
{
"call": "Download Lists via specific proxy section",
"key": "Download Lists via specific proxy section",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:297"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:297"
]
},
{
"call": "Downloading all lists via specific Proxy/VPN",
"key": "Downloading all lists via specific Proxy/VPN",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:289",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:298"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:289",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:298"
]
},
{
"call": "Dynamic List",
"key": "Dynamic List",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:443",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:523"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:443",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:523"
]
},
{
"call": "Enable autostart",
"key": "Enable autostart",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:93"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:93"
]
},
{
"call": "Enable built-in DNS resolver for domains handled by this section",
"key": "Enable built-in DNS resolver for domains handled by this section",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:307"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:307"
]
},
{
"call": "Enable DNS resolve to get real IP when routing",
"key": "Enable DNS resolve to get real IP when routing",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:746"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:746"
]
},
{
"call": "Enable Mixed Proxy",
"key": "Enable Mixed Proxy",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:717"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:717"
]
},
{
"call": "Enable Output Network Interface",
"key": "Enable Output Network Interface",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:126"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:126"
]
},
{
"call": "Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies",
"key": "Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:718"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:718"
]
},
{
"call": "Enable YACD",
"key": "Enable YACD",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:237"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:237"
]
},
{
"call": "Enable YACD WAN Access",
"key": "Enable YACD WAN Access",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:246"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:246"
]
},
{
"call": "Enter complete outbound configuration in JSON format",
"key": "Enter complete outbound configuration in JSON format",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:67"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:67"
]
},
{
"call": "Enter domain names separated by commas, spaces, or newlines. You can add comments using //",
"key": "Enter domain names separated by commas, spaces, or newlines. You can add comments using //",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:478"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:478"
]
},
{
"call": "Enter domain names without protocols, e.g. example.com or sub.example.com",
"key": "Enter domain names without protocols, e.g. example.com or sub.example.com",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:452"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:452"
]
},
{
"call": "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses",
"key": "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:532"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:532"
]
},
{
"call": "Enter the subscription URL to fetch proxy configurations from your provider",
"key": "Enter the subscription URL to fetch proxy configurations from your provider",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:90"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:90"
]
},
{
"call": "Every 1 minute",
"key": "Every 1 minute",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:187"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:187"
]
},
{
"call": "Every 12 hours",
"key": "Every 12 hours",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:119"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:119"
]
},
{
"call": "Every 3 hours",
"key": "Every 3 hours",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:117"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:117"
]
},
{
"call": "Every 3 minutes",
"key": "Every 3 minutes",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:188"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:188"
]
},
{
"call": "Every 30 minutes",
"key": "Every 30 minutes",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:115"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:115"
]
},
{
"call": "Every 30 seconds",
"key": "Every 30 seconds",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:186"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:186"
]
},
{
"call": "Every 5 minutes",
"key": "Every 5 minutes",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:189"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:189"
]
},
{
"call": "Every 6 hours",
"key": "Every 6 hours",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:118"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:118"
]
},
{
"call": "Every day",
"key": "Every day",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:120"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:120"
]
},
{
"call": "Every hour",
"key": "Every hour",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:116"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:116"
]
},
{
"call": "Exclude NTP",
"key": "Exclude NTP",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:402"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:402"
]
},
{
"call": "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN",
"key": "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:403"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:403"
]
},
{
@@ -620,94 +620,94 @@
"call": "Failed to execute!",
"key": "Failed to execute!",
"places": [
- "src\\podkop\\tabs\\diagnostic\\initController.ts:229",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:233",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:263",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:267",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:304",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:308",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:342",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:346"
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:229",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:233",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:263",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:267",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:304",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:308",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:342",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:346"
]
},
{
"call": "Fastest",
"key": "Fastest",
"places": [
- "src\\podkop\\methods\\custom\\getDashboardSections.ts:150",
- "src\\podkop\\methods\\custom\\getDashboardSections.ts:181",
- "src\\podkop\\methods\\custom\\getDashboardSections.ts:218",
- "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:59"
+ "src\\netshift\\methods\\custom\\getDashboardSections.ts:150",
+ "src\\netshift\\methods\\custom\\getDashboardSections.ts:181",
+ "src\\netshift\\methods\\custom\\getDashboardSections.ts:218",
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:58"
]
},
{
"call": "Fully Routed IPs",
"key": "Fully Routed IPs",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:690"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:690"
]
},
{
"call": "Get global check",
"key": "Get global check",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:102"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:102"
]
},
{
"call": "Global check",
"key": "Global check",
"places": [
- "src\\podkop\\tabs\\diagnostic\\initController.ts:224"
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:224"
]
},
{
"call": "How often to automatically update the subscription",
"key": "How often to automatically update the subscription",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:113"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:113"
]
},
{
"call": "HTTP error",
"key": "HTTP error",
"places": [
- "src\\podkop\\api.ts:27"
+ "src\\netshift\\api.ts:27"
]
},
{
"call": "Install extended",
"key": "Install extended",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:129"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:129"
]
},
{
"call": "Install stable",
"key": "Install stable",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:129"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:129"
]
},
{
"call": "Interface Monitoring",
"key": "Interface Monitoring",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:189"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:189"
]
},
{
"call": "Interface Monitoring Delay",
"key": "Interface Monitoring Delay",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:221"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:221"
]
},
{
"call": "Interface monitoring for Bad WAN",
"key": "Interface monitoring for Bad WAN",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:190"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:190"
]
},
{
@@ -1024,118 +1024,139 @@
"call": "Issues detected",
"key": "Issues detected",
"places": [
- "src\\podkop\\tabs\\diagnostic\\helpers\\getMeta.ts:20"
+ "src\\netshift\\tabs\\diagnostic\\helpers\\getMeta.ts:20"
]
},
{
"call": "Latest",
"key": "Latest",
"places": [
- "src\\podkop\\tabs\\diagnostic\\helpers\\getPodkopVersionRow.ts:48"
+ "src\\netshift\\tabs\\diagnostic\\helpers\\getNetshiftVersionRow.ts:48"
]
},
{
"call": "List Update Frequency",
"key": "List Update Frequency",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:276"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:276"
]
},
{
"call": "Local Domain Lists",
"key": "Local Domain Lists",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:598"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:598"
]
},
{
"call": "Local Subnet Lists",
"key": "Local Subnet Lists",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:621"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:621"
]
},
{
"call": "Log Level",
"key": "Log Level",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:384"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:384"
]
},
{
"call": "Main DNS",
"key": "Main DNS",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runDnsCheck.ts:72"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runDnsCheck.ts:72"
]
},
{
"call": "Memory Usage",
"key": "Memory Usage",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:311"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:311"
]
},
{
"call": "Mixed Proxy Port",
"key": "Mixed Proxy Port",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:730"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:730"
]
},
{
"call": "Monitored Interfaces",
"key": "Monitored Interfaces",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:198"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:198"
]
},
{
"call": "Must be a number in the range of 50 - 1000",
"key": "Must be a number in the range of 50 - 1000",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:215"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:215"
+ ]
+ },
+ {
+ "call": "NetShift",
+ "key": "NetShift",
+ "places": [
+ "src\\netshift\\tabs\\dashboard\\initController.ts:343"
+ ]
+ },
+ {
+ "call": "NetShift Settings",
+ "key": "NetShift Settings",
+ "places": [
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\netshift.js:26"
+ ]
+ },
+ {
+ "call": "NetShift will not modify your DHCP configuration",
+ "key": "NetShift will not modify your DHCP configuration",
+ "places": [
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:327"
]
},
{
"call": "Network Interface",
"key": "Network Interface",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:260"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:260"
]
},
{
"call": "No other marking rules found",
"key": "No other marking rules found",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:105"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runNftCheck.ts:105"
]
},
{
"call": "Not implement yet",
"key": "Not implement yet",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderCheckSection.ts:189"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderCheckSection.ts:189"
]
},
{
"call": "Not responding",
"key": "Not responding",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:75",
- "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:81",
- "src\\podkop\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:100"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:74",
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:80",
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSectionsCheck.ts:99"
]
},
{
"call": "Not running",
"key": "Not running",
"places": [
- "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:59",
- "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:67",
- "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:75",
- "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:83",
- "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:91"
+ "src\\netshift\\tabs\\diagnostic\\diagnostic.store.ts:59",
+ "src\\netshift\\tabs\\diagnostic\\diagnostic.store.ts:67",
+ "src\\netshift\\tabs\\diagnostic\\diagnostic.store.ts:75",
+ "src\\netshift\\tabs\\diagnostic\\diagnostic.store.ts:83",
+ "src\\netshift\\tabs\\diagnostic\\diagnostic.store.ts:91"
]
},
{
@@ -1149,28 +1170,28 @@
"call": "Outbound Config",
"key": "Outbound Config",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:30"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:30"
]
},
{
"call": "Outbound Configuration",
"key": "Outbound Configuration",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:66"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:66"
]
},
{
"call": "Outdated",
"key": "Outdated",
"places": [
- "src\\podkop\\tabs\\diagnostic\\helpers\\getPodkopVersionRow.ts:38"
+ "src\\netshift\\tabs\\diagnostic\\helpers\\getNetshiftVersionRow.ts:38"
]
},
{
"call": "Output Network Interface",
"key": "Output Network Interface",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:135"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:135"
]
},
{
@@ -1184,483 +1205,462 @@
"call": "Path must be absolute (start with /)",
"key": "Path must be absolute (start with /)",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:366"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:366"
]
},
{
"call": "Path must contain at least one directory (like /tmp/cache.db)",
"key": "Path must contain at least one directory (like /tmp/cache.db)",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:375"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:375"
]
},
{
"call": "Path must end with cache.db",
"key": "Path must end with cache.db",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:370"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:370"
]
},
{
"call": "Pending",
"key": "Pending",
"places": [
- "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:107",
- "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:115",
- "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:123",
- "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:131",
- "src\\podkop\\tabs\\diagnostic\\diagnostic.store.ts:139"
- ]
- },
- {
- "call": "Podkop",
- "key": "Podkop",
- "places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:343"
- ]
- },
- {
- "call": "Podkop Settings",
- "key": "Podkop Settings",
- "places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:26"
- ]
- },
- {
- "call": "Podkop will not modify your DHCP configuration",
- "key": "Podkop will not modify your DHCP configuration",
- "places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:327"
+ "src\\netshift\\tabs\\diagnostic\\diagnostic.store.ts:107",
+ "src\\netshift\\tabs\\diagnostic\\diagnostic.store.ts:115",
+ "src\\netshift\\tabs\\diagnostic\\diagnostic.store.ts:123",
+ "src\\netshift\\tabs\\diagnostic\\diagnostic.store.ts:131",
+ "src\\netshift\\tabs\\diagnostic\\diagnostic.store.ts:139"
]
},
{
"call": "Proxy Configuration URL",
"key": "Proxy Configuration URL",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:37"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:37"
]
},
{
"call": "Proxy traffic is not routed via FakeIP",
"key": "Proxy traffic is not routed via FakeIP",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:66"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:66"
]
},
{
"call": "Proxy traffic is routed via FakeIP",
"key": "Proxy traffic is routed via FakeIP",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:65"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:65"
]
},
{
"call": "Regional options cannot be used together",
"key": "Regional options cannot be used together",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:385"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:385"
]
},
{
"call": "Remote Domain Lists",
"key": "Remote Domain Lists",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:644"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:644"
]
},
{
"call": "Remote Subnet Lists",
"key": "Remote Subnet Lists",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:667"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:667"
]
},
{
"call": "Resolve real IP for routing",
"key": "Resolve real IP for routing",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:745"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:745"
]
},
{
- "call": "Restart podkop",
- "key": "Restart podkop",
+ "call": "Restart NetShift",
+ "key": "Restart NetShift",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:53"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:53"
]
},
{
"call": "Router DNS is not routed through sing-box",
"key": "Router DNS is not routed through sing-box",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:51"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:51"
]
},
{
"call": "Router DNS is routed through sing-box",
"key": "Router DNS is routed through sing-box",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:50"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runFakeIPCheck.ts:50"
]
},
{
"call": "Routing Excluded IPs",
"key": "Routing Excluded IPs",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:413"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:413"
]
},
{
"call": "Rules mangle counters",
"key": "Rules mangle counters",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:79"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runNftCheck.ts:79"
]
},
{
"call": "Rules mangle exist",
"key": "Rules mangle exist",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:74"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runNftCheck.ts:74"
]
},
{
"call": "Rules mangle output counters",
"key": "Rules mangle output counters",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:89"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runNftCheck.ts:89"
]
},
{
"call": "Rules mangle output exist",
"key": "Rules mangle output exist",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:84"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runNftCheck.ts:84"
]
},
{
"call": "Rules proxy counters",
"key": "Rules proxy counters",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:99"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runNftCheck.ts:99"
]
},
{
"call": "Rules proxy exist",
"key": "Rules proxy exist",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:94"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runNftCheck.ts:94"
]
},
{
"call": "Run Diagnostic",
"key": "Run Diagnostic",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderRunAction.ts:15"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderRunAction.ts:15"
]
},
{
"call": "Russia inside restrictions",
"key": "Russia inside restrictions",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:404"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:404"
]
},
{
"call": "Secret key for authenticating remote access to YACD when WAN access is enabled.",
"key": "Secret key for authenticating remote access to YACD when WAN access is enabled.",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:257"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:257"
]
},
{
"call": "Sections",
"key": "Sections",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:36"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\netshift.js:36"
]
},
{
"call": "Select a predefined list for routing",
"key": "Select a predefined list for routing",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:352"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:352"
]
},
{
"call": "Select between VPN and Proxy connection methods for traffic routing",
"key": "Select between VPN and Proxy connection methods for traffic routing",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:13"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:13"
]
},
{
"call": "Select DNS protocol to use",
"key": "Select DNS protocol to use",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:13"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:13"
]
},
{
"call": "Select how often the domain or subnet lists are updated automatically",
"key": "Select how often the domain or subnet lists are updated automatically",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:277"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:277"
]
},
{
"call": "Select how to configure the proxy",
"key": "Select how to configure the proxy",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:24"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:24"
]
},
{
"call": "Select network interface for VPN connection",
"key": "Select network interface for VPN connection",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:261"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:261"
]
},
{
"call": "Select or enter DNS server address",
"key": "Select or enter DNS server address",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:330",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:25"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:330",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:25"
]
},
{
"call": "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing",
"key": "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:349"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:349"
]
},
{
"call": "Select path for sing-box config file. Change this ONLY if you know what you are doing",
"key": "Select path for sing-box config file. Change this ONLY if you know what you are doing",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:336"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:336"
]
},
{
"call": "Select the DNS protocol type for the domain resolver",
"key": "Select the DNS protocol type for the domain resolver",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:317"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:317"
]
},
{
"call": "Select the list type for adding custom domains",
"key": "Select the list type for adding custom domains",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:440"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:440"
]
},
{
"call": "Select the list type for adding custom subnets",
"key": "Select the list type for adding custom subnets",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:520"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:520"
]
},
{
"call": "Select the log level for sing-box",
"key": "Select the log level for sing-box",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:385"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:385"
]
},
{
"call": "Select the network interface from which the traffic will originate",
"key": "Select the network interface from which the traffic will originate",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:90"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:90"
]
},
{
"call": "Select the network interface to which the traffic will originate",
"key": "Select the network interface to which the traffic will originate",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:136"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:136"
]
},
{
"call": "Select the WAN interfaces to be monitored",
"key": "Select the WAN interfaces to be monitored",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:199"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:199"
]
},
{
"call": "Selector",
"key": "Selector",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:27"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:27"
]
},
{
"call": "Selector Proxy Links",
"key": "Selector Proxy Links",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:137"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:137"
]
},
{
"call": "Services info",
"key": "Services info",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:340"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:340"
]
},
{
"call": "Settings",
"key": "Settings",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\podkop.js:49"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\netshift.js:49"
]
},
{
"call": "Show sing-box config",
"key": "Show sing-box config",
"places": [
- "src\\podkop\\tabs\\diagnostic\\initController.ts:292",
- "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:120"
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:292",
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:120"
]
},
{
"call": "Sing-box",
"key": "Sing-box",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:354"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:354"
]
},
{
"call": "Sing-box autostart disabled",
"key": "Sing-box autostart disabled",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:77"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:77"
]
},
{
"call": "Sing-box core changed, version:",
"key": "Sing-box core changed, version:",
"places": [
- "src\\podkop\\tabs\\diagnostic\\initController.ts:337"
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:337"
]
},
{
"call": "Sing-box installed",
"key": "Sing-box installed",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:62"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:62"
]
},
{
"call": "Sing-box listening ports",
"key": "Sing-box listening ports",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:87"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:87"
]
},
{
"call": "Sing-box process running",
"key": "Sing-box process running",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:82"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:82"
]
},
{
"call": "Sing-box service exist",
"key": "Sing-box service exist",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:72"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:72"
]
},
{
"call": "Sing-box version is compatible (newer than 1.12.4)",
"key": "Sing-box version is compatible (newer than 1.12.4)",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:67"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runSingBoxCheck.ts:67"
]
},
{
"call": "Source Network Interface",
"key": "Source Network Interface",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:89"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:89"
]
},
{
"call": "Specify a local IP address to be excluded from routing",
"key": "Specify a local IP address to be excluded from routing",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:414"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:414"
]
},
{
"call": "Specify local IP addresses or subnets whose traffic will always be routed through the configured route",
"key": "Specify local IP addresses or subnets whose traffic will always be routed through the configured route",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:691"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:691"
]
},
{
"call": "Specify remote URLs to download and use domain lists",
"key": "Specify remote URLs to download and use domain lists",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:645"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:645"
]
},
{
"call": "Specify remote URLs to download and use subnet lists",
"key": "Specify remote URLs to download and use subnet lists",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:668"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:668"
]
},
{
"call": "Specify the path to the list file located on the router filesystem",
"key": "Specify the path to the list file located on the router filesystem",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:599",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:622"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:599",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:622"
]
},
{
- "call": "Start podkop",
- "key": "Start podkop",
+ "call": "Start NetShift",
+ "key": "Start NetShift",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:73"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:73"
]
},
{
- "call": "Stop podkop",
- "key": "Stop podkop",
+ "call": "Stop NetShift",
+ "key": "Stop NetShift",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:63"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:63"
]
},
{
"call": "Subscription",
"key": "Subscription",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:29"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:29"
]
},
{
"call": "Subscription Update Interval",
"key": "Subscription Update Interval",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:112"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:112"
]
},
{
"call": "Subscription URL",
"key": "Subscription URL",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:89"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:89"
]
},
{
@@ -1674,149 +1674,149 @@
"call": "System info",
"key": "System info",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:304"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:304"
]
},
{
"call": "System information",
"key": "System information",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderSystemInfo.ts:21"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderSystemInfo.ts:21"
]
},
{
"call": "Table exist",
"key": "Table exist",
"places": [
- "src\\podkop\\tabs\\diagnostic\\checks\\runNftCheck.ts:69"
+ "src\\netshift\\tabs\\diagnostic\\checks\\runNftCheck.ts:69"
]
},
{
"call": "Test latency",
"key": "Test latency",
"places": [
- "src\\podkop\\tabs\\dashboard\\partials\\renderSections.ts:108"
+ "src\\netshift\\tabs\\dashboard\\partials\\renderSections.ts:108"
]
},
{
"call": "Text List",
"key": "Text List",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:444",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:524"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:444",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:524"
]
},
{
"call": "The DNS server used to look up the IP address of an upstream DNS server",
"key": "The DNS server used to look up the IP address of an upstream DNS server",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:46"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:46"
]
},
{
"call": "The interval between connectivity tests",
"key": "The interval between connectivity tests",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:184"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:184"
]
},
{
"call": "The maximum difference in response times (ms) allowed when comparing servers",
"key": "The maximum difference in response times (ms) allowed when comparing servers",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:198"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:198"
]
},
{
"call": "The URL used to test server connectivity",
"key": "The URL used to test server connectivity",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:222"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:222"
]
},
{
"call": "Time in seconds for DNS record caching (default: 60)",
"key": "Time in seconds for DNS record caching (default: 60)",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:69"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:69"
]
},
{
"call": "Traffic",
"key": "Traffic",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:238"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:238"
]
},
{
"call": "Traffic Total",
"key": "Traffic Total",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:268"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:268"
]
},
{
"call": "Troubleshooting",
"key": "Troubleshooting",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderWikiDisclaimer.ts:25"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderWikiDisclaimer.ts:25"
]
},
{
"call": "TTL must be a positive number",
"key": "TTL must be a positive number",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:80"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:80"
]
},
{
"call": "TTL value cannot be empty",
"key": "TTL value cannot be empty",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:75"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:75"
]
},
{
"call": "UDP (Unprotected DNS)",
"key": "UDP (Unprotected DNS)",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:321",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:17"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:321",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:17"
]
},
{
"call": "UDP over TCP",
"key": "UDP over TCP",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:250"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:250"
]
},
{
"call": "unknown",
"key": "unknown",
"places": [
- "src\\podkop\\tabs\\diagnostic\\initController.ts:39",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:40",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:41",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:42",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:43",
- "src\\podkop\\tabs\\diagnostic\\initController.ts:44",
- "src\\podkop\\tabs\\diagnostic\\helpers\\getPodkopVersionRow.ts:7"
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:39",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:40",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:41",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:42",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:43",
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:44",
+ "src\\netshift\\tabs\\diagnostic\\helpers\\getNetshiftVersionRow.ts:7"
]
},
{
"call": "Unknown error",
"key": "Unknown error",
"places": [
- "src\\podkop\\api.ts:40"
+ "src\\netshift\\api.ts:40"
]
},
{
"call": "Uplink",
"key": "Uplink",
"places": [
- "src\\podkop\\tabs\\dashboard\\initController.ts:240",
- "src\\podkop\\tabs\\dashboard\\initController.ts:271"
+ "src\\netshift\\tabs\\dashboard\\initController.ts:240",
+ "src\\netshift\\tabs\\dashboard\\initController.ts:271"
]
},
{
@@ -1837,77 +1837,77 @@
"call": "URLTest",
"key": "URLTest",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:28"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:28"
]
},
{
"call": "URLTest Check Interval",
"key": "URLTest Check Interval",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:183"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:183"
]
},
{
"call": "URLTest Proxy Links",
"key": "URLTest Proxy Links",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:160"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:160"
]
},
{
"call": "URLTest Testing URL",
"key": "URLTest Testing URL",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:221"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:221"
]
},
{
"call": "URLTest Tolerance",
"key": "URLTest Tolerance",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:197"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:197"
]
},
{
"call": "User Domain List Type",
"key": "User Domain List Type",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:439"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:439"
]
},
{
"call": "User Domains",
"key": "User Domains",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:451"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:451"
]
},
{
"call": "User Domains List",
"key": "User Domains List",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:477"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:477"
]
},
{
"call": "User Subnet List Type",
"key": "User Subnet List Type",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:519"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:519"
]
},
{
"call": "User Subnets",
"key": "User Subnets",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:531"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:531"
]
},
{
"call": "User Subnets List",
"key": "User Subnets List",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:557"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:557"
]
},
{
@@ -1934,60 +1934,60 @@
"call": "Validation errors:",
"key": "Validation errors:",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:510",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:589"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:510",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:589"
]
},
{
"call": "View logs",
"key": "View logs",
"places": [
- "src\\podkop\\tabs\\diagnostic\\initController.ts:258",
- "src\\podkop\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:111"
+ "src\\netshift\\tabs\\diagnostic\\initController.ts:258",
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderAvailableActions.ts:111"
]
},
{
"call": "Visit Wiki",
"key": "Visit Wiki",
"places": [
- "src\\podkop\\tabs\\diagnostic\\partials\\renderWikiDisclaimer.ts:31"
+ "src\\netshift\\tabs\\diagnostic\\partials\\renderWikiDisclaimer.ts:31"
]
},
{
"call": "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links",
"key": "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:38",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:138",
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:161"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:38",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:138",
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:161"
]
},
{
"call": "Warning: %s cannot be used together with %s. Previous selections have been removed.",
"key": "Warning: %s cannot be used together with %s. Previous selections have been removed.",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:387"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:387"
]
},
{
"call": "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection.",
"key": "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection.",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\section.js:406"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\section.js:406"
]
},
{
"call": "YACD Secret Key",
"key": "YACD Secret Key",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:256"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:256"
]
},
{
"call": "You can select Output Network Interface, by default autodetect",
"key": "You can select Output Network Interface, by default autodetect",
"places": [
- "..\\luci-app-podkop\\htdocs\\luci-static\\resources\\view\\podkop\\settings.js:127"
+ "..\\luci-app-netshift\\htdocs\\luci-static\\resources\\view\\netshift\\settings.js:127"
]
}
]
\ No newline at end of file
diff --git a/fe-app-netshift/locales/netshift.pot b/fe-app-netshift/locales/netshift.pot
new file mode 100644
index 00000000..6e8ed43a
--- /dev/null
+++ b/fe-app-netshift/locales/netshift.pot
@@ -0,0 +1,1183 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2026 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the NETSHIFT package.
+# yandexru45 , 2026.
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: NETSHIFT\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2026-06-02 14:15+0300\n"
+"PO-Revision-Date: 2026-06-02 14:15+0300\n"
+"Last-Translator: yandexru45 \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src\netshift\tabs\dashboard\initController.ts:345
+msgid "✔ Enabled"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:356
+msgid "✔ Running"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:346
+msgid "✘ Disabled"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:357
+msgid "✘ Stopped"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:127
+msgid "Группировать по странам"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:128
+msgid "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:307
+msgid "Active Connections"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:106
+msgid "Additional marking rules found"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:247
+msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:251
+msgid "Applicable for SOCKS and Shadowsocks proxy"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:496
+msgid "At least one valid domain must be specified. Comments-only content is not allowed."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:577
+msgid "At least one valid subnet or IP must be specified. Comments-only content is not allowed."
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:47
+msgid "Available actions"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:65
+msgid "Bootsrap DNS"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:45
+msgid "Bootstrap DNS server"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:58
+msgid "Browser is not using FakeIP"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:57
+msgid "Browser is using FakeIP correctly"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:348
+msgid "Cache File Path"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:362
+msgid "Cache file path cannot be empty"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:27
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:28
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:27
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:25
+msgid "Cannot receive checks result"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:15
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:15
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:13
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:15
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:13
+msgid "Checking, please wait"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getCheckTitle.ts:2
+msgid "checks"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getMeta.ts:26
+msgid "Checks failed"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getMeta.ts:13
+msgid "Checks passed"
+msgstr ""
+
+#: src\validators\validateSubnet.ts:33
+msgid "CIDR must be between 0 and 32"
+msgstr ""
+
+#: src\partials\modal\renderModal.ts:26
+msgid "Close"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:351
+msgid "Community Lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:335
+msgid "Config File Path"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:27
+msgid "Configuration for NetShift service"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:23
+msgid "Configuration Type"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:12
+msgid "Connection Type"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:26
+msgid "Connection URL"
+msgstr ""
+
+#: src\partials\modal\renderModal.ts:20
+msgid "Copy"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\partials\renderWidget.ts:22
+msgid "Currently unavailable"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:80
+msgid "Dashboard"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\partials\renderSections.ts:19
+msgid "Dashboard currently unavailable"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:222
+msgid "Delay in milliseconds before reloading NetShift after interface UP"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:229
+msgid "Delay value cannot be empty"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:82
+msgid "DHCP has DNS server"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:65
+msgid "Diagnostics"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:83
+msgid "Disable autostart"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:265
+msgid "Disable QUIC"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:266
+msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:442
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:522
+msgid "Disabled"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:77
+msgid "DNS on router"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:319
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:15
+msgid "DNS over HTTPS (DoH)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:320
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:16
+msgid "DNS over TLS (DoT)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:316
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:12
+msgid "DNS Protocol Type"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:68
+msgid "DNS Rewrite TTL"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:329
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:24
+msgid "DNS Server"
+msgstr ""
+
+#: src\validators\validateDns.ts:7
+msgid "DNS server address cannot be empty"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderWikiDisclaimer.ts:26
+msgid "Do not panic, everything can be fixed, just..."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:306
+msgid "Domain Resolver"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:326
+msgid "Dont Touch My DHCP!"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:241
+#: src\netshift\tabs\dashboard\initController.ts:275
+msgid "Downlink"
+msgstr ""
+
+#: src\partials\modal\renderModal.ts:15
+msgid "Download"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:288
+msgid "Download Lists via Proxy/VPN"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:297
+msgid "Download Lists via specific proxy section"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:289
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:298
+msgid "Downloading all lists via specific Proxy/VPN"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:443
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:523
+msgid "Dynamic List"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:93
+msgid "Enable autostart"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:307
+msgid "Enable built-in DNS resolver for domains handled by this section"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:746
+msgid "Enable DNS resolve to get real IP when routing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:717
+msgid "Enable Mixed Proxy"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:126
+msgid "Enable Output Network Interface"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:718
+msgid "Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:237
+msgid "Enable YACD"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:246
+msgid "Enable YACD WAN Access"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:67
+msgid "Enter complete outbound configuration in JSON format"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:478
+msgid "Enter domain names separated by commas, spaces, or newlines. You can add comments using //"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:452
+msgid "Enter domain names without protocols, e.g. example.com or sub.example.com"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:532
+msgid "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:90
+msgid "Enter the subscription URL to fetch proxy configurations from your provider"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:187
+msgid "Every 1 minute"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:119
+msgid "Every 12 hours"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:117
+msgid "Every 3 hours"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:188
+msgid "Every 3 minutes"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:115
+msgid "Every 30 minutes"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:186
+msgid "Every 30 seconds"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:189
+msgid "Every 5 minutes"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:118
+msgid "Every 6 hours"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:120
+msgid "Every day"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:116
+msgid "Every hour"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:402
+msgid "Exclude NTP"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:403
+msgid "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN"
+msgstr ""
+
+#: src\helpers\copyToClipboard.ts:12
+msgid "Failed to copy!"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:229
+#: src\netshift\tabs\diagnostic\initController.ts:233
+#: src\netshift\tabs\diagnostic\initController.ts:263
+#: src\netshift\tabs\diagnostic\initController.ts:267
+#: src\netshift\tabs\diagnostic\initController.ts:304
+#: src\netshift\tabs\diagnostic\initController.ts:308
+#: src\netshift\tabs\diagnostic\initController.ts:342
+#: src\netshift\tabs\diagnostic\initController.ts:346
+msgid "Failed to execute!"
+msgstr ""
+
+#: src\netshift\methods\custom\getDashboardSections.ts:150
+#: src\netshift\methods\custom\getDashboardSections.ts:181
+#: src\netshift\methods\custom\getDashboardSections.ts:218
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:58
+msgid "Fastest"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:690
+msgid "Fully Routed IPs"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:102
+msgid "Get global check"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:224
+msgid "Global check"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:113
+msgid "How often to automatically update the subscription"
+msgstr ""
+
+#: src\netshift\api.ts:27
+msgid "HTTP error"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:129
+msgid "Install extended"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:129
+msgid "Install stable"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:189
+msgid "Interface Monitoring"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:221
+msgid "Interface Monitoring Delay"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:190
+msgid "Interface monitoring for Bad WAN"
+msgstr ""
+
+#: src\validators\validateDns.ts:23
+msgid "Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH"
+msgstr ""
+
+#: src\validators\validateDomain.ts:18
+#: src\validators\validateDomain.ts:27
+msgid "Invalid domain address"
+msgstr ""
+
+#: src\validators\validateSubnet.ts:11
+msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:90
+msgid "Invalid HY2 URL: insecure must be 0 or 1"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:77
+msgid "Invalid HY2 URL: invalid port number"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:30
+msgid "Invalid HY2 URL: missing credentials/server"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:47
+msgid "Invalid HY2 URL: missing host"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:41
+msgid "Invalid HY2 URL: missing host & port"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:36
+msgid "Invalid HY2 URL: missing password"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:50
+msgid "Invalid HY2 URL: missing port"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:18
+msgid "Invalid HY2 URL: must not contain spaces"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:12
+msgid "Invalid HY2 URL: must start with hysteria2:// or hy2://"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:108
+msgid "Invalid HY2 URL: obfs-password required when obfs is set"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:122
+msgid "Invalid HY2 URL: parsing failed"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:116
+msgid "Invalid HY2 URL: sni cannot be empty"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:98
+msgid "Invalid HY2 URL: unsupported obfs type"
+msgstr ""
+
+#: src\validators\validateIp.ts:11
+msgid "Invalid IP address"
+msgstr ""
+
+#: src\validators\validateOutboundJson.ts:9
+msgid "Invalid JSON format"
+msgstr ""
+
+#: src\validators\validatePath.ts:22
+msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:85
+msgid "Invalid port number. Must be between 1 and 65535"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:37
+msgid "Invalid Shadowsocks URL: decoded credentials must contain method:password"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:27
+msgid "Invalid Shadowsocks URL: missing credentials"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:46
+msgid "Invalid Shadowsocks URL: missing method and password separator \":\""
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:76
+msgid "Invalid Shadowsocks URL: missing port"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:67
+msgid "Invalid Shadowsocks URL: missing server"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:58
+msgid "Invalid Shadowsocks URL: missing server address"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:16
+msgid "Invalid Shadowsocks URL: must not contain spaces"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:8
+msgid "Invalid Shadowsocks URL: must start with ss://"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:91
+msgid "Invalid Shadowsocks URL: parsing failed"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:73
+msgid "Invalid SOCKS URL: invalid host format"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:63
+msgid "Invalid SOCKS URL: invalid port number"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:42
+msgid "Invalid SOCKS URL: missing host and port"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:51
+msgid "Invalid SOCKS URL: missing hostname or IP"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:56
+msgid "Invalid SOCKS URL: missing port"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:34
+msgid "Invalid SOCKS URL: missing username"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:19
+msgid "Invalid SOCKS URL: must not contain spaces"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:10
+msgid "Invalid SOCKS URL: must start with socks4://, socks4a://, or socks5://"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:77
+msgid "Invalid SOCKS URL: parsing failed"
+msgstr ""
+
+#: src\validators\validateTrojanUrl.ts:15
+msgid "Invalid Trojan URL: must not contain spaces"
+msgstr ""
+
+#: src\validators\validateTrojanUrl.ts:8
+msgid "Invalid Trojan URL: must start with trojan://"
+msgstr ""
+
+#: src\validators\validateTrojanUrl.ts:56
+msgid "Invalid Trojan URL: parsing failed"
+msgstr ""
+
+#: src\validators\validateUrl.ts:8
+#: src\validators\validateUrl.ts:31
+msgid "Invalid URL format"
+msgstr ""
+
+#: src\validators\validateVlessUrl.ts:110
+msgid "Invalid VLESS URL: parsing failed"
+msgstr ""
+
+#: src\validators\validateSubnet.ts:18
+msgid "IP address 0.0.0.0 is not allowed"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getMeta.ts:20
+msgid "Issues detected"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getNetshiftVersionRow.ts:48
+msgid "Latest"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:276
+msgid "List Update Frequency"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:598
+msgid "Local Domain Lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:621
+msgid "Local Subnet Lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:384
+msgid "Log Level"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:72
+msgid "Main DNS"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:311
+msgid "Memory Usage"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:730
+msgid "Mixed Proxy Port"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:198
+msgid "Monitored Interfaces"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:215
+msgid "Must be a number in the range of 50 - 1000"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:343
+msgid "NetShift"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:26
+msgid "NetShift Settings"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:327
+msgid "NetShift will not modify your DHCP configuration"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:260
+msgid "Network Interface"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:105
+msgid "No other marking rules found"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderCheckSection.ts:189
+msgid "Not implement yet"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:74
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:80
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:99
+msgid "Not responding"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:59
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:67
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:75
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:83
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:91
+msgid "Not running"
+msgstr ""
+
+#: src\helpers\withTimeout.ts:7
+msgid "Operation timed out"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:30
+msgid "Outbound Config"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:66
+msgid "Outbound Configuration"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getNetshiftVersionRow.ts:38
+msgid "Outdated"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:135
+msgid "Output Network Interface"
+msgstr ""
+
+#: src\validators\validatePath.ts:7
+msgid "Path cannot be empty"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:366
+msgid "Path must be absolute (start with /)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:375
+msgid "Path must contain at least one directory (like /tmp/cache.db)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:370
+msgid "Path must end with cache.db"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:107
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:115
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:123
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:131
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:139
+msgid "Pending"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:37
+msgid "Proxy Configuration URL"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:66
+msgid "Proxy traffic is not routed via FakeIP"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:65
+msgid "Proxy traffic is routed via FakeIP"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:385
+msgid "Regional options cannot be used together"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:644
+msgid "Remote Domain Lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:667
+msgid "Remote Subnet Lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:745
+msgid "Resolve real IP for routing"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:53
+msgid "Restart NetShift"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:51
+msgid "Router DNS is not routed through sing-box"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:50
+msgid "Router DNS is routed through sing-box"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:413
+msgid "Routing Excluded IPs"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:79
+msgid "Rules mangle counters"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:74
+msgid "Rules mangle exist"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:89
+msgid "Rules mangle output counters"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:84
+msgid "Rules mangle output exist"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:99
+msgid "Rules proxy counters"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:94
+msgid "Rules proxy exist"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderRunAction.ts:15
+msgid "Run Diagnostic"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:404
+msgid "Russia inside restrictions"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:257
+msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:36
+msgid "Sections"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:352
+msgid "Select a predefined list for routing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:13
+msgid "Select between VPN and Proxy connection methods for traffic routing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:13
+msgid "Select DNS protocol to use"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:277
+msgid "Select how often the domain or subnet lists are updated automatically"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:24
+msgid "Select how to configure the proxy"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:261
+msgid "Select network interface for VPN connection"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:330
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:25
+msgid "Select or enter DNS server address"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:349
+msgid "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:336
+msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:317
+msgid "Select the DNS protocol type for the domain resolver"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:440
+msgid "Select the list type for adding custom domains"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:520
+msgid "Select the list type for adding custom subnets"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:385
+msgid "Select the log level for sing-box"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:90
+msgid "Select the network interface from which the traffic will originate"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:136
+msgid "Select the network interface to which the traffic will originate"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:199
+msgid "Select the WAN interfaces to be monitored"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:27
+msgid "Selector"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:137
+msgid "Selector Proxy Links"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:340
+msgid "Services info"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:49
+msgid "Settings"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:292
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:120
+msgid "Show sing-box config"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:354
+msgid "Sing-box"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:77
+msgid "Sing-box autostart disabled"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:337
+msgid "Sing-box core changed, version:"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:62
+msgid "Sing-box installed"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:87
+msgid "Sing-box listening ports"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:82
+msgid "Sing-box process running"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:72
+msgid "Sing-box service exist"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:67
+msgid "Sing-box version is compatible (newer than 1.12.4)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:89
+msgid "Source Network Interface"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:414
+msgid "Specify a local IP address to be excluded from routing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:691
+msgid "Specify local IP addresses or subnets whose traffic will always be routed through the configured route"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:645
+msgid "Specify remote URLs to download and use domain lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:668
+msgid "Specify remote URLs to download and use subnet lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:599
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:622
+msgid "Specify the path to the list file located on the router filesystem"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:73
+msgid "Start NetShift"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:63
+msgid "Stop NetShift"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:29
+msgid "Subscription"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:112
+msgid "Subscription Update Interval"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:89
+msgid "Subscription URL"
+msgstr ""
+
+#: src\helpers\copyToClipboard.ts:10
+msgid "Successfully copied!"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:304
+msgid "System info"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderSystemInfo.ts:21
+msgid "System information"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:69
+msgid "Table exist"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\partials\renderSections.ts:108
+msgid "Test latency"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:444
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:524
+msgid "Text List"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:46
+msgid "The DNS server used to look up the IP address of an upstream DNS server"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:184
+msgid "The interval between connectivity tests"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:198
+msgid "The maximum difference in response times (ms) allowed when comparing servers"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:222
+msgid "The URL used to test server connectivity"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:69
+msgid "Time in seconds for DNS record caching (default: 60)"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:238
+msgid "Traffic"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:268
+msgid "Traffic Total"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderWikiDisclaimer.ts:25
+msgid "Troubleshooting"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:80
+msgid "TTL must be a positive number"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:75
+msgid "TTL value cannot be empty"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:321
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:17
+msgid "UDP (Unprotected DNS)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:250
+msgid "UDP over TCP"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:39
+#: src\netshift\tabs\diagnostic\initController.ts:40
+#: src\netshift\tabs\diagnostic\initController.ts:41
+#: src\netshift\tabs\diagnostic\initController.ts:42
+#: src\netshift\tabs\diagnostic\initController.ts:43
+#: src\netshift\tabs\diagnostic\initController.ts:44
+#: src\netshift\tabs\diagnostic\helpers\getNetshiftVersionRow.ts:7
+msgid "unknown"
+msgstr ""
+
+#: src\netshift\api.ts:40
+msgid "Unknown error"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:240
+#: src\netshift\tabs\dashboard\initController.ts:271
+msgid "Uplink"
+msgstr ""
+
+#: src\validators\validateProxyUrl.ts:37
+msgid "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://"
+msgstr ""
+
+#: src\validators\validateUrl.ts:17
+msgid "URL must use one of the following protocols:"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:28
+msgid "URLTest"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:183
+msgid "URLTest Check Interval"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:160
+msgid "URLTest Proxy Links"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:221
+msgid "URLTest Testing URL"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:197
+msgid "URLTest Tolerance"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:439
+msgid "User Domain List Type"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:451
+msgid "User Domains"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:477
+msgid "User Domains List"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:519
+msgid "User Subnet List Type"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:531
+msgid "User Subnets"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:557
+msgid "User Subnets List"
+msgstr ""
+
+#: src\validators\validateDns.ts:14
+#: src\validators\validateDns.ts:18
+#: src\validators\validateDomain.ts:13
+#: src\validators\validateDomain.ts:30
+#: src\validators\validateHysteriaUrl.ts:120
+#: src\validators\validateIp.ts:8
+#: src\validators\validateOutboundJson.ts:7
+#: src\validators\validatePath.ts:16
+#: src\validators\validateShadowsocksUrl.ts:95
+#: src\validators\validateSocksUrl.ts:80
+#: src\validators\validateSubnet.ts:38
+#: src\validators\validateTrojanUrl.ts:59
+#: src\validators\validateUrl.ts:28
+#: src\validators\validateVlessUrl.ts:108
+msgid "Valid"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:510
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:589
+msgid "Validation errors:"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:258
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:111
+msgid "View logs"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderWikiDisclaimer.ts:31
+msgid "Visit Wiki"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:38
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:138
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:161
+msgid "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:387
+msgid "Warning: %s cannot be used together with %s. Previous selections have been removed."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:406
+msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:256
+msgid "YACD Secret Key"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:127
+msgid "You can select Output Network Interface, by default autodetect"
+msgstr ""
diff --git a/luci-app-podkop/po/ru/podkop.po b/fe-app-netshift/locales/netshift.ru.po
similarity index 96%
rename from luci-app-podkop/po/ru/podkop.po
rename to fe-app-netshift/locales/netshift.ru.po
index 8bd2c236..2b4db66d 100644
--- a/luci-app-podkop/po/ru/podkop.po
+++ b/fe-app-netshift/locales/netshift.ru.po
@@ -1,14 +1,14 @@
-# RU translations for PODKOP package.
-# Copyright (C) 2026 THE PODKOP'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PODKOP package.
+# RU translations for NETSHIFT package.
+# Copyright (C) 2026 THE NETSHIFT'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the NETSHIFT package.
# yandexru45, 2026.
#
msgid ""
msgstr ""
-"Project-Id-Version: PODKOP\n"
+"Project-Id-Version: NETSHIFT\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-06-02 14:25+0300\n"
-"PO-Revision-Date: 2026-06-02 14:25+0300\n"
+"POT-Creation-Date: 2026-06-02 17:15+0300\n"
+"PO-Revision-Date: 2026-06-02 17:15+0300\n"
"Last-Translator: yandexru45\n"
"Language-Team: none\n"
"Language: ru\n"
@@ -101,8 +101,8 @@ msgstr "Списки сообщества"
msgid "Config File Path"
msgstr "Путь к файлу конфигурации"
-msgid "Configuration for Podkop service"
-msgstr "Настройки сервиса Podkop"
+msgid "Configuration for NetShift service"
+msgstr ""
msgid "Configuration Type"
msgstr "Тип конфигурации"
@@ -125,8 +125,8 @@ msgstr "Дашборд"
msgid "Dashboard currently unavailable"
msgstr "Дашборд сейчас недоступен"
-msgid "Delay in milliseconds before reloading podkop after interface UP"
-msgstr "Задержка в миллисекундах перед перезагрузкой podkop после поднятия интерфейса"
+msgid "Delay in milliseconds before reloading NetShift after interface UP"
+msgstr ""
msgid "Delay value cannot be empty"
msgstr "Значение задержки не может быть пустым"
@@ -476,6 +476,15 @@ msgstr "Наблюдаемые интерфейсы"
msgid "Must be a number in the range of 50 - 1000"
msgstr "Должно быть числом от 50 до 1000"
+msgid "NetShift"
+msgstr ""
+
+msgid "NetShift Settings"
+msgstr ""
+
+msgid "NetShift will not modify your DHCP configuration"
+msgstr ""
+
msgid "Network Interface"
msgstr "Сетевой интерфейс"
@@ -521,15 +530,6 @@ msgstr "Путь должен заканчиваться на cache.db"
msgid "Pending"
msgstr "Ожидает запуска"
-msgid "Podkop"
-msgstr "Podkop"
-
-msgid "Podkop Settings"
-msgstr "Настройки podkop"
-
-msgid "Podkop will not modify your DHCP configuration"
-msgstr "Podkop не будет изменять вашу конфигурацию DHCP."
-
msgid "Proxy Configuration URL"
msgstr "URL конфигурации прокси"
@@ -551,8 +551,8 @@ msgstr "Внешние списки подсетей"
msgid "Resolve real IP for routing"
msgstr "Разрешение реальных IP-адресов"
-msgid "Restart podkop"
-msgstr "Перезапустить Podkop"
+msgid "Restart NetShift"
+msgstr ""
msgid "Router DNS is not routed through sing-box"
msgstr "DNS роутера не проходит через sing-box"
@@ -698,11 +698,11 @@ msgstr "Укажите URL-адреса для загрузки и исполь
msgid "Specify the path to the list file located on the router filesystem"
msgstr "Укажите путь к файлу списка, расположенному в файловой системе маршрутизатора."
-msgid "Start podkop"
-msgstr "Запустить podkop"
+msgid "Start NetShift"
+msgstr ""
-msgid "Stop podkop"
-msgstr "Остановить podkop"
+msgid "Stop NetShift"
+msgstr ""
msgid "Subscription"
msgstr ""
diff --git a/fe-app-podkop/package.json b/fe-app-netshift/package.json
similarity index 90%
rename from fe-app-podkop/package.json
rename to fe-app-netshift/package.json
index 3b60630c..aebbf833 100644
--- a/fe-app-podkop/package.json
+++ b/fe-app-netshift/package.json
@@ -1,11 +1,11 @@
{
- "name": "fe-app-podkop",
+ "name": "fe-app-netshift",
"version": "1.0.0",
"license": "MIT",
"type": "module",
"scripts": {
"format": "prettier --write src",
- "format:js": "prettier --write ../luci-app-podkop/htdocs/luci-static/resources/view/podkop",
+ "format:js": "prettier --write ../luci-app-netshift/htdocs/luci-static/resources/view/netshift",
"lint": "eslint src --ext .ts,.tsx",
"lint:fix": "eslint src --ext .ts,.tsx --fix",
"build": "tsup src/main.ts",
diff --git a/fe-app-podkop/src/constants.ts b/fe-app-netshift/src/constants.ts
similarity index 97%
rename from fe-app-podkop/src/constants.ts
rename to fe-app-netshift/src/constants.ts
index 19e0907c..60596254 100644
--- a/fe-app-podkop/src/constants.ts
+++ b/fe-app-netshift/src/constants.ts
@@ -4,7 +4,7 @@ export const STATUS_COLORS = {
WARNING: '#ff9800',
};
-export const PODKOP_LUCI_APP_VERSION = '__COMPILED_VERSION_VARIABLE__';
+export const NETSHIFT_LUCI_APP_VERSION = '__COMPILED_VERSION_VARIABLE__';
export const FAKEIP_CHECK_DOMAIN = 'fakeip.podkop.fyi';
export const IP_CHECK_DOMAIN = 'ip.podkop.fyi';
diff --git a/fe-app-podkop/src/helpers/copyToClipboard.ts b/fe-app-netshift/src/helpers/copyToClipboard.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/copyToClipboard.ts
rename to fe-app-netshift/src/helpers/copyToClipboard.ts
diff --git a/fe-app-podkop/src/helpers/downloadAsTxt.ts b/fe-app-netshift/src/helpers/downloadAsTxt.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/downloadAsTxt.ts
rename to fe-app-netshift/src/helpers/downloadAsTxt.ts
diff --git a/fe-app-podkop/src/helpers/executeShellCommand.ts b/fe-app-netshift/src/helpers/executeShellCommand.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/executeShellCommand.ts
rename to fe-app-netshift/src/helpers/executeShellCommand.ts
diff --git a/fe-app-podkop/src/helpers/getClashApiUrl.ts b/fe-app-netshift/src/helpers/getClashApiUrl.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/getClashApiUrl.ts
rename to fe-app-netshift/src/helpers/getClashApiUrl.ts
diff --git a/fe-app-podkop/src/helpers/getProxyUrlName.ts b/fe-app-netshift/src/helpers/getProxyUrlName.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/getProxyUrlName.ts
rename to fe-app-netshift/src/helpers/getProxyUrlName.ts
diff --git a/fe-app-podkop/src/helpers/index.ts b/fe-app-netshift/src/helpers/index.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/index.ts
rename to fe-app-netshift/src/helpers/index.ts
diff --git a/fe-app-podkop/src/helpers/injectGlobalStyles.ts b/fe-app-netshift/src/helpers/injectGlobalStyles.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/injectGlobalStyles.ts
rename to fe-app-netshift/src/helpers/injectGlobalStyles.ts
diff --git a/fe-app-podkop/src/helpers/insertIf.ts b/fe-app-netshift/src/helpers/insertIf.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/insertIf.ts
rename to fe-app-netshift/src/helpers/insertIf.ts
diff --git a/fe-app-podkop/src/helpers/maskIP.ts b/fe-app-netshift/src/helpers/maskIP.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/maskIP.ts
rename to fe-app-netshift/src/helpers/maskIP.ts
diff --git a/fe-app-podkop/src/helpers/normalizeCompiledVersion.ts b/fe-app-netshift/src/helpers/normalizeCompiledVersion.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/normalizeCompiledVersion.ts
rename to fe-app-netshift/src/helpers/normalizeCompiledVersion.ts
diff --git a/fe-app-podkop/src/helpers/onMount.ts b/fe-app-netshift/src/helpers/onMount.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/onMount.ts
rename to fe-app-netshift/src/helpers/onMount.ts
diff --git a/fe-app-podkop/src/helpers/parseQueryString.ts b/fe-app-netshift/src/helpers/parseQueryString.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/parseQueryString.ts
rename to fe-app-netshift/src/helpers/parseQueryString.ts
diff --git a/fe-app-podkop/src/helpers/parseValueList.ts b/fe-app-netshift/src/helpers/parseValueList.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/parseValueList.ts
rename to fe-app-netshift/src/helpers/parseValueList.ts
diff --git a/fe-app-podkop/src/helpers/preserveScrollForPage.ts b/fe-app-netshift/src/helpers/preserveScrollForPage.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/preserveScrollForPage.ts
rename to fe-app-netshift/src/helpers/preserveScrollForPage.ts
diff --git a/fe-app-podkop/src/helpers/prettyBytes.ts b/fe-app-netshift/src/helpers/prettyBytes.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/prettyBytes.ts
rename to fe-app-netshift/src/helpers/prettyBytes.ts
diff --git a/fe-app-podkop/src/helpers/removeVersionPrefix.ts b/fe-app-netshift/src/helpers/removeVersionPrefix.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/removeVersionPrefix.ts
rename to fe-app-netshift/src/helpers/removeVersionPrefix.ts
diff --git a/fe-app-podkop/src/helpers/showToast.ts b/fe-app-netshift/src/helpers/showToast.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/showToast.ts
rename to fe-app-netshift/src/helpers/showToast.ts
diff --git a/fe-app-podkop/src/helpers/splitProxyString.ts b/fe-app-netshift/src/helpers/splitProxyString.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/splitProxyString.ts
rename to fe-app-netshift/src/helpers/splitProxyString.ts
diff --git a/fe-app-podkop/src/helpers/svgEl.ts b/fe-app-netshift/src/helpers/svgEl.ts
similarity index 100%
rename from fe-app-podkop/src/helpers/svgEl.ts
rename to fe-app-netshift/src/helpers/svgEl.ts
diff --git a/fe-app-podkop/src/helpers/tests/maskIp.test.js b/fe-app-netshift/src/helpers/tests/maskIp.test.js
similarity index 100%
rename from fe-app-podkop/src/helpers/tests/maskIp.test.js
rename to fe-app-netshift/src/helpers/tests/maskIp.test.js
diff --git a/fe-app-podkop/src/helpers/withTimeout.ts b/fe-app-netshift/src/helpers/withTimeout.ts
similarity index 94%
rename from fe-app-podkop/src/helpers/withTimeout.ts
rename to fe-app-netshift/src/helpers/withTimeout.ts
index e2207409..d9319af4 100644
--- a/fe-app-podkop/src/helpers/withTimeout.ts
+++ b/fe-app-netshift/src/helpers/withTimeout.ts
@@ -1,4 +1,4 @@
-import { logger } from '../podkop';
+import { logger } from '../netshift';
export async function withTimeout(
promise: Promise,
diff --git a/fe-app-podkop/src/icons/index.ts b/fe-app-netshift/src/icons/index.ts
similarity index 100%
rename from fe-app-podkop/src/icons/index.ts
rename to fe-app-netshift/src/icons/index.ts
diff --git a/fe-app-podkop/src/icons/renderBookOpenTextIcon24.ts b/fe-app-netshift/src/icons/renderBookOpenTextIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderBookOpenTextIcon24.ts
rename to fe-app-netshift/src/icons/renderBookOpenTextIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderCheckIcon24.ts b/fe-app-netshift/src/icons/renderCheckIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderCheckIcon24.ts
rename to fe-app-netshift/src/icons/renderCheckIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderCircleAlertIcon24.ts b/fe-app-netshift/src/icons/renderCircleAlertIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderCircleAlertIcon24.ts
rename to fe-app-netshift/src/icons/renderCircleAlertIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderCircleCheckBigIcon24.ts b/fe-app-netshift/src/icons/renderCircleCheckBigIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderCircleCheckBigIcon24.ts
rename to fe-app-netshift/src/icons/renderCircleCheckBigIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderCircleCheckIcon24.ts b/fe-app-netshift/src/icons/renderCircleCheckIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderCircleCheckIcon24.ts
rename to fe-app-netshift/src/icons/renderCircleCheckIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderCirclePlayIcon24.ts b/fe-app-netshift/src/icons/renderCirclePlayIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderCirclePlayIcon24.ts
rename to fe-app-netshift/src/icons/renderCirclePlayIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderCircleSlashIcon24.ts b/fe-app-netshift/src/icons/renderCircleSlashIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderCircleSlashIcon24.ts
rename to fe-app-netshift/src/icons/renderCircleSlashIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderCircleStopIcon24.ts b/fe-app-netshift/src/icons/renderCircleStopIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderCircleStopIcon24.ts
rename to fe-app-netshift/src/icons/renderCircleStopIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderCircleXIcon24.ts b/fe-app-netshift/src/icons/renderCircleXIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderCircleXIcon24.ts
rename to fe-app-netshift/src/icons/renderCircleXIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderCogIcon24.ts b/fe-app-netshift/src/icons/renderCogIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderCogIcon24.ts
rename to fe-app-netshift/src/icons/renderCogIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderLoaderCircleIcon24.ts b/fe-app-netshift/src/icons/renderLoaderCircleIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderLoaderCircleIcon24.ts
rename to fe-app-netshift/src/icons/renderLoaderCircleIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderPauseIcon24.ts b/fe-app-netshift/src/icons/renderPauseIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderPauseIcon24.ts
rename to fe-app-netshift/src/icons/renderPauseIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderPlayIcon24.ts b/fe-app-netshift/src/icons/renderPlayIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderPlayIcon24.ts
rename to fe-app-netshift/src/icons/renderPlayIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderRotateCcwIcon24.ts b/fe-app-netshift/src/icons/renderRotateCcwIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderRotateCcwIcon24.ts
rename to fe-app-netshift/src/icons/renderRotateCcwIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderSearchIcon24.ts b/fe-app-netshift/src/icons/renderSearchIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderSearchIcon24.ts
rename to fe-app-netshift/src/icons/renderSearchIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderSquareChartGanttIcon24.ts b/fe-app-netshift/src/icons/renderSquareChartGanttIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderSquareChartGanttIcon24.ts
rename to fe-app-netshift/src/icons/renderSquareChartGanttIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderTriangleAlertIcon24.ts b/fe-app-netshift/src/icons/renderTriangleAlertIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderTriangleAlertIcon24.ts
rename to fe-app-netshift/src/icons/renderTriangleAlertIcon24.ts
diff --git a/fe-app-podkop/src/icons/renderXIcon24.ts b/fe-app-netshift/src/icons/renderXIcon24.ts
similarity index 100%
rename from fe-app-podkop/src/icons/renderXIcon24.ts
rename to fe-app-netshift/src/icons/renderXIcon24.ts
diff --git a/fe-app-podkop/src/luci.d.ts b/fe-app-netshift/src/luci.d.ts
similarity index 100%
rename from fe-app-podkop/src/luci.d.ts
rename to fe-app-netshift/src/luci.d.ts
diff --git a/fe-app-podkop/src/main.ts b/fe-app-netshift/src/main.ts
similarity index 90%
rename from fe-app-podkop/src/main.ts
rename to fe-app-netshift/src/main.ts
index 497e60ef..3df7b463 100644
--- a/fe-app-podkop/src/main.ts
+++ b/fe-app-netshift/src/main.ts
@@ -9,5 +9,5 @@ if (typeof structuredClone !== 'function')
export * from './validators';
export * from './helpers';
-export * from './podkop';
+export * from './netshift';
export * from './constants';
diff --git a/fe-app-podkop/src/podkop/api.ts b/fe-app-netshift/src/netshift/api.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/api.ts
rename to fe-app-netshift/src/netshift/api.ts
diff --git a/fe-app-netshift/src/netshift/fetchers/fetchServicesInfo.ts b/fe-app-netshift/src/netshift/fetchers/fetchServicesInfo.ts
new file mode 100644
index 00000000..f2886309
--- /dev/null
+++ b/fe-app-netshift/src/netshift/fetchers/fetchServicesInfo.ts
@@ -0,0 +1,32 @@
+import { NetShiftShellMethods } from '../methods';
+import { store } from '../services';
+
+export async function fetchServicesInfo() {
+ const [netshift, singbox] = await Promise.all([
+ NetShiftShellMethods.getStatus(),
+ NetShiftShellMethods.getSingBoxStatus(),
+ ]);
+
+ if (!netshift.success || !singbox.success) {
+ store.set({
+ servicesInfoWidget: {
+ loading: false,
+ failed: true,
+ data: { singbox: 0, netshift: 0 },
+ },
+ });
+ }
+
+ if (netshift.success && singbox.success) {
+ store.set({
+ servicesInfoWidget: {
+ loading: false,
+ failed: false,
+ data: {
+ singbox: singbox.data.running,
+ netshift: netshift.data.enabled,
+ },
+ },
+ });
+ }
+}
diff --git a/fe-app-podkop/src/podkop/fetchers/index.ts b/fe-app-netshift/src/netshift/fetchers/index.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/fetchers/index.ts
rename to fe-app-netshift/src/netshift/fetchers/index.ts
diff --git a/fe-app-podkop/src/podkop/index.ts b/fe-app-netshift/src/netshift/index.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/index.ts
rename to fe-app-netshift/src/netshift/index.ts
diff --git a/fe-app-podkop/src/podkop/methods/custom/getClashApiSecret.ts b/fe-app-netshift/src/netshift/methods/custom/getClashApiSecret.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/methods/custom/getClashApiSecret.ts
rename to fe-app-netshift/src/netshift/methods/custom/getClashApiSecret.ts
diff --git a/fe-app-netshift/src/netshift/methods/custom/getConfigSections.ts b/fe-app-netshift/src/netshift/methods/custom/getConfigSections.ts
new file mode 100644
index 00000000..1f40d04f
--- /dev/null
+++ b/fe-app-netshift/src/netshift/methods/custom/getConfigSections.ts
@@ -0,0 +1,5 @@
+import { NetShift } from '../../types';
+
+export async function getConfigSections(): Promise {
+ return uci.load('netshift').then(() => uci.sections('netshift'));
+}
diff --git a/fe-app-podkop/src/podkop/methods/custom/getDashboardSections.ts b/fe-app-netshift/src/netshift/methods/custom/getDashboardSections.ts
similarity index 97%
rename from fe-app-podkop/src/podkop/methods/custom/getDashboardSections.ts
rename to fe-app-netshift/src/netshift/methods/custom/getDashboardSections.ts
index fedb0723..9248568f 100644
--- a/fe-app-podkop/src/podkop/methods/custom/getDashboardSections.ts
+++ b/fe-app-netshift/src/netshift/methods/custom/getDashboardSections.ts
@@ -1,16 +1,16 @@
import { getConfigSections } from './getConfigSections';
-import { Podkop } from '../../types';
+import { NetShift } from '../../types';
import { getProxyUrlName, splitProxyString } from '../../../helpers';
-import { PodkopShellMethods } from '../shell';
+import { NetShiftShellMethods } from '../shell';
interface IGetDashboardSectionsResponse {
success: boolean;
- data: Podkop.OutboundGroup[];
+ data: NetShift.OutboundGroup[];
}
export async function getDashboardSections(): Promise {
const configSections = await getConfigSections();
- const clashProxies = await PodkopShellMethods.getClashApiProxies();
+ const clashProxies = await NetShiftShellMethods.getClashApiProxies();
if (!clashProxies.success) {
return {
diff --git a/fe-app-podkop/src/podkop/methods/custom/index.ts b/fe-app-netshift/src/netshift/methods/custom/index.ts
similarity index 86%
rename from fe-app-podkop/src/podkop/methods/custom/index.ts
rename to fe-app-netshift/src/netshift/methods/custom/index.ts
index 8aba225c..13a8f0d3 100644
--- a/fe-app-podkop/src/podkop/methods/custom/index.ts
+++ b/fe-app-netshift/src/netshift/methods/custom/index.ts
@@ -2,7 +2,7 @@ import { getConfigSections } from './getConfigSections';
import { getDashboardSections } from './getDashboardSections';
import { getClashApiSecret } from './getClashApiSecret';
-export const CustomPodkopMethods = {
+export const CustomNetShiftMethods = {
getConfigSections,
getDashboardSections,
getClashApiSecret,
diff --git a/fe-app-podkop/src/podkop/methods/fakeip/getFakeIpCheck.ts b/fe-app-netshift/src/netshift/methods/fakeip/getFakeIpCheck.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/methods/fakeip/getFakeIpCheck.ts
rename to fe-app-netshift/src/netshift/methods/fakeip/getFakeIpCheck.ts
diff --git a/fe-app-podkop/src/podkop/methods/fakeip/getIpCheck.ts b/fe-app-netshift/src/netshift/methods/fakeip/getIpCheck.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/methods/fakeip/getIpCheck.ts
rename to fe-app-netshift/src/netshift/methods/fakeip/getIpCheck.ts
diff --git a/fe-app-podkop/src/podkop/methods/fakeip/index.ts b/fe-app-netshift/src/netshift/methods/fakeip/index.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/methods/fakeip/index.ts
rename to fe-app-netshift/src/netshift/methods/fakeip/index.ts
diff --git a/fe-app-podkop/src/podkop/methods/index.ts b/fe-app-netshift/src/netshift/methods/index.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/methods/index.ts
rename to fe-app-netshift/src/netshift/methods/index.ts
diff --git a/fe-app-podkop/src/podkop/methods/shell/callBaseMethod.ts b/fe-app-netshift/src/netshift/methods/shell/callBaseMethod.ts
similarity index 77%
rename from fe-app-podkop/src/podkop/methods/shell/callBaseMethod.ts
rename to fe-app-netshift/src/netshift/methods/shell/callBaseMethod.ts
index 519cd369..f278a3de 100644
--- a/fe-app-podkop/src/podkop/methods/shell/callBaseMethod.ts
+++ b/fe-app-netshift/src/netshift/methods/shell/callBaseMethod.ts
@@ -1,11 +1,11 @@
import { executeShellCommand } from '../../../helpers';
-import { Podkop } from '../../types';
+import { NetShift } from '../../types';
export async function callBaseMethod(
- method: Podkop.AvailableMethods,
+ method: NetShift.AvailableMethods,
args: string[] = [],
- command: string = '/usr/bin/podkop',
-): Promise> {
+ command: string = '/usr/bin/netshift',
+): Promise> {
const response = await executeShellCommand({
command,
args: [method as string, ...args],
diff --git a/fe-app-netshift/src/netshift/methods/shell/index.ts b/fe-app-netshift/src/netshift/methods/shell/index.ts
new file mode 100644
index 00000000..428a42c5
--- /dev/null
+++ b/fe-app-netshift/src/netshift/methods/shell/index.ts
@@ -0,0 +1,129 @@
+import { callBaseMethod } from './callBaseMethod';
+import { ClashAPI, NetShift } from '../../types';
+import { executeShellCommand } from '../../../helpers';
+
+interface SingBoxComponentActionResult {
+ success: boolean;
+ version?: string;
+ message?: string;
+}
+
+export const NetShiftShellMethods = {
+ checkDNSAvailable: async () =>
+ callBaseMethod(
+ NetShift.AvailableMethods.CHECK_DNS_AVAILABLE,
+ ),
+ checkFakeIP: async () =>
+ callBaseMethod(
+ NetShift.AvailableMethods.CHECK_FAKEIP,
+ ),
+ checkNftRules: async () =>
+ callBaseMethod(
+ NetShift.AvailableMethods.CHECK_NFT_RULES,
+ ),
+ getStatus: async () =>
+ callBaseMethod(NetShift.AvailableMethods.GET_STATUS),
+ checkSingBox: async () =>
+ callBaseMethod(
+ NetShift.AvailableMethods.CHECK_SING_BOX,
+ ),
+ getSingBoxStatus: async () =>
+ callBaseMethod(
+ NetShift.AvailableMethods.GET_SING_BOX_STATUS,
+ ),
+ getClashApiProxies: async () =>
+ callBaseMethod(NetShift.AvailableMethods.CLASH_API, [
+ NetShift.AvailableClashAPIMethods.GET_PROXIES,
+ ]),
+ getClashApiProxyLatency: async (tag: string) =>
+ callBaseMethod(
+ NetShift.AvailableMethods.CLASH_API,
+ [NetShift.AvailableClashAPIMethods.GET_PROXY_LATENCY, tag, '5000'],
+ ),
+ getClashApiGroupLatency: async (tag: string) =>
+ callBaseMethod(
+ NetShift.AvailableMethods.CLASH_API,
+ [NetShift.AvailableClashAPIMethods.GET_GROUP_LATENCY, tag, '10000'],
+ ),
+ setClashApiGroupProxy: async (group: string, proxy: string) =>
+ callBaseMethod(NetShift.AvailableMethods.CLASH_API, [
+ NetShift.AvailableClashAPIMethods.SET_GROUP_PROXY,
+ group,
+ proxy,
+ ]),
+ restart: async () =>
+ callBaseMethod(
+ NetShift.AvailableMethods.RESTART,
+ [],
+ '/etc/init.d/netshift',
+ ),
+ start: async () =>
+ callBaseMethod(
+ NetShift.AvailableMethods.START,
+ [],
+ '/etc/init.d/netshift',
+ ),
+ stop: async () =>
+ callBaseMethod(
+ NetShift.AvailableMethods.STOP,
+ [],
+ '/etc/init.d/netshift',
+ ),
+ enable: async () =>
+ callBaseMethod(
+ NetShift.AvailableMethods.ENABLE,
+ [],
+ '/etc/init.d/netshift',
+ ),
+ disable: async () =>
+ callBaseMethod(
+ NetShift.AvailableMethods.DISABLE,
+ [],
+ '/etc/init.d/netshift',
+ ),
+ globalCheck: async () =>
+ callBaseMethod(NetShift.AvailableMethods.GLOBAL_CHECK),
+ showSingBoxConfig: async () =>
+ callBaseMethod(NetShift.AvailableMethods.SHOW_SING_BOX_CONFIG),
+ checkLogs: async () =>
+ callBaseMethod(NetShift.AvailableMethods.CHECK_LOGS),
+ getSystemInfo: async () =>
+ callBaseMethod(
+ NetShift.AvailableMethods.GET_SYSTEM_INFO,
+ ),
+ subscriptionUpdate: async () =>
+ callBaseMethod(NetShift.AvailableMethods.SUBSCRIPTION_UPDATE),
+ singBoxComponentAction: async (
+ action: 'install_extended' | 'install_stable' | 'check_update',
+ ): Promise => {
+ const response = await executeShellCommand({
+ command: '/usr/bin/netshift',
+ args: ['component_action', 'sing_box', action],
+ timeout: 600000,
+ });
+
+ if (response.stdout) {
+ try {
+ const parsed = JSON.parse(
+ response.stdout,
+ ) as SingBoxComponentActionResult;
+
+ return {
+ success: Boolean(parsed.success),
+ version: parsed.version,
+ message: parsed.message,
+ };
+ } catch (_e) {
+ return {
+ success: false,
+ message: response.stdout,
+ };
+ }
+ }
+
+ return {
+ success: false,
+ message: response.stderr || '',
+ };
+ },
+};
diff --git a/fe-app-podkop/src/podkop/services/core.service.ts b/fe-app-netshift/src/netshift/services/core.service.ts
similarity index 71%
rename from fe-app-podkop/src/podkop/services/core.service.ts
rename to fe-app-netshift/src/netshift/services/core.service.ts
index 79b63dbb..e401565c 100644
--- a/fe-app-podkop/src/podkop/services/core.service.ts
+++ b/fe-app-netshift/src/netshift/services/core.service.ts
@@ -1,8 +1,8 @@
import { TabServiceInstance } from './tab.service';
import { store } from './store.service';
import { logger } from './logger.service';
-import { PodkopLogWatcher } from './podkopLogWatcher.service';
-import { PodkopShellMethods } from '../methods';
+import { NetShiftLogWatcher } from './netshiftLogWatcher.service';
+import { NetShiftShellMethods } from '../methods';
export function coreService() {
TabServiceInstance.onChange((activeId, tabs) => {
@@ -15,11 +15,11 @@ export function coreService() {
});
});
- const watcher = PodkopLogWatcher.getInstance();
+ const watcher = NetShiftLogWatcher.getInstance();
watcher.init(
async () => {
- const logs = await PodkopShellMethods.checkLogs();
+ const logs = await NetShiftShellMethods.checkLogs();
if (logs.success) {
return logs.data as string;
@@ -34,7 +34,7 @@ export function coreService() {
line.toLowerCase().includes('[error]') ||
line.toLowerCase().includes('[fatal]')
) {
- ui.addNotification('Podkop Error', E('div', {}, line), 'error');
+ ui.addNotification('NetShift Error', E('div', {}, line), 'error');
}
},
},
diff --git a/fe-app-podkop/src/podkop/services/index.ts b/fe-app-netshift/src/netshift/services/index.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/services/index.ts
rename to fe-app-netshift/src/netshift/services/index.ts
diff --git a/fe-app-podkop/src/podkop/services/logger.service.ts b/fe-app-netshift/src/netshift/services/logger.service.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/services/logger.service.ts
rename to fe-app-netshift/src/netshift/services/logger.service.ts
diff --git a/fe-app-podkop/src/podkop/services/podkopLogWatcher.service.ts b/fe-app-netshift/src/netshift/services/netshiftLogWatcher.service.ts
similarity index 68%
rename from fe-app-podkop/src/podkop/services/podkopLogWatcher.service.ts
rename to fe-app-netshift/src/netshift/services/netshiftLogWatcher.service.ts
index e09b288e..560af7cc 100644
--- a/fe-app-podkop/src/podkop/services/podkopLogWatcher.service.ts
+++ b/fe-app-netshift/src/netshift/services/netshiftLogWatcher.service.ts
@@ -2,13 +2,13 @@ import { logger } from './logger.service';
export type LogFetcher = () => Promise | string;
-export interface PodkopLogWatcherOptions {
+export interface NetShiftLogWatcherOptions {
intervalMs?: number;
onNewLog?: (line: string) => void;
}
-export class PodkopLogWatcher {
- private static instance: PodkopLogWatcher;
+export class NetShiftLogWatcher {
+ private static instance: NetShiftLogWatcher;
private fetcher?: LogFetcher;
private onNewLog?: (line: string) => void;
private intervalMs = 5000;
@@ -26,31 +26,31 @@ export class PodkopLogWatcher {
}
}
- static getInstance(): PodkopLogWatcher {
- if (!PodkopLogWatcher.instance) {
- PodkopLogWatcher.instance = new PodkopLogWatcher();
+ static getInstance(): NetShiftLogWatcher {
+ if (!NetShiftLogWatcher.instance) {
+ NetShiftLogWatcher.instance = new NetShiftLogWatcher();
}
- return PodkopLogWatcher.instance;
+ return NetShiftLogWatcher.instance;
}
- init(fetcher: LogFetcher, options?: PodkopLogWatcherOptions): void {
+ init(fetcher: LogFetcher, options?: NetShiftLogWatcherOptions): void {
this.fetcher = fetcher;
this.onNewLog = options?.onNewLog;
this.intervalMs = options?.intervalMs ?? 5000;
logger.info(
- '[PodkopLogWatcher]',
+ '[NetShiftLogWatcher]',
`initialized (interval: ${this.intervalMs}ms)`,
);
}
async checkOnce(): Promise {
if (!this.fetcher) {
- logger.warn('[PodkopLogWatcher]', 'fetcher not found');
+ logger.warn('[NetShiftLogWatcher]', 'fetcher not found');
return;
}
if (this.paused) {
- logger.debug('[PodkopLogWatcher]', 'skipped check — tab not visible');
+ logger.debug('[NetShiftLogWatcher]', 'skipped check — tab not visible');
return;
}
@@ -70,21 +70,21 @@ export class PodkopLogWatcher {
this.lastLines = new Set(arr.slice(-500));
}
} catch (err) {
- logger.error('[PodkopLogWatcher]', 'failed to read logs:', err);
+ logger.error('[NetShiftLogWatcher]', 'failed to read logs:', err);
}
}
start(): void {
if (this.running) return;
if (!this.fetcher) {
- logger.warn('[PodkopLogWatcher]', 'attempted to start without fetcher');
+ logger.warn('[NetShiftLogWatcher]', 'attempted to start without fetcher');
return;
}
this.running = true;
this.timer = setInterval(() => this.checkOnce(), this.intervalMs);
logger.info(
- '[PodkopLogWatcher]',
+ '[NetShiftLogWatcher]',
`started (interval: ${this.intervalMs}ms)`,
);
}
@@ -93,24 +93,24 @@ export class PodkopLogWatcher {
if (!this.running) return;
this.running = false;
if (this.timer) clearInterval(this.timer);
- logger.info('[PodkopLogWatcher]', 'stopped');
+ logger.info('[NetShiftLogWatcher]', 'stopped');
}
pause(): void {
if (!this.running || this.paused) return;
this.paused = true;
- logger.info('[PodkopLogWatcher]', 'paused (tab not visible)');
+ logger.info('[NetShiftLogWatcher]', 'paused (tab not visible)');
}
resume(): void {
if (!this.running || !this.paused) return;
this.paused = false;
- logger.info('[PodkopLogWatcher]', 'resumed (tab active)');
+ logger.info('[NetShiftLogWatcher]', 'resumed (tab active)');
this.checkOnce(); // сразу проверить, не появились ли новые логи
}
reset(): void {
this.lastLines.clear();
- logger.info('[PodkopLogWatcher]', 'log history reset');
+ logger.info('[NetShiftLogWatcher]', 'log history reset');
}
}
diff --git a/fe-app-podkop/src/podkop/services/socket.service.ts b/fe-app-netshift/src/netshift/services/socket.service.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/services/socket.service.ts
rename to fe-app-netshift/src/netshift/services/socket.service.ts
diff --git a/fe-app-podkop/src/podkop/services/store.service.ts b/fe-app-netshift/src/netshift/services/store.service.ts
similarity index 95%
rename from fe-app-podkop/src/podkop/services/store.service.ts
rename to fe-app-netshift/src/netshift/services/store.service.ts
index ca9db829..255d61e3 100644
--- a/fe-app-podkop/src/podkop/services/store.service.ts
+++ b/fe-app-netshift/src/netshift/services/store.service.ts
@@ -1,4 +1,4 @@
-import { Podkop } from '../types';
+import { NetShift } from '../types';
import { initialDiagnosticStore } from '../tabs/diagnostic/diagnostic.store';
function jsonStableStringify(obj: T): string {
@@ -159,12 +159,12 @@ export interface StoreType {
servicesInfoWidget: {
loading: boolean;
failed: boolean;
- data: { singbox: number; podkop: number };
+ data: { singbox: number; netshift: number };
};
sectionsWidget: {
loading: boolean;
failed: boolean;
- data: Podkop.OutboundGroup[];
+ data: NetShift.OutboundGroup[];
latencyFetching: boolean;
};
diagnosticsRunAction: {
@@ -184,8 +184,8 @@ export interface StoreType {
};
diagnosticsSystemInfo: {
loading: boolean;
- podkop_version: string;
- podkop_latest_version: string;
+ netshift_version: string;
+ netshift_latest_version: string;
luci_app_version: string;
sing_box_version: string;
openwrt_version: string;
@@ -217,7 +217,7 @@ const initialStore: StoreType = {
servicesInfoWidget: {
loading: true,
failed: false,
- data: { singbox: 0, podkop: 0 },
+ data: { singbox: 0, netshift: 0 },
},
sectionsWidget: {
loading: true,
diff --git a/fe-app-podkop/src/podkop/services/tab.service.ts b/fe-app-netshift/src/netshift/services/tab.service.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/services/tab.service.ts
rename to fe-app-netshift/src/netshift/services/tab.service.ts
diff --git a/fe-app-podkop/src/podkop/tabs/dashboard/index.ts b/fe-app-netshift/src/netshift/tabs/dashboard/index.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/dashboard/index.ts
rename to fe-app-netshift/src/netshift/tabs/dashboard/index.ts
diff --git a/fe-app-podkop/src/podkop/tabs/dashboard/initController.ts b/fe-app-netshift/src/netshift/tabs/dashboard/initController.ts
similarity index 95%
rename from fe-app-podkop/src/podkop/tabs/dashboard/initController.ts
rename to fe-app-netshift/src/netshift/tabs/dashboard/initController.ts
index 2b8c9487..f30c414d 100644
--- a/fe-app-podkop/src/podkop/tabs/dashboard/initController.ts
+++ b/fe-app-netshift/src/netshift/tabs/dashboard/initController.ts
@@ -4,7 +4,7 @@ import {
preserveScrollForPage,
} from '../../../helpers';
import { prettyBytes } from '../../../helpers/prettyBytes';
-import { CustomPodkopMethods, PodkopShellMethods } from '../../methods';
+import { CustomNetShiftMethods, NetShiftShellMethods } from '../../methods';
import { logger, socket, store, StoreType } from '../../services';
import { renderSections, renderWidget } from './partials';
import { fetchServicesInfo } from '../../fetchers';
@@ -22,7 +22,7 @@ async function fetchDashboardSections() {
},
});
- const { data, success } = await CustomPodkopMethods.getDashboardSections();
+ const { data, success } = await CustomNetShiftMethods.getDashboardSections();
if (!success) {
logger.error('[DASHBOARD]', 'fetchDashboardSections: failed to fetch');
@@ -122,7 +122,7 @@ async function connectToClashSockets() {
// Handlers
async function handleChooseOutbound(selector: string, tag: string) {
- await PodkopShellMethods.setClashApiGroupProxy(selector, tag);
+ await NetShiftShellMethods.setClashApiGroupProxy(selector, tag);
await fetchDashboardSections();
}
@@ -134,7 +134,7 @@ async function handleTestGroupLatency(tag: string) {
},
});
- await PodkopShellMethods.getClashApiGroupLatency(tag);
+ await NetShiftShellMethods.getClashApiGroupLatency(tag);
await fetchDashboardSections();
store.set({
@@ -153,7 +153,7 @@ async function handleTestProxyLatency(tag: string) {
},
});
- await PodkopShellMethods.getClashApiProxyLatency(tag);
+ await NetShiftShellMethods.getClashApiProxyLatency(tag);
await fetchDashboardSections();
store.set({
@@ -340,12 +340,12 @@ async function renderServicesInfoWidget() {
title: _('Services info'),
items: [
{
- key: _('Podkop'),
- value: servicesInfoWidget.data.podkop
+ key: _('NetShift'),
+ value: servicesInfoWidget.data.netshift
? _('✔ Enabled')
: _('✘ Disabled'),
attributes: {
- class: servicesInfoWidget.data.podkop
+ class: servicesInfoWidget.data.netshift
? 'pdk_dashboard-page__widgets-section__item__row--success'
: 'pdk_dashboard-page__widgets-section__item__row--error',
},
diff --git a/fe-app-podkop/src/podkop/tabs/dashboard/partials/index.ts b/fe-app-netshift/src/netshift/tabs/dashboard/partials/index.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/dashboard/partials/index.ts
rename to fe-app-netshift/src/netshift/tabs/dashboard/partials/index.ts
diff --git a/fe-app-podkop/src/podkop/tabs/dashboard/partials/renderSections.ts b/fe-app-netshift/src/netshift/tabs/dashboard/partials/renderSections.ts
similarity index 96%
rename from fe-app-podkop/src/podkop/tabs/dashboard/partials/renderSections.ts
rename to fe-app-netshift/src/netshift/tabs/dashboard/partials/renderSections.ts
index bf78e7e6..eeb90871 100644
--- a/fe-app-podkop/src/podkop/tabs/dashboard/partials/renderSections.ts
+++ b/fe-app-netshift/src/netshift/tabs/dashboard/partials/renderSections.ts
@@ -1,9 +1,9 @@
-import { Podkop } from '../../../types';
+import { NetShift } from '../../../types';
interface IRenderSectionsProps {
loading: boolean;
failed: boolean;
- section: Podkop.OutboundGroup;
+ section: NetShift.OutboundGroup;
onTestLatency: (tag: string) => void;
onChooseOutbound: (selector: string, tag: string) => void;
latencyFetching: boolean;
@@ -44,7 +44,7 @@ export function renderDefaultState({
}
}
- function renderOutbound(outbound: Podkop.Outbound) {
+ function renderOutbound(outbound: NetShift.Outbound) {
function getLatencyClass() {
if (!outbound.latency) {
return 'pdk_dashboard-page__outbound-grid__item__latency--empty';
diff --git a/fe-app-podkop/src/podkop/tabs/dashboard/partials/renderWidget.ts b/fe-app-netshift/src/netshift/tabs/dashboard/partials/renderWidget.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/dashboard/partials/renderWidget.ts
rename to fe-app-netshift/src/netshift/tabs/dashboard/partials/renderWidget.ts
diff --git a/fe-app-podkop/src/podkop/tabs/dashboard/render.ts b/fe-app-netshift/src/netshift/tabs/dashboard/render.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/dashboard/render.ts
rename to fe-app-netshift/src/netshift/tabs/dashboard/render.ts
diff --git a/fe-app-podkop/src/podkop/tabs/dashboard/styles.ts b/fe-app-netshift/src/netshift/tabs/dashboard/styles.ts
similarity index 97%
rename from fe-app-podkop/src/podkop/tabs/dashboard/styles.ts
rename to fe-app-netshift/src/netshift/tabs/dashboard/styles.ts
index 32066e5b..4b26b71e 100644
--- a/fe-app-podkop/src/podkop/tabs/dashboard/styles.ts
+++ b/fe-app-netshift/src/netshift/tabs/dashboard/styles.ts
@@ -1,10 +1,10 @@
// language=CSS
export const styles = `
-#cbi-podkop-dashboard-_mount_node > div {
+#cbi-netshift-dashboard-_mount_node > div {
width: 100%;
}
-#cbi-podkop-dashboard > h3 {
+#cbi-netshift-dashboard > h3 {
display: none;
}
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/contstants.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/contstants.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/checks/contstants.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/checks/contstants.ts
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runDnsCheck.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/runDnsCheck.ts
similarity index 94%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/checks/runDnsCheck.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/checks/runDnsCheck.ts
index 4a13b4ec..116e4692 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runDnsCheck.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/runDnsCheck.ts
@@ -1,6 +1,6 @@
import { insertIf } from '../../../../helpers';
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
-import { PodkopShellMethods } from '../../../methods';
+import { NetShiftShellMethods } from '../../../methods';
import { IDiagnosticsChecksItem } from '../../../services';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
@@ -17,7 +17,7 @@ export async function runDnsCheck() {
items: [],
});
- const dnsChecks = await PodkopShellMethods.checkDNSAvailable();
+ const dnsChecks = await NetShiftShellMethods.checkDNSAvailable();
if (!dnsChecks.success) {
updateCheckStore({
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/runFakeIPCheck.ts
similarity index 93%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/checks/runFakeIPCheck.ts
index ebfc7298..f77ff6f2 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/runFakeIPCheck.ts
@@ -1,6 +1,6 @@
import { insertIf } from '../../../../helpers';
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
-import { PodkopShellMethods, RemoteFakeIPMethods } from '../../../methods';
+import { NetShiftShellMethods, RemoteFakeIPMethods } from '../../../methods';
import { IDiagnosticsChecksItem } from '../../../services';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
@@ -17,7 +17,7 @@ export async function runFakeIPCheck() {
items: [],
});
- const routerFakeIPResponse = await PodkopShellMethods.checkFakeIP();
+ const routerFakeIPResponse = await NetShiftShellMethods.checkFakeIP();
const checkFakeIPResponse = await RemoteFakeIPMethods.getFakeIpCheck();
const checkIPResponse = await RemoteFakeIPMethods.getIpCheck();
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runNftCheck.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/runNftCheck.ts
similarity index 95%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/checks/runNftCheck.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/checks/runNftCheck.ts
index a28a378e..65bc065f 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runNftCheck.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/runNftCheck.ts
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
-import { RemoteFakeIPMethods, PodkopShellMethods } from '../../../methods';
+import { RemoteFakeIPMethods, NetShiftShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
@@ -18,7 +18,7 @@ export async function runNftCheck() {
await RemoteFakeIPMethods.getFakeIpCheck();
await RemoteFakeIPMethods.getIpCheck();
- const nftablesChecks = await PodkopShellMethods.checkNftRules();
+ const nftablesChecks = await NetShiftShellMethods.checkNftRules();
if (!nftablesChecks.success) {
updateCheckStore({
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/runSectionsCheck.ts
similarity index 92%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/checks/runSectionsCheck.ts
index 35c85ae2..67fb9731 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/runSectionsCheck.ts
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
-import { PodkopShellMethods } from '../../../methods';
+import { NetShiftShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
import { getDashboardSections } from '../../../methods/custom/getDashboardSections';
@@ -36,9 +36,8 @@ export async function runSectionsCheck() {
sections.data.map(async (section) => {
async function getLatency() {
if (section.withTagSelect) {
- const latencyGroup = await PodkopShellMethods.getClashApiGroupLatency(
- section.code,
- );
+ const latencyGroup =
+ await NetShiftShellMethods.getClashApiGroupLatency(section.code);
const selectedOutbound = section.outbounds.find(
(item) => item.selected,
@@ -82,7 +81,7 @@ export async function runSectionsCheck() {
};
}
- const latencyProxy = await PodkopShellMethods.getClashApiProxyLatency(
+ const latencyProxy = await NetShiftShellMethods.getClashApiProxyLatency(
section.code,
);
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/runSingBoxCheck.ts
similarity index 95%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/checks/runSingBoxCheck.ts
index b13f0bc0..3d5f9bb2 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/runSingBoxCheck.ts
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
-import { PodkopShellMethods } from '../../../methods';
+import { NetShiftShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
@@ -15,7 +15,7 @@ export async function runSingBoxCheck() {
items: [],
});
- const singBoxChecks = await PodkopShellMethods.checkSingBox();
+ const singBoxChecks = await NetShiftShellMethods.checkSingBox();
if (!singBoxChecks.success) {
updateCheckStore({
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/checks/updateCheckStore.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/checks/updateCheckStore.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/checks/updateCheckStore.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/checks/updateCheckStore.ts
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/diagnostic.store.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/diagnostic.store.ts
similarity index 97%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/diagnostic.store.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/diagnostic.store.ts
index 261f1cdf..9000f4da 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/diagnostic.store.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/diagnostic.store.ts
@@ -13,8 +13,8 @@ export const initialDiagnosticStore: Pick<
> = {
diagnosticsSystemInfo: {
loading: true,
- podkop_version: 'loading',
- podkop_latest_version: 'loading',
+ netshift_version: 'loading',
+ netshift_latest_version: 'loading',
luci_app_version: 'loading',
sing_box_version: 'loading',
openwrt_version: 'loading',
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/helpers/getCheckTitle.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/helpers/getCheckTitle.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/helpers/getCheckTitle.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/helpers/getCheckTitle.ts
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/helpers/getMeta.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/helpers/getMeta.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/helpers/getMeta.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/helpers/getMeta.ts
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/helpers/getNetshiftVersionRow.ts
similarity index 71%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/helpers/getNetshiftVersionRow.ts
index 32b0688b..6f3b6761 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/helpers/getNetshiftVersionRow.ts
@@ -7,32 +7,32 @@ function isUnknownVersion(version?: string | null): boolean {
return version === 'unknown' || version === _('unknown');
}
-export function getPodkopVersionRow(
+export function getNetshiftVersionRow(
diagnosticsSystemInfo: StoreType['diagnosticsSystemInfo'],
): IRenderSystemInfoRow {
const loading = diagnosticsSystemInfo.loading;
- const unknown = isUnknownVersion(diagnosticsSystemInfo.podkop_version);
+ const unknown = isUnknownVersion(diagnosticsSystemInfo.netshift_version);
const hasActualVersion =
- Boolean(diagnosticsSystemInfo.podkop_latest_version) &&
- !isUnknownVersion(diagnosticsSystemInfo.podkop_latest_version);
+ Boolean(diagnosticsSystemInfo.netshift_latest_version) &&
+ !isUnknownVersion(diagnosticsSystemInfo.netshift_latest_version);
const version = normalizeCompiledVersion(
- diagnosticsSystemInfo.podkop_version,
+ diagnosticsSystemInfo.netshift_version,
);
const isDevVersion = version === 'dev';
if (loading || unknown || !hasActualVersion || isDevVersion) {
return {
- key: 'Podkop',
+ key: 'NetShift',
value: version,
};
}
if (
removeVersionPrefix(version) !==
- removeVersionPrefix(diagnosticsSystemInfo.podkop_latest_version)
+ removeVersionPrefix(diagnosticsSystemInfo.netshift_latest_version)
) {
return {
- key: 'Podkop',
+ key: 'NetShift',
value: version,
tag: {
label: _('Outdated'),
@@ -42,7 +42,7 @@ export function getPodkopVersionRow(
}
return {
- key: 'Podkop',
+ key: 'NetShift',
value: version,
tag: {
label: _('Latest'),
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/index.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/index.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/index.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/index.ts
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/initController.ts
similarity index 93%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/initController.ts
index 3e90e9f9..fd515d18 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/initController.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/initController.ts
@@ -11,18 +11,18 @@ import {
renderRunAction,
renderSystemInfo,
} from './partials';
-import { PodkopShellMethods } from '../../methods';
+import { NetShiftShellMethods } from '../../methods';
import { fetchServicesInfo } from '../../fetchers';
import { normalizeCompiledVersion } from '../../../helpers/normalizeCompiledVersion';
import { renderModal } from '../../../partials';
-import { PODKOP_LUCI_APP_VERSION } from '../../../constants';
+import { NETSHIFT_LUCI_APP_VERSION } from '../../../constants';
import { showToast } from '../../../helpers/showToast';
import { renderWikiDisclaimer } from './partials/renderWikiDisclaimer';
import { runSectionsCheck } from './checks/runSectionsCheck';
-import { getPodkopVersionRow } from './helpers/getPodkopVersionRow';
+import { getNetshiftVersionRow } from './helpers/getNetshiftVersionRow';
async function fetchSystemInfo() {
- const systemInfo = await PodkopShellMethods.getSystemInfo();
+ const systemInfo = await NetShiftShellMethods.getSystemInfo();
if (systemInfo.success) {
store.set({
@@ -36,8 +36,8 @@ async function fetchSystemInfo() {
store.set({
diagnosticsSystemInfo: {
loading: false,
- podkop_version: _('unknown'),
- podkop_latest_version: _('unknown'),
+ netshift_version: _('unknown'),
+ netshift_latest_version: _('unknown'),
luci_app_version: _('unknown'),
sing_box_version: _('unknown'),
openwrt_version: _('unknown'),
@@ -90,7 +90,7 @@ async function handleRestart() {
});
try {
- await PodkopShellMethods.restart();
+ await NetShiftShellMethods.restart();
} catch (e) {
logger.error('[DIAGNOSTIC]', 'handleRestart - e', e);
} finally {
@@ -117,7 +117,7 @@ async function handleStop() {
});
try {
- await PodkopShellMethods.stop();
+ await NetShiftShellMethods.stop();
} catch (e) {
logger.error('[DIAGNOSTIC]', 'handleStop - e', e);
} finally {
@@ -142,7 +142,7 @@ async function handleStart() {
});
try {
- await PodkopShellMethods.start();
+ await NetShiftShellMethods.start();
} catch (e) {
logger.error('[DIAGNOSTIC]', 'handleStart - e', e);
} finally {
@@ -169,7 +169,7 @@ async function handleEnable() {
});
try {
- await PodkopShellMethods.enable();
+ await NetShiftShellMethods.enable();
} catch (e) {
logger.error('[DIAGNOSTIC]', 'handleEnable - e', e);
} finally {
@@ -193,7 +193,7 @@ async function handleDisable() {
});
try {
- await PodkopShellMethods.disable();
+ await NetShiftShellMethods.disable();
} catch (e) {
logger.error('[DIAGNOSTIC]', 'handleDisable - e', e);
} finally {
@@ -217,7 +217,7 @@ async function handleShowGlobalCheck() {
});
try {
- const globalCheck = await PodkopShellMethods.globalCheck();
+ const globalCheck = await NetShiftShellMethods.globalCheck();
if (globalCheck.success) {
ui.showModal(
@@ -251,7 +251,7 @@ async function handleViewLogs() {
});
try {
- const viewLogs = await PodkopShellMethods.checkLogs();
+ const viewLogs = await NetShiftShellMethods.checkLogs();
if (viewLogs.success) {
ui.showModal(
@@ -285,7 +285,7 @@ async function handleShowSingBoxConfig() {
});
try {
- const showSingBoxConfig = await PodkopShellMethods.showSingBoxConfig();
+ const showSingBoxConfig = await NetShiftShellMethods.showSingBoxConfig();
if (showSingBoxConfig.success) {
ui.showModal(
@@ -328,7 +328,7 @@ async function handleInstallSingBox() {
const isExtended = store.get().diagnosticsSystemInfo.sing_box_extended === 1;
try {
- const result = await PodkopShellMethods.singBoxComponentAction(
+ const result = await NetShiftShellMethods.singBoxComponentAction(
isExtended ? 'install_stable' : 'install_extended',
);
@@ -384,7 +384,7 @@ function renderDiagnosticAvailableActionsWidget() {
const servicesInfoWidget = store.get().servicesInfoWidget;
logger.debug('[DIAGNOSTIC]', 'renderDiagnosticAvailableActionsWidget');
- const podkopEnabled = Boolean(servicesInfoWidget.data.podkop);
+ const netshiftEnabled = Boolean(servicesInfoWidget.data.netshift);
const singBoxRunning = Boolean(servicesInfoWidget.data.singbox);
const atLeastOneServiceCommandLoading =
servicesInfoWidget.loading ||
@@ -415,13 +415,13 @@ function renderDiagnosticAvailableActionsWidget() {
},
enable: {
loading: diagnosticsActions.enable.loading,
- visible: !podkopEnabled,
+ visible: !netshiftEnabled,
onClick: handleEnable,
disabled: atLeastOneServiceCommandLoading,
},
disable: {
loading: diagnosticsActions.disable.loading,
- visible: podkopEnabled,
+ visible: netshiftEnabled,
onClick: handleDisable,
disabled: atLeastOneServiceCommandLoading,
},
@@ -467,10 +467,10 @@ function renderDiagnosticSystemInfoWidget() {
const renderedSystemInfo = renderSystemInfo({
items: [
- getPodkopVersionRow(diagnosticsSystemInfo),
+ getNetshiftVersionRow(diagnosticsSystemInfo),
{
key: 'Luci App',
- value: normalizeCompiledVersion(PODKOP_LUCI_APP_VERSION),
+ value: normalizeCompiledVersion(NETSHIFT_LUCI_APP_VERSION),
},
{
key: 'Sing-box',
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/index.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/partials/index.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/partials/index.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/partials/index.ts
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderAvailableActions.ts
similarity index 97%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderAvailableActions.ts
index 65ac61c8..4de4d208 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderAvailableActions.ts
@@ -50,7 +50,7 @@ export function renderAvailableActions({
classNames: ['cbi-button-apply'],
onClick: restart.onClick,
icon: renderRotateCcwIcon24,
- text: _('Restart podkop'),
+ text: _('Restart NetShift'),
loading: restart.loading,
disabled: restart.disabled,
}),
@@ -60,7 +60,7 @@ export function renderAvailableActions({
classNames: ['cbi-button-remove'],
onClick: stop.onClick,
icon: renderCircleStopIcon24,
- text: _('Stop podkop'),
+ text: _('Stop NetShift'),
loading: stop.loading,
disabled: stop.disabled,
}),
@@ -70,7 +70,7 @@ export function renderAvailableActions({
classNames: ['cbi-button-save'],
onClick: start.onClick,
icon: renderCirclePlayIcon24,
- text: _('Start podkop'),
+ text: _('Start NetShift'),
loading: start.loading,
disabled: start.disabled,
}),
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderCheckSection.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderCheckSection.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderCheckSection.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderCheckSection.ts
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderRunAction.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderRunAction.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderRunAction.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderRunAction.ts
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderSystemInfo.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderSystemInfo.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderSystemInfo.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderSystemInfo.ts
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderWikiDisclaimer.ts
similarity index 94%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderWikiDisclaimer.ts
index 9511e80a..adb8801a 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/partials/renderWikiDisclaimer.ts
@@ -31,7 +31,7 @@ export function renderWikiDisclaimer(kind: 'default' | 'error' | 'warning') {
text: _('Visit Wiki'),
onClick: () =>
window.open(
- 'https://podkop.net/docs/troubleshooting/?utm_source=podkop',
+ 'https://podkop.net/docs/troubleshooting/?utm_source=netshift',
'_blank',
'noopener,noreferrer',
),
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/renderDiagnostic.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/renderDiagnostic.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/renderDiagnostic.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/renderDiagnostic.ts
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/styles.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/styles.ts
similarity index 98%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/styles.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/styles.ts
index def1d781..6d258aab 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/styles.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/styles.ts
@@ -1,11 +1,11 @@
// language=CSS
export const styles = `
-#cbi-podkop-diagnostic-_mount_node > div {
+#cbi-netshift-diagnostic-_mount_node > div {
width: 100%;
}
-#cbi-podkop-diagnostic > h3 {
+#cbi-netshift-diagnostic > h3 {
display: none;
}
diff --git a/fe-app-podkop/src/podkop/tabs/diagnostic/tests/getPodkopVersionRow.test.ts b/fe-app-netshift/src/netshift/tabs/diagnostic/tests/getNetshiftVersionRow.test.ts
similarity index 66%
rename from fe-app-podkop/src/podkop/tabs/diagnostic/tests/getPodkopVersionRow.test.ts
rename to fe-app-netshift/src/netshift/tabs/diagnostic/tests/getNetshiftVersionRow.test.ts
index 88642f42..e8d93c89 100644
--- a/fe-app-podkop/src/podkop/tabs/diagnostic/tests/getPodkopVersionRow.test.ts
+++ b/fe-app-netshift/src/netshift/tabs/diagnostic/tests/getNetshiftVersionRow.test.ts
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
-import { getPodkopVersionRow } from '../helpers/getPodkopVersionRow';
+import { getNetshiftVersionRow } from '../helpers/getNetshiftVersionRow';
import type { StoreType } from '../../../services/store.service';
function makeDiagnosticsSystemInfo(
@@ -7,8 +7,8 @@ function makeDiagnosticsSystemInfo(
): StoreType['diagnosticsSystemInfo'] {
return {
loading: false,
- podkop_version: '1.2.3',
- podkop_latest_version: '1.2.3',
+ netshift_version: '1.2.3',
+ netshift_latest_version: '1.2.3',
luci_app_version: '1.0.0',
sing_box_version: '1.11.0',
openwrt_version: 'OpenWrt 25.12',
@@ -17,17 +17,17 @@ function makeDiagnosticsSystemInfo(
};
}
-describe('getPodkopVersionRow', () => {
+describe('getNetshiftVersionRow', () => {
it('returns Latest when versions differ only by leading v', () => {
- const row = getPodkopVersionRow(
+ const row = getNetshiftVersionRow(
makeDiagnosticsSystemInfo({
- podkop_version: 'v1.2.3',
- podkop_latest_version: '1.2.3',
+ netshift_version: 'v1.2.3',
+ netshift_latest_version: '1.2.3',
}),
);
expect(row).toEqual({
- key: 'Podkop',
+ key: 'NetShift',
value: 'v1.2.3',
tag: {
label: 'Latest',
@@ -37,15 +37,15 @@ describe('getPodkopVersionRow', () => {
});
it('returns Outdated when versions differ', () => {
- const row = getPodkopVersionRow(
+ const row = getNetshiftVersionRow(
makeDiagnosticsSystemInfo({
- podkop_version: '1.2.2',
- podkop_latest_version: '1.2.3',
+ netshift_version: '1.2.2',
+ netshift_latest_version: '1.2.3',
}),
);
expect(row).toEqual({
- key: 'Podkop',
+ key: 'NetShift',
value: '1.2.2',
tag: {
label: 'Outdated',
@@ -55,14 +55,14 @@ describe('getPodkopVersionRow', () => {
});
it('returns plain row without tag for dev build', () => {
- const row = getPodkopVersionRow(
+ const row = getNetshiftVersionRow(
makeDiagnosticsSystemInfo({
- podkop_version: 'COMPILED_VERSION',
+ netshift_version: 'COMPILED_VERSION',
}),
);
expect(row).toEqual({
- key: 'Podkop',
+ key: 'NetShift',
value: 'dev',
});
});
diff --git a/fe-app-podkop/src/podkop/tabs/index.ts b/fe-app-netshift/src/netshift/tabs/index.ts
similarity index 100%
rename from fe-app-podkop/src/podkop/tabs/index.ts
rename to fe-app-netshift/src/netshift/tabs/index.ts
diff --git a/fe-app-podkop/src/podkop/types.ts b/fe-app-netshift/src/netshift/types.ts
similarity index 88%
rename from fe-app-podkop/src/podkop/types.ts
rename to fe-app-netshift/src/netshift/types.ts
index f336ab6f..2994034c 100644
--- a/fe-app-podkop/src/podkop/types.ts
+++ b/fe-app-netshift/src/netshift/types.ts
@@ -20,30 +20,30 @@ export namespace ClashAPI {
}
// eslint-disable-next-line @typescript-eslint/no-namespace
-export namespace Podkop {
+export namespace NetShift {
// Available commands:
- // start Start podkop service
- // stop Stop podkop service
- // reload Reload podkop configuration
- // restart Restart podkop service
- // enable Enable podkop autostart
- // disable Disable podkop autostart
- // main Run main podkop process
+ // start Start netshift service
+ // stop Stop netshift service
+ // reload Reload netshift configuration
+ // restart Restart netshift service
+ // enable Enable netshift autostart
+ // disable Disable netshift autostart
+ // main Run main netshift process
// list_update Update domain lists
// check_proxy Check proxy connectivity
// check_nft Check NFT rules
// check_nft_rules Check NFT rules status
// check_sing_box Check sing-box installation and status
- // check_logs Show podkop logs from system journal
+ // check_logs Show netshift logs from system journal
// check_sing_box_logs Show sing-box logs
// check_fakeip Test FakeIP on router
// clash_api Clash API interface for managing proxies and groups
- // show_config Display current podkop configuration
- // show_version Show podkop version
+ // show_config Display current netshift configuration
+ // show_version Show netshift version
// show_sing_box_config Show sing-box configuration
// show_sing_box_version Show sing-box version
// show_system_info Show system information
- // get_status Get podkop service status
+ // get_status Get netshift service status
// get_sing_box_status Get sing-box service status
// check_dns_available Check DNS server availability
// global_check Run global system check
@@ -212,8 +212,8 @@ export namespace Podkop {
}
export interface GetSystemInfo {
- podkop_version: string;
- podkop_latest_version: string;
+ netshift_version: string;
+ netshift_latest_version: string;
luci_app_version: string;
sing_box_version: string;
openwrt_version: string;
diff --git a/fe-app-podkop/src/partials/button/renderButton.ts b/fe-app-netshift/src/partials/button/renderButton.ts
similarity index 100%
rename from fe-app-podkop/src/partials/button/renderButton.ts
rename to fe-app-netshift/src/partials/button/renderButton.ts
diff --git a/fe-app-podkop/src/partials/button/styles.ts b/fe-app-netshift/src/partials/button/styles.ts
similarity index 100%
rename from fe-app-podkop/src/partials/button/styles.ts
rename to fe-app-netshift/src/partials/button/styles.ts
diff --git a/fe-app-podkop/src/partials/index.ts b/fe-app-netshift/src/partials/index.ts
similarity index 100%
rename from fe-app-podkop/src/partials/index.ts
rename to fe-app-netshift/src/partials/index.ts
diff --git a/fe-app-podkop/src/partials/modal/renderModal.ts b/fe-app-netshift/src/partials/modal/renderModal.ts
similarity index 100%
rename from fe-app-podkop/src/partials/modal/renderModal.ts
rename to fe-app-netshift/src/partials/modal/renderModal.ts
diff --git a/fe-app-podkop/src/partials/modal/styles.ts b/fe-app-netshift/src/partials/modal/styles.ts
similarity index 100%
rename from fe-app-podkop/src/partials/modal/styles.ts
rename to fe-app-netshift/src/partials/modal/styles.ts
diff --git a/fe-app-podkop/src/styles.ts b/fe-app-netshift/src/styles.ts
similarity index 91%
rename from fe-app-podkop/src/styles.ts
rename to fe-app-netshift/src/styles.ts
index e40000dd..b553b600 100644
--- a/fe-app-podkop/src/styles.ts
+++ b/fe-app-netshift/src/styles.ts
@@ -1,5 +1,5 @@
// language=CSS
-import { DashboardTab, DiagnosticTab } from './podkop';
+import { DashboardTab, DiagnosticTab } from './netshift';
import { PartialStyles } from './partials';
export const GlobalStyles = `
@@ -9,17 +9,17 @@ ${PartialStyles}
/* Hide extra H3 for settings tab */
-#cbi-podkop-settings > h3 {
+#cbi-netshift-settings > h3 {
display: none;
}
/* Hide extra H3 for sections tab */
-#cbi-podkop-section > h3:nth-child(1) {
+#cbi-netshift-section > h3:nth-child(1) {
display: none;
}
/* Vertical align for remove section action button */
-#cbi-podkop-section > .cbi-section-remove {
+#cbi-netshift-section > .cbi-section-remove {
margin-bottom: -32px;
}
diff --git a/fe-app-podkop/src/validators/bulkValidate.ts b/fe-app-netshift/src/validators/bulkValidate.ts
similarity index 100%
rename from fe-app-podkop/src/validators/bulkValidate.ts
rename to fe-app-netshift/src/validators/bulkValidate.ts
diff --git a/fe-app-podkop/src/validators/index.ts b/fe-app-netshift/src/validators/index.ts
similarity index 100%
rename from fe-app-podkop/src/validators/index.ts
rename to fe-app-netshift/src/validators/index.ts
diff --git a/fe-app-podkop/src/validators/tests/validateDns.test.js b/fe-app-netshift/src/validators/tests/validateDns.test.js
similarity index 100%
rename from fe-app-podkop/src/validators/tests/validateDns.test.js
rename to fe-app-netshift/src/validators/tests/validateDns.test.js
diff --git a/fe-app-podkop/src/validators/tests/validateDomain.test.js b/fe-app-netshift/src/validators/tests/validateDomain.test.js
similarity index 100%
rename from fe-app-podkop/src/validators/tests/validateDomain.test.js
rename to fe-app-netshift/src/validators/tests/validateDomain.test.js
diff --git a/fe-app-podkop/src/validators/tests/validateHysteriaUrl.test.js b/fe-app-netshift/src/validators/tests/validateHysteriaUrl.test.js
similarity index 100%
rename from fe-app-podkop/src/validators/tests/validateHysteriaUrl.test.js
rename to fe-app-netshift/src/validators/tests/validateHysteriaUrl.test.js
diff --git a/fe-app-podkop/src/validators/tests/validateIp.test.js b/fe-app-netshift/src/validators/tests/validateIp.test.js
similarity index 100%
rename from fe-app-podkop/src/validators/tests/validateIp.test.js
rename to fe-app-netshift/src/validators/tests/validateIp.test.js
diff --git a/fe-app-podkop/src/validators/tests/validatePath.test.js b/fe-app-netshift/src/validators/tests/validatePath.test.js
similarity index 100%
rename from fe-app-podkop/src/validators/tests/validatePath.test.js
rename to fe-app-netshift/src/validators/tests/validatePath.test.js
diff --git a/fe-app-podkop/src/validators/tests/validateShadowsocksUrl.test.js b/fe-app-netshift/src/validators/tests/validateShadowsocksUrl.test.js
similarity index 100%
rename from fe-app-podkop/src/validators/tests/validateShadowsocksUrl.test.js
rename to fe-app-netshift/src/validators/tests/validateShadowsocksUrl.test.js
diff --git a/fe-app-podkop/src/validators/tests/validateSocksUrl.test.js b/fe-app-netshift/src/validators/tests/validateSocksUrl.test.js
similarity index 100%
rename from fe-app-podkop/src/validators/tests/validateSocksUrl.test.js
rename to fe-app-netshift/src/validators/tests/validateSocksUrl.test.js
diff --git a/fe-app-podkop/src/validators/tests/validateSubnet.test.js b/fe-app-netshift/src/validators/tests/validateSubnet.test.js
similarity index 100%
rename from fe-app-podkop/src/validators/tests/validateSubnet.test.js
rename to fe-app-netshift/src/validators/tests/validateSubnet.test.js
diff --git a/fe-app-podkop/src/validators/tests/validateTrojanUrl.test.js b/fe-app-netshift/src/validators/tests/validateTrojanUrl.test.js
similarity index 100%
rename from fe-app-podkop/src/validators/tests/validateTrojanUrl.test.js
rename to fe-app-netshift/src/validators/tests/validateTrojanUrl.test.js
diff --git a/fe-app-podkop/src/validators/tests/validateUrl.test.js b/fe-app-netshift/src/validators/tests/validateUrl.test.js
similarity index 100%
rename from fe-app-podkop/src/validators/tests/validateUrl.test.js
rename to fe-app-netshift/src/validators/tests/validateUrl.test.js
diff --git a/fe-app-podkop/src/validators/tests/validateVlessUrl.test.js b/fe-app-netshift/src/validators/tests/validateVlessUrl.test.js
similarity index 100%
rename from fe-app-podkop/src/validators/tests/validateVlessUrl.test.js
rename to fe-app-netshift/src/validators/tests/validateVlessUrl.test.js
diff --git a/fe-app-podkop/src/validators/types.ts b/fe-app-netshift/src/validators/types.ts
similarity index 100%
rename from fe-app-podkop/src/validators/types.ts
rename to fe-app-netshift/src/validators/types.ts
diff --git a/fe-app-podkop/src/validators/validateDns.ts b/fe-app-netshift/src/validators/validateDns.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateDns.ts
rename to fe-app-netshift/src/validators/validateDns.ts
diff --git a/fe-app-podkop/src/validators/validateDomain.ts b/fe-app-netshift/src/validators/validateDomain.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateDomain.ts
rename to fe-app-netshift/src/validators/validateDomain.ts
diff --git a/fe-app-podkop/src/validators/validateHysteriaUrl.ts b/fe-app-netshift/src/validators/validateHysteriaUrl.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateHysteriaUrl.ts
rename to fe-app-netshift/src/validators/validateHysteriaUrl.ts
diff --git a/fe-app-podkop/src/validators/validateIp.ts b/fe-app-netshift/src/validators/validateIp.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateIp.ts
rename to fe-app-netshift/src/validators/validateIp.ts
diff --git a/fe-app-podkop/src/validators/validateOutboundJson.ts b/fe-app-netshift/src/validators/validateOutboundJson.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateOutboundJson.ts
rename to fe-app-netshift/src/validators/validateOutboundJson.ts
diff --git a/fe-app-podkop/src/validators/validatePath.ts b/fe-app-netshift/src/validators/validatePath.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validatePath.ts
rename to fe-app-netshift/src/validators/validatePath.ts
diff --git a/fe-app-podkop/src/validators/validateProxyUrl.ts b/fe-app-netshift/src/validators/validateProxyUrl.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateProxyUrl.ts
rename to fe-app-netshift/src/validators/validateProxyUrl.ts
diff --git a/fe-app-podkop/src/validators/validateShadowsocksUrl.ts b/fe-app-netshift/src/validators/validateShadowsocksUrl.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateShadowsocksUrl.ts
rename to fe-app-netshift/src/validators/validateShadowsocksUrl.ts
diff --git a/fe-app-podkop/src/validators/validateSocksUrl.ts b/fe-app-netshift/src/validators/validateSocksUrl.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateSocksUrl.ts
rename to fe-app-netshift/src/validators/validateSocksUrl.ts
diff --git a/fe-app-podkop/src/validators/validateSubnet.ts b/fe-app-netshift/src/validators/validateSubnet.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateSubnet.ts
rename to fe-app-netshift/src/validators/validateSubnet.ts
diff --git a/fe-app-podkop/src/validators/validateTrojanUrl.ts b/fe-app-netshift/src/validators/validateTrojanUrl.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateTrojanUrl.ts
rename to fe-app-netshift/src/validators/validateTrojanUrl.ts
diff --git a/fe-app-podkop/src/validators/validateUrl.ts b/fe-app-netshift/src/validators/validateUrl.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateUrl.ts
rename to fe-app-netshift/src/validators/validateUrl.ts
diff --git a/fe-app-podkop/src/validators/validateVlessUrl.ts b/fe-app-netshift/src/validators/validateVlessUrl.ts
similarity index 100%
rename from fe-app-podkop/src/validators/validateVlessUrl.ts
rename to fe-app-netshift/src/validators/validateVlessUrl.ts
diff --git a/fe-app-podkop/tests/setup/global-mocks.ts b/fe-app-netshift/tests/setup/global-mocks.ts
similarity index 100%
rename from fe-app-podkop/tests/setup/global-mocks.ts
rename to fe-app-netshift/tests/setup/global-mocks.ts
diff --git a/fe-app-podkop/tsconfig.json b/fe-app-netshift/tsconfig.json
similarity index 100%
rename from fe-app-podkop/tsconfig.json
rename to fe-app-netshift/tsconfig.json
diff --git a/fe-app-podkop/tsup.config.ts b/fe-app-netshift/tsup.config.ts
similarity index 85%
rename from fe-app-podkop/tsup.config.ts
rename to fe-app-netshift/tsup.config.ts
index 6caf4791..8f210667 100644
--- a/fe-app-podkop/tsup.config.ts
+++ b/fe-app-netshift/tsup.config.ts
@@ -5,7 +5,7 @@ import path from 'path';
export default defineConfig({
entry: ['src/main.ts'],
format: ['esm'], // пусть tsup генерит export {...}
- outDir: '../luci-app-podkop/htdocs/luci-static/resources/view/podkop',
+ outDir: '../luci-app-netshift/htdocs/luci-static/resources/view/netshift',
outExtension: () => ({ js: '.js' }),
dts: false,
clean: false,
@@ -18,7 +18,7 @@ export default defineConfig({
},
onSuccess: () => {
const outDir =
- '../luci-app-podkop/htdocs/luci-static/resources/view/podkop';
+ '../luci-app-netshift/htdocs/luci-static/resources/view/netshift';
const file = path.join(outDir, 'main.js');
let code = fs.readFileSync(file, 'utf8');
diff --git a/fe-app-podkop/vitest.config.js b/fe-app-netshift/vitest.config.js
similarity index 100%
rename from fe-app-podkop/vitest.config.js
rename to fe-app-netshift/vitest.config.js
diff --git a/fe-app-podkop/watch-upload.js b/fe-app-netshift/watch-upload.js
similarity index 87%
rename from fe-app-podkop/watch-upload.js
rename to fe-app-netshift/watch-upload.js
index 0f578328..4b3ea844 100644
--- a/fe-app-podkop/watch-upload.js
+++ b/fe-app-netshift/watch-upload.js
@@ -18,19 +18,19 @@ const config = {
const syncDirs = [
{
- local: path.resolve(process.env.LOCAL_DIR_FE ?? '../luci-app-podkop/htdocs/luci-static/resources/view/podkop'),
- remote: process.env.REMOTE_DIR_FE ?? '/www/luci-static/resources/view/podkop',
+ local: path.resolve(process.env.LOCAL_DIR_FE ?? '../luci-app-netshift/htdocs/luci-static/resources/view/netshift'),
+ remote: process.env.REMOTE_DIR_FE ?? '/www/luci-static/resources/view/netshift',
},
{
- local: path.resolve(process.env.LOCAL_DIR_BIN ?? '../podkop/files/usr/bin/'),
+ local: path.resolve(process.env.LOCAL_DIR_BIN ?? '../netshift/files/usr/bin/'),
remote: process.env.REMOTE_DIR_BIN ?? '/usr/bin/',
},
{
- local: path.resolve(process.env.LOCAL_DIR_LIB ?? '../podkop/files/usr/lib/'),
- remote: process.env.REMOTE_DIR_LIB ?? '/usr/lib/podkop/',
+ local: path.resolve(process.env.LOCAL_DIR_LIB ?? '../netshift/files/usr/lib/'),
+ remote: process.env.REMOTE_DIR_LIB ?? '/usr/lib/netshift/',
},
{
- local: path.resolve(process.env.LOCAL_DIR_INIT ?? '../podkop/files/etc/init.d/'),
+ local: path.resolve(process.env.LOCAL_DIR_INIT ?? '../netshift/files/etc/init.d/'),
remote: process.env.REMOTE_DIR_INIT ?? '/etc/init.d/',
}
];
diff --git a/fe-app-podkop/yarn.lock b/fe-app-netshift/yarn.lock
similarity index 100%
rename from fe-app-podkop/yarn.lock
rename to fe-app-netshift/yarn.lock
diff --git a/fe-app-podkop/.env.example b/fe-app-podkop/.env.example
deleted file mode 100644
index b82d0b60..00000000
--- a/fe-app-podkop/.env.example
+++ /dev/null
@@ -1,16 +0,0 @@
-SFTP_HOST=192.168.160.129
-SFTP_PORT=22
-SFTP_USER=root
-SFTP_PASS=
-
-# you can use key if needed
-# SFTP_PRIVATE_KEY=~/.ssh/id_rsa
-
-LOCAL_DIR_FE=../luci-app-podkop/htdocs/luci-static/resources/view/podkop
-REMOTE_DIR_FE=/www/luci-static/resources/view/podkop
-
-LOCAL_DIR_BIN=../podkop/files/usr/bin/
-REMOTE_DIR_BIN=/usr/bin/
-
-LOCAL_DIR_LIB=../podkop/files/usr/lib/
-REMOTE_DIR_LIB=/usr/lib/podkop/
diff --git a/fe-app-podkop/locales/podkop.pot b/fe-app-podkop/locales/podkop.pot
deleted file mode 100644
index a722424c..00000000
--- a/fe-app-podkop/locales/podkop.pot
+++ /dev/null
@@ -1,1183 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) 2026 THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PODKOP package.
-# yandexru45 , 2026.
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PODKOP\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-06-02 11:25+0300\n"
-"PO-Revision-Date: 2026-06-02 11:25+0300\n"
-"Last-Translator: yandexru45 \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: src\podkop\tabs\dashboard\initController.ts:345
-msgid "✔ Enabled"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:356
-msgid "✔ Running"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:346
-msgid "✘ Disabled"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:357
-msgid "✘ Stopped"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:127
-msgid "Группировать по странам"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:128
-msgid "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:307
-msgid "Active Connections"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:106
-msgid "Additional marking rules found"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:247
-msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:251
-msgid "Applicable for SOCKS and Shadowsocks proxy"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:496
-msgid "At least one valid domain must be specified. Comments-only content is not allowed."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:577
-msgid "At least one valid subnet or IP must be specified. Comments-only content is not allowed."
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:47
-msgid "Available actions"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:65
-msgid "Bootsrap DNS"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:45
-msgid "Bootstrap DNS server"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:58
-msgid "Browser is not using FakeIP"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:57
-msgid "Browser is using FakeIP correctly"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:348
-msgid "Cache File Path"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:362
-msgid "Cache file path cannot be empty"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:27
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:28
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:27
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:25
-msgid "Cannot receive checks result"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:15
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:15
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:13
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:15
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:13
-msgid "Checking, please wait"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getCheckTitle.ts:2
-msgid "checks"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:26
-msgid "Checks failed"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:13
-msgid "Checks passed"
-msgstr ""
-
-#: src\validators\validateSubnet.ts:33
-msgid "CIDR must be between 0 and 32"
-msgstr ""
-
-#: src\partials\modal\renderModal.ts:26
-msgid "Close"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:351
-msgid "Community Lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:335
-msgid "Config File Path"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:27
-msgid "Configuration for Podkop service"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:23
-msgid "Configuration Type"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:12
-msgid "Connection Type"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:26
-msgid "Connection URL"
-msgstr ""
-
-#: src\partials\modal\renderModal.ts:20
-msgid "Copy"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\partials\renderWidget.ts:22
-msgid "Currently unavailable"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:80
-msgid "Dashboard"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\partials\renderSections.ts:19
-msgid "Dashboard currently unavailable"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:222
-msgid "Delay in milliseconds before reloading podkop after interface UP"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:229
-msgid "Delay value cannot be empty"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:82
-msgid "DHCP has DNS server"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:65
-msgid "Diagnostics"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:83
-msgid "Disable autostart"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:265
-msgid "Disable QUIC"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:266
-msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:442
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:522
-msgid "Disabled"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:77
-msgid "DNS on router"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:319
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:15
-msgid "DNS over HTTPS (DoH)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:320
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:16
-msgid "DNS over TLS (DoT)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:316
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:12
-msgid "DNS Protocol Type"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:68
-msgid "DNS Rewrite TTL"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:329
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:24
-msgid "DNS Server"
-msgstr ""
-
-#: src\validators\validateDns.ts:7
-msgid "DNS server address cannot be empty"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:26
-msgid "Do not panic, everything can be fixed, just..."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:306
-msgid "Domain Resolver"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:326
-msgid "Dont Touch My DHCP!"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:241
-#: src\podkop\tabs\dashboard\initController.ts:275
-msgid "Downlink"
-msgstr ""
-
-#: src\partials\modal\renderModal.ts:15
-msgid "Download"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:288
-msgid "Download Lists via Proxy/VPN"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:297
-msgid "Download Lists via specific proxy section"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:289
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:298
-msgid "Downloading all lists via specific Proxy/VPN"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:443
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:523
-msgid "Dynamic List"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:93
-msgid "Enable autostart"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:307
-msgid "Enable built-in DNS resolver for domains handled by this section"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:746
-msgid "Enable DNS resolve to get real IP when routing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:717
-msgid "Enable Mixed Proxy"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:126
-msgid "Enable Output Network Interface"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:718
-msgid "Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:237
-msgid "Enable YACD"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:246
-msgid "Enable YACD WAN Access"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:67
-msgid "Enter complete outbound configuration in JSON format"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:478
-msgid "Enter domain names separated by commas, spaces, or newlines. You can add comments using //"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:452
-msgid "Enter domain names without protocols, e.g. example.com or sub.example.com"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:532
-msgid "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:90
-msgid "Enter the subscription URL to fetch proxy configurations from your provider"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:187
-msgid "Every 1 minute"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:119
-msgid "Every 12 hours"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:117
-msgid "Every 3 hours"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:188
-msgid "Every 3 minutes"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:115
-msgid "Every 30 minutes"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:186
-msgid "Every 30 seconds"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:189
-msgid "Every 5 minutes"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:118
-msgid "Every 6 hours"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:120
-msgid "Every day"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:116
-msgid "Every hour"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:402
-msgid "Exclude NTP"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:403
-msgid "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN"
-msgstr ""
-
-#: src\helpers\copyToClipboard.ts:12
-msgid "Failed to copy!"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:229
-#: src\podkop\tabs\diagnostic\initController.ts:233
-#: src\podkop\tabs\diagnostic\initController.ts:263
-#: src\podkop\tabs\diagnostic\initController.ts:267
-#: src\podkop\tabs\diagnostic\initController.ts:304
-#: src\podkop\tabs\diagnostic\initController.ts:308
-#: src\podkop\tabs\diagnostic\initController.ts:342
-#: src\podkop\tabs\diagnostic\initController.ts:346
-msgid "Failed to execute!"
-msgstr ""
-
-#: src\podkop\methods\custom\getDashboardSections.ts:150
-#: src\podkop\methods\custom\getDashboardSections.ts:181
-#: src\podkop\methods\custom\getDashboardSections.ts:218
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:59
-msgid "Fastest"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:690
-msgid "Fully Routed IPs"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:102
-msgid "Get global check"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:224
-msgid "Global check"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:113
-msgid "How often to automatically update the subscription"
-msgstr ""
-
-#: src\podkop\api.ts:27
-msgid "HTTP error"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:129
-msgid "Install extended"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:129
-msgid "Install stable"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:189
-msgid "Interface Monitoring"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:221
-msgid "Interface Monitoring Delay"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:190
-msgid "Interface monitoring for Bad WAN"
-msgstr ""
-
-#: src\validators\validateDns.ts:23
-msgid "Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH"
-msgstr ""
-
-#: src\validators\validateDomain.ts:18
-#: src\validators\validateDomain.ts:27
-msgid "Invalid domain address"
-msgstr ""
-
-#: src\validators\validateSubnet.ts:11
-msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:90
-msgid "Invalid HY2 URL: insecure must be 0 or 1"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:77
-msgid "Invalid HY2 URL: invalid port number"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:30
-msgid "Invalid HY2 URL: missing credentials/server"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:47
-msgid "Invalid HY2 URL: missing host"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:41
-msgid "Invalid HY2 URL: missing host & port"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:36
-msgid "Invalid HY2 URL: missing password"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:50
-msgid "Invalid HY2 URL: missing port"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:18
-msgid "Invalid HY2 URL: must not contain spaces"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:12
-msgid "Invalid HY2 URL: must start with hysteria2:// or hy2://"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:108
-msgid "Invalid HY2 URL: obfs-password required when obfs is set"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:122
-msgid "Invalid HY2 URL: parsing failed"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:116
-msgid "Invalid HY2 URL: sni cannot be empty"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:98
-msgid "Invalid HY2 URL: unsupported obfs type"
-msgstr ""
-
-#: src\validators\validateIp.ts:11
-msgid "Invalid IP address"
-msgstr ""
-
-#: src\validators\validateOutboundJson.ts:9
-msgid "Invalid JSON format"
-msgstr ""
-
-#: src\validators\validatePath.ts:22
-msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:85
-msgid "Invalid port number. Must be between 1 and 65535"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:37
-msgid "Invalid Shadowsocks URL: decoded credentials must contain method:password"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:27
-msgid "Invalid Shadowsocks URL: missing credentials"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:46
-msgid "Invalid Shadowsocks URL: missing method and password separator \":\""
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:76
-msgid "Invalid Shadowsocks URL: missing port"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:67
-msgid "Invalid Shadowsocks URL: missing server"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:58
-msgid "Invalid Shadowsocks URL: missing server address"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:16
-msgid "Invalid Shadowsocks URL: must not contain spaces"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:8
-msgid "Invalid Shadowsocks URL: must start with ss://"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:91
-msgid "Invalid Shadowsocks URL: parsing failed"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:73
-msgid "Invalid SOCKS URL: invalid host format"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:63
-msgid "Invalid SOCKS URL: invalid port number"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:42
-msgid "Invalid SOCKS URL: missing host and port"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:51
-msgid "Invalid SOCKS URL: missing hostname or IP"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:56
-msgid "Invalid SOCKS URL: missing port"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:34
-msgid "Invalid SOCKS URL: missing username"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:19
-msgid "Invalid SOCKS URL: must not contain spaces"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:10
-msgid "Invalid SOCKS URL: must start with socks4://, socks4a://, or socks5://"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:77
-msgid "Invalid SOCKS URL: parsing failed"
-msgstr ""
-
-#: src\validators\validateTrojanUrl.ts:15
-msgid "Invalid Trojan URL: must not contain spaces"
-msgstr ""
-
-#: src\validators\validateTrojanUrl.ts:8
-msgid "Invalid Trojan URL: must start with trojan://"
-msgstr ""
-
-#: src\validators\validateTrojanUrl.ts:56
-msgid "Invalid Trojan URL: parsing failed"
-msgstr ""
-
-#: src\validators\validateUrl.ts:8
-#: src\validators\validateUrl.ts:31
-msgid "Invalid URL format"
-msgstr ""
-
-#: src\validators\validateVlessUrl.ts:110
-msgid "Invalid VLESS URL: parsing failed"
-msgstr ""
-
-#: src\validators\validateSubnet.ts:18
-msgid "IP address 0.0.0.0 is not allowed"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:20
-msgid "Issues detected"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:48
-msgid "Latest"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:276
-msgid "List Update Frequency"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:598
-msgid "Local Domain Lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:621
-msgid "Local Subnet Lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:384
-msgid "Log Level"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:72
-msgid "Main DNS"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:311
-msgid "Memory Usage"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:730
-msgid "Mixed Proxy Port"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:198
-msgid "Monitored Interfaces"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:215
-msgid "Must be a number in the range of 50 - 1000"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:260
-msgid "Network Interface"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:105
-msgid "No other marking rules found"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderCheckSection.ts:189
-msgid "Not implement yet"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:75
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:81
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:100
-msgid "Not responding"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:59
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:67
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:75
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:83
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:91
-msgid "Not running"
-msgstr ""
-
-#: src\helpers\withTimeout.ts:7
-msgid "Operation timed out"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:30
-msgid "Outbound Config"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:66
-msgid "Outbound Configuration"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:38
-msgid "Outdated"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:135
-msgid "Output Network Interface"
-msgstr ""
-
-#: src\validators\validatePath.ts:7
-msgid "Path cannot be empty"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:366
-msgid "Path must be absolute (start with /)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:375
-msgid "Path must contain at least one directory (like /tmp/cache.db)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:370
-msgid "Path must end with cache.db"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:107
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:115
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:123
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:131
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:139
-msgid "Pending"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:343
-msgid "Podkop"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:26
-msgid "Podkop Settings"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:327
-msgid "Podkop will not modify your DHCP configuration"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:37
-msgid "Proxy Configuration URL"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:66
-msgid "Proxy traffic is not routed via FakeIP"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:65
-msgid "Proxy traffic is routed via FakeIP"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:385
-msgid "Regional options cannot be used together"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:644
-msgid "Remote Domain Lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:667
-msgid "Remote Subnet Lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:745
-msgid "Resolve real IP for routing"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:53
-msgid "Restart podkop"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:51
-msgid "Router DNS is not routed through sing-box"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:50
-msgid "Router DNS is routed through sing-box"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:413
-msgid "Routing Excluded IPs"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:79
-msgid "Rules mangle counters"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:74
-msgid "Rules mangle exist"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:89
-msgid "Rules mangle output counters"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:84
-msgid "Rules mangle output exist"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:99
-msgid "Rules proxy counters"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:94
-msgid "Rules proxy exist"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderRunAction.ts:15
-msgid "Run Diagnostic"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:404
-msgid "Russia inside restrictions"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:257
-msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:36
-msgid "Sections"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:352
-msgid "Select a predefined list for routing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:13
-msgid "Select between VPN and Proxy connection methods for traffic routing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:13
-msgid "Select DNS protocol to use"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:277
-msgid "Select how often the domain or subnet lists are updated automatically"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:24
-msgid "Select how to configure the proxy"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:261
-msgid "Select network interface for VPN connection"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:330
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:25
-msgid "Select or enter DNS server address"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:349
-msgid "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:336
-msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:317
-msgid "Select the DNS protocol type for the domain resolver"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:440
-msgid "Select the list type for adding custom domains"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:520
-msgid "Select the list type for adding custom subnets"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:385
-msgid "Select the log level for sing-box"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:90
-msgid "Select the network interface from which the traffic will originate"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:136
-msgid "Select the network interface to which the traffic will originate"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:199
-msgid "Select the WAN interfaces to be monitored"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:27
-msgid "Selector"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:137
-msgid "Selector Proxy Links"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:340
-msgid "Services info"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:49
-msgid "Settings"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:292
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:120
-msgid "Show sing-box config"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:354
-msgid "Sing-box"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:77
-msgid "Sing-box autostart disabled"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:337
-msgid "Sing-box core changed, version:"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:62
-msgid "Sing-box installed"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:87
-msgid "Sing-box listening ports"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:82
-msgid "Sing-box process running"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:72
-msgid "Sing-box service exist"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:67
-msgid "Sing-box version is compatible (newer than 1.12.4)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:89
-msgid "Source Network Interface"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:414
-msgid "Specify a local IP address to be excluded from routing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:691
-msgid "Specify local IP addresses or subnets whose traffic will always be routed through the configured route"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:645
-msgid "Specify remote URLs to download and use domain lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:668
-msgid "Specify remote URLs to download and use subnet lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:599
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:622
-msgid "Specify the path to the list file located on the router filesystem"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:73
-msgid "Start podkop"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:63
-msgid "Stop podkop"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:29
-msgid "Subscription"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:112
-msgid "Subscription Update Interval"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:89
-msgid "Subscription URL"
-msgstr ""
-
-#: src\helpers\copyToClipboard.ts:10
-msgid "Successfully copied!"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:304
-msgid "System info"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderSystemInfo.ts:21
-msgid "System information"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:69
-msgid "Table exist"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\partials\renderSections.ts:108
-msgid "Test latency"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:444
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:524
-msgid "Text List"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:46
-msgid "The DNS server used to look up the IP address of an upstream DNS server"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:184
-msgid "The interval between connectivity tests"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:198
-msgid "The maximum difference in response times (ms) allowed when comparing servers"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:222
-msgid "The URL used to test server connectivity"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:69
-msgid "Time in seconds for DNS record caching (default: 60)"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:238
-msgid "Traffic"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:268
-msgid "Traffic Total"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:25
-msgid "Troubleshooting"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:80
-msgid "TTL must be a positive number"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:75
-msgid "TTL value cannot be empty"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:321
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:17
-msgid "UDP (Unprotected DNS)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:250
-msgid "UDP over TCP"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:39
-#: src\podkop\tabs\diagnostic\initController.ts:40
-#: src\podkop\tabs\diagnostic\initController.ts:41
-#: src\podkop\tabs\diagnostic\initController.ts:42
-#: src\podkop\tabs\diagnostic\initController.ts:43
-#: src\podkop\tabs\diagnostic\initController.ts:44
-#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:7
-msgid "unknown"
-msgstr ""
-
-#: src\podkop\api.ts:40
-msgid "Unknown error"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:240
-#: src\podkop\tabs\dashboard\initController.ts:271
-msgid "Uplink"
-msgstr ""
-
-#: src\validators\validateProxyUrl.ts:37
-msgid "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://"
-msgstr ""
-
-#: src\validators\validateUrl.ts:17
-msgid "URL must use one of the following protocols:"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:28
-msgid "URLTest"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:183
-msgid "URLTest Check Interval"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:160
-msgid "URLTest Proxy Links"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:221
-msgid "URLTest Testing URL"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:197
-msgid "URLTest Tolerance"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:439
-msgid "User Domain List Type"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:451
-msgid "User Domains"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:477
-msgid "User Domains List"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:519
-msgid "User Subnet List Type"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:531
-msgid "User Subnets"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:557
-msgid "User Subnets List"
-msgstr ""
-
-#: src\validators\validateDns.ts:14
-#: src\validators\validateDns.ts:18
-#: src\validators\validateDomain.ts:13
-#: src\validators\validateDomain.ts:30
-#: src\validators\validateHysteriaUrl.ts:120
-#: src\validators\validateIp.ts:8
-#: src\validators\validateOutboundJson.ts:7
-#: src\validators\validatePath.ts:16
-#: src\validators\validateShadowsocksUrl.ts:95
-#: src\validators\validateSocksUrl.ts:80
-#: src\validators\validateSubnet.ts:38
-#: src\validators\validateTrojanUrl.ts:59
-#: src\validators\validateUrl.ts:28
-#: src\validators\validateVlessUrl.ts:108
-msgid "Valid"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:510
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:589
-msgid "Validation errors:"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:258
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:111
-msgid "View logs"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:31
-msgid "Visit Wiki"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:38
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:138
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:161
-msgid "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:387
-msgid "Warning: %s cannot be used together with %s. Previous selections have been removed."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:406
-msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:256
-msgid "YACD Secret Key"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:127
-msgid "You can select Output Network Interface, by default autodetect"
-msgstr ""
diff --git a/fe-app-podkop/src/podkop/fetchers/fetchServicesInfo.ts b/fe-app-podkop/src/podkop/fetchers/fetchServicesInfo.ts
deleted file mode 100644
index 777ab94b..00000000
--- a/fe-app-podkop/src/podkop/fetchers/fetchServicesInfo.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { PodkopShellMethods } from '../methods';
-import { store } from '../services';
-
-export async function fetchServicesInfo() {
- const [podkop, singbox] = await Promise.all([
- PodkopShellMethods.getStatus(),
- PodkopShellMethods.getSingBoxStatus(),
- ]);
-
- if (!podkop.success || !singbox.success) {
- store.set({
- servicesInfoWidget: {
- loading: false,
- failed: true,
- data: { singbox: 0, podkop: 0 },
- },
- });
- }
-
- if (podkop.success && singbox.success) {
- store.set({
- servicesInfoWidget: {
- loading: false,
- failed: false,
- data: { singbox: singbox.data.running, podkop: podkop.data.enabled },
- },
- });
- }
-}
diff --git a/fe-app-podkop/src/podkop/methods/custom/getConfigSections.ts b/fe-app-podkop/src/podkop/methods/custom/getConfigSections.ts
deleted file mode 100644
index bdb13c46..00000000
--- a/fe-app-podkop/src/podkop/methods/custom/getConfigSections.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { Podkop } from '../../types';
-
-export async function getConfigSections(): Promise {
- return uci.load('podkop').then(() => uci.sections('podkop'));
-}
diff --git a/fe-app-podkop/src/podkop/methods/shell/index.ts b/fe-app-podkop/src/podkop/methods/shell/index.ts
deleted file mode 100644
index 24038743..00000000
--- a/fe-app-podkop/src/podkop/methods/shell/index.ts
+++ /dev/null
@@ -1,129 +0,0 @@
-import { callBaseMethod } from './callBaseMethod';
-import { ClashAPI, Podkop } from '../../types';
-import { executeShellCommand } from '../../../helpers';
-
-interface SingBoxComponentActionResult {
- success: boolean;
- version?: string;
- message?: string;
-}
-
-export const PodkopShellMethods = {
- checkDNSAvailable: async () =>
- callBaseMethod(
- Podkop.AvailableMethods.CHECK_DNS_AVAILABLE,
- ),
- checkFakeIP: async () =>
- callBaseMethod(
- Podkop.AvailableMethods.CHECK_FAKEIP,
- ),
- checkNftRules: async () =>
- callBaseMethod(
- Podkop.AvailableMethods.CHECK_NFT_RULES,
- ),
- getStatus: async () =>
- callBaseMethod(Podkop.AvailableMethods.GET_STATUS),
- checkSingBox: async () =>
- callBaseMethod(
- Podkop.AvailableMethods.CHECK_SING_BOX,
- ),
- getSingBoxStatus: async () =>
- callBaseMethod(
- Podkop.AvailableMethods.GET_SING_BOX_STATUS,
- ),
- getClashApiProxies: async () =>
- callBaseMethod(Podkop.AvailableMethods.CLASH_API, [
- Podkop.AvailableClashAPIMethods.GET_PROXIES,
- ]),
- getClashApiProxyLatency: async (tag: string) =>
- callBaseMethod(
- Podkop.AvailableMethods.CLASH_API,
- [Podkop.AvailableClashAPIMethods.GET_PROXY_LATENCY, tag, '5000'],
- ),
- getClashApiGroupLatency: async (tag: string) =>
- callBaseMethod(
- Podkop.AvailableMethods.CLASH_API,
- [Podkop.AvailableClashAPIMethods.GET_GROUP_LATENCY, tag, '10000'],
- ),
- setClashApiGroupProxy: async (group: string, proxy: string) =>
- callBaseMethod(Podkop.AvailableMethods.CLASH_API, [
- Podkop.AvailableClashAPIMethods.SET_GROUP_PROXY,
- group,
- proxy,
- ]),
- restart: async () =>
- callBaseMethod(
- Podkop.AvailableMethods.RESTART,
- [],
- '/etc/init.d/podkop',
- ),
- start: async () =>
- callBaseMethod(
- Podkop.AvailableMethods.START,
- [],
- '/etc/init.d/podkop',
- ),
- stop: async () =>
- callBaseMethod(
- Podkop.AvailableMethods.STOP,
- [],
- '/etc/init.d/podkop',
- ),
- enable: async () =>
- callBaseMethod(
- Podkop.AvailableMethods.ENABLE,
- [],
- '/etc/init.d/podkop',
- ),
- disable: async () =>
- callBaseMethod(
- Podkop.AvailableMethods.DISABLE,
- [],
- '/etc/init.d/podkop',
- ),
- globalCheck: async () =>
- callBaseMethod(Podkop.AvailableMethods.GLOBAL_CHECK),
- showSingBoxConfig: async () =>
- callBaseMethod(Podkop.AvailableMethods.SHOW_SING_BOX_CONFIG),
- checkLogs: async () =>
- callBaseMethod(Podkop.AvailableMethods.CHECK_LOGS),
- getSystemInfo: async () =>
- callBaseMethod(
- Podkop.AvailableMethods.GET_SYSTEM_INFO,
- ),
- subscriptionUpdate: async () =>
- callBaseMethod(Podkop.AvailableMethods.SUBSCRIPTION_UPDATE),
- singBoxComponentAction: async (
- action: 'install_extended' | 'install_stable' | 'check_update',
- ): Promise => {
- const response = await executeShellCommand({
- command: '/usr/bin/podkop',
- args: ['component_action', 'sing_box', action],
- timeout: 600000,
- });
-
- if (response.stdout) {
- try {
- const parsed = JSON.parse(
- response.stdout,
- ) as SingBoxComponentActionResult;
-
- return {
- success: Boolean(parsed.success),
- version: parsed.version,
- message: parsed.message,
- };
- } catch (_e) {
- return {
- success: false,
- message: response.stdout,
- };
- }
- }
-
- return {
- success: false,
- message: response.stderr || '',
- };
- },
-};
diff --git a/install.sh b/install.sh
index 6fde5176..0580f410 100755
--- a/install.sh
+++ b/install.sh
@@ -2,7 +2,7 @@
# shellcheck shell=dash
REPO="https://api.github.com/repos/yandexru45/podkop-evolution/releases/latest"
-DOWNLOAD_DIR="/tmp/podkop"
+DOWNLOAD_DIR="/tmp/netshift"
COUNT=3
# Cached flag to switch between ipk or apk package managers
@@ -63,9 +63,9 @@ pkg_install() {
update_config() {
printf "\033[48;5;196m\033[1m╔══════════════════════════════════════════════════════════════════════╗\033[0m\n"
- printf "\033[48;5;196m\033[1m║ ! Обнаружена старая версия podkop. ║\033[0m\n"
- printf "\033[48;5;196m\033[1m║ Если продолжите обновление, вам потребуется настроить Podkop заново. ║\033[0m\n"
- printf "\033[48;5;196m\033[1m║ Старая конфигурация будет сохранена в /etc/config/podkop-070 ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ ! Обнаружена старая версия NetShift. ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Если продолжите обновление, вам потребуется настроить NetShift заново.║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Старая конфигурация будет сохранена в /etc/config/netshift-070 ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Подробности: https://github.com/yandexru45/podkop-evolution ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Точно хотите продолжить? ║\033[0m\n"
printf "\033[48;5;196m\033[1m╚══════════════════════════════════════════════════════════════════════╝\033[0m\n"
@@ -73,9 +73,9 @@ update_config() {
echo ""
printf "\033[48;5;196m\033[1m╔══════════════════════════════════════════════════════════════════════╗\033[0m\n"
- printf "\033[48;5;196m\033[1m║ ! Detected old podkop version. ║\033[0m\n"
- printf "\033[48;5;196m\033[1m║ If you continue the update, you will need to RECONFIGURE podkop. ║\033[0m\n"
- printf "\033[48;5;196m\033[1m║ Your old configuration will be saved to /etc/config/podkop-070 ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ ! Detected old NetShift version. ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ If you continue the update, you will need to RECONFIGURE NetShift. ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Your old configuration will be saved to /etc/config/netshift-070 ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Details: https://github.com/yandexru45/podkop-evolution ║\033[0m\n"
printf "\033[48;5;196m\033[1m║ Are you sure you want to continue? ║\033[0m\n"
printf "\033[48;5;196m\033[1m╚══════════════════════════════════════════════════════════════════════╝\033[0m\n"
@@ -87,9 +87,9 @@ update_config() {
case $CONFIG_UPDATE in
yes|y|Y)
- mv /etc/config/podkop /etc/config/podkop-070
- wget -O /etc/config/podkop https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/podkop/files/etc/config/podkop
- msg "Podkop config has been reset to default. Your old config saved in /etc/config/podkop-070"
+ mv /etc/config/netshift /etc/config/netshift-070
+ wget -O /etc/config/netshift https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/netshift/files/etc/config/netshift
+ msg "NetShift config has been reset to default. Your old config saved in /etc/config/netshift-070"
break
;;
*)
@@ -100,6 +100,138 @@ update_config() {
done
}
+# Detect whether an OLD podkop install is present on this router.
+# Returns 0 (true) if any podkop artifact is found.
+podkop_is_installed() {
+ if [ -f "/etc/config/podkop" ]; then
+ return 0
+ fi
+ if command -v podkop >/dev/null 2>&1; then
+ return 0
+ fi
+ if [ -x "/etc/init.d/podkop" ] || [ -f "/etc/init.d/podkop" ]; then
+ return 0
+ fi
+ return 1
+}
+
+# Migrate an existing podkop (< 0.8.0) install to NetShift.
+# podkop never reached 0.8.0, so any old podkop install triggers this.
+# Every step is guarded by existence checks, POSIX, and idempotent so that
+# re-running install.sh is safe.
+migrate_from_podkop() {
+ local old_version
+ old_version=$(/usr/bin/podkop show_version 2>/dev/null)
+
+ # 1. Bilingual banner (RU first, EN second) + confirmation prompt.
+ printf "\033[48;5;196m\033[1m╔══════════════════════════════════════════════════════════════════════╗\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ ! Обнаружена установка podkop. Она будет перенесена в NetShift. ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Ваша конфигурация будет перенесена автоматически. ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Старая конфигурация сохранится в /etc/config/podkop.bak.pre-netshift║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Старый пакет podkop будет удалён, NetShift будет установлен. ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Подробности: https://github.com/yandexru45/podkop-evolution ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Точно хотите продолжить? ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m╚══════════════════════════════════════════════════════════════════════╝\033[0m\n"
+
+ echo ""
+
+ printf "\033[48;5;196m\033[1m╔══════════════════════════════════════════════════════════════════════╗\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ ! Detected a podkop install. It will be migrated to NetShift. ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Your configuration will be carried over automatically. ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Old config will be backed up to /etc/config/podkop.bak.pre-netshift ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ The old podkop package will be removed, NetShift installed. ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Details: https://github.com/yandexru45/podkop-evolution ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m║ Are you sure you want to continue? ║\033[0m\n"
+ printf "\033[48;5;196m\033[1m╚══════════════════════════════════════════════════════════════════════╝\033[0m\n"
+
+ if [ -n "$old_version" ]; then
+ msg "Detected podkop version: $old_version"
+ fi
+
+ msg "Continue migration to NetShift? (yes/no)"
+
+ read -r -p '' MIGRATE_CONFIRM
+ case $MIGRATE_CONFIRM in
+ yes|y|Y)
+ ;;
+ *)
+ msg "Exit"
+ exit 1
+ ;;
+ esac
+
+ # 2. Stop the old service if running. The old 'stop' restores dnsmasq
+ # (podkop_server/noresolv/cachesize keys in /etc/config/dhcp), removes
+ # the old nft table PodkopTable and the '105 podkop' rt_tables line.
+ # Must run BEFORE removing the package for a clean teardown.
+ if [ -x "/etc/init.d/podkop" ]; then
+ msg "Stopping old podkop service..."
+ /etc/init.d/podkop stop 2>/dev/null || true
+ fi
+
+ # 3. Disable old rc.d autostart (best-effort).
+ if [ -x "/etc/init.d/podkop" ]; then
+ msg "Disabling old podkop autostart..."
+ /etc/init.d/podkop disable 2>/dev/null || true
+ fi
+
+ # 4. Migrate config (copy, not move — keep a backup). Schema is compatible.
+ if [ -f "/etc/config/podkop" ]; then
+ if [ ! -f "/etc/config/netshift" ]; then
+ msg "Migrating config /etc/config/podkop -> /etc/config/netshift..."
+ cp /etc/config/podkop /etc/config/netshift 2>/dev/null || true
+ else
+ msg "/etc/config/netshift already exists, keeping it."
+ fi
+ if [ ! -f "/etc/config/podkop.bak.pre-netshift" ]; then
+ cp /etc/config/podkop /etc/config/podkop.bak.pre-netshift 2>/dev/null || true
+ fi
+ fi
+
+ # 5. Migrate state dir (preserves subscription cache). Best-effort.
+ if [ -d "/etc/podkop" ] && [ ! -d "/etc/netshift" ]; then
+ msg "Migrating state dir /etc/podkop -> /etc/netshift..."
+ cp -r /etc/podkop /etc/netshift 2>/dev/null || true
+ fi
+
+ # 6. Clean leftover OLD persistent system state that opkg/apk remove won't.
+ # rt_tables: remove old '105 podkop' line (NetShift adds '105 netshift'
+ # itself on start).
+ if [ -f "/etc/iproute2/rt_tables" ] && grep -q "105 podkop" /etc/iproute2/rt_tables 2>/dev/null; then
+ msg "Removing old '105 podkop' rt_tables entry..."
+ sed -i "/105 podkop/d" /etc/iproute2/rt_tables 2>/dev/null || true
+ fi
+ # Old cron lines: strip entries that call the old binary (NetShift re-adds
+ # its own on start).
+ if crontab -l >/dev/null 2>&1; then
+ if crontab -l 2>/dev/null | grep -q "/usr/bin/podkop"; then
+ msg "Removing old podkop cron entries..."
+ crontab -l 2>/dev/null | grep -v "/usr/bin/podkop" | crontab - 2>/dev/null || true
+ fi
+ fi
+ # NOTE: nft table PodkopTable + dnsmasq keys are cleaned by the
+ # '/etc/init.d/podkop stop' above; we do NOT hand-edit /etc/config/dhcp.
+
+ # 7. Remove OLD packages (after config/state migrated).
+ # Order: i18n, then luci-app, then backend.
+ if pkg_is_installed luci-i18n-podkop; then
+ msg "Removing old luci-i18n-podkop* packages..."
+ pkg_remove luci-i18n-podkop*
+ fi
+ if pkg_is_installed luci-app-podkop; then
+ msg "Removing old luci-app-podkop package..."
+ pkg_remove luci-app-podkop
+ fi
+ if pkg_is_installed "^podkop" || command -v podkop >/dev/null 2>&1; then
+ msg "Removing old podkop package..."
+ pkg_remove podkop
+ fi
+
+ # 8. Done.
+ msg "Migration complete. NetShift will now be installed."
+ msg "Your old config is preserved at /etc/config/podkop.bak.pre-netshift"
+}
+
main() {
check_system
sing_box
@@ -108,10 +240,10 @@ main() {
pkg_list_update || { echo "Packages list update failed"; exit 1; }
- if [ -f "/etc/init.d/podkop" ]; then
- msg "Podkop is already installed. Upgrading..."
+ if [ -f "/etc/init.d/netshift" ]; then
+ msg "NetShift is already installed. Upgrading..."
else
- msg "Installing podkop..."
+ msg "Installing NetShift..."
fi
if command -v curl >/dev/null 2>&1; then
@@ -154,12 +286,12 @@ main() {
done
# Check if any files were downloaded
- if ! ls "$DOWNLOAD_DIR"/*podkop* >/dev/null 2>&1; then
+ if ! ls "$DOWNLOAD_DIR"/*netshift* >/dev/null 2>&1; then
msg "No packages were downloaded successfully"
exit 1
fi
- for pkg in podkop luci-app-podkop; do
+ for pkg in netshift luci-app-netshift; do
file=""
for f in "$DOWNLOAD_DIR"/"$pkg"*; do
if [ -f "$f" ]; then
@@ -175,16 +307,16 @@ main() {
done
ru=""
- for f in "$DOWNLOAD_DIR"/luci-i18n-podkop-ru*; do
+ for f in "$DOWNLOAD_DIR"/luci-i18n-netshift-ru*; do
if [ -f "$f" ]; then
ru=$(basename "$f")
break
fi
done
if [ -n "$ru" ]; then
- if pkg_is_installed luci-i18n-podkop-ru; then
+ if pkg_is_installed luci-i18n-netshift-ru; then
msg "Upgrading Russian translation..."
- pkg_remove luci-i18n-podkop*
+ pkg_remove luci-i18n-netshift*
pkg_install "$DOWNLOAD_DIR/$ru"
else
msg "Русский язык интерфейса ставим? y/n (Install the Russian interface language?)"
@@ -192,7 +324,7 @@ main() {
read -r -p '' RUS
case $RUS in
y)
- pkg_remove luci-i18n-podkop*
+ pkg_remove luci-i18n-netshift*
pkg_install "$DOWNLOAD_DIR/$ru"
break
;;
@@ -207,7 +339,7 @@ main() {
fi
fi
- find "$DOWNLOAD_DIR" -type f -name '*podkop*' -exec rm {} \;
+ find "$DOWNLOAD_DIR" -type f -name '*netshift*' -exec rm {} \;
}
check_system() {
@@ -218,8 +350,8 @@ check_system() {
# Check OpenWrt version
openwrt_version=$(cat /etc/openwrt_release | grep DISTRIB_RELEASE | cut -d"'" -f2 | cut -d'.' -f1)
if [ "$openwrt_version" = "23" ]; then
- msg "OpenWrt 23.05 не поддерживается начиная с podkop 0.5.0"
- msg "Для OpenWrt 23.05 используйте podkop версии 0.4.11 или устанавливайте зависимости и podkop вручную"
+ msg "OpenWrt 23.05 не поддерживается начиная с NetShift 0.8.0"
+ msg "Для OpenWrt 23.05 устанавливайте зависимости и NetShift вручную"
msg "Подробности: https://podkop.net/docs/install/#%d1%83%d1%81%d1%82%d0%b0%d0%bd%d0%be%d0%b2%d0%ba%d0%b0-%d0%bd%d0%b0-2305"
exit 1
fi
@@ -240,10 +372,17 @@ check_system() {
exit 1
fi
- # Check version
- if command -v podkop > /dev/null 2>&1; then
+ # Old podkop install detected -> migrate to NetShift before installing the
+ # new packages. podkop never reached 0.8.0, so ANY old podkop triggers this.
+ if podkop_is_installed; then
+ migrate_from_podkop
+ return
+ fi
+
+ # Otherwise check existing NetShift version (just upgrading NetShift).
+ if command -v netshift > /dev/null 2>&1; then
local version
- version=$(/usr/bin/podkop show_version 2> /dev/null)
+ version=$(/usr/bin/netshift show_version 2> /dev/null)
if [ -n "$version" ]; then
version=$(echo "$version" | sed 's/^v//')
local major
@@ -253,18 +392,17 @@ check_system() {
minor=$(echo "$version" | cut -d. -f2)
patch=$(echo "$version" | cut -d. -f3)
- # Compare version: must be >= 0.7.0
+ # Compare version: must be >= 0.8.0
if [ "$major" -gt 0 ] ||
- [ "$major" -eq 0 ] && [ "$minor" -gt 7 ] ||
- [ "$major" -eq 0 ] && [ "$minor" -eq 7 ] && [ "$patch" -ge 0 ]; then
- msg "Podkop version >= 0.7.0"
- break
+ { [ "$major" -eq 0 ] && [ "$minor" -gt 8 ]; } ||
+ { [ "$major" -eq 0 ] && [ "$minor" -eq 8 ] && [ "$patch" -ge 0 ]; }; then
+ msg "NetShift version >= 0.8.0"
else
- msg "Podkop version < 0.7.0"
+ msg "NetShift version < 0.8.0"
update_config
fi
else
- msg "Unknown podkop version"
+ msg "Unknown NetShift version"
update_config
fi
fi
@@ -302,7 +440,7 @@ sing_box() {
if [ "$(printf '%s\n%s\n' "$sing_box_version" "$required_version" | sort -V | head -n 1)" != "$required_version" ]; then
msg "sing-box version $sing_box_version is older than the required version $required_version."
msg "Removing old version..."
- service podkop stop
+ service netshift stop 2>/dev/null || service podkop stop 2>/dev/null || true
pkg_remove sing-box
fi
}
diff --git a/luci-app-podkop/Makefile b/luci-app-netshift/Makefile
similarity index 69%
rename from luci-app-podkop/Makefile
rename to luci-app-netshift/Makefile
index f6ae3a0d..712405e4 100644
--- a/luci-app-podkop/Makefile
+++ b/luci-app-netshift/Makefile
@@ -1,13 +1,13 @@
include $(TOPDIR)/rules.mk
-PKG_NAME:=luci-app-podkop
+PKG_NAME:=luci-app-netshift
-PKG_VERSION := $(if $(PODKOP_VERSION),$(PODKOP_VERSION),0.$(shell date +%d%m%Y))
+PKG_VERSION := $(if $(NETSHIFT_VERSION),$(NETSHIFT_VERSION),0.$(shell date +%d%m%Y))
PKG_RELEASE:=1
-LUCI_TITLE:=LuCI podkop app
-LUCI_DEPENDS:=+luci-base +podkop
+LUCI_TITLE:=LuCI NetShift app
+LUCI_DEPENDS:=+luci-base +netshift
LUCI_PKGARCH:=all
LUCI_LANG.ru:=Русский (Russian)
LUCI_LANG.en:=English
@@ -24,7 +24,7 @@ define Package/$(PKG_NAME)/install
$(CP) $(PKG_BUILD_DIR)/htdocs/* $(1)$(HTDOCS)/
$(INSTALL_DIR) $(1)/
$(CP) $(PKG_BUILD_DIR)/root/* $(1)/
- sed -i -e 's/__COMPILED_VERSION_VARIABLE__/$(PKG_VERSION)/g' $(1)$(HTDOCS)/luci-static/resources/view/podkop/main.js || true
+ sed -i -e 's/__COMPILED_VERSION_VARIABLE__/$(PKG_VERSION)/g' $(1)$(HTDOCS)/luci-static/resources/view/netshift/main.js || true
endef
$(eval $(call BuildPackage,$(PKG_NAME)))
\ No newline at end of file
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/dashboard.js b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/dashboard.js
similarity index 91%
rename from luci-app-podkop/htdocs/luci-static/resources/view/podkop/dashboard.js
rename to luci-app-netshift/htdocs/luci-static/resources/view/netshift/dashboard.js
index 6fd97cfe..990d4fac 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/dashboard.js
+++ b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/dashboard.js
@@ -4,7 +4,7 @@
"require ui";
"require uci";
"require fs";
-"require view.podkop.main as main";
+"require view.netshift.main as main";
function createDashboardContent(section) {
const o = section.option(form.DummyValue, "_mount_node");
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnostic.js b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/diagnostic.js
similarity index 91%
rename from luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnostic.js
rename to luci-app-netshift/htdocs/luci-static/resources/view/netshift/diagnostic.js
index e6ac146d..02304a4b 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/diagnostic.js
+++ b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/diagnostic.js
@@ -4,7 +4,7 @@
"require ui";
"require uci";
"require fs";
-"require view.podkop.main as main";
+"require view.netshift.main as main";
function createDiagnosticContent(section) {
const o = section.option(form.DummyValue, "_mount_node");
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/main.js
similarity index 93%
rename from luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
rename to luci-app-netshift/htdocs/luci-static/resources/view/netshift/main.js
index 11453b9d..7f9abdb0 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/main.js
+++ b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/main.js
@@ -570,13 +570,13 @@ function parseValueList(value) {
return value.split(/\n/).map((line) => line.split("//")[0]).join(" ").split(/[,\s]+/).map((s) => s.trim()).filter(Boolean);
}
-// src/podkop/methods/custom/getConfigSections.ts
+// src/netshift/methods/custom/getConfigSections.ts
async function getConfigSections() {
- return uci.load("podkop").then(() => uci.sections("podkop"));
+ return uci.load("netshift").then(() => uci.sections("netshift"));
}
-// src/podkop/methods/shell/callBaseMethod.ts
-async function callBaseMethod(method, args = [], command = "/usr/bin/podkop") {
+// src/netshift/methods/shell/callBaseMethod.ts
+async function callBaseMethod(method, args = [], command = "/usr/bin/netshift") {
const response = await executeShellCommand({
command,
args: [method, ...args],
@@ -601,9 +601,9 @@ async function callBaseMethod(method, args = [], command = "/usr/bin/podkop") {
};
}
-// src/podkop/types.ts
-var Podkop;
-((Podkop2) => {
+// src/netshift/types.ts
+var NetShift;
+((NetShift2) => {
let AvailableMethods;
((AvailableMethods2) => {
AvailableMethods2["CHECK_DNS_AVAILABLE"] = "check_dns_available";
@@ -623,85 +623,85 @@ var Podkop;
AvailableMethods2["CHECK_LOGS"] = "check_logs";
AvailableMethods2["GET_SYSTEM_INFO"] = "get_system_info";
AvailableMethods2["SUBSCRIPTION_UPDATE"] = "subscription_update";
- })(AvailableMethods = Podkop2.AvailableMethods || (Podkop2.AvailableMethods = {}));
+ })(AvailableMethods = NetShift2.AvailableMethods || (NetShift2.AvailableMethods = {}));
let AvailableClashAPIMethods;
((AvailableClashAPIMethods2) => {
AvailableClashAPIMethods2["GET_PROXIES"] = "get_proxies";
AvailableClashAPIMethods2["GET_PROXY_LATENCY"] = "get_proxy_latency";
AvailableClashAPIMethods2["GET_GROUP_LATENCY"] = "get_group_latency";
AvailableClashAPIMethods2["SET_GROUP_PROXY"] = "set_group_proxy";
- })(AvailableClashAPIMethods = Podkop2.AvailableClashAPIMethods || (Podkop2.AvailableClashAPIMethods = {}));
-})(Podkop || (Podkop = {}));
+ })(AvailableClashAPIMethods = NetShift2.AvailableClashAPIMethods || (NetShift2.AvailableClashAPIMethods = {}));
+})(NetShift || (NetShift = {}));
-// src/podkop/methods/shell/index.ts
-var PodkopShellMethods = {
+// src/netshift/methods/shell/index.ts
+var NetShiftShellMethods = {
checkDNSAvailable: async () => callBaseMethod(
- Podkop.AvailableMethods.CHECK_DNS_AVAILABLE
+ NetShift.AvailableMethods.CHECK_DNS_AVAILABLE
),
checkFakeIP: async () => callBaseMethod(
- Podkop.AvailableMethods.CHECK_FAKEIP
+ NetShift.AvailableMethods.CHECK_FAKEIP
),
checkNftRules: async () => callBaseMethod(
- Podkop.AvailableMethods.CHECK_NFT_RULES
+ NetShift.AvailableMethods.CHECK_NFT_RULES
),
- getStatus: async () => callBaseMethod(Podkop.AvailableMethods.GET_STATUS),
+ getStatus: async () => callBaseMethod(NetShift.AvailableMethods.GET_STATUS),
checkSingBox: async () => callBaseMethod(
- Podkop.AvailableMethods.CHECK_SING_BOX
+ NetShift.AvailableMethods.CHECK_SING_BOX
),
getSingBoxStatus: async () => callBaseMethod(
- Podkop.AvailableMethods.GET_SING_BOX_STATUS
+ NetShift.AvailableMethods.GET_SING_BOX_STATUS
),
- getClashApiProxies: async () => callBaseMethod(Podkop.AvailableMethods.CLASH_API, [
- Podkop.AvailableClashAPIMethods.GET_PROXIES
+ getClashApiProxies: async () => callBaseMethod(NetShift.AvailableMethods.CLASH_API, [
+ NetShift.AvailableClashAPIMethods.GET_PROXIES
]),
getClashApiProxyLatency: async (tag) => callBaseMethod(
- Podkop.AvailableMethods.CLASH_API,
- [Podkop.AvailableClashAPIMethods.GET_PROXY_LATENCY, tag, "5000"]
+ NetShift.AvailableMethods.CLASH_API,
+ [NetShift.AvailableClashAPIMethods.GET_PROXY_LATENCY, tag, "5000"]
),
getClashApiGroupLatency: async (tag) => callBaseMethod(
- Podkop.AvailableMethods.CLASH_API,
- [Podkop.AvailableClashAPIMethods.GET_GROUP_LATENCY, tag, "10000"]
+ NetShift.AvailableMethods.CLASH_API,
+ [NetShift.AvailableClashAPIMethods.GET_GROUP_LATENCY, tag, "10000"]
),
- setClashApiGroupProxy: async (group, proxy) => callBaseMethod(Podkop.AvailableMethods.CLASH_API, [
- Podkop.AvailableClashAPIMethods.SET_GROUP_PROXY,
+ setClashApiGroupProxy: async (group, proxy) => callBaseMethod(NetShift.AvailableMethods.CLASH_API, [
+ NetShift.AvailableClashAPIMethods.SET_GROUP_PROXY,
group,
proxy
]),
restart: async () => callBaseMethod(
- Podkop.AvailableMethods.RESTART,
+ NetShift.AvailableMethods.RESTART,
[],
- "/etc/init.d/podkop"
+ "/etc/init.d/netshift"
),
start: async () => callBaseMethod(
- Podkop.AvailableMethods.START,
+ NetShift.AvailableMethods.START,
[],
- "/etc/init.d/podkop"
+ "/etc/init.d/netshift"
),
stop: async () => callBaseMethod(
- Podkop.AvailableMethods.STOP,
+ NetShift.AvailableMethods.STOP,
[],
- "/etc/init.d/podkop"
+ "/etc/init.d/netshift"
),
enable: async () => callBaseMethod(
- Podkop.AvailableMethods.ENABLE,
+ NetShift.AvailableMethods.ENABLE,
[],
- "/etc/init.d/podkop"
+ "/etc/init.d/netshift"
),
disable: async () => callBaseMethod(
- Podkop.AvailableMethods.DISABLE,
+ NetShift.AvailableMethods.DISABLE,
[],
- "/etc/init.d/podkop"
+ "/etc/init.d/netshift"
),
- globalCheck: async () => callBaseMethod(Podkop.AvailableMethods.GLOBAL_CHECK),
- showSingBoxConfig: async () => callBaseMethod(Podkop.AvailableMethods.SHOW_SING_BOX_CONFIG),
- checkLogs: async () => callBaseMethod(Podkop.AvailableMethods.CHECK_LOGS),
+ globalCheck: async () => callBaseMethod(NetShift.AvailableMethods.GLOBAL_CHECK),
+ showSingBoxConfig: async () => callBaseMethod(NetShift.AvailableMethods.SHOW_SING_BOX_CONFIG),
+ checkLogs: async () => callBaseMethod(NetShift.AvailableMethods.CHECK_LOGS),
getSystemInfo: async () => callBaseMethod(
- Podkop.AvailableMethods.GET_SYSTEM_INFO
+ NetShift.AvailableMethods.GET_SYSTEM_INFO
),
- subscriptionUpdate: async () => callBaseMethod(Podkop.AvailableMethods.SUBSCRIPTION_UPDATE),
+ subscriptionUpdate: async () => callBaseMethod(NetShift.AvailableMethods.SUBSCRIPTION_UPDATE),
singBoxComponentAction: async (action) => {
const response = await executeShellCommand({
- command: "/usr/bin/podkop",
+ command: "/usr/bin/netshift",
args: ["component_action", "sing_box", action],
timeout: 6e5
});
@@ -729,10 +729,10 @@ var PodkopShellMethods = {
}
};
-// src/podkop/methods/custom/getDashboardSections.ts
+// src/netshift/methods/custom/getDashboardSections.ts
async function getDashboardSections() {
const configSections = await getConfigSections();
- const clashProxies = await PodkopShellMethods.getClashApiProxies();
+ const clashProxies = await NetShiftShellMethods.getClashApiProxies();
if (!clashProxies.success) {
return {
success: false,
@@ -943,15 +943,15 @@ async function getDashboardSections() {
};
}
-// src/podkop/methods/custom/getClashApiSecret.ts
+// src/netshift/methods/custom/getClashApiSecret.ts
async function getClashApiSecret() {
const sections = await getConfigSections();
const settings = sections.find((section) => section[".type"] === "settings");
return settings?.yacd_secret_key || "";
}
-// src/podkop/methods/custom/index.ts
-var CustomPodkopMethods = {
+// src/netshift/methods/custom/index.ts
+var CustomNetShiftMethods = {
getConfigSections,
getDashboardSections,
getClashApiSecret
@@ -963,7 +963,7 @@ var STATUS_COLORS = {
ERROR: "#f44336",
WARNING: "#ff9800"
};
-var PODKOP_LUCI_APP_VERSION = "__COMPILED_VERSION_VARIABLE__";
+var NETSHIFT_LUCI_APP_VERSION = "__COMPILED_VERSION_VARIABLE__";
var FAKEIP_CHECK_DOMAIN = "fakeip.podkop.fyi";
var IP_CHECK_DOMAIN = "ip.podkop.fyi";
var REGIONAL_OPTIONS = [
@@ -1078,7 +1078,7 @@ var COMMAND_SCHEDULING = {
// Lowest priority
};
-// src/podkop/api.ts
+// src/netshift/api.ts
async function createBaseApiRequest(fetchFn, options) {
const wrappedFn = () => options?.timeoutMs && options?.operationName ? withTimeout(
fetchFn(),
@@ -1107,7 +1107,7 @@ async function createBaseApiRequest(fetchFn, options) {
}
}
-// src/podkop/methods/fakeip/getFakeIpCheck.ts
+// src/netshift/methods/fakeip/getFakeIpCheck.ts
async function getFakeIpCheck() {
return createBaseApiRequest(
() => fetch(`https://${FAKEIP_CHECK_DOMAIN}/check`, {
@@ -1121,7 +1121,7 @@ async function getFakeIpCheck() {
);
}
-// src/podkop/methods/fakeip/getIpCheck.ts
+// src/netshift/methods/fakeip/getIpCheck.ts
async function getIpCheck() {
return createBaseApiRequest(
() => fetch(`https://${IP_CHECK_DOMAIN}/check`, {
@@ -1135,13 +1135,13 @@ async function getIpCheck() {
);
}
-// src/podkop/methods/fakeip/index.ts
+// src/netshift/methods/fakeip/index.ts
var RemoteFakeIPMethods = {
getFakeIpCheck,
getIpCheck
};
-// src/podkop/services/tab.service.ts
+// src/netshift/services/tab.service.ts
var TabService = class _TabService {
constructor() {
this.observer = null;
@@ -1208,12 +1208,12 @@ var TabService = class _TabService {
};
var TabServiceInstance = TabService.getInstance();
-// src/podkop/tabs/diagnostic/helpers/getCheckTitle.ts
+// src/netshift/tabs/diagnostic/helpers/getCheckTitle.ts
function getCheckTitle(name) {
return `${name} ${_("checks")}`;
}
-// src/podkop/tabs/diagnostic/checks/contstants.ts
+// src/netshift/tabs/diagnostic/checks/contstants.ts
var DIAGNOSTICS_CHECKS_MAP = {
["DNS" /* DNS */]: {
order: 1,
@@ -1242,12 +1242,12 @@ var DIAGNOSTICS_CHECKS_MAP = {
}
};
-// src/podkop/tabs/diagnostic/diagnostic.store.ts
+// src/netshift/tabs/diagnostic/diagnostic.store.ts
var initialDiagnosticStore = {
diagnosticsSystemInfo: {
loading: true,
- podkop_version: "loading",
- podkop_latest_version: "loading",
+ netshift_version: "loading",
+ netshift_latest_version: "loading",
luci_app_version: "loading",
sing_box_version: "loading",
openwrt_version: "loading",
@@ -1372,7 +1372,7 @@ var loadingDiagnosticsChecksStore = {
]
};
-// src/podkop/services/store.service.ts
+// src/netshift/services/store.service.ts
function jsonStableStringify(obj) {
return JSON.stringify(obj, (_2, value) => {
if (value && typeof value === "object" && !Array.isArray(value)) {
@@ -1485,7 +1485,7 @@ var initialStore = {
servicesInfoWidget: {
loading: true,
failed: false,
- data: { singbox: 0, podkop: 0 }
+ data: { singbox: 0, netshift: 0 }
},
sectionsWidget: {
loading: true,
@@ -1510,7 +1510,7 @@ function downloadAsTxt(text, filename) {
URL.revokeObjectURL(link.href);
}
-// src/podkop/services/logger.service.ts
+// src/netshift/services/logger.service.ts
var Logger = class {
constructor() {
this.logs = [];
@@ -1565,8 +1565,8 @@ var Logger = class {
};
var logger = new Logger();
-// src/podkop/services/podkopLogWatcher.service.ts
-var PodkopLogWatcher = class _PodkopLogWatcher {
+// src/netshift/services/netshiftLogWatcher.service.ts
+var NetShiftLogWatcher = class _NetShiftLogWatcher {
constructor() {
this.intervalMs = 5e3;
this.lastLines = /* @__PURE__ */ new Set();
@@ -1580,27 +1580,27 @@ var PodkopLogWatcher = class _PodkopLogWatcher {
}
}
static getInstance() {
- if (!_PodkopLogWatcher.instance) {
- _PodkopLogWatcher.instance = new _PodkopLogWatcher();
+ if (!_NetShiftLogWatcher.instance) {
+ _NetShiftLogWatcher.instance = new _NetShiftLogWatcher();
}
- return _PodkopLogWatcher.instance;
+ return _NetShiftLogWatcher.instance;
}
init(fetcher, options) {
this.fetcher = fetcher;
this.onNewLog = options?.onNewLog;
this.intervalMs = options?.intervalMs ?? 5e3;
logger.info(
- "[PodkopLogWatcher]",
+ "[NetShiftLogWatcher]",
`initialized (interval: ${this.intervalMs}ms)`
);
}
async checkOnce() {
if (!this.fetcher) {
- logger.warn("[PodkopLogWatcher]", "fetcher not found");
+ logger.warn("[NetShiftLogWatcher]", "fetcher not found");
return;
}
if (this.paused) {
- logger.debug("[PodkopLogWatcher]", "skipped check \u2014 tab not visible");
+ logger.debug("[NetShiftLogWatcher]", "skipped check \u2014 tab not visible");
return;
}
try {
@@ -1617,19 +1617,19 @@ var PodkopLogWatcher = class _PodkopLogWatcher {
this.lastLines = new Set(arr.slice(-500));
}
} catch (err) {
- logger.error("[PodkopLogWatcher]", "failed to read logs:", err);
+ logger.error("[NetShiftLogWatcher]", "failed to read logs:", err);
}
}
start() {
if (this.running) return;
if (!this.fetcher) {
- logger.warn("[PodkopLogWatcher]", "attempted to start without fetcher");
+ logger.warn("[NetShiftLogWatcher]", "attempted to start without fetcher");
return;
}
this.running = true;
this.timer = setInterval(() => this.checkOnce(), this.intervalMs);
logger.info(
- "[PodkopLogWatcher]",
+ "[NetShiftLogWatcher]",
`started (interval: ${this.intervalMs}ms)`
);
}
@@ -1637,26 +1637,26 @@ var PodkopLogWatcher = class _PodkopLogWatcher {
if (!this.running) return;
this.running = false;
if (this.timer) clearInterval(this.timer);
- logger.info("[PodkopLogWatcher]", "stopped");
+ logger.info("[NetShiftLogWatcher]", "stopped");
}
pause() {
if (!this.running || this.paused) return;
this.paused = true;
- logger.info("[PodkopLogWatcher]", "paused (tab not visible)");
+ logger.info("[NetShiftLogWatcher]", "paused (tab not visible)");
}
resume() {
if (!this.running || !this.paused) return;
this.paused = false;
- logger.info("[PodkopLogWatcher]", "resumed (tab active)");
+ logger.info("[NetShiftLogWatcher]", "resumed (tab active)");
this.checkOnce();
}
reset() {
this.lastLines.clear();
- logger.info("[PodkopLogWatcher]", "log history reset");
+ logger.info("[NetShiftLogWatcher]", "log history reset");
}
};
-// src/podkop/services/core.service.ts
+// src/netshift/services/core.service.ts
function coreService() {
TabServiceInstance.onChange((activeId, tabs) => {
logger.info("[TAB]", activeId);
@@ -1667,10 +1667,10 @@ function coreService() {
}
});
});
- const watcher = PodkopLogWatcher.getInstance();
+ const watcher = NetShiftLogWatcher.getInstance();
watcher.init(
async () => {
- const logs = await PodkopShellMethods.checkLogs();
+ const logs = await NetShiftShellMethods.checkLogs();
if (logs.success) {
return logs.data;
}
@@ -1680,7 +1680,7 @@ function coreService() {
intervalMs: 3e3,
onNewLog: (line) => {
if (line.toLowerCase().includes("[error]") || line.toLowerCase().includes("[fatal]")) {
- ui.addNotification("Podkop Error", E("div", {}, line), "error");
+ ui.addNotification("NetShift Error", E("div", {}, line), "error");
}
}
}
@@ -1688,7 +1688,7 @@ function coreService() {
watcher.start();
}
-// src/podkop/services/socket.service.ts
+// src/netshift/services/socket.service.ts
var SocketManager = class _SocketManager {
constructor() {
this.sockets = /* @__PURE__ */ new Map();
@@ -1827,7 +1827,7 @@ var SocketManager = class _SocketManager {
};
var socket = SocketManager.getInstance();
-// src/podkop/tabs/dashboard/partials/renderSections.ts
+// src/netshift/tabs/dashboard/partials/renderSections.ts
function renderFailedState() {
return E(
"div",
@@ -1931,7 +1931,7 @@ function renderSections(props) {
return renderDefaultState(props);
}
-// src/podkop/tabs/dashboard/partials/renderWidget.ts
+// src/netshift/tabs/dashboard/partials/renderWidget.ts
function renderFailedState2() {
return E(
"div",
@@ -1993,7 +1993,7 @@ function renderWidget(props) {
return renderDefaultState2(props);
}
-// src/podkop/tabs/dashboard/render.ts
+// src/netshift/tabs/dashboard/render.ts
function render() {
return E(
"div",
@@ -2061,33 +2061,36 @@ function prettyBytes(n) {
return n + " " + unit;
}
-// src/podkop/fetchers/fetchServicesInfo.ts
+// src/netshift/fetchers/fetchServicesInfo.ts
async function fetchServicesInfo() {
- const [podkop, singbox] = await Promise.all([
- PodkopShellMethods.getStatus(),
- PodkopShellMethods.getSingBoxStatus()
+ const [netshift, singbox] = await Promise.all([
+ NetShiftShellMethods.getStatus(),
+ NetShiftShellMethods.getSingBoxStatus()
]);
- if (!podkop.success || !singbox.success) {
+ if (!netshift.success || !singbox.success) {
store.set({
servicesInfoWidget: {
loading: false,
failed: true,
- data: { singbox: 0, podkop: 0 }
+ data: { singbox: 0, netshift: 0 }
}
});
}
- if (podkop.success && singbox.success) {
+ if (netshift.success && singbox.success) {
store.set({
servicesInfoWidget: {
loading: false,
failed: false,
- data: { singbox: singbox.data.running, podkop: podkop.data.enabled }
+ data: {
+ singbox: singbox.data.running,
+ netshift: netshift.data.enabled
+ }
}
});
}
}
-// src/podkop/tabs/dashboard/initController.ts
+// src/netshift/tabs/dashboard/initController.ts
async function fetchDashboardSections() {
const prev = store.get().sectionsWidget;
store.set({
@@ -2096,7 +2099,7 @@ async function fetchDashboardSections() {
failed: false
}
});
- const { data, success } = await CustomPodkopMethods.getDashboardSections();
+ const { data, success } = await CustomNetShiftMethods.getDashboardSections();
if (!success) {
logger.error("[DASHBOARD]", "fetchDashboardSections: failed to fetch");
}
@@ -2186,7 +2189,7 @@ async function connectToClashSockets() {
);
}
async function handleChooseOutbound(selector, tag) {
- await PodkopShellMethods.setClashApiGroupProxy(selector, tag);
+ await NetShiftShellMethods.setClashApiGroupProxy(selector, tag);
await fetchDashboardSections();
}
async function handleTestGroupLatency(tag) {
@@ -2196,7 +2199,7 @@ async function handleTestGroupLatency(tag) {
latencyFetching: true
}
});
- await PodkopShellMethods.getClashApiGroupLatency(tag);
+ await NetShiftShellMethods.getClashApiGroupLatency(tag);
await fetchDashboardSections();
store.set({
sectionsWidget: {
@@ -2212,7 +2215,7 @@ async function handleTestProxyLatency(tag) {
latencyFetching: true
}
});
- await PodkopShellMethods.getClashApiProxyLatency(tag);
+ await NetShiftShellMethods.getClashApiProxyLatency(tag);
await fetchDashboardSections();
store.set({
sectionsWidget: {
@@ -2369,10 +2372,10 @@ async function renderServicesInfoWidget() {
title: _("Services info"),
items: [
{
- key: _("Podkop"),
- value: servicesInfoWidget.data.podkop ? _("\u2714 Enabled") : _("\u2718 Disabled"),
+ key: _("NetShift"),
+ value: servicesInfoWidget.data.netshift ? _("\u2714 Enabled") : _("\u2718 Disabled"),
attributes: {
- class: servicesInfoWidget.data.podkop ? "pdk_dashboard-page__widgets-section__item__row--success" : "pdk_dashboard-page__widgets-section__item__row--error"
+ class: servicesInfoWidget.data.netshift ? "pdk_dashboard-page__widgets-section__item__row--success" : "pdk_dashboard-page__widgets-section__item__row--error"
}
},
{
@@ -2457,13 +2460,13 @@ async function initController() {
});
}
-// src/podkop/tabs/dashboard/styles.ts
+// src/netshift/tabs/dashboard/styles.ts
var styles = `
-#cbi-podkop-dashboard-_mount_node > div {
+#cbi-netshift-dashboard-_mount_node > div {
width: 100%;
}
-#cbi-podkop-dashboard > h3 {
+#cbi-netshift-dashboard > h3 {
display: none;
}
@@ -2578,14 +2581,14 @@ var styles = `
`;
-// src/podkop/tabs/dashboard/index.ts
+// src/netshift/tabs/dashboard/index.ts
var DashboardTab = {
render,
initController,
styles
};
-// src/podkop/tabs/diagnostic/renderDiagnostic.ts
+// src/netshift/tabs/diagnostic/renderDiagnostic.ts
function render2() {
return E("div", { id: "diagnostic-status", class: "pdk_diagnostic-page" }, [
E("div", { class: "pdk_diagnostic-page__left-bar" }, [
@@ -2603,7 +2606,7 @@ function render2() {
]);
}
-// src/podkop/tabs/diagnostic/checks/updateCheckStore.ts
+// src/netshift/tabs/diagnostic/checks/updateCheckStore.ts
function updateCheckStore(check, minified) {
const diagnosticsChecks = store.get().diagnosticsChecks;
const other = diagnosticsChecks.filter((item) => item.code !== check.code);
@@ -2617,7 +2620,7 @@ function updateCheckStore(check, minified) {
});
}
-// src/podkop/tabs/diagnostic/helpers/getMeta.ts
+// src/netshift/tabs/diagnostic/helpers/getMeta.ts
function getMeta({ allGood, atLeastOneGood }) {
if (allGood) {
return {
@@ -2637,7 +2640,7 @@ function getMeta({ allGood, atLeastOneGood }) {
};
}
-// src/podkop/tabs/diagnostic/checks/runDnsCheck.ts
+// src/netshift/tabs/diagnostic/checks/runDnsCheck.ts
async function runDnsCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.DNS;
updateCheckStore({
@@ -2648,7 +2651,7 @@ async function runDnsCheck() {
state: "loading",
items: []
});
- const dnsChecks = await PodkopShellMethods.checkDNSAvailable();
+ const dnsChecks = await NetShiftShellMethods.checkDNSAvailable();
if (!dnsChecks.success) {
updateCheckStore({
order,
@@ -2703,7 +2706,7 @@ async function runDnsCheck() {
}
}
-// src/podkop/tabs/diagnostic/checks/runSingBoxCheck.ts
+// src/netshift/tabs/diagnostic/checks/runSingBoxCheck.ts
async function runSingBoxCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.SINGBOX;
updateCheckStore({
@@ -2714,7 +2717,7 @@ async function runSingBoxCheck() {
state: "loading",
items: []
});
- const singBoxChecks = await PodkopShellMethods.checkSingBox();
+ const singBoxChecks = await NetShiftShellMethods.checkSingBox();
if (!singBoxChecks.success) {
updateCheckStore({
order,
@@ -2774,7 +2777,7 @@ async function runSingBoxCheck() {
}
}
-// src/podkop/tabs/diagnostic/checks/runNftCheck.ts
+// src/netshift/tabs/diagnostic/checks/runNftCheck.ts
async function runNftCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.NFT;
updateCheckStore({
@@ -2787,7 +2790,7 @@ async function runNftCheck() {
});
await RemoteFakeIPMethods.getFakeIpCheck();
await RemoteFakeIPMethods.getIpCheck();
- const nftablesChecks = await PodkopShellMethods.checkNftRules();
+ const nftablesChecks = await NetShiftShellMethods.checkNftRules();
if (!nftablesChecks.success) {
updateCheckStore({
order,
@@ -2857,7 +2860,7 @@ async function runNftCheck() {
}
}
-// src/podkop/tabs/diagnostic/checks/runFakeIPCheck.ts
+// src/netshift/tabs/diagnostic/checks/runFakeIPCheck.ts
async function runFakeIPCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.FAKEIP;
updateCheckStore({
@@ -2868,7 +2871,7 @@ async function runFakeIPCheck() {
state: "loading",
items: []
});
- const routerFakeIPResponse = await PodkopShellMethods.checkFakeIP();
+ const routerFakeIPResponse = await NetShiftShellMethods.checkFakeIP();
const checkFakeIPResponse = await RemoteFakeIPMethods.getFakeIpCheck();
const checkIPResponse = await RemoteFakeIPMethods.getIpCheck();
const checks = {
@@ -3597,7 +3600,7 @@ ${styles2}
${styles3}
`;
-// src/podkop/tabs/diagnostic/partials/renderAvailableActions.ts
+// src/netshift/tabs/diagnostic/partials/renderAvailableActions.ts
function renderAvailableActions({
restart,
start,
@@ -3617,7 +3620,7 @@ function renderAvailableActions({
classNames: ["cbi-button-apply"],
onClick: restart.onClick,
icon: renderRotateCcwIcon24,
- text: _("Restart podkop"),
+ text: _("Restart NetShift"),
loading: restart.loading,
disabled: restart.disabled
})
@@ -3627,7 +3630,7 @@ function renderAvailableActions({
classNames: ["cbi-button-remove"],
onClick: stop.onClick,
icon: renderCircleStopIcon24,
- text: _("Stop podkop"),
+ text: _("Stop NetShift"),
loading: stop.loading,
disabled: stop.disabled
})
@@ -3637,7 +3640,7 @@ function renderAvailableActions({
classNames: ["cbi-button-save"],
onClick: start.onClick,
icon: renderCirclePlayIcon24,
- text: _("Start podkop"),
+ text: _("Start NetShift"),
loading: start.loading,
disabled: start.disabled
})
@@ -3701,7 +3704,7 @@ function renderAvailableActions({
]);
}
-// src/podkop/tabs/diagnostic/partials/renderCheckSection.ts
+// src/netshift/tabs/diagnostic/partials/renderCheckSection.ts
function renderCheckSummary(items) {
if (!items.length) {
return E("div", {}, "");
@@ -3856,7 +3859,7 @@ function renderCheckSection(props) {
return E("div", {}, _("Not implement yet"));
}
-// src/podkop/tabs/diagnostic/partials/renderRunAction.ts
+// src/netshift/tabs/diagnostic/partials/renderRunAction.ts
function renderRunAction({
loading,
click
@@ -3872,7 +3875,7 @@ function renderRunAction({
]);
}
-// src/podkop/tabs/diagnostic/partials/renderSystemInfo.ts
+// src/netshift/tabs/diagnostic/partials/renderSystemInfo.ts
function renderSystemInfo({ items }) {
return E("div", { class: "pdk_diagnostic-page__right-bar__system-info" }, [
E(
@@ -3913,7 +3916,7 @@ function normalizeCompiledVersion(version) {
return version;
}
-// src/podkop/tabs/diagnostic/partials/renderWikiDisclaimer.ts
+// src/netshift/tabs/diagnostic/partials/renderWikiDisclaimer.ts
function renderWikiDisclaimer(kind) {
const iconWrap = E("span", {
class: "pdk_diagnostic-page__right-bar__wiki__icon"
@@ -3940,7 +3943,7 @@ function renderWikiDisclaimer(kind) {
classNames: ["cbi-button-save"],
text: _("Visit Wiki"),
onClick: () => window.open(
- "https://podkop.net/docs/troubleshooting/?utm_source=podkop",
+ "https://podkop.net/docs/troubleshooting/?utm_source=netshift",
"_blank",
"noopener,noreferrer"
)
@@ -3948,7 +3951,7 @@ function renderWikiDisclaimer(kind) {
]);
}
-// src/podkop/tabs/diagnostic/checks/runSectionsCheck.ts
+// src/netshift/tabs/diagnostic/checks/runSectionsCheck.ts
async function runSectionsCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.OUTBOUNDS;
updateCheckStore({
@@ -3975,9 +3978,7 @@ async function runSectionsCheck() {
sections.data.map(async (section) => {
async function getLatency() {
if (section.withTagSelect) {
- const latencyGroup = await PodkopShellMethods.getClashApiGroupLatency(
- section.code
- );
+ const latencyGroup = await NetShiftShellMethods.getClashApiGroupLatency(section.code);
const selectedOutbound = section.outbounds.find(
(item) => item.selected
);
@@ -4008,7 +4009,7 @@ async function runSectionsCheck() {
latency: _("Not responding")
};
}
- const latencyProxy = await PodkopShellMethods.getClashApiProxyLatency(
+ const latencyProxy = await NetShiftShellMethods.getClashApiProxyLatency(
section.code
);
const success2 = latencyProxy.success && !latencyProxy.data.message;
@@ -4052,27 +4053,27 @@ function removeVersionPrefix(version) {
return version.replace(/^v/, "");
}
-// src/podkop/tabs/diagnostic/helpers/getPodkopVersionRow.ts
+// src/netshift/tabs/diagnostic/helpers/getNetshiftVersionRow.ts
function isUnknownVersion(version) {
return version === "unknown" || version === _("unknown");
}
-function getPodkopVersionRow(diagnosticsSystemInfo) {
+function getNetshiftVersionRow(diagnosticsSystemInfo) {
const loading = diagnosticsSystemInfo.loading;
- const unknown = isUnknownVersion(diagnosticsSystemInfo.podkop_version);
- const hasActualVersion = Boolean(diagnosticsSystemInfo.podkop_latest_version) && !isUnknownVersion(diagnosticsSystemInfo.podkop_latest_version);
+ const unknown = isUnknownVersion(diagnosticsSystemInfo.netshift_version);
+ const hasActualVersion = Boolean(diagnosticsSystemInfo.netshift_latest_version) && !isUnknownVersion(diagnosticsSystemInfo.netshift_latest_version);
const version = normalizeCompiledVersion(
- diagnosticsSystemInfo.podkop_version
+ diagnosticsSystemInfo.netshift_version
);
const isDevVersion = version === "dev";
if (loading || unknown || !hasActualVersion || isDevVersion) {
return {
- key: "Podkop",
+ key: "NetShift",
value: version
};
}
- if (removeVersionPrefix(version) !== removeVersionPrefix(diagnosticsSystemInfo.podkop_latest_version)) {
+ if (removeVersionPrefix(version) !== removeVersionPrefix(diagnosticsSystemInfo.netshift_latest_version)) {
return {
- key: "Podkop",
+ key: "NetShift",
value: version,
tag: {
label: _("Outdated"),
@@ -4081,7 +4082,7 @@ function getPodkopVersionRow(diagnosticsSystemInfo) {
};
}
return {
- key: "Podkop",
+ key: "NetShift",
value: version,
tag: {
label: _("Latest"),
@@ -4090,9 +4091,9 @@ function getPodkopVersionRow(diagnosticsSystemInfo) {
};
}
-// src/podkop/tabs/diagnostic/initController.ts
+// src/netshift/tabs/diagnostic/initController.ts
async function fetchSystemInfo() {
- const systemInfo = await PodkopShellMethods.getSystemInfo();
+ const systemInfo = await NetShiftShellMethods.getSystemInfo();
if (systemInfo.success) {
store.set({
diagnosticsSystemInfo: {
@@ -4105,8 +4106,8 @@ async function fetchSystemInfo() {
store.set({
diagnosticsSystemInfo: {
loading: false,
- podkop_version: _("unknown"),
- podkop_latest_version: _("unknown"),
+ netshift_version: _("unknown"),
+ netshift_latest_version: _("unknown"),
luci_app_version: _("unknown"),
sing_box_version: _("unknown"),
openwrt_version: _("unknown"),
@@ -4148,7 +4149,7 @@ async function handleRestart() {
}
});
try {
- await PodkopShellMethods.restart();
+ await NetShiftShellMethods.restart();
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleRestart - e", e);
} finally {
@@ -4173,7 +4174,7 @@ async function handleStop() {
}
});
try {
- await PodkopShellMethods.stop();
+ await NetShiftShellMethods.stop();
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleStop - e", e);
} finally {
@@ -4196,7 +4197,7 @@ async function handleStart() {
}
});
try {
- await PodkopShellMethods.start();
+ await NetShiftShellMethods.start();
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleStart - e", e);
} finally {
@@ -4221,7 +4222,7 @@ async function handleEnable() {
}
});
try {
- await PodkopShellMethods.enable();
+ await NetShiftShellMethods.enable();
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleEnable - e", e);
} finally {
@@ -4243,7 +4244,7 @@ async function handleDisable() {
}
});
try {
- await PodkopShellMethods.disable();
+ await NetShiftShellMethods.disable();
} catch (e) {
logger.error("[DIAGNOSTIC]", "handleDisable - e", e);
} finally {
@@ -4265,7 +4266,7 @@ async function handleShowGlobalCheck() {
}
});
try {
- const globalCheck = await PodkopShellMethods.globalCheck();
+ const globalCheck = await NetShiftShellMethods.globalCheck();
if (globalCheck.success) {
ui.showModal(
_("Global check"),
@@ -4296,7 +4297,7 @@ async function handleViewLogs() {
}
});
try {
- const viewLogs = await PodkopShellMethods.checkLogs();
+ const viewLogs = await NetShiftShellMethods.checkLogs();
if (viewLogs.success) {
ui.showModal(
_("View logs"),
@@ -4327,7 +4328,7 @@ async function handleShowSingBoxConfig() {
}
});
try {
- const showSingBoxConfig = await PodkopShellMethods.showSingBoxConfig();
+ const showSingBoxConfig = await NetShiftShellMethods.showSingBoxConfig();
if (showSingBoxConfig.success) {
ui.showModal(
_("Show sing-box config"),
@@ -4366,7 +4367,7 @@ async function handleInstallSingBox() {
});
const isExtended = store.get().diagnosticsSystemInfo.sing_box_extended === 1;
try {
- const result = await PodkopShellMethods.singBoxComponentAction(
+ const result = await NetShiftShellMethods.singBoxComponentAction(
isExtended ? "install_stable" : "install_extended"
);
if (result.success) {
@@ -4412,7 +4413,7 @@ function renderDiagnosticAvailableActionsWidget() {
const diagnosticsActions = store.get().diagnosticsActions;
const servicesInfoWidget = store.get().servicesInfoWidget;
logger.debug("[DIAGNOSTIC]", "renderDiagnosticAvailableActionsWidget");
- const podkopEnabled = Boolean(servicesInfoWidget.data.podkop);
+ const netshiftEnabled = Boolean(servicesInfoWidget.data.netshift);
const singBoxRunning = Boolean(servicesInfoWidget.data.singbox);
const atLeastOneServiceCommandLoading = servicesInfoWidget.loading || diagnosticsActions.restart.loading || diagnosticsActions.start.loading || diagnosticsActions.stop.loading;
const container = document.getElementById("pdk_diagnostic-page-actions");
@@ -4437,13 +4438,13 @@ function renderDiagnosticAvailableActionsWidget() {
},
enable: {
loading: diagnosticsActions.enable.loading,
- visible: !podkopEnabled,
+ visible: !netshiftEnabled,
onClick: handleEnable,
disabled: atLeastOneServiceCommandLoading
},
disable: {
loading: diagnosticsActions.disable.loading,
- visible: podkopEnabled,
+ visible: netshiftEnabled,
onClick: handleDisable,
disabled: atLeastOneServiceCommandLoading
},
@@ -4483,10 +4484,10 @@ function renderDiagnosticSystemInfoWidget() {
const container = document.getElementById("pdk_diagnostic-page-system-info");
const renderedSystemInfo = renderSystemInfo({
items: [
- getPodkopVersionRow(diagnosticsSystemInfo),
+ getNetshiftVersionRow(diagnosticsSystemInfo),
{
key: "Luci App",
- value: normalizeCompiledVersion(PODKOP_LUCI_APP_VERSION)
+ value: normalizeCompiledVersion(NETSHIFT_LUCI_APP_VERSION)
},
{
key: "Sing-box",
@@ -4594,14 +4595,14 @@ async function initController2() {
});
}
-// src/podkop/tabs/diagnostic/styles.ts
+// src/netshift/tabs/diagnostic/styles.ts
var styles4 = `
-#cbi-podkop-diagnostic-_mount_node > div {
+#cbi-netshift-diagnostic-_mount_node > div {
width: 100%;
}
-#cbi-podkop-diagnostic > h3 {
+#cbi-netshift-diagnostic > h3 {
display: none;
}
@@ -4785,7 +4786,7 @@ var styles4 = `
}
`;
-// src/podkop/tabs/diagnostic/index.ts
+// src/netshift/tabs/diagnostic/index.ts
var DiagnosticTab = {
render: render2,
initController: initController2,
@@ -4800,17 +4801,17 @@ ${PartialStyles}
/* Hide extra H3 for settings tab */
-#cbi-podkop-settings > h3 {
+#cbi-netshift-settings > h3 {
display: none;
}
/* Hide extra H3 for sections tab */
-#cbi-podkop-section > h3:nth-child(1) {
+#cbi-netshift-section > h3:nth-child(1) {
display: none;
}
/* Vertical align for remove section action button */
-#cbi-podkop-section > .cbi-section-remove {
+#cbi-netshift-section > .cbi-section-remove {
margin-bottom: -32px;
}
@@ -5048,7 +5049,7 @@ return baseclass.extend({
CACHE_TIMEOUT,
COMMAND_SCHEDULING,
COMMAND_TIMEOUT,
- CustomPodkopMethods,
+ CustomNetShiftMethods,
DIAGNOSTICS_INITIAL_DELAY,
DIAGNOSTICS_UPDATE_INTERVAL,
DNS_SERVER_OPTIONS,
@@ -5060,8 +5061,8 @@ return baseclass.extend({
FETCH_TIMEOUT,
IP_CHECK_DOMAIN,
Logger,
- PODKOP_LUCI_APP_VERSION,
- PodkopShellMethods,
+ NETSHIFT_LUCI_APP_VERSION,
+ NetShiftShellMethods,
REGIONAL_OPTIONS,
RemoteFakeIPMethods,
STATUS_COLORS,
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/netshift.js
similarity index 73%
rename from luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js
rename to luci-app-netshift/htdocs/luci-static/resources/view/netshift/netshift.js
index f699e173..4893143d 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/podkop.js
+++ b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/netshift.js
@@ -3,34 +3,34 @@
"require form";
"require baseclass";
"require network";
-"require view.podkop.main as main";
+"require view.netshift.main as main";
// Settings content
-"require view.podkop.settings as settings";
+"require view.netshift.settings as settings";
// Sections content
-"require view.podkop.section as section";
+"require view.netshift.section as section";
// Dashboard content
-"require view.podkop.dashboard as dashboard";
+"require view.netshift.dashboard as dashboard";
// Diagnostic content
-"require view.podkop.diagnostic as diagnostic";
+"require view.netshift.diagnostic as diagnostic";
const EntryPoint = {
async render() {
main.injectGlobalStyles();
- const podkopMap = new form.Map(
- "podkop",
- _("Podkop Settings"),
- _("Configuration for Podkop service"),
+ const netshiftMap = new form.Map(
+ "netshift",
+ _("NetShift Settings"),
+ _("Configuration for NetShift service"),
);
// Enable tab views
- podkopMap.tabbed = true;
+ netshiftMap.tabbed = true;
// Sections tab
- const sectionsSection = podkopMap.section(
+ const sectionsSection = netshiftMap.section(
form.TypedSection,
"section",
_("Sections"),
@@ -43,7 +43,7 @@ const EntryPoint = {
section.createSectionContent(sectionsSection);
// Settings tab
- const settingsSection = podkopMap.section(
+ const settingsSection = netshiftMap.section(
form.TypedSection,
"settings",
_("Settings"),
@@ -59,7 +59,7 @@ const EntryPoint = {
settings.createSettingsContent(settingsSection);
// Diagnostic tab
- const diagnosticSection = podkopMap.section(
+ const diagnosticSection = netshiftMap.section(
form.TypedSection,
"diagnostic",
_("Diagnostics"),
@@ -74,7 +74,7 @@ const EntryPoint = {
diagnostic.createDiagnosticContent(diagnosticSection);
// Dashboard tab
- const dashboardSection = podkopMap.section(
+ const dashboardSection = netshiftMap.section(
form.TypedSection,
"dashboard",
_("Dashboard"),
@@ -91,7 +91,7 @@ const EntryPoint = {
// Inject core service
main.coreService();
- return podkopMap.render();
+ return netshiftMap.render();
},
};
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/section.js
similarity index 99%
rename from luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js
rename to luci-app-netshift/htdocs/luci-static/resources/view/netshift/section.js
index a3f052d3..e15eb010 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/section.js
+++ b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/section.js
@@ -3,7 +3,7 @@
"require baseclass";
"require ui";
"require tools.widgets as widgets";
-"require view.podkop.main as main";
+"require view.netshift.main as main";
function createSectionContent(section) {
let o = section.option(
diff --git a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/settings.js
similarity index 97%
rename from luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js
rename to luci-app-netshift/htdocs/luci-static/resources/view/netshift/settings.js
index efe3c29f..8f1cc56f 100644
--- a/luci-app-podkop/htdocs/luci-static/resources/view/podkop/settings.js
+++ b/luci-app-netshift/htdocs/luci-static/resources/view/netshift/settings.js
@@ -3,7 +3,7 @@
"require uci";
"require baseclass";
"require tools.widgets as widgets";
-"require view.podkop.main as main";
+"require view.netshift.main as main";
function createSettingsContent(section) {
let o = section.option(
@@ -219,7 +219,7 @@ function createSettingsContent(section) {
form.Value,
"badwan_reload_delay",
_("Interface Monitoring Delay"),
- _("Delay in milliseconds before reloading podkop after interface UP"),
+ _("Delay in milliseconds before reloading NetShift after interface UP"),
);
o.depends("enable_badwan_interface_monitoring", "1");
o.default = "2000";
@@ -301,10 +301,10 @@ function createSettingsContent(section) {
o.rmempty = false;
o.depends("download_lists_via_proxy", "1");
o.cfgvalue = function (section_id) {
- return uci.get("podkop", section_id, "download_lists_via_proxy_section");
+ return uci.get("netshift", section_id, "download_lists_via_proxy_section");
};
o.load = function () {
- const sections = this.map?.data?.state?.values?.podkop ?? {};
+ const sections = this.map?.data?.state?.values?.netshift ?? {};
this.keylist = [];
this.vallist = [];
@@ -324,7 +324,7 @@ function createSettingsContent(section) {
form.Flag,
"dont_touch_dhcp",
_("Dont Touch My DHCP!"),
- _("Podkop will not modify your DHCP configuration"),
+ _("NetShift will not modify your DHCP configuration"),
);
o.default = "0";
o.rmempty = false;
diff --git a/luci-app-podkop/msgmerge.sh b/luci-app-netshift/msgmerge.sh
similarity index 89%
rename from luci-app-podkop/msgmerge.sh
rename to luci-app-netshift/msgmerge.sh
index 06e57061..a4b4ddb8 100644
--- a/luci-app-podkop/msgmerge.sh
+++ b/luci-app-netshift/msgmerge.sh
@@ -2,7 +2,7 @@
set -euo pipefail
PODIR="po"
-POTFILE="$PODIR/templates/podkop.pot"
+POTFILE="$PODIR/templates/netshift.pot"
WIDTH=120
if [ $# -ne 1 ]; then
@@ -11,7 +11,7 @@ if [ $# -ne 1 ]; then
fi
LANG="$1"
-POFILE="$PODIR/$LANG/podkop.po"
+POFILE="$PODIR/$LANG/netshift.po"
if [ ! -f "$POTFILE" ]; then
echo "Template $POTFILE not found. Run xgettext first."
diff --git a/fe-app-podkop/locales/podkop.ru.po b/luci-app-netshift/po/ru/netshift.po
similarity index 96%
rename from fe-app-podkop/locales/podkop.ru.po
rename to luci-app-netshift/po/ru/netshift.po
index 8bd2c236..2b4db66d 100644
--- a/fe-app-podkop/locales/podkop.ru.po
+++ b/luci-app-netshift/po/ru/netshift.po
@@ -1,14 +1,14 @@
-# RU translations for PODKOP package.
-# Copyright (C) 2026 THE PODKOP'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PODKOP package.
+# RU translations for NETSHIFT package.
+# Copyright (C) 2026 THE NETSHIFT'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the NETSHIFT package.
# yandexru45, 2026.
#
msgid ""
msgstr ""
-"Project-Id-Version: PODKOP\n"
+"Project-Id-Version: NETSHIFT\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-06-02 14:25+0300\n"
-"PO-Revision-Date: 2026-06-02 14:25+0300\n"
+"POT-Creation-Date: 2026-06-02 17:15+0300\n"
+"PO-Revision-Date: 2026-06-02 17:15+0300\n"
"Last-Translator: yandexru45\n"
"Language-Team: none\n"
"Language: ru\n"
@@ -101,8 +101,8 @@ msgstr "Списки сообщества"
msgid "Config File Path"
msgstr "Путь к файлу конфигурации"
-msgid "Configuration for Podkop service"
-msgstr "Настройки сервиса Podkop"
+msgid "Configuration for NetShift service"
+msgstr ""
msgid "Configuration Type"
msgstr "Тип конфигурации"
@@ -125,8 +125,8 @@ msgstr "Дашборд"
msgid "Dashboard currently unavailable"
msgstr "Дашборд сейчас недоступен"
-msgid "Delay in milliseconds before reloading podkop after interface UP"
-msgstr "Задержка в миллисекундах перед перезагрузкой podkop после поднятия интерфейса"
+msgid "Delay in milliseconds before reloading NetShift after interface UP"
+msgstr ""
msgid "Delay value cannot be empty"
msgstr "Значение задержки не может быть пустым"
@@ -476,6 +476,15 @@ msgstr "Наблюдаемые интерфейсы"
msgid "Must be a number in the range of 50 - 1000"
msgstr "Должно быть числом от 50 до 1000"
+msgid "NetShift"
+msgstr ""
+
+msgid "NetShift Settings"
+msgstr ""
+
+msgid "NetShift will not modify your DHCP configuration"
+msgstr ""
+
msgid "Network Interface"
msgstr "Сетевой интерфейс"
@@ -521,15 +530,6 @@ msgstr "Путь должен заканчиваться на cache.db"
msgid "Pending"
msgstr "Ожидает запуска"
-msgid "Podkop"
-msgstr "Podkop"
-
-msgid "Podkop Settings"
-msgstr "Настройки podkop"
-
-msgid "Podkop will not modify your DHCP configuration"
-msgstr "Podkop не будет изменять вашу конфигурацию DHCP."
-
msgid "Proxy Configuration URL"
msgstr "URL конфигурации прокси"
@@ -551,8 +551,8 @@ msgstr "Внешние списки подсетей"
msgid "Resolve real IP for routing"
msgstr "Разрешение реальных IP-адресов"
-msgid "Restart podkop"
-msgstr "Перезапустить Podkop"
+msgid "Restart NetShift"
+msgstr ""
msgid "Router DNS is not routed through sing-box"
msgstr "DNS роутера не проходит через sing-box"
@@ -698,11 +698,11 @@ msgstr "Укажите URL-адреса для загрузки и исполь
msgid "Specify the path to the list file located on the router filesystem"
msgstr "Укажите путь к файлу списка, расположенному в файловой системе маршрутизатора."
-msgid "Start podkop"
-msgstr "Запустить podkop"
+msgid "Start NetShift"
+msgstr ""
-msgid "Stop podkop"
-msgstr "Остановить podkop"
+msgid "Stop NetShift"
+msgstr ""
msgid "Subscription"
msgstr ""
diff --git a/luci-app-netshift/po/templates/netshift.pot b/luci-app-netshift/po/templates/netshift.pot
new file mode 100644
index 00000000..6e8ed43a
--- /dev/null
+++ b/luci-app-netshift/po/templates/netshift.pot
@@ -0,0 +1,1183 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) 2026 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the NETSHIFT package.
+# yandexru45 , 2026.
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: NETSHIFT\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2026-06-02 14:15+0300\n"
+"PO-Revision-Date: 2026-06-02 14:15+0300\n"
+"Last-Translator: yandexru45 \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src\netshift\tabs\dashboard\initController.ts:345
+msgid "✔ Enabled"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:356
+msgid "✔ Running"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:346
+msgid "✘ Disabled"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:357
+msgid "✘ Stopped"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:127
+msgid "Группировать по странам"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:128
+msgid "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:307
+msgid "Active Connections"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:106
+msgid "Additional marking rules found"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:247
+msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:251
+msgid "Applicable for SOCKS and Shadowsocks proxy"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:496
+msgid "At least one valid domain must be specified. Comments-only content is not allowed."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:577
+msgid "At least one valid subnet or IP must be specified. Comments-only content is not allowed."
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:47
+msgid "Available actions"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:65
+msgid "Bootsrap DNS"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:45
+msgid "Bootstrap DNS server"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:58
+msgid "Browser is not using FakeIP"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:57
+msgid "Browser is using FakeIP correctly"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:348
+msgid "Cache File Path"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:362
+msgid "Cache file path cannot be empty"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:27
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:28
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:27
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:25
+msgid "Cannot receive checks result"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:15
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:15
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:13
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:15
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:13
+msgid "Checking, please wait"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getCheckTitle.ts:2
+msgid "checks"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getMeta.ts:26
+msgid "Checks failed"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getMeta.ts:13
+msgid "Checks passed"
+msgstr ""
+
+#: src\validators\validateSubnet.ts:33
+msgid "CIDR must be between 0 and 32"
+msgstr ""
+
+#: src\partials\modal\renderModal.ts:26
+msgid "Close"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:351
+msgid "Community Lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:335
+msgid "Config File Path"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:27
+msgid "Configuration for NetShift service"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:23
+msgid "Configuration Type"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:12
+msgid "Connection Type"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:26
+msgid "Connection URL"
+msgstr ""
+
+#: src\partials\modal\renderModal.ts:20
+msgid "Copy"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\partials\renderWidget.ts:22
+msgid "Currently unavailable"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:80
+msgid "Dashboard"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\partials\renderSections.ts:19
+msgid "Dashboard currently unavailable"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:222
+msgid "Delay in milliseconds before reloading NetShift after interface UP"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:229
+msgid "Delay value cannot be empty"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:82
+msgid "DHCP has DNS server"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:65
+msgid "Diagnostics"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:83
+msgid "Disable autostart"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:265
+msgid "Disable QUIC"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:266
+msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:442
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:522
+msgid "Disabled"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:77
+msgid "DNS on router"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:319
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:15
+msgid "DNS over HTTPS (DoH)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:320
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:16
+msgid "DNS over TLS (DoT)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:316
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:12
+msgid "DNS Protocol Type"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:68
+msgid "DNS Rewrite TTL"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:329
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:24
+msgid "DNS Server"
+msgstr ""
+
+#: src\validators\validateDns.ts:7
+msgid "DNS server address cannot be empty"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderWikiDisclaimer.ts:26
+msgid "Do not panic, everything can be fixed, just..."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:306
+msgid "Domain Resolver"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:326
+msgid "Dont Touch My DHCP!"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:241
+#: src\netshift\tabs\dashboard\initController.ts:275
+msgid "Downlink"
+msgstr ""
+
+#: src\partials\modal\renderModal.ts:15
+msgid "Download"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:288
+msgid "Download Lists via Proxy/VPN"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:297
+msgid "Download Lists via specific proxy section"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:289
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:298
+msgid "Downloading all lists via specific Proxy/VPN"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:443
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:523
+msgid "Dynamic List"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:93
+msgid "Enable autostart"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:307
+msgid "Enable built-in DNS resolver for domains handled by this section"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:746
+msgid "Enable DNS resolve to get real IP when routing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:717
+msgid "Enable Mixed Proxy"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:126
+msgid "Enable Output Network Interface"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:718
+msgid "Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:237
+msgid "Enable YACD"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:246
+msgid "Enable YACD WAN Access"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:67
+msgid "Enter complete outbound configuration in JSON format"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:478
+msgid "Enter domain names separated by commas, spaces, or newlines. You can add comments using //"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:452
+msgid "Enter domain names without protocols, e.g. example.com or sub.example.com"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:532
+msgid "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:90
+msgid "Enter the subscription URL to fetch proxy configurations from your provider"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:187
+msgid "Every 1 minute"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:119
+msgid "Every 12 hours"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:117
+msgid "Every 3 hours"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:188
+msgid "Every 3 minutes"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:115
+msgid "Every 30 minutes"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:186
+msgid "Every 30 seconds"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:189
+msgid "Every 5 minutes"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:118
+msgid "Every 6 hours"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:120
+msgid "Every day"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:116
+msgid "Every hour"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:402
+msgid "Exclude NTP"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:403
+msgid "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN"
+msgstr ""
+
+#: src\helpers\copyToClipboard.ts:12
+msgid "Failed to copy!"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:229
+#: src\netshift\tabs\diagnostic\initController.ts:233
+#: src\netshift\tabs\diagnostic\initController.ts:263
+#: src\netshift\tabs\diagnostic\initController.ts:267
+#: src\netshift\tabs\diagnostic\initController.ts:304
+#: src\netshift\tabs\diagnostic\initController.ts:308
+#: src\netshift\tabs\diagnostic\initController.ts:342
+#: src\netshift\tabs\diagnostic\initController.ts:346
+msgid "Failed to execute!"
+msgstr ""
+
+#: src\netshift\methods\custom\getDashboardSections.ts:150
+#: src\netshift\methods\custom\getDashboardSections.ts:181
+#: src\netshift\methods\custom\getDashboardSections.ts:218
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:58
+msgid "Fastest"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:690
+msgid "Fully Routed IPs"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:102
+msgid "Get global check"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:224
+msgid "Global check"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:113
+msgid "How often to automatically update the subscription"
+msgstr ""
+
+#: src\netshift\api.ts:27
+msgid "HTTP error"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:129
+msgid "Install extended"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:129
+msgid "Install stable"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:189
+msgid "Interface Monitoring"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:221
+msgid "Interface Monitoring Delay"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:190
+msgid "Interface monitoring for Bad WAN"
+msgstr ""
+
+#: src\validators\validateDns.ts:23
+msgid "Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH"
+msgstr ""
+
+#: src\validators\validateDomain.ts:18
+#: src\validators\validateDomain.ts:27
+msgid "Invalid domain address"
+msgstr ""
+
+#: src\validators\validateSubnet.ts:11
+msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:90
+msgid "Invalid HY2 URL: insecure must be 0 or 1"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:77
+msgid "Invalid HY2 URL: invalid port number"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:30
+msgid "Invalid HY2 URL: missing credentials/server"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:47
+msgid "Invalid HY2 URL: missing host"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:41
+msgid "Invalid HY2 URL: missing host & port"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:36
+msgid "Invalid HY2 URL: missing password"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:50
+msgid "Invalid HY2 URL: missing port"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:18
+msgid "Invalid HY2 URL: must not contain spaces"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:12
+msgid "Invalid HY2 URL: must start with hysteria2:// or hy2://"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:108
+msgid "Invalid HY2 URL: obfs-password required when obfs is set"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:122
+msgid "Invalid HY2 URL: parsing failed"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:116
+msgid "Invalid HY2 URL: sni cannot be empty"
+msgstr ""
+
+#: src\validators\validateHysteriaUrl.ts:98
+msgid "Invalid HY2 URL: unsupported obfs type"
+msgstr ""
+
+#: src\validators\validateIp.ts:11
+msgid "Invalid IP address"
+msgstr ""
+
+#: src\validators\validateOutboundJson.ts:9
+msgid "Invalid JSON format"
+msgstr ""
+
+#: src\validators\validatePath.ts:22
+msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:85
+msgid "Invalid port number. Must be between 1 and 65535"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:37
+msgid "Invalid Shadowsocks URL: decoded credentials must contain method:password"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:27
+msgid "Invalid Shadowsocks URL: missing credentials"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:46
+msgid "Invalid Shadowsocks URL: missing method and password separator \":\""
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:76
+msgid "Invalid Shadowsocks URL: missing port"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:67
+msgid "Invalid Shadowsocks URL: missing server"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:58
+msgid "Invalid Shadowsocks URL: missing server address"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:16
+msgid "Invalid Shadowsocks URL: must not contain spaces"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:8
+msgid "Invalid Shadowsocks URL: must start with ss://"
+msgstr ""
+
+#: src\validators\validateShadowsocksUrl.ts:91
+msgid "Invalid Shadowsocks URL: parsing failed"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:73
+msgid "Invalid SOCKS URL: invalid host format"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:63
+msgid "Invalid SOCKS URL: invalid port number"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:42
+msgid "Invalid SOCKS URL: missing host and port"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:51
+msgid "Invalid SOCKS URL: missing hostname or IP"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:56
+msgid "Invalid SOCKS URL: missing port"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:34
+msgid "Invalid SOCKS URL: missing username"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:19
+msgid "Invalid SOCKS URL: must not contain spaces"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:10
+msgid "Invalid SOCKS URL: must start with socks4://, socks4a://, or socks5://"
+msgstr ""
+
+#: src\validators\validateSocksUrl.ts:77
+msgid "Invalid SOCKS URL: parsing failed"
+msgstr ""
+
+#: src\validators\validateTrojanUrl.ts:15
+msgid "Invalid Trojan URL: must not contain spaces"
+msgstr ""
+
+#: src\validators\validateTrojanUrl.ts:8
+msgid "Invalid Trojan URL: must start with trojan://"
+msgstr ""
+
+#: src\validators\validateTrojanUrl.ts:56
+msgid "Invalid Trojan URL: parsing failed"
+msgstr ""
+
+#: src\validators\validateUrl.ts:8
+#: src\validators\validateUrl.ts:31
+msgid "Invalid URL format"
+msgstr ""
+
+#: src\validators\validateVlessUrl.ts:110
+msgid "Invalid VLESS URL: parsing failed"
+msgstr ""
+
+#: src\validators\validateSubnet.ts:18
+msgid "IP address 0.0.0.0 is not allowed"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getMeta.ts:20
+msgid "Issues detected"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getNetshiftVersionRow.ts:48
+msgid "Latest"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:276
+msgid "List Update Frequency"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:598
+msgid "Local Domain Lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:621
+msgid "Local Subnet Lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:384
+msgid "Log Level"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runDnsCheck.ts:72
+msgid "Main DNS"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:311
+msgid "Memory Usage"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:730
+msgid "Mixed Proxy Port"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:198
+msgid "Monitored Interfaces"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:215
+msgid "Must be a number in the range of 50 - 1000"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:343
+msgid "NetShift"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:26
+msgid "NetShift Settings"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:327
+msgid "NetShift will not modify your DHCP configuration"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:260
+msgid "Network Interface"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:105
+msgid "No other marking rules found"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderCheckSection.ts:189
+msgid "Not implement yet"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:74
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:80
+#: src\netshift\tabs\diagnostic\checks\runSectionsCheck.ts:99
+msgid "Not responding"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:59
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:67
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:75
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:83
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:91
+msgid "Not running"
+msgstr ""
+
+#: src\helpers\withTimeout.ts:7
+msgid "Operation timed out"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:30
+msgid "Outbound Config"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:66
+msgid "Outbound Configuration"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\helpers\getNetshiftVersionRow.ts:38
+msgid "Outdated"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:135
+msgid "Output Network Interface"
+msgstr ""
+
+#: src\validators\validatePath.ts:7
+msgid "Path cannot be empty"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:366
+msgid "Path must be absolute (start with /)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:375
+msgid "Path must contain at least one directory (like /tmp/cache.db)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:370
+msgid "Path must end with cache.db"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:107
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:115
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:123
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:131
+#: src\netshift\tabs\diagnostic\diagnostic.store.ts:139
+msgid "Pending"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:37
+msgid "Proxy Configuration URL"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:66
+msgid "Proxy traffic is not routed via FakeIP"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:65
+msgid "Proxy traffic is routed via FakeIP"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:385
+msgid "Regional options cannot be used together"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:644
+msgid "Remote Domain Lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:667
+msgid "Remote Subnet Lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:745
+msgid "Resolve real IP for routing"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:53
+msgid "Restart NetShift"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:51
+msgid "Router DNS is not routed through sing-box"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runFakeIPCheck.ts:50
+msgid "Router DNS is routed through sing-box"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:413
+msgid "Routing Excluded IPs"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:79
+msgid "Rules mangle counters"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:74
+msgid "Rules mangle exist"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:89
+msgid "Rules mangle output counters"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:84
+msgid "Rules mangle output exist"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:99
+msgid "Rules proxy counters"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:94
+msgid "Rules proxy exist"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderRunAction.ts:15
+msgid "Run Diagnostic"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:404
+msgid "Russia inside restrictions"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:257
+msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:36
+msgid "Sections"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:352
+msgid "Select a predefined list for routing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:13
+msgid "Select between VPN and Proxy connection methods for traffic routing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:13
+msgid "Select DNS protocol to use"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:277
+msgid "Select how often the domain or subnet lists are updated automatically"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:24
+msgid "Select how to configure the proxy"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:261
+msgid "Select network interface for VPN connection"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:330
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:25
+msgid "Select or enter DNS server address"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:349
+msgid "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:336
+msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:317
+msgid "Select the DNS protocol type for the domain resolver"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:440
+msgid "Select the list type for adding custom domains"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:520
+msgid "Select the list type for adding custom subnets"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:385
+msgid "Select the log level for sing-box"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:90
+msgid "Select the network interface from which the traffic will originate"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:136
+msgid "Select the network interface to which the traffic will originate"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:199
+msgid "Select the WAN interfaces to be monitored"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:27
+msgid "Selector"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:137
+msgid "Selector Proxy Links"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:340
+msgid "Services info"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\netshift.js:49
+msgid "Settings"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:292
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:120
+msgid "Show sing-box config"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:354
+msgid "Sing-box"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:77
+msgid "Sing-box autostart disabled"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:337
+msgid "Sing-box core changed, version:"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:62
+msgid "Sing-box installed"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:87
+msgid "Sing-box listening ports"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:82
+msgid "Sing-box process running"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:72
+msgid "Sing-box service exist"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runSingBoxCheck.ts:67
+msgid "Sing-box version is compatible (newer than 1.12.4)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:89
+msgid "Source Network Interface"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:414
+msgid "Specify a local IP address to be excluded from routing"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:691
+msgid "Specify local IP addresses or subnets whose traffic will always be routed through the configured route"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:645
+msgid "Specify remote URLs to download and use domain lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:668
+msgid "Specify remote URLs to download and use subnet lists"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:599
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:622
+msgid "Specify the path to the list file located on the router filesystem"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:73
+msgid "Start NetShift"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:63
+msgid "Stop NetShift"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:29
+msgid "Subscription"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:112
+msgid "Subscription Update Interval"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:89
+msgid "Subscription URL"
+msgstr ""
+
+#: src\helpers\copyToClipboard.ts:10
+msgid "Successfully copied!"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:304
+msgid "System info"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderSystemInfo.ts:21
+msgid "System information"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\checks\runNftCheck.ts:69
+msgid "Table exist"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\partials\renderSections.ts:108
+msgid "Test latency"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:444
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:524
+msgid "Text List"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:46
+msgid "The DNS server used to look up the IP address of an upstream DNS server"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:184
+msgid "The interval between connectivity tests"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:198
+msgid "The maximum difference in response times (ms) allowed when comparing servers"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:222
+msgid "The URL used to test server connectivity"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:69
+msgid "Time in seconds for DNS record caching (default: 60)"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:238
+msgid "Traffic"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:268
+msgid "Traffic Total"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderWikiDisclaimer.ts:25
+msgid "Troubleshooting"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:80
+msgid "TTL must be a positive number"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:75
+msgid "TTL value cannot be empty"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:321
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:17
+msgid "UDP (Unprotected DNS)"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:250
+msgid "UDP over TCP"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:39
+#: src\netshift\tabs\diagnostic\initController.ts:40
+#: src\netshift\tabs\diagnostic\initController.ts:41
+#: src\netshift\tabs\diagnostic\initController.ts:42
+#: src\netshift\tabs\diagnostic\initController.ts:43
+#: src\netshift\tabs\diagnostic\initController.ts:44
+#: src\netshift\tabs\diagnostic\helpers\getNetshiftVersionRow.ts:7
+msgid "unknown"
+msgstr ""
+
+#: src\netshift\api.ts:40
+msgid "Unknown error"
+msgstr ""
+
+#: src\netshift\tabs\dashboard\initController.ts:240
+#: src\netshift\tabs\dashboard\initController.ts:271
+msgid "Uplink"
+msgstr ""
+
+#: src\validators\validateProxyUrl.ts:37
+msgid "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://"
+msgstr ""
+
+#: src\validators\validateUrl.ts:17
+msgid "URL must use one of the following protocols:"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:28
+msgid "URLTest"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:183
+msgid "URLTest Check Interval"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:160
+msgid "URLTest Proxy Links"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:221
+msgid "URLTest Testing URL"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:197
+msgid "URLTest Tolerance"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:439
+msgid "User Domain List Type"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:451
+msgid "User Domains"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:477
+msgid "User Domains List"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:519
+msgid "User Subnet List Type"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:531
+msgid "User Subnets"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:557
+msgid "User Subnets List"
+msgstr ""
+
+#: src\validators\validateDns.ts:14
+#: src\validators\validateDns.ts:18
+#: src\validators\validateDomain.ts:13
+#: src\validators\validateDomain.ts:30
+#: src\validators\validateHysteriaUrl.ts:120
+#: src\validators\validateIp.ts:8
+#: src\validators\validateOutboundJson.ts:7
+#: src\validators\validatePath.ts:16
+#: src\validators\validateShadowsocksUrl.ts:95
+#: src\validators\validateSocksUrl.ts:80
+#: src\validators\validateSubnet.ts:38
+#: src\validators\validateTrojanUrl.ts:59
+#: src\validators\validateUrl.ts:28
+#: src\validators\validateVlessUrl.ts:108
+msgid "Valid"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:510
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:589
+msgid "Validation errors:"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\initController.ts:258
+#: src\netshift\tabs\diagnostic\partials\renderAvailableActions.ts:111
+msgid "View logs"
+msgstr ""
+
+#: src\netshift\tabs\diagnostic\partials\renderWikiDisclaimer.ts:31
+msgid "Visit Wiki"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:38
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:138
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:161
+msgid "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:387
+msgid "Warning: %s cannot be used together with %s. Previous selections have been removed."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\section.js:406
+msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:256
+msgid "YACD Secret Key"
+msgstr ""
+
+#: ..\luci-app-netshift\htdocs\luci-static\resources\view\netshift\settings.js:127
+msgid "You can select Output Network Interface, by default autodetect"
+msgstr ""
diff --git a/luci-app-podkop/root/etc/uci-defaults/50_luci-podkop b/luci-app-netshift/root/etc/uci-defaults/50_luci-netshift
similarity index 66%
rename from luci-app-podkop/root/etc/uci-defaults/50_luci-podkop
rename to luci-app-netshift/root/etc/uci-defaults/50_luci-netshift
index 519c3581..67f6c1e1 100644
--- a/luci-app-podkop/root/etc/uci-defaults/50_luci-podkop
+++ b/luci-app-netshift/root/etc/uci-defaults/50_luci-netshift
@@ -5,6 +5,6 @@ rm -f /tmp/luci-indexcache*
[ -x /etc/init.d/rpcd ] && /etc/init.d/rpcd reload
-logger -t "podkop" "$timestamp uci-defaults script executed"
+logger -t "netshift" "$timestamp uci-defaults script executed"
exit 0
\ No newline at end of file
diff --git a/luci-app-netshift/root/usr/share/luci/menu.d/luci-app-netshift.json b/luci-app-netshift/root/usr/share/luci/menu.d/luci-app-netshift.json
new file mode 100644
index 00000000..455f866e
--- /dev/null
+++ b/luci-app-netshift/root/usr/share/luci/menu.d/luci-app-netshift.json
@@ -0,0 +1,14 @@
+{
+ "admin/services/netshift": {
+ "title": "NetShift",
+ "order": 42,
+ "action": {
+ "type": "view",
+ "path": "netshift/netshift"
+ },
+ "depends": {
+ "acl": [ "luci-app-netshift" ],
+ "uci": { "netshift": true }
+ }
+ }
+}
diff --git a/luci-app-podkop/root/usr/share/rpcd/acl.d/luci-app-podkop.json b/luci-app-netshift/root/usr/share/rpcd/acl.d/luci-app-netshift.json
similarity index 55%
rename from luci-app-podkop/root/usr/share/rpcd/acl.d/luci-app-podkop.json
rename to luci-app-netshift/root/usr/share/rpcd/acl.d/luci-app-netshift.json
index 6d0eabcb..26c097e7 100644
--- a/luci-app-podkop/root/usr/share/rpcd/acl.d/luci-app-podkop.json
+++ b/luci-app-netshift/root/usr/share/rpcd/acl.d/luci-app-netshift.json
@@ -1,12 +1,12 @@
{
- "luci-app-podkop": {
- "description": "Grant UCI and RPC access to LuCI app podkop",
+ "luci-app-netshift": {
+ "description": "Grant UCI and RPC access to LuCI app NetShift",
"read": {
"file": {
- "/etc/init.d/podkop": [
+ "/etc/init.d/netshift": [
"exec"
],
- "/usr/bin/podkop": [
+ "/usr/bin/netshift": [
"exec"
]
},
@@ -16,13 +16,13 @@
]
},
"uci": [
- "podkop"
+ "netshift"
]
},
"write": {
"uci": [
- "podkop"
+ "netshift"
]
}
}
-}
\ No newline at end of file
+}
diff --git a/luci-app-podkop/xgettext.sh b/luci-app-netshift/xgettext.sh
similarity index 82%
rename from luci-app-podkop/xgettext.sh
rename to luci-app-netshift/xgettext.sh
index 0db78fbd..29d9dfb6 100644
--- a/luci-app-podkop/xgettext.sh
+++ b/luci-app-netshift/xgettext.sh
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
-SRC_DIR="htdocs/luci-static/resources/view/podkop"
-OUT_POT="po/templates/podkop.pot"
+SRC_DIR="htdocs/luci-static/resources/view/netshift"
+OUT_POT="po/templates/netshift.pot"
ENCODING="UTF-8"
WIDTH=120
@@ -21,7 +21,7 @@ xgettext --language=JavaScript \
--from-code="$ENCODING" \
--output="$OUT_POT" \
--width="$WIDTH" \
- --package-name="PODKOP" \
+ --package-name="NETSHIFT" \
"${FILES[@]}"
echo "POT template generated: $OUT_POT"
\ No newline at end of file
diff --git a/luci-app-podkop/po/templates/podkop.pot b/luci-app-podkop/po/templates/podkop.pot
deleted file mode 100644
index a722424c..00000000
--- a/luci-app-podkop/po/templates/podkop.pot
+++ /dev/null
@@ -1,1183 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) 2026 THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PODKOP package.
-# yandexru45 , 2026.
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PODKOP\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-06-02 11:25+0300\n"
-"PO-Revision-Date: 2026-06-02 11:25+0300\n"
-"Last-Translator: yandexru45 \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: src\podkop\tabs\dashboard\initController.ts:345
-msgid "✔ Enabled"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:356
-msgid "✔ Running"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:346
-msgid "✘ Disabled"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:357
-msgid "✘ Stopped"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:127
-msgid "Группировать по странам"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:128
-msgid "Группирует прокси подписки по флагу страны в начале тега в отдельные URLTest-группы"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:307
-msgid "Active Connections"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:106
-msgid "Additional marking rules found"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:247
-msgid "Allows access to YACD from the WAN. Make sure to open the appropriate port in your firewall."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:251
-msgid "Applicable for SOCKS and Shadowsocks proxy"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:496
-msgid "At least one valid domain must be specified. Comments-only content is not allowed."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:577
-msgid "At least one valid subnet or IP must be specified. Comments-only content is not allowed."
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:47
-msgid "Available actions"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:65
-msgid "Bootsrap DNS"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:45
-msgid "Bootstrap DNS server"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:58
-msgid "Browser is not using FakeIP"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:57
-msgid "Browser is using FakeIP correctly"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:348
-msgid "Cache File Path"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:362
-msgid "Cache file path cannot be empty"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:27
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:28
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:27
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:25
-msgid "Cannot receive checks result"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:15
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:15
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:13
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:15
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:13
-msgid "Checking, please wait"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getCheckTitle.ts:2
-msgid "checks"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:26
-msgid "Checks failed"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:13
-msgid "Checks passed"
-msgstr ""
-
-#: src\validators\validateSubnet.ts:33
-msgid "CIDR must be between 0 and 32"
-msgstr ""
-
-#: src\partials\modal\renderModal.ts:26
-msgid "Close"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:351
-msgid "Community Lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:335
-msgid "Config File Path"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:27
-msgid "Configuration for Podkop service"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:23
-msgid "Configuration Type"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:12
-msgid "Connection Type"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:26
-msgid "Connection URL"
-msgstr ""
-
-#: src\partials\modal\renderModal.ts:20
-msgid "Copy"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\partials\renderWidget.ts:22
-msgid "Currently unavailable"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:80
-msgid "Dashboard"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\partials\renderSections.ts:19
-msgid "Dashboard currently unavailable"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:222
-msgid "Delay in milliseconds before reloading podkop after interface UP"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:229
-msgid "Delay value cannot be empty"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:82
-msgid "DHCP has DNS server"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:65
-msgid "Diagnostics"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:83
-msgid "Disable autostart"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:265
-msgid "Disable QUIC"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:266
-msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:442
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:522
-msgid "Disabled"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:77
-msgid "DNS on router"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:319
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:15
-msgid "DNS over HTTPS (DoH)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:320
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:16
-msgid "DNS over TLS (DoT)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:316
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:12
-msgid "DNS Protocol Type"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:68
-msgid "DNS Rewrite TTL"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:329
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:24
-msgid "DNS Server"
-msgstr ""
-
-#: src\validators\validateDns.ts:7
-msgid "DNS server address cannot be empty"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:26
-msgid "Do not panic, everything can be fixed, just..."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:306
-msgid "Domain Resolver"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:326
-msgid "Dont Touch My DHCP!"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:241
-#: src\podkop\tabs\dashboard\initController.ts:275
-msgid "Downlink"
-msgstr ""
-
-#: src\partials\modal\renderModal.ts:15
-msgid "Download"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:288
-msgid "Download Lists via Proxy/VPN"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:297
-msgid "Download Lists via specific proxy section"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:289
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:298
-msgid "Downloading all lists via specific Proxy/VPN"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:443
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:523
-msgid "Dynamic List"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:93
-msgid "Enable autostart"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:307
-msgid "Enable built-in DNS resolver for domains handled by this section"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:746
-msgid "Enable DNS resolve to get real IP when routing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:717
-msgid "Enable Mixed Proxy"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:126
-msgid "Enable Output Network Interface"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:718
-msgid "Enable the mixed proxy, allowing this section to route traffic through both HTTP and SOCKS proxies"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:237
-msgid "Enable YACD"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:246
-msgid "Enable YACD WAN Access"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:67
-msgid "Enter complete outbound configuration in JSON format"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:478
-msgid "Enter domain names separated by commas, spaces, or newlines. You can add comments using //"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:452
-msgid "Enter domain names without protocols, e.g. example.com or sub.example.com"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:532
-msgid "Enter subnets in CIDR notation (e.g. 103.21.244.0/22) or single IP addresses"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:90
-msgid "Enter the subscription URL to fetch proxy configurations from your provider"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:187
-msgid "Every 1 minute"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:119
-msgid "Every 12 hours"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:117
-msgid "Every 3 hours"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:188
-msgid "Every 3 minutes"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:115
-msgid "Every 30 minutes"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:186
-msgid "Every 30 seconds"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:189
-msgid "Every 5 minutes"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:118
-msgid "Every 6 hours"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:120
-msgid "Every day"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:116
-msgid "Every hour"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:402
-msgid "Exclude NTP"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:403
-msgid "Exclude NTP protocol traffic from the tunnel to prevent it from being routed through the proxy or VPN"
-msgstr ""
-
-#: src\helpers\copyToClipboard.ts:12
-msgid "Failed to copy!"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:229
-#: src\podkop\tabs\diagnostic\initController.ts:233
-#: src\podkop\tabs\diagnostic\initController.ts:263
-#: src\podkop\tabs\diagnostic\initController.ts:267
-#: src\podkop\tabs\diagnostic\initController.ts:304
-#: src\podkop\tabs\diagnostic\initController.ts:308
-#: src\podkop\tabs\diagnostic\initController.ts:342
-#: src\podkop\tabs\diagnostic\initController.ts:346
-msgid "Failed to execute!"
-msgstr ""
-
-#: src\podkop\methods\custom\getDashboardSections.ts:150
-#: src\podkop\methods\custom\getDashboardSections.ts:181
-#: src\podkop\methods\custom\getDashboardSections.ts:218
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:59
-msgid "Fastest"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:690
-msgid "Fully Routed IPs"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:102
-msgid "Get global check"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:224
-msgid "Global check"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:113
-msgid "How often to automatically update the subscription"
-msgstr ""
-
-#: src\podkop\api.ts:27
-msgid "HTTP error"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:129
-msgid "Install extended"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:129
-msgid "Install stable"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:189
-msgid "Interface Monitoring"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:221
-msgid "Interface Monitoring Delay"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:190
-msgid "Interface monitoring for Bad WAN"
-msgstr ""
-
-#: src\validators\validateDns.ts:23
-msgid "Invalid DNS server format. Examples: 8.8.8.8 or dns.example.com or dns.example.com/nicedns for DoH"
-msgstr ""
-
-#: src\validators\validateDomain.ts:18
-#: src\validators\validateDomain.ts:27
-msgid "Invalid domain address"
-msgstr ""
-
-#: src\validators\validateSubnet.ts:11
-msgid "Invalid format. Use X.X.X.X or X.X.X.X/Y"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:90
-msgid "Invalid HY2 URL: insecure must be 0 or 1"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:77
-msgid "Invalid HY2 URL: invalid port number"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:30
-msgid "Invalid HY2 URL: missing credentials/server"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:47
-msgid "Invalid HY2 URL: missing host"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:41
-msgid "Invalid HY2 URL: missing host & port"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:36
-msgid "Invalid HY2 URL: missing password"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:50
-msgid "Invalid HY2 URL: missing port"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:18
-msgid "Invalid HY2 URL: must not contain spaces"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:12
-msgid "Invalid HY2 URL: must start with hysteria2:// or hy2://"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:108
-msgid "Invalid HY2 URL: obfs-password required when obfs is set"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:122
-msgid "Invalid HY2 URL: parsing failed"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:116
-msgid "Invalid HY2 URL: sni cannot be empty"
-msgstr ""
-
-#: src\validators\validateHysteriaUrl.ts:98
-msgid "Invalid HY2 URL: unsupported obfs type"
-msgstr ""
-
-#: src\validators\validateIp.ts:11
-msgid "Invalid IP address"
-msgstr ""
-
-#: src\validators\validateOutboundJson.ts:9
-msgid "Invalid JSON format"
-msgstr ""
-
-#: src\validators\validatePath.ts:22
-msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:85
-msgid "Invalid port number. Must be between 1 and 65535"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:37
-msgid "Invalid Shadowsocks URL: decoded credentials must contain method:password"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:27
-msgid "Invalid Shadowsocks URL: missing credentials"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:46
-msgid "Invalid Shadowsocks URL: missing method and password separator \":\""
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:76
-msgid "Invalid Shadowsocks URL: missing port"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:67
-msgid "Invalid Shadowsocks URL: missing server"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:58
-msgid "Invalid Shadowsocks URL: missing server address"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:16
-msgid "Invalid Shadowsocks URL: must not contain spaces"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:8
-msgid "Invalid Shadowsocks URL: must start with ss://"
-msgstr ""
-
-#: src\validators\validateShadowsocksUrl.ts:91
-msgid "Invalid Shadowsocks URL: parsing failed"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:73
-msgid "Invalid SOCKS URL: invalid host format"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:63
-msgid "Invalid SOCKS URL: invalid port number"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:42
-msgid "Invalid SOCKS URL: missing host and port"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:51
-msgid "Invalid SOCKS URL: missing hostname or IP"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:56
-msgid "Invalid SOCKS URL: missing port"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:34
-msgid "Invalid SOCKS URL: missing username"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:19
-msgid "Invalid SOCKS URL: must not contain spaces"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:10
-msgid "Invalid SOCKS URL: must start with socks4://, socks4a://, or socks5://"
-msgstr ""
-
-#: src\validators\validateSocksUrl.ts:77
-msgid "Invalid SOCKS URL: parsing failed"
-msgstr ""
-
-#: src\validators\validateTrojanUrl.ts:15
-msgid "Invalid Trojan URL: must not contain spaces"
-msgstr ""
-
-#: src\validators\validateTrojanUrl.ts:8
-msgid "Invalid Trojan URL: must start with trojan://"
-msgstr ""
-
-#: src\validators\validateTrojanUrl.ts:56
-msgid "Invalid Trojan URL: parsing failed"
-msgstr ""
-
-#: src\validators\validateUrl.ts:8
-#: src\validators\validateUrl.ts:31
-msgid "Invalid URL format"
-msgstr ""
-
-#: src\validators\validateVlessUrl.ts:110
-msgid "Invalid VLESS URL: parsing failed"
-msgstr ""
-
-#: src\validators\validateSubnet.ts:18
-msgid "IP address 0.0.0.0 is not allowed"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getMeta.ts:20
-msgid "Issues detected"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:48
-msgid "Latest"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:276
-msgid "List Update Frequency"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:598
-msgid "Local Domain Lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:621
-msgid "Local Subnet Lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:384
-msgid "Log Level"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runDnsCheck.ts:72
-msgid "Main DNS"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:311
-msgid "Memory Usage"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:730
-msgid "Mixed Proxy Port"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:198
-msgid "Monitored Interfaces"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:215
-msgid "Must be a number in the range of 50 - 1000"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:260
-msgid "Network Interface"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:105
-msgid "No other marking rules found"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderCheckSection.ts:189
-msgid "Not implement yet"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:75
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:81
-#: src\podkop\tabs\diagnostic\checks\runSectionsCheck.ts:100
-msgid "Not responding"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:59
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:67
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:75
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:83
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:91
-msgid "Not running"
-msgstr ""
-
-#: src\helpers\withTimeout.ts:7
-msgid "Operation timed out"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:30
-msgid "Outbound Config"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:66
-msgid "Outbound Configuration"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:38
-msgid "Outdated"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:135
-msgid "Output Network Interface"
-msgstr ""
-
-#: src\validators\validatePath.ts:7
-msgid "Path cannot be empty"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:366
-msgid "Path must be absolute (start with /)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:375
-msgid "Path must contain at least one directory (like /tmp/cache.db)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:370
-msgid "Path must end with cache.db"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:107
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:115
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:123
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:131
-#: src\podkop\tabs\diagnostic\diagnostic.store.ts:139
-msgid "Pending"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:343
-msgid "Podkop"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:26
-msgid "Podkop Settings"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:327
-msgid "Podkop will not modify your DHCP configuration"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:37
-msgid "Proxy Configuration URL"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:66
-msgid "Proxy traffic is not routed via FakeIP"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:65
-msgid "Proxy traffic is routed via FakeIP"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:385
-msgid "Regional options cannot be used together"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:644
-msgid "Remote Domain Lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:667
-msgid "Remote Subnet Lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:745
-msgid "Resolve real IP for routing"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:53
-msgid "Restart podkop"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:51
-msgid "Router DNS is not routed through sing-box"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runFakeIPCheck.ts:50
-msgid "Router DNS is routed through sing-box"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:413
-msgid "Routing Excluded IPs"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:79
-msgid "Rules mangle counters"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:74
-msgid "Rules mangle exist"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:89
-msgid "Rules mangle output counters"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:84
-msgid "Rules mangle output exist"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:99
-msgid "Rules proxy counters"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:94
-msgid "Rules proxy exist"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderRunAction.ts:15
-msgid "Run Diagnostic"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:404
-msgid "Russia inside restrictions"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:257
-msgid "Secret key for authenticating remote access to YACD when WAN access is enabled."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:36
-msgid "Sections"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:352
-msgid "Select a predefined list for routing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:13
-msgid "Select between VPN and Proxy connection methods for traffic routing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:13
-msgid "Select DNS protocol to use"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:277
-msgid "Select how often the domain or subnet lists are updated automatically"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:24
-msgid "Select how to configure the proxy"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:261
-msgid "Select network interface for VPN connection"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:330
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:25
-msgid "Select or enter DNS server address"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:349
-msgid "Select or enter path for sing-box cache file. Change this ONLY if you know what you are doing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:336
-msgid "Select path for sing-box config file. Change this ONLY if you know what you are doing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:317
-msgid "Select the DNS protocol type for the domain resolver"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:440
-msgid "Select the list type for adding custom domains"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:520
-msgid "Select the list type for adding custom subnets"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:385
-msgid "Select the log level for sing-box"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:90
-msgid "Select the network interface from which the traffic will originate"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:136
-msgid "Select the network interface to which the traffic will originate"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:199
-msgid "Select the WAN interfaces to be monitored"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:27
-msgid "Selector"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:137
-msgid "Selector Proxy Links"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:340
-msgid "Services info"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\podkop.js:49
-msgid "Settings"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:292
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:120
-msgid "Show sing-box config"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:354
-msgid "Sing-box"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:77
-msgid "Sing-box autostart disabled"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:337
-msgid "Sing-box core changed, version:"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:62
-msgid "Sing-box installed"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:87
-msgid "Sing-box listening ports"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:82
-msgid "Sing-box process running"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:72
-msgid "Sing-box service exist"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runSingBoxCheck.ts:67
-msgid "Sing-box version is compatible (newer than 1.12.4)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:89
-msgid "Source Network Interface"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:414
-msgid "Specify a local IP address to be excluded from routing"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:691
-msgid "Specify local IP addresses or subnets whose traffic will always be routed through the configured route"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:645
-msgid "Specify remote URLs to download and use domain lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:668
-msgid "Specify remote URLs to download and use subnet lists"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:599
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:622
-msgid "Specify the path to the list file located on the router filesystem"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:73
-msgid "Start podkop"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:63
-msgid "Stop podkop"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:29
-msgid "Subscription"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:112
-msgid "Subscription Update Interval"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:89
-msgid "Subscription URL"
-msgstr ""
-
-#: src\helpers\copyToClipboard.ts:10
-msgid "Successfully copied!"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:304
-msgid "System info"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderSystemInfo.ts:21
-msgid "System information"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\checks\runNftCheck.ts:69
-msgid "Table exist"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\partials\renderSections.ts:108
-msgid "Test latency"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:444
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:524
-msgid "Text List"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:46
-msgid "The DNS server used to look up the IP address of an upstream DNS server"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:184
-msgid "The interval between connectivity tests"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:198
-msgid "The maximum difference in response times (ms) allowed when comparing servers"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:222
-msgid "The URL used to test server connectivity"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:69
-msgid "Time in seconds for DNS record caching (default: 60)"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:238
-msgid "Traffic"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:268
-msgid "Traffic Total"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:25
-msgid "Troubleshooting"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:80
-msgid "TTL must be a positive number"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:75
-msgid "TTL value cannot be empty"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:321
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:17
-msgid "UDP (Unprotected DNS)"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:250
-msgid "UDP over TCP"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:39
-#: src\podkop\tabs\diagnostic\initController.ts:40
-#: src\podkop\tabs\diagnostic\initController.ts:41
-#: src\podkop\tabs\diagnostic\initController.ts:42
-#: src\podkop\tabs\diagnostic\initController.ts:43
-#: src\podkop\tabs\diagnostic\initController.ts:44
-#: src\podkop\tabs\diagnostic\helpers\getPodkopVersionRow.ts:7
-msgid "unknown"
-msgstr ""
-
-#: src\podkop\api.ts:40
-msgid "Unknown error"
-msgstr ""
-
-#: src\podkop\tabs\dashboard\initController.ts:240
-#: src\podkop\tabs\dashboard\initController.ts:271
-msgid "Uplink"
-msgstr ""
-
-#: src\validators\validateProxyUrl.ts:37
-msgid "URL must start with vless://, ss://, trojan://, socks4/5://, or hysteria2://hy2://"
-msgstr ""
-
-#: src\validators\validateUrl.ts:17
-msgid "URL must use one of the following protocols:"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:28
-msgid "URLTest"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:183
-msgid "URLTest Check Interval"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:160
-msgid "URLTest Proxy Links"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:221
-msgid "URLTest Testing URL"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:197
-msgid "URLTest Tolerance"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:439
-msgid "User Domain List Type"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:451
-msgid "User Domains"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:477
-msgid "User Domains List"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:519
-msgid "User Subnet List Type"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:531
-msgid "User Subnets"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:557
-msgid "User Subnets List"
-msgstr ""
-
-#: src\validators\validateDns.ts:14
-#: src\validators\validateDns.ts:18
-#: src\validators\validateDomain.ts:13
-#: src\validators\validateDomain.ts:30
-#: src\validators\validateHysteriaUrl.ts:120
-#: src\validators\validateIp.ts:8
-#: src\validators\validateOutboundJson.ts:7
-#: src\validators\validatePath.ts:16
-#: src\validators\validateShadowsocksUrl.ts:95
-#: src\validators\validateSocksUrl.ts:80
-#: src\validators\validateSubnet.ts:38
-#: src\validators\validateTrojanUrl.ts:59
-#: src\validators\validateUrl.ts:28
-#: src\validators\validateVlessUrl.ts:108
-msgid "Valid"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:510
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:589
-msgid "Validation errors:"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\initController.ts:258
-#: src\podkop\tabs\diagnostic\partials\renderAvailableActions.ts:111
-msgid "View logs"
-msgstr ""
-
-#: src\podkop\tabs\diagnostic\partials\renderWikiDisclaimer.ts:31
-msgid "Visit Wiki"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:38
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:138
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:161
-msgid "vless://, ss://, trojan://, socks4/5://, hy2/hysteria2:// links"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:387
-msgid "Warning: %s cannot be used together with %s. Previous selections have been removed."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\section.js:406
-msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:256
-msgid "YACD Secret Key"
-msgstr ""
-
-#: ..\luci-app-podkop\htdocs\luci-static\resources\view\podkop\settings.js:127
-msgid "You can select Output Network Interface, by default autodetect"
-msgstr ""
diff --git a/luci-app-podkop/root/usr/share/luci/menu.d/luci-app-podkop.json b/luci-app-podkop/root/usr/share/luci/menu.d/luci-app-podkop.json
deleted file mode 100644
index e7e6ffaf..00000000
--- a/luci-app-podkop/root/usr/share/luci/menu.d/luci-app-podkop.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "admin/services/podkop": {
- "title": "Podkop",
- "order": 42,
- "action": {
- "type": "view",
- "path": "podkop/podkop"
- },
- "depends": {
- "acl": [ "luci-app-podkop" ],
- "uci": { "podkop": true }
- }
- }
-}
\ No newline at end of file
diff --git a/netshift/Makefile b/netshift/Makefile
new file mode 100644
index 00000000..593c9135
--- /dev/null
+++ b/netshift/Makefile
@@ -0,0 +1,64 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=netshift
+
+PKG_VERSION := $(if $(NETSHIFT_VERSION),$(NETSHIFT_VERSION),0.$(shell date +%d%m%Y))
+
+PKG_RELEASE:=1
+
+PKG_MAINTAINER:=ITDog
+PKG_LICENSE:=GPL-2.0-or-later
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/netshift
+ SECTION:=net
+ CATEGORY:=Network
+ DEPENDS:=+sing-box +curl +jq +kmod-nft-tproxy +coreutils-base64 +bind-dig
+ CONFLICTS:=https-dns-proxy nextdns luci-app-passwall luci-app-passwall2
+ TITLE:=Domain routing app
+ URL:=https://podkop.net
+ PKGARCH:=all
+endef
+
+define Package/netshift/description
+ Domain routing. Use of VLESS, Shadowsocks technologies
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/netshift/prerm
+#!/bin/sh
+
+grep -q "105 netshift" /etc/iproute2/rt_tables && sed -i "/105 netshift/d" /etc/iproute2/rt_tables
+
+/etc/init.d/netshift stop
+
+exit 0
+endef
+
+define Package/netshift/conffiles
+/etc/config/netshift
+endef
+
+define Package/netshift/install
+ $(INSTALL_DIR) $(1)/etc/init.d
+ $(INSTALL_BIN) ./files/etc/init.d/netshift $(1)/etc/init.d/netshift
+
+ $(INSTALL_DIR) $(1)/etc/config
+ $(INSTALL_CONF) ./files/etc/config/netshift $(1)/etc/config/netshift
+
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(INSTALL_BIN) ./files/usr/bin/netshift $(1)/usr/bin/netshift
+
+ $(INSTALL_DIR) $(1)/usr/lib/netshift
+ $(CP) ./files/usr/lib/* $(1)/usr/lib/netshift/
+
+ sed -i -e 's/__COMPILED_VERSION_VARIABLE__/$(PKG_VERSION)/g' $(1)/usr/lib/netshift/constants.sh
+endef
+
+$(eval $(call BuildPackage,netshift))
diff --git a/podkop/files/etc/config/podkop b/netshift/files/etc/config/netshift
similarity index 100%
rename from podkop/files/etc/config/podkop
rename to netshift/files/etc/config/netshift
diff --git a/podkop/files/etc/init.d/podkop b/netshift/files/etc/init.d/netshift
similarity index 88%
rename from podkop/files/etc/init.d/podkop
rename to netshift/files/etc/init.d/netshift
index 73825efe..e4e055ac 100755
--- a/podkop/files/etc/init.d/podkop
+++ b/netshift/files/etc/init.d/netshift
@@ -9,13 +9,13 @@ NAME="$(basename ${script:-$initscript})"
config_load "$NAME"
start_service() {
- echo "Start podkop"
+ echo "Start netshift"
config_get enable_badwan_interface_monitoring "settings" "enable_badwan_interface_monitoring"
config_get badwan_monitored_interfaces "settings" "badwan_monitored_interfaces"
procd_open_instance
- procd_set_param command /usr/bin/podkop start
+ procd_set_param command /usr/bin/netshift start
[ "$enable_badwan_interface_monitoring" = "1" ] && [ -n "$badwan_monitored_interfaces" ] &&
procd_set_param netdev "$badwan_monitored_interfaces"
procd_set_param stdout 1
@@ -24,11 +24,11 @@ start_service() {
}
stop_service() {
- /usr/bin/podkop stop
+ /usr/bin/netshift stop
}
reload_service() {
- /usr/bin/podkop reload > /dev/null 2>&1
+ /usr/bin/netshift reload > /dev/null 2>&1
}
service_triggers() {
@@ -45,7 +45,7 @@ service_triggers() {
if [ "$enable_badwan_interface_monitoring" = "1" ]; then
for iface in $badwan_monitored_interfaces; do
- procd_add_interface_trigger "interface.*.up" "$iface" /etc/init.d/podkop reload
+ procd_add_interface_trigger "interface.*.up" "$iface" /etc/init.d/netshift reload
done
fi
procd_close_trigger
diff --git a/podkop/files/usr/bin/podkop b/netshift/files/usr/bin/netshift
similarity index 95%
rename from podkop/files/usr/bin/podkop
rename to netshift/files/usr/bin/netshift
index 736a516f..363249fd 100755
--- a/podkop/files/usr/bin/podkop
+++ b/netshift/files/usr/bin/netshift
@@ -9,31 +9,31 @@ check_required_file() {
fi
}
-PODKOP_LIB="/usr/lib/podkop"
+NETSHIFT_LIB="/usr/lib/netshift"
check_required_file /lib/functions.sh
check_required_file /lib/config/uci.sh
check_required_file /lib/functions/network.sh
-check_required_file "$PODKOP_LIB/constants.sh"
-check_required_file "$PODKOP_LIB/nft.sh"
-check_required_file "$PODKOP_LIB/helpers.sh"
-check_required_file "$PODKOP_LIB/sing_box_config_manager.sh"
-check_required_file "$PODKOP_LIB/sing_box_config_facade.sh"
-check_required_file "$PODKOP_LIB/logging.sh"
-check_required_file "$PODKOP_LIB/rulesets.sh"
-check_required_file "$PODKOP_LIB/updater.sh"
+check_required_file "$NETSHIFT_LIB/constants.sh"
+check_required_file "$NETSHIFT_LIB/nft.sh"
+check_required_file "$NETSHIFT_LIB/helpers.sh"
+check_required_file "$NETSHIFT_LIB/sing_box_config_manager.sh"
+check_required_file "$NETSHIFT_LIB/sing_box_config_facade.sh"
+check_required_file "$NETSHIFT_LIB/logging.sh"
+check_required_file "$NETSHIFT_LIB/rulesets.sh"
+check_required_file "$NETSHIFT_LIB/updater.sh"
. /lib/functions.sh
. /lib/config/uci.sh
. /lib/functions/network.sh
-. "$PODKOP_LIB/constants.sh"
-. "$PODKOP_LIB/nft.sh"
-. "$PODKOP_LIB/helpers.sh"
-. "$PODKOP_LIB/sing_box_config_manager.sh"
-. "$PODKOP_LIB/sing_box_config_facade.sh"
-. "$PODKOP_LIB/logging.sh"
-. "$PODKOP_LIB/rulesets.sh"
-. "$PODKOP_LIB/updater.sh"
+. "$NETSHIFT_LIB/constants.sh"
+. "$NETSHIFT_LIB/nft.sh"
+. "$NETSHIFT_LIB/helpers.sh"
+. "$NETSHIFT_LIB/sing_box_config_manager.sh"
+. "$NETSHIFT_LIB/sing_box_config_facade.sh"
+. "$NETSHIFT_LIB/logging.sh"
+. "$NETSHIFT_LIB/rulesets.sh"
+. "$NETSHIFT_LIB/updater.sh"
-config_load "$PODKOP_CONFIG"
+config_load "$NETSHIFT_CONFIG"
check_requirements() {
log "Check Requirements"
@@ -171,9 +171,9 @@ ensure_subscription_cache_dir() {
local state_dir_created=0 cache_dir_created=0
local mkdir_errfile mkdir_rc
- [ -d "$PODKOP_STATE_DIR" ] || state_dir_created=1
+ [ -d "$NETSHIFT_STATE_DIR" ] || state_dir_created=1
[ -d "$SUBSCRIPTION_CACHE_FOLDER" ] || cache_dir_created=1
- mkdir_errfile="/tmp/podkop-subscription-cache-mkdir.$$"
+ mkdir_errfile="/tmp/netshift-subscription-cache-mkdir.$$"
if mkdir -p "$SUBSCRIPTION_CACHE_FOLDER" 2>"$mkdir_errfile"; then
rm -f "$mkdir_errfile"
else
@@ -191,7 +191,7 @@ ensure_subscription_cache_dir() {
fi
if [ "$state_dir_created" -eq 1 ] || [ "$cache_dir_created" -eq 1 ]; then
- chmod 700 "$PODKOP_STATE_DIR" 2>/dev/null
+ chmod 700 "$NETSHIFT_STATE_DIR" 2>/dev/null
chmod 700 "$SUBSCRIPTION_CACHE_FOLDER" 2>/dev/null
fi
}
@@ -498,7 +498,7 @@ prepare_subscription_caches_for_startup() {
}
stop_subscription_startup_retry_worker() {
- local pidfile="/var/run/podkop_subscription_retry.pid"
+ local pidfile="/var/run/netshift_subscription_retry.pid"
if [ -f "$pidfile" ]; then
pid="$(cat "$pidfile" 2> /dev/null)"
@@ -511,7 +511,7 @@ stop_subscription_startup_retry_worker() {
}
start_subscription_startup_retry_worker() {
- local pidfile="/var/run/podkop_subscription_retry.pid"
+ local pidfile="/var/run/netshift_subscription_retry.pid"
if [ -f "$pidfile" ]; then
pid="$(cat "$pidfile" 2> /dev/null)"
@@ -530,13 +530,13 @@ start_subscription_startup_retry_worker() {
sleep 10
while true; do
- config_load "$PODKOP_CONFIG"
+ config_load "$NETSHIFT_CONFIG"
# Run in a child process: a successful subscription_update performs
# its own restart, which stops this worker via the pidfile safely.
# Keep diagnostics visible in syslog; subscription URLs are redacted
# in the lower-level download helpers.
- if /usr/bin/podkop subscription_update; then
+ if /usr/bin/netshift subscription_update; then
log "Deferred subscription refresh succeeded; updated configuration is being applied" "info"
rm -f "$pidfile"
exit 0
@@ -552,7 +552,7 @@ start_subscription_startup_retry_worker() {
}
start_main() {
- log "Starting podkop"
+ log "Starting netshift"
check_requirements
@@ -575,7 +575,7 @@ start_main() {
prepare_subscription_caches_for_startup
if [ "$subscription_startup_blocked" -ne 0 ]; then
- log "Podkop startup continues with temporary blocked subscription outbounds until sources become reachable" "warn"
+ log "NetShift startup continues with temporary blocked subscription outbounds until sources become reachable" "warn"
fi
stop_subscription_startup_retry_worker
@@ -596,21 +596,21 @@ start_main() {
log "Nice"
list_update &
- echo $! > /var/run/podkop_list_update.pid
+ echo $! > /var/run/netshift_list_update.pid
}
stop_main() {
- log "Stopping the podkop"
+ log "Stopping the netshift"
stop_subscription_startup_retry_worker
- if [ -f /var/run/podkop_list_update.pid ]; then
- pid=$(cat /var/run/podkop_list_update.pid)
+ if [ -f /var/run/netshift_list_update.pid ]; then
+ pid=$(cat /var/run/netshift_list_update.pid)
if kill -0 "$pid" 2> /dev/null; then
kill "$pid" 2> /dev/null
log "Stopped list_update"
fi
- rm -f /var/run/podkop_list_update.pid
+ rm -f /var/run/netshift_list_update.pid
fi
remove_cron_job
@@ -623,7 +623,7 @@ stop_main() {
fi
log "Flush ip rule"
- if ip rule list | grep -q "podkop"; then
+ if ip rule list | grep -q "netshift"; then
ip rule del fwmark "$NFT_FAKEIP_MARK"/"$NFT_FAKEIP_MARK" table "$RT_TABLE_NAME" priority 105
fi
@@ -653,8 +653,8 @@ start() {
dnsmasq_configure
fi
- uci_set "podkop" "settings" "shutdown_correctly" 0
- uci commit "podkop" && config_load "$PODKOP_CONFIG"
+ uci_set "netshift" "settings" "shutdown_correctly" 0
+ uci commit "netshift" && config_load "$NETSHIFT_CONFIG"
}
stop() {
@@ -666,18 +666,18 @@ stop() {
stop_main
- uci_set "podkop" "settings" "shutdown_correctly" 1
- uci commit "podkop" && config_load "$PODKOP_CONFIG"
+ uci_set "netshift" "settings" "shutdown_correctly" 1
+ uci commit "netshift" && config_load "$NETSHIFT_CONFIG"
}
reload() {
- log "Podkop reload"
+ log "NetShift reload"
stop
start
}
restart() {
- log "Podkop restart"
+ log "NetShift restart"
stop
start
}
@@ -820,7 +820,7 @@ dnsmasq_configure() {
local shutdown_correctly
config_get shutdown_correctly "settings" "shutdown_correctly"
if [ "$shutdown_correctly" -eq 0 ]; then
- log "Previous shutdown of podkop was not correct, reconfiguration of dnsmasq is not required"
+ log "Previous shutdown of netshift was not correct, reconfiguration of dnsmasq is not required"
return 0
fi
@@ -829,14 +829,14 @@ dnsmasq_configure() {
if [ -n "$current_servers" ]; then
for server in $(uci_get "dhcp" "@dnsmasq[0]" "server"); do
if ! [ "$server" == "$SB_DNS_INBOUND_ADDRESS" ]; then
- uci_add_list "dhcp" "@dnsmasq[0]" "podkop_server" "$server"
+ uci_add_list "dhcp" "@dnsmasq[0]" "netshift_server" "$server"
fi
done
uci_remove "dhcp" "@dnsmasq[0]" "server"
fi
- backup_dnsmasq_config_option "noresolv" "podkop_noresolv"
- backup_dnsmasq_config_option "cachesize" "podkop_cachesize"
+ backup_dnsmasq_config_option "noresolv" "netshift_noresolv"
+ backup_dnsmasq_config_option "cachesize" "netshift_cachesize"
log "Configure dnsmasq for sing-box"
uci_add_list "dhcp" "@dnsmasq[0]" "server" "$SB_DNS_INBOUND_ADDRESS"
@@ -852,39 +852,39 @@ dnsmasq_restore() {
local shutdown_correctly
config_get shutdown_correctly "settings" "shutdown_correctly"
if [ "$shutdown_correctly" -eq 1 ]; then
- log "Previous shutdown of podkop was correct, reconfiguration of dnsmasq is not required"
+ log "Previous shutdown of netshift was correct, reconfiguration of dnsmasq is not required"
return 0
fi
local cachesize noresolv backup_servers resolvfile
log "Restoring cachesize" "debug"
- cachesize="$(uci_get "dhcp" "@dnsmasq[0]" "podkop_cachesize")"
+ cachesize="$(uci_get "dhcp" "@dnsmasq[0]" "netshift_cachesize")"
if [ -z "$cachesize" ]; then
uci_remove "dhcp" "@dnsmasq[0]" "cachesize"
uci_set "dhcp" "@dnsmasq[0]" "cachesize" 150
else
uci_set "dhcp" "@dnsmasq[0]" "cachesize" "$cachesize"
- uci_remove "dhcp" "@dnsmasq[0]" "podkop_cachesize"
+ uci_remove "dhcp" "@dnsmasq[0]" "netshift_cachesize"
fi
log "Restoring noresolv" "debug"
- noresolv="$(uci_get "dhcp" "@dnsmasq[0]" "podkop_noresolv")"
+ noresolv="$(uci_get "dhcp" "@dnsmasq[0]" "netshift_noresolv")"
if [ -z "$noresolv" ]; then
uci_set "dhcp" "@dnsmasq[0]" "noresolv" 0
else
uci_set "dhcp" "@dnsmasq[0]" "noresolv" "$noresolv"
- uci_remove "dhcp" "@dnsmasq[0]" "podkop_noresolv"
+ uci_remove "dhcp" "@dnsmasq[0]" "netshift_noresolv"
fi
log "Restoring DNS servers" "debug"
uci_remove "dhcp" "@dnsmasq[0]" "server"
resolvfile="/tmp/resolv.conf.d/resolv.conf.auto"
- backup_servers="$(uci_get "dhcp" "@dnsmasq[0]" "podkop_server")"
+ backup_servers="$(uci_get "dhcp" "@dnsmasq[0]" "netshift_server")"
if [ -n "$backup_servers" ]; then
for server in $backup_servers; do
uci_add_list "dhcp" "@dnsmasq[0]" "server" "$server"
done
- uci_remove "dhcp" "@dnsmasq[0]" "podkop_server"
+ uci_remove "dhcp" "@dnsmasq[0]" "netshift_server"
elif file_exists "$resolvfile"; then
log "Backup DNS servers not found, using default resolvfile" "debug"
uci_set "dhcp" "@dnsmasq[0]" "resolvfile" "$resolvfile"
@@ -911,19 +911,19 @@ add_cron_job() {
case "$update_interval" in
"1h")
- cron_job="13 * * * * /usr/bin/podkop list_update"
+ cron_job="13 * * * * /usr/bin/netshift list_update"
;;
"3h")
- cron_job="13 */3 * * * /usr/bin/podkop list_update"
+ cron_job="13 */3 * * * /usr/bin/netshift list_update"
;;
"12h")
- cron_job="13 */12 * * * /usr/bin/podkop list_update"
+ cron_job="13 */12 * * * /usr/bin/netshift list_update"
;;
"1d")
- cron_job="13 9 * * * /usr/bin/podkop list_update"
+ cron_job="13 9 * * * /usr/bin/netshift list_update"
;;
"3d")
- cron_job="13 9 */3 * * /usr/bin/podkop list_update"
+ cron_job="13 9 */3 * * /usr/bin/netshift list_update"
;;
*)
log "Invalid update_interval value: $update_interval"
@@ -944,7 +944,7 @@ add_cron_job() {
}
remove_cron_job() {
- (crontab -l | grep -v "/usr/bin/podkop list_update" | grep -v "/usr/bin/podkop subscription_update") | crontab -
+ (crontab -l | grep -v "/usr/bin/netshift list_update" | grep -v "/usr/bin/netshift subscription_update") | crontab -
log "The cron job removed"
}
@@ -966,22 +966,22 @@ add_subscription_cron_job() {
case "$subscription_update_interval" in
"30m")
- cron_job="*/30 * * * * /usr/bin/podkop subscription_update"
+ cron_job="*/30 * * * * /usr/bin/netshift subscription_update"
;;
"1h")
- cron_job="17 * * * * /usr/bin/podkop subscription_update"
+ cron_job="17 * * * * /usr/bin/netshift subscription_update"
;;
"3h")
- cron_job="17 */3 * * * /usr/bin/podkop subscription_update"
+ cron_job="17 */3 * * * /usr/bin/netshift subscription_update"
;;
"6h")
- cron_job="17 */6 * * * /usr/bin/podkop subscription_update"
+ cron_job="17 */6 * * * /usr/bin/netshift subscription_update"
;;
"12h")
- cron_job="17 */12 * * * /usr/bin/podkop subscription_update"
+ cron_job="17 */12 * * * /usr/bin/netshift subscription_update"
;;
"1d")
- cron_job="17 9 * * * /usr/bin/podkop subscription_update"
+ cron_job="17 9 * * * /usr/bin/netshift subscription_update"
;;
*)
log "Invalid subscription_update_interval value: $subscription_update_interval"
@@ -990,7 +990,7 @@ add_subscription_cron_job() {
esac
# Avoid duplicate subscription cron
- (crontab -l | grep -v "/usr/bin/podkop subscription_update") | {
+ (crontab -l | grep -v "/usr/bin/netshift subscription_update") | {
cat
echo "$cron_job"
} | crontab -
@@ -1225,11 +1225,11 @@ subscription_update() {
return 0
fi
- echolog "рџ”„ Restarting podkop to apply updated subscriptions..."
+ echolog "рџ”„ Restarting netshift to apply updated subscriptions..."
restart
restart_rc=$?
if [ "$restart_rc" -ne 0 ]; then
- echolog "вќЊ Subscription was downloaded, but podkop restart failed"
+ echolog "вќЊ Subscription was downloaded, but netshift restart failed"
return "$restart_rc"
fi
@@ -2520,13 +2520,13 @@ get_first_outbound_section() {
get_sections_by_connection_type() {
local connection_type="$1"
- uci show podkop | grep "\.connection_type='$connection_type'" | cut -d'.' -f2
+ uci show netshift | grep "\.connection_type='$connection_type'" | cut -d'.' -f2
}
section_by_connection_type_exists() {
local connection_type="$1"
- if uci show podkop | grep -q "\.connection_type='$connection_type'"; then
+ if uci show netshift | grep -q "\.connection_type='$connection_type'"; then
return 0
else
return 1
@@ -2708,7 +2708,7 @@ check_nft() {
if [ "$found_hetzner" -eq 1 ] || [ "$found_ovh" -eq 1 ]; then
- local sets="podkop_subnets podkop_domains interfaces podkop_discord_subnets localv4"
+ local sets="netshift_subnets netshift_domains interfaces netshift_discord_subnets localv4"
nolog "Sets statistics:"
for set_name in $sets; do
@@ -2747,21 +2747,21 @@ check_logs() {
fi
local logs
- logs=$(logread | grep -E "podkop|sing-box")
+ logs=$(logread | grep -E "netshift|sing-box")
if [ -z "$logs" ]; then
nolog "Logs not found"
return 1
fi
- # Find the last occurrence of "Starting podkop"
+ # Find the last occurrence of "Starting netshift"
local start_line
- start_line=$(echo "$logs" | grep -n "podkop.*Starting podkop" | tail -n 1 | cut -d: -f1)
+ start_line=$(echo "$logs" | grep -n "netshift.*Starting netshift" | tail -n 1 | cut -d: -f1)
if [ -n "$start_line" ]; then
echo "$logs" | tail -n +"$start_line"
else
- nolog "No 'Starting podkop' message found, showing last 100 lines"
+ nolog "No 'Starting netshift' message found, showing last 100 lines"
echo "$logs" | tail -n 100
fi
}
@@ -2803,7 +2803,7 @@ show_sing_box_config() {
}
show_config() {
- if [ ! -f "$PODKOP_CONFIG" ]; then
+ if [ ! -f "$NETSHIFT_CONFIG" ]; then
nolog "Configuration file not found"
return 1
fi
@@ -2817,14 +2817,14 @@ show_config() {
-e "s@\\(option dns_server '[^/]*\\)/[^']*'@\\1/MASKED'@g" \
-e "s@\\(option domain_resolver_dns_server '[^/]*\\)/[^']*'@\\1/MASKED'@g" \
-e 's/\(option yacd_secret_key\).*/\1 '\''MASKED'\''/g' \
- "$PODKOP_CONFIG" > "$tmp_config"
+ "$NETSHIFT_CONFIG" > "$tmp_config"
cat "$tmp_config"
rm -f "$tmp_config"
}
show_version() {
- echo "$PODKOP_VERSION"
+ echo "$NETSHIFT_VERSION"
}
show_sing_box_version() {
@@ -2842,15 +2842,15 @@ show_system_info() {
}
get_system_info() {
- local podkop_version podkop_latest_version luci_app_version sing_box_version openwrt_version device_model sing_box_extended
+ local netshift_version netshift_latest_version luci_app_version sing_box_version openwrt_version device_model sing_box_extended
- podkop_version="$PODKOP_VERSION"
+ netshift_version="$NETSHIFT_VERSION"
- podkop_latest_version=$(curl -m 3 -s https://api.github.com/repos/yandexru45/podkop-evolution/releases/latest | grep '"tag_name":' | cut -d'"' -f4)
- [ -z "$podkop_latest_version" ] && podkop_latest_version="unknown"
+ netshift_latest_version=$(curl -m 3 -s https://api.github.com/repos/yandexru45/podkop-evolution/releases/latest | grep '"tag_name":' | cut -d'"' -f4)
+ [ -z "$netshift_latest_version" ] && netshift_latest_version="unknown"
- if [ -f /www/luci-static/resources/view/podkop/main.js ]; then
- luci_app_version=$(grep 'var PODKOP_LUCI_APP_VERSION' /www/luci-static/resources/view/podkop/main.js | cut -d'"' -f2)
+ if [ -f /www/luci-static/resources/view/netshift/main.js ]; then
+ luci_app_version=$(grep 'var NETSHIFT_LUCI_APP_VERSION' /www/luci-static/resources/view/netshift/main.js | cut -d'"' -f2)
else
luci_app_version="not installed"
fi
@@ -2881,7 +2881,7 @@ get_system_info() {
device_model="unknown"
fi
- echo "{\"podkop_version\": \"$podkop_version\", \"podkop_latest_version\": \"$podkop_latest_version\", \"luci_app_version\": \"$luci_app_version\", \"sing_box_version\": \"$sing_box_version\", \"sing_box_extended\": $sing_box_extended, \"openwrt_version\": \"$openwrt_version\", \"device_model\": \"$device_model\"}" | jq .
+ echo "{\"netshift_version\": \"$netshift_version\", \"netshift_latest_version\": \"$netshift_latest_version\", \"luci_app_version\": \"$luci_app_version\", \"sing_box_version\": \"$sing_box_version\", \"sing_box_extended\": $sing_box_extended, \"openwrt_version\": \"$openwrt_version\", \"device_model\": \"$device_model\"}" | jq .
}
get_sing_box_status() {
@@ -2932,7 +2932,7 @@ get_status() {
local status=""
# Check if service is enabled
- if [ -x /etc/rc.d/S99podkop ]; then
+ if [ -x /etc/rc.d/S99netshift ]; then
enabled=1
status="enabled"
else
@@ -3002,13 +3002,13 @@ check_dns_available() {
# Check if /etc/config/dhcp has server 127.0.0.42
config_load dhcp
- config_foreach check_dhcp_has_podkop_dns dnsmasq
- config_load "$PODKOP_CONFIG"
+ config_foreach check_dhcp_has_netshift_dns dnsmasq
+ config_load "$NETSHIFT_CONFIG"
echo "{\"dns_type\":\"$dns_type\",\"dns_server\":\"$display_dns_server\",\"dns_status\":$dns_status,\"dns_on_router\":$dns_on_router,\"bootstrap_dns_server\":\"$bootstrap_dns_server\",\"bootstrap_dns_status\":$bootstrap_dns_status,\"dhcp_config_status\":$dhcp_config_status}" | jq .
}
-check_dhcp_has_podkop_dns() {
+check_dhcp_has_netshift_dns() {
local server_list cachesize noresolv server_found
config_get server_list "$1" "server"
config_get cachesize "$1" "cachesize"
@@ -3040,7 +3040,7 @@ check_nft_rules() {
local rules_proxy_counters=0
local rules_other_mark_exist=0
- # Generate traffic through PodkopTable
+ # Generate traffic through NetShiftTable
curl -m 3 -s "https://$CHECK_PROXY_IP_DOMAIN/check" > /dev/null 2>&1 &
local pid1=$!
curl -m 3 -s "https://$FAKEIP_TEST_DOMAIN/check" > /dev/null 2>&1 &
@@ -3050,7 +3050,7 @@ check_nft_rules() {
wait $pid2 2> /dev/null
sleep 1
- # Check if PodkopTable exists
+ # Check if NetShiftTable exists
if nft list table inet "$NFT_TABLE_NAME" > /dev/null 2>&1; then
table_exist=1
@@ -3094,21 +3094,21 @@ check_nft_rules() {
fi
fi
- # Check for other mark rules outside PodkopTable
+ # Check for other mark rules outside NetShiftTable
nft list tables 2> /dev/null | while read -r _ family table_name; do
[ -z "$table_name" ] && continue
[ "$table_name" = "$NFT_TABLE_NAME" ] && continue
if nft list table "$family" "$table_name" 2> /dev/null | grep -q "meta mark set"; then
- touch /tmp/podkop_mark_check.$$
+ touch /tmp/netshift_mark_check.$$
break
fi
done
- if [ -f /tmp/podkop_mark_check.$$ ]; then
+ if [ -f /tmp/netshift_mark_check.$$ ]; then
rules_other_mark_exist=1
- rm -f /tmp/podkop_mark_check.$$
+ rm -f /tmp/netshift_mark_check.$$
fi
echo "{\"table_exist\":$table_exist,\"rules_mangle_exist\":$rules_mangle_exist,\"rules_mangle_counters\":$rules_mangle_counters,\"rules_mangle_output_exist\":$rules_mangle_output_exist,\"rules_mangle_output_counters\":$rules_mangle_output_counters,\"rules_proxy_exist\":$rules_proxy_exist,\"rules_proxy_counters\":$rules_proxy_counters,\"rules_other_mark_exist\":$rules_other_mark_exist}" | jq .
@@ -3329,8 +3329,8 @@ print_global() {
}
global_check() {
- local PODKOP_LUCI_VERSION="Unknown"
- [ -n "$1" ] && PODKOP_LUCI_VERSION="$1"
+ local NETSHIFT_LUCI_VERSION="Unknown"
+ [ -n "$1" ] && NETSHIFT_LUCI_VERSION="$1"
print_global "рџ“Ў Global check run!"
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
@@ -3340,16 +3340,16 @@ global_check() {
system_info_json=$(get_system_info)
if [ -n "$system_info_json" ]; then
- local podkop_version podkop_latest_version luci_app_version sing_box_version openwrt_version device_model sing_box_extended
+ local netshift_version netshift_latest_version luci_app_version sing_box_version openwrt_version device_model sing_box_extended
- podkop_version=$(echo "$system_info_json" | jq -r '.podkop_version // "unknown"')
- podkop_latest_version=$(echo "$system_info_json" | jq -r '.podkop_latest_version // "unknown"')
+ netshift_version=$(echo "$system_info_json" | jq -r '.netshift_version // "unknown"')
+ netshift_latest_version=$(echo "$system_info_json" | jq -r '.netshift_latest_version // "unknown"')
luci_app_version=$(echo "$system_info_json" | jq -r '.luci_app_version // "unknown"')
sing_box_version=$(echo "$system_info_json" | jq -r '.sing_box_version // "unknown"')
openwrt_version=$(echo "$system_info_json" | jq -r '.openwrt_version // "unknown"')
device_model=$(echo "$system_info_json" | jq -r '.device_model // "unknown"')
- print_global "рџ•іпёЏ Podkop: $podkop_version (latest: $podkop_latest_version)"
+ print_global "рџ•іпёЏ NetShift: $netshift_version (latest: $netshift_latest_version)"
print_global "рџ•іпёЏ LuCI App: $luci_app_version"
print_global "📦 Sing-box: $sing_box_version"
print_global "рџ›њ OpenWrt: $openwrt_version"
@@ -3541,7 +3541,7 @@ global_check() {
fi
print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
- print_global "рџ“„ Podkop config"
+ print_global "рџ“„ NetShift config"
show_config
# print_global "в”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓв”Ѓ"
@@ -3651,27 +3651,27 @@ show_help() {
Usage: $0 COMMAND
Available commands:
- start Start podkop service
- stop Stop podkop service
- reload Reload podkop configuration
- restart Restart podkop service
- main Run main podkop process
+ start Start NetShift service
+ stop Stop NetShift service
+ reload Reload NetShift configuration
+ restart Restart NetShift service
+ main Run main NetShift process
list_update Update domain lists
subscription_update Update subscription proxies
check_proxy Check proxy connectivity
check_nft Check NFT rules
check_nft_rules Check NFT rules status
check_sing_box Check sing-box installation and status
- check_logs Show podkop logs from system journal
+ check_logs Show NetShift logs from system journal
check_sing_box_logs Show sing-box logs
check_fakeip Test FakeIP on router
clash_api Clash API interface for managing proxies and groups
- show_config Display current podkop configuration
- show_version Show podkop version
+ show_config Display current NetShift configuration
+ show_version Show NetShift version
show_sing_box_config Show sing-box configuration
show_sing_box_version Show sing-box version
show_system_info Show system information
- get_status Get podkop service status
+ get_status Get NetShift service status
get_sing_box_status Get sing-box service status
get_system_info Get system information in JSON format
check_dns_available Check DNS server availability
@@ -3762,8 +3762,8 @@ component_action)
component_action "$2" "$3"
;;
component_action_async)
- "$0" component_action "$2" "$3" > "/tmp/podkop-component-$$.json" 2>&1 &
- echo "{\"success\":true,\"job\":\"/tmp/podkop-component-$$.json\"}"
+ "$0" component_action "$2" "$3" > "/tmp/netshift-component-$$.json" 2>&1 &
+ echo "{\"success\":true,\"job\":\"/tmp/netshift-component-$$.json\"}"
;;
*)
show_help
diff --git a/podkop/files/usr/lib/constants.sh b/netshift/files/usr/lib/constants.sh
similarity index 89%
rename from podkop/files/usr/lib/constants.sh
rename to netshift/files/usr/lib/constants.sh
index db40974e..fdc193f9 100644
--- a/podkop/files/usr/lib/constants.sh
+++ b/netshift/files/usr/lib/constants.sh
@@ -1,9 +1,9 @@
# shellcheck disable=SC2034
-PODKOP_VERSION="__COMPILED_VERSION_VARIABLE__"
+NETSHIFT_VERSION="__COMPILED_VERSION_VARIABLE__"
## Common
-PODKOP_CONFIG="/etc/config/podkop"
-PODKOP_STATE_DIR="/etc/podkop"
+NETSHIFT_CONFIG="/etc/config/netshift"
+NETSHIFT_STATE_DIR="/etc/netshift"
RESOLV_CONF="/etc/resolv.conf"
DNS_RESOLVERS="1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 9.9.9.9 9.9.9.11 94.140.14.14 94.140.15.15 208.67.220.220 208.67.222.222 77.88.8.1 77.88.8.8"
CHECK_PROXY_IP_DOMAIN="ip.podkop.fyi"
@@ -11,18 +11,18 @@ FAKEIP_TEST_DOMAIN="fakeip.podkop.fyi"
TMP_SING_BOX_FOLDER="/tmp/sing-box"
TMP_RULESET_FOLDER="$TMP_SING_BOX_FOLDER/rulesets"
TMP_SUBSCRIPTION_FOLDER="$TMP_SING_BOX_FOLDER/subscriptions"
-SUBSCRIPTION_CACHE_FOLDER="$PODKOP_STATE_DIR/subscriptions"
+SUBSCRIPTION_CACHE_FOLDER="$NETSHIFT_STATE_DIR/subscriptions"
TMP_SUBSCRIPTION_DOWNLOAD_FOLDER="$TMP_SING_BOX_FOLDER/subscription-downloads"
CLOUDFLARE_OCTETS="8.47 162.159 188.114" # Endpoints https://github.com/ampetelin/warp-endpoint-checker
JQ_REQUIRED_VERSION="1.7.1"
COREUTILS_BASE64_REQUIRED_VERSION="9.7"
-RT_TABLE_NAME="podkop"
+RT_TABLE_NAME="netshift"
## nft
-NFT_TABLE_NAME="PodkopTable"
+NFT_TABLE_NAME="NetShiftTable"
NFT_LOCALV4_SET_NAME="localv4"
-NFT_COMMON_SET_NAME="podkop_subnets"
-NFT_DISCORD_SET_NAME="podkop_discord_subnets"
+NFT_COMMON_SET_NAME="netshift_subnets"
+NFT_DISCORD_SET_NAME="netshift_discord_subnets"
NFT_INTERFACE_SET_NAME="interfaces"
NFT_FAKEIP_MARK="0x00100000"
NFT_OUTBOUND_MARK="0x00200000"
diff --git a/podkop/files/usr/lib/helpers.jq b/netshift/files/usr/lib/helpers.jq
similarity index 100%
rename from podkop/files/usr/lib/helpers.jq
rename to netshift/files/usr/lib/helpers.jq
diff --git a/podkop/files/usr/lib/helpers.sh b/netshift/files/usr/lib/helpers.sh
similarity index 99%
rename from podkop/files/usr/lib/helpers.sh
rename to netshift/files/usr/lib/helpers.sh
index 7d1e8660..31d13dd7 100644
--- a/podkop/files/usr/lib/helpers.sh
+++ b/netshift/files/usr/lib/helpers.sh
@@ -449,7 +449,7 @@ log_wget_failure() {
log "$operation failed [$attempt/$retries]: wget rc=$rc, mode=$mode, family=$family, timeout=${timeout}s, host=${host:-unknown}, url=$(redact_url_for_log "$url"), error_class=$err_class, error=\"$err\"" "warn"
if echo "$err" | grep -qi 'Operation not permitted'; then
- log "$operation got 'Operation not permitted'. On OpenWrt this can indicate firewall, routing, or IPv6 preference issues; podkop will retry with IPv4 when supported." "warn"
+ log "$operation got 'Operation not permitted'. On OpenWrt this can indicate firewall, routing, or IPv6 preference issues; netshift will retry with IPv4 when supported." "warn"
fi
}
@@ -814,7 +814,7 @@ check_subscription_connectivity() {
hwid="$(generate_hwid)"
local attempt errfile rc family
- errfile="/tmp/podkop-subscription-check.$$"
+ errfile="/tmp/netshift-subscription-check.$$"
rm -f "$errfile"
for attempt in $(seq 1 "$retries"); do
family="any"
diff --git a/podkop/files/usr/lib/logging.sh b/netshift/files/usr/lib/logging.sh
similarity index 92%
rename from podkop/files/usr/lib/logging.sh
rename to netshift/files/usr/lib/logging.sh
index 3a529ad5..247a02f3 100644
--- a/podkop/files/usr/lib/logging.sh
+++ b/netshift/files/usr/lib/logging.sh
@@ -10,7 +10,7 @@ log() {
level="info"
fi
- logger -t "podkop" "[$level] $message"
+ logger -t "netshift" "[$level] $message"
}
nolog() {
diff --git a/podkop/files/usr/lib/nft.sh b/netshift/files/usr/lib/nft.sh
similarity index 100%
rename from podkop/files/usr/lib/nft.sh
rename to netshift/files/usr/lib/nft.sh
diff --git a/podkop/files/usr/lib/rulesets.sh b/netshift/files/usr/lib/rulesets.sh
similarity index 100%
rename from podkop/files/usr/lib/rulesets.sh
rename to netshift/files/usr/lib/rulesets.sh
diff --git a/podkop/files/usr/lib/sing_box_config_facade.sh b/netshift/files/usr/lib/sing_box_config_facade.sh
similarity index 99%
rename from podkop/files/usr/lib/sing_box_config_facade.sh
rename to netshift/files/usr/lib/sing_box_config_facade.sh
index a0884a7c..5d58aab5 100644
--- a/podkop/files/usr/lib/sing_box_config_facade.sh
+++ b/netshift/files/usr/lib/sing_box_config_facade.sh
@@ -1,6 +1,6 @@
-PODKOP_LIB="/usr/lib/podkop"
-. "$PODKOP_LIB/helpers.sh"
-. "$PODKOP_LIB/sing_box_config_manager.sh"
+NETSHIFT_LIB="/usr/lib/netshift"
+. "$NETSHIFT_LIB/helpers.sh"
+. "$NETSHIFT_LIB/sing_box_config_manager.sh"
sing_box_cf_add_dns_server() {
local config="$1"
diff --git a/podkop/files/usr/lib/sing_box_config_manager.sh b/netshift/files/usr/lib/sing_box_config_manager.sh
similarity index 99%
rename from podkop/files/usr/lib/sing_box_config_manager.sh
rename to netshift/files/usr/lib/sing_box_config_manager.sh
index 635b4312..eefb7e1f 100644
--- a/podkop/files/usr/lib/sing_box_config_manager.sh
+++ b/netshift/files/usr/lib/sing_box_config_manager.sh
@@ -11,7 +11,7 @@
#
# Usage:
# Include this script in your ash script with:
-# . /usr/lib/podkop/sing_box_config_manager.sh
+# . /usr/lib/netshift/sing_box_config_manager.sh
#
# After that, sing_box_cm_* functions are available for generating
# and modifying sing-box JSON configuration.
@@ -287,7 +287,7 @@ sing_box_cm_patch_dns_route_rule() {
--arg tag "$tag" \
--arg key "$key" \
--argjson value "$value" \
- 'import "helpers" as h {"search": "/usr/lib/podkop"};
+ 'import "helpers" as h {"search": "/usr/lib/netshift"};
.dns.rules |= map(
if .[$service_tag] == $tag then
if has($key) then
@@ -1222,7 +1222,7 @@ sing_box_cm_patch_route_rule() {
--arg tag "$tag" \
--arg key "$key" \
--argjson value "$value" \
- 'import "helpers" as h {"search": "/usr/lib/podkop"};
+ 'import "helpers" as h {"search": "/usr/lib/netshift"};
.route.rules |= map(
if .[$service_tag] == $tag then
if has($key) then
@@ -1385,11 +1385,11 @@ sing_box_cm_add_inline_ruleset_rule() {
value=$(_normalize_arg "$value")
- echo "$config" | jq -L /usr/lib/podkop \
+ echo "$config" | jq -L /usr/lib/netshift \
--arg tag "$tag" \
--arg key "$key" \
--argjson value "$value" \
- 'import "helpers" as h {"search": "/usr/lib/podkop"};
+ 'import "helpers" as h {"search": "/usr/lib/netshift"};
.route.rule_set |= map(
if .tag == $tag then
if has($key) then
diff --git a/podkop/files/usr/lib/updater.sh b/netshift/files/usr/lib/updater.sh
similarity index 96%
rename from podkop/files/usr/lib/updater.sh
rename to netshift/files/usr/lib/updater.sh
index fd8f6fbf..65717d04 100644
--- a/podkop/files/usr/lib/updater.sh
+++ b/netshift/files/usr/lib/updater.sh
@@ -2,7 +2,7 @@
# Runtime updater for sing-box-extended and stock sing-box.
# JSON parsing is done with jq (no ucode, no extra package deps).
-# This file is sourced from /usr/bin/podkop, so log() is available.
+# This file is sourced from /usr/bin/netshift, so log() is available.
SB_EXT_ARCH_SUFFIX=""
UPDATES_SING_BOX_EXTENDED_REPO="shtorm-7/sing-box-extended"
@@ -145,11 +145,11 @@ updates_download_to_file() {
return 1
}
-# Restarts podkop if its init script is present (best-effort).
-updates_restart_podkop() {
- if [ -x /etc/init.d/podkop ]; then
- updates_log "Restarting podkop after component change"
- /etc/init.d/podkop restart >/dev/null 2>&1 || true
+# Restarts netshift if its init script is present (best-effort).
+updates_restart_netshift() {
+ if [ -x /etc/init.d/netshift ]; then
+ updates_log "Restarting netshift after component change"
+ /etc/init.d/netshift restart >/dev/null 2>&1 || true
fi
}
@@ -188,7 +188,7 @@ updates_install_sing_box_extended() {
return 1
fi
- tmp_dir="$(mktemp -d /tmp/podkop-sbext.XXXXXX 2>/dev/null)"
+ tmp_dir="$(mktemp -d /tmp/netshift-sbext.XXXXXX 2>/dev/null)"
if [ -z "$tmp_dir" ]; then
updates_log "Failed to create temporary directory" "error"
echo "{\"success\":false,\"message\":\"Failed to create temporary directory\"}"
@@ -279,7 +279,7 @@ updates_install_sing_box_extended() {
rm -f "$backup_binary" "$backup_cronet"
rm -rf "$tmp_dir"
- updates_restart_podkop
+ updates_restart_netshift
updates_log "Installed sing-box-extended $new_version"
echo "{\"success\":true,\"version\":\"$new_version\"}"
return 0
@@ -310,7 +310,7 @@ updates_install_sing_box_stable() {
return 1
fi
- updates_restart_podkop
+ updates_restart_netshift
new_version="$(get_sing_box_version)"
updates_log "Stable sing-box installed: ${new_version:-unknown}"
echo "{\"success\":true,\"version\":\"$new_version\"}"
diff --git a/podkop/Makefile b/podkop/Makefile
deleted file mode 100644
index 5cef0c1e..00000000
--- a/podkop/Makefile
+++ /dev/null
@@ -1,64 +0,0 @@
-include $(TOPDIR)/rules.mk
-
-PKG_NAME:=podkop
-
-PKG_VERSION := $(if $(PODKOP_VERSION),$(PODKOP_VERSION),0.$(shell date +%d%m%Y))
-
-PKG_RELEASE:=1
-
-PKG_MAINTAINER:=ITDog
-PKG_LICENSE:=GPL-2.0-or-later
-
-include $(INCLUDE_DIR)/package.mk
-
-define Package/podkop
- SECTION:=net
- CATEGORY:=Network
- DEPENDS:=+sing-box +curl +jq +kmod-nft-tproxy +coreutils-base64 +bind-dig
- CONFLICTS:=https-dns-proxy nextdns luci-app-passwall luci-app-passwall2
- TITLE:=Domain routing app
- URL:=https://podkop.net
- PKGARCH:=all
-endef
-
-define Package/podkop/description
- Domain routing. Use of VLESS, Shadowsocks technologies
-endef
-
-define Build/Configure
-endef
-
-define Build/Compile
-endef
-
-define Package/podkop/prerm
-#!/bin/sh
-
-grep -q "105 podkop" /etc/iproute2/rt_tables && sed -i "/105 podkop/d" /etc/iproute2/rt_tables
-
-/etc/init.d/podkop stop
-
-exit 0
-endef
-
-define Package/podkop/conffiles
-/etc/config/podkop
-endef
-
-define Package/podkop/install
- $(INSTALL_DIR) $(1)/etc/init.d
- $(INSTALL_BIN) ./files/etc/init.d/podkop $(1)/etc/init.d/podkop
-
- $(INSTALL_DIR) $(1)/etc/config
- $(INSTALL_CONF) ./files/etc/config/podkop $(1)/etc/config/podkop
-
- $(INSTALL_DIR) $(1)/usr/bin
- $(INSTALL_BIN) ./files/usr/bin/podkop $(1)/usr/bin/podkop
-
- $(INSTALL_DIR) $(1)/usr/lib/podkop
- $(CP) ./files/usr/lib/* $(1)/usr/lib/podkop/
-
- sed -i -e 's/__COMPILED_VERSION_VARIABLE__/$(PKG_VERSION)/g' $(1)/usr/lib/podkop/constants.sh
-endef
-
-$(eval $(call BuildPackage,podkop))
From 69c9a69acdbefc3032fbce0524f131c0182c42fb Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Tue, 2 Jun 2026 17:37:17 +0300
Subject: [PATCH 21/75] docs: redesign README (centered header, badges,
structured sections)
Rework README in the style of well-presented project READMEs:
- centered header with NETSHIFT ASCII banner + badges (release, license,
OpenWrt, docs, DeepWiki)
- one-line tagline + upstream attribution
- checkbox Features list with sub-descriptions
- collapsible for pre-install requirements, migration (0.8.0 /
0.7.0), UCI example
- Subscription headers as a table
- Acknowledgments + License sections
Content preserved: install one-liner, system requirements, subscription /
HWID docs, extended/xhttp, migration notes, roadmap, upstream links.
---
README.md | 231 ++++++++++++++++++++++++++++++++++--------------------
1 file changed, 146 insertions(+), 85 deletions(-)
diff --git a/README.md b/README.md
index db3976a9..4cb674a3 100644
--- a/README.md
+++ b/README.md
@@ -1,70 +1,117 @@
+
+
+```
+ ╔╗╔╔═╗╔╦╗╔═╗╦ ╦╦╔═╗╔╦╗
+ ║║║║╣ ║ ╚═╗╠═╣║╠╣ ║
+ ╝╚╝╚═╝ ╩ ╚═╝╩ ╩╩╚ ╩
+ shift your traffic
+```
+
# NetShift
-> **Форк с поддержкой Subscription URL + HWID и переключаемым ядром sing-box-extended (xhttp)**
->
-> NetShift добавляет поддержку ссылок подписки (subscription URL) с кастомными заголовками (HWID, Device-OS, Device-Model) и автоматическим обновлением, а также переключение ядра на sing-box-extended с поддержкой клиентского транспорта xhttp. Основан на [itdoginfo/podkop](https://github.com/itdoginfo/podkop).
+**Маршрутизация трафика для OpenWrt — нужное в туннель, остальное напрямую.**
-Маршрутизация трафика для OpenWrt.
+Открытое ПО на базе [sing-box](https://github.com/SagerNet/sing-box) · форк [itdoginfo/podkop](https://github.com/itdoginfo/podkop) с поддержкой Subscription URL, HWID и переключаемого ядра sing-box-extended.
-Направляйте нужные ресурсы в туннель, а остальное — напрямую. Открытое программное обеспечение на базе [sing-box](https://github.com/SagerNet/sing-box).
+[](https://github.com/yandexru45/podkop-evolution/releases)
+[](LICENSE)
+[](https://openwrt.org/)
+[](https://podkop.net/)
+[](https://deepwiki.com/itdoginfo/podkop)
+
+
> [!WARNING]
> Проект находится в стадии бета-версии. Возможны ошибки, нестабильная работа и существенные изменения функциональности.
---
-# Вещи, которые вам нужно знать перед установкой
+## Возможности
-### Обновления и конфигурация
-- При обновлении **обязательно** [очищайте кэш LuCI](https://podkop.net/docs/clear-browser-cache/).
-- После обновления проверяйте конфигурацию — она может изменяться между версиями.
-- При старте NetShift модифицируется конфигурация Dnsmasq.
-- NetShift изменяет конфигурацию sing-box. Если вы используете собственную конфигурацию, заранее сохраните её.
+- [x] **Маршрутизация по доменам и подсетям** — направляйте нужные ресурсы в туннель, остальное идёт напрямую VLESS · Shadowsocks · Trojan · Hysteria2 · готовые community-списки
+- [x] **Subscription URL** — ссылки подписки от провайдера с автообновлением и автовыбором лучшего сервера кастомные заголовки HWID / Device-OS / Device-Model · URLTest · ручное переключение
+- [x] **Переключаемое ядро sing-box** — стабильное ↔ sing-box-extended прямо из веб-интерфейса клиентский транспорт xhttp · установка/откат в один клик
+- [x] **Веб-интерфейс LuCI** — дашборд, диагностика и настройки без правки конфигов статус серверов · проверка соединения · логи
+- [x] **Автоматическая миграция** — обновление со старого podkop переносит конфиг без перенастройки
-### Системные требования
-- Требуется OpenWrt 24.10 или выше.
-- Необходимо минимум 25 МБ свободного места на устройстве. Устройства с флеш-памятью 16 МБ не поддерживаются.
+## Скриншоты
-### Важные ограничения и особенности
-- Если установлен Getdomains, его [необходимо удалить](https://github.com/itdoginfo/domain-routing-openwrt?tab=readme-ov-file#скрипт-для-удаления)
-- Dashboard доступен только при подключении по HTTP (из-за особенностей Clash API). При использовании HTTPS или домена работа может быть недоступна.
-
-### Поддержка и диагностика
-- [Руководство по диагностике](https://podkop.net/docs/diagnostics/)
-- Актуальные изменения публикуются в [Telegram-чате](https://t.me/itdogchat/81758/420321). Пожалуйста, ознакомьтесь с закрепленными сообщениями.
-- При возникновении проблем оставляйте технически грамотный фидбэк в GitHub Issues и Telegram-чате.
+> Интерфейс доступен в LuCI: **Services → NetShift**.
+> _(скриншоты будут добавлены)_
+## Установка
-# Документация
-https://podkop.net/
+Полная инструкция — в [документации](https://podkop.net/docs/install/).
-# Установка NetShift
-Полная информация в [документации](https://podkop.net/docs/install/)
+Для установки и обновления достаточно одного скрипта:
-Для установки и обновления достаточно выполнить один скрипт:
-```
+```sh
sh <(wget -O - https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/install.sh)
```
-## Новое в NetShift: Подписки (Subscription)
+> [!IMPORTANT]
+> Перед установкой ознакомьтесь с разделом [**Перед установкой**](#перед-установкой) ниже — там системные требования и важные ограничения.
+
+## Перед установкой
+
+
+Системные требования
+
+- OpenWrt **24.10** или выше.
+- Минимум **25 МБ** свободного места. Устройства с флеш-памятью 16 МБ не поддерживаются.
+
+
+
+
+Обновления и конфигурация
+
+- При обновлении **обязательно** [очищайте кэш LuCI](https://podkop.net/docs/clear-browser-cache/).
+- После обновления проверяйте конфигурацию — она может меняться между версиями.
+- При старте NetShift модифицирует конфигурацию Dnsmasq.
+- NetShift изменяет конфигурацию sing-box. Если используете собственную — заранее сохраните её.
+
+
+
+
+Ограничения и особенности
+
+- Если установлен **Getdomains**, его [необходимо удалить](https://github.com/itdoginfo/domain-routing-openwrt?tab=readme-ov-file#скрипт-для-удаления).
+- **Dashboard** работает только по HTTP (особенность Clash API). По HTTPS или через домен может быть недоступен.
+
+
+
+
+Поддержка и диагностика
+
+- [Руководство по диагностике](https://podkop.net/docs/diagnostics/)
+- Актуальные изменения — в [Telegram-чате](https://t.me/itdogchat/81758/420321) (читайте закреплённые сообщения).
+- При проблемах оставляйте технически грамотный фидбэк в GitHub Issues и Telegram-чате.
+
+
-Добавлена поддержка subscription URL — ссылки подписки от провайдера прокси. При выборе типа конфигурации **Subscription** в LuCI:
+## Subscription URL
-- Введите URL подписки от вашего провайдера
-- Выберите интервал автообновления (от 30 минут до 1 дня)
-- Все серверы из подписки автоматически появятся в дашборде
-- Автоматический выбор лучшего сервера по задержке (URLTest)
-- Ручное переключение между серверами через дашборд
+Поддержка ссылок подписки от провайдера прокси. При выборе типа конфигурации **Subscription** в LuCI:
+
+- Введите URL подписки от вашего провайдера.
+- Выберите интервал автообновления (от 30 минут до 1 дня).
+- Все серверы из подписки автоматически появятся в дашборде.
+- Автовыбор лучшего сервера по задержке (URLTest) и ручное переключение.
При скачивании подписки отправляются заголовки:
-- `User-Agent: singbox/<версия>`
-- `X-HWID` — уникальный идентификатор роутера
-- `X-Device-OS: OpenWrt Linux`
-- `X-Device-Model` — модель роутера
-- `X-Ver-OS` — версия ядра
-Пример конфигурации через UCI:
-```
+| Заголовок | Значение |
+|---|---|
+| `User-Agent` | `singbox/<версия>` |
+| `X-HWID` | уникальный идентификатор роутера |
+| `X-Device-OS` | `OpenWrt Linux` |
+| `X-Device-Model` | модель роутера |
+| `X-Ver-OS` | версия ядра |
+
+
+Пример настройки через UCI
+
+```sh
uci set netshift.my_sub=section
uci set netshift.my_sub.connection_type='proxy'
uci set netshift.my_sub.proxy_config_type='subscription'
@@ -75,69 +122,83 @@ uci commit netshift
```
Ручное обновление подписки:
-```
+
+```sh
/usr/bin/netshift subscription_update
```
-## Новое в NetShift: ядро sing-box-extended (xhttp)
+
-NetShift позволяет переключать ядро между стабильным sing-box и сборкой
-sing-box-extended прямо из вкладки **Diagnostics** в LuCI:
+## Ядро sing-box-extended (xhttp)
+
+Переключение ядра между стабильным sing-box и сборкой **sing-box-extended** прямо из вкладки **Diagnostics** в LuCI:
- **Install extended** — установить расширенное ядро sing-box-extended.
-- **Install stable** — вернуться на стабильное ядро sing-box.
+- **Install stable** — вернуться на стабильное ядро.
-После установки расширенного ядра становится доступен клиентский транспорт
-**xhttp**. Поддерживается только клиентский режим xhttp (не серверный).
+После установки расширенного ядра становится доступен клиентский транспорт **xhttp**. Поддерживается только клиентский режим (не серверный). По умолчанию ставится стабильное ядро — extended включается по желанию.
-## Изменения 0.8.0 — переименование в NetShift
-Начиная с версии 0.8.0 проект переименован из `podkop` в **NetShift**. Пакет
-теперь называется `netshift` (бинарь `/usr/bin/netshift`), а конфигурация
-переехала на `/etc/config/netshift`. LuCI-приложение — `luci-app-netshift`.
+## Миграция
-При обновлении старый конфиг `/etc/config/podkop` автоматически мигрируется в
-`/etc/config/netshift`, а резервная копия сохраняется в
-`/etc/config/podkop.bak.pre-netshift`.
+
+0.8.0 — переименование в NetShift
-## Изменения 0.7.0
-Начиная с версии 0.7.0 изменена структура конфига `/etc/config/netshift`
-(на тот момент — `/etc/config/podkop`). Старые значения несовместимы с новыми.
-Нужно заново настроить NetShift.
+С версии 0.8.0 проект переименован из `podkop` в **NetShift**:
-Скрипт установки обнаружит старую версию и предупредит вас об этом. Если вы согласитесь, то он сделает автоматически написанное ниже.
+- пакет — `netshift` (бинарь `/usr/bin/netshift`);
+- конфигурация — `/etc/config/netshift`;
+- LuCI-приложение — `luci-app-netshift`.
-При обновлении вручную нужно:
+При обновлении старый конфиг `/etc/config/podkop` **автоматически мигрируется** в `/etc/config/netshift`, резервная копия сохраняется в `/etc/config/podkop.bak.pre-netshift`. VPN продолжит работать без перенастройки.
-0. Не ныть в issue и чатик.
-1. Забэкапить старый конфиг:
-```
+
+
+
+0.7.0 — несовместимый формат конфига
+
+С версии 0.7.0 изменена структура конфига (на тот момент — `/etc/config/podkop`). Старые значения несовместимы — нужно настроить заново. Скрипт установки обнаружит старую версию и предложит сделать это автоматически.
+
+Вручную:
+
+```sh
+# 1. Забэкапить старый конфиг
mv /etc/config/netshift /etc/config/netshift-070
-```
-2. Стянуть новый дефолтный конфиг:
-```
+# 2. Стянуть новый дефолтный конфиг
wget -O /etc/config/netshift https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/netshift/files/etc/config/netshift
+# 3. Настроить заново через LuCI или UCI
```
-3. Настроить заново ваш NetShift через Luci или UCI.
-# ToDo
+
+
+## Документация
-> [!IMPORTANT]
-> Pull Request принимаются только после согласования с авторами в Telegram-чате. На данный момент PR без предварительного обсуждения не рассматриваются.
+Полная документация: ****
-## Будущее
-- [x] [Подписка](https://github.com/itdoginfo/podkop/issues/118) — **реализовано в NetShift!**
+## Дорожная карта
+
+> [!IMPORTANT]
+> Pull Request принимаются только после согласования с авторами в [Telegram-чате](https://t.me/itdogchat/81758/420321). PR без предварительного обсуждения не рассматриваются.
+
+- [x] [Подписка (Subscription URL)](https://github.com/itdoginfo/podkop/issues/118) — **реализовано**
+- [x] Переключаемое ядро sing-box-extended + xhttp — **реализовано**
- [ ] Весь трафик в sing-box и маршрутизация полностью на его уровне.
-- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. [Issue](https://github.com/itdoginfo/podkop/issues/111)
-- [ ] Галочка, которая режет доступ к doh серверам.
-- [ ] IPv6. Только после наполнения Wiki.
+- [ ] Фоновый режим со слежением за состоянием sing-box и авто-restore dnsmasq при падении. [Issue](https://github.com/itdoginfo/podkop/issues/111)
+- [ ] Опция, ограничивающая доступ к DoH-серверам.
+- [ ] IPv6 (после наполнения Wiki).
-## Тесты
-- [ ] Unit тесты (BATS)
-- [ ] Интеграционные тесты бекенда (OpenWrt rootfs + BATS)
+**Тесты:**
-> [!WARNING]
-> Данное программное обеспечение предоставляется «как есть», без каких-либо явных или подразумеваемых гарантий, включая гарантии коммерческой пригодности и соответствия определённой цели.
->
-> Правообладатели и участники проекта не несут ответственности за любые прямые, косвенные, случайные, специальные или иные убытки, возникшие в результате использования программного обеспечения, включая потерю данных, прибыли или прерывание деятельности, даже если они были предупреждены о возможности таких последствий.
+- [ ] Unit-тесты (BATS)
+- [ ] Интеграционные тесты бэкенда (OpenWrt rootfs + BATS)
-[](https://deepwiki.com/itdoginfo/podkop)
+## Благодарности
+
+- [itdoginfo/podkop](https://github.com/itdoginfo/podkop) — исходный проект, форком которого является NetShift.
+- [sing-box](https://github.com/SagerNet/sing-box) — движок маршрутизации.
+
+## Лицензия
+
+GPL-2.0-or-later — см. [LICENSE](LICENSE).
+
+> [!WARNING]
+> Программное обеспечение предоставляется «как есть», без каких-либо явных или подразумеваемых гарантий, включая гарантии коммерческой пригодности и соответствия определённой цели. Правообладатели и участники проекта не несут ответственности за любые убытки, возникшие в результате использования ПО.
From 3b4554e61d6511a78be035b125c5a614150f417f Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Tue, 2 Jun 2026 17:44:02 +0300
Subject: [PATCH 22/75] Revert "docs: redesign README (centered header, badges,
structured sections)"
This reverts commit 69c9a69acdbefc3032fbce0524f131c0182c42fb.
---
README.md | 231 ++++++++++++++++++++----------------------------------
1 file changed, 85 insertions(+), 146 deletions(-)
diff --git a/README.md b/README.md
index 4cb674a3..db3976a9 100644
--- a/README.md
+++ b/README.md
@@ -1,117 +1,70 @@
-
-
-```
- ╔╗╔╔═╗╔╦╗╔═╗╦ ╦╦╔═╗╔╦╗
- ║║║║╣ ║ ╚═╗╠═╣║╠╣ ║
- ╝╚╝╚═╝ ╩ ╚═╝╩ ╩╩╚ ╩
- shift your traffic
-```
-
# NetShift
-**Маршрутизация трафика для OpenWrt — нужное в туннель, остальное напрямую.**
+> **Форк с поддержкой Subscription URL + HWID и переключаемым ядром sing-box-extended (xhttp)**
+>
+> NetShift добавляет поддержку ссылок подписки (subscription URL) с кастомными заголовками (HWID, Device-OS, Device-Model) и автоматическим обновлением, а также переключение ядра на sing-box-extended с поддержкой клиентского транспорта xhttp. Основан на [itdoginfo/podkop](https://github.com/itdoginfo/podkop).
-Открытое ПО на базе [sing-box](https://github.com/SagerNet/sing-box) · форк [itdoginfo/podkop](https://github.com/itdoginfo/podkop) с поддержкой Subscription URL, HWID и переключаемого ядра sing-box-extended.
+Маршрутизация трафика для OpenWrt.
-[](https://github.com/yandexru45/podkop-evolution/releases)
-[](LICENSE)
-[](https://openwrt.org/)
-[](https://podkop.net/)
-[](https://deepwiki.com/itdoginfo/podkop)
-
-
+Направляйте нужные ресурсы в туннель, а остальное — напрямую. Открытое программное обеспечение на базе [sing-box](https://github.com/SagerNet/sing-box).
> [!WARNING]
> Проект находится в стадии бета-версии. Возможны ошибки, нестабильная работа и существенные изменения функциональности.
---
-## Возможности
-
-- [x] **Маршрутизация по доменам и подсетям** — направляйте нужные ресурсы в туннель, остальное идёт напрямую VLESS · Shadowsocks · Trojan · Hysteria2 · готовые community-списки
-- [x] **Subscription URL** — ссылки подписки от провайдера с автообновлением и автовыбором лучшего сервера кастомные заголовки HWID / Device-OS / Device-Model · URLTest · ручное переключение
-- [x] **Переключаемое ядро sing-box** — стабильное ↔ sing-box-extended прямо из веб-интерфейса клиентский транспорт xhttp · установка/откат в один клик
-- [x] **Веб-интерфейс LuCI** — дашборд, диагностика и настройки без правки конфигов статус серверов · проверка соединения · логи
-- [x] **Автоматическая миграция** — обновление со старого podkop переносит конфиг без перенастройки
-
-## Скриншоты
-
-> Интерфейс доступен в LuCI: **Services → NetShift**.
-> _(скриншоты будут добавлены)_
-
-## Установка
-
-Полная инструкция — в [документации](https://podkop.net/docs/install/).
-
-Для установки и обновления достаточно одного скрипта:
-
-```sh
-sh <(wget -O - https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/install.sh)
-```
-
-> [!IMPORTANT]
-> Перед установкой ознакомьтесь с разделом [**Перед установкой**](#перед-установкой) ниже — там системные требования и важные ограничения.
-
-## Перед установкой
-
-
-Системные требования
-
-- OpenWrt **24.10** или выше.
-- Минимум **25 МБ** свободного места. Устройства с флеш-памятью 16 МБ не поддерживаются.
-
-
-
-
-Обновления и конфигурация
+# Вещи, которые вам нужно знать перед установкой
+### Обновления и конфигурация
- При обновлении **обязательно** [очищайте кэш LuCI](https://podkop.net/docs/clear-browser-cache/).
-- После обновления проверяйте конфигурацию — она может меняться между версиями.
-- При старте NetShift модифицирует конфигурацию Dnsmasq.
-- NetShift изменяет конфигурацию sing-box. Если используете собственную — заранее сохраните её.
+- После обновления проверяйте конфигурацию — она может изменяться между версиями.
+- При старте NetShift модифицируется конфигурация Dnsmasq.
+- NetShift изменяет конфигурацию sing-box. Если вы используете собственную конфигурацию, заранее сохраните её.
-
+### Системные требования
+- Требуется OpenWrt 24.10 или выше.
+- Необходимо минимум 25 МБ свободного места на устройстве. Устройства с флеш-памятью 16 МБ не поддерживаются.
-
-Ограничения и особенности
+### Важные ограничения и особенности
+- Если установлен Getdomains, его [необходимо удалить](https://github.com/itdoginfo/domain-routing-openwrt?tab=readme-ov-file#скрипт-для-удаления)
+- Dashboard доступен только при подключении по HTTP (из-за особенностей Clash API). При использовании HTTPS или домена работа может быть недоступна.
-- Если установлен **Getdomains**, его [необходимо удалить](https://github.com/itdoginfo/domain-routing-openwrt?tab=readme-ov-file#скрипт-для-удаления).
-- **Dashboard** работает только по HTTP (особенность Clash API). По HTTPS или через домен может быть недоступен.
+### Поддержка и диагностика
+- [Руководство по диагностике](https://podkop.net/docs/diagnostics/)
+- Актуальные изменения публикуются в [Telegram-чате](https://t.me/itdogchat/81758/420321). Пожалуйста, ознакомьтесь с закрепленными сообщениями.
+- При возникновении проблем оставляйте технически грамотный фидбэк в GitHub Issues и Telegram-чате.
-
-
-Поддержка и диагностика
+# Документация
+https://podkop.net/
-- [Руководство по диагностике](https://podkop.net/docs/diagnostics/)
-- Актуальные изменения — в [Telegram-чате](https://t.me/itdogchat/81758/420321) (читайте закреплённые сообщения).
-- При проблемах оставляйте технически грамотный фидбэк в GitHub Issues и Telegram-чате.
+# Установка NetShift
+Полная информация в [документации](https://podkop.net/docs/install/)
-
+Для установки и обновления достаточно выполнить один скрипт:
+```
+sh <(wget -O - https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/install.sh)
+```
-## Subscription URL
+## Новое в NetShift: Подписки (Subscription)
-Поддержка ссылок подписки от провайдера прокси. При выборе типа конфигурации **Subscription** в LuCI:
+Добавлена поддержка subscription URL — ссылки подписки от провайдера прокси. При выборе типа конфигурации **Subscription** в LuCI:
-- Введите URL подписки от вашего провайдера.
-- Выберите интервал автообновления (от 30 минут до 1 дня).
-- Все серверы из подписки автоматически появятся в дашборде.
-- Автовыбор лучшего сервера по задержке (URLTest) и ручное переключение.
+- Введите URL подписки от вашего провайдера
+- Выберите интервал автообновления (от 30 минут до 1 дня)
+- Все серверы из подписки автоматически появятся в дашборде
+- Автоматический выбор лучшего сервера по задержке (URLTest)
+- Ручное переключение между серверами через дашборд
При скачивании подписки отправляются заголовки:
+- `User-Agent: singbox/<версия>`
+- `X-HWID` — уникальный идентификатор роутера
+- `X-Device-OS: OpenWrt Linux`
+- `X-Device-Model` — модель роутера
+- `X-Ver-OS` — версия ядра
-| Заголовок | Значение |
-|---|---|
-| `User-Agent` | `singbox/<версия>` |
-| `X-HWID` | уникальный идентификатор роутера |
-| `X-Device-OS` | `OpenWrt Linux` |
-| `X-Device-Model` | модель роутера |
-| `X-Ver-OS` | версия ядра |
-
-
-Пример настройки через UCI
-
-```sh
+Пример конфигурации через UCI:
+```
uci set netshift.my_sub=section
uci set netshift.my_sub.connection_type='proxy'
uci set netshift.my_sub.proxy_config_type='subscription'
@@ -122,83 +75,69 @@ uci commit netshift
```
Ручное обновление подписки:
-
-```sh
+```
/usr/bin/netshift subscription_update
```
-
+## Новое в NetShift: ядро sing-box-extended (xhttp)
-## Ядро sing-box-extended (xhttp)
-
-Переключение ядра между стабильным sing-box и сборкой **sing-box-extended** прямо из вкладки **Diagnostics** в LuCI:
+NetShift позволяет переключать ядро между стабильным sing-box и сборкой
+sing-box-extended прямо из вкладки **Diagnostics** в LuCI:
- **Install extended** — установить расширенное ядро sing-box-extended.
-- **Install stable** — вернуться на стабильное ядро.
-
-После установки расширенного ядра становится доступен клиентский транспорт **xhttp**. Поддерживается только клиентский режим (не серверный). По умолчанию ставится стабильное ядро — extended включается по желанию.
-
-## Миграция
+- **Install stable** — вернуться на стабильное ядро sing-box.
-
-0.8.0 — переименование в NetShift
+После установки расширенного ядра становится доступен клиентский транспорт
+**xhttp**. Поддерживается только клиентский режим xhttp (не серверный).
-С версии 0.8.0 проект переименован из `podkop` в **NetShift**:
+## Изменения 0.8.0 — переименование в NetShift
+Начиная с версии 0.8.0 проект переименован из `podkop` в **NetShift**. Пакет
+теперь называется `netshift` (бинарь `/usr/bin/netshift`), а конфигурация
+переехала на `/etc/config/netshift`. LuCI-приложение — `luci-app-netshift`.
-- пакет — `netshift` (бинарь `/usr/bin/netshift`);
-- конфигурация — `/etc/config/netshift`;
-- LuCI-приложение — `luci-app-netshift`.
+При обновлении старый конфиг `/etc/config/podkop` автоматически мигрируется в
+`/etc/config/netshift`, а резервная копия сохраняется в
+`/etc/config/podkop.bak.pre-netshift`.
-При обновлении старый конфиг `/etc/config/podkop` **автоматически мигрируется** в `/etc/config/netshift`, резервная копия сохраняется в `/etc/config/podkop.bak.pre-netshift`. VPN продолжит работать без перенастройки.
+## Изменения 0.7.0
+Начиная с версии 0.7.0 изменена структура конфига `/etc/config/netshift`
+(на тот момент — `/etc/config/podkop`). Старые значения несовместимы с новыми.
+Нужно заново настроить NetShift.
-
+Скрипт установки обнаружит старую версию и предупредит вас об этом. Если вы согласитесь, то он сделает автоматически написанное ниже.
-
-0.7.0 — несовместимый формат конфига
+При обновлении вручную нужно:
-С версии 0.7.0 изменена структура конфига (на тот момент — `/etc/config/podkop`). Старые значения несовместимы — нужно настроить заново. Скрипт установки обнаружит старую версию и предложит сделать это автоматически.
-
-Вручную:
-
-```sh
-# 1. Забэкапить старый конфиг
+0. Не ныть в issue и чатик.
+1. Забэкапить старый конфиг:
+```
mv /etc/config/netshift /etc/config/netshift-070
-# 2. Стянуть новый дефолтный конфиг
+```
+2. Стянуть новый дефолтный конфиг:
+```
wget -O /etc/config/netshift https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/netshift/files/etc/config/netshift
-# 3. Настроить заново через LuCI или UCI
```
+3. Настроить заново ваш NetShift через Luci или UCI.
-
-
-## Документация
+# ToDo
-Полная документация: ****
+> [!IMPORTANT]
+> Pull Request принимаются только после согласования с авторами в Telegram-чате. На данный момент PR без предварительного обсуждения не рассматриваются.
-## Дорожная карта
-
-> [!IMPORTANT]
-> Pull Request принимаются только после согласования с авторами в [Telegram-чате](https://t.me/itdogchat/81758/420321). PR без предварительного обсуждения не рассматриваются.
-
-- [x] [Подписка (Subscription URL)](https://github.com/itdoginfo/podkop/issues/118) — **реализовано**
-- [x] Переключаемое ядро sing-box-extended + xhttp — **реализовано**
+## Будущее
+- [x] [Подписка](https://github.com/itdoginfo/podkop/issues/118) — **реализовано в NetShift!**
- [ ] Весь трафик в sing-box и маршрутизация полностью на его уровне.
-- [ ] Фоновый режим со слежением за состоянием sing-box и авто-restore dnsmasq при падении. [Issue](https://github.com/itdoginfo/podkop/issues/111)
-- [ ] Опция, ограничивающая доступ к DoH-серверам.
-- [ ] IPv6 (после наполнения Wiki).
-
-**Тесты:**
+- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. [Issue](https://github.com/itdoginfo/podkop/issues/111)
+- [ ] Галочка, которая режет доступ к doh серверам.
+- [ ] IPv6. Только после наполнения Wiki.
-- [ ] Unit-тесты (BATS)
-- [ ] Интеграционные тесты бэкенда (OpenWrt rootfs + BATS)
-
-## Благодарности
-
-- [itdoginfo/podkop](https://github.com/itdoginfo/podkop) — исходный проект, форком которого является NetShift.
-- [sing-box](https://github.com/SagerNet/sing-box) — движок маршрутизации.
-
-## Лицензия
-
-GPL-2.0-or-later — см. [LICENSE](LICENSE).
+## Тесты
+- [ ] Unit тесты (BATS)
+- [ ] Интеграционные тесты бекенда (OpenWrt rootfs + BATS)
> [!WARNING]
-> Программное обеспечение предоставляется «как есть», без каких-либо явных или подразумеваемых гарантий, включая гарантии коммерческой пригодности и соответствия определённой цели. Правообладатели и участники проекта не несут ответственности за любые убытки, возникшие в результате использования ПО.
+> Данное программное обеспечение предоставляется «как есть», без каких-либо явных или подразумеваемых гарантий, включая гарантии коммерческой пригодности и соответствия определённой цели.
+>
+> Правообладатели и участники проекта не несут ответственности за любые прямые, косвенные, случайные, специальные или иные убытки, возникшие в результате использования программного обеспечения, включая потерю данных, прибыли или прерывание деятельности, даже если они были предупреждены о возможности таких последствий.
+
+[](https://deepwiki.com/itdoginfo/podkop)
From 9a677de868a33c960070aef76e01387e3c35452c Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Tue, 2 Jun 2026 17:46:18 +0300
Subject: [PATCH 23/75] docs: restructure README (header, screenshot, sections
per spec)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Layout top-to-bottom: title + badges, divider, screenshot, description,
divider, then sections: Функции, Вещи перед установкой, Установка NetShift,
Project Structure, Build Artifacts, Star History, Credits.
---
README.md | 253 ++++++++++++++++++++++++++++++++++++------------------
1 file changed, 168 insertions(+), 85 deletions(-)
diff --git a/README.md b/README.md
index db3976a9..c45241a1 100644
--- a/README.md
+++ b/README.md
@@ -1,70 +1,129 @@
+
+
+```
+ ╔╗╔╔═╗╔╦╗╔═╗╦ ╦╦╔═╗╔╦╗
+ ║║║║╣ ║ ╚═╗╠═╣║╠╣ ║
+ ╝╚╝╚═╝ ╩ ╚═╝╩ ╩╩╚ ╩
+ shift your traffic
+```
+
# NetShift
-> **Форк с поддержкой Subscription URL + HWID и переключаемым ядром sing-box-extended (xhttp)**
->
-> NetShift добавляет поддержку ссылок подписки (subscription URL) с кастомными заголовками (HWID, Device-OS, Device-Model) и автоматическим обновлением, а также переключение ядра на sing-box-extended с поддержкой клиентского транспорта xhttp. Основан на [itdoginfo/podkop](https://github.com/itdoginfo/podkop).
+[](https://github.com/yandexru45/podkop-evolution/releases)
+[](LICENSE)
+[](https://openwrt.org/)
+[](https://podkop.net/)
+
+
+
+---
-Маршрутизация трафика для OpenWrt.
+**NetShift** — маршрутизатор трафика для OpenWrt. Направляйте нужные ресурсы в туннель, а остальное — напрямую. Открытое ПО на базе [sing-box](https://github.com/SagerNet/sing-box).
-Направляйте нужные ресурсы в туннель, а остальное — напрямую. Открытое программное обеспечение на базе [sing-box](https://github.com/SagerNet/sing-box).
+Это форк [itdoginfo/podkop](https://github.com/itdoginfo/podkop), добавляющий поддержку **Subscription URL** с заголовками **HWID**, а также переключаемое ядро **sing-box-extended** с клиентским транспортом **xhttp**.
> [!WARNING]
> Проект находится в стадии бета-версии. Возможны ошибки, нестабильная работа и существенные изменения функциональности.
---
-# Вещи, которые вам нужно знать перед установкой
+## Функции
+
+- [x] **Маршрутизация по доменам и подсетям** — нужное в туннель, остальное напрямую VLESS · Shadowsocks · Trojan · Hysteria2 · готовые community-списки
+- [x] **Subscription URL** — ссылки подписки от провайдера с автообновлением и автовыбором сервера заголовки HWID / Device-OS / Device-Model · URLTest · ручное переключение
+- [x] **Переключаемое ядро sing-box** — стабильное ↔ sing-box-extended прямо из веб-интерфейса клиентский транспорт xhttp · установка и откат в один клик
+- [x] **Веб-интерфейс LuCI** — дашборд, диагностика и настройки без правки конфигов статус серверов · проверка соединения · логи
+- [x] **Автоматическая миграция** — обновление со старого podkop переносит конфиг без перенастройки
+
+## Вещи, которые необходимо знать перед установкой
+
+
+Системные требования
+
+- OpenWrt **24.10** или выше.
+- Минимум **25 МБ** свободного места. Устройства с флеш-памятью 16 МБ не поддерживаются.
+
+
+
+
+Обновления и конфигурация
-### Обновления и конфигурация
- При обновлении **обязательно** [очищайте кэш LuCI](https://podkop.net/docs/clear-browser-cache/).
-- После обновления проверяйте конфигурацию — она может изменяться между версиями.
-- При старте NetShift модифицируется конфигурация Dnsmasq.
-- NetShift изменяет конфигурацию sing-box. Если вы используете собственную конфигурацию, заранее сохраните её.
+- После обновления проверяйте конфигурацию — она может меняться между версиями.
+- При старте NetShift модифицирует конфигурацию Dnsmasq.
+- NetShift изменяет конфигурацию sing-box. Если используете собственную — заранее сохраните её.
+
+
+
+
+Ограничения и особенности
-### Системные требования
-- Требуется OpenWrt 24.10 или выше.
-- Необходимо минимум 25 МБ свободного места на устройстве. Устройства с флеш-памятью 16 МБ не поддерживаются.
+- Если установлен **Getdomains**, его [необходимо удалить](https://github.com/itdoginfo/domain-routing-openwrt?tab=readme-ov-file#скрипт-для-удаления).
+- **Dashboard** работает только по HTTP (особенность Clash API). По HTTPS или через домен может быть недоступен.
-### Важные ограничения и особенности
-- Если установлен Getdomains, его [необходимо удалить](https://github.com/itdoginfo/domain-routing-openwrt?tab=readme-ov-file#скрипт-для-удаления)
-- Dashboard доступен только при подключении по HTTP (из-за особенностей Clash API). При использовании HTTPS или домена работа может быть недоступна.
+
+
+
+Поддержка и диагностика
-### Поддержка и диагностика
- [Руководство по диагностике](https://podkop.net/docs/diagnostics/)
-- Актуальные изменения публикуются в [Telegram-чате](https://t.me/itdogchat/81758/420321). Пожалуйста, ознакомьтесь с закрепленными сообщениями.
-- При возникновении проблем оставляйте технически грамотный фидбэк в GitHub Issues и Telegram-чате.
+- Актуальные изменения — в [Telegram-чате](https://t.me/itdogchat/81758/420321) (читайте закреплённые сообщения).
+- При проблемах оставляйте технически грамотный фидбэк в GitHub Issues и Telegram-чате.
+
+
+
+Миграция с podkop (0.8.0) и смена формата конфига (0.7.0)
-# Документация
-https://podkop.net/
+**0.8.0 — переименование в NetShift.** Пакет теперь `netshift` (бинарь `/usr/bin/netshift`), конфиг — `/etc/config/netshift`, LuCI-приложение — `luci-app-netshift`. При обновлении старый конфиг `/etc/config/podkop` автоматически мигрируется в `/etc/config/netshift`, резервная копия сохраняется в `/etc/config/podkop.bak.pre-netshift`. VPN продолжит работать без перенастройки.
-# Установка NetShift
-Полная информация в [документации](https://podkop.net/docs/install/)
+**0.7.0 — несовместимый формат конфига.** Старые значения несовместимы — нужно настроить заново. Скрипт установки обнаружит старую версию и предложит сделать это автоматически. Вручную:
-Для установки и обновления достаточно выполнить один скрипт:
+```sh
+mv /etc/config/netshift /etc/config/netshift-070
+wget -O /etc/config/netshift https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/netshift/files/etc/config/netshift
+# затем настроить заново через LuCI или UCI
```
+
+
+
+## Установка NetShift
+
+Полная инструкция — в [документации](https://podkop.net/docs/install/).
+
+Для установки и обновления достаточно одного скрипта:
+
+```sh
sh <(wget -O - https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/install.sh)
```
-## Новое в NetShift: Подписки (Subscription)
+Интерфейс появится в LuCI: **Services → NetShift**.
-Добавлена поддержка subscription URL — ссылки подписки от провайдера прокси. При выборе типа конфигурации **Subscription** в LuCI:
-
-- Введите URL подписки от вашего провайдера
-- Выберите интервал автообновления (от 30 минут до 1 дня)
-- Все серверы из подписки автоматически появятся в дашборде
-- Автоматический выбор лучшего сервера по задержке (URLTest)
-- Ручное переключение между серверами через дашборд
+
+Настройка подписки (Subscription URL) через UCI
При скачивании подписки отправляются заголовки:
-- `User-Agent: singbox/<версия>`
-- `X-HWID` — уникальный идентификатор роутера
-- `X-Device-OS: OpenWrt Linux`
-- `X-Device-Model` — модель роутера
-- `X-Ver-OS` — версия ядра
-Пример конфигурации через UCI:
-```
+| Заголовок | Значение |
+|---|---|
+| `User-Agent` | `singbox/<версия>` |
+| `X-HWID` | уникальный идентификатор роутера |
+| `X-Device-OS` | `OpenWrt Linux` |
+| `X-Device-Model` | модель роутера |
+| `X-Ver-OS` | версия ядра |
+
+```sh
uci set netshift.my_sub=section
uci set netshift.my_sub.connection_type='proxy'
uci set netshift.my_sub.proxy_config_type='subscription'
@@ -75,69 +134,93 @@ uci commit netshift
```
Ручное обновление подписки:
-```
+
+```sh
/usr/bin/netshift subscription_update
```
-## Новое в NetShift: ядро sing-box-extended (xhttp)
+
+
+
+Ядро sing-box-extended (xhttp)
-NetShift позволяет переключать ядро между стабильным sing-box и сборкой
-sing-box-extended прямо из вкладки **Diagnostics** в LuCI:
+Переключение ядра между стабильным sing-box и сборкой **sing-box-extended** прямо из вкладки **Diagnostics** в LuCI:
- **Install extended** — установить расширенное ядро sing-box-extended.
-- **Install stable** — вернуться на стабильное ядро sing-box.
+- **Install stable** — вернуться на стабильное ядро.
-После установки расширенного ядра становится доступен клиентский транспорт
-**xhttp**. Поддерживается только клиентский режим xhttp (не серверный).
+После установки расширенного ядра становится доступен клиентский транспорт **xhttp** (только клиентский режим, не серверный). По умолчанию ставится стабильное ядро — extended включается по желанию.
-## Изменения 0.8.0 — переименование в NetShift
-Начиная с версии 0.8.0 проект переименован из `podkop` в **NetShift**. Пакет
-теперь называется `netshift` (бинарь `/usr/bin/netshift`), а конфигурация
-переехала на `/etc/config/netshift`. LuCI-приложение — `luci-app-netshift`.
+
-При обновлении старый конфиг `/etc/config/podkop` автоматически мигрируется в
-`/etc/config/netshift`, а резервная копия сохраняется в
-`/etc/config/podkop.bak.pre-netshift`.
+## Project Structure
-## Изменения 0.7.0
-Начиная с версии 0.7.0 изменена структура конфига `/etc/config/netshift`
-(на тот момент — `/etc/config/podkop`). Старые значения несовместимы с новыми.
-Нужно заново настроить NetShift.
+```
+.
+├── netshift/ # Бэкенд-пакет (POSIX ash + jq)
+│ ├── Makefile # Описание OpenWrt-пакета
+│ └── files/
+│ ├── etc/config/netshift # UCI-конфиг по умолчанию
+│ ├── etc/init.d/netshift # procd init-скрипт
+│ └── usr/
+│ ├── bin/netshift # Точка входа CLI (диспетчер команд)
+│ └── lib/ # constants, helpers, nft, rulesets,
+│ # sing_box_config_*, updater, logging
+│
+├── luci-app-netshift/ # LuCI веб-интерфейс
+│ ├── Makefile
+│ ├── htdocs/.../view/netshift/ # main.js (автоген) + hand-written views
+│ ├── po/ # Переводы (генерируются из fe-app)
+│ └── root/ # menu.d · acl.d · uci-defaults
+│
+├── fe-app-netshift/ # TypeScript-исходник для main.js (tsup)
+│ ├── src/netshift/ # fetchers · methods · services · tabs
+│ ├── src/{validators,helpers,icons,partials}
+│ └── locales/ # Исходные переводы (netshift.pot / .po)
+│
+├── sdk/ # Базовые образы OpenWrt SDK
+├── Dockerfile-ipk · Dockerfile-apk # Сборка пакетов
+└── install.sh # Установщик + миграция с podkop
+```
-Скрипт установки обнаружит старую версию и предупредит вас об этом. Если вы согласитесь, то он сделает автоматически написанное ниже.
+## Build Artifacts
-При обновлении вручную нужно:
+Пакеты собираются в Docker-образе OpenWrt SDK (24.10) и публикуются как релиз при push git-тега ([`.github/workflows/build.yml`](.github/workflows/build.yml)).
-0. Не ныть в issue и чатик.
-1. Забэкапить старый конфиг:
-```
-mv /etc/config/netshift /etc/config/netshift-070
-```
-2. Стянуть новый дефолтный конфиг:
-```
-wget -O /etc/config/netshift https://raw.githubusercontent.com/yandexru45/podkop-evolution/refs/heads/main/netshift/files/etc/config/netshift
+| Пакет | Формат | Назначение |
+|---|---|---|
+| `netshift` | `.ipk` / `.apk` | Бэкенд: CLI, init-скрипт, библиотеки, UCI-конфиг |
+| `luci-app-netshift` | `.ipk` / `.apk` | Веб-интерфейс LuCI |
+| `luci-i18n-netshift-ru` | `.ipk` / `.apk` | Русская локализация интерфейса |
+
+Локальная сборка:
+
+```sh
+# ipk (большинство устройств OpenWrt 24.10)
+docker build -f Dockerfile-ipk --build-arg NETSHIFT_VERSION=0.8.0 -t netshift:ipk .
+
+# apk (новые сборки OpenWrt на apk)
+docker build -f Dockerfile-apk --build-arg NETSHIFT_VERSION=0.8.0 -t netshift:apk .
```
-3. Настроить заново ваш NetShift через Luci или UCI.
-# ToDo
+> Требуется sing-box >= 1.12.0 и jq >= 1.7.1 на целевом устройстве.
-> [!IMPORTANT]
-> Pull Request принимаются только после согласования с авторами в Telegram-чате. На данный момент PR без предварительного обсуждения не рассматриваются.
+## Star History
-## Будущее
-- [x] [Подписка](https://github.com/itdoginfo/podkop/issues/118) — **реализовано в NetShift!**
-- [ ] Весь трафик в sing-box и маршрутизация полностью на его уровне.
-- [ ] При успешном запуске переходит в фоновый режим и следит за состоянием sing-box. Если вдруг идёт exit 1, выполняется dnsmasq restore и снова следит за состоянием. [Issue](https://github.com/itdoginfo/podkop/issues/111)
-- [ ] Галочка, которая режет доступ к doh серверам.
-- [ ] IPv6. Только после наполнения Wiki.
+
+
+
+
+
+
+
-## Тесты
-- [ ] Unit тесты (BATS)
-- [ ] Интеграционные тесты бекенда (OpenWrt rootfs + BATS)
+## Credits
-> [!WARNING]
-> Данное программное обеспечение предоставляется «как есть», без каких-либо явных или подразумеваемых гарантий, включая гарантии коммерческой пригодности и соответствия определённой цели.
->
-> Правообладатели и участники проекта не несут ответственности за любые прямые, косвенные, случайные, специальные или иные убытки, возникшие в результате использования программного обеспечения, включая потерю данных, прибыли или прерывание деятельности, даже если они были предупреждены о возможности таких последствий.
+- [itdoginfo/podkop](https://github.com/itdoginfo/podkop) — исходный проект, форком которого является NetShift.
+- [sing-box](https://github.com/SagerNet/sing-box) — движок маршрутизации.
+
+Лицензия: **GPL-2.0-or-later** — см. [LICENSE](LICENSE).
-[](https://deepwiki.com/itdoginfo/podkop)
+> [!IMPORTANT]
+> Pull Request принимаются только после согласования с авторами в [Telegram-чате](https://t.me/itdogchat/81758/420321).
From 46e3fc5eb7d3afa612ab71a4ac0df6ee4cccd9ec Mon Sep 17 00:00:00 2001
From: yandexru45
Date: Tue, 2 Jun 2026 18:05:15 +0300
Subject: [PATCH 24/75] upd readme
---
README.md | 52 +++++++++++++++++++++-------------------------------
1 file changed, 21 insertions(+), 31 deletions(-)
diff --git a/README.md b/README.md
index c45241a1..96a0bae1 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,5 @@