Add zapret2 section action support
This commit is contained in:
@@ -2,6 +2,13 @@ name: Differential ShellCheck
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
base:
|
||||
description: Base revision for manual differential scan
|
||||
required: true
|
||||
head:
|
||||
description: Head revision for manual differential scan
|
||||
required: true
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
@@ -27,6 +34,7 @@ permissions:
|
||||
jobs:
|
||||
shellcheck:
|
||||
name: Differential ShellCheck
|
||||
if: ${{ github.event_name != 'push' || github.event.forced != true }}
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
permissions:
|
||||
@@ -42,6 +50,9 @@ jobs:
|
||||
- name: Differential ShellCheck
|
||||
uses: redhat-plumbers-in-action/differential-shellcheck@v5.5.5
|
||||
with:
|
||||
triggering-event: ${{ github.event_name == 'workflow_dispatch' && 'manual' || github.event_name }}
|
||||
base: ${{ github.event.inputs.base }}
|
||||
head: ${{ github.event.inputs.head }}
|
||||
severity: error
|
||||
include-path: |
|
||||
podkop/files/usr/bin/podkop
|
||||
|
||||
@@ -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.
|
||||
- Обновленный LuCI-интерфейс и расширенное управление секциями.
|
||||
- Новые условия маршрутизации.
|
||||
- Режим сервера с возможностю поднять собственный VPN/Proxy для подключения внешних устройств.
|
||||
- Менеджер обновлений и установки компонентов.
|
||||
- Встроенный мониторинг соединений.
|
||||
- Менеджер обновлений.
|
||||
- Переход на нативный для OpenWrt `ucode`
|
||||
|
||||
### Подписки
|
||||
|
||||
@@ -100,11 +99,11 @@ Podkop Plus позволяет поднять собственный VPN/Proxy
|
||||
|
||||
### Интеграция 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` намеренно запрещены:
|
||||
|
||||
@@ -113,11 +112,29 @@ Zapret доступен как действие отдельной секции.
|
||||
- ручное управление очередью и fwmark: **`--qnum`**, **`--dpi-desync-fwmark`**;
|
||||
- режимы, которые ломают lifecycle процесса: **`--daemon`**;
|
||||
- режимы, которые не должны быть итоговой стратегией запуска: **`--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 доступен как действие отдельной секции. Используется [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-обхода.
|
||||
|
||||
|
||||
+578
-327
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PODKOP PLUS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-06-02 17:11+0300\n"
|
||||
"PO-Revision-Date: 2026-06-02 17:11+0300\n"
|
||||
"POT-Creation-Date: 2026-06-02 18:22+0300\n"
|
||||
"PO-Revision-Date: 2026-06-02 18:22+0300\n"
|
||||
"Last-Translator: ushan0v\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ru\n"
|
||||
@@ -299,6 +299,9 @@ msgstr "Отключить QUIC"
|
||||
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
|
||||
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"
|
||||
msgstr "DNS на роутере"
|
||||
|
||||
@@ -443,6 +446,9 @@ msgstr "Открыть эту секцию как локальный HTTP+SOCKS
|
||||
msgid "External nfqws config files bypass Podkop Plus queue management and explicit validation."
|
||||
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"
|
||||
msgstr "Не удалось закрыть подключение"
|
||||
|
||||
@@ -485,6 +491,9 @@ msgstr "Proxy protocol для фронтинга"
|
||||
msgid "Fully Routed IPs"
|
||||
msgstr "Полностью маршрутизированные IP-адреса"
|
||||
|
||||
msgid "Fuzzing is not supported here because Podkop Plus needs deterministic runtime validation."
|
||||
msgstr "Fuzzing здесь не поддерживается, потому что Podkop Plus нужна предсказуемая runtime-валидация."
|
||||
|
||||
msgid "Generated inbound"
|
||||
msgstr "Созданный inbound"
|
||||
|
||||
@@ -608,6 +617,9 @@ msgstr "Неверный IP-адрес"
|
||||
msgid "Invalid JSON format"
|
||||
msgstr "Неверный формат JSON"
|
||||
|
||||
msgid "Invalid NFQWS2 strategy"
|
||||
msgstr "Некорректная стратегия NFQWS2"
|
||||
|
||||
msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
|
||||
msgstr "Неверный формат пути. Путь должен начинаться с \"/\" и содержать допустимые символы"
|
||||
|
||||
@@ -803,6 +815,12 @@ msgstr "Стратегия NFQWS"
|
||||
msgid "NFQWS strategy cannot be empty"
|
||||
msgstr "Стратегия NFQWS не может быть пустой"
|
||||
|
||||
msgid "NFQWS2 Strategy"
|
||||
msgstr "Стратегия NFQWS2"
|
||||
|
||||
msgid "NFQWS2 strategy cannot be empty"
|
||||
msgstr "Стратегия NFQWS2 не может быть пустой"
|
||||
|
||||
msgid "No active connections"
|
||||
msgstr "Нет активных подключений"
|
||||
|
||||
@@ -827,6 +845,9 @@ msgstr "Нет правил, использующих ByeDPI"
|
||||
msgid "No rules use Zapret"
|
||||
msgstr "Нет правил, использующих Zapret"
|
||||
|
||||
msgid "No rules use Zapret2"
|
||||
msgstr "Нет правил, использующих Zapret2"
|
||||
|
||||
msgid "None"
|
||||
msgstr "Нет"
|
||||
|
||||
@@ -884,6 +905,9 @@ msgstr "Параметр \"%s\" содержит некорректное зна
|
||||
msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws."
|
||||
msgstr "Необязательное значение здесь нужно передавать только через '='; отдельный токен nfqws проигнорирует."
|
||||
|
||||
msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws2."
|
||||
msgstr "Необязательные значения здесь должны задаваться через '='; отдельный токен будет проигнорирован nfqws2."
|
||||
|
||||
msgid "Outbound JSON"
|
||||
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."
|
||||
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"
|
||||
msgstr "Правила Podkop Plus"
|
||||
|
||||
@@ -968,6 +995,15 @@ msgstr "NFQWS Podkop Plus не запущен"
|
||||
msgid "Podkop Plus-managed nfqws runtime is ready"
|
||||
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"
|
||||
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."
|
||||
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."
|
||||
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"
|
||||
msgstr "Перезапустить Podkop Plus"
|
||||
|
||||
@@ -1277,6 +1319,18 @@ msgstr "Отдельная служба Zapret активна"
|
||||
msgid "Standalone Zapret service is inactive"
|
||||
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"
|
||||
msgstr "Запустить Podkop Plus"
|
||||
|
||||
@@ -1346,6 +1400,9 @@ msgstr "Есть правила, использующие ByeDPI"
|
||||
msgid "There are rules using Zapret"
|
||||
msgstr "Есть правила использующие Zapret"
|
||||
|
||||
msgid "There are rules using Zapret2"
|
||||
msgstr "Есть правила, использующие Zapret2"
|
||||
|
||||
msgid "This field is required"
|
||||
msgstr "Заполните это поле"
|
||||
|
||||
@@ -1358,6 +1415,12 @@ msgstr "Это поле должно запускать рабочую стра
|
||||
msgid "This field must start a working nfqws strategy; --version exits immediately and is not allowed here."
|
||||
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."
|
||||
msgstr "Этот флаг не принимает значение."
|
||||
|
||||
@@ -1418,6 +1481,9 @@ msgstr "Не удалось проверить стратегию ByeDPI чер
|
||||
msgid "Unable to validate the NFQWS strategy through the backend parser."
|
||||
msgstr "Не удалось проверить стратегию NFQWS через backend-парсер."
|
||||
|
||||
msgid "Unable to validate the NFQWS2 strategy through the backend parser."
|
||||
msgstr "Не удалось проверить стратегию NFQWS2 через backend-парсер."
|
||||
|
||||
msgid "Unexpected ByeDPI strategy argument: %s"
|
||||
msgstr "Неожиданный аргумент стратегии ByeDPI: %s"
|
||||
|
||||
@@ -1427,6 +1493,9 @@ msgstr "Обнаружен лишний ciadpi Podkop Plus"
|
||||
msgid "Unexpected Podkop Plus-managed nfqws runtime is running"
|
||||
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."
|
||||
msgstr "Неожиданный отдельный токен. Используйте явные флаги вида --имя или --имя=значение."
|
||||
|
||||
@@ -1448,6 +1517,9 @@ msgstr "Неизвестная ошибка"
|
||||
msgid "Unknown NFQWS flag."
|
||||
msgstr "Неизвестный флаг NFQWS."
|
||||
|
||||
msgid "Unknown NFQWS2 flag."
|
||||
msgstr "Неизвестный флаг NFQWS2."
|
||||
|
||||
msgid "Unsupported security mode for this protocol"
|
||||
msgstr "Этот протокол не поддерживает выбранный режим защиты"
|
||||
|
||||
@@ -1591,3 +1663,30 @@ msgstr "Бинарник провайдера Zapret доступен"
|
||||
|
||||
msgid "Zapret provider binary is not available"
|
||||
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>(
|
||||
Podkop.AvailableMethods.CHECK_ZAPRET_RUNTIME,
|
||||
),
|
||||
checkZapret2Runtime: async () =>
|
||||
callBaseMethod<Podkop.Zapret2CheckResult>(
|
||||
Podkop.AvailableMethods.CHECK_ZAPRET2_RUNTIME,
|
||||
),
|
||||
checkByedpiRuntime: async () =>
|
||||
callBaseMethod<Podkop.ByedpiCheckResult>(
|
||||
Podkop.AvailableMethods.CHECK_BYEDPI_RUNTIME,
|
||||
@@ -180,6 +184,10 @@ export const PodkopShellMethods = {
|
||||
callBaseMethod<Podkop.GetZapretStatus>(
|
||||
Podkop.AvailableMethods.GET_ZAPRET_STATUS,
|
||||
),
|
||||
getZapret2Status: async () =>
|
||||
callBaseMethod<Podkop.GetZapret2Status>(
|
||||
Podkop.AvailableMethods.GET_ZAPRET2_STATUS,
|
||||
),
|
||||
getByedpiStatus: async () =>
|
||||
callBaseMethod<Podkop.GetByedpiStatus>(
|
||||
Podkop.AvailableMethods.GET_BYEDPI_STATUS,
|
||||
|
||||
@@ -196,6 +196,8 @@ export interface StoreType {
|
||||
sing_box_extended: number;
|
||||
zapret_version: string;
|
||||
zapret_installed: number;
|
||||
zapret2_version: string;
|
||||
zapret2_installed: number;
|
||||
byedpi_version: string;
|
||||
byedpi_installed: number;
|
||||
server_inbounds_enabled_count: number;
|
||||
@@ -212,6 +214,9 @@ export interface StoreType {
|
||||
zapretCheck: { loading: boolean };
|
||||
zapretInstall: { loading: boolean };
|
||||
zapretRemove: { loading: boolean };
|
||||
zapret2Check: { loading: boolean };
|
||||
zapret2Install: { loading: boolean };
|
||||
zapret2Remove: { loading: boolean };
|
||||
byedpiCheck: { loading: boolean };
|
||||
byedpiInstall: { loading: boolean };
|
||||
byedpiRemove: { loading: boolean };
|
||||
|
||||
@@ -13,6 +13,8 @@ const UNKNOWN_SYSTEM_INFO: StoreType['diagnosticsSystemInfo'] = {
|
||||
sing_box_extended: 0,
|
||||
zapret_version: _('unknown'),
|
||||
zapret_installed: 0,
|
||||
zapret2_version: _('unknown'),
|
||||
zapret2_installed: 0,
|
||||
byedpi_version: _('unknown'),
|
||||
byedpi_installed: 0,
|
||||
server_inbounds_enabled_count: -1,
|
||||
@@ -94,6 +96,7 @@ export async function ensureSystemInfo({
|
||||
loaded: false,
|
||||
providerInfoLoaded: latestSystemInfo.providerInfoLoaded,
|
||||
zapret_installed: latestSystemInfo.zapret_installed,
|
||||
zapret2_installed: latestSystemInfo.zapret2_installed,
|
||||
byedpi_installed: latestSystemInfo.byedpi_installed,
|
||||
server_inbounds_enabled_count:
|
||||
latestSystemInfo.server_inbounds_enabled_count,
|
||||
|
||||
@@ -5,6 +5,7 @@ export enum DIAGNOSTICS_CHECKS {
|
||||
SINGBOX = 'SINGBOX',
|
||||
NFT = 'NFT',
|
||||
ZAPRET = 'ZAPRET',
|
||||
ZAPRET2 = 'ZAPRET2',
|
||||
BYEDPI = 'BYEDPI',
|
||||
FAKEIP = 'FAKEIP',
|
||||
OUTBOUNDS = 'OUTBOUNDS',
|
||||
@@ -36,17 +37,22 @@ export const DIAGNOSTICS_CHECKS_MAP: Record<
|
||||
code: DIAGNOSTICS_CHECKS.ZAPRET,
|
||||
},
|
||||
[DIAGNOSTICS_CHECKS.BYEDPI]: {
|
||||
order: 6,
|
||||
order: 7,
|
||||
title: getCheckTitle('ByeDPI'),
|
||||
code: DIAGNOSTICS_CHECKS.BYEDPI,
|
||||
},
|
||||
[DIAGNOSTICS_CHECKS.ZAPRET2]: {
|
||||
order: 6,
|
||||
title: getCheckTitle('Zapret2'),
|
||||
code: DIAGNOSTICS_CHECKS.ZAPRET2,
|
||||
},
|
||||
[DIAGNOSTICS_CHECKS.OUTBOUNDS]: {
|
||||
order: 7,
|
||||
order: 8,
|
||||
title: getCheckTitle('Outbounds'),
|
||||
code: DIAGNOSTICS_CHECKS.OUTBOUNDS,
|
||||
},
|
||||
[DIAGNOSTICS_CHECKS.FAKEIP]: {
|
||||
order: 8,
|
||||
order: 9,
|
||||
title: getCheckTitle('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 {
|
||||
includeZapret?: boolean;
|
||||
includeZapret2?: boolean;
|
||||
includeByedpi?: boolean;
|
||||
includeInbounds?: boolean;
|
||||
}
|
||||
@@ -42,6 +43,10 @@ export function getDiagnosticsChecks(
|
||||
checks.push(DIAGNOSTICS_CHECKS.ZAPRET);
|
||||
}
|
||||
|
||||
if (options.includeZapret2) {
|
||||
checks.push(DIAGNOSTICS_CHECKS.ZAPRET2);
|
||||
}
|
||||
|
||||
if (options.includeByedpi) {
|
||||
checks.push(DIAGNOSTICS_CHECKS.BYEDPI);
|
||||
}
|
||||
@@ -79,6 +84,8 @@ export const initialDiagnosticStore: Pick<
|
||||
sing_box_extended: 0,
|
||||
zapret_version: 'loading',
|
||||
zapret_installed: 0,
|
||||
zapret2_version: 'loading',
|
||||
zapret2_installed: 0,
|
||||
byedpi_version: 'loading',
|
||||
byedpi_installed: 0,
|
||||
server_inbounds_enabled_count: -1,
|
||||
@@ -123,6 +130,9 @@ export const initialDiagnosticStore: Pick<
|
||||
zapretCheck: { loading: false },
|
||||
zapretInstall: { loading: false },
|
||||
zapretRemove: { loading: false },
|
||||
zapret2Check: { loading: false },
|
||||
zapret2Install: { loading: false },
|
||||
zapret2Remove: { loading: false },
|
||||
byedpiCheck: { loading: false },
|
||||
byedpiInstall: { loading: false },
|
||||
byedpiRemove: { loading: false },
|
||||
@@ -131,6 +141,7 @@ export const initialDiagnosticStore: Pick<
|
||||
podkop: { status: null, latest_version: '', release_url: '' },
|
||||
sing_box: { 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: '' },
|
||||
},
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import { runInboundsCheck } from './checks/runInboundsCheck';
|
||||
import { runNftCheck } from './checks/runNftCheck';
|
||||
import { runFakeIPCheck } from './checks/runFakeIPCheck';
|
||||
import { runZapretCheck } from './checks/runZapretCheck';
|
||||
import { runZapret2Check } from './checks/runZapret2Check';
|
||||
import { runByedpiCheck } from './checks/runByedpiCheck';
|
||||
import {
|
||||
DiagnosticsProviderOptions,
|
||||
@@ -47,11 +48,15 @@ function sleep(ms: number) {
|
||||
function getDiagnosticsProviderOptions(
|
||||
systemInfo: Pick<
|
||||
StoreType['diagnosticsSystemInfo'],
|
||||
'zapret_installed' | 'byedpi_installed' | 'server_inbounds_enabled_count'
|
||||
| 'zapret_installed'
|
||||
| 'zapret2_installed'
|
||||
| 'byedpi_installed'
|
||||
| 'server_inbounds_enabled_count'
|
||||
> = store.get().diagnosticsSystemInfo,
|
||||
): DiagnosticsProviderOptions {
|
||||
return {
|
||||
includeZapret: Boolean(systemInfo.zapret_installed),
|
||||
includeZapret2: Boolean(systemInfo.zapret2_installed),
|
||||
includeByedpi: Boolean(systemInfo.byedpi_installed),
|
||||
includeInbounds: systemInfo.server_inbounds_enabled_count !== 0,
|
||||
};
|
||||
@@ -220,11 +225,13 @@ async function fetchDiagnosticsProviderInfo({
|
||||
const requestId = ++latestProviderInfoRequestId;
|
||||
|
||||
try {
|
||||
const [zapretRuntime, byedpiRuntime, inboundsConfig] = await Promise.all([
|
||||
PodkopShellMethods.checkZapretRuntime(),
|
||||
PodkopShellMethods.checkByedpiRuntime(),
|
||||
PodkopShellMethods.checkInboundsConfig(),
|
||||
]);
|
||||
const [zapretRuntime, zapret2Runtime, byedpiRuntime, inboundsConfig] =
|
||||
await Promise.all([
|
||||
PodkopShellMethods.checkZapretRuntime(),
|
||||
PodkopShellMethods.checkZapret2Runtime(),
|
||||
PodkopShellMethods.checkByedpiRuntime(),
|
||||
PodkopShellMethods.checkInboundsConfig(),
|
||||
]);
|
||||
|
||||
if (requestId !== latestProviderInfoRequestId) {
|
||||
return;
|
||||
@@ -237,6 +244,9 @@ async function fetchDiagnosticsProviderInfo({
|
||||
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,
|
||||
@@ -249,6 +259,14 @@ async function fetchDiagnosticsProviderInfo({
|
||||
logger.error('[DIAGNOSTIC]', 'fetchZapretRuntime failed', zapretRuntime);
|
||||
}
|
||||
|
||||
if (!zapret2Runtime.success) {
|
||||
logger.error(
|
||||
'[DIAGNOSTIC]',
|
||||
'fetchZapret2Runtime failed',
|
||||
zapret2Runtime,
|
||||
);
|
||||
}
|
||||
|
||||
if (!byedpiRuntime.success) {
|
||||
logger.error('[DIAGNOSTIC]', 'fetchByedpiRuntime failed', byedpiRuntime);
|
||||
}
|
||||
@@ -265,6 +283,10 @@ async function fetchDiagnosticsProviderInfo({
|
||||
nextSystemInfo.zapret_version = 'not installed';
|
||||
}
|
||||
|
||||
if (!nextSystemInfo.zapret2_installed) {
|
||||
nextSystemInfo.zapret2_version = 'not installed';
|
||||
}
|
||||
|
||||
if (!nextSystemInfo.byedpi_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) {
|
||||
items.push({
|
||||
key: 'ByeDPI',
|
||||
@@ -702,6 +731,7 @@ async function runChecks() {
|
||||
...(providerOptions.includeInbounds ? [runInboundsCheck] : []),
|
||||
runNftCheck,
|
||||
...(providerOptions.includeZapret ? [runZapretCheck] : []),
|
||||
...(providerOptions.includeZapret2 ? [runZapret2Check] : []),
|
||||
...(providerOptions.includeByedpi ? [runByedpiCheck] : []),
|
||||
runSectionsCheck,
|
||||
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';
|
||||
|
||||
type UpdatesActionKey = keyof StoreType['updatesActions'];
|
||||
type UpdateStatus =
|
||||
StoreType['updatesChecks'][Podkop.ComponentName]['status'];
|
||||
type UpdateStatus = StoreType['updatesChecks'][Podkop.ComponentName]['status'];
|
||||
|
||||
interface ComponentActionButton {
|
||||
key: UpdatesActionKey;
|
||||
@@ -138,7 +137,9 @@ function getExpectedLatestVersionForAction(button: ComponentActionButton) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return store.get().updatesChecks[button.component].latest_version || undefined;
|
||||
return (
|
||||
store.get().updatesChecks[button.component].latest_version || undefined
|
||||
);
|
||||
}
|
||||
|
||||
function getCheckToastMessage(status: UpdateStatus) {
|
||||
@@ -168,6 +169,7 @@ function notifyActionProvidersAvailabilityChanged(
|
||||
new CustomEvent(PODKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT, {
|
||||
detail: {
|
||||
zapretInstalled: Boolean(systemInfo.zapret_installed),
|
||||
zapret2Installed: Boolean(systemInfo.zapret2_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') {
|
||||
nextSystemInfo.providerInfoLoaded = true;
|
||||
|
||||
@@ -230,7 +244,11 @@ function patchSystemInfoAfterMutation(result: Podkop.ComponentActionResult) {
|
||||
diagnosticsSystemInfo: nextSystemInfo,
|
||||
});
|
||||
|
||||
if (result.component === 'zapret' || result.component === 'byedpi') {
|
||||
if (
|
||||
result.component === 'zapret' ||
|
||||
result.component === 'zapret2' ||
|
||||
result.component === 'byedpi'
|
||||
) {
|
||||
notifyActionProvidersAvailabilityChanged(nextSystemInfo);
|
||||
}
|
||||
}
|
||||
@@ -332,6 +350,7 @@ function getComponentCards(): ComponentCard[] {
|
||||
const systemInfo = store.get().diagnosticsSystemInfo;
|
||||
const systemInfoLoading = isSystemInfoLoading();
|
||||
const zapretInstalled = Boolean(systemInfo.zapret_installed);
|
||||
const zapret2Installed = Boolean(systemInfo.zapret2_installed);
|
||||
const byedpiInstalled = Boolean(systemInfo.byedpi_installed);
|
||||
const singBoxInstallAction: ComponentActionButton =
|
||||
systemInfo.sing_box_extended
|
||||
@@ -368,11 +387,7 @@ function getComponentCards(): ComponentCard[] {
|
||||
releaseUrl: getGitHubReleaseUrl('sing_box'),
|
||||
tag: getCheckTag('sing_box'),
|
||||
actions: [
|
||||
getPrimaryUpdateAction(
|
||||
'sing_box',
|
||||
'singBoxCheck',
|
||||
'singBoxInstall',
|
||||
),
|
||||
getPrimaryUpdateAction('sing_box', 'singBoxCheck', 'singBoxInstall'),
|
||||
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',
|
||||
version: systemInfoLoading
|
||||
@@ -503,11 +548,7 @@ function renderComponentCard(card: ComponentCard) {
|
||||
}
|
||||
|
||||
return E('div', { class: 'pdk_updates-page__component' }, [
|
||||
E(
|
||||
'div',
|
||||
{ class: 'pdk_updates-page__component__header' },
|
||||
headerChildren,
|
||||
),
|
||||
E('div', { class: 'pdk_updates-page__component__header' }, headerChildren),
|
||||
E('div', { class: 'pdk_updates-page__component__version' }, [
|
||||
E(
|
||||
'span',
|
||||
|
||||
@@ -56,6 +56,7 @@ export namespace Podkop {
|
||||
CHECK_FAKEIP = 'check_fakeip',
|
||||
CHECK_NFT_RULES = 'check_nft_rules',
|
||||
CHECK_ZAPRET_RUNTIME = 'check_zapret_runtime',
|
||||
CHECK_ZAPRET2_RUNTIME = 'check_zapret2_runtime',
|
||||
CHECK_BYEDPI_RUNTIME = 'check_byedpi_runtime',
|
||||
CHECK_INBOUNDS_CONFIG = 'check_inbounds_config',
|
||||
GET_STATUS = 'get_status',
|
||||
@@ -67,6 +68,7 @@ export namespace Podkop {
|
||||
CHECK_INBOUNDS = 'check_inbounds',
|
||||
GET_SING_BOX_STATUS = 'get_sing_box_status',
|
||||
GET_ZAPRET_STATUS = 'get_zapret_status',
|
||||
GET_ZAPRET2_STATUS = 'get_zapret2_status',
|
||||
GET_BYEDPI_STATUS = 'get_byedpi_status',
|
||||
CLASH_API = 'clash_api',
|
||||
RESTART = 'restart',
|
||||
@@ -153,6 +155,7 @@ export namespace Podkop {
|
||||
| 'direct'
|
||||
| 'block'
|
||||
| 'zapret'
|
||||
| 'zapret2'
|
||||
| 'byedpi';
|
||||
type LegacyConnectionType = 'proxy' | 'vpn' | 'block' | 'exclusion';
|
||||
type ProxyConfigType =
|
||||
@@ -179,6 +182,7 @@ export namespace Podkop {
|
||||
update_interval?: string;
|
||||
proxy_string?: string;
|
||||
nfqws_opt?: string;
|
||||
nfqws2_opt?: string;
|
||||
byedpi_cmd_opts?: string;
|
||||
cmd_opts?: string;
|
||||
selector_proxy_links?: string[];
|
||||
@@ -326,6 +330,8 @@ export namespace Podkop {
|
||||
sing_box_extended: 0 | 1;
|
||||
zapret_version: string;
|
||||
zapret_installed: 0 | 1;
|
||||
zapret2_version: string;
|
||||
zapret2_installed: 0 | 1;
|
||||
byedpi_version: string;
|
||||
byedpi_installed: 0 | 1;
|
||||
openwrt_version: string;
|
||||
@@ -340,11 +346,17 @@ export namespace Podkop {
|
||||
export interface GetUiCapabilities {
|
||||
sing_box_extended: 0 | 1;
|
||||
zapret_installed: 0 | 1;
|
||||
zapret2_installed: 0 | 1;
|
||||
byedpi_installed: 0 | 1;
|
||||
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 =
|
||||
| 'check_update'
|
||||
@@ -422,6 +434,40 @@ export namespace Podkop {
|
||||
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 {
|
||||
installed: 0 | 1;
|
||||
package_installed: 0 | 1;
|
||||
|
||||
@@ -549,6 +549,12 @@ function validateHysteria2Url(url) {
|
||||
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") };
|
||||
} catch (_e) {
|
||||
@@ -2074,6 +2080,7 @@ var Podkop;
|
||||
AvailableMethods2["CHECK_FAKEIP"] = "check_fakeip";
|
||||
AvailableMethods2["CHECK_NFT_RULES"] = "check_nft_rules";
|
||||
AvailableMethods2["CHECK_ZAPRET_RUNTIME"] = "check_zapret_runtime";
|
||||
AvailableMethods2["CHECK_ZAPRET2_RUNTIME"] = "check_zapret2_runtime";
|
||||
AvailableMethods2["CHECK_BYEDPI_RUNTIME"] = "check_byedpi_runtime";
|
||||
AvailableMethods2["CHECK_INBOUNDS_CONFIG"] = "check_inbounds_config";
|
||||
AvailableMethods2["GET_STATUS"] = "get_status";
|
||||
@@ -2085,6 +2092,7 @@ var Podkop;
|
||||
AvailableMethods2["CHECK_INBOUNDS"] = "check_inbounds";
|
||||
AvailableMethods2["GET_SING_BOX_STATUS"] = "get_sing_box_status";
|
||||
AvailableMethods2["GET_ZAPRET_STATUS"] = "get_zapret_status";
|
||||
AvailableMethods2["GET_ZAPRET2_STATUS"] = "get_zapret2_status";
|
||||
AvailableMethods2["GET_BYEDPI_STATUS"] = "get_byedpi_status";
|
||||
AvailableMethods2["CLASH_API"] = "clash_api";
|
||||
AvailableMethods2["RESTART"] = "restart";
|
||||
@@ -2216,6 +2224,9 @@ var PodkopShellMethods = {
|
||||
checkZapretRuntime: async () => callBaseMethod(
|
||||
Podkop.AvailableMethods.CHECK_ZAPRET_RUNTIME
|
||||
),
|
||||
checkZapret2Runtime: async () => callBaseMethod(
|
||||
Podkop.AvailableMethods.CHECK_ZAPRET2_RUNTIME
|
||||
),
|
||||
checkByedpiRuntime: async () => callBaseMethod(
|
||||
Podkop.AvailableMethods.CHECK_BYEDPI_RUNTIME
|
||||
),
|
||||
@@ -2251,6 +2262,9 @@ var PodkopShellMethods = {
|
||||
getZapretStatus: async () => callBaseMethod(
|
||||
Podkop.AvailableMethods.GET_ZAPRET_STATUS
|
||||
),
|
||||
getZapret2Status: async () => callBaseMethod(
|
||||
Podkop.AvailableMethods.GET_ZAPRET2_STATUS
|
||||
),
|
||||
getByedpiStatus: async () => callBaseMethod(
|
||||
Podkop.AvailableMethods.GET_BYEDPI_STATUS
|
||||
),
|
||||
@@ -3020,17 +3034,22 @@ var DIAGNOSTICS_CHECKS_MAP = {
|
||||
code: "ZAPRET" /* ZAPRET */
|
||||
},
|
||||
["BYEDPI" /* BYEDPI */]: {
|
||||
order: 6,
|
||||
order: 7,
|
||||
title: getCheckTitle("ByeDPI"),
|
||||
code: "BYEDPI" /* BYEDPI */
|
||||
},
|
||||
["ZAPRET2" /* ZAPRET2 */]: {
|
||||
order: 6,
|
||||
title: getCheckTitle("Zapret2"),
|
||||
code: "ZAPRET2" /* ZAPRET2 */
|
||||
},
|
||||
["OUTBOUNDS" /* OUTBOUNDS */]: {
|
||||
order: 7,
|
||||
order: 8,
|
||||
title: getCheckTitle("Outbounds"),
|
||||
code: "OUTBOUNDS" /* OUTBOUNDS */
|
||||
},
|
||||
["FAKEIP" /* FAKEIP */]: {
|
||||
order: 8,
|
||||
order: 9,
|
||||
title: getCheckTitle("FakeIP"),
|
||||
code: "FAKEIP" /* FAKEIP */
|
||||
},
|
||||
@@ -3062,6 +3081,9 @@ function getDiagnosticsChecks(description, options = {}) {
|
||||
if (options.includeZapret) {
|
||||
checks.push("ZAPRET" /* ZAPRET */);
|
||||
}
|
||||
if (options.includeZapret2) {
|
||||
checks.push("ZAPRET2" /* ZAPRET2 */);
|
||||
}
|
||||
if (options.includeByedpi) {
|
||||
checks.push("BYEDPI" /* BYEDPI */);
|
||||
}
|
||||
@@ -3085,6 +3107,8 @@ var initialDiagnosticStore = {
|
||||
sing_box_extended: 0,
|
||||
zapret_version: "loading",
|
||||
zapret_installed: 0,
|
||||
zapret2_version: "loading",
|
||||
zapret2_installed: 0,
|
||||
byedpi_version: "loading",
|
||||
byedpi_installed: 0,
|
||||
server_inbounds_enabled_count: -1,
|
||||
@@ -3129,6 +3153,9 @@ var initialDiagnosticStore = {
|
||||
zapretCheck: { loading: false },
|
||||
zapretInstall: { loading: false },
|
||||
zapretRemove: { loading: false },
|
||||
zapret2Check: { loading: false },
|
||||
zapret2Install: { loading: false },
|
||||
zapret2Remove: { loading: false },
|
||||
byedpiCheck: { loading: false },
|
||||
byedpiInstall: { loading: false },
|
||||
byedpiRemove: { loading: false }
|
||||
@@ -3137,6 +3164,7 @@ var initialDiagnosticStore = {
|
||||
podkop: { status: null, latest_version: "", release_url: "" },
|
||||
sing_box: { 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: "" }
|
||||
}
|
||||
};
|
||||
@@ -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
|
||||
async function runByedpiCheck() {
|
||||
const { order, title, code } = DIAGNOSTICS_CHECKS_MAP.BYEDPI;
|
||||
@@ -5387,6 +5527,8 @@ var UNKNOWN_SYSTEM_INFO = {
|
||||
sing_box_extended: 0,
|
||||
zapret_version: _("unknown"),
|
||||
zapret_installed: 0,
|
||||
zapret2_version: _("unknown"),
|
||||
zapret2_installed: 0,
|
||||
byedpi_version: _("unknown"),
|
||||
byedpi_installed: 0,
|
||||
server_inbounds_enabled_count: -1,
|
||||
@@ -5450,6 +5592,7 @@ async function ensureSystemInfo({
|
||||
loaded: false,
|
||||
providerInfoLoaded: latestSystemInfo.providerInfoLoaded,
|
||||
zapret_installed: latestSystemInfo.zapret_installed,
|
||||
zapret2_installed: latestSystemInfo.zapret2_installed,
|
||||
byedpi_installed: latestSystemInfo.byedpi_installed,
|
||||
server_inbounds_enabled_count: latestSystemInfo.server_inbounds_enabled_count
|
||||
};
|
||||
@@ -6268,6 +6411,7 @@ function sleep2(ms) {
|
||||
function getDiagnosticsProviderOptions(systemInfo = store.get().diagnosticsSystemInfo) {
|
||||
return {
|
||||
includeZapret: Boolean(systemInfo.zapret_installed),
|
||||
includeZapret2: Boolean(systemInfo.zapret2_installed),
|
||||
includeByedpi: Boolean(systemInfo.byedpi_installed),
|
||||
includeInbounds: systemInfo.server_inbounds_enabled_count !== 0
|
||||
};
|
||||
@@ -6391,8 +6535,9 @@ async function fetchDiagnosticsProviderInfo({
|
||||
} = {}) {
|
||||
const requestId = ++latestProviderInfoRequestId;
|
||||
try {
|
||||
const [zapretRuntime, byedpiRuntime, inboundsConfig] = await Promise.all([
|
||||
const [zapretRuntime, zapret2Runtime, byedpiRuntime, inboundsConfig] = await Promise.all([
|
||||
PodkopShellMethods.checkZapretRuntime(),
|
||||
PodkopShellMethods.checkZapret2Runtime(),
|
||||
PodkopShellMethods.checkByedpiRuntime(),
|
||||
PodkopShellMethods.checkInboundsConfig()
|
||||
]);
|
||||
@@ -6404,12 +6549,20 @@ async function fetchDiagnosticsProviderInfo({
|
||||
...currentSystemInfo,
|
||||
providerInfoLoaded: true,
|
||||
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,
|
||||
server_inbounds_enabled_count: inboundsConfig.success ? inboundsConfig.data.enabled_count : -1
|
||||
};
|
||||
if (!zapretRuntime.success) {
|
||||
logger.error("[DIAGNOSTIC]", "fetchZapretRuntime failed", zapretRuntime);
|
||||
}
|
||||
if (!zapret2Runtime.success) {
|
||||
logger.error(
|
||||
"[DIAGNOSTIC]",
|
||||
"fetchZapret2Runtime failed",
|
||||
zapret2Runtime
|
||||
);
|
||||
}
|
||||
if (!byedpiRuntime.success) {
|
||||
logger.error("[DIAGNOSTIC]", "fetchByedpiRuntime failed", byedpiRuntime);
|
||||
}
|
||||
@@ -6423,6 +6576,9 @@ async function fetchDiagnosticsProviderInfo({
|
||||
if (!nextSystemInfo.zapret_installed) {
|
||||
nextSystemInfo.zapret_version = "not installed";
|
||||
}
|
||||
if (!nextSystemInfo.zapret2_installed) {
|
||||
nextSystemInfo.zapret2_version = "not installed";
|
||||
}
|
||||
if (!nextSystemInfo.byedpi_installed) {
|
||||
nextSystemInfo.byedpi_version = "not installed";
|
||||
}
|
||||
@@ -6727,6 +6883,12 @@ function renderDiagnosticSystemInfoWidget() {
|
||||
value: diagnosticsSystemInfo.zapret_version
|
||||
});
|
||||
}
|
||||
if (diagnosticsSystemInfo.zapret2_installed) {
|
||||
items.push({
|
||||
key: "Zapret2",
|
||||
value: diagnosticsSystemInfo.zapret2_version
|
||||
});
|
||||
}
|
||||
if (diagnosticsSystemInfo.byedpi_installed) {
|
||||
items.push({
|
||||
key: "ByeDPI",
|
||||
@@ -6781,6 +6943,7 @@ async function runChecks() {
|
||||
...providerOptions.includeInbounds ? [runInboundsCheck] : [],
|
||||
runNftCheck,
|
||||
...providerOptions.includeZapret ? [runZapretCheck] : [],
|
||||
...providerOptions.includeZapret2 ? [runZapret2Check] : [],
|
||||
...providerOptions.includeByedpi ? [runByedpiCheck] : [],
|
||||
runSectionsCheck,
|
||||
runFakeIPCheck
|
||||
@@ -8914,6 +9077,7 @@ function notifyActionProvidersAvailabilityChanged(systemInfo) {
|
||||
new CustomEvent(PODKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT, {
|
||||
detail: {
|
||||
zapretInstalled: Boolean(systemInfo.zapret_installed),
|
||||
zapret2Installed: Boolean(systemInfo.zapret2_installed),
|
||||
byedpiInstalled: Boolean(systemInfo.byedpi_installed)
|
||||
}
|
||||
})
|
||||
@@ -8950,6 +9114,16 @@ function patchSystemInfoAfterMutation(result) {
|
||||
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") {
|
||||
nextSystemInfo.providerInfoLoaded = true;
|
||||
if (result.action === "remove") {
|
||||
@@ -8963,7 +9137,7 @@ function patchSystemInfoAfterMutation(result) {
|
||||
store.set({
|
||||
diagnosticsSystemInfo: nextSystemInfo
|
||||
});
|
||||
if (result.component === "zapret" || result.component === "byedpi") {
|
||||
if (result.component === "zapret" || result.component === "zapret2" || result.component === "byedpi") {
|
||||
notifyActionProvidersAvailabilityChanged(nextSystemInfo);
|
||||
}
|
||||
}
|
||||
@@ -9044,6 +9218,7 @@ function getComponentCards() {
|
||||
const systemInfo = store.get().diagnosticsSystemInfo;
|
||||
const systemInfoLoading = isSystemInfoLoading();
|
||||
const zapretInstalled = Boolean(systemInfo.zapret_installed);
|
||||
const zapret2Installed = Boolean(systemInfo.zapret2_installed);
|
||||
const byedpiInstalled = Boolean(systemInfo.byedpi_installed);
|
||||
const singBoxInstallAction = systemInfo.sing_box_extended ? {
|
||||
key: "singBoxInstallStable",
|
||||
@@ -9074,11 +9249,7 @@ function getComponentCards() {
|
||||
releaseUrl: getGitHubReleaseUrl("sing_box"),
|
||||
tag: getCheckTag("sing_box"),
|
||||
actions: [
|
||||
getPrimaryUpdateAction(
|
||||
"sing_box",
|
||||
"singBoxCheck",
|
||||
"singBoxInstall"
|
||||
),
|
||||
getPrimaryUpdateAction("sing_box", "singBoxCheck", "singBoxInstall"),
|
||||
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",
|
||||
version: systemInfoLoading ? "loading" : byedpiInstalled ? systemInfo.byedpi_version : _("Not installed"),
|
||||
@@ -9184,11 +9379,7 @@ function renderComponentCard(card) {
|
||||
);
|
||||
}
|
||||
return E("div", { class: "pdk_updates-page__component" }, [
|
||||
E(
|
||||
"div",
|
||||
{ class: "pdk_updates-page__component__header" },
|
||||
headerChildren
|
||||
),
|
||||
E("div", { class: "pdk_updates-page__component__header" }, headerChildren),
|
||||
E("div", { class: "pdk_updates-page__component__version" }, [
|
||||
E(
|
||||
"span",
|
||||
|
||||
@@ -105,6 +105,7 @@ const EntryPoint = {
|
||||
loaded: false,
|
||||
singBoxExtended: false,
|
||||
zapretInstalled: false,
|
||||
zapret2Installed: false,
|
||||
byedpiInstalled: false,
|
||||
serverInboundsEnabledCount: -1,
|
||||
};
|
||||
@@ -121,6 +122,7 @@ const EntryPoint = {
|
||||
new CustomEvent(main.PODKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT, {
|
||||
detail: {
|
||||
zapretInstalled: uiCapabilities.zapretInstalled,
|
||||
zapret2Installed: uiCapabilities.zapret2Installed,
|
||||
byedpiInstalled: uiCapabilities.byedpiInstalled,
|
||||
},
|
||||
}),
|
||||
@@ -135,12 +137,16 @@ const EntryPoint = {
|
||||
providerInfoLoaded: true,
|
||||
sing_box_extended: uiCapabilities.singBoxExtended ? 1 : 0,
|
||||
zapret_installed: uiCapabilities.zapretInstalled ? 1 : 0,
|
||||
zapret2_installed: uiCapabilities.zapret2Installed ? 1 : 0,
|
||||
byedpi_installed: uiCapabilities.byedpiInstalled ? 1 : 0,
|
||||
server_inbounds_enabled_count:
|
||||
uiCapabilities.serverInboundsEnabledCount,
|
||||
zapret_version: uiCapabilities.zapretInstalled
|
||||
? currentSystemInfo.zapret_version
|
||||
: "not installed",
|
||||
zapret2_version: uiCapabilities.zapret2Installed
|
||||
? currentSystemInfo.zapret2_version
|
||||
: "not installed",
|
||||
byedpi_version: uiCapabilities.byedpiInstalled
|
||||
? currentSystemInfo.byedpi_version
|
||||
: "not installed",
|
||||
@@ -157,6 +163,9 @@ const EntryPoint = {
|
||||
uiCapabilities.zapretInstalled = Boolean(
|
||||
Number(data?.zapret_installed) === 1,
|
||||
);
|
||||
uiCapabilities.zapret2Installed = Boolean(
|
||||
Number(data?.zapret2_installed) === 1,
|
||||
);
|
||||
uiCapabilities.byedpiInstalled = Boolean(
|
||||
Number(data?.byedpi_installed) === 1,
|
||||
);
|
||||
@@ -179,12 +188,14 @@ const EntryPoint = {
|
||||
return Promise.allSettled([
|
||||
main.PodkopShellMethods.getServerCapabilities(),
|
||||
main.PodkopShellMethods.checkZapretRuntime(),
|
||||
main.PodkopShellMethods.checkZapret2Runtime(),
|
||||
main.PodkopShellMethods.checkByedpiRuntime(),
|
||||
main.PodkopShellMethods.checkInboundsConfig(),
|
||||
]).then(
|
||||
([
|
||||
serverCapabilitiesResult,
|
||||
zapretRuntimeResult,
|
||||
zapret2RuntimeResult,
|
||||
byedpiRuntimeResult,
|
||||
inboundsConfigResult,
|
||||
]) => {
|
||||
@@ -196,6 +207,10 @@ const EntryPoint = {
|
||||
zapretRuntimeResult.status === "fulfilled"
|
||||
? zapretRuntimeResult.value
|
||||
: null;
|
||||
const zapret2Runtime =
|
||||
zapret2RuntimeResult.status === "fulfilled"
|
||||
? zapret2RuntimeResult.value
|
||||
: null;
|
||||
const byedpiRuntime =
|
||||
byedpiRuntimeResult.status === "fulfilled"
|
||||
? byedpiRuntimeResult.value
|
||||
@@ -216,6 +231,11 @@ const EntryPoint = {
|
||||
Number(zapretRuntime.data?.zapret_installed) === 1
|
||||
? 1
|
||||
: 0,
|
||||
zapret2_installed:
|
||||
zapret2Runtime?.success &&
|
||||
Number(zapret2Runtime.data?.zapret2_installed) === 1
|
||||
? 1
|
||||
: 0,
|
||||
byedpi_installed:
|
||||
byedpiRuntime?.success &&
|
||||
Number(byedpiRuntime.data?.byedpi_installed) === 1
|
||||
|
||||
@@ -49,12 +49,17 @@ const ZAPRET_LEGACY_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";
|
||||
|
||||
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 ANNOTATED_TEXTAREA_STYLE_ID = "pdk-annotated-textarea-styles";
|
||||
const NFQWS_REMOTE_VALIDATION_DEBOUNCE_MS = 500;
|
||||
const NFQWS_VALIDATION_COMMAND = "/usr/bin/podkop-plus";
|
||||
const nfqwsRemoteValidationCache = new Map();
|
||||
const nfqwsRemoteValidationInflight = new Map();
|
||||
const nfqws2RemoteValidationCache = new Map();
|
||||
const nfqws2RemoteValidationInflight = new Map();
|
||||
const byedpiRemoteValidationCache = new Map();
|
||||
const byedpiRemoteValidationInflight = new Map();
|
||||
const BYEDPI_LONG_VALUE_OPTIONS = new Set([
|
||||
@@ -245,9 +250,70 @@ const NFQWS_REQUIRED_ARG_OPTIONS = new Set([
|
||||
"--wssize-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 = {
|
||||
loaded: false,
|
||||
zapretInstalled: false,
|
||||
zapret2Installed: false,
|
||||
byedpiInstalled: false,
|
||||
};
|
||||
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") {
|
||||
actionProvidersAvailabilityState.byedpiInstalled = Boolean(
|
||||
nextState.byedpiInstalled,
|
||||
@@ -291,6 +363,7 @@ function updateActionProvidersAvailabilityFromSystemInfo(systemInfo) {
|
||||
|
||||
updateActionProvidersAvailabilityState({
|
||||
zapretInstalled: Boolean(systemInfo.zapret_installed),
|
||||
zapret2Installed: Boolean(systemInfo.zapret2_installed),
|
||||
byedpiInstalled: Boolean(systemInfo.byedpi_installed),
|
||||
});
|
||||
}
|
||||
@@ -468,6 +541,7 @@ function ensureActionProvidersAvailabilityLoaded() {
|
||||
.then((capabilities) => {
|
||||
updateActionProvidersAvailabilityState({
|
||||
zapretInstalled: Boolean(capabilities?.zapretInstalled),
|
||||
zapret2Installed: Boolean(capabilities?.zapret2Installed),
|
||||
byedpiInstalled: Boolean(capabilities?.byedpiInstalled),
|
||||
});
|
||||
return actionProvidersAvailabilityState;
|
||||
@@ -486,13 +560,18 @@ function ensureActionProvidersAvailabilityLoaded() {
|
||||
|
||||
actionProvidersAvailabilityPromise = Promise.allSettled([
|
||||
main.PodkopShellMethods.checkZapretRuntime(),
|
||||
main.PodkopShellMethods.checkZapret2Runtime(),
|
||||
main.PodkopShellMethods.checkByedpiRuntime(),
|
||||
])
|
||||
.then(([zapretResult, byedpiResult]) => {
|
||||
.then(([zapretResult, zapret2Result, byedpiResult]) => {
|
||||
const zapret =
|
||||
zapretResult && zapretResult.status === "fulfilled"
|
||||
? zapretResult.value
|
||||
: null;
|
||||
const zapret2 =
|
||||
zapret2Result && zapret2Result.status === "fulfilled"
|
||||
? zapret2Result.value
|
||||
: null;
|
||||
const byedpi =
|
||||
byedpiResult && byedpiResult.status === "fulfilled"
|
||||
? byedpiResult.value
|
||||
@@ -500,22 +579,23 @@ function ensureActionProvidersAvailabilityLoaded() {
|
||||
|
||||
actionProvidersAvailabilityState.loaded = true;
|
||||
actionProvidersAvailabilityState.zapretInstalled = Boolean(
|
||||
zapret &&
|
||||
zapret.success &&
|
||||
zapret.data &&
|
||||
zapret.data.zapret_installed,
|
||||
zapret && zapret.success && zapret.data && zapret.data.zapret_installed,
|
||||
);
|
||||
actionProvidersAvailabilityState.zapret2Installed = Boolean(
|
||||
zapret2 &&
|
||||
zapret2.success &&
|
||||
zapret2.data &&
|
||||
zapret2.data.zapret2_installed,
|
||||
);
|
||||
actionProvidersAvailabilityState.byedpiInstalled = Boolean(
|
||||
byedpi &&
|
||||
byedpi.success &&
|
||||
byedpi.data &&
|
||||
byedpi.data.byedpi_installed,
|
||||
byedpi && byedpi.success && byedpi.data && byedpi.data.byedpi_installed,
|
||||
);
|
||||
return actionProvidersAvailabilityState;
|
||||
})
|
||||
.catch(() => {
|
||||
actionProvidersAvailabilityState.loaded = true;
|
||||
actionProvidersAvailabilityState.zapretInstalled = false;
|
||||
actionProvidersAvailabilityState.zapret2Installed = false;
|
||||
actionProvidersAvailabilityState.byedpiInstalled = false;
|
||||
return actionProvidersAvailabilityState;
|
||||
})
|
||||
@@ -530,6 +610,10 @@ function isZapretInstalledForUi() {
|
||||
return actionProvidersAvailabilityState.zapretInstalled;
|
||||
}
|
||||
|
||||
function isZapret2InstalledForUi() {
|
||||
return actionProvidersAvailabilityState.zapret2Installed;
|
||||
}
|
||||
|
||||
function isByedpiInstalledForUi() {
|
||||
return actionProvidersAvailabilityState.byedpiInstalled;
|
||||
}
|
||||
@@ -553,6 +637,8 @@ function getActionOptionLabel(action) {
|
||||
return "VPN";
|
||||
case "zapret":
|
||||
return "Zapret";
|
||||
case "zapret2":
|
||||
return "Zapret2";
|
||||
case "byedpi":
|
||||
return "ByeDPI";
|
||||
case "outbound":
|
||||
@@ -570,6 +656,10 @@ function getRuleActionDisplayValue(section_id) {
|
||||
return "Zapret";
|
||||
}
|
||||
|
||||
if (action === "zapret2") {
|
||||
return "Zapret2";
|
||||
}
|
||||
|
||||
if (action === "byedpi") {
|
||||
return "ByeDPI";
|
||||
}
|
||||
@@ -592,6 +682,9 @@ function populateActionOptionValues(option) {
|
||||
if (isZapretInstalledForUi()) {
|
||||
option.value("zapret", getActionOptionLabel("zapret"));
|
||||
}
|
||||
if (isZapret2InstalledForUi()) {
|
||||
option.value("zapret2", getActionOptionLabel("zapret2"));
|
||||
}
|
||||
if (isByedpiInstalledForUi()) {
|
||||
option.value("byedpi", getActionOptionLabel("byedpi"));
|
||||
}
|
||||
@@ -1237,6 +1330,54 @@ function attachNfqwsRemoteValidation(option, section_id, textarea) {
|
||||
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) {
|
||||
const text = value ? `${value}` : "";
|
||||
const tokens = [];
|
||||
@@ -1392,6 +1533,22 @@ function getNfqwsOptionArgumentMode(option) {
|
||||
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) {
|
||||
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) {
|
||||
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;
|
||||
|
||||
option.renderWidget = function (section_id, option_index, cfgvalue) {
|
||||
@@ -2279,12 +2844,127 @@ function configureTextareaOption(option, analyzer) {
|
||||
if (typeof analyzer === "function") {
|
||||
attachAnnotatedTextarea(textarea, analyzer);
|
||||
}
|
||||
if (typeof remoteValidationAttacher === "function") {
|
||||
remoteValidationAttacher(this, section_id, textarea);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
const o = section.taboption(
|
||||
"conditions",
|
||||
@@ -2593,13 +3273,69 @@ function createSectionContent(section) {
|
||||
? ZAPRET_DEFAULT_NFQWS_OPT
|
||||
: 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) {
|
||||
const analysis = analyzeNfqwsStrategy(value);
|
||||
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(
|
||||
"settings",
|
||||
@@ -3130,6 +3866,7 @@ function createSectionContent(section) {
|
||||
o.depends("action", "vpn");
|
||||
o.depends("action", "byedpi");
|
||||
o.depends("action", "zapret");
|
||||
o.depends("action", "zapret2");
|
||||
o.modalonly = true;
|
||||
|
||||
o = section.taboption(
|
||||
@@ -3145,6 +3882,7 @@ function createSectionContent(section) {
|
||||
o.depends({ action: "vpn", mixed_proxy_enabled: "1" });
|
||||
o.depends({ action: "byedpi", mixed_proxy_enabled: "1" });
|
||||
o.depends({ action: "zapret", mixed_proxy_enabled: "1" });
|
||||
o.depends({ action: "zapret2", mixed_proxy_enabled: "1" });
|
||||
o.modalonly = true;
|
||||
o.validate = function (_section_id, value) {
|
||||
if (!value || value.length === 0) {
|
||||
@@ -3173,6 +3911,7 @@ function createSectionContent(section) {
|
||||
o.depends({ action: "vpn", mixed_proxy_enabled: "1" });
|
||||
o.depends({ action: "byedpi", mixed_proxy_enabled: "1" });
|
||||
o.depends({ action: "zapret", mixed_proxy_enabled: "1" });
|
||||
o.depends({ action: "zapret2", mixed_proxy_enabled: "1" });
|
||||
o.modalonly = true;
|
||||
|
||||
o = section.taboption(
|
||||
@@ -3207,6 +3946,11 @@ function createSectionContent(section) {
|
||||
mixed_proxy_enabled: "1",
|
||||
mixed_proxy_auth_enabled: "1",
|
||||
});
|
||||
o.depends({
|
||||
action: "zapret2",
|
||||
mixed_proxy_enabled: "1",
|
||||
mixed_proxy_auth_enabled: "1",
|
||||
});
|
||||
o.modalonly = true;
|
||||
o.validate = function (_section_id, value) {
|
||||
if (!value || value.length === 0) {
|
||||
@@ -3248,6 +3992,11 @@ function createSectionContent(section) {
|
||||
mixed_proxy_enabled: "1",
|
||||
mixed_proxy_auth_enabled: "1",
|
||||
});
|
||||
o.depends({
|
||||
action: "zapret2",
|
||||
mixed_proxy_enabled: "1",
|
||||
mixed_proxy_auth_enabled: "1",
|
||||
});
|
||||
o.modalonly = true;
|
||||
o.validate = function (_section_id, value) {
|
||||
if (!value || value.length === 0) {
|
||||
|
||||
@@ -15,6 +15,7 @@ const ROUTING_SECTION_ACTIONS = [
|
||||
"vpn",
|
||||
"byedpi",
|
||||
"zapret",
|
||||
"zapret2",
|
||||
"direct",
|
||||
"block",
|
||||
];
|
||||
|
||||
@@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PODKOP PLUS\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-06-02 17:11+0300\n"
|
||||
"PO-Revision-Date: 2026-06-02 17:11+0300\n"
|
||||
"POT-Creation-Date: 2026-06-02 18:22+0300\n"
|
||||
"PO-Revision-Date: 2026-06-02 18:22+0300\n"
|
||||
"Last-Translator: ushan0v\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: ru\n"
|
||||
@@ -299,6 +299,9 @@ msgstr "Отключить QUIC"
|
||||
msgid "Disable the QUIC protocol to improve compatibility or fix issues with video streaming"
|
||||
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"
|
||||
msgstr "DNS на роутере"
|
||||
|
||||
@@ -443,6 +446,9 @@ msgstr "Открыть эту секцию как локальный HTTP+SOCKS
|
||||
msgid "External nfqws config files bypass Podkop Plus queue management and explicit validation."
|
||||
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"
|
||||
msgstr "Не удалось закрыть подключение"
|
||||
|
||||
@@ -485,6 +491,9 @@ msgstr "Proxy protocol для фронтинга"
|
||||
msgid "Fully Routed IPs"
|
||||
msgstr "Полностью маршрутизированные IP-адреса"
|
||||
|
||||
msgid "Fuzzing is not supported here because Podkop Plus needs deterministic runtime validation."
|
||||
msgstr "Fuzzing здесь не поддерживается, потому что Podkop Plus нужна предсказуемая runtime-валидация."
|
||||
|
||||
msgid "Generated inbound"
|
||||
msgstr "Созданный inbound"
|
||||
|
||||
@@ -608,6 +617,9 @@ msgstr "Неверный IP-адрес"
|
||||
msgid "Invalid JSON format"
|
||||
msgstr "Неверный формат JSON"
|
||||
|
||||
msgid "Invalid NFQWS2 strategy"
|
||||
msgstr "Некорректная стратегия NFQWS2"
|
||||
|
||||
msgid "Invalid path format. Path must start with \"/\" and contain valid characters"
|
||||
msgstr "Неверный формат пути. Путь должен начинаться с \"/\" и содержать допустимые символы"
|
||||
|
||||
@@ -803,6 +815,12 @@ msgstr "Стратегия NFQWS"
|
||||
msgid "NFQWS strategy cannot be empty"
|
||||
msgstr "Стратегия NFQWS не может быть пустой"
|
||||
|
||||
msgid "NFQWS2 Strategy"
|
||||
msgstr "Стратегия NFQWS2"
|
||||
|
||||
msgid "NFQWS2 strategy cannot be empty"
|
||||
msgstr "Стратегия NFQWS2 не может быть пустой"
|
||||
|
||||
msgid "No active connections"
|
||||
msgstr "Нет активных подключений"
|
||||
|
||||
@@ -827,6 +845,9 @@ msgstr "Нет правил, использующих ByeDPI"
|
||||
msgid "No rules use Zapret"
|
||||
msgstr "Нет правил, использующих Zapret"
|
||||
|
||||
msgid "No rules use Zapret2"
|
||||
msgstr "Нет правил, использующих Zapret2"
|
||||
|
||||
msgid "None"
|
||||
msgstr "Нет"
|
||||
|
||||
@@ -884,6 +905,9 @@ msgstr "Параметр \"%s\" содержит некорректное зна
|
||||
msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws."
|
||||
msgstr "Необязательное значение здесь нужно передавать только через '='; отдельный токен nfqws проигнорирует."
|
||||
|
||||
msgid "Optional values must be attached with '=' here; a separate token would be ignored by nfqws2."
|
||||
msgstr "Необязательные значения здесь должны задаваться через '='; отдельный токен будет проигнорирован nfqws2."
|
||||
|
||||
msgid "Outbound JSON"
|
||||
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."
|
||||
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"
|
||||
msgstr "Правила Podkop Plus"
|
||||
|
||||
@@ -968,6 +995,15 @@ msgstr "NFQWS Podkop Plus не запущен"
|
||||
msgid "Podkop Plus-managed nfqws runtime is ready"
|
||||
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"
|
||||
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."
|
||||
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."
|
||||
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"
|
||||
msgstr "Перезапустить Podkop Plus"
|
||||
|
||||
@@ -1277,6 +1319,18 @@ msgstr "Отдельная служба Zapret активна"
|
||||
msgid "Standalone Zapret service is inactive"
|
||||
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"
|
||||
msgstr "Запустить Podkop Plus"
|
||||
|
||||
@@ -1346,6 +1400,9 @@ msgstr "Есть правила, использующие ByeDPI"
|
||||
msgid "There are rules using Zapret"
|
||||
msgstr "Есть правила использующие Zapret"
|
||||
|
||||
msgid "There are rules using Zapret2"
|
||||
msgstr "Есть правила, использующие Zapret2"
|
||||
|
||||
msgid "This field is required"
|
||||
msgstr "Заполните это поле"
|
||||
|
||||
@@ -1358,6 +1415,12 @@ msgstr "Это поле должно запускать рабочую стра
|
||||
msgid "This field must start a working nfqws strategy; --version exits immediately and is not allowed here."
|
||||
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."
|
||||
msgstr "Этот флаг не принимает значение."
|
||||
|
||||
@@ -1418,6 +1481,9 @@ msgstr "Не удалось проверить стратегию ByeDPI чер
|
||||
msgid "Unable to validate the NFQWS strategy through the backend parser."
|
||||
msgstr "Не удалось проверить стратегию NFQWS через backend-парсер."
|
||||
|
||||
msgid "Unable to validate the NFQWS2 strategy through the backend parser."
|
||||
msgstr "Не удалось проверить стратегию NFQWS2 через backend-парсер."
|
||||
|
||||
msgid "Unexpected ByeDPI strategy argument: %s"
|
||||
msgstr "Неожиданный аргумент стратегии ByeDPI: %s"
|
||||
|
||||
@@ -1427,6 +1493,9 @@ msgstr "Обнаружен лишний ciadpi Podkop Plus"
|
||||
msgid "Unexpected Podkop Plus-managed nfqws runtime is running"
|
||||
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."
|
||||
msgstr "Неожиданный отдельный токен. Используйте явные флаги вида --имя или --имя=значение."
|
||||
|
||||
@@ -1448,6 +1517,9 @@ msgstr "Неизвестная ошибка"
|
||||
msgid "Unknown NFQWS flag."
|
||||
msgstr "Неизвестный флаг NFQWS."
|
||||
|
||||
msgid "Unknown NFQWS2 flag."
|
||||
msgstr "Неизвестный флаг NFQWS2."
|
||||
|
||||
msgid "Unsupported security mode for this protocol"
|
||||
msgstr "Этот протокол не поддерживает выбранный режим защиты"
|
||||
|
||||
@@ -1591,3 +1663,30 @@ msgstr "Бинарник провайдера Zapret доступен"
|
||||
|
||||
msgid "Zapret provider binary is not available"
|
||||
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
@@ -124,7 +124,7 @@ podkop_fast_ui_capabilities_server_handler() {
|
||||
}
|
||||
|
||||
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
|
||||
sing_box_version="$(sing-box version 2>/dev/null | head -1 | awk '{print $NF}')"
|
||||
@@ -134,15 +134,17 @@ podkop_fast_get_ui_capabilities() {
|
||||
fi
|
||||
|
||||
[ -x "$ZAPRET_PROVIDER_NFQWS_BIN" ] && zapret_installed=1
|
||||
[ -x "$ZAPRET2_PROVIDER_NFQWS2_BIN" ] && zapret2_installed=1
|
||||
[ -x "$BYEDPI_BIN" ] && byedpi_installed=1
|
||||
|
||||
PODKOP_FAST_SERVER_INBOUNDS_ENABLED_COUNT=0
|
||||
config_load "$PODKOP_CONFIG_NAME"
|
||||
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" \
|
||||
"$zapret_installed" \
|
||||
"$zapret2_installed" \
|
||||
"$byedpi_installed" \
|
||||
"${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/provider_status.uc"
|
||||
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/sing_box_config_manager.sh"
|
||||
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/helpers.sh"
|
||||
. "$PODKOP_LIB/zapret.sh"
|
||||
. "$PODKOP_LIB/zapret2.sh"
|
||||
. "$PODKOP_LIB/byedpi.sh"
|
||||
. "$PODKOP_LIB/sing_box_config_manager.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_NFT_POPULATE_ENABLED=1
|
||||
PODKOP_ZAPRET_MISSING_WARNING_EMITTED=0
|
||||
PODKOP_ZAPRET2_MISSING_WARNING_EMITTED=0
|
||||
PODKOP_BYEDPI_MISSING_WARNING_EMITTED=0
|
||||
PODKOP_URLTEST_NEW_ENABLED_SECTIONS=""
|
||||
PODKOP_URLTEST_SELECTOR_SWITCHES=""
|
||||
@@ -1745,7 +1750,7 @@ append_sing_box_rule_signature() {
|
||||
|
||||
signature_add "rule.$section.resolve_real_ip_for_routing" "1"
|
||||
;;
|
||||
zapret)
|
||||
zapret | zapret2)
|
||||
config_get_bool mixed_proxy_enabled "$section" "mixed_proxy_enabled" 0
|
||||
signature_add "rule.$section.mixed_proxy_enabled" "$mixed_proxy_enabled"
|
||||
if [ "$mixed_proxy_enabled" -eq 1 ]; then
|
||||
@@ -2105,6 +2110,63 @@ build_zapret_runtime_signature() {
|
||||
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() {
|
||||
local section="$1"
|
||||
local byedpi_index
|
||||
@@ -2394,6 +2456,8 @@ capture_reload_state() {
|
||||
RELOAD_STATE_NFT_SIGNATURE="$(build_nft_signature)"
|
||||
RELOAD_STATE_ZAPRET_QUEUE_SIGNATURE="$(build_zapret_queue_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_LIST_SIGNATURE="$(build_list_update_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
|
||||
zapret_queue_signature=$RELOAD_STATE_ZAPRET_QUEUE_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
|
||||
list_signature=$RELOAD_STATE_LIST_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"
|
||||
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_string"
|
||||
podkop_uci_delete_option "$section" "urltest_proxy_links"
|
||||
@@ -3172,6 +3238,7 @@ check_requirements() {
|
||||
fi
|
||||
|
||||
check_zapret_requirements
|
||||
check_zapret2_requirements
|
||||
check_byedpi_requirements
|
||||
}
|
||||
|
||||
@@ -3192,6 +3259,16 @@ _check_outbound_node() {
|
||||
return 0
|
||||
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" ] ||
|
||||
[ -n "$outbound_json" ] || [ -n "$interface" ]; then
|
||||
section_exists=0
|
||||
@@ -3266,6 +3343,7 @@ start_main() {
|
||||
apply_pending_urltest_selector_switches
|
||||
run_deferred_subscription_bootstrap
|
||||
start_zapret_runtime
|
||||
start_zapret2_runtime
|
||||
|
||||
(close_inherited_service_lock_fd; list_update) &
|
||||
}
|
||||
@@ -3291,6 +3369,7 @@ stop_main() {
|
||||
rm -f "$TMP_RULESET_FOLDER"/*
|
||||
|
||||
stop_zapret_runtime
|
||||
stop_zapret2_runtime
|
||||
stop_byedpi_runtime
|
||||
|
||||
log "Flush nft"
|
||||
@@ -3419,10 +3498,10 @@ reload() {
|
||||
local status
|
||||
local force_runtime_reload=0
|
||||
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_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_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_zapret2_restart needs_byedpi_restart
|
||||
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"
|
||||
|
||||
@@ -3458,6 +3537,8 @@ reload() {
|
||||
previous_nft_signature="$(read_reload_state_value "nft_signature")"
|
||||
previous_zapret_queue_signature="$(read_reload_state_value "zapret_queue_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_list_signature="$(read_reload_state_value "list_signature")"
|
||||
previous_cron_signature="$(read_reload_state_value "cron_signature")"
|
||||
@@ -3481,6 +3562,7 @@ reload() {
|
||||
needs_sing_box_reload=0
|
||||
needs_nft_rebuild=0
|
||||
needs_zapret_restart=0
|
||||
needs_zapret2_restart=0
|
||||
needs_byedpi_restart=0
|
||||
needs_dnsmasq_configure=0
|
||||
needs_dnsmasq_restore=0
|
||||
@@ -3492,6 +3574,8 @@ reload() {
|
||||
changed_nft=0
|
||||
changed_zapret_queue=0
|
||||
changed_zapret_runtime=0
|
||||
changed_zapret2_queue=0
|
||||
changed_zapret2_runtime=0
|
||||
changed_byedpi_runtime=0
|
||||
changed_cron=0
|
||||
changed_list=0
|
||||
@@ -3520,6 +3604,18 @@ reload() {
|
||||
needs_zapret_restart=1
|
||||
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
|
||||
changed_byedpi_runtime=1
|
||||
needs_byedpi_restart=1
|
||||
@@ -3552,18 +3648,20 @@ reload() {
|
||||
[ "$needs_sing_box_reload" -eq 0 ] &&
|
||||
[ "$needs_nft_rebuild" -eq 0 ] &&
|
||||
[ "$needs_zapret_restart" -eq 0 ] &&
|
||||
[ "$needs_zapret2_restart" -eq 0 ] &&
|
||||
[ "$needs_byedpi_restart" -eq 0 ] &&
|
||||
[ "$needs_dnsmasq_configure" -eq 0 ] &&
|
||||
[ "$needs_dnsmasq_restore" -eq 0 ]; then
|
||||
needs_sing_box_reload=1
|
||||
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 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 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 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 ] &&
|
||||
[ "$needs_nft_rebuild" -eq 0 ] &&
|
||||
[ "$needs_zapret_restart" -eq 0 ] &&
|
||||
[ "$needs_zapret2_restart" -eq 0 ] &&
|
||||
[ "$needs_byedpi_restart" -eq 0 ] &&
|
||||
[ "$needs_dnsmasq_configure" -eq 0 ] &&
|
||||
[ "$needs_dnsmasq_restore" -eq 0 ] &&
|
||||
@@ -3582,6 +3680,10 @@ reload() {
|
||||
stop_zapret_runtime
|
||||
fi
|
||||
|
||||
if [ "$needs_zapret2_restart" -eq 1 ]; then
|
||||
stop_zapret2_runtime
|
||||
fi
|
||||
|
||||
if [ "$needs_byedpi_restart" -eq 1 ]; then
|
||||
stop_byedpi_runtime
|
||||
fi
|
||||
@@ -3609,6 +3711,10 @@ reload() {
|
||||
start_zapret_runtime
|
||||
fi
|
||||
|
||||
if [ "$needs_zapret2_restart" -eq 1 ]; then
|
||||
start_zapret2_runtime
|
||||
fi
|
||||
|
||||
if [ "$needs_byedpi_restart" -eq 1 ]; then
|
||||
start_byedpi_runtime
|
||||
fi
|
||||
@@ -4034,6 +4140,22 @@ process_validate_rule() {
|
||||
validate_rule_nfqws_opt "$section"
|
||||
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 ! is_byedpi_installed; 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 meta mark "$NFT_OUTBOUND_MARK" counter return
|
||||
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 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
|
||||
@@ -7895,6 +8018,26 @@ configure_outbound_handler() {
|
||||
return 0
|
||||
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 ! is_byedpi_installed; then
|
||||
return 0
|
||||
@@ -8282,10 +8425,10 @@ create_route_rule_for_action() {
|
||||
outbound_tag="$(get_outbound_tag_by_section "$section")"
|
||||
config=$(sing_box_cm_add_route_rule "$config" "$rule_tag" "$inbound_tag" "$outbound_tag")
|
||||
;;
|
||||
zapret)
|
||||
local zapret_outbound_tag
|
||||
zapret_outbound_tag="$(get_outbound_tag_by_section "$section")"
|
||||
config=$(sing_box_cm_add_route_rule "$config" "$rule_tag" "$inbound_tag" "$zapret_outbound_tag")
|
||||
zapret | zapret2)
|
||||
local provider_outbound_tag
|
||||
provider_outbound_tag="$(get_outbound_tag_by_section "$section")"
|
||||
config=$(sing_box_cm_add_route_rule "$config" "$rule_tag" "$inbound_tag" "$provider_outbound_tag")
|
||||
;;
|
||||
direct)
|
||||
local direct_outbound_tag
|
||||
@@ -8570,6 +8713,10 @@ configure_route_rule_handler() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$action" = "zapret2" ] && ! is_zapret2_installed; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$action" = "byedpi" ] && ! is_byedpi_installed; then
|
||||
return 0
|
||||
fi
|
||||
@@ -8681,6 +8828,10 @@ configure_fully_routed_ips_for_section() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$action" = "zapret2" ] && ! is_zapret2_installed; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ "$action" = "byedpi" ] && ! is_byedpi_installed; then
|
||||
return 0
|
||||
fi
|
||||
@@ -8800,13 +8951,16 @@ configure_section_mixed_proxy() {
|
||||
subscription_section_is_deferred "$section" && return 0
|
||||
|
||||
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
|
||||
return 0
|
||||
fi
|
||||
if [ "$action" = "zapret" ] && ! is_zapret_installed; then
|
||||
return 0
|
||||
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 \
|
||||
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
|
||||
|
||||
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"
|
||||
fi
|
||||
}
|
||||
@@ -10299,7 +10456,7 @@ write_system_info_cache() {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
podkop_version="$PODKOP_VERSION"
|
||||
@@ -10327,6 +10484,15 @@ build_system_info() {
|
||||
zapret_version="not installed"
|
||||
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
|
||||
if is_byedpi_installed; then
|
||||
byedpi_installed=1
|
||||
@@ -10359,6 +10525,7 @@ build_system_info() {
|
||||
"$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" "$generated_at"
|
||||
}
|
||||
@@ -10397,11 +10564,12 @@ get_server_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)"
|
||||
is_sing_box_extended "$sing_box_version" && sing_box_extended=1
|
||||
is_zapret_installed && zapret_installed=1
|
||||
is_zapret2_installed && zapret2_installed=1
|
||||
is_byedpi_installed && byedpi_installed=1
|
||||
|
||||
INBOUNDS_CONFIG_ENABLED_COUNT=0
|
||||
@@ -10410,6 +10578,7 @@ get_ui_capabilities() {
|
||||
json_utils_ucode object-json \
|
||||
sing_box_extended n "$sing_box_extended" \
|
||||
zapret_installed n "$zapret_installed" \
|
||||
zapret2_installed n "$zapret2_installed" \
|
||||
byedpi_installed n "$byedpi_installed" \
|
||||
server_inbounds_enabled_count n "${INBOUNDS_CONFIG_ENABLED_COUNT:-0}"
|
||||
}
|
||||
@@ -10418,6 +10587,10 @@ get_zapret_status() {
|
||||
get_zapret_status_json
|
||||
}
|
||||
|
||||
get_zapret2_status() {
|
||||
get_zapret2_status_json
|
||||
}
|
||||
|
||||
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."
|
||||
}
|
||||
@@ -10426,6 +10599,10 @@ check_zapret_runtime() {
|
||||
check_zapret_runtime_json
|
||||
}
|
||||
|
||||
check_zapret2_runtime() {
|
||||
check_zapret2_runtime_json
|
||||
}
|
||||
|
||||
get_byedpi_status() {
|
||||
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"
|
||||
}
|
||||
|
||||
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() {
|
||||
local host="$1"
|
||||
local resolver="$2"
|
||||
@@ -11212,7 +11400,7 @@ global_check() {
|
||||
system_info_json=$(get_system_info)
|
||||
|
||||
if [ -n "$system_info_json" ]; then
|
||||
local podkop_version podkop_latest_version luci_app_version sing_box_version 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_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)
|
||||
zapret_version=$(json_get_default "$system_info_json" zapret_version "unknown")
|
||||
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_installed=$(json_get_default "$system_info_json" byedpi_installed 0)
|
||||
openwrt_version=$(json_get_default "$system_info_json" openwrt_version "unknown")
|
||||
@@ -11237,6 +11427,9 @@ global_check() {
|
||||
if [ "$zapret_installed" -eq 1 ]; then
|
||||
print_global "🧵 Zapret: $zapret_version"
|
||||
fi
|
||||
if [ "$zapret2_installed" -eq 1 ]; then
|
||||
print_global "🧵 Zapret2: $zapret2_version"
|
||||
fi
|
||||
if [ "$byedpi_installed" -eq 1 ]; then
|
||||
print_global "🧵 ByeDPI: $byedpi_version"
|
||||
fi
|
||||
@@ -11488,6 +11681,15 @@ global_check() {
|
||||
print_global "✅ Zapret provider available"
|
||||
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
|
||||
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."
|
||||
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 "🥸 FakeIP status"
|
||||
|
||||
@@ -11701,6 +11911,7 @@ Available commands:
|
||||
check_sing_box_logs Show sing-box logs
|
||||
check_fakeip Test sing-box FakeIP DNS
|
||||
check_zapret_runtime Check external zapret provider state
|
||||
check_zapret2_runtime Check external zapret2 provider state
|
||||
check_byedpi_runtime Check external ByeDPI provider state
|
||||
neutralize_zapret_defaults
|
||||
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_sing_box_status Get sing-box service 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_system_info Get system information in JSON format
|
||||
get_server_capabilities Get server UI capabilities in JSON format
|
||||
get_ui_capabilities Get lightweight UI capabilities in JSON format
|
||||
generate_reality_keypair
|
||||
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_status Get background component action status
|
||||
check_dns_available Check DNS server availability
|
||||
global_check Run global system check
|
||||
validate_nfqws_strategy_json
|
||||
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 a ByeDPI ciadpi strategy and print JSON result
|
||||
EOF
|
||||
@@ -11810,6 +12024,9 @@ check_fakeip)
|
||||
check_zapret_runtime)
|
||||
check_zapret_runtime
|
||||
;;
|
||||
check_zapret2_runtime)
|
||||
check_zapret2_runtime
|
||||
;;
|
||||
check_byedpi_runtime)
|
||||
check_byedpi_runtime
|
||||
;;
|
||||
@@ -11852,6 +12069,9 @@ get_sing_box_status)
|
||||
get_zapret_status)
|
||||
get_zapret_status
|
||||
;;
|
||||
get_zapret2_status)
|
||||
get_zapret2_status
|
||||
;;
|
||||
get_byedpi_status)
|
||||
get_byedpi_status
|
||||
;;
|
||||
@@ -11885,6 +12105,9 @@ global_check)
|
||||
validate_nfqws_strategy_json)
|
||||
validate_nfqws_strategy_json "${2:-}"
|
||||
;;
|
||||
validate_nfqws2_strategy_json)
|
||||
validate_nfqws2_strategy_json "${2:-}"
|
||||
;;
|
||||
validate_byedpi_strategy_json)
|
||||
validate_byedpi_strategy_json "${2:-}"
|
||||
;;
|
||||
|
||||
@@ -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_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_BIN="/usr/bin/ciadpi"
|
||||
BYEDPI_SERVICE_INIT="/etc/init.d/byedpi"
|
||||
|
||||
@@ -345,6 +345,45 @@ function byedpi_asset_matches(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) {
|
||||
for (let arch in split(as_string(arch_candidates), " ")) {
|
||||
if (arch == "")
|
||||
@@ -427,11 +466,13 @@ function system_info_json() {
|
||||
sing_box_extended: arg_number(ARGV[5]),
|
||||
zapret_version: as_string(ARGV[6]),
|
||||
zapret_installed: arg_number(ARGV[7]),
|
||||
byedpi_version: as_string(ARGV[8]),
|
||||
byedpi_installed: arg_number(ARGV[9]),
|
||||
openwrt_version: as_string(ARGV[10]),
|
||||
device_model: as_string(ARGV[11]),
|
||||
generated_at: arg_number(ARGV[12])
|
||||
zapret2_version: as_string(ARGV[8]),
|
||||
zapret2_installed: arg_number(ARGV[9]),
|
||||
byedpi_version: as_string(ARGV[10]),
|
||||
byedpi_installed: arg_number(ARGV[11]),
|
||||
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]);
|
||||
else if (mode == "release-asset-url-by-suffix")
|
||||
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")
|
||||
byedpi_select_asset(ARGV[1], ARGV[2], ARGV[3]);
|
||||
else if (mode == "sing-box-extended-release-tag")
|
||||
|
||||
@@ -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] || "";
|
||||
|
||||
if (mode == "byedpi-validation")
|
||||
@@ -249,6 +287,10 @@ else if (mode == "zapret-status")
|
||||
zapret_status(slice(ARGV, 1));
|
||||
else if (mode == "zapret-check")
|
||||
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 {
|
||||
warn("Usage: provider_status.uc <operation> ...\n");
|
||||
exit(1);
|
||||
|
||||
@@ -10,6 +10,13 @@ UPDATES_ZAPRET_PACKAGE_FILE=""
|
||||
UPDATES_ZAPRET_PACKAGE_NAME=""
|
||||
UPDATES_ZAPRET_PACKAGE_VERSION=""
|
||||
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_PACKAGE_URL=""
|
||||
UPDATES_BYEDPI_PACKAGE_NAME=""
|
||||
@@ -146,9 +153,11 @@ updates_fail() {
|
||||
local message="$3"
|
||||
local current_version="${4:-}"
|
||||
local latest_version="${5:-}"
|
||||
local status="${6:-}"
|
||||
local release_url="${7:-}"
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1020,6 +1029,8 @@ updates_extract_arch_package_version() {
|
||||
version="$(printf '%s\n' "$package_name" | sed 's/\.ipk$//;s/\.apk$//')"
|
||||
|
||||
case "$version" in
|
||||
zapret2_*) version="${version#zapret2_}" ;;
|
||||
zapret2-*) version="${version#zapret2-}" ;;
|
||||
zapret_*) version="${version#zapret_}" ;;
|
||||
zapret-*) version="${version#zapret-}" ;;
|
||||
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")"
|
||||
}
|
||||
|
||||
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() {
|
||||
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_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() {
|
||||
[ -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_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() {
|
||||
local action="$1"
|
||||
local current_version installed
|
||||
@@ -1436,6 +1548,17 @@ updates_extract_zapret_bundle_version() {
|
||||
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() {
|
||||
printf '%s\n' "$1" |
|
||||
sed 's/^v//;s/-r[0-9][0-9]*$//;s/+.*$//;s/[[:space:]].*$//'
|
||||
@@ -1771,6 +1894,12 @@ component_action() {
|
||||
zapret:remove)
|
||||
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)
|
||||
updates_install_byedpi "$action"
|
||||
;;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user