refactor(rebrand): полный ребрендинг в Tachyon и перенос служб watchdog/doctor на Ucode

This commit is contained in:
Dushnilin
2026-07-13 14:09:38 +03:00
parent e89a795566
commit f57dac6b52
203 changed files with 7756 additions and 7063 deletions
+4 -4
View File
@@ -11,8 +11,8 @@ body:
Спасибо за создание отчета об ошибке!
Перед отправкой, пожалуйста:
- Проверьте [существующие issues](https://github.com/ushan0v/forkop/issues)
- Просмотрите [README проекта](https://github.com/ushan0v/forkop#readme)
- Проверьте [существующие issues](https://github.com/ushan0v/tachyon/issues)
- Просмотрите [README проекта](https://github.com/ushan0v/tachyon#readme)
- type: textarea
id: description
@@ -53,7 +53,7 @@ body:
Информация о вашей системе (заполните всё применимое)
value: |
- **OpenWrt версия**:
- **Forkop версия**:
- **Tachyon версия**:
- **Роутер модель**:
- **Sing-box версия**:
render: markdown
@@ -68,7 +68,7 @@ body:
Релевантные части конфигурации (удалите чувствительную информацию!)
placeholder: |
Например:
- Содержимое /etc/config/forkop
- Содержимое /etc/config/tachyon
- Конфигурация 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/forkop
url: https://github.com/ushan0v/tachyon
about: README проекта
- name: 📚 Если вы не нашли в README документацию, то вот ссылка на неё
url: https://github.com/ushan0v/forkop#readme
about: README Forkop
url: https://github.com/ushan0v/tachyon#readme
about: README Tachyon
+5 -5
View File
@@ -1,6 +1,6 @@
---
name: ✨ Запрос новой функции
description: Предложите новую функцию или улучшение для Forkop
description: Предложите новую функцию или улучшение для Tachyon
title: "[FEATURE] "
labels: ["enhancement", "needs-discussion"]
assignees: []
@@ -11,14 +11,14 @@ body:
Спасибо за предложение новой функции!
Перед отправкой, пожалуйста:
- Проверьте [существующие запросы](https://github.com/ushan0v/forkop/issues?q=is%3Aissue+label%3Aenhancement)
- Убедитесь, что функции не существует в [README проекта](https://github.com/ushan0v/forkop#readme)
- Проверьте [существующие запросы](https://github.com/ushan0v/tachyon/issues?q=is%3Aissue+label%3Aenhancement)
- Убедитесь, что функции не существует в [README проекта](https://github.com/ushan0v/tachyon#readme)
- type: textarea
id: summary
attributes:
label: Краткое описание
description: Краткое описание предлагаемой функции для Forkop
description: Краткое описание предлагаемой функции для Tachyon
placeholder: В одном предложении опишите, что вы хотите добавить...
validations:
required: true
@@ -40,7 +40,7 @@ body:
label: 💡 Предлагаемое решение
description: Четкое и краткое описание того, что вы хотите реализовать
placeholder: |
Я хочу, чтобы Forkop мог [...]
Я хочу, чтобы Tachyon мог [...]
Предлагаю добавить функцию, которая [...]
Можно было бы улучшить [...] путем [...]
validations:
+6 -6
View File
@@ -3,8 +3,8 @@ name: Backend CI
on:
pull_request:
paths:
- 'forkop/files/**'
- 'forkop/Makefile'
- 'tachyon/files/**'
- 'tachyon/Makefile'
- 'build.sh'
- 'tests/**'
- '.github/workflows/backend-ci.yml'
@@ -12,8 +12,8 @@ on:
branches:
- main
paths:
- 'forkop/files/**'
- 'forkop/Makefile'
- 'tachyon/files/**'
- 'tachyon/Makefile'
- 'build.sh'
- 'tests/**'
- '.github/workflows/backend-ci.yml'
@@ -48,8 +48,8 @@ jobs:
- name: Check ucode syntax
shell: bash
run: |
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
find tachyon/files/usr/lib -name '*.uc' -print0 | xargs -0 -n1 ucode -c -o /dev/null
find tachyon/files/usr/lib -name '*.uc' -print0 | xargs -0 -n1 ucode -S -c -o /dev/null
- name: Run backend tests
shell: bash
+20 -20
View File
@@ -45,7 +45,7 @@ jobs:
echo "commitish=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
build:
name: Build Forkop release packages
name: Build Tachyon release packages
runs-on: ubuntu-latest
needs: preparation
steps:
@@ -122,12 +122,12 @@ jobs:
mkdir -p "$web_dir/releases"
expected_files=(
"forkop_${VERSION}.ipk"
"forkop_${VERSION}.apk"
"luci-app-forkop_${VERSION}.ipk"
"luci-app-forkop_${VERSION}.apk"
"luci-i18n-forkop-ru_${VERSION}.ipk"
"luci-i18n-forkop-ru_${VERSION}.apk"
"tachyon_${VERSION}.ipk"
"tachyon_${VERSION}.apk"
"luci-app-tachyon_${VERSION}.ipk"
"luci-app-tachyon_${VERSION}.apk"
"luci-i18n-tachyon-ru_${VERSION}.ipk"
"luci-i18n-tachyon-ru_${VERSION}.apk"
)
for file_name in "${expected_files[@]}"; do
test -s "$release_dir/$file_name"
@@ -150,7 +150,7 @@ jobs:
replace_once(
b'https://api.github.com/repos/${owner}/${repo}/releases/latest',
b'https://forkop.sourceforge.io/releases/latest.json',
b'https://tachyon.sourceforge.io/releases/latest.json',
)
replace_once(b'\nSING_BOX_INSTALL_VARIANT=""\n', b'\n')
replace_once(
@@ -211,7 +211,7 @@ jobs:
version = sys.argv[1]
release_dir = pathlib.Path(sys.argv[2])
output_path = pathlib.Path(sys.argv[3])
base_url = f"https://downloads.sourceforge.net/project/forkop/releases/{version}"
base_url = f"https://downloads.sourceforge.net/project/tachyon/releases/{version}"
assets = []
for path in sorted(release_dir.iterdir(), key=lambda item: item.name):
assets.append({
@@ -253,9 +253,9 @@ jobs:
set -euo pipefail
sftp -i "$HOME/.ssh/sourceforge" -b - ushan0v@frs.sourceforge.net <<EOF
-mkdir /home/frs/project/forkop/releases
-mkdir /home/frs/project/forkop/releases/${VERSION}
put $PWD/filtered-bin/release/* /home/frs/project/forkop/releases/${VERSION}/
-mkdir /home/frs/project/tachyon/releases
-mkdir /home/frs/project/tachyon/releases/${VERSION}
put $PWD/filtered-bin/release/* /home/frs/project/tachyon/releases/${VERSION}/
EOF
- name: Verify SourceForge package downloads
@@ -267,7 +267,7 @@ jobs:
for package_path in "$PWD"/filtered-bin/release/*; do
package_name="$(basename "$package_path")"
package_url="https://downloads.sourceforge.net/project/forkop/releases/${VERSION}/${package_name}"
package_url="https://downloads.sourceforge.net/project/tachyon/releases/${VERSION}/${package_name}"
download_path="$(mktemp)"
verified=0
for attempt in $(seq 1 12); do
@@ -293,11 +293,11 @@ jobs:
web_dir="$PWD/filtered-bin/sourceforge-web"
sftp -i "$HOME/.ssh/sourceforge" -b - ushan0v@web.sourceforge.net <<EOF
-mkdir /home/project-web/forkop/htdocs/releases
put ${web_dir}/install.sh /home/project-web/forkop/htdocs/install.sh.new
rename /home/project-web/forkop/htdocs/install.sh.new /home/project-web/forkop/htdocs/install.sh
put ${web_dir}/releases/latest.json /home/project-web/forkop/htdocs/releases/latest.json.new
rename /home/project-web/forkop/htdocs/releases/latest.json.new /home/project-web/forkop/htdocs/releases/latest.json
-mkdir /home/project-web/tachyon/htdocs/releases
put ${web_dir}/install.sh /home/project-web/tachyon/htdocs/install.sh.new
rename /home/project-web/tachyon/htdocs/install.sh.new /home/project-web/tachyon/htdocs/install.sh
put ${web_dir}/releases/latest.json /home/project-web/tachyon/htdocs/releases/latest.json.new
rename /home/project-web/tachyon/htdocs/releases/latest.json.new /home/project-web/tachyon/htdocs/releases/latest.json
EOF
- name: Verify SourceForge installer metadata
@@ -327,11 +327,11 @@ jobs:
}
verify_public_file \
"https://forkop.sourceforge.io/install.sh" \
"https://tachyon.sourceforge.io/install.sh" \
"$web_dir/install.sh" \
"$web_dir/published-install.sh"
verify_public_file \
"https://forkop.sourceforge.io/releases/latest.json" \
"https://tachyon.sourceforge.io/releases/latest.json" \
"$web_dir/releases/latest.json" \
"$web_dir/releases/published-latest.json"
+4 -4
View File
@@ -3,7 +3,7 @@ name: Frontend CI
on:
pull_request:
paths:
- 'fe-app-forkop/**'
- 'fe-app-tachyon/**'
- '.github/workflows/frontend-ci.yml'
jobs:
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-24.04
defaults:
run:
working-directory: fe-app-forkop
working-directory: fe-app-tachyon
steps:
- name: Checkout code
@@ -28,14 +28,14 @@ jobs:
- name: Get yarn cache directory path
id: yarn-cache-dir-path
working-directory: fe-app-forkop
working-directory: fe-app-tachyon
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-forkop/yarn.lock') }}
key: ${{ runner.os }}-yarn-${{ hashFiles('fe-app-tachyon/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
+6 -6
View File
@@ -9,8 +9,8 @@ on:
paths:
- 'build.sh'
- 'install.sh'
- 'forkop/files/etc/init.d/**'
- 'luci-app-forkop/root/etc/uci-defaults/**'
- 'tachyon/files/etc/init.d/**'
- 'luci-app-tachyon/root/etc/uci-defaults/**'
- 'tests/**/*.sh'
- '.github/workflows/shellcheck.yml'
pull_request:
@@ -20,8 +20,8 @@ on:
paths:
- 'build.sh'
- 'install.sh'
- 'forkop/files/etc/init.d/**'
- 'luci-app-forkop/root/etc/uci-defaults/**'
- 'tachyon/files/etc/init.d/**'
- 'luci-app-tachyon/root/etc/uci-defaults/**'
- 'tests/**/*.sh'
- '.github/workflows/shellcheck.yml'
@@ -48,8 +48,8 @@ jobs:
mapfile -d '' scripts < <(
find . -type f \
\( -name '*.sh' -o -path './build.sh' -o -path './install.sh' \
-o -path './forkop/files/etc/init.d/*' \
-o -path './luci-app-forkop/root/etc/uci-defaults/*' \) \
-o -path './tachyon/files/etc/init.d/*' \
-o -path './luci-app-tachyon/root/etc/uci-defaults/*' \) \
-print0
)
shellcheck --severity=error "${scripts[@]}"
+3 -3
View File
@@ -1,7 +1,7 @@
.idea
fe-app-forkop/node_modules
fe-app-forkop/.yarn/
fe-app-forkop/.env
fe-app-tachyon/node_modules
fe-app-tachyon/.yarn/
fe-app-tachyon/.env
.DS_Store
sandbox/
+4 -8
View File
@@ -2,8 +2,6 @@
[![Star](https://img.shields.io/github/stars/Dushnilin/tachyon?style=social)](https://github.com/Dushnilin/tachyon/stargazers)
[![Releases](https://img.shields.io/github/v/release/Dushnilin/tachyon?label=releases)](https://github.com/Dushnilin/tachyon/releases)
[![Telegram Chat](https://img.shields.io/badge/Telegram-Tachyon%20%7C%20Chat-2CA5E0?logo=telegram&logoColor=white)](https://t.me/tachyon_chat)
[![AI Assistant](https://img.shields.io/badge/Telegram-Tachyon%20%7C%20AI%20Assistant-2CA5E0?logo=telegram&logoColor=white)](https://t.me/tachyon_aibot)
> **Tachyon** — это форк проекта **Forkop** (ранее известного как **Podkop Plus**). Проект переименован и развивается как независимое решение для OpenWrt на базе оригинального [Podkop](https://github.com/itdoginfo/podkop).
@@ -13,6 +11,10 @@
sh <(wget -O - https://raw.githubusercontent.com/Dushnilin/tachyon/main/install.sh)
```
> [!NOTE]
> Конфигурация от **Forkop** (или **Podkop Plus**) полностью совместима и автоматически перенесется и переименуется в `/etc/config/tachyon` во время установки!
### Что нового в этом форке
* **Поддержка подписок**: автоматическое управление узлами и их регулярное обновление.
@@ -29,12 +31,6 @@ sh <(wget -O - https://raw.githubusercontent.com/Dushnilin/tachyon/main/install.
* **Интеграция средств обхода**: встроенная поддержка `Zapret`, `Zapret2` и `ByeDPI` как индивидуальных действий для каждого правила.
* **Современный бэкенд**: вся логика службы полностью переписана на `ucode`.
### Документация и поддержка
Отдельной документации со всеми деталями пока нет. Задать вопрос, сообщить об ошибке или обсудить проект можно в [Telegram-чате](https://t.me/tachyon_chat).
Для быстрых ответов на вопросы о настройке и работе проекта вы можете использовать бесплатного AI-ассистента [@tachyon_aibot](https://t.me/tachyon_aibot) в Telegram.
### Поддержать проект
* 💳 **Карты РФ / СБП / Tinkoff Pay:** [Донат на Dalink](https://dalink.to/dushnilin)
+83 -83
View File
@@ -22,22 +22,22 @@ if [[ ! "$RELEASE_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
fi
APK_INTERNAL_VERSION="$RELEASE_VERSION"
WSL_NATIVE_ROOT="${WSL_NATIVE_ROOT:-$DEFAULT_BUILD_HOME/build/forkop}"
WSL_NATIVE_ROOT="${WSL_NATIVE_ROOT:-$DEFAULT_BUILD_HOME/build/tachyon}"
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/forkop/openwrt-sdk}"
SDK_CACHE_DIR="${SDK_CACHE_DIR:-$DEFAULT_BUILD_HOME/.cache/tachyon/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 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)"
BACKEND_DESCRIPTION="Rule-based Tachyon backend with hybrid sing-box + zapret orchestration"
APP_DESCRIPTION="Rule-based Tachyon LuCI app with hybrid sing-box + zapret orchestration"
I18N_DESCRIPTION="Translation for luci-app-tachyon - Русский (Russian)"
MAINTAINER="Dushnilin <dushnilin@gmail.com>"
PROJECT_URL="https://github.com/Dushnilin/tachyon"
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, forkop"
APP_DEPENDS_APK="libc luci-base forkop"
APP_DEPENDS_IPK="libc, luci-base, tachyon"
APP_DEPENDS_APK="libc luci-base tachyon"
APT_PACKAGES=(
build-essential
@@ -70,14 +70,14 @@ copy_to_native_root() {
--exclude "dist" \
--exclude ".idea" \
--exclude "sandbox" \
--exclude "fe-app-forkop/node_modules" \
--exclude "fe-app-forkop/tests" \
--exclude "fe-app-tachyon/node_modules" \
--exclude "fe-app-tachyon/tests" \
"$ROOT_DIR/" "$target_root/"
rm -rf \
"$target_root/.idea" \
"$target_root/sandbox" \
"$target_root/fe-app-forkop/node_modules" \
"$target_root/fe-app-forkop/tests"
"$target_root/fe-app-tachyon/node_modules" \
"$target_root/fe-app-tachyon/tests"
if [[ -n "$OUTPUT_DIR_INPUT" ]]; then
target_output="$OUTPUT_DIR_INPUT"
@@ -172,7 +172,7 @@ extract_sdk() {
local archive_path="$2"
local sdk_url="$3"
local destination="$SDK_WORK_DIR/$kind"
local marker_file="$destination/.forkop-sdk-url"
local marker_file="$destination/.tachyon-sdk-url"
local temp_dir
local extracted_root
@@ -235,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/forkop"
make_dir "$output_root/usr/lib/tachyon"
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/"
install -m 0755 "$ROOT_DIR/tachyon/files/etc/init.d/tachyon" "$output_root/etc/init.d/tachyon"
install -m 0644 "$ROOT_DIR/tachyon/files/etc/config/tachyon" "$output_root/etc/config/tachyon"
install -m 0755 "$ROOT_DIR/tachyon/files/usr/bin/tachyon" "$output_root/usr/bin/tachyon"
cp -a "$ROOT_DIR/tachyon/files/usr/lib/." "$output_root/usr/lib/tachyon/"
sed -i -e "s/__COMPILED_VERSION_VARIABLE__/${RELEASE_VERSION}/g" \
"$output_root/usr/lib/forkop/core/constants.uc"
"$output_root/usr/lib/tachyon/core/constants.uc"
normalize_package_root_modes "$output_root"
chmod 0755 "$output_root/etc/init.d/forkop" "$output_root/usr/bin/forkop"
chmod 0755 "$output_root/etc/init.d/tachyon" "$output_root/usr/bin/tachyon"
}
build_app_root() {
@@ -255,17 +255,17 @@ build_app_root() {
rm -rf "$output_root"
make_dir "$output_root/www"
if [[ -d "$ROOT_DIR/luci-app-forkop/htdocs" ]]; then
cp -a "$ROOT_DIR/luci-app-forkop/htdocs/." "$output_root/www/"
if [[ -d "$ROOT_DIR/luci-app-tachyon/htdocs" ]]; then
cp -a "$ROOT_DIR/luci-app-tachyon/htdocs/." "$output_root/www/"
fi
if [[ -d "$ROOT_DIR/luci-app-forkop/root" ]]; then
cp -a "$ROOT_DIR/luci-app-forkop/root/." "$output_root/"
if [[ -d "$ROOT_DIR/luci-app-tachyon/root" ]]; then
cp -a "$ROOT_DIR/luci-app-tachyon/root/." "$output_root/"
fi
if [[ -f "$output_root/www/luci-static/resources/view/forkop/main.js" ]]; then
if [[ -f "$output_root/www/luci-static/resources/view/tachyon/main.js" ]]; then
sed -i -e "s/__COMPILED_VERSION_VARIABLE__/${RELEASE_VERSION}/g" \
"$output_root/www/luci-static/resources/view/forkop/main.js"
"$output_root/www/luci-static/resources/view/tachyon/main.js"
fi
normalize_package_root_modes "$output_root"
@@ -275,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/forkop.ru.lmo"
local lmo_path="$output_root/usr/lib/lua/luci/i18n/tachyon.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-forkop-ru" <<'EOF'
cat > "$output_root/etc/uci-defaults/luci-i18n-tachyon-ru" <<'EOF'
uci set luci.languages.ru='Русский (Russian)'; uci commit luci
EOF
"$po2lmo_bin" "$ROOT_DIR/luci-app-forkop/po/ru/forkop.po" "$lmo_path"
"$po2lmo_bin" "$ROOT_DIR/luci-app-tachyon/po/ru/tachyon.po" "$lmo_path"
normalize_package_root_modes "$output_root"
find "$output_root/etc/uci-defaults" -type f -exec chmod 0755 {} + 2>/dev/null || true
@@ -326,7 +326,7 @@ write_backend_ipk_control() {
make_dir "$control_dir"
cat > "$control_dir/control" <<EOF
Package: forkop
Package: tachyon
Version: ${RELEASE_VERSION}
Depends: ${BACKEND_DEPENDS_IPK}
License: GPL-2.0-or-later
@@ -339,20 +339,20 @@ Description: ${BACKEND_DESCRIPTION}
EOF
cat > "$control_dir/conffiles" <<'EOF'
/etc/config/forkop
/etc/config/tachyon
EOF
cat > "$control_dir/postinst" <<'EOF'
#!/bin/sh
[ -n "${IPKG_INSTROOT}" ] && exit 0
FORKOP_LIB=/usr/lib/forkop ucode -L /usr/lib/forkop /usr/lib/forkop/config/migration.uc migrate
TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate
EOF
cat > "$control_dir/prerm" <<'EOF'
#!/usr/bin/ucode
if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "")
system("/usr/bin/forkop package_prerm >/dev/null 2>&1");
system("/usr/bin/tachyon package_prerm >/dev/null 2>&1");
exit(0);
EOF
@@ -368,7 +368,7 @@ write_app_ipk_control() {
make_dir "$control_dir"
cat > "$control_dir/control" <<EOF
Package: luci-app-forkop
Package: luci-app-tachyon
Version: ${RELEASE_VERSION}
Depends: ${APP_DEPENDS_IPK}
License: GPL-2.0-or-later
@@ -389,7 +389,7 @@ default_postinst $0 $@
EOF
cat > "$control_dir/postinst-pkg" <<'EOF'
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/forkop luci_postinst >/dev/null 2>&1 || true
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/tachyon luci_postinst >/dev/null 2>&1 || true
EOF
cat > "$control_dir/prerm" <<'EOF'
@@ -410,9 +410,9 @@ write_i18n_ipk_control() {
make_dir "$control_dir"
cat > "$control_dir/control" <<EOF
Package: luci-i18n-forkop-ru
Package: luci-i18n-tachyon-ru
Version: ${RELEASE_VERSION}
Depends: libc, luci-app-forkop
Depends: libc, luci-app-tachyon
License: GPL-2.0-or-later
Section: luci
URL: ${PROJECT_URL}
@@ -494,7 +494,7 @@ EOF
cat > "$scripts_dir/backend-post-install.sh" <<'EOF'
#!/usr/bin/ucode
if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "")
exit(system("FORKOP_LIB=/usr/lib/forkop ucode -L /usr/lib/forkop /usr/lib/forkop/config/migration.uc migrate"));
exit(system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate"));
exit(0);
EOF
@@ -502,7 +502,7 @@ EOF
#!/usr/bin/ucode
if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "")
system("/usr/bin/forkop package_prerm >/dev/null 2>&1");
system("/usr/bin/tachyon package_prerm >/dev/null 2>&1");
exit(0);
EOF
@@ -515,7 +515,7 @@ EOF
cat > "$scripts_dir/backend-post-upgrade.sh" <<'EOF'
#!/usr/bin/ucode
if (getenv("IPKG_INSTROOT") == null || getenv("IPKG_INSTROOT") == "")
exit(system("FORKOP_LIB=/usr/lib/forkop ucode -L /usr/lib/forkop /usr/lib/forkop/config/migration.uc migrate"));
exit(system("TACHYON_LIB=/usr/lib/tachyon ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc migrate"));
exit(0);
EOF
@@ -538,10 +538,10 @@ EOF
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
export root="${IPKG_INSTROOT}"
export pkgname="luci-app-forkop"
export pkgname="luci-app-tachyon"
add_group_and_user
default_postinst
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/forkop luci_postinst >/dev/null 2>&1 || true
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/tachyon luci_postinst >/dev/null 2>&1 || true
EOF
cat > "$scripts_dir/app-pre-deinstall.sh" <<'EOF'
@@ -549,7 +549,7 @@ EOF
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
export root="${IPKG_INSTROOT}"
export pkgname="luci-app-forkop"
export pkgname="luci-app-tachyon"
default_prerm
exit 0
EOF
@@ -566,10 +566,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-forkop"
export pkgname="luci-app-tachyon"
add_group_and_user
default_postinst
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/forkop luci_postinst >/dev/null 2>&1 || true
[ -n "${IPKG_INSTROOT}" ] || /usr/bin/tachyon luci_postinst >/dev/null 2>&1 || true
EOF
chmod 0755 "$scripts_dir"/app-*.sh
@@ -591,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-forkop-ru"
export pkgname="luci-i18n-tachyon-ru"
add_group_and_user
default_postinst
EOF
@@ -601,7 +601,7 @@ EOF
[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0
. ${IPKG_INSTROOT}/lib/functions.sh
export root="${IPKG_INSTROOT}"
export pkgname="luci-i18n-forkop-ru"
export pkgname="luci-i18n-tachyon-ru"
default_prerm
EOF
@@ -617,7 +617,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-forkop-ru"
export pkgname="luci-i18n-tachyon-ru"
add_group_and_user
default_postinst
EOF
@@ -654,7 +654,7 @@ build_apk_package() {
-I "description:${description}" \
-I "arch:noarch" \
-I "license:GPL-2.0-or-later" \
-I "origin:forkop" \
-I "origin:tachyon" \
-I "maintainer:${maintainer}" \
-I "url:${PROJECT_URL}" \
-I "depends:${depends}" \
@@ -673,7 +673,7 @@ build_apk_package() {
-I "description:${description}" \
-I "arch:noarch" \
-I "license:GPL-2.0-or-later" \
-I "origin:forkop" \
-I "origin:tachyon" \
-I "maintainer:${maintainer}" \
-I "url:${PROJECT_URL}" \
-I "depends:${depends}" \
@@ -695,7 +695,7 @@ build_apk_package() {
-I 'description:${description}' \
-I 'arch:noarch' \
-I 'license:GPL-2.0-or-later' \
-I 'origin:forkop' \
-I 'origin:tachyon' \
-I 'maintainer:${maintainer}' \
-I 'url:${PROJECT_URL}' \
-I 'depends:${depends}' \
@@ -717,7 +717,7 @@ build_apk_package() {
-I 'description:${description}' \
-I 'arch:noarch' \
-I 'license:GPL-2.0-or-later' \
-I 'origin:forkop' \
-I 'origin:tachyon' \
-I 'maintainer:${maintainer}' \
-I 'url:${PROJECT_URL}' \
-I 'depends:${depends}' \
@@ -798,17 +798,17 @@ sync_artifacts_to_windows() {
fi
rm -f \
"$WINDOWS_ARTIFACTS_DIR"/forkop_* \
"$WINDOWS_ARTIFACTS_DIR"/luci-app-forkop_* \
"$WINDOWS_ARTIFACTS_DIR"/luci-i18n-forkop-ru_*
"$WINDOWS_ARTIFACTS_DIR"/tachyon_* \
"$WINDOWS_ARTIFACTS_DIR"/luci-app-tachyon_* \
"$WINDOWS_ARTIFACTS_DIR"/luci-i18n-tachyon-ru_*
cp -f \
"$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 \
"$output_dir"/tachyon_"${RELEASE_VERSION}".ipk \
"$output_dir"/luci-app-tachyon_"${RELEASE_VERSION}".ipk \
"$output_dir"/luci-i18n-tachyon-ru_"${RELEASE_VERSION}".ipk \
"$output_dir"/tachyon_"${RELEASE_VERSION}".apk \
"$output_dir"/luci-app-tachyon_"${RELEASE_VERSION}".apk \
"$output_dir"/luci-i18n-tachyon-ru_"${RELEASE_VERSION}".apk \
"$WINDOWS_ARTIFACTS_DIR"/
echo "Synced artifacts to Windows path: $WINDOWS_ARTIFACTS_DIR" >&2
@@ -853,7 +853,7 @@ main() {
mkdir -p "$WORK_DIR"
output_dir="${OUTPUT_DIR_INPUT:-$ROOT_DIR/dist/release-final}"
mkdir -p "$output_dir"
rm -f "$output_dir"/forkop_* "$output_dir"/luci-app-forkop_* "$output_dir"/luci-i18n-forkop-ru_*
rm -f "$output_dir"/tachyon_* "$output_dir"/luci-app-tachyon_* "$output_dir"/luci-i18n-tachyon-ru_*
ipk_archive="$(download_sdk_archive "$IPK_SDK_URL")"
apk_archive="$(download_sdk_archive "$APK_SDK_URL")"
@@ -880,74 +880,74 @@ main() {
build_ipk_package \
"$ipkg_build_bin" \
"forkop" \
"tachyon" \
"$backend_root" \
"$backend_control" \
"$output_dir/forkop_${RELEASE_VERSION}.ipk"
"$output_dir/tachyon_${RELEASE_VERSION}.ipk"
build_ipk_package \
"$ipkg_build_bin" \
"luci-app-forkop" \
"luci-app-tachyon" \
"$app_root" \
"$app_control" \
"$output_dir/luci-app-forkop_${RELEASE_VERSION}.ipk"
"$output_dir/luci-app-tachyon_${RELEASE_VERSION}.ipk"
build_ipk_package \
"$ipkg_build_bin" \
"luci-i18n-forkop-ru" \
"luci-i18n-tachyon-ru" \
"$i18n_root" \
"$i18n_control" \
"$output_dir/luci-i18n-forkop-ru_${RELEASE_VERSION}.ipk"
"$output_dir/luci-i18n-tachyon-ru_${RELEASE_VERSION}.ipk"
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"
generate_apk_metadata_files "tachyon" "$backend_root" "/etc/config/tachyon"
generate_apk_metadata_files "luci-app-tachyon" "$app_root"
generate_apk_metadata_files "luci-i18n-tachyon-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" \
"forkop" \
"tachyon" \
"$APK_INTERNAL_VERSION" \
"$BACKEND_DESCRIPTION" \
"$BACKEND_DEPENDS_APK" \
"$backend_root" \
"$apk_scripts" \
"backend" \
"$output_dir/forkop_${RELEASE_VERSION}.apk" \
"$output_dir/tachyon_${RELEASE_VERSION}.apk" \
"$MAINTAINER"
build_apk_package \
"$apk_bin" \
"luci-app-forkop" \
"luci-app-tachyon" \
"$APK_INTERNAL_VERSION" \
"$APP_DESCRIPTION" \
"$APP_DEPENDS_APK" \
"$app_root" \
"$apk_scripts" \
"app" \
"$output_dir/luci-app-forkop_${RELEASE_VERSION}.apk" \
"$output_dir/luci-app-tachyon_${RELEASE_VERSION}.apk" \
"$MAINTAINER"
build_apk_package \
"$apk_bin" \
"luci-i18n-forkop-ru" \
"luci-i18n-tachyon-ru" \
"$APK_INTERNAL_VERSION" \
"$I18N_DESCRIPTION" \
"libc luci-app-forkop" \
"libc luci-app-tachyon" \
"$i18n_root" \
"$apk_scripts" \
"i18n" \
"$output_dir/luci-i18n-forkop-ru_${RELEASE_VERSION}.apk" \
"$output_dir/luci-i18n-tachyon-ru_${RELEASE_VERSION}.apk" \
"$MAINTAINER"
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"
verify_ipk_metadata "$output_dir/tachyon_${RELEASE_VERSION}.ipk" "tachyon" "$RELEASE_VERSION"
verify_ipk_metadata "$output_dir/luci-app-tachyon_${RELEASE_VERSION}.ipk" "luci-app-tachyon" "$RELEASE_VERSION"
verify_ipk_metadata "$output_dir/luci-i18n-tachyon-ru_${RELEASE_VERSION}.ipk" "luci-i18n-tachyon-ru" "$RELEASE_VERSION"
verify_apk_metadata "$apk_bin" "$output_dir/tachyon_${RELEASE_VERSION}.apk" "tachyon" "$APK_INTERNAL_VERSION"
verify_apk_metadata "$apk_bin" "$output_dir/luci-app-tachyon_${RELEASE_VERSION}.apk" "luci-app-tachyon" "$APK_INTERNAL_VERSION"
verify_apk_metadata "$apk_bin" "$output_dir/luci-i18n-tachyon-ru_${RELEASE_VERSION}.apk" "luci-i18n-tachyon-ru" "$APK_INTERNAL_VERSION"
cleanup_work_dir
sync_artifacts_to_windows "$output_dir"
+5 -5
View File
@@ -6,11 +6,11 @@ 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_FE=../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon
REMOTE_DIR_FE=/www/luci-static/resources/view/tachyon
LOCAL_DIR_BIN=../forkop/files/usr/bin/
LOCAL_DIR_BIN=../tachyon/files/usr/bin/
REMOTE_DIR_BIN=/usr/bin/
LOCAL_DIR_LIB=../forkop/files/usr/lib/
REMOTE_DIR_LIB=/usr/lib/forkop/
LOCAL_DIR_LIB=../tachyon/files/usr/lib/
REMOTE_DIR_LIB=/usr/lib/tachyon/
+5 -5
View File
@@ -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-forkop/po');
const targetRoot = path.resolve(__dirname, '../luci-app-tachyon/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 === 'forkop.pot') {
const potTarget = path.join(targetRoot, 'templates', 'forkop.pot');
if (file === 'tachyon.pot') {
const potTarget = path.join(targetRoot, 'templates', 'tachyon.pot');
await fs.mkdir(path.dirname(potTarget), { recursive: true });
await fs.copyFile(filePath, potTarget);
console.log(`✅ Copied POT: ${filePath} → ${potTarget}`);
}
const match = file.match(/^forkop\.([a-zA-Z_]+)\.po$/);
const match = file.match(/^tachyon\.([a-zA-Z_]+)\.po$/);
if (match) {
const lang = match[1];
const poTarget = path.join(targetRoot, lang, 'forkop.po');
const poTarget = path.join(targetRoot, lang, 'tachyon.po');
await fs.mkdir(path.dirname(poTarget), { recursive: true });
await fs.copyFile(filePath, poTarget);
console.log(`✅ Copied ${lang.toUpperCase()}: ${filePath} → ${poTarget}`);
+2 -2
View File
@@ -19,12 +19,12 @@ function stripIllegalReturn(code) {
const files = await glob([
'src/**/*.ts',
'../luci-app-forkop/htdocs/luci-static/resources/view/forkop/**/*.js',
'../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/**/*.js',
], {
ignore: [
'**/*.test.ts',
'**/main.js',
'../luci-app-forkop/htdocs/luci-static/resources/view/forkop/main.js',
'../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/main.js',
],
absolute: true,
});
+5 -5
View File
@@ -8,7 +8,7 @@ if (!lang) {
}
const callsPath = 'locales/calls.json';
const poPath = `locales/forkop.${lang}.po`;
const poPath = `locales/tachyon.${lang}.po`;
function getHeader(lang) {
const now = new Date();
@@ -28,14 +28,14 @@ function getHeader(lang) {
: 'nplurals=2; plural=(n != 1);';
return [
`# ${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.`,
`# ${lang.toUpperCase()} translations for TACHYON package.`,
`# Copyright (C) ${now.getFullYear()} THE TACHYON COPYRIGHT HOLDER`,
`# This file is distributed under the same license as the TACHYON package.`,
`# ${translator}, ${now.getFullYear()}.`,
'#',
'msgid ""',
'msgstr ""',
`"Project-Id-Version: FORKOP\\n"`,
`"Project-Id-Version: TACHYON\\n"`,
`"Report-Msgid-Bugs-To: \\n"`,
`"POT-Creation-Date: ${date} ${time}${tzOffset}\\n"`,
`"PO-Revision-Date: ${date} ${time}${tzOffset}\\n"`,
+2 -2
View File
@@ -2,8 +2,8 @@ import fs from 'fs/promises';
import { getGitUser } from './locales-utils.js';
const inputFile = 'locales/calls.json';
const outputFile = 'locales/forkop.pot';
const projectId = 'FORKOP';
const outputFile = 'locales/tachyon.pot';
const projectId = 'TACHYON';
function getPotHeader({ name, email }) {
const now = new Date();
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+112 -112
View File
@@ -1,15 +1,15 @@
# 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.
# RU translations for TACHYON package.
# Copyright (C) 2026 THE TACHYON COPYRIGHT HOLDER
# This file is distributed under the same license as the TACHYON package.
# Dushnilin, 2026.
#
msgid ""
msgstr ""
"Project-Id-Version: FORKOP\n"
"Project-Id-Version: TACHYON\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-13 03:22+0300\n"
"PO-Revision-Date: 2026-07-13 03:22+0300\n"
"Last-Translator: ushan0v\n"
"POT-Creation-Date: 2026-07-13 13:18+0300\n"
"PO-Revision-Date: 2026-07-13 13:18+0300\n"
"Last-Translator: Dushnilin\n"
"Language-Team: none\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
@@ -191,8 +191,8 @@ msgstr "Встроенные наборы правил"
msgid "By flag emoji from name"
msgstr "По эмодзи флага из названия"
msgid "ByeDPI listen address and port are assigned by Forkop and must not be set in the strategy."
msgstr "Адрес и порт прослушивания ByeDPI назначаются Forkop и не должны задаваться в стратегии."
msgid "ByeDPI listen address and port are assigned by Tachyon and must not be set in the strategy."
msgstr "Адрес и порт прослушивания ByeDPI назначаются Tachyon и не должны задаваться в стратегии."
msgid "ByeDPI option does not accept a compact value: %s"
msgstr "Опция ByeDPI не принимает компактное значение: %s"
@@ -323,8 +323,8 @@ msgstr "Условия"
msgid "Config File Path"
msgstr "Путь к файлу конфигурации"
msgid "Configuration for Forkop service"
msgstr "Настройки сервиса Forkop"
msgid "Configuration for Tachyon service"
msgstr "Настройки сервиса Tachyon"
msgid "Configure User-Agent in the item settings"
msgstr "Настройте User-Agent в настройках элемента"
@@ -374,8 +374,8 @@ msgstr "Дашборд сейчас недоступен"
msgid "Default address for checking server availability and latency. URLTest uses its own address."
msgstr "Адрес по умолчанию для проверки доступности и задержки серверов. Для URLTest используется собственный адрес."
msgid "Delay in milliseconds before reloading Forkop after interface UP"
msgstr "Задержка в миллисекундах перед перезапуском Forkop после поднятия интерфейса"
msgid "Delay in milliseconds before reloading Tachyon after interface UP"
msgstr "Задержка в миллисекундах перед перезапуском Tachyon после поднятия интерфейса"
msgid "Delay value cannot be empty"
msgstr "Значение задержки не может быть пустым"
@@ -419,8 +419,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 Forkop sends matched traffic through NFQUEUE."
msgstr "Отключение перехвата несовместимо с action=zapret2, потому что Forkop отправляет совпавший трафик через NFQUEUE."
msgid "Disabling interception is incompatible with action=zapret2 because Tachyon sends matched traffic through NFQUEUE."
msgstr "Отключение перехвата несовместимо с action=zapret2, потому что Tachyon отправляет совпавший трафик через NFQUEUE."
msgid "Display name"
msgstr "Отображаемое имя"
@@ -656,11 +656,11 @@ msgstr "Истекает"
msgid "Expose this section as a local HTTP+SOCKS proxy"
msgstr "Открыть эту секцию как локальный HTTP+SOCKS прокси"
msgid "External nfqws config files bypass Forkop queue management and explicit validation."
msgstr "Внешние конфиги nfqws обходят управление очередями и явную валидацию Forkop."
msgid "External nfqws config files bypass Tachyon queue management and explicit validation."
msgstr "Внешние конфиги nfqws обходят управление очередями и явную валидацию Tachyon."
msgid "External nfqws2 config files bypass Forkop queue management and explicit validation."
msgstr "Внешние конфиги nfqws2 обходят управление очередями и явную валидацию Forkop."
msgid "External nfqws2 config files bypass Tachyon queue management and explicit validation."
msgstr "Внешние конфиги nfqws2 обходят управление очередями и явную валидацию Tachyon."
msgid "Failed to close connection"
msgstr "Не удалось закрыть подключение"
@@ -707,63 +707,6 @@ 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 фронтинга"
@@ -773,8 +716,8 @@ msgstr "Порт фронтинга"
msgid "Fronting proxy protocol"
msgstr "Proxy protocol для фронтинга"
msgid "Fuzzing is not supported here because Forkop needs deterministic runtime validation."
msgstr "Fuzzing здесь не поддерживается, потому что Forkop нужна предсказуемая runtime-валидация."
msgid "Fuzzing is not supported here because Tachyon needs deterministic runtime validation."
msgstr "Fuzzing здесь не поддерживается, потому что Tachyon нужна предсказуемая runtime-валидация."
msgid "Generate HWID from router hardware information for this subscription"
msgstr "Генерировать HWID из аппаратной информации роутера для этой подписки"
@@ -1541,8 +1484,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 Forkop"
msgstr "Перезапустить Forkop"
msgid "Restart Tachyon"
msgstr "Перезапустить Tachyon"
msgid "Resume updates"
msgstr "Продолжить обновление"
@@ -1787,8 +1730,8 @@ msgstr "Укажите разные файлы"
msgid "Standalone ByeDPI autostart is enabled"
msgstr "Автозапуск отдельной службы ByeDPI включен"
msgid "Standalone ByeDPI is active together with Forkop ByeDPI rules"
msgstr "Отдельная служба ByeDPI активна вместе с правилами ByeDPI в Forkop"
msgid "Standalone ByeDPI is active together with Tachyon ByeDPI rules"
msgstr "Отдельная служба ByeDPI активна вместе с правилами ByeDPI в Tachyon"
msgid "Standalone ByeDPI service is active"
msgstr "Отдельная служба ByeDPI активна"
@@ -1796,8 +1739,8 @@ msgstr "Отдельная служба ByeDPI активна"
msgid "Standalone ByeDPI service is inactive"
msgstr "Отдельная служба ByeDPI неактивна"
msgid "Standalone Zapret is active together with Forkop Zapret rules"
msgstr "Отдельная служба Zapret активна вместе с правилами Zapret в Forkop"
msgid "Standalone Zapret is active together with Tachyon Zapret rules"
msgstr "Отдельная служба Zapret активна вместе с правилами Zapret в Tachyon"
msgid "Standalone Zapret service is active"
msgstr "Отдельная служба Zapret активна"
@@ -1808,8 +1751,8 @@ msgstr "Отдельная служба Zapret неактивна"
msgid "Standalone Zapret2 autostart is enabled"
msgstr "Автозапуск отдельной службы Zapret2 включён"
msgid "Standalone Zapret2 is active together with Forkop Zapret2 rules"
msgstr "Отдельная служба Zapret2 активна вместе с правилами Zapret2 в Forkop"
msgid "Standalone Zapret2 is active together with Tachyon Zapret2 rules"
msgstr "Отдельная служба Zapret2 активна вместе с правилами Zapret2 в Tachyon"
msgid "Standalone Zapret2 service is active"
msgstr "Отдельная служба Zapret2 активна"
@@ -1817,14 +1760,14 @@ msgstr "Отдельная служба Zapret2 активна"
msgid "Standalone Zapret2 service is inactive"
msgstr "Отдельная служба Zapret2 неактивна"
msgid "Start Forkop"
msgstr "Запустить Forkop"
msgid "Start Tachyon"
msgstr "Запустить Tachyon"
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 Forkop"
msgstr "Остановить Forkop"
msgid "Stop Tachyon"
msgstr "Остановить Tachyon"
msgid "Subnets from the list will be extracted and added to nftables"
msgstr "IP-адреса и подсети из списка будут извлечены и добавлены в nftables"
@@ -1862,6 +1805,63 @@ msgstr "Системная информация"
msgid "Table exist"
msgstr "Таблица существует"
msgid "Tachyon Error"
msgstr "Ошибка Tachyon"
msgid "Tachyon manages ciadpi pid files itself, so pidfile options are not allowed here."
msgstr "Tachyon сам управляет pid-файлами ciadpi, поэтому параметры pidfile здесь запрещены."
msgid "Tachyon manages the ciadpi process lifecycle itself, so daemon mode is not allowed here."
msgstr "Tachyon сам управляет жизненным циклом ciadpi, поэтому режим daemon здесь запрещён."
msgid "Tachyon manages the nfqws process lifecycle itself, so daemon mode is not allowed here."
msgstr "Tachyon сам управляет жизненным циклом nfqws, поэтому режим daemon здесь запрещён."
msgid "Tachyon manages the nfqws2 process lifecycle itself, so daemon mode is not allowed here."
msgstr "Tachyon сам управляет жизненным циклом nfqws2, поэтому режим daemon здесь запрещён."
msgid "Tachyon service is stopped. Start the service to display connections."
msgstr "Служба Tachyon остановлена. Запустите сервис для отображения подключений."
msgid "Tachyon service is stopped. Start the service to display the dashboard."
msgstr "Служба Tachyon остановлена. Запустите сервис для отображения дашборда."
msgid "Tachyon Settings"
msgstr "Настройки Tachyon"
msgid "Tachyon will not modify your DHCP configuration"
msgstr "Tachyon не будет изменять вашу конфигурацию DHCP"
msgid "Tachyon-managed ciadpi runtime has restarted"
msgstr "ciadpi Tachyon перезапускался"
msgid "Tachyon-managed ciadpi runtime is not ready"
msgstr "ciadpi Tachyon не готов"
msgid "Tachyon-managed ciadpi runtime is not running"
msgstr "ciadpi Tachyon не запущен"
msgid "Tachyon-managed ciadpi runtime is ready"
msgstr "ciadpi Tachyon готов"
msgid "Tachyon-managed nfqws runtime is not ready"
msgstr "NFQWS Tachyon не готов"
msgid "Tachyon-managed nfqws runtime is not running"
msgstr "NFQWS Tachyon не запущен"
msgid "Tachyon-managed nfqws runtime is ready"
msgstr "NFQWS Tachyon готов"
msgid "Tachyon-managed nfqws2 runtime is not ready"
msgstr "NFQWS2 Tachyon не готов"
msgid "Tachyon-managed nfqws2 runtime is not running"
msgstr "NFQWS2 Tachyon не запущен"
msgid "Tachyon-managed nfqws2 runtime is ready"
msgstr "NFQWS2 Tachyon готов"
msgid "Tailscale auth key"
msgstr "Ключ авторизации Tailscale"
@@ -1898,11 +1898,11 @@ msgstr "Тестировать серверы, имена которых сов
msgid "Testing URL"
msgstr "URL проверки"
msgid "The desync fwmark is managed by Forkop for loop prevention and must not be overridden here."
msgstr "Desync fwmark управляется Forkop для защиты от циклов и не должен переопределяться здесь."
msgid "The desync fwmark is managed by Tachyon for loop prevention and must not be overridden here."
msgstr "Desync fwmark управляется Tachyon для защиты от циклов и не должен переопределяться здесь."
msgid "The NFQUEUE number is assigned by Forkop for each rule and must not be overridden here."
msgstr "Номер NFQUEUE назначается Forkop для каждого правила и не должен переопределяться здесь."
msgid "The NFQUEUE number is assigned by Tachyon for each rule and must not be overridden here."
msgstr "Номер NFQUEUE назначается Tachyon для каждого правила и не должен переопределяться здесь."
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:."
@@ -1967,8 +1967,8 @@ msgstr "Трафик этого уровня пойдёт напрямую."
msgid "Traffic Total"
msgstr "Всего трафика"
msgid "Transparent proxy mode is incompatible with action=byedpi because Forkop connects to ciadpi through SOCKS."
msgstr "Режим прозрачного прокси несовместим с action=byedpi, потому что Forkop подключается к ciadpi через SOCKS."
msgid "Transparent proxy mode is incompatible with action=byedpi because Tachyon connects to ciadpi through SOCKS."
msgstr "Режим прозрачного прокси несовместим с action=byedpi, потому что Tachyon подключается к ciadpi через SOCKS."
msgid "Transport"
msgstr "Транспорт"
@@ -2015,18 +2015,18 @@ msgstr "Таймаут недоступности"
msgid "Unexpected ByeDPI strategy argument: %s"
msgstr "Неожиданный аргумент стратегии ByeDPI: %s"
msgid "Unexpected Forkop-managed ciadpi runtime is running"
msgstr "Обнаружен лишний ciadpi Forkop"
msgid "Unexpected Forkop-managed nfqws runtime is running"
msgstr "Обнаружен лишний NFQWS Forkop"
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 "Неожиданный отдельный токен. Используйте явные флаги вида --имя или --имя=значение."
msgid "Unexpected Tachyon-managed ciadpi runtime is running"
msgstr "Обнаружен лишний ciadpi Tachyon"
msgid "Unexpected Tachyon-managed nfqws runtime is running"
msgstr "Обнаружен лишний NFQWS Tachyon"
msgid "Unexpected Tachyon-managed nfqws2 runtime is running"
msgstr "Обнаружен лишний NFQWS2 Tachyon"
msgid "unique UCI identifier"
msgstr "уникальный идентификатор UCI"
@@ -2192,8 +2192,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 Forkop should do when this section matches"
msgstr "Что Forkop должен делать при совпадении этой секции"
msgid "What Tachyon should do when this section matches"
msgstr "Что Tachyon должен делать при совпадении этой секции"
msgid "When switching to another level, test every server and select the fastest instead of the first working one."
msgstr "При переходе на другой уровень тестировать все сервера и выбирать самый быстрый, а не первый работающий."
@@ -2210,8 +2210,8 @@ msgstr "Да"
msgid "You can select Output Network Interface, by default autodetect"
msgstr "Вы можете выбрать выходной сетевой интерфейс, по умолчанию он определяется автоматически."
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 hostlist templates are not supported here because Tachyon does not expand them for per-rule NFQWS strategies."
msgstr "Шаблоны hostlist zapret здесь не поддерживаются, потому что Tachyon не разворачивает их для per-rule стратегий NFQWS."
msgid "Zapret package is installed"
msgstr "Пакет Zapret установлен"
@@ -2231,8 +2231,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 Forkop does not expand them for per-rule NFQWS2 strategies."
msgstr "Шаблоны hostlist Zapret2 здесь не поддерживаются, потому что Forkop не разворачивает их для per-rule стратегий NFQWS2."
msgid "Zapret2 hostlist templates are not supported here because Tachyon does not expand them for per-rule NFQWS2 strategies."
msgstr "Шаблоны hostlist Zapret2 здесь не поддерживаются, потому что Tachyon не разворачивает их для per-rule стратегий NFQWS2."
msgid "Zapret2 package is installed"
msgstr "Пакет Zapret2 установлен"
+4 -4
View File
@@ -1,7 +1,7 @@
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 TACHYON_UCI_PACKAGE = 'tachyon';
export const TACHYON_LUCI_APP_VERSION = '__COMPILED_VERSION_VARIABLE__';
export const TACHYON_ACTION_PROVIDERS_AVAILABILITY_EVENT =
'tachyon: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';
@@ -1,16 +1,16 @@
import { GlobalStyles } from '../styles';
const FORKOP_GLOBAL_STYLES_ID = 'forkop-global-styles';
const TACHYON_GLOBAL_STYLES_ID = 'tachyon-global-styles';
export function injectGlobalStyles() {
if (document.getElementById(FORKOP_GLOBAL_STYLES_ID)) {
if (document.getElementById(TACHYON_GLOBAL_STYLES_ID)) {
return;
}
document.head.insertAdjacentHTML(
'beforeend',
`
<style id="${FORKOP_GLOBAL_STYLES_ID}">
<style id="${TACHYON_GLOBAL_STYLES_ID}">
${GlobalStyles}
</style>
`,
@@ -7,7 +7,7 @@ import { describe, expect, it } from 'vitest';
const testDir = path.dirname(fileURLToPath(import.meta.url));
const serverViewPath = path.resolve(
testDir,
'../../../../luci-app-forkop/htdocs/luci-static/resources/view/forkop/server.js',
'../../../../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon/server.js',
);
function loadBuildHysteria2Link(values) {
@@ -52,7 +52,7 @@ function loadBuildHysteria2Link(values) {
},
};
return factory(uci, 'forkop', { location: { hostname: 'router.lan' } });
return factory(uci, 'tachyon', { location: { hostname: 'router.lan' } });
}
describe('buildHysteria2Link', () => {
+1 -1
View File
@@ -1,4 +1,4 @@
import { logger } from '../forkop/services/logger.service';
import { logger } from '../tachyon/services/logger.service';
export async function withTimeout<T>(
promise: Promise<T>,
+10 -10
View File
@@ -25,14 +25,14 @@ export { getProxyUrlName } from './helpers/getProxyUrlName';
export { injectGlobalStyles } from './helpers/injectGlobalStyles';
export { showToast } from './helpers/showToast';
export { getClashUIUrl } from './helpers/getClashApiUrl';
export { ForkopShellMethods } from './forkop/methods/shell';
export { coreService } from './forkop/services/core.service';
export { store } from './forkop/services/store.service';
export { applyUiStateToStore } from './forkop/services/uiState.service';
export { DashboardTab } from './forkop/tabs/dashboard';
export { DiagnosticTab } from './forkop/tabs/diagnostic';
export { MonitoringTab } from './forkop/tabs/monitoring';
export { UpdatesTab } from './forkop/tabs/updates';
export { TachyonShellMethods } from './tachyon/methods/shell';
export { coreService } from './tachyon/services/core.service';
export { store } from './tachyon/services/store.service';
export { applyUiStateToStore } from './tachyon/services/uiState.service';
export { DashboardTab } from './tachyon/tabs/dashboard';
export { DiagnosticTab } from './tachyon/tabs/diagnostic';
export { MonitoringTab } from './tachyon/tabs/monitoring';
export { UpdatesTab } from './tachyon/tabs/updates';
export {
ALLOWED_WITH_RUSSIA_INSIDE,
BOOTSTRAP_DNS_SERVER_OPTIONS,
@@ -40,7 +40,7 @@ export {
DNS_SERVER_OPTIONS,
DOMAIN_LIST_OPTIONS,
LATENCY_TEST_URL_OPTIONS,
FORKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT,
FORKOP_UCI_PACKAGE,
TACHYON_ACTION_PROVIDERS_AVAILABILITY_EVENT,
TACHYON_UCI_PACKAGE,
REGIONAL_OPTIONS,
} from './constants';
+18 -18
View File
@@ -1,10 +1,10 @@
// language=CSS
import { DashboardTab } from './forkop/tabs/dashboard';
import { DiagnosticTab } from './forkop/tabs/diagnostic';
import { MonitoringTab } from './forkop/tabs/monitoring';
import { UpdatesTab } from './forkop/tabs/updates';
import { DashboardTab } from './tachyon/tabs/dashboard';
import { DiagnosticTab } from './tachyon/tabs/diagnostic';
import { MonitoringTab } from './tachyon/tabs/monitoring';
import { UpdatesTab } from './tachyon/tabs/updates';
import { PartialStyles } from './partials';
import { FORKOP_UCI_PACKAGE as FORKOP_CBI_PREFIX } from './constants';
import { TACHYON_UCI_PACKAGE as TACHYON_CBI_PREFIX } from './constants';
export const GlobalStyles = `
${DashboardTab.styles}
@@ -15,49 +15,49 @@ ${PartialStyles}
/* Hide extra H3 for settings tab */
#cbi-${FORKOP_CBI_PREFIX}-settings > h3 {
#cbi-${TACHYON_CBI_PREFIX}-settings > h3 {
display: none;
}
/* Hide extra H3 for rules tab */
#cbi-${FORKOP_CBI_PREFIX}-section > h3:nth-child(1) {
#cbi-${TACHYON_CBI_PREFIX}-section > h3:nth-child(1) {
display: none;
}
/* Vertical align for remove rule action button */
#cbi-${FORKOP_CBI_PREFIX}-section > .cbi-section-remove {
#cbi-${TACHYON_CBI_PREFIX}-section > .cbi-section-remove {
margin-bottom: -32px;
}
#cbi-${FORKOP_CBI_PREFIX}-section .cbi-section-actions > div {
#cbi-${TACHYON_CBI_PREFIX}-section .cbi-section-actions > div {
display: inline-flex;
align-items: center;
gap: 4px;
}
#cbi-${FORKOP_CBI_PREFIX}-section .cbi-section-actions {
#cbi-${TACHYON_CBI_PREFIX}-section .cbi-section-actions {
text-align: right;
}
/* Rule reorder visuals */
#cbi-${FORKOP_CBI_PREFIX}-section {
#cbi-${TACHYON_CBI_PREFIX}-section {
position: relative;
}
#cbi-${FORKOP_CBI_PREFIX}-section .cbi-section-table-row {
#cbi-${TACHYON_CBI_PREFIX}-section .cbi-section-table-row {
position: relative;
}
#cbi-${FORKOP_CBI_PREFIX}-section .cbi-section-table-row.placeholder {
#cbi-${TACHYON_CBI_PREFIX}-section .cbi-section-table-row.placeholder {
opacity: 1;
}
#cbi-${FORKOP_CBI_PREFIX}-section .cbi-section-table-row.placeholder em {
#cbi-${TACHYON_CBI_PREFIX}-section .cbi-section-table-row.placeholder em {
font-style: italic;
}
#cbi-${FORKOP_CBI_PREFIX}-section .cbi-section-table-row.drag-over-above::after,
#cbi-${FORKOP_CBI_PREFIX}-section .cbi-section-table-row.drag-over-below::after {
#cbi-${TACHYON_CBI_PREFIX}-section .cbi-section-table-row.drag-over-above::after,
#cbi-${TACHYON_CBI_PREFIX}-section .cbi-section-table-row.drag-over-below::after {
content: '';
position: absolute;
left: 10px;
@@ -69,11 +69,11 @@ ${PartialStyles}
z-index: 2;
}
#cbi-${FORKOP_CBI_PREFIX}-section .cbi-section-table-row.drag-over-above::after {
#cbi-${TACHYON_CBI_PREFIX}-section .cbi-section-table-row.drag-over-above::after {
top: -1px;
}
#cbi-${FORKOP_CBI_PREFIX}-section .cbi-section-table-row.drag-over-below::after {
#cbi-${TACHYON_CBI_PREFIX}-section .cbi-section-table-row.drag-over-below::after {
bottom: -1px;
}
@@ -1,15 +1,15 @@
import { ForkopShellMethods } from '../methods';
import { TachyonShellMethods } from '../methods';
import { logger } from '../services/logger.service';
import { store } from '../services/store.service';
import { refreshRuntimeUiState } from '../services/runtimeUiState.service';
import { Forkop } from '../types';
import { Tachyon } from '../types';
let latestServicesInfoRequestId = 0;
function getSettledMethodResponse<T>(
scope: string,
result: PromiseSettledResult<Forkop.MethodResponse<T>>,
): Forkop.MethodResponse<T> {
result: PromiseSettledResult<Tachyon.MethodResponse<T>>,
): Tachyon.MethodResponse<T> {
if (result.status === 'fulfilled') {
return result.value;
}
@@ -34,34 +34,34 @@ export async function fetchServicesInfo() {
return uiState;
}
const [forkopResult, singboxResult] = await Promise.allSettled([
ForkopShellMethods.getStatus(),
ForkopShellMethods.getSingBoxStatus(),
const [tachyonResult, singboxResult] = await Promise.allSettled([
TachyonShellMethods.getStatus(),
TachyonShellMethods.getSingBoxStatus(),
]);
if (requestId !== latestServicesInfoRequestId) {
return;
}
const forkop = getSettledMethodResponse('getStatus', forkopResult);
const tachyon = getSettledMethodResponse('getStatus', tachyonResult);
const singbox = getSettledMethodResponse('getSingBoxStatus', singboxResult);
const previousData = store.get().servicesInfoWidget.data;
store.set({
servicesInfoWidget: {
loading: false,
failed: !forkop.success || !singbox.success,
failed: !tachyon.success || !singbox.success,
data: {
singbox: singbox.success ? singbox.data.running : previousData.singbox,
forkopRunning: forkop.success
? forkop.data.running
: previousData.forkopRunning,
forkopEnabled: forkop.success
? forkop.data.enabled
: previousData.forkopEnabled,
forkopStatus: forkop.success
? forkop.data.status
: previousData.forkopStatus,
tachyonRunning: tachyon.success
? tachyon.data.running
: previousData.tachyonRunning,
tachyonEnabled: tachyon.success
? tachyon.data.enabled
: previousData.tachyonEnabled,
tachyonStatus: tachyon.success
? tachyon.data.status
: previousData.tachyonStatus,
},
},
});
@@ -20,7 +20,7 @@ describe('fetchServicesInfo', () => {
it('returns the fast UI state after applying it to the shared store', async () => {
const uiState = {
service: {
forkop: {
tachyon: {
running: 1,
enabled: 1,
status: 'restarting',
@@ -67,7 +67,7 @@ describe('fetchServicesInfo', () => {
const state = store.get();
expect(state.servicesInfoWidget.data.forkopStatus).toBe('restarting');
expect(state.servicesInfoWidget.data.tachyonStatus).toBe('restarting');
expect(state.diagnosticsActions.restart.loading).toBe(true);
});
@@ -78,9 +78,9 @@ describe('fetchServicesInfo', () => {
failed: false,
data: {
singbox: 1,
forkopRunning: 1,
forkopEnabled: 1,
forkopStatus: 'running & enabled',
tachyonRunning: 1,
tachyonEnabled: 1,
tachyonStatus: 'running & enabled',
},
},
});
@@ -113,9 +113,9 @@ describe('fetchServicesInfo', () => {
expect(state.servicesInfoWidget.failed).toBe(true);
expect(state.servicesInfoWidget.data).toEqual({
singbox: 0,
forkopRunning: 1,
forkopEnabled: 1,
forkopStatus: 'running & enabled',
tachyonRunning: 1,
tachyonEnabled: 1,
tachyonStatus: 'running & enabled',
});
});
});
@@ -1,11 +1,11 @@
import type { StoreType } from '../services/store.service';
import type { Forkop } from '../types';
import type { Tachyon } from '../types';
export type UpdatesActionKey = keyof StoreType['updatesActions'];
const componentActionKeyMap: Record<string, UpdatesActionKey> = {
'forkop:check_update': 'forkopCheck',
'forkop:install': 'forkopInstall',
'tachyon:check_update': 'tachyonCheck',
'tachyon:install': 'tachyonInstall',
'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: Forkop.ComponentName,
action: Forkop.ComponentAction,
component: Tachyon.ComponentName,
action: Tachyon.ComponentAction,
): UpdatesActionKey | undefined {
return componentActionKeyMap[`${component}:${action}`];
}
@@ -1,8 +1,8 @@
import { Forkop } from '../../types';
import { FORKOP_UCI_PACKAGE } from '../../../constants';
import { Tachyon } from '../../types';
import { TACHYON_UCI_PACKAGE } from '../../../constants';
export async function getConfigSections(): Promise<Forkop.ConfigSection[]> {
export async function getConfigSections(): Promise<Tachyon.ConfigSection[]> {
return uci
.load(FORKOP_UCI_PACKAGE)
.then(() => uci.sections(FORKOP_UCI_PACKAGE));
.load(TACHYON_UCI_PACKAGE)
.then(() => uci.sections(TACHYON_UCI_PACKAGE));
}
@@ -1,5 +1,5 @@
import { getConfigSections } from './getConfigSections';
import { ClashAPI, Forkop } from '../../types';
import { ClashAPI, Tachyon } from '../../types';
import {
canUseDirectClashApi,
getClashHttpUrl,
@@ -7,11 +7,11 @@ import {
isCopyableProxyLink,
} from '../../../helpers';
import { getOutboundTagBySection } from '../../runtimeTags';
import { ForkopShellMethods } from '../shell';
import { TachyonShellMethods } from '../shell';
interface IGetDashboardSectionsResponse {
success: boolean;
data: Forkop.OutboundGroup[];
data: Tachyon.OutboundGroup[];
}
interface IGetDashboardSectionsOptions {
@@ -28,12 +28,12 @@ type DashboardSectionCache = {
section?: string;
links?: Record<string, string>;
linkRefs?: Record<string, unknown>;
outboundMetadata?: Forkop.GetOutboundMetadata;
outboundMetadata?: Tachyon.GetOutboundMetadata;
urltestGroups?: Record<string, UrlTestCacheGroup>;
priorityGroups?: Record<string, PriorityCacheGroup>;
subscriptionMetadata?:
| Forkop.SubscriptionMetadata
| Forkop.SubscriptionMetadata[];
| Tachyon.SubscriptionMetadata
| Tachyon.SubscriptionMetadata[];
};
type UrlTestCacheGroup = {
@@ -130,21 +130,21 @@ type ChildType =
| 'priority_group'
| 'priority_level';
const DASHBOARD_SECTION_CACHE_DIR = '/var/run/forkop/section-cache';
const DASHBOARD_SECTION_CACHE_DIR = '/var/run/tachyon/section-cache';
function getDisplayName(section: Forkop.ConfigSection) {
function getDisplayName(section: Tachyon.ConfigSection) {
return section.label || section['.name'];
}
function getSectionAction(section: Forkop.ConfigSection) {
function getSectionAction(section: Tachyon.ConfigSection) {
return section.action || '';
}
function getSettingsSection(configSections: Forkop.ConfigSection[]) {
function getSettingsSection(configSections: Tachyon.ConfigSection[]) {
return configSections.find((section) => section['.type'] === 'settings');
}
function getClashApiSecret(configSections: Forkop.ConfigSection[]) {
function getClashApiSecret(configSections: Tachyon.ConfigSection[]) {
return getSettingsSection(configSections)?.yacd_secret_key || '';
}
@@ -153,8 +153,8 @@ function canFetchClashApiDirectly() {
}
async function getClashApiProxies(
configSections: Forkop.ConfigSection[],
): Promise<Forkop.MethodResponse<ClashAPI.Proxies>> {
configSections: Tachyon.ConfigSection[],
): Promise<Tachyon.MethodResponse<ClashAPI.Proxies>> {
if (canFetchClashApiDirectly()) {
const secret = getClashApiSecret(configSections);
@@ -174,7 +174,7 @@ async function getClashApiProxies(
}
}
return ForkopShellMethods.getClashApiProxies();
return TachyonShellMethods.getClashApiProxies();
}
function getListValues(value?: string[] | string) {
@@ -193,25 +193,25 @@ function getListValues(value?: string[] | string) {
}
function childSections(
configSections: Forkop.ConfigSection[],
configSections: Tachyon.ConfigSection[],
type: ChildType,
) {
return configSections.filter((section) => section['.type'] === type);
}
function ownedChildSections(
parent: Forkop.ConfigSection,
children: Forkop.ConfigSection[],
parent: Tachyon.ConfigSection,
children: Tachyon.ConfigSection[],
) {
return children.filter(
(section): section is Forkop.ConfigSection =>
(section): section is Tachyon.ConfigSection =>
section.section === parent['.name'],
);
}
function childSectionsByOwner(
children: Forkop.ConfigSection[],
ownerKey: keyof Forkop.ConfigSection,
children: Tachyon.ConfigSection[],
ownerKey: keyof Tachyon.ConfigSection,
ownerValue: string,
) {
return children.filter((section) => section[ownerKey] === ownerValue);
@@ -221,7 +221,7 @@ function compactSettingsMap(settings: Record<string, ItemSettings>) {
return Object.keys(settings).length ? JSON.stringify(settings) : undefined;
}
function hydrateConfigSections(configSections: Forkop.ConfigSection[]) {
function hydrateConfigSections(configSections: Tachyon.ConfigSection[]) {
const subscriptionUrls = childSections(configSections, 'subscription_url');
const interfaces = childSections(configSections, 'section_interface');
const urltests = childSections(configSections, 'urltest');
@@ -233,7 +233,7 @@ function hydrateConfigSections(configSections: Forkop.ConfigSection[]) {
return section;
}
const next: Forkop.ConfigSection = { ...section };
const next: Tachyon.ConfigSection = { ...section };
const subscriptionUrlItems = ownedChildSections(next, subscriptionUrls);
const interfaceItems = ownedChildSections(next, interfaces);
const urltestItems = ownedChildSections(next, urltests);
@@ -350,16 +350,16 @@ function hydrateConfigSections(configSections: Forkop.ConfigSection[]) {
});
}
function getManualProxyLinks(section: Forkop.ConfigSection) {
function getManualProxyLinks(section: Tachyon.ConfigSection) {
return getListValues(section.selector_proxy_links);
}
function getConnectionInterfaces(section: Forkop.ConfigSection) {
function getConnectionInterfaces(section: Tachyon.ConfigSection) {
const values = getListValues(section.interfaces);
return values.length ? values : getListValues(section.interface);
}
function getJsonOutbounds(section: Forkop.ConfigSection) {
function getJsonOutbounds(section: Tachyon.ConfigSection) {
const values = getListValues(section.outbound_jsons);
return values.length ? values : getListValues(section.outbound_json);
}
@@ -368,27 +368,27 @@ function isConnectionAction(action: string) {
return ['connection', 'proxy', 'outbound', 'vpn'].includes(action);
}
function hasSubscriptionSources(section: Forkop.ConfigSection) {
function hasSubscriptionSources(section: Tachyon.ConfigSection) {
return getSubscriptionSourceCount(section) > 0;
}
function getSubscriptionSourceCount(section: Forkop.ConfigSection) {
function getSubscriptionSourceCount(section: Tachyon.ConfigSection) {
return getListValues(section.subscription_urls).length;
}
function shouldSortByLatency(section: Forkop.ConfigSection) {
function shouldSortByLatency(section: Tachyon.ConfigSection) {
return section.sort_by_latency === '1';
}
function hasConfiguredUrlTestList(section: Forkop.ConfigSection) {
function hasConfiguredUrlTestList(section: Tachyon.ConfigSection) {
return getListValues(section.urltests).length > 0;
}
function hasConfiguredPriorityList(section: Forkop.ConfigSection) {
function hasConfiguredPriorityList(section: Tachyon.ConfigSection) {
return getListValues(section.priority_groups).length > 0;
}
function getUrlTestIds(section: Forkop.ConfigSection) {
function getUrlTestIds(section: Tachyon.ConfigSection) {
const values = getListValues(section.urltests);
return values.length
? values
@@ -397,11 +397,11 @@ function getUrlTestIds(section: Forkop.ConfigSection) {
: [];
}
function isUrlTestEnabled(section: Forkop.ConfigSection) {
function isUrlTestEnabled(section: Tachyon.ConfigSection) {
return getUrlTestIds(section).length > 0;
}
function shouldUseProxyGroup(section: Forkop.ConfigSection) {
function shouldUseProxyGroup(section: Tachyon.ConfigSection) {
return (
getManualProxyLinks(section).length > 0 ||
hasSubscriptionSources(section) ||
@@ -412,7 +412,7 @@ function shouldUseProxyGroup(section: Forkop.ConfigSection) {
);
}
function getSectionProxyConfigType(section: Forkop.ConfigSection) {
function getSectionProxyConfigType(section: Tachyon.ConfigSection) {
if (hasSubscriptionSources(section)) {
return 'subscription' as const;
}
@@ -440,7 +440,7 @@ function getSectionProxyConfigType(section: Forkop.ConfigSection) {
return undefined;
}
function getJsonOutboundDisplayName(section: Forkop.ConfigSection) {
function getJsonOutboundDisplayName(section: Tachyon.ConfigSection) {
try {
const parsedOutbound = JSON.parse(section.outbound_json || '{}');
return parsedOutbound?.tag ? decodeURIComponent(parsedOutbound.tag) : '';
@@ -449,7 +449,7 @@ function getJsonOutboundDisplayName(section: Forkop.ConfigSection) {
}
}
function buildManualLinkByCode(section: Forkop.ConfigSection) {
function buildManualLinkByCode(section: Tachyon.ConfigSection) {
const sectionName = section['.name'];
return new Map(
@@ -468,7 +468,7 @@ function uniqueCodes(codes: string[]) {
return Array.from(new Set(codes.filter(Boolean)));
}
function isSelectorOutbound(outbound: Forkop.Outbound) {
function isSelectorOutbound(outbound: Tachyon.Outbound) {
return outbound.type?.toLowerCase() === 'selector';
}
@@ -485,7 +485,7 @@ function getLatencySortValue(outbound: { latency: number }) {
}
function sortOutboundsForDashboard(
outbounds: Forkop.Outbound[],
outbounds: Tachyon.Outbound[],
options: {
pinnedCode?: string;
pinnedCodes?: string[];
@@ -536,7 +536,7 @@ function sortOutboundsForDashboard(
.map((item) => item.outbound);
}
function sortUrlTestMembers(outbounds: Forkop.UrlTestMember[]) {
function sortUrlTestMembers(outbounds: Tachyon.UrlTestMember[]) {
return outbounds
.map((outbound, index) => ({ outbound, index }))
.sort((left, right) => {
@@ -636,7 +636,7 @@ function getPriorityTag(sectionName: string, id: string) {
}
function getUrlTestDisplayName(
section: Forkop.ConfigSection,
section: Tachyon.ConfigSection,
id: string,
settings: ItemSettings | undefined,
) {
@@ -647,7 +647,7 @@ function getUrlTestDisplayName(
);
}
function getUrlTestConfigs(section: Forkop.ConfigSection): UrlTestConfig[] {
function getUrlTestConfigs(section: Tachyon.ConfigSection): UrlTestConfig[] {
const settingsMap = itemSettingsMap(section.urltest_settings);
const sectionName = section['.name'];
@@ -730,11 +730,11 @@ function priorityLevelConfigsFromSettings(
);
}
function getPriorityGroupIds(section: Forkop.ConfigSection) {
function getPriorityGroupIds(section: Tachyon.ConfigSection) {
return getListValues(section.priority_groups);
}
function getPriorityConfigs(section: Forkop.ConfigSection): PriorityConfig[] {
function getPriorityConfigs(section: Tachyon.ConfigSection): PriorityConfig[] {
const settingsMap = itemSettingsMap(section.priority_group_settings);
const sectionName = section['.name'];
@@ -840,7 +840,7 @@ function getOutboundDisplayName(
code: string,
entry: ClashProxyEntry | undefined,
link: string,
outboundMetadata?: Forkop.GetOutboundMetadata,
outboundMetadata?: Tachyon.GetOutboundMetadata,
preferMetadata = false,
) {
const metadataName = outboundMetadata?.names?.[code];
@@ -872,10 +872,10 @@ function buildUrlTestInfo({
proxyByCode: Map<string, ClashProxyEntry>;
manualLinkByCode: Map<string, string>;
cachedProxyLinks: Map<string, string>;
outboundMetadata?: Forkop.GetOutboundMetadata;
outboundMetadata?: Tachyon.GetOutboundMetadata;
subscriptionCopyableCodes: Set<string>;
showDetectedCountries: boolean;
}): Forkop.UrlTestInfo {
}): Tachyon.UrlTestInfo {
const childCodes = uniqueCodes(
groupCache?.outbounds?.length
? groupCache.outbounds
@@ -949,10 +949,10 @@ function buildPriorityInfo({
proxyByCode: Map<string, ClashProxyEntry>;
manualLinkByCode: Map<string, string>;
cachedProxyLinks: Map<string, string>;
outboundMetadata?: Forkop.GetOutboundMetadata;
outboundMetadata?: Tachyon.GetOutboundMetadata;
subscriptionCopyableCodes: Set<string>;
showDetectedCountries: boolean;
}): Forkop.PriorityInfo {
}): Tachyon.PriorityInfo {
const selectedCode = entry?.value.now || '';
const cacheLevels = Array.isArray(groupCache?.levels)
? groupCache.levels
@@ -1076,9 +1076,9 @@ function buildPriorityInfo({
}
function buildProxyGroupOutbounds(
section: Forkop.ConfigSection,
section: Tachyon.ConfigSection,
proxies: ClashProxyEntry[],
outboundMetadata?: Forkop.GetOutboundMetadata,
outboundMetadata?: Tachyon.GetOutboundMetadata,
urltestGroups: Record<string, UrlTestCacheGroup> = {},
priorityGroups: Record<string, PriorityCacheGroup> = {},
subscriptionCopyableCodes: Set<string> = new Set(),
@@ -1261,9 +1261,9 @@ function buildProxyGroupOutbounds(
function metadataMatchesCurrentSource(
sectionName: string,
sourceCount: number,
metadata: Forkop.SubscriptionMetadata,
metadata: Tachyon.SubscriptionMetadata,
) {
const legacyMetadata = metadata as Forkop.SubscriptionMetadata & {
const legacyMetadata = metadata as Tachyon.SubscriptionMetadata & {
source_index?: number;
source_section?: string;
};
@@ -1315,9 +1315,9 @@ function metadataMatchesCurrentSource(
function getSubscriptionMetadataSourceIndex(
sectionName: string,
sourceCount: number,
metadata: Forkop.SubscriptionMetadata,
metadata: Tachyon.SubscriptionMetadata,
) {
const legacyMetadata = metadata as Forkop.SubscriptionMetadata & {
const legacyMetadata = metadata as Tachyon.SubscriptionMetadata & {
source_index?: number;
source_section?: string;
};
@@ -1340,9 +1340,9 @@ function getSubscriptionMetadataSourceIndex(
}
function isSubscriptionMetadataVisible(
section: Forkop.ConfigSection,
section: Tachyon.ConfigSection,
sourceCount: number,
metadata: Forkop.SubscriptionMetadata,
metadata: Tachyon.SubscriptionMetadata,
) {
const sourceIndex = getSubscriptionMetadataSourceIndex(
section['.name'],
@@ -1363,7 +1363,7 @@ function isSubscriptionMetadataVisible(
}
function getSubscriptionMetadata(
section: Forkop.ConfigSection,
section: Tachyon.ConfigSection,
sourceCount: number,
dashboardCache?: DashboardSectionCache,
) {
@@ -2,7 +2,7 @@ import { getConfigSections } from './getConfigSections';
import { getDashboardSections } from './getDashboardSections';
import { getClashApiSecret } from './getClashApiSecret';
export const CustomForkopMethods = {
export const CustomTachyonMethods = {
getConfigSections,
getDashboardSections,
getClashApiSecret,
@@ -12,7 +12,7 @@ vi.mock('../getConfigSections', () => ({
}));
vi.mock('../../shell', () => ({
ForkopShellMethods: {
TachyonShellMethods: {
getClashApiProxies: mocks.getClashApiProxies,
},
}));
@@ -26,7 +26,7 @@ vi.mock('../../../../helpers', () => ({
}));
import { getDashboardSections } from '../getDashboardSections';
import { ClashAPI, Forkop } from '../../../types';
import { ClashAPI, Tachyon } from '../../../types';
function proxy(
type: string,
@@ -43,8 +43,8 @@ function proxy(
}
function proxySection(
options: Partial<Forkop.ConfigSection> = {},
): Forkop.ConfigSection {
options: Partial<Tachyon.ConfigSection> = {},
): Tachyon.ConfigSection {
return {
'.name': 'main',
'.type': 'section',
@@ -71,8 +71,8 @@ function urlTestSettings(
function priorityGroup(
id: string,
options: Partial<Forkop.ConfigSection> = {},
): Forkop.ConfigSection {
options: Partial<Tachyon.ConfigSection> = {},
): Tachyon.ConfigSection {
return {
'.name': id,
'.type': 'priority_group',
@@ -94,8 +94,8 @@ function priorityGroup(
function priorityLevel(
id: string,
options: Partial<Forkop.ConfigSection> = {},
): Forkop.ConfigSection {
options: Partial<Tachyon.ConfigSection> = {},
): Tachyon.ConfigSection {
return {
'.name': id,
'.type': 'priority_level',
@@ -1,5 +1,5 @@
import { executeShellCommand } from '../../../helpers';
import { Forkop } from '../../types';
import { Tachyon } from '../../types';
interface CallBaseMethodOptions {
allowNonZeroWithStdout?: boolean;
@@ -7,11 +7,11 @@ interface CallBaseMethodOptions {
}
export async function callBaseMethod<T>(
method: Forkop.AvailableMethods,
method: Tachyon.AvailableMethods,
args: string[] = [],
command: string = '/usr/bin/forkop',
command: string = '/usr/bin/tachyon',
options: CallBaseMethodOptions = {},
): Promise<Forkop.MethodResponse<T>> {
): Promise<Tachyon.MethodResponse<T>> {
try {
const response = await executeShellCommand({
command,
+141 -141
View File
@@ -1,5 +1,5 @@
import { callBaseMethod } from './callBaseMethod';
import { ClashAPI, Forkop } from '../../types';
import { ClashAPI, Tachyon } 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/forkop/component-actions';
const COMPONENT_ACTION_STATE_DIR = '/var/run/tachyon/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<Forkop.ComponentActionResult>(output);
return parseJsonObjectOutput<Tachyon.ComponentActionResult>(output);
}
function parseComponentActionResult(
@@ -68,13 +68,13 @@ function parseComponentActionStartResult(
return null;
}
return parsedResponse as unknown as Forkop.ComponentActionStartResult;
return parsedResponse as unknown as Tachyon.ComponentActionStartResult;
}
function parseSubscriptionUpdateStartResult(
response: Awaited<ReturnType<typeof executeShellCommand>>,
) {
return parseJsonObjectOutput<Forkop.SubscriptionUpdateStartResult>(
return parseJsonObjectOutput<Tachyon.SubscriptionUpdateStartResult>(
response.stdout,
);
}
@@ -82,7 +82,7 @@ function parseSubscriptionUpdateStartResult(
function parseSubscriptionUpdateJobState(
response: Awaited<ReturnType<typeof executeShellCommand>>,
) {
return parseJsonObjectOutput<Forkop.SubscriptionUpdateJobState>(
return parseJsonObjectOutput<Tachyon.SubscriptionUpdateJobState>(
response.stdout,
);
}
@@ -90,19 +90,19 @@ function parseSubscriptionUpdateJobState(
function parseUiActionStartResult(
response: Awaited<ReturnType<typeof executeShellCommand>>,
) {
return parseJsonObjectOutput<Forkop.UiActionStartResult>(response.stdout);
return parseJsonObjectOutput<Tachyon.UiActionStartResult>(response.stdout);
}
function parseServiceActionState(
response: Awaited<ReturnType<typeof executeShellCommand>>,
) {
return parseJsonObjectOutput<Forkop.ServiceActionState>(response.stdout);
return parseJsonObjectOutput<Tachyon.ServiceActionState>(response.stdout);
}
function parseLatencyActionState(
response: Awaited<ReturnType<typeof executeShellCommand>>,
) {
return parseJsonObjectOutput<Forkop.LatencyActionState>(response.stdout);
return parseJsonObjectOutput<Tachyon.LatencyActionState>(response.stdout);
}
function isComponentActionJobId(jobId: string) {
@@ -123,9 +123,9 @@ async function readComponentActionState(jobId: string) {
}
}
async function readForkopVersion() {
async function readTachyonVersion() {
const response = await executeShellCommand({
command: '/usr/bin/forkop',
command: '/usr/bin/tachyon',
args: ['show_version'],
timeout: COMPONENT_ACTION_RPC_TIMEOUT_MS,
});
@@ -139,13 +139,13 @@ async function readForkopVersion() {
async function isComponentActionStillRunning(
jobId: string,
component: Forkop.ComponentName,
action: Forkop.ComponentAction,
component: Tachyon.ComponentName,
action: Tachyon.ComponentAction,
) {
const response = await callBaseMethod<Forkop.UiState>(
Forkop.AvailableMethods.GET_UI_STATE,
const response = await callBaseMethod<Tachyon.UiState>(
Tachyon.AvailableMethods.GET_UI_STATE,
[],
'/usr/bin/forkop',
'/usr/bin/tachyon',
{ timeout: GET_UI_STATE_RPC_TIMEOUT_MS },
);
@@ -163,12 +163,12 @@ async function isComponentActionStillRunning(
function componentActionFailure(
response: Awaited<ReturnType<typeof executeShellCommand>>,
parsedResponse?: Pick<Forkop.ComponentActionResult, 'message'> | null,
parsedResponse?: Pick<Tachyon.ComponentActionResult, 'message'> | null,
) {
return {
success: false,
error: parsedResponse?.message || response.stderr || _('Failed to execute'),
} as Forkop.MethodFailureResponse;
} as Tachyon.MethodFailureResponse;
}
function uiActionFailure(
@@ -179,7 +179,7 @@ function uiActionFailure(
return {
success: false,
error: parsedResponse?.message || response.stderr || fallback,
} as Forkop.MethodFailureResponse;
} as Tachyon.MethodFailureResponse;
}
function createTransientRpcGraceTracker(graceMs: number) {
@@ -204,170 +204,170 @@ function createTransientRpcGraceTracker(graceMs: number) {
};
}
export const ForkopShellMethods = {
export const TachyonShellMethods = {
checkDNSAvailable: async () =>
callBaseMethod<Forkop.DnsCheckResult>(
Forkop.AvailableMethods.CHECK_DNS_AVAILABLE,
callBaseMethod<Tachyon.DnsCheckResult>(
Tachyon.AvailableMethods.CHECK_DNS_AVAILABLE,
),
checkFakeIP: async () =>
callBaseMethod<Forkop.FakeIPCheckResult>(
Forkop.AvailableMethods.CHECK_FAKEIP,
callBaseMethod<Tachyon.FakeIPCheckResult>(
Tachyon.AvailableMethods.CHECK_FAKEIP,
),
checkNftRules: async () =>
callBaseMethod<Forkop.NftRulesCheckResult>(
Forkop.AvailableMethods.CHECK_NFT_RULES,
callBaseMethod<Tachyon.NftRulesCheckResult>(
Tachyon.AvailableMethods.CHECK_NFT_RULES,
),
checkZapretRuntime: async () =>
callBaseMethod<Forkop.ZapretCheckResult>(
Forkop.AvailableMethods.CHECK_ZAPRET_RUNTIME,
callBaseMethod<Tachyon.ZapretCheckResult>(
Tachyon.AvailableMethods.CHECK_ZAPRET_RUNTIME,
),
checkZapret2Runtime: async () =>
callBaseMethod<Forkop.Zapret2CheckResult>(
Forkop.AvailableMethods.CHECK_ZAPRET2_RUNTIME,
callBaseMethod<Tachyon.Zapret2CheckResult>(
Tachyon.AvailableMethods.CHECK_ZAPRET2_RUNTIME,
),
checkByedpiRuntime: async () =>
callBaseMethod<Forkop.ByedpiCheckResult>(
Forkop.AvailableMethods.CHECK_BYEDPI_RUNTIME,
callBaseMethod<Tachyon.ByedpiCheckResult>(
Tachyon.AvailableMethods.CHECK_BYEDPI_RUNTIME,
),
checkInboundsConfig: async () =>
callBaseMethod<Forkop.InboundsConfigCheckResult>(
Forkop.AvailableMethods.CHECK_INBOUNDS_CONFIG,
callBaseMethod<Tachyon.InboundsConfigCheckResult>(
Tachyon.AvailableMethods.CHECK_INBOUNDS_CONFIG,
),
getStatus: async () =>
callBaseMethod<Forkop.GetStatus>(Forkop.AvailableMethods.GET_STATUS),
callBaseMethod<Tachyon.GetStatus>(Tachyon.AvailableMethods.GET_STATUS),
getOutboundLink: async (section: string, tag: string) =>
callBaseMethod<Forkop.GetOutboundLink>(
Forkop.AvailableMethods.GET_OUTBOUND_LINK,
callBaseMethod<Tachyon.GetOutboundLink>(
Tachyon.AvailableMethods.GET_OUTBOUND_LINK,
[section, tag],
),
getOutboundLinkStates: async (section: string) =>
callBaseMethod<Forkop.GetOutboundLinkStates>(
Forkop.AvailableMethods.GET_OUTBOUND_LINK_STATES,
callBaseMethod<Tachyon.GetOutboundLinkStates>(
Tachyon.AvailableMethods.GET_OUTBOUND_LINK_STATES,
[section],
),
getOutboundMetadata: async (section: string) =>
callBaseMethod<Forkop.GetOutboundMetadata>(
Forkop.AvailableMethods.GET_OUTBOUND_METADATA,
callBaseMethod<Tachyon.GetOutboundMetadata>(
Tachyon.AvailableMethods.GET_OUTBOUND_METADATA,
[section],
),
getSubscriptionMetadata: async (section: string) =>
callBaseMethod<Forkop.SubscriptionMetadata | Forkop.SubscriptionMetadata[]>(
Forkop.AvailableMethods.GET_SUBSCRIPTION_METADATA,
callBaseMethod<Tachyon.SubscriptionMetadata | Tachyon.SubscriptionMetadata[]>(
Tachyon.AvailableMethods.GET_SUBSCRIPTION_METADATA,
[section],
),
checkSingBox: async () =>
callBaseMethod<Forkop.SingBoxCheckResult>(
Forkop.AvailableMethods.CHECK_SING_BOX,
callBaseMethod<Tachyon.SingBoxCheckResult>(
Tachyon.AvailableMethods.CHECK_SING_BOX,
),
checkInbounds: async () =>
callBaseMethod<Forkop.InboundsCheckResult>(
Forkop.AvailableMethods.CHECK_INBOUNDS,
callBaseMethod<Tachyon.InboundsCheckResult>(
Tachyon.AvailableMethods.CHECK_INBOUNDS,
),
getSingBoxStatus: async () =>
callBaseMethod<Forkop.GetSingBoxStatus>(
Forkop.AvailableMethods.GET_SING_BOX_STATUS,
callBaseMethod<Tachyon.GetSingBoxStatus>(
Tachyon.AvailableMethods.GET_SING_BOX_STATUS,
),
getZapretStatus: async () =>
callBaseMethod<Forkop.GetZapretStatus>(
Forkop.AvailableMethods.GET_ZAPRET_STATUS,
callBaseMethod<Tachyon.GetZapretStatus>(
Tachyon.AvailableMethods.GET_ZAPRET_STATUS,
),
getZapret2Status: async () =>
callBaseMethod<Forkop.GetZapret2Status>(
Forkop.AvailableMethods.GET_ZAPRET2_STATUS,
callBaseMethod<Tachyon.GetZapret2Status>(
Tachyon.AvailableMethods.GET_ZAPRET2_STATUS,
),
getByedpiStatus: async () =>
callBaseMethod<Forkop.GetByedpiStatus>(
Forkop.AvailableMethods.GET_BYEDPI_STATUS,
callBaseMethod<Tachyon.GetByedpiStatus>(
Tachyon.AvailableMethods.GET_BYEDPI_STATUS,
),
getClashApiProxies: async () =>
callBaseMethod<ClashAPI.Proxies>(Forkop.AvailableMethods.CLASH_API, [
Forkop.AvailableClashAPIMethods.GET_PROXIES,
callBaseMethod<ClashAPI.Proxies>(Tachyon.AvailableMethods.CLASH_API, [
Tachyon.AvailableClashAPIMethods.GET_PROXIES,
]),
getClashApiConnections: async () =>
callBaseMethod<unknown>(Forkop.AvailableMethods.CLASH_API, [
Forkop.AvailableClashAPIMethods.GET_CONNECTIONS,
callBaseMethod<unknown>(Tachyon.AvailableMethods.CLASH_API, [
Tachyon.AvailableClashAPIMethods.GET_CONNECTIONS,
]),
getClashApiProxyLatency: async (tag: string, timeout = '5000') =>
callBaseMethod<Forkop.GetClashApiProxyLatency>(
Forkop.AvailableMethods.CLASH_API,
[Forkop.AvailableClashAPIMethods.GET_PROXY_LATENCY, tag, timeout],
callBaseMethod<Tachyon.GetClashApiProxyLatency>(
Tachyon.AvailableMethods.CLASH_API,
[Tachyon.AvailableClashAPIMethods.GET_PROXY_LATENCY, tag, timeout],
),
getClashApiProxyLatencies: async (tags: string[]) =>
callBaseMethod<Forkop.GetClashApiProxyLatencies>(
Forkop.AvailableMethods.CLASH_API,
callBaseMethod<Tachyon.GetClashApiProxyLatencies>(
Tachyon.AvailableMethods.CLASH_API,
[
Forkop.AvailableClashAPIMethods.GET_PROXY_LATENCIES,
Tachyon.AvailableClashAPIMethods.GET_PROXY_LATENCIES,
JSON.stringify(tags),
'5000',
],
),
getClashApiGroupLatency: async (tag: string) =>
callBaseMethod<Forkop.GetClashApiGroupLatency>(
Forkop.AvailableMethods.CLASH_API,
[Forkop.AvailableClashAPIMethods.GET_GROUP_LATENCY, tag, '10000'],
callBaseMethod<Tachyon.GetClashApiGroupLatency>(
Tachyon.AvailableMethods.CLASH_API,
[Tachyon.AvailableClashAPIMethods.GET_GROUP_LATENCY, tag, '10000'],
),
setClashApiGroupProxy: async (group: string, proxy: string) =>
callBaseMethod<unknown>(Forkop.AvailableMethods.CLASH_API, [
Forkop.AvailableClashAPIMethods.SET_GROUP_PROXY,
callBaseMethod<unknown>(Tachyon.AvailableMethods.CLASH_API, [
Tachyon.AvailableClashAPIMethods.SET_GROUP_PROXY,
group,
proxy,
]),
closeClashApiConnection: async (connectionId: string) =>
callBaseMethod<unknown>(Forkop.AvailableMethods.CLASH_API, [
Forkop.AvailableClashAPIMethods.CLOSE_CONNECTION,
callBaseMethod<unknown>(Tachyon.AvailableMethods.CLASH_API, [
Tachyon.AvailableClashAPIMethods.CLOSE_CONNECTION,
connectionId,
]),
closeAllClashApiConnections: async () =>
callBaseMethod<unknown>(Forkop.AvailableMethods.CLASH_API, [
Forkop.AvailableClashAPIMethods.CLOSE_ALL_CONNECTIONS,
callBaseMethod<unknown>(Tachyon.AvailableMethods.CLASH_API, [
Tachyon.AvailableClashAPIMethods.CLOSE_ALL_CONNECTIONS,
]),
enable: async () =>
callBaseMethod<unknown>(
Forkop.AvailableMethods.ENABLE,
Tachyon.AvailableMethods.ENABLE,
[],
'/etc/init.d/forkop',
'/etc/init.d/tachyon',
),
disable: async () =>
callBaseMethod<unknown>(
Forkop.AvailableMethods.DISABLE,
Tachyon.AvailableMethods.DISABLE,
[],
'/etc/init.d/forkop',
'/etc/init.d/tachyon',
),
globalCheck: async (masked = true) =>
callBaseMethod<unknown>(Forkop.AvailableMethods.GLOBAL_CHECK, [
callBaseMethod<unknown>(Tachyon.AvailableMethods.GLOBAL_CHECK, [
masked ? 'masked' : 'raw',
]),
showSingBoxConfig: async (masked = true) =>
callBaseMethod<unknown>(Forkop.AvailableMethods.SHOW_SING_BOX_CONFIG, [
callBaseMethod<unknown>(Tachyon.AvailableMethods.SHOW_SING_BOX_CONFIG, [
masked ? 'masked' : 'raw',
]),
checkLogs: async () =>
callBaseMethod<unknown>(Forkop.AvailableMethods.CHECK_LOGS),
callBaseMethod<unknown>(Tachyon.AvailableMethods.CHECK_LOGS),
checkSingBoxLogs: async () =>
callBaseMethod<unknown>(Forkop.AvailableMethods.CHECK_SING_BOX_LOGS),
callBaseMethod<unknown>(Tachyon.AvailableMethods.CHECK_SING_BOX_LOGS),
getSystemInfo: async () =>
callBaseMethod<Forkop.GetSystemInfo>(
Forkop.AvailableMethods.GET_SYSTEM_INFO,
callBaseMethod<Tachyon.GetSystemInfo>(
Tachyon.AvailableMethods.GET_SYSTEM_INFO,
),
getServerCapabilities: async () =>
callBaseMethod<Forkop.GetServerCapabilities>(
Forkop.AvailableMethods.GET_SERVER_CAPABILITIES,
callBaseMethod<Tachyon.GetServerCapabilities>(
Tachyon.AvailableMethods.GET_SERVER_CAPABILITIES,
),
getUiCapabilities: async () =>
callBaseMethod<Forkop.GetUiCapabilities>(
Forkop.AvailableMethods.GET_UI_CAPABILITIES,
callBaseMethod<Tachyon.GetUiCapabilities>(
Tachyon.AvailableMethods.GET_UI_CAPABILITIES,
),
getUiState: async () =>
callBaseMethod<Forkop.UiState>(
Forkop.AvailableMethods.GET_UI_STATE,
callBaseMethod<Tachyon.UiState>(
Tachyon.AvailableMethods.GET_UI_STATE,
[],
'/usr/bin/forkop',
'/usr/bin/tachyon',
{ timeout: GET_UI_STATE_RPC_TIMEOUT_MS },
),
serviceActionStart: async (action: Forkop.ServiceAction) => {
serviceActionStart: async (action: Tachyon.ServiceAction) => {
const response = await executeShellCommand({
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.SERVICE_ACTION_ASYNC, action],
command: '/usr/bin/tachyon',
args: [Tachyon.AvailableMethods.SERVICE_ACTION_ASYNC, action],
timeout: UI_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseUiActionStartResult(response);
@@ -387,12 +387,12 @@ export const ForkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Forkop.MethodSuccessResponse<Forkop.UiActionStartResult>;
} as Tachyon.MethodSuccessResponse<Tachyon.UiActionStartResult>;
},
serviceActionStatus: async (jobId: string) => {
const response = await executeShellCommand({
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.SERVICE_ACTION_STATUS, jobId],
command: '/usr/bin/tachyon',
args: [Tachyon.AvailableMethods.SERVICE_ACTION_STATUS, jobId],
timeout: UI_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseServiceActionState(response);
@@ -408,13 +408,13 @@ export const ForkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Forkop.MethodSuccessResponse<Forkop.ServiceActionState>;
} as Tachyon.MethodSuccessResponse<Tachyon.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 ForkopShellMethods.serviceActionStatus(jobId);
const response = await TachyonShellMethods.serviceActionStatus(jobId);
if (!response.success) {
return response;
@@ -430,18 +430,18 @@ export const ForkopShellMethods = {
return {
success: false,
error: _('Operation timed out'),
} as Forkop.MethodFailureResponse;
} as Tachyon.MethodFailureResponse;
},
latencyTestStart: async (
latencyType: Forkop.LatencyActionState['latency_type'],
latencyType: Tachyon.LatencyActionState['latency_type'],
section: string,
tag: string,
timeout?: string,
) => {
const response = await executeShellCommand({
command: '/usr/bin/forkop',
command: '/usr/bin/tachyon',
args: [
Forkop.AvailableMethods.LATENCY_TEST_ASYNC,
Tachyon.AvailableMethods.LATENCY_TEST_ASYNC,
latencyType,
section,
tag,
@@ -466,12 +466,12 @@ export const ForkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Forkop.MethodSuccessResponse<Forkop.UiActionStartResult>;
} as Tachyon.MethodSuccessResponse<Tachyon.UiActionStartResult>;
},
latencyTestStatus: async (jobId: string) => {
const response = await executeShellCommand({
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.LATENCY_TEST_STATUS, jobId],
command: '/usr/bin/tachyon',
args: [Tachyon.AvailableMethods.LATENCY_TEST_STATUS, jobId],
timeout: UI_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseLatencyActionState(response);
@@ -487,7 +487,7 @@ export const ForkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Forkop.MethodSuccessResponse<Forkop.LatencyActionState>;
} as Tachyon.MethodSuccessResponse<Tachyon.LatencyActionState>;
},
waitLatencyTestJob: async (jobId: string, startedAt = Date.now()) => {
const transientRpc = createTransientRpcGraceTracker(
@@ -497,7 +497,7 @@ export const ForkopShellMethods = {
while (Date.now() - startedAt < LATENCY_TEST_TIMEOUT_MS) {
await sleep(LATENCY_TEST_POLL_INTERVAL_MS);
const response = await ForkopShellMethods.latencyTestStatus(jobId);
const response = await TachyonShellMethods.latencyTestStatus(jobId);
if (!response.success) {
if (transientRpc.shouldContinue(response.error)) {
@@ -518,15 +518,15 @@ export const ForkopShellMethods = {
return {
success: false,
error: _('Operation timed out'),
} as Forkop.MethodFailureResponse;
} as Tachyon.MethodFailureResponse;
},
uiActionAck: async (
kind: 'service' | 'latency' | 'component' | 'subscription',
jobId: string,
) => {
const response = await executeShellCommand({
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.UI_ACTION_ACK, kind, jobId],
command: '/usr/bin/tachyon',
args: [Tachyon.AvailableMethods.UI_ACTION_ACK, kind, jobId],
timeout: UI_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseUiActionStartResult(response);
@@ -538,15 +538,15 @@ export const ForkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Forkop.MethodSuccessResponse<Forkop.UiActionStartResult>;
} as Tachyon.MethodSuccessResponse<Tachyon.UiActionStartResult>;
},
componentActionStart: async (
component: Forkop.ComponentName,
action: Forkop.ComponentAction,
component: Tachyon.ComponentName,
action: Tachyon.ComponentAction,
) => {
const response = await executeShellCommand({
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.COMPONENT_ACTION_ASYNC, component, action],
command: '/usr/bin/tachyon',
args: [Tachyon.AvailableMethods.COMPONENT_ACTION_ASYNC, component, action],
timeout: COMPONENT_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseComponentActionStartResult(response);
@@ -562,12 +562,12 @@ export const ForkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Forkop.MethodSuccessResponse<Forkop.ComponentActionStartResult>;
} as Tachyon.MethodSuccessResponse<Tachyon.ComponentActionStartResult>;
},
componentActionStatus: async (jobId: string) => {
const response = await executeShellCommand({
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.COMPONENT_ACTION_STATUS, jobId],
command: '/usr/bin/tachyon',
args: [Tachyon.AvailableMethods.COMPONENT_ACTION_STATUS, jobId],
timeout: COMPONENT_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseComponentActionResult(response);
@@ -579,16 +579,16 @@ export const ForkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Forkop.MethodSuccessResponse<Forkop.ComponentActionResult>;
} as Tachyon.MethodSuccessResponse<Tachyon.ComponentActionResult>;
},
componentUpdateCheckCache: async () =>
callBaseMethod<Forkop.ComponentUpdateCheckCache>(
Forkop.AvailableMethods.COMPONENT_UPDATE_CHECK_CACHE,
callBaseMethod<Tachyon.ComponentUpdateCheckCache>(
Tachyon.AvailableMethods.COMPONENT_UPDATE_CHECK_CACHE,
),
waitComponentActionJob: async (
jobId: string,
component: Forkop.ComponentName,
action: Forkop.ComponentAction,
component: Tachyon.ComponentName,
action: Tachyon.ComponentAction,
expectedLatestVersion?: string,
) => {
let selfUpdateVersionMatchedAt = 0;
@@ -608,7 +608,7 @@ export const ForkopShellMethods = {
return {
success: true,
data: stateResponse,
} as Forkop.MethodSuccessResponse<Forkop.ComponentActionResult>;
} as Tachyon.MethodSuccessResponse<Tachyon.ComponentActionResult>;
}
if (
@@ -621,8 +621,8 @@ export const ForkopShellMethods = {
lastStatusRefreshAt = Date.now();
const statusResponse = await executeShellCommand({
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.COMPONENT_ACTION_STATUS, jobId],
command: '/usr/bin/tachyon',
args: [Tachyon.AvailableMethods.COMPONENT_ACTION_STATUS, jobId],
timeout: COMPONENT_ACTION_RPC_TIMEOUT_MS,
});
const parsedResponse = parseComponentActionResult(statusResponse);
@@ -644,9 +644,9 @@ export const ForkopShellMethods = {
continue;
}
if (component === 'forkop' && action === 'install') {
if (component === 'tachyon' && action === 'install') {
const installedVersion = expectedLatestVersion
? await readForkopVersion()
? await readTachyonVersion()
: '';
if (
@@ -667,13 +667,13 @@ export const ForkopShellMethods = {
success: true,
component,
action,
message: translate('Forkop has been installed'),
message: translate('Tachyon has been installed'),
current_version: installedVersion,
latest_version: expectedLatestVersion,
changed: true,
status: 'latest',
},
} as Forkop.MethodSuccessResponse<Forkop.ComponentActionResult>;
} as Tachyon.MethodSuccessResponse<Tachyon.ComponentActionResult>;
}
}
@@ -691,17 +691,17 @@ export const ForkopShellMethods = {
return {
success: true,
data: parsedResponse,
} as Forkop.MethodSuccessResponse<Forkop.ComponentActionResult>;
} as Tachyon.MethodSuccessResponse<Tachyon.ComponentActionResult>;
}
},
subscriptionUpdateStart: async (section?: string, sourceIndex?: number) => {
const startArgs = [
Forkop.AvailableMethods.SUBSCRIPTION_UPDATE_ASYNC,
Tachyon.AvailableMethods.SUBSCRIPTION_UPDATE_ASYNC,
...(section ? [section] : []),
...(section && sourceIndex !== undefined ? [String(sourceIndex)] : []),
];
const response = await executeShellCommand({
command: '/usr/bin/forkop',
command: '/usr/bin/tachyon',
args: startArgs,
timeout: SUBSCRIPTION_UPDATE_RPC_TIMEOUT_MS,
});
@@ -718,18 +718,18 @@ export const ForkopShellMethods = {
parsedResponse?.message ||
response.stderr ||
_('Subscription update failed'),
} as Forkop.MethodFailureResponse;
} as Tachyon.MethodFailureResponse;
}
return {
success: true,
data: parsedResponse,
} as Forkop.MethodSuccessResponse<Forkop.SubscriptionUpdateStartResult>;
} as Tachyon.MethodSuccessResponse<Tachyon.SubscriptionUpdateStartResult>;
},
subscriptionUpdateStatus: async (jobId: string) => {
const response = await executeShellCommand({
command: '/usr/bin/forkop',
args: [Forkop.AvailableMethods.SUBSCRIPTION_UPDATE_STATUS, jobId],
command: '/usr/bin/tachyon',
args: [Tachyon.AvailableMethods.SUBSCRIPTION_UPDATE_STATUS, jobId],
timeout: SUBSCRIPTION_UPDATE_RPC_TIMEOUT_MS,
});
const parsedResponse = parseSubscriptionUpdateJobState(response);
@@ -738,13 +738,13 @@ export const ForkopShellMethods = {
return {
success: false,
error: response.stderr || _('Subscription update failed'),
} as Forkop.MethodFailureResponse;
} as Tachyon.MethodFailureResponse;
}
return {
success: true,
data: parsedResponse,
} as Forkop.MethodSuccessResponse<Forkop.SubscriptionUpdateJobState>;
} as Tachyon.MethodSuccessResponse<Tachyon.SubscriptionUpdateJobState>;
},
waitSubscriptionUpdateJob: async (jobId: string) => {
const transientRpc = createTransientRpcGraceTracker(
@@ -754,7 +754,7 @@ export const ForkopShellMethods = {
while (true) {
await sleep(SUBSCRIPTION_UPDATE_POLL_INTERVAL_MS);
const response = await ForkopShellMethods.subscriptionUpdateStatus(jobId);
const response = await TachyonShellMethods.subscriptionUpdateStatus(jobId);
if (!response.success) {
if (transientRpc.shouldContinue(response.error)) {
@@ -8,7 +8,7 @@ vi.mock('../../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { Forkop } from '../../../types';
import { Tachyon } from '../../../types';
import { callBaseMethod } from '../callBaseMethod';
describe('callBaseMethod', () => {
@@ -24,7 +24,7 @@ describe('callBaseMethod', () => {
});
const response = await callBaseMethod(
Forkop.AvailableMethods.CHECK_DNS_AVAILABLE,
Tachyon.AvailableMethods.CHECK_DNS_AVAILABLE,
);
expect(response).toEqual({
@@ -41,9 +41,9 @@ describe('callBaseMethod', () => {
});
const response = await callBaseMethod(
Forkop.AvailableMethods.GLOBAL_CHECK,
Tachyon.AvailableMethods.GLOBAL_CHECK,
[],
'/usr/bin/forkop',
'/usr/bin/tachyon',
{ allowNonZeroWithStdout: true },
);
@@ -61,14 +61,14 @@ describe('callBaseMethod', () => {
});
await callBaseMethod(
Forkop.AvailableMethods.GLOBAL_CHECK,
Tachyon.AvailableMethods.GLOBAL_CHECK,
[],
'/usr/bin/forkop',
'/usr/bin/tachyon',
{ timeout: 60000 },
);
expect(mocks.executeShellCommand).toHaveBeenCalledWith({
command: '/usr/bin/forkop',
command: '/usr/bin/tachyon',
args: ['global_check'],
timeout: 60000,
});
@@ -9,9 +9,9 @@ vi.mock('../../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { ForkopShellMethods } from '../index';
import { TachyonShellMethods } from '../index';
describe('ForkopShellMethods.componentAction', () => {
describe('TachyonShellMethods.componentAction', () => {
beforeEach(() => {
vi.useFakeTimers();
mocks.executeShellCommand.mockReset();
@@ -26,7 +26,7 @@ describe('ForkopShellMethods.componentAction', () => {
vi.unstubAllGlobals();
});
it('does not fail Forkop self-update when status polling disappears after package replacement', async () => {
it('does not fail Tachyon 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('ForkopShellMethods.componentAction', () => {
});
});
const responsePromise = ForkopShellMethods.waitComponentActionJob(
const responsePromise = TachyonShellMethods.waitComponentActionJob(
'job-1',
'forkop',
'tachyon',
'install',
'0.7.17.11',
);
@@ -65,9 +65,9 @@ describe('ForkopShellMethods.componentAction', () => {
success: true,
data: {
success: true,
component: 'forkop',
component: 'tachyon',
action: 'install',
message: 'Forkop has been installed',
message: 'Tachyon has been installed',
current_version: '0.7.17.11',
latest_version: '0.7.17.11',
changed: true,
@@ -87,7 +87,7 @@ describe('ForkopShellMethods.componentAction', () => {
});
await expect(
ForkopShellMethods.componentActionStart('zapret', 'install'),
TachyonShellMethods.componentActionStart('zapret', 'install'),
).resolves.toEqual({
success: false,
error: 'Another component action is already running',
@@ -147,7 +147,7 @@ describe('ForkopShellMethods.componentAction', () => {
});
});
const responsePromise = ForkopShellMethods.waitComponentActionJob(
const responsePromise = TachyonShellMethods.waitComponentActionJob(
'job-1',
'sing_box',
'check_update',
@@ -201,7 +201,7 @@ describe('ForkopShellMethods.componentAction', () => {
return Promise.resolve({
stdout: JSON.stringify({
service: {
forkop: {
tachyon: {
running: 0,
enabled: 1,
status: 'stopped but enabled',
@@ -251,7 +251,7 @@ describe('ForkopShellMethods.componentAction', () => {
});
});
const responsePromise = ForkopShellMethods.waitComponentActionJob(
const responsePromise = TachyonShellMethods.waitComponentActionJob(
'job-1',
'sing_box',
'install_extended',
@@ -274,7 +274,7 @@ describe('ForkopShellMethods.componentAction', () => {
},
});
expect(mocks.executeShellCommand).toHaveBeenCalledWith({
command: '/usr/bin/forkop',
command: '/usr/bin/tachyon',
args: ['get_ui_state'],
timeout: 3000,
});
@@ -321,7 +321,7 @@ describe('ForkopShellMethods.componentAction', () => {
});
});
const responsePromise = ForkopShellMethods.waitComponentActionJob(
const responsePromise = TachyonShellMethods.waitComponentActionJob(
'job-1',
'zapret',
'check_update',
@@ -8,9 +8,9 @@ vi.mock('../../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { ForkopShellMethods } from '../index';
import { TachyonShellMethods } from '../index';
describe('ForkopShellMethods.latencyAction', () => {
describe('TachyonShellMethods.latencyAction', () => {
beforeEach(() => {
vi.useFakeTimers();
mocks.executeShellCommand.mockReset();
@@ -52,7 +52,7 @@ describe('ForkopShellMethods.latencyAction', () => {
});
});
const responsePromise = ForkopShellMethods.waitLatencyTestJob('job-1');
const responsePromise = TachyonShellMethods.waitLatencyTestJob('job-1');
await vi.advanceTimersByTimeAsync(2000);
@@ -76,7 +76,7 @@ describe('ForkopShellMethods.latencyAction', () => {
code: 0,
});
await ForkopShellMethods.latencyTestStart(
await TachyonShellMethods.latencyTestStart(
'proxy',
'AWG',
'AWG-out',
@@ -84,7 +84,7 @@ describe('ForkopShellMethods.latencyAction', () => {
);
expect(mocks.executeShellCommand).toHaveBeenCalledWith({
command: '/usr/bin/forkop',
command: '/usr/bin/tachyon',
args: ['latency_test_async', 'proxy', 'AWG', 'AWG-out', '10000'],
timeout: 15000,
});
@@ -8,9 +8,9 @@ vi.mock('../../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { ForkopShellMethods } from '../index';
import { TachyonShellMethods } from '../index';
describe('ForkopShellMethods.serviceAction', () => {
describe('TachyonShellMethods.serviceAction', () => {
beforeEach(() => {
vi.useFakeTimers();
mocks.executeShellCommand.mockReset();
@@ -44,7 +44,7 @@ describe('ForkopShellMethods.serviceAction', () => {
});
});
const responsePromise = ForkopShellMethods.waitServiceActionJob('job-1');
const responsePromise = TachyonShellMethods.waitServiceActionJob('job-1');
await vi.advanceTimersByTimeAsync(1000);
@@ -72,7 +72,7 @@ describe('ForkopShellMethods.serviceAction', () => {
});
await expect(
ForkopShellMethods.serviceActionStart('restart'),
TachyonShellMethods.serviceActionStart('restart'),
).resolves.toEqual({
success: false,
error: 'Another service action is already running',
@@ -8,9 +8,9 @@ vi.mock('../../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { ForkopShellMethods } from '../index';
import { TachyonShellMethods } from '../index';
describe('ForkopShellMethods.subscriptionUpdate', () => {
describe('TachyonShellMethods.subscriptionUpdate', () => {
beforeEach(() => {
vi.useFakeTimers();
mocks.executeShellCommand.mockReset();
@@ -55,7 +55,7 @@ describe('ForkopShellMethods.subscriptionUpdate', () => {
});
await expect(
ForkopShellMethods.subscriptionUpdateStart('main'),
TachyonShellMethods.subscriptionUpdateStart('main'),
).resolves.toEqual({
success: true,
data: {
@@ -66,7 +66,7 @@ describe('ForkopShellMethods.subscriptionUpdate', () => {
});
const responsePromise =
ForkopShellMethods.waitSubscriptionUpdateJob('job-1');
TachyonShellMethods.waitSubscriptionUpdateJob('job-1');
await vi.advanceTimersByTimeAsync(1500);
@@ -80,12 +80,12 @@ describe('ForkopShellMethods.subscriptionUpdate', () => {
},
});
expect(mocks.executeShellCommand).toHaveBeenNthCalledWith(1, {
command: '/usr/bin/forkop',
command: '/usr/bin/tachyon',
args: ['subscription_update_async', 'main'],
timeout: 15000,
});
expect(mocks.executeShellCommand).toHaveBeenNthCalledWith(2, {
command: '/usr/bin/forkop',
command: '/usr/bin/tachyon',
args: ['subscription_update_status', 'job-1'],
timeout: 15000,
});
@@ -102,7 +102,7 @@ describe('ForkopShellMethods.subscriptionUpdate', () => {
});
await expect(
ForkopShellMethods.subscriptionUpdateStart('main'),
TachyonShellMethods.subscriptionUpdateStart('main'),
).resolves.toEqual({
success: false,
error: 'Subscription update is already running',
@@ -134,7 +134,7 @@ describe('ForkopShellMethods.subscriptionUpdate', () => {
});
const responsePromise =
ForkopShellMethods.waitSubscriptionUpdateJob('job-1');
TachyonShellMethods.waitSubscriptionUpdateJob('job-1');
await vi.advanceTimersByTimeAsync(1500);
@@ -189,7 +189,7 @@ describe('ForkopShellMethods.subscriptionUpdate', () => {
});
const responsePromise =
ForkopShellMethods.waitSubscriptionUpdateJob('job-1');
TachyonShellMethods.waitSubscriptionUpdateJob('job-1');
await vi.advanceTimersByTimeAsync(10 * 60 * 1000 + 3000);
@@ -239,7 +239,7 @@ describe('ForkopShellMethods.subscriptionUpdate', () => {
});
const responsePromise =
ForkopShellMethods.waitSubscriptionUpdateJob('job-1');
TachyonShellMethods.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 { ForkopLogWatcher } from './forkopLogWatcher.service';
import { TachyonLogWatcher } from './tachyonLogWatcher.service';
import {
getForkopLogNotification,
getTachyonLogNotification,
LogNotificationDeduper,
ForkopLogNotification,
TachyonLogNotification,
} from './logNotificationDeduper.service';
import { ForkopShellMethods } from '../methods';
import { TachyonShellMethods } 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> = {
forkop: 'Forkop',
tachyon: 'Tachyon',
sing_box: 'sing-box',
zapret: 'Zapret',
zapret2: 'Zapret2',
@@ -33,7 +33,7 @@ function componentDisplayName(component: string) {
return names[component] || component;
}
function showLogNotification(notification: ForkopLogNotification) {
function showLogNotification(notification: TachyonLogNotification) {
if (notification.kind === 'component-update') {
const message = _('New version %s is available for %s')
.replace('%s', notification.version)
@@ -49,7 +49,7 @@ function showLogNotification(notification: ForkopLogNotification) {
}
ui.addNotification(
_('Forkop Error'),
_('Tachyon Error'),
E('div', {}, notification.line),
'error',
'fkp-log-error-notification',
@@ -67,12 +67,12 @@ export function coreService(options: CoreServiceOptions = {}) {
});
});
const watcher = ForkopLogWatcher.getInstance();
const watcher = TachyonLogWatcher.getInstance();
const logNotificationDeduper = new LogNotificationDeduper();
watcher.init(
async () => {
const logs = await ForkopShellMethods.checkLogs();
const logs = await TachyonShellMethods.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 = getForkopLogNotification(line);
const notification = getTachyonLogNotification(line);
if (notification) {
showLogNotification(notification);
}
@@ -1,7 +1,7 @@
const LOG_NOTIFICATION_STORAGE_KEY = 'forkop:shown-log-error-notifications:v1';
const LOG_NOTIFICATION_STORAGE_KEY = 'tachyon:shown-log-error-notifications:v1';
const MAX_STORED_LOG_NOTIFICATIONS = 500;
export type ForkopLogNotification =
export type TachyonLogNotification =
| { kind: 'error'; line: string }
| {
kind: 'component-update';
@@ -60,15 +60,15 @@ export function isErrorLogLine(line: string) {
return lower.includes('[error]') || lower.includes('[fatal]');
}
export function getForkopLogNotification(
export function getTachyonLogNotification(
line: string,
): ForkopLogNotification | null {
): TachyonLogNotification | null {
if (isErrorLogLine(line)) {
return { kind: 'error', line };
}
const update = line.match(
/\[component-update\]\s+(forkop|sing_box|zapret|zapret2|byedpi)\s+(\S+)/i,
/\[component-update\]\s+(tachyon|sing_box|zapret|zapret2|byedpi)\s+(\S+)/i,
);
if (!update) {
@@ -97,7 +97,7 @@ export class LogNotificationDeduper {
}
shouldNotify(line: string) {
if (!getForkopLogNotification(line)) {
if (!getTachyonLogNotification(line)) {
return false;
}
@@ -1,17 +1,17 @@
import { ForkopShellMethods } from '../methods';
import { Forkop } from '../types';
import { TachyonShellMethods } from '../methods';
import { Tachyon } 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: Forkop.UiState) => void;
type RuntimeUiStateListener = (uiState: Tachyon.UiState) => void;
let runtimeUiStateRefreshPromise: Promise<Forkop.UiState | undefined> | null =
let runtimeUiStateRefreshPromise: Promise<Tachyon.UiState | undefined> | null =
null;
let lastRuntimeUiStateRefreshAt = 0;
let lastRuntimeUiState: Forkop.UiState | undefined;
let lastRuntimeUiState: Tachyon.UiState | undefined;
let runtimeStateResumeRefreshRegistered = false;
let runtimeStatePollTimer: ReturnType<typeof setTimeout> | null = null;
let runtimeStatePollingStarted = false;
@@ -26,7 +26,7 @@ function isDocumentVisible() {
);
}
function hasRunningAction(uiState: Forkop.UiState) {
function hasRunningAction(uiState: Tachyon.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: Forkop.UiState) {
function notifyRuntimeUiStateListeners(uiState: Tachyon.UiState) {
for (const listener of runtimeUiStateListeners) {
try {
listener(uiState);
@@ -69,7 +69,7 @@ function notifyRuntimeUiStateListeners(uiState: Forkop.UiState) {
export async function refreshRuntimeUiState({
force = false,
}: { force?: boolean } = {}): Promise<Forkop.UiState | undefined> {
}: { force?: boolean } = {}): Promise<Tachyon.UiState | undefined> {
if (!isDocumentVisible()) {
return undefined;
}
@@ -88,7 +88,7 @@ export async function refreshRuntimeUiState({
lastRuntimeUiStateRefreshAt = now;
const promise = ForkopShellMethods.getUiState()
const promise = TachyonShellMethods.getUiState()
.then((response) => {
if (!response.success) {
return undefined;
@@ -1,4 +1,4 @@
import { Forkop } from '../types';
import { Tachyon } 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;
forkopRunning: number;
forkopEnabled: number;
forkopStatus: string;
tachyonRunning: number;
tachyonEnabled: number;
tachyonStatus: string;
};
};
sectionsWidget: {
loading: boolean;
failed: boolean;
data: Forkop.OutboundGroup[];
data: Tachyon.OutboundGroup[];
latencyFetchingSections: Record<string, boolean>;
latencyProgressSections: Record<string, Forkop.LatencyActionProgress>;
latencyProgressSections: Record<string, Tachyon.LatencyActionProgress>;
selectorSwitchingSections: Record<string, string>;
subscriptionUpdatingSections: Record<string, boolean>;
};
@@ -190,8 +190,8 @@ export interface StoreType {
loading: boolean;
loaded: boolean;
providerInfoLoaded: boolean;
forkop_version: string;
forkop_latest_version: string;
tachyon_version: string;
tachyon_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: {
forkopCheck: { loading: boolean };
forkopInstall: { loading: boolean };
tachyonCheck: { loading: boolean };
tachyonInstall: { loading: boolean };
singBoxCheck: { loading: boolean };
singBoxInstall: { loading: boolean };
singBoxInstallExtended: { loading: boolean };
@@ -228,7 +228,7 @@ export interface StoreType {
byedpiRemove: { loading: boolean };
};
updatesChecks: Record<
Forkop.ComponentName,
Tachyon.ComponentName,
{
status: 'latest' | 'outdated' | 'dev' | null;
latest_version: string;
@@ -262,9 +262,9 @@ const initialStore: StoreType = {
failed: false,
data: {
singbox: 0,
forkopRunning: 0,
forkopEnabled: 0,
forkopStatus: '',
tachyonRunning: 0,
tachyonEnabled: 0,
tachyonStatus: '',
},
},
sectionsWidget: {
@@ -1,4 +1,4 @@
import { ForkopShellMethods } from '../methods';
import { TachyonShellMethods } 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,
forkop_version: _('unknown'),
forkop_latest_version: _('unknown'),
tachyon_version: _('unknown'),
tachyon_latest_version: _('unknown'),
luci_app_version: _('unknown'),
sing_box_version: _('unknown'),
sing_box_extended: 0,
@@ -65,7 +65,7 @@ export async function ensureSystemInfo({
const promise = (async () => {
try {
const systemInfo = await ForkopShellMethods.getSystemInfo();
const systemInfo = await TachyonShellMethods.getSystemInfo();
if (requestId !== latestSystemInfoRequestId) {
return store.get().diagnosticsSystemInfo;
@@ -2,14 +2,14 @@ import { logger } from './logger.service';
type LogFetcher = () => Promise<string> | string;
interface ForkopLogWatcherOptions {
interface TachyonLogWatcherOptions {
intervalMs?: number;
onNewLog?: (line: string) => void;
maxTrackedLines?: number;
}
export class ForkopLogWatcher {
private static instance: ForkopLogWatcher;
export class TachyonLogWatcher {
private static instance: TachyonLogWatcher;
private fetcher?: LogFetcher;
private onNewLog?: (line: string) => void;
private intervalMs = 5000;
@@ -29,21 +29,21 @@ export class ForkopLogWatcher {
}
}
static getInstance(): ForkopLogWatcher {
if (!ForkopLogWatcher.instance) {
ForkopLogWatcher.instance = new ForkopLogWatcher();
static getInstance(): TachyonLogWatcher {
if (!TachyonLogWatcher.instance) {
TachyonLogWatcher.instance = new TachyonLogWatcher();
}
return ForkopLogWatcher.instance;
return TachyonLogWatcher.instance;
}
init(fetcher: LogFetcher, options?: ForkopLogWatcherOptions): void {
init(fetcher: LogFetcher, options?: TachyonLogWatcherOptions): void {
this.fetcher = fetcher;
this.onNewLog = options?.onNewLog;
this.intervalMs = options?.intervalMs ?? 5000;
this.maxTrackedLines = options?.maxTrackedLines ?? 500;
this.lastLines = new Set();
logger.info(
'[ForkopLogWatcher]',
'[TachyonLogWatcher]',
`initialized (interval: ${this.intervalMs}ms)`,
);
}
@@ -54,18 +54,18 @@ export class ForkopLogWatcher {
async checkOnce(): Promise<void> {
if (!this.fetcher) {
logger.warn('[ForkopLogWatcher]', 'fetcher not found');
logger.warn('[TachyonLogWatcher]', 'fetcher not found');
return;
}
if (this.paused) {
logger.debug('[ForkopLogWatcher]', 'skipped check — tab not visible');
logger.debug('[TachyonLogWatcher]', 'skipped check — tab not visible');
return;
}
if (this.checking) {
logger.debug(
'[ForkopLogWatcher]',
'[TachyonLogWatcher]',
'skipped check — previous check is running',
);
return;
@@ -92,7 +92,7 @@ export class ForkopLogWatcher {
);
}
} catch (err) {
logger.error('[ForkopLogWatcher]', 'failed to read logs:', err);
logger.error('[TachyonLogWatcher]', 'failed to read logs:', err);
} finally {
this.checking = false;
}
@@ -101,7 +101,7 @@ export class ForkopLogWatcher {
start(): void {
if (this.running) return;
if (!this.fetcher) {
logger.warn('[ForkopLogWatcher]', 'attempted to start without fetcher');
logger.warn('[TachyonLogWatcher]', 'attempted to start without fetcher');
return;
}
@@ -109,7 +109,7 @@ export class ForkopLogWatcher {
void this.checkOnce();
this.timer = setInterval(() => this.checkOnce(), this.intervalMs);
logger.info(
'[ForkopLogWatcher]',
'[TachyonLogWatcher]',
`started (interval: ${this.intervalMs}ms)`,
);
}
@@ -118,25 +118,25 @@ export class ForkopLogWatcher {
if (!this.running) return;
this.running = false;
if (this.timer) clearInterval(this.timer);
logger.info('[ForkopLogWatcher]', 'stopped');
logger.info('[TachyonLogWatcher]', 'stopped');
}
pause(): void {
if (!this.running || this.paused) return;
this.paused = true;
logger.info('[ForkopLogWatcher]', 'paused (tab not visible)');
logger.info('[TachyonLogWatcher]', 'paused (tab not visible)');
}
resume(): void {
if (!this.running || !this.paused) return;
this.paused = false;
logger.info('[ForkopLogWatcher]', 'resumed (tab active)');
logger.info('[TachyonLogWatcher]', 'resumed (tab active)');
void this.checkOnce();
}
reset(): void {
this.lastLines = new Set();
this.checking = false;
logger.info('[ForkopLogWatcher]', 'log history reset');
logger.info('[TachyonLogWatcher]', 'log history reset');
}
}
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
import {
getLogNotificationKey,
getForkopLogNotification,
getTachyonLogNotification,
isErrorLogLine,
LogNotificationDeduper,
} from '../logNotificationDeduper.service';
@@ -37,44 +37,44 @@ class MemoryStorage implements Storage {
describe('LogNotificationDeduper', () => {
it('accepts error, fatal, and component update log lines', () => {
expect(isErrorLogLine('forkop: [info] ok')).toBe(false);
expect(isErrorLogLine('forkop: [error] failed')).toBe(true);
expect(isErrorLogLine('forkop: [fatal] failed')).toBe(true);
expect(isErrorLogLine('tachyon: [info] ok')).toBe(false);
expect(isErrorLogLine('tachyon: [error] failed')).toBe(true);
expect(isErrorLogLine('tachyon: [fatal] failed')).toBe(true);
expect(
getForkopLogNotification(
'forkop: [info] [component-update] zapret2 v1.2.3',
getTachyonLogNotification(
'tachyon: [info] [component-update] zapret2 v1.2.3',
),
).toEqual({
kind: 'component-update',
line: 'forkop: [info] [component-update] zapret2 v1.2.3',
line: 'tachyon: [info] [component-update] zapret2 v1.2.3',
component: 'zapret2',
version: 'v1.2.3',
});
expect(getForkopLogNotification('forkop: [info] ok')).toBeNull();
expect(getTachyonLogNotification('tachyon: [info] ok')).toBeNull();
});
it('dedupes already shown log lines through session storage', () => {
const storage = new MemoryStorage();
const first = new LogNotificationDeduper(storage);
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('tachyon: [error] failed')).toBe(true);
expect(first.shouldNotify('tachyon: [error] failed')).toBe(false);
expect(first.shouldNotify('tachyon: [error] another failure')).toBe(true);
expect(
first.shouldNotify('forkop: [info] [component-update] forkop 1.2.3'),
first.shouldNotify('tachyon: [info] [component-update] tachyon 1.2.3'),
).toBe(true);
const afterReload = new LogNotificationDeduper(storage);
expect(afterReload.shouldNotify('forkop: [error] failed')).toBe(false);
expect(afterReload.shouldNotify('forkop: [fatal] fatal failure')).toBe(
expect(afterReload.shouldNotify('tachyon: [error] failed')).toBe(false);
expect(afterReload.shouldNotify('tachyon: [fatal] fatal failure')).toBe(
true,
);
});
it('keeps the full log line as the replay key', () => {
expect(getLogNotificationKey(' Jun 06 forkop: [error] failed ')).toBe(
'Jun 06 forkop: [error] failed',
expect(getLogNotificationKey(' Jun 06 tachyon: [error] failed ')).toBe(
'Jun 06 tachyon: [error] failed',
);
});
});
@@ -8,7 +8,7 @@ vi.mock('../../../helpers', () => ({
executeShellCommand: mocks.executeShellCommand,
}));
import { Forkop } from '../../types';
import { Tachyon } from '../../types';
import { store } from '../store.service';
import {
getCachedRuntimeUiState,
@@ -19,10 +19,10 @@ import {
function createUiState(
status = 'running & enabled',
running = 1,
): Forkop.UiState {
): Tachyon.UiState {
return {
service: {
forkop: {
tachyon: {
running,
enabled: 1,
status,
@@ -73,14 +73,14 @@ describe('refreshRuntimeUiState', () => {
);
expect(mocks.executeShellCommand).toHaveBeenCalledWith({
command: '/usr/bin/forkop',
command: '/usr/bin/tachyon',
args: ['get_ui_state'],
timeout: 3000,
});
expect(store.get().servicesInfoWidget.data).toMatchObject({
forkopRunning: 0,
forkopEnabled: 1,
forkopStatus: 'stopped but enabled',
tachyonRunning: 0,
tachyonEnabled: 1,
tachyonStatus: 'stopped but enabled',
});
});
@@ -1,9 +1,9 @@
import { beforeEach, describe, expect, it } from 'vitest';
import { ForkopLogWatcher } from '../forkopLogWatcher.service';
import { TachyonLogWatcher } from '../tachyonLogWatcher.service';
describe('ForkopLogWatcher', () => {
const watcher = ForkopLogWatcher.getInstance();
describe('TachyonLogWatcher', () => {
const watcher = TachyonLogWatcher.getInstance();
let rawLogs = '';
let seenLines: string[] = [];
@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it } from 'vitest';
import { applyUiStateToStore } from '../uiState.service';
import { store } from '../store.service';
import { Forkop } from '../../types';
import { Tachyon } from '../../types';
import {
clearLocalActionOverlay,
setLocalComponentAction,
@@ -11,12 +11,12 @@ import {
} from '../localActionOverlay.service';
function createUiState(
actions: Partial<Forkop.UiState['actions']> = {},
capabilities: Partial<Forkop.UiState['capabilities']> = {},
): Forkop.UiState {
actions: Partial<Tachyon.UiState['actions']> = {},
capabilities: Partial<Tachyon.UiState['capabilities']> = {},
): Tachyon.UiState {
return {
service: {
forkop: {
tachyon: {
running: 0,
enabled: 1,
status: 'starting',
@@ -114,9 +114,9 @@ describe('applyUiStateToStore', () => {
loading: false,
failed: false,
data: {
forkopEnabled: 1,
forkopRunning: 0,
forkopStatus: 'starting',
tachyonEnabled: 1,
tachyonRunning: 0,
tachyonStatus: 'starting',
},
});
expect(state.diagnosticsSystemInfo).toMatchObject({
@@ -1,5 +1,5 @@
const UI_ACTION_NOTIFICATION_STORAGE_KEY =
'forkop:owned-ui-action-notifications:v1';
'tachyon: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 { Forkop } from '../types';
import type { Tachyon } from '../types';
import { getLocalActionOverlay } from './localActionOverlay.service';
import { store } from './store.service';
import type { StoreType } from './store.service';
type UiActionMap = Partial<Forkop.UiState['actions']>;
type UiActionMap = Partial<Tachyon.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 {
forkopCheck: { loading: false },
forkopInstall: { loading: false },
tachyonCheck: { loading: false },
tachyonInstall: { loading: false },
singBoxCheck: { loading: false },
singBoxInstall: { loading: false },
singBoxInstallExtended: { loading: false },
@@ -43,8 +43,8 @@ function getEmptyDiagnosticsActions(): StoreType['diagnosticsActions'] {
}
function normalizeLatencyProgress(
progress?: Forkop.LatencyActionProgress,
): Forkop.LatencyActionProgress | undefined {
progress?: Tachyon.LatencyActionProgress,
): Tachyon.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: Forkop.UiState) {
function applyServiceState(uiState: Tachyon.UiState) {
const currentSystemInfo = store.get().diagnosticsSystemInfo;
const nextSystemInfo = {
...currentSystemInfo,
@@ -88,9 +88,9 @@ function applyServiceState(uiState: Forkop.UiState) {
failed: false,
data: {
singbox: uiState.service.sing_box.running,
forkopRunning: uiState.service.forkop.running,
forkopEnabled: uiState.service.forkop.enabled,
forkopStatus: uiState.service.forkop.status,
tachyonRunning: uiState.service.tachyon.running,
tachyonEnabled: uiState.service.tachyon.enabled,
tachyonStatus: uiState.service.tachyon.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, Forkop.LatencyActionProgress> =
const latencyProgressSections: Record<string, Tachyon.LatencyActionProgress> =
{};
const updatesActions = getEmptyUpdatesActions();
const diagnosticsActions = getEmptyDiagnosticsActions();
@@ -188,7 +188,7 @@ function applyActionState(actions: UiActionMap = {}) {
});
}
export function applyUiStateToStore(uiState: Forkop.UiState) {
export function applyUiStateToStore(uiState: Tachyon.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 { CustomForkopMethods, ForkopShellMethods } from '../../methods';
import { CustomTachyonMethods, TachyonShellMethods } 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 { Forkop } from '../../types';
import { Tachyon } from '../../types';
import {
getCachedRuntimeUiState,
refreshRuntimeUiState,
@@ -79,7 +79,7 @@ async function fetchDashboardSectionsOnce(mountId: number) {
});
try {
const { data, success } = await CustomForkopMethods.getDashboardSections();
const { data, success } = await CustomTachyonMethods.getDashboardSections();
if (
!dashboardMounted ||
@@ -219,7 +219,7 @@ function setLatencyFetching(
sectionName: string,
fetching: boolean,
local = false,
progress?: Forkop.LatencyActionProgress,
progress?: Tachyon.LatencyActionProgress,
) {
if (local || !fetching) {
setLocalLatencyAction(sectionName, fetching && local);
@@ -255,7 +255,7 @@ function setLatencyFetching(
async function completeSubscriptionUpdateJob(
jobId: string,
sectionName: string,
response: Forkop.MethodResponse<Forkop.SubscriptionUpdateJobState>,
response: Tachyon.MethodResponse<Tachyon.SubscriptionUpdateJobState>,
) {
if (pageUnloading) {
setSubscriptionUpdating(sectionName, false);
@@ -287,7 +287,7 @@ async function completeSubscriptionUpdateJob(
setSubscriptionUpdating(sectionName, false);
if (jobId && response.success) {
void ForkopShellMethods.uiActionAck('subscription', jobId);
void TachyonShellMethods.uiActionAck('subscription', jobId);
}
if (failed) {
@@ -305,7 +305,7 @@ async function completeSubscriptionUpdateJob(
}
async function followSubscriptionUpdateState(
state: Forkop.SubscriptionUpdateJobState,
state: Tachyon.SubscriptionUpdateJobState,
) {
const jobId = state.job_id;
const sectionName = state.section || '';
@@ -325,11 +325,11 @@ async function followSubscriptionUpdateState(
try {
const response = state.running
? await ForkopShellMethods.waitSubscriptionUpdateJob(jobId)
? await TachyonShellMethods.waitSubscriptionUpdateJob(jobId)
: ({
success: true,
data: state,
} as Forkop.MethodSuccessResponse<Forkop.SubscriptionUpdateJobState>);
} as Tachyon.MethodSuccessResponse<Tachyon.SubscriptionUpdateJobState>);
await completeSubscriptionUpdateJob(jobId, sectionName, response);
} catch (error) {
@@ -366,13 +366,13 @@ async function completeLatencyTestJob(jobId: string, sectionName: string) {
}
if (jobId) {
void ForkopShellMethods.uiActionAck('latency', jobId);
void TachyonShellMethods.uiActionAck('latency', jobId);
}
void fetchDashboardSections({ force: true });
}
async function followLatencyTestState(state: Forkop.LatencyActionState) {
async function followLatencyTestState(state: Tachyon.LatencyActionState) {
const jobId = state.job_id;
const sectionName = state.section || '';
@@ -391,7 +391,7 @@ async function followLatencyTestState(state: Forkop.LatencyActionState) {
try {
if (state.running) {
await ForkopShellMethods.waitLatencyTestJob(jobId);
await TachyonShellMethods.waitLatencyTestJob(jobId);
}
await completeLatencyTestJob(jobId, sectionName);
@@ -405,13 +405,13 @@ async function followLatencyTestState(state: Forkop.LatencyActionState) {
}
}
function followDashboardActionsFromUiState(uiState: Forkop.UiState) {
function followDashboardActionsFromUiState(uiState: Tachyon.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 ForkopShellMethods.uiActionAck('subscription', state.job_id);
void TachyonShellMethods.uiActionAck('subscription', state.job_id);
}
}
@@ -420,7 +420,7 @@ function followDashboardActionsFromUiState(uiState: Forkop.UiState) {
void followLatencyTestState(state);
} else if (state.job_id && !handledLatencyJobs.has(state.job_id)) {
handledLatencyJobs.add(state.job_id);
void ForkopShellMethods.uiActionAck('latency', state.job_id);
void TachyonShellMethods.uiActionAck('latency', state.job_id);
}
}
}
@@ -571,7 +571,7 @@ function getDashboardServiceAvailability() {
return getServiceAvailability({
loading: service.loading,
failed: service.failed,
running: service.data.forkopRunning,
running: service.data.tachyonRunning,
});
}
@@ -646,7 +646,7 @@ async function handleChooseOutbound(
setSelectorSwitching(sectionName, tag);
try {
await ForkopShellMethods.setClashApiGroupProxy(selector, tag);
await TachyonShellMethods.setClashApiGroupProxy(selector, tag);
await fetchDashboardSections({ force: true });
} finally {
setSelectorSwitching(sectionName);
@@ -654,9 +654,9 @@ async function handleChooseOutbound(
}
function getInitialLatencyProgress(
latencyType: Forkop.LatencyActionState['latency_type'],
latencyType: Tachyon.LatencyActionState['latency_type'],
tag: string,
): Forkop.LatencyActionProgress | undefined {
): Tachyon.LatencyActionProgress | undefined {
if (latencyType !== 'proxy_list') {
return undefined;
}
@@ -678,7 +678,7 @@ function getInitialLatencyProgress(
}
async function handleTestLatency(
latencyType: Forkop.LatencyActionState['latency_type'],
latencyType: Tachyon.LatencyActionState['latency_type'],
sectionName: string,
tag: string,
timeout?: string,
@@ -698,7 +698,7 @@ async function handleTestLatency(
let completed = false;
try {
const startResponse = await ForkopShellMethods.latencyTestStart(
const startResponse = await TachyonShellMethods.latencyTestStart(
latencyType,
sectionName,
tag,
@@ -717,7 +717,7 @@ async function handleTestLatency(
followedLatencyJobs.add(jobId);
ownsJobFollow = true;
await ForkopShellMethods.waitLatencyTestJob(jobId);
await TachyonShellMethods.waitLatencyTestJob(jobId);
await completeLatencyTestJob(jobId, sectionName);
completed = true;
} catch (error) {
@@ -734,8 +734,8 @@ async function handleTestLatency(
}
async function handleCopyOutbound(
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
section: Tachyon.OutboundGroup,
outbound: Tachyon.Outbound,
) {
const link = outbound.link;
@@ -744,7 +744,7 @@ async function handleCopyOutbound(
return;
}
const response = await ForkopShellMethods.getOutboundLink(
const response = await TachyonShellMethods.getOutboundLink(
section.sectionName,
outbound.code,
);
@@ -817,7 +817,7 @@ function getDetectedCountryFlag(country?: string) {
);
}
function renderDetailsMemberName(member: Forkop.UrlTestMember) {
function renderDetailsMemberName(member: Tachyon.UrlTestMember) {
const countryFlag = getDetectedCountryFlag(member.country);
if (!countryFlag) {
return member.displayName;
@@ -833,7 +833,7 @@ function renderDetailsMemberName(member: Forkop.UrlTestMember) {
];
}
function renderUrlTestSelectedValue(info: Forkop.UrlTestInfo) {
function renderUrlTestSelectedValue(info: Tachyon.UrlTestInfo) {
const selectedMember = info.outbounds.find((member) => member.selected);
const selectedName =
selectedMember?.displayName || info.selectedName || info.selectedCode || '';
@@ -892,8 +892,8 @@ function renderUrlTestCopyButton(
}
function renderUrlTestInfoModal(
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
section: Tachyon.OutboundGroup,
outbound: Tachyon.Outbound,
) {
const info = outbound.urlTestInfo;
@@ -1031,8 +1031,8 @@ function renderUrlTestInfoModal(
}
function handleShowUrlTestInfo(
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
section: Tachyon.OutboundGroup,
outbound: Tachyon.Outbound,
) {
if (!outbound.urlTestInfo) {
return;
@@ -1046,7 +1046,7 @@ function handleShowUrlTestInfo(
);
}
function renderPrioritySelectedValue(info: Forkop.PriorityInfo) {
function renderPrioritySelectedValue(info: Tachyon.PriorityInfo) {
const selectedMember = info.outbounds.find((member) => member.selected);
const selectedName =
selectedMember?.displayName || info.selectedName || info.selectedCode || '';
@@ -1096,7 +1096,7 @@ function renderPrioritySelectedValue(info: Forkop.PriorityInfo) {
);
}
function renderPriorityMemberName(member: Forkop.PriorityMember) {
function renderPriorityMemberName(member: Tachyon.PriorityMember) {
const levelName = member.levelName || _('Level');
return [
@@ -1119,8 +1119,8 @@ function renderPriorityMemberName(member: Forkop.PriorityMember) {
}
function renderPriorityInfoModal(
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
section: Tachyon.OutboundGroup,
outbound: Tachyon.Outbound,
) {
const info = outbound.priorityInfo;
@@ -1287,8 +1287,8 @@ function renderPriorityInfoModal(
}
function handleShowPriorityInfo(
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
section: Tachyon.OutboundGroup,
outbound: Tachyon.Outbound,
) {
if (!outbound.priorityInfo) {
return;
@@ -1302,7 +1302,7 @@ function handleShowPriorityInfo(
);
}
async function handleUpdateSubscription(section: Forkop.OutboundGroup) {
async function handleUpdateSubscription(section: Tachyon.OutboundGroup) {
if (
store.get().sectionsWidget.subscriptionUpdatingSections[section.sectionName]
) {
@@ -1314,7 +1314,7 @@ async function handleUpdateSubscription(section: Forkop.OutboundGroup) {
let ownsJobFollow = false;
try {
const startResponse = await ForkopShellMethods.subscriptionUpdateStart(
const startResponse = await TachyonShellMethods.subscriptionUpdateStart(
section.sectionName,
);
@@ -1330,7 +1330,7 @@ async function handleUpdateSubscription(section: Forkop.OutboundGroup) {
followedSubscriptionJobs.add(jobId);
ownsJobFollow = true;
const response = await ForkopShellMethods.waitSubscriptionUpdateJob(jobId);
const response = await TachyonShellMethods.waitSubscriptionUpdateJob(jobId);
await completeSubscriptionUpdateJob(jobId, section.sectionName, response);
} catch (error) {
logger.error('[DASHBOARD]', 'handleUpdateSubscription: failed', error);
@@ -1670,12 +1670,12 @@ async function renderServicesInfoWidget() {
title: _('Services info'),
items: [
{
key: 'Forkop',
value: servicesInfoWidget.data.forkopRunning
key: 'Tachyon',
value: servicesInfoWidget.data.tachyonRunning
? _('✔ Running')
: _('✘ Stopped'),
attributes: {
class: servicesInfoWidget.data.forkopRunning
class: servicesInfoWidget.data.tachyonRunning
? '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 { Forkop } from '../../../types';
import { Tachyon } from '../../../types';
interface IRenderSectionsProps {
loading: boolean;
failed: boolean;
section: Forkop.OutboundGroup;
section: Tachyon.OutboundGroup;
onTestLatency: (tag: string | string[]) => void;
onChooseOutbound: (
sectionName: string,
@@ -19,20 +19,20 @@ interface IRenderSectionsProps {
tag: string,
) => void;
onCopyOutbound: (
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
section: Tachyon.OutboundGroup,
outbound: Tachyon.Outbound,
) => void;
onShowUrlTestInfo: (
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
section: Tachyon.OutboundGroup,
outbound: Tachyon.Outbound,
) => void;
onShowPriorityInfo: (
section: Forkop.OutboundGroup,
outbound: Forkop.Outbound,
section: Tachyon.OutboundGroup,
outbound: Tachyon.Outbound,
) => void;
onUpdateSubscription: (section: Forkop.OutboundGroup) => void;
onUpdateSubscription: (section: Tachyon.OutboundGroup) => void;
latencyFetching: boolean;
latencyProgress?: Forkop.LatencyActionProgress;
latencyProgress?: Tachyon.LatencyActionProgress;
subscriptionUpdating: boolean;
selectorSwitchingTag?: string;
}
@@ -109,7 +109,7 @@ function renderMetadataAction(label: string, url?: string) {
}
function renderSubscriptionMetadata(
metadata: Forkop.SubscriptionMetadata | undefined,
metadata: Tachyon.SubscriptionMetadata | undefined,
) {
if (!metadata || Object.keys(metadata).length <= 1) {
return undefined;
@@ -203,9 +203,9 @@ function renderSubscriptionMetadata(
}
function renderSubscriptionUpdateAction(
section: Forkop.OutboundGroup,
section: Tachyon.OutboundGroup,
subscriptionUpdating: boolean,
onUpdateSubscription: (section: Forkop.OutboundGroup) => void,
onUpdateSubscription: (section: Tachyon.OutboundGroup) => void,
) {
if (!section.subscriptionSourceCount) {
return undefined;
@@ -235,7 +235,7 @@ function renderSubscriptionUpdateAction(
}
export function getLatencyTestLabel(
latencyProgress?: Forkop.LatencyActionProgress,
latencyProgress?: Tachyon.LatencyActionProgress,
) {
const total = Math.trunc(Number(latencyProgress?.total ?? 0));
if (!Number.isFinite(total) || total <= 0) {
@@ -280,7 +280,7 @@ function renderDefaultState({
}
}
function renderOutbound(outbound: Forkop.Outbound) {
function renderOutbound(outbound: Tachyon.Outbound) {
function getLatencyClass() {
if (!outbound.latency) {
return 'fkp_dashboard-page__outbound-grid__item__latency--empty';
@@ -15,7 +15,7 @@ export function render() {
role: 'status',
},
_(
'Forkop service is stopped. Start the service to display the dashboard.',
'Tachyon service is stopped. Start the service to display the dashboard.',
),
),
E('div', { class: 'fkp_dashboard-page__content' }, [
@@ -1,21 +1,21 @@
// language=CSS
import { FORKOP_UCI_PACKAGE as FORKOP_CBI_PREFIX } from '../../../constants';
import { TACHYON_UCI_PACKAGE as TACHYON_CBI_PREFIX } from '../../../constants';
export const styles = `
#cbi-${FORKOP_CBI_PREFIX}-dashboard-_mount_node > .cbi-value-title {
#cbi-${TACHYON_CBI_PREFIX}-dashboard-_mount_node > .cbi-value-title {
display: none;
}
#cbi-${FORKOP_CBI_PREFIX}-dashboard-_mount_node > .cbi-value-field {
#cbi-${TACHYON_CBI_PREFIX}-dashboard-_mount_node > .cbi-value-field {
margin-left: 0;
width: 100%;
}
#cbi-${FORKOP_CBI_PREFIX}-dashboard-_mount_node > div {
#cbi-${TACHYON_CBI_PREFIX}-dashboard-_mount_node > div {
width: 100%;
}
#cbi-${FORKOP_CBI_PREFIX}-dashboard > h3 {
#cbi-${TACHYON_CBI_PREFIX}-dashboard > h3 {
display: none;
}
@@ -1,9 +1,9 @@
import type { Forkop } from '../../../types';
import type { Tachyon } from '../../../types';
import { getMeta } from '../helpers/getMeta';
type DnsCheckState = 'error' | 'success' | 'warning';
export function getDnsCheckPresentation(data: Forkop.DnsCheckResult) {
export function getDnsCheckPresentation(data: Tachyon.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 { ForkopShellMethods } from '../../../methods';
import { TachyonShellMethods } 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 ForkopShellMethods.getByedpiStatus();
const byedpiStatus = await TachyonShellMethods.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 forkopRuntimeReady =
const tachyonRuntimeReady =
!hasByedpiRules ||
(runningProcesses === expectedProcesses &&
supervisorProcesses === expectedProcesses);
@@ -81,20 +81,20 @@ export async function runByedpiCheck() {
},
{
state:
unexpectedRuntime || !forkopRuntimeReady
unexpectedRuntime || !tachyonRuntimeReady
? 'error'
: runtimeUnstable
? 'warning'
: 'success',
key: hasByedpiRules
? runtimeUnstable
? _('Forkop-managed ciadpi runtime has restarted')
: forkopRuntimeReady
? _('Forkop-managed ciadpi runtime is ready')
: _('Forkop-managed ciadpi runtime is not ready')
? _('Tachyon-managed ciadpi runtime has restarted')
: tachyonRuntimeReady
? _('Tachyon-managed ciadpi runtime is ready')
: _('Tachyon-managed ciadpi runtime is not ready')
: unexpectedRuntime
? _('Unexpected Forkop-managed ciadpi runtime is running')
: _('Forkop-managed ciadpi runtime is not running'),
? _('Unexpected Tachyon-managed ciadpi runtime is running')
: _('Tachyon-managed ciadpi runtime is not running'),
value: hasByedpiRules
? runtimeUnstable
? `${restartCount}`
@@ -116,7 +116,7 @@ export async function runByedpiCheck() {
: 'success',
key: standaloneServiceRunning
? hasByedpiRules
? _('Standalone ByeDPI is active together with Forkop ByeDPI rules')
? _('Standalone ByeDPI is active together with Tachyon 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 { ForkopShellMethods } from '../../../methods';
import { TachyonShellMethods } 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 ForkopShellMethods.checkDNSAvailable();
const dnsChecks = await TachyonShellMethods.checkDNSAvailable();
if (!dnsChecks.success) {
updateCheckStore({
@@ -1,6 +1,6 @@
import { insertIf } from '../../../../helpers';
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { ForkopShellMethods, RemoteFakeIPMethods } from '../../../methods';
import { TachyonShellMethods, 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 ForkopShellMethods.checkFakeIP();
const routerFakeIPResponse = await TachyonShellMethods.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 { ForkopShellMethods } from '../../../methods';
import { TachyonShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
import { IDiagnosticsChecksItem } from '../../../services';
import { Forkop } from '../../../types';
import { Tachyon } from '../../../types';
function serverPrefix(item: Forkop.InboundCheckItem) {
function serverPrefix(item: Tachyon.InboundCheckItem) {
return `${item.label}:`;
}
function formatListen(item: Forkop.InboundCheckItem) {
function formatListen(item: Tachyon.InboundCheckItem) {
return `${item.listen}:${Number(item.listen_port || 0)} [${item.required_proto}]`;
}
function getPortConflictItem(
item: Forkop.InboundCheckItem,
item: Tachyon.InboundCheckItem,
): IDiagnosticsChecksItem {
const key = `${serverPrefix(item)} ${_('Port conflict')}`;
@@ -34,7 +34,7 @@ function getPortConflictItem(
}
function getPublicHostItem(
item: Forkop.InboundCheckItem,
item: Tachyon.InboundCheckItem,
wanIp: string,
): IDiagnosticsChecksItem {
const key = `${serverPrefix(item)} ${_('Public host')}`;
@@ -79,7 +79,7 @@ function getPublicHostItem(
}
function getServerItems(
item: Forkop.InboundCheckItem,
item: Tachyon.InboundCheckItem,
wanIp: string,
): IDiagnosticsChecksItem[] {
const prefix = serverPrefix(item);
@@ -162,7 +162,7 @@ export async function runInboundsCheck() {
items: [],
});
const inboundsChecks = await ForkopShellMethods.checkInbounds();
const inboundsChecks = await TachyonShellMethods.checkInbounds();
if (!inboundsChecks.success) {
updateCheckStore({
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { RemoteFakeIPMethods, ForkopShellMethods } from '../../../methods';
import { RemoteFakeIPMethods, TachyonShellMethods } 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 ForkopShellMethods.checkNftRules();
const nftablesChecks = await TachyonShellMethods.checkNftRules();
if (!nftablesChecks.success) {
updateCheckStore({
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { ForkopShellMethods } from '../../../methods';
import { TachyonShellMethods } 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 ForkopShellMethods.getClashApiProxyLatency(
const latencyProxy = await TachyonShellMethods.getClashApiProxyLatency(
selectedOutbound.code,
section.latencyTestTimeout,
);
@@ -91,7 +91,7 @@ export async function runSectionsCheck() {
};
}
const latencyGroup = await ForkopShellMethods.getClashApiGroupLatency(
const latencyGroup = await TachyonShellMethods.getClashApiGroupLatency(
section.code,
);
const success = latencyGroup.success && !latencyGroup.data.message;
@@ -125,7 +125,7 @@ export async function runSectionsCheck() {
}
const selectedOutbound = section.outbounds[0];
const latencyProxy = await ForkopShellMethods.getClashApiProxyLatency(
const latencyProxy = await TachyonShellMethods.getClashApiProxyLatency(
section.code,
section.latencyTestTimeout,
);
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { ForkopShellMethods } from '../../../methods';
import { TachyonShellMethods } from '../../../methods';
import { updateCheckStore } from './updateCheckStore';
import { getMeta } from '../helpers/getMeta';
@@ -15,7 +15,7 @@ export async function runSingBoxCheck() {
items: [],
});
const singBoxChecks = await ForkopShellMethods.checkSingBox();
const singBoxChecks = await TachyonShellMethods.checkSingBox();
if (!singBoxChecks.success) {
updateCheckStore({
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { ForkopShellMethods } from '../../../methods';
import { TachyonShellMethods } 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 ForkopShellMethods.getZapret2Status();
const zapret2Status = await TachyonShellMethods.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 forkopRuntimeReady =
const tachyonRuntimeReady =
!hasZapret2Rules ||
(runningProcesses === expectedProcesses &&
supervisorProcesses === expectedProcesses);
@@ -83,14 +83,14 @@ export async function runZapret2Check() {
value: '',
},
{
state: unexpectedRuntime || !forkopRuntimeReady ? 'error' : 'success',
state: unexpectedRuntime || !tachyonRuntimeReady ? 'error' : 'success',
key: hasZapret2Rules
? forkopRuntimeReady
? _('Forkop-managed nfqws2 runtime is ready')
: _('Forkop-managed nfqws2 runtime is not ready')
? tachyonRuntimeReady
? _('Tachyon-managed nfqws2 runtime is ready')
: _('Tachyon-managed nfqws2 runtime is not ready')
: unexpectedRuntime
? _('Unexpected Forkop-managed nfqws2 runtime is running')
: _('Forkop-managed nfqws2 runtime is not running'),
? _('Unexpected Tachyon-managed nfqws2 runtime is running')
: _('Tachyon-managed nfqws2 runtime is not running'),
value: hasZapret2Rules ? `${runningProcesses}/${expectedProcesses}` : '',
},
{
@@ -115,7 +115,7 @@ export async function runZapret2Check() {
: 'success',
key: standaloneServiceRunning
? hasZapret2Rules
? _('Standalone Zapret2 is active together with Forkop Zapret2 rules')
? _('Standalone Zapret2 is active together with Tachyon Zapret2 rules')
: _('Standalone Zapret2 service is active')
: standaloneAutostartRisk
? _('Standalone Zapret2 autostart is enabled')
@@ -1,5 +1,5 @@
import { DIAGNOSTICS_CHECKS_MAP } from './contstants';
import { ForkopShellMethods } from '../../../methods';
import { TachyonShellMethods } 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 ForkopShellMethods.getZapretStatus();
const zapretStatus = await TachyonShellMethods.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 forkopRuntimeReady =
const tachyonRuntimeReady =
!hasZapretRules ||
(runningProcesses === expectedProcesses &&
supervisorProcesses === expectedProcesses);
@@ -80,14 +80,14 @@ export async function runZapretCheck() {
value: '',
},
{
state: unexpectedRuntime || !forkopRuntimeReady ? 'error' : 'success',
state: unexpectedRuntime || !tachyonRuntimeReady ? 'error' : 'success',
key: hasZapretRules
? forkopRuntimeReady
? _('Forkop-managed nfqws runtime is ready')
: _('Forkop-managed nfqws runtime is not ready')
? tachyonRuntimeReady
? _('Tachyon-managed nfqws runtime is ready')
: _('Tachyon-managed nfqws runtime is not ready')
: unexpectedRuntime
? _('Unexpected Forkop-managed nfqws runtime is running')
: _('Forkop-managed nfqws runtime is not running'),
? _('Unexpected Tachyon-managed nfqws runtime is running')
: _('Tachyon-managed nfqws runtime is not running'),
value: hasZapretRules ? `${runningProcesses}/${expectedProcesses}` : '',
},
{
@@ -108,7 +108,7 @@ export async function runZapretCheck() {
state: standaloneConflict ? 'warning' : 'success',
key: standaloneServiceRunning
? hasZapretRules
? _('Standalone Zapret is active together with Forkop Zapret rules')
? _('Standalone Zapret is active together with Tachyon Zapret rules')
: _('Standalone Zapret service is active')
: _('Standalone Zapret service is inactive'),
value: '',
@@ -8,7 +8,7 @@ const mocks = vi.hoisted(() => ({
}));
vi.mock('../../../../methods', () => ({
ForkopShellMethods: {
TachyonShellMethods: {
getZapretStatus: mocks.getZapretStatus,
getZapret2Status: mocks.getZapret2Status,
getByedpiStatus: mocks.getByedpiStatus,
@@ -12,7 +12,7 @@ vi.mock('../../../../methods/custom/getDashboardSections', () => ({
}));
vi.mock('../../../../methods', () => ({
ForkopShellMethods: {
TachyonShellMethods: {
getClashApiProxyLatency: mocks.getClashApiProxyLatency,
getClashApiGroupLatency: mocks.getClashApiGroupLatency,
},
@@ -77,8 +77,8 @@ export const initialDiagnosticStore: Pick<
loading: true,
loaded: false,
providerInfoLoaded: false,
forkop_version: 'loading',
forkop_latest_version: 'loading',
tachyon_version: 'loading',
tachyon_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: {
forkopCheck: { loading: false },
forkopInstall: { loading: false },
tachyonCheck: { loading: false },
tachyonInstall: { loading: false },
singBoxCheck: { loading: false },
singBoxInstall: { loading: false },
singBoxInstallExtended: { loading: false },
@@ -143,7 +143,7 @@ export const initialDiagnosticStore: Pick<
byedpiRemove: { loading: false },
},
updatesChecks: {
forkop: { status: null, latest_version: '', release_url: '' },
tachyon: { 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 = 'forkop:diagnostic-run:v1';
const DIAGNOSTIC_RUN_STORAGE_KEY = 'tachyon: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 FORKOP_MASK_AFTER_TOKEN = [
const TACHYON_MASK_AFTER_TOKEN = [
'option proxy_string',
'option subscription_url',
'list subscription_urls',
@@ -47,7 +47,7 @@ const FORKOP_MASK_AFTER_TOKEN = [
'option yacd_secret_key',
];
const FORKOP_MASK_AFTER_TOKEN_SPACE = [
const TACHYON_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 FORKOP_MASK_AFTER_TOKEN) {
for (const token of TACHYON_MASK_AFTER_TOKEN) {
maskedLine = maskAfterToken(maskedLine, token);
}
for (const token of FORKOP_MASK_AFTER_TOKEN_SPACE) {
for (const token of TACHYON_MASK_AFTER_TOKEN_SPACE) {
maskedLine = maskAfterTokenSpace(maskedLine, token);
}
@@ -32,14 +32,14 @@ import {
renderRunAction,
renderSystemInfo,
} from './partials';
import { ForkopShellMethods } from '../../methods';
import { TachyonShellMethods } from '../../methods';
import { fetchServicesInfo } from '../../fetchers/fetchServicesInfo';
import { normalizeCompiledVersion } from '../../../helpers/normalizeCompiledVersion';
import { renderModal } from '../../../partials';
import { FORKOP_LUCI_APP_VERSION } from '../../../constants';
import { TACHYON_LUCI_APP_VERSION } from '../../../constants';
import { renderWikiDisclaimer } from './partials/renderWikiDisclaimer';
import { runSectionsCheck } from './checks/runSectionsCheck';
import { Forkop } from '../../types';
import { Tachyon } from '../../types';
import {
getAvailableActionsDisabledState,
getServiceTransition,
@@ -156,11 +156,11 @@ function isLocalMutatingServiceActionLoading() {
function isMutatingServiceActionLoading() {
return (
isLocalMutatingServiceActionLoading() ||
isServiceTransitionStatus(store.get().servicesInfoWidget.data.forkopStatus)
isServiceTransitionStatus(store.get().servicesInfoWidget.data.tachyonStatus)
);
}
function getForkopStatusText(running: boolean, enabled: boolean) {
function getTachyonStatusText(running: boolean, enabled: boolean) {
if (running) {
return enabled ? 'running & enabled' : 'running but disabled';
}
@@ -168,9 +168,9 @@ function getForkopStatusText(running: boolean, enabled: boolean) {
return enabled ? 'stopped but enabled' : 'stopped & disabled';
}
function setDisplayedForkopRunning(running: boolean) {
function setDisplayedTachyonRunning(running: boolean) {
const servicesInfoWidget = store.get().servicesInfoWidget;
const enabled = Boolean(servicesInfoWidget.data.forkopEnabled);
const enabled = Boolean(servicesInfoWidget.data.tachyonEnabled);
store.set({
servicesInfoWidget: {
@@ -178,8 +178,8 @@ function setDisplayedForkopRunning(running: boolean) {
loading: false,
data: {
...servicesInfoWidget.data,
forkopRunning: running ? 1 : 0,
forkopStatus: getForkopStatusText(running, enabled),
tachyonRunning: running ? 1 : 0,
tachyonStatus: getTachyonStatusText(running, enabled),
},
},
});
@@ -232,17 +232,17 @@ async function refreshDiagnosticServicesInfo({
return promise;
}
async function waitForForkopRunningState(expectedRunning: boolean) {
async function waitForTachyonRunningState(expectedRunning: boolean) {
const startedAt = Date.now();
while (Date.now() - startedAt < SERVICE_ACTION_STATUS_TIMEOUT_MS) {
await refreshDiagnosticServicesInfo({ force: true, allowInactive: true });
const forkopRunning = Boolean(
store.get().servicesInfoWidget.data.forkopRunning,
const tachyonRunning = Boolean(
store.get().servicesInfoWidget.data.tachyonRunning,
);
if (forkopRunning === expectedRunning) {
if (tachyonRunning === expectedRunning) {
return true;
}
@@ -274,13 +274,13 @@ function stopServiceActionStateWatcher() {
}
function isVisibleServiceRuntimeAction(
action: Forkop.ServiceActionState['action'],
action: Tachyon.ServiceActionState['action'],
): action is ServiceRuntimeAction {
return action === 'restart' || action === 'start' || action === 'stop';
}
function setServiceActionStateLoading(
state: Forkop.ServiceActionState,
state: Tachyon.ServiceActionState,
loading: boolean,
) {
if (!isVisibleServiceRuntimeAction(state.action)) {
@@ -290,7 +290,7 @@ function setServiceActionStateLoading(
setDiagnosticActionLoading(state.action, loading);
}
async function followServiceActionState(state: Forkop.ServiceActionState) {
async function followServiceActionState(state: Tachyon.ServiceActionState) {
const jobId = state.job_id;
if (!jobId || followedServiceActionJobs.has(jobId)) {
@@ -308,7 +308,7 @@ async function followServiceActionState(state: Forkop.ServiceActionState) {
try {
if (state.running) {
await ForkopShellMethods.waitServiceActionJob(jobId);
await TachyonShellMethods.waitServiceActionJob(jobId);
}
} catch (error) {
logger.error('[DIAGNOSTIC]', 'followServiceActionState failed', error);
@@ -316,13 +316,13 @@ async function followServiceActionState(state: Forkop.ServiceActionState) {
handledServiceActionJobs.add(jobId);
setServiceActionStateLoading(state, false);
await refreshDiagnosticServicesInfo({ force: true, allowInactive: true });
void ForkopShellMethods.uiActionAck('service', jobId);
void TachyonShellMethods.uiActionAck('service', jobId);
followedServiceActionJobs.delete(jobId);
resetDiagnosticsChecks();
}
}
function followServiceActionsFromUiState(uiState?: Forkop.UiState) {
function followServiceActionsFromUiState(uiState?: Tachyon.UiState) {
if (!uiState) {
return;
}
@@ -411,10 +411,10 @@ async function fetchDiagnosticsProviderInfo({
const [zapretRuntime, zapret2Runtime, byedpiRuntime, inboundsConfig] =
await Promise.all([
ForkopShellMethods.checkZapretRuntime(),
ForkopShellMethods.checkZapret2Runtime(),
ForkopShellMethods.checkByedpiRuntime(),
ForkopShellMethods.checkInboundsConfig(),
TachyonShellMethods.checkZapretRuntime(),
TachyonShellMethods.checkZapret2Runtime(),
TachyonShellMethods.checkByedpiRuntime(),
TachyonShellMethods.checkInboundsConfig(),
]);
if (requestId !== latestProviderInfoRequestId) {
@@ -532,7 +532,7 @@ function renderDiagnosticRunActionWidget() {
const providerInfoLoaded =
store.get().diagnosticsSystemInfo.providerInfoLoaded;
const servicesInfoWidget = store.get().servicesInfoWidget;
const forkopRunning = Boolean(servicesInfoWidget.data.forkopRunning);
const tachyonRunning = Boolean(servicesInfoWidget.data.tachyonRunning);
const container = document.getElementById('fkp_diagnostic-page-run-check');
const renderedAction = renderRunAction({
@@ -540,7 +540,7 @@ function renderDiagnosticRunActionWidget() {
disabled: shouldDisableDiagnosticRunAction({
providerInfoLoaded,
servicesInfoLoading: servicesInfoWidget.loading,
forkopRunning,
tachyonRunning,
mutatingServiceActionLoading: isMutatingServiceActionLoading(),
}),
click: () => runChecks(),
@@ -566,11 +566,11 @@ async function handleServiceRuntimeAction({
let delegatedToWatcher = false;
if (optimisticRunning !== undefined) {
setDisplayedForkopRunning(optimisticRunning);
setDisplayedTachyonRunning(optimisticRunning);
}
try {
const startResponse = await ForkopShellMethods.serviceActionStart(action);
const startResponse = await TachyonShellMethods.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 ForkopShellMethods.waitServiceActionJob(jobId);
const result = await TachyonShellMethods.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 waitForForkopRunningState(expectedRunning);
await waitForTachyonRunningState(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 ForkopShellMethods.uiActionAck('service', jobId);
void TachyonShellMethods.uiActionAck('service', jobId);
}
resetDiagnosticsChecks();
}
@@ -640,7 +640,7 @@ async function handleEnable() {
setDiagnosticActionLoading('enable', true);
try {
await ForkopShellMethods.enable();
await TachyonShellMethods.enable();
} catch (e) {
logger.error('[DIAGNOSTIC]', 'handleEnable - e', e);
} finally {
@@ -656,7 +656,7 @@ async function handleDisable() {
setDiagnosticActionLoading('disable', true);
try {
await ForkopShellMethods.disable();
await TachyonShellMethods.disable();
} catch (e) {
logger.error('[DIAGNOSTIC]', 'handleDisable - e', e);
} finally {
@@ -672,7 +672,7 @@ async function handleShowGlobalCheck() {
setDiagnosticActionLoading('globalCheck', true);
try {
const globalCheck = await ForkopShellMethods.globalCheck(false);
const globalCheck = await TachyonShellMethods.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 ForkopShellMethods.checkLogs();
const viewLogs = await TachyonShellMethods.checkLogs();
if (viewLogs.success) {
const getLatestLogs = async () => {
const latestLogs = await ForkopShellMethods.checkLogs();
const latestLogs = await TachyonShellMethods.checkLogs();
if (!latestLogs.success) {
throw latestLogs;
@@ -737,7 +737,7 @@ async function handleShowSingBoxConfig() {
setDiagnosticActionLoading('showSingBoxConfig', true);
try {
const showSingBoxConfig = await ForkopShellMethods.showSingBoxConfig(false);
const showSingBoxConfig = await TachyonShellMethods.showSingBoxConfig(false);
if (showSingBoxConfig.success) {
const rawSingBoxConfigText = stringifySingBoxConfig(
@@ -799,10 +799,10 @@ function renderDiagnosticAvailableActionsWidget() {
const servicesInfoWidget = store.get().servicesInfoWidget;
logger.debug('[DIAGNOSTIC]', 'renderDiagnosticAvailableActionsWidget');
const forkopEnabled = Boolean(servicesInfoWidget.data.forkopEnabled);
const forkopRunning = Boolean(servicesInfoWidget.data.forkopRunning);
const tachyonEnabled = Boolean(servicesInfoWidget.data.tachyonEnabled);
const tachyonRunning = Boolean(servicesInfoWidget.data.tachyonRunning);
const serviceTransition = getServiceTransition(
servicesInfoWidget.data.forkopStatus,
servicesInfoWidget.data.tachyonStatus,
);
const restartLoading =
diagnosticsActions.restart.loading || serviceTransition.restarting;
@@ -824,13 +824,13 @@ function renderDiagnosticAvailableActionsWidget() {
componentActionLoading,
});
const startVisible = shouldShowStartAction({
forkopRunning,
tachyonRunning,
restartLoading,
startLoading,
stopLoading,
});
const stopVisible = shouldShowStopAction({
forkopRunning,
tachyonRunning,
restartLoading,
startLoading,
stopLoading,
@@ -842,7 +842,7 @@ function renderDiagnosticAvailableActionsWidget() {
restart: {
loading: restartLoading,
visible: shouldShowRestartAction({
forkopRunning,
tachyonRunning,
restartLoading,
startLoading,
stopLoading,
@@ -864,13 +864,13 @@ function renderDiagnosticAvailableActionsWidget() {
},
enable: {
loading: diagnosticsActions.enable.loading,
visible: !forkopEnabled,
visible: !tachyonEnabled,
onClick: handleEnable,
disabled: serviceControlsDisabled,
},
disable: {
loading: diagnosticsActions.disable.loading,
visible: forkopEnabled,
visible: tachyonEnabled,
onClick: handleDisable,
disabled: serviceControlsDisabled,
},
@@ -907,12 +907,12 @@ function renderDiagnosticSystemInfoWidget() {
const items = [
{
key: 'Forkop',
value: normalizeCompiledVersion(diagnosticsSystemInfo.forkop_version),
key: 'Tachyon',
value: normalizeCompiledVersion(diagnosticsSystemInfo.tachyon_version),
},
{
key: 'Luci App',
value: normalizeCompiledVersion(FORKOP_LUCI_APP_VERSION),
value: normalizeCompiledVersion(TACHYON_LUCI_APP_VERSION),
},
{
key: 'Sing-box',
@@ -46,7 +46,7 @@ export function renderAvailableActions({
classNames: ['cbi-button-apply'],
onClick: restart.onClick,
icon: renderRotateCcwIcon24,
text: _('Restart Forkop'),
text: _('Restart Tachyon'),
loading: restart.loading,
disabled: restart.disabled,
}),
@@ -56,7 +56,7 @@ export function renderAvailableActions({
classNames: ['cbi-button-remove'],
onClick: stop.onClick,
icon: renderCircleStopIcon24,
text: _('Stop Forkop'),
text: _('Stop Tachyon'),
loading: stop.loading,
disabled: stop.disabled,
}),
@@ -66,7 +66,7 @@ export function renderAvailableActions({
classNames: ['cbi-button-save'],
onClick: start.onClick,
icon: renderCirclePlayIcon24,
text: _('Start Forkop'),
text: _('Start Tachyon'),
loading: start.loading,
disabled: start.disabled,
}),
@@ -59,18 +59,18 @@ export function shouldResetDiagnosticsChecks({
export function shouldDisableDiagnosticRunAction({
providerInfoLoaded,
servicesInfoLoading,
forkopRunning,
tachyonRunning,
mutatingServiceActionLoading,
}: {
providerInfoLoaded: boolean;
servicesInfoLoading: boolean;
forkopRunning: boolean;
tachyonRunning: boolean;
mutatingServiceActionLoading: boolean;
}) {
return (
!providerInfoLoaded ||
servicesInfoLoading ||
!forkopRunning ||
!tachyonRunning ||
mutatingServiceActionLoading
);
}
@@ -100,43 +100,43 @@ export function getAvailableActionsDisabledState({
}
export function shouldShowRestartAction({
forkopRunning,
tachyonRunning,
restartLoading,
startLoading,
stopLoading,
}: {
forkopRunning: boolean;
tachyonRunning: boolean;
restartLoading: boolean;
startLoading: boolean;
stopLoading: boolean;
}) {
return restartLoading || (forkopRunning && !startLoading && !stopLoading);
return restartLoading || (tachyonRunning && !startLoading && !stopLoading);
}
export function shouldShowStartAction({
forkopRunning,
tachyonRunning,
restartLoading,
startLoading,
stopLoading,
}: {
forkopRunning: boolean;
tachyonRunning: boolean;
restartLoading: boolean;
startLoading: boolean;
stopLoading: boolean;
}) {
return startLoading || (!restartLoading && !forkopRunning && !stopLoading);
return startLoading || (!restartLoading && !tachyonRunning && !stopLoading);
}
export function shouldShowStopAction({
forkopRunning,
tachyonRunning,
restartLoading,
startLoading,
stopLoading,
}: {
forkopRunning: boolean;
tachyonRunning: boolean;
restartLoading: boolean;
startLoading: boolean;
stopLoading: boolean;
}) {
return stopLoading || restartLoading || (forkopRunning && !startLoading);
return stopLoading || restartLoading || (tachyonRunning && !startLoading);
}
@@ -1,13 +1,13 @@
// language=CSS
import { FORKOP_UCI_PACKAGE as FORKOP_CBI_PREFIX } from '../../../constants';
import { TACHYON_UCI_PACKAGE as TACHYON_CBI_PREFIX } from '../../../constants';
export const styles = `
#cbi-${FORKOP_CBI_PREFIX}-diagnostic-_mount_node > div {
#cbi-${TACHYON_CBI_PREFIX}-diagnostic-_mount_node > div {
width: 100%;
}
#cbi-${FORKOP_CBI_PREFIX}-diagnostic > h3 {
#cbi-${TACHYON_CBI_PREFIX}-diagnostic > h3 {
display: none;
}
@@ -91,7 +91,7 @@ describe('diagnostic run persistence', () => {
it('removes invalid snapshots', () => {
const storage = new MemoryStorage();
storage.setItem('forkop:diagnostic-run:v1', '{broken');
storage.setItem('tachyon:diagnostic-run:v1', '{broken');
expect(readPersistedDiagnosticRun(storage)).toBeNull();
expect(storage.length).toBe(0);
@@ -118,7 +118,7 @@ describe('diagnostic run persistence', () => {
const storage = new MemoryStorage();
storage.setItem(
'forkop:diagnostic-run:v1',
'tachyon:diagnostic-run:v1',
JSON.stringify({
nextRunnerIndex: 0,
providerOptions: {},
@@ -144,7 +144,7 @@ describe('diagnostic run persistence', () => {
const storage = new MemoryStorage();
storage.setItem(
'forkop:diagnostic-run:v1',
'tachyon:diagnostic-run:v1',
JSON.stringify({
nextRunnerIndex: 0,
providerOptions: {},
@@ -94,7 +94,7 @@ describe('diagnostic service transitions', () => {
it('shows restart while the service is running even when autostart is disabled', () => {
expect(
shouldShowRestartAction({
forkopRunning: true,
tachyonRunning: true,
restartLoading: false,
startLoading: false,
stopLoading: false,
@@ -102,7 +102,7 @@ describe('diagnostic service transitions', () => {
).toBe(true);
expect(
shouldShowRestartAction({
forkopRunning: false,
tachyonRunning: false,
restartLoading: false,
startLoading: false,
stopLoading: false,
@@ -110,7 +110,7 @@ describe('diagnostic service transitions', () => {
).toBe(false);
expect(
shouldShowRestartAction({
forkopRunning: false,
tachyonRunning: false,
restartLoading: true,
startLoading: false,
stopLoading: false,
@@ -121,7 +121,7 @@ describe('diagnostic service transitions', () => {
it('does not show restart from transient running state while start or stop is active', () => {
expect(
shouldShowRestartAction({
forkopRunning: true,
tachyonRunning: true,
restartLoading: false,
startLoading: true,
stopLoading: false,
@@ -129,7 +129,7 @@ describe('diagnostic service transitions', () => {
).toBe(false);
expect(
shouldShowRestartAction({
forkopRunning: true,
tachyonRunning: true,
restartLoading: false,
startLoading: false,
stopLoading: true,
@@ -142,7 +142,7 @@ describe('diagnostic service transitions', () => {
shouldDisableDiagnosticRunAction({
providerInfoLoaded: true,
servicesInfoLoading: false,
forkopRunning: true,
tachyonRunning: true,
mutatingServiceActionLoading: false,
}),
).toBe(false);
@@ -150,7 +150,7 @@ describe('diagnostic service transitions', () => {
shouldDisableDiagnosticRunAction({
providerInfoLoaded: true,
servicesInfoLoading: false,
forkopRunning: false,
tachyonRunning: false,
mutatingServiceActionLoading: false,
}),
).toBe(true);
@@ -159,7 +159,7 @@ describe('diagnostic service transitions', () => {
it('detects running component actions', () => {
expect(
hasComponentActionLoading({
forkopCheck: { loading: false },
tachyonCheck: { loading: false },
zapretInstall: { loading: true },
}),
).toBe(true);
@@ -196,7 +196,7 @@ describe('diagnostic service transitions', () => {
it('keeps the lower service action visible while restart is running', () => {
expect(
shouldShowStopAction({
forkopRunning: false,
tachyonRunning: false,
restartLoading: true,
startLoading: false,
stopLoading: false,
@@ -204,7 +204,7 @@ describe('diagnostic service transitions', () => {
).toBe(true);
expect(
shouldShowStartAction({
forkopRunning: false,
tachyonRunning: false,
restartLoading: true,
startLoading: false,
stopLoading: false,
@@ -215,7 +215,7 @@ describe('diagnostic service transitions', () => {
it('keeps the normal start and stop visibility outside restart', () => {
expect(
shouldShowStartAction({
forkopRunning: false,
tachyonRunning: false,
restartLoading: false,
startLoading: false,
stopLoading: false,
@@ -223,7 +223,7 @@ describe('diagnostic service transitions', () => {
).toBe(true);
expect(
shouldShowStopAction({
forkopRunning: false,
tachyonRunning: false,
restartLoading: false,
startLoading: false,
stopLoading: false,
@@ -231,7 +231,7 @@ describe('diagnostic service transitions', () => {
).toBe(false);
expect(
shouldShowStopAction({
forkopRunning: true,
tachyonRunning: true,
restartLoading: false,
startLoading: false,
stopLoading: false,
@@ -7,11 +7,11 @@ import {
renderSearchIcon24,
renderXIcon24,
} from '../../../icons';
import { CustomForkopMethods, ForkopShellMethods } from '../../methods';
import { CustomTachyonMethods, TachyonShellMethods } from '../../methods';
import { getOutboundTagBySection } from '../../runtimeTags';
import { getClashApiSecret } from '../../methods/custom/getClashApiSecret';
import { logger, socket, store, StoreType } from '../../services';
import { Forkop } from '../../types';
import { Tachyon } from '../../types';
import {
getCachedRuntimeUiState,
refreshRuntimeUiState,
@@ -127,7 +127,7 @@ function getListValues(value?: string[] | string) {
.filter(Boolean);
}
function getUrlTestIds(section: Forkop.ConfigSection) {
function getUrlTestIds(section: Tachyon.ConfigSection) {
const values = getListValues(section.urltests);
return values.length
? values
@@ -167,11 +167,11 @@ function formatEndpoint(address?: string, port?: string | number): string {
return `${normalizedAddress}:${normalizedPort}`;
}
function getDisplayName(section: Forkop.ConfigSection) {
function getDisplayName(section: Tachyon.ConfigSection) {
return normalizeString(section.label) || section['.name'];
}
function buildRouteDisplayNames(sections: Forkop.ConfigSection[]) {
function buildRouteDisplayNames(sections: Tachyon.ConfigSection[]) {
const map: Record<string, string> = {
'bypass-out': 'Bypass',
'direct-out': 'direct',
@@ -923,7 +923,7 @@ function renderConnections(options: { force?: boolean } = {}) {
container.replaceChildren(
renderConnectionsTable([], {
text: _(
'Forkop service is stopped. Start the service to display connections.',
'Tachyon service is stopped. Start the service to display connections.',
),
}),
);
@@ -1207,7 +1207,7 @@ async function closeConnection(connectionId: string) {
try {
const response =
await ForkopShellMethods.closeClashApiConnection(connectionId);
await TachyonShellMethods.closeClashApiConnection(connectionId);
if (!response.success) {
showToast(_('Failed to close connection'), 'error');
@@ -1241,7 +1241,7 @@ async function closeAllConnections() {
renderControls();
try {
const response = await ForkopShellMethods.closeAllClashApiConnections();
const response = await TachyonShellMethods.closeAllClashApiConnections();
if (!response.success) {
showToast(_('Failed to close connections'), 'error');
@@ -1343,7 +1343,7 @@ async function loadLocalDevices() {
async function loadRouteDisplayNames() {
try {
buildRouteDisplayNames(await CustomForkopMethods.getConfigSections());
buildRouteDisplayNames(await CustomTachyonMethods.getConfigSections());
} catch (error) {
logger.warn('[MONITORING]', 'loadRouteDisplayNames: failed', error);
buildRouteDisplayNames([]);
@@ -1367,7 +1367,7 @@ async function pollConnectionsSnapshot() {
pollingConnections = true;
try {
const response = await ForkopShellMethods.getClashApiConnections();
const response = await TachyonShellMethods.getClashApiConnections();
if (
!monitoringMounted ||
@@ -1532,7 +1532,7 @@ function watchServiceState() {
getServiceAvailability({
loading: false,
failed: false,
running: uiState.service.forkop.running,
running: uiState.service.tachyon.running,
}),
);
});
@@ -1,26 +1,26 @@
// language=CSS
import { FORKOP_UCI_PACKAGE as FORKOP_CBI_PREFIX } from '../../../constants';
import { TACHYON_UCI_PACKAGE as TACHYON_CBI_PREFIX } from '../../../constants';
export const styles = `
#cbi-${FORKOP_CBI_PREFIX}-monitoring-_mount_node {
#cbi-${TACHYON_CBI_PREFIX}-monitoring-_mount_node {
margin: 16px 0 22px;
padding: 0;
}
#cbi-${FORKOP_CBI_PREFIX}-monitoring-_mount_node > .cbi-value-title {
#cbi-${TACHYON_CBI_PREFIX}-monitoring-_mount_node > .cbi-value-title {
display: none;
}
#cbi-${FORKOP_CBI_PREFIX}-monitoring-_mount_node > .cbi-value-field {
#cbi-${TACHYON_CBI_PREFIX}-monitoring-_mount_node > .cbi-value-field {
margin-left: 0;
width: 100%;
}
#cbi-${FORKOP_CBI_PREFIX}-monitoring-_mount_node > div {
#cbi-${TACHYON_CBI_PREFIX}-monitoring-_mount_node > div {
width: 100%;
}
#cbi-${FORKOP_CBI_PREFIX}-monitoring > h3 {
#cbi-${TACHYON_CBI_PREFIX}-monitoring > h3 {
display: none;
}
@@ -1,10 +1,10 @@
import type { Forkop } from '../../types';
import type { Tachyon } from '../../types';
export function shouldPreserveCompletedCheckResultOnNextMount({
action,
mounted,
}: {
action: Forkop.ComponentAction;
action: Tachyon.ComponentAction;
mounted: boolean;
}) {
return action === 'check_update' && !mounted;
@@ -27,7 +27,7 @@ export function shouldResetCheckResultsOnMount({
}
export function shouldRefreshComponentStateBeforeRender(
uiState?: Pick<Forkop.UiState, 'actions'>,
uiState?: Pick<Tachyon.UiState, 'actions'>,
) {
return Boolean(
uiState?.actions.component.some((state) => state.running === true),
@@ -1,7 +1,7 @@
import type { Forkop } from '../../types';
import type { Tachyon } from '../../types';
export function shouldApplyCompletedComponentActionResult(
result: Pick<Forkop.ComponentActionResult, 'action'>,
result: Pick<Tachyon.ComponentActionResult, 'action'>,
notify: boolean,
) {
return result.action !== 'check_update' || notify;
@@ -1,5 +1,5 @@
import { onMount, preserveScrollForPage } from '../../../helpers';
import { FORKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT } from '../../../constants';
import { TACHYON_ACTION_PROVIDERS_AVAILABILITY_EVENT } from '../../../constants';
import { normalizeCompiledVersion } from '../../../helpers/normalizeCompiledVersion';
import { showToast } from '../../../helpers/showToast';
import {
@@ -29,7 +29,7 @@ import {
shouldRefreshComponentStateBeforeRender,
shouldResetCheckResultsOnMount,
} from './checkResultLifecycle';
import { ForkopShellMethods } from '../../methods';
import { TachyonShellMethods } from '../../methods';
import {
logger,
markUiActionOwned,
@@ -44,20 +44,20 @@ import {
refreshRuntimeUiState,
subscribeRuntimeUiState,
} from '../../services/runtimeUiState.service';
import { Forkop } from '../../types';
import { Tachyon } from '../../types';
type UpdateStatus = StoreType['updatesChecks'][Forkop.ComponentName]['status'];
type UpdateStatus = StoreType['updatesChecks'][Tachyon.ComponentName]['status'];
interface ComponentActionButton {
key: UpdatesActionKey;
text: string;
icon: () => SVGSVGElement;
component: Forkop.ComponentName;
action: Forkop.ComponentAction;
component: Tachyon.ComponentName;
action: Tachyon.ComponentAction;
}
interface ComponentCard {
component: Forkop.ComponentName;
component: Tachyon.ComponentName;
column: 0 | 1;
title: string;
version: string;
@@ -73,9 +73,9 @@ let updatesMountId = 0;
let pageUnloading = false;
let preserveCheckResultsOnNextMount = false;
let componentUpdateCheckCacheResolved = false;
let componentUpdateCheckCacheSnapshot: Forkop.ComponentUpdateCheckCache | null =
let componentUpdateCheckCacheSnapshot: Tachyon.ComponentUpdateCheckCache | null =
null;
let componentUpdateCheckCachePromise: Promise<Forkop.ComponentUpdateCheckCache> | null =
let componentUpdateCheckCachePromise: Promise<Tachyon.ComponentUpdateCheckCache> | null =
null;
let componentActionStateUnsubscribe: (() => void) | null = null;
let componentActionStateRefreshPromise: Promise<void> | null = null;
@@ -95,13 +95,13 @@ function isNotInstalled(version: string | undefined) {
return !version || version === 'not installed';
}
function shouldShowInstallAfterCheck(component: Forkop.ComponentName) {
function shouldShowInstallAfterCheck(component: Tachyon.ComponentName) {
const status = getVisibleCheckResult(component)?.status;
return status === 'outdated' || status === 'dev';
}
function getVisibleCheckResult(component: Forkop.ComponentName) {
function getVisibleCheckResult(component: Tachyon.ComponentName) {
if (
!shouldExposeCheckResults({
mounted: updatesMounted,
@@ -114,7 +114,7 @@ function getVisibleCheckResult(component: Forkop.ComponentName) {
return store.get().updatesChecks[component];
}
function getLatestVersion(component: Forkop.ComponentName) {
function getLatestVersion(component: Tachyon.ComponentName) {
const checkResult = getVisibleCheckResult(component);
if (!checkResult || !shouldShowInstallAfterCheck(component)) {
@@ -124,7 +124,7 @@ function getLatestVersion(component: Forkop.ComponentName) {
return checkResult.latest_version || undefined;
}
function getGitHubReleaseUrl(component: Forkop.ComponentName) {
function getGitHubReleaseUrl(component: Tachyon.ComponentName) {
const checkResult = getVisibleCheckResult(component);
if (
@@ -147,7 +147,7 @@ function isServiceRuntimeActionLoading() {
return (
hasLocalMutatingServiceActionLoading(state.diagnosticsActions) ||
isServiceTransitionStatus(state.servicesInfoWidget.data.forkopStatus)
isServiceTransitionStatus(state.servicesInfoWidget.data.tachyonStatus)
);
}
@@ -186,7 +186,7 @@ function beginComponentAction(button: ComponentActionButton) {
}
function setCheckResult(
component: Forkop.ComponentName,
component: Tachyon.ComponentName,
status: UpdateStatus,
latestVersion: string,
releaseUrl: string = '',
@@ -205,11 +205,11 @@ function setCheckResult(
});
}
function resetCheckResult(component: Forkop.ComponentName) {
function resetCheckResult(component: Tachyon.ComponentName) {
setCheckResult(component, null, '');
}
function applyCachedCheckResults(results: Forkop.ComponentActionResult[]) {
function applyCachedCheckResults(results: Tachyon.ComponentActionResult[]) {
results.forEach((result) => {
const status = result.status || null;
@@ -233,14 +233,14 @@ function loadComponentUpdateCheckCache({ force = false } = {}) {
return componentUpdateCheckCachePromise;
}
const promise = ForkopShellMethods.componentUpdateCheckCache()
const promise = TachyonShellMethods.componentUpdateCheckCache()
.then((response) =>
response.success
? response.data
: ({
enabled: false,
results: [],
} satisfies Forkop.ComponentUpdateCheckCache),
} satisfies Tachyon.ComponentUpdateCheckCache),
)
.then((cache) => {
componentUpdateCheckCacheSnapshot = cache;
@@ -262,7 +262,7 @@ function getErrorMessage(error: unknown, fallback: string) {
async function ackComponentActionJob(jobId: string) {
try {
const response = await ForkopShellMethods.uiActionAck('component', jobId);
const response = await TachyonShellMethods.uiActionAck('component', jobId);
if (!response.success) {
logger.debug('[UPDATES]', 'component action ack failed', response.error);
@@ -273,7 +273,7 @@ async function ackComponentActionJob(jobId: string) {
}
function getExpectedLatestVersionForAction(button: ComponentActionButton) {
if (button.component !== 'forkop' || button.action !== 'install') {
if (button.component !== 'tachyon' || button.action !== 'install') {
return undefined;
}
@@ -306,7 +306,7 @@ function notifyActionProvidersAvailabilityChanged(
}
window.dispatchEvent(
new CustomEvent(FORKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT, {
new CustomEvent(TACHYON_ACTION_PROVIDERS_AVAILABILITY_EVENT, {
detail: {
zapretInstalled: Boolean(systemInfo.zapret_installed),
zapret2Installed: Boolean(systemInfo.zapret2_installed),
@@ -316,20 +316,20 @@ function notifyActionProvidersAvailabilityChanged(
);
}
function reloadPageAfterForkopUpdate() {
function reloadPageAfterTachyonUpdate() {
window.setTimeout(() => {
window.location.reload();
}, 1200);
}
function patchSystemInfoAfterMutation(result: Forkop.ComponentActionResult) {
function patchSystemInfoAfterMutation(result: Tachyon.ComponentActionResult) {
const systemInfo = store.get().diagnosticsSystemInfo;
const nextSystemInfo = { ...systemInfo, loading: false, loaded: true };
const version =
result.current_version || result.latest_version || _('unknown');
if (result.component === 'forkop' && result.action === 'install') {
nextSystemInfo.forkop_version = version;
if (result.component === 'tachyon' && result.action === 'install') {
nextSystemInfo.tachyon_version = version;
}
if (result.component === 'sing_box') {
@@ -421,7 +421,7 @@ async function applyCompletedComponentAction({
notify,
}: {
key: UpdatesActionKey;
result: Forkop.ComponentActionResult;
result: Tachyon.ComponentActionResult;
notify: boolean;
}) {
if (result.action === 'check_update') {
@@ -466,13 +466,13 @@ async function applyCompletedComponentAction({
patchSystemInfoAfterMutation(result);
setActionLoading(key, false);
if (result.component === 'forkop' && result.action === 'install') {
if (result.component === 'tachyon' && result.action === 'install') {
if (notify && result.message) {
showToast(result.message, 'success', 1200);
}
if (notify) {
reloadPageAfterForkopUpdate();
reloadPageAfterTachyonUpdate();
}
return;
}
@@ -487,7 +487,7 @@ async function applyCompletedComponentAction({
async function completeComponentActionJob(
key: UpdatesActionKey,
jobId: string,
response: Forkop.MethodResponse<Forkop.ComponentActionResult>,
response: Tachyon.MethodResponse<Tachyon.ComponentActionResult>,
) {
if (pageUnloading) {
setActionLoading(key, false);
@@ -532,7 +532,7 @@ async function completeComponentActionJob(
});
}
async function followComponentActionState(state: Forkop.ComponentActionResult) {
async function followComponentActionState(state: Tachyon.ComponentActionResult) {
const jobId = state.job_id;
const key = getComponentActionKey(state.component, state.action);
@@ -551,7 +551,7 @@ async function followComponentActionState(state: Forkop.ComponentActionResult) {
try {
const response = state.running
? await ForkopShellMethods.waitComponentActionJob(
? await TachyonShellMethods.waitComponentActionJob(
jobId,
state.component,
state.action,
@@ -560,7 +560,7 @@ async function followComponentActionState(state: Forkop.ComponentActionResult) {
: ({
success: true,
data: state,
} as Forkop.MethodSuccessResponse<Forkop.ComponentActionResult>);
} as Tachyon.MethodSuccessResponse<Tachyon.ComponentActionResult>);
await completeComponentActionJob(key, jobId, response);
} catch (error) {
@@ -611,7 +611,7 @@ function isComponentActionAlreadyRunningError(message: string | undefined) {
);
}
function handleComponentUiState(uiState: Forkop.UiState) {
function handleComponentUiState(uiState: Tachyon.UiState) {
for (const state of uiState.actions.component || []) {
void followComponentActionState(state);
}
@@ -671,7 +671,7 @@ async function handleComponentAction(button: ComponentActionButton) {
let ownsJobFollow = false;
try {
const startResponse = await ForkopShellMethods.componentActionStart(
const startResponse = await TachyonShellMethods.componentActionStart(
button.component,
button.action,
);
@@ -705,7 +705,7 @@ async function handleComponentAction(button: ComponentActionButton) {
followedComponentJobs.add(jobId);
ownsJobFollow = true;
const response = await ForkopShellMethods.waitComponentActionJob(
const response = await TachyonShellMethods.waitComponentActionJob(
jobId,
button.component,
button.action,
@@ -732,7 +732,7 @@ async function handleComponentAction(button: ComponentActionButton) {
}
function getCheckAction(
component: Forkop.ComponentName,
component: Tachyon.ComponentName,
key: UpdatesActionKey,
): ComponentActionButton {
return {
@@ -745,7 +745,7 @@ function getCheckAction(
}
function getInstallAction(
component: Forkop.ComponentName,
component: Tachyon.ComponentName,
key: UpdatesActionKey,
installed: boolean,
): ComponentActionButton {
@@ -759,7 +759,7 @@ function getInstallAction(
}
function getInstalledUpdateActions(
component: Forkop.ComponentName,
component: Tachyon.ComponentName,
checkKey: UpdatesActionKey,
installKey: UpdatesActionKey,
installed = true,
@@ -824,10 +824,10 @@ function getComponentCards(): ComponentCard[] {
Boolean(systemInfo.sing_box_compressed);
const singBoxTiny = Boolean(systemInfo.sing_box_tiny);
const forkopActions = getInstalledUpdateActions(
'forkop',
'forkopCheck',
'forkopInstall',
const tachyonActions = getInstalledUpdateActions(
'tachyon',
'tachyonCheck',
'tachyonInstall',
);
const singBoxActions = getInstalledUpdateActions(
'sing_box',
@@ -898,15 +898,15 @@ function getComponentCards(): ComponentCard[] {
return [
{
component: 'forkop',
component: 'tachyon',
column: 0,
title: 'Forkop',
title: 'Tachyon',
version: systemInfoLoading
? _('Loading...')
: normalizeCompiledVersion(systemInfo.forkop_version),
latestVersion: getLatestVersion('forkop'),
releaseUrl: getGitHubReleaseUrl('forkop'),
actions: forkopActions,
: normalizeCompiledVersion(systemInfo.tachyon_version),
latestVersion: getLatestVersion('tachyon'),
releaseUrl: getGitHubReleaseUrl('tachyon'),
actions: tachyonActions,
},
{
component: 'sing_box',
@@ -1228,7 +1228,7 @@ function onStoreUpdate(
}
function applyComponentUpdateCheckCache(
componentUpdateCheckCache: Forkop.ComponentUpdateCheckCache,
componentUpdateCheckCache: Tachyon.ComponentUpdateCheckCache,
) {
componentUpdateCheckCacheResolved = true;
@@ -1,12 +1,12 @@
// language=CSS
import { FORKOP_UCI_PACKAGE as FORKOP_CBI_PREFIX } from '../../../constants';
import { TACHYON_UCI_PACKAGE as TACHYON_CBI_PREFIX } from '../../../constants';
export const styles = `
#cbi-${FORKOP_CBI_PREFIX}-updates-_mount_node > div {
#cbi-${TACHYON_CBI_PREFIX}-updates-_mount_node > div {
width: 100%;
}
#cbi-${FORKOP_CBI_PREFIX}-updates > h3 {
#cbi-${TACHYON_CBI_PREFIX}-updates > h3 {
display: none;
}
+17 -17
View File
@@ -20,16 +20,16 @@ export namespace ClashAPI {
}
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Forkop {
export namespace Tachyon {
// Available commands:
// start Start forkop service
// stop Stop forkop service
// reload Reload forkop configuration
// restart Restart forkop service
// enable Enable forkop autostart
// disable Disable forkop autostart
// uninstall Remove forkop files installed outside opkg/apk
// main Run main forkop process
// start Start tachyon service
// stop Stop tachyon service
// reload Reload tachyon configuration
// restart Restart tachyon service
// enable Enable tachyon autostart
// disable Disable tachyon autostart
// uninstall Remove tachyon files installed outside opkg/apk
// main Run main tachyon process
// list_update Update domain lists
// check_proxy Check proxy connectivity
// check_nft Check NFT rules
@@ -37,15 +37,15 @@ export namespace Forkop {
// check_sing_box Check sing-box installation and status
// check_inbounds_config Check whether enabled server inbounds are configured
// check_inbounds Check server inbounds from the Servers tab
// check_logs Show forkop logs from system journal
// check_logs Show tachyon logs from system journal
// check_sing_box_logs Show sing-box logs
// check_fakeip Test sing-box FakeIP DNS
// clash_api Clash API interface for managing proxies and groups
// show_config Display current forkop configuration
// show_version Show forkop version
// show_config Display current tachyon configuration
// show_version Show tachyon version
// show_sing_box_config Show sing-box configuration
// show_sing_box_version Show sing-box version
// get_status Get forkop service status
// get_status Get tachyon service status
// get_sing_box_status Get sing-box service status
// get_ui_capabilities Get lightweight UI capabilities
// check_dns_available Check DNS server availability
@@ -455,8 +455,8 @@ export namespace Forkop {
}
export interface GetSystemInfo {
forkop_version: string;
forkop_latest_version: string;
tachyon_version: string;
tachyon_latest_version: string;
luci_app_version: string;
sing_box_version: string;
sing_box_extended: 0 | 1;
@@ -527,7 +527,7 @@ export namespace Forkop {
export interface UiState {
service: {
forkop: GetStatus;
tachyon: GetStatus;
sing_box: GetSingBoxStatus;
};
capabilities: GetUiCapabilities;
@@ -540,7 +540,7 @@ export namespace Forkop {
}
export type ComponentName =
| 'forkop'
| 'tachyon'
| 'sing_box'
| 'zapret'
| 'zapret2'
@@ -8,7 +8,7 @@ const validUrls = [
['With query', 'https://example.com/?q=test'],
['With port', 'http://example.com:8080'],
['With subdomain', 'https://sub.example.com'],
['IPv4 host', 'http://127.0.0.1/forkop-subscription-test.json'],
['IPv4 host', 'http://127.0.0.1/tachyon-subscription-test.json'],
['IPv4 host with port', 'http://192.168.1.1:8080/sub'],
['IPv6 host', 'https://[2001:db8::1]/sub'],
['IPv6 host with port', 'https://[2001:db8::1]:8443/sub'],
+2 -2
View File
@@ -5,7 +5,7 @@ import path from 'path';
export default defineConfig({
entry: ['src/main.ts'],
format: ['esm'], // пусть tsup генерит export {...}
outDir: '../luci-app-forkop/htdocs/luci-static/resources/view/forkop',
outDir: '../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon',
outExtension: () => ({ js: '.js' }),
dts: false,
clean: false,
@@ -18,7 +18,7 @@ export default defineConfig({
},
onSuccess: () => {
const outDir =
'../luci-app-forkop/htdocs/luci-static/resources/view/forkop';
'../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon';
const file = path.join(outDir, 'main.js');
let code = fs.readFileSync(file, 'utf8');
+6 -6
View File
@@ -18,19 +18,19 @@ const config = {
const syncDirs = [
{
local: path.resolve(process.env.LOCAL_DIR_FE ?? '../luci-app-forkop/htdocs/luci-static/resources/view/forkop'),
remote: process.env.REMOTE_DIR_FE ?? '/www/luci-static/resources/view/forkop',
local: path.resolve(process.env.LOCAL_DIR_FE ?? '../luci-app-tachyon/htdocs/luci-static/resources/view/tachyon'),
remote: process.env.REMOTE_DIR_FE ?? '/www/luci-static/resources/view/tachyon',
},
{
local: path.resolve(process.env.LOCAL_DIR_BIN ?? '../forkop/files/usr/bin/'),
local: path.resolve(process.env.LOCAL_DIR_BIN ?? '../tachyon/files/usr/bin/'),
remote: process.env.REMOTE_DIR_BIN ?? '/usr/bin/',
},
{
local: path.resolve(process.env.LOCAL_DIR_LIB ?? '../forkop/files/usr/lib/'),
remote: process.env.REMOTE_DIR_LIB ?? '/usr/lib/forkop/',
local: path.resolve(process.env.LOCAL_DIR_LIB ?? '../tachyon/files/usr/lib/'),
remote: process.env.REMOTE_DIR_LIB ?? '/usr/lib/tachyon/',
},
{
local: path.resolve(process.env.LOCAL_DIR_INIT ?? '../forkop/files/etc/init.d/'),
local: path.resolve(process.env.LOCAL_DIR_INIT ?? '../tachyon/files/etc/init.d/'),
remote: process.env.REMOTE_DIR_INIT ?? '/etc/init.d/',
}
];
+234 -197
View File
@@ -12,25 +12,26 @@ DOWNLOAD_TIMEOUT_SECONDS=600
PKG_IS_APK=0
FETCHER=""
TMP_DIR=""
FORKOP_WAS_ENABLED=0
FORKOP_WAS_RUNNING=0
FORKOP_LEGACY_DETECTED=0
FORKOP_I18N_REQUESTED=0
TACHYON_WAS_ENABLED=0
TACHYON_WAS_RUNNING=0
TACHYON_LEGACY_DETECTED=0
TACHYON_FORKOP_MIGRATION=0
TACHYON_I18N_REQUESTED=0
INSTALLER_LANG="en"
SING_BOX_INSTALL_VARIANT=""
FORKOP_RELEASE_JSON=""
FORKOP_RELEASE_TAG=""
FORKOP_BACKEND_URL=""
FORKOP_BACKEND_NAME=""
FORKOP_BACKEND_FILE=""
FORKOP_APP_URL=""
FORKOP_APP_NAME=""
FORKOP_APP_FILE=""
FORKOP_I18N_URL=""
FORKOP_I18N_NAME=""
FORKOP_I18N_FILE=""
FORKOP_PACKAGE_VERSION=""
TACHYON_RELEASE_JSON=""
TACHYON_RELEASE_TAG=""
TACHYON_BACKEND_URL=""
TACHYON_BACKEND_NAME=""
TACHYON_BACKEND_FILE=""
TACHYON_APP_URL=""
TACHYON_APP_NAME=""
TACHYON_APP_FILE=""
TACHYON_I18N_URL=""
TACHYON_I18N_NAME=""
TACHYON_I18N_FILE=""
TACHYON_PACKAGE_VERSION=""
LEGACY_BRAND="$(printf '\160\157\144\153\157\160')"
LEGACY_BACKEND_PACKAGE="${LEGACY_BRAND}-plus"
LEGACY_CONFIG_PACKAGE_ALT="${LEGACY_BRAND}_plus"
@@ -55,10 +56,10 @@ usage() {
cat <<EOF
Usage: $0
Installs or updates Forkop packages:
- forkop
- luci-app-forkop
- luci-i18n-forkop-ru when requested or when LuCI language is Russian
Installs or updates Tachyon packages:
- tachyon
- luci-app-tachyon
- luci-i18n-tachyon-ru when requested or when LuCI language is Russian
Can also install or switch sing-box variant:
- stable sing-box from OpenWrt feeds
@@ -97,10 +98,10 @@ command_exists() {
}
init_tmp_dir() {
TMP_DIR="$(mktemp -d /tmp/forkop.XXXXXX 2>/dev/null || true)"
TMP_DIR="$(mktemp -d /tmp/tachyon.XXXXXX 2>/dev/null || true)"
if [ -z "$TMP_DIR" ]; then
TMP_DIR="/tmp/forkop.$$"
TMP_DIR="/tmp/tachyon.$$"
mkdir -p "$TMP_DIR" || fail "Failed to create temporary directory: $TMP_DIR"
fi
}
@@ -116,29 +117,29 @@ detect_fetcher() {
return 0
fi
fail "wget or curl is required to download Forkop"
fail "wget or curl is required to download Tachyon"
}
run_with_deadline() {
forkop_deadline_seconds="$1"
tachyon_deadline_seconds="$1"
shift
"$@" &
forkop_deadline_command_pid=$!
tachyon_deadline_command_pid=$!
(
trap 'kill "$forkop_deadline_sleep_pid" 2>/dev/null || true; exit 0' TERM INT
sleep "$forkop_deadline_seconds" &
forkop_deadline_sleep_pid=$!
wait "$forkop_deadline_sleep_pid"
kill "$forkop_deadline_command_pid" 2>/dev/null || true
trap 'kill "$tachyon_deadline_sleep_pid" 2>/dev/null || true; exit 0' TERM INT
sleep "$tachyon_deadline_seconds" &
tachyon_deadline_sleep_pid=$!
wait "$tachyon_deadline_sleep_pid"
kill "$tachyon_deadline_command_pid" 2>/dev/null || true
) &
forkop_deadline_watchdog_pid=$!
tachyon_deadline_watchdog_pid=$!
wait "$forkop_deadline_command_pid"
forkop_deadline_status=$?
kill "$forkop_deadline_watchdog_pid" 2>/dev/null || true
wait "$forkop_deadline_watchdog_pid" 2>/dev/null || true
return "$forkop_deadline_status"
wait "$tachyon_deadline_command_pid"
tachyon_deadline_status=$?
kill "$tachyon_deadline_watchdog_pid" 2>/dev/null || true
wait "$tachyon_deadline_watchdog_pid" 2>/dev/null || true
return "$tachyon_deadline_status"
}
http_get() {
@@ -436,53 +437,53 @@ function env(name, fallback) {
return as_string(value);
}
const INSTALLER_FORKOP_INIT = env("FORKOP_INSTALLER_INIT", "/etc/init.d/forkop");
const INSTALLER_FORKOP_BIN = env("FORKOP_INSTALLER_BIN", "/usr/bin/forkop");
const INSTALLER_FORKOP_LIB = env("FORKOP_INSTALLER_LIB", "/usr/lib/forkop");
const INSTALLER_FORKOP_PERSISTENT_DIR = env("FORKOP_INSTALLER_PERSISTENT_DIR", "/etc/forkop");
const INSTALLER_FORKOP_UCI_DEFAULTS = env("FORKOP_INSTALLER_UCI_DEFAULTS", "/etc/uci-defaults/50_luci-forkop");
const INSTALLER_FORKOP_LUCI_VIEW = env("FORKOP_INSTALLER_LUCI_VIEW", "/www/luci-static/resources/view/forkop");
const INSTALLER_MENU_JSON = env("FORKOP_INSTALLER_MENU_JSON", "/usr/share/luci/menu.d/luci-app-forkop.json");
const INSTALLER_ACL_JSON = env("FORKOP_INSTALLER_ACL_JSON", "/usr/share/rpcd/acl.d/luci-app-forkop.json");
const INSTALLER_RU_LMO = env("FORKOP_INSTALLER_RU_LMO", "/usr/lib/lua/luci/i18n/forkop.ru.lmo");
const INSTALLER_EN_LMO = env("FORKOP_INSTALLER_EN_LMO", "/usr/lib/lua/luci/i18n/forkop.en.lmo");
const INSTALLER_RU_LUA = env("FORKOP_INSTALLER_RU_LUA", "/usr/lib/lua/luci/i18n/forkop.ru.lua");
const INSTALLER_EN_LUA = env("FORKOP_INSTALLER_EN_LUA", "/usr/lib/lua/luci/i18n/forkop.en.lua");
const INSTALLER_RPCD_INIT = env("FORKOP_INSTALLER_RPCD_INIT", "/etc/init.d/rpcd");
const LEGACY_BRAND = env("FORKOP_INSTALLER_LEGACY_BRAND", "");
const LEGACY_BACKEND_PACKAGE = env("FORKOP_INSTALLER_LEGACY_BACKEND", LEGACY_BRAND + "-plus");
const LEGACY_CONFIG_PACKAGE_ALT = env("FORKOP_INSTALLER_LEGACY_CONFIG_ALT", LEGACY_BRAND + "_plus");
const INSTALLER_LEGACY_INIT = env("FORKOP_INSTALLER_LEGACY_INIT", "/etc/init.d/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_BASE_INIT = env("FORKOP_INSTALLER_LEGACY_BASE_INIT", "/etc/init.d/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_BIN = env("FORKOP_INSTALLER_LEGACY_BASE_BIN", "/usr/bin/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_LIB = env("FORKOP_INSTALLER_LEGACY_BASE_LIB", "/usr/lib/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_UCI_DEFAULTS = env("FORKOP_INSTALLER_LEGACY_BASE_UCI_DEFAULTS", "/etc/uci-defaults/50_luci-" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_LUCI_VIEW = env("FORKOP_INSTALLER_LEGACY_BASE_LUCI_VIEW", "/www/luci-static/resources/view/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_MENU_JSON = env("FORKOP_INSTALLER_LEGACY_BASE_MENU_JSON", "/usr/share/luci/menu.d/luci-app-" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_ACL_JSON = env("FORKOP_INSTALLER_LEGACY_BASE_ACL_JSON", "/usr/share/rpcd/acl.d/luci-app-" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_I18N = env("FORKOP_INSTALLER_LEGACY_BASE_I18N", "/usr/lib/lua/luci/i18n/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_CONFIG = env("FORKOP_INSTALLER_LEGACY_BASE_CONFIG", "/etc/config/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_PERSISTENT_DIR = env("FORKOP_INSTALLER_LEGACY_BASE_PERSISTENT_DIR", "/etc/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_RUNTIME_DIR = env("FORKOP_INSTALLER_LEGACY_BASE_RUNTIME_DIR", "/var/run/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_TMP_DIR = env("FORKOP_INSTALLER_LEGACY_BASE_TMP_DIR", "/tmp/" + LEGACY_BRAND);
const INSTALLER_LEGACY_TMP_PACKAGE_GLOB = env("FORKOP_INSTALLER_LEGACY_TMP_PACKAGE_GLOB", "/tmp/*" + LEGACY_BRAND + "*");
const INSTALLER_LEGACY_SCAN_ROOTS = env("FORKOP_INSTALLER_LEGACY_SCAN_ROOTS", "/tmp /var/run /etc /usr/lib /usr/share/luci /usr/share/rpcd /www/luci-static/resources/view");
const INSTALLER_LEGACY_BIN = env("FORKOP_INSTALLER_LEGACY_BIN", "/usr/bin/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_LIB = env("FORKOP_INSTALLER_LEGACY_LIB", "/usr/lib/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_UCI_DEFAULTS = env("FORKOP_INSTALLER_LEGACY_UCI_DEFAULTS", "/etc/uci-defaults/50_luci-" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_LUCI_VIEW = env("FORKOP_INSTALLER_LEGACY_LUCI_VIEW", "/www/luci-static/resources/view/" + LEGACY_CONFIG_PACKAGE_ALT);
const INSTALLER_LEGACY_MENU_JSON = env("FORKOP_INSTALLER_LEGACY_MENU_JSON", "/usr/share/luci/menu.d/luci-app-" + LEGACY_BACKEND_PACKAGE + ".json");
const INSTALLER_LEGACY_ACL_JSON = env("FORKOP_INSTALLER_LEGACY_ACL_JSON", "/usr/share/rpcd/acl.d/luci-app-" + LEGACY_BACKEND_PACKAGE + ".json");
const INSTALLER_LEGACY_CONFIG = env("FORKOP_INSTALLER_LEGACY_CONFIG", "/etc/config/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_CONFIG_ALT = env("FORKOP_INSTALLER_LEGACY_CONFIG_FILE_ALT", "/etc/config/" + LEGACY_CONFIG_PACKAGE_ALT);
const INSTALLER_LEGACY_PERSISTENT_DIR = env("FORKOP_INSTALLER_LEGACY_PERSISTENT_DIR", "/etc/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_RUNTIME_DIR = env("FORKOP_INSTALLER_LEGACY_RUNTIME_DIR", "/var/run/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_TMP_DIR = env("FORKOP_INSTALLER_LEGACY_TMP_DIR", "/tmp/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_TMP_ALT_DIR = env("FORKOP_INSTALLER_LEGACY_TMP_ALT_DIR", "/tmp/" + LEGACY_CONFIG_PACKAGE_ALT);
const INSTALLER_TACHYON_INIT = env("TACHYON_INSTALLER_INIT", "/etc/init.d/tachyon");
const INSTALLER_TACHYON_BIN = env("TACHYON_INSTALLER_BIN", "/usr/bin/tachyon");
const INSTALLER_TACHYON_LIB = env("TACHYON_INSTALLER_LIB", "/usr/lib/tachyon");
const INSTALLER_TACHYON_PERSISTENT_DIR = env("TACHYON_INSTALLER_PERSISTENT_DIR", "/etc/tachyon");
const INSTALLER_TACHYON_UCI_DEFAULTS = env("TACHYON_INSTALLER_UCI_DEFAULTS", "/etc/uci-defaults/50_luci-tachyon");
const INSTALLER_TACHYON_LUCI_VIEW = env("TACHYON_INSTALLER_LUCI_VIEW", "/www/luci-static/resources/view/tachyon");
const INSTALLER_MENU_JSON = env("TACHYON_INSTALLER_MENU_JSON", "/usr/share/luci/menu.d/luci-app-tachyon.json");
const INSTALLER_ACL_JSON = env("TACHYON_INSTALLER_ACL_JSON", "/usr/share/rpcd/acl.d/luci-app-tachyon.json");
const INSTALLER_RU_LMO = env("TACHYON_INSTALLER_RU_LMO", "/usr/lib/lua/luci/i18n/tachyon.ru.lmo");
const INSTALLER_EN_LMO = env("TACHYON_INSTALLER_EN_LMO", "/usr/lib/lua/luci/i18n/tachyon.en.lmo");
const INSTALLER_RU_LUA = env("TACHYON_INSTALLER_RU_LUA", "/usr/lib/lua/luci/i18n/tachyon.ru.lua");
const INSTALLER_EN_LUA = env("TACHYON_INSTALLER_EN_LUA", "/usr/lib/lua/luci/i18n/tachyon.en.lua");
const INSTALLER_RPCD_INIT = env("TACHYON_INSTALLER_RPCD_INIT", "/etc/init.d/rpcd");
const LEGACY_BRAND = env("TACHYON_INSTALLER_LEGACY_BRAND", "");
const LEGACY_BACKEND_PACKAGE = env("TACHYON_INSTALLER_LEGACY_BACKEND", LEGACY_BRAND + "-plus");
const LEGACY_CONFIG_PACKAGE_ALT = env("TACHYON_INSTALLER_LEGACY_CONFIG_ALT", LEGACY_BRAND + "_plus");
const INSTALLER_LEGACY_INIT = env("TACHYON_INSTALLER_LEGACY_INIT", "/etc/init.d/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_BASE_INIT = env("TACHYON_INSTALLER_LEGACY_BASE_INIT", "/etc/init.d/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_BIN = env("TACHYON_INSTALLER_LEGACY_BASE_BIN", "/usr/bin/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_LIB = env("TACHYON_INSTALLER_LEGACY_BASE_LIB", "/usr/lib/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_UCI_DEFAULTS = env("TACHYON_INSTALLER_LEGACY_BASE_UCI_DEFAULTS", "/etc/uci-defaults/50_luci-" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_LUCI_VIEW = env("TACHYON_INSTALLER_LEGACY_BASE_LUCI_VIEW", "/www/luci-static/resources/view/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_MENU_JSON = env("TACHYON_INSTALLER_LEGACY_BASE_MENU_JSON", "/usr/share/luci/menu.d/luci-app-" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_ACL_JSON = env("TACHYON_INSTALLER_LEGACY_BASE_ACL_JSON", "/usr/share/rpcd/acl.d/luci-app-" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_I18N = env("TACHYON_INSTALLER_LEGACY_BASE_I18N", "/usr/lib/lua/luci/i18n/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_CONFIG = env("TACHYON_INSTALLER_LEGACY_BASE_CONFIG", "/etc/config/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_PERSISTENT_DIR = env("TACHYON_INSTALLER_LEGACY_BASE_PERSISTENT_DIR", "/etc/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_RUNTIME_DIR = env("TACHYON_INSTALLER_LEGACY_BASE_RUNTIME_DIR", "/var/run/" + LEGACY_BRAND);
const INSTALLER_LEGACY_BASE_TMP_DIR = env("TACHYON_INSTALLER_LEGACY_BASE_TMP_DIR", "/tmp/" + LEGACY_BRAND);
const INSTALLER_LEGACY_TMP_PACKAGE_GLOB = env("TACHYON_INSTALLER_LEGACY_TMP_PACKAGE_GLOB", "/tmp/*" + LEGACY_BRAND + "*");
const INSTALLER_LEGACY_SCAN_ROOTS = env("TACHYON_INSTALLER_LEGACY_SCAN_ROOTS", "/tmp /var/run /etc /usr/lib /usr/share/luci /usr/share/rpcd /www/luci-static/resources/view");
const INSTALLER_LEGACY_BIN = env("TACHYON_INSTALLER_LEGACY_BIN", "/usr/bin/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_LIB = env("TACHYON_INSTALLER_LEGACY_LIB", "/usr/lib/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_UCI_DEFAULTS = env("TACHYON_INSTALLER_LEGACY_UCI_DEFAULTS", "/etc/uci-defaults/50_luci-" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_LUCI_VIEW = env("TACHYON_INSTALLER_LEGACY_LUCI_VIEW", "/www/luci-static/resources/view/" + LEGACY_CONFIG_PACKAGE_ALT);
const INSTALLER_LEGACY_MENU_JSON = env("TACHYON_INSTALLER_LEGACY_MENU_JSON", "/usr/share/luci/menu.d/luci-app-" + LEGACY_BACKEND_PACKAGE + ".json");
const INSTALLER_LEGACY_ACL_JSON = env("TACHYON_INSTALLER_LEGACY_ACL_JSON", "/usr/share/rpcd/acl.d/luci-app-" + LEGACY_BACKEND_PACKAGE + ".json");
const INSTALLER_LEGACY_CONFIG = env("TACHYON_INSTALLER_LEGACY_CONFIG", "/etc/config/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_CONFIG_ALT = env("TACHYON_INSTALLER_LEGACY_CONFIG_FILE_ALT", "/etc/config/" + LEGACY_CONFIG_PACKAGE_ALT);
const INSTALLER_LEGACY_PERSISTENT_DIR = env("TACHYON_INSTALLER_LEGACY_PERSISTENT_DIR", "/etc/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_RUNTIME_DIR = env("TACHYON_INSTALLER_LEGACY_RUNTIME_DIR", "/var/run/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_TMP_DIR = env("TACHYON_INSTALLER_LEGACY_TMP_DIR", "/tmp/" + LEGACY_BACKEND_PACKAGE);
const INSTALLER_LEGACY_TMP_ALT_DIR = env("TACHYON_INSTALLER_LEGACY_TMP_ALT_DIR", "/tmp/" + LEGACY_CONFIG_PACKAGE_ALT);
let dns_owner_config = "forkop";
let dns_owner_section = "forkop";
let dns_owner_option_prefix = "forkop_";
let dns_owner_config = "tachyon";
let dns_owner_section = "tachyon";
let dns_owner_option_prefix = "tachyon_";
function path_exists(path) {
return fs.stat(as_string(path)) != null;
@@ -667,9 +668,9 @@ function select_dns_owner(legacy) {
dns_owner_option_prefix = LEGACY_BRAND + "_";
}
else {
dns_owner_config = "forkop";
dns_owner_section = "forkop";
dns_owner_option_prefix = "forkop_";
dns_owner_config = "tachyon";
dns_owner_section = "tachyon";
dns_owner_option_prefix = "tachyon_";
}
}
@@ -686,21 +687,21 @@ function installer_deactivate_legacy_base() {
return;
if (installer_service_running(INSTALLER_LEGACY_BASE_INIT)) {
warn("Detected a running legacy service. Stopping it before installing Forkop.\n");
warn("Detected a running legacy service. Stopping it before installing Tachyon.\n");
run_args([ INSTALLER_LEGACY_BASE_INIT, "stop" ]);
}
if (installer_service_enabled(INSTALLER_LEGACY_BASE_INIT)) {
warn("Detected an enabled legacy autostart. Disabling it before installing Forkop.\n");
warn("Detected an enabled legacy autostart. Disabling it before installing Tachyon.\n");
run_args([ INSTALLER_LEGACY_BASE_INIT, "disable" ]);
}
}
function installer_cleanup_legacy() {
let forkop_installed = installer_package_installed("forkop");
let tachyon_installed = installer_package_installed("tachyon");
let legacy_installed = LEGACY_BRAND != "" && installer_package_installed(LEGACY_BACKEND_PACKAGE);
let active_init = legacy_installed ? INSTALLER_LEGACY_INIT : INSTALLER_FORKOP_INIT;
let active_bin = legacy_installed ? INSTALLER_LEGACY_BIN : INSTALLER_FORKOP_BIN;
let active_init = legacy_installed ? INSTALLER_LEGACY_INIT : INSTALLER_TACHYON_INIT;
let active_bin = legacy_installed ? INSTALLER_LEGACY_BIN : INSTALLER_TACHYON_BIN;
let was_enabled = installer_service_enabled(active_init);
let was_running = installer_service_running(active_init) || installer_backend_status_running(active_bin);
@@ -732,9 +733,18 @@ function installer_cleanup_legacy() {
packages_removed = false;
}
if (!installer_remove_package_prefix("luci-i18n-forkop"))
if (installer_package_installed("forkop")) {
if (!installer_remove_package_prefix("luci-i18n-forkop"))
packages_removed = false;
if (!installer_remove_package("luci-app-forkop"))
packages_removed = false;
if (!installer_remove_package("forkop"))
packages_removed = false;
}
if (!installer_remove_package_prefix("luci-i18n-tachyon"))
packages_removed = false;
if (!installer_remove_package("luci-app-forkop"))
if (!installer_remove_package("luci-app-tachyon"))
packages_removed = false;
if (!packages_removed) {
@@ -755,17 +765,17 @@ function installer_cleanup_legacy() {
remove_path(path);
}
if (!forkop_installed) {
remove_path(INSTALLER_FORKOP_LIB);
remove_path(INSTALLER_FORKOP_INIT);
remove_path(INSTALLER_FORKOP_BIN);
if (!tachyon_installed) {
remove_path(INSTALLER_TACHYON_LIB);
remove_path(INSTALLER_TACHYON_INIT);
remove_path(INSTALLER_TACHYON_BIN);
}
for (let path in [
INSTALLER_FORKOP_LUCI_VIEW,
INSTALLER_TACHYON_LUCI_VIEW,
INSTALLER_MENU_JSON,
INSTALLER_ACL_JSON,
INSTALLER_FORKOP_UCI_DEFAULTS,
INSTALLER_TACHYON_UCI_DEFAULTS,
INSTALLER_RU_LMO,
INSTALLER_EN_LMO,
INSTALLER_RU_LUA,
@@ -773,9 +783,9 @@ function installer_cleanup_legacy() {
])
remove_path(path);
print("FORKOP_WAS_ENABLED=", was_enabled ? "1" : "0", "\n");
print("FORKOP_WAS_RUNNING=", was_running ? "1" : "0", "\n");
print("FORKOP_LEGACY_DETECTED=", legacy_installed ? "1" : "0", "\n");
print("TACHYON_WAS_ENABLED=", was_enabled ? "1" : "0", "\n");
print("TACHYON_WAS_RUNNING=", was_running ? "1" : "0", "\n");
print("TACHYON_LEGACY_DETECTED=", legacy_installed ? "1" : "0", "\n");
return true;
}
@@ -786,8 +796,8 @@ function installer_finalize_legacy() {
let legacy_tailscale_dir = INSTALLER_LEGACY_PERSISTENT_DIR + "/tailscale";
if (path_exists(legacy_tailscale_dir)) {
let entries = fs.lsdir(legacy_tailscale_dir);
let forkop_tailscale_dir = INSTALLER_FORKOP_PERSISTENT_DIR + "/tailscale";
if (type(entries) != "array" || !run_args([ "mkdir", "-p", forkop_tailscale_dir ])) {
let tachyon_tailscale_dir = INSTALLER_TACHYON_PERSISTENT_DIR + "/tailscale";
if (type(entries) != "array" || !run_args([ "mkdir", "-p", tachyon_tailscale_dir ])) {
warn("Failed to prepare legacy Tailscale state migration; the legacy directory was preserved.\n");
return false;
}
@@ -795,11 +805,11 @@ function installer_finalize_legacy() {
for (let entry in entries) {
entry = as_string(entry);
let source = legacy_tailscale_dir + "/" + entry;
let target = forkop_tailscale_dir + "/" + entry;
let target = tachyon_tailscale_dir + "/" + entry;
if (path_exists(target))
continue;
let temporary = forkop_tailscale_dir + "/." + entry + ".forkop-migrate";
let temporary = tachyon_tailscale_dir + "/." + entry + ".tachyon-migrate";
if (!remove_path(temporary) ||
!run_args([ "cp", "-a", source, temporary ]) ||
!run_args([ "mv", temporary, target ])) {
@@ -863,26 +873,26 @@ function installer_finalize_legacy() {
}
function installer_post_install() {
remove_globs(env("FORKOP_INSTALLER_LUCI_CACHE_GLOBS", "/var/luci-indexcache* /tmp/luci-indexcache*"));
remove_globs(env("TACHYON_INSTALLER_LUCI_CACHE_GLOBS", "/var/luci-indexcache* /tmp/luci-indexcache*"));
for (let path in [
env("FORKOP_INSTALLER_LATEST_VERSION_CACHE", "/tmp/forkop.latest-version.cache"),
env("FORKOP_INSTALLER_SYSTEM_INFO_CACHE", "/var/run/forkop/system-info.json"),
env("FORKOP_INSTALLER_SERVER_COUNTRY_CACHE", "/var/run/forkop/server-country-cache.json"),
env("FORKOP_INSTALLER_SING_BOX_VERSION_CACHE", "/var/run/forkop/ui-state/sing-box-version"),
env("FORKOP_INSTALLER_TMP_SYSTEM_INFO_CACHE", "/tmp/forkop/system-info.json")
env("TACHYON_INSTALLER_LATEST_VERSION_CACHE", "/tmp/tachyon.latest-version.cache"),
env("TACHYON_INSTALLER_SYSTEM_INFO_CACHE", "/var/run/tachyon/system-info.json"),
env("TACHYON_INSTALLER_SERVER_COUNTRY_CACHE", "/var/run/tachyon/server-country-cache.json"),
env("TACHYON_INSTALLER_SING_BOX_VERSION_CACHE", "/var/run/tachyon/ui-state/sing-box-version"),
env("TACHYON_INSTALLER_TMP_SYSTEM_INFO_CACHE", "/tmp/tachyon/system-info.json")
])
remove_path(path);
if (path_executable(INSTALLER_RPCD_INIT))
run_args([ INSTALLER_RPCD_INIT, "reload" ]);
if (env("FORKOP_WAS_ENABLED", "0") == "1" && path_executable(INSTALLER_FORKOP_INIT))
run_args([ INSTALLER_FORKOP_INIT, "enable" ]);
if (env("TACHYON_WAS_ENABLED", "0") == "1" && path_executable(INSTALLER_TACHYON_INIT))
run_args([ INSTALLER_TACHYON_INIT, "enable" ]);
if (env("FORKOP_WAS_RUNNING", "0") == "1" && path_executable(INSTALLER_FORKOP_INIT)) {
if (!run_args([ INSTALLER_FORKOP_INIT, "start" ]) &&
!run_args([ INSTALLER_FORKOP_INIT, "restart" ]))
warn("Failed to start Forkop after upgrade.\n");
if (env("TACHYON_WAS_RUNNING", "0") == "1" && path_executable(INSTALLER_TACHYON_INIT)) {
if (!run_args([ INSTALLER_TACHYON_INIT, "start" ]) &&
!run_args([ INSTALLER_TACHYON_INIT, "restart" ]))
warn("Failed to start Tachyon after upgrade.\n");
}
return true;
@@ -1024,11 +1034,11 @@ function asset_matches(name, kind, ext, version) {
return false;
if (kind == "backend")
return name == "forkop_" + version + "." + ext;
return name == "tachyon_" + version + "." + ext;
if (kind == "app")
return name == "luci-app-forkop_" + version + "." + ext;
return name == "luci-app-tachyon_" + version + "." + ext;
if (kind == "i18n")
return name == "luci-i18n-forkop-ru_" + version + "." + ext;
return name == "luci-i18n-tachyon-ru_" + version + "." + ext;
return false;
}
@@ -1092,9 +1102,9 @@ EOF
install_json_ucode() {
FORKOP_INSTALLER_LEGACY_BRAND="$LEGACY_BRAND" \
FORKOP_INSTALLER_LEGACY_BACKEND="$LEGACY_BACKEND_PACKAGE" \
FORKOP_INSTALLER_LEGACY_CONFIG_ALT="$LEGACY_CONFIG_PACKAGE_ALT" \
TACHYON_INSTALLER_LEGACY_BRAND="$LEGACY_BRAND" \
TACHYON_INSTALLER_LEGACY_BACKEND="$LEGACY_BACKEND_PACKAGE" \
TACHYON_INSTALLER_LEGACY_CONFIG_ALT="$LEGACY_CONFIG_PACKAGE_ALT" \
ucode "$(install_json_helper_path)" "$@"
}
@@ -1244,7 +1254,7 @@ check_system() {
major="$(printf '%s' "$release" | sed 's/[^0-9].*$//' | cut -d. -f1)"
if [ -n "$major" ] && [ "$major" -lt 24 ]; then
fail "Forkop requires OpenWrt 24.10 or newer"
fail "Tachyon requires OpenWrt 24.10 or newer"
fi
available_space="$(df /overlay 2>/dev/null | awk 'NR==2 {print $4}')"
@@ -1302,7 +1312,7 @@ detect_installer_language() {
luci_lang="$(get_luci_main_lang)"
INSTALLER_LANG="en"
if pkg_is_installed "luci-i18n-forkop-ru"; then
if pkg_is_installed "luci-i18n-tachyon-ru"; then
INSTALLER_LANG="ru"
return 0
fi
@@ -1357,14 +1367,14 @@ extract_package_version() {
package_name="$1"
case "$package_name" in
forkop_*.ipk|forkop_*.apk)
printf '%s\n' "$package_name" | sed 's/^forkop_//;s/\.ipk$//;s/\.apk$//'
tachyon_*.ipk|tachyon_*.apk)
printf '%s\n' "$package_name" | sed 's/^tachyon_//;s/\.ipk$//;s/\.apk$//'
;;
luci-app-forkop_*.ipk|luci-app-forkop_*.apk)
printf '%s\n' "$package_name" | sed 's/^luci-app-forkop_//;s/\.ipk$//;s/\.apk$//'
luci-app-tachyon_*.ipk|luci-app-tachyon_*.apk)
printf '%s\n' "$package_name" | sed 's/^luci-app-tachyon_//;s/\.ipk$//;s/\.apk$//'
;;
luci-i18n-forkop-ru_*.ipk|luci-i18n-forkop-ru_*.apk)
printf '%s\n' "$package_name" | sed 's/^luci-i18n-forkop-ru_//;s/\.ipk$//;s/\.apk$//'
luci-i18n-tachyon-ru_*.ipk|luci-i18n-tachyon-ru_*.apk)
printf '%s\n' "$package_name" | sed 's/^luci-i18n-tachyon-ru_//;s/\.ipk$//;s/\.apk$//'
;;
*)
printf '%s\n' "$package_name"
@@ -1396,32 +1406,32 @@ fetch_github_latest_release_json() {
printf '%s' "$response"
}
resolve_forkop_release() {
resolve_tachyon_release() {
asset_ext="ipk"
[ "$PKG_IS_APK" -eq 1 ] && asset_ext="apk"
FORKOP_RELEASE_JSON="$(fetch_github_latest_release_json "$REPO_OWNER" "$REPO_NAME")"
FORKOP_RELEASE_TAG="$(printf '%s' "$FORKOP_RELEASE_JSON" | install_json_ucode release-tag 2>/dev/null)"
[ -n "$FORKOP_RELEASE_TAG" ] || fail "Failed to detect the Forkop release tag"
TACHYON_RELEASE_JSON="$(fetch_github_latest_release_json "$REPO_OWNER" "$REPO_NAME")"
TACHYON_RELEASE_TAG="$(printf '%s' "$TACHYON_RELEASE_JSON" | install_json_ucode release-tag 2>/dev/null)"
[ -n "$TACHYON_RELEASE_TAG" ] || fail "Failed to detect the Tachyon release tag"
FORKOP_BACKEND_URL="$(printf '%s' "$FORKOP_RELEASE_JSON" | install_json_ucode release-asset-url backend "$asset_ext" 2>/dev/null)"
[ -n "$FORKOP_BACKEND_URL" ] || fail "The Forkop release does not contain a forkop .$asset_ext package"
TACHYON_BACKEND_URL="$(printf '%s' "$TACHYON_RELEASE_JSON" | install_json_ucode release-asset-url backend "$asset_ext" 2>/dev/null)"
[ -n "$TACHYON_BACKEND_URL" ] || fail "The Tachyon release does not contain a tachyon .$asset_ext package"
FORKOP_APP_URL="$(printf '%s' "$FORKOP_RELEASE_JSON" | install_json_ucode release-asset-url app "$asset_ext" 2>/dev/null)"
[ -n "$FORKOP_APP_URL" ] || fail "The Forkop release does not contain a luci-app-forkop .$asset_ext package"
TACHYON_APP_URL="$(printf '%s' "$TACHYON_RELEASE_JSON" | install_json_ucode release-asset-url app "$asset_ext" 2>/dev/null)"
[ -n "$TACHYON_APP_URL" ] || fail "The Tachyon release does not contain a luci-app-tachyon .$asset_ext package"
FORKOP_BACKEND_NAME="$(basename "$FORKOP_BACKEND_URL")"
FORKOP_APP_NAME="$(basename "$FORKOP_APP_URL")"
FORKOP_PACKAGE_VERSION="$(extract_package_version "$FORKOP_BACKEND_NAME")"
TACHYON_BACKEND_NAME="$(basename "$TACHYON_BACKEND_URL")"
TACHYON_APP_NAME="$(basename "$TACHYON_APP_URL")"
TACHYON_PACKAGE_VERSION="$(extract_package_version "$TACHYON_BACKEND_NAME")"
FORKOP_I18N_URL=""
FORKOP_I18N_NAME=""
TACHYON_I18N_URL=""
TACHYON_I18N_NAME=""
if [ "$FORKOP_I18N_REQUESTED" -eq 1 ]; then
FORKOP_I18N_URL="$(printf '%s' "$FORKOP_RELEASE_JSON" | install_json_ucode release-asset-url i18n "$asset_ext" 2>/dev/null)"
[ -n "$FORKOP_I18N_URL" ] || fail "The Forkop release does not contain a luci-i18n-forkop-ru .$asset_ext package"
FORKOP_I18N_NAME="$(basename "$FORKOP_I18N_URL")"
if [ "$TACHYON_I18N_REQUESTED" -eq 1 ]; then
TACHYON_I18N_URL="$(printf '%s' "$TACHYON_RELEASE_JSON" | install_json_ucode release-asset-url i18n "$asset_ext" 2>/dev/null)"
[ -n "$TACHYON_I18N_URL" ] || fail "The Tachyon release does not contain a luci-i18n-tachyon-ru .$asset_ext package"
TACHYON_I18N_NAME="$(basename "$TACHYON_I18N_URL")"
fi
}
@@ -1436,11 +1446,11 @@ select_sing_box_installation() {
answer=""
default_choice=1
if [ "$FORKOP_LEGACY_DETECTED" -eq 1 ] &&
if [ "$TACHYON_LEGACY_DETECTED" -eq 1 ] &&
[ -r /etc/init.d/sing-box ] &&
grep -Fq 'managed sing-box service for binary variants' /etc/init.d/sing-box; then
SING_BOX_INSTALL_VARIANT="extended-compressed"
msg "The legacy binary-managed sing-box variant will be reinstalled for Forkop"
msg "The legacy binary-managed sing-box variant will be reinstalled for Tachyon"
return 0
fi
@@ -1499,9 +1509,9 @@ install_selected_sing_box() {
;;
esac
[ -x /usr/bin/forkop ] || fail "forkop backend must be installed before sing-box component action"
msg "Installing selected sing-box variant through Forkop ucode backend"
if ! /usr/bin/forkop component_action sing_box "$action" >"$output_file" 2>&1; then
[ -x /usr/bin/tachyon ] || fail "tachyon backend must be installed before sing-box component action"
msg "Installing selected sing-box variant through Tachyon ucode backend"
if ! /usr/bin/tachyon component_action sing_box "$action" >"$output_file" 2>&1; then
cat "$output_file" >&2 2>/dev/null || true
fail "Failed to install selected sing-box variant"
fi
@@ -1511,31 +1521,53 @@ cleanup_legacy_installation() {
state_file="$TMP_DIR/install-state.env"
install_json_ucode installer-cleanup-legacy >"$state_file" ||
fail "Failed to prepare the system before Forkop package installation"
fail "Failed to prepare the system before Tachyon package installation"
# shellcheck disable=SC1090
. "$state_file"
}
detect_legacy_installation() {
FORKOP_LEGACY_DETECTED=0
TACHYON_LEGACY_DETECTED=0
TACHYON_FORKOP_MIGRATION=0
LEGACY_CONFIG_BACKUP=""
pkg_is_installed "$LEGACY_BACKEND_PACKAGE" || return 0
if pkg_is_installed "forkop" || pkg_is_installed "luci-app-forkop"; then
TACHYON_LEGACY_DETECTED=1
TACHYON_FORKOP_MIGRATION=1
fi
FORKOP_LEGACY_DETECTED=1
for legacy_config_path in \
"/etc/config/$LEGACY_BACKEND_PACKAGE" \
"/etc/config/$LEGACY_CONFIG_PACKAGE_ALT"; do
for legacy_config_path in "/etc/config/forkop" "/etc/config/forkop_plus"; do
if [ -r "$legacy_config_path" ]; then
LEGACY_CONFIG_BACKUP="$TMP_DIR/legacy-config.backup"
cp "$legacy_config_path" "$LEGACY_CONFIG_BACKUP" ||
fail "Failed to back up the legacy configuration"
TACHYON_LEGACY_DETECTED=1
TACHYON_FORKOP_MIGRATION=1
break
fi
done
msg "Legacy installation detected; its packages will be removed and its configuration will be upgraded"
if [ "$TACHYON_LEGACY_DETECTED" -eq 0 ]; then
if pkg_is_installed "$LEGACY_BACKEND_PACKAGE"; then
TACHYON_LEGACY_DETECTED=1
fi
for legacy_config_path in \
"/etc/config/$LEGACY_BACKEND_PACKAGE" \
"/etc/config/$LEGACY_CONFIG_PACKAGE_ALT"; do
if [ -r "$legacy_config_path" ]; then
LEGACY_CONFIG_BACKUP="$TMP_DIR/legacy-config.backup"
cp "$legacy_config_path" "$LEGACY_CONFIG_BACKUP" ||
fail "Failed to back up the legacy configuration"
TACHYON_LEGACY_DETECTED=1
break
fi
done
fi
if [ "$TACHYON_LEGACY_DETECTED" -eq 1 ]; then
msg "Legacy installation detected; its packages will be removed and its configuration will be upgraded"
fi
}
decide_i18n_installation() {
@@ -1543,22 +1575,22 @@ decide_i18n_installation() {
detect_installer_language
if pkg_is_installed "luci-i18n-forkop-ru"; then
FORKOP_I18N_REQUESTED=1
if pkg_is_installed "luci-i18n-tachyon-ru"; then
TACHYON_I18N_REQUESTED=1
msg "$(installer_text i18n_installed)"
return 0
fi
if [ "$FORKOP_LEGACY_DETECTED" -eq 1 ] &&
if [ "$TACHYON_LEGACY_DETECTED" -eq 1 ] &&
pkg_is_installed "luci-i18n-${LEGACY_BACKEND_PACKAGE}-ru"; then
FORKOP_I18N_REQUESTED=1
TACHYON_I18N_REQUESTED=1
msg "$(installer_text i18n_installed)"
return 0
fi
case "$luci_lang" in
ru|ru_*|ru-*)
FORKOP_I18N_REQUESTED=1
TACHYON_I18N_REQUESTED=1
INSTALLER_LANG="ru"
msg "$(installer_text luci_ru)"
return 0
@@ -1566,7 +1598,7 @@ decide_i18n_installation() {
esac
if confirm_prompt "$(installer_text i18n_prompt)"; then
FORKOP_I18N_REQUESTED=1
TACHYON_I18N_REQUESTED=1
INSTALLER_LANG="ru"
return 0
fi
@@ -1574,42 +1606,47 @@ decide_i18n_installation() {
warn "$(installer_text i18n_skip)"
}
download_forkop_packages() {
FORKOP_BACKEND_FILE="$TMP_DIR/$FORKOP_BACKEND_NAME"
FORKOP_APP_FILE="$TMP_DIR/$FORKOP_APP_NAME"
FORKOP_I18N_FILE=""
download_tachyon_packages() {
TACHYON_BACKEND_FILE="$TMP_DIR/$TACHYON_BACKEND_NAME"
TACHYON_APP_FILE="$TMP_DIR/$TACHYON_APP_NAME"
TACHYON_I18N_FILE=""
download_with_retry "$FORKOP_BACKEND_URL" "$FORKOP_BACKEND_FILE" "$FORKOP_BACKEND_NAME" || fail "Failed to download $FORKOP_BACKEND_NAME"
download_with_retry "$FORKOP_APP_URL" "$FORKOP_APP_FILE" "$FORKOP_APP_NAME" || fail "Failed to download $FORKOP_APP_NAME"
download_with_retry "$TACHYON_BACKEND_URL" "$TACHYON_BACKEND_FILE" "$TACHYON_BACKEND_NAME" || fail "Failed to download $TACHYON_BACKEND_NAME"
download_with_retry "$TACHYON_APP_URL" "$TACHYON_APP_FILE" "$TACHYON_APP_NAME" || fail "Failed to download $TACHYON_APP_NAME"
if [ -n "$FORKOP_I18N_URL" ]; then
FORKOP_I18N_FILE="$TMP_DIR/$FORKOP_I18N_NAME"
download_with_retry "$FORKOP_I18N_URL" "$FORKOP_I18N_FILE" "$FORKOP_I18N_NAME" || fail "Failed to download $FORKOP_I18N_NAME"
if [ -n "$TACHYON_I18N_URL" ]; then
TACHYON_I18N_FILE="$TMP_DIR/$TACHYON_I18N_NAME"
download_with_retry "$TACHYON_I18N_URL" "$TACHYON_I18N_FILE" "$TACHYON_I18N_NAME" || fail "Failed to download $TACHYON_I18N_NAME"
fi
}
install_backend_package() {
pkg_install_files "$FORKOP_BACKEND_FILE" || fail "forkop installation failed"
pkg_install_files "$TACHYON_BACKEND_FILE" || fail "tachyon installation failed"
}
migrate_legacy_configuration() {
[ "$FORKOP_LEGACY_DETECTED" -eq 1 ] || return 0
[ "$TACHYON_LEGACY_DETECTED" -eq 1 ] || return 0
if [ -n "$LEGACY_CONFIG_BACKUP" ]; then
cp "$LEGACY_CONFIG_BACKUP" /etc/config/forkop ||
cp "$LEGACY_CONFIG_BACKUP" /etc/config/tachyon ||
fail "Failed to restore the legacy configuration for migration"
chmod 0644 /etc/config/forkop ||
fail "Failed to set permissions on the Forkop configuration"
chmod 0644 /etc/config/tachyon ||
fail "Failed to set permissions on the Tachyon configuration"
msg "Migrating the legacy configuration to Forkop"
if ! FORKOP_CONFIG_NAME="forkop" \
FORKOP_LIB="/usr/lib/forkop" \
ucode -L /usr/lib/forkop /usr/lib/forkop/config/migration.uc migrate-podkop; then
cp "$LEGACY_CONFIG_BACKUP" /etc/config/forkop 2>/dev/null || true
msg "Migrating the legacy configuration to Tachyon"
local migration_mode="migrate-podkop"
if [ "$TACHYON_FORKOP_MIGRATION" -eq 1 ]; then
migration_mode="migrate"
fi
if ! TACHYON_CONFIG_NAME="tachyon" \
TACHYON_LIB="/usr/lib/tachyon" \
ucode -L /usr/lib/tachyon /usr/lib/tachyon/config/migration.uc "$migration_mode"; then
cp "$LEGACY_CONFIG_BACKUP" /etc/config/tachyon 2>/dev/null || true
fail "Legacy configuration migration failed; the original configuration was restored"
fi
else
warn "The legacy package had no readable configuration; Forkop defaults will be used"
warn "The legacy package had no readable configuration; Tachyon defaults will be used"
fi
install_json_ucode installer-finalize-legacy ||
@@ -1617,17 +1654,17 @@ migrate_legacy_configuration() {
}
install_ui_packages() {
pkg_install_files "$FORKOP_APP_FILE" || fail "luci-app-forkop installation failed"
pkg_install_files "$TACHYON_APP_FILE" || fail "luci-app-tachyon installation failed"
if [ -n "$FORKOP_I18N_FILE" ]; then
pkg_install_files "$FORKOP_I18N_FILE" || fail "luci-i18n-forkop-ru installation failed"
if [ -n "$TACHYON_I18N_FILE" ]; then
pkg_install_files "$TACHYON_I18N_FILE" || fail "luci-i18n-tachyon-ru installation failed"
fi
}
post_install() {
FORKOP_WAS_ENABLED="$FORKOP_WAS_ENABLED" FORKOP_WAS_RUNNING="$FORKOP_WAS_RUNNING" \
TACHYON_WAS_ENABLED="$TACHYON_WAS_ENABLED" TACHYON_WAS_RUNNING="$TACHYON_WAS_RUNNING" \
install_json_ucode installer-post-install ||
fail "Failed to complete Forkop post-install actions"
fail "Failed to complete Tachyon post-install actions"
}
main() {
@@ -1647,8 +1684,8 @@ main() {
pkg_list_update || fail "Failed to update package lists"
ensure_bootstrap_ucode_runtime
resolve_forkop_release
download_forkop_packages
resolve_tachyon_release
download_tachyon_packages
cleanup_legacy_installation
install_backend_package
@@ -1657,9 +1694,9 @@ main() {
install_selected_sing_box
post_install
msg "Forkop $FORKOP_PACKAGE_VERSION has been installed successfully"
msg "Source release: ${REPO_OWNER}/${REPO_NAME}@${FORKOP_RELEASE_TAG}"
warn "Open LuCI and review your rules before enabling Forkop"
msg "Tachyon $TACHYON_PACKAGE_VERSION has been installed successfully"
msg "Source release: ${REPO_OWNER}/${REPO_NAME}@${TACHYON_RELEASE_TAG}"
warn "Open LuCI and review your rules before enabling Tachyon"
}
main "$@"
+12 -12
View File
@@ -1,34 +1,34 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-forkop
PKG_NAME:=luci-app-tachyon
FORKOP_SOURCE_VERSION:=$(or $(FORKOP_PACKAGE_VERSION),$(FORKOP_VERSION))
FORKOP_HAS_RELEASE_VERSION:=$(if $(filter dev,$(FORKOP_SOURCE_VERSION)),,$(FORKOP_SOURCE_VERSION))
FORKOP_COMPILED_VERSION:=$(if $(FORKOP_HAS_RELEASE_VERSION),$(FORKOP_SOURCE_VERSION),$(or $(FORKOP_SOURCE_VERSION),dev))
FORKOP_RELEASE_VERSION_VALID:=$(if $(FORKOP_HAS_RELEASE_VERSION),$(shell printf '%s\n' '$(FORKOP_SOURCE_VERSION)' | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$$' && printf '1'),1)
TACHYON_SOURCE_VERSION:=$(or $(TACHYON_PACKAGE_VERSION),$(TACHYON_VERSION))
TACHYON_HAS_RELEASE_VERSION:=$(if $(filter dev,$(TACHYON_SOURCE_VERSION)),,$(TACHYON_SOURCE_VERSION))
TACHYON_COMPILED_VERSION:=$(if $(TACHYON_HAS_RELEASE_VERSION),$(TACHYON_SOURCE_VERSION),$(or $(TACHYON_SOURCE_VERSION),dev))
TACHYON_RELEASE_VERSION_VALID:=$(if $(TACHYON_HAS_RELEASE_VERSION),$(shell printf '%s\n' '$(TACHYON_SOURCE_VERSION)' | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$$' && printf '1'),1)
ifeq ($(FORKOP_RELEASE_VERSION_VALID),)
$(error FORKOP_PACKAGE_VERSION and FORKOP_VERSION must use x.y.z format)
ifeq ($(TACHYON_RELEASE_VERSION_VALID),)
$(error TACHYON_PACKAGE_VERSION and TACHYON_VERSION must use x.y.z format)
endif
PKG_VERSION:=$(or $(FORKOP_HAS_RELEASE_VERSION),0.0.0)
PKG_VERSION:=$(or $(TACHYON_HAS_RELEASE_VERSION),0.0.0)
PKG_RELEASE:=
PKG_PO_VERSION:=$(PKG_VERSION)
LUCI_TITLE:=LuCI Forkop app
LUCI_DEPENDS:=+luci-base +forkop
LUCI_TITLE:=LuCI Tachyon app
LUCI_DEPENDS:=+luci-base +tachyon
LUCI_PKGARCH:=all
LUCI_LANG.ru:=Русский (Russian)
LUCI_LANG.en:=English
LUCI_MAINTAINER:=Dushnilin <dushnilin@gmail.com>
LUCI_DESCRIPTION:=Rule-based Forkop LuCI app with hybrid sing-box + zapret orchestration
LUCI_DESCRIPTION:=Rule-based Tachyon LuCI app with hybrid sing-box + zapret orchestration
LUCI_MINIFY_JS:=0
PKG_LICENSE:=GPL-2.0-or-later
LUCI_LANGUAGES:=en ru
define Build/Prepare/$(PKG_NAME)
sed -i -e 's/__COMPILED_VERSION_VARIABLE__/$(FORKOP_COMPILED_VERSION)/g' $(PKG_BUILD_DIR)/htdocs/luci-static/resources/view/forkop/main.js || true
sed -i -e 's/__COMPILED_VERSION_VARIABLE__/$(TACHYON_COMPILED_VERSION)/g' $(PKG_BUILD_DIR)/htdocs/luci-static/resources/view/tachyon/main.js || true
endef
include $(TOPDIR)/feeds/luci/luci.mk
@@ -4,7 +4,7 @@
"require ui";
"require uci";
"require fs";
"require view.forkop.main as main";
"require view.tachyon.main as main";
function createDashboardContent(section) {
const o = section.option(form.DummyValue, "_mount_node");
@@ -4,7 +4,7 @@
"require ui";
"require uci";
"require fs";
"require view.forkop.main as main";
"require view.tachyon.main as main";
function createDiagnosticContent(section) {
const o = section.option(form.DummyValue, "_mount_node");
@@ -2,7 +2,7 @@
"require baseclass";
"require rpc";
"require ui";
"require view.forkop.main as main";
"require view.tachyon.main as main";
const callHostHints = rpc.declare({
object: "luci-rpc",
File diff suppressed because it is too large Load Diff
@@ -1,8 +1,8 @@
"use strict";
"require baseclass";
"require form";
"require view.forkop.local_devices as localDevices";
"require view.forkop.main as main";
"require view.tachyon.local_devices as localDevices";
"require view.tachyon.main as main";
function createMonitoringContent(section) {
const o = section.option(form.DummyValue, "_mount_node");
@@ -5,13 +5,13 @@
"require network";
"require ui";
"require uci";
"require view.forkop.local_devices as localDevices";
"require view.forkop.main as main";
"require view.tachyon.local_devices as localDevices";
"require view.tachyon.main as main";
const UCI_PACKAGE = main.FORKOP_UCI_PACKAGE;
const UCI_PACKAGE = main.TACHYON_UCI_PACKAGE;
const ACTION_PROVIDERS_AVAILABILITY_EVENT =
main.FORKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT ||
"forkop:action-providers-availability";
main.TACHYON_ACTION_PROVIDERS_AVAILABILITY_EVENT ||
"tachyon:action-providers-availability";
const RULE_SET_ITEM_SETTINGS_KEY = "rule_set_settings";
const ROUTING_ACTIONS = [
"connection",
@@ -165,7 +165,7 @@ const BYEDPI_DEFAULT_CMD_OPTS = "-o 2 --auto=t,r,a,s -d 2";
const ANNOTATED_TEXTAREA_STYLE_ID = "fkp-annotated-textarea-styles";
const CONNECTIONS_DYNLIST_STYLE_ID = "fkp-connections-dynlist-styles";
const NFQWS_REMOTE_VALIDATION_DEBOUNCE_MS = 500;
const NFQWS_VALIDATION_COMMAND = "/usr/bin/forkop";
const NFQWS_VALIDATION_COMMAND = "/usr/bin/tachyon";
const nfqwsRemoteValidationCache = new Map();
const nfqwsRemoteValidationInflight = new Map();
const nfqws2RemoteValidationCache = new Map();
@@ -431,7 +431,7 @@ let actionProvidersAvailabilityLoader = null;
const outboundNameChoicesCache = new Map();
const outboundNameChoicesInflight = new Map();
const outboundNameSourceOptions = new Map();
const SECTION_CACHE_DIR = "/var/run/forkop/section-cache";
const SECTION_CACHE_DIR = "/var/run/tachyon/section-cache";
const COUNTRY_CODES =
"AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CR CU CV CW CX CY CZ DE DJ DK DM DO DZ EC EE EG EH ER ES ET FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN HR HT HU ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS ST SV SX SY SZ TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UM US UY UZ VA VC VE VG VI VN VU WF WS YE YT ZA ZM ZW XK".split(
" ",
@@ -3550,9 +3550,9 @@ function ensureActionProvidersAvailabilityLoaded() {
}
actionProvidersAvailabilityPromise = Promise.allSettled([
main.ForkopShellMethods.checkZapretRuntime(),
main.ForkopShellMethods.checkZapret2Runtime(),
main.ForkopShellMethods.checkByedpiRuntime(),
main.TachyonShellMethods.checkZapretRuntime(),
main.TachyonShellMethods.checkZapret2Runtime(),
main.TachyonShellMethods.checkByedpiRuntime(),
])
.then(([zapretResult, zapret2Result, byedpiResult]) => {
const zapret =
@@ -4441,9 +4441,9 @@ function attachAnnotatedTextarea(textarea, analyzer) {
ensureAnnotatedTextareaStyles();
if (textarea.__forkopAnnotatedTextareaController) {
textarea.__forkopAnnotatedTextareaController.analyzer = analyzer;
textarea.__forkopAnnotatedTextareaController.update();
if (textarea.__tachyonAnnotatedTextareaController) {
textarea.__tachyonAnnotatedTextareaController.analyzer = analyzer;
textarea.__tachyonAnnotatedTextareaController.update();
return;
}
@@ -4474,7 +4474,7 @@ function attachAnnotatedTextarea(textarea, analyzer) {
},
};
textarea.__forkopAnnotatedTextareaController = controller;
textarea.__tachyonAnnotatedTextareaController = controller;
const updateAnnotatedTextarea = () => controller.update();
textarea.addEventListener("input", updateAnnotatedTextarea);
@@ -4500,21 +4500,21 @@ function refreshAnnotatedTextareaValidation(option, section_id, textarea) {
if (
textarea &&
textarea.__forkopAnnotatedTextareaController &&
typeof textarea.__forkopAnnotatedTextareaController.update === "function"
textarea.__tachyonAnnotatedTextareaController &&
typeof textarea.__tachyonAnnotatedTextareaController.update === "function"
) {
textarea.__forkopAnnotatedTextareaController.update();
textarea.__tachyonAnnotatedTextareaController.update();
}
}
function attachNfqwsRemoteValidation(option, section_id, textarea) {
if (!textarea || textarea.__forkopNfqwsRemoteValidationAttached) {
if (!textarea || textarea.__tachyonNfqwsRemoteValidationAttached) {
return;
}
textarea.__forkopNfqwsRemoteValidationAttached = true;
textarea.__forkopNfqwsRemoteValidationRequestId = 0;
textarea.__forkopNfqwsRemoteValidationTimer = null;
textarea.__tachyonNfqwsRemoteValidationAttached = true;
textarea.__tachyonNfqwsRemoteValidationRequestId = 0;
textarea.__tachyonNfqwsRemoteValidationTimer = null;
const runValidation = () => {
const value = textarea.value;
@@ -4525,11 +4525,11 @@ function attachNfqwsRemoteValidation(option, section_id, textarea) {
}
const requestId =
(textarea.__forkopNfqwsRemoteValidationRequestId || 0) + 1;
textarea.__forkopNfqwsRemoteValidationRequestId = requestId;
(textarea.__tachyonNfqwsRemoteValidationRequestId || 0) + 1;
textarea.__tachyonNfqwsRemoteValidationRequestId = requestId;
validateNfqwsStrategyRemotely(value).then(() => {
if (textarea.__forkopNfqwsRemoteValidationRequestId !== requestId) {
if (textarea.__tachyonNfqwsRemoteValidationRequestId !== requestId) {
return;
}
@@ -4538,12 +4538,12 @@ function attachNfqwsRemoteValidation(option, section_id, textarea) {
};
const scheduleValidation = (delay = NFQWS_REMOTE_VALIDATION_DEBOUNCE_MS) => {
if (textarea.__forkopNfqwsRemoteValidationTimer) {
window.clearTimeout(textarea.__forkopNfqwsRemoteValidationTimer);
if (textarea.__tachyonNfqwsRemoteValidationTimer) {
window.clearTimeout(textarea.__tachyonNfqwsRemoteValidationTimer);
}
textarea.__forkopNfqwsRemoteValidationTimer = window.setTimeout(() => {
textarea.__forkopNfqwsRemoteValidationTimer = null;
textarea.__tachyonNfqwsRemoteValidationTimer = window.setTimeout(() => {
textarea.__tachyonNfqwsRemoteValidationTimer = null;
runValidation();
}, delay);
};
@@ -4556,13 +4556,13 @@ function attachNfqwsRemoteValidation(option, section_id, textarea) {
}
function attachNfqws2RemoteValidation(option, section_id, textarea) {
if (!textarea || textarea.__forkopNfqws2RemoteValidationAttached) {
if (!textarea || textarea.__tachyonNfqws2RemoteValidationAttached) {
return;
}
textarea.__forkopNfqws2RemoteValidationAttached = true;
textarea.__forkopNfqws2RemoteValidationRequestId = 0;
textarea.__forkopNfqws2RemoteValidationTimer = null;
textarea.__tachyonNfqws2RemoteValidationAttached = true;
textarea.__tachyonNfqws2RemoteValidationRequestId = 0;
textarea.__tachyonNfqws2RemoteValidationTimer = null;
const runValidation = () => {
const value = textarea.value;
@@ -4573,11 +4573,11 @@ function attachNfqws2RemoteValidation(option, section_id, textarea) {
}
const requestId =
(textarea.__forkopNfqws2RemoteValidationRequestId || 0) + 1;
textarea.__forkopNfqws2RemoteValidationRequestId = requestId;
(textarea.__tachyonNfqws2RemoteValidationRequestId || 0) + 1;
textarea.__tachyonNfqws2RemoteValidationRequestId = requestId;
validateNfqws2StrategyRemotely(value).then(() => {
if (textarea.__forkopNfqws2RemoteValidationRequestId !== requestId) {
if (textarea.__tachyonNfqws2RemoteValidationRequestId !== requestId) {
return;
}
@@ -4586,12 +4586,12 @@ function attachNfqws2RemoteValidation(option, section_id, textarea) {
};
const scheduleValidation = (delay = NFQWS_REMOTE_VALIDATION_DEBOUNCE_MS) => {
if (textarea.__forkopNfqws2RemoteValidationTimer) {
window.clearTimeout(textarea.__forkopNfqws2RemoteValidationTimer);
if (textarea.__tachyonNfqws2RemoteValidationTimer) {
window.clearTimeout(textarea.__tachyonNfqws2RemoteValidationTimer);
}
textarea.__forkopNfqws2RemoteValidationTimer = window.setTimeout(() => {
textarea.__forkopNfqws2RemoteValidationTimer = null;
textarea.__tachyonNfqws2RemoteValidationTimer = window.setTimeout(() => {
textarea.__tachyonNfqws2RemoteValidationTimer = null;
runValidation();
}, delay);
};
@@ -5026,7 +5026,7 @@ function validateNfqwsStrategyRemotely(value) {
function getNfqwsForbiddenTokenInfo(token, index) {
const configFileMessage = _(
"External nfqws config files bypass Forkop queue management and explicit validation.",
"External nfqws config files bypass Tachyon queue management and explicit validation.",
);
const hostSelectionMessage = _(
"Resource selection by hostname inside nfqws is not supported here; sing-box selects resources before NFQUEUE.",
@@ -5035,16 +5035,16 @@ function getNfqwsForbiddenTokenInfo(token, index) {
"Resource selection by IP or CIDR inside nfqws is not supported here; sing-box selects resources before NFQUEUE.",
);
const placeholderMessage = _(
"Zapret hostlist templates are not supported here because Forkop does not expand them for per-rule NFQWS strategies.",
"Zapret hostlist templates are not supported here because Tachyon does not expand them for per-rule NFQWS strategies.",
);
const queueMessage = _(
"The NFQUEUE number is assigned by Forkop for each rule and must not be overridden here.",
"The NFQUEUE number is assigned by Tachyon for each rule and must not be overridden here.",
);
const fwmarkMessage = _(
"The desync fwmark is managed by Forkop for loop prevention and must not be overridden here.",
"The desync fwmark is managed by Tachyon for loop prevention and must not be overridden here.",
);
const daemonMessage = _(
"Forkop manages the nfqws process lifecycle itself, so daemon mode is not allowed here.",
"Tachyon manages the nfqws process lifecycle itself, so daemon mode is not allowed here.",
);
const dryRunMessage = _(
"This field must start a working nfqws strategy; --dry-run exits immediately and is not allowed here.",
@@ -5471,7 +5471,7 @@ function validateNfqws2StrategyRemotely(value) {
function getNfqws2ForbiddenTokenInfo(token, index, nextToken) {
const configFileMessage = _(
"External nfqws2 config files bypass Forkop queue management and explicit validation.",
"External nfqws2 config files bypass Tachyon queue management and explicit validation.",
);
const hostSelectionMessage = _(
"Resource selection by hostname inside nfqws2 is not supported here; sing-box selects resources before NFQUEUE.",
@@ -5480,22 +5480,22 @@ function getNfqws2ForbiddenTokenInfo(token, index, nextToken) {
"Resource selection by IP or CIDR inside nfqws2 is not supported here; sing-box selects resources before NFQUEUE.",
);
const placeholderMessage = _(
"Zapret2 hostlist templates are not supported here because Forkop does not expand them for per-rule NFQWS2 strategies.",
"Zapret2 hostlist templates are not supported here because Tachyon does not expand them for per-rule NFQWS2 strategies.",
);
const queueMessage = _(
"The NFQUEUE number is assigned by Forkop for each rule and must not be overridden here.",
"The NFQUEUE number is assigned by Tachyon for each rule and must not be overridden here.",
);
const fwmarkMessage = _(
"The desync fwmark is managed by Forkop for loop prevention and must not be overridden here.",
"The desync fwmark is managed by Tachyon for loop prevention and must not be overridden here.",
);
const fuzzMessage = _(
"Fuzzing is not supported here because Forkop needs deterministic runtime validation.",
"Fuzzing is not supported here because Tachyon needs deterministic runtime validation.",
);
const interceptMessage = _(
"Disabling interception is incompatible with action=zapret2 because Forkop sends matched traffic through NFQUEUE.",
"Disabling interception is incompatible with action=zapret2 because Tachyon sends matched traffic through NFQUEUE.",
);
const daemonMessage = _(
"Forkop manages the nfqws2 process lifecycle itself, so daemon mode is not allowed here.",
"Tachyon manages the nfqws2 process lifecycle itself, so daemon mode is not allowed here.",
);
const dryRunMessage = _(
"This field must start a working nfqws2 strategy; --dry-run exits immediately and is not allowed here.",
@@ -5891,16 +5891,16 @@ function byedpiTokenLooksLikeOption(token) {
function getByedpiControlledTokenInfo(token) {
const listenMessage = _(
"ByeDPI listen address and port are assigned by Forkop and must not be set in the strategy.",
"ByeDPI listen address and port are assigned by Tachyon and must not be set in the strategy.",
);
const transparentMessage = _(
"Transparent proxy mode is incompatible with action=byedpi because Forkop connects to ciadpi through SOCKS.",
"Transparent proxy mode is incompatible with action=byedpi because Tachyon connects to ciadpi through SOCKS.",
);
const daemonMessage = _(
"Forkop manages the ciadpi process lifecycle itself, so daemon mode is not allowed here.",
"Tachyon manages the ciadpi process lifecycle itself, so daemon mode is not allowed here.",
);
const pidfileMessage = _(
"Forkop manages ciadpi pid files itself, so pidfile options are not allowed here.",
"Tachyon manages ciadpi pid files itself, so pidfile options are not allowed here.",
);
const exitMessage = _(
"This field must start a working ciadpi strategy; help/version options exit immediately and are not allowed here.",
@@ -6630,7 +6630,7 @@ function createSectionContent(section) {
form.ListValue,
"action",
_("Action"),
_("What Forkop should do when this section matches"),
_("What Tachyon should do when this section matches"),
);
populateActionOptionValues(o);
o.default = "connection";
@@ -6,9 +6,9 @@
"require rpc";
"require uci";
"require ui";
"require view.forkop.main as main";
"require view.tachyon.main as main";
const UCI_PACKAGE = main.FORKOP_UCI_PACKAGE;
const UCI_PACKAGE = main.TACHYON_UCI_PACKAGE;
const ROUTING_SECTION_ACTIONS = [
"connection",
"proxy",
@@ -385,7 +385,7 @@ function qrSvgDataUri(text) {
return `data:image/svg+xml,${encodeURIComponent(svg)}`;
}
const SERVER_STYLE_ID = "forkop-server-styles";
const SERVER_STYLE_ID = "tachyon-server-styles";
const DEFAULT_SERVER_PROTOCOL = "tailscale";
const FALLBACK_SERVER_PROTOCOL = "vless";
const STREAM_PROTOCOLS = ["vless", "vmess", "trojan"];
@@ -938,15 +938,15 @@ function serverSafeName(sectionId) {
}
function defaultTlsCertificatePath(sectionId) {
return `/etc/forkop/server-certs/${serverSafeName(sectionId)}.crt`;
return `/etc/tachyon/server-certs/${serverSafeName(sectionId)}.crt`;
}
function defaultTlsKeyPath(sectionId) {
return `/etc/forkop/server-certs/${serverSafeName(sectionId)}.key`;
return `/etc/tachyon/server-certs/${serverSafeName(sectionId)}.key`;
}
function defaultTailscaleHostname(sectionId) {
return `forkop-${serverSafeName(sectionId)}`;
return `tachyon-${serverSafeName(sectionId)}`;
}
function setDefault(sectionId, key, value) {
@@ -1356,7 +1356,7 @@ function loadGeneratedTlsCertificatePin(sectionId) {
}
return fs
.exec("/usr/bin/forkop", ["get_tls_certificate_sha256", sectionId])
.exec("/usr/bin/tachyon", ["get_tls_certificate_sha256", sectionId])
.then((response) => {
if ((response.code ?? 0) !== 0 || !response.stdout) {
throw new Error(response.stderr || response.stdout || "");
@@ -2262,7 +2262,7 @@ function hasRealityKeypair(sectionId) {
function generateRealityKeypair(sectionId) {
return fs
.exec("/usr/bin/forkop", ["generate_reality_keypair"])
.exec("/usr/bin/tachyon", ["generate_reality_keypair"])
.then((response) => {
if ((response.code ?? 0) !== 0 || !response.stdout) {
throw new Error(response.stderr || response.stdout || "");
@@ -3,9 +3,9 @@
"require uci";
"require baseclass";
"require tools.widgets as widgets";
"require view.forkop.main as main";
"require view.tachyon.main as main";
const UCI_PACKAGE = main.FORKOP_UCI_PACKAGE;
const UCI_PACKAGE = main.TACHYON_UCI_PACKAGE;
function isSingBoxDuration(value) {
return /^([0-9]+(?:\.[0-9]+)?(?:ns|us|ms|s|m|h|d))+$/.test(value);
@@ -124,8 +124,8 @@ function configureDnsList(option, choices, defaultValue) {
}
function configureDnsFailoverVisibility(option, dnsOption, bootstrapOption) {
option.depends("dns_server", "__forkop_multiple_dns__");
option.depends("bootstrap_dns_server", "__forkop_multiple_dns__");
option.depends("dns_server", "__tachyon_multiple_dns__");
option.depends("bootstrap_dns_server", "__tachyon_multiple_dns__");
option.retain = true;
option.checkDepends = function (section_id) {
return (
@@ -395,7 +395,7 @@ function createSettingsContent(section, capabilities) {
form.Value,
"badwan_reload_delay",
_("Interface Monitoring Delay"),
_("Delay in milliseconds before reloading Forkop after interface UP"),
_("Delay in milliseconds before reloading Tachyon after interface UP"),
);
o.depends("enable_badwan_interface_monitoring", "1");
o.default = "2000";
@@ -599,7 +599,7 @@ function createSettingsContent(section, capabilities) {
form.Flag,
"dont_touch_dhcp",
_("Dont Touch My DHCP!"),
_("Forkop will not modify your DHCP configuration"),
_("Tachyon will not modify your DHCP configuration"),
);
o.default = "0";
o.rmempty = false;
@@ -4,30 +4,30 @@
"require baseclass";
"require uci";
"require ui";
"require view.forkop.main as main";
"require view.tachyon.main as main";
// Global settings
"require view.forkop.settings as settings";
"require view.tachyon.settings as settings";
// Sections
"require view.forkop.section as section";
"require view.tachyon.section as section";
// Server
"require view.forkop.server as server";
"require view.tachyon.server as server";
// Dashboard
"require view.forkop.dashboard as dashboard";
"require view.tachyon.dashboard as dashboard";
// Monitoring
"require view.forkop.monitoring as monitoring";
"require view.tachyon.monitoring as monitoring";
// Diagnostic
"require view.forkop.diagnostic as diagnostic";
"require view.tachyon.diagnostic as diagnostic";
// Updates
"require view.forkop.updates as updates";
"require view.tachyon.updates as updates";
const UCI_PACKAGE = main.FORKOP_UCI_PACKAGE;
const UCI_PACKAGE = main.TACHYON_UCI_PACKAGE;
const CBI_PREFIX = UCI_PACKAGE;
function renderSectionAdd(sectionRef, extra_class) {
@@ -121,7 +121,7 @@ const EntryPoint = {
if (typeof window !== "undefined") {
window.dispatchEvent(
new CustomEvent(main.FORKOP_ACTION_PROVIDERS_AVAILABILITY_EVENT, {
new CustomEvent(main.TACHYON_ACTION_PROVIDERS_AVAILABILITY_EVENT, {
detail: {
zapretInstalled: uiCapabilities.zapretInstalled,
zapret2Installed: uiCapabilities.zapret2Installed,
@@ -209,9 +209,9 @@ const EntryPoint = {
failed: false,
data: {
singbox: Number(data.service.sing_box?.running) || 0,
forkopRunning: Number(data.service.forkop?.running) || 0,
forkopEnabled: Number(data.service.forkop?.enabled) || 0,
forkopStatus: data.service.forkop?.status || "",
tachyonRunning: Number(data.service.tachyon?.running) || 0,
tachyonEnabled: Number(data.service.tachyon?.enabled) || 0,
tachyonStatus: data.service.tachyon?.status || "",
},
},
});
@@ -222,11 +222,11 @@ const EntryPoint = {
const loadFallbackUiCapabilities = function () {
return Promise.allSettled([
main.ForkopShellMethods.getServerCapabilities(),
main.ForkopShellMethods.checkZapretRuntime(),
main.ForkopShellMethods.checkZapret2Runtime(),
main.ForkopShellMethods.checkByedpiRuntime(),
main.ForkopShellMethods.checkInboundsConfig(),
main.TachyonShellMethods.getServerCapabilities(),
main.TachyonShellMethods.checkZapretRuntime(),
main.TachyonShellMethods.checkZapret2Runtime(),
main.TachyonShellMethods.checkByedpiRuntime(),
main.TachyonShellMethods.checkInboundsConfig(),
]).then(
([
serverCapabilitiesResult,
@@ -308,7 +308,7 @@ const EntryPoint = {
return uiCapabilitiesPromise;
}
uiCapabilitiesPromise = main.ForkopShellMethods.getUiCapabilities()
uiCapabilitiesPromise = main.TachyonShellMethods.getUiCapabilities()
.then((response) => {
if (!response?.success) {
throw new Error("UI capabilities request failed");
@@ -317,8 +317,8 @@ const EntryPoint = {
return updateUiCapabilities(response.data);
})
.catch((error) => {
console.warn("Failed to load Forkop UI capabilities", error);
return main.ForkopShellMethods.getUiState()
console.warn("Failed to load Tachyon UI capabilities", error);
return main.TachyonShellMethods.getUiState()
.then((response) => {
if (!response?.success) {
throw new Error("UI state request failed");
@@ -327,7 +327,7 @@ const EntryPoint = {
return applyUiState(response.data);
})
.catch((fallbackError) => {
console.warn("Failed to load Forkop UI state", fallbackError);
console.warn("Failed to load Tachyon UI state", fallbackError);
return loadFallbackUiCapabilities();
});
})
@@ -337,16 +337,16 @@ const EntryPoint = {
return uiCapabilitiesPromise;
};
const forkopMap = new form.Map(
const tachyonMap = new form.Map(
UCI_PACKAGE,
_("Forkop Settings"),
_("Configuration for Forkop service"),
_("Tachyon Settings"),
_("Configuration for Tachyon service"),
);
forkopMap.tabbed = true;
const originalHandleSaveApply = forkopMap.handleSaveApply;
forkopMap.handleSaveApply = function (ev, mode) {
tachyonMap.tabbed = true;
const originalHandleSaveApply = tachyonMap.handleSaveApply;
tachyonMap.handleSaveApply = function (ev, mode) {
const refreshUiState = function () {
main.ForkopShellMethods.getUiState()
main.TachyonShellMethods.getUiState()
.then((response) => {
if (
response?.success &&
@@ -365,7 +365,7 @@ const EntryPoint = {
...servicesInfoWidget,
data: {
...servicesInfoWidget.data,
forkopStatus: "reloading",
tachyonStatus: "reloading",
},
},
});
@@ -384,7 +384,7 @@ const EntryPoint = {
});
};
const rulesSection = forkopMap.section(
const rulesSection = tachyonMap.section(
form.GridSection,
"section",
_("Sections"),
@@ -401,7 +401,7 @@ const EntryPoint = {
});
section.createSectionContent(rulesSection);
const serverSection = forkopMap.section(
const serverSection = tachyonMap.section(
form.GridSection,
"server",
_("Servers"),
@@ -419,7 +419,7 @@ const EntryPoint = {
});
server.createServerContent(serverSection, uiCapabilities);
const settingsSection = forkopMap.section(
const settingsSection = tachyonMap.section(
form.TypedSection,
"settings",
_("Settings"),
@@ -431,7 +431,7 @@ const EntryPoint = {
};
settings.createSettingsContent(settingsSection, uiCapabilities);
const diagnosticSection = forkopMap.section(
const diagnosticSection = tachyonMap.section(
form.TypedSection,
"diagnostic",
_("Diagnostics"),
@@ -443,7 +443,7 @@ const EntryPoint = {
};
diagnostic.createDiagnosticContent(diagnosticSection);
const dashboardSection = forkopMap.section(
const dashboardSection = tachyonMap.section(
form.TypedSection,
"dashboard",
_("Dashboard"),
@@ -455,7 +455,7 @@ const EntryPoint = {
};
dashboard.createDashboardContent(dashboardSection);
const monitoringSection = forkopMap.section(
const monitoringSection = tachyonMap.section(
form.TypedSection,
"monitoring",
_("Monitoring"),
@@ -467,7 +467,7 @@ const EntryPoint = {
};
monitoring.createMonitoringContent(monitoringSection);
const updatesSection = forkopMap.section(
const updatesSection = tachyonMap.section(
form.TypedSection,
"updates",
_("Components"),
@@ -481,7 +481,7 @@ const EntryPoint = {
await loadUiCapabilities().catch(() => null);
const rendered = await forkopMap.render();
const rendered = await tachyonMap.render();
main.coreService({
waitForLogWatcherStart: loadUiCapabilities,
logWatcherStartDelayMs: 5000,
@@ -4,7 +4,7 @@
"require ui";
"require uci";
"require fs";
"require view.forkop.main as main";
"require view.tachyon.main as main";
function createUpdatesContent(section) {
const o = section.option(form.DummyValue, "_mount_node");
+112 -112
View File
@@ -1,15 +1,15 @@
# 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.
# RU translations for TACHYON package.
# Copyright (C) 2026 THE TACHYON COPYRIGHT HOLDER
# This file is distributed under the same license as the TACHYON package.
# Dushnilin, 2026.
#
msgid ""
msgstr ""
"Project-Id-Version: FORKOP\n"
"Project-Id-Version: TACHYON\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-13 03:22+0300\n"
"PO-Revision-Date: 2026-07-13 03:22+0300\n"
"Last-Translator: ushan0v\n"
"POT-Creation-Date: 2026-07-13 13:18+0300\n"
"PO-Revision-Date: 2026-07-13 13:18+0300\n"
"Last-Translator: Dushnilin\n"
"Language-Team: none\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
@@ -191,8 +191,8 @@ msgstr "Встроенные наборы правил"
msgid "By flag emoji from name"
msgstr "По эмодзи флага из названия"
msgid "ByeDPI listen address and port are assigned by Forkop and must not be set in the strategy."
msgstr "Адрес и порт прослушивания ByeDPI назначаются Forkop и не должны задаваться в стратегии."
msgid "ByeDPI listen address and port are assigned by Tachyon and must not be set in the strategy."
msgstr "Адрес и порт прослушивания ByeDPI назначаются Tachyon и не должны задаваться в стратегии."
msgid "ByeDPI option does not accept a compact value: %s"
msgstr "Опция ByeDPI не принимает компактное значение: %s"
@@ -323,8 +323,8 @@ msgstr "Условия"
msgid "Config File Path"
msgstr "Путь к файлу конфигурации"
msgid "Configuration for Forkop service"
msgstr "Настройки сервиса Forkop"
msgid "Configuration for Tachyon service"
msgstr "Настройки сервиса Tachyon"
msgid "Configure User-Agent in the item settings"
msgstr "Настройте User-Agent в настройках элемента"
@@ -374,8 +374,8 @@ msgstr "Дашборд сейчас недоступен"
msgid "Default address for checking server availability and latency. URLTest uses its own address."
msgstr "Адрес по умолчанию для проверки доступности и задержки серверов. Для URLTest используется собственный адрес."
msgid "Delay in milliseconds before reloading Forkop after interface UP"
msgstr "Задержка в миллисекундах перед перезапуском Forkop после поднятия интерфейса"
msgid "Delay in milliseconds before reloading Tachyon after interface UP"
msgstr "Задержка в миллисекундах перед перезапуском Tachyon после поднятия интерфейса"
msgid "Delay value cannot be empty"
msgstr "Значение задержки не может быть пустым"
@@ -419,8 +419,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 Forkop sends matched traffic through NFQUEUE."
msgstr "Отключение перехвата несовместимо с action=zapret2, потому что Forkop отправляет совпавший трафик через NFQUEUE."
msgid "Disabling interception is incompatible with action=zapret2 because Tachyon sends matched traffic through NFQUEUE."
msgstr "Отключение перехвата несовместимо с action=zapret2, потому что Tachyon отправляет совпавший трафик через NFQUEUE."
msgid "Display name"
msgstr "Отображаемое имя"
@@ -656,11 +656,11 @@ msgstr "Истекает"
msgid "Expose this section as a local HTTP+SOCKS proxy"
msgstr "Открыть эту секцию как локальный HTTP+SOCKS прокси"
msgid "External nfqws config files bypass Forkop queue management and explicit validation."
msgstr "Внешние конфиги nfqws обходят управление очередями и явную валидацию Forkop."
msgid "External nfqws config files bypass Tachyon queue management and explicit validation."
msgstr "Внешние конфиги nfqws обходят управление очередями и явную валидацию Tachyon."
msgid "External nfqws2 config files bypass Forkop queue management and explicit validation."
msgstr "Внешние конфиги nfqws2 обходят управление очередями и явную валидацию Forkop."
msgid "External nfqws2 config files bypass Tachyon queue management and explicit validation."
msgstr "Внешние конфиги nfqws2 обходят управление очередями и явную валидацию Tachyon."
msgid "Failed to close connection"
msgstr "Не удалось закрыть подключение"
@@ -707,63 +707,6 @@ 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 фронтинга"
@@ -773,8 +716,8 @@ msgstr "Порт фронтинга"
msgid "Fronting proxy protocol"
msgstr "Proxy protocol для фронтинга"
msgid "Fuzzing is not supported here because Forkop needs deterministic runtime validation."
msgstr "Fuzzing здесь не поддерживается, потому что Forkop нужна предсказуемая runtime-валидация."
msgid "Fuzzing is not supported here because Tachyon needs deterministic runtime validation."
msgstr "Fuzzing здесь не поддерживается, потому что Tachyon нужна предсказуемая runtime-валидация."
msgid "Generate HWID from router hardware information for this subscription"
msgstr "Генерировать HWID из аппаратной информации роутера для этой подписки"
@@ -1541,8 +1484,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 Forkop"
msgstr "Перезапустить Forkop"
msgid "Restart Tachyon"
msgstr "Перезапустить Tachyon"
msgid "Resume updates"
msgstr "Продолжить обновление"
@@ -1787,8 +1730,8 @@ msgstr "Укажите разные файлы"
msgid "Standalone ByeDPI autostart is enabled"
msgstr "Автозапуск отдельной службы ByeDPI включен"
msgid "Standalone ByeDPI is active together with Forkop ByeDPI rules"
msgstr "Отдельная служба ByeDPI активна вместе с правилами ByeDPI в Forkop"
msgid "Standalone ByeDPI is active together with Tachyon ByeDPI rules"
msgstr "Отдельная служба ByeDPI активна вместе с правилами ByeDPI в Tachyon"
msgid "Standalone ByeDPI service is active"
msgstr "Отдельная служба ByeDPI активна"
@@ -1796,8 +1739,8 @@ msgstr "Отдельная служба ByeDPI активна"
msgid "Standalone ByeDPI service is inactive"
msgstr "Отдельная служба ByeDPI неактивна"
msgid "Standalone Zapret is active together with Forkop Zapret rules"
msgstr "Отдельная служба Zapret активна вместе с правилами Zapret в Forkop"
msgid "Standalone Zapret is active together with Tachyon Zapret rules"
msgstr "Отдельная служба Zapret активна вместе с правилами Zapret в Tachyon"
msgid "Standalone Zapret service is active"
msgstr "Отдельная служба Zapret активна"
@@ -1808,8 +1751,8 @@ msgstr "Отдельная служба Zapret неактивна"
msgid "Standalone Zapret2 autostart is enabled"
msgstr "Автозапуск отдельной службы Zapret2 включён"
msgid "Standalone Zapret2 is active together with Forkop Zapret2 rules"
msgstr "Отдельная служба Zapret2 активна вместе с правилами Zapret2 в Forkop"
msgid "Standalone Zapret2 is active together with Tachyon Zapret2 rules"
msgstr "Отдельная служба Zapret2 активна вместе с правилами Zapret2 в Tachyon"
msgid "Standalone Zapret2 service is active"
msgstr "Отдельная служба Zapret2 активна"
@@ -1817,14 +1760,14 @@ msgstr "Отдельная служба Zapret2 активна"
msgid "Standalone Zapret2 service is inactive"
msgstr "Отдельная служба Zapret2 неактивна"
msgid "Start Forkop"
msgstr "Запустить Forkop"
msgid "Start Tachyon"
msgstr "Запустить Tachyon"
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 Forkop"
msgstr "Остановить Forkop"
msgid "Stop Tachyon"
msgstr "Остановить Tachyon"
msgid "Subnets from the list will be extracted and added to nftables"
msgstr "IP-адреса и подсети из списка будут извлечены и добавлены в nftables"
@@ -1862,6 +1805,63 @@ msgstr "Системная информация"
msgid "Table exist"
msgstr "Таблица существует"
msgid "Tachyon Error"
msgstr "Ошибка Tachyon"
msgid "Tachyon manages ciadpi pid files itself, so pidfile options are not allowed here."
msgstr "Tachyon сам управляет pid-файлами ciadpi, поэтому параметры pidfile здесь запрещены."
msgid "Tachyon manages the ciadpi process lifecycle itself, so daemon mode is not allowed here."
msgstr "Tachyon сам управляет жизненным циклом ciadpi, поэтому режим daemon здесь запрещён."
msgid "Tachyon manages the nfqws process lifecycle itself, so daemon mode is not allowed here."
msgstr "Tachyon сам управляет жизненным циклом nfqws, поэтому режим daemon здесь запрещён."
msgid "Tachyon manages the nfqws2 process lifecycle itself, so daemon mode is not allowed here."
msgstr "Tachyon сам управляет жизненным циклом nfqws2, поэтому режим daemon здесь запрещён."
msgid "Tachyon service is stopped. Start the service to display connections."
msgstr "Служба Tachyon остановлена. Запустите сервис для отображения подключений."
msgid "Tachyon service is stopped. Start the service to display the dashboard."
msgstr "Служба Tachyon остановлена. Запустите сервис для отображения дашборда."
msgid "Tachyon Settings"
msgstr "Настройки Tachyon"
msgid "Tachyon will not modify your DHCP configuration"
msgstr "Tachyon не будет изменять вашу конфигурацию DHCP"
msgid "Tachyon-managed ciadpi runtime has restarted"
msgstr "ciadpi Tachyon перезапускался"
msgid "Tachyon-managed ciadpi runtime is not ready"
msgstr "ciadpi Tachyon не готов"
msgid "Tachyon-managed ciadpi runtime is not running"
msgstr "ciadpi Tachyon не запущен"
msgid "Tachyon-managed ciadpi runtime is ready"
msgstr "ciadpi Tachyon готов"
msgid "Tachyon-managed nfqws runtime is not ready"
msgstr "NFQWS Tachyon не готов"
msgid "Tachyon-managed nfqws runtime is not running"
msgstr "NFQWS Tachyon не запущен"
msgid "Tachyon-managed nfqws runtime is ready"
msgstr "NFQWS Tachyon готов"
msgid "Tachyon-managed nfqws2 runtime is not ready"
msgstr "NFQWS2 Tachyon не готов"
msgid "Tachyon-managed nfqws2 runtime is not running"
msgstr "NFQWS2 Tachyon не запущен"
msgid "Tachyon-managed nfqws2 runtime is ready"
msgstr "NFQWS2 Tachyon готов"
msgid "Tailscale auth key"
msgstr "Ключ авторизации Tailscale"
@@ -1898,11 +1898,11 @@ msgstr "Тестировать серверы, имена которых сов
msgid "Testing URL"
msgstr "URL проверки"
msgid "The desync fwmark is managed by Forkop for loop prevention and must not be overridden here."
msgstr "Desync fwmark управляется Forkop для защиты от циклов и не должен переопределяться здесь."
msgid "The desync fwmark is managed by Tachyon for loop prevention and must not be overridden here."
msgstr "Desync fwmark управляется Tachyon для защиты от циклов и не должен переопределяться здесь."
msgid "The NFQUEUE number is assigned by Forkop for each rule and must not be overridden here."
msgstr "Номер NFQUEUE назначается Forkop для каждого правила и не должен переопределяться здесь."
msgid "The NFQUEUE number is assigned by Tachyon for each rule and must not be overridden here."
msgstr "Номер NFQUEUE назначается Tachyon для каждого правила и не должен переопределяться здесь."
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:."
@@ -1967,8 +1967,8 @@ msgstr "Трафик этого уровня пойдёт напрямую."
msgid "Traffic Total"
msgstr "Всего трафика"
msgid "Transparent proxy mode is incompatible with action=byedpi because Forkop connects to ciadpi through SOCKS."
msgstr "Режим прозрачного прокси несовместим с action=byedpi, потому что Forkop подключается к ciadpi через SOCKS."
msgid "Transparent proxy mode is incompatible with action=byedpi because Tachyon connects to ciadpi through SOCKS."
msgstr "Режим прозрачного прокси несовместим с action=byedpi, потому что Tachyon подключается к ciadpi через SOCKS."
msgid "Transport"
msgstr "Транспорт"
@@ -2015,18 +2015,18 @@ msgstr "Таймаут недоступности"
msgid "Unexpected ByeDPI strategy argument: %s"
msgstr "Неожиданный аргумент стратегии ByeDPI: %s"
msgid "Unexpected Forkop-managed ciadpi runtime is running"
msgstr "Обнаружен лишний ciadpi Forkop"
msgid "Unexpected Forkop-managed nfqws runtime is running"
msgstr "Обнаружен лишний NFQWS Forkop"
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 "Неожиданный отдельный токен. Используйте явные флаги вида --имя или --имя=значение."
msgid "Unexpected Tachyon-managed ciadpi runtime is running"
msgstr "Обнаружен лишний ciadpi Tachyon"
msgid "Unexpected Tachyon-managed nfqws runtime is running"
msgstr "Обнаружен лишний NFQWS Tachyon"
msgid "Unexpected Tachyon-managed nfqws2 runtime is running"
msgstr "Обнаружен лишний NFQWS2 Tachyon"
msgid "unique UCI identifier"
msgstr "уникальный идентификатор UCI"
@@ -2192,8 +2192,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 Forkop should do when this section matches"
msgstr "Что Forkop должен делать при совпадении этой секции"
msgid "What Tachyon should do when this section matches"
msgstr "Что Tachyon должен делать при совпадении этой секции"
msgid "When switching to another level, test every server and select the fastest instead of the first working one."
msgstr "При переходе на другой уровень тестировать все сервера и выбирать самый быстрый, а не первый работающий."
@@ -2210,8 +2210,8 @@ msgstr "Да"
msgid "You can select Output Network Interface, by default autodetect"
msgstr "Вы можете выбрать выходной сетевой интерфейс, по умолчанию он определяется автоматически."
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 hostlist templates are not supported here because Tachyon does not expand them for per-rule NFQWS strategies."
msgstr "Шаблоны hostlist zapret здесь не поддерживаются, потому что Tachyon не разворачивает их для per-rule стратегий NFQWS."
msgid "Zapret package is installed"
msgstr "Пакет Zapret установлен"
@@ -2231,8 +2231,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 Forkop does not expand them for per-rule NFQWS2 strategies."
msgstr "Шаблоны hostlist Zapret2 здесь не поддерживаются, потому что Forkop не разворачивает их для per-rule стратегий NFQWS2."
msgid "Zapret2 hostlist templates are not supported here because Tachyon does not expand them for per-rule NFQWS2 strategies."
msgstr "Шаблоны hostlist Zapret2 здесь не поддерживаются, потому что Tachyon не разворачивает их для per-rule стратегий NFQWS2."
msgid "Zapret2 package is installed"
msgstr "Пакет Zapret2 установлен"
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
#!/bin/sh
/usr/bin/forkop luci_postinst >/dev/null 2>&1 || true
/usr/bin/tachyon luci_postinst >/dev/null 2>&1 || true
exit 0

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