Add zapret2 section action support

This commit is contained in:
ushan0v
2026-06-02 22:07:40 +03:00
parent 5cf4183550
commit e677d992d6
27 changed files with 4774 additions and 1073 deletions
+11
View File
@@ -2,6 +2,13 @@ name: Differential ShellCheck
on: on:
workflow_dispatch: workflow_dispatch:
inputs:
base:
description: Base revision for manual differential scan
required: true
head:
description: Head revision for manual differential scan
required: true
push: push:
branches: branches:
- main - main
@@ -27,6 +34,7 @@ permissions:
jobs: jobs:
shellcheck: shellcheck:
name: Differential ShellCheck name: Differential ShellCheck
if: ${{ github.event_name != 'push' || github.event.forced != true }}
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
permissions: permissions:
@@ -42,6 +50,9 @@ jobs:
- name: Differential ShellCheck - name: Differential ShellCheck
uses: redhat-plumbers-in-action/differential-shellcheck@v5.5.5 uses: redhat-plumbers-in-action/differential-shellcheck@v5.5.5
with: with:
triggering-event: ${{ github.event_name == 'workflow_dispatch' && 'manual' || github.event_name }}
base: ${{ github.event.inputs.base }}
head: ${{ github.event.inputs.head }}
severity: error severity: error
include-path: | include-path: |
podkop/files/usr/bin/podkop podkop/files/usr/bin/podkop
+25 -8
View File
@@ -32,14 +32,13 @@ sh <(wget -O - https://raw.githubusercontent.com/ushan0v/podkop-plus/main/instal
### Что нового в этом форке ### Что нового в этом форке
- Добавлена поддержка подписок. - Добавлена поддержка подписок.
- Интеграция Zapret и ByeDPI как отдельного действия. - Интеграция Zapret, Zapret2 и ByeDPI как отдельных действий секции.
- Поддержка sing-box extended и транспорта XHTTP. - Поддержка sing-box extended и транспорта XHTTP.
- Обновленный LuCI-интерфейс и расширенное управление секциями. - Обновленный LuCI-интерфейс и расширенное управление секциями.
- Новые условия маршрутизации. - Новые условия маршрутизации.
- Режим сервера с возможностю поднять собственный VPN/Proxy для подключения внешних устройств. - Режим сервера с возможностю поднять собственный VPN/Proxy для подключения внешних устройств.
- Менеджер обновлений и установки компонентов.
- Встроенный мониторинг соединений. - Встроенный мониторинг соединений.
- Менеджер обновлений.
- Переход на нативный для OpenWrt `ucode`
### Подписки ### Подписки
@@ -100,11 +99,11 @@ Podkop Plus позволяет поднять собственный VPN/Proxy
### Интеграция Zapret ### Интеграция Zapret
Zapret доступен как действие отдельной секции. Используется [remittor/zapret-openwrt](https://github.com/remittor/zapret-openwrt/releases). Интеграция не конфликтует с отдельным полноценным пакетом zapret (`luci-app-zapret`). Zapret доступен как действие отдельной секции. Используется [bol-van/zapret](https://github.com/bol-van/zapret) в OpenWrt-пакете `zapret` из релизов [remittor/zapret-openwrt](https://github.com/remittor/zapret-openwrt/releases).
> Вы можете найти адаптированные и проверенные стратегии обхода для Podkop Plus в специальном репозитории: [ushan0v/podkop-plus-zapret-strategies](https://github.com/ushan0v/podkop-plus-zapret-strategies). > Вы можете найти адаптированные и проверенные стратегии zapret для Podkop Plus в специальном репозитории: [ushan0v/podkop-plus-zapret-strategies](https://github.com/ushan0v/podkop-plus-zapret-strategies).
Реализация: `sing-box` отбирает трафик секции и отправляет его в `direct` outbound с отдельным `routing_mark` -> `zapret` принимает помеченные пакеты из очереди и применяет выбранную стратегию DPI-обхода. Реализация: `sing-box` отбирает трафик секции и отправляет его в `direct` outbound с отдельным `routing_mark` -> `zapret` принимает помеченные пакеты из очереди (`4000-4255`) и применяет выбранную стратегию DPI-обхода.
Для стратегии `zapret` намеренно запрещены: Для стратегии `zapret` намеренно запрещены:
@@ -113,11 +112,29 @@ Zapret доступен как действие отдельной секции.
- ручное управление очередью и fwmark: **`--qnum`**, **`--dpi-desync-fwmark`**; - ручное управление очередью и fwmark: **`--qnum`**, **`--dpi-desync-fwmark`**;
- режимы, которые ломают lifecycle процесса: **`--daemon`**; - режимы, которые ломают lifecycle процесса: **`--daemon`**;
- режимы, которые не должны быть итоговой стратегией запуска: **`--dry-run`**, **`--version`**; - режимы, которые не должны быть итоговой стратегией запуска: **`--dry-run`**, **`--version`**;
- внешние конфиги вида **`@file`** или **`$file`**, которые обходят встроенную валидацию и управление очередями. - внешние файлы аргументов вида **`@file`** или **`$file`**, которые обходят встроенную валидацию и управление очередями.
### Интеграция Zapret2
Zapret2 доступен как отдельное действие секции `zapret2`. Используется [bol-van/zapret2](https://github.com/bol-van/zapret2) в OpenWrt-пакете `zapret2` из релизов [remittor/zapret-openwrt](https://github.com/remittor/zapret-openwrt/releases).
Реализация: `sing-box` отбирает трафик секции и отправляет его в `direct` outbound с отдельным `routing_mark` -> `zapret2` принимает помеченные пакеты из очереди (`4300-4555`) и применяет выбранную стратегию DPI-обхода.
`NFQWS2 Strategy` принимает параметры `nfqws2` без `--qnum` и без базовых Lua-инициализаций: Podkop Plus добавляет очередь, `--fwmark` и стандартные `zapret2` Lua-модули самостоятельно. Свои `--blob` и `--lua-init` допустимы для расширенных стратегий.
Для стратегии `zapret2` намеренно запрещены:
- шаблоны и hostlist placeholders: **`<HOSTLIST>`**, **`<HOSTLIST_NOAUTO>`**;
- hostname/IP selectors внутри самой стратегии: **`--hostlist*`**, **`--hostlist-auto*`**, **`--ipset*`**;
- ручное управление очередью и fwmark: **`--qnum`**, **`--fwmark`**, **`--dpi-desync-fwmark`**;
- режимы, несовместимые с управляемой NFQUEUE-архитектурой: **`--fuzz`**, **`--intercept=0`**;
- режимы, которые ломают lifecycle процесса: **`--daemon`**;
- режимы, которые не должны быть итоговой стратегией запуска: **`--dry-run`**, **`--version`**;
- внешние файлы аргументов вида **`@file`** или **`$file`**, которые обходят встроенную валидацию и управление очередями.
### Интеграция ByeDPI ### Интеграция ByeDPI
ByeDPI доступен как действие отдельной секции. Используется [DPITrickster/ByeDPI-OpenWrt](https://github.com/DPITrickster/ByeDPI-OpenWrt/releases). Процесс работает под управлением Podkop Plus. ByeDPI доступен как действие отдельной секции. Используется [DPITrickster/ByeDPI-OpenWrt](https://github.com/DPITrickster/ByeDPI-OpenWrt/releases).
Реализация: `sing-box` отбирает трафик секции и отправляет его в локальный SOCKS5 outbound -> `ciadpi` принимает соединение на `127.0.0.1:1080+` и применяет выбранную стратегию DPI-обхода. Реализация: `sing-box` отбирает трафик секции и отправляет его в локальный SOCKS5 outbound -> `ciadpi` принимает соединение на `127.0.0.1:1080+` и применяет выбранную стратегию DPI-обхода.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+101 -2
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PODKOP PLUS\n" "Project-Id-Version: PODKOP PLUS\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-06-02 17:11+0300\n" "POT-Creation-Date: 2026-06-02 18:22+0300\n"
"PO-Revision-Date: 2026-06-02 17:11+0300\n" "PO-Revision-Date: 2026-06-02 18:22+0300\n"
"Last-Translator: ushan0v\n" "Last-Translator: ushan0v\n"
"Language-Team: none\n" "Language-Team: none\n"
"Language: ru\n" "Language: ru\n"
@@ -299,6 +299,9 @@ msgstr "Отключить QUIC"
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming" msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
msgstr "Отключить QUIC протокол для улучшения совместимости или исправления видео стриминга" msgstr "Отключить QUIC протокол для улучшения совместимости или исправления видео стриминга"
msgid "Disabling interception is incompatible with action=zapret2 because Podkop Plus sends matched traffic through NFQUEUE."
msgstr "Отключение перехвата несовместимо с action=zapret2, потому что Podkop Plus отправляет совпавший трафик через NFQUEUE."
msgid "DNS on router" msgid "DNS on router"
msgstr "DNS на роутере" msgstr "DNS на роутере"
@@ -443,6 +446,9 @@ msgstr "Открыть эту секцию как локальный HTTP+SOCKS
msgid "External nfqws config files bypass Podkop Plus queue management and explicit validation." msgid "External nfqws config files bypass Podkop Plus queue management and explicit validation."
msgstr "Внешние конфиги nfqws обходят управление очередями и явную валидацию Podkop Plus." msgstr "Внешние конфиги nfqws обходят управление очередями и явную валидацию Podkop Plus."
msgid "External nfqws2 config files bypass Podkop Plus queue management and explicit validation."
msgstr "Внешние конфиги nfqws2 обходят управление очередями и явную валидацию Podkop Plus."
msgid "Failed to close connection" msgid "Failed to close connection"
msgstr "Не удалось закрыть подключение" msgstr "Не удалось закрыть подключение"
@@ -485,6 +491,9 @@ msgstr "Proxy protocol для фронтинга"
msgid "Fully Routed IPs" msgid "Fully Routed IPs"
msgstr "Полностью маршрутизированные IP-адреса" msgstr "Полностью маршрутизированные IP-адреса"
msgid "Fuzzing is not supported here because Podkop Plus needs deterministic runtime validation."
msgstr "Fuzzing здесь не поддерживается, потому что Podkop Plus нужна предсказуемая runtime-валидация."
msgid "Generated inbound" msgid "Generated inbound"
msgstr "Созданный inbound" msgstr "Созданный inbound"
@@ -608,6 +617,9 @@ msgstr "Неверный IP-адрес"
msgid "Invalid JSON format" msgid "Invalid JSON format"
msgstr "Неверный формат JSON" msgstr "Неверный формат JSON"
msgid "Invalid NFQWS2 strategy"
msgstr "Некорректная стратегия NFQWS2"
msgid "Invalid path format. Path must start with \"/\" and contain valid characters" msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
msgstr "Неверный формат пути. Путь должен начинаться с \"/\" и содержать допустимые символы" msgstr "Неверный формат пути. Путь должен начинаться с \"/\" и содержать допустимые символы"
@@ -803,6 +815,12 @@ msgstr "Стратегия NFQWS"
msgid "NFQWS strategy cannot be empty" msgid "NFQWS strategy cannot be empty"
msgstr "Стратегия NFQWS не может быть пустой" msgstr "Стратегия NFQWS не может быть пустой"
msgid "NFQWS2 Strategy"
msgstr "Стратегия NFQWS2"
msgid "NFQWS2 strategy cannot be empty"
msgstr "Стратегия NFQWS2 не может быть пустой"
msgid "No active connections" msgid "No active connections"
msgstr "Нет активных подключений" msgstr "Нет активных подключений"
@@ -827,6 +845,9 @@ msgstr "Нет правил, использующих ByeDPI"
msgid "No rules use Zapret" msgid "No rules use Zapret"
msgstr "Нет правил, использующих Zapret" msgstr "Нет правил, использующих Zapret"
msgid "No rules use Zapret2"
msgstr "Нет правил, использующих Zapret2"
msgid "None" msgid "None"
msgstr "Нет" msgstr "Нет"
@@ -884,6 +905,9 @@ msgstr "Параметр \"%s\" содержит некорректное зна
msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws." msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws."
msgstr "Необязательное значение здесь нужно передавать только через '='; отдельный токен nfqws проигнорирует." msgstr "Необязательное значение здесь нужно передавать только через '='; отдельный токен nfqws проигнорирует."
msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws2."
msgstr "Необязательные значения здесь должны задаваться через '='; отдельный токен будет проигнорирован nfqws2."
msgid "Outbound JSON" msgid "Outbound JSON"
msgstr "JSON outbound" msgstr "JSON outbound"
@@ -938,6 +962,9 @@ msgstr "Podkop Plus сам управляет жизненным циклом ci
msgid "Podkop Plus manages the nfqws process lifecycle itself, so daemon mode is not allowed here." msgid "Podkop Plus manages the nfqws process lifecycle itself, so daemon mode is not allowed here."
msgstr "Podkop Plus сам управляет жизненным циклом nfqws, поэтому режим daemon здесь запрещён." msgstr "Podkop Plus сам управляет жизненным циклом nfqws, поэтому режим daemon здесь запрещён."
msgid "Podkop Plus manages the nfqws2 process lifecycle itself, so daemon mode is not allowed here."
msgstr "Podkop Plus сам управляет жизненным циклом nfqws2, поэтому режим daemon здесь запрещён."
msgid "Podkop Plus rules" msgid "Podkop Plus rules"
msgstr "Правила Podkop Plus" msgstr "Правила Podkop Plus"
@@ -968,6 +995,15 @@ msgstr "NFQWS Podkop Plus не запущен"
msgid "Podkop Plus-managed nfqws runtime is ready" msgid "Podkop Plus-managed nfqws runtime is ready"
msgstr "NFQWS Podkop Plus готов" msgstr "NFQWS Podkop Plus готов"
msgid "Podkop Plus-managed nfqws2 runtime is not ready"
msgstr "NFQWS2 Podkop Plus не готов"
msgid "Podkop Plus-managed nfqws2 runtime is not running"
msgstr "NFQWS2 Podkop Plus не запущен"
msgid "Podkop Plus-managed nfqws2 runtime is ready"
msgstr "NFQWS2 Podkop Plus готов"
msgid "Port cannot be empty" msgid "Port cannot be empty"
msgstr "Порт не может быть пустым" msgstr "Порт не может быть пустым"
@@ -1052,9 +1088,15 @@ msgstr "Определять страны серверов через country.is
msgid "Resource selection by hostname inside nfqws is not supported here; sing-box selects resources before NFQUEUE." msgid "Resource selection by hostname inside nfqws is not supported here; sing-box selects resources before NFQUEUE."
msgstr "Выбор ресурсов по hostname внутри nfqws здесь не поддерживается; ресурсы выбирает sing-box до NFQUEUE." msgstr "Выбор ресурсов по hostname внутри nfqws здесь не поддерживается; ресурсы выбирает sing-box до NFQUEUE."
msgid "Resource selection by hostname inside nfqws2 is not supported here; sing-box selects resources before NFQUEUE."
msgstr "Выбор ресурсов по hostname внутри nfqws2 здесь не поддерживается; sing-box выбирает ресурсы до NFQUEUE."
msgid "Resource selection by IP or CIDR inside nfqws is not supported here; sing-box selects resources before NFQUEUE." msgid "Resource selection by IP or CIDR inside nfqws is not supported here; sing-box selects resources before NFQUEUE."
msgstr "Выбор ресурсов по IP или CIDR внутри nfqws здесь не поддерживается; ресурсы выбирает sing-box до NFQUEUE." msgstr "Выбор ресурсов по IP или CIDR внутри nfqws здесь не поддерживается; ресурсы выбирает sing-box до NFQUEUE."
msgid "Resource selection by IP or CIDR inside nfqws2 is not supported here; sing-box selects resources before NFQUEUE."
msgstr "Выбор ресурсов по IP или CIDR внутри nfqws2 здесь не поддерживается; sing-box выбирает ресурсы до NFQUEUE."
msgid "Restart Podkop Plus" msgid "Restart Podkop Plus"
msgstr "Перезапустить Podkop Plus" msgstr "Перезапустить Podkop Plus"
@@ -1277,6 +1319,18 @@ msgstr "Отдельная служба Zapret активна"
msgid "Standalone Zapret service is inactive" msgid "Standalone Zapret service is inactive"
msgstr "Отдельная служба Zapret неактивна" msgstr "Отдельная служба Zapret неактивна"
msgid "Standalone Zapret2 autostart is enabled"
msgstr "Автозапуск отдельной службы Zapret2 включён"
msgid "Standalone Zapret2 is active together with Podkop Plus Zapret2 rules"
msgstr "Отдельная служба Zapret2 активна вместе с правилами Zapret2 в Podkop Plus"
msgid "Standalone Zapret2 service is active"
msgstr "Отдельная служба Zapret2 активна"
msgid "Standalone Zapret2 service is inactive"
msgstr "Отдельная служба Zapret2 неактивна"
msgid "Start Podkop Plus" msgid "Start Podkop Plus"
msgstr "Запустить Podkop Plus" msgstr "Запустить Podkop Plus"
@@ -1346,6 +1400,9 @@ msgstr "Есть правила, использующие ByeDPI"
msgid "There are rules using Zapret" msgid "There are rules using Zapret"
msgstr "Есть правила использующие Zapret" msgstr "Есть правила использующие Zapret"
msgid "There are rules using Zapret2"
msgstr "Есть правила, использующие Zapret2"
msgid "This field is required" msgid "This field is required"
msgstr "Заполните это поле" msgstr "Заполните это поле"
@@ -1358,6 +1415,12 @@ msgstr "Это поле должно запускать рабочую стра
msgid "This field must start a working nfqws strategy; --version exits immediately and is not allowed here." msgid "This field must start a working nfqws strategy; --version exits immediately and is not allowed here."
msgstr "Это поле должно запускать рабочую стратегию nfqws; --version сразу завершает процесс и здесь запрещён." msgstr "Это поле должно запускать рабочую стратегию nfqws; --version сразу завершает процесс и здесь запрещён."
msgid "This field must start a working nfqws2 strategy; --dry-run exits immediately and is not allowed here."
msgstr "Это поле должно запускать рабочую стратегию nfqws2; --dry-run сразу завершает работу и здесь запрещён."
msgid "This field must start a working nfqws2 strategy; --version exits immediately and is not allowed here."
msgstr "Это поле должно запускать рабочую стратегию nfqws2; --version сразу завершает работу и здесь запрещён."
msgid "This flag does not accept a value." msgid "This flag does not accept a value."
msgstr "Этот флаг не принимает значение." msgstr "Этот флаг не принимает значение."
@@ -1418,6 +1481,9 @@ msgstr "Не удалось проверить стратегию ByeDPI чер
msgid "Unable to validate the NFQWS strategy through the backend parser." msgid "Unable to validate the NFQWS strategy through the backend parser."
msgstr "Не удалось проверить стратегию NFQWS через backend-парсер." msgstr "Не удалось проверить стратегию NFQWS через backend-парсер."
msgid "Unable to validate the NFQWS2 strategy through the backend parser."
msgstr "Не удалось проверить стратегию NFQWS2 через backend-парсер."
msgid "Unexpected ByeDPI strategy argument: %s" msgid "Unexpected ByeDPI strategy argument: %s"
msgstr "Неожиданный аргумент стратегии ByeDPI: %s" msgstr "Неожиданный аргумент стратегии ByeDPI: %s"
@@ -1427,6 +1493,9 @@ msgstr "Обнаружен лишний ciadpi Podkop Plus"
msgid "Unexpected Podkop Plus-managed nfqws runtime is running" msgid "Unexpected Podkop Plus-managed nfqws runtime is running"
msgstr "Обнаружен лишний NFQWS Podkop Plus" msgstr "Обнаружен лишний NFQWS Podkop Plus"
msgid "Unexpected Podkop Plus-managed nfqws2 runtime is running"
msgstr "Обнаружен лишний NFQWS2 Podkop Plus"
msgid "Unexpected standalone token. Use explicit flags such as --name or --name=value." msgid "Unexpected standalone token. Use explicit flags such as --name or --name=value."
msgstr "Неожиданный отдельный токен. Используйте явные флаги вида --имя или --имя=значение." msgstr "Неожиданный отдельный токен. Используйте явные флаги вида --имя или --имя=значение."
@@ -1448,6 +1517,9 @@ msgstr "Неизвестная ошибка"
msgid "Unknown NFQWS flag." msgid "Unknown NFQWS flag."
msgstr "Неизвестный флаг NFQWS." msgstr "Неизвестный флаг NFQWS."
msgid "Unknown NFQWS2 flag."
msgstr "Неизвестный флаг NFQWS2."
msgid "Unsupported security mode for this protocol" msgid "Unsupported security mode for this protocol"
msgstr "Этот протокол не поддерживает выбранный режим защиты" msgstr "Этот протокол не поддерживает выбранный режим защиты"
@@ -1591,3 +1663,30 @@ msgstr "Бинарник провайдера Zapret доступен"
msgid "Zapret provider binary is not available" msgid "Zapret provider binary is not available"
msgstr "Бинарник провайдера Zapret недоступен" msgstr "Бинарник провайдера Zapret недоступен"
msgid "Zapret2 hostlist templates are not supported here because Podkop Plus does not expand them for per-rule NFQWS2 strategies."
msgstr "Шаблоны hostlist Zapret2 здесь не поддерживаются, потому что Podkop Plus не разворачивает их для per-rule стратегий NFQWS2."
msgid "Zapret2 package is installed"
msgstr "Пакет Zapret2 установлен"
msgid "Zapret2 package is not installed"
msgstr "Пакет Zapret2 не установлен"
msgid "Zapret2 provider binary is available"
msgstr "Бинарник провайдера Zapret2 доступен"
msgid "Zapret2 provider binary is not available"
msgstr "Бинарник провайдера Zapret2 недоступен"
msgid "Zapret2 route rules are configured"
msgstr "Правила маршрутизации Zapret2 настроены"
msgid "Zapret2 route rules are not configured"
msgstr "Правила маршрутизации Zapret2 не настроены"
msgid "Zapret2 sing-box outbound is configured"
msgstr "sing-box outbound для Zapret2 настроен"
msgid "Zapret2 sing-box outbound is not configured"
msgstr "sing-box outbound для Zapret2 не настроен"
@@ -134,6 +134,10 @@ export const PodkopShellMethods = {
callBaseMethod<Podkop.ZapretCheckResult>( callBaseMethod<Podkop.ZapretCheckResult>(
Podkop.AvailableMethods.CHECK_ZAPRET_RUNTIME, Podkop.AvailableMethods.CHECK_ZAPRET_RUNTIME,
), ),
checkZapret2Runtime: async () =>
callBaseMethod<Podkop.Zapret2CheckResult>(
Podkop.AvailableMethods.CHECK_ZAPRET2_RUNTIME,
),
checkByedpiRuntime: async () => checkByedpiRuntime: async () =>
callBaseMethod<Podkop.ByedpiCheckResult>( callBaseMethod<Podkop.ByedpiCheckResult>(
Podkop.AvailableMethods.CHECK_BYEDPI_RUNTIME, Podkop.AvailableMethods.CHECK_BYEDPI_RUNTIME,
@@ -180,6 +184,10 @@ export const PodkopShellMethods = {
callBaseMethod<Podkop.GetZapretStatus>( callBaseMethod<Podkop.GetZapretStatus>(
Podkop.AvailableMethods.GET_ZAPRET_STATUS, Podkop.AvailableMethods.GET_ZAPRET_STATUS,
), ),
getZapret2Status: async () =>
callBaseMethod<Podkop.GetZapret2Status>(
Podkop.AvailableMethods.GET_ZAPRET2_STATUS,
),
getByedpiStatus: async () => getByedpiStatus: async () =>
callBaseMethod<Podkop.GetByedpiStatus>( callBaseMethod<Podkop.GetByedpiStatus>(
Podkop.AvailableMethods.GET_BYEDPI_STATUS, Podkop.AvailableMethods.GET_BYEDPI_STATUS,
@@ -196,6 +196,8 @@ export interface StoreType {
sing_box_extended: number; sing_box_extended: number;
zapret_version: string; zapret_version: string;
zapret_installed: number; zapret_installed: number;
zapret2_version: string;
zapret2_installed: number;
byedpi_version: string; byedpi_version: string;
byedpi_installed: number; byedpi_installed: number;
server_inbounds_enabled_count: number; server_inbounds_enabled_count: number;
@@ -212,6 +214,9 @@ export interface StoreType {
zapretCheck: { loading: boolean }; zapretCheck: { loading: boolean };
zapretInstall: { loading: boolean }; zapretInstall: { loading: boolean };
zapretRemove: { loading: boolean }; zapretRemove: { loading: boolean };
zapret2Check: { loading: boolean };
zapret2Install: { loading: boolean };
zapret2Remove: { loading: boolean };
byedpiCheck: { loading: boolean }; byedpiCheck: { loading: boolean };
byedpiInstall: { loading: boolean }; byedpiInstall: { loading: boolean };
byedpiRemove: { loading: boolean }; byedpiRemove: { loading: boolean };
@@ -13,6 +13,8 @@ const UNKNOWN_SYSTEM_INFO: StoreType['diagnosticsSystemInfo'] = {
sing_box_extended: 0, sing_box_extended: 0,
zapret_version: _('unknown'), zapret_version: _('unknown'),
zapret_installed: 0, zapret_installed: 0,
zapret2_version: _('unknown'),
zapret2_installed: 0,
byedpi_version: _('unknown'), byedpi_version: _('unknown'),
byedpi_installed: 0, byedpi_installed: 0,
server_inbounds_enabled_count: -1, server_inbounds_enabled_count: -1,
@@ -94,6 +96,7 @@ export async function ensureSystemInfo({
loaded: false, loaded: false,
providerInfoLoaded: latestSystemInfo.providerInfoLoaded, providerInfoLoaded: latestSystemInfo.providerInfoLoaded,
zapret_installed: latestSystemInfo.zapret_installed, zapret_installed: latestSystemInfo.zapret_installed,
zapret2_installed: latestSystemInfo.zapret2_installed,
byedpi_installed: latestSystemInfo.byedpi_installed, byedpi_installed: latestSystemInfo.byedpi_installed,
server_inbounds_enabled_count: server_inbounds_enabled_count:
latestSystemInfo.server_inbounds_enabled_count, latestSystemInfo.server_inbounds_enabled_count,
@@ -5,6 +5,7 @@ export enum DIAGNOSTICS_CHECKS {
SINGBOX = 'SINGBOX', SINGBOX = 'SINGBOX',
NFT = 'NFT', NFT = 'NFT',
ZAPRET = 'ZAPRET', ZAPRET = 'ZAPRET',
ZAPRET2 = 'ZAPRET2',
BYEDPI = 'BYEDPI', BYEDPI = 'BYEDPI',
FAKEIP = 'FAKEIP', FAKEIP = 'FAKEIP',
OUTBOUNDS = 'OUTBOUNDS', OUTBOUNDS = 'OUTBOUNDS',
@@ -36,17 +37,22 @@ export const DIAGNOSTICS_CHECKS_MAP: Record<
code: DIAGNOSTICS_CHECKS.ZAPRET, code: DIAGNOSTICS_CHECKS.ZAPRET,
}, },
[DIAGNOSTICS_CHECKS.BYEDPI]: { [DIAGNOSTICS_CHECKS.BYEDPI]: {
order: 6, order: 7,
title: getCheckTitle('ByeDPI'), title: getCheckTitle('ByeDPI'),
code: DIAGNOSTICS_CHECKS.BYEDPI, code: DIAGNOSTICS_CHECKS.BYEDPI,
}, },
[DIAGNOSTICS_CHECKS.ZAPRET2]: {
order: 6,
title: getCheckTitle('Zapret2'),
code: DIAGNOSTICS_CHECKS.ZAPRET2,
},
[DIAGNOSTICS_CHECKS.OUTBOUNDS]: { [DIAGNOSTICS_CHECKS.OUTBOUNDS]: {
order: 7, order: 8,
title: getCheckTitle('Outbounds'), title: getCheckTitle('Outbounds'),
code: DIAGNOSTICS_CHECKS.OUTBOUNDS, code: DIAGNOSTICS_CHECKS.OUTBOUNDS,
}, },
[DIAGNOSTICS_CHECKS.FAKEIP]: { [DIAGNOSTICS_CHECKS.FAKEIP]: {
order: 8, order: 9,
title: getCheckTitle('FakeIP'), title: getCheckTitle('FakeIP'),
code: DIAGNOSTICS_CHECKS.FAKEIP, code: DIAGNOSTICS_CHECKS.FAKEIP,
}, },
@@ -0,0 +1,167 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { IDiagnosticsChecksItem } from '../../../services';
type CheckState = 'success' | 'warning' | 'error';
export async function runZapret2Check() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.ZAPRET2;
updateCheckStore({
order,
code,
title,
description: _('Checking, please wait'),
state: 'loading',
items: [],
});
const zapret2Status = await PodkopShellMethods.getZapret2Status();
if (!zapret2Status.success) {
updateCheckStore({
order,
code,
title,
description: _('Cannot receive checks result'),
state: 'error',
items: [],
});
throw new Error('Zapret2 checks failed');
}
const data = zapret2Status.data;
const providerAvailable = Boolean(data.provider_available ?? data.installed);
const packageInstalled = Boolean(data.package_installed);
const hasZapret2Rules = Number(data.enabled_rule_count || 0) > 0;
const ready = Boolean(data.ready);
const queueOverlap = Boolean(data.queue_overlap);
const expectedProcesses = Number(data.expected_process_count || 0);
const runningProcesses = Number(data.running_process_count || 0);
const supervisorProcesses = Number(data.supervisor_process_count || 0);
const podkopRuntimeReady =
!hasZapret2Rules ||
(runningProcesses === expectedProcesses &&
supervisorProcesses === expectedProcesses);
const unexpectedRuntime =
!hasZapret2Rules && (runningProcesses > 0 || supervisorProcesses > 0);
const outboundsConfigured = Boolean(data.outbounds_configured);
const routesConfigured = Boolean(data.routes_configured);
const standaloneServiceEnabled = Boolean(data.standalone_service_enabled);
const standaloneServiceRunning = Boolean(data.standalone_service_running);
const standaloneConflict = hasZapret2Rules && standaloneServiceRunning;
const standaloneAutostartRisk =
hasZapret2Rules && standaloneServiceEnabled && !standaloneServiceRunning;
let state: CheckState = 'success';
let description = _('Checks passed');
if (hasZapret2Rules && !providerAvailable) {
state = 'error';
description = _('Checks failed');
} else if (hasZapret2Rules && !ready) {
state = 'error';
description = _('Checks failed');
} else if (queueOverlap) {
state = 'error';
description = _('Checks failed');
} else if (standaloneConflict) {
state = 'error';
description = _('Checks failed');
} else if (standaloneAutostartRisk || !packageInstalled) {
state = 'warning';
description = _('Issues detected');
}
const items: Array<IDiagnosticsChecksItem> = [
{
state: providerAvailable
? 'success'
: hasZapret2Rules
? 'error'
: 'warning',
key: providerAvailable
? _('Zapret2 provider binary is available')
: _('Zapret2 provider binary is not available'),
value: '',
},
{
state: packageInstalled
? 'success'
: hasZapret2Rules
? 'error'
: 'warning',
key: packageInstalled
? _('Zapret2 package is installed')
: _('Zapret2 package is not installed'),
value: '',
},
{
state: hasZapret2Rules && !providerAvailable ? 'error' : 'success',
key: hasZapret2Rules
? _('There are rules using Zapret2')
: _('No rules use Zapret2'),
value: '',
},
{
state: unexpectedRuntime || !podkopRuntimeReady ? 'error' : 'success',
key: hasZapret2Rules
? podkopRuntimeReady
? _('Podkop Plus-managed nfqws2 runtime is ready')
: _('Podkop Plus-managed nfqws2 runtime is not ready')
: unexpectedRuntime
? _('Unexpected Podkop Plus-managed nfqws2 runtime is running')
: _('Podkop Plus-managed nfqws2 runtime is not running'),
value: hasZapret2Rules ? `${runningProcesses}/${expectedProcesses}` : '',
},
{
state: queueOverlap ? 'error' : 'success',
key: queueOverlap
? _('NFQUEUE range overlaps with another rule')
: _('NFQUEUE range is available'),
value: `${Number(data.queue_base || 0)}-${Number(data.queue_range_end || 0)}`,
},
{
state: !hasZapret2Rules || outboundsConfigured ? 'success' : 'error',
key: outboundsConfigured
? _('Zapret2 sing-box outbound is configured')
: _('Zapret2 sing-box outbound is not configured'),
value: '',
},
{
state: !hasZapret2Rules || routesConfigured ? 'success' : 'error',
key: routesConfigured
? _('Zapret2 route rules are configured')
: _('Zapret2 route rules are not configured'),
value: '',
},
{
state: standaloneConflict
? 'error'
: standaloneAutostartRisk
? 'warning'
: 'success',
key: standaloneServiceRunning
? hasZapret2Rules
? _(
'Standalone Zapret2 is active together with Podkop Plus Zapret2 rules',
)
: _('Standalone Zapret2 service is active')
: standaloneAutostartRisk
? _('Standalone Zapret2 autostart is enabled')
: _('Standalone Zapret2 service is inactive'),
value: '',
},
];
updateCheckStore({
order,
code,
title,
description,
state,
items,
});
}
@@ -6,6 +6,7 @@ import { IDiagnosticsChecksStoreItem, StoreType } from '../../services';
export interface DiagnosticsProviderOptions { export interface DiagnosticsProviderOptions {
includeZapret?: boolean; includeZapret?: boolean;
includeZapret2?: boolean;
includeByedpi?: boolean; includeByedpi?: boolean;
includeInbounds?: boolean; includeInbounds?: boolean;
} }
@@ -42,6 +43,10 @@ export function getDiagnosticsChecks(
checks.push(DIAGNOSTICS_CHECKS.ZAPRET); checks.push(DIAGNOSTICS_CHECKS.ZAPRET);
} }
if (options.includeZapret2) {
checks.push(DIAGNOSTICS_CHECKS.ZAPRET2);
}
if (options.includeByedpi) { if (options.includeByedpi) {
checks.push(DIAGNOSTICS_CHECKS.BYEDPI); checks.push(DIAGNOSTICS_CHECKS.BYEDPI);
} }
@@ -79,6 +84,8 @@ export const initialDiagnosticStore: Pick<
sing_box_extended: 0, sing_box_extended: 0,
zapret_version: 'loading', zapret_version: 'loading',
zapret_installed: 0, zapret_installed: 0,
zapret2_version: 'loading',
zapret2_installed: 0,
byedpi_version: 'loading', byedpi_version: 'loading',
byedpi_installed: 0, byedpi_installed: 0,
server_inbounds_enabled_count: -1, server_inbounds_enabled_count: -1,
@@ -123,6 +130,9 @@ export const initialDiagnosticStore: Pick<
zapretCheck: { loading: false }, zapretCheck: { loading: false },
zapretInstall: { loading: false }, zapretInstall: { loading: false },
zapretRemove: { loading: false }, zapretRemove: { loading: false },
zapret2Check: { loading: false },
zapret2Install: { loading: false },
zapret2Remove: { loading: false },
byedpiCheck: { loading: false }, byedpiCheck: { loading: false },
byedpiInstall: { loading: false }, byedpiInstall: { loading: false },
byedpiRemove: { loading: false }, byedpiRemove: { loading: false },
@@ -131,6 +141,7 @@ export const initialDiagnosticStore: Pick<
podkop: { status: null, latest_version: '', release_url: '' }, podkop: { status: null, latest_version: '', release_url: '' },
sing_box: { status: null, latest_version: '', release_url: '' }, sing_box: { status: null, latest_version: '', release_url: '' },
zapret: { status: null, latest_version: '', release_url: '' }, zapret: { status: null, latest_version: '', release_url: '' },
zapret2: { status: null, latest_version: '', release_url: '' },
byedpi: { status: null, latest_version: '', release_url: '' }, byedpi: { status: null, latest_version: '', release_url: '' },
}, },
}; };
@@ -5,6 +5,7 @@ import { runInboundsCheck } from './checks/runInboundsCheck';
import { runNftCheck } from './checks/runNftCheck'; import { runNftCheck } from './checks/runNftCheck';
import { runFakeIPCheck } from './checks/runFakeIPCheck'; import { runFakeIPCheck } from './checks/runFakeIPCheck';
import { runZapretCheck } from './checks/runZapretCheck'; import { runZapretCheck } from './checks/runZapretCheck';
import { runZapret2Check } from './checks/runZapret2Check';
import { runByedpiCheck } from './checks/runByedpiCheck'; import { runByedpiCheck } from './checks/runByedpiCheck';
import { import {
DiagnosticsProviderOptions, DiagnosticsProviderOptions,
@@ -47,11 +48,15 @@ function sleep(ms: number) {
function getDiagnosticsProviderOptions( function getDiagnosticsProviderOptions(
systemInfo: Pick< systemInfo: Pick<
StoreType['diagnosticsSystemInfo'], StoreType['diagnosticsSystemInfo'],
'zapret_installed' | 'byedpi_installed' | 'server_inbounds_enabled_count' | 'zapret_installed'
| 'zapret2_installed'
| 'byedpi_installed'
| 'server_inbounds_enabled_count'
> = store.get().diagnosticsSystemInfo, > = store.get().diagnosticsSystemInfo,
): DiagnosticsProviderOptions { ): DiagnosticsProviderOptions {
return { return {
includeZapret: Boolean(systemInfo.zapret_installed), includeZapret: Boolean(systemInfo.zapret_installed),
includeZapret2: Boolean(systemInfo.zapret2_installed),
includeByedpi: Boolean(systemInfo.byedpi_installed), includeByedpi: Boolean(systemInfo.byedpi_installed),
includeInbounds: systemInfo.server_inbounds_enabled_count !== 0, includeInbounds: systemInfo.server_inbounds_enabled_count !== 0,
}; };
@@ -220,11 +225,13 @@ async function fetchDiagnosticsProviderInfo({
const requestId = ++latestProviderInfoRequestId; const requestId = ++latestProviderInfoRequestId;
try { try {
const [zapretRuntime, byedpiRuntime, inboundsConfig] = await Promise.all([ const [zapretRuntime, zapret2Runtime, byedpiRuntime, inboundsConfig] =
PodkopShellMethods.checkZapretRuntime(), await Promise.all([
PodkopShellMethods.checkByedpiRuntime(), PodkopShellMethods.checkZapretRuntime(),
PodkopShellMethods.checkInboundsConfig(), PodkopShellMethods.checkZapret2Runtime(),
]); PodkopShellMethods.checkByedpiRuntime(),
PodkopShellMethods.checkInboundsConfig(),
]);
if (requestId !== latestProviderInfoRequestId) { if (requestId !== latestProviderInfoRequestId) {
return; return;
@@ -237,6 +244,9 @@ async function fetchDiagnosticsProviderInfo({
zapret_installed: zapretRuntime.success zapret_installed: zapretRuntime.success
? zapretRuntime.data.zapret_installed ? zapretRuntime.data.zapret_installed
: currentSystemInfo.zapret_installed, : currentSystemInfo.zapret_installed,
zapret2_installed: zapret2Runtime.success
? zapret2Runtime.data.zapret2_installed
: currentSystemInfo.zapret2_installed,
byedpi_installed: byedpiRuntime.success byedpi_installed: byedpiRuntime.success
? byedpiRuntime.data.byedpi_installed ? byedpiRuntime.data.byedpi_installed
: currentSystemInfo.byedpi_installed, : currentSystemInfo.byedpi_installed,
@@ -249,6 +259,14 @@ async function fetchDiagnosticsProviderInfo({
logger.error('[DIAGNOSTIC]', 'fetchZapretRuntime failed', zapretRuntime); logger.error('[DIAGNOSTIC]', 'fetchZapretRuntime failed', zapretRuntime);
} }
if (!zapret2Runtime.success) {
logger.error(
'[DIAGNOSTIC]',
'fetchZapret2Runtime failed',
zapret2Runtime,
);
}
if (!byedpiRuntime.success) { if (!byedpiRuntime.success) {
logger.error('[DIAGNOSTIC]', 'fetchByedpiRuntime failed', byedpiRuntime); logger.error('[DIAGNOSTIC]', 'fetchByedpiRuntime failed', byedpiRuntime);
} }
@@ -265,6 +283,10 @@ async function fetchDiagnosticsProviderInfo({
nextSystemInfo.zapret_version = 'not installed'; nextSystemInfo.zapret_version = 'not installed';
} }
if (!nextSystemInfo.zapret2_installed) {
nextSystemInfo.zapret2_version = 'not installed';
}
if (!nextSystemInfo.byedpi_installed) { if (!nextSystemInfo.byedpi_installed) {
nextSystemInfo.byedpi_version = 'not installed'; nextSystemInfo.byedpi_version = 'not installed';
} }
@@ -632,6 +654,13 @@ function renderDiagnosticSystemInfoWidget() {
}); });
} }
if (diagnosticsSystemInfo.zapret2_installed) {
items.push({
key: 'Zapret2',
value: diagnosticsSystemInfo.zapret2_version,
});
}
if (diagnosticsSystemInfo.byedpi_installed) { if (diagnosticsSystemInfo.byedpi_installed) {
items.push({ items.push({
key: 'ByeDPI', key: 'ByeDPI',
@@ -702,6 +731,7 @@ async function runChecks() {
...(providerOptions.includeInbounds ? [runInboundsCheck] : []), ...(providerOptions.includeInbounds ? [runInboundsCheck] : []),
runNftCheck, runNftCheck,
...(providerOptions.includeZapret ? [runZapretCheck] : []), ...(providerOptions.includeZapret ? [runZapretCheck] : []),
...(providerOptions.includeZapret2 ? [runZapret2Check] : []),
...(providerOptions.includeByedpi ? [runByedpiCheck] : []), ...(providerOptions.includeByedpi ? [runByedpiCheck] : []),
runSectionsCheck, runSectionsCheck,
runFakeIPCheck, runFakeIPCheck,
@@ -0,0 +1,38 @@
import { describe, expect, it } from 'vitest';
import { DIAGNOSTICS_CHECKS } from '../checks/contstants';
import { getDiagnosticsChecks } from '../diagnostic.store';
describe('diagnostic store provider checks', () => {
it('adds Zapret2 as a separate provider check between Zapret and ByeDPI', () => {
const checks = getDiagnosticsChecks('Pending', {
includeZapret: true,
includeZapret2: true,
includeByedpi: true,
includeInbounds: false,
});
expect(checks.map((check) => check.code)).toEqual([
DIAGNOSTICS_CHECKS.DNS,
DIAGNOSTICS_CHECKS.SINGBOX,
DIAGNOSTICS_CHECKS.NFT,
DIAGNOSTICS_CHECKS.ZAPRET,
DIAGNOSTICS_CHECKS.ZAPRET2,
DIAGNOSTICS_CHECKS.BYEDPI,
DIAGNOSTICS_CHECKS.OUTBOUNDS,
DIAGNOSTICS_CHECKS.FAKEIP,
]);
});
it('keeps Zapret2 hidden when the provider is not available', () => {
const checks = getDiagnosticsChecks('Pending', {
includeZapret: true,
includeZapret2: false,
includeByedpi: true,
includeInbounds: false,
});
expect(checks.map((check) => check.code)).not.toContain(
DIAGNOSTICS_CHECKS.ZAPRET2,
);
});
});
@@ -14,8 +14,7 @@ import { ensureSystemInfo } from '../../services/systemInfo.service';
import { Podkop } from '../../types'; import { Podkop } from '../../types';
type UpdatesActionKey = keyof StoreType['updatesActions']; type UpdatesActionKey = keyof StoreType['updatesActions'];
type UpdateStatus = type UpdateStatus = StoreType['updatesChecks'][Podkop.ComponentName]['status'];
StoreType['updatesChecks'][Podkop.ComponentName]['status'];
interface ComponentActionButton { interface ComponentActionButton {
key: UpdatesActionKey; key: UpdatesActionKey;
@@ -138,7 +137,9 @@ function getExpectedLatestVersionForAction(button: ComponentActionButton) {
return undefined; return undefined;
} }
return store.get().updatesChecks[button.component].latest_version || undefined; return (
store.get().updatesChecks[button.component].latest_version || undefined
);
} }
function getCheckToastMessage(status: UpdateStatus) { function getCheckToastMessage(status: UpdateStatus) {
@@ -168,6 +169,7 @@ function notifyActionProvidersAvailabilityChanged(
new CustomEvent(PODKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT, { new CustomEvent(PODKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT, {
detail: { detail: {
zapretInstalled: Boolean(systemInfo.zapret_installed), zapretInstalled: Boolean(systemInfo.zapret_installed),
zapret2Installed: Boolean(systemInfo.zapret2_installed),
byedpiInstalled: Boolean(systemInfo.byedpi_installed), byedpiInstalled: Boolean(systemInfo.byedpi_installed),
}, },
}), }),
@@ -214,6 +216,18 @@ function patchSystemInfoAfterMutation(result: Podkop.ComponentActionResult) {
} }
} }
if (result.component === 'zapret2') {
nextSystemInfo.providerInfoLoaded = true;
if (result.action === 'remove') {
nextSystemInfo.zapret2_installed = 0;
nextSystemInfo.zapret2_version = 'not installed';
} else {
nextSystemInfo.zapret2_installed = 1;
nextSystemInfo.zapret2_version = version;
}
}
if (result.component === 'byedpi') { if (result.component === 'byedpi') {
nextSystemInfo.providerInfoLoaded = true; nextSystemInfo.providerInfoLoaded = true;
@@ -230,7 +244,11 @@ function patchSystemInfoAfterMutation(result: Podkop.ComponentActionResult) {
diagnosticsSystemInfo: nextSystemInfo, diagnosticsSystemInfo: nextSystemInfo,
}); });
if (result.component === 'zapret' || result.component === 'byedpi') { if (
result.component === 'zapret' ||
result.component === 'zapret2' ||
result.component === 'byedpi'
) {
notifyActionProvidersAvailabilityChanged(nextSystemInfo); notifyActionProvidersAvailabilityChanged(nextSystemInfo);
} }
} }
@@ -332,6 +350,7 @@ function getComponentCards(): ComponentCard[] {
const systemInfo = store.get().diagnosticsSystemInfo; const systemInfo = store.get().diagnosticsSystemInfo;
const systemInfoLoading = isSystemInfoLoading(); const systemInfoLoading = isSystemInfoLoading();
const zapretInstalled = Boolean(systemInfo.zapret_installed); const zapretInstalled = Boolean(systemInfo.zapret_installed);
const zapret2Installed = Boolean(systemInfo.zapret2_installed);
const byedpiInstalled = Boolean(systemInfo.byedpi_installed); const byedpiInstalled = Boolean(systemInfo.byedpi_installed);
const singBoxInstallAction: ComponentActionButton = const singBoxInstallAction: ComponentActionButton =
systemInfo.sing_box_extended systemInfo.sing_box_extended
@@ -368,11 +387,7 @@ function getComponentCards(): ComponentCard[] {
releaseUrl: getGitHubReleaseUrl('sing_box'), releaseUrl: getGitHubReleaseUrl('sing_box'),
tag: getCheckTag('sing_box'), tag: getCheckTag('sing_box'),
actions: [ actions: [
getPrimaryUpdateAction( getPrimaryUpdateAction('sing_box', 'singBoxCheck', 'singBoxInstall'),
'sing_box',
'singBoxCheck',
'singBoxInstall',
),
singBoxInstallAction, singBoxInstallAction,
], ],
}, },
@@ -406,6 +421,36 @@ function getComponentCards(): ComponentCard[] {
}, },
], ],
}, },
{
title: 'Zapret2',
version: systemInfoLoading
? 'loading'
: zapret2Installed
? systemInfo.zapret2_version
: _('Not installed'),
releaseUrl: getGitHubReleaseUrl('zapret2'),
tag: zapret2Installed ? getCheckTag('zapret2') : undefined,
actions: zapret2Installed
? [
getPrimaryUpdateAction('zapret2', 'zapret2Check', 'zapret2Install'),
{
key: 'zapret2Remove',
text: _('Remove'),
icon: renderXIcon24,
component: 'zapret2',
action: 'remove',
},
]
: [
{
key: 'zapret2Install',
text: _('Install'),
icon: renderRotateCcwIcon24,
component: 'zapret2',
action: 'install',
},
],
},
{ {
title: 'ByeDPI', title: 'ByeDPI',
version: systemInfoLoading version: systemInfoLoading
@@ -503,11 +548,7 @@ function renderComponentCard(card: ComponentCard) {
} }
return E('div', { class: 'pdk_updates-page__component' }, [ return E('div', { class: 'pdk_updates-page__component' }, [
E( E('div', { class: 'pdk_updates-page__component__header' }, headerChildren),
'div',
{ class: 'pdk_updates-page__component__header' },
headerChildren,
),
E('div', { class: 'pdk_updates-page__component__version' }, [ E('div', { class: 'pdk_updates-page__component__version' }, [
E( E(
'span', 'span',
+47 -1
View File
@@ -56,6 +56,7 @@ export namespace Podkop {
CHECK_FAKEIP = 'check_fakeip', CHECK_FAKEIP = 'check_fakeip',
CHECK_NFT_RULES = 'check_nft_rules', CHECK_NFT_RULES = 'check_nft_rules',
CHECK_ZAPRET_RUNTIME = 'check_zapret_runtime', CHECK_ZAPRET_RUNTIME = 'check_zapret_runtime',
CHECK_ZAPRET2_RUNTIME = 'check_zapret2_runtime',
CHECK_BYEDPI_RUNTIME = 'check_byedpi_runtime', CHECK_BYEDPI_RUNTIME = 'check_byedpi_runtime',
CHECK_INBOUNDS_CONFIG = 'check_inbounds_config', CHECK_INBOUNDS_CONFIG = 'check_inbounds_config',
GET_STATUS = 'get_status', GET_STATUS = 'get_status',
@@ -67,6 +68,7 @@ export namespace Podkop {
CHECK_INBOUNDS = 'check_inbounds', CHECK_INBOUNDS = 'check_inbounds',
GET_SING_BOX_STATUS = 'get_sing_box_status', GET_SING_BOX_STATUS = 'get_sing_box_status',
GET_ZAPRET_STATUS = 'get_zapret_status', GET_ZAPRET_STATUS = 'get_zapret_status',
GET_ZAPRET2_STATUS = 'get_zapret2_status',
GET_BYEDPI_STATUS = 'get_byedpi_status', GET_BYEDPI_STATUS = 'get_byedpi_status',
CLASH_API = 'clash_api', CLASH_API = 'clash_api',
RESTART = 'restart', RESTART = 'restart',
@@ -153,6 +155,7 @@ export namespace Podkop {
| 'direct' | 'direct'
| 'block' | 'block'
| 'zapret' | 'zapret'
| 'zapret2'
| 'byedpi'; | 'byedpi';
type LegacyConnectionType = 'proxy' | 'vpn' | 'block' | 'exclusion'; type LegacyConnectionType = 'proxy' | 'vpn' | 'block' | 'exclusion';
type ProxyConfigType = type ProxyConfigType =
@@ -179,6 +182,7 @@ export namespace Podkop {
update_interval?: string; update_interval?: string;
proxy_string?: string; proxy_string?: string;
nfqws_opt?: string; nfqws_opt?: string;
nfqws2_opt?: string;
byedpi_cmd_opts?: string; byedpi_cmd_opts?: string;
cmd_opts?: string; cmd_opts?: string;
selector_proxy_links?: string[]; selector_proxy_links?: string[];
@@ -326,6 +330,8 @@ export namespace Podkop {
sing_box_extended: 0 | 1; sing_box_extended: 0 | 1;
zapret_version: string; zapret_version: string;
zapret_installed: 0 | 1; zapret_installed: 0 | 1;
zapret2_version: string;
zapret2_installed: 0 | 1;
byedpi_version: string; byedpi_version: string;
byedpi_installed: 0 | 1; byedpi_installed: 0 | 1;
openwrt_version: string; openwrt_version: string;
@@ -340,11 +346,17 @@ export namespace Podkop {
export interface GetUiCapabilities { export interface GetUiCapabilities {
sing_box_extended: 0 | 1; sing_box_extended: 0 | 1;
zapret_installed: 0 | 1; zapret_installed: 0 | 1;
zapret2_installed: 0 | 1;
byedpi_installed: 0 | 1; byedpi_installed: 0 | 1;
server_inbounds_enabled_count: number; server_inbounds_enabled_count: number;
} }
export type ComponentName = 'podkop' | 'sing_box' | 'zapret' | 'byedpi'; export type ComponentName =
| 'podkop'
| 'sing_box'
| 'zapret'
| 'zapret2'
| 'byedpi';
export type ComponentAction = export type ComponentAction =
| 'check_update' | 'check_update'
@@ -422,6 +434,40 @@ export namespace Podkop {
zapret_provider_path: string; zapret_provider_path: string;
} }
export interface GetZapret2Status {
installed: 0 | 1;
package_installed: 0 | 1;
provider_available: 0 | 1;
provider_path: string;
files_available: 0 | 1;
ipset_available: 0 | 1;
version: string;
configured: 0 | 1;
enabled_rule_count: number;
expected_process_count: number;
running_process_count: number;
supervisor_process_count: number;
standalone_service_enabled: 0 | 1;
standalone_service_running: 0 | 1;
standalone_config_present: 0 | 1;
standalone_conflict: 0 | 1;
luci_app_installed: 0 | 1;
queue_base: number;
queue_range_end: number;
queue_overlap: 0 | 1;
ready: 0 | 1;
conflict: 0 | 1;
outbounds_configured: 0 | 1;
routes_configured: 0 | 1;
status_message: string;
}
export interface Zapret2CheckResult {
zapret2_installed: 0 | 1;
zapret2_package_installed: 0 | 1;
zapret2_provider_path: string;
}
export interface GetByedpiStatus { export interface GetByedpiStatus {
installed: 0 | 1; installed: 0 | 1;
package_installed: 0 | 1; package_installed: 0 | 1;
@@ -549,6 +549,12 @@ function validateHysteria2Url(url) {
message: _("Invalid HY2 URL: sni cannot be empty") message: _("Invalid HY2 URL: sni cannot be empty")
}; };
} }
if (paramsKeys.includes("mport") && (!params.mport || !params.mport.split(",").every(isValidPortEntry))) {
return {
valid: false,
message: _("Invalid HY2 URL: invalid port number")
};
}
} }
return { valid: true, message: _("Valid") }; return { valid: true, message: _("Valid") };
} catch (_e) { } catch (_e) {
@@ -2074,6 +2080,7 @@ var Podkop;
AvailableMethods2["CHECK_FAKEIP"] = "check_fakeip"; AvailableMethods2["CHECK_FAKEIP"] = "check_fakeip";
AvailableMethods2["CHECK_NFT_RULES"] = "check_nft_rules"; AvailableMethods2["CHECK_NFT_RULES"] = "check_nft_rules";
AvailableMethods2["CHECK_ZAPRET_RUNTIME"] = "check_zapret_runtime"; AvailableMethods2["CHECK_ZAPRET_RUNTIME"] = "check_zapret_runtime";
AvailableMethods2["CHECK_ZAPRET2_RUNTIME"] = "check_zapret2_runtime";
AvailableMethods2["CHECK_BYEDPI_RUNTIME"] = "check_byedpi_runtime"; AvailableMethods2["CHECK_BYEDPI_RUNTIME"] = "check_byedpi_runtime";
AvailableMethods2["CHECK_INBOUNDS_CONFIG"] = "check_inbounds_config"; AvailableMethods2["CHECK_INBOUNDS_CONFIG"] = "check_inbounds_config";
AvailableMethods2["GET_STATUS"] = "get_status"; AvailableMethods2["GET_STATUS"] = "get_status";
@@ -2085,6 +2092,7 @@ var Podkop;
AvailableMethods2["CHECK_INBOUNDS"] = "check_inbounds"; AvailableMethods2["CHECK_INBOUNDS"] = "check_inbounds";
AvailableMethods2["GET_SING_BOX_STATUS"] = "get_sing_box_status"; AvailableMethods2["GET_SING_BOX_STATUS"] = "get_sing_box_status";
AvailableMethods2["GET_ZAPRET_STATUS"] = "get_zapret_status"; AvailableMethods2["GET_ZAPRET_STATUS"] = "get_zapret_status";
AvailableMethods2["GET_ZAPRET2_STATUS"] = "get_zapret2_status";
AvailableMethods2["GET_BYEDPI_STATUS"] = "get_byedpi_status"; AvailableMethods2["GET_BYEDPI_STATUS"] = "get_byedpi_status";
AvailableMethods2["CLASH_API"] = "clash_api"; AvailableMethods2["CLASH_API"] = "clash_api";
AvailableMethods2["RESTART"] = "restart"; AvailableMethods2["RESTART"] = "restart";
@@ -2216,6 +2224,9 @@ var PodkopShellMethods = {
checkZapretRuntime: async () => callBaseMethod( checkZapretRuntime: async () => callBaseMethod(
Podkop.AvailableMethods.CHECK_ZAPRET_RUNTIME Podkop.AvailableMethods.CHECK_ZAPRET_RUNTIME
), ),
checkZapret2Runtime: async () => callBaseMethod(
Podkop.AvailableMethods.CHECK_ZAPRET2_RUNTIME
),
checkByedpiRuntime: async () => callBaseMethod( checkByedpiRuntime: async () => callBaseMethod(
Podkop.AvailableMethods.CHECK_BYEDPI_RUNTIME Podkop.AvailableMethods.CHECK_BYEDPI_RUNTIME
), ),
@@ -2251,6 +2262,9 @@ var PodkopShellMethods = {
getZapretStatus: async () => callBaseMethod( getZapretStatus: async () => callBaseMethod(
Podkop.AvailableMethods.GET_ZAPRET_STATUS Podkop.AvailableMethods.GET_ZAPRET_STATUS
), ),
getZapret2Status: async () => callBaseMethod(
Podkop.AvailableMethods.GET_ZAPRET2_STATUS
),
getByedpiStatus: async () => callBaseMethod( getByedpiStatus: async () => callBaseMethod(
Podkop.AvailableMethods.GET_BYEDPI_STATUS Podkop.AvailableMethods.GET_BYEDPI_STATUS
), ),
@@ -3020,17 +3034,22 @@ var DIAGNOSTICS_CHECKS_MAP = {
code: "ZAPRET" /* ZAPRET */ code: "ZAPRET" /* ZAPRET */
}, },
["BYEDPI" /* BYEDPI */]: { ["BYEDPI" /* BYEDPI */]: {
order: 6, order: 7,
title: getCheckTitle("ByeDPI"), title: getCheckTitle("ByeDPI"),
code: "BYEDPI" /* BYEDPI */ code: "BYEDPI" /* BYEDPI */
}, },
["ZAPRET2" /* ZAPRET2 */]: {
order: 6,
title: getCheckTitle("Zapret2"),
code: "ZAPRET2" /* ZAPRET2 */
},
["OUTBOUNDS" /* OUTBOUNDS */]: { ["OUTBOUNDS" /* OUTBOUNDS */]: {
order: 7, order: 8,
title: getCheckTitle("Outbounds"), title: getCheckTitle("Outbounds"),
code: "OUTBOUNDS" /* OUTBOUNDS */ code: "OUTBOUNDS" /* OUTBOUNDS */
}, },
["FAKEIP" /* FAKEIP */]: { ["FAKEIP" /* FAKEIP */]: {
order: 8, order: 9,
title: getCheckTitle("FakeIP"), title: getCheckTitle("FakeIP"),
code: "FAKEIP" /* FAKEIP */ code: "FAKEIP" /* FAKEIP */
}, },
@@ -3062,6 +3081,9 @@ function getDiagnosticsChecks(description, options = {}) {
if (options.includeZapret) { if (options.includeZapret) {
checks.push("ZAPRET" /* ZAPRET */); checks.push("ZAPRET" /* ZAPRET */);
} }
if (options.includeZapret2) {
checks.push("ZAPRET2" /* ZAPRET2 */);
}
if (options.includeByedpi) { if (options.includeByedpi) {
checks.push("BYEDPI" /* BYEDPI */); checks.push("BYEDPI" /* BYEDPI */);
} }
@@ -3085,6 +3107,8 @@ var initialDiagnosticStore = {
sing_box_extended: 0, sing_box_extended: 0,
zapret_version: "loading", zapret_version: "loading",
zapret_installed: 0, zapret_installed: 0,
zapret2_version: "loading",
zapret2_installed: 0,
byedpi_version: "loading", byedpi_version: "loading",
byedpi_installed: 0, byedpi_installed: 0,
server_inbounds_enabled_count: -1, server_inbounds_enabled_count: -1,
@@ -3129,6 +3153,9 @@ var initialDiagnosticStore = {
zapretCheck: { loading: false }, zapretCheck: { loading: false },
zapretInstall: { loading: false }, zapretInstall: { loading: false },
zapretRemove: { loading: false }, zapretRemove: { loading: false },
zapret2Check: { loading: false },
zapret2Install: { loading: false },
zapret2Remove: { loading: false },
byedpiCheck: { loading: false }, byedpiCheck: { loading: false },
byedpiInstall: { loading: false }, byedpiInstall: { loading: false },
byedpiRemove: { loading: false } byedpiRemove: { loading: false }
@@ -3137,6 +3164,7 @@ var initialDiagnosticStore = {
podkop: { status: null, latest_version: "", release_url: "" }, podkop: { status: null, latest_version: "", release_url: "" },
sing_box: { status: null, latest_version: "", release_url: "" }, sing_box: { status: null, latest_version: "", release_url: "" },
zapret: { status: null, latest_version: "", release_url: "" }, zapret: { status: null, latest_version: "", release_url: "" },
zapret2: { status: null, latest_version: "", release_url: "" },
byedpi: { status: null, latest_version: "", release_url: "" } byedpi: { status: null, latest_version: "", release_url: "" }
} }
}; };
@@ -5264,6 +5292,118 @@ async function runZapretCheck() {
}); });
} }
// src/podkop/tabs/diagnostic/checks/runZapret2Check.ts
async function runZapret2Check() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.ZAPRET2;
updateCheckStore({
order,
code,
title,
description: _("Checking, please wait"),
state: "loading",
items: []
});
const zapret2Status = await PodkopShellMethods.getZapret2Status();
if (!zapret2Status.success) {
updateCheckStore({
order,
code,
title,
description: _("Cannot receive checks result"),
state: "error",
items: []
});
throw new Error("Zapret2 checks failed");
}
const data = zapret2Status.data;
const providerAvailable = Boolean(data.provider_available ?? data.installed);
const packageInstalled = Boolean(data.package_installed);
const hasZapret2Rules = Number(data.enabled_rule_count || 0) > 0;
const ready = Boolean(data.ready);
const queueOverlap = Boolean(data.queue_overlap);
const expectedProcesses = Number(data.expected_process_count || 0);
const runningProcesses = Number(data.running_process_count || 0);
const supervisorProcesses = Number(data.supervisor_process_count || 0);
const podkopRuntimeReady = !hasZapret2Rules || runningProcesses === expectedProcesses && supervisorProcesses === expectedProcesses;
const unexpectedRuntime = !hasZapret2Rules && (runningProcesses > 0 || supervisorProcesses > 0);
const outboundsConfigured = Boolean(data.outbounds_configured);
const routesConfigured = Boolean(data.routes_configured);
const standaloneServiceEnabled = Boolean(data.standalone_service_enabled);
const standaloneServiceRunning = Boolean(data.standalone_service_running);
const standaloneConflict = hasZapret2Rules && standaloneServiceRunning;
const standaloneAutostartRisk = hasZapret2Rules && standaloneServiceEnabled && !standaloneServiceRunning;
let state = "success";
let description = _("Checks passed");
if (hasZapret2Rules && !providerAvailable) {
state = "error";
description = _("Checks failed");
} else if (hasZapret2Rules && !ready) {
state = "error";
description = _("Checks failed");
} else if (queueOverlap) {
state = "error";
description = _("Checks failed");
} else if (standaloneConflict) {
state = "error";
description = _("Checks failed");
} else if (standaloneAutostartRisk || !packageInstalled) {
state = "warning";
description = _("Issues detected");
}
const items = [
{
state: providerAvailable ? "success" : hasZapret2Rules ? "error" : "warning",
key: providerAvailable ? _("Zapret2 provider binary is available") : _("Zapret2 provider binary is not available"),
value: ""
},
{
state: packageInstalled ? "success" : hasZapret2Rules ? "error" : "warning",
key: packageInstalled ? _("Zapret2 package is installed") : _("Zapret2 package is not installed"),
value: ""
},
{
state: hasZapret2Rules && !providerAvailable ? "error" : "success",
key: hasZapret2Rules ? _("There are rules using Zapret2") : _("No rules use Zapret2"),
value: ""
},
{
state: unexpectedRuntime || !podkopRuntimeReady ? "error" : "success",
key: hasZapret2Rules ? podkopRuntimeReady ? _("Podkop Plus-managed nfqws2 runtime is ready") : _("Podkop Plus-managed nfqws2 runtime is not ready") : unexpectedRuntime ? _("Unexpected Podkop Plus-managed nfqws2 runtime is running") : _("Podkop Plus-managed nfqws2 runtime is not running"),
value: hasZapret2Rules ? `${runningProcesses}/${expectedProcesses}` : ""
},
{
state: queueOverlap ? "error" : "success",
key: queueOverlap ? _("NFQUEUE range overlaps with another rule") : _("NFQUEUE range is available"),
value: `${Number(data.queue_base || 0)}-${Number(data.queue_range_end || 0)}`
},
{
state: !hasZapret2Rules || outboundsConfigured ? "success" : "error",
key: outboundsConfigured ? _("Zapret2 sing-box outbound is configured") : _("Zapret2 sing-box outbound is not configured"),
value: ""
},
{
state: !hasZapret2Rules || routesConfigured ? "success" : "error",
key: routesConfigured ? _("Zapret2 route rules are configured") : _("Zapret2 route rules are not configured"),
value: ""
},
{
state: standaloneConflict ? "error" : standaloneAutostartRisk ? "warning" : "success",
key: standaloneServiceRunning ? hasZapret2Rules ? _(
"Standalone Zapret2 is active together with Podkop Plus Zapret2 rules"
) : _("Standalone Zapret2 service is active") : standaloneAutostartRisk ? _("Standalone Zapret2 autostart is enabled") : _("Standalone Zapret2 service is inactive"),
value: ""
}
];
updateCheckStore({
order,
code,
title,
description,
state,
items
});
}
// src/podkop/tabs/diagnostic/checks/runByedpiCheck.ts // src/podkop/tabs/diagnostic/checks/runByedpiCheck.ts
async function runByedpiCheck() { async function runByedpiCheck() {
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.BYEDPI; const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.BYEDPI;
@@ -5387,6 +5527,8 @@ var UNKNOWN_SYSTEM_INFO = {
sing_box_extended: 0, sing_box_extended: 0,
zapret_version: _("unknown"), zapret_version: _("unknown"),
zapret_installed: 0, zapret_installed: 0,
zapret2_version: _("unknown"),
zapret2_installed: 0,
byedpi_version: _("unknown"), byedpi_version: _("unknown"),
byedpi_installed: 0, byedpi_installed: 0,
server_inbounds_enabled_count: -1, server_inbounds_enabled_count: -1,
@@ -5450,6 +5592,7 @@ async function ensureSystemInfo({
loaded: false, loaded: false,
providerInfoLoaded: latestSystemInfo.providerInfoLoaded, providerInfoLoaded: latestSystemInfo.providerInfoLoaded,
zapret_installed: latestSystemInfo.zapret_installed, zapret_installed: latestSystemInfo.zapret_installed,
zapret2_installed: latestSystemInfo.zapret2_installed,
byedpi_installed: latestSystemInfo.byedpi_installed, byedpi_installed: latestSystemInfo.byedpi_installed,
server_inbounds_enabled_count: latestSystemInfo.server_inbounds_enabled_count server_inbounds_enabled_count: latestSystemInfo.server_inbounds_enabled_count
}; };
@@ -6268,6 +6411,7 @@ function sleep2(ms) {
function getDiagnosticsProviderOptions(systemInfo = store.get().diagnosticsSystemInfo) { function getDiagnosticsProviderOptions(systemInfo = store.get().diagnosticsSystemInfo) {
return { return {
includeZapret: Boolean(systemInfo.zapret_installed), includeZapret: Boolean(systemInfo.zapret_installed),
includeZapret2: Boolean(systemInfo.zapret2_installed),
includeByedpi: Boolean(systemInfo.byedpi_installed), includeByedpi: Boolean(systemInfo.byedpi_installed),
includeInbounds: systemInfo.server_inbounds_enabled_count !== 0 includeInbounds: systemInfo.server_inbounds_enabled_count !== 0
}; };
@@ -6391,8 +6535,9 @@ async function fetchDiagnosticsProviderInfo({
} = {}) { } = {}) {
const requestId = ++latestProviderInfoRequestId; const requestId = ++latestProviderInfoRequestId;
try { try {
const [zapretRuntime, byedpiRuntime, inboundsConfig] = await Promise.all([ const [zapretRuntime, zapret2Runtime, byedpiRuntime, inboundsConfig] = await Promise.all([
PodkopShellMethods.checkZapretRuntime(), PodkopShellMethods.checkZapretRuntime(),
PodkopShellMethods.checkZapret2Runtime(),
PodkopShellMethods.checkByedpiRuntime(), PodkopShellMethods.checkByedpiRuntime(),
PodkopShellMethods.checkInboundsConfig() PodkopShellMethods.checkInboundsConfig()
]); ]);
@@ -6404,12 +6549,20 @@ async function fetchDiagnosticsProviderInfo({
...currentSystemInfo, ...currentSystemInfo,
providerInfoLoaded: true, providerInfoLoaded: true,
zapret_installed: zapretRuntime.success ? zapretRuntime.data.zapret_installed : currentSystemInfo.zapret_installed, zapret_installed: zapretRuntime.success ? zapretRuntime.data.zapret_installed : currentSystemInfo.zapret_installed,
zapret2_installed: zapret2Runtime.success ? zapret2Runtime.data.zapret2_installed : currentSystemInfo.zapret2_installed,
byedpi_installed: byedpiRuntime.success ? byedpiRuntime.data.byedpi_installed : currentSystemInfo.byedpi_installed, byedpi_installed: byedpiRuntime.success ? byedpiRuntime.data.byedpi_installed : currentSystemInfo.byedpi_installed,
server_inbounds_enabled_count: inboundsConfig.success ? inboundsConfig.data.enabled_count : -1 server_inbounds_enabled_count: inboundsConfig.success ? inboundsConfig.data.enabled_count : -1
}; };
if (!zapretRuntime.success) { if (!zapretRuntime.success) {
logger.error("[DIAGNOSTIC]", "fetchZapretRuntime failed", zapretRuntime); logger.error("[DIAGNOSTIC]", "fetchZapretRuntime failed", zapretRuntime);
} }
if (!zapret2Runtime.success) {
logger.error(
"[DIAGNOSTIC]",
"fetchZapret2Runtime failed",
zapret2Runtime
);
}
if (!byedpiRuntime.success) { if (!byedpiRuntime.success) {
logger.error("[DIAGNOSTIC]", "fetchByedpiRuntime failed", byedpiRuntime); logger.error("[DIAGNOSTIC]", "fetchByedpiRuntime failed", byedpiRuntime);
} }
@@ -6423,6 +6576,9 @@ async function fetchDiagnosticsProviderInfo({
if (!nextSystemInfo.zapret_installed) { if (!nextSystemInfo.zapret_installed) {
nextSystemInfo.zapret_version = "not installed"; nextSystemInfo.zapret_version = "not installed";
} }
if (!nextSystemInfo.zapret2_installed) {
nextSystemInfo.zapret2_version = "not installed";
}
if (!nextSystemInfo.byedpi_installed) { if (!nextSystemInfo.byedpi_installed) {
nextSystemInfo.byedpi_version = "not installed"; nextSystemInfo.byedpi_version = "not installed";
} }
@@ -6727,6 +6883,12 @@ function renderDiagnosticSystemInfoWidget() {
value: diagnosticsSystemInfo.zapret_version value: diagnosticsSystemInfo.zapret_version
}); });
} }
if (diagnosticsSystemInfo.zapret2_installed) {
items.push({
key: "Zapret2",
value: diagnosticsSystemInfo.zapret2_version
});
}
if (diagnosticsSystemInfo.byedpi_installed) { if (diagnosticsSystemInfo.byedpi_installed) {
items.push({ items.push({
key: "ByeDPI", key: "ByeDPI",
@@ -6781,6 +6943,7 @@ async function runChecks() {
...providerOptions.includeInbounds ? [runInboundsCheck] : [], ...providerOptions.includeInbounds ? [runInboundsCheck] : [],
runNftCheck, runNftCheck,
...providerOptions.includeZapret ? [runZapretCheck] : [], ...providerOptions.includeZapret ? [runZapretCheck] : [],
...providerOptions.includeZapret2 ? [runZapret2Check] : [],
...providerOptions.includeByedpi ? [runByedpiCheck] : [], ...providerOptions.includeByedpi ? [runByedpiCheck] : [],
runSectionsCheck, runSectionsCheck,
runFakeIPCheck runFakeIPCheck
@@ -8914,6 +9077,7 @@ function notifyActionProvidersAvailabilityChanged(systemInfo) {
new CustomEvent(PODKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT, { new CustomEvent(PODKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT, {
detail: { detail: {
zapretInstalled: Boolean(systemInfo.zapret_installed), zapretInstalled: Boolean(systemInfo.zapret_installed),
zapret2Installed: Boolean(systemInfo.zapret2_installed),
byedpiInstalled: Boolean(systemInfo.byedpi_installed) byedpiInstalled: Boolean(systemInfo.byedpi_installed)
} }
}) })
@@ -8950,6 +9114,16 @@ function patchSystemInfoAfterMutation(result) {
nextSystemInfo.zapret_version = version; nextSystemInfo.zapret_version = version;
} }
} }
if (result.component === "zapret2") {
nextSystemInfo.providerInfoLoaded = true;
if (result.action === "remove") {
nextSystemInfo.zapret2_installed = 0;
nextSystemInfo.zapret2_version = "not installed";
} else {
nextSystemInfo.zapret2_installed = 1;
nextSystemInfo.zapret2_version = version;
}
}
if (result.component === "byedpi") { if (result.component === "byedpi") {
nextSystemInfo.providerInfoLoaded = true; nextSystemInfo.providerInfoLoaded = true;
if (result.action === "remove") { if (result.action === "remove") {
@@ -8963,7 +9137,7 @@ function patchSystemInfoAfterMutation(result) {
store.set({ store.set({
diagnosticsSystemInfo: nextSystemInfo diagnosticsSystemInfo: nextSystemInfo
}); });
if (result.component === "zapret" || result.component === "byedpi") { if (result.component === "zapret" || result.component === "zapret2" || result.component === "byedpi") {
notifyActionProvidersAvailabilityChanged(nextSystemInfo); notifyActionProvidersAvailabilityChanged(nextSystemInfo);
} }
} }
@@ -9044,6 +9218,7 @@ function getComponentCards() {
const systemInfo = store.get().diagnosticsSystemInfo; const systemInfo = store.get().diagnosticsSystemInfo;
const systemInfoLoading = isSystemInfoLoading(); const systemInfoLoading = isSystemInfoLoading();
const zapretInstalled = Boolean(systemInfo.zapret_installed); const zapretInstalled = Boolean(systemInfo.zapret_installed);
const zapret2Installed = Boolean(systemInfo.zapret2_installed);
const byedpiInstalled = Boolean(systemInfo.byedpi_installed); const byedpiInstalled = Boolean(systemInfo.byedpi_installed);
const singBoxInstallAction = systemInfo.sing_box_extended ? { const singBoxInstallAction = systemInfo.sing_box_extended ? {
key: "singBoxInstallStable", key: "singBoxInstallStable",
@@ -9074,11 +9249,7 @@ function getComponentCards() {
releaseUrl: getGitHubReleaseUrl("sing_box"), releaseUrl: getGitHubReleaseUrl("sing_box"),
tag: getCheckTag("sing_box"), tag: getCheckTag("sing_box"),
actions: [ actions: [
getPrimaryUpdateAction( getPrimaryUpdateAction("sing_box", "singBoxCheck", "singBoxInstall"),
"sing_box",
"singBoxCheck",
"singBoxInstall"
),
singBoxInstallAction singBoxInstallAction
] ]
}, },
@@ -9106,6 +9277,30 @@ function getComponentCards() {
} }
] ]
}, },
{
title: "Zapret2",
version: systemInfoLoading ? "loading" : zapret2Installed ? systemInfo.zapret2_version : _("Not installed"),
releaseUrl: getGitHubReleaseUrl("zapret2"),
tag: zapret2Installed ? getCheckTag("zapret2") : void 0,
actions: zapret2Installed ? [
getPrimaryUpdateAction("zapret2", "zapret2Check", "zapret2Install"),
{
key: "zapret2Remove",
text: _("Remove"),
icon: renderXIcon24,
component: "zapret2",
action: "remove"
}
] : [
{
key: "zapret2Install",
text: _("Install"),
icon: renderRotateCcwIcon24,
component: "zapret2",
action: "install"
}
]
},
{ {
title: "ByeDPI", title: "ByeDPI",
version: systemInfoLoading ? "loading" : byedpiInstalled ? systemInfo.byedpi_version : _("Not installed"), version: systemInfoLoading ? "loading" : byedpiInstalled ? systemInfo.byedpi_version : _("Not installed"),
@@ -9184,11 +9379,7 @@ function renderComponentCard(card) {
); );
} }
return E("div", { class: "pdk_updates-page__component" }, [ return E("div", { class: "pdk_updates-page__component" }, [
E( E("div", { class: "pdk_updates-page__component__header" }, headerChildren),
"div",
{ class: "pdk_updates-page__component__header" },
headerChildren
),
E("div", { class: "pdk_updates-page__component__version" }, [ E("div", { class: "pdk_updates-page__component__version" }, [
E( E(
"span", "span",
@@ -105,6 +105,7 @@ const EntryPoint = {
loaded: false, loaded: false,
singBoxExtended: false, singBoxExtended: false,
zapretInstalled: false, zapretInstalled: false,
zapret2Installed: false,
byedpiInstalled: false, byedpiInstalled: false,
serverInboundsEnabledCount: -1, serverInboundsEnabledCount: -1,
}; };
@@ -121,6 +122,7 @@ const EntryPoint = {
new CustomEvent(main.PODKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT, { new CustomEvent(main.PODKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT, {
detail: { detail: {
zapretInstalled: uiCapabilities.zapretInstalled, zapretInstalled: uiCapabilities.zapretInstalled,
zapret2Installed: uiCapabilities.zapret2Installed,
byedpiInstalled: uiCapabilities.byedpiInstalled, byedpiInstalled: uiCapabilities.byedpiInstalled,
}, },
}), }),
@@ -135,12 +137,16 @@ const EntryPoint = {
providerInfoLoaded: true, providerInfoLoaded: true,
sing_box_extended: uiCapabilities.singBoxExtended ? 1 : 0, sing_box_extended: uiCapabilities.singBoxExtended ? 1 : 0,
zapret_installed: uiCapabilities.zapretInstalled ? 1 : 0, zapret_installed: uiCapabilities.zapretInstalled ? 1 : 0,
zapret2_installed: uiCapabilities.zapret2Installed ? 1 : 0,
byedpi_installed: uiCapabilities.byedpiInstalled ? 1 : 0, byedpi_installed: uiCapabilities.byedpiInstalled ? 1 : 0,
server_inbounds_enabled_count: server_inbounds_enabled_count:
uiCapabilities.serverInboundsEnabledCount, uiCapabilities.serverInboundsEnabledCount,
zapret_version: uiCapabilities.zapretInstalled zapret_version: uiCapabilities.zapretInstalled
? currentSystemInfo.zapret_version ? currentSystemInfo.zapret_version
: "not installed", : "not installed",
zapret2_version: uiCapabilities.zapret2Installed
? currentSystemInfo.zapret2_version
: "not installed",
byedpi_version: uiCapabilities.byedpiInstalled byedpi_version: uiCapabilities.byedpiInstalled
? currentSystemInfo.byedpi_version ? currentSystemInfo.byedpi_version
: "not installed", : "not installed",
@@ -157,6 +163,9 @@ const EntryPoint = {
uiCapabilities.zapretInstalled = Boolean( uiCapabilities.zapretInstalled = Boolean(
Number(data?.zapret_installed) === 1, Number(data?.zapret_installed) === 1,
); );
uiCapabilities.zapret2Installed = Boolean(
Number(data?.zapret2_installed) === 1,
);
uiCapabilities.byedpiInstalled = Boolean( uiCapabilities.byedpiInstalled = Boolean(
Number(data?.byedpi_installed) === 1, Number(data?.byedpi_installed) === 1,
); );
@@ -179,12 +188,14 @@ const EntryPoint = {
return Promise.allSettled([ return Promise.allSettled([
main.PodkopShellMethods.getServerCapabilities(), main.PodkopShellMethods.getServerCapabilities(),
main.PodkopShellMethods.checkZapretRuntime(), main.PodkopShellMethods.checkZapretRuntime(),
main.PodkopShellMethods.checkZapret2Runtime(),
main.PodkopShellMethods.checkByedpiRuntime(), main.PodkopShellMethods.checkByedpiRuntime(),
main.PodkopShellMethods.checkInboundsConfig(), main.PodkopShellMethods.checkInboundsConfig(),
]).then( ]).then(
([ ([
serverCapabilitiesResult, serverCapabilitiesResult,
zapretRuntimeResult, zapretRuntimeResult,
zapret2RuntimeResult,
byedpiRuntimeResult, byedpiRuntimeResult,
inboundsConfigResult, inboundsConfigResult,
]) => { ]) => {
@@ -196,6 +207,10 @@ const EntryPoint = {
zapretRuntimeResult.status === "fulfilled" zapretRuntimeResult.status === "fulfilled"
? zapretRuntimeResult.value ? zapretRuntimeResult.value
: null; : null;
const zapret2Runtime =
zapret2RuntimeResult.status === "fulfilled"
? zapret2RuntimeResult.value
: null;
const byedpiRuntime = const byedpiRuntime =
byedpiRuntimeResult.status === "fulfilled" byedpiRuntimeResult.status === "fulfilled"
? byedpiRuntimeResult.value ? byedpiRuntimeResult.value
@@ -216,6 +231,11 @@ const EntryPoint = {
Number(zapretRuntime.data?.zapret_installed) === 1 Number(zapretRuntime.data?.zapret_installed) === 1
? 1 ? 1
: 0, : 0,
zapret2_installed:
zapret2Runtime?.success &&
Number(zapret2Runtime.data?.zapret2_installed) === 1
? 1
: 0,
byedpi_installed: byedpi_installed:
byedpiRuntime?.success && byedpiRuntime?.success &&
Number(byedpiRuntime.data?.byedpi_installed) === 1 Number(byedpiRuntime.data?.byedpi_installed) === 1
@@ -49,12 +49,17 @@ const ZAPRET_LEGACY_DEFAULT_NFQWS_OPT =
const ZAPRET_DEFAULT_NFQWS_OPT = const ZAPRET_DEFAULT_NFQWS_OPT =
"--filter-tcp=80 --dpi-desync=fake,fakedsplit --dpi-desync-autottl=2 --dpi-desync-fooling=badsum --new --filter-tcp=443 --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-repeats=11 --dpi-desync-fooling=badsum --dpi-desync-fake-tls-mod=rnd,dupsid,sni=www.google.com --new --filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=11 --dpi-desync-fake-quic=/opt/zapret/files/fake/quic_initial_www_google_com.bin"; "--filter-tcp=80 --dpi-desync=fake,fakedsplit --dpi-desync-autottl=2 --dpi-desync-fooling=badsum --new --filter-tcp=443 --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-repeats=11 --dpi-desync-fooling=badsum --dpi-desync-fake-tls-mod=rnd,dupsid,sni=www.google.com --new --filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=11 --dpi-desync-fake-quic=/opt/zapret/files/fake/quic_initial_www_google_com.bin";
const ZAPRET2_DEFAULT_NFQWS2_OPT =
"--filter-tcp=80 --filter-l7=http --payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5 --lua-desync=multisplit:pos=method+2 --new --filter-tcp=443 --filter-l7=tls --payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tcp_seq=-10000 --lua-desync=multidisorder:pos=1,midsld --new --filter-udp=443 --filter-l7=quic --payload=quic_initial --lua-desync=fake:blob=fake_default_quic:repeats=6";
const BYEDPI_DEFAULT_CMD_OPTS = "-o 2 --auto=t,r,a,s -d 2"; const BYEDPI_DEFAULT_CMD_OPTS = "-o 2 --auto=t,r,a,s -d 2";
const ANNOTATED_TEXTAREA_STYLE_ID = "pdk-annotated-textarea-styles"; const ANNOTATED_TEXTAREA_STYLE_ID = "pdk-annotated-textarea-styles";
const NFQWS_REMOTE_VALIDATION_DEBOUNCE_MS = 500; const NFQWS_REMOTE_VALIDATION_DEBOUNCE_MS = 500;
const NFQWS_VALIDATION_COMMAND = "/usr/bin/podkop-plus"; const NFQWS_VALIDATION_COMMAND = "/usr/bin/podkop-plus";
const nfqwsRemoteValidationCache = new Map(); const nfqwsRemoteValidationCache = new Map();
const nfqwsRemoteValidationInflight = new Map(); const nfqwsRemoteValidationInflight = new Map();
const nfqws2RemoteValidationCache = new Map();
const nfqws2RemoteValidationInflight = new Map();
const byedpiRemoteValidationCache = new Map(); const byedpiRemoteValidationCache = new Map();
const byedpiRemoteValidationInflight = new Map(); const byedpiRemoteValidationInflight = new Map();
const BYEDPI_LONG_VALUE_OPTIONS = new Set([ const BYEDPI_LONG_VALUE_OPTIONS = new Set([
@@ -245,9 +250,70 @@ const NFQWS_REQUIRED_ARG_OPTIONS = new Set([
"--wssize-cutoff", "--wssize-cutoff",
"--wssize-forced-cutoff", "--wssize-forced-cutoff",
]); ]);
const NFQWS2_OPTIONAL_ARG_OPTIONS = new Set([
"--chdir",
"--comment",
"--ctrack-disable",
"--debug",
"--hostlist-auto-retrans-reset",
"--intercept",
"--ipcache-hostname",
"--new",
"--payload-disable",
"--reasm-disable",
"--server",
"--template",
"--writeable",
]);
const NFQWS2_NO_ARG_OPTIONS = new Set([
"--bind-fix4",
"--bind-fix6",
"--daemon",
"--dry-run",
"--skip",
"--version",
]);
const NFQWS2_REQUIRED_ARG_OPTIONS = new Set([
"--blob",
"--cookie",
"--ctrack-timeouts",
"--filter-l3",
"--filter-l7",
"--filter-tcp",
"--filter-udp",
"--fwmark",
"--fuzz",
"--hostlist",
"--hostlist-auto",
"--hostlist-auto-debug",
"--hostlist-auto-fail-threshold",
"--hostlist-auto-fail-time",
"--hostlist-auto-retrans-threshold",
"--hostlist-domains",
"--hostlist-exclude",
"--hostlist-exclude-domains",
"--import",
"--in-range",
"--ipcache-lifetime",
"--ipset",
"--ipset-exclude",
"--ipset-exclude-ip",
"--ipset-ip",
"--lua-gc",
"--lua-init",
"--lua-desync",
"--name",
"--out-range",
"--payload",
"--pidfile",
"--qnum",
"--uid",
"--user",
]);
const actionProvidersAvailabilityState = { const actionProvidersAvailabilityState = {
loaded: false, loaded: false,
zapretInstalled: false, zapretInstalled: false,
zapret2Installed: false,
byedpiInstalled: false, byedpiInstalled: false,
}; };
let actionProvidersAvailabilityPromise = null; let actionProvidersAvailabilityPromise = null;
@@ -275,6 +341,12 @@ function updateActionProvidersAvailabilityState(nextState) {
); );
} }
if (typeof nextState.zapret2Installed !== "undefined") {
actionProvidersAvailabilityState.zapret2Installed = Boolean(
nextState.zapret2Installed,
);
}
if (typeof nextState.byedpiInstalled !== "undefined") { if (typeof nextState.byedpiInstalled !== "undefined") {
actionProvidersAvailabilityState.byedpiInstalled = Boolean( actionProvidersAvailabilityState.byedpiInstalled = Boolean(
nextState.byedpiInstalled, nextState.byedpiInstalled,
@@ -291,6 +363,7 @@ function updateActionProvidersAvailabilityFromSystemInfo(systemInfo) {
updateActionProvidersAvailabilityState({ updateActionProvidersAvailabilityState({
zapretInstalled: Boolean(systemInfo.zapret_installed), zapretInstalled: Boolean(systemInfo.zapret_installed),
zapret2Installed: Boolean(systemInfo.zapret2_installed),
byedpiInstalled: Boolean(systemInfo.byedpi_installed), byedpiInstalled: Boolean(systemInfo.byedpi_installed),
}); });
} }
@@ -468,6 +541,7 @@ function ensureActionProvidersAvailabilityLoaded() {
.then((capabilities) => { .then((capabilities) => {
updateActionProvidersAvailabilityState({ updateActionProvidersAvailabilityState({
zapretInstalled: Boolean(capabilities?.zapretInstalled), zapretInstalled: Boolean(capabilities?.zapretInstalled),
zapret2Installed: Boolean(capabilities?.zapret2Installed),
byedpiInstalled: Boolean(capabilities?.byedpiInstalled), byedpiInstalled: Boolean(capabilities?.byedpiInstalled),
}); });
return actionProvidersAvailabilityState; return actionProvidersAvailabilityState;
@@ -486,13 +560,18 @@ function ensureActionProvidersAvailabilityLoaded() {
actionProvidersAvailabilityPromise = Promise.allSettled([ actionProvidersAvailabilityPromise = Promise.allSettled([
main.PodkopShellMethods.checkZapretRuntime(), main.PodkopShellMethods.checkZapretRuntime(),
main.PodkopShellMethods.checkZapret2Runtime(),
main.PodkopShellMethods.checkByedpiRuntime(), main.PodkopShellMethods.checkByedpiRuntime(),
]) ])
.then(([zapretResult, byedpiResult]) => { .then(([zapretResult, zapret2Result, byedpiResult]) => {
const zapret = const zapret =
zapretResult && zapretResult.status === "fulfilled" zapretResult && zapretResult.status === "fulfilled"
? zapretResult.value ? zapretResult.value
: null; : null;
const zapret2 =
zapret2Result && zapret2Result.status === "fulfilled"
? zapret2Result.value
: null;
const byedpi = const byedpi =
byedpiResult && byedpiResult.status === "fulfilled" byedpiResult && byedpiResult.status === "fulfilled"
? byedpiResult.value ? byedpiResult.value
@@ -500,22 +579,23 @@ function ensureActionProvidersAvailabilityLoaded() {
actionProvidersAvailabilityState.loaded = true; actionProvidersAvailabilityState.loaded = true;
actionProvidersAvailabilityState.zapretInstalled = Boolean( actionProvidersAvailabilityState.zapretInstalled = Boolean(
zapret && zapret && zapret.success && zapret.data && zapret.data.zapret_installed,
zapret.success && );
zapret.data && actionProvidersAvailabilityState.zapret2Installed = Boolean(
zapret.data.zapret_installed, zapret2 &&
zapret2.success &&
zapret2.data &&
zapret2.data.zapret2_installed,
); );
actionProvidersAvailabilityState.byedpiInstalled = Boolean( actionProvidersAvailabilityState.byedpiInstalled = Boolean(
byedpi && byedpi && byedpi.success && byedpi.data && byedpi.data.byedpi_installed,
byedpi.success &&
byedpi.data &&
byedpi.data.byedpi_installed,
); );
return actionProvidersAvailabilityState; return actionProvidersAvailabilityState;
}) })
.catch(() => { .catch(() => {
actionProvidersAvailabilityState.loaded = true; actionProvidersAvailabilityState.loaded = true;
actionProvidersAvailabilityState.zapretInstalled = false; actionProvidersAvailabilityState.zapretInstalled = false;
actionProvidersAvailabilityState.zapret2Installed = false;
actionProvidersAvailabilityState.byedpiInstalled = false; actionProvidersAvailabilityState.byedpiInstalled = false;
return actionProvidersAvailabilityState; return actionProvidersAvailabilityState;
}) })
@@ -530,6 +610,10 @@ function isZapretInstalledForUi() {
return actionProvidersAvailabilityState.zapretInstalled; return actionProvidersAvailabilityState.zapretInstalled;
} }
function isZapret2InstalledForUi() {
return actionProvidersAvailabilityState.zapret2Installed;
}
function isByedpiInstalledForUi() { function isByedpiInstalledForUi() {
return actionProvidersAvailabilityState.byedpiInstalled; return actionProvidersAvailabilityState.byedpiInstalled;
} }
@@ -553,6 +637,8 @@ function getActionOptionLabel(action) {
return "VPN"; return "VPN";
case "zapret": case "zapret":
return "Zapret"; return "Zapret";
case "zapret2":
return "Zapret2";
case "byedpi": case "byedpi":
return "ByeDPI"; return "ByeDPI";
case "outbound": case "outbound":
@@ -570,6 +656,10 @@ function getRuleActionDisplayValue(section_id) {
return "Zapret"; return "Zapret";
} }
if (action === "zapret2") {
return "Zapret2";
}
if (action === "byedpi") { if (action === "byedpi") {
return "ByeDPI"; return "ByeDPI";
} }
@@ -592,6 +682,9 @@ function populateActionOptionValues(option) {
if (isZapretInstalledForUi()) { if (isZapretInstalledForUi()) {
option.value("zapret", getActionOptionLabel("zapret")); option.value("zapret", getActionOptionLabel("zapret"));
} }
if (isZapret2InstalledForUi()) {
option.value("zapret2", getActionOptionLabel("zapret2"));
}
if (isByedpiInstalledForUi()) { if (isByedpiInstalledForUi()) {
option.value("byedpi", getActionOptionLabel("byedpi")); option.value("byedpi", getActionOptionLabel("byedpi"));
} }
@@ -1237,6 +1330,54 @@ function attachNfqwsRemoteValidation(option, section_id, textarea) {
scheduleValidation(0); scheduleValidation(0);
} }
function attachNfqws2RemoteValidation(option, section_id, textarea) {
if (!textarea || textarea.__podkopNfqws2RemoteValidationAttached) {
return;
}
textarea.__podkopNfqws2RemoteValidationAttached = true;
textarea.__podkopNfqws2RemoteValidationRequestId = 0;
textarea.__podkopNfqws2RemoteValidationTimer = null;
const runValidation = () => {
const value = textarea.value;
const localAnalysis = buildNfqws2LocalAnalysis(value);
if (!localAnalysis.valid) {
refreshAnnotatedTextareaValidation(option, section_id, textarea);
return;
}
const requestId =
(textarea.__podkopNfqws2RemoteValidationRequestId || 0) + 1;
textarea.__podkopNfqws2RemoteValidationRequestId = requestId;
validateNfqws2StrategyRemotely(value).then(() => {
if (textarea.__podkopNfqws2RemoteValidationRequestId !== requestId) {
return;
}
refreshAnnotatedTextareaValidation(option, section_id, textarea);
});
};
const scheduleValidation = (delay = NFQWS_REMOTE_VALIDATION_DEBOUNCE_MS) => {
if (textarea.__podkopNfqws2RemoteValidationTimer) {
window.clearTimeout(textarea.__podkopNfqws2RemoteValidationTimer);
}
textarea.__podkopNfqws2RemoteValidationTimer = window.setTimeout(() => {
textarea.__podkopNfqws2RemoteValidationTimer = null;
runValidation();
}, delay);
};
textarea.addEventListener("input", () => scheduleValidation());
textarea.addEventListener("change", () => scheduleValidation(0));
textarea.addEventListener("blur", () => scheduleValidation(0));
scheduleValidation(0);
}
function parseCommentAwareListTokens(value) { function parseCommentAwareListTokens(value) {
const text = value ? `${value}` : ""; const text = value ? `${value}` : "";
const tokens = []; const tokens = [];
@@ -1392,6 +1533,22 @@ function getNfqwsOptionArgumentMode(option) {
return "unknown"; return "unknown";
} }
function getNfqws2OptionArgumentMode(option) {
if (NFQWS2_REQUIRED_ARG_OPTIONS.has(option)) {
return "required";
}
if (NFQWS2_OPTIONAL_ARG_OPTIONS.has(option)) {
return "optional";
}
if (NFQWS2_NO_ARG_OPTIONS.has(option)) {
return "none";
}
return "unknown";
}
function normalizeNfqwsStrategyWhitespace(value) { function normalizeNfqwsStrategyWhitespace(value) {
return value ? `${value}`.replace(/\s+/g, " ").trim() : ""; return value ? `${value}`.replace(/\s+/g, " ").trim() : "";
} }
@@ -1863,6 +2020,414 @@ function analyzeNfqwsStrategy(value) {
}; };
} }
function normalizeNfqws2StrategyValue(value) {
const normalized = normalizeNfqwsStrategyWhitespace(value);
return normalized.length ? normalized : ZAPRET2_DEFAULT_NFQWS2_OPT;
}
function getCachedNfqws2RemoteValidation(value) {
const normalized = normalizeNfqws2StrategyValue(value);
return normalized.length
? nfqws2RemoteValidationCache.get(normalized) || null
: null;
}
function cacheNfqws2RemoteValidation(value, result) {
const normalized = normalizeNfqws2StrategyValue(value);
if (!normalized.length) {
return result;
}
const cached = {
valid: result && result.valid === true,
message: result && result.message ? `${result.message}` : "",
needle: result && result.needle ? `${result.needle}` : "",
needles:
result && Array.isArray(result.needles)
? result.needles.filter(Boolean).map((item) => `${item}`)
: result && result.needle
? [`${result.needle}`]
: [],
};
nfqws2RemoteValidationCache.set(normalized, cached);
return cached;
}
function buildNfqws2RemoteValidationFallback(error) {
const message =
error && error.message
? `${error.message}`
: _("Unable to validate the NFQWS2 strategy through the backend parser.");
return {
valid: false,
message: _("Backend validation failed: %s").format(message),
needle: "",
needles: [],
};
}
function validateNfqws2StrategyRemotely(value) {
const normalized = normalizeNfqws2StrategyValue(value);
if (!normalized.length) {
return Promise.resolve({
valid: true,
message: "",
needle: "",
needles: [],
});
}
if (nfqws2RemoteValidationCache.has(normalized)) {
return Promise.resolve(nfqws2RemoteValidationCache.get(normalized));
}
if (nfqws2RemoteValidationInflight.has(normalized)) {
return nfqws2RemoteValidationInflight.get(normalized);
}
const validationTask = fs
.exec(NFQWS_VALIDATION_COMMAND, [
"validate_nfqws2_strategy_json",
normalized,
])
.then((result) => {
const payload = JSON.parse(
(result && result.stdout ? result.stdout : "{}").trim() || "{}",
);
return cacheNfqws2RemoteValidation(normalized, {
valid: payload.valid === true,
message: payload.message || "",
needle: payload.needle || "",
needles: Array.isArray(payload.needles)
? payload.needles.filter(Boolean)
: payload.needle
? [payload.needle]
: [],
});
})
.catch((error) =>
cacheNfqws2RemoteValidation(
normalized,
buildNfqws2RemoteValidationFallback(error),
),
)
.finally(() => {
nfqws2RemoteValidationInflight.delete(normalized);
});
nfqws2RemoteValidationInflight.set(normalized, validationTask);
return validationTask;
}
function getNfqws2ForbiddenTokenInfo(token, index, nextToken) {
const configFileMessage = _(
"External nfqws2 config files bypass Podkop Plus queue management and explicit validation.",
);
const hostSelectionMessage = _(
"Resource selection by hostname inside nfqws2 is not supported here; sing-box selects resources before NFQUEUE.",
);
const ipSelectionMessage = _(
"Resource selection by IP or CIDR inside nfqws2 is not supported here; sing-box selects resources before NFQUEUE.",
);
const placeholderMessage = _(
"Zapret2 hostlist templates are not supported here because Podkop Plus does not expand them for per-rule NFQWS2 strategies.",
);
const queueMessage = _(
"The NFQUEUE number is assigned by Podkop Plus for each rule and must not be overridden here.",
);
const fwmarkMessage = _(
"The desync fwmark is managed by Podkop Plus for loop prevention and must not be overridden here.",
);
const fuzzMessage = _(
"Fuzzing is not supported here because Podkop Plus needs deterministic runtime validation.",
);
const interceptMessage = _(
"Disabling interception is incompatible with action=zapret2 because Podkop Plus sends matched traffic through NFQUEUE.",
);
const daemonMessage = _(
"Podkop Plus manages the nfqws2 process lifecycle itself, so daemon mode is not allowed here.",
);
const dryRunMessage = _(
"This field must start a working nfqws2 strategy; --dry-run exits immediately and is not allowed here.",
);
const versionMessage = _(
"This field must start a working nfqws2 strategy; --version exits immediately and is not allowed here.",
);
if (index === 0 && (token.startsWith("@") || token.startsWith("$"))) {
return {
reason: configFileMessage,
captureNextValue: false,
};
}
if (token === "<HOSTLIST>" || token === "<HOSTLIST_NOAUTO>") {
return {
reason: placeholderMessage,
captureNextValue: false,
};
}
if (
token === "--hostlist" ||
token.startsWith("--hostlist=") ||
token === "--hostlist-domains" ||
token.startsWith("--hostlist-domains=") ||
token === "--hostlist-exclude" ||
token.startsWith("--hostlist-exclude=") ||
token === "--hostlist-exclude-domains" ||
token.startsWith("--hostlist-exclude-domains=") ||
token === "--hostlist-auto" ||
token.startsWith("--hostlist-auto=") ||
token === "--hostlist-auto-fail-threshold" ||
token.startsWith("--hostlist-auto-fail-threshold=") ||
token === "--hostlist-auto-fail-time" ||
token.startsWith("--hostlist-auto-fail-time=") ||
token === "--hostlist-auto-retrans-threshold" ||
token.startsWith("--hostlist-auto-retrans-threshold=") ||
token === "--hostlist-auto-debug" ||
token.startsWith("--hostlist-auto-debug=") ||
token === "--hostlist-auto-retrans-reset" ||
token.startsWith("--hostlist-auto-retrans-reset=")
) {
return {
reason: hostSelectionMessage,
captureNextValue: !token.includes("="),
};
}
if (
token === "--ipset" ||
token.startsWith("--ipset=") ||
token === "--ipset-ip" ||
token.startsWith("--ipset-ip=") ||
token === "--ipset-exclude" ||
token.startsWith("--ipset-exclude=") ||
token === "--ipset-exclude-ip" ||
token.startsWith("--ipset-exclude-ip=")
) {
return {
reason: ipSelectionMessage,
captureNextValue: !token.includes("="),
};
}
if (token === "--qnum" || token.startsWith("--qnum=")) {
return {
reason: queueMessage,
captureNextValue: !token.includes("="),
};
}
if (
token === "--fwmark" ||
token.startsWith("--fwmark=") ||
token === "--dpi-desync-fwmark" ||
token.startsWith("--dpi-desync-fwmark=")
) {
return {
reason: fwmarkMessage,
captureNextValue: !token.includes("="),
};
}
if (token === "--fuzz" || token.startsWith("--fuzz=")) {
return {
reason: fuzzMessage,
captureNextValue: !token.includes("="),
};
}
if (
token === "--intercept=0" ||
token === "--intercept=false" ||
token === "--intercept=no" ||
(token === "--intercept" && nextToken && nextToken.value === "0")
) {
return {
reason: interceptMessage,
captureNextValue: token === "--intercept",
};
}
if (token === "--daemon") {
return {
reason: daemonMessage,
captureNextValue: false,
};
}
if (token === "--dry-run") {
return {
reason: dryRunMessage,
captureNextValue: false,
};
}
if (token === "--version") {
return {
reason: versionMessage,
captureNextValue: false,
};
}
return null;
}
function buildNfqws2LocalAnalysis(value) {
const text = value ? `${value}` : "";
if (!text.trim().length) {
return {
valid: false,
message: _("NFQWS2 strategy cannot be empty"),
annotations: [],
};
}
const tokens = parseNfqwsRuntimeTokens(text);
const annotationMap = new Map();
const errors = [];
for (let index = 0; index < tokens.length; ) {
const token = tokens[index];
const bareToken = token.value.includes("=")
? token.value.slice(0, token.value.indexOf("="))
: token.value;
const nextToken = tokens[index + 1] || null;
const forbidden = getNfqws2ForbiddenTokenInfo(
token.value,
index,
nextToken,
);
if (forbidden) {
addAnnotationIssue(annotationMap, token, forbidden.reason);
let displayToken = token.value;
if (
forbidden.captureNextValue &&
nextToken &&
!nextToken.value.startsWith("--")
) {
addAnnotationIssue(annotationMap, nextToken, forbidden.reason);
displayToken = `${displayToken} ${nextToken.value}`;
index += 2;
} else {
index += 1;
}
errors.push(`${displayToken}: ${forbidden.reason}`);
continue;
}
if (!token.value.startsWith("--")) {
const reason = _(
"Unexpected standalone token. Use explicit flags such as --name or --name=value.",
);
addAnnotationIssue(annotationMap, token, reason);
errors.push(`${token.value}: ${reason}`);
index += 1;
continue;
}
const mode = getNfqws2OptionArgumentMode(bareToken);
if (mode === "unknown") {
const reason = _("Unknown NFQWS2 flag.");
addAnnotationIssue(annotationMap, token, reason);
errors.push(`${token.value}: ${reason}`);
index += 1;
continue;
}
if (mode === "none") {
if (token.value.includes("=")) {
const reason = _("This flag does not accept a value.");
addAnnotationIssue(annotationMap, token, reason);
errors.push(`${token.value}: ${reason}`);
}
index += 1;
continue;
}
if (mode === "optional") {
if (
nextToken &&
!token.value.includes("=") &&
!nextToken.value.startsWith("--")
) {
const reason = _(
"Optional values must be attached with '=' here; a separate token would be ignored by nfqws2.",
);
addAnnotationIssue(annotationMap, token, reason);
addAnnotationIssue(annotationMap, nextToken, reason);
errors.push(`${token.value} ${nextToken.value}: ${reason}`);
index += 2;
} else {
index += 1;
}
continue;
}
if (!token.value.includes("=")) {
if (!nextToken || nextToken.value.startsWith("--")) {
const reason = _("This option requires a value.");
addAnnotationIssue(annotationMap, token, reason);
errors.push(`${token.value}: ${reason}`);
index += 1;
continue;
}
index += 2;
continue;
}
index += 1;
}
if (!errors.length) {
return { valid: true, message: "", annotations: [] };
}
return {
valid: false,
message: [getValidationHeaderText(), ...errors].join("\n"),
annotations: finalizeAnnotations(annotationMap),
};
}
function analyzeNfqws2Strategy(value) {
const localAnalysis = buildNfqws2LocalAnalysis(value);
if (!localAnalysis.valid) {
return localAnalysis;
}
const remoteValidation = getCachedNfqws2RemoteValidation(value);
if (!remoteValidation || remoteValidation.valid) {
return localAnalysis;
}
const text = value ? `${value}` : "";
const tokens = parseNfqwsRuntimeTokens(text);
const annotationMap = new Map();
localAnalysis.annotations.forEach((annotation) =>
addAnnotationIssue(annotationMap, annotation, annotation.message),
);
addNfqwsRemoteValidationAnnotations(annotationMap, tokens, remoteValidation);
return {
valid: false,
message: [getValidationHeaderText(), remoteValidation.message].join("\n"),
annotations: finalizeAnnotations(annotationMap),
};
}
function normalizeByedpiStrategyWhitespace(value) { function normalizeByedpiStrategyWhitespace(value) {
return value ? `${value}`.replace(/\s+/g, " ").trim() : ""; return value ? `${value}`.replace(/\s+/g, " ").trim() : "";
} }
@@ -2259,7 +2824,7 @@ function analyzeByedpiStrategy(value) {
}; };
} }
function configureTextareaOption(option, analyzer) { function configureTextareaOption(option, analyzer, remoteValidationAttacher) {
const originalRenderWidget = option.renderWidget; const originalRenderWidget = option.renderWidget;
option.renderWidget = function (section_id, option_index, cfgvalue) { option.renderWidget = function (section_id, option_index, cfgvalue) {
@@ -2279,12 +2844,127 @@ function configureTextareaOption(option, analyzer) {
if (typeof analyzer === "function") { if (typeof analyzer === "function") {
attachAnnotatedTextarea(textarea, analyzer); attachAnnotatedTextarea(textarea, analyzer);
} }
if (typeof remoteValidationAttacher === "function") {
remoteValidationAttacher(this, section_id, textarea);
}
} }
return node; return node;
}; };
} }
function getOptionTextarea(option, section_id) {
const field =
typeof option.map.findElement === "function"
? option.map.findElement("data-field", option.cbid(section_id))
: null;
if (field && typeof field.querySelector === "function") {
return field.querySelector("textarea");
}
const elem =
typeof option.getUIElement === "function"
? option.getUIElement(section_id)
: null;
const node = elem && elem.node ? elem.node : null;
if (node && node.nodeName === "TEXTAREA") {
return node;
}
return node && typeof node.querySelector === "function"
? node.querySelector("textarea")
: null;
}
function rejectStrategyValidation(option, section_id, message) {
const title = option.stripTags(option.title).trim();
const error = message || option.getValidationError(section_id) || "";
return Promise.reject(
new TypeError(
`${_('Option "%s" contains an invalid input value.').format(title || option.option)} ${error}`,
),
);
}
function parseStrategyWithRemoteValidation(section_id, config) {
const active = this.isActive(section_id);
if (active) {
if (typeof this.triggerValidation === "function") {
this.triggerValidation(section_id);
}
if (!this.isValid(section_id)) {
return rejectStrategyValidation(
this,
section_id,
this.getValidationError(section_id),
);
}
const cval = this.cfgvalue(section_id);
const fval = this.formvalue(section_id);
const cvalString = cval == null ? "" : `${cval}`;
const fvalString = fval == null ? "" : `${fval}`;
const shouldWrite = this.forcewrite || cvalString !== fvalString;
if (!shouldWrite) {
return Promise.resolve();
}
return config.remoteValidate(fvalString).then((result) => {
const textarea = getOptionTextarea(this, section_id);
if (textarea) {
refreshAnnotatedTextareaValidation(this, section_id, textarea);
}
if (typeof this.triggerValidation === "function") {
this.triggerValidation(section_id);
}
if (!result || result.valid !== true) {
return rejectStrategyValidation(
this,
section_id,
result && result.message
? result.message
: config.invalidMessage,
);
}
return Promise.resolve(this.write(section_id, fvalString));
});
}
if (!this.retain) {
return Promise.resolve(this.remove(section_id));
}
return Promise.resolve();
}
function parseNfqwsStrategyOnSave(section_id) {
return parseStrategyWithRemoteValidation.call(this, section_id, {
remoteValidate: validateNfqwsStrategyRemotely,
invalidMessage: _(
"Unable to validate the NFQWS strategy through the backend parser.",
),
});
}
function parseNfqws2StrategyOnSave(section_id) {
return parseStrategyWithRemoteValidation.call(this, section_id, {
remoteValidate: validateNfqws2StrategyRemotely,
invalidMessage: _(
"Unable to validate the NFQWS2 strategy through the backend parser.",
),
});
}
function addDynamicConditionField(section, config) { function addDynamicConditionField(section, config) {
const o = section.taboption( const o = section.taboption(
"conditions", "conditions",
@@ -2593,13 +3273,69 @@ function createSectionContent(section) {
? ZAPRET_DEFAULT_NFQWS_OPT ? ZAPRET_DEFAULT_NFQWS_OPT
: normalized; : normalized;
uci.set(UCI_PACKAGE, section_id, "nfqws_opt", nextValue); return validateNfqwsStrategyRemotely(nextValue).then((result) => {
if (!result || result.valid !== true) {
throw new TypeError(
result && result.message
? result.message
: _(
"Unable to validate the NFQWS strategy through the backend parser.",
),
);
}
uci.set(UCI_PACKAGE, section_id, "nfqws_opt", nextValue);
});
}; };
o.validate = function (_section_id, value) { o.validate = function (_section_id, value) {
const analysis = analyzeNfqwsStrategy(value); const analysis = analyzeNfqwsStrategy(value);
return analysis.valid ? true : analysis.message; return analysis.valid ? true : analysis.message;
}; };
configureTextareaOption(o, analyzeNfqwsStrategy); o.parse = parseNfqwsStrategyOnSave;
configureTextareaOption(o, analyzeNfqwsStrategy, attachNfqwsRemoteValidation);
o = section.taboption(
"settings",
form.TextValue,
"nfqws2_opt",
_("NFQWS2 Strategy"),
);
o.depends("action", "zapret2");
o.rows = 6;
o.wrap = "soft";
o.textarea = true;
o.modalonly = true;
o.load = function (section_id) {
return (
uci.get(UCI_PACKAGE, section_id, "nfqws2_opt") ||
ZAPRET2_DEFAULT_NFQWS2_OPT
);
};
o.write = function (section_id, value) {
const normalized = normalizeNfqws2StrategyValue(value);
return validateNfqws2StrategyRemotely(normalized).then((result) => {
if (!result || result.valid !== true) {
throw new TypeError(
result && result.message
? result.message
: _("Invalid NFQWS2 strategy"),
);
}
uci.set(UCI_PACKAGE, section_id, "nfqws2_opt", normalized);
});
};
o.validate = function (_section_id, value) {
const analysis = analyzeNfqws2Strategy(value);
return analysis.valid ? true : analysis.message;
};
o.parse = parseNfqws2StrategyOnSave;
configureTextareaOption(
o,
analyzeNfqws2Strategy,
attachNfqws2RemoteValidation,
);
o = section.taboption( o = section.taboption(
"settings", "settings",
@@ -3130,6 +3866,7 @@ function createSectionContent(section) {
o.depends("action", "vpn"); o.depends("action", "vpn");
o.depends("action", "byedpi"); o.depends("action", "byedpi");
o.depends("action", "zapret"); o.depends("action", "zapret");
o.depends("action", "zapret2");
o.modalonly = true; o.modalonly = true;
o = section.taboption( o = section.taboption(
@@ -3145,6 +3882,7 @@ function createSectionContent(section) {
o.depends({ action: "vpn", mixed_proxy_enabled: "1" }); o.depends({ action: "vpn", mixed_proxy_enabled: "1" });
o.depends({ action: "byedpi", mixed_proxy_enabled: "1" }); o.depends({ action: "byedpi", mixed_proxy_enabled: "1" });
o.depends({ action: "zapret", mixed_proxy_enabled: "1" }); o.depends({ action: "zapret", mixed_proxy_enabled: "1" });
o.depends({ action: "zapret2", mixed_proxy_enabled: "1" });
o.modalonly = true; o.modalonly = true;
o.validate = function (_section_id, value) { o.validate = function (_section_id, value) {
if (!value || value.length === 0) { if (!value || value.length === 0) {
@@ -3173,6 +3911,7 @@ function createSectionContent(section) {
o.depends({ action: "vpn", mixed_proxy_enabled: "1" }); o.depends({ action: "vpn", mixed_proxy_enabled: "1" });
o.depends({ action: "byedpi", mixed_proxy_enabled: "1" }); o.depends({ action: "byedpi", mixed_proxy_enabled: "1" });
o.depends({ action: "zapret", mixed_proxy_enabled: "1" }); o.depends({ action: "zapret", mixed_proxy_enabled: "1" });
o.depends({ action: "zapret2", mixed_proxy_enabled: "1" });
o.modalonly = true; o.modalonly = true;
o = section.taboption( o = section.taboption(
@@ -3207,6 +3946,11 @@ function createSectionContent(section) {
mixed_proxy_enabled: "1", mixed_proxy_enabled: "1",
mixed_proxy_auth_enabled: "1", mixed_proxy_auth_enabled: "1",
}); });
o.depends({
action: "zapret2",
mixed_proxy_enabled: "1",
mixed_proxy_auth_enabled: "1",
});
o.modalonly = true; o.modalonly = true;
o.validate = function (_section_id, value) { o.validate = function (_section_id, value) {
if (!value || value.length === 0) { if (!value || value.length === 0) {
@@ -3248,6 +3992,11 @@ function createSectionContent(section) {
mixed_proxy_enabled: "1", mixed_proxy_enabled: "1",
mixed_proxy_auth_enabled: "1", mixed_proxy_auth_enabled: "1",
}); });
o.depends({
action: "zapret2",
mixed_proxy_enabled: "1",
mixed_proxy_auth_enabled: "1",
});
o.modalonly = true; o.modalonly = true;
o.validate = function (_section_id, value) { o.validate = function (_section_id, value) {
if (!value || value.length === 0) { if (!value || value.length === 0) {
@@ -15,6 +15,7 @@ const ROUTING_SECTION_ACTIONS = [
"vpn", "vpn",
"byedpi", "byedpi",
"zapret", "zapret",
"zapret2",
"direct", "direct",
"block", "block",
]; ];
+101 -2
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PODKOP PLUS\n" "Project-Id-Version: PODKOP PLUS\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-06-02 17:11+0300\n" "POT-Creation-Date: 2026-06-02 18:22+0300\n"
"PO-Revision-Date: 2026-06-02 17:11+0300\n" "PO-Revision-Date: 2026-06-02 18:22+0300\n"
"Last-Translator: ushan0v\n" "Last-Translator: ushan0v\n"
"Language-Team: none\n" "Language-Team: none\n"
"Language: ru\n" "Language: ru\n"
@@ -299,6 +299,9 @@ msgstr "Отключить QUIC"
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming" msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
msgstr "Отключить QUIC протокол для улучшения совместимости или исправления видео стриминга" msgstr "Отключить QUIC протокол для улучшения совместимости или исправления видео стриминга"
msgid "Disabling interception is incompatible with action=zapret2 because Podkop Plus sends matched traffic through NFQUEUE."
msgstr "Отключение перехвата несовместимо с action=zapret2, потому что Podkop Plus отправляет совпавший трафик через NFQUEUE."
msgid "DNS on router" msgid "DNS on router"
msgstr "DNS на роутере" msgstr "DNS на роутере"
@@ -443,6 +446,9 @@ msgstr "Открыть эту секцию как локальный HTTP+SOCKS
msgid "External nfqws config files bypass Podkop Plus queue management and explicit validation." msgid "External nfqws config files bypass Podkop Plus queue management and explicit validation."
msgstr "Внешние конфиги nfqws обходят управление очередями и явную валидацию Podkop Plus." msgstr "Внешние конфиги nfqws обходят управление очередями и явную валидацию Podkop Plus."
msgid "External nfqws2 config files bypass Podkop Plus queue management and explicit validation."
msgstr "Внешние конфиги nfqws2 обходят управление очередями и явную валидацию Podkop Plus."
msgid "Failed to close connection" msgid "Failed to close connection"
msgstr "Не удалось закрыть подключение" msgstr "Не удалось закрыть подключение"
@@ -485,6 +491,9 @@ msgstr "Proxy protocol для фронтинга"
msgid "Fully Routed IPs" msgid "Fully Routed IPs"
msgstr "Полностью маршрутизированные IP-адреса" msgstr "Полностью маршрутизированные IP-адреса"
msgid "Fuzzing is not supported here because Podkop Plus needs deterministic runtime validation."
msgstr "Fuzzing здесь не поддерживается, потому что Podkop Plus нужна предсказуемая runtime-валидация."
msgid "Generated inbound" msgid "Generated inbound"
msgstr "Созданный inbound" msgstr "Созданный inbound"
@@ -608,6 +617,9 @@ msgstr "Неверный IP-адрес"
msgid "Invalid JSON format" msgid "Invalid JSON format"
msgstr "Неверный формат JSON" msgstr "Неверный формат JSON"
msgid "Invalid NFQWS2 strategy"
msgstr "Некорректная стратегия NFQWS2"
msgid "Invalid path format. Path must start with \"/\" and contain valid characters" msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
msgstr "Неверный формат пути. Путь должен начинаться с \"/\" и содержать допустимые символы" msgstr "Неверный формат пути. Путь должен начинаться с \"/\" и содержать допустимые символы"
@@ -803,6 +815,12 @@ msgstr "Стратегия NFQWS"
msgid "NFQWS strategy cannot be empty" msgid "NFQWS strategy cannot be empty"
msgstr "Стратегия NFQWS не может быть пустой" msgstr "Стратегия NFQWS не может быть пустой"
msgid "NFQWS2 Strategy"
msgstr "Стратегия NFQWS2"
msgid "NFQWS2 strategy cannot be empty"
msgstr "Стратегия NFQWS2 не может быть пустой"
msgid "No active connections" msgid "No active connections"
msgstr "Нет активных подключений" msgstr "Нет активных подключений"
@@ -827,6 +845,9 @@ msgstr "Нет правил, использующих ByeDPI"
msgid "No rules use Zapret" msgid "No rules use Zapret"
msgstr "Нет правил, использующих Zapret" msgstr "Нет правил, использующих Zapret"
msgid "No rules use Zapret2"
msgstr "Нет правил, использующих Zapret2"
msgid "None" msgid "None"
msgstr "Нет" msgstr "Нет"
@@ -884,6 +905,9 @@ msgstr "Параметр \"%s\" содержит некорректное зна
msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws." msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws."
msgstr "Необязательное значение здесь нужно передавать только через '='; отдельный токен nfqws проигнорирует." msgstr "Необязательное значение здесь нужно передавать только через '='; отдельный токен nfqws проигнорирует."
msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws2."
msgstr "Необязательные значения здесь должны задаваться через '='; отдельный токен будет проигнорирован nfqws2."
msgid "Outbound JSON" msgid "Outbound JSON"
msgstr "JSON outbound" msgstr "JSON outbound"
@@ -938,6 +962,9 @@ msgstr "Podkop Plus сам управляет жизненным циклом ci
msgid "Podkop Plus manages the nfqws process lifecycle itself, so daemon mode is not allowed here." msgid "Podkop Plus manages the nfqws process lifecycle itself, so daemon mode is not allowed here."
msgstr "Podkop Plus сам управляет жизненным циклом nfqws, поэтому режим daemon здесь запрещён." msgstr "Podkop Plus сам управляет жизненным циклом nfqws, поэтому режим daemon здесь запрещён."
msgid "Podkop Plus manages the nfqws2 process lifecycle itself, so daemon mode is not allowed here."
msgstr "Podkop Plus сам управляет жизненным циклом nfqws2, поэтому режим daemon здесь запрещён."
msgid "Podkop Plus rules" msgid "Podkop Plus rules"
msgstr "Правила Podkop Plus" msgstr "Правила Podkop Plus"
@@ -968,6 +995,15 @@ msgstr "NFQWS Podkop Plus не запущен"
msgid "Podkop Plus-managed nfqws runtime is ready" msgid "Podkop Plus-managed nfqws runtime is ready"
msgstr "NFQWS Podkop Plus готов" msgstr "NFQWS Podkop Plus готов"
msgid "Podkop Plus-managed nfqws2 runtime is not ready"
msgstr "NFQWS2 Podkop Plus не готов"
msgid "Podkop Plus-managed nfqws2 runtime is not running"
msgstr "NFQWS2 Podkop Plus не запущен"
msgid "Podkop Plus-managed nfqws2 runtime is ready"
msgstr "NFQWS2 Podkop Plus готов"
msgid "Port cannot be empty" msgid "Port cannot be empty"
msgstr "Порт не может быть пустым" msgstr "Порт не может быть пустым"
@@ -1052,9 +1088,15 @@ msgstr "Определять страны серверов через country.is
msgid "Resource selection by hostname inside nfqws is not supported here; sing-box selects resources before NFQUEUE." msgid "Resource selection by hostname inside nfqws is not supported here; sing-box selects resources before NFQUEUE."
msgstr "Выбор ресурсов по hostname внутри nfqws здесь не поддерживается; ресурсы выбирает sing-box до NFQUEUE." msgstr "Выбор ресурсов по hostname внутри nfqws здесь не поддерживается; ресурсы выбирает sing-box до NFQUEUE."
msgid "Resource selection by hostname inside nfqws2 is not supported here; sing-box selects resources before NFQUEUE."
msgstr "Выбор ресурсов по hostname внутри nfqws2 здесь не поддерживается; sing-box выбирает ресурсы до NFQUEUE."
msgid "Resource selection by IP or CIDR inside nfqws is not supported here; sing-box selects resources before NFQUEUE." msgid "Resource selection by IP or CIDR inside nfqws is not supported here; sing-box selects resources before NFQUEUE."
msgstr "Выбор ресурсов по IP или CIDR внутри nfqws здесь не поддерживается; ресурсы выбирает sing-box до NFQUEUE." msgstr "Выбор ресурсов по IP или CIDR внутри nfqws здесь не поддерживается; ресурсы выбирает sing-box до NFQUEUE."
msgid "Resource selection by IP or CIDR inside nfqws2 is not supported here; sing-box selects resources before NFQUEUE."
msgstr "Выбор ресурсов по IP или CIDR внутри nfqws2 здесь не поддерживается; sing-box выбирает ресурсы до NFQUEUE."
msgid "Restart Podkop Plus" msgid "Restart Podkop Plus"
msgstr "Перезапустить Podkop Plus" msgstr "Перезапустить Podkop Plus"
@@ -1277,6 +1319,18 @@ msgstr "Отдельная служба Zapret активна"
msgid "Standalone Zapret service is inactive" msgid "Standalone Zapret service is inactive"
msgstr "Отдельная служба Zapret неактивна" msgstr "Отдельная служба Zapret неактивна"
msgid "Standalone Zapret2 autostart is enabled"
msgstr "Автозапуск отдельной службы Zapret2 включён"
msgid "Standalone Zapret2 is active together with Podkop Plus Zapret2 rules"
msgstr "Отдельная служба Zapret2 активна вместе с правилами Zapret2 в Podkop Plus"
msgid "Standalone Zapret2 service is active"
msgstr "Отдельная служба Zapret2 активна"
msgid "Standalone Zapret2 service is inactive"
msgstr "Отдельная служба Zapret2 неактивна"
msgid "Start Podkop Plus" msgid "Start Podkop Plus"
msgstr "Запустить Podkop Plus" msgstr "Запустить Podkop Plus"
@@ -1346,6 +1400,9 @@ msgstr "Есть правила, использующие ByeDPI"
msgid "There are rules using Zapret" msgid "There are rules using Zapret"
msgstr "Есть правила использующие Zapret" msgstr "Есть правила использующие Zapret"
msgid "There are rules using Zapret2"
msgstr "Есть правила, использующие Zapret2"
msgid "This field is required" msgid "This field is required"
msgstr "Заполните это поле" msgstr "Заполните это поле"
@@ -1358,6 +1415,12 @@ msgstr "Это поле должно запускать рабочую стра
msgid "This field must start a working nfqws strategy; --version exits immediately and is not allowed here." msgid "This field must start a working nfqws strategy; --version exits immediately and is not allowed here."
msgstr "Это поле должно запускать рабочую стратегию nfqws; --version сразу завершает процесс и здесь запрещён." msgstr "Это поле должно запускать рабочую стратегию nfqws; --version сразу завершает процесс и здесь запрещён."
msgid "This field must start a working nfqws2 strategy; --dry-run exits immediately and is not allowed here."
msgstr "Это поле должно запускать рабочую стратегию nfqws2; --dry-run сразу завершает работу и здесь запрещён."
msgid "This field must start a working nfqws2 strategy; --version exits immediately and is not allowed here."
msgstr "Это поле должно запускать рабочую стратегию nfqws2; --version сразу завершает работу и здесь запрещён."
msgid "This flag does not accept a value." msgid "This flag does not accept a value."
msgstr "Этот флаг не принимает значение." msgstr "Этот флаг не принимает значение."
@@ -1418,6 +1481,9 @@ msgstr "Не удалось проверить стратегию ByeDPI чер
msgid "Unable to validate the NFQWS strategy through the backend parser." msgid "Unable to validate the NFQWS strategy through the backend parser."
msgstr "Не удалось проверить стратегию NFQWS через backend-парсер." msgstr "Не удалось проверить стратегию NFQWS через backend-парсер."
msgid "Unable to validate the NFQWS2 strategy through the backend parser."
msgstr "Не удалось проверить стратегию NFQWS2 через backend-парсер."
msgid "Unexpected ByeDPI strategy argument: %s" msgid "Unexpected ByeDPI strategy argument: %s"
msgstr "Неожиданный аргумент стратегии ByeDPI: %s" msgstr "Неожиданный аргумент стратегии ByeDPI: %s"
@@ -1427,6 +1493,9 @@ msgstr "Обнаружен лишний ciadpi Podkop Plus"
msgid "Unexpected Podkop Plus-managed nfqws runtime is running" msgid "Unexpected Podkop Plus-managed nfqws runtime is running"
msgstr "Обнаружен лишний NFQWS Podkop Plus" msgstr "Обнаружен лишний NFQWS Podkop Plus"
msgid "Unexpected Podkop Plus-managed nfqws2 runtime is running"
msgstr "Обнаружен лишний NFQWS2 Podkop Plus"
msgid "Unexpected standalone token. Use explicit flags such as --name or --name=value." msgid "Unexpected standalone token. Use explicit flags such as --name or --name=value."
msgstr "Неожиданный отдельный токен. Используйте явные флаги вида --имя или --имя=значение." msgstr "Неожиданный отдельный токен. Используйте явные флаги вида --имя или --имя=значение."
@@ -1448,6 +1517,9 @@ msgstr "Неизвестная ошибка"
msgid "Unknown NFQWS flag." msgid "Unknown NFQWS flag."
msgstr "Неизвестный флаг NFQWS." msgstr "Неизвестный флаг NFQWS."
msgid "Unknown NFQWS2 flag."
msgstr "Неизвестный флаг NFQWS2."
msgid "Unsupported security mode for this protocol" msgid "Unsupported security mode for this protocol"
msgstr "Этот протокол не поддерживает выбранный режим защиты" msgstr "Этот протокол не поддерживает выбранный режим защиты"
@@ -1591,3 +1663,30 @@ msgstr "Бинарник провайдера Zapret доступен"
msgid "Zapret provider binary is not available" msgid "Zapret provider binary is not available"
msgstr "Бинарник провайдера Zapret недоступен" msgstr "Бинарник провайдера Zapret недоступен"
msgid "Zapret2 hostlist templates are not supported here because Podkop Plus does not expand them for per-rule NFQWS2 strategies."
msgstr "Шаблоны hostlist Zapret2 здесь не поддерживаются, потому что Podkop Plus не разворачивает их для per-rule стратегий NFQWS2."
msgid "Zapret2 package is installed"
msgstr "Пакет Zapret2 установлен"
msgid "Zapret2 package is not installed"
msgstr "Пакет Zapret2 не установлен"
msgid "Zapret2 provider binary is available"
msgstr "Бинарник провайдера Zapret2 доступен"
msgid "Zapret2 provider binary is not available"
msgstr "Бинарник провайдера Zapret2 недоступен"
msgid "Zapret2 route rules are configured"
msgstr "Правила маршрутизации Zapret2 настроены"
msgid "Zapret2 route rules are not configured"
msgstr "Правила маршрутизации Zapret2 не настроены"
msgid "Zapret2 sing-box outbound is configured"
msgstr "sing-box outbound для Zapret2 настроен"
msgid "Zapret2 sing-box outbound is not configured"
msgstr "sing-box outbound для Zapret2 не настроен"
File diff suppressed because it is too large Load Diff
+242 -19
View File
@@ -124,7 +124,7 @@ podkop_fast_ui_capabilities_server_handler() {
} }
podkop_fast_get_ui_capabilities() { podkop_fast_get_ui_capabilities() {
local sing_box_version sing_box_extended=0 zapret_installed=0 byedpi_installed=0 local sing_box_version sing_box_extended=0 zapret_installed=0 zapret2_installed=0 byedpi_installed=0
if command -v sing-box >/dev/null 2>&1; then if command -v sing-box >/dev/null 2>&1; then
sing_box_version="$(sing-box version 2>/dev/null | head -1 | awk '{print $NF}')" sing_box_version="$(sing-box version 2>/dev/null | head -1 | awk '{print $NF}')"
@@ -134,15 +134,17 @@ podkop_fast_get_ui_capabilities() {
fi fi
[ -x "$ZAPRET_PROVIDER_NFQWS_BIN" ] && zapret_installed=1 [ -x "$ZAPRET_PROVIDER_NFQWS_BIN" ] && zapret_installed=1
[ -x "$ZAPRET2_PROVIDER_NFQWS2_BIN" ] && zapret2_installed=1
[ -x "$BYEDPI_BIN" ] && byedpi_installed=1 [ -x "$BYEDPI_BIN" ] && byedpi_installed=1
PODKOP_FAST_SERVER_INBOUNDS_ENABLED_COUNT=0 PODKOP_FAST_SERVER_INBOUNDS_ENABLED_COUNT=0
config_load "$PODKOP_CONFIG_NAME" config_load "$PODKOP_CONFIG_NAME"
config_foreach podkop_fast_ui_capabilities_server_handler "server" config_foreach podkop_fast_ui_capabilities_server_handler "server"
printf '{ "sing_box_extended": %s, "zapret_installed": %s, "byedpi_installed": %s, "server_inbounds_enabled_count": %s }\n' \ printf '{ "sing_box_extended": %s, "zapret_installed": %s, "zapret2_installed": %s, "byedpi_installed": %s, "server_inbounds_enabled_count": %s }\n' \
"$sing_box_extended" \ "$sing_box_extended" \
"$zapret_installed" \ "$zapret_installed" \
"$zapret2_installed" \
"$byedpi_installed" \ "$byedpi_installed" \
"${PODKOP_FAST_SERVER_INBOUNDS_ENABLED_COUNT:-0}" "${PODKOP_FAST_SERVER_INBOUNDS_ENABLED_COUNT:-0}"
} }
@@ -160,6 +162,7 @@ check_required_file "$PODKOP_LIB/helpers.sh"
check_required_file "$PODKOP_LIB/json_utils.uc" check_required_file "$PODKOP_LIB/json_utils.uc"
check_required_file "$PODKOP_LIB/provider_status.uc" check_required_file "$PODKOP_LIB/provider_status.uc"
check_required_file "$PODKOP_LIB/zapret.sh" check_required_file "$PODKOP_LIB/zapret.sh"
check_required_file "$PODKOP_LIB/zapret2.sh"
check_required_file "$PODKOP_LIB/byedpi.sh" check_required_file "$PODKOP_LIB/byedpi.sh"
check_required_file "$PODKOP_LIB/sing_box_config_manager.sh" check_required_file "$PODKOP_LIB/sing_box_config_manager.sh"
check_required_file "$PODKOP_LIB/sing_box_config_manager.uc" check_required_file "$PODKOP_LIB/sing_box_config_manager.uc"
@@ -176,6 +179,7 @@ check_required_file "$PODKOP_LIB/updater.sh"
. "$PODKOP_LIB/nft.sh" . "$PODKOP_LIB/nft.sh"
. "$PODKOP_LIB/helpers.sh" . "$PODKOP_LIB/helpers.sh"
. "$PODKOP_LIB/zapret.sh" . "$PODKOP_LIB/zapret.sh"
. "$PODKOP_LIB/zapret2.sh"
. "$PODKOP_LIB/byedpi.sh" . "$PODKOP_LIB/byedpi.sh"
. "$PODKOP_LIB/sing_box_config_manager.sh" . "$PODKOP_LIB/sing_box_config_manager.sh"
. "$PODKOP_LIB/sing_box_config_facade.sh" . "$PODKOP_LIB/sing_box_config_facade.sh"
@@ -214,6 +218,7 @@ PODKOP_SUBSCRIPTION_UPDATE_JOB_FINISHED_TTL_MINUTES=60
PODKOP_SUBSCRIPTION_UPDATE_JOB_ORPHAN_OUTPUT_TTL_MINUTES=60 PODKOP_SUBSCRIPTION_UPDATE_JOB_ORPHAN_OUTPUT_TTL_MINUTES=60
PODKOP_NFT_POPULATE_ENABLED=1 PODKOP_NFT_POPULATE_ENABLED=1
PODKOP_ZAPRET_MISSING_WARNING_EMITTED=0 PODKOP_ZAPRET_MISSING_WARNING_EMITTED=0
PODKOP_ZAPRET2_MISSING_WARNING_EMITTED=0
PODKOP_BYEDPI_MISSING_WARNING_EMITTED=0 PODKOP_BYEDPI_MISSING_WARNING_EMITTED=0
PODKOP_URLTEST_NEW_ENABLED_SECTIONS="" PODKOP_URLTEST_NEW_ENABLED_SECTIONS=""
PODKOP_URLTEST_SELECTOR_SWITCHES="" PODKOP_URLTEST_SELECTOR_SWITCHES=""
@@ -1745,7 +1750,7 @@ append_sing_box_rule_signature() {
signature_add "rule.$section.resolve_real_ip_for_routing" "1" signature_add "rule.$section.resolve_real_ip_for_routing" "1"
;; ;;
zapret) zapret | zapret2)
config_get_bool mixed_proxy_enabled "$section" "mixed_proxy_enabled" 0 config_get_bool mixed_proxy_enabled "$section" "mixed_proxy_enabled" 0
signature_add "rule.$section.mixed_proxy_enabled" "$mixed_proxy_enabled" signature_add "rule.$section.mixed_proxy_enabled" "$mixed_proxy_enabled"
if [ "$mixed_proxy_enabled" -eq 1 ]; then if [ "$mixed_proxy_enabled" -eq 1 ]; then
@@ -2105,6 +2110,63 @@ build_zapret_runtime_signature() {
signature_finish signature_finish
} }
append_zapret2_queue_signature() {
local section="$1"
rule_is_enabled "$section" || return 0
[ "$(get_rule_action "$section")" = "zapret2" ] || return 0
signature_add "zapret2_queue.section" "$section"
}
build_zapret2_queue_signature() {
signature_begin
config_foreach append_zapret2_queue_signature "section"
signature_finish
}
append_zapret2_runtime_signature() {
local section="$1"
local domain domain_suffix community_lists rule_set rule_set_with_subnets domain_ip_lists user_domain_list_type items
rule_is_enabled "$section" || return 0
[ "$(get_rule_action "$section")" = "zapret2" ] || return 0
domain="$(get_rule_condition_commas_string "$section" "domain" "domains")"
domain_suffix="$(get_rule_condition_commas_string "$section" "domain_suffix" "domains")"
config_get community_lists "$section" "community_lists"
config_get rule_set "$section" "rule_set"
config_get rule_set_with_subnets "$section" "rule_set_with_subnets"
config_get domain_ip_lists "$section" "domain_ip_lists"
config_get user_domain_list_type "$section" "user_domain_list_type" "disabled"
signature_add "zapret2.$section.nfqws2_opt" "$(get_rule_nfqws2_opt "$section")"
signature_add "zapret2.$section.domain" "$domain"
signature_add "zapret2.$section.domain_suffix" "$domain_suffix"
signature_add "zapret2.$section.community_lists" "$community_lists"
signature_add "zapret2.$section.rule_set" "$rule_set"
signature_add "zapret2.$section.rule_set_with_subnets" "$rule_set_with_subnets"
signature_add "zapret2.$section.domain_ip_lists" "$domain_ip_lists"
signature_add "zapret2.$section.user_domain_list_type" "$user_domain_list_type"
config_get items "$section" "local_domain_lists"
signature_add "zapret2.$section.local_domain_lists" "$items"
config_get items "$section" "remote_domain_lists"
signature_add "zapret2.$section.remote_domain_lists" "$items"
case "$user_domain_list_type" in
dynamic) config_get items "$section" "user_domains" ;;
text) config_get items "$section" "user_domains_text" ;;
*) items="" ;;
esac
signature_add "zapret2.$section.user_domains" "$items"
}
build_zapret2_runtime_signature() {
signature_begin
config_foreach append_zapret2_runtime_signature "section"
signature_finish
}
append_byedpi_runtime_signature() { append_byedpi_runtime_signature() {
local section="$1" local section="$1"
local byedpi_index local byedpi_index
@@ -2394,6 +2456,8 @@ capture_reload_state() {
RELOAD_STATE_NFT_SIGNATURE="$(build_nft_signature)" RELOAD_STATE_NFT_SIGNATURE="$(build_nft_signature)"
RELOAD_STATE_ZAPRET_QUEUE_SIGNATURE="$(build_zapret_queue_signature)" RELOAD_STATE_ZAPRET_QUEUE_SIGNATURE="$(build_zapret_queue_signature)"
RELOAD_STATE_ZAPRET_RUNTIME_SIGNATURE="$(build_zapret_runtime_signature)" RELOAD_STATE_ZAPRET_RUNTIME_SIGNATURE="$(build_zapret_runtime_signature)"
RELOAD_STATE_ZAPRET2_QUEUE_SIGNATURE="$(build_zapret2_queue_signature)"
RELOAD_STATE_ZAPRET2_RUNTIME_SIGNATURE="$(build_zapret2_runtime_signature)"
RELOAD_STATE_BYEDPI_RUNTIME_SIGNATURE="$(build_byedpi_runtime_signature)" RELOAD_STATE_BYEDPI_RUNTIME_SIGNATURE="$(build_byedpi_runtime_signature)"
RELOAD_STATE_LIST_SIGNATURE="$(build_list_update_signature)" RELOAD_STATE_LIST_SIGNATURE="$(build_list_update_signature)"
RELOAD_STATE_CRON_SIGNATURE="$(build_cron_signature)" RELOAD_STATE_CRON_SIGNATURE="$(build_cron_signature)"
@@ -2411,6 +2475,8 @@ sing_box_signature=$RELOAD_STATE_SING_BOX_SIGNATURE
nft_signature=$RELOAD_STATE_NFT_SIGNATURE nft_signature=$RELOAD_STATE_NFT_SIGNATURE
zapret_queue_signature=$RELOAD_STATE_ZAPRET_QUEUE_SIGNATURE zapret_queue_signature=$RELOAD_STATE_ZAPRET_QUEUE_SIGNATURE
zapret_runtime_signature=$RELOAD_STATE_ZAPRET_RUNTIME_SIGNATURE zapret_runtime_signature=$RELOAD_STATE_ZAPRET_RUNTIME_SIGNATURE
zapret2_queue_signature=$RELOAD_STATE_ZAPRET2_QUEUE_SIGNATURE
zapret2_runtime_signature=$RELOAD_STATE_ZAPRET2_RUNTIME_SIGNATURE
byedpi_runtime_signature=$RELOAD_STATE_BYEDPI_RUNTIME_SIGNATURE byedpi_runtime_signature=$RELOAD_STATE_BYEDPI_RUNTIME_SIGNATURE
list_signature=$RELOAD_STATE_LIST_SIGNATURE list_signature=$RELOAD_STATE_LIST_SIGNATURE
cron_signature=$RELOAD_STATE_CRON_SIGNATURE cron_signature=$RELOAD_STATE_CRON_SIGNATURE
@@ -3055,7 +3121,7 @@ migrate_0_7_17_8_rule() {
migrate_0_7_17_8_delete_subscription_cache "$section" migrate_0_7_17_8_delete_subscription_cache "$section"
fi fi
;; ;;
vpn | outbound | block | direct | zapret | byedpi) vpn | outbound | block | direct | zapret | zapret2 | byedpi)
podkop_uci_delete_option "$section" "proxy_config_type" podkop_uci_delete_option "$section" "proxy_config_type"
podkop_uci_delete_option "$section" "proxy_string" podkop_uci_delete_option "$section" "proxy_string"
podkop_uci_delete_option "$section" "urltest_proxy_links" podkop_uci_delete_option "$section" "urltest_proxy_links"
@@ -3172,6 +3238,7 @@ check_requirements() {
fi fi
check_zapret_requirements check_zapret_requirements
check_zapret2_requirements
check_byedpi_requirements check_byedpi_requirements
} }
@@ -3192,6 +3259,16 @@ _check_outbound_node() {
return 0 return 0
fi fi
if [ "$action" = "zapret" ] && is_zapret_installed; then
section_exists=0
return 0
fi
if [ "$action" = "zapret2" ] && is_zapret2_installed; then
section_exists=0
return 0
fi
if [ -n "$selector_proxy_links" ] || [ -n "$subscription_urls" ] || if [ -n "$selector_proxy_links" ] || [ -n "$subscription_urls" ] ||
[ -n "$outbound_json" ] || [ -n "$interface" ]; then [ -n "$outbound_json" ] || [ -n "$interface" ]; then
section_exists=0 section_exists=0
@@ -3266,6 +3343,7 @@ start_main() {
apply_pending_urltest_selector_switches apply_pending_urltest_selector_switches
run_deferred_subscription_bootstrap run_deferred_subscription_bootstrap
start_zapret_runtime start_zapret_runtime
start_zapret2_runtime
(close_inherited_service_lock_fd; list_update) & (close_inherited_service_lock_fd; list_update) &
} }
@@ -3291,6 +3369,7 @@ stop_main() {
rm -f "$TMP_RULESET_FOLDER"/* rm -f "$TMP_RULESET_FOLDER"/*
stop_zapret_runtime stop_zapret_runtime
stop_zapret2_runtime
stop_byedpi_runtime stop_byedpi_runtime
log "Flush nft" log "Flush nft"
@@ -3419,10 +3498,10 @@ reload() {
local status local status
local force_runtime_reload=0 local force_runtime_reload=0
local previous_service_trigger_signature previous_dnsmasq_signature previous_sing_box_signature previous_nft_signature local previous_service_trigger_signature previous_dnsmasq_signature previous_sing_box_signature previous_nft_signature
local previous_zapret_queue_signature previous_zapret_runtime_signature previous_byedpi_runtime_signature previous_list_signature previous_cron_signature local previous_zapret_queue_signature previous_zapret_runtime_signature previous_zapret2_queue_signature previous_zapret2_runtime_signature previous_byedpi_runtime_signature previous_list_signature previous_cron_signature
local previous_urltest_enabled_sections previous_urltest_enabled_sections_known previous_dont_touch_dhcp needs_sing_box_reload needs_nft_rebuild needs_zapret_restart needs_byedpi_restart local previous_urltest_enabled_sections previous_urltest_enabled_sections_known previous_dont_touch_dhcp needs_sing_box_reload needs_nft_rebuild needs_zapret_restart needs_zapret2_restart needs_byedpi_restart
local needs_dnsmasq_configure needs_dnsmasq_restore needs_cron_refresh needs_list_update local needs_dnsmasq_configure needs_dnsmasq_restore needs_cron_refresh needs_list_update
local changed_service_triggers changed_dnsmasq changed_sing_box changed_nft changed_zapret_queue changed_zapret_runtime changed_byedpi_runtime changed_cron changed_list local changed_service_triggers changed_dnsmasq changed_sing_box changed_nft changed_zapret_queue changed_zapret_runtime changed_zapret2_queue changed_zapret2_runtime changed_byedpi_runtime changed_cron changed_list
log "Podkop Plus reload" log "Podkop Plus reload"
@@ -3458,6 +3537,8 @@ reload() {
previous_nft_signature="$(read_reload_state_value "nft_signature")" previous_nft_signature="$(read_reload_state_value "nft_signature")"
previous_zapret_queue_signature="$(read_reload_state_value "zapret_queue_signature")" previous_zapret_queue_signature="$(read_reload_state_value "zapret_queue_signature")"
previous_zapret_runtime_signature="$(read_reload_state_value "zapret_runtime_signature")" previous_zapret_runtime_signature="$(read_reload_state_value "zapret_runtime_signature")"
previous_zapret2_queue_signature="$(read_reload_state_value "zapret2_queue_signature")"
previous_zapret2_runtime_signature="$(read_reload_state_value "zapret2_runtime_signature")"
previous_byedpi_runtime_signature="$(read_reload_state_value "byedpi_runtime_signature")" previous_byedpi_runtime_signature="$(read_reload_state_value "byedpi_runtime_signature")"
previous_list_signature="$(read_reload_state_value "list_signature")" previous_list_signature="$(read_reload_state_value "list_signature")"
previous_cron_signature="$(read_reload_state_value "cron_signature")" previous_cron_signature="$(read_reload_state_value "cron_signature")"
@@ -3481,6 +3562,7 @@ reload() {
needs_sing_box_reload=0 needs_sing_box_reload=0
needs_nft_rebuild=0 needs_nft_rebuild=0
needs_zapret_restart=0 needs_zapret_restart=0
needs_zapret2_restart=0
needs_byedpi_restart=0 needs_byedpi_restart=0
needs_dnsmasq_configure=0 needs_dnsmasq_configure=0
needs_dnsmasq_restore=0 needs_dnsmasq_restore=0
@@ -3492,6 +3574,8 @@ reload() {
changed_nft=0 changed_nft=0
changed_zapret_queue=0 changed_zapret_queue=0
changed_zapret_runtime=0 changed_zapret_runtime=0
changed_zapret2_queue=0
changed_zapret2_runtime=0
changed_byedpi_runtime=0 changed_byedpi_runtime=0
changed_cron=0 changed_cron=0
changed_list=0 changed_list=0
@@ -3520,6 +3604,18 @@ reload() {
needs_zapret_restart=1 needs_zapret_restart=1
fi fi
if [ "$RELOAD_STATE_ZAPRET2_QUEUE_SIGNATURE" != "$previous_zapret2_queue_signature" ]; then
changed_zapret2_queue=1
needs_zapret2_restart=1
needs_nft_rebuild=1
needs_sing_box_reload=1
fi
if [ "$RELOAD_STATE_ZAPRET2_RUNTIME_SIGNATURE" != "$previous_zapret2_runtime_signature" ]; then
changed_zapret2_runtime=1
needs_zapret2_restart=1
fi
if [ "$RELOAD_STATE_BYEDPI_RUNTIME_SIGNATURE" != "$previous_byedpi_runtime_signature" ]; then if [ "$RELOAD_STATE_BYEDPI_RUNTIME_SIGNATURE" != "$previous_byedpi_runtime_signature" ]; then
changed_byedpi_runtime=1 changed_byedpi_runtime=1
needs_byedpi_restart=1 needs_byedpi_restart=1
@@ -3552,18 +3648,20 @@ reload() {
[ "$needs_sing_box_reload" -eq 0 ] && [ "$needs_sing_box_reload" -eq 0 ] &&
[ "$needs_nft_rebuild" -eq 0 ] && [ "$needs_nft_rebuild" -eq 0 ] &&
[ "$needs_zapret_restart" -eq 0 ] && [ "$needs_zapret_restart" -eq 0 ] &&
[ "$needs_zapret2_restart" -eq 0 ] &&
[ "$needs_byedpi_restart" -eq 0 ] && [ "$needs_byedpi_restart" -eq 0 ] &&
[ "$needs_dnsmasq_configure" -eq 0 ] && [ "$needs_dnsmasq_configure" -eq 0 ] &&
[ "$needs_dnsmasq_restore" -eq 0 ]; then [ "$needs_dnsmasq_restore" -eq 0 ]; then
needs_sing_box_reload=1 needs_sing_box_reload=1
fi fi
log "Reload signature changes: service_triggers=$changed_service_triggers sing-box=$changed_sing_box nft=$changed_nft zapret_queue=$changed_zapret_queue zapret_runtime=$changed_zapret_runtime byedpi_runtime=$changed_byedpi_runtime dnsmasq=$changed_dnsmasq cron=$changed_cron list=$changed_list forced=$force_runtime_reload" "debug" log "Reload signature changes: service_triggers=$changed_service_triggers sing-box=$changed_sing_box nft=$changed_nft zapret_queue=$changed_zapret_queue zapret_runtime=$changed_zapret_runtime zapret2_queue=$changed_zapret2_queue zapret2_runtime=$changed_zapret2_runtime byedpi_runtime=$changed_byedpi_runtime dnsmasq=$changed_dnsmasq cron=$changed_cron list=$changed_list forced=$force_runtime_reload" "debug"
log "Reload plan: sing-box=$needs_sing_box_reload nft=$needs_nft_rebuild zapret=$needs_zapret_restart byedpi=$needs_byedpi_restart dnsmasq_configure=$needs_dnsmasq_configure dnsmasq_restore=$needs_dnsmasq_restore cron=$needs_cron_refresh list_update=$needs_list_update" "debug" log "Reload plan: sing-box=$needs_sing_box_reload nft=$needs_nft_rebuild zapret=$needs_zapret_restart zapret2=$needs_zapret2_restart byedpi=$needs_byedpi_restart dnsmasq_configure=$needs_dnsmasq_configure dnsmasq_restore=$needs_dnsmasq_restore cron=$needs_cron_refresh list_update=$needs_list_update" "debug"
if [ "$needs_sing_box_reload" -eq 0 ] && if [ "$needs_sing_box_reload" -eq 0 ] &&
[ "$needs_nft_rebuild" -eq 0 ] && [ "$needs_nft_rebuild" -eq 0 ] &&
[ "$needs_zapret_restart" -eq 0 ] && [ "$needs_zapret_restart" -eq 0 ] &&
[ "$needs_zapret2_restart" -eq 0 ] &&
[ "$needs_byedpi_restart" -eq 0 ] && [ "$needs_byedpi_restart" -eq 0 ] &&
[ "$needs_dnsmasq_configure" -eq 0 ] && [ "$needs_dnsmasq_configure" -eq 0 ] &&
[ "$needs_dnsmasq_restore" -eq 0 ] && [ "$needs_dnsmasq_restore" -eq 0 ] &&
@@ -3582,6 +3680,10 @@ reload() {
stop_zapret_runtime stop_zapret_runtime
fi fi
if [ "$needs_zapret2_restart" -eq 1 ]; then
stop_zapret2_runtime
fi
if [ "$needs_byedpi_restart" -eq 1 ]; then if [ "$needs_byedpi_restart" -eq 1 ]; then
stop_byedpi_runtime stop_byedpi_runtime
fi fi
@@ -3609,6 +3711,10 @@ reload() {
start_zapret_runtime start_zapret_runtime
fi fi
if [ "$needs_zapret2_restart" -eq 1 ]; then
start_zapret2_runtime
fi
if [ "$needs_byedpi_restart" -eq 1 ]; then if [ "$needs_byedpi_restart" -eq 1 ]; then
start_byedpi_runtime start_byedpi_runtime
fi fi
@@ -4034,6 +4140,22 @@ process_validate_rule() {
validate_rule_nfqws_opt "$section" validate_rule_nfqws_opt "$section"
fi fi
if [ "$action" = "zapret2" ]; then
if ! is_zapret2_installed; then
if [ "$PODKOP_ZAPRET2_MISSING_WARNING_EMITTED" -eq 0 ]; then
log "There are enabled rules with action 'zapret2', but the zapret2 package is not installed. These rules will be skipped." "error"
PODKOP_ZAPRET2_MISSING_WARNING_EMITTED=1
fi
config_list_foreach "$section" "community_lists" validate_service
config_list_foreach "$section" "rule_set" validate_ruleset_reference
config_list_foreach "$section" "rule_set_with_subnets" validate_ruleset_reference
config_list_foreach "$section" "domain_ip_lists" validate_plain_domain_ip_list_reference
return 0
fi
validate_rule_nfqws2_opt "$section"
fi
if [ "$action" = "byedpi" ]; then if [ "$action" = "byedpi" ]; then
if ! is_byedpi_installed; then if ! is_byedpi_installed; then
if [ "$PODKOP_BYEDPI_MISSING_WARNING_EMITTED" -eq 0 ]; then if [ "$PODKOP_BYEDPI_MISSING_WARNING_EMITTED" -eq 0 ]; then
@@ -4523,6 +4645,7 @@ create_nft_rules() {
nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "@$NFT_LOCALV4_SET_NAME" return nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "@$NFT_LOCALV4_SET_NAME" return
nft add rule inet "$NFT_TABLE_NAME" mangle_output meta mark "$NFT_OUTBOUND_MARK" counter return nft add rule inet "$NFT_TABLE_NAME" mangle_output meta mark "$NFT_OUTBOUND_MARK" counter return
create_zapret_nft_rules create_zapret_nft_rules
create_zapret2_nft_rules
nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "@$NFT_COMMON_SET_NAME" meta l4proto tcp meta mark set "$NFT_FAKEIP_MARK" counter nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "@$NFT_COMMON_SET_NAME" meta l4proto tcp meta mark set "$NFT_FAKEIP_MARK" counter
nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "@$NFT_COMMON_SET_NAME" meta l4proto udp meta mark set "$NFT_FAKEIP_MARK" counter nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr "@$NFT_COMMON_SET_NAME" meta l4proto udp meta mark set "$NFT_FAKEIP_MARK" counter
nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr . tcp dport "@$NFT_IP_PORT_SET_NAME" meta mark set "$NFT_FAKEIP_MARK" counter nft add rule inet "$NFT_TABLE_NAME" mangle_output ip daddr . tcp dport "@$NFT_IP_PORT_SET_NAME" meta mark set "$NFT_FAKEIP_MARK" counter
@@ -7895,6 +8018,26 @@ configure_outbound_handler() {
return 0 return 0
fi fi
if [ "$action" = "zapret2" ]; then
write_subscription_metadata_json "$section" ""
if ! is_zapret2_installed; then
return 0
fi
local routing_mark outbound_tag zapret2_index
zapret2_index="$(get_zapret2_rule_index "$section")"
if [ "${zapret2_index:-0}" -le 0 ]; then
log "Unable to resolve Zapret2 index for rule '$section'. Aborted." "fatal"
exit 1
fi
routing_mark="$(get_zapret2_rule_mark_value "$zapret2_index")"
outbound_tag="$(get_outbound_tag_by_section "$section")"
config=$(sing_box_cm_add_direct_outbound "$config" "$outbound_tag" "$routing_mark")
return 0
fi
if [ "$action" = "byedpi" ]; then if [ "$action" = "byedpi" ]; then
if ! is_byedpi_installed; then if ! is_byedpi_installed; then
return 0 return 0
@@ -8282,10 +8425,10 @@ create_route_rule_for_action() {
outbound_tag="$(get_outbound_tag_by_section "$section")" outbound_tag="$(get_outbound_tag_by_section "$section")"
config=$(sing_box_cm_add_route_rule "$config" "$rule_tag" "$inbound_tag" "$outbound_tag") config=$(sing_box_cm_add_route_rule "$config" "$rule_tag" "$inbound_tag" "$outbound_tag")
;; ;;
zapret) zapret | zapret2)
local zapret_outbound_tag local provider_outbound_tag
zapret_outbound_tag="$(get_outbound_tag_by_section "$section")" provider_outbound_tag="$(get_outbound_tag_by_section "$section")"
config=$(sing_box_cm_add_route_rule "$config" "$rule_tag" "$inbound_tag" "$zapret_outbound_tag") config=$(sing_box_cm_add_route_rule "$config" "$rule_tag" "$inbound_tag" "$provider_outbound_tag")
;; ;;
direct) direct)
local direct_outbound_tag local direct_outbound_tag
@@ -8570,6 +8713,10 @@ configure_route_rule_handler() {
return 0 return 0
fi fi
if [ "$action" = "zapret2" ] && ! is_zapret2_installed; then
return 0
fi
if [ "$action" = "byedpi" ] && ! is_byedpi_installed; then if [ "$action" = "byedpi" ] && ! is_byedpi_installed; then
return 0 return 0
fi fi
@@ -8681,6 +8828,10 @@ configure_fully_routed_ips_for_section() {
return 0 return 0
fi fi
if [ "$action" = "zapret2" ] && ! is_zapret2_installed; then
return 0
fi
if [ "$action" = "byedpi" ] && ! is_byedpi_installed; then if [ "$action" = "byedpi" ] && ! is_byedpi_installed; then
return 0 return 0
fi fi
@@ -8800,13 +8951,16 @@ configure_section_mixed_proxy() {
subscription_section_is_deferred "$section" && return 0 subscription_section_is_deferred "$section" && return 0
action="$(get_rule_action "$section")" action="$(get_rule_action "$section")"
[ "$action" = "proxy" ] || [ "$action" = "outbound" ] || [ "$action" = "vpn" ] || [ "$action" = "byedpi" ] || [ "$action" = "zapret" ] || return 0 [ "$action" = "proxy" ] || [ "$action" = "outbound" ] || [ "$action" = "vpn" ] || [ "$action" = "byedpi" ] || [ "$action" = "zapret" ] || [ "$action" = "zapret2" ] || return 0
if [ "$action" = "byedpi" ] && ! is_byedpi_installed; then if [ "$action" = "byedpi" ] && ! is_byedpi_installed; then
return 0 return 0
fi fi
if [ "$action" = "zapret" ] && ! is_zapret_installed; then if [ "$action" = "zapret" ] && ! is_zapret_installed; then
return 0 return 0
fi fi
if [ "$action" = "zapret2" ] && ! is_zapret2_installed; then
return 0
fi
local mixed_inbound_enabled mixed_proxy_port mixed_inbound_tag mixed_outbound_tag mixed_proxy_address \ local mixed_inbound_enabled mixed_proxy_port mixed_inbound_tag mixed_outbound_tag mixed_proxy_address \
mixed_proxy_auth_enabled mixed_proxy_username mixed_proxy_password mixed_proxy_auth_enabled mixed_proxy_username mixed_proxy_password
@@ -9332,7 +9486,10 @@ _determine_first_outbound_section() {
subscription_section_is_deferred "$section" && return 0 subscription_section_is_deferred "$section" && return 0
action="$(get_rule_action "$section")" action="$(get_rule_action "$section")"
if [ "$action" = "proxy" ] || [ "$action" = "outbound" ] || [ "$action" = "vpn" ] || { [ "$action" = "byedpi" ] && is_byedpi_installed; }; then if [ "$action" = "proxy" ] || [ "$action" = "outbound" ] || [ "$action" = "vpn" ] ||
{ [ "$action" = "byedpi" ] && is_byedpi_installed; } ||
{ [ "$action" = "zapret" ] && is_zapret_installed; } ||
{ [ "$action" = "zapret2" ] && is_zapret2_installed; }; then
[ -z "$first_section" ] && first_section="$1" [ -z "$first_section" ] && first_section="$1"
fi fi
} }
@@ -10299,7 +10456,7 @@ write_system_info_cache() {
} }
build_system_info() { build_system_info() {
local podkop_version podkop_latest_version luci_app_version sing_box_version sing_box_extended zapret_version zapret_installed byedpi_version byedpi_installed openwrt_version device_model local podkop_version podkop_latest_version luci_app_version sing_box_version sing_box_extended zapret_version zapret_installed zapret2_version zapret2_installed byedpi_version byedpi_installed openwrt_version device_model
local generated_at local generated_at
podkop_version="$PODKOP_VERSION" podkop_version="$PODKOP_VERSION"
@@ -10327,6 +10484,15 @@ build_system_info() {
zapret_version="not installed" zapret_version="not installed"
fi fi
zapret2_installed=0
if is_zapret2_installed; then
zapret2_installed=1
zapret2_version="$(get_zapret2_package_version)"
[ -z "$zapret2_version" ] && zapret2_version="unknown"
else
zapret2_version="not installed"
fi
byedpi_installed=0 byedpi_installed=0
if is_byedpi_installed; then if is_byedpi_installed; then
byedpi_installed=1 byedpi_installed=1
@@ -10359,6 +10525,7 @@ build_system_info() {
"$podkop_version" "$podkop_latest_version" "$luci_app_version" \ "$podkop_version" "$podkop_latest_version" "$luci_app_version" \
"$sing_box_version" "$sing_box_extended" \ "$sing_box_version" "$sing_box_extended" \
"$zapret_version" "$zapret_installed" \ "$zapret_version" "$zapret_installed" \
"$zapret2_version" "$zapret2_installed" \
"$byedpi_version" "$byedpi_installed" \ "$byedpi_version" "$byedpi_installed" \
"$openwrt_version" "$device_model" "$generated_at" "$openwrt_version" "$device_model" "$generated_at"
} }
@@ -10397,11 +10564,12 @@ get_server_capabilities() {
} }
get_ui_capabilities() { get_ui_capabilities() {
local sing_box_version sing_box_extended=0 zapret_installed=0 byedpi_installed=0 local sing_box_version sing_box_extended=0 zapret_installed=0 zapret2_installed=0 byedpi_installed=0
sing_box_version="$(get_sing_box_version)" sing_box_version="$(get_sing_box_version)"
is_sing_box_extended "$sing_box_version" && sing_box_extended=1 is_sing_box_extended "$sing_box_version" && sing_box_extended=1
is_zapret_installed && zapret_installed=1 is_zapret_installed && zapret_installed=1
is_zapret2_installed && zapret2_installed=1
is_byedpi_installed && byedpi_installed=1 is_byedpi_installed && byedpi_installed=1
INBOUNDS_CONFIG_ENABLED_COUNT=0 INBOUNDS_CONFIG_ENABLED_COUNT=0
@@ -10410,6 +10578,7 @@ get_ui_capabilities() {
json_utils_ucode object-json \ json_utils_ucode object-json \
sing_box_extended n "$sing_box_extended" \ sing_box_extended n "$sing_box_extended" \
zapret_installed n "$zapret_installed" \ zapret_installed n "$zapret_installed" \
zapret2_installed n "$zapret2_installed" \
byedpi_installed n "$byedpi_installed" \ byedpi_installed n "$byedpi_installed" \
server_inbounds_enabled_count n "${INBOUNDS_CONFIG_ENABLED_COUNT:-0}" server_inbounds_enabled_count n "${INBOUNDS_CONFIG_ENABLED_COUNT:-0}"
} }
@@ -10418,6 +10587,10 @@ get_zapret_status() {
get_zapret_status_json get_zapret_status_json
} }
get_zapret2_status() {
get_zapret2_status_json
}
neutralize_zapret_defaults() { neutralize_zapret_defaults() {
log "Standalone zapret is not neutralized automatically; Podkop Plus uses /opt/zapret/nfq/nfqws as an external provider and manages only its own NFQUEUE range." log "Standalone zapret is not neutralized automatically; Podkop Plus uses /opt/zapret/nfq/nfqws as an external provider and manages only its own NFQUEUE range."
} }
@@ -10426,6 +10599,10 @@ check_zapret_runtime() {
check_zapret_runtime_json check_zapret_runtime_json
} }
check_zapret2_runtime() {
check_zapret2_runtime_json
}
get_byedpi_status() { get_byedpi_status() {
get_byedpi_status_json get_byedpi_status_json
} }
@@ -10593,6 +10770,17 @@ validate_nfqws_strategy_json() {
json_utils_ucode nfqws-strategy-validation false "$NFQWS_VALIDATE_ERROR" "$NFQWS_VALIDATE_NEEDLE" "$NFQWS_VALIDATE_NEEDLES" json_utils_ucode nfqws-strategy-validation false "$NFQWS_VALIDATE_ERROR" "$NFQWS_VALIDATE_NEEDLE" "$NFQWS_VALIDATE_NEEDLES"
} }
validate_nfqws2_strategy_json() {
local raw_opt="${1:-}"
if check_nfqws2_strategy "$raw_opt"; then
json_utils_ucode nfqws-strategy-validation true "" "" ""
return 0
fi
json_utils_ucode nfqws-strategy-validation false "$NFQWS2_VALIDATE_ERROR" "$NFQWS2_VALIDATE_NEEDLE" "$NFQWS2_VALIDATE_NEEDLES"
}
dns_check_resolve_host() { dns_check_resolve_host() {
local host="$1" local host="$1"
local resolver="$2" local resolver="$2"
@@ -11212,7 +11400,7 @@ global_check() {
system_info_json=$(get_system_info) system_info_json=$(get_system_info)
if [ -n "$system_info_json" ]; then if [ -n "$system_info_json" ]; then
local podkop_version podkop_latest_version luci_app_version sing_box_version sing_box_extended zapret_version zapret_installed byedpi_version byedpi_installed openwrt_version device_model local podkop_version podkop_latest_version luci_app_version sing_box_version sing_box_extended zapret_version zapret_installed zapret2_version zapret2_installed byedpi_version byedpi_installed openwrt_version device_model
podkop_version=$(json_get_default "$system_info_json" podkop_version "unknown") podkop_version=$(json_get_default "$system_info_json" podkop_version "unknown")
podkop_latest_version=$(json_get_default "$system_info_json" podkop_latest_version "unknown") podkop_latest_version=$(json_get_default "$system_info_json" podkop_latest_version "unknown")
@@ -11221,6 +11409,8 @@ global_check() {
sing_box_extended=$(json_get_default "$system_info_json" sing_box_extended 0) sing_box_extended=$(json_get_default "$system_info_json" sing_box_extended 0)
zapret_version=$(json_get_default "$system_info_json" zapret_version "unknown") zapret_version=$(json_get_default "$system_info_json" zapret_version "unknown")
zapret_installed=$(json_get_default "$system_info_json" zapret_installed 0) zapret_installed=$(json_get_default "$system_info_json" zapret_installed 0)
zapret2_version=$(json_get_default "$system_info_json" zapret2_version "unknown")
zapret2_installed=$(json_get_default "$system_info_json" zapret2_installed 0)
byedpi_version=$(json_get_default "$system_info_json" byedpi_version "unknown") byedpi_version=$(json_get_default "$system_info_json" byedpi_version "unknown")
byedpi_installed=$(json_get_default "$system_info_json" byedpi_installed 0) byedpi_installed=$(json_get_default "$system_info_json" byedpi_installed 0)
openwrt_version=$(json_get_default "$system_info_json" openwrt_version "unknown") openwrt_version=$(json_get_default "$system_info_json" openwrt_version "unknown")
@@ -11237,6 +11427,9 @@ global_check() {
if [ "$zapret_installed" -eq 1 ]; then if [ "$zapret_installed" -eq 1 ]; then
print_global "🧵 Zapret: $zapret_version" print_global "🧵 Zapret: $zapret_version"
fi fi
if [ "$zapret2_installed" -eq 1 ]; then
print_global "🧵 Zapret2: $zapret2_version"
fi
if [ "$byedpi_installed" -eq 1 ]; then if [ "$byedpi_installed" -eq 1 ]; then
print_global "🧵 ByeDPI: $byedpi_version" print_global "🧵 ByeDPI: $byedpi_version"
fi fi
@@ -11488,6 +11681,15 @@ global_check() {
print_global "✅ Zapret provider available" print_global "✅ Zapret provider available"
fi fi
local zapret2_check_json
zapret2_check_json=$(check_zapret2_runtime)
if [ -n "$zapret2_check_json" ] && [ "$(json_get_default "$zapret2_check_json" zapret2_installed 0)" = "1" ]; then
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "🧵 Zapret2 status"
print_global "✅ Zapret2 provider available"
fi
local byedpi_check_json local byedpi_check_json
byedpi_check_json=$(check_byedpi_runtime) byedpi_check_json=$(check_byedpi_runtime)
@@ -11646,6 +11848,14 @@ global_check() {
print_global "⚠️ Standalone zapret autostart is enabled. Podkop Plus will not modify /etc/config/zapret." print_global "⚠️ Standalone zapret autostart is enabled. Podkop Plus will not modify /etc/config/zapret."
fi fi
if [ -x /etc/init.d/zapret2 ] && /etc/init.d/zapret2 status >/dev/null 2>&1; then
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "⚠️ Standalone zapret2 service is active. Podkop Plus uses separate queues, but packet-level policy overlap is possible."
elif [ -x /etc/init.d/zapret2 ] && /etc/init.d/zapret2 enabled >/dev/null 2>&1; then
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "⚠️ Standalone zapret2 autostart is enabled. Podkop Plus will not modify /etc/config/zapret2."
fi
print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━" print_global "━━━━━━━━━━━━━━━━━━━━━━━━━━━"
print_global "🥸 FakeIP status" print_global "🥸 FakeIP status"
@@ -11701,6 +11911,7 @@ Available commands:
check_sing_box_logs Show sing-box logs check_sing_box_logs Show sing-box logs
check_fakeip Test sing-box FakeIP DNS check_fakeip Test sing-box FakeIP DNS
check_zapret_runtime Check external zapret provider state check_zapret_runtime Check external zapret provider state
check_zapret2_runtime Check external zapret2 provider state
check_byedpi_runtime Check external ByeDPI provider state check_byedpi_runtime Check external ByeDPI provider state
neutralize_zapret_defaults neutralize_zapret_defaults
Compatibility no-op; Podkop Plus does not manage standalone zapret Compatibility no-op; Podkop Plus does not manage standalone zapret
@@ -11716,19 +11927,22 @@ Available commands:
Get UI-only metadata for a subscription rule Get UI-only metadata for a subscription rule
get_sing_box_status Get sing-box service status get_sing_box_status Get sing-box service status
get_zapret_status Get zapret provider and Podkop Plus-managed nfqws status get_zapret_status Get zapret provider and Podkop Plus-managed nfqws status
get_zapret2_status Get zapret2 provider and Podkop Plus-managed nfqws2 status
get_byedpi_status Get ByeDPI provider and Podkop Plus-managed ciadpi status get_byedpi_status Get ByeDPI provider and Podkop Plus-managed ciadpi status
get_system_info Get system information in JSON format get_system_info Get system information in JSON format
get_server_capabilities Get server UI capabilities in JSON format get_server_capabilities Get server UI capabilities in JSON format
get_ui_capabilities Get lightweight UI capabilities in JSON format get_ui_capabilities Get lightweight UI capabilities in JSON format
generate_reality_keypair generate_reality_keypair
Generate a sing-box Reality key pair in JSON format Generate a sing-box Reality key pair in JSON format
component_action Manage Podkop Plus, sing-box, zapret, and ByeDPI components component_action Manage Podkop Plus, sing-box, zapret, zapret2, and ByeDPI components
component_action_async Start a component action in the background component_action_async Start a component action in the background
component_action_status Get background component action status component_action_status Get background component action status
check_dns_available Check DNS server availability check_dns_available Check DNS server availability
global_check Run global system check global_check Run global system check
validate_nfqws_strategy_json validate_nfqws_strategy_json
Validate an NFQWS strategy and print JSON result Validate an NFQWS strategy and print JSON result
validate_nfqws2_strategy_json
Validate an NFQWS2 strategy and print JSON result
validate_byedpi_strategy_json validate_byedpi_strategy_json
Validate a ByeDPI ciadpi strategy and print JSON result Validate a ByeDPI ciadpi strategy and print JSON result
EOF EOF
@@ -11810,6 +12024,9 @@ check_fakeip)
check_zapret_runtime) check_zapret_runtime)
check_zapret_runtime check_zapret_runtime
;; ;;
check_zapret2_runtime)
check_zapret2_runtime
;;
check_byedpi_runtime) check_byedpi_runtime)
check_byedpi_runtime check_byedpi_runtime
;; ;;
@@ -11852,6 +12069,9 @@ get_sing_box_status)
get_zapret_status) get_zapret_status)
get_zapret_status get_zapret_status
;; ;;
get_zapret2_status)
get_zapret2_status
;;
get_byedpi_status) get_byedpi_status)
get_byedpi_status get_byedpi_status
;; ;;
@@ -11885,6 +12105,9 @@ global_check)
validate_nfqws_strategy_json) validate_nfqws_strategy_json)
validate_nfqws_strategy_json "${2:-}" validate_nfqws_strategy_json "${2:-}"
;; ;;
validate_nfqws2_strategy_json)
validate_nfqws2_strategy_json "${2:-}"
;;
validate_byedpi_strategy_json) validate_byedpi_strategy_json)
validate_byedpi_strategy_json "${2:-}" validate_byedpi_strategy_json "${2:-}"
;; ;;
+20
View File
@@ -97,6 +97,26 @@ ZAPRET_DESYNC_MARK_POSTNAT="0x20000000"
ZAPRET_LEGACY_DEFAULT_NFQWS_OPT="--filter-tcp=80 <HOSTLIST> --dpi-desync=fake,fakedsplit --dpi-desync-autottl=2 --dpi-desync-fooling=badsum --new --filter-tcp=443 --hostlist=/opt/zapret/ipset/zapret-hosts-google.txt --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-repeats=11 --dpi-desync-fooling=badsum --dpi-desync-fake-tls-mod=rnd,dupsid,sni=www.google.com --new --filter-udp=443 --hostlist=/opt/zapret/ipset/zapret-hosts-google.txt --dpi-desync=fake --dpi-desync-repeats=11 --dpi-desync-fake-quic=/opt/zapret/files/fake/quic_initial_www_google_com.bin --new --filter-udp=443 <HOSTLIST_NOAUTO> --dpi-desync=fake --dpi-desync-repeats=11 --new --filter-tcp=443 <HOSTLIST> --dpi-desync=multidisorder --dpi-desync-split-pos=1,sniext+1,host+1,midsld-2,midsld,midsld+2,endhost-1" ZAPRET_LEGACY_DEFAULT_NFQWS_OPT="--filter-tcp=80 <HOSTLIST> --dpi-desync=fake,fakedsplit --dpi-desync-autottl=2 --dpi-desync-fooling=badsum --new --filter-tcp=443 --hostlist=/opt/zapret/ipset/zapret-hosts-google.txt --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-repeats=11 --dpi-desync-fooling=badsum --dpi-desync-fake-tls-mod=rnd,dupsid,sni=www.google.com --new --filter-udp=443 --hostlist=/opt/zapret/ipset/zapret-hosts-google.txt --dpi-desync=fake --dpi-desync-repeats=11 --dpi-desync-fake-quic=/opt/zapret/files/fake/quic_initial_www_google_com.bin --new --filter-udp=443 <HOSTLIST_NOAUTO> --dpi-desync=fake --dpi-desync-repeats=11 --new --filter-tcp=443 <HOSTLIST> --dpi-desync=multidisorder --dpi-desync-split-pos=1,sniext+1,host+1,midsld-2,midsld,midsld+2,endhost-1"
ZAPRET_DEFAULT_NFQWS_OPT="--filter-tcp=80 --dpi-desync=fake,fakedsplit --dpi-desync-autottl=2 --dpi-desync-fooling=badsum --new --filter-tcp=443 --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-repeats=11 --dpi-desync-fooling=badsum --dpi-desync-fake-tls-mod=rnd,dupsid,sni=www.google.com --new --filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=11 --dpi-desync-fake-quic=/opt/zapret/files/fake/quic_initial_www_google_com.bin" ZAPRET_DEFAULT_NFQWS_OPT="--filter-tcp=80 --dpi-desync=fake,fakedsplit --dpi-desync-autottl=2 --dpi-desync-fooling=badsum --new --filter-tcp=443 --dpi-desync=fake,multidisorder --dpi-desync-split-pos=1,midsld --dpi-desync-repeats=11 --dpi-desync-fooling=badsum --dpi-desync-fake-tls-mod=rnd,dupsid,sni=www.google.com --new --filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=11 --dpi-desync-fake-quic=/opt/zapret/files/fake/quic_initial_www_google_com.bin"
## Zapret2
ZAPRET2_PROVIDER_BASE_DIR="/opt/zapret2"
ZAPRET2_PROVIDER_NFQWS2_BIN="$ZAPRET2_PROVIDER_BASE_DIR/nfq2/nfqws2"
ZAPRET2_PROVIDER_FILES_DIR="$ZAPRET2_PROVIDER_BASE_DIR/files"
ZAPRET2_PROVIDER_IPSET_DIR="$ZAPRET2_PROVIDER_BASE_DIR/ipset"
ZAPRET2_PROVIDER_LUA_DIR="$ZAPRET2_PROVIDER_BASE_DIR/lua"
ZAPRET2_NFQWS2_BIN="$ZAPRET2_PROVIDER_NFQWS2_BIN"
ZAPRET2_STATE_DIR="/var/run/podkop-plus/zapret2"
ZAPRET2_PID_DIR="$ZAPRET2_STATE_DIR/pid"
ZAPRET2_CHILD_PID_DIR="$ZAPRET2_STATE_DIR/child-pid"
ZAPRET2_LOG_DIR="$ZAPRET2_STATE_DIR/log"
ZAPRET2_ROUTE_MARK_BASE="0x01100000"
# Keep zapret2 queues outside the zapret provider range (4000-4255).
ZAPRET2_QUEUE_BASE=4300
ZAPRET2_QUEUE_RANGE_SIZE=256
ZAPRET2_NFQWS2_RESPAWN_DELAY=5
ZAPRET2_DESYNC_MARK="0x40000000"
ZAPRET2_DESYNC_MARK_POSTNAT="0x20000000"
ZAPRET2_DEFAULT_NFQWS2_OPT="--filter-tcp=80 --filter-l7=http --payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5 --lua-desync=multisplit:pos=method+2 --new --filter-tcp=443 --filter-l7=tls --payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tcp_seq=-10000 --lua-desync=multidisorder:pos=1,midsld --new --filter-udp=443 --filter-l7=quic --payload=quic_initial --lua-desync=fake:blob=fake_default_quic:repeats=6"
## ByeDPI ## ByeDPI
BYEDPI_BIN="/usr/bin/ciadpi" BYEDPI_BIN="/usr/bin/ciadpi"
BYEDPI_SERVICE_INIT="/etc/init.d/byedpi" BYEDPI_SERVICE_INIT="/etc/init.d/byedpi"
+48 -5
View File
@@ -345,6 +345,45 @@ function byedpi_asset_matches(name, arch, ext) {
(str_contains(name, "_" + arch + "." + ext) || str_contains(name, "-" + arch + "." + ext)); (str_contains(name, "_" + arch + "." + ext) || str_contains(name, "-" + arch + "." + ext));
} }
function release_asset_matches_arch(name, prefix, arch, ext) {
return (str_startswith(name, prefix + "_") || str_startswith(name, prefix + "-")) &&
str_endswith(name, "." + ext) &&
(str_contains(name, "_" + arch + "." + ext) || str_contains(name, "-" + arch + "." + ext));
}
function named_release_select_asset(release_prefix, asset_prefix, asset_ext, arch_candidates) {
let releases = array_or_empty(read_stdin_json());
for (let release in releases) {
if (type(release) != "object")
continue;
if (release.draft === true)
continue;
let release_name = as_string(release.name || "");
if (!str_startswith(release_name, release_prefix))
continue;
for (let arch in split(as_string(arch_candidates), " ")) {
if (arch == "")
continue;
for (let asset in array_or_empty(release.assets)) {
if (type(asset) != "object")
continue;
let name = as_string(asset.name || "");
let url = as_string(asset.browser_download_url || "");
if (url != "" && release_asset_matches_arch(name, asset_prefix, arch, asset_ext)) {
print(arch, "\t", name, "\t", url, "\t",
as_string(release.html_url || ""), "\t", as_string(release.tag_name || ""), "\n");
return;
}
}
}
}
}
function select_byedpi_asset_from_release(release, asset_ext, arch_candidates) { function select_byedpi_asset_from_release(release, asset_ext, arch_candidates) {
for (let arch in split(as_string(arch_candidates), " ")) { for (let arch in split(as_string(arch_candidates), " ")) {
if (arch == "") if (arch == "")
@@ -427,11 +466,13 @@ function system_info_json() {
sing_box_extended: arg_number(ARGV[5]), sing_box_extended: arg_number(ARGV[5]),
zapret_version: as_string(ARGV[6]), zapret_version: as_string(ARGV[6]),
zapret_installed: arg_number(ARGV[7]), zapret_installed: arg_number(ARGV[7]),
byedpi_version: as_string(ARGV[8]), zapret2_version: as_string(ARGV[8]),
byedpi_installed: arg_number(ARGV[9]), zapret2_installed: arg_number(ARGV[9]),
openwrt_version: as_string(ARGV[10]), byedpi_version: as_string(ARGV[10]),
device_model: as_string(ARGV[11]), byedpi_installed: arg_number(ARGV[11]),
generated_at: arg_number(ARGV[12]) openwrt_version: as_string(ARGV[12]),
device_model: as_string(ARGV[13]),
generated_at: arg_number(ARGV[14])
}); });
} }
@@ -932,6 +973,8 @@ else if (mode == "release-asset-name-by-suffix")
release_asset_name_by_suffix(ARGV[1]); release_asset_name_by_suffix(ARGV[1]);
else if (mode == "release-asset-url-by-suffix") else if (mode == "release-asset-url-by-suffix")
release_asset_url_by_suffix(ARGV[1]); release_asset_url_by_suffix(ARGV[1]);
else if (mode == "named-release-select-asset")
named_release_select_asset(ARGV[1], ARGV[2], ARGV[3], ARGV[4]);
else if (mode == "byedpi-select-asset") else if (mode == "byedpi-select-asset")
byedpi_select_asset(ARGV[1], ARGV[2], ARGV[3]); byedpi_select_asset(ARGV[1], ARGV[2], ARGV[3]);
else if (mode == "sing-box-extended-release-tag") else if (mode == "sing-box-extended-release-tag")
+42
View File
@@ -225,6 +225,44 @@ function zapret_check(installed, package_installed, provider_path) {
}); });
} }
function zapret2_status(args) {
write_json({
installed: bool_arg(args[0]),
package_installed: bool_arg(args[1]),
provider_available: bool_arg(args[2]),
provider_path: as_string(args[3]),
files_available: bool_arg(args[4]),
ipset_available: bool_arg(args[5]),
version: as_string(args[6]),
configured: bool_arg(args[7]),
enabled_rule_count: int_arg(args[8]),
expected_process_count: int_arg(args[9]),
running_process_count: int_arg(args[10]),
supervisor_process_count: int_arg(args[11]),
standalone_service_enabled: bool_arg(args[12]),
standalone_service_running: bool_arg(args[13]),
standalone_config_present: bool_arg(args[14]),
standalone_conflict: bool_arg(args[15]),
luci_app_installed: bool_arg(args[16]),
queue_base: int_arg(args[17]),
queue_range_end: int_arg(args[18]),
queue_overlap: bool_arg(args[19]),
ready: bool_arg(args[20]),
conflict: bool_arg(args[21]),
outbounds_configured: bool_arg(args[22]),
routes_configured: bool_arg(args[23]),
status_message: as_string(args[24])
});
}
function zapret2_check(installed, package_installed, provider_path) {
write_json({
zapret2_installed: bool_arg(installed),
zapret2_package_installed: bool_arg(package_installed),
zapret2_provider_path: as_string(provider_path)
});
}
let mode = ARGV[0] || ""; let mode = ARGV[0] || "";
if (mode == "byedpi-validation") if (mode == "byedpi-validation")
@@ -249,6 +287,10 @@ else if (mode == "zapret-status")
zapret_status(slice(ARGV, 1)); zapret_status(slice(ARGV, 1));
else if (mode == "zapret-check") else if (mode == "zapret-check")
zapret_check(ARGV[1], ARGV[2], ARGV[3]); zapret_check(ARGV[1], ARGV[2], ARGV[3]);
else if (mode == "zapret2-status")
zapret2_status(slice(ARGV, 1));
else if (mode == "zapret2-check")
zapret2_check(ARGV[1], ARGV[2], ARGV[3]);
else { else {
warn("Usage: provider_status.uc <operation> ...\n"); warn("Usage: provider_status.uc <operation> ...\n");
exit(1); exit(1);
+130 -1
View File
@@ -10,6 +10,13 @@ UPDATES_ZAPRET_PACKAGE_FILE=""
UPDATES_ZAPRET_PACKAGE_NAME="" UPDATES_ZAPRET_PACKAGE_NAME=""
UPDATES_ZAPRET_PACKAGE_VERSION="" UPDATES_ZAPRET_PACKAGE_VERSION=""
UPDATES_ZAPRET_RELEASE_URL="" UPDATES_ZAPRET_RELEASE_URL=""
UPDATES_ZAPRET2_ARCH=""
UPDATES_ZAPRET2_BUNDLE_URL=""
UPDATES_ZAPRET2_BUNDLE_NAME=""
UPDATES_ZAPRET2_PACKAGE_FILE=""
UPDATES_ZAPRET2_PACKAGE_NAME=""
UPDATES_ZAPRET2_PACKAGE_VERSION=""
UPDATES_ZAPRET2_RELEASE_URL=""
UPDATES_BYEDPI_ARCH="" UPDATES_BYEDPI_ARCH=""
UPDATES_BYEDPI_PACKAGE_URL="" UPDATES_BYEDPI_PACKAGE_URL=""
UPDATES_BYEDPI_PACKAGE_NAME="" UPDATES_BYEDPI_PACKAGE_NAME=""
@@ -146,9 +153,11 @@ updates_fail() {
local message="$3" local message="$3"
local current_version="${4:-}" local current_version="${4:-}"
local latest_version="${5:-}" local latest_version="${5:-}"
local status="${6:-}"
local release_url="${7:-}"
updates_log "$message" "error" updates_log "$message" "error"
updates_json_response false "$component" "$action" "$message" "$current_version" "$latest_version" 0 updates_json_response false "$component" "$action" "$message" "$current_version" "$latest_version" 0 "$status" "$release_url"
exit 1 exit 1
} }
@@ -1020,6 +1029,8 @@ updates_extract_arch_package_version() {
version="$(printf '%s\n' "$package_name" | sed 's/\.ipk$//;s/\.apk$//')" version="$(printf '%s\n' "$package_name" | sed 's/\.ipk$//;s/\.apk$//')"
case "$version" in case "$version" in
zapret2_*) version="${version#zapret2_}" ;;
zapret2-*) version="${version#zapret2-}" ;;
zapret_*) version="${version#zapret_}" ;; zapret_*) version="${version#zapret_}" ;;
zapret-*) version="${version#zapret-}" ;; zapret-*) version="${version#zapret-}" ;;
byedpi_*) version="${version#byedpi_}" ;; byedpi_*) version="${version#byedpi_}" ;;
@@ -1184,6 +1195,63 @@ updates_download_and_extract_zapret_package() {
[ -n "$UPDATES_ZAPRET_PACKAGE_VERSION" ] || UPDATES_ZAPRET_PACKAGE_VERSION="$(updates_extract_arch_package_version "$UPDATES_ZAPRET_PACKAGE_NAME" "$UPDATES_ZAPRET_ARCH")" [ -n "$UPDATES_ZAPRET_PACKAGE_VERSION" ] || UPDATES_ZAPRET_PACKAGE_VERSION="$(updates_extract_arch_package_version "$UPDATES_ZAPRET_PACKAGE_NAME" "$UPDATES_ZAPRET_ARCH")"
} }
updates_resolve_zapret2_release() {
local releases_json resolved
UPDATES_ZAPRET2_ARCH=""
UPDATES_ZAPRET2_BUNDLE_URL=""
UPDATES_ZAPRET2_BUNDLE_NAME=""
UPDATES_ZAPRET2_PACKAGE_VERSION=""
UPDATES_ZAPRET2_RELEASE_URL=""
releases_json="$(updates_fetch_github_releases_json "remittor" "zapret-openwrt" 30)" || return 1
resolved="$(
printf '%s' "$releases_json" |
json_utils_ucode named-release-select-asset "zapret2 " "zapret2" "zip" "$UPDATES_ARCH_CANDIDATES" 2>/dev/null
)"
[ -n "$resolved" ] || return 1
UPDATES_ZAPRET2_ARCH="$(printf '%s\n' "$resolved" | cut -f1)"
UPDATES_ZAPRET2_BUNDLE_NAME="$(printf '%s\n' "$resolved" | cut -f2)"
UPDATES_ZAPRET2_BUNDLE_URL="$(printf '%s\n' "$resolved" | cut -f3)"
UPDATES_ZAPRET2_RELEASE_URL="$(printf '%s\n' "$resolved" | cut -f4)"
[ -n "$UPDATES_ZAPRET2_ARCH" ] || return 1
[ -n "$UPDATES_ZAPRET2_BUNDLE_NAME" ] || return 1
[ -n "$UPDATES_ZAPRET2_BUNDLE_URL" ] || return 1
UPDATES_ZAPRET2_PACKAGE_VERSION="$(updates_extract_zapret2_bundle_version "$UPDATES_ZAPRET2_BUNDLE_NAME")"
[ -n "$UPDATES_ZAPRET2_PACKAGE_VERSION" ] || UPDATES_ZAPRET2_PACKAGE_VERSION="$(printf '%s\n' "$UPDATES_ZAPRET2_BUNDLE_NAME" | sed 's/\.zip$//')"
}
updates_download_and_extract_zapret2_package() {
local bundle_file inner_package_path
UPDATES_ZAPRET2_PACKAGE_FILE=""
UPDATES_ZAPRET2_PACKAGE_NAME=""
UPDATES_ZAPRET2_PACKAGE_VERSION=""
bundle_file="$UPDATES_TMP_DIR/$UPDATES_ZAPRET2_BUNDLE_NAME"
updates_download_with_retry "$UPDATES_ZAPRET2_BUNDLE_URL" "$bundle_file" "$UPDATES_ZAPRET2_BUNDLE_NAME" || return 1
if updates_is_apk; then
inner_package_path="$(unzip -l "$bundle_file" | awk '{print $4}' | grep -E '^apk/zapret2-.*\.apk$' | sed -n '1p')"
else
inner_package_path="$(unzip -l "$bundle_file" | awk '{print $4}' | grep -E "^zapret2_.*_${UPDATES_ZAPRET2_ARCH}\.ipk$" | sed -n '1p')"
[ -n "$inner_package_path" ] || inner_package_path="$(unzip -l "$bundle_file" | awk '{print $4}' | grep -E '^zapret2_.*\.ipk$' | sed -n '1p')"
fi
[ -n "$inner_package_path" ] || return 1
UPDATES_ZAPRET2_PACKAGE_NAME="$(basename "$inner_package_path")"
UPDATES_ZAPRET2_PACKAGE_FILE="$UPDATES_TMP_DIR/$UPDATES_ZAPRET2_PACKAGE_NAME"
unzip -p "$bundle_file" "$inner_package_path" >"$UPDATES_ZAPRET2_PACKAGE_FILE" || return 1
[ -s "$UPDATES_ZAPRET2_PACKAGE_FILE" ] || return 1
[ -n "$UPDATES_ZAPRET2_PACKAGE_VERSION" ] || UPDATES_ZAPRET2_PACKAGE_VERSION="$(updates_extract_zapret2_bundle_version "$UPDATES_ZAPRET2_BUNDLE_NAME")"
[ -n "$UPDATES_ZAPRET2_PACKAGE_VERSION" ] || UPDATES_ZAPRET2_PACKAGE_VERSION="$(updates_extract_arch_package_version "$UPDATES_ZAPRET2_PACKAGE_NAME" "$UPDATES_ZAPRET2_ARCH")"
}
updates_resolve_byedpi_release() { updates_resolve_byedpi_release() {
local response release_series asset_ext resolved local response release_series asset_ext resolved
@@ -1230,6 +1298,13 @@ updates_disable_standalone_zapret_service() {
updates_log_command "Disabling standalone zapret autostart" /etc/init.d/zapret disable || true updates_log_command "Disabling standalone zapret autostart" /etc/init.d/zapret disable || true
} }
updates_disable_standalone_zapret2_service() {
[ -x /etc/init.d/zapret2 ] || return 0
updates_log_command "Stopping standalone zapret2 service" /etc/init.d/zapret2 stop || true
updates_log_command "Disabling standalone zapret2 autostart" /etc/init.d/zapret2 disable || true
}
updates_disable_standalone_byedpi_service() { updates_disable_standalone_byedpi_service() {
[ -x /etc/init.d/byedpi ] || return 0 [ -x /etc/init.d/byedpi ] || return 0
@@ -1272,6 +1347,43 @@ updates_install_zapret() {
updates_success "zapret" "$action" "zapret package has been installed" "$current_version" "$UPDATES_ZAPRET_PACKAGE_VERSION" 1 "latest" updates_success "zapret" "$action" "zapret package has been installed" "$current_version" "$UPDATES_ZAPRET_PACKAGE_VERSION" 1 "latest"
} }
updates_install_zapret2() {
local action="$1"
local current_version installed normalized_current normalized_latest
updates_init_tmp_dir || updates_fail "zapret2" "$action" "Failed to create temporary directory"
updates_resolve_arch_candidates || updates_fail "zapret2" "$action" "Failed to detect package architecture"
updates_retry_resolve "Resolving zapret2 package" updates_resolve_zapret2_release ||
updates_fail "zapret2" "$action" "Failed to resolve zapret2 package for this router architecture"
installed=0
is_zapret2_installed && installed=1
current_version="$(get_zapret2_package_version)"
if [ "$action" = "check_update" ]; then
[ "$installed" -eq 1 ] ||
updates_fail "zapret2" "$action" "zapret2 is not installed" "$current_version" "$UPDATES_ZAPRET2_PACKAGE_VERSION" "" "$UPDATES_ZAPRET2_RELEASE_URL"
normalized_current="$(updates_normalize_zapret_version "$current_version")"
normalized_latest="$(updates_normalize_zapret_version "$UPDATES_ZAPRET2_PACKAGE_VERSION")"
updates_check_success_compared "zapret2" "$current_version" "$UPDATES_ZAPRET2_PACKAGE_VERSION" "$normalized_current" "$normalized_latest" "$UPDATES_ZAPRET2_RELEASE_URL"
fi
updates_ensure_package_tool "unzip" "unzip" || updates_fail "zapret2" "$action" "Failed to install unzip"
updates_download_and_extract_zapret2_package ||
updates_fail "zapret2" "$action" "Failed to download zapret2 package" "$current_version" "$UPDATES_ZAPRET2_PACKAGE_VERSION" "" "$UPDATES_ZAPRET2_RELEASE_URL"
if ! updates_log_command "Installing zapret2 package $UPDATES_ZAPRET2_PACKAGE_NAME" updates_pkg_install_files "$UPDATES_ZAPRET2_PACKAGE_FILE"; then
updates_fail "zapret2" "$action" "Failed to install zapret2 package" "$current_version" "$UPDATES_ZAPRET2_PACKAGE_VERSION" "" "$UPDATES_ZAPRET2_RELEASE_URL"
fi
updates_disable_standalone_zapret2_service
updates_restart_podkop_after_successful_change
updates_clear_version_caches
current_version="$(get_zapret2_package_version)"
updates_success "zapret2" "$action" "zapret2 package has been installed" "$current_version" "$UPDATES_ZAPRET2_PACKAGE_VERSION" 1 "latest" "$UPDATES_ZAPRET2_RELEASE_URL"
}
updates_install_byedpi() { updates_install_byedpi() {
local action="$1" local action="$1"
local current_version installed local current_version installed
@@ -1436,6 +1548,17 @@ updates_extract_zapret_bundle_version() {
printf '%s\n' "$version" | sed 's/^v//' printf '%s\n' "$version" | sed 's/^v//'
} }
updates_extract_zapret2_bundle_version() {
local bundle_name="$1"
local version
version="$(basename "$bundle_name" | sed -n 's/^zapret2_v\([^_][^_]*\)_.*/\1/p')"
[ -n "$version" ] || version="$(basename "$bundle_name" | sed -n 's/^zapret2_\([^_][^_]*\)_.*/\1/p')"
[ -n "$version" ] || version="$(updates_extract_zapret_bundle_version "$bundle_name")"
printf '%s\n' "$version" | sed 's/^v//'
}
updates_normalize_zapret_version() { updates_normalize_zapret_version() {
printf '%s\n' "$1" | printf '%s\n' "$1" |
sed 's/^v//;s/-r[0-9][0-9]*$//;s/+.*$//;s/[[:space:]].*$//' sed 's/^v//;s/-r[0-9][0-9]*$//;s/+.*$//;s/[[:space:]].*$//'
@@ -1771,6 +1894,12 @@ component_action() {
zapret:remove) zapret:remove)
updates_remove_optional_component "zapret" "zapret" "zapret" is_zapret_installed get_zapret_package_version updates_remove_optional_component "zapret" "zapret" "zapret" is_zapret_installed get_zapret_package_version
;; ;;
zapret2:check_update | zapret2:install)
updates_install_zapret2 "$action"
;;
zapret2:remove)
updates_remove_optional_component "zapret2" "zapret2" "zapret2" is_zapret2_installed get_zapret2_package_version
;;
byedpi:check_update | byedpi:install) byedpi:check_update | byedpi:install)
updates_install_byedpi "$action" updates_install_byedpi "$action"
;; ;;
File diff suppressed because it is too large Load Diff