Rebrand project as Forkop

This commit is contained in:
ushan0v
2026-07-11 18:23:20 +03:00
parent dc052f03a7
commit b5f2eb3724
362 changed files with 14228 additions and 13950 deletions
+4 -4
View File
@@ -11,8 +11,8 @@ body:
Спасибо за создание отчета об ошибке!
Перед отправкой, пожалуйста:
- Проверьте [существующие issues](https://github.com/ushan0v/podkop-plus/issues)
- Просмотрите [README проекта](https://github.com/ushan0v/podkop-plus#readme)
- Проверьте [существующие issues](https://github.com/ushan0v/forkop/issues)
- Просмотрите [README проекта](https://github.com/ushan0v/forkop#readme)
- type: textarea
id: description
@@ -53,7 +53,7 @@ body:
Информация о вашей системе (заполните всё применимое)
value: |
- **OpenWrt версия**:
- **Podkop Plus версия**:
- **Forkop версия**:
- **Роутер модель**:
- **Sing-box версия**:
render: markdown
@@ -68,7 +68,7 @@ body:
Релевантные части конфигурации (удалите чувствительную информацию!)
placeholder: |
Например:
- Содержимое /etc/config/podkop-plus
- Содержимое /etc/config/forkop
- Конфигурация sing-box (если релевантно)
- Дополнительные конфиги, которые потребуются wireless/network/dhcp и т.д.
render: shell
+3 -3
View File
@@ -1,8 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 💬 Если у вас что-то не работает, прежде всего прочитайте README проекта
url: https://github.com/ushan0v/podkop-plus
url: https://github.com/ushan0v/forkop
about: README проекта
- name: 📚 Если вы не нашли в README документацию, то вот ссылка на неё
url: https://github.com/ushan0v/podkop-plus#readme
about: README Podkop Plus
url: https://github.com/ushan0v/forkop#readme
about: README Forkop
+5 -5
View File
@@ -1,6 +1,6 @@
---
name: ✨ Запрос новой функции
description: Предложите новую функцию или улучшение для Podkop Plus
description: Предложите новую функцию или улучшение для Forkop
title: "[FEATURE] "
labels: ["enhancement", "needs-discussion"]
assignees: []
@@ -11,14 +11,14 @@ body:
Спасибо за предложение новой функции!
Перед отправкой, пожалуйста:
- Проверьте [существующие запросы](https://github.com/ushan0v/podkop-plus/issues?q=is%3Aissue+label%3Aenhancement)
- Убедитесь, что функции не существует в [README проекта](https://github.com/ushan0v/podkop-plus#readme)
- Проверьте [существующие запросы](https://github.com/ushan0v/forkop/issues?q=is%3Aissue+label%3Aenhancement)
- Убедитесь, что функции не существует в [README проекта](https://github.com/ushan0v/forkop#readme)
- type: textarea
id: summary
attributes:
label: Краткое описание
description: Краткое описание предлагаемой функции для Podkop Plus
description: Краткое описание предлагаемой функции для Forkop
placeholder: В одном предложении опишите, что вы хотите добавить...
validations:
required: true
@@ -40,7 +40,7 @@ body:
label: 💡 Предлагаемое решение
description: Четкое и краткое описание того, что вы хотите реализовать
placeholder: |
Я хочу, чтобы Podkop мог [...]
Я хочу, чтобы Forkop мог [...]
Предлагаю добавить функцию, которая [...]
Можно было бы улучшить [...] путем [...]
validations:
+6 -6
View File
@@ -3,8 +3,8 @@ name: Backend CI
on:
pull_request:
paths:
- 'podkop/files/**'
- 'podkop/Makefile'
- 'forkop/files/**'
- 'forkop/Makefile'
- 'build.sh'
- 'tests/**'
- '.github/workflows/backend-ci.yml'
@@ -12,8 +12,8 @@ on:
branches:
- main
paths:
- 'podkop/files/**'
- 'podkop/Makefile'
- 'forkop/files/**'
- 'forkop/Makefile'
- 'build.sh'
- 'tests/**'
- '.github/workflows/backend-ci.yml'
@@ -48,8 +48,8 @@ jobs:
- name: Check ucode syntax
shell: bash
run: |
find podkop/files/usr/lib -name '*.uc' -print0 | xargs -0 -n1 ucode -c -o /dev/null
find podkop/files/usr/lib -name '*.uc' -print0 | xargs -0 -n1 ucode -S -c -o /dev/null
find forkop/files/usr/lib -name '*.uc' -print0 | xargs -0 -n1 ucode -c -o /dev/null
find forkop/files/usr/lib -name '*.uc' -print0 | xargs -0 -n1 ucode -S -c -o /dev/null
- name: Run backend tests
shell: bash
+3 -3
View File
@@ -37,15 +37,15 @@ jobs:
else
VERSION=$(git describe --tags --exact-match 2>/dev/null || true)
fi
if [[ ! "$VERSION" =~ ^([0-9]+(\.[0-9]+)*-[0-9]+|[0-9]+(\.[0-9]+){3})$ ]]; then
echo "Release version must use x.y.z.n format; legacy x.y.z-n is also accepted, got: ${VERSION:-<empty>}" >&2
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Release version must use x.y.z format, got: ${VERSION:-<empty>}" >&2
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "commitish=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
build:
name: Build Podkop Plus release packages
name: Build Forkop release packages
runs-on: ubuntu-latest
needs: preparation
steps:
+4 -4
View File
@@ -3,7 +3,7 @@ name: Frontend CI
on:
pull_request:
paths:
- 'fe-app-podkop/**'
- 'fe-app-forkop/**'
- '.github/workflows/frontend-ci.yml'
jobs:
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-24.04
defaults:
run:
working-directory: fe-app-podkop
working-directory: fe-app-forkop
steps:
- name: Checkout code
@@ -28,14 +28,14 @@ jobs:
- name: Get yarn cache directory path
id: yarn-cache-dir-path
working-directory: fe-app-podkop
working-directory: fe-app-forkop
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
- name: Cache yarn dependencies
uses: actions/cache@v4.3.0
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('fe-app-podkop/yarn.lock') }}
key: ${{ runner.os }}-yarn-${{ hashFiles('fe-app-forkop/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
+6 -6
View File
@@ -15,8 +15,8 @@ on:
- 'rc/**'
paths:
- 'install.sh'
- 'podkop/files/usr/bin/**'
- 'podkop/files/usr/lib/**'
- 'forkop/files/usr/bin/**'
- 'forkop/files/usr/lib/**'
- '.github/workflows/shellcheck.yml'
pull_request:
branches:
@@ -24,8 +24,8 @@ on:
- 'rc/**'
paths:
- 'install.sh'
- 'podkop/files/usr/bin/**'
- 'podkop/files/usr/lib/**'
- 'forkop/files/usr/bin/**'
- 'forkop/files/usr/lib/**'
- '.github/workflows/shellcheck.yml'
permissions:
@@ -55,7 +55,7 @@ jobs:
head: ${{ github.event.inputs.head }}
severity: error
include-path: |
podkop/files/usr/bin/podkop
podkop/files/usr/lib/**.sh
forkop/files/usr/bin/forkop
forkop/files/usr/lib/**.sh
install.sh
token: ${{ secrets.GITHUB_TOKEN }}
+3 -3
View File
@@ -1,7 +1,7 @@
.idea
fe-app-podkop/node_modules
fe-app-podkop/.yarn/
fe-app-podkop/.env
fe-app-forkop/node_modules
fe-app-forkop/.yarn/
fe-app-forkop/.env
.DS_Store
sandbox/
+85 -142
View File
@@ -16,35 +16,28 @@ if [[ "$DEFAULT_BUILD_HOME" == "/root" ]]; then
DEFAULT_BUILD_HOME="${DEFAULT_BUILD_HOME:-/root}"
fi
if [[ "$RELEASE_VERSION" =~ ^[0-9]+(\.[0-9]+)*-[0-9]+$ ]]; then
BASE_VERSION="${RELEASE_VERSION%-*}"
FORK_RELEASE="${RELEASE_VERSION##*-}"
APK_INTERNAL_VERSION="${APK_INTERNAL_VERSION:-${BASE_VERSION}-r${FORK_RELEASE}}"
elif [[ "$RELEASE_VERSION" =~ ^[0-9]+(\.[0-9]+){3}$ ]]; then
BASE_VERSION="${RELEASE_VERSION%.*}"
FORK_RELEASE="${RELEASE_VERSION##*.}"
APK_INTERNAL_VERSION="${APK_INTERNAL_VERSION:-${RELEASE_VERSION}}"
else
echo "Expected release version in the form x.y.z.n; legacy x.y.z-n is also accepted" >&2
if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Expected release version in the form x.y.z" >&2
exit 1
fi
APK_INTERNAL_VERSION="$RELEASE_VERSION"
WSL_NATIVE_ROOT="${WSL_NATIVE_ROOT:-$DEFAULT_BUILD_HOME/build/podkop-plus}"
WSL_NATIVE_ROOT="${WSL_NATIVE_ROOT:-$DEFAULT_BUILD_HOME/build/forkop}"
WORK_DIR="${WORK_DIR:-$ROOT_DIR/.wsl-build}"
SDK_WORK_DIR="${SDK_WORK_DIR:-$WORK_DIR/sdk}"
SDK_CACHE_DIR="${SDK_CACHE_DIR:-$DEFAULT_BUILD_HOME/.cache/podkop-plus/openwrt-sdk}"
SDK_CACHE_DIR="${SDK_CACHE_DIR:-$DEFAULT_BUILD_HOME/.cache/forkop/openwrt-sdk}"
IPK_SDK_URL="${IPK_SDK_URL:-https://downloads.openwrt.org/releases/24.10.6/targets/x86/64/openwrt-sdk-24.10.6-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst}"
APK_SDK_URL="${APK_SDK_URL:-https://downloads.openwrt.org/releases/25.12.3/targets/x86/64/openwrt-sdk-25.12.3-x86-64_gcc-14.3.0_musl.Linux-x86_64.tar.zst}"
BACKEND_DESCRIPTION="Rule-based Podkop Plus backend with hybrid sing-box + zapret orchestration"
APP_DESCRIPTION="Rule-based Podkop Plus LuCI app with hybrid sing-box + zapret orchestration"
I18N_DESCRIPTION="Translation for luci-app-podkop-plus - Русский (Russian)"
BACKEND_DESCRIPTION="Rule-based Forkop backend with hybrid sing-box + zapret orchestration"
APP_DESCRIPTION="Rule-based Forkop LuCI app with hybrid sing-box + zapret orchestration"
I18N_DESCRIPTION="Translation for luci-app-forkop - Русский (Russian)"
MAINTAINER="ushan0v <ushan0v@users.noreply.github.com>"
PROJECT_URL="https://github.com/ushan0v/podkop-plus"
PROJECT_URL="https://github.com/ushan0v/forkop"
BACKEND_DEPENDS_IPK="libc, ca-bundle, kmod-inet-diag, kmod-netlink-diag, kmod-tun, curl, ucode, ucode-mod-fs, ucode-mod-uci, kmod-nft-tproxy, coreutils-base64, coreutils-sort, bind-dig, nftables, kmod-nft-nat, ip-full"
BACKEND_DEPENDS_APK="bind-dig ca-bundle coreutils-base64 coreutils-sort curl ip-full kmod-inet-diag kmod-netlink-diag kmod-nft-nat kmod-nft-tproxy kmod-tun libc nftables ucode ucode-mod-fs ucode-mod-uci"
APP_DEPENDS_IPK="libc, luci-base, podkop-plus"
APP_DEPENDS_APK="libc luci-base podkop-plus"
APP_DEPENDS_IPK="libc, luci-base, forkop"
APP_DEPENDS_APK="libc luci-base forkop"
APT_PACKAGES=(
build-essential
@@ -77,14 +70,14 @@ copy_to_native_root() {
--exclude "dist" \
--exclude ".idea" \
--exclude "sandbox" \
--exclude "fe-app-podkop/node_modules" \
--exclude "fe-app-podkop/tests" \
--exclude "fe-app-forkop/node_modules" \
--exclude "fe-app-forkop/tests" \
"$ROOT_DIR/" "$target_root/"
rm -rf \
"$target_root/.idea" \
"$target_root/sandbox" \
"$target_root/fe-app-podkop/node_modules" \
"$target_root/fe-app-podkop/tests"
"$target_root/fe-app-forkop/node_modules" \
"$target_root/fe-app-forkop/tests"
if [[ -n "$OUTPUT_DIR_INPUT" ]]; then
target_output="$OUTPUT_DIR_INPUT"
@@ -179,7 +172,7 @@ extract_sdk() {
local archive_path="$2"
local sdk_url="$3"
local destination="$SDK_WORK_DIR/$kind"
local marker_file="$destination/.podkop-sdk-url"
local marker_file="$destination/.forkop-sdk-url"
local temp_dir
local extracted_root
@@ -242,18 +235,18 @@ build_backend_root() {
make_dir "$output_root/etc/init.d"
make_dir "$output_root/etc/config"
make_dir "$output_root/usr/bin"
make_dir "$output_root/usr/lib/podkop-plus"
make_dir "$output_root/usr/lib/forkop"
install -m 0755 "$ROOT_DIR/podkop/files/etc/init.d/podkop" "$output_root/etc/init.d/podkop-plus"
install -m 0644 "$ROOT_DIR/podkop/files/etc/config/podkop" "$output_root/etc/config/podkop-plus"
install -m 0755 "$ROOT_DIR/podkop/files/usr/bin/podkop" "$output_root/usr/bin/podkop-plus"
cp -a "$ROOT_DIR/podkop/files/usr/lib/." "$output_root/usr/lib/podkop-plus/"
install -m 0755 "$ROOT_DIR/forkop/files/etc/init.d/forkop" "$output_root/etc/init.d/forkop"
install -m 0644 "$ROOT_DIR/forkop/files/etc/config/forkop" "$output_root/etc/config/forkop"
install -m 0755 "$ROOT_DIR/forkop/files/usr/bin/forkop" "$output_root/usr/bin/forkop"
cp -a "$ROOT_DIR/forkop/files/usr/lib/." "$output_root/usr/lib/forkop/"
sed -i -e "s/__COMPILED_VERSION_VARIABLE__/${RELEASE_VERSION}/g" \
"$output_root/usr/lib/podkop-plus/core/constants.uc"
"$output_root/usr/lib/forkop/core/constants.uc"
normalize_package_root_modes "$output_root"
chmod 0755 "$output_root/etc/init.d/podkop-plus" "$output_root/usr/bin/podkop-plus"
chmod 0755 "$output_root/etc/init.d/forkop" "$output_root/usr/bin/forkop"
}
build_app_root() {
@@ -262,23 +255,17 @@ build_app_root() {
rm -rf "$output_root"
make_dir "$output_root/www"
if [[ -d "$ROOT_DIR/luci-app-podkop-plus/htdocs" ]]; then
cp -a "$ROOT_DIR/luci-app-podkop-plus/htdocs/." "$output_root/www/"
if [[ -d "$ROOT_DIR/luci-app-forkop/htdocs" ]]; then
cp -a "$ROOT_DIR/luci-app-forkop/htdocs/." "$output_root/www/"
fi
if [[ -d "$output_root/www/luci-static/resources/view/podkop" ]]; then
rm -rf "$output_root/www/luci-static/resources/view/podkop_plus"
mv "$output_root/www/luci-static/resources/view/podkop" \
"$output_root/www/luci-static/resources/view/podkop_plus"
if [[ -d "$ROOT_DIR/luci-app-forkop/root" ]]; then
cp -a "$ROOT_DIR/luci-app-forkop/root/." "$output_root/"
fi
if [[ -d "$ROOT_DIR/luci-app-podkop-plus/root" ]]; then
cp -a "$ROOT_DIR/luci-app-podkop-plus/root/." "$output_root/"
fi
if [[ -f "$output_root/www/luci-static/resources/view/podkop_plus/main.js" ]]; then
if [[ -f "$output_root/www/luci-static/resources/view/forkop/main.js" ]]; then
sed -i -e "s/__COMPILED_VERSION_VARIABLE__/${RELEASE_VERSION}/g" \
"$output_root/www/luci-static/resources/view/podkop_plus/main.js"
"$output_root/www/luci-static/resources/view/forkop/main.js"
fi
normalize_package_root_modes "$output_root"
@@ -288,17 +275,17 @@ build_app_root() {
build_i18n_root() {
local output_root="$1"
local po2lmo_bin="$2"
local lmo_path="$output_root/usr/lib/lua/luci/i18n/podkop_plus.ru.lmo"
local lmo_path="$output_root/usr/lib/lua/luci/i18n/forkop.ru.lmo"
rm -rf "$output_root"
make_dir "$output_root/etc/uci-defaults"
make_dir "$(dirname "$lmo_path")"
cat > "$output_root/etc/uci-defaults/luci-i18n-podkop-plus-ru" <<'EOF'
cat > "$output_root/etc/uci-defaults/luci-i18n-forkop-ru" <<'EOF'
uci set luci.languages.ru='Русский (Russian)'; uci commit luci
EOF
"$po2lmo_bin" "$ROOT_DIR/luci-app-podkop-plus/po/ru/podkop_plus.po" "$lmo_path"
"$po2lmo_bin" "$ROOT_DIR/luci-app-forkop/po/ru/forkop.po" "$lmo_path"
normalize_package_root_modes "$output_root"
find "$output_root/etc/uci-defaults" -type f -exec chmod 0755 {} + 2>/dev/null || true
@@ -339,7 +326,7 @@ write_backend_ipk_control() {
make_dir "$control_dir"
cat > "$control_dir/control" <<EOF
Package: podkop-plus
Package: forkop
Version: ${RELEASE_VERSION}
Depends: ${BACKEND_DEPENDS_IPK}
License: GPL-2.0-or-later
@@ -352,37 +339,19 @@ Description: ${BACKEND_DESCRIPTION}
EOF
cat > "$control_dir/conffiles" <<'EOF'
/etc/config/podkop-plus
EOF
cat > "$control_dir/preinst" <<'EOF'
#!/usr/bin/ucode
let fs = require("fs");
if (getenv("IPKG_INSTROOT") != null && getenv("IPKG_INSTROOT") != "")
exit(0);
if (fs.stat("/etc/config/podkop-plus") == null && fs.stat("/etc/config/podkop_plus") != null) {
let data = fs.readfile("/etc/config/podkop_plus");
if (data == null || fs.writefile("/etc/config/podkop-plus", data) == null)
exit(1);
system("chmod 0644 /etc/config/podkop-plus >/dev/null 2>&1");
}
exit(0);
/etc/config/forkop
EOF
cat > "$control_dir/prerm" <<'EOF'
#!/usr/bin/ucode
if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "")
system("/usr/bin/podkop-plus package_prerm >/dev/null 2>&1");
system("/usr/bin/forkop package_prerm >/dev/null 2>&1");
exit(0);
EOF
chmod 0755 "$control_dir/preinst" "$control_dir/prerm"
chmod 0755 "$control_dir/prerm"
}
write_app_ipk_control() {
@@ -393,7 +362,7 @@ write_app_ipk_control() {
make_dir "$control_dir"
cat > "$control_dir/control" <<EOF
Package: luci-app-podkop-plus
Package: luci-app-forkop
Version: ${RELEASE_VERSION}
Depends: ${APP_DEPENDS_IPK}
License: GPL-2.0-or-later
@@ -414,7 +383,7 @@ default_postinst $0 $@
EOF
cat > "$control_dir/postinst-pkg" <<'EOF'
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/podkop-plus luci_postinst >/dev/null 2>&1 || true
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/forkop luci_postinst >/dev/null 2>&1 || true
EOF
cat > "$control_dir/prerm" <<'EOF'
@@ -435,9 +404,9 @@ write_i18n_ipk_control() {
make_dir "$control_dir"
cat > "$control_dir/control" <<EOF
Package: luci-i18n-podkop-plus-ru
Package: luci-i18n-forkop-ru
Version: ${RELEASE_VERSION}
Depends: libc, luci-app-podkop-plus
Depends: libc, luci-app-forkop
License: GPL-2.0-or-later
Section: luci
URL: ${PROJECT_URL}
@@ -513,19 +482,6 @@ write_backend_apk_scripts() {
cat > "$scripts_dir/backend-pre-install.sh" <<'EOF'
#!/usr/bin/ucode
let fs = require("fs");
if (getenv("IPKG_INSTROOT") != null && getenv("IPKG_INSTROOT") != "")
exit(0);
if (fs.stat("/etc/config/podkop-plus") == null && fs.stat("/etc/config/podkop_plus") != null) {
let data = fs.readfile("/etc/config/podkop_plus");
if (data == null || fs.writefile("/etc/config/podkop-plus", data) == null)
exit(1);
system("chmod 0644 /etc/config/podkop-plus >/dev/null 2>&1");
}
exit(0);
EOF
@@ -538,26 +494,13 @@ EOF
#!/usr/bin/ucode
if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "")
system("/usr/bin/podkop-plus package_prerm >/dev/null 2>&1");
system("/usr/bin/forkop package_prerm >/dev/null 2>&1");
exit(0);
EOF
cat > "$scripts_dir/backend-pre-upgrade.sh" <<'EOF'
#!/usr/bin/ucode
let fs = require("fs");
if (getenv("IPKG_INSTROOT") != null && getenv("IPKG_INSTROOT") != "")
exit(0);
if (fs.stat("/etc/config/podkop-plus") == null && fs.stat("/etc/config/podkop_plus") != null) {
let data = fs.readfile("/etc/config/podkop_plus");
if (data == null || fs.writefile("/etc/config/podkop-plus", data) == null)
exit(1);
system("chmod 0644 /etc/config/podkop-plus >/dev/null 2>&1");
}
exit(0);
EOF
@@ -585,10 +528,10 @@ EOF
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
export root="${IPKG_INSTROOT}"
export pkgname="luci-app-podkop-plus"
export pkgname="luci-app-forkop"
add_group_and_user
default_postinst
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/podkop-plus luci_postinst >/dev/null 2>&1 || true
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/forkop luci_postinst >/dev/null 2>&1 || true
EOF
cat > "$scripts_dir/app-pre-deinstall.sh" <<'EOF'
@@ -596,7 +539,7 @@ EOF
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
export root="${IPKG_INSTROOT}"
export pkgname="luci-app-podkop-plus"
export pkgname="luci-app-forkop"
default_prerm
exit 0
EOF
@@ -613,10 +556,10 @@ export PKG_UPGRADE=1
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
export root="${IPKG_INSTROOT}"
export pkgname="luci-app-podkop-plus"
export pkgname="luci-app-forkop"
add_group_and_user
default_postinst
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/podkop-plus luci_postinst >/dev/null 2>&1 || true
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/forkop luci_postinst >/dev/null 2>&1 || true
EOF
chmod 0755 "$scripts_dir"/app-*.sh
@@ -638,7 +581,7 @@ EOF
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
export root="${IPKG_INSTROOT}"
export pkgname="luci-i18n-podkop-plus-ru"
export pkgname="luci-i18n-forkop-ru"
add_group_and_user
default_postinst
EOF
@@ -648,7 +591,7 @@ EOF
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
export root="${IPKG_INSTROOT}"
export pkgname="luci-i18n-podkop-plus-ru"
export pkgname="luci-i18n-forkop-ru"
default_prerm
EOF
@@ -664,7 +607,7 @@ export PKG_UPGRADE=1
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
export root="${IPKG_INSTROOT}"
export pkgname="luci-i18n-podkop-plus-ru"
export pkgname="luci-i18n-forkop-ru"
add_group_and_user
default_postinst
EOF
@@ -701,7 +644,7 @@ build_apk_package() {
-I "description:${description}" \
-I "arch:noarch" \
-I "license:GPL-2.0-or-later" \
-I "origin:podkop-plus" \
-I "origin:forkop" \
-I "maintainer:${maintainer}" \
-I "url:${PROJECT_URL}" \
-I "depends:${depends}" \
@@ -720,7 +663,7 @@ build_apk_package() {
-I "description:${description}" \
-I "arch:noarch" \
-I "license:GPL-2.0-or-later" \
-I "origin:podkop-plus" \
-I "origin:forkop" \
-I "maintainer:${maintainer}" \
-I "url:${PROJECT_URL}" \
-I "depends:${depends}" \
@@ -742,7 +685,7 @@ build_apk_package() {
-I 'description:${description}' \
-I 'arch:noarch' \
-I 'license:GPL-2.0-or-later' \
-I 'origin:podkop-plus' \
-I 'origin:forkop' \
-I 'maintainer:${maintainer}' \
-I 'url:${PROJECT_URL}' \
-I 'depends:${depends}' \
@@ -764,7 +707,7 @@ build_apk_package() {
-I 'description:${description}' \
-I 'arch:noarch' \
-I 'license:GPL-2.0-or-later' \
-I 'origin:podkop-plus' \
-I 'origin:forkop' \
-I 'maintainer:${maintainer}' \
-I 'url:${PROJECT_URL}' \
-I 'depends:${depends}' \
@@ -845,17 +788,17 @@ sync_artifacts_to_windows() {
fi
rm -f \
"$WINDOWS_ARTIFACTS_DIR"/podkop-plus_* \
"$WINDOWS_ARTIFACTS_DIR"/luci-app-podkop-plus_* \
"$WINDOWS_ARTIFACTS_DIR"/luci-i18n-podkop-plus-ru_*
"$WINDOWS_ARTIFACTS_DIR"/forkop_* \
"$WINDOWS_ARTIFACTS_DIR"/luci-app-forkop_* \
"$WINDOWS_ARTIFACTS_DIR"/luci-i18n-forkop-ru_*
cp -f \
"$output_dir"/podkop-plus_"${RELEASE_VERSION}"_all.ipk \
"$output_dir"/luci-app-podkop-plus_"${RELEASE_VERSION}"_all.ipk \
"$output_dir"/luci-i18n-podkop-plus-ru_"${RELEASE_VERSION}"_all.ipk \
"$output_dir"/podkop-plus_"${RELEASE_VERSION}".apk \
"$output_dir"/luci-app-podkop-plus_"${RELEASE_VERSION}".apk \
"$output_dir"/luci-i18n-podkop-plus-ru_"${RELEASE_VERSION}".apk \
"$output_dir"/forkop_"${RELEASE_VERSION}".ipk \
"$output_dir"/luci-app-forkop_"${RELEASE_VERSION}".ipk \
"$output_dir"/luci-i18n-forkop-ru_"${RELEASE_VERSION}".ipk \
"$output_dir"/forkop_"${RELEASE_VERSION}".apk \
"$output_dir"/luci-app-forkop_"${RELEASE_VERSION}".apk \
"$output_dir"/luci-i18n-forkop-ru_"${RELEASE_VERSION}".apk \
"$WINDOWS_ARTIFACTS_DIR"/
echo "Synced artifacts to Windows path: $WINDOWS_ARTIFACTS_DIR" >&2
@@ -900,7 +843,7 @@ main() {
mkdir -p "$WORK_DIR"
output_dir="${OUTPUT_DIR_INPUT:-$ROOT_DIR/dist/release-final}"
mkdir -p "$output_dir"
rm -f "$output_dir"/podkop-plus_* "$output_dir"/luci-app-podkop-plus_* "$output_dir"/luci-i18n-podkop-plus-ru_*
rm -f "$output_dir"/forkop_* "$output_dir"/luci-app-forkop_* "$output_dir"/luci-i18n-forkop-ru_*
ipk_archive="$(download_sdk_archive "$IPK_SDK_URL")"
apk_archive="$(download_sdk_archive "$APK_SDK_URL")"
@@ -927,74 +870,74 @@ main() {
build_ipk_package \
"$ipkg_build_bin" \
"podkop-plus" \
"forkop" \
"$backend_root" \
"$backend_control" \
"$output_dir/podkop-plus_${RELEASE_VERSION}_all.ipk"
"$output_dir/forkop_${RELEASE_VERSION}.ipk"
build_ipk_package \
"$ipkg_build_bin" \
"luci-app-podkop-plus" \
"luci-app-forkop" \
"$app_root" \
"$app_control" \
"$output_dir/luci-app-podkop-plus_${RELEASE_VERSION}_all.ipk"
"$output_dir/luci-app-forkop_${RELEASE_VERSION}.ipk"
build_ipk_package \
"$ipkg_build_bin" \
"luci-i18n-podkop-plus-ru" \
"luci-i18n-forkop-ru" \
"$i18n_root" \
"$i18n_control" \
"$output_dir/luci-i18n-podkop-plus-ru_${RELEASE_VERSION}_all.ipk"
"$output_dir/luci-i18n-forkop-ru_${RELEASE_VERSION}.ipk"
generate_apk_metadata_files "podkop-plus" "$backend_root" "/etc/config/podkop-plus"
generate_apk_metadata_files "luci-app-podkop-plus" "$app_root"
generate_apk_metadata_files "luci-i18n-podkop-plus-ru" "$i18n_root"
generate_apk_metadata_files "forkop" "$backend_root" "/etc/config/forkop"
generate_apk_metadata_files "luci-app-forkop" "$app_root"
generate_apk_metadata_files "luci-i18n-forkop-ru" "$i18n_root"
write_backend_apk_scripts "$apk_scripts"
write_app_apk_scripts "$apk_scripts"
write_i18n_apk_scripts "$apk_scripts"
build_apk_package \
"$apk_bin" \
"podkop-plus" \
"forkop" \
"$APK_INTERNAL_VERSION" \
"$BACKEND_DESCRIPTION" \
"$BACKEND_DEPENDS_APK" \
"$backend_root" \
"$apk_scripts" \
"backend" \
"$output_dir/podkop-plus_${RELEASE_VERSION}.apk" \
"$output_dir/forkop_${RELEASE_VERSION}.apk" \
"$MAINTAINER"
build_apk_package \
"$apk_bin" \
"luci-app-podkop-plus" \
"luci-app-forkop" \
"$APK_INTERNAL_VERSION" \
"$APP_DESCRIPTION" \
"$APP_DEPENDS_APK" \
"$app_root" \
"$apk_scripts" \
"app" \
"$output_dir/luci-app-podkop-plus_${RELEASE_VERSION}.apk" \
"$output_dir/luci-app-forkop_${RELEASE_VERSION}.apk" \
"$MAINTAINER"
build_apk_package \
"$apk_bin" \
"luci-i18n-podkop-plus-ru" \
"luci-i18n-forkop-ru" \
"$APK_INTERNAL_VERSION" \
"$I18N_DESCRIPTION" \
"libc luci-app-podkop-plus" \
"libc luci-app-forkop" \
"$i18n_root" \
"$apk_scripts" \
"i18n" \
"$output_dir/luci-i18n-podkop-plus-ru_${RELEASE_VERSION}.apk" \
"$output_dir/luci-i18n-forkop-ru_${RELEASE_VERSION}.apk" \
"$MAINTAINER"
verify_ipk_metadata "$output_dir/podkop-plus_${RELEASE_VERSION}_all.ipk" "podkop-plus" "$RELEASE_VERSION"
verify_ipk_metadata "$output_dir/luci-app-podkop-plus_${RELEASE_VERSION}_all.ipk" "luci-app-podkop-plus" "$RELEASE_VERSION"
verify_ipk_metadata "$output_dir/luci-i18n-podkop-plus-ru_${RELEASE_VERSION}_all.ipk" "luci-i18n-podkop-plus-ru" "$RELEASE_VERSION"
verify_apk_metadata "$apk_bin" "$output_dir/podkop-plus_${RELEASE_VERSION}.apk" "podkop-plus" "$APK_INTERNAL_VERSION"
verify_apk_metadata "$apk_bin" "$output_dir/luci-app-podkop-plus_${RELEASE_VERSION}.apk" "luci-app-podkop-plus" "$APK_INTERNAL_VERSION"
verify_apk_metadata "$apk_bin" "$output_dir/luci-i18n-podkop-plus-ru_${RELEASE_VERSION}.apk" "luci-i18n-podkop-plus-ru" "$APK_INTERNAL_VERSION"
verify_ipk_metadata "$output_dir/forkop_${RELEASE_VERSION}.ipk" "forkop" "$RELEASE_VERSION"
verify_ipk_metadata "$output_dir/luci-app-forkop_${RELEASE_VERSION}.ipk" "luci-app-forkop" "$RELEASE_VERSION"
verify_ipk_metadata "$output_dir/luci-i18n-forkop-ru_${RELEASE_VERSION}.ipk" "luci-i18n-forkop-ru" "$RELEASE_VERSION"
verify_apk_metadata "$apk_bin" "$output_dir/forkop_${RELEASE_VERSION}.apk" "forkop" "$APK_INTERNAL_VERSION"
verify_apk_metadata "$apk_bin" "$output_dir/luci-app-forkop_${RELEASE_VERSION}.apk" "luci-app-forkop" "$APK_INTERNAL_VERSION"
verify_apk_metadata "$apk_bin" "$output_dir/luci-i18n-forkop-ru_${RELEASE_VERSION}.apk" "luci-i18n-forkop-ru" "$APK_INTERNAL_VERSION"
cleanup_work_dir
sync_artifacts_to_windows "$output_dir"
+16
View File
@@ -0,0 +1,16 @@
SFTP_HOST=192.168.160.129
SFTP_PORT=22
SFTP_USER=root
SFTP_PASS=
# you can use key if needed
# SFTP_PRIVATE_KEY=~/.ssh/id_rsa
LOCAL_DIR_FE=../luci-app-forkop/htdocs/luci-static/resources/view/forkop
REMOTE_DIR_FE=/www/luci-static/resources/view/forkop
LOCAL_DIR_BIN=../forkop/files/usr/bin/
REMOTE_DIR_BIN=/usr/bin/
LOCAL_DIR_LIB=../forkop/files/usr/lib/
REMOTE_DIR_LIB=/usr/lib/forkop/
@@ -6,7 +6,7 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const sourceDir = path.resolve(__dirname, 'locales');
const targetRoot = path.resolve(__dirname, '../luci-app-podkop-plus/po');
const targetRoot = path.resolve(__dirname, '../luci-app-forkop/po');
async function main() {
const files = await fs.readdir(sourceDir);
@@ -14,17 +14,17 @@ async function main() {
for (const file of files) {
const filePath = path.join(sourceDir, file);
if (file === 'podkop_plus.pot') {
const potTarget = path.join(targetRoot, 'templates', 'podkop_plus.pot');
if (file === 'forkop.pot') {
const potTarget = path.join(targetRoot, 'templates', 'forkop.pot');
await fs.mkdir(path.dirname(potTarget), { recursive: true });
await fs.copyFile(filePath, potTarget);
console.log(`✅ Copied POT: ${filePath} → ${potTarget}`);
}
const match = file.match(/^podkop_plus\.([a-zA-Z_]+)\.po$/);
const match = file.match(/^forkop\.([a-zA-Z_]+)\.po$/);
if (match) {
const lang = match[1];
const poTarget = path.join(targetRoot, lang, 'podkop_plus.po');
const poTarget = path.join(targetRoot, lang, 'forkop.po');
await fs.mkdir(path.dirname(poTarget), { recursive: true });
await fs.copyFile(filePath, poTarget);
console.log(`✅ Copied ${lang.toUpperCase()}: ${filePath} → ${poTarget}`);
@@ -19,12 +19,12 @@ function stripIllegalReturn(code) {
const files = await glob([
'src/**/*.ts',
'../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/**/*.js',
'../luci-app-forkop/htdocs/luci-static/resources/view/forkop/**/*.js',
], {
ignore: [
'**/*.test.ts',
'**/main.js',
'../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop/main.js',
'../luci-app-forkop/htdocs/luci-static/resources/view/forkop/main.js',
],
absolute: true,
});
@@ -8,7 +8,7 @@ if (!lang) {
}
const callsPath = 'locales/calls.json';
const poPath = `locales/podkop_plus.${lang}.po`;
const poPath = `locales/forkop.${lang}.po`;
function getGitUser() {
try {
@@ -36,14 +36,14 @@ function getHeader(lang) {
: 'nplurals=2; plural=(n != 1);';
return [
`# ${lang.toUpperCase()} translations for PODKOP PLUS package.`,
`# Copyright (C) ${now.getFullYear()} THE PODKOP PLUS COPYRIGHT HOLDER`,
`# This file is distributed under the same license as the PODKOP PLUS package.`,
`# ${lang.toUpperCase()} translations for FORKOP package.`,
`# Copyright (C) ${now.getFullYear()} THE FORKOP COPYRIGHT HOLDER`,
`# This file is distributed under the same license as the FORKOP package.`,
`# ${translator}, ${now.getFullYear()}.`,
'#',
'msgid ""',
'msgstr ""',
`"Project-Id-Version: PODKOP PLUS\\n"`,
`"Project-Id-Version: FORKOP\\n"`,
`"Report-Msgid-Bugs-To: \\n"`,
`"POT-Creation-Date: ${date} ${time}${tzOffset}\\n"`,
`"PO-Revision-Date: ${date} ${time}${tzOffset}\\n"`,
@@ -2,8 +2,8 @@ import fs from 'fs/promises';
import { execSync } from 'child_process';
const inputFile = 'locales/calls.json';
const outputFile = 'locales/podkop_plus.pot';
const projectId = 'PODKOP PLUS';
const outputFile = 'locales/forkop.pot';
const projectId = 'FORKOP';
function getGitUser() {
try {
@@ -18,8 +18,8 @@ function getGitUser() {
}
return {
name: 'Podkop Plus',
email: 'noreply@podkop.plus',
name: 'Forkop',
email: 'ushan0v@users.noreply.github.com',
};
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,14 +1,14 @@
# RU translations for PODKOP PLUS package.
# Copyright (C) 2026 THE PODKOP PLUS COPYRIGHT HOLDER
# This file is distributed under the same license as the PODKOP PLUS package.
# RU translations for FORKOP package.
# Copyright (C) 2026 THE FORKOP COPYRIGHT HOLDER
# This file is distributed under the same license as the FORKOP package.
# ushan0v, 2026.
#
msgid ""
msgstr ""
"Project-Id-Version: PODKOP PLUS\n"
"Project-Id-Version: FORKOP\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-10 02:05+0300\n"
"PO-Revision-Date: 2026-07-10 02:05+0300\n"
"POT-Creation-Date: 2026-07-11 06:01+0300\n"
"PO-Revision-Date: 2026-07-11 06:01+0300\n"
"Last-Translator: ushan0v\n"
"Language-Team: none\n"
"Language: ru\n"
@@ -185,8 +185,8 @@ msgstr "Встроенные наборы правил"
msgid "By flag emoji from name"
msgstr "По эмодзи флага из названия"
msgid "ByeDPI listen address and port are assigned by Podkop Plus and must not be set in the strategy."
msgstr "Адрес и порт прослушивания ByeDPI назначаются Podkop Plus и не должны задаваться в стратегии."
msgid "ByeDPI listen address and port are assigned by Forkop and must not be set in the strategy."
msgstr "Адрес и порт прослушивания ByeDPI назначаются Forkop и не должны задаваться в стратегии."
msgid "ByeDPI option does not accept a compact value: %s"
msgstr "Опция ByeDPI не принимает компактное значение: %s"
@@ -317,8 +317,8 @@ msgstr "Условия"
msgid "Config File Path"
msgstr "Путь к файлу конфигурации"
msgid "Configuration for Podkop Plus service"
msgstr "Настройки сервиса Podkop Plus"
msgid "Configuration for Forkop service"
msgstr "Настройки сервиса Forkop"
msgid "Configure User-Agent in the item settings"
msgstr "Настройте User-Agent в настройках элемента"
@@ -368,8 +368,8 @@ msgstr "Дашборд сейчас недоступен"
msgid "Default address for checking server availability and latency. URLTest uses its own address."
msgstr "Адрес по умолчанию для проверки доступности и задержки серверов. Для URLTest используется собственный адрес."
msgid "Delay in milliseconds before reloading Podkop Plus after interface UP"
msgstr "Задержка в миллисекундах перед перезапуском Podkop Plus после поднятия интерфейса"
msgid "Delay in milliseconds before reloading Forkop after interface UP"
msgstr "Задержка в миллисекундах перед перезапуском Forkop после поднятия интерфейса"
msgid "Delay value cannot be empty"
msgstr "Значение задержки не может быть пустым"
@@ -416,8 +416,8 @@ 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 "Disabling interception is incompatible with action=zapret2 because Forkop sends matched traffic through NFQUEUE."
msgstr "Отключение перехвата несовместимо с action=zapret2, потому что Forkop отправляет совпавший трафик через NFQUEUE."
msgid "Dismiss"
msgstr "Отмена"
@@ -602,11 +602,11 @@ msgstr "Истекает"
msgid "Expose this section as a local HTTP+SOCKS proxy"
msgstr "Открыть эту секцию как локальный HTTP+SOCKS прокси"
msgid "External nfqws config files bypass Podkop Plus queue management and explicit validation."
msgstr "Внешние конфиги nfqws обходят управление очередями и явную валидацию Podkop Plus."
msgid "External nfqws config files bypass Forkop queue management and explicit validation."
msgstr "Внешние конфиги nfqws обходят управление очередями и явную валидацию Forkop."
msgid "External nfqws2 config files bypass Podkop Plus queue management and explicit validation."
msgstr "Внешние конфиги nfqws2 обходят управление очередями и явную валидацию Podkop Plus."
msgid "External nfqws2 config files bypass Forkop queue management and explicit validation."
msgstr "Внешние конфиги nfqws2 обходят управление очередями и явную валидацию Forkop."
msgid "Failed to close connection"
msgstr "Не удалось закрыть подключение"
@@ -653,6 +653,63 @@ msgstr "Исправьте подсвеченные поля и сохранит
msgid "Forced device routing"
msgstr "Принудительная маршрутизация устройств"
msgid "Forkop Error"
msgstr "Ошибка Forkop"
msgid "Forkop manages ciadpi pid files itself, so pidfile options are not allowed here."
msgstr "Forkop сам управляет pid-файлами ciadpi, поэтому параметры pidfile здесь запрещены."
msgid "Forkop manages the ciadpi process lifecycle itself, so daemon mode is not allowed here."
msgstr "Forkop сам управляет жизненным циклом ciadpi, поэтому режим daemon здесь запрещён."
msgid "Forkop manages the nfqws process lifecycle itself, so daemon mode is not allowed here."
msgstr "Forkop сам управляет жизненным циклом nfqws, поэтому режим daemon здесь запрещён."
msgid "Forkop manages the nfqws2 process lifecycle itself, so daemon mode is not allowed here."
msgstr "Forkop сам управляет жизненным циклом nfqws2, поэтому режим daemon здесь запрещён."
msgid "Forkop service is stopped. Start the service to display connections."
msgstr "Служба Forkop остановлена. Запустите сервис для отображения подключений."
msgid "Forkop service is stopped. Start the service to display the dashboard."
msgstr "Служба Forkop остановлена. Запустите сервис для отображения дашборда."
msgid "Forkop Settings"
msgstr "Настройки Forkop"
msgid "Forkop will not modify your DHCP configuration"
msgstr "Forkop не будет изменять вашу конфигурацию DHCP"
msgid "Forkop-managed ciadpi runtime has restarted"
msgstr "ciadpi Forkop перезапускался"
msgid "Forkop-managed ciadpi runtime is not ready"
msgstr "ciadpi Forkop не готов"
msgid "Forkop-managed ciadpi runtime is not running"
msgstr "ciadpi Forkop не запущен"
msgid "Forkop-managed ciadpi runtime is ready"
msgstr "ciadpi Forkop готов"
msgid "Forkop-managed nfqws runtime is not ready"
msgstr "NFQWS Forkop не готов"
msgid "Forkop-managed nfqws runtime is not running"
msgstr "NFQWS Forkop не запущен"
msgid "Forkop-managed nfqws runtime is ready"
msgstr "NFQWS Forkop готов"
msgid "Forkop-managed nfqws2 runtime is not ready"
msgstr "NFQWS2 Forkop не готов"
msgid "Forkop-managed nfqws2 runtime is not running"
msgstr "NFQWS2 Forkop не запущен"
msgid "Forkop-managed nfqws2 runtime is ready"
msgstr "NFQWS2 Forkop готов"
msgid "Fronting IP"
msgstr "IP фронтинга"
@@ -662,8 +719,8 @@ msgstr "Порт фронтинга"
msgid "Fronting proxy protocol"
msgstr "Proxy protocol для фронтинга"
msgid "Fuzzing is not supported here because Podkop Plus needs deterministic runtime validation."
msgstr "Fuzzing здесь не поддерживается, потому что Podkop Plus нужна предсказуемая runtime-валидация."
msgid "Fuzzing is not supported here because Forkop needs deterministic runtime validation."
msgstr "Fuzzing здесь не поддерживается, потому что Forkop нужна предсказуемая runtime-валидация."
msgid "Generate HWID from router hardware information for this subscription"
msgstr "Генерировать HWID из аппаратной информации роутера для этой подписки"
@@ -1283,63 +1340,6 @@ msgstr "Периодически проверять текущий уровен
msgid "Pin on dashboard"
msgstr "Закрепить в дашборде"
msgid "Podkop Plus Error"
msgstr "Ошибка Podkop Plus"
msgid "Podkop Plus manages ciadpi pid files itself, so pidfile options are not allowed here."
msgstr "Podkop Plus сам управляет pid-файлами ciadpi, поэтому параметры pidfile здесь запрещены."
msgid "Podkop Plus manages the ciadpi process lifecycle itself, so daemon mode is not allowed here."
msgstr "Podkop Plus сам управляет жизненным циклом ciadpi, поэтому режим daemon здесь запрещён."
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 service is stopped. Start the service to display connections."
msgstr "Служба Podkop Plus остановлена. Запустите сервис для отображения подключений."
msgid "Podkop Plus service is stopped. Start the service to display the dashboard."
msgstr "Служба Podkop Plus остановлена. Запустите сервис для отображения дашборда."
msgid "Podkop Plus Settings"
msgstr "Настройки Podkop Plus"
msgid "Podkop Plus will not modify your DHCP configuration"
msgstr "Podkop Plus не будет изменять вашу конфигурацию DHCP"
msgid "Podkop Plus-managed ciadpi runtime has restarted"
msgstr "ciadpi Podkop Plus перезапускался"
msgid "Podkop Plus-managed ciadpi runtime is not ready"
msgstr "ciadpi Podkop Plus не готов"
msgid "Podkop Plus-managed ciadpi runtime is not running"
msgstr "ciadpi Podkop Plus не запущен"
msgid "Podkop Plus-managed ciadpi runtime is ready"
msgstr "ciadpi Podkop Plus готов"
msgid "Podkop Plus-managed nfqws runtime is not ready"
msgstr "NFQWS Podkop Plus не готов"
msgid "Podkop Plus-managed nfqws runtime is not running"
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 "Порт не может быть пустым"
@@ -1457,8 +1457,8 @@ msgstr "Выбор ресурсов по IP или CIDR внутри nfqws зд
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"
msgid "Restart Forkop"
msgstr "Перезапустить Forkop"
msgid "Resume updates"
msgstr "Продолжить обновление"
@@ -1703,8 +1703,8 @@ msgstr "Укажите разные файлы"
msgid "Standalone ByeDPI autostart is enabled"
msgstr "Автозапуск отдельной службы ByeDPI включен"
msgid "Standalone ByeDPI is active together with Podkop Plus ByeDPI rules"
msgstr "Отдельная служба ByeDPI активна вместе с правилами ByeDPI в Podkop Plus"
msgid "Standalone ByeDPI is active together with Forkop ByeDPI rules"
msgstr "Отдельная служба ByeDPI активна вместе с правилами ByeDPI в Forkop"
msgid "Standalone ByeDPI service is active"
msgstr "Отдельная служба ByeDPI активна"
@@ -1712,8 +1712,8 @@ msgstr "Отдельная служба ByeDPI активна"
msgid "Standalone ByeDPI service is inactive"
msgstr "Отдельная служба ByeDPI неактивна"
msgid "Standalone Zapret is active together with Podkop Plus Zapret rules"
msgstr "Отдельная служба Zapret активна вместе с правилами Zapret в Podkop Plus"
msgid "Standalone Zapret is active together with Forkop Zapret rules"
msgstr "Отдельная служба Zapret активна вместе с правилами Zapret в Forkop"
msgid "Standalone Zapret service is active"
msgstr "Отдельная служба Zapret активна"
@@ -1724,8 +1724,8 @@ 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 is active together with Forkop Zapret2 rules"
msgstr "Отдельная служба Zapret2 активна вместе с правилами Zapret2 в Forkop"
msgid "Standalone Zapret2 service is active"
msgstr "Отдельная служба Zapret2 активна"
@@ -1733,14 +1733,14 @@ msgstr "Отдельная служба Zapret2 активна"
msgid "Standalone Zapret2 service is inactive"
msgstr "Отдельная служба Zapret2 неактивна"
msgid "Start Podkop Plus"
msgstr "Запустить Podkop Plus"
msgid "Start Forkop"
msgstr "Запустить Forkop"
msgid "Stop checking when URLTest group is not used. Use sing-box duration format like 1d, 12h or 30m."
msgstr "Останавливать проверку если URLTest группа не используется. Используйте формат длительности sing-box, например 1d, 12h или 30m."
msgid "Stop Podkop Plus"
msgstr "Остановить Podkop Plus"
msgid "Stop Forkop"
msgstr "Остановить Forkop"
msgid "Subnets from the list will be extracted and added to nftables"
msgstr "IP-адреса и подсети из списка будут извлечены и добавлены в nftables"
@@ -1805,11 +1805,11 @@ msgstr "Тестировать серверы, имена которых сов
msgid "Testing URL"
msgstr "URL проверки"
msgid "The desync fwmark is managed by Podkop Plus for loop prevention and must not be overridden here."
msgstr "Desync fwmark управляется Podkop Plus для защиты от циклов и не должен переопределяться здесь."
msgid "The desync fwmark is managed by Forkop for loop prevention and must not be overridden here."
msgstr "Desync fwmark управляется Forkop для защиты от циклов и не должен переопределяться здесь."
msgid "The NFQUEUE number is assigned by Podkop Plus for each rule and must not be overridden here."
msgstr "Номер NFQUEUE назначается Podkop Plus для каждого правила и не должен переопределяться здесь."
msgid "The NFQUEUE number is assigned by Forkop for each rule and must not be overridden here."
msgstr "Номер NFQUEUE назначается Forkop для каждого правила и не должен переопределяться здесь."
msgid "The rule applies to the domain and all its subdomains. Use full:, keyword:, or regex: prefixes for exact match, keyword match, or regular expression."
msgstr "Правило применяется к домену и всем его поддоменам. Для полного совпадения, совпадению по слову или регулярному выражению используйте префиксы full:, keyword: или regex:."
@@ -1874,8 +1874,8 @@ msgstr "Трафик этого уровня пойдёт напрямую."
msgid "Traffic Total"
msgstr "Всего трафика"
msgid "Transparent proxy mode is incompatible with action=byedpi because Podkop Plus connects to ciadpi through SOCKS."
msgstr "Режим прозрачного прокси несовместим с action=byedpi, потому что Podkop Plus подключается к ciadpi через SOCKS."
msgid "Transparent proxy mode is incompatible with action=byedpi because Forkop connects to ciadpi through SOCKS."
msgstr "Режим прозрачного прокси несовместим с action=byedpi, потому что Forkop подключается к ciadpi через SOCKS."
msgid "Transport"
msgstr "Транспорт"
@@ -1922,14 +1922,14 @@ msgstr "Таймаут недоступности"
msgid "Unexpected ByeDPI strategy argument: %s"
msgstr "Неожиданный аргумент стратегии ByeDPI: %s"
msgid "Unexpected Podkop Plus-managed ciadpi runtime is running"
msgstr "Обнаружен лишний ciadpi Podkop Plus"
msgid "Unexpected Forkop-managed ciadpi runtime is running"
msgstr "Обнаружен лишний ciadpi Forkop"
msgid "Unexpected Podkop Plus-managed nfqws runtime is running"
msgstr "Обнаружен лишний NFQWS Podkop Plus"
msgid "Unexpected Forkop-managed nfqws runtime is running"
msgstr "Обнаружен лишний NFQWS Forkop"
msgid "Unexpected Podkop Plus-managed nfqws2 runtime is running"
msgstr "Обнаружен лишний NFQWS2 Podkop Plus"
msgid "Unexpected Forkop-managed nfqws2 runtime is running"
msgstr "Обнаружен лишний NFQWS2 Forkop"
msgid "Unexpected standalone token. Use explicit flags such as --name or --name=value."
msgstr "Неожиданный отдельный токен. Используйте явные флаги вида --имя или --имя=значение."
@@ -2099,8 +2099,8 @@ msgstr "Предупреждение: %s нельзя использовать
msgid "Warning: Russia inside can only be used with %s. %s already in Russia inside and have been removed from selection."
msgstr "Предупреждение: Russia inside может быть использован только с %s. %s уже есть в Russia inside и будет удален из выбранных."
msgid "What Podkop Plus should do when this section matches"
msgstr "Что Podkop Plus должен делать при совпадении этой секции"
msgid "What Forkop should do when this section matches"
msgstr "Что Forkop должен делать при совпадении этой секции"
msgid "When switching to another level, test every server and select the fastest instead of the first working one."
msgstr "При переходе на другой уровень тестировать все сервера и выбирать самый быстрый, а не первый работающий."
@@ -2117,8 +2117,8 @@ msgstr "Да"
msgid "You can select Output Network Interface, by default autodetect"
msgstr "Вы можете выбрать выходной сетевой интерфейс, по умолчанию он определяется автоматически."
msgid "Zapret hostlist templates are not supported here because Podkop Plus does not expand them for per-rule NFQWS strategies."
msgstr "Шаблоны hostlist zapret здесь не поддерживаются, потому что Podkop Plus не разворачивает их для per-rule стратегий NFQWS."
msgid "Zapret hostlist templates are not supported here because Forkop does not expand them for per-rule NFQWS strategies."
msgstr "Шаблоны hostlist zapret здесь не поддерживаются, потому что Forkop не разворачивает их для per-rule стратегий NFQWS."
msgid "Zapret package is installed"
msgstr "Пакет Zapret установлен"
@@ -2138,8 +2138,8 @@ msgstr "sing-box outbound для Zapret настроен"
msgid "Zapret sing-box outbound is not configured"
msgstr "sing-box outbound для 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 hostlist templates are not supported here because Forkop does not expand them for per-rule NFQWS2 strategies."
msgstr "Шаблоны hostlist Zapret2 здесь не поддерживаются, потому что Forkop не разворачивает их для per-rule стратегий NFQWS2."
msgid "Zapret2 package is installed"
msgstr "Пакет Zapret2 установлен"
@@ -1,11 +1,11 @@
{
"name": "fe-app-podkop",
"name": "fe-app-forkop",
"version": "1.0.0",
"license": "MIT",
"type": "module",
"scripts": {
"format": "prettier --write src",
"format:js": "prettier --write ../luci-app-podkop-plus/htdocs/luci-static/resources/view/podkop",
"format:js": "prettier --write ../luci-app-forkop/htdocs/luci-static/resources/view/forkop",
"lint": "eslint src --ext .ts,.tsx",
"lint:fix": "eslint src --ext .ts,.tsx --fix",
"build": "tsup src/main.ts",
@@ -1,11 +1,10 @@
export const PODKOP_UCI_PACKAGE = 'podkop-plus';
export const PODKOP_LUCI_APP_VERSION = '__COMPILED_VERSION_VARIABLE__';
export const PODKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT =
'podkop:action-providers-availability';
export const FAKEIP_CHECK_DOMAIN = 'fakeip.podkop.fyi';
export const IP_CHECK_DOMAIN = 'ip.podkop.fyi';
export const DEFAULT_LATENCY_TEST_URL =
'https://www.gstatic.com/generate_204';
export const FORKOP_UCI_PACKAGE = 'forkop';
export const FORKOP_LUCI_APP_VERSION = '__COMPILED_VERSION_VARIABLE__';
export const FORKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT =
'forkop:action-providers-availability';
export const FAKEIP_CHECK_DOMAIN = 'fakeip.forkop.fyi';
export const IP_CHECK_DOMAIN = 'ip.forkop.fyi';
export const DEFAULT_LATENCY_TEST_URL = 'https://www.gstatic.com/generate_204';
export const LATENCY_TEST_URL_OPTIONS = [
DEFAULT_LATENCY_TEST_URL,
'https://cp.cloudflare.com/generate_204',
@@ -1,15 +1,15 @@
import { PodkopShellMethods } from '../methods';
import { ForkopShellMethods } from '../methods';
import { logger } from '../services/logger.service';
import { store } from '../services/store.service';
import { refreshRuntimeUiState } from '../services/runtimeUiState.service';
import { Podkop } from '../types';
import { Forkop } from '../types';
let latestServicesInfoRequestId = 0;
function getSettledMethodResponse<T>(
scope: string,
result: PromiseSettledResult<Podkop.MethodResponse<T>>,
): Podkop.MethodResponse<T> {
result: PromiseSettledResult<Forkop.MethodResponse<T>>,
): Forkop.MethodResponse<T> {
if (result.status === 'fulfilled') {
return result.value;
}
@@ -34,34 +34,34 @@ export async function fetchServicesInfo() {
return uiState;
}
const [podkopResult, singboxResult] = await Promise.allSettled([
PodkopShellMethods.getStatus(),
PodkopShellMethods.getSingBoxStatus(),
const [forkopResult, singboxResult] = await Promise.allSettled([
ForkopShellMethods.getStatus(),
ForkopShellMethods.getSingBoxStatus(),
]);
if (requestId !== latestServicesInfoRequestId) {
return;
}
const podkop = getSettledMethodResponse('getStatus', podkopResult);
const forkop = getSettledMethodResponse('getStatus', forkopResult);
const singbox = getSettledMethodResponse('getSingBoxStatus', singboxResult);
const previousData = store.get().servicesInfoWidget.data;
store.set({
servicesInfoWidget: {
loading: false,
failed: !podkop.success || !singbox.success,
failed: !forkop.success || !singbox.success,
data: {
singbox: singbox.success ? singbox.data.running : previousData.singbox,
podkopRunning: podkop.success
? podkop.data.running
: previousData.podkopRunning,
podkopEnabled: podkop.success
? podkop.data.enabled
: previousData.podkopEnabled,
podkopStatus: podkop.success
? podkop.data.status
: previousData.podkopStatus,
forkopRunning: forkop.success
? forkop.data.running
: previousData.forkopRunning,
forkopEnabled: forkop.success
? forkop.data.enabled
: previousData.forkopEnabled,
forkopStatus: forkop.success
? forkop.data.status
: previousData.forkopStatus,
},
},
});
@@ -20,7 +20,7 @@ describe('fetchServicesInfo', () => {
it('returns the fast UI state after applying it to the shared store', async () => {
const uiState = {
service: {
podkop: {
forkop: {
running: 1,
enabled: 1,
status: 'restarting',
@@ -67,7 +67,7 @@ describe('fetchServicesInfo', () => {
const state = store.get();
expect(state.servicesInfoWidget.data.podkopStatus).toBe('restarting');
expect(state.servicesInfoWidget.data.forkopStatus).toBe('restarting');
expect(state.diagnosticsActions.restart.loading).toBe(true);
});
@@ -78,9 +78,9 @@ describe('fetchServicesInfo', () => {
failed: false,
data: {
singbox: 1,
podkopRunning: 1,
podkopEnabled: 1,
podkopStatus: 'running & enabled',
forkopRunning: 1,
forkopEnabled: 1,
forkopStatus: 'running & enabled',
},
},
});
@@ -113,9 +113,9 @@ describe('fetchServicesInfo', () => {
expect(state.servicesInfoWidget.failed).toBe(true);
expect(state.servicesInfoWidget.data).toEqual({
singbox: 0,
podkopRunning: 1,
podkopEnabled: 1,
podkopStatus: 'running & enabled',
forkopRunning: 1,
forkopEnabled: 1,
forkopStatus: 'running & enabled',
});
});
});
@@ -1,11 +1,11 @@
import type { StoreType } from '../services/store.service';
import type { Podkop } from '../types';
import type { Forkop } from '../types';
export type UpdatesActionKey = keyof StoreType['updatesActions'];
const componentActionKeyMap: Record<string, UpdatesActionKey> = {
'podkop:check_update': 'podkopCheck',
'podkop:install': 'podkopInstall',
'forkop:check_update': 'forkopCheck',
'forkop:install': 'forkopInstall',
'sing_box:check_update': 'singBoxCheck',
'sing_box:install': 'singBoxInstall',
'sing_box:install_extended': 'singBoxInstallExtended',
@@ -24,8 +24,8 @@ const componentActionKeyMap: Record<string, UpdatesActionKey> = {
};
export function getComponentActionKey(
component: Podkop.ComponentName,
action: Podkop.ComponentAction,
component: Forkop.ComponentName,
action: Forkop.ComponentAction,
): UpdatesActionKey | undefined {
return componentActionKeyMap[`${component}:${action}`];
}
@@ -26,8 +26,7 @@ function isVersionPlaceholder(version?: string) {
return (
(typeof _ === 'function' && normalized === _('unknown').toLowerCase()) ||
(typeof _ === 'function' &&
normalized === _('Not installed').toLowerCase())
(typeof _ === 'function' && normalized === _('Not installed').toLowerCase())
);
}
@@ -68,7 +67,6 @@ export function normalizeSingBoxVariantFields<T extends SingBoxVariantFields>(
sing_box_extended: singBoxExtended ? 1 : 0,
sing_box_tiny: singBoxExtended ? 0 : value.sing_box_tiny ? 1 : 0,
sing_box_compressed: singBoxExtended && value.sing_box_compressed ? 1 : 0,
sing_box_tailscale:
singBoxExtended || value.sing_box_tailscale ? 1 : 0,
sing_box_tailscale: singBoxExtended || value.sing_box_tailscale ? 1 : 0,
} as T;
}
@@ -0,0 +1,8 @@
import { Forkop } from '../../types';
import { FORKOP_UCI_PACKAGE } from '../../../constants';
export async function getConfigSections(): Promise<Forkop.ConfigSection[]> {
return uci
.load(FORKOP_UCI_PACKAGE)
.then(() => uci.sections(FORKOP_UCI_PACKAGE));
}
@@ -1,5 +1,5 @@
import { getConfigSections } from './getConfigSections';
import { ClashAPI, Podkop } from '../../types';
import { ClashAPI, Forkop } from '../../types';
import {
canUseDirectClashApi,
getClashHttpUrl,
@@ -7,11 +7,11 @@ import {
isCopyableProxyLink,
} from '../../../helpers';
import { getOutboundTagBySection } from '../../runtimeTags';
import { PodkopShellMethods } from '../shell';
import { ForkopShellMethods } from '../shell';
interface IGetDashboardSectionsResponse {
success: boolean;
data: Podkop.OutboundGroup[];
data: Forkop.OutboundGroup[];
}
interface IGetDashboardSectionsOptions {
@@ -28,12 +28,12 @@ type DashboardSectionCache = {
section?: string;
links?: Record<string, string>;
linkRefs?: Record<string, unknown>;
outboundMetadata?: Podkop.GetOutboundMetadata;
outboundMetadata?: Forkop.GetOutboundMetadata;
urltestGroups?: Record<string, UrlTestCacheGroup>;
priorityGroups?: Record<string, PriorityCacheGroup>;
subscriptionMetadata?:
| Podkop.SubscriptionMetadata
| Podkop.SubscriptionMetadata[];
| Forkop.SubscriptionMetadata
| Forkop.SubscriptionMetadata[];
};
type UrlTestCacheGroup = {
@@ -129,21 +129,21 @@ type ChildType =
| 'priority_group'
| 'priority_level';
const DASHBOARD_SECTION_CACHE_DIR = '/var/run/podkop-plus/section-cache';
const DASHBOARD_SECTION_CACHE_DIR = '/var/run/forkop/section-cache';
function getDisplayName(section: Podkop.ConfigSection) {
function getDisplayName(section: Forkop.ConfigSection) {
return section.label || section['.name'];
}
function getSectionAction(section: Podkop.ConfigSection) {
function getSectionAction(section: Forkop.ConfigSection) {
return section.action || '';
}
function getSettingsSection(configSections: Podkop.ConfigSection[]) {
function getSettingsSection(configSections: Forkop.ConfigSection[]) {
return configSections.find((section) => section['.type'] === 'settings');
}
function getClashApiSecret(configSections: Podkop.ConfigSection[]) {
function getClashApiSecret(configSections: Forkop.ConfigSection[]) {
return getSettingsSection(configSections)?.yacd_secret_key || '';
}
@@ -152,8 +152,8 @@ function canFetchClashApiDirectly() {
}
async function getClashApiProxies(
configSections: Podkop.ConfigSection[],
): Promise<Podkop.MethodResponse<ClashAPI.Proxies>> {
configSections: Forkop.ConfigSection[],
): Promise<Forkop.MethodResponse<ClashAPI.Proxies>> {
if (canFetchClashApiDirectly()) {
const secret = getClashApiSecret(configSections);
@@ -173,7 +173,7 @@ async function getClashApiProxies(
}
}
return PodkopShellMethods.getClashApiProxies();
return ForkopShellMethods.getClashApiProxies();
}
function getListValues(value?: string[] | string) {
@@ -192,25 +192,25 @@ function getListValues(value?: string[] | string) {
}
function childSections(
configSections: Podkop.ConfigSection[],
configSections: Forkop.ConfigSection[],
type: ChildType,
) {
return configSections.filter((section) => section['.type'] === type);
}
function ownedChildSections(
parent: Podkop.ConfigSection,
children: Podkop.ConfigSection[],
parent: Forkop.ConfigSection,
children: Forkop.ConfigSection[],
) {
return children.filter(
(section): section is Podkop.ConfigSection =>
(section): section is Forkop.ConfigSection =>
section.section === parent['.name'],
);
}
function childSectionsByOwner(
children: Podkop.ConfigSection[],
ownerKey: keyof Podkop.ConfigSection,
children: Forkop.ConfigSection[],
ownerKey: keyof Forkop.ConfigSection,
ownerValue: string,
) {
return children.filter((section) => section[ownerKey] === ownerValue);
@@ -220,7 +220,7 @@ function compactSettingsMap(settings: Record<string, ItemSettings>) {
return Object.keys(settings).length ? JSON.stringify(settings) : undefined;
}
function hydrateConfigSections(configSections: Podkop.ConfigSection[]) {
function hydrateConfigSections(configSections: Forkop.ConfigSection[]) {
const subscriptionUrls = childSections(configSections, 'subscription_url');
const urltests = childSections(configSections, 'urltest');
const priorityGroups = childSections(configSections, 'priority_group');
@@ -231,7 +231,7 @@ function hydrateConfigSections(configSections: Podkop.ConfigSection[]) {
return section;
}
const next: Podkop.ConfigSection = { ...section };
const next: Forkop.ConfigSection = { ...section };
const subscriptionUrlItems = ownedChildSections(next, subscriptionUrls);
const urltestItems = ownedChildSections(next, urltests);
const priorityGroupItems = ownedChildSections(next, priorityGroups);
@@ -341,16 +341,16 @@ function hydrateConfigSections(configSections: Podkop.ConfigSection[]) {
});
}
function getManualProxyLinks(section: Podkop.ConfigSection) {
function getManualProxyLinks(section: Forkop.ConfigSection) {
return getListValues(section.selector_proxy_links);
}
function getConnectionInterfaces(section: Podkop.ConfigSection) {
function getConnectionInterfaces(section: Forkop.ConfigSection) {
const values = getListValues(section.interfaces);
return values.length ? values : getListValues(section.interface);
}
function getJsonOutbounds(section: Podkop.ConfigSection) {
function getJsonOutbounds(section: Forkop.ConfigSection) {
const values = getListValues(section.outbound_jsons);
return values.length ? values : getListValues(section.outbound_json);
}
@@ -359,27 +359,27 @@ function isConnectionAction(action: string) {
return ['connection', 'proxy', 'outbound', 'vpn'].includes(action);
}
function hasSubscriptionSources(section: Podkop.ConfigSection) {
function hasSubscriptionSources(section: Forkop.ConfigSection) {
return getSubscriptionSourceCount(section) > 0;
}
function getSubscriptionSourceCount(section: Podkop.ConfigSection) {
function getSubscriptionSourceCount(section: Forkop.ConfigSection) {
return getListValues(section.subscription_urls).length;
}
function shouldSortByLatency(section: Podkop.ConfigSection) {
function shouldSortByLatency(section: Forkop.ConfigSection) {
return section.sort_by_latency === '1';
}
function hasConfiguredUrlTestList(section: Podkop.ConfigSection) {
function hasConfiguredUrlTestList(section: Forkop.ConfigSection) {
return getListValues(section.urltests).length > 0;
}
function hasConfiguredPriorityList(section: Podkop.ConfigSection) {
function hasConfiguredPriorityList(section: Forkop.ConfigSection) {
return getListValues(section.priority_groups).length > 0;
}
function getUrlTestIds(section: Podkop.ConfigSection) {
function getUrlTestIds(section: Forkop.ConfigSection) {
const values = getListValues(section.urltests);
return values.length
? values
@@ -388,11 +388,11 @@ function getUrlTestIds(section: Podkop.ConfigSection) {
: [];
}
function isUrlTestEnabled(section: Podkop.ConfigSection) {
function isUrlTestEnabled(section: Forkop.ConfigSection) {
return getUrlTestIds(section).length > 0;
}
function shouldUseProxyGroup(section: Podkop.ConfigSection) {
function shouldUseProxyGroup(section: Forkop.ConfigSection) {
return (
getManualProxyLinks(section).length > 0 ||
hasSubscriptionSources(section) ||
@@ -403,7 +403,7 @@ function shouldUseProxyGroup(section: Podkop.ConfigSection) {
);
}
function getSectionProxyConfigType(section: Podkop.ConfigSection) {
function getSectionProxyConfigType(section: Forkop.ConfigSection) {
if (hasSubscriptionSources(section)) {
return 'subscription' as const;
}
@@ -431,7 +431,7 @@ function getSectionProxyConfigType(section: Podkop.ConfigSection) {
return undefined;
}
function getJsonOutboundDisplayName(section: Podkop.ConfigSection) {
function getJsonOutboundDisplayName(section: Forkop.ConfigSection) {
try {
const parsedOutbound = JSON.parse(section.outbound_json || '{}');
return parsedOutbound?.tag ? decodeURIComponent(parsedOutbound.tag) : '';
@@ -440,7 +440,7 @@ function getJsonOutboundDisplayName(section: Podkop.ConfigSection) {
}
}
function buildManualLinkByCode(section: Podkop.ConfigSection) {
function buildManualLinkByCode(section: Forkop.ConfigSection) {
const sectionName = section['.name'];
return new Map(
@@ -459,7 +459,7 @@ function uniqueCodes(codes: string[]) {
return Array.from(new Set(codes.filter(Boolean)));
}
function isSelectorOutbound(outbound: Podkop.Outbound) {
function isSelectorOutbound(outbound: Forkop.Outbound) {
return outbound.type?.toLowerCase() === 'selector';
}
@@ -476,7 +476,7 @@ function getLatencySortValue(outbound: { latency: number }) {
}
function sortOutboundsForDashboard(
outbounds: Podkop.Outbound[],
outbounds: Forkop.Outbound[],
options: {
pinnedCode?: string;
pinnedCodes?: string[];
@@ -527,7 +527,7 @@ function sortOutboundsForDashboard(
.map((item) => item.outbound);
}
function sortUrlTestMembers(outbounds: Podkop.UrlTestMember[]) {
function sortUrlTestMembers(outbounds: Forkop.UrlTestMember[]) {
return outbounds
.map((outbound, index) => ({ outbound, index }))
.sort((left, right) => {
@@ -627,7 +627,7 @@ function getPriorityTag(sectionName: string, id: string) {
}
function getUrlTestDisplayName(
section: Podkop.ConfigSection,
section: Forkop.ConfigSection,
id: string,
settings: ItemSettings | undefined,
) {
@@ -638,7 +638,7 @@ function getUrlTestDisplayName(
);
}
function getUrlTestConfigs(section: Podkop.ConfigSection): UrlTestConfig[] {
function getUrlTestConfigs(section: Forkop.ConfigSection): UrlTestConfig[] {
const settingsMap = itemSettingsMap(section.urltest_settings);
const sectionName = section['.name'];
@@ -721,11 +721,11 @@ function priorityLevelConfigsFromSettings(
);
}
function getPriorityGroupIds(section: Podkop.ConfigSection) {
function getPriorityGroupIds(section: Forkop.ConfigSection) {
return getListValues(section.priority_groups);
}
function getPriorityConfigs(section: Podkop.ConfigSection): PriorityConfig[] {
function getPriorityConfigs(section: Forkop.ConfigSection): PriorityConfig[] {
const settingsMap = itemSettingsMap(section.priority_group_settings);
const sectionName = section['.name'];
@@ -831,7 +831,7 @@ function getOutboundDisplayName(
code: string,
entry: ClashProxyEntry | undefined,
link: string,
outboundMetadata?: Podkop.GetOutboundMetadata,
outboundMetadata?: Forkop.GetOutboundMetadata,
preferMetadata = false,
) {
const metadataName = outboundMetadata?.names?.[code];
@@ -863,10 +863,10 @@ function buildUrlTestInfo({
proxyByCode: Map<string, ClashProxyEntry>;
manualLinkByCode: Map<string, string>;
cachedProxyLinks: Map<string, string>;
outboundMetadata?: Podkop.GetOutboundMetadata;
outboundMetadata?: Forkop.GetOutboundMetadata;
subscriptionCopyableCodes: Set<string>;
showDetectedCountries: boolean;
}): Podkop.UrlTestInfo {
}): Forkop.UrlTestInfo {
const childCodes = uniqueCodes(
groupCache?.outbounds?.length
? groupCache.outbounds
@@ -940,10 +940,10 @@ function buildPriorityInfo({
proxyByCode: Map<string, ClashProxyEntry>;
manualLinkByCode: Map<string, string>;
cachedProxyLinks: Map<string, string>;
outboundMetadata?: Podkop.GetOutboundMetadata;
outboundMetadata?: Forkop.GetOutboundMetadata;
subscriptionCopyableCodes: Set<string>;
showDetectedCountries: boolean;
}): Podkop.PriorityInfo {
}): Forkop.PriorityInfo {
const selectedCode = entry?.value.now || '';
const cacheLevels = Array.isArray(groupCache?.levels)
? groupCache.levels
@@ -1067,9 +1067,9 @@ function buildPriorityInfo({
}
function buildProxyGroupOutbounds(
section: Podkop.ConfigSection,
section: Forkop.ConfigSection,
proxies: ClashProxyEntry[],
outboundMetadata?: Podkop.GetOutboundMetadata,
outboundMetadata?: Forkop.GetOutboundMetadata,
urltestGroups: Record<string, UrlTestCacheGroup> = {},
priorityGroups: Record<string, PriorityCacheGroup> = {},
subscriptionCopyableCodes: Set<string> = new Set(),
@@ -1252,9 +1252,9 @@ function buildProxyGroupOutbounds(
function metadataMatchesCurrentSource(
sectionName: string,
sourceCount: number,
metadata: Podkop.SubscriptionMetadata,
metadata: Forkop.SubscriptionMetadata,
) {
const legacyMetadata = metadata as Podkop.SubscriptionMetadata & {
const legacyMetadata = metadata as Forkop.SubscriptionMetadata & {
source_index?: number;
source_section?: string;
};
@@ -1306,9 +1306,9 @@ function metadataMatchesCurrentSource(
function getSubscriptionMetadataSourceIndex(
sectionName: string,
sourceCount: number,
metadata: Podkop.SubscriptionMetadata,
metadata: Forkop.SubscriptionMetadata,
) {
const legacyMetadata = metadata as Podkop.SubscriptionMetadata & {
const legacyMetadata = metadata as Forkop.SubscriptionMetadata & {
source_index?: number;
source_section?: string;
};
@@ -1331,9 +1331,9 @@ function getSubscriptionMetadataSourceIndex(
}
function isSubscriptionMetadataVisible(
section: Podkop.ConfigSection,
section: Forkop.ConfigSection,
sourceCount: number,
metadata: Podkop.SubscriptionMetadata,
metadata: Forkop.SubscriptionMetadata,
) {
const sourceIndex = getSubscriptionMetadataSourceIndex(
section['.name'],
@@ -1354,7 +1354,7 @@ function isSubscriptionMetadataVisible(
}
function getSubscriptionMetadata(
section: Podkop.ConfigSection,
section: Forkop.ConfigSection,
sourceCount: number,
dashboardCache?: DashboardSectionCache,
) {
@@ -2,7 +2,7 @@ import { getConfigSections } from './getConfigSections';
import { getDashboardSections } from './getDashboardSections';
import { getClashApiSecret } from './getClashApiSecret';
export const CustomPodkopMethods = {
export const CustomForkopMethods = {
getConfigSections,
getDashboardSections,
getClashApiSecret,
@@ -12,7 +12,7 @@ vi.mock('../getConfigSections', () => ({
}));
vi.mock('../../shell', () => ({
PodkopShellMethods: {
ForkopShellMethods: {
getClashApiProxies: mocks.getClashApiProxies,
},
}));
@@ -26,7 +26,7 @@ vi.mock('../../../../helpers', () => ({
}));
import { getDashboardSections } from '../getDashboardSections';
import { ClashAPI, Podkop } from '../../../types';
import { ClashAPI, Forkop } from '../../../types';
function proxy(
type: string,
@@ -43,8 +43,8 @@ function proxy(
}
function proxySection(
options: Partial<Podkop.ConfigSection> = {},
): Podkop.ConfigSection {
options: Partial<Forkop.ConfigSection> = {},
): Forkop.ConfigSection {
return {
'.name': 'main',
'.type': 'section',
@@ -71,8 +71,8 @@ function urlTestSettings(
function priorityGroup(
id: string,
options: Partial<Podkop.ConfigSection> = {},
): Podkop.ConfigSection {
options: Partial<Forkop.ConfigSection> = {},
): Forkop.ConfigSection {
return {
'.name': id,
'.type': 'priority_group',
@@ -94,8 +94,8 @@ function priorityGroup(
function priorityLevel(
id: string,
options: Partial<Podkop.ConfigSection> = {},
): Podkop.ConfigSection {
options: Partial<Forkop.ConfigSection> = {},
): Forkop.ConfigSection {
return {
'.name': id,
'.type': 'priority_level',
@@ -1,5 +1,5 @@
import { executeShellCommand } from '../../../helpers';
import { Podkop } from '../../types';
import { Forkop } from '../../types';
interface CallBaseMethodOptions {
allowNonZeroWithStdout?: boolean;
@@ -7,11 +7,11 @@ interface CallBaseMethodOptions {
}
export async function callBaseMethod<T>(
method: Podkop.AvailableMethods,
method: Forkop.AvailableMethods,
args: string[] = [],
command: string = '/usr/bin/podkop-plus',
command: string = '/usr/bin/forkop',
options: CallBaseMethodOptions = {},
): Promise<Podkop.MethodResponse<T>> {
): Promise<Forkop.MethodResponse<T>> {
try {
const response = await executeShellCommand({
command,
@@ -1,5 +1,5 @@
import { callBaseMethod } from './callBaseMethod';
import { ClashAPI, Podkop } from '../../types';
import { ClashAPI, Forkop } from '../../types';
import { executeShellCommand } from '../../../helpers';
import { isTransientRpcError } from '../../helpers/isTransientRpcError';
@@ -16,7 +16,7 @@ const COMPONENT_ACTION_POLL_INTERVAL_MS = 1500;
const COMPONENT_ACTION_STATUS_REFRESH_INTERVAL_MS = 15000;
const COMPONENT_ACTION_SELF_UPDATE_SETTLE_MS = 30000;
const COMPONENT_ACTION_TRANSIENT_RPC_GRACE_MS = 30000;
const COMPONENT_ACTION_STATE_DIR = '/var/run/podkop-plus/component-actions';
const COMPONENT_ACTION_STATE_DIR = '/var/run/forkop/component-actions';
const GET_UI_STATE_RPC_TIMEOUT_MS = 3000;
function sleep(ms: number) {
@@ -50,7 +50,7 @@ function parseJsonObjectOutput<T>(output: string): T | null {
}
function parseComponentActionOutput(output: string) {
return parseJsonObjectOutput<Podkop.ComponentActionResult>(output);
return parseJsonObjectOutput<Forkop.ComponentActionResult>(output);
}
function parseComponentActionResult(
@@ -68,13 +68,13 @@ function parseComponentActionStartResult(
return null;
}
return parsedResponse as unknown as Podkop.ComponentActionStartResult;
return parsedResponse as unknown as Forkop.ComponentActionStartResult;
}
function parseSubscriptionUpdateStartResult(
response: Awaited<ReturnType<typeof executeShellCommand>>,
) {
return parseJsonObjectOutput<Podkop.SubscriptionUpdateStartResult>(
return parseJsonObjectOutput<Forkop.SubscriptionUpdateStartResult>(
response.stdout,
);
}
@@ -82,7 +82,7 @@ function parseSubscriptionUpdateStartResult(
function parseSubscriptionUpdateJobState(
response: Awaited<ReturnType<typeof executeShellCommand>>,
) {
return parseJsonObjectOutput<Podkop.SubscriptionUpdateJobState>(
return parseJsonObjectOutput<Forkop.SubscriptionUpdateJobState>(
response.stdout,
);
}
@@ -90,19 +90,19 @@ function parseSubscriptionUpdateJobState(
function parseUiActionStartResult(
response: Awaited<ReturnType<typeof executeShellCommand>>,
) {
return parseJsonObjectOutput<Podkop.UiActionStartResult>(response.stdout);
return parseJsonObjectOutput<Forkop.UiActionStartResult>(response.stdout);
}
function parseServiceActionState(
response: Awaited<ReturnType<typeof executeShellCommand>>,
) {
return parseJsonObjectOutput<Podkop.ServiceActionState>(response.stdout);
return parseJsonObjectOutput<Forkop.ServiceActionState>(response.stdout);
}
function parseLatencyActionState(
response: Awaited<ReturnType<typeof executeShellCommand>>,
) {
return parseJsonObjectOutput<Podkop.LatencyActionState>(response.stdout);
return parseJsonObjectOutput<Forkop.LatencyActionState>(response.stdout);
}
function isComponentActionJobId(jobId: string) {
@@ -123,9 +123,9 @@ async function readComponentActionState(jobId: string) {
}
}
async function readPodkopVersion() {
async function readForkopVersion() {
const response = await executeShellCommand({
command: '/usr/bin/podkop-plus',
command: '/usr/bin/forkop',
args: ['show_version'],
timeout: COMPONENT_ACTION_RPC_TIMEOUT_MS,
});
@@ -139,13 +139,13 @@ async function readPodkopVersion() {
async function isComponentActionStillRunning(
jobId: string,
component: Podkop.ComponentName,
action: Podkop.ComponentAction,
component: Forkop.ComponentName,
action: Forkop.ComponentAction,
) {
const response = await callBaseMethod<Podkop.UiState>(
Podkop.AvailableMethods.GET_UI_STATE,
const response = await callBaseMethod<Forkop.UiState>(
Forkop.AvailableMethods.GET_UI_STATE,
[],
'/usr/bin/podkop-plus',
'/usr/bin/forkop',
{ timeout: GET_UI_STATE_RPC_TIMEOUT_MS },
);
@@ -163,12 +163,12 @@ async function isComponentActionStillRunning(
function componentActionFailure(
response: Awaited<ReturnType<typeof executeShellCommand>>,
parsedResponse?: Pick<Podkop.ComponentActionResult, 'message'> | null,
parsedResponse?: Pick<Forkop.ComponentActionResult, 'message'> | null,
) {
return {
success: false,
error: parsedResponse?.message || response.stderr || _('Failed to execute'),
} as Podkop.MethodFailureResponse;
} as Forkop.MethodFailureResponse;
}
function uiActionFailure(
@@ -179,7 +179,7 @@ function uiActionFailure(
return {
success: false,
error: parsedResponse?.message || response.stderr || fallback,
} as Podkop.MethodFailureResponse;
} as Forkop.MethodFailureResponse;
}
function createTransientRpcGraceTracker(graceMs: number) {
@@ -204,170 +204,170 @@ function createTransientRpcGraceTracker(graceMs: number) {
};
}
export const PodkopShellMethods = {
export const ForkopShellMethods = {
checkDNSAvailable: async () =>
callBaseMethod<Podkop.DnsCheckResult>(
Podkop.AvailableMethods.CHECK_DNS_AVAILABLE,
callBaseMethod<Forkop.DnsCheckResult>(
Forkop.AvailableMethods.CHECK_DNS_AVAILABLE,
),
checkFakeIP: async () =>
callBaseMethod<Podkop.FakeIPCheckResult>(
Podkop.AvailableMethods.CHECK_FAKEIP,
callBaseMethod<Forkop.FakeIPCheckResult>(
Forkop.AvailableMethods.CHECK_FAKEIP,
),
checkNftRules: async () =>
callBaseMethod<Podkop.NftRulesCheckResult>(
Podkop.AvailableMethods.CHECK_NFT_RULES,
callBaseMethod<Forkop.NftRulesCheckResult>(
Forkop.AvailableMethods.CHECK_NFT_RULES,
),
checkZapretRuntime: async () =>
callBaseMethod<Podkop.ZapretCheckResult>(
Podkop.AvailableMethods.CHECK_ZAPRET_RUNTIME,
callBaseMethod<Forkop.ZapretCheckResult>(
Forkop.AvailableMethods.CHECK_ZAPRET_RUNTIME,
),
checkZapret2Runtime: async () =>
callBaseMethod<Podkop.Zapret2CheckResult>(
Podkop.AvailableMethods.CHECK_ZAPRET2_RUNTIME,
callBaseMethod<Forkop.Zapret2CheckResult>(
Forkop.AvailableMethods.CHECK_ZAPRET2_RUNTIME,
),
checkByedpiRuntime: async () =>
callBaseMethod<Podkop.ByedpiCheckResult>(
Podkop.AvailableMethods.CHECK_BYEDPI_RUNTIME,
callBaseMethod<Forkop.ByedpiCheckResult>(
Forkop.AvailableMethods.CHECK_BYEDPI_RUNTIME,
),
checkInboundsConfig: async () =>
callBaseMethod<Podkop.InboundsConfigCheckResult>(
Podkop.AvailableMethods.CHECK_INBOUNDS_CONFIG,
callBaseMethod<Forkop.InboundsConfigCheckResult>(
Forkop.AvailableMethods.CHECK_INBOUNDS_CONFIG,
),
getStatus: async () =>
callBaseMethod<Podkop.GetStatus>(Podkop.AvailableMethods.GET_STATUS),
callBaseMethod<Forkop.GetStatus>(Forkop.AvailableMethods.GET_STATUS),
getOutboundLink: async (section: string, tag: string) =>
callBaseMethod<Podkop.GetOutboundLink>(
Podkop.AvailableMethods.GET_OUTBOUND_LINK,
callBaseMethod<Forkop.GetOutboundLink>(
Forkop.AvailableMethods.GET_OUTBOUND_LINK,
[section, tag],
),
getOutboundLinkStates: async (section: string) =>
callBaseMethod<Podkop.GetOutboundLinkStates>(
Podkop.AvailableMethods.GET_OUTBOUND_LINK_STATES,
callBaseMethod<Forkop.GetOutboundLinkStates>(
Forkop.AvailableMethods.GET_OUTBOUND_LINK_STATES,
[section],
),
getOutboundMetadata: async (section: string) =>
callBaseMethod<Podkop.GetOutboundMetadata>(
Podkop.AvailableMethods.GET_OUTBOUND_METADATA,
callBaseMethod<Forkop.GetOutboundMetadata>(
Forkop.AvailableMethods.GET_OUTBOUND_METADATA,
[section],
),
getSubscriptionMetadata: async (section: string) =>
callBaseMethod<Podkop.SubscriptionMetadata | Podkop.SubscriptionMetadata[]>(
Podkop.AvailableMethods.GET_SUBSCRIPTION_METADATA,
callBaseMethod<Forkop.SubscriptionMetadata | Forkop.SubscriptionMetadata[]>(
Forkop.AvailableMethods.GET_SUBSCRIPTION_METADATA,
[section],
),
checkSingBox: async () =>
callBaseMethod<Podkop.SingBoxCheckResult>(
Podkop.AvailableMethods.CHECK_SING_BOX,
callBaseMethod<Forkop.SingBoxCheckResult>(
Forkop.AvailableMethods.CHECK_SING_BOX,
),
checkInbounds: async () =>
callBaseMethod<Podkop.InboundsCheckResult>(
Podkop.AvailableMethods.CHECK_INBOUNDS,
callBaseMethod<Forkop.InboundsCheckResult>(
Forkop.AvailableMethods.CHECK_INBOUNDS,
),
getSingBoxStatus: async () =>
callBaseMethod<Podkop.GetSingBoxStatus>(
Podkop.AvailableMethods.GET_SING_BOX_STATUS,
callBaseMethod<Forkop.GetSingBoxStatus>(
Forkop.AvailableMethods.GET_SING_BOX_STATUS,
),
getZapretStatus: async () =>
callBaseMethod<Podkop.GetZapretStatus>(
Podkop.AvailableMethods.GET_ZAPRET_STATUS,
callBaseMethod<Forkop.GetZapretStatus>(
Forkop.AvailableMethods.GET_ZAPRET_STATUS,
),
getZapret2Status: async () =>
callBaseMethod<Podkop.GetZapret2Status>(
Podkop.AvailableMethods.GET_ZAPRET2_STATUS,
callBaseMethod<Forkop.GetZapret2Status>(
Forkop.AvailableMethods.GET_ZAPRET2_STATUS,
),
getByedpiStatus: async () =>
callBaseMethod<Podkop.GetByedpiStatus>(
Podkop.AvailableMethods.GET_BYEDPI_STATUS,
callBaseMethod<Forkop.GetByedpiStatus>(
Forkop.AvailableMethods.GET_BYEDPI_STATUS,
),
getClashApiProxies: async () =>
callBaseMethod<ClashAPI.Proxies>(Podkop.AvailableMethods.CLASH_API, [
Podkop.AvailableClashAPIMethods.GET_PROXIES,
callBaseMethod<ClashAPI.Proxies>(Forkop.AvailableMethods.CLASH_API, [
Forkop.AvailableClashAPIMethods.GET_PROXIES,
]),
getClashApiConnections: async () =>
callBaseMethod<unknown>(Podkop.AvailableMethods.CLASH_API, [
Podkop.AvailableClashAPIMethods.GET_CONNECTIONS,
callBaseMethod<unknown>(Forkop.AvailableMethods.CLASH_API, [
Forkop.AvailableClashAPIMethods.GET_CONNECTIONS,
]),
getClashApiProxyLatency: async (tag: string, timeout = '5000') =>
callBaseMethod<Podkop.GetClashApiProxyLatency>(
Podkop.AvailableMethods.CLASH_API,
[Podkop.AvailableClashAPIMethods.GET_PROXY_LATENCY, tag, timeout],
callBaseMethod<Forkop.GetClashApiProxyLatency>(
Forkop.AvailableMethods.CLASH_API,
[Forkop.AvailableClashAPIMethods.GET_PROXY_LATENCY, tag, timeout],
),
getClashApiProxyLatencies: async (tags: string[]) =>
callBaseMethod<Podkop.GetClashApiProxyLatencies>(
Podkop.AvailableMethods.CLASH_API,
callBaseMethod<Forkop.GetClashApiProxyLatencies>(
Forkop.AvailableMethods.CLASH_API,
[
Podkop.AvailableClashAPIMethods.GET_PROXY_LATENCIES,
Forkop.AvailableClashAPIMethods.GET_PROXY_LATENCIES,
JSON.stringify(tags),
'5000',
],
),
getClashApiGroupLatency: async (tag: string) =>
callBaseMethod<Podkop.GetClashApiGroupLatency>(
Podkop.AvailableMethods.CLASH_API,
[Podkop.AvailableClashAPIMethods.GET_GROUP_LATENCY, tag, '10000'],
callBaseMethod<Forkop.GetClashApiGroupLatency>(
Forkop.AvailableMethods.CLASH_API,
[Forkop.AvailableClashAPIMethods.GET_GROUP_LATENCY, tag, '10000'],
),
setClashApiGroupProxy: async (group: string, proxy: string) =>
callBaseMethod<unknown>(Podkop.AvailableMethods.CLASH_API, [
Podkop.AvailableClashAPIMethods.SET_GROUP_PROXY,
callBaseMethod<unknown>(Forkop.AvailableMethods.CLASH_API, [
Forkop.AvailableClashAPIMethods.SET_GROUP_PROXY,
group,
proxy,
]),
closeClashApiConnection: async (connectionId: string) =>
callBaseMethod<unknown>(Podkop.AvailableMethods.CLASH_API, [
Podkop.AvailableClashAPIMethods.CLOSE_CONNECTION,
callBaseMethod<unknown>(Forkop.AvailableMethods.CLASH_API, [
Forkop.AvailableClashAPIMethods.CLOSE_CONNECTION,
connectionId,
]),
closeAllClashApiConnections: async () =>
callBaseMethod<unknown>(Podkop.AvailableMethods.CLASH_API, [
Podkop.AvailableClashAPIMethods.CLOSE_ALL_CONNECTIONS,
callBaseMethod<unknown>(Forkop.AvailableMethods.CLASH_API, [
Forkop.AvailableClashAPIMethods.CLOSE_ALL_CONNECTIONS,
]),
enable: async () =>
callBaseMethod<unknown>(
Podkop.AvailableMethods.ENABLE,
Forkop.AvailableMethods.ENABLE,
[],
'/etc/init.d/podkop-plus',
'/etc/init.d/forkop',
),
disable: async () =>
callBaseMethod<unknown>(
Podkop.AvailableMethods.DISABLE,
Forkop.AvailableMethods.DISABLE,
[],
'/etc/init.d/podkop-plus',
'/etc/init.d/forkop',
),
globalCheck: async (masked = true) =>
callBaseMethod<unknown>(Podkop.AvailableMethods.GLOBAL_CHECK, [
callBaseMethod<unknown>(Forkop.AvailableMethods.GLOBAL_CHECK, [
masked ? 'masked' : 'raw',
]),
showSingBoxConfig: async (masked = true) =>
callBaseMethod<unknown>(Podkop.AvailableMethods.SHOW_SING_BOX_CONFIG, [
callBaseMethod<unknown>(Forkop.AvailableMethods.SHOW_SING_BOX_CONFIG, [
masked ? 'masked' : 'raw',
]),
checkLogs: async () =>
callBaseMethod<unknown>(Podkop.AvailableMethods.CHECK_LOGS),
callBaseMethod<unknown>(Forkop.AvailableMethods.CHECK_LOGS),
checkSingBoxLogs: async () =>
callBaseMethod<unknown>(Podkop.AvailableMethods.CHECK_SING_BOX_LOGS),
callBaseMethod<unknown>(Forkop.AvailableMethods.CHECK_SING_BOX_LOGS),
getSystemInfo: async () =>
callBaseMethod<Podkop.GetSystemInfo>(
Podkop.AvailableMethods.GET_SYSTEM_INFO,
callBaseMethod<Forkop.GetSystemInfo>(
Forkop.AvailableMethods.GET_SYSTEM_INFO,
),
getServerCapabilities: async () =>
callBaseMethod<Podkop.GetServerCapabilities>(
Podkop.AvailableMethods.GET_SERVER_CAPABILITIES,
callBaseMethod<Forkop.GetServerCapabilities>(
Forkop.AvailableMethods.GET_SERVER_CAPABILITIES,
),
getUiCapabilities: async () =>
callBaseMethod<Podkop.GetUiCapabilities>(
Podkop.AvailableMethods.GET_UI_CAPABILITIES,
callBaseMethod<Forkop.GetUiCapabilities>(
Forkop.AvailableMethods.GET_UI_CAPABILITIES,
),
getUiState: async () =>
callBaseMethod<Podkop.UiState>(
Podkop.AvailableMethods.GET_UI_STATE,
callBaseMethod<Forkop.UiState>(
Forkop.AvailableMethods.GET_UI_STATE,
[],
'/usr/bin/podkop-plus',
'/usr/bin/forkop',
{ timeout: GET_UI_STATE_RPC_TIMEOUT_MS },
),
serviceActionStart: async (action: Podkop.ServiceAction) => {
serviceActionStart: async (action: Forkop.ServiceAction) => {
const response = await executeShellCommand({
command: '/usr/bin/podkop-plus',
args: [Podkop.AvailableMethods.SERVICE_ACTION_ASYNC, action],
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.SERVICE_ACTION_ASYNC, action],
timeout: UI_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseUiActionStartResult(response);
@@ -387,12 +387,12 @@ export const PodkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Podkop.MethodSuccessResponse<Podkop.UiActionStartResult>;
} as Forkop.MethodSuccessResponse<Forkop.UiActionStartResult>;
},
serviceActionStatus: async (jobId: string) => {
const response = await executeShellCommand({
command: '/usr/bin/podkop-plus',
args: [Podkop.AvailableMethods.SERVICE_ACTION_STATUS, jobId],
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.SERVICE_ACTION_STATUS, jobId],
timeout: UI_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseServiceActionState(response);
@@ -408,13 +408,13 @@ export const PodkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Podkop.MethodSuccessResponse<Podkop.ServiceActionState>;
} as Forkop.MethodSuccessResponse<Forkop.ServiceActionState>;
},
waitServiceActionJob: async (jobId: string, startedAt = Date.now()) => {
while (Date.now() - startedAt < SERVICE_ACTION_TIMEOUT_MS) {
await sleep(SERVICE_ACTION_POLL_INTERVAL_MS);
const response = await PodkopShellMethods.serviceActionStatus(jobId);
const response = await ForkopShellMethods.serviceActionStatus(jobId);
if (!response.success) {
return response;
@@ -430,18 +430,18 @@ export const PodkopShellMethods = {
return {
success: false,
error: _('Operation timed out'),
} as Podkop.MethodFailureResponse;
} as Forkop.MethodFailureResponse;
},
latencyTestStart: async (
latencyType: Podkop.LatencyActionState['latency_type'],
latencyType: Forkop.LatencyActionState['latency_type'],
section: string,
tag: string,
timeout?: string,
) => {
const response = await executeShellCommand({
command: '/usr/bin/podkop-plus',
command: '/usr/bin/forkop',
args: [
Podkop.AvailableMethods.LATENCY_TEST_ASYNC,
Forkop.AvailableMethods.LATENCY_TEST_ASYNC,
latencyType,
section,
tag,
@@ -466,12 +466,12 @@ export const PodkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Podkop.MethodSuccessResponse<Podkop.UiActionStartResult>;
} as Forkop.MethodSuccessResponse<Forkop.UiActionStartResult>;
},
latencyTestStatus: async (jobId: string) => {
const response = await executeShellCommand({
command: '/usr/bin/podkop-plus',
args: [Podkop.AvailableMethods.LATENCY_TEST_STATUS, jobId],
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.LATENCY_TEST_STATUS, jobId],
timeout: UI_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseLatencyActionState(response);
@@ -487,7 +487,7 @@ export const PodkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Podkop.MethodSuccessResponse<Podkop.LatencyActionState>;
} as Forkop.MethodSuccessResponse<Forkop.LatencyActionState>;
},
waitLatencyTestJob: async (jobId: string, startedAt = Date.now()) => {
const transientRpc = createTransientRpcGraceTracker(
@@ -497,7 +497,7 @@ export const PodkopShellMethods = {
while (Date.now() - startedAt < LATENCY_TEST_TIMEOUT_MS) {
await sleep(LATENCY_TEST_POLL_INTERVAL_MS);
const response = await PodkopShellMethods.latencyTestStatus(jobId);
const response = await ForkopShellMethods.latencyTestStatus(jobId);
if (!response.success) {
if (transientRpc.shouldContinue(response.error)) {
@@ -518,15 +518,15 @@ export const PodkopShellMethods = {
return {
success: false,
error: _('Operation timed out'),
} as Podkop.MethodFailureResponse;
} as Forkop.MethodFailureResponse;
},
uiActionAck: async (
kind: 'service' | 'latency' | 'component' | 'subscription',
jobId: string,
) => {
const response = await executeShellCommand({
command: '/usr/bin/podkop-plus',
args: [Podkop.AvailableMethods.UI_ACTION_ACK, kind, jobId],
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.UI_ACTION_ACK, kind, jobId],
timeout: UI_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseUiActionStartResult(response);
@@ -538,15 +538,15 @@ export const PodkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Podkop.MethodSuccessResponse<Podkop.UiActionStartResult>;
} as Forkop.MethodSuccessResponse<Forkop.UiActionStartResult>;
},
componentActionStart: async (
component: Podkop.ComponentName,
action: Podkop.ComponentAction,
component: Forkop.ComponentName,
action: Forkop.ComponentAction,
) => {
const response = await executeShellCommand({
command: '/usr/bin/podkop-plus',
args: [Podkop.AvailableMethods.COMPONENT_ACTION_ASYNC, component, action],
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.COMPONENT_ACTION_ASYNC, component, action],
timeout: COMPONENT_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseComponentActionStartResult(response);
@@ -562,12 +562,12 @@ export const PodkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Podkop.MethodSuccessResponse<Podkop.ComponentActionStartResult>;
} as Forkop.MethodSuccessResponse<Forkop.ComponentActionStartResult>;
},
componentActionStatus: async (jobId: string) => {
const response = await executeShellCommand({
command: '/usr/bin/podkop-plus',
args: [Podkop.AvailableMethods.COMPONENT_ACTION_STATUS, jobId],
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.COMPONENT_ACTION_STATUS, jobId],
timeout: COMPONENT_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseComponentActionResult(response);
@@ -579,16 +579,16 @@ export const PodkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Podkop.MethodSuccessResponse<Podkop.ComponentActionResult>;
} as Forkop.MethodSuccessResponse<Forkop.ComponentActionResult>;
},
componentUpdateCheckCache: async () =>
callBaseMethod<Podkop.ComponentUpdateCheckCache>(
Podkop.AvailableMethods.COMPONENT_UPDATE_CHECK_CACHE,
callBaseMethod<Forkop.ComponentUpdateCheckCache>(
Forkop.AvailableMethods.COMPONENT_UPDATE_CHECK_CACHE,
),
waitComponentActionJob: async (
jobId: string,
component: Podkop.ComponentName,
action: Podkop.ComponentAction,
component: Forkop.ComponentName,
action: Forkop.ComponentAction,
expectedLatestVersion?: string,
) => {
let selfUpdateVersionMatchedAt = 0;
@@ -608,7 +608,7 @@ export const PodkopShellMethods = {
return {
success: true,
data: stateResponse,
} as Podkop.MethodSuccessResponse<Podkop.ComponentActionResult>;
} as Forkop.MethodSuccessResponse<Forkop.ComponentActionResult>;
}
if (
@@ -621,8 +621,8 @@ export const PodkopShellMethods = {
lastStatusRefreshAt = Date.now();
const statusResponse = await executeShellCommand({
command: '/usr/bin/podkop-plus',
args: [Podkop.AvailableMethods.COMPONENT_ACTION_STATUS, jobId],
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.COMPONENT_ACTION_STATUS, jobId],
timeout: COMPONENT_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseComponentActionResult(statusResponse);
@@ -644,9 +644,9 @@ export const PodkopShellMethods = {
continue;
}
if (component === 'podkop' && action === 'install') {
if (component === 'forkop' && action === 'install') {
const installedVersion = expectedLatestVersion
? await readPodkopVersion()
? await readForkopVersion()
: '';
if (
@@ -667,13 +667,13 @@ export const PodkopShellMethods = {
success: true,
component,
action,
message: translate('Podkop Plus has been installed'),
message: translate('Forkop has been installed'),
current_version: installedVersion,
latest_version: expectedLatestVersion,
changed: true,
status: 'latest',
},
} as Podkop.MethodSuccessResponse<Podkop.ComponentActionResult>;
} as Forkop.MethodSuccessResponse<Forkop.ComponentActionResult>;
}
}
@@ -691,17 +691,17 @@ export const PodkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Podkop.MethodSuccessResponse<Podkop.ComponentActionResult>;
} as Forkop.MethodSuccessResponse<Forkop.ComponentActionResult>;
}
},
subscriptionUpdateStart: async (section?: string, sourceIndex?: number) => {
const startArgs = [
Podkop.AvailableMethods.SUBSCRIPTION_UPDATE_ASYNC,
Forkop.AvailableMethods.SUBSCRIPTION_UPDATE_ASYNC,
...(section ? [section] : []),
...(section && sourceIndex !== undefined ? [String(sourceIndex)] : []),
];
const response = await executeShellCommand({
command: '/usr/bin/podkop-plus',
command: '/usr/bin/forkop',
args: startArgs,
timeout: SUBSCRIPTION_UPDATE_RPC_TIMEOUT_MS,
});
@@ -718,18 +718,18 @@ export const PodkopShellMethods = {
parsedResponse?.message ||
response.stderr ||
_('Subscription update failed'),
} as Podkop.MethodFailureResponse;
} as Forkop.MethodFailureResponse;
}
return {
success: true,
data: parsedResponse,
} as Podkop.MethodSuccessResponse<Podkop.SubscriptionUpdateStartResult>;
} as Forkop.MethodSuccessResponse<Forkop.SubscriptionUpdateStartResult>;
},
subscriptionUpdateStatus: async (jobId: string) => {
const response = await executeShellCommand({
command: '/usr/bin/podkop-plus',
args: [Podkop.AvailableMethods.SUBSCRIPTION_UPDATE_STATUS, jobId],
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.SUBSCRIPTION_UPDATE_STATUS, jobId],
timeout: SUBSCRIPTION_UPDATE_RPC_TIMEOUT_MS,
});
const parsedResponse = parseSubscriptionUpdateJobState(response);
@@ -738,13 +738,13 @@ export const PodkopShellMethods = {
return {
success: false,
error: response.stderr || _('Subscription update failed'),
} as Podkop.MethodFailureResponse;
} as Forkop.MethodFailureResponse;
}
return {
success: true,
data: parsedResponse,
} as Podkop.MethodSuccessResponse<Podkop.SubscriptionUpdateJobState>;
} as Forkop.MethodSuccessResponse<Forkop.SubscriptionUpdateJobState>;
},
waitSubscriptionUpdateJob: async (jobId: string) => {
const transientRpc = createTransientRpcGraceTracker(
@@ -754,7 +754,7 @@ export const PodkopShellMethods = {
while (true) {
await sleep(SUBSCRIPTION_UPDATE_POLL_INTERVAL_MS);
const response = await PodkopShellMethods.subscriptionUpdateStatus(jobId);
const response = await ForkopShellMethods.subscriptionUpdateStatus(jobId);
if (!response.success) {
if (transientRpc.shouldContinue(response.error)) {
@@ -8,7 +8,7 @@ vi.mock('../../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { Podkop } from '../../../types';
import { Forkop } from '../../../types';
import { callBaseMethod } from '../callBaseMethod';
describe('callBaseMethod', () => {
@@ -24,7 +24,7 @@ describe('callBaseMethod', () => {
});
const response = await callBaseMethod(
Podkop.AvailableMethods.CHECK_DNS_AVAILABLE,
Forkop.AvailableMethods.CHECK_DNS_AVAILABLE,
);
expect(response).toEqual({
@@ -41,9 +41,9 @@ describe('callBaseMethod', () => {
});
const response = await callBaseMethod(
Podkop.AvailableMethods.GLOBAL_CHECK,
Forkop.AvailableMethods.GLOBAL_CHECK,
[],
'/usr/bin/podkop-plus',
'/usr/bin/forkop',
{ allowNonZeroWithStdout: true },
);
@@ -61,14 +61,14 @@ describe('callBaseMethod', () => {
});
await callBaseMethod(
Podkop.AvailableMethods.GLOBAL_CHECK,
Forkop.AvailableMethods.GLOBAL_CHECK,
[],
'/usr/bin/podkop-plus',
'/usr/bin/forkop',
{ timeout: 60000 },
);
expect(mocks.executeShellCommand).toHaveBeenCalledWith({
command: '/usr/bin/podkop-plus',
command: '/usr/bin/forkop',
args: ['global_check'],
timeout: 60000,
});
@@ -9,9 +9,9 @@ vi.mock('../../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { PodkopShellMethods } from '../index';
import { ForkopShellMethods } from '../index';
describe('PodkopShellMethods.componentAction', () => {
describe('ForkopShellMethods.componentAction', () => {
beforeEach(() => {
vi.useFakeTimers();
mocks.executeShellCommand.mockReset();
@@ -26,7 +26,7 @@ describe('PodkopShellMethods.componentAction', () => {
vi.unstubAllGlobals();
});
it('does not fail Podkop Plus self-update when status polling disappears after package replacement', async () => {
it('does not fail Forkop self-update when status polling disappears after package replacement', async () => {
mocks.fsRead.mockRejectedValue(new Error('Access denied'));
mocks.executeShellCommand.mockImplementation(({ args }) => {
if (args[0] === 'component_action_status') {
@@ -52,9 +52,9 @@ describe('PodkopShellMethods.componentAction', () => {
});
});
const responsePromise = PodkopShellMethods.waitComponentActionJob(
const responsePromise = ForkopShellMethods.waitComponentActionJob(
'job-1',
'podkop',
'forkop',
'install',
'0.7.17.11',
);
@@ -65,9 +65,9 @@ describe('PodkopShellMethods.componentAction', () => {
success: true,
data: {
success: true,
component: 'podkop',
component: 'forkop',
action: 'install',
message: 'Podkop Plus has been installed',
message: 'Forkop has been installed',
current_version: '0.7.17.11',
latest_version: '0.7.17.11',
changed: true,
@@ -87,7 +87,7 @@ describe('PodkopShellMethods.componentAction', () => {
});
await expect(
PodkopShellMethods.componentActionStart('zapret', 'install'),
ForkopShellMethods.componentActionStart('zapret', 'install'),
).resolves.toEqual({
success: false,
error: 'Another component action is already running',
@@ -147,7 +147,7 @@ describe('PodkopShellMethods.componentAction', () => {
});
});
const responsePromise = PodkopShellMethods.waitComponentActionJob(
const responsePromise = ForkopShellMethods.waitComponentActionJob(
'job-1',
'sing_box',
'check_update',
@@ -201,7 +201,7 @@ describe('PodkopShellMethods.componentAction', () => {
return Promise.resolve({
stdout: JSON.stringify({
service: {
podkop: {
forkop: {
running: 0,
enabled: 1,
status: 'stopped but enabled',
@@ -251,7 +251,7 @@ describe('PodkopShellMethods.componentAction', () => {
});
});
const responsePromise = PodkopShellMethods.waitComponentActionJob(
const responsePromise = ForkopShellMethods.waitComponentActionJob(
'job-1',
'sing_box',
'install_extended',
@@ -274,7 +274,7 @@ describe('PodkopShellMethods.componentAction', () => {
},
});
expect(mocks.executeShellCommand).toHaveBeenCalledWith({
command: '/usr/bin/podkop-plus',
command: '/usr/bin/forkop',
args: ['get_ui_state'],
timeout: 3000,
});
@@ -321,7 +321,7 @@ describe('PodkopShellMethods.componentAction', () => {
});
});
const responsePromise = PodkopShellMethods.waitComponentActionJob(
const responsePromise = ForkopShellMethods.waitComponentActionJob(
'job-1',
'zapret',
'check_update',
@@ -8,9 +8,9 @@ vi.mock('../../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { PodkopShellMethods } from '../index';
import { ForkopShellMethods } from '../index';
describe('PodkopShellMethods.latencyAction', () => {
describe('ForkopShellMethods.latencyAction', () => {
beforeEach(() => {
vi.useFakeTimers();
mocks.executeShellCommand.mockReset();
@@ -52,7 +52,7 @@ describe('PodkopShellMethods.latencyAction', () => {
});
});
const responsePromise = PodkopShellMethods.waitLatencyTestJob('job-1');
const responsePromise = ForkopShellMethods.waitLatencyTestJob('job-1');
await vi.advanceTimersByTimeAsync(2000);
@@ -76,7 +76,7 @@ describe('PodkopShellMethods.latencyAction', () => {
code: 0,
});
await PodkopShellMethods.latencyTestStart(
await ForkopShellMethods.latencyTestStart(
'proxy',
'AWG',
'AWG-out',
@@ -84,7 +84,7 @@ describe('PodkopShellMethods.latencyAction', () => {
);
expect(mocks.executeShellCommand).toHaveBeenCalledWith({
command: '/usr/bin/podkop-plus',
command: '/usr/bin/forkop',
args: ['latency_test_async', 'proxy', 'AWG', 'AWG-out', '10000'],
timeout: 15000,
});
@@ -8,9 +8,9 @@ vi.mock('../../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { PodkopShellMethods } from '../index';
import { ForkopShellMethods } from '../index';
describe('PodkopShellMethods.serviceAction', () => {
describe('ForkopShellMethods.serviceAction', () => {
beforeEach(() => {
vi.useFakeTimers();
mocks.executeShellCommand.mockReset();
@@ -44,7 +44,7 @@ describe('PodkopShellMethods.serviceAction', () => {
});
});
const responsePromise = PodkopShellMethods.waitServiceActionJob('job-1');
const responsePromise = ForkopShellMethods.waitServiceActionJob('job-1');
await vi.advanceTimersByTimeAsync(1000);
@@ -72,7 +72,7 @@ describe('PodkopShellMethods.serviceAction', () => {
});
await expect(
PodkopShellMethods.serviceActionStart('restart'),
ForkopShellMethods.serviceActionStart('restart'),
).resolves.toEqual({
success: false,
error: 'Another service action is already running',
@@ -8,9 +8,9 @@ vi.mock('../../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { PodkopShellMethods } from '../index';
import { ForkopShellMethods } from '../index';
describe('PodkopShellMethods.subscriptionUpdate', () => {
describe('ForkopShellMethods.subscriptionUpdate', () => {
beforeEach(() => {
vi.useFakeTimers();
mocks.executeShellCommand.mockReset();
@@ -55,7 +55,7 @@ describe('PodkopShellMethods.subscriptionUpdate', () => {
});
await expect(
PodkopShellMethods.subscriptionUpdateStart('main'),
ForkopShellMethods.subscriptionUpdateStart('main'),
).resolves.toEqual({
success: true,
data: {
@@ -66,7 +66,7 @@ describe('PodkopShellMethods.subscriptionUpdate', () => {
});
const responsePromise =
PodkopShellMethods.waitSubscriptionUpdateJob('job-1');
ForkopShellMethods.waitSubscriptionUpdateJob('job-1');
await vi.advanceTimersByTimeAsync(1500);
@@ -80,12 +80,12 @@ describe('PodkopShellMethods.subscriptionUpdate', () => {
},
});
expect(mocks.executeShellCommand).toHaveBeenNthCalledWith(1, {
command: '/usr/bin/podkop-plus',
command: '/usr/bin/forkop',
args: ['subscription_update_async', 'main'],
timeout: 15000,
});
expect(mocks.executeShellCommand).toHaveBeenNthCalledWith(2, {
command: '/usr/bin/podkop-plus',
command: '/usr/bin/forkop',
args: ['subscription_update_status', 'job-1'],
timeout: 15000,
});
@@ -102,7 +102,7 @@ describe('PodkopShellMethods.subscriptionUpdate', () => {
});
await expect(
PodkopShellMethods.subscriptionUpdateStart('main'),
ForkopShellMethods.subscriptionUpdateStart('main'),
).resolves.toEqual({
success: false,
error: 'Subscription update is already running',
@@ -134,7 +134,7 @@ describe('PodkopShellMethods.subscriptionUpdate', () => {
});
const responsePromise =
PodkopShellMethods.waitSubscriptionUpdateJob('job-1');
ForkopShellMethods.waitSubscriptionUpdateJob('job-1');
await vi.advanceTimersByTimeAsync(1500);
@@ -189,7 +189,7 @@ describe('PodkopShellMethods.subscriptionUpdate', () => {
});
const responsePromise =
PodkopShellMethods.waitSubscriptionUpdateJob('job-1');
ForkopShellMethods.waitSubscriptionUpdateJob('job-1');
await vi.advanceTimersByTimeAsync(10 * 60 * 1000 + 3000);
@@ -239,7 +239,7 @@ describe('PodkopShellMethods.subscriptionUpdate', () => {
});
const responsePromise =
PodkopShellMethods.waitSubscriptionUpdateJob('job-1');
ForkopShellMethods.waitSubscriptionUpdateJob('job-1');
await vi.advanceTimersByTimeAsync(3000);
@@ -1,13 +1,13 @@
import { TabServiceInstance } from './tab.service';
import { store } from './store.service';
import { logger } from './logger.service';
import { PodkopLogWatcher } from './podkopLogWatcher.service';
import { ForkopLogWatcher } from './forkopLogWatcher.service';
import {
getPodkopLogNotification,
getForkopLogNotification,
LogNotificationDeduper,
PodkopLogNotification,
ForkopLogNotification,
} from './logNotificationDeduper.service';
import { PodkopShellMethods } from '../methods';
import { ForkopShellMethods } from '../methods';
import {
registerRuntimeStateResumeRefresh,
startRuntimeUiStatePolling,
@@ -23,7 +23,7 @@ const LOG_WATCHER_START_DELAY_MS = 5000;
function componentDisplayName(component: string) {
const names: Record<string, string> = {
podkop: 'Podkop Plus',
forkop: 'Forkop',
sing_box: 'sing-box',
zapret: 'Zapret',
zapret2: 'Zapret2',
@@ -33,7 +33,7 @@ function componentDisplayName(component: string) {
return names[component] || component;
}
function showLogNotification(notification: PodkopLogNotification) {
function showLogNotification(notification: ForkopLogNotification) {
if (notification.kind === 'component-update') {
const message = _('New version %s is available for %s')
.replace('%s', notification.version)
@@ -43,16 +43,16 @@ function showLogNotification(notification: PodkopLogNotification) {
_('Component update available'),
E('div', {}, message),
'warning',
'pdk-component-update-notification',
'fkp-component-update-notification',
);
return;
}
ui.addNotification(
_('Podkop Plus Error'),
_('Forkop Error'),
E('div', {}, notification.line),
'error',
'pdk-log-error-notification',
'fkp-log-error-notification',
);
}
@@ -67,12 +67,12 @@ export function coreService(options: CoreServiceOptions = {}) {
});
});
const watcher = PodkopLogWatcher.getInstance();
const watcher = ForkopLogWatcher.getInstance();
const logNotificationDeduper = new LogNotificationDeduper();
watcher.init(
async () => {
const logs = await PodkopShellMethods.checkLogs();
const logs = await ForkopShellMethods.checkLogs();
if (logs.success) {
return logs.data as string;
@@ -84,7 +84,7 @@ export function coreService(options: CoreServiceOptions = {}) {
intervalMs: LOG_WATCHER_INTERVAL_MS,
onNewLog: (line) => {
if (logNotificationDeduper.shouldNotify(line)) {
const notification = getPodkopLogNotification(line);
const notification = getForkopLogNotification(line);
if (notification) {
showLogNotification(notification);
}
@@ -2,14 +2,14 @@ import { logger } from './logger.service';
type LogFetcher = () => Promise<string> | string;
interface PodkopLogWatcherOptions {
interface ForkopLogWatcherOptions {
intervalMs?: number;
onNewLog?: (line: string) => void;
maxTrackedLines?: number;
}
export class PodkopLogWatcher {
private static instance: PodkopLogWatcher;
export class ForkopLogWatcher {
private static instance: ForkopLogWatcher;
private fetcher?: LogFetcher;
private onNewLog?: (line: string) => void;
private intervalMs = 5000;
@@ -29,21 +29,21 @@ export class PodkopLogWatcher {
}
}
static getInstance(): PodkopLogWatcher {
if (!PodkopLogWatcher.instance) {
PodkopLogWatcher.instance = new PodkopLogWatcher();
static getInstance(): ForkopLogWatcher {
if (!ForkopLogWatcher.instance) {
ForkopLogWatcher.instance = new ForkopLogWatcher();
}
return PodkopLogWatcher.instance;
return ForkopLogWatcher.instance;
}
init(fetcher: LogFetcher, options?: PodkopLogWatcherOptions): void {
init(fetcher: LogFetcher, options?: ForkopLogWatcherOptions): void {
this.fetcher = fetcher;
this.onNewLog = options?.onNewLog;
this.intervalMs = options?.intervalMs ?? 5000;
this.maxTrackedLines = options?.maxTrackedLines ?? 500;
this.lastLines = new Set();
logger.info(
'[PodkopLogWatcher]',
'[ForkopLogWatcher]',
`initialized (interval: ${this.intervalMs}ms)`,
);
}
@@ -54,18 +54,18 @@ export class PodkopLogWatcher {
async checkOnce(): Promise<void> {
if (!this.fetcher) {
logger.warn('[PodkopLogWatcher]', 'fetcher not found');
logger.warn('[ForkopLogWatcher]', 'fetcher not found');
return;
}
if (this.paused) {
logger.debug('[PodkopLogWatcher]', 'skipped check — tab not visible');
logger.debug('[ForkopLogWatcher]', 'skipped check — tab not visible');
return;
}
if (this.checking) {
logger.debug(
'[PodkopLogWatcher]',
'[ForkopLogWatcher]',
'skipped check — previous check is running',
);
return;
@@ -92,7 +92,7 @@ export class PodkopLogWatcher {
);
}
} catch (err) {
logger.error('[PodkopLogWatcher]', 'failed to read logs:', err);
logger.error('[ForkopLogWatcher]', 'failed to read logs:', err);
} finally {
this.checking = false;
}
@@ -101,7 +101,7 @@ export class PodkopLogWatcher {
start(): void {
if (this.running) return;
if (!this.fetcher) {
logger.warn('[PodkopLogWatcher]', 'attempted to start without fetcher');
logger.warn('[ForkopLogWatcher]', 'attempted to start without fetcher');
return;
}
@@ -109,7 +109,7 @@ export class PodkopLogWatcher {
void this.checkOnce();
this.timer = setInterval(() => this.checkOnce(), this.intervalMs);
logger.info(
'[PodkopLogWatcher]',
'[ForkopLogWatcher]',
`started (interval: ${this.intervalMs}ms)`,
);
}
@@ -118,25 +118,25 @@ export class PodkopLogWatcher {
if (!this.running) return;
this.running = false;
if (this.timer) clearInterval(this.timer);
logger.info('[PodkopLogWatcher]', 'stopped');
logger.info('[ForkopLogWatcher]', 'stopped');
}
pause(): void {
if (!this.running || this.paused) return;
this.paused = true;
logger.info('[PodkopLogWatcher]', 'paused (tab not visible)');
logger.info('[ForkopLogWatcher]', 'paused (tab not visible)');
}
resume(): void {
if (!this.running || !this.paused) return;
this.paused = false;
logger.info('[PodkopLogWatcher]', 'resumed (tab active)');
logger.info('[ForkopLogWatcher]', 'resumed (tab active)');
void this.checkOnce();
}
reset(): void {
this.lastLines = new Set();
this.checking = false;
logger.info('[PodkopLogWatcher]', 'log history reset');
logger.info('[ForkopLogWatcher]', 'log history reset');
}
}
@@ -1,8 +1,7 @@
const LOG_NOTIFICATION_STORAGE_KEY =
'podkop-plus:shown-log-error-notifications:v1';
const LOG_NOTIFICATION_STORAGE_KEY = 'forkop:shown-log-error-notifications:v1';
const MAX_STORED_LOG_NOTIFICATIONS = 500;
export type PodkopLogNotification =
export type ForkopLogNotification =
| { kind: 'error'; line: string }
| {
kind: 'component-update';
@@ -61,15 +60,15 @@ export function isErrorLogLine(line: string) {
return lower.includes('[error]') || lower.includes('[fatal]');
}
export function getPodkopLogNotification(
export function getForkopLogNotification(
line: string,
): PodkopLogNotification | null {
): ForkopLogNotification | null {
if (isErrorLogLine(line)) {
return { kind: 'error', line };
}
const update = line.match(
/\[component-update\]\s+(podkop|sing_box|zapret|zapret2|byedpi)\s+(\S+)/i,
/\[component-update\]\s+(forkop|sing_box|zapret|zapret2|byedpi)\s+(\S+)/i,
);
if (!update) {
@@ -98,7 +97,7 @@ export class LogNotificationDeduper {
}
shouldNotify(line: string) {
if (!getPodkopLogNotification(line)) {
if (!getForkopLogNotification(line)) {
return false;
}
@@ -1,17 +1,17 @@
import { PodkopShellMethods } from '../methods';
import { Podkop } from '../types';
import { ForkopShellMethods } from '../methods';
import { Forkop } from '../types';
import { logger } from './logger.service';
import { applyUiStateToStore } from './uiState.service';
const RUNTIME_UI_STATE_REFRESH_MIN_INTERVAL_MS = 500;
const RUNTIME_UI_STATE_IDLE_POLL_INTERVAL_MS = 1000;
const RUNTIME_UI_STATE_ACTIVE_POLL_INTERVAL_MS = 500;
type RuntimeUiStateListener = (uiState: Podkop.UiState) => void;
type RuntimeUiStateListener = (uiState: Forkop.UiState) => void;
let runtimeUiStateRefreshPromise: Promise<Podkop.UiState | undefined> | null =
let runtimeUiStateRefreshPromise: Promise<Forkop.UiState | undefined> | null =
null;
let lastRuntimeUiStateRefreshAt = 0;
let lastRuntimeUiState: Podkop.UiState | undefined;
let lastRuntimeUiState: Forkop.UiState | undefined;
let runtimeStateResumeRefreshRegistered = false;
let runtimeStatePollTimer: ReturnType<typeof setTimeout> | null = null;
let runtimeStatePollingStarted = false;
@@ -26,7 +26,7 @@ function isDocumentVisible() {
);
}
function hasRunningAction(uiState: Podkop.UiState) {
function hasRunningAction(uiState: Forkop.UiState) {
return Object.values(uiState.actions).some((actions) =>
actions.some((action) => action.running),
);
@@ -57,7 +57,7 @@ function scheduleRuntimeUiStatePoll(delay = getNextPollDelay()) {
}, delay);
}
function notifyRuntimeUiStateListeners(uiState: Podkop.UiState) {
function notifyRuntimeUiStateListeners(uiState: Forkop.UiState) {
for (const listener of runtimeUiStateListeners) {
try {
listener(uiState);
@@ -69,7 +69,7 @@ function notifyRuntimeUiStateListeners(uiState: Podkop.UiState) {
export async function refreshRuntimeUiState({
force = false,
}: { force?: boolean } = {}): Promise<Podkop.UiState | undefined> {
}: { force?: boolean } = {}): Promise<Forkop.UiState | undefined> {
if (!isDocumentVisible()) {
return undefined;
}
@@ -88,7 +88,7 @@ export async function refreshRuntimeUiState({
lastRuntimeUiStateRefreshAt = now;
const promise = PodkopShellMethods.getUiState()
const promise = ForkopShellMethods.getUiState()
.then((response) => {
if (!response.success) {
return undefined;
@@ -1,4 +1,4 @@
import { Podkop } from '../types';
import { Forkop } from '../types';
import { initialDiagnosticStore } from '../tabs/diagnostic/diagnostic.store';
function jsonStableStringify<T, V>(obj: T): string {
@@ -158,17 +158,17 @@ export interface StoreType {
failed: boolean;
data: {
singbox: number;
podkopRunning: number;
podkopEnabled: number;
podkopStatus: string;
forkopRunning: number;
forkopEnabled: number;
forkopStatus: string;
};
};
sectionsWidget: {
loading: boolean;
failed: boolean;
data: Podkop.OutboundGroup[];
data: Forkop.OutboundGroup[];
latencyFetchingSections: Record<string, boolean>;
latencyProgressSections: Record<string, Podkop.LatencyActionProgress>;
latencyProgressSections: Record<string, Forkop.LatencyActionProgress>;
selectorSwitchingSections: Record<string, string>;
subscriptionUpdatingSections: Record<string, boolean>;
};
@@ -190,8 +190,8 @@ export interface StoreType {
loading: boolean;
loaded: boolean;
providerInfoLoaded: boolean;
podkop_version: string;
podkop_latest_version: string;
forkop_version: string;
forkop_latest_version: string;
luci_app_version: string;
sing_box_version: string;
sing_box_extended: number;
@@ -209,8 +209,8 @@ export interface StoreType {
device_model: string;
};
updatesActions: {
podkopCheck: { loading: boolean };
podkopInstall: { loading: boolean };
forkopCheck: { loading: boolean };
forkopInstall: { loading: boolean };
singBoxCheck: { loading: boolean };
singBoxInstall: { loading: boolean };
singBoxInstallExtended: { loading: boolean };
@@ -228,7 +228,7 @@ export interface StoreType {
byedpiRemove: { loading: boolean };
};
updatesChecks: Record<
Podkop.ComponentName,
Forkop.ComponentName,
{
status: 'latest' | 'outdated' | 'dev' | null;
latest_version: string;
@@ -262,9 +262,9 @@ const initialStore: StoreType = {
failed: false,
data: {
singbox: 0,
podkopRunning: 0,
podkopEnabled: 0,
podkopStatus: '',
forkopRunning: 0,
forkopEnabled: 0,
forkopStatus: '',
},
},
sectionsWidget: {
@@ -1,4 +1,4 @@
import { PodkopShellMethods } from '../methods';
import { ForkopShellMethods } from '../methods';
import { normalizeSingBoxVariantFields } from '../helpers/singBoxVariant';
import { logger } from './logger.service';
import { store, StoreType } from './store.service';
@@ -7,8 +7,8 @@ const UNKNOWN_SYSTEM_INFO: StoreType['diagnosticsSystemInfo'] = {
loading: false,
loaded: false,
providerInfoLoaded: false,
podkop_version: _('unknown'),
podkop_latest_version: _('unknown'),
forkop_version: _('unknown'),
forkop_latest_version: _('unknown'),
luci_app_version: _('unknown'),
sing_box_version: _('unknown'),
sing_box_extended: 0,
@@ -65,22 +65,23 @@ export async function ensureSystemInfo({
const promise = (async () => {
try {
const systemInfo = await PodkopShellMethods.getSystemInfo();
const systemInfo = await ForkopShellMethods.getSystemInfo();
if (requestId !== latestSystemInfoRequestId) {
return store.get().diagnosticsSystemInfo;
}
if (systemInfo.success) {
const nextSystemInfo: StoreType['diagnosticsSystemInfo'] = normalizeSingBoxVariantFields({
...UNKNOWN_SYSTEM_INFO,
loading: false,
loaded: true,
providerInfoLoaded: true,
server_inbounds_enabled_count:
currentSystemInfo.server_inbounds_enabled_count,
...systemInfo.data,
});
const nextSystemInfo: StoreType['diagnosticsSystemInfo'] =
normalizeSingBoxVariantFields({
...UNKNOWN_SYSTEM_INFO,
loading: false,
loaded: true,
providerInfoLoaded: true,
server_inbounds_enabled_count:
currentSystemInfo.server_inbounds_enabled_count,
...systemInfo.data,
});
store.set({
diagnosticsSystemInfo: nextSystemInfo,
@@ -1,9 +1,9 @@
import { beforeEach, describe, expect, it } from 'vitest';
import { PodkopLogWatcher } from '../podkopLogWatcher.service';
import { ForkopLogWatcher } from '../forkopLogWatcher.service';
describe('PodkopLogWatcher', () => {
const watcher = PodkopLogWatcher.getInstance();
describe('ForkopLogWatcher', () => {
const watcher = ForkopLogWatcher.getInstance();
let rawLogs = '';
let seenLines: string[] = [];
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
import {
getLogNotificationKey,
getPodkopLogNotification,
getForkopLogNotification,
isErrorLogLine,
LogNotificationDeduper,
} from '../logNotificationDeduper.service';
@@ -37,46 +37,44 @@ class MemoryStorage implements Storage {
describe('LogNotificationDeduper', () => {
it('accepts error, fatal, and component update log lines', () => {
expect(isErrorLogLine('podkop-plus: [info] ok')).toBe(false);
expect(isErrorLogLine('podkop-plus: [error] failed')).toBe(true);
expect(isErrorLogLine('podkop-plus: [fatal] failed')).toBe(true);
expect(isErrorLogLine('forkop: [info] ok')).toBe(false);
expect(isErrorLogLine('forkop: [error] failed')).toBe(true);
expect(isErrorLogLine('forkop: [fatal] failed')).toBe(true);
expect(
getPodkopLogNotification(
'podkop-plus: [info] [component-update] zapret2 v1.2.3',
getForkopLogNotification(
'forkop: [info] [component-update] zapret2 v1.2.3',
),
).toEqual({
kind: 'component-update',
line: 'podkop-plus: [info] [component-update] zapret2 v1.2.3',
line: 'forkop: [info] [component-update] zapret2 v1.2.3',
component: 'zapret2',
version: 'v1.2.3',
});
expect(getPodkopLogNotification('podkop-plus: [info] ok')).toBeNull();
expect(getForkopLogNotification('forkop: [info] ok')).toBeNull();
});
it('dedupes already shown log lines through session storage', () => {
const storage = new MemoryStorage();
const first = new LogNotificationDeduper(storage);
expect(first.shouldNotify('podkop-plus: [error] failed')).toBe(true);
expect(first.shouldNotify('podkop-plus: [error] failed')).toBe(false);
expect(first.shouldNotify('podkop-plus: [error] another failure')).toBe(
true,
);
expect(first.shouldNotify('forkop: [error] failed')).toBe(true);
expect(first.shouldNotify('forkop: [error] failed')).toBe(false);
expect(first.shouldNotify('forkop: [error] another failure')).toBe(true);
expect(
first.shouldNotify('podkop-plus: [info] [component-update] podkop 1.2.3'),
first.shouldNotify('forkop: [info] [component-update] forkop 1.2.3'),
).toBe(true);
const afterReload = new LogNotificationDeduper(storage);
expect(afterReload.shouldNotify('podkop-plus: [error] failed')).toBe(false);
expect(afterReload.shouldNotify('podkop-plus: [fatal] fatal failure')).toBe(
expect(afterReload.shouldNotify('forkop: [error] failed')).toBe(false);
expect(afterReload.shouldNotify('forkop: [fatal] fatal failure')).toBe(
true,
);
});
it('keeps the full log line as the replay key', () => {
expect(
getLogNotificationKey(' Jun 06 podkop-plus: [error] failed '),
).toBe('Jun 06 podkop-plus: [error] failed');
expect(getLogNotificationKey(' Jun 06 forkop: [error] failed ')).toBe(
'Jun 06 forkop: [error] failed',
);
});
});
@@ -8,7 +8,7 @@ vi.mock('../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { Podkop } from '../../types';
import { Forkop } from '../../types';
import { store } from '../store.service';
import {
getCachedRuntimeUiState,
@@ -19,10 +19,10 @@ import {
function createUiState(
status = 'running & enabled',
running = 1,
): Podkop.UiState {
): Forkop.UiState {
return {
service: {
podkop: {
forkop: {
running,
enabled: 1,
status,
@@ -73,14 +73,14 @@ describe('refreshRuntimeUiState', () => {
);
expect(mocks.executeShellCommand).toHaveBeenCalledWith({
command: '/usr/bin/podkop-plus',
command: '/usr/bin/forkop',
args: ['get_ui_state'],
timeout: 3000,
});
expect(store.get().servicesInfoWidget.data).toMatchObject({
podkopRunning: 0,
podkopEnabled: 1,
podkopStatus: 'stopped but enabled',
forkopRunning: 0,
forkopEnabled: 1,
forkopStatus: 'stopped but enabled',
});
});
@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it } from 'vitest';
import { applyUiStateToStore } from '../uiState.service';
import { store } from '../store.service';
import { Podkop } from '../../types';
import { Forkop } from '../../types';
import {
clearLocalActionOverlay,
setLocalComponentAction,
@@ -11,12 +11,12 @@ import {
} from '../localActionOverlay.service';
function createUiState(
actions: Partial<Podkop.UiState['actions']> = {},
capabilities: Partial<Podkop.UiState['capabilities']> = {},
): Podkop.UiState {
actions: Partial<Forkop.UiState['actions']> = {},
capabilities: Partial<Forkop.UiState['capabilities']> = {},
): Forkop.UiState {
return {
service: {
podkop: {
forkop: {
running: 0,
enabled: 1,
status: 'starting',
@@ -114,9 +114,9 @@ describe('applyUiStateToStore', () => {
loading: false,
failed: false,
data: {
podkopEnabled: 1,
podkopRunning: 0,
podkopStatus: 'starting',
forkopEnabled: 1,
forkopRunning: 0,
forkopStatus: 'starting',
},
});
expect(state.diagnosticsSystemInfo).toMatchObject({
@@ -1,5 +1,5 @@
const UI_ACTION_NOTIFICATION_STORAGE_KEY =
'podkop-plus:owned-ui-action-notifications:v1';
'forkop:owned-ui-action-notifications:v1';
const MAX_STORED_UI_ACTION_NOTIFICATIONS = 100;
export type UiActionNotificationKind = 'component' | 'subscription';
@@ -1,11 +1,11 @@
import { getComponentActionKey } from '../helpers/getComponentActionKey';
import { normalizeSingBoxVariantFields } from '../helpers/singBoxVariant';
import type { Podkop } from '../types';
import type { Forkop } from '../types';
import { getLocalActionOverlay } from './localActionOverlay.service';
import { store } from './store.service';
import type { StoreType } from './store.service';
type UiActionMap = Partial<Podkop.UiState['actions']>;
type UiActionMap = Partial<Forkop.UiState['actions']>;
function isRunningAction(state: { running?: boolean }) {
return state.running === true;
@@ -13,8 +13,8 @@ function isRunningAction(state: { running?: boolean }) {
function getEmptyUpdatesActions(): StoreType['updatesActions'] {
return {
podkopCheck: { loading: false },
podkopInstall: { loading: false },
forkopCheck: { loading: false },
forkopInstall: { loading: false },
singBoxCheck: { loading: false },
singBoxInstall: { loading: false },
singBoxInstallExtended: { loading: false },
@@ -43,8 +43,8 @@ function getEmptyDiagnosticsActions(): StoreType['diagnosticsActions'] {
}
function normalizeLatencyProgress(
progress?: Podkop.LatencyActionProgress,
): Podkop.LatencyActionProgress | undefined {
progress?: Forkop.LatencyActionProgress,
): Forkop.LatencyActionProgress | undefined {
const total = Math.trunc(Number(progress?.total ?? 0));
if (!Number.isFinite(total) || total <= 0) {
@@ -65,7 +65,7 @@ function normalizeLatencyProgress(
};
}
function applyServiceState(uiState: Podkop.UiState) {
function applyServiceState(uiState: Forkop.UiState) {
const currentSystemInfo = store.get().diagnosticsSystemInfo;
const nextSystemInfo = {
...currentSystemInfo,
@@ -88,9 +88,9 @@ function applyServiceState(uiState: Podkop.UiState) {
failed: false,
data: {
singbox: uiState.service.sing_box.running,
podkopRunning: uiState.service.podkop.running,
podkopEnabled: uiState.service.podkop.enabled,
podkopStatus: uiState.service.podkop.status,
forkopRunning: uiState.service.forkop.running,
forkopEnabled: uiState.service.forkop.enabled,
forkopStatus: uiState.service.forkop.status,
},
},
diagnosticsSystemInfo: normalizeSingBoxVariantFields(nextSystemInfo),
@@ -104,7 +104,7 @@ function applyActionState(actions: UiActionMap = {}) {
current.sectionsWidget.latencyProgressSections;
const subscriptionUpdatingSections: Record<string, boolean> = {};
const latencyFetchingSections: Record<string, boolean> = {};
const latencyProgressSections: Record<string, Podkop.LatencyActionProgress> =
const latencyProgressSections: Record<string, Forkop.LatencyActionProgress> =
{};
const updatesActions = getEmptyUpdatesActions();
const diagnosticsActions = getEmptyDiagnosticsActions();
@@ -188,7 +188,7 @@ function applyActionState(actions: UiActionMap = {}) {
});
}
export function applyUiStateToStore(uiState: Podkop.UiState) {
export function applyUiStateToStore(uiState: Forkop.UiState) {
applyServiceState(uiState);
applyActionState(uiState.actions);
}
@@ -8,7 +8,7 @@ import { copyToClipboard } from '../../../helpers/copyToClipboard';
import { showToast } from '../../../helpers/showToast';
import { prettyBytes } from '../../../helpers/prettyBytes';
import { renderCopyIcon24 } from '../../../icons';
import { CustomPodkopMethods, PodkopShellMethods } from '../../methods';
import { CustomForkopMethods, ForkopShellMethods } from '../../methods';
import {
logger,
markUiActionOwned,
@@ -22,7 +22,7 @@ import {
import { getLatencyTestLabel, renderSections, renderWidget } from './partials';
import { fetchServicesInfo } from '../../fetchers/fetchServicesInfo';
import { getClashApiSecret } from '../../methods/custom/getClashApiSecret';
import { Podkop } from '../../types';
import { Forkop } from '../../types';
import {
getCachedRuntimeUiState,
refreshRuntimeUiState,
@@ -79,7 +79,7 @@ async function fetchDashboardSectionsOnce(mountId: number) {
});
try {
const { data, success } = await CustomPodkopMethods.getDashboardSections();
const { data, success } = await CustomForkopMethods.getDashboardSections();
if (
!dashboardMounted ||
@@ -219,7 +219,7 @@ function setLatencyFetching(
sectionName: string,
fetching: boolean,
local = false,
progress?: Podkop.LatencyActionProgress,
progress?: Forkop.LatencyActionProgress,
) {
if (local || !fetching) {
setLocalLatencyAction(sectionName, fetching && local);
@@ -255,7 +255,7 @@ function setLatencyFetching(
async function completeSubscriptionUpdateJob(
jobId: string,
sectionName: string,
response: Podkop.MethodResponse<Podkop.SubscriptionUpdateJobState>,
response: Forkop.MethodResponse<Forkop.SubscriptionUpdateJobState>,
) {
if (pageUnloading) {
setSubscriptionUpdating(sectionName, false);
@@ -287,7 +287,7 @@ async function completeSubscriptionUpdateJob(
setSubscriptionUpdating(sectionName, false);
if (jobId && response.success) {
void PodkopShellMethods.uiActionAck('subscription', jobId);
void ForkopShellMethods.uiActionAck('subscription', jobId);
}
if (failed) {
@@ -305,7 +305,7 @@ async function completeSubscriptionUpdateJob(
}
async function followSubscriptionUpdateState(
state: Podkop.SubscriptionUpdateJobState,
state: Forkop.SubscriptionUpdateJobState,
) {
const jobId = state.job_id;
const sectionName = state.section || '';
@@ -325,11 +325,11 @@ async function followSubscriptionUpdateState(
try {
const response = state.running
? await PodkopShellMethods.waitSubscriptionUpdateJob(jobId)
? await ForkopShellMethods.waitSubscriptionUpdateJob(jobId)
: ({
success: true,
data: state,
} as Podkop.MethodSuccessResponse<Podkop.SubscriptionUpdateJobState>);
} as Forkop.MethodSuccessResponse<Forkop.SubscriptionUpdateJobState>);
await completeSubscriptionUpdateJob(jobId, sectionName, response);
} catch (error) {
@@ -366,13 +366,13 @@ async function completeLatencyTestJob(jobId: string, sectionName: string) {
}
if (jobId) {
void PodkopShellMethods.uiActionAck('latency', jobId);
void ForkopShellMethods.uiActionAck('latency', jobId);
}
void fetchDashboardSections({ force: true });
}
async function followLatencyTestState(state: Podkop.LatencyActionState) {
async function followLatencyTestState(state: Forkop.LatencyActionState) {
const jobId = state.job_id;
const sectionName = state.section || '';
@@ -391,7 +391,7 @@ async function followLatencyTestState(state: Podkop.LatencyActionState) {
try {
if (state.running) {
await PodkopShellMethods.waitLatencyTestJob(jobId);
await ForkopShellMethods.waitLatencyTestJob(jobId);
}
await completeLatencyTestJob(jobId, sectionName);
@@ -405,13 +405,13 @@ async function followLatencyTestState(state: Podkop.LatencyActionState) {
}
}
function followDashboardActionsFromUiState(uiState: Podkop.UiState) {
function followDashboardActionsFromUiState(uiState: Forkop.UiState) {
for (const state of uiState.actions.subscription || []) {
if (state.running || (state.job_id && state.section)) {
void followSubscriptionUpdateState(state);
} else if (state.job_id && !handledSubscriptionJobs.has(state.job_id)) {
handledSubscriptionJobs.add(state.job_id);
void PodkopShellMethods.uiActionAck('subscription', state.job_id);
void ForkopShellMethods.uiActionAck('subscription', state.job_id);
}
}
@@ -420,7 +420,7 @@ function followDashboardActionsFromUiState(uiState: Podkop.UiState) {
void followLatencyTestState(state);
} else if (state.job_id && !handledLatencyJobs.has(state.job_id)) {
handledLatencyJobs.add(state.job_id);
void PodkopShellMethods.uiActionAck('latency', state.job_id);
void ForkopShellMethods.uiActionAck('latency', state.job_id);
}
}
}
@@ -571,7 +571,7 @@ function getDashboardServiceAvailability() {
return getServiceAvailability({
loading: service.loading,
failed: service.failed,
running: service.data.podkopRunning,
running: service.data.forkopRunning,
});
}
@@ -611,7 +611,7 @@ function syncDashboardServiceAvailability() {
const stopped = availability === 'stopped';
const container = document.getElementById('dashboard-status');
container?.classList.toggle('pdk_dashboard-page--service-stopped', stopped);
container?.classList.toggle('fkp_dashboard-page--service-stopped', stopped);
if (stopped || availability === 'loading') {
stopDashboardDataUpdates();
@@ -646,7 +646,7 @@ async function handleChooseOutbound(
setSelectorSwitching(sectionName, tag);
try {
await PodkopShellMethods.setClashApiGroupProxy(selector, tag);
await ForkopShellMethods.setClashApiGroupProxy(selector, tag);
await fetchDashboardSections({ force: true });
} finally {
setSelectorSwitching(sectionName);
@@ -654,9 +654,9 @@ async function handleChooseOutbound(
}
function getInitialLatencyProgress(
latencyType: Podkop.LatencyActionState['latency_type'],
latencyType: Forkop.LatencyActionState['latency_type'],
tag: string,
): Podkop.LatencyActionProgress | undefined {
): Forkop.LatencyActionProgress | undefined {
if (latencyType !== 'proxy_list') {
return undefined;
}
@@ -678,7 +678,7 @@ function getInitialLatencyProgress(
}
async function handleTestLatency(
latencyType: Podkop.LatencyActionState['latency_type'],
latencyType: Forkop.LatencyActionState['latency_type'],
sectionName: string,
tag: string,
timeout?: string,
@@ -698,7 +698,7 @@ async function handleTestLatency(
let completed = false;
try {
const startResponse = await PodkopShellMethods.latencyTestStart(
const startResponse = await ForkopShellMethods.latencyTestStart(
latencyType,
sectionName,
tag,
@@ -717,7 +717,7 @@ async function handleTestLatency(
followedLatencyJobs.add(jobId);
ownsJobFollow = true;
await PodkopShellMethods.waitLatencyTestJob(jobId);
await ForkopShellMethods.waitLatencyTestJob(jobId);
await completeLatencyTestJob(jobId, sectionName);
completed = true;
} catch (error) {
@@ -734,8 +734,8 @@ async function handleTestLatency(
}
async function handleCopyOutbound(
section: Podkop.OutboundGroup,
outbound: Podkop.Outbound,
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
) {
const link = outbound.link;
@@ -744,7 +744,7 @@ async function handleCopyOutbound(
return;
}
const response = await PodkopShellMethods.getOutboundLink(
const response = await ForkopShellMethods.getOutboundLink(
section.sectionName,
outbound.code,
);
@@ -768,18 +768,18 @@ function formatUrlTestModalValue(value: unknown) {
function getUrlTestLatencyClass(latency: number) {
if (!latency) {
return 'pdk_dashboard-page__outbound-grid__item__latency--empty';
return 'fkp_dashboard-page__outbound-grid__item__latency--empty';
}
if (latency < 800) {
return 'pdk_dashboard-page__outbound-grid__item__latency--green';
return 'fkp_dashboard-page__outbound-grid__item__latency--green';
}
if (latency < 1500) {
return 'pdk_dashboard-page__outbound-grid__item__latency--yellow';
return 'fkp_dashboard-page__outbound-grid__item__latency--yellow';
}
return 'pdk_dashboard-page__outbound-grid__item__latency--red';
return 'fkp_dashboard-page__outbound-grid__item__latency--red';
}
function formatUrlTestLatency(latency: number) {
@@ -796,7 +796,7 @@ function renderDetailsUrl(value: unknown) {
return E(
'a',
{
class: 'pdk_dashboard-page__urltest-details__url',
class: 'fkp_dashboard-page__urltest-details__url',
href: url,
target: '_blank',
rel: 'noopener noreferrer',
@@ -817,7 +817,7 @@ function getDetectedCountryFlag(country?: string) {
);
}
function renderDetailsMemberName(member: Podkop.UrlTestMember) {
function renderDetailsMemberName(member: Forkop.UrlTestMember) {
const countryFlag = getDetectedCountryFlag(member.country);
if (!countryFlag) {
return member.displayName;
@@ -826,14 +826,14 @@ function renderDetailsMemberName(member: Podkop.UrlTestMember) {
return [
E(
'span',
{ class: 'pdk_dashboard-page__urltest-details__country-badge' },
{ class: 'fkp_dashboard-page__urltest-details__country-badge' },
countryFlag,
),
member.displayName,
];
}
function renderUrlTestSelectedValue(info: Podkop.UrlTestInfo) {
function renderUrlTestSelectedValue(info: Forkop.UrlTestInfo) {
const selectedMember = info.outbounds.find((member) => member.selected);
const selectedName =
selectedMember?.displayName || info.selectedName || info.selectedCode || '';
@@ -845,18 +845,18 @@ function renderUrlTestSelectedValue(info: Podkop.UrlTestInfo) {
return E(
'span',
{ class: 'pdk_dashboard-page__urltest-details__selected-value' },
{ class: 'fkp_dashboard-page__urltest-details__selected-value' },
[
E(
'span',
{ class: 'pdk_dashboard-page__urltest-details__selected-name' },
{ class: 'fkp_dashboard-page__urltest-details__selected-name' },
selectedMember ? renderDetailsMemberName(selectedMember) : name,
),
...(selectedMember?.type
? [
E(
'span',
{ class: 'pdk_dashboard-page__urltest-details__selected-type' },
{ class: 'fkp_dashboard-page__urltest-details__selected-type' },
selectedMember.type,
),
]
@@ -882,7 +882,7 @@ function renderUrlTestCopyButton(
'button',
{
type: 'button',
class: 'btn pdk_dashboard-page__urltest-details__copy-button',
class: 'btn fkp_dashboard-page__urltest-details__copy-button',
title,
'aria-label': title,
click: onClick,
@@ -892,8 +892,8 @@ function renderUrlTestCopyButton(
}
function renderUrlTestInfoModal(
section: Podkop.OutboundGroup,
outbound: Podkop.Outbound,
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
) {
const info = outbound.urlTestInfo;
@@ -920,12 +920,12 @@ function renderUrlTestInfoModal(
},
];
return E('div', { class: 'pdk_dashboard-page__urltest-details' }, [
return E('div', { class: 'fkp_dashboard-page__urltest-details' }, [
E(
'dl',
{ class: 'pdk_dashboard-page__urltest-details__params' },
{ class: 'fkp_dashboard-page__urltest-details__params' },
fields.map(({ label, value, children }) =>
E('div', { class: 'pdk_dashboard-page__urltest-details__param' }, [
E('div', { class: 'fkp_dashboard-page__urltest-details__param' }, [
E('dt', {}, label),
E(
'dd',
@@ -935,24 +935,24 @@ function renderUrlTestInfoModal(
]),
),
),
E('div', { class: 'pdk_dashboard-page__urltest-details__outbounds' }, [
E('div', { class: 'fkp_dashboard-page__urltest-details__outbounds' }, [
E(
'div',
{ class: 'pdk_dashboard-page__urltest-details__outbounds-title' },
{ class: 'fkp_dashboard-page__urltest-details__outbounds-title' },
_('Nodes'),
),
E(
'div',
{ class: 'pdk_dashboard-page__urltest-details__table' },
{ class: 'fkp_dashboard-page__urltest-details__table' },
info.outbounds.length
? info.outbounds.map((member) =>
E(
'div',
{
class: [
'pdk_dashboard-page__urltest-details__row',
'fkp_dashboard-page__urltest-details__row',
member.selected
? 'pdk_dashboard-page__urltest-details__row--active'
? 'fkp_dashboard-page__urltest-details__row--active'
: '',
]
.filter(Boolean)
@@ -962,7 +962,7 @@ function renderUrlTestInfoModal(
E(
'div',
{
class: 'pdk_dashboard-page__urltest-details__row-name',
class: 'fkp_dashboard-page__urltest-details__row-name',
},
[
E('b', {}, renderDetailsMemberName(member)),
@@ -972,7 +972,7 @@ function renderUrlTestInfoModal(
'span',
{
class:
'pdk_dashboard-page__urltest-details__row-type',
'fkp_dashboard-page__urltest-details__row-type',
},
member.type,
),
@@ -983,7 +983,7 @@ function renderUrlTestInfoModal(
E(
'div',
{
class: 'pdk_dashboard-page__urltest-details__row-meta',
class: 'fkp_dashboard-page__urltest-details__row-meta',
},
[
E(
@@ -1000,7 +1000,7 @@ function renderUrlTestInfoModal(
})
: E('span', {
class:
'pdk_dashboard-page__urltest-details__copy-placeholder',
'fkp_dashboard-page__urltest-details__copy-placeholder',
}),
],
),
@@ -1008,13 +1008,13 @@ function renderUrlTestInfoModal(
: [
E(
'div',
{ class: 'pdk_dashboard-page__urltest-details__empty' },
{ class: 'fkp_dashboard-page__urltest-details__empty' },
_('Node list is empty'),
),
],
),
]),
E('div', { class: 'pdk_dashboard-page__urltest-details__footer' }, [
E('div', { class: 'fkp_dashboard-page__urltest-details__footer' }, [
E(
'button',
{
@@ -1031,8 +1031,8 @@ function renderUrlTestInfoModal(
}
function handleShowUrlTestInfo(
section: Podkop.OutboundGroup,
outbound: Podkop.Outbound,
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
) {
if (!outbound.urlTestInfo) {
return;
@@ -1046,7 +1046,7 @@ function handleShowUrlTestInfo(
);
}
function renderPrioritySelectedValue(info: Podkop.PriorityInfo) {
function renderPrioritySelectedValue(info: Forkop.PriorityInfo) {
const selectedMember = info.outbounds.find((member) => member.selected);
const selectedName =
selectedMember?.displayName || info.selectedName || info.selectedCode || '';
@@ -1058,15 +1058,15 @@ function renderPrioritySelectedValue(info: Podkop.PriorityInfo) {
return E(
'span',
{ class: 'pdk_dashboard-page__urltest-details__selected-value' },
{ class: 'fkp_dashboard-page__urltest-details__selected-value' },
[
E(
'span',
{
class: [
'pdk_dashboard-page__urltest-details__selected-name',
'fkp_dashboard-page__urltest-details__selected-name',
selectedMember
? 'pdk_dashboard-page__urltest-details__priority-name'
? 'fkp_dashboard-page__urltest-details__priority-name'
: '',
]
.filter(Boolean)
@@ -1078,7 +1078,7 @@ function renderPrioritySelectedValue(info: Podkop.PriorityInfo) {
? [
E(
'span',
{ class: 'pdk_dashboard-page__urltest-details__selected-type' },
{ class: 'fkp_dashboard-page__urltest-details__selected-type' },
selectedMember.type,
),
]
@@ -1096,31 +1096,31 @@ function renderPrioritySelectedValue(info: Podkop.PriorityInfo) {
);
}
function renderPriorityMemberName(member: Podkop.PriorityMember) {
function renderPriorityMemberName(member: Forkop.PriorityMember) {
const levelName = member.levelName || _('Level');
return [
E(
'span',
{ class: 'pdk_dashboard-page__urltest-details__priority-number' },
{ class: 'fkp_dashboard-page__urltest-details__priority-number' },
`#${member.levelIndex + 1}`,
),
E(
'span',
{ class: 'pdk_dashboard-page__urltest-details__priority-level' },
{ class: 'fkp_dashboard-page__urltest-details__priority-level' },
levelName,
),
E(
'span',
{ class: 'pdk_dashboard-page__urltest-details__priority-node' },
{ class: 'fkp_dashboard-page__urltest-details__priority-node' },
renderDetailsMemberName(member),
),
];
}
function renderPriorityInfoModal(
section: Podkop.OutboundGroup,
outbound: Podkop.Outbound,
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
) {
const info = outbound.priorityInfo;
@@ -1169,12 +1169,12 @@ function renderPriorityInfoModal(
},
];
return E('div', { class: 'pdk_dashboard-page__urltest-details' }, [
return E('div', { class: 'fkp_dashboard-page__urltest-details' }, [
E(
'dl',
{ class: 'pdk_dashboard-page__urltest-details__params' },
{ class: 'fkp_dashboard-page__urltest-details__params' },
fields.map(({ label, value, children }) =>
E('div', { class: 'pdk_dashboard-page__urltest-details__param' }, [
E('div', { class: 'fkp_dashboard-page__urltest-details__param' }, [
E('dt', {}, label),
E(
'dd',
@@ -1184,24 +1184,24 @@ function renderPriorityInfoModal(
]),
),
),
E('div', { class: 'pdk_dashboard-page__urltest-details__outbounds' }, [
E('div', { class: 'fkp_dashboard-page__urltest-details__outbounds' }, [
E(
'div',
{ class: 'pdk_dashboard-page__urltest-details__outbounds-title' },
{ class: 'fkp_dashboard-page__urltest-details__outbounds-title' },
_('Nodes'),
),
E(
'div',
{ class: 'pdk_dashboard-page__urltest-details__table' },
{ class: 'fkp_dashboard-page__urltest-details__table' },
info.outbounds.length
? info.outbounds.map((member) =>
E(
'div',
{
class: [
'pdk_dashboard-page__urltest-details__row',
'fkp_dashboard-page__urltest-details__row',
member.selected
? 'pdk_dashboard-page__urltest-details__row--active'
? 'fkp_dashboard-page__urltest-details__row--active'
: '',
]
.filter(Boolean)
@@ -1211,14 +1211,14 @@ function renderPriorityInfoModal(
E(
'div',
{
class: 'pdk_dashboard-page__urltest-details__row-name',
class: 'fkp_dashboard-page__urltest-details__row-name',
},
[
E(
'b',
{
class:
'pdk_dashboard-page__urltest-details__priority-name',
'fkp_dashboard-page__urltest-details__priority-name',
},
renderPriorityMemberName(member),
),
@@ -1228,7 +1228,7 @@ function renderPriorityInfoModal(
'span',
{
class:
'pdk_dashboard-page__urltest-details__row-type',
'fkp_dashboard-page__urltest-details__row-type',
},
member.type,
),
@@ -1239,7 +1239,7 @@ function renderPriorityInfoModal(
E(
'div',
{
class: 'pdk_dashboard-page__urltest-details__row-meta',
class: 'fkp_dashboard-page__urltest-details__row-meta',
},
[
E(
@@ -1256,7 +1256,7 @@ function renderPriorityInfoModal(
})
: E('span', {
class:
'pdk_dashboard-page__urltest-details__copy-placeholder',
'fkp_dashboard-page__urltest-details__copy-placeholder',
}),
],
),
@@ -1264,13 +1264,13 @@ function renderPriorityInfoModal(
: [
E(
'div',
{ class: 'pdk_dashboard-page__urltest-details__empty' },
{ class: 'fkp_dashboard-page__urltest-details__empty' },
_('Node list is empty'),
),
],
),
]),
E('div', { class: 'pdk_dashboard-page__urltest-details__footer' }, [
E('div', { class: 'fkp_dashboard-page__urltest-details__footer' }, [
E(
'button',
{
@@ -1287,8 +1287,8 @@ function renderPriorityInfoModal(
}
function handleShowPriorityInfo(
section: Podkop.OutboundGroup,
outbound: Podkop.Outbound,
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
) {
if (!outbound.priorityInfo) {
return;
@@ -1302,7 +1302,7 @@ function handleShowPriorityInfo(
);
}
async function handleUpdateSubscription(section: Podkop.OutboundGroup) {
async function handleUpdateSubscription(section: Forkop.OutboundGroup) {
if (
store.get().sectionsWidget.subscriptionUpdatingSections[section.sectionName]
) {
@@ -1314,7 +1314,7 @@ async function handleUpdateSubscription(section: Podkop.OutboundGroup) {
let ownsJobFollow = false;
try {
const startResponse = await PodkopShellMethods.subscriptionUpdateStart(
const startResponse = await ForkopShellMethods.subscriptionUpdateStart(
section.sectionName,
);
@@ -1330,7 +1330,7 @@ async function handleUpdateSubscription(section: Podkop.OutboundGroup) {
followedSubscriptionJobs.add(jobId);
ownsJobFollow = true;
const response = await PodkopShellMethods.waitSubscriptionUpdateJob(jobId);
const response = await ForkopShellMethods.waitSubscriptionUpdateJob(jobId);
await completeSubscriptionUpdateJob(jobId, section.sectionName, response);
} catch (error) {
logger.error('[DASHBOARD]', 'handleUpdateSubscription: failed', error);
@@ -1670,14 +1670,14 @@ async function renderServicesInfoWidget() {
title: _('Services info'),
items: [
{
key: 'Podkop Plus',
value: servicesInfoWidget.data.podkopRunning
key: 'Forkop',
value: servicesInfoWidget.data.forkopRunning
? _('✔ Running')
: _('✘ Stopped'),
attributes: {
class: servicesInfoWidget.data.podkopRunning
? 'pdk_dashboard-page__widgets-section__item__row--success'
: 'pdk_dashboard-page__widgets-section__item__row--error',
class: servicesInfoWidget.data.forkopRunning
? 'fkp_dashboard-page__widgets-section__item__row--success'
: 'fkp_dashboard-page__widgets-section__item__row--error',
},
},
{
@@ -1687,8 +1687,8 @@ async function renderServicesInfoWidget() {
: _('✘ Stopped'),
attributes: {
class: servicesInfoWidget.data.singbox
? 'pdk_dashboard-page__widgets-section__item__row--success'
: 'pdk_dashboard-page__widgets-section__item__row--error',
? 'fkp_dashboard-page__widgets-section__item__row--success'
: 'fkp_dashboard-page__widgets-section__item__row--error',
},
},
],
@@ -6,12 +6,12 @@ import {
} from '../../../../icons';
import { isCopyableProxyLink, svgEl } from '../../../../helpers';
import { prettyBytes } from '../../../../helpers/prettyBytes';
import { Podkop } from '../../../types';
import { Forkop } from '../../../types';
interface IRenderSectionsProps {
loading: boolean;
failed: boolean;
section: Podkop.OutboundGroup;
section: Forkop.OutboundGroup;
onTestLatency: (tag: string | string[]) => void;
onChooseOutbound: (
sectionName: string,
@@ -19,20 +19,20 @@ interface IRenderSectionsProps {
tag: string,
) => void;
onCopyOutbound: (
section: Podkop.OutboundGroup,
outbound: Podkop.Outbound,
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
) => void;
onShowUrlTestInfo: (
section: Podkop.OutboundGroup,
outbound: Podkop.Outbound,
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
) => void;
onShowPriorityInfo: (
section: Podkop.OutboundGroup,
outbound: Podkop.Outbound,
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
) => void;
onUpdateSubscription: (section: Podkop.OutboundGroup) => void;
onUpdateSubscription: (section: Forkop.OutboundGroup) => void;
latencyFetching: boolean;
latencyProgress?: Podkop.LatencyActionProgress;
latencyProgress?: Forkop.LatencyActionProgress;
subscriptionUpdating: boolean;
selectorSwitchingTag?: string;
}
@@ -41,7 +41,7 @@ function renderFailedState() {
return E(
'div',
{
class: 'pdk_dashboard-page__outbound-section centered',
class: 'fkp_dashboard-page__outbound-section centered',
style: 'height: 127px',
},
E('span', {}, [E('span', {}, _('Dashboard currently unavailable'))]),
@@ -51,7 +51,7 @@ function renderFailedState() {
function renderLoadingState() {
return E('div', {
id: 'dashboard-sections-grid-skeleton',
class: 'pdk_dashboard-page__outbound-section skeleton',
class: 'fkp_dashboard-page__outbound-section skeleton',
style: 'height: 127px',
});
}
@@ -97,7 +97,7 @@ function renderMetadataAction(label: string, url?: string) {
return E(
'a',
{
class: 'btn pdk_dashboard-page__subscription-meta__action',
class: 'btn fkp_dashboard-page__subscription-meta__action',
href: url,
target: '_blank',
rel: 'noopener noreferrer',
@@ -109,7 +109,7 @@ function renderMetadataAction(label: string, url?: string) {
}
function renderSubscriptionMetadata(
metadata: Podkop.SubscriptionMetadata | undefined,
metadata: Forkop.SubscriptionMetadata | undefined,
) {
if (!metadata || Object.keys(metadata).length <= 1) {
return undefined;
@@ -141,33 +141,33 @@ function renderSubscriptionMetadata(
renderMetadataAction('More details', metadata.announceUrl),
].filter(Boolean) as HTMLElement[];
return E('div', { class: 'pdk_dashboard-page__subscription-meta' }, [
E('div', { class: 'pdk_dashboard-page__subscription-meta__main' }, [
return E('div', { class: 'fkp_dashboard-page__subscription-meta' }, [
E('div', { class: 'fkp_dashboard-page__subscription-meta__main' }, [
E(
'div',
{ class: 'pdk_dashboard-page__subscription-meta__heading' },
{ class: 'fkp_dashboard-page__subscription-meta__heading' },
_('Subscription info:'),
),
title
? E(
'div',
{ class: 'pdk_dashboard-page__subscription-meta__title' },
{ class: 'fkp_dashboard-page__subscription-meta__title' },
title,
)
: '',
rows.length
? E(
'div',
{ class: 'pdk_dashboard-page__subscription-meta__facts' },
{ class: 'fkp_dashboard-page__subscription-meta__facts' },
rows.map((row) =>
E(
'div',
{ class: 'pdk_dashboard-page__subscription-meta__fact' },
{ class: 'fkp_dashboard-page__subscription-meta__fact' },
[
E(
'span',
{
class: 'pdk_dashboard-page__subscription-meta__fact-key',
class: 'fkp_dashboard-page__subscription-meta__fact-key',
},
row.label,
),
@@ -175,7 +175,7 @@ function renderSubscriptionMetadata(
'span',
{
class:
'pdk_dashboard-page__subscription-meta__fact-value',
'fkp_dashboard-page__subscription-meta__fact-value',
},
row.value,
),
@@ -187,7 +187,7 @@ function renderSubscriptionMetadata(
actions.length
? E(
'div',
{ class: 'pdk_dashboard-page__subscription-meta__actions' },
{ class: 'fkp_dashboard-page__subscription-meta__actions' },
actions,
)
: '',
@@ -195,7 +195,7 @@ function renderSubscriptionMetadata(
metadata.announce
? E(
'blockquote',
{ class: 'pdk_dashboard-page__subscription-meta__announce' },
{ class: 'fkp_dashboard-page__subscription-meta__announce' },
metadata.announce,
)
: '',
@@ -203,9 +203,9 @@ function renderSubscriptionMetadata(
}
function renderSubscriptionUpdateAction(
section: Podkop.OutboundGroup,
section: Forkop.OutboundGroup,
subscriptionUpdating: boolean,
onUpdateSubscription: (section: Podkop.OutboundGroup) => void,
onUpdateSubscription: (section: Forkop.OutboundGroup) => void,
) {
if (!section.subscriptionSourceCount) {
return undefined;
@@ -215,7 +215,7 @@ function renderSubscriptionUpdateAction(
'button',
{
type: 'button',
class: 'btn pdk_dashboard-page__outbound-section__subscription-update',
class: 'btn fkp_dashboard-page__outbound-section__subscription-update',
'aria-label': _('Update subscriptions'),
disabled: subscriptionUpdating ? true : undefined,
click: (event: MouseEvent) => {
@@ -235,7 +235,7 @@ function renderSubscriptionUpdateAction(
}
export function getLatencyTestLabel(
latencyProgress?: Podkop.LatencyActionProgress,
latencyProgress?: Forkop.LatencyActionProgress,
) {
const total = Math.trunc(Number(latencyProgress?.total ?? 0));
if (!Number.isFinite(total) || total <= 0) {
@@ -280,21 +280,21 @@ function renderDefaultState({
}
}
function renderOutbound(outbound: Podkop.Outbound) {
function renderOutbound(outbound: Forkop.Outbound) {
function getLatencyClass() {
if (!outbound.latency) {
return 'pdk_dashboard-page__outbound-grid__item__latency--empty';
return 'fkp_dashboard-page__outbound-grid__item__latency--empty';
}
if (outbound.latency < 800) {
return 'pdk_dashboard-page__outbound-grid__item__latency--green';
return 'fkp_dashboard-page__outbound-grid__item__latency--green';
}
if (outbound.latency < 1500) {
return 'pdk_dashboard-page__outbound-grid__item__latency--yellow';
return 'fkp_dashboard-page__outbound-grid__item__latency--yellow';
}
return 'pdk_dashboard-page__outbound-grid__item__latency--red';
return 'fkp_dashboard-page__outbound-grid__item__latency--red';
}
const canCopyLink =
@@ -307,18 +307,18 @@ function renderDefaultState({
!selectorSwitching &&
!outbound.selected;
const className = [
'pdk_dashboard-page__outbound-grid__item',
'fkp_dashboard-page__outbound-grid__item',
outbound.selected
? 'pdk_dashboard-page__outbound-grid__item--active'
? 'fkp_dashboard-page__outbound-grid__item--active'
: '',
canChooseOutbound
? 'pdk_dashboard-page__outbound-grid__item--selectable'
? 'fkp_dashboard-page__outbound-grid__item--selectable'
: '',
section.withTagSelect && !canChooseOutbound
? 'pdk_dashboard-page__outbound-grid__item--disabled'
? 'fkp_dashboard-page__outbound-grid__item--disabled'
: '',
outboundSwitching
? 'pdk_dashboard-page__outbound-grid__item--switching'
? 'fkp_dashboard-page__outbound-grid__item--switching'
: '',
]
.filter(Boolean)
@@ -339,7 +339,7 @@ function renderDefaultState({
? [
svgEl(
'svg',
{ class: 'pdk_dashboard-page__outbound-grid__item__snake' },
{ class: 'fkp_dashboard-page__outbound-grid__item__snake' },
[
svgEl('rect', {
width: '100%',
@@ -353,7 +353,7 @@ function renderDefaultState({
),
]
: []),
E('div', { class: 'pdk_dashboard-page__outbound-grid__item__header' }, [
E('div', { class: 'fkp_dashboard-page__outbound-grid__item__header' }, [
E('b', {}, outbound.displayName),
...(canCopyLink
? [
@@ -362,7 +362,7 @@ function renderDefaultState({
{
type: 'button',
class:
'btn pdk_dashboard-page__outbound-grid__item__copy-button',
'btn fkp_dashboard-page__outbound-grid__item__copy-button',
title: _('Copy proxy link'),
'aria-label': _('Copy proxy link'),
click: (event: MouseEvent) => {
@@ -381,7 +381,7 @@ function renderDefaultState({
{
type: 'button',
class:
'btn pdk_dashboard-page__outbound-grid__item__copy-button',
'btn fkp_dashboard-page__outbound-grid__item__copy-button',
title: _('URLTest details'),
'aria-label': _('URLTest details'),
click: (event: MouseEvent) => {
@@ -400,7 +400,7 @@ function renderDefaultState({
{
type: 'button',
class:
'btn pdk_dashboard-page__outbound-grid__item__copy-button',
'btn fkp_dashboard-page__outbound-grid__item__copy-button',
title: _('Priority details'),
'aria-label': _('Priority details'),
click: (event: MouseEvent) => {
@@ -413,10 +413,10 @@ function renderDefaultState({
]
: []),
]),
E('div', { class: 'pdk_dashboard-page__outbound-grid__item__footer' }, [
E('div', { class: 'fkp_dashboard-page__outbound-grid__item__footer' }, [
E(
'div',
{ class: 'pdk_dashboard-page__outbound-grid__item__type' },
{ class: 'fkp_dashboard-page__outbound-grid__item__type' },
[outbound.type].filter(Boolean),
),
E(
@@ -438,20 +438,20 @@ function renderDefaultState({
onUpdateSubscription,
);
return E('div', { class: 'pdk_dashboard-page__outbound-section' }, [
return E('div', { class: 'fkp_dashboard-page__outbound-section' }, [
// Title with test latency
E('div', { class: 'pdk_dashboard-page__outbound-section__title-section' }, [
E('div', { class: 'fkp_dashboard-page__outbound-section__title-section' }, [
E(
'div',
{
class: 'pdk_dashboard-page__outbound-section__title-section__title',
class: 'fkp_dashboard-page__outbound-section__title-section__title',
},
section.displayName,
),
E(
'div',
{
class: 'pdk_dashboard-page__outbound-section__title-section__actions',
class: 'fkp_dashboard-page__outbound-section__title-section__actions',
},
[
...(subscriptionUpdateAction ? [subscriptionUpdateAction] : []),
@@ -494,7 +494,7 @@ function renderDefaultState({
],
),
]),
E('div', { class: 'pdk_dashboard-page__outbound-grid' }, [
E('div', { class: 'fkp_dashboard-page__outbound-grid' }, [
...metadataNodes,
...section.outbounds.map((outbound) => renderOutbound(outbound)),
]),
@@ -17,7 +17,7 @@ function renderFailedState() {
{
id: '',
style: 'height: 78px',
class: 'pdk_dashboard-page__widgets-section__item centered',
class: 'fkp_dashboard-page__widgets-section__item centered',
},
_('Currently unavailable'),
);
@@ -29,34 +29,34 @@ function renderLoadingState() {
{
id: '',
style: 'height: 78px',
class: 'pdk_dashboard-page__widgets-section__item skeleton',
class: 'fkp_dashboard-page__widgets-section__item skeleton',
},
'',
);
}
function renderDefaultState({ title, items }: IRenderWidgetProps) {
return E('div', { class: 'pdk_dashboard-page__widgets-section__item' }, [
return E('div', { class: 'fkp_dashboard-page__widgets-section__item' }, [
E(
'b',
{ class: 'pdk_dashboard-page__widgets-section__item__title' },
{ class: 'fkp_dashboard-page__widgets-section__item__title' },
title,
),
...items.map((item) =>
E(
'div',
{
class: `pdk_dashboard-page__widgets-section__item__row ${item?.attributes?.class || ''}`,
class: `fkp_dashboard-page__widgets-section__item__row ${item?.attributes?.class || ''}`,
},
[
E(
'span',
{ class: 'pdk_dashboard-page__widgets-section__item__row__key' },
{ class: 'fkp_dashboard-page__widgets-section__item__row__key' },
`${item.key}: `,
),
E(
'span',
{ class: 'pdk_dashboard-page__widgets-section__item__row__value' },
{ class: 'fkp_dashboard-page__widgets-section__item__row__value' },
item.value,
),
],
@@ -5,22 +5,22 @@ export function render() {
'div',
{
id: 'dashboard-status',
class: 'pdk_dashboard-page',
class: 'fkp_dashboard-page',
},
[
E(
'div',
{
class: 'pdk_dashboard-page__service-stopped',
class: 'fkp_dashboard-page__service-stopped',
role: 'status',
},
_(
'Podkop Plus service is stopped. Start the service to display the dashboard.',
'Forkop service is stopped. Start the service to display the dashboard.',
),
),
E('div', { class: 'pdk_dashboard-page__content' }, [
E('div', { class: 'fkp_dashboard-page__content' }, [
// Widgets section
E('div', { class: 'pdk_dashboard-page__widgets-section' }, [
E('div', { class: 'fkp_dashboard-page__widgets-section' }, [
E(
'div',
{ id: 'dashboard-widget-traffic' },
@@ -1,31 +1,31 @@
// language=CSS
import { PODKOP_UCI_PACKAGE as PODKOP_CBI_PREFIX } from '../../../constants';
import { FORKOP_UCI_PACKAGE as FORKOP_CBI_PREFIX } from '../../../constants';
export const styles = `
#cbi-${PODKOP_CBI_PREFIX}-dashboard-_mount_node > .cbi-value-title {
#cbi-${FORKOP_CBI_PREFIX}-dashboard-_mount_node > .cbi-value-title {
display: none;
}
#cbi-${PODKOP_CBI_PREFIX}-dashboard-_mount_node > .cbi-value-field {
#cbi-${FORKOP_CBI_PREFIX}-dashboard-_mount_node > .cbi-value-field {
margin-left: 0;
width: 100%;
}
#cbi-${PODKOP_CBI_PREFIX}-dashboard-_mount_node > div {
#cbi-${FORKOP_CBI_PREFIX}-dashboard-_mount_node > div {
width: 100%;
}
#cbi-${PODKOP_CBI_PREFIX}-dashboard > h3 {
#cbi-${FORKOP_CBI_PREFIX}-dashboard > h3 {
display: none;
}
.pdk_dashboard-page {
.fkp_dashboard-page {
width: 100%;
--dashboard-grid-columns: 4;
--dashboard-grid-min-width: 180px;
}
.pdk_dashboard-page__service-stopped {
.fkp_dashboard-page__service-stopped {
display: none;
width: 100%;
min-height: 180px;
@@ -46,72 +46,72 @@ export const styles = `
text-align: center;
}
.pdk_dashboard-page--service-stopped {
.fkp_dashboard-page--service-stopped {
display: grid;
grid-template-columns: repeat(var(--dashboard-grid-columns), minmax(var(--dashboard-grid-min-width), 1fr));
gap: 10px;
}
.pdk_dashboard-page--service-stopped .pdk_dashboard-page__service-stopped {
.fkp_dashboard-page--service-stopped .fkp_dashboard-page__service-stopped {
display: flex;
grid-column: 1 / -1;
}
.pdk_dashboard-page--service-stopped .pdk_dashboard-page__content {
.fkp_dashboard-page--service-stopped .fkp_dashboard-page__content {
display: none;
}
@media (max-width: 900px) {
.pdk_dashboard-page {
.fkp_dashboard-page {
--dashboard-grid-columns: 2;
}
}
@media (max-width: 560px) {
.pdk_dashboard-page {
.fkp_dashboard-page {
--dashboard-grid-columns: 1;
--dashboard-grid-min-width: 0;
}
}
.pdk_dashboard-page__widgets-section {
.fkp_dashboard-page__widgets-section {
margin-top: 10px;
display: grid;
grid-template-columns: repeat(var(--dashboard-grid-columns), minmax(var(--dashboard-grid-min-width), 1fr));
grid-gap: 10px;
}
.pdk_dashboard-page__widgets-section__item {
.fkp_dashboard-page__widgets-section__item {
border: 2px var(--background-color-low, lightgray) solid;
border-radius: 4px;
padding: 10px;
min-width: 0;
}
.pdk_dashboard-page__widgets-section__item__title {}
.fkp_dashboard-page__widgets-section__item__title {}
.pdk_dashboard-page__widgets-section__item__row {}
.fkp_dashboard-page__widgets-section__item__row {}
.pdk_dashboard-page__widgets-section__item__row--success .pdk_dashboard-page__widgets-section__item__row__value {
.fkp_dashboard-page__widgets-section__item__row--success .fkp_dashboard-page__widgets-section__item__row__value {
color: var(--success-color-medium, green);
}
.pdk_dashboard-page__widgets-section__item__row--error .pdk_dashboard-page__widgets-section__item__row__value {
.fkp_dashboard-page__widgets-section__item__row--error .fkp_dashboard-page__widgets-section__item__row__value {
color: var(--error-color-medium, red);
}
.pdk_dashboard-page__widgets-section__item__row__key {}
.fkp_dashboard-page__widgets-section__item__row__key {}
.pdk_dashboard-page__widgets-section__item__row__value {}
.fkp_dashboard-page__widgets-section__item__row__value {}
.pdk_dashboard-page__outbound-section {
.fkp_dashboard-page__outbound-section {
margin-top: 10px;
border: 2px var(--background-color-low, lightgray) solid;
border-radius: 4px;
padding: 10px;
}
.pdk_dashboard-page__outbound-section__title-section {
.fkp_dashboard-page__outbound-section__title-section {
display: flex;
align-items: center;
justify-content: space-between;
@@ -119,14 +119,14 @@ export const styles = `
min-width: 0;
}
.pdk_dashboard-page__outbound-section__title-section__title {
.fkp_dashboard-page__outbound-section__title-section__title {
color: var(--text-color-high);
font-weight: 700;
min-width: 0;
overflow-wrap: anywhere;
}
.pdk_dashboard-page__outbound-section__title-section__actions {
.fkp_dashboard-page__outbound-section__title-section__actions {
display: flex;
align-items: center;
justify-content: flex-end;
@@ -134,7 +134,7 @@ export const styles = `
flex: 0 0 auto;
}
.pdk_dashboard-page .btn.pdk_dashboard-page__outbound-section__subscription-update {
.fkp_dashboard-page .btn.fkp_dashboard-page__outbound-section__subscription-update {
min-width: 130px;
min-height: 28px;
display: inline-flex;
@@ -143,19 +143,19 @@ export const styles = `
gap: 6px;
}
.pdk_dashboard-page__outbound-section__subscription-update svg {
.fkp_dashboard-page__outbound-section__subscription-update svg {
width: 15px;
height: 15px;
display: block;
flex: 0 0 auto;
}
.pdk_dashboard-page__outbound-section__subscription-update[disabled] {
.fkp_dashboard-page__outbound-section__subscription-update[disabled] {
cursor: not-allowed;
opacity: 0.65;
}
.pdk_dashboard-page .btn.dashboard-sections-grid-item-test-latency {
.fkp_dashboard-page .btn.dashboard-sections-grid-item-test-latency {
min-width: 99px;
min-height: 28px;
display: inline-flex;
@@ -164,26 +164,26 @@ export const styles = `
gap: 6px;
}
.pdk_dashboard-page .btn.dashboard-sections-grid-item-test-latency svg {
.fkp_dashboard-page .btn.dashboard-sections-grid-item-test-latency svg {
width: 15px;
height: 15px;
display: block;
flex: 0 0 auto;
}
.pdk_dashboard-page .btn.dashboard-sections-grid-item-test-latency[disabled] {
.fkp_dashboard-page .btn.dashboard-sections-grid-item-test-latency[disabled] {
cursor: not-allowed;
opacity: 0.65;
}
.pdk_dashboard-page__outbound-grid {
.fkp_dashboard-page__outbound-grid {
margin-top: 5px;
display: grid;
grid-template-columns: repeat(var(--dashboard-grid-columns), minmax(var(--dashboard-grid-min-width), 1fr));
grid-gap: 10px;
}
.pdk_dashboard-page__subscription-meta {
.fkp_dashboard-page__subscription-meta {
--subscription-meta-action-size: 28px;
--subscription-meta-action-gap: 6px;
grid-column: 1 / -1;
@@ -193,14 +193,14 @@ export const styles = `
background: var(--background-color-high, transparent);
}
.pdk_dashboard-page__subscription-meta__main {
.fkp_dashboard-page__subscription-meta__main {
display: flex;
align-items: center;
gap: 6px 10px;
min-width: 0;
}
.pdk_dashboard-page__subscription-meta__heading {
.fkp_dashboard-page__subscription-meta__heading {
flex: 0 0 auto;
color: var(--text-color-high);
font-weight: 700;
@@ -208,7 +208,7 @@ export const styles = `
white-space: nowrap;
}
.pdk_dashboard-page__subscription-meta__title {
.fkp_dashboard-page__subscription-meta__title {
flex: 0 1 auto;
width: max-content;
max-width: min(28ch, 30%);
@@ -219,7 +219,7 @@ export const styles = `
overflow-wrap: anywhere;
}
.pdk_dashboard-page__subscription-meta__facts {
.fkp_dashboard-page__subscription-meta__facts {
flex: 1 1 auto;
min-width: 0;
display: flex;
@@ -228,7 +228,7 @@ export const styles = `
gap: 5px 12px;
}
.pdk_dashboard-page__subscription-meta__fact {
.fkp_dashboard-page__subscription-meta__fact {
display: flex;
align-items: baseline;
gap: 4px;
@@ -236,19 +236,19 @@ export const styles = `
line-height: 1.25;
}
.pdk_dashboard-page__subscription-meta__fact-key {
.fkp_dashboard-page__subscription-meta__fact-key {
color: var(--text-color-medium);
font-size: 12px;
white-space: nowrap;
}
.pdk_dashboard-page__subscription-meta__fact-value {
.fkp_dashboard-page__subscription-meta__fact-value {
color: var(--text-color-high);
font-weight: 600;
overflow-wrap: anywhere;
}
.pdk_dashboard-page__subscription-meta__actions {
.fkp_dashboard-page__subscription-meta__actions {
flex: 0 0 auto;
margin-left: auto;
display: flex;
@@ -256,7 +256,7 @@ export const styles = `
gap: var(--subscription-meta-action-gap);
}
.pdk_dashboard-page .btn.pdk_dashboard-page__subscription-meta__action {
.fkp_dashboard-page .btn.fkp_dashboard-page__subscription-meta__action {
width: var(--subscription-meta-action-size);
height: var(--subscription-meta-action-size);
min-width: var(--subscription-meta-action-size);
@@ -271,14 +271,14 @@ export const styles = `
margin: 0;
}
.pdk_dashboard-page__subscription-meta__action svg {
.fkp_dashboard-page__subscription-meta__action svg {
width: 15px;
height: 15px;
display: block;
flex: 0 0 auto;
}
.pdk_dashboard-page__subscription-meta__announce {
.fkp_dashboard-page__subscription-meta__announce {
margin: 6px 0 0;
border-left: 3px solid var(--primary-color-medium, dodgerblue);
padding: 4px 8px;
@@ -290,31 +290,31 @@ export const styles = `
}
@media (max-width: 700px) {
.pdk_dashboard-page__subscription-meta__main {
.fkp_dashboard-page__subscription-meta__main {
align-items: flex-start;
flex-wrap: wrap;
}
.pdk_dashboard-page__subscription-meta__heading,
.pdk_dashboard-page__subscription-meta__title {
.fkp_dashboard-page__subscription-meta__heading,
.fkp_dashboard-page__subscription-meta__title {
order: 1;
}
.pdk_dashboard-page__subscription-meta__actions {
.fkp_dashboard-page__subscription-meta__actions {
order: 2;
}
.pdk_dashboard-page__subscription-meta__facts {
.fkp_dashboard-page__subscription-meta__facts {
order: 3;
flex-basis: 100%;
}
.pdk_dashboard-page__subscription-meta__title {
.fkp_dashboard-page__subscription-meta__title {
max-width: calc(100% - 42px);
}
}
.pdk_dashboard-page__outbound-grid__item {
.fkp_dashboard-page__outbound-grid__item {
border: 2px var(--background-color-low, lightgray) solid;
border-radius: 4px;
padding: 10px;
@@ -323,29 +323,29 @@ export const styles = `
position: relative;
}
.pdk_dashboard-page__outbound-grid__item--selectable {
.fkp_dashboard-page__outbound-grid__item--selectable {
cursor: pointer;
}
.pdk_dashboard-page__outbound-grid__item--selectable:hover {
.fkp_dashboard-page__outbound-grid__item--selectable:hover {
border-color: var(--primary-color-high, dodgerblue);
}
.pdk_dashboard-page__outbound-grid__item--active {
.fkp_dashboard-page__outbound-grid__item--active {
border-color: var(--success-color-medium, green);
}
.pdk_dashboard-page__outbound-grid__item--disabled {
.fkp_dashboard-page__outbound-grid__item--disabled {
cursor: default;
}
.pdk_dashboard-page__outbound-grid__item--switching {
.fkp_dashboard-page__outbound-grid__item--switching {
border-color: transparent !important;
overflow: hidden;
cursor: wait;
}
.pdk_dashboard-page__outbound-grid__item__snake {
.fkp_dashboard-page__outbound-grid__item__snake {
position: absolute;
top: 0;
left: 0;
@@ -356,13 +356,13 @@ export const styles = `
box-sizing: border-box;
}
.pdk_dashboard-page__outbound-grid__item__snake rect {
.fkp_dashboard-page__outbound-grid__item__snake rect {
stroke: var(--primary-color-high, dodgerblue);
stroke-width: 4;
animation: pdk-dashboard-selector-snake-svg 1.2s linear infinite;
animation: fkp-dashboard-selector-snake-svg 1.2s linear infinite;
}
@keyframes pdk-dashboard-selector-snake-svg {
@keyframes fkp-dashboard-selector-snake-svg {
0% {
stroke-dasharray: 30 70;
stroke-dashoffset: 100;
@@ -373,20 +373,20 @@ export const styles = `
}
}
.pdk_dashboard-page__outbound-grid__item__header {
.fkp_dashboard-page__outbound-grid__item__header {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 8px;
}
.pdk_dashboard-page__outbound-grid__item__header b {
.fkp_dashboard-page__outbound-grid__item__header b {
min-width: 0;
line-height: 1.25;
overflow-wrap: anywhere;
}
.pdk_dashboard-page .btn.pdk_dashboard-page__outbound-grid__item__copy-button {
.fkp_dashboard-page .btn.fkp_dashboard-page__outbound-grid__item__copy-button {
width: 22px;
height: 22px;
min-width: 22px;
@@ -400,14 +400,14 @@ export const styles = `
line-height: 1;
}
.pdk_dashboard-page__outbound-grid__item__copy-button svg {
.fkp_dashboard-page__outbound-grid__item__copy-button svg {
width: 13px;
height: 13px;
display: block;
flex: 0 0 auto;
}
.pdk_dashboard-page__outbound-grid__item__footer {
.fkp_dashboard-page__outbound-grid__item__footer {
display: flex;
align-items: center;
justify-content: space-between;
@@ -415,51 +415,51 @@ export const styles = `
margin-top: 10px;
}
.pdk_dashboard-page__outbound-grid__item__type {
.fkp_dashboard-page__outbound-grid__item__type {
min-width: 0;
overflow-wrap: anywhere;
}
.pdk_dashboard-page__outbound-grid__item__latency--empty {
.fkp_dashboard-page__outbound-grid__item__latency--empty {
color: var(--primary-color-low, lightgray);
}
.pdk_dashboard-page__outbound-grid__item__latency--green {
.fkp_dashboard-page__outbound-grid__item__latency--green {
color: var(--success-color-medium, green);
}
.pdk_dashboard-page__outbound-grid__item__latency--yellow {
.fkp_dashboard-page__outbound-grid__item__latency--yellow {
color: var(--warn-color-medium, orange);
}
.pdk_dashboard-page__outbound-grid__item__latency--red {
.fkp_dashboard-page__outbound-grid__item__latency--red {
color: var(--error-color-medium, red);
}
.pdk_dashboard-page__urltest-details {
.fkp_dashboard-page__urltest-details {
box-sizing: border-box;
width: min(760px, calc(100vw - 56px));
max-width: 100%;
padding-top: 10px;
}
.pdk_dashboard-page__urltest-details__params {
.fkp_dashboard-page__urltest-details__params {
display: grid;
grid-template-columns: minmax(120px, max-content) minmax(0, 1fr);
gap: 8px 16px;
margin: 0 0 18px;
}
.pdk_dashboard-page__urltest-details__param {
.fkp_dashboard-page__urltest-details__param {
display: contents;
}
.pdk_dashboard-page__urltest-details__param dt {
.fkp_dashboard-page__urltest-details__param dt {
color: var(--text-color-medium, #666);
line-height: 1.35;
}
.pdk_dashboard-page__urltest-details__param dd {
.fkp_dashboard-page__urltest-details__param dd {
display: flex;
align-items: center;
gap: 8px;
@@ -467,23 +467,23 @@ export const styles = `
margin: 0;
}
.pdk_dashboard-page__urltest-details__param dd span {
.fkp_dashboard-page__urltest-details__param dd span {
min-width: 0;
overflow-wrap: anywhere;
}
.pdk_dashboard-page__urltest-details__url {
.fkp_dashboard-page__urltest-details__url {
min-width: 0;
color: var(--primary-color-high, #337ab7);
text-decoration: none;
overflow-wrap: anywhere;
}
.pdk_dashboard-page__urltest-details__url:hover {
.fkp_dashboard-page__urltest-details__url:hover {
text-decoration: underline;
}
.pdk_dashboard-page__urltest-details__selected-value {
.fkp_dashboard-page__urltest-details__selected-value {
display: inline-flex;
align-items: center;
gap: 6px;
@@ -497,22 +497,22 @@ export const styles = `
line-height: 1.3;
}
.pdk_dashboard-page__urltest-details__selected-name {
.fkp_dashboard-page__urltest-details__selected-name {
min-width: 0;
font-weight: 600;
overflow-wrap: anywhere;
}
.pdk_dashboard-page__urltest-details__selected-type {
.fkp_dashboard-page__urltest-details__selected-type {
color: var(--text-color-medium, #666);
}
.pdk_dashboard-page__urltest-details__outbounds-title {
.fkp_dashboard-page__urltest-details__outbounds-title {
margin-bottom: 8px;
font-weight: 600;
}
.pdk_dashboard-page__urltest-details__table {
.fkp_dashboard-page__urltest-details__table {
display: grid;
gap: 6px;
width: calc(100% + 14px);
@@ -524,7 +524,7 @@ export const styles = `
scrollbar-gutter: auto;
}
.pdk_dashboard-page__urltest-details__row {
.fkp_dashboard-page__urltest-details__row {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(54px, max-content) 20px;
align-items: center;
@@ -538,13 +538,13 @@ export const styles = `
border-radius: 4px;
}
.pdk_dashboard-page__urltest-details__row--active {
.fkp_dashboard-page__urltest-details__row--active {
border-color: var(--success-color-low, #2d7d46);
background: transparent;
}
.pdk_dashboard-page__urltest-details__row-name,
.pdk_dashboard-page__urltest-details__row-meta {
.fkp_dashboard-page__urltest-details__row-name,
.fkp_dashboard-page__urltest-details__row-meta {
display: flex;
align-items: center;
gap: 6px;
@@ -552,24 +552,24 @@ export const styles = `
line-height: 1.3;
}
.pdk_dashboard-page__urltest-details__row-name {
.fkp_dashboard-page__urltest-details__row-name {
flex-wrap: wrap;
}
.pdk_dashboard-page__urltest-details__row-name b {
.fkp_dashboard-page__urltest-details__row-name b {
min-width: 0;
overflow-wrap: anywhere;
line-height: 1.3;
}
.pdk_dashboard-page__urltest-details__priority-name {
.fkp_dashboard-page__urltest-details__priority-name {
display: inline-flex;
align-items: center;
flex-wrap: wrap;
gap: 2px 0;
}
.pdk_dashboard-page__urltest-details__priority-number {
.fkp_dashboard-page__urltest-details__priority-number {
margin-right: 6px;
color: var(--text-color-medium, #aaa);
font-family: monospace;
@@ -577,7 +577,7 @@ export const styles = `
font-weight: 600;
}
.pdk_dashboard-page__urltest-details__priority-level {
.fkp_dashboard-page__urltest-details__priority-level {
margin-right: 8px;
padding: 2px 6px;
border-radius: 4px;
@@ -587,7 +587,7 @@ export const styles = `
font-weight: 400;
}
.pdk_dashboard-page__urltest-details__country-badge {
.fkp_dashboard-page__urltest-details__country-badge {
display: inline-flex;
align-items: center;
user-select: none;
@@ -599,27 +599,27 @@ export const styles = `
line-height: 1;
}
.pdk_dashboard-page__urltest-details__priority-node {
.fkp_dashboard-page__urltest-details__priority-node {
color: var(--text-color-high, #fff);
font-weight: 600;
}
.pdk_dashboard-page__urltest-details__row-type,
.pdk_dashboard-page__urltest-details__row-meta {
.fkp_dashboard-page__urltest-details__row-type,
.fkp_dashboard-page__urltest-details__row-meta {
color: var(--text-color-medium, #666);
}
.pdk_dashboard-page__urltest-details__row-type {
.fkp_dashboard-page__urltest-details__row-type {
white-space: nowrap;
line-height: 1.3;
}
.pdk_dashboard-page__urltest-details__row-meta {
.fkp_dashboard-page__urltest-details__row-meta {
justify-content: flex-end;
white-space: nowrap;
}
.pdk_dashboard-page__urltest-details__copy-button {
.fkp_dashboard-page__urltest-details__copy-button {
display: inline-flex;
align-items: center;
justify-content: center;
@@ -631,19 +631,19 @@ export const styles = `
box-sizing: border-box;
}
.pdk_dashboard-page__urltest-details__copy-button svg {
.fkp_dashboard-page__urltest-details__copy-button svg {
width: 12px;
height: 12px;
}
.pdk_dashboard-page__urltest-details__copy-placeholder {
.fkp_dashboard-page__urltest-details__copy-placeholder {
display: block;
width: 20px;
min-width: 20px;
height: 1px;
}
.pdk_dashboard-page__urltest-details__empty {
.fkp_dashboard-page__urltest-details__empty {
margin-top: 4px;
padding: 24px 0;
border: 1px dashed var(--border-color-high, #555);
@@ -654,22 +654,22 @@ export const styles = `
text-align: center;
}
.pdk_dashboard-page__urltest-details__footer {
.fkp_dashboard-page__urltest-details__footer {
display: flex;
justify-content: flex-end;
margin-top: 14px;
}
@media (max-width: 560px) {
.pdk_dashboard-page__urltest-details__params {
.fkp_dashboard-page__urltest-details__params {
grid-template-columns: 1fr;
}
.pdk_dashboard-page__urltest-details__row {
.fkp_dashboard-page__urltest-details__row {
grid-template-columns: minmax(0, 1fr) 20px;
}
.pdk_dashboard-page__urltest-details__row-meta {
.fkp_dashboard-page__urltest-details__row-meta {
grid-column: 1 / -1;
justify-content: flex-start;
}
@@ -1,9 +1,9 @@
import type { Podkop } from '../../../types';
import type { Forkop } from '../../../types';
import { getMeta } from '../helpers/getMeta';
type DnsCheckState = 'error' | 'success' | 'warning';
export function getDnsCheckPresentation(data: Podkop.DnsCheckResult) {
export function getDnsCheckPresentation(data: Forkop.DnsCheckResult) {
const dhcpManagedManually = Boolean(data.dont_touch_dhcp);
const dhcpCheckOk = dhcpManagedManually || Boolean(data.dhcp_config_status);
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods } from '../../../methods';
import { ForkopShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { IDiagnosticsChecksItem } from '../../../services';
import { getCheckItemsMeta } from './getCheckItemsMeta';
@@ -16,7 +16,7 @@ export async function runByedpiCheck() {
items: [],
});
const byedpiStatus = await PodkopShellMethods.getByedpiStatus();
const byedpiStatus = await ForkopShellMethods.getByedpiStatus();
if (!byedpiStatus.success) {
updateCheckStore({
@@ -40,7 +40,7 @@ export async function runByedpiCheck() {
const supervisorProcesses = Number(data.supervisor_process_count || 0);
const restartCount = Number(data.restart_count || 0);
const runtimeUnstable = Boolean(data.runtime_unstable);
const podkopRuntimeReady =
const forkopRuntimeReady =
!hasByedpiRules ||
(runningProcesses === expectedProcesses &&
supervisorProcesses === expectedProcesses);
@@ -81,20 +81,20 @@ export async function runByedpiCheck() {
},
{
state:
unexpectedRuntime || !podkopRuntimeReady
unexpectedRuntime || !forkopRuntimeReady
? 'error'
: runtimeUnstable
? 'warning'
: 'success',
key: hasByedpiRules
? runtimeUnstable
? _('Podkop Plus-managed ciadpi runtime has restarted')
: podkopRuntimeReady
? _('Podkop Plus-managed ciadpi runtime is ready')
: _('Podkop Plus-managed ciadpi runtime is not ready')
? _('Forkop-managed ciadpi runtime has restarted')
: forkopRuntimeReady
? _('Forkop-managed ciadpi runtime is ready')
: _('Forkop-managed ciadpi runtime is not ready')
: unexpectedRuntime
? _('Unexpected Podkop Plus-managed ciadpi runtime is running')
: _('Podkop Plus-managed ciadpi runtime is not running'),
? _('Unexpected Forkop-managed ciadpi runtime is running')
: _('Forkop-managed ciadpi runtime is not running'),
value: hasByedpiRules
? runtimeUnstable
? `${restartCount}`
@@ -116,9 +116,7 @@ export async function runByedpiCheck() {
: 'success',
key: standaloneServiceRunning
? hasByedpiRules
? _(
'Standalone ByeDPI is active together with Podkop Plus ByeDPI rules',
)
? _('Standalone ByeDPI is active together with Forkop ByeDPI rules')
: _('Standalone ByeDPI service is active')
: standaloneAutostartRisk
? _('Standalone ByeDPI autostart is enabled')
@@ -1,6 +1,6 @@
import { insertIf } from '../../../../helpers';
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods } from '../../../methods';
import { ForkopShellMethods } from '../../../methods';
import type { IDiagnosticsChecksItem } from '../../../services';
import { updateCheckStore } from './updateCheckStore';
import { getDnsCheckPresentation } from './getDnsCheckPresentation';
@@ -17,7 +17,7 @@ export async function runDnsCheck() {
items: [],
});
const dnsChecks = await PodkopShellMethods.checkDNSAvailable();
const dnsChecks = await ForkopShellMethods.checkDNSAvailable();
if (!dnsChecks.success) {
updateCheckStore({
@@ -1,6 +1,6 @@
import { insertIf } from '../../../../helpers';
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods, RemoteFakeIPMethods } from '../../../methods';
import { ForkopShellMethods, RemoteFakeIPMethods } from '../../../methods';
import type { IDiagnosticsChecksItem } from '../../../services';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
@@ -17,7 +17,7 @@ export async function runFakeIPCheck() {
items: [],
});
const routerFakeIPResponse = await PodkopShellMethods.checkFakeIP();
const routerFakeIPResponse = await ForkopShellMethods.checkFakeIP();
const checkFakeIPResponse = await RemoteFakeIPMethods.getFakeIpCheck();
const checkIPResponse = await RemoteFakeIPMethods.getIpCheck();
const browserFakeIPCheckUnavailable = !checkFakeIPResponse.success;
@@ -1,20 +1,20 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods } from '../../../methods';
import { ForkopShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
import { IDiagnosticsChecksItem } from '../../../services';
import { Podkop } from '../../../types';
import { Forkop } from '../../../types';
function serverPrefix(item: Podkop.InboundCheckItem) {
function serverPrefix(item: Forkop.InboundCheckItem) {
return `${item.label}:`;
}
function formatListen(item: Podkop.InboundCheckItem) {
function formatListen(item: Forkop.InboundCheckItem) {
return `${item.listen}:${Number(item.listen_port || 0)} [${item.required_proto}]`;
}
function getPortConflictItem(
item: Podkop.InboundCheckItem,
item: Forkop.InboundCheckItem,
): IDiagnosticsChecksItem {
const key = `${serverPrefix(item)} ${_('Port conflict')}`;
@@ -34,7 +34,7 @@ function getPortConflictItem(
}
function getPublicHostItem(
item: Podkop.InboundCheckItem,
item: Forkop.InboundCheckItem,
wanIp: string,
): IDiagnosticsChecksItem {
const key = `${serverPrefix(item)} ${_('Public host')}`;
@@ -79,7 +79,7 @@ function getPublicHostItem(
}
function getServerItems(
item: Podkop.InboundCheckItem,
item: Forkop.InboundCheckItem,
wanIp: string,
): IDiagnosticsChecksItem[] {
const prefix = serverPrefix(item);
@@ -162,7 +162,7 @@ export async function runInboundsCheck() {
items: [],
});
const inboundsChecks = await PodkopShellMethods.checkInbounds();
const inboundsChecks = await ForkopShellMethods.checkInbounds();
if (!inboundsChecks.success) {
updateCheckStore({
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { RemoteFakeIPMethods, PodkopShellMethods } from '../../../methods';
import { RemoteFakeIPMethods, ForkopShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
@@ -18,7 +18,7 @@ export async function runNftCheck() {
await RemoteFakeIPMethods.getFakeIpCheck();
await RemoteFakeIPMethods.getIpCheck();
const nftablesChecks = await PodkopShellMethods.checkNftRules();
const nftablesChecks = await ForkopShellMethods.checkNftRules();
if (!nftablesChecks.success) {
updateCheckStore({
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods } from '../../../methods';
import { ForkopShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
import { getDashboardSections } from '../../../methods/custom/getDashboardSections';
@@ -71,7 +71,7 @@ export async function runSectionsCheck() {
const isSubscription = section.proxyConfigType === 'subscription';
if (selectedOutbound?.code) {
const latencyProxy = await PodkopShellMethods.getClashApiProxyLatency(
const latencyProxy = await ForkopShellMethods.getClashApiProxyLatency(
selectedOutbound.code,
section.latencyTestTimeout,
);
@@ -91,8 +91,9 @@ export async function runSectionsCheck() {
};
}
const latencyGroup =
await PodkopShellMethods.getClashApiGroupLatency(section.code);
const latencyGroup = await ForkopShellMethods.getClashApiGroupLatency(
section.code,
);
const success = latencyGroup.success && !latencyGroup.data.message;
if (success) {
@@ -124,7 +125,7 @@ export async function runSectionsCheck() {
}
const selectedOutbound = section.outbounds[0];
const latencyProxy = await PodkopShellMethods.getClashApiProxyLatency(
const latencyProxy = await ForkopShellMethods.getClashApiProxyLatency(
section.code,
section.latencyTestTimeout,
);
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods } from '../../../methods';
import { ForkopShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
@@ -15,7 +15,7 @@ export async function runSingBoxCheck() {
items: [],
});
const singBoxChecks = await PodkopShellMethods.checkSingBox();
const singBoxChecks = await ForkopShellMethods.checkSingBox();
if (!singBoxChecks.success) {
updateCheckStore({
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods } from '../../../methods';
import { ForkopShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { IDiagnosticsChecksItem } from '../../../services';
import { getCheckItemsMeta } from './getCheckItemsMeta';
@@ -16,7 +16,7 @@ export async function runZapret2Check() {
items: [],
});
const zapret2Status = await PodkopShellMethods.getZapret2Status();
const zapret2Status = await ForkopShellMethods.getZapret2Status();
if (!zapret2Status.success) {
updateCheckStore({
@@ -39,7 +39,7 @@ export async function runZapret2Check() {
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 =
const forkopRuntimeReady =
!hasZapret2Rules ||
(runningProcesses === expectedProcesses &&
supervisorProcesses === expectedProcesses);
@@ -83,14 +83,14 @@ export async function runZapret2Check() {
value: '',
},
{
state: unexpectedRuntime || !podkopRuntimeReady ? 'error' : 'success',
state: unexpectedRuntime || !forkopRuntimeReady ? 'error' : 'success',
key: hasZapret2Rules
? podkopRuntimeReady
? _('Podkop Plus-managed nfqws2 runtime is ready')
: _('Podkop Plus-managed nfqws2 runtime is not ready')
? forkopRuntimeReady
? _('Forkop-managed nfqws2 runtime is ready')
: _('Forkop-managed nfqws2 runtime is not ready')
: unexpectedRuntime
? _('Unexpected Podkop Plus-managed nfqws2 runtime is running')
: _('Podkop Plus-managed nfqws2 runtime is not running'),
? _('Unexpected Forkop-managed nfqws2 runtime is running')
: _('Forkop-managed nfqws2 runtime is not running'),
value: hasZapret2Rules ? `${runningProcesses}/${expectedProcesses}` : '',
},
{
@@ -115,9 +115,7 @@ export async function runZapret2Check() {
: 'success',
key: standaloneServiceRunning
? hasZapret2Rules
? _(
'Standalone Zapret2 is active together with Podkop Plus Zapret2 rules',
)
? _('Standalone Zapret2 is active together with Forkop Zapret2 rules')
: _('Standalone Zapret2 service is active')
: standaloneAutostartRisk
? _('Standalone Zapret2 autostart is enabled')
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { PodkopShellMethods } from '../../../methods';
import { ForkopShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { IDiagnosticsChecksItem } from '../../../services';
import { getCheckItemsMeta } from './getCheckItemsMeta';
@@ -16,7 +16,7 @@ export async function runZapretCheck() {
items: [],
});
const zapretStatus = await PodkopShellMethods.getZapretStatus();
const zapretStatus = await ForkopShellMethods.getZapretStatus();
if (!zapretStatus.success) {
updateCheckStore({
@@ -41,7 +41,7 @@ export async function runZapretCheck() {
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 =
const forkopRuntimeReady =
!hasZapretRules ||
(runningProcesses === expectedProcesses &&
supervisorProcesses === expectedProcesses);
@@ -80,14 +80,14 @@ export async function runZapretCheck() {
value: '',
},
{
state: unexpectedRuntime || !podkopRuntimeReady ? 'error' : 'success',
state: unexpectedRuntime || !forkopRuntimeReady ? 'error' : 'success',
key: hasZapretRules
? podkopRuntimeReady
? _('Podkop Plus-managed nfqws runtime is ready')
: _('Podkop Plus-managed nfqws runtime is not ready')
? forkopRuntimeReady
? _('Forkop-managed nfqws runtime is ready')
: _('Forkop-managed nfqws runtime is not ready')
: unexpectedRuntime
? _('Unexpected Podkop Plus-managed nfqws runtime is running')
: _('Podkop Plus-managed nfqws runtime is not running'),
? _('Unexpected Forkop-managed nfqws runtime is running')
: _('Forkop-managed nfqws runtime is not running'),
value: hasZapretRules ? `${runningProcesses}/${expectedProcesses}` : '',
},
{
@@ -108,9 +108,7 @@ export async function runZapretCheck() {
state: standaloneConflict ? 'warning' : 'success',
key: standaloneServiceRunning
? hasZapretRules
? _(
'Standalone Zapret is active together with Podkop Plus Zapret rules',
)
? _('Standalone Zapret is active together with Forkop Zapret rules')
: _('Standalone Zapret service is active')
: _('Standalone Zapret service is inactive'),
value: '',
@@ -8,7 +8,7 @@ const mocks = vi.hoisted(() => ({
}));
vi.mock('../../../../methods', () => ({
PodkopShellMethods: {
ForkopShellMethods: {
getZapretStatus: mocks.getZapretStatus,
getZapret2Status: mocks.getZapret2Status,
getByedpiStatus: mocks.getByedpiStatus,
@@ -12,7 +12,7 @@ vi.mock('../../../../methods/custom/getDashboardSections', () => ({
}));
vi.mock('../../../../methods', () => ({
PodkopShellMethods: {
ForkopShellMethods: {
getClashApiProxyLatency: mocks.getClashApiProxyLatency,
getClashApiGroupLatency: mocks.getClashApiGroupLatency,
},
@@ -77,8 +77,8 @@ export const initialDiagnosticStore: Pick<
loading: true,
loaded: false,
providerInfoLoaded: false,
podkop_version: 'loading',
podkop_latest_version: 'loading',
forkop_version: 'loading',
forkop_latest_version: 'loading',
luci_app_version: 'loading',
sing_box_version: 'loading',
sing_box_extended: 0,
@@ -124,8 +124,8 @@ export const initialDiagnosticStore: Pick<
diagnosticsRunAction: { loading: false },
diagnosticsChecks: getDiagnosticsChecks(_('Not running')),
updatesActions: {
podkopCheck: { loading: false },
podkopInstall: { loading: false },
forkopCheck: { loading: false },
forkopInstall: { loading: false },
singBoxCheck: { loading: false },
singBoxInstall: { loading: false },
singBoxInstallExtended: { loading: false },
@@ -143,7 +143,7 @@ export const initialDiagnosticStore: Pick<
byedpiRemove: { loading: false },
},
updatesChecks: {
podkop: { status: null, latest_version: '', release_url: '' },
forkop: { 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: '' },
@@ -2,7 +2,7 @@ import type { StoreType } from '../../services';
import type { DiagnosticsProviderOptions } from './diagnostic.store';
import { DIAGNOSTICS_CHECKS } from './checks/contstants';
const DIAGNOSTIC_RUN_STORAGE_KEY = 'podkop-plus:diagnostic-run:v1';
const DIAGNOSTIC_RUN_STORAGE_KEY = 'forkop:diagnostic-run:v1';
const DIAGNOSTIC_RUN_TTL_MS = 30 * 60 * 1000;
const CHECK_STATES = ['loading', 'warning', 'success', 'error', 'skipped'];
const CHECK_ITEM_STATES = ['error', 'warning', 'success'];
@@ -28,7 +28,7 @@ const SING_BOX_MASKED_KEYS = new Set([
'source_ip_cidr',
]);
const PODKOP_MASK_AFTER_TOKEN = [
const FORKOP_MASK_AFTER_TOKEN = [
'option proxy_string',
'option subscription_url',
'list subscription_urls',
@@ -47,7 +47,7 @@ const PODKOP_MASK_AFTER_TOKEN = [
'option yacd_secret_key',
];
const PODKOP_MASK_AFTER_TOKEN_SPACE = [
const FORKOP_MASK_AFTER_TOKEN_SPACE = [
'option outbound_json',
'list domain',
'list domain_suffix',
@@ -149,11 +149,11 @@ function maskOptionPath(line: string, token: string) {
function maskGlobalCheckLine(line: string) {
let maskedLine = line;
for (const token of PODKOP_MASK_AFTER_TOKEN) {
for (const token of FORKOP_MASK_AFTER_TOKEN) {
maskedLine = maskAfterToken(maskedLine, token);
}
for (const token of PODKOP_MASK_AFTER_TOKEN_SPACE) {
for (const token of FORKOP_MASK_AFTER_TOKEN_SPACE) {
maskedLine = maskAfterTokenSpace(maskedLine, token);
}
@@ -32,14 +32,14 @@ import {
renderRunAction,
renderSystemInfo,
} from './partials';
import { PodkopShellMethods } from '../../methods';
import { ForkopShellMethods } from '../../methods';
import { fetchServicesInfo } from '../../fetchers/fetchServicesInfo';
import { normalizeCompiledVersion } from '../../../helpers/normalizeCompiledVersion';
import { renderModal } from '../../../partials';
import { PODKOP_LUCI_APP_VERSION } from '../../../constants';
import { FORKOP_LUCI_APP_VERSION } from '../../../constants';
import { renderWikiDisclaimer } from './partials/renderWikiDisclaimer';
import { runSectionsCheck } from './checks/runSectionsCheck';
import { Podkop } from '../../types';
import { Forkop } from '../../types';
import {
getAvailableActionsDisabledState,
getServiceTransition,
@@ -156,11 +156,11 @@ function isLocalMutatingServiceActionLoading() {
function isMutatingServiceActionLoading() {
return (
isLocalMutatingServiceActionLoading() ||
isServiceTransitionStatus(store.get().servicesInfoWidget.data.podkopStatus)
isServiceTransitionStatus(store.get().servicesInfoWidget.data.forkopStatus)
);
}
function getPodkopStatusText(running: boolean, enabled: boolean) {
function getForkopStatusText(running: boolean, enabled: boolean) {
if (running) {
return enabled ? 'running & enabled' : 'running but disabled';
}
@@ -168,9 +168,9 @@ function getPodkopStatusText(running: boolean, enabled: boolean) {
return enabled ? 'stopped but enabled' : 'stopped & disabled';
}
function setDisplayedPodkopRunning(running: boolean) {
function setDisplayedForkopRunning(running: boolean) {
const servicesInfoWidget = store.get().servicesInfoWidget;
const enabled = Boolean(servicesInfoWidget.data.podkopEnabled);
const enabled = Boolean(servicesInfoWidget.data.forkopEnabled);
store.set({
servicesInfoWidget: {
@@ -178,8 +178,8 @@ function setDisplayedPodkopRunning(running: boolean) {
loading: false,
data: {
...servicesInfoWidget.data,
podkopRunning: running ? 1 : 0,
podkopStatus: getPodkopStatusText(running, enabled),
forkopRunning: running ? 1 : 0,
forkopStatus: getForkopStatusText(running, enabled),
},
},
});
@@ -232,17 +232,17 @@ async function refreshDiagnosticServicesInfo({
return promise;
}
async function waitForPodkopRunningState(expectedRunning: boolean) {
async function waitForForkopRunningState(expectedRunning: boolean) {
const startedAt = Date.now();
while (Date.now() - startedAt < SERVICE_ACTION_STATUS_TIMEOUT_MS) {
await refreshDiagnosticServicesInfo({ force: true, allowInactive: true });
const podkopRunning = Boolean(
store.get().servicesInfoWidget.data.podkopRunning,
const forkopRunning = Boolean(
store.get().servicesInfoWidget.data.forkopRunning,
);
if (podkopRunning === expectedRunning) {
if (forkopRunning === expectedRunning) {
return true;
}
@@ -274,13 +274,13 @@ function stopServiceActionStateWatcher() {
}
function isVisibleServiceRuntimeAction(
action: Podkop.ServiceActionState['action'],
action: Forkop.ServiceActionState['action'],
): action is ServiceRuntimeAction {
return action === 'restart' || action === 'start' || action === 'stop';
}
function setServiceActionStateLoading(
state: Podkop.ServiceActionState,
state: Forkop.ServiceActionState,
loading: boolean,
) {
if (!isVisibleServiceRuntimeAction(state.action)) {
@@ -290,7 +290,7 @@ function setServiceActionStateLoading(
setDiagnosticActionLoading(state.action, loading);
}
async function followServiceActionState(state: Podkop.ServiceActionState) {
async function followServiceActionState(state: Forkop.ServiceActionState) {
const jobId = state.job_id;
if (!jobId || followedServiceActionJobs.has(jobId)) {
@@ -308,7 +308,7 @@ async function followServiceActionState(state: Podkop.ServiceActionState) {
try {
if (state.running) {
await PodkopShellMethods.waitServiceActionJob(jobId);
await ForkopShellMethods.waitServiceActionJob(jobId);
}
} catch (error) {
logger.error('[DIAGNOSTIC]', 'followServiceActionState failed', error);
@@ -316,13 +316,13 @@ async function followServiceActionState(state: Podkop.ServiceActionState) {
handledServiceActionJobs.add(jobId);
setServiceActionStateLoading(state, false);
await refreshDiagnosticServicesInfo({ force: true, allowInactive: true });
void PodkopShellMethods.uiActionAck('service', jobId);
void ForkopShellMethods.uiActionAck('service', jobId);
followedServiceActionJobs.delete(jobId);
resetDiagnosticsChecks();
}
}
function followServiceActionsFromUiState(uiState?: Podkop.UiState) {
function followServiceActionsFromUiState(uiState?: Forkop.UiState) {
if (!uiState) {
return;
}
@@ -411,10 +411,10 @@ async function fetchDiagnosticsProviderInfo({
const [zapretRuntime, zapret2Runtime, byedpiRuntime, inboundsConfig] =
await Promise.all([
PodkopShellMethods.checkZapretRuntime(),
PodkopShellMethods.checkZapret2Runtime(),
PodkopShellMethods.checkByedpiRuntime(),
PodkopShellMethods.checkInboundsConfig(),
ForkopShellMethods.checkZapretRuntime(),
ForkopShellMethods.checkZapret2Runtime(),
ForkopShellMethods.checkByedpiRuntime(),
ForkopShellMethods.checkInboundsConfig(),
]);
if (requestId !== latestProviderInfoRequestId) {
@@ -514,7 +514,7 @@ function renderDiagnosticsChecks() {
const diagnosticsChecks = [...store.get().diagnosticsChecks].sort(
(a, b) => a.order - b.order,
);
const container = document.getElementById('pdk_diagnostic-page-checks');
const container = document.getElementById('fkp_diagnostic-page-checks');
const renderedDiagnosticsChecks = diagnosticsChecks.map((check) =>
renderCheckSection(check),
@@ -532,15 +532,15 @@ function renderDiagnosticRunActionWidget() {
const providerInfoLoaded =
store.get().diagnosticsSystemInfo.providerInfoLoaded;
const servicesInfoWidget = store.get().servicesInfoWidget;
const podkopRunning = Boolean(servicesInfoWidget.data.podkopRunning);
const container = document.getElementById('pdk_diagnostic-page-run-check');
const forkopRunning = Boolean(servicesInfoWidget.data.forkopRunning);
const container = document.getElementById('fkp_diagnostic-page-run-check');
const renderedAction = renderRunAction({
loading,
disabled: shouldDisableDiagnosticRunAction({
providerInfoLoaded,
servicesInfoLoading: servicesInfoWidget.loading,
podkopRunning,
forkopRunning,
mutatingServiceActionLoading: isMutatingServiceActionLoading(),
}),
click: () => runChecks(),
@@ -566,11 +566,11 @@ async function handleServiceRuntimeAction({
let delegatedToWatcher = false;
if (optimisticRunning !== undefined) {
setDisplayedPodkopRunning(optimisticRunning);
setDisplayedForkopRunning(optimisticRunning);
}
try {
const startResponse = await PodkopShellMethods.serviceActionStart(action);
const startResponse = await ForkopShellMethods.serviceActionStart(action);
if (!startResponse.success) {
throw new Error(startResponse.error);
@@ -584,7 +584,7 @@ async function handleServiceRuntimeAction({
followedServiceActionJobs.add(jobId);
ownsJobFollow = true;
const result = await PodkopShellMethods.waitServiceActionJob(jobId);
const result = await ForkopShellMethods.waitServiceActionJob(jobId);
if (!result.success) {
throw new Error(result.error);
@@ -594,7 +594,7 @@ async function handleServiceRuntimeAction({
throw new Error(result.data.message || _('Service action failed'));
}
await waitForPodkopRunningState(expectedRunning);
await waitForForkopRunningState(expectedRunning);
} catch (e) {
logger.error('[DIAGNOSTIC]', `handleServiceRuntimeAction(${action})`, e);
} finally {
@@ -607,7 +607,7 @@ async function handleServiceRuntimeAction({
await refreshDiagnosticServicesInfo({ force: true, allowInactive: true });
if (jobId) {
handledServiceActionJobs.add(jobId);
void PodkopShellMethods.uiActionAck('service', jobId);
void ForkopShellMethods.uiActionAck('service', jobId);
}
resetDiagnosticsChecks();
}
@@ -640,7 +640,7 @@ async function handleEnable() {
setDiagnosticActionLoading('enable', true);
try {
await PodkopShellMethods.enable();
await ForkopShellMethods.enable();
} catch (e) {
logger.error('[DIAGNOSTIC]', 'handleEnable - e', e);
} finally {
@@ -656,7 +656,7 @@ async function handleDisable() {
setDiagnosticActionLoading('disable', true);
try {
await PodkopShellMethods.disable();
await ForkopShellMethods.disable();
} catch (e) {
logger.error('[DIAGNOSTIC]', 'handleDisable - e', e);
} finally {
@@ -672,7 +672,7 @@ async function handleShowGlobalCheck() {
setDiagnosticActionLoading('globalCheck', true);
try {
const globalCheck = await PodkopShellMethods.globalCheck(false);
const globalCheck = await ForkopShellMethods.globalCheck(false);
if (globalCheck.success) {
const rawGlobalCheckText = (globalCheck.data as string) ?? '';
@@ -700,11 +700,11 @@ async function handleViewLogs() {
setDiagnosticActionLoading('viewLogs', true);
try {
const viewLogs = await PodkopShellMethods.checkLogs();
const viewLogs = await ForkopShellMethods.checkLogs();
if (viewLogs.success) {
const getLatestLogs = async () => {
const latestLogs = await PodkopShellMethods.checkLogs();
const latestLogs = await ForkopShellMethods.checkLogs();
if (!latestLogs.success) {
throw latestLogs;
@@ -737,7 +737,7 @@ async function handleShowSingBoxConfig() {
setDiagnosticActionLoading('showSingBoxConfig', true);
try {
const showSingBoxConfig = await PodkopShellMethods.showSingBoxConfig(false);
const showSingBoxConfig = await ForkopShellMethods.showSingBoxConfig(false);
if (showSingBoxConfig.success) {
const rawSingBoxConfigText = stringifySingBoxConfig(
@@ -786,7 +786,7 @@ function renderWikiDisclaimerWidget() {
return 'default';
}
const container = document.getElementById('pdk_diagnostic-page-wiki');
const container = document.getElementById('fkp_diagnostic-page-wiki');
return preserveScrollForPage(() => {
container!.replaceChildren(renderWikiDisclaimer(getWikiKind()));
@@ -799,10 +799,10 @@ function renderDiagnosticAvailableActionsWidget() {
const servicesInfoWidget = store.get().servicesInfoWidget;
logger.debug('[DIAGNOSTIC]', 'renderDiagnosticAvailableActionsWidget');
const podkopEnabled = Boolean(servicesInfoWidget.data.podkopEnabled);
const podkopRunning = Boolean(servicesInfoWidget.data.podkopRunning);
const forkopEnabled = Boolean(servicesInfoWidget.data.forkopEnabled);
const forkopRunning = Boolean(servicesInfoWidget.data.forkopRunning);
const serviceTransition = getServiceTransition(
servicesInfoWidget.data.podkopStatus,
servicesInfoWidget.data.forkopStatus,
);
const restartLoading =
diagnosticsActions.restart.loading || serviceTransition.restarting;
@@ -824,25 +824,25 @@ function renderDiagnosticAvailableActionsWidget() {
componentActionLoading,
});
const startVisible = shouldShowStartAction({
podkopRunning,
forkopRunning,
restartLoading,
startLoading,
stopLoading,
});
const stopVisible = shouldShowStopAction({
podkopRunning,
forkopRunning,
restartLoading,
startLoading,
stopLoading,
});
const container = document.getElementById('pdk_diagnostic-page-actions');
const container = document.getElementById('fkp_diagnostic-page-actions');
const renderedActions = renderAvailableActions({
restart: {
loading: restartLoading,
visible: shouldShowRestartAction({
podkopRunning,
forkopRunning,
restartLoading,
startLoading,
stopLoading,
@@ -864,13 +864,13 @@ function renderDiagnosticAvailableActionsWidget() {
},
enable: {
loading: diagnosticsActions.enable.loading,
visible: !podkopEnabled,
visible: !forkopEnabled,
onClick: handleEnable,
disabled: serviceControlsDisabled,
},
disable: {
loading: diagnosticsActions.disable.loading,
visible: podkopEnabled,
visible: forkopEnabled,
onClick: handleDisable,
disabled: serviceControlsDisabled,
},
@@ -903,16 +903,16 @@ function renderDiagnosticSystemInfoWidget() {
logger.debug('[DIAGNOSTIC]', 'renderDiagnosticSystemInfoWidget');
const diagnosticsSystemInfo = store.get().diagnosticsSystemInfo;
const container = document.getElementById('pdk_diagnostic-page-system-info');
const container = document.getElementById('fkp_diagnostic-page-system-info');
const items = [
{
key: 'Podkop Plus',
value: normalizeCompiledVersion(diagnosticsSystemInfo.podkop_version),
key: 'Forkop',
value: normalizeCompiledVersion(diagnosticsSystemInfo.forkop_version),
},
{
key: 'Luci App',
value: normalizeCompiledVersion(PODKOP_LUCI_APP_VERSION),
value: normalizeCompiledVersion(FORKOP_LUCI_APP_VERSION),
},
{
key: 'Sing-box',

Some files were not shown because too many files have changed in this diff Show More