Compare commits

..

3 Commits

Author SHA1 Message Date
62a2558174 Change build scripts 2025-10-25 08:39:53 +03:00
45b6fe4d5a Format build scripts with shfmt 2025-10-25 03:24:28 +03:00
6b8b2d0b1b Make all build scripts executable 2025-10-24 13:36:52 +03:00
180 changed files with 1425 additions and 1456 deletions

View File

@@ -9,6 +9,10 @@ end_of_line = crlf
trim_trailing_whitespace = true
insert_final_newline = true
[*.sh]
end_of_line = lf
indent_size = 2
[*.{yml,yaml}]
indent_style = space
indent_size = 2
@@ -157,14 +161,14 @@ dotnet_naming_rule.non_field_members_should_be_pascal.symbols = non_field_member
dotnet_naming_rule.non_field_members_should_be_pascal.style = pascal
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = *
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = *
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = *
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_style.pascal.required_prefix =
dotnet_naming_style.pascal.required_suffix =
dotnet_naming_style.pascal.word_separator =
dotnet_naming_style.pascal.capitalization = pascal_case
dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_style.pascal.required_prefix =
dotnet_naming_style.pascal.required_suffix =
dotnet_naming_style.pascal.word_separator =
dotnet_naming_style.pascal.capitalization = pascal_case

View File

@@ -3,18 +3,6 @@ description: 在提出问题前请先自行排除服务器端问题和升级到
title: "[Bug]: "
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
### 报告 Bug 前请务必确认以下事项:
> ** **
> **✓ 已自行排除服务器端问题。**
> **✓ 已升级到最新客户端版本。**
> **✓ 已通过搜索确认没有人提出过相同问题。**
> **✓ 已确认自己的电脑系统环境是受支持的。**
---
- type: input
id: "os-version"
attributes:
@@ -22,7 +10,6 @@ body:
description: "操作系统和版本"
validations:
required: true
- type: input
id: "expectation"
attributes:
@@ -30,7 +17,6 @@ body:
description: "描述你认为应该发生什么"
validations:
required: true
- type: textarea
id: "describe-the-bug"
attributes:
@@ -38,34 +24,22 @@ body:
description: "描述实际发生了什么"
validations:
required: true
- type: textarea
id: "reproduction-method"
attributes:
label: "复现方法"
description: "在BUG出现前执行了哪些操作"
placeholder: "标序号"
placeholder: 标序号
validations:
required: true
- type: textarea
id: "gui-log"
id: "log"
attributes:
label: "软件日志"
label: "日志信息"
description: "位置在软件当前目录下的guiLogs"
placeholder: "在日志开始和结束位置粘贴冒号后的内容到这:"
placeholder: 在日志开始和结束位置粘贴冒号后的内容```
validations:
required: true
- type: textarea
id: "core-log"
attributes:
label: "内核日志"
description: "位置在软件主界面的信息框内"
placeholder: "在信息框内鼠标右键复制全部信息粘贴在这:"
validations:
required: true
- type: textarea
id: "more"
attributes:
@@ -73,7 +47,6 @@ body:
description: "可选"
validations:
required: false
- type: checkboxes
id: "latest-version"
attributes:
@@ -82,7 +55,6 @@ body:
options:
- label:
required: true
- type: checkboxes
id: "issues"
attributes:
@@ -91,12 +63,3 @@ body:
options:
- label:
required: true
- type: checkboxes
id: "system-version"
attributes:
label: "我确认系统版本是受支持的"
description: "否则请切换后尝试"
options:
- label:
required: true

View File

@@ -1,9 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Discussions / 讨论区
url: https://github.com/2dust/v2rayN/discussions
about: 使用问题或需要帮助请前往 Discussions。
- name: Wiki / 使用说明
url: https://github.com/2dust/v2rayN/wiki
about: 查看常见问题和使用文档。

10
package-appimage.sh Normal file → Executable file
View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -euo pipefail
# Install deps
@@ -17,10 +17,10 @@ cp -rf "$OutputPath64"/* "$APPDIR_X64/usr/lib/v2rayN" || true
[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/usr/share/pixmaps/v2rayN.png" || true
[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/v2rayN.png" || true
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' > "$APPDIR_X64/AppRun"
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' >"$APPDIR_X64/AppRun"
chmod +x "$APPDIR_X64/AppRun"
ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_X64/usr/bin/v2rayN"
cat > "$APPDIR_X64/v2rayN.desktop" <<EOF
cat >"$APPDIR_X64/v2rayN.desktop" <<EOF
[Desktop Entry]
Name=v2rayN
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
@@ -43,10 +43,10 @@ cp -rf "$OutputPathArm64"/* "$APPDIR_ARM64/usr/lib/v2rayN" || true
[ -f "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_ARM64/usr/share/pixmaps/v2rayN.png" || true
[ -f "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_ARM64/v2rayN.png" || true
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' > "$APPDIR_ARM64/AppRun"
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' >"$APPDIR_ARM64/AppRun"
chmod +x "$APPDIR_ARM64/AppRun"
ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_ARM64/usr/bin/v2rayN"
cat > "$APPDIR_ARM64/v2rayN.desktop" <<EOF
cat >"$APPDIR_ARM64/v2rayN.desktop" <<EOF
[Desktop Entry]
Name=v2rayN
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others

51
package-debian.sh Normal file → Executable file
View File

@@ -1,34 +1,52 @@
#!/bin/bash
#!/usr/bin/env bash
set -euo pipefail
Arch="$1"
OutputPath="$2"
Version="$3"
# Root directory = the script's location
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
source ./utils.sh
FileName="v2rayN-${Arch}.zip"
wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/$FileName"
7z x $FileName
cp -rf v2rayN-${Arch}/* $OutputPath
Arch="linux-64"
OutputPath="$(mktemp -d)"
Version="$1"
PROJ="./v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj"
dotnet clean "${PROJ}" -c Release
sudo rm -rf "$(dirname "$PROJ")/bin/Release/net8.0"
dotnet publish "${PROJ}" -c Release -r "linux-x64" --self-contained -o "$OutputPath"
PROJ="./v2rayN/AmazTool/AmazTool.csproj"
dotnet clean "${PROJ}" -c Release
sudo rm -rf "$(dirname "$PROJ")/bin/Release/net8.0"
dotnet publish "${PROJ}" -c Release -r "linux-x64" --self-contained -p:PublishTrimmed=true -o "$OutputPath"
export RID_DIR="linux-x64"
download_xray "$OutputPath/bin/xray"
download_singbox "$OutputPath/bin/sing_box"
download_geo_assets "$OutputPath"
PackagePath="v2rayN-Package-${Arch}"
mkdir -p "${PackagePath}/DEBIAN"
mkdir -p "${PackagePath}/opt"
cp -rf $OutputPath "${PackagePath}/opt/v2rayN"
echo "When this file exists, app will not store configs under this folder" > "${PackagePath}/opt/v2rayN/NotStoreConfigHere.txt"
cp -rf "$OutputPath" "${PackagePath}/opt/v2rayN"
echo "When this file exists, app will not store configs under this folder" >"${PackagePath}/opt/v2rayN/NotStoreConfigHere.txt"
if [ $Arch = "linux-64" ]; then
Arch2="amd64"
if [ "$Arch" = "linux-64" ]; then
Arch2="amd64"
else
Arch2="arm64"
Arch2="arm64"
fi
echo $Arch2
# basic
cat >"${PackagePath}/DEBIAN/control" <<-EOF
Package: v2rayN
Package: v2rayn-unofficial
Version: $Version
Maintainer: Vlyaii <voronin9032n3@gmail.com>
Architecture: $Arch2
Maintainer: https://github.com/2dust/v2rayN
Replaces: v2rayN, v2rayn
Depends: desktop-file-utils, xdg-utils
Breaks: v2rayN, v2rayn
Conflicts: v2rayN, v2rayn
Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others
EOF
@@ -65,5 +83,6 @@ sudo chmod 755 "${PackagePath}/opt/v2rayN/v2rayN" 2>/dev/null || true
sudo chmod 755 "${PackagePath}/opt/v2rayN/AmazTool" 2>/dev/null || true
# build deb package
sudo dpkg-deb -Zxz --build $PackagePath
sudo dpkg-deb -Zzstd --build "$PackagePath"
sudo mv "${PackagePath}.deb" "v2rayN-${Arch}.deb"
sudo rm -rf "$OutputPath"

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
Arch="$1"
OutputPath="$2"
@@ -13,7 +13,7 @@ PackagePath="v2rayN-Package-${Arch}"
mkdir -p "$PackagePath/v2rayN.app/Contents/Resources"
cp -rf "$OutputPath" "$PackagePath/v2rayN.app/Contents/MacOS"
cp -f "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.icns" "$PackagePath/v2rayN.app/Contents/Resources/AppIcon.icns"
echo "When this file exists, app will not store configs under this folder" > "$PackagePath/v2rayN.app/Contents/MacOS/NotStoreConfigHere.txt"
echo "When this file exists, app will not store configs under this folder" >"$PackagePath/v2rayN.app/Contents/MacOS/NotStoreConfigHere.txt"
chmod +x "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN"
cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF
@@ -48,11 +48,11 @@ cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF
EOF
create-dmg \
--volname "v2rayN Installer" \
--window-size 700 420 \
--icon-size 100 \
--icon "v2rayN.app" 160 185 \
--hide-extension "v2rayN.app" \
--app-drop-link 500 185 \
"v2rayN-${Arch}.dmg" \
"$PackagePath/v2rayN.app"
--volname "v2rayN Installer" \
--window-size 700 420 \
--icon-size 100 \
--icon "v2rayN.app" 160 185 \
--hide-extension "v2rayN.app" \
--app-drop-link 500 185 \
"v2rayN-${Arch}.dmg" \
"$PackagePath/v2rayN.app"

4
package-release-zip.sh Normal file → Executable file
View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
Arch="$1"
OutputPath="$2"
@@ -12,4 +12,4 @@ ZipPath64="./$OutputArch"
mkdir $ZipPath64
cp -rf $OutputPath "$ZipPath64/$OutputArch"
7z a -tZip $FileName "$ZipPath64/$OutputArch" -mx1
7z a -tZip $FileName "$ZipPath64/$OutputArch" -mx1

657
package-rhel.sh Normal file → Executable file
View File

@@ -1,18 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail
# == Require Red Hat Enterprise Linux/FedoraLinux/RockyLinux/AlmaLinux/CentOS OR Ubuntu/Debian ==
# == Require Red Hat Enterprise Linux/FedoraLinux/RockyLinux/AlmaLinux/CentOS OR Ubuntu ==
if [[ -r /etc/os-release ]]; then
. /etc/os-release
source /etc/os-release
case "$ID" in
rhel|rocky|almalinux|fedora|centos|ubuntu|debian)
echo "[OK] Detected supported system: $NAME $VERSION_ID"
;;
*)
echo "[ERROR] Unsupported system: $NAME ($ID)."
echo "This script only supports Red Hat Enterprise Linux/RockyLinux/AlmaLinux/CentOS or Ubuntu/Debian."
exit 1
;;
rhel | rocky | almalinux | fedora | centos | ubuntu)
echo "[OK] Detected supported system: $NAME $VERSION_ID"
;;
*)
echo "[ERROR] Unsupported system: $NAME ($ID)."
echo "This script only supports Red Hat Enterprise Linux/RockyLinux/AlmaLinux/CentOS or Ubuntu."
exit 1
;;
esac
else
echo "[ERROR] Cannot detect system (missing /etc/os-release)."
@@ -20,12 +20,9 @@ else
fi
# ===== Config & Parse arguments =========================================================
VERSION_ARG="${1:-}" # Pass version number like 7.13.8, or leave empty
WITH_CORE="both" # Default: bundle both xray+sing-box
AUTOSTART=0 # 1 = enable system-wide autostart (/etc/xdg/autostart)
FORCE_NETCORE=0 # --netcore => skip archive bundle, use separate downloads
ARCH_OVERRIDE="" # --arch x64|arm64|all (optional compile target)
BUILD_FROM="" # --buildfrom 1|2|3 to select channel non-interactively
VERSION_ARG="${1:-}" # Pass version number like 7.13.8, or leave empty
WITH_CORE="both" # Default: bundle both xray+sing-box
ARCH_OVERRIDE="" # --arch x64|arm64|all (optional compile target)
# If the first argument starts with --, do not treat it as a version number
if [[ "${VERSION_ARG:-}" == --* ]]; then
@@ -37,97 +34,86 @@ if [[ -n "${VERSION_ARG:-}" ]]; then shift || true; fi
# Parse remaining optional arguments
while [[ $# -gt 0 ]]; do
case "$1" in
--with-core) WITH_CORE="${2:-both}"; shift 2;;
--autostart) AUTOSTART=1; shift;;
--xray-ver) XRAY_VER="${2:-}"; shift 2;;
--singbox-ver) SING_VER="${2:-}"; shift 2;;
--netcore) FORCE_NETCORE=1; shift;;
--arch) ARCH_OVERRIDE="${2:-}"; shift 2;;
--buildfrom) BUILD_FROM="${2:-}"; shift 2;;
*)
if [[ -z "${VERSION_ARG:-}" ]]; then VERSION_ARG="$1"; fi
shift;;
--with-core)
WITH_CORE="${2:-both}"
shift 2
;;
--xray-ver)
XRAY_VER="${2:-}"
shift 2
;;
--singbox-ver)
SING_VER="${2:-}"
shift 2
;;
--arch)
ARCH_OVERRIDE="${2:-}"
shift 2
;;
--release)
RPM_RELEASE="${2:-}"
shift 2
;;
*)
if [[ -z "${VERSION_ARG:-}" ]]; then VERSION_ARG="$1"; fi
shift
;;
esac
done
# Conflict: version number AND --buildfrom cannot be used together
if [[ -n "${VERSION_ARG:-}" && -n "${BUILD_FROM:-}" ]]; then
echo "[ERROR] You cannot specify both an explicit version and --buildfrom at the same time."
echo " Provide either a version (e.g. 7.14.0) OR --buildfrom 1|2|3."
if [[ -z "${RPM_RELEASE:-}" ]]; then
echo "--release is required"
exit 1
fi
# ===== Environment check + Dependencies ========================================
host_arch="$(uname -m)"
[[ "$host_arch" == "aarch64" || "$host_arch" == "x86_64" ]] || { echo "Only supports aarch64 / x86_64"; exit 1; }
if ! [[ "$host_arch" == "aarch64" || "$host_arch" == "x86_64" ]]; then
echo "Only supports aarch64 / x86_64"
exit 1
fi
install_ok=0
case "$ID" in
# ------------------------------ RHEL family (UNCHANGED) ------------------------------
rhel|rocky|almalinux|centos)
if command -v dnf >/dev/null 2>&1; then
sudo dnf -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar rsync || \
# ------------------------------ RHEL family (UNCHANGED) ------------------------------
rhel | rocky | almalinux | centos)
if command -v dnf >/dev/null 2>&1; then
sudo dnf -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar rsync ||
sudo dnf -y install dotnet-sdk rpm-build rpmdevtools curl unzip tar rsync
install_ok=1
elif command -v yum >/dev/null 2>&1; then
sudo yum -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar rsync || \
install_ok=1
elif command -v yum >/dev/null 2>&1; then
sudo yum -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar rsync ||
sudo yum -y install dotnet-sdk rpm-build rpmdevtools curl unzip tar rsync
install_ok=1
fi
;;
# ------------------------------ Ubuntu ----------------------------------------------
ubuntu)
sudo apt-get update
# Ensure 'universe' (Ubuntu) to get 'rpm'
if ! apt-cache policy | grep -q '^500 .*ubuntu.com/ubuntu.* universe'; then
sudo apt-get -y install software-properties-common || true
sudo add-apt-repository -y universe || true
sudo apt-get update
fi
# Base tools + rpm (provides rpmbuild)
sudo apt-get -y install curl unzip tar rsync rpm || true
# Cross-arch binutils so strip matches target arch + objdump for brp scripts
sudo apt-get -y install binutils binutils-x86-64-linux-gnu binutils-aarch64-linux-gnu || true
# rpmbuild presence check
if ! command -v rpmbuild >/dev/null 2>&1; then
echo "[ERROR] 'rpmbuild' not found after installing 'rpm'."
echo " Please ensure the 'rpm' package is available from your repos (universe on Ubuntu)."
exit 1
fi
# .NET SDK 8 (best effort via apt)
if ! command -v dotnet >/dev/null 2>&1; then
sudo apt-get -y install dotnet-sdk-8.0 || true
sudo apt-get -y install dotnet-sdk-8 || true
sudo apt-get -y install dotnet-sdk || true
fi
install_ok=1
;;
# ------------------------------ Debian (KEEP, with local dotnet install) ------------
debian)
fi
;;
# ------------------------------ Ubuntu ----------------------------------------------
ubuntu)
sudo apt-get update
# Ensure 'universe' (Ubuntu) to get 'rpm'
if ! apt-cache policy | grep -q '^500 .*ubuntu.com/ubuntu.* universe'; then
sudo apt-get -y install software-properties-common || true
sudo add-apt-repository -y universe || true
sudo apt-get update
# Base tools + rpm (provides rpmbuild on Debian) + objdump/strip
sudo apt-get -y install curl unzip tar rsync rpm binutils || true
# rpmbuild presence check
if ! command -v rpmbuild >/dev/null 2>&1; then
echo "[ERROR] 'rpmbuild' not found after installing 'rpm'."
echo " Please ensure 'rpm' is available from Debian repos."
exit 1
fi
# Try apt for dotnet; fallback to official installer into $HOME/.dotnet
if ! command -v dotnet >/dev/null 2>&1; then
echo "[INFO] 'dotnet' not found. Installing .NET 8 SDK locally to \$HOME/.dotnet ..."
tmp="$(mktemp -d)"; trap '[[ -n "${tmp:-}" ]] && rm -rf "$tmp"' RETURN
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$tmp/dotnet-install.sh"
bash "$tmp/dotnet-install.sh" --channel 8.0 --install-dir "$HOME/.dotnet"
export PATH="$HOME/.dotnet:$HOME/.dotnet/tools:$PATH"
export DOTNET_ROOT="$HOME/.dotnet"
if ! command -v dotnet >/dev/null 2>&1; then
echo "[ERROR] dotnet installation failed."
exit 1
fi
fi
install_ok=1
;;
fi
# Base tools + rpm (provides rpmbuild)
sudo apt-get -y install curl unzip tar rsync rpm || true
# Cross-arch binutils so strip matches target arch + objdump for brp scripts
sudo apt-get -y install binutils binutils-x86-64-linux-gnu binutils-aarch64-linux-gnu || true
# rpmbuild presence check
if ! command -v rpmbuild >/dev/null 2>&1; then
echo "[ERROR] 'rpmbuild' not found after installing 'rpm'."
echo " Please ensure the 'rpm' package is available from your repos (universe on Ubuntu)."
exit 1
fi
# .NET SDK 8 (best effort via apt)
if ! command -v dotnet >/dev/null 2>&1; then
sudo apt-get -y install dotnet-sdk-8.0 || true
sudo apt-get -y install dotnet-sdk-8 || true
sudo apt-get -y install dotnet-sdk || true
fi
install_ok=1
;;
esac
if [[ "$install_ok" -ne 1 ]]; then
@@ -141,6 +127,8 @@ command -v curl >/dev/null
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
source ./utils.sh
# Git submodules (best effort)
if [[ -f .gitmodules ]]; then
git submodule sync --recursive || true
@@ -152,348 +140,16 @@ PROJECT="v2rayN.Desktop/v2rayN.Desktop.csproj"
if [[ ! -f "$PROJECT" ]]; then
PROJECT="$(find . -maxdepth 3 -name 'v2rayN.Desktop.csproj' | head -n1 || true)"
fi
[[ -f "$PROJECT" ]] || { echo "v2rayN.Desktop.csproj not found"; exit 1; }
# ===== Resolve GUI version & auto checkout ============================================
VERSION=""
choose_channel() {
# If --buildfrom provided, map it directly and skip interaction.
if [[ -n "${BUILD_FROM:-}" ]]; then
case "$BUILD_FROM" in
1) echo "latest"; return 0;;
2) echo "prerelease"; return 0;;
3) echo "keep"; return 0;;
*) echo "[ERROR] Invalid --buildfrom value: ${BUILD_FROM}. Use 1|2|3." >&2; exit 1;;
esac
fi
# Print menu to stderr and read from /dev/tty so stdout only carries the token.
local ch="latest" sel=""
if [[ -t 0 ]]; then
echo "[?] Choose v2rayN release channel:" >&2
echo " 1) Latest (stable) [default]" >&2
echo " 2) Pre-release (preview)" >&2
echo " 3) Keep current (do nothing)" >&2
printf "Enter 1, 2 or 3 [default 1]: " >&2
if read -r sel </dev/tty; then
case "${sel:-}" in
2) ch="prerelease" ;;
3) ch="keep" ;;
*) ch="latest" ;;
esac
else
ch="latest"
fi
else
ch="latest"
fi
echo "$ch"
[[ -f "$PROJECT" ]] || {
echo "v2rayN.Desktop.csproj not found"
exit 1
}
get_latest_tag_latest() {
# Resolve /releases/latest → tag_name
curl -fsSL "https://api.github.com/repos/2dust/v2rayN/releases/latest" \
| grep -Eo '"tag_name":\s*"v?[^"]+"' \
| head -n1 \
| sed -E 's/.*"tag_name":\s*"v?([^"]+)".*/\1/'
}
get_latest_tag_prerelease() {
# Resolve newest prerelease=true tag; prefer jq, fallback to sed/grep (no awk)
local json tag
json="$(curl -fsSL "https://api.github.com/repos/2dust/v2rayN/releases?per_page=20")" || return 1
# 1) Use jq if present
if command -v jq >/dev/null 2>&1; then
tag="$(printf '%s' "$json" \
| jq -r '[.[] | select(.prerelease==true)][0].tag_name' 2>/dev/null \
| sed 's/^v//')" || true
fi
# 2) Fallback to sed/grep only
if [[ -z "${tag:-}" || "${tag:-}" == "null" ]]; then
tag="$(printf '%s' "$json" \
| tr '\n' ' ' \
| sed 's/},[[:space:]]*{/\n/g' \
| grep -m1 -E '"prerelease"[[:space:]]*:[[:space:]]*true' \
| grep -Eo '"tag_name"[[:space:]]*:[[:space:]]*"v?[^"]+"' \
| head -n1 \
| sed -E 's/.*"tag_name"[[:space:]]*:[[:space:]]*"v?([^"]+)".*/\1/')" || true
fi
[[ -n "${tag:-}" && "${tag:-}" != "null" ]] || return 1
printf '%s\n' "$tag"
}
git_try_checkout() {
# Try a series of refs and checkout when found.
local want="$1" ref=""
if git rev-parse --git-dir >/dev/null 2>&1; then
git fetch --tags --force --prune --depth=1 || true
if git rev-parse "refs/tags/v${want}" >/dev/null 2>&1; then
ref="v${want}"
elif git rev-parse "refs/tags/${want}" >/dev/null 2>&1; then
ref="${want}"
elif git rev-parse --verify "${want}" >/dev/null 2>&1; then
ref="${want}"
fi
if [[ -n "$ref" ]]; then
echo "[OK] Found ref '${ref}', checking out..."
git checkout -f "${ref}"
if [[ -f .gitmodules ]]; then
git submodule sync --recursive || true
git submodule update --init --recursive || true
fi
return 0
fi
fi
return 1
}
if git rev-parse --git-dir >/dev/null 2>&1; then
if [[ -n "${VERSION_ARG:-}" ]]; then
echo "[*] Trying to switch v2rayN repo to version: ${VERSION_ARG}"
if git_try_checkout "${VERSION_ARG#v}"; then
VERSION="${VERSION_ARG#v}"
else
echo "[WARN] Tag '${VERSION_ARG}' not found."
ch="$(choose_channel)"
if [[ "$ch" == "keep" ]]; then
echo "[*] Keep current repository state (no checkout)."
if git describe --tags --abbrev=0 >/dev/null 2>&1; then
VERSION="$(git describe --tags --abbrev=0)"
else
VERSION="0.0.0+git"
fi
VERSION="${VERSION#v}"
else
echo "[*] Resolving ${ch} tag from GitHub releases..."
tag=""
if [[ "$ch" == "prerelease" ]]; then
tag="$(get_latest_tag_prerelease || true)"
if [[ -z "$tag" ]]; then
echo "[WARN] Failed to resolve prerelease tag, falling back to latest."
tag="$(get_latest_tag_latest || true)"
fi
else
tag="$(get_latest_tag_latest || true)"
fi
[[ -n "$tag" ]] || { echo "[ERROR] Failed to resolve latest tag for channel '${ch}'."; exit 1; }
echo "[*] Latest tag for '${ch}': ${tag}"
git_try_checkout "$tag" || { echo "[ERROR] Failed to checkout '${tag}'."; exit 1; }
VERSION="${tag#v}"
fi
fi
else
ch="$(choose_channel)"
if [[ "$ch" == "keep" ]]; then
echo "[*] Keep current repository state (no checkout)."
if git describe --tags --abbrev=0 >/dev/null 2>&1; then
VERSION="$(git describe --tags --abbrev=0)"
else
VERSION="0.0.0+git"
fi
VERSION="${VERSION#v}"
else
echo "[*] Resolving ${ch} tag from GitHub releases..."
tag=""
if [[ "$ch" == "prerelease" ]]; then
tag="$(get_latest_tag_prerelease || true)"
if [[ -z "$tag" ]]; then
echo "[WARN] Failed to resolve prerelease tag, falling back to latest."
tag="$(get_latest_tag_latest || true)"
fi
else
tag="$(get_latest_tag_latest || true)"
fi
[[ -n "$tag" ]] || { echo "[ERROR] Failed to resolve latest tag for channel '${ch}'."; exit 1; }
echo "[*] Latest tag for '${ch}': ${tag}"
git_try_checkout "$tag" || { echo "[ERROR] Failed to checkout '${tag}'."; exit 1; }
VERSION="${tag#v}"
fi
fi
else
echo "[WARN] Current directory is not a git repo; cannot checkout version. Proceeding on current tree."
VERSION="${VERSION_ARG:-}"
if [[ -z "$VERSION" ]]; then
if git describe --tags --abbrev=0 >/dev/null 2>&1; then
VERSION="$(git describe --tags --abbrev=0)"
else
VERSION="0.0.0+git"
fi
fi
VERSION="${VERSION#v}"
fi
echo "[*] GUI version resolved as: ${VERSION}"
# ===== Helpers for core/rules download (use RID_DIR for arch sync) =====================
download_xray() {
# Download Xray core and install to outdir/xray
local outdir="$1" ver="${XRAY_VER:-}" url tmp zipname="xray.zip"
mkdir -p "$outdir"
if [[ -n "${XRAY_VER:-}" ]]; then ver="${XRAY_VER}"; fi
if [[ -z "$ver" ]]; then
ver="$(curl -fsSL https://api.github.com/repos/XTLS/Xray-core/releases/latest \
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
fi
[[ -n "$ver" ]] || { echo "[xray] Failed to get version"; return 1; }
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://github.com/XTLS/Xray-core/releases/download/v${ver}/Xray-linux-arm64-v8a.zip"
else
url="https://github.com/XTLS/Xray-core/releases/download/v${ver}/Xray-linux-64.zip"
fi
echo "[+] Download xray: $url"
tmp="$(mktemp -d)"; trap '[[ -n "${tmp:-}" ]] && rm -rf "$tmp"' RETURN
curl -fL "$url" -o "$tmp/$zipname"
unzip -q "$tmp/$zipname" -d "$tmp"
install -Dm755 "$tmp/xray" "$outdir/xray"
}
download_singbox() {
# Download sing-box core and install to outdir/sing-box
local outdir="$1" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin
mkdir -p "$outdir"
if [[ -n "${SING_VER:-}" ]]; then ver="${SING_VER}"; fi
if [[ -z "$ver" ]]; then
ver="$(curl -fsSL https://api.github.com/repos/SagerNet/sing-box/releases/latest \
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
fi
[[ -n "$ver" ]] || { echo "[sing-box] Failed to get version"; return 1; }
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://github.com/SagerNet/sing-box/releases/download/v${ver}/sing-box-${ver}-linux-arm64.tar.gz"
else
url="https://github.com/SagerNet/sing-box/releases/download/v${ver}/sing-box-${ver}-linux-amd64.tar.gz"
fi
echo "[+] Download sing-box: $url"
tmp="$(mktemp -d)"; trap '[[ -n "${tmp:-}" ]] && rm -rf "$tmp"' RETURN
curl -fL "$url" -o "$tmp/$tarname"
tar -C "$tmp" -xzf "$tmp/$tarname"
bin="$(find "$tmp" -type f -name 'sing-box' | head -n1 || true)"
[[ -n "$bin" ]] || { echo "[!] sing-box unpack failed"; return 1; }
install -Dm755 "$bin" "$outdir/sing-box"
}
# ---- NEW: download_mihomo (REQUIRED in --netcore mode) ----
download_mihomo() {
# Download mihomo into outroot/bin/mihomo/mihomo
local outroot="$1"
local url=""
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-arm64/bin/mihomo/mihomo"
else
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-64/bin/mihomo/mihomo"
fi
echo "[+] Download mihomo: $url"
mkdir -p "$outroot/bin/mihomo"
curl -fL "$url" -o "$outroot/bin/mihomo/mihomo"
chmod +x "$outroot/bin/mihomo/mihomo" || true
}
# Move geo files to a unified path: outroot/bin
unify_geo_layout() {
local outroot="$1"
mkdir -p "$outroot/bin"
local names=( \
"geosite.dat" \
"geoip.dat" \
"geoip-only-cn-private.dat" \
"Country.mmdb" \
"geoip.metadb" \
)
for n in "${names[@]}"; do
# If file exists under bin/xray/, move it up to bin/
if [[ -f "$outroot/bin/xray/$n" ]]; then
mv -f "$outroot/bin/xray/$n" "$outroot/bin/$n"
fi
# If file already in bin/, leave it as-is
if [[ -f "$outroot/bin/$n" ]]; then
:
fi
done
}
# Download geo/rule assets; then unify to bin/
download_geo_assets() {
local outroot="$1"
local bin_dir="$outroot/bin"
local srss_dir="$bin_dir/srss"
mkdir -p "$bin_dir" "$srss_dir"
echo "[+] Download Xray Geo to ${bin_dir}"
curl -fsSL -o "$bin_dir/geosite.dat" \
"https://github.com/Loyalsoldier/V2ray-rules-dat/releases/latest/download/geosite.dat"
curl -fsSL -o "$bin_dir/geoip.dat" \
"https://github.com/Loyalsoldier/V2ray-rules-dat/releases/latest/download/geoip.dat"
curl -fsSL -o "$bin_dir/geoip-only-cn-private.dat" \
"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat"
curl -fsSL -o "$bin_dir/Country.mmdb" \
"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb"
echo "[+] Download sing-box rule DB & rule-sets"
curl -fsSL -o "$bin_dir/geoip.metadb" \
"https://github.com/MetaCubeX/meta-rules-dat/releases/latest/download/geoip.metadb" || true
for f in \
geoip-private.srs geoip-cn.srs geoip-facebook.srs geoip-fastly.srs \
geoip-google.srs geoip-netflix.srs geoip-telegram.srs geoip-twitter.srs; do
curl -fsSL -o "$srss_dir/$f" \
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geoip/$f" || true
done
for f in \
geosite-cn.srs geosite-gfw.srs geosite-greatfire.srs \
geosite-geolocation-cn.srs geosite-category-ads-all.srs geosite-private.srs; do
curl -fsSL -o "$srss_dir/$f" \
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geosite/$f" || true
done
# Unify to bin/
unify_geo_layout "$outroot"
}
# Prefer the prebuilt v2rayN core bundle; then unify geo layout
download_v2rayn_bundle() {
local outroot="$1"
local url=""
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-arm64.zip"
else
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-64.zip"
fi
echo "[+] Try v2rayN bundle archive: $url"
local tmp zipname
tmp="$(mktemp -d)"; zipname="$tmp/v2rayn.zip"
curl -fL "$url" -o "$zipname" || { echo "[!] Bundle download failed"; return 1; }
unzip -q "$zipname" -d "$tmp" || { echo "[!] Bundle unzip failed"; return 1; }
if [[ -d "$tmp/bin" ]]; then
mkdir -p "$outroot/bin"
rsync -a "$tmp/bin/" "$outroot/bin/"
else
rsync -a "$tmp/" "$outroot/"
fi
rm -f "$outroot/v2rayn.zip" 2>/dev/null || true
# keep mihomo
# find "$outroot" -type d -name "mihomo" -prune -exec rm -rf {} + 2>/dev/null || true
local nested_dir
nested_dir="$(find "$outroot" -maxdepth 1 -type d -name 'v2rayN-linux-*' | head -n1 || true)"
if [[ -n "${nested_dir:-}" && -d "$nested_dir/bin" ]]; then
mkdir -p "$outroot/bin"
rsync -a "$nested_dir/bin/" "$outroot/bin/"
rm -rf "$nested_dir"
fi
# Unify to bin/
unify_geo_layout "$outroot"
echo "[+] Bundle extracted to $outroot"
}
VERSION="$VERSION_ARG"
# ===== Build results collection for --arch all ========================================
BUILT_RPMS=() # Will collect absolute paths of built RPMs
BUILT_ALL=0 # Flag to know if we should print the final summary
BUILT_RPMS=() # Will collect absolute paths of built RPMs
BUILT_ALL=0 # Flag to know if we should print the final summary
# ===== Build (single-arch) function ====================================================
build_for_arch() {
@@ -501,9 +157,20 @@ build_for_arch() {
local short="$1"
local rid rpm_target archdir
case "$short" in
x64) rid="linux-x64"; rpm_target="x86_64"; archdir="x86_64" ;;
arm64) rid="linux-arm64"; rpm_target="aarch64"; archdir="aarch64" ;;
*) echo "[ERROR] Unknown arch '$short' (use x64|arm64)"; return 1;;
x64)
rid="linux-x64"
rpm_target="x86_64"
archdir="x86_64"
;;
arm64)
rid="linux-arm64"
rpm_target="aarch64"
archdir="aarch64"
;;
*)
echo "[ERROR] Unknown arch '$short' (use x64|arm64)"
return 1
;;
esac
echo "[*] Building for target: $short (RID=$rid, RPM --target $rpm_target)"
@@ -515,6 +182,7 @@ build_for_arch() {
dotnet restore "$PROJECT"
dotnet publish "$PROJECT" \
-c Release -r "$rid" \
--sc \
-p:PublishSingleFile=false \
-p:SelfContained=true \
-p:IncludeNativeLibrariesForSelfExtract=true
@@ -563,31 +231,13 @@ build_for_arch() {
mkdir -p "$WORKDIR/$PKGROOT/bin/xray" "$WORKDIR/$PKGROOT/bin/sing_box"
# Bundle / cores per-arch
if [[ "$FORCE_NETCORE" -eq 0 ]]; then
if download_v2rayn_bundle "$WORKDIR/$PKGROOT"; then
echo "[*] Using v2rayN bundle archive."
else
echo "[*] Bundle failed, fallback to separate core + rules."
if [[ "$WITH_CORE" == "xray" || "$WITH_CORE" == "both" ]]; then
download_xray "$WORKDIR/$PKGROOT/bin/xray" || echo "[!] xray download failed (skipped)"
fi
if [[ "$WITH_CORE" == "sing-box" || "$WITH_CORE" == "both" ]]; then
download_singbox "$WORKDIR/$PKGROOT/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
fi
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
fi
else
echo "[*] --netcore specified: use separate core + rules."
if [[ "$WITH_CORE" == "xray" || "$WITH_CORE" == "both" ]]; then
download_xray "$WORKDIR/$PKGROOT/bin/xray" || echo "[!] xray download failed (skipped)"
fi
if [[ "$WITH_CORE" == "sing-box" || "$WITH_CORE" == "both" ]]; then
download_singbox "$WORKDIR/$PKGROOT/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
fi
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
# ---- REQUIRED: always fetch mihomo in netcore mode, per-arch ----
download_mihomo "$WORKDIR/$PKGROOT" || echo "[!] mihomo download failed (skipped)"
if [[ "$WITH_CORE" == "xray" || "$WITH_CORE" == "both" ]]; then
download_xray "$WORKDIR/$PKGROOT/bin/xray" || echo "[!] xray download failed (skipped)"
fi
if [[ "$WITH_CORE" == "sing-box" || "$WITH_CORE" == "both" ]]; then
download_singbox "$WORKDIR/$PKGROOT/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
fi
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
# Tarball
mkdir -p "$SOURCEDIR"
@@ -596,19 +246,19 @@ build_for_arch() {
# SPEC
local SPECFILE="$SPECDIR/v2rayN.spec"
mkdir -p "$SPECDIR"
cat > "$SPECFILE" <<'SPEC'
cat >"$SPECFILE" <<'SPEC'
%global debug_package %{nil}
%undefine _debuginfo_subpackages
%undefine _debugsource_packages
# Ignore outdated LTTng dependencies incorrectly reported by the .NET runtime (to avoid installation failures)
%global __requires_exclude ^liblttng-ust\.so\..*$
Name: v2rayN
Name: v2rayn-unofficial
Version: __VERSION__
Release: 1%{?dist}
Release: __RELEASE__
Summary: v2rayN (Avalonia) GUI client for Linux (x86_64/aarch64)
License: GPL-3.0-only
URL: https://github.com/2dust/v2rayN
URL: https://git.vlyaii.ru/voronin9032/v2rayN
BugURL: https://github.com/2dust/v2rayN/issues
ExclusiveArch: aarch64 x86_64
Source0: __PKGROOT__.tar.gz
@@ -617,6 +267,9 @@ Source0: __PKGROOT__.tar.gz
Requires: libX11, libXrandr, libXcursor, libXi, libXext, libxcb, libXrender, libXfixes, libXinerama, libxkbcommon
Requires: fontconfig, freetype, cairo, pango, mesa-libEGL, mesa-libGL, xdg-utils
Conflicts: v2rayN
Obsoletes: v2rayN
%description
v2rayN Linux for Red Hat Enterprise Linux
Support vless / vmess / Trojan / http / socks / Anytls / Hysteria2 / Shadowsocks / tuic / WireGuard
@@ -689,41 +342,9 @@ fi
%{_datadir}/icons/hicolor/256x256/apps/v2rayn.png
SPEC
# Autostart injection (inside %install) and %files entry
if [[ "$AUTOSTART" -eq 1 ]]; then
awk '
BEGIN{ins=0}
/^%post$/ && !ins {
print "# --- Autostart (.desktop) ---"
print "install -dm0755 %{buildroot}/etc/xdg/autostart"
print "cat > %{buildroot}/etc/xdg/autostart/v2rayn.desktop << '\''EOF'\''"
print "[Desktop Entry]"
print "Type=Application"
print "Name=v2rayN (Autostart)"
print "Exec=v2rayn"
print "X-GNOME-Autostart-enabled=true"
print "NoDisplay=false"
print "EOF"
ins=1
}
{print}
' "$SPECFILE" > "${SPECFILE}.tmp" && mv "${SPECFILE}.tmp" "$SPECFILE"
awk '
BEGIN{infiles=0; done=0}
/^%files$/ {infiles=1}
infiles && done==0 && $0 ~ /%{_datadir}\/icons\/hicolor\/256x256\/apps\/v2rayn\.png/ {
print
print "%config(noreplace) /etc/xdg/autostart/v2rayn.desktop"
done=1
next
}
{print}
' "$SPECFILE" > "${SPECFILE}.tmp" && mv "${SPECFILE}.tmp" "$SPECFILE"
fi
# Replace placeholders
sed -i "s/__VERSION__/${VERSION}/g" "$SPECFILE"
sed -i "s/__RELEASE__/${RPM_RELEASE}/g" "$SPECFILE"
sed -i "s/__PKGROOT__/${PKGROOT}/g" "$SPECFILE"
# ----- Select proper 'strip' per target arch on Ubuntu only (cross-binutils) -----
@@ -738,7 +359,7 @@ SPEC
STRIP_BIN="/usr/bin/aarch64-linux-gnu-strip"
fi
if [[ -x "$STRIP_BIN" ]]; then
STRIP_ARGS=( --define "__strip $STRIP_BIN" )
STRIP_ARGS=(--define "__strip $STRIP_BIN")
fi
fi
@@ -758,7 +379,7 @@ SPEC
echo "Build done for $short. RPM at:"
local f
for f in "${TOPDIR}/RPMS/${archdir}/v2rayN-${VERSION}-1"*.rpm; do
for f in "${TOPDIR}/RPMS/${archdir}/v2rayN-${VERSION}-${RPM_RELEASE}"*.rpm; do
[[ -e "$f" ]] || continue
echo " $f"
BUILT_RPMS+=("$f")
@@ -767,30 +388,30 @@ SPEC
# ===== Arch selection and build orchestration =========================================
case "${ARCH_OVERRIDE:-}" in
"")
# No --arch: use host architecture
if [[ "$host_arch" == "aarch64" ]]; then
build_for_arch arm64
else
build_for_arch x64
fi
;;
x64|amd64)
build_for_arch x64
;;
arm64|aarch64)
"")
# No --arch: use host architecture
if [[ "$host_arch" == "aarch64" ]]; then
build_for_arch arm64
;;
all)
BUILT_ALL=1
# Build x64 and arm64 separately; each package contains its own arch-only binaries.
else
build_for_arch x64
build_for_arch arm64
;;
*)
echo "[ERROR] Unknown --arch '${ARCH_OVERRIDE}'. Use x64|arm64|all."
exit 1
;;
fi
;;
x64 | amd64)
build_for_arch x64
;;
arm64 | aarch64)
build_for_arch arm64
;;
all)
BUILT_ALL=1
# Build x64 and arm64 separately; each package contains its own arch-only binaries.
build_for_arch x64
build_for_arch arm64
;;
*)
echo "[ERROR] Unknown --arch '${ARCH_OVERRIDE}'. Use x64|arm64|all."
exit 1
;;
esac
# ===== Final summary if building both arches ==========================================

120
utils.sh Normal file
View File

@@ -0,0 +1,120 @@
#!/usr/bin/env bash
download_xray() {
# Download Xray core and install to outdir/xray
local outdir="$1" ver="${XRAY_VER:-}" url tmp zipname="xray.zip"
mkdir -p "$outdir"
if [[ -n "${XRAY_VER:-}" ]]; then ver="${XRAY_VER}"; fi
if [[ -z "$ver" ]]; then
echo "Downloading latest xray"
ver="$(curl -fsSL https://api.github.com/repos/XTLS/Xray-core/releases/latest |
grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
fi
if [[ -z "$ver" ]]; then
echo "[xray] Failed to get version"
return 1
fi
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://github.com/XTLS/Xray-core/releases/download/v${ver}/Xray-linux-arm64-v8a.zip"
else
url="https://github.com/XTLS/Xray-core/releases/download/v${ver}/Xray-linux-64.zip"
fi
echo "[+] Download xray: $url"
tmp="$(mktemp -d)"
trap '[[ -n "${tmp:-}" ]] && rm -rf "$tmp"' RETURN
curl -fL "$url" -o "$tmp/$zipname"
unzip -q "$tmp/$zipname" -d "$tmp"
install -Dm755 "$tmp/xray" "$outdir/xray"
}
download_singbox() {
# Download sing-box core and install to outdir/sing-box
local outdir="$1" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin
mkdir -p "$outdir"
if [[ -n "${SING_VER:-}" ]]; then ver="${SING_VER}"; fi
if [[ -z "$ver" ]]; then
echo "Downloading latest sing-box"
ver="$(curl -fsSL https://api.github.com/repos/SagerNet/sing-box/releases/latest |
grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
fi
if [[ -z "$ver" ]]; then
echo "[sing-box] Failed to get version"
return 1
fi
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://github.com/SagerNet/sing-box/releases/download/v${ver}/sing-box-${ver}-linux-arm64.tar.gz"
else
url="https://github.com/SagerNet/sing-box/releases/download/v${ver}/sing-box-${ver}-linux-amd64.tar.gz"
fi
echo "[+] Download sing-box: $url"
tmp="$(mktemp -d)"
trap '[[ -n "${tmp:-}" ]] && rm -rf "$tmp"' RETURN
curl -fL "$url" -o "$tmp/$tarname"
tar -C "$tmp" -xzf "$tmp/$tarname"
bin="$(find "$tmp" -type f -name 'sing-box' | head -n1 || true)"
[[ -n "$bin" ]] || {
echo "[!] sing-box unpack failed"
return 1
}
install -Dm755 "$bin" "$outdir/sing-box"
}
# Move geo files to a unified path: outroot/bin
unify_geo_layout() {
local outroot="$1"
mkdir -p "$outroot/bin"
local names=(
"geosite.dat"
"geoip.dat"
"geoip-only-cn-private.dat"
"Country.mmdb"
"geoip.metadb"
)
for n in "${names[@]}"; do
# If file exists under bin/xray/, move it up to bin/
if [[ -f "$outroot/bin/xray/$n" ]]; then
mv -f "$outroot/bin/xray/$n" "$outroot/bin/$n"
fi
# If file already in bin/, leave it as-is
if [[ -f "$outroot/bin/$n" ]]; then
:
fi
done
}
# Download geo/rule assets; then unify to bin/
download_geo_assets() {
local outroot="$1"
local bin_dir="$outroot/bin"
local srss_dir="$bin_dir/srss"
mkdir -p "$bin_dir" "$srss_dir"
echo "[+] Download Xray Geo to ${bin_dir}"
curl -fsSL -o "$bin_dir/geosite.dat" \
"https://github.com/Loyalsoldier/V2ray-rules-dat/releases/latest/download/geosite.dat"
curl -fsSL -o "$bin_dir/geoip.dat" \
"https://github.com/Loyalsoldier/V2ray-rules-dat/releases/latest/download/geoip.dat"
curl -fsSL -o "$bin_dir/geoip-only-cn-private.dat" \
"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat"
curl -fsSL -o "$bin_dir/Country.mmdb" \
"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb"
echo "[+] Download sing-box rule DB & rule-sets"
curl -fsSL -o "$bin_dir/geoip.metadb" \
"https://github.com/MetaCubeX/meta-rules-dat/releases/latest/download/geoip.metadb" || true
for f in \
geoip-private.srs geoip-cn.srs geoip-facebook.srs geoip-fastly.srs \
geoip-google.srs geoip-netflix.srs geoip-telegram.srs geoip-twitter.srs; do
curl -fsSL -o "$srss_dir/$f" \
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geoip/$f" || true
done
for f in \
geosite-cn.srs geosite-gfw.srs geosite-greatfire.srs \
geosite-geolocation-cn.srs geosite-category-ads-all.srs geosite-private.srs; do
curl -fsSL -o "$srss_dir/$f" \
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geosite/$f" || true
done
# Unify to bin/
unify_geo_layout "$outroot"
}

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.15.6</Version>
<Version>7.15.4</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -6,13 +6,13 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.8" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.8" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.8" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.7" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.7" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.7" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.7" />
<PackageVersion Include="CliWrap" Version="3.9.0" />
<PackageVersion Include="Downloader" Version="4.0.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.2" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.1" />
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageVersion Include="QRCoder" Version="1.7.0" />

View File

@@ -1,3 +1,5 @@
using ReactiveUI;
namespace ServiceLib.Base;
public class MyReactiveObject : ReactiveObject

View File

@@ -1,3 +1,6 @@
using System.Collections.Concurrent;
using System.Reflection;
namespace ServiceLib.Common;
public static class EmbedUtils

View File

@@ -1,5 +1,6 @@
using System.Formats.Tar;
using System.IO.Compression;
using System.Text;
namespace ServiceLib.Common;

View File

@@ -1,3 +1,8 @@
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace ServiceLib.Common;
public class JsonUtils

View File

@@ -1,3 +1,5 @@
using System.Diagnostics;
namespace ServiceLib.Common;
public static class ProcUtils

View File

@@ -1,5 +1,13 @@
using System.Collections.Specialized;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
using CliWrap;
using CliWrap.Buffered;
@@ -355,110 +363,6 @@ public class Utils
return userHostsMap;
}
/// <summary>
/// Parse a possibly non-standard URL into scheme, domain, port, and path.
/// If parsing fails, the entire input is returned as domain, and others are empty or zero.
/// </summary>
/// <param name="url">Input URL or string</param>
/// <returns>(domain, scheme, port, path)</returns>
public static (string domain, string scheme, int port, string path) ParseUrl(string url)
{
if (string.IsNullOrWhiteSpace(url))
{
return ("", "", 0, "");
}
// 1. First, try to parse using the standard Uri class.
if (Uri.TryCreate(url, UriKind.Absolute, out var uri) && !string.IsNullOrEmpty(uri.Host))
{
var scheme = uri.Scheme;
var domain = uri.Host;
var port = uri.IsDefaultPort ? 0 : uri.Port;
var path = uri.PathAndQuery;
return (domain, scheme, port, path);
}
// 2. Try to handle more general cases with a regular expression, including non-standard schemes.
// This regex captures the scheme (optional), authority (host+port), and path (optional).
var match = Regex.Match(url, @"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):/{2,})?([^/?#]+)([^?#]*)?.*$");
if (match.Success)
{
var scheme = match.Groups[1].Value;
var authority = match.Groups[2].Value;
var path = match.Groups[3].Value;
// Remove userinfo from the authority part.
var atIndex = authority.LastIndexOf('@');
if (atIndex > 0)
{
authority = authority.Substring(atIndex + 1);
}
var (domain, port) = ParseAuthority(authority);
// If the parsed domain is empty, it means the authority part is malformed, so trigger the fallback.
if (!string.IsNullOrEmpty(domain))
{
return (domain, scheme, port, path);
}
}
// 3. If all of the above fails, execute the final fallback strategy.
return (url, "", 0, "");
}
/// <summary>
/// Helper function to parse domain and port from the authority part, with correct handling for IPv6.
/// </summary>
private static (string domain, int port) ParseAuthority(string authority)
{
if (string.IsNullOrEmpty(authority))
{
return ("", 0);
}
var port = 0;
var domain = authority;
// Handle IPv6 addresses, e.g., "[2001:db8::1]:443"
if (authority.StartsWith("[") && authority.Contains("]"))
{
int closingBracketIndex = authority.LastIndexOf(']');
if (closingBracketIndex < authority.Length - 1 && authority[closingBracketIndex + 1] == ':')
{
// Port exists
var portStr = authority.Substring(closingBracketIndex + 2);
if (int.TryParse(portStr, out var portNum))
{
port = portNum;
}
domain = authority.Substring(0, closingBracketIndex + 1);
}
else
{
// No port
domain = authority;
}
}
else // Handle IPv4 or domain names
{
var lastColonIndex = authority.LastIndexOf(':');
// Ensure there are digits after the colon and that this colon is not part of an IPv6 address.
if (lastColonIndex > 0 && lastColonIndex < authority.Length - 1 && authority.Substring(lastColonIndex + 1).All(char.IsDigit))
{
var portStr = authority.Substring(lastColonIndex + 1);
if (int.TryParse(portStr, out var portNum))
{
port = portNum;
domain = authority.Substring(0, lastColonIndex);
}
}
}
return (domain, port);
}
#endregion
#region

View File

@@ -1,3 +1,5 @@
using System.Security.Cryptography;
using System.Text;
using Microsoft.Win32;
namespace ServiceLib.Common;

View File

@@ -5,6 +5,5 @@ public enum ESpeedActionType
Tcping,
Realping,
Speedtest,
Mixedtest,
FastRealping
Mixedtest
}

View File

@@ -1,3 +1,5 @@
using System.Reactive;
namespace ServiceLib.Events;
public static class AppEvents

View File

@@ -1,3 +1,5 @@
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
namespace ServiceLib.Events;

View File

@@ -316,7 +316,7 @@ public class Global
];
public static readonly HashSet<EConfigType> SingboxOnlyConfigType = SingboxSupportConfigType.Except(XraySupportConfigType).ToHashSet();
public static readonly List<string> DomainStrategies =
[
AsIs,

View File

@@ -1,36 +1,14 @@
global using System.Collections.Concurrent;
global using System.Diagnostics;
global using System.Net;
global using System.Net.NetworkInformation;
global using System.Net.Sockets;
global using System.Reactive;
global using System.Reactive.Disposables;
global using System.Reactive.Linq;
global using System.Reflection;
global using System.Runtime.InteropServices;
global using System.Security.Cryptography;
global using System.Text;
global using System.Text.Encodings.Web;
global using System.Text.Json;
global using System.Text.Json.Nodes;
global using System.Text.Json.Serialization;
global using System.Text.RegularExpressions;
global using DynamicData;
global using DynamicData.Binding;
global using ReactiveUI;
global using ReactiveUI.Fody.Helpers;
global using ServiceLib.Base;
global using ServiceLib.Common;
global using ServiceLib.Enums;
global using ServiceLib.Events;
global using ServiceLib.Handler;
global using ServiceLib.Handler.Fmt;
global using ServiceLib.Handler.SysProxy;
global using ServiceLib.Helper;
global using ServiceLib.Manager;
global using ServiceLib.Handler.Fmt;
global using ServiceLib.Services;
global using ServiceLib.Services.Statistics;
global using ServiceLib.Services.CoreConfig;
global using ServiceLib.Models;
global using ServiceLib.Resx;
global using ServiceLib.Services;
global using ServiceLib.Services.CoreConfig;
global using ServiceLib.Services.Statistics;
global using SQLite;
global using ServiceLib.Handler.SysProxy;

View File

@@ -1,4 +1,5 @@
using System.Security.Principal;
using System.Text.RegularExpressions;
namespace ServiceLib.Handler;

View File

@@ -1,4 +1,5 @@
using System.Data;
using System.Text.RegularExpressions;
namespace ServiceLib.Handler;
@@ -112,8 +113,10 @@ public static class ConfigHandler
config.ConstItem ??= new ConstItem();
config.SimpleDNSItem ??= InitBuiltinSimpleDNS();
config.SimpleDNSItem.GlobalFakeIp ??= true;
config.SimpleDNSItem.BootstrapDNS ??= Global.DomainPureIPDNSAddress.FirstOrDefault();
if (config.SimpleDNSItem.GlobalFakeIp is null)
{
config.SimpleDNSItem.GlobalFakeIp = true;
}
config.SpeedTestItem ??= new();
if (config.SpeedTestItem.SpeedTestTimeout < 10)
@@ -1221,7 +1224,6 @@ public static class ConfigHandler
CoreType = coreType,
ConfigType = EConfigType.PolicyGroup,
Remarks = remark,
IsSub = false
};
if (!subId.IsNullOrEmpty())
{
@@ -1482,7 +1484,7 @@ public static class ConfigHandler
if (profileItem is null)
{
profileItem = Hysteria2Fmt.ResolveFull2(strData, subRemarks);
}
}
if (profileItem is null || profileItem.Address.IsNullOrEmpty())
{
return -1;
@@ -2272,7 +2274,6 @@ public static class ConfigHandler
BlockBindingQuery = true,
DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(),
BootstrapDNS = Global.DomainPureIPDNSAddress.FirstOrDefault(),
};
}

View File

@@ -1,3 +1,5 @@
using System.Net;
namespace ServiceLib.Handler;
public static class ConnectionHandler

View File

@@ -1,3 +1,5 @@
using System.Text.RegularExpressions;
namespace ServiceLib.Handler.Fmt;
public class ShadowsocksFmt : BaseFmt

View File

@@ -1,3 +1,4 @@
using System.Runtime.InteropServices;
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
namespace ServiceLib.Handler.SysProxy;

View File

@@ -1,3 +1,4 @@
using System.Net;
using Downloader;
namespace ServiceLib.Helper;

View File

@@ -1,5 +1,8 @@
using System.Diagnostics;
using System.Net;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
namespace ServiceLib.Helper;

View File

@@ -1,4 +1,5 @@
using System.Collections;
using SQLite;
namespace ServiceLib.Helper;

View File

@@ -1,3 +1,4 @@
using System.Text;
using CliWrap;
using CliWrap.Buffered;

View File

@@ -1,3 +1,6 @@
using System.Net.Sockets;
using System.Text;
namespace ServiceLib.Manager;
public class PacManager

View File

@@ -1,3 +1,5 @@
using System.Collections.Concurrent;
//using System.Reactive.Linq;
namespace ServiceLib.Manager;

View File

@@ -1,3 +1,5 @@
using System.Collections.Concurrent;
namespace ServiceLib.Manager;
public class ProfileGroupItemManager

View File

@@ -26,29 +26,15 @@ public class TaskManager
await Task.Delay(1000 * 60);
//Execute once 1 minute
try
{
await UpdateTaskRunSubscription();
}
catch (Exception ex)
{
Logging.SaveLog("ScheduledTasks - UpdateTaskRunSubscription", ex);
}
await UpdateTaskRunSubscription();
//Execute once 20 minute
if (numOfExecuted % 20 == 0)
{
//Logging.SaveLog("Execute save config");
try
{
await ConfigHandler.SaveConfig(_config);
await ProfileExManager.Instance.SaveTo();
}
catch (Exception ex)
{
Logging.SaveLog("ScheduledTasks - SaveConfig", ex);
}
await ConfigHandler.SaveConfig(_config);
await ProfileExManager.Instance.SaveTo();
}
//Execute once 1 hour
@@ -60,14 +46,8 @@ public class TaskManager
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
try
{
await UpdateTaskRunGeo(numOfExecuted / 60);
}
catch (Exception ex)
{
Logging.SaveLog("ScheduledTasks - UpdateTaskRunGeo", ex);
}
//Check once 1 hour
await UpdateTaskRunGeo(numOfExecuted / 60);
}
numOfExecuted++;

View File

@@ -1,3 +1,4 @@
using System.Net;
using WebDav;
namespace ServiceLib.Manager;

View File

@@ -1,3 +1,6 @@
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.Models;
public class CheckUpdateModel : ReactiveObject

View File

@@ -1,3 +1,6 @@
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.Models;
[Serializable]

View File

@@ -71,6 +71,7 @@ public class GUIItem
public bool DisplayRealTimeSpeed { get; set; }
public bool KeepOlderDedupl { get; set; }
public int AutoUpdateInterval { get; set; }
public bool EnableSecurityProtocolTls13 { get; set; }
public int TrayMenuServersLimit { get; set; } = 20;
public bool EnableHWA { get; set; } = false;
public bool EnableLog { get; set; } = true;
@@ -263,7 +264,6 @@ public class SimpleDNSItem
public bool? BlockBindingQuery { get; set; }
public string? DirectDNS { get; set; }
public string? RemoteDNS { get; set; }
public string? BootstrapDNS { get; set; }
public string? RayStrategy4Freedom { get; set; }
public string? SingboxStrategy4Direct { get; set; }
public string? SingboxStrategy4Proxy { get; set; }

View File

@@ -1,3 +1,5 @@
using SQLite;
namespace ServiceLib.Models;
[Serializable]

View File

@@ -1,3 +1,5 @@
using SQLite;
namespace ServiceLib.Models;
[Serializable]

View File

@@ -1,3 +1,5 @@
using System.Text.Json.Serialization;
namespace ServiceLib.Models;
public class GitHubReleaseAsset

View File

@@ -1,3 +1,5 @@
using SQLite;
namespace ServiceLib.Models;
[Serializable]

View File

@@ -1,3 +1,5 @@
using SQLite;
namespace ServiceLib.Models;
[Serializable]

View File

@@ -1,3 +1,6 @@
using ReactiveUI;
using SQLite;
namespace ServiceLib.Models;
[Serializable]

View File

@@ -1,3 +1,5 @@
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.Models;
[Serializable]

View File

@@ -1,3 +1,5 @@
using SQLite;
namespace ServiceLib.Models;
[Serializable]

View File

@@ -1,3 +1,5 @@
using SQLite;
namespace ServiceLib.Models;
[Serializable]

View File

@@ -1,3 +1,5 @@
using System.Text.Json.Serialization;
namespace ServiceLib.Models;
public class SingboxConfig

View File

@@ -1,3 +1,5 @@
using SQLite;
namespace ServiceLib.Models;
[Serializable]

View File

@@ -1,3 +1,5 @@
using System.Text.Json.Serialization;
namespace ServiceLib.Models;
public class V2rayConfig
@@ -215,7 +217,6 @@ public class Dns4Ray
public class DnsServer4Ray
{
public string? address { get; set; }
public int? port { get; set; }
public List<string>? domains { get; set; }
public bool? skipFallback { get; set; }
public List<string>? expectedIPs { get; set; }

View File

@@ -1,3 +1,5 @@
using System.Text.Json.Serialization;
namespace ServiceLib.Models;
/// <summary>

View File

@@ -19,7 +19,7 @@ namespace ServiceLib.Resx {
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class ResUI {
@@ -1023,15 +1023,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Test real delay 的本地化字符串。
/// </summary>
public static string menuFastRealPing {
get {
return ResourceManager.GetString("menuFastRealPing", resourceCulture);
}
}
/// <summary>
/// 查找类似 Full Config Template Setting 的本地化字符串。
/// </summary>
@@ -2526,24 +2517,6 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Bootstrap DNS 的本地化字符串。
/// </summary>
public static string TbBootstrapDNS {
get {
return ResourceManager.GetString("TbBootstrapDNS", resourceCulture);
}
}
/// <summary>
/// 查找类似 Resolve DNS server domains, requires IP 的本地化字符串。
/// </summary>
public static string TbBootstrapDNSTips {
get {
return ResourceManager.GetString("TbBootstrapDNSTips", resourceCulture);
}
}
/// <summary>
/// 查找类似 Browse 的本地化字符串。
/// </summary>
@@ -3993,6 +3966,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Enable Security Protocol TLS v1.3 (subscription/update) 的本地化字符串。
/// </summary>
public static string TbSettingsTLS13 {
get {
return ResourceManager.GetString("TbSettingsTLS13", resourceCulture);
}
}
/// <summary>
/// 查找类似 Tray right-click menu Configurations display limit 的本地化字符串。
/// </summary>

View File

@@ -747,6 +747,9 @@
<data name="TbSettingsSystemproxy" xml:space="preserve">
<value>تنظیمات پراکسی سیستم</value>
</data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>فعال کردن پروتکل امنیتی TLS نسخه 1.3 (اشتراک/به‌روزرسانی)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>محدودیت نمایش سرورهای منوی سینی کلیک راست</value>
</data>
@@ -1590,13 +1593,4 @@
<data name="TbRuleTypeTips" xml:space="preserve">
<value>You can set separate rules for Routing and DNS, or select "ALL" to apply to both</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root>

View File

@@ -747,6 +747,9 @@
<data name="TbSettingsSystemproxy" xml:space="preserve">
<value>Rendszerproxy beállítások</value>
</data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>Biztonsági protokoll TLS v1.3 engedélyezése (előfizetés/frissítés)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>Tálca jobb egérgombos menü konfigurációk megjelenítési limitje</value>
</data>
@@ -1590,13 +1593,4 @@
<data name="TbRuleType" xml:space="preserve">
<value>Rule Type</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root>

View File

@@ -747,6 +747,9 @@
<data name="TbSettingsSystemproxy" xml:space="preserve">
<value>System proxy settings</value>
</data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>Enable Security Protocol TLS v1.3 (subscription/update)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>Tray right-click menu Configurations display limit</value>
</data>
@@ -1590,13 +1593,4 @@
<data name="TbRuleType" xml:space="preserve">
<value>Rule Type</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root>

View File

@@ -747,6 +747,9 @@
<data name="TbSettingsSystemproxy" xml:space="preserve">
<value>Настройки системного прокси</value>
</data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>Включить протокол безопасности TLS v1.3 (обновление подписки)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>Лимит серверов в меню трея</value>
</data>
@@ -1590,13 +1593,4 @@
<data name="TbRuleType" xml:space="preserve">
<value>Rule Type</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root>

View File

@@ -121,7 +121,7 @@
<value>导出分享链接至剪贴板成功</value>
</data>
<data name="CheckServerSettings" xml:space="preserve">
<value>请先检查设置</value>
<value>请先检查配置文件设置</value>
</data>
<data name="ConfigurationFormatIncorrect" xml:space="preserve">
<value>配置格式不正确</value>
@@ -133,7 +133,7 @@
<value>下载开始...</value>
</data>
<data name="FailedConversionConfiguration" xml:space="preserve">
<value>转换配置失败</value>
<value>转换配置文件失败</value>
</data>
<data name="FailedGenDefaultConfiguration" xml:space="preserve">
<value>生成默认配置文件失败</value>
@@ -142,10 +142,10 @@
<value>获取默认配置失败</value>
</data>
<data name="FailedImportedCustomServer" xml:space="preserve">
<value>导入自定义配置失败</value>
<value>导入自定义配置文件失败</value>
</data>
<data name="FailedReadConfiguration" xml:space="preserve">
<value>读取配置失败</value>
<value>读取配置文件失败</value>
</data>
<data name="FillCorrectServerPort" xml:space="preserve">
<value>请填写正确格式的端口</value>
@@ -265,13 +265,13 @@
<value>请选择协议</value>
</data>
<data name="PleaseSelectServer" xml:space="preserve">
<value>请先选择配置</value>
<value>请先选择配置文件</value>
</data>
<data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>配置去重完成。原数量: {0},现数量: {1}。</value>
<value>配置文件去重完成。原数量: {0},现数量: {1}。</value>
</data>
<data name="RemoveServer" xml:space="preserve">
<value>是否确定移除?</value>
<value>是否确定移除配置文件</value>
</data>
<data name="SaveClientConfigurationIn" xml:space="preserve">
<value>客户端配置文件保存在:{0}</value>
@@ -283,10 +283,10 @@
<value>配置成功。 {0}</value>
</data>
<data name="SuccessfullyImportedCustomServer" xml:space="preserve">
<value>成功导入自定义配置</value>
<value>成功导入自定义配置文件</value>
</data>
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
<value>成功从剪贴板导入 {0} 个配置</value>
<value>成功从剪贴板导入 {0} 个配置文件</value>
</data>
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
<value>扫描导入分享链接成功</value>
@@ -385,7 +385,7 @@
<value>所有</value>
</data>
<data name="FillServerAddressCustom" xml:space="preserve">
<value>请浏览导入配置</value>
<value>请浏览导入配置文件配置</value>
</data>
<data name="Speedtesting" xml:space="preserve">
<value>测试中...</value>
@@ -397,7 +397,7 @@
<value>本地</value>
</data>
<data name="MsgServerTitle" xml:space="preserve">
<value>过滤器,按回车执行</value>
<value>配置文件过滤器,按回车执行</value>
</data>
<data name="menuCheckUpdate" xml:space="preserve">
<value>检查更新</value>
@@ -478,55 +478,55 @@
<value>扫描屏幕上的二维码 (Ctrl+S)</value>
</data>
<data name="menuCopyServer" xml:space="preserve">
<value>克隆所选</value>
<value>克隆所选配置文件</value>
</data>
<data name="menuRemoveDuplicateServer" xml:space="preserve">
<value>移除重复</value>
<value>移除重复的配置文件</value>
</data>
<data name="menuRemoveServer" xml:space="preserve">
<value>移除所选 (多选) (Delete)</value>
<value>移除所选配置文件 (多选) (Delete)</value>
</data>
<data name="menuSetDefaultServer" xml:space="preserve">
<value>设为活动 (Enter)</value>
<value>设为活动配置文件 (Enter)</value>
</data>
<data name="menuClearServerStatistics" xml:space="preserve">
<value>清除所有服务统计数据</value>
</data>
<data name="menuRealPingServer" xml:space="preserve">
<value>测试真连接延迟 (多选) (Ctrl+R)</value>
<value>测试配置文件真连接延迟 (多选) (Ctrl+R)</value>
</data>
<data name="menuSortServerResult" xml:space="preserve">
<value>按测试结果排序</value>
</data>
<data name="menuSpeedServer" xml:space="preserve">
<value>测试速度 (多选) (Ctrl+T)</value>
<value>测试配置文件速度 (多选) (Ctrl+T)</value>
</data>
<data name="menuTcpingServer" xml:space="preserve">
<value>测试延迟 Tcping (多选) (Ctrl+O)</value>
<value>测试配置文件延迟 Tcping (多选) (Ctrl+O)</value>
</data>
<data name="menuExport2ClientConfig" xml:space="preserve">
<value>导出所选完整配置</value>
<value>导出所选配置文件完整配置</value>
</data>
<data name="menuExport2ShareUrl" xml:space="preserve">
<value>导出分享链接至剪贴板 (多选) (Ctrl+C)</value>
</data>
<data name="menuAddCustomServer" xml:space="preserve">
<value>添加自定义配置</value>
<value>添加自定义配置文件</value>
</data>
<data name="menuAddShadowsocksServer" xml:space="preserve">
<value>添加 [Shadowsocks]</value>
<value>添加 [Shadowsocks] 配置文件</value>
</data>
<data name="menuAddSocksServer" xml:space="preserve">
<value>添加 [SOCKS] </value>
<value>添加 [SOCKS] 配置文件</value>
</data>
<data name="menuAddTrojanServer" xml:space="preserve">
<value>添加 [Trojan] </value>
<value>添加 [Trojan] 配置文件</value>
</data>
<data name="menuAddVlessServer" xml:space="preserve">
<value>添加 [VLESS] </value>
<value>添加 [VLESS] 配置文件</value>
</data>
<data name="menuAddVmessServer" xml:space="preserve">
<value>添加 [VMess] </value>
<value>添加 [VMess] 配置文件</value>
</data>
<data name="menuSelectAll" xml:space="preserve">
<value>全选 (Ctrl+A)</value>
@@ -691,7 +691,7 @@
<value>Outbound Freedom domainStrategy</value>
</data>
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
<value>自动调整配置列宽在更新订阅后</value>
<value>自动调整配置文件列宽在更新订阅后</value>
</data>
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>检查 Pre-Release 更新 (请谨慎启用)</value>
@@ -700,7 +700,7 @@
<value>例外</value>
</data>
<data name="TbSettingsExceptionTip" xml:space="preserve">
<value>例外:对于下列字符开头的地址,不使用代理配置。使用分号 (;) 分隔。</value>
<value>例外:对于下列字符开头的地址,不使用代理配置文件。使用分号 (;) 分隔。</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>显示实时速度 (需重启)</value>
@@ -747,8 +747,11 @@
<data name="TbSettingsSystemproxy" xml:space="preserve">
<value>系统代理设置</value>
</data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>启用安全协议 TLS v1.3 (订阅/检查更新)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>托盘右键菜单配置展示数量限制</value>
<value>托盘右键菜单配置文件展示数量限制</value>
</data>
<data name="TbSettingsUdpEnabled" xml:space="preserve">
<value>开启 UDP</value>
@@ -781,7 +784,7 @@
<value>Pac 模式</value>
</data>
<data name="menuShareServer" xml:space="preserve">
<value>分享 (Ctrl+F)</value>
<value>分享配置文件 (Ctrl+F)</value>
</data>
<data name="menuRouting" xml:space="preserve">
<value>路由</value>
@@ -913,7 +916,7 @@
<value>移至订阅分组</value>
</data>
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
<value>启用配置拖放排序 (需重启)</value>
<value>启用配置文件拖放排序 (需重启)</value>
</data>
<data name="TbAutoRefresh" xml:space="preserve">
<value>自动刷新</value>
@@ -922,10 +925,10 @@
<value>跳过测试</value>
</data>
<data name="menuEditServer" xml:space="preserve">
<value>编辑 (Ctrl+D)</value>
<value>编辑配置文件 (Ctrl+D)</value>
</data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>主界面双击设为活动</value>
<value>主界面双击设为活动配置文件</value>
</data>
<data name="SpeedtestingCompleted" xml:space="preserve">
<value>测试完成</value>
@@ -1030,7 +1033,7 @@
<value>Domain</value>
</data>
<data name="menuAddHysteria2Server" xml:space="preserve">
<value>添加 [Hysteria2] </value>
<value>添加 [Hysteria2] 配置文件</value>
</data>
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
<value>Hysteria 最大带宽 (Up/Dw)</value>
@@ -1039,19 +1042,19 @@
<value>使用系统 hosts</value>
</data>
<data name="menuAddTuicServer" xml:space="preserve">
<value>添加 [TUIC] </value>
<value>添加 [TUIC] 配置文件</value>
</data>
<data name="TbHeaderType8" xml:space="preserve">
<value>拥塞控制算法</value>
</data>
<data name="LvPrevProfile" xml:space="preserve">
<value>前置代理配置别名</value>
<value>前置代理配置文件别名</value>
</data>
<data name="LvNextProfile" xml:space="preserve">
<value>落地代理配置別名</value>
<value>落地代理配置文件別名</value>
</data>
<data name="LvPrevProfileTip" xml:space="preserve">
<value>请确保配置别名存在并唯一</value>
<value>请确保配置文件别名存在并唯一</value>
</data>
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
<value>自动路由</value>
@@ -1072,7 +1075,7 @@
<value>启用 IPv6</value>
</data>
<data name="menuAddWireguardServer" xml:space="preserve">
<value>添加 [WireGuard] </value>
<value>添加 [WireGuard] 配置文件</value>
</data>
<data name="TbPrivateKey" xml:space="preserve">
<value>PrivateKey</value>
@@ -1105,7 +1108,7 @@
<value>*grpc Authority</value>
</data>
<data name="menuAddHttpServer" xml:space="preserve">
<value>添加 [HTTP] </value>
<value>添加 [HTTP] 配置文件</value>
</data>
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
<value>和分组前置代理冲突</value>
@@ -1195,13 +1198,13 @@
<value>延迟测试</value>
</data>
<data name="menuProxiesDelaytestPart" xml:space="preserve">
<value>当前部分延迟测试</value>
<value>当前部分节点延迟测试</value>
</data>
<data name="menuProxiesReload" xml:space="preserve">
<value>刷新</value>
</data>
<data name="menuProxiesSelectActivity" xml:space="preserve">
<value>设为活动 (Enter)</value>
<value>设为活动节点 (Enter)</value>
</data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>Outbound 默认解析策略</value>
@@ -1219,7 +1222,7 @@
<value>导出分享链接至剪贴板 (多选) Base64 编码</value>
</data>
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
<value>导出所选完整配置至剪贴板</value>
<value>导出所选配置文件完整配置至剪贴板</value>
</data>
<data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>显示或隐藏主界面</value>
@@ -1336,7 +1339,7 @@
<value>多线程测试时的并发数量</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外:对于下列地址不使用代理配置。使用逗号 (,) 分隔。</value>
<value>例外:对于下列地址不使用代理配置文件。使用逗号 (,) 分隔。</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>流量探测类型</value>
@@ -1372,31 +1375,31 @@
<value>会覆盖端口,多组时用逗号 (,) 隔开</value>
</data>
<data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>多生成策略组</value>
<value>多配置文件生成策略组</value>
</data>
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>多随机 Xray</value>
<value>多配置文件随机 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>多负载均衡 Xray</value>
<value>多配置文件负载均衡 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>多最低延迟 Xray</value>
<value>多配置文件最低延迟 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>多最稳定 Xray</value>
<value>多配置文件最稳定 Xray</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>多最低延迟 sing-box</value>
<value>多配置文件最低延迟 sing-box</value>
</data>
<data name="menuExportConfig" xml:space="preserve">
<value>导出</value>
<value>导出配置文件</value>
</data>
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>当前连接信息测试地址</value>
</data>
<data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>可以填写配置别名,请确保存在并唯一</value>
<value>可以填写配置文件别名,请确保存在并唯一</value>
</data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>密码错误,请重试。</value>
@@ -1405,7 +1408,7 @@
<value>Mldsa65Verify</value>
</data>
<data name="menuAddAnytlsServer" xml:space="preserve">
<value>添加 [Anytls] </value>
<value>添加 [Anytls] 配置文件</value>
</data>
<data name="TbRemoteDNS" xml:space="preserve">
<value>远程 DNS</value>
@@ -1492,13 +1495,13 @@
<value>开始解析和处理订阅内容</value>
</data>
<data name="TbSelectProfile" xml:space="preserve">
<value>选择配置</value>
<value>选择配置文件</value>
</data>
<data name="TbFakeIPTips" xml:space="preserve">
<value>默认全局生效,内置 FakeIP 过滤,仅在 sing-box 中生效</value>
</data>
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
<value>请至少添加一个配置</value>
<value>请至少添加一个配置文件</value>
</data>
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
<value>策略组</value>
@@ -1522,28 +1525,28 @@
<value>策略组类型</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>添加策略组</value>
<value>添加策略组配置文件</value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>添加链式代理</value>
<value>添加链式代理配置文件</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>添加子</value>
<value>添加子配置文件</value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>删除子</value>
<value>删除子配置文件</value>
</data>
<data name="menuServerList" xml:space="preserve">
<value>子项列表</value>
<value>服务器列表</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>故障转移</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>多故障转移 sing-box</value>
<value>多配置文件故障转移 sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>多故障转移 Xray</value>
<value>多配置文件故障转移 Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve">
<value>核心 '{0}' 不支持网络类型 '{1}'。</value>
@@ -1564,10 +1567,10 @@
<value>策略组: </value>
</data>
<data name="NodeTagNotExist" xml:space="preserve">
<value>别名 '{0}' 不存在。</value>
<value>节点别名 '{0}' 不存在。</value>
</data>
<data name="GroupEmpty" xml:space="preserve">
<value>组“{0}”为空。请至少添加一个配置。</value>
<value>组“{0}”为空。请至少添加一个节点。</value>
</data>
<data name="InvalidProperty" xml:space="preserve">
<value>{0}属性无效,请检查</value>
@@ -1587,13 +1590,4 @@
<data name="TbRuleTypeTips" xml:space="preserve">
<value>可对 Routing 和 DNS 单独设定规则ALL 则都生效</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>解析 DNS 服务器域名,需指定为 IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>一键测试真连接延迟</value>
</data>
</root>

View File

@@ -121,7 +121,7 @@
<value>匯出分享連結至剪貼簿成功</value>
</data>
<data name="CheckServerSettings" xml:space="preserve">
<value>請先檢查設定</value>
<value>請先檢查設定檔設定</value>
</data>
<data name="ConfigurationFormatIncorrect" xml:space="preserve">
<value>設定格式不正確</value>
@@ -133,7 +133,7 @@
<value>下載開始...</value>
</data>
<data name="FailedConversionConfiguration" xml:space="preserve">
<value>轉換設定失敗</value>
<value>轉換設定失敗</value>
</data>
<data name="FailedGenDefaultConfiguration" xml:space="preserve">
<value>生成預設設定檔失敗</value>
@@ -142,10 +142,10 @@
<value>獲取預設設定失敗</value>
</data>
<data name="FailedImportedCustomServer" xml:space="preserve">
<value>匯入自訂設定失敗</value>
<value>匯入自訂設定設定檔失敗</value>
</data>
<data name="FailedReadConfiguration" xml:space="preserve">
<value>讀取設定失敗</value>
<value>讀取設定失敗</value>
</data>
<data name="FillCorrectServerPort" xml:space="preserve">
<value>請填寫正確格式的埠</value>
@@ -265,13 +265,13 @@
<value>請選擇協定</value>
</data>
<data name="PleaseSelectServer" xml:space="preserve">
<value>請先選擇設定</value>
<value>請先選擇設定</value>
</data>
<data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>去重完成。原數量: {0},現數量: {1}。</value>
<value>設定檔去重完成。原數量: {0},現數量: {1}。</value>
</data>
<data name="RemoveServer" xml:space="preserve">
<value>是否確定移除?</value>
<value>是否確定移除設定檔</value>
</data>
<data name="SaveClientConfigurationIn" xml:space="preserve">
<value>用戶端設定檔儲存在:{0}</value>
@@ -283,10 +283,10 @@
<value>設定成功。{0}</value>
</data>
<data name="SuccessfullyImportedCustomServer" xml:space="preserve">
<value>成功匯入自訂節點</value>
<value>成功匯入自訂設定設定檔</value>
</data>
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
<value>成功從剪貼簿匯入 {0} 個節點</value>
<value>成功從剪貼簿匯入 {0} 個設定檔</value>
</data>
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
<value>掃描匯入分享連結成功</value>
@@ -385,7 +385,7 @@
<value>所有</value>
</data>
<data name="FillServerAddressCustom" xml:space="preserve">
<value>請瀏覽匯入設定</value>
<value>請瀏覽匯入設定檔設定</value>
</data>
<data name="Speedtesting" xml:space="preserve">
<value>測試中...</value>
@@ -397,7 +397,7 @@
<value>本機</value>
</data>
<data name="MsgServerTitle" xml:space="preserve">
<value>過濾,按 Enter 執行</value>
<value>設定檔過濾,按 Enter 執行</value>
</data>
<data name="menuCheckUpdate" xml:space="preserve">
<value>檢查更新</value>
@@ -478,55 +478,55 @@
<value>掃描螢幕上的二維碼 (Ctrl+S)</value>
</data>
<data name="menuCopyServer" xml:space="preserve">
<value>複製所選</value>
<value>複製所選設定檔</value>
</data>
<data name="menuRemoveDuplicateServer" xml:space="preserve">
<value>移除重複</value>
<value>移除重複的設定檔</value>
</data>
<data name="menuRemoveServer" xml:space="preserve">
<value>移除所選 (多選) (Delete)</value>
<value>移除所選設定檔 (多選) (Delete)</value>
</data>
<data name="menuSetDefaultServer" xml:space="preserve">
<value>設為活動 (Enter)</value>
<value>設為活動設定檔 (Enter)</value>
</data>
<data name="menuClearServerStatistics" xml:space="preserve">
<value>清除所有服務統計資料</value>
</data>
<data name="menuRealPingServer" xml:space="preserve">
<value>測試真連線延遲 (多選) (Ctrl+R)</value>
<value>測試設定檔真連線延遲 (多選) (Ctrl+R)</value>
</data>
<data name="menuSortServerResult" xml:space="preserve">
<value>按測試結果排序</value>
</data>
<data name="menuSpeedServer" xml:space="preserve">
<value>測試速度 (多選) (Ctrl+T)</value>
<value>測試設定檔速度 (多選) (Ctrl+T)</value>
</data>
<data name="menuTcpingServer" xml:space="preserve">
<value>測試延遲 Tcping (多選) (Ctrl+O)</value>
<value>測試設定檔延遲 Tcping (多選) (Ctrl+O)</value>
</data>
<data name="menuExport2ClientConfig" xml:space="preserve">
<value>匯出所選完整設定</value>
<value>匯出所選設定檔完整設定</value>
</data>
<data name="menuExport2ShareUrl" xml:space="preserve">
<value>匯出分享連結至剪貼簿 (多選) (Ctrl+C)</value>
</data>
<data name="menuAddCustomServer" xml:space="preserve">
<value>新增自訂節點</value>
<value>新增自訂設定設定檔</value>
</data>
<data name="menuAddShadowsocksServer" xml:space="preserve">
<value>新增 [Shadowsocks] 節點</value>
<value>新增 [Shadowsocks] 設定檔</value>
</data>
<data name="menuAddSocksServer" xml:space="preserve">
<value>新增 [SOCKS] 節點</value>
<value>新增 [SOCKS] 設定檔</value>
</data>
<data name="menuAddTrojanServer" xml:space="preserve">
<value>新增 [Trojan] 節點</value>
<value>新增 [Trojan] 設定檔</value>
</data>
<data name="menuAddVlessServer" xml:space="preserve">
<value>新增 [VLESS] 節點</value>
<value>新增 [VLESS] 設定檔</value>
</data>
<data name="menuAddVmessServer" xml:space="preserve">
<value>新增 [VMess] 節點</value>
<value>新增 [VMess] 設定檔</value>
</data>
<data name="menuSelectAll" xml:space="preserve">
<value>全選 (Ctrl+A)</value>
@@ -676,7 +676,7 @@
<value>Core: 基礎設定</value>
</data>
<data name="TbCustomDnsRay" xml:space="preserve">
<value>v2ray 自訂 DNS</value>
<value>V2ray Custom DNS</value>
</data>
<data name="TbSettingsCoreKcp" xml:space="preserve">
<value>Core: KCP 設定</value>
@@ -691,7 +691,7 @@
<value>Outbound Freedom domainStrategy</value>
</data>
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
<value>在更新訂閱後自動調整列寬</value>
<value>在更新訂閱後自動調整設定檔列寬</value>
</data>
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>檢查 Pre-Release 更新 (請謹慎啟用)</value>
@@ -700,7 +700,7 @@
<value>例外</value>
</data>
<data name="TbSettingsExceptionTip" xml:space="preserve">
<value>例外:對於下列字元開頭的位址,不使用代理。使用分號 (;) 分隔。</value>
<value>例外:對於下列字元開頭的位址,不使用代理設定檔。使用分號 (;) 分隔。</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>顯示即時速度(需重啟)</value>
@@ -747,8 +747,11 @@
<data name="TbSettingsSystemproxy" xml:space="preserve">
<value>系統代理設定</value>
</data>
<data name="TbSettingsTLS13" xml:space="preserve">
<value>啟用安全協定 TLS v1.3 (訂閱/檢查更新)</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>工具列右鍵選單設定展示數量限制</value>
<value>工具列右鍵選單設定展示數量限制</value>
</data>
<data name="TbSettingsUdpEnabled" xml:space="preserve">
<value>開啟 UDP</value>
@@ -781,7 +784,7 @@
<value>PAC 模式</value>
</data>
<data name="menuShareServer" xml:space="preserve">
<value>分享 (Ctrl+F)</value>
<value>分享設定檔 (Ctrl+F)</value>
</data>
<data name="menuRouting" xml:space="preserve">
<value>路由</value>
@@ -883,7 +886,7 @@
<value>請勿將代理伺服器用於本機Intranet位址</value>
</data>
<data name="menuMixedTestServer" xml:space="preserve">
<value>一鍵延遲速度測試 (Ctrl+E)</value>
<value>一鍵多執行緒測試延遲速度 (Ctrl+E)</value>
</data>
<data name="LvTestDelay" xml:space="preserve">
<value>延遲 (ms)</value>
@@ -913,7 +916,7 @@
<value>移至訂閱分組</value>
</data>
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
<value>啟拖放排序 (需重啟)</value>
<value>啟動設定檔拖放排序 (需重啟)</value>
</data>
<data name="TbAutoRefresh" xml:space="preserve">
<value>自動重新整理</value>
@@ -922,10 +925,10 @@
<value>跳過測試</value>
</data>
<data name="menuEditServer" xml:space="preserve">
<value>編輯 (Ctrl+D)</value>
<value>編輯設定檔 (Ctrl+D)</value>
</data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>主介面輕按兩下設為活動</value>
<value>主介面輕按兩下設為活動設定檔</value>
</data>
<data name="SpeedtestingCompleted" xml:space="preserve">
<value>測試完成</value>
@@ -943,7 +946,7 @@
<value>目前字型 (需重啟)</value>
</data>
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
<value>複製字型 TTF/TTC 檔案到目錄 guiFonts新啟動後生效</value>
<value>複製字型 TTF/TTC 檔案到目錄 guiFonts啟設定</value>
</data>
<data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>Pac 連接埠 = +3Xray API 連接埠 = +4mihomo API 連接埠 = +5</value>
@@ -1003,10 +1006,10 @@
<value>不需要轉換時請留空</value>
</data>
<data name="menuDNSSetting" xml:space="preserve">
<value>DNS設定</value>
<value>DNS 設定</value>
</data>
<data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box 自訂 DNS</value>
<value>sing-box Custom DNS</value>
</data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>請填寫 DNS JSON 結構,點擊查看檔案</value>
@@ -1030,7 +1033,7 @@
<value>Domain</value>
</data>
<data name="menuAddHysteria2Server" xml:space="preserve">
<value>新增 [Hysteria2] 節點</value>
<value>添加 [Hysteria2] 設定檔</value>
</data>
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
<value>Hysteria 最大頻寬 (Up/Dw)</value>
@@ -1039,19 +1042,19 @@
<value>使用系統 hosts</value>
</data>
<data name="menuAddTuicServer" xml:space="preserve">
<value>新增 [TUIC] 節點</value>
<value>新增 [TUIC] 設定檔</value>
</data>
<data name="TbHeaderType8" xml:space="preserve">
<value>擁塞控制算法</value>
</data>
<data name="LvPrevProfile" xml:space="preserve">
<value>前置代理節點別名</value>
<value>前置代理設定檔別名</value>
</data>
<data name="LvNextProfile" xml:space="preserve">
<value>落地代理節點別名</value>
<value>落地代理設定檔別名</value>
</data>
<data name="LvPrevProfileTip" xml:space="preserve">
<value>請確保節點別名存在並且唯一</value>
<value>請確保設定檔別名存在並且唯一</value>
</data>
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
<value>自動路由</value>
@@ -1072,7 +1075,7 @@
<value>啟用 IPv6</value>
</data>
<data name="menuAddWireguardServer" xml:space="preserve">
<value>新增 [WireGuard] 節點</value>
<value>添加 [WireGuard] 設定檔</value>
</data>
<data name="TbPrivateKey" xml:space="preserve">
<value>PrivateKey</value>
@@ -1105,7 +1108,7 @@
<value>*grpc Authority</value>
</data>
<data name="menuAddHttpServer" xml:space="preserve">
<value>新增 [HTTP] 節點</value>
<value>新增 [HTTP] 設定檔</value>
</data>
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
<value>和分組前置代理衝突</value>
@@ -1219,13 +1222,13 @@
<value>匯出分享連結至剪貼簿 (多選) Base64 編碼</value>
</data>
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
<value>匯出所選完整設定至剪貼簿</value>
<value>匯出所選設定檔完整設定至剪貼簿</value>
</data>
<data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>顯示或隱藏主介面</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>自訂 Socks 連接埠</value>
<value>自訂設定的 Socks 連接埠</value>
</data>
<data name="menuBackupAndRestore" xml:space="preserve">
<value>備份和還原</value>
@@ -1309,7 +1312,7 @@
<value>請不要使用不安全的 HTTP 協定訂閱位址</value>
</data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>安裝字體到系統中,選擇或填入字體名稱,重新啟動後生效</value>
<value>安裝字體到系統中,選擇或填入字體名稱,重新啟動設定</value>
</data>
<data name="menuExitTips" xml:space="preserve">
<value>是否確定退出?</value>
@@ -1336,7 +1339,7 @@
<value>多執行緒測試時的並發數量</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外:對於下列位址不使用代理,使用逗號 (,) 分隔。</value>
<value>例外:對於下列位址不使用代理設定檔,使用逗號 (,) 分隔。</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>流量探測類型</value>
@@ -1372,31 +1375,31 @@
<value>會覆蓋埠,多組時用逗號 (,) 隔開</value>
</data>
<data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>多選生成策略組</value>
<value>Generate Policy Group from Multiple Profiles</value>
</data>
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>多隨機 Xray</value>
<value>多設定檔隨機 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>多負載平衡 Xray</value>
<value>多設定檔負載平衡 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>多最低延遲 Xray</value>
<value>多設定檔最低延遲 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>多最穩定 Xray</value>
<value>多設定檔最穩定 Xray</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>多最低延遲 sing-box</value>
<value>多設定檔最低延遲 sing-box</value>
</data>
<data name="menuExportConfig" xml:space="preserve">
<value>匯出</value>
<value>匯出設定檔</value>
</data>
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>目前連接資訊測試地址</value>
</data>
<data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>可以填寫節點別名,請確保存在並唯一</value>
<value>可以填寫設定檔別名,請確保存在並唯一</value>
</data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>密碼錯誤,請重試。</value>
@@ -1405,195 +1408,186 @@
<value>Mldsa65Verify</value>
</data>
<data name="menuAddAnytlsServer" xml:space="preserve">
<value>新增 [Anytls] 節點</value>
<value>新增 [Anytls] 設定檔</value>
</data>
<data name="TbRemoteDNS" xml:space="preserve">
<value>遠程 DNS</value>
<value>Remote DNS</value>
</data>
<data name="TbDomesticDNS" xml:space="preserve">
<value>直連 DNS</value>
<value>Domestic DNS</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>通过代理,请确保远程可用</value>
<value>Via proxy — please ensure remote availability</value>
</data>
<data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>xray freedom 解析策略</value>
<value>xray Freedom Resolution Strategy</value>
</data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
<value>sing-box 直連解析策略</value>
<value>sing-box Direct Resolution Strategy</value>
</data>
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
<value>sing-box 遠程解析策略</value>
<value>sing-box Remote Resolution Strategy</value>
</data>
<data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>新增常用 DNS Hosts</value>
<value>Add Common DNS Hosts</value>
</data>
<data name="TbFakeIP" xml:space="preserve">
<value>FakeIP</value>
</data>
<data name="TbBlockSVCBHTTPSQueries" xml:space="preserve">
<value>阻止 SVCB HTTPS 查詢</value>
<value>Block SVCB and HTTPS Queries</value>
</data>
<data name="TbDNSHostsConfig" xml:space="preserve">
<value>DNS Hosts“網域名稱1 ip1 ip2” 一行一個)</value>
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
</data>
<data name="ThBasicDNSSettings" xml:space="preserve">
<value>DNS 基礎設定</value>
<value>Basic DNS Settings</value>
</data>
<data name="ThAdvancedDNSSettings" xml:space="preserve">
<value>DNS 進階設定</value>
<value>Advanced DNS Settings</value>
</data>
<data name="TbValidateDirectExpectedIPs" xml:space="preserve">
<value>校驗相應地區域名 IP</value>
<value>Validate Regional Domain IPs</value>
</data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>配置後,會對相應地區域名(如 geosite:cn的返回 IP 進行校驗,僅返回期望 IP</value>
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
</data>
<data name="TbCustomDNSEnable" xml:space="preserve">
<value>啟用自訂 DNS</value>
<value>Enable Custom DNS</value>
</data>
<data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve">
<value>自訂 DNS 已啟用,此頁面配置將無效</value>
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
</data>
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
<value>開啟後將阻止 ECH HTTP/3 可用性查詢</value>
<value>Block ECH and HTTP/3 availability checks when enabled</value>
</data>
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
<value>請填寫正確的配置範本</value>
<value>Please fill in the correct config template</value>
</data>
<data name="menuFullConfigTemplate" xml:space="preserve">
<value>完整配置範本設定</value>
<value>Full Config Template Setting</value>
</data>
<data name="TbFullConfigTemplateEnable" xml:space="preserve">
<value>啟用完整配置範本</value>
<value>Enable Full Config Template</value>
</data>
<data name="TbRayFullConfigTemplate" xml:space="preserve">
<value>v2ray 完整配置範本</value>
<value>v2ray Full Config Template</value>
</data>
<data name="TbRayFullConfigTemplateDesc" xml:space="preserve">
<value>僅添加出站配置,routing.balancers routing.rules.outboundTag,點擊查看文檔</value>
<value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag, Click to view the document</value>
</data>
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
<value>不添加非代理協定出站</value>
<value>Do Not Add Non-Proxy Protocol Outbound</value>
</data>
<data name="TbSetUpstreamProxyDetour" xml:space="preserve">
<value>設定上游代理 tag</value>
<value>Set Upstream Proxy Tag</value>
</data>
<data name="TbSBFullConfigTemplate" xml:space="preserve">
<value>sing-box 完整配置範本</value>
<value>sing-box Full Config Template</value>
</data>
<data name="TbSBFullConfigTemplateDesc" xml:space="preserve">
<value>僅添加出站和端點配置,點擊查看文檔</value>
<value>Add Outbound and Endpoint Config Only, Click to view the document</value>
</data>
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
<value>此功能供高級用戶和有特殊需求的用戶使用。 啟用此功能後,將忽略 Core 的基礎設定DNS 設定 ,路由設定。你需要保證系統代理的埠和流量統計等功能的配置正確,一切都由你來設定。</value>
<value>This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core's basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you.</value>
</data>
<data name="MsgStartParsingSubscription" xml:space="preserve">
<value>開始解析和處理訂閱內容</value>
</data>
<data name="TbSelectProfile" xml:space="preserve">
<value>選擇節點</value>
<value>Select Profile</value>
</data>
<data name="TbFakeIPTips" xml:space="preserve">
<value>默認全域生效,內置 FakeIP 過濾,僅在 sing-box 中生效</value>
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
</data>
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
<value>請至少添加一個節點</value>
<value>Please Add At Least One Configuration</value>
</data>
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
<value>策略組</value>
<value>Policy Group</value>
</data>
<data name="TbConfigTypeProxyChain" xml:space="preserve">
<value>鏈式代理</value>
<value>Proxy Chain</value>
</data>
<data name="TbLeastPing" xml:space="preserve">
<value>最低延遲</value>
<value>Lowest Latency</value>
</data>
<data name="TbRandom" xml:space="preserve">
<value>隨機</value>
<value>Random</value>
</data>
<data name="TbRoundRobin" xml:space="preserve">
<value>負載均衡</value>
<value>Round Robin</value>
</data>
<data name="TbLeastLoad" xml:space="preserve">
<value>最穩定</value>
<value>Most Stable</value>
</data>
<data name="TbPolicyGroupType" xml:space="preserve">
<value>策略組類型</value>
<value>Policy Group Type</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>添加策略組</value>
<value>Add Policy Group Configuration</value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>添加鏈式代理</value>
<value>Add Proxy Chain Configuration</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>添加子項</value>
<value>Add Child Configuration</value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>刪除子項</value>
<value>Remove Child Configuration</value>
</data>
<data name="menuServerList" xml:space="preserve">
<value>子項清單</value>
<value>Server List</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>容錯移轉</value>
<value>Fallback</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>多選容錯移轉 sing-box</value>
<value>Multi-Configuration Fallback by sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>多選容錯移轉 Xray</value>
<value>Multi-Configuration Fallback by Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve">
<value>核心 '{0}' 不支援網路類型 '{1}'.</value>
<value>Core '{0}' does not support network type '{1}'.</value>
</data>
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
<value>核心 '{0}' 在使用傳輸方式 '{2}' 時不支援協定 '{1}'.</value>
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
</data>
<data name="CoreNotSupportProtocol" xml:space="preserve">
<value>核心 '{0}' 不支援協定 '{1}'.</value>
<value>Core '{0}' does not support protocol '{1}'.</value>
</data>
<data name="ProxyChainedPrefix" xml:space="preserve">
<value>代理鏈: </value>
<value>Proxy chained: </value>
</data>
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
<value>路由規則出站: </value>
<value>Routing rule outbound: </value>
</data>
<data name="PolicyGroupPrefix" xml:space="preserve">
<value>策略組: </value>
<value>Policy group: </value>
</data>
<data name="NodeTagNotExist" xml:space="preserve">
<value>別名 '{0}' 不存在。</value>
<value>Node alias '{0}' does not exist.</value>
</data>
<data name="GroupEmpty" xml:space="preserve">
<value>組“{0}”為空.請至少添加一個配置。</value>
<value>Group '{0}' is empty. Please add at least one node.</value>
</data>
<data name="InvalidProperty" xml:space="preserve">
<value>{0}屬性無效,請檢查</value>
<value>The {0} property is invalid, please check.</value>
</data>
<data name="GroupSelfReference" xml:space="preserve">
<value>{0} 分組不能引用自身或循環引用</value>
</data>
<data name="NotSupportProtocol" xml:space="preserve">
<value>不支援協定 '{0}'.</value>
<value>Not support protocol '{0}'.</value>
</data>
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
<value>如果系統沒有盤功能,請不要開啟</value>
<value>如果系統沒有盤功能,請不要開啟</value>
</data>
<data name="TbRuleType" xml:space="preserve">
<value>規則類型</value>
<value>规则类型</value>
</data>
<data name="TbRuleTypeTips" xml:space="preserve">
<value>可 Routing 和 DNS 單獨設定規則ALL 都生效</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>解析 DNS 伺服器網域名稱,需指定為 IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>一鍵測試真連線延遲</value>
<value>可 Routing 和 DNS 单独设定规则ALL 都生效</value>
</data>
</root>

View File

@@ -1,3 +1,7 @@
using System.Net;
using System.Net.NetworkInformation;
using ServiceLib.Common;
namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigSingboxService(Config config)

View File

@@ -1,3 +1,5 @@
using System.Text.Json.Nodes;
namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigSingboxService

View File

@@ -138,7 +138,12 @@ public partial class CoreConfigSingboxService
private async Task<Server4Sbox> GenDnsDomains(SingboxConfig singboxConfig, SimpleDNSItem? simpleDNSItem)
{
var finalDns = ParseDnsAddress(simpleDNSItem.BootstrapDNS);
var finalDnsAddress = "local";
if (_config.TunModeItem.EnableTun)
{
finalDnsAddress = "dhcp://auto";
}
var finalDns = ParseDnsAddress(finalDnsAddress);
finalDns.tag = Global.SingboxLocalDNSTag;
singboxConfig.dns ??= new Dns4Sbox();
singboxConfig.dns.servers ??= new List<Server4Sbox>();
@@ -454,41 +459,79 @@ public partial class CoreConfigSingboxService
return server;
}
var (domain, scheme, port, path) = Utils.ParseUrl(addressFirst);
if (scheme.Equals("dhcp", StringComparison.OrdinalIgnoreCase))
if (addressFirst.StartsWith("dhcp://", StringComparison.OrdinalIgnoreCase))
{
var interface_name = addressFirst.Substring(7);
server.type = "dhcp";
if ((!domain.IsNullOrEmpty()) && domain != "auto")
{
server.server = domain;
}
server.Interface = interface_name == "auto" ? null : interface_name;
return server;
}
if (scheme.IsNullOrEmpty())
if (!addressFirst.Contains("://"))
{
// udp dns
server.type = "udp";
}
else
{
// server.type = scheme.ToLower();
// remove "+local" suffix
// TODO: "+local" suffix decide server.detour = "direct" ?
server.type = scheme.Replace("+local", "", StringComparison.OrdinalIgnoreCase).ToLower();
server.server = addressFirst;
return server;
}
server.server = domain;
if (port != 0)
try
{
server.server_port = port;
var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal);
server.type = addressFirst.Substring(0, protocolEndIndex).ToLower();
var uri = new Uri(addressFirst);
server.server = uri.Host;
if (!uri.IsDefaultPort)
{
server.server_port = uri.Port;
}
if ((server.type == "https" || server.type == "h3") && !string.IsNullOrEmpty(uri.AbsolutePath) && uri.AbsolutePath != "/")
{
server.path = uri.AbsolutePath;
}
}
if ((server.type == "https" || server.type == "h3") && !string.IsNullOrEmpty(path) && path != "/")
catch (UriFormatException)
{
server.path = path;
var protocolEndIndex = addressFirst.IndexOf("://", StringComparison.Ordinal);
if (protocolEndIndex > 0)
{
server.type = addressFirst.Substring(0, protocolEndIndex).ToLower();
var remaining = addressFirst.Substring(protocolEndIndex + 3);
var portIndex = remaining.IndexOf(':');
var pathIndex = remaining.IndexOf('/');
if (portIndex > 0)
{
server.server = remaining.Substring(0, portIndex);
var portPart = pathIndex > portIndex
? remaining.Substring(portIndex + 1, pathIndex - portIndex - 1)
: remaining.Substring(portIndex + 1);
if (int.TryParse(portPart, out var parsedPort))
{
server.server_port = parsedPort;
}
}
else if (pathIndex > 0)
{
server.server = remaining.Substring(0, pathIndex);
}
else
{
server.server = remaining;
}
if (pathIndex > 0 && (server.type == "https" || server.type == "h3"))
{
server.path = remaining.Substring(pathIndex);
}
}
}
return server;
}
}

View File

@@ -294,7 +294,7 @@ public partial class CoreConfigSingboxService
var tls = new Tls4Sbox()
{
enabled = true,
record_fragment = _config.CoreBasicItem.EnableFragment ? true : null,
record_fragment = _config.CoreBasicItem.EnableFragment,
server_name = server_name,
insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
alpn = node.GetAlpn(),

View File

@@ -1,3 +1,6 @@
using System.Net;
using System.Net.NetworkInformation;
namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigV2rayService(Config config)

View File

@@ -1,3 +1,5 @@
using System.Text.Json.Nodes;
namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigV2rayService
@@ -86,8 +88,8 @@ public partial class CoreConfigV2rayService
}
}
var customOutboundsNode = new JsonArray();
// Handle outbounds - append instead of override
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
foreach (var outbound in v2rayConfig.outbounds)
{
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
@@ -97,30 +99,14 @@ public partial class CoreConfigV2rayService
continue;
}
}
else if ((!fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
&& ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() ?? true) == true))
else if ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == true) && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) && !(Utils.IsPrivateNetwork(outbound.settings?.servers?.FirstOrDefault()?.address ?? string.Empty) || Utils.IsPrivateNetwork(outbound.settings?.vnext?.FirstOrDefault()?.address ?? string.Empty)))
{
var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address
?? outbound.settings?.vnext?.FirstOrDefault()?.address
?? string.Empty;
if (!Utils.IsPrivateNetwork(outboundAddress))
{
outbound.streamSettings ??= new StreamSettings4Ray();
outbound.streamSettings.sockopt ??= new Sockopt4Ray();
outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour;
}
outbound.streamSettings ??= new StreamSettings4Ray();
outbound.streamSettings.sockopt ??= new Sockopt4Ray();
outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour;
}
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
}
if (fullConfigTemplateNode["outbounds"] is JsonArray templateOutbounds)
{
foreach (var outbound in templateOutbounds)
{
customOutboundsNode.Add(outbound?.DeepClone());
}
}
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));

View File

@@ -1,3 +1,7 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace ServiceLib.Services.CoreConfig;
public partial class CoreConfigV2rayService
@@ -79,23 +83,9 @@ public partial class CoreConfigV2rayService
static object CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
{
var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
var domainFinal = dnsAddress;
int? portFinal = null;
if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
{
domainFinal = domain;
portFinal = port > 0 ? port : null;
}
else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase))
{
domainFinal = scheme + "://" + domain;
portFinal = port > 0 ? port : null;
}
var dnsServer = new DnsServer4Ray
{
address = domainFinal,
port = portFinal,
address = dnsAddress,
skipFallback = true,
domains = domains.Count > 0 ? domains : null,
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
@@ -117,35 +107,6 @@ public partial class CoreConfigV2rayService
var expectedIPs = new List<string>();
var regionNames = new HashSet<string>();
var bootstrapDNSAddress = ParseDnsAddresses(simpleDNSItem?.BootstrapDNS, Global.DomainPureIPDNSAddress.FirstOrDefault());
var dnsServerDomains = new List<string>();
foreach (var dns in directDNSAddress)
{
var (domain, _, _, _) = Utils.ParseUrl(dns);
if (domain == "localhost")
{
continue;
}
if (Utils.IsDomain(domain))
{
dnsServerDomains.Add($"full:{domain}");
}
}
foreach (var dns in remoteDNSAddress)
{
var (domain, _, _, _) = Utils.ParseUrl(dns);
if (domain == "localhost")
{
continue;
}
if (Utils.IsDomain(domain))
{
dnsServerDomains.Add($"full:{domain}");
}
}
dnsServerDomains = dnsServerDomains.Distinct().ToList();
if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs))
{
expectedIPs = simpleDNSItem.DirectExpectedIPs
@@ -260,10 +221,6 @@ public partial class CoreConfigV2rayService
AddDnsServers(remoteDNSAddress, proxyGeositeList);
AddDnsServers(directDNSAddress, directGeositeList);
AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs);
if (dnsServerDomains.Count > 0)
{
AddDnsServers(bootstrapDNSAddress, dnsServerDomains);
}
var useDirectDns = rules?.LastOrDefault() is { } lastRule
&& lastRule.OutboundTag == Global.DirectTag

View File

@@ -1,4 +1,6 @@
using System.Net;
using System.Net.Http.Headers;
using System.Net.Sockets;
namespace ServiceLib.Services;
@@ -17,6 +19,8 @@ public class DownloadService
{
try
{
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
var progress = new Progress<string>();
progress.ProgressChanged += (sender, value) => updateFunc?.Invoke(false, $"{value}");
@@ -40,6 +44,7 @@ public class DownloadService
{
try
{
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
UpdateCompleted?.Invoke(this, new RetResult(false, $"{ResUI.Downloading} {url}"));
var progress = new Progress<double>();
@@ -66,6 +71,7 @@ public class DownloadService
public async Task<string?> UrlRedirectAsync(string url, bool blProxy)
{
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
var webRequestHandler = new SocketsHttpHandler
{
AllowAutoRedirect = false,
@@ -135,6 +141,7 @@ public class DownloadService
{
try
{
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
var webProxy = await GetWebProxy(blProxy);
var client = new HttpClient(new SocketsHttpHandler()
{
@@ -179,6 +186,8 @@ public class DownloadService
{
try
{
SetSecurityProtocol(AppManager.Instance.Config.GuiItem.EnableSecurityProtocolTls13);
var webProxy = await GetWebProxy(blProxy);
if (userAgent.IsNullOrEmpty())
@@ -229,4 +238,17 @@ public class DownloadService
return false;
}
}
private static void SetSecurityProtocol(bool enableSecurityProtocolTls13)
{
if (enableSecurityProtocolTls13)
{
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
}
else
{
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
}
ServicePointManager.DefaultConnectionLimit = 256;
}
}

View File

@@ -1,3 +1,6 @@
using System.Diagnostics;
using System.Text;
namespace ServiceLib.Services;
public class ProcessService : IDisposable

View File

@@ -1,3 +1,8 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
namespace ServiceLib.Services;
public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateFunc)

View File

@@ -1,4 +1,5 @@
using System.Net.WebSockets;
using System.Text;
namespace ServiceLib.Services.Statistics;

View File

@@ -1,3 +1,6 @@
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
namespace ServiceLib.Services;
public class UpdateService

View File

@@ -1,3 +1,8 @@
using System.Reactive;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class AddGroupServerViewModel : MyReactiveObject

View File

@@ -1,3 +1,7 @@
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class AddServer2ViewModel : MyReactiveObject

View File

@@ -1,3 +1,7 @@
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class AddServerViewModel : MyReactiveObject

View File

@@ -1,3 +1,7 @@
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class BackupAndRestoreViewModel : MyReactiveObject

View File

@@ -1,3 +1,11 @@
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class CheckUpdateViewModel : MyReactiveObject

View File

@@ -1,3 +1,11 @@
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class ClashConnectionsViewModel : MyReactiveObject

View File

@@ -1,4 +1,11 @@
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using static ServiceLib.Models.ClashProviders;
using static ServiceLib.Models.ClashProxies;

View File

@@ -1,3 +1,8 @@
using System.Reactive;
using System.Reactive.Linq;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class DNSSettingViewModel : MyReactiveObject
@@ -8,7 +13,6 @@ public class DNSSettingViewModel : MyReactiveObject
[Reactive] public bool? BlockBindingQuery { get; set; }
[Reactive] public string? DirectDNS { get; set; }
[Reactive] public string? RemoteDNS { get; set; }
[Reactive] public string? BootstrapDNS { get; set; }
[Reactive] public string? RayStrategy4Freedom { get; set; }
[Reactive] public string? SingboxStrategy4Direct { get; set; }
[Reactive] public string? SingboxStrategy4Proxy { get; set; }
@@ -69,7 +73,6 @@ public class DNSSettingViewModel : MyReactiveObject
BlockBindingQuery = item.BlockBindingQuery;
DirectDNS = item.DirectDNS;
RemoteDNS = item.RemoteDNS;
BootstrapDNS = item.BootstrapDNS;
RayStrategy4Freedom = item.RayStrategy4Freedom;
SingboxStrategy4Direct = item.SingboxStrategy4Direct;
SingboxStrategy4Proxy = item.SingboxStrategy4Proxy;
@@ -99,7 +102,6 @@ public class DNSSettingViewModel : MyReactiveObject
_config.SimpleDNSItem.BlockBindingQuery = BlockBindingQuery;
_config.SimpleDNSItem.DirectDNS = DirectDNS;
_config.SimpleDNSItem.RemoteDNS = RemoteDNS;
_config.SimpleDNSItem.BootstrapDNS = BootstrapDNS;
_config.SimpleDNSItem.RayStrategy4Freedom = RayStrategy4Freedom;
_config.SimpleDNSItem.SingboxStrategy4Direct = SingboxStrategy4Direct;
_config.SimpleDNSItem.SingboxStrategy4Proxy = SingboxStrategy4Proxy;

View File

@@ -1,3 +1,7 @@
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class FullConfigTemplateViewModel : MyReactiveObject

View File

@@ -1,3 +1,6 @@
using System.Reactive;
using ReactiveUI;
namespace ServiceLib.ViewModels;
public class GlobalHotkeySettingViewModel : MyReactiveObject

View File

@@ -1,4 +1,8 @@
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;

View File

@@ -1,3 +1,10 @@
using System.Collections.Concurrent;
using System.Reactive.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class MsgViewModel : MyReactiveObject

View File

@@ -1,3 +1,7 @@
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class OptionSettingViewModel : MyReactiveObject
@@ -48,6 +52,7 @@ public class OptionSettingViewModel : MyReactiveObject
[Reactive] public bool DisplayRealTimeSpeed { get; set; }
[Reactive] public bool EnableAutoAdjustMainLvColWidth { get; set; }
[Reactive] public bool EnableUpdateSubOnlyRemarksExist { get; set; }
[Reactive] public bool EnableSecurityProtocolTls13 { get; set; }
[Reactive] public bool AutoHideStartup { get; set; }
[Reactive] public bool Hide2TrayWhenClose { get; set; }
[Reactive] public bool EnableDragDropSort { get; set; }
@@ -165,6 +170,7 @@ public class OptionSettingViewModel : MyReactiveObject
KeepOlderDedupl = _config.GuiItem.KeepOlderDedupl;
EnableAutoAdjustMainLvColWidth = _config.UiItem.EnableAutoAdjustMainLvColWidth;
EnableUpdateSubOnlyRemarksExist = _config.UiItem.EnableUpdateSubOnlyRemarksExist;
EnableSecurityProtocolTls13 = _config.GuiItem.EnableSecurityProtocolTls13;
AutoHideStartup = _config.UiItem.AutoHideStartup;
Hide2TrayWhenClose = _config.UiItem.Hide2TrayWhenClose;
EnableDragDropSort = _config.UiItem.EnableDragDropSort;
@@ -324,6 +330,7 @@ public class OptionSettingViewModel : MyReactiveObject
_config.GuiItem.KeepOlderDedupl = KeepOlderDedupl;
_config.UiItem.EnableAutoAdjustMainLvColWidth = EnableAutoAdjustMainLvColWidth;
_config.UiItem.EnableUpdateSubOnlyRemarksExist = EnableUpdateSubOnlyRemarksExist;
_config.GuiItem.EnableSecurityProtocolTls13 = EnableSecurityProtocolTls13;
_config.UiItem.AutoHideStartup = AutoHideStartup;
_config.UiItem.Hide2TrayWhenClose = Hide2TrayWhenClose;
_config.GuiItem.AutoUpdateInterval = AutoUpdateInterval;

View File

@@ -1,3 +1,9 @@
using System.Reactive.Linq;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class ProfilesSelectViewModel : MyReactiveObject

View File

@@ -1,3 +1,12 @@
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class ProfilesViewModel : MyReactiveObject
@@ -66,7 +75,6 @@ public class ProfilesViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> SpeedServerCmd { get; }
public ReactiveCommand<Unit, Unit> SortServerResultCmd { get; }
public ReactiveCommand<Unit, Unit> RemoveInvalidServerResultCmd { get; }
public ReactiveCommand<Unit, Unit> FastRealPingCmd { get; }
//servers export
public ReactiveCommand<Unit, Unit> Export2ClientConfigCmd { get; }
@@ -180,10 +188,6 @@ public class ProfilesViewModel : MyReactiveObject
}, canEditRemove);
//servers ping
FastRealPingCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ServerSpeedtest(ESpeedActionType.FastRealping);
});
MixedTestServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ServerSpeedtest(ESpeedActionType.Mixedtest);
@@ -734,12 +738,6 @@ public class ProfilesViewModel : MyReactiveObject
{
SelectedProfiles = ProfileItems;
}
else if (actionType == ESpeedActionType.FastRealping)
{
SelectedProfiles = ProfileItems;
actionType = ESpeedActionType.Realping;
}
var lstSelected = await GetProfileItems(false);
if (lstSelected == null)
{

View File

@@ -1,3 +1,7 @@
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class RoutingRuleDetailsViewModel : MyReactiveObject

View File

@@ -1,3 +1,10 @@
using System.Reactive;
using System.Text.Json;
using System.Text.Json.Serialization;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class RoutingRuleSettingViewModel : MyReactiveObject

View File

@@ -1,3 +1,8 @@
using System.Reactive;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class RoutingSettingViewModel : MyReactiveObject

View File

@@ -1,3 +1,11 @@
using System.Reactive;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Text;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class StatusBarViewModel : MyReactiveObject

View File

@@ -1,3 +1,7 @@
using System.Reactive;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class SubEditViewModel : MyReactiveObject

View File

@@ -1,3 +1,9 @@
using System.Reactive;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
namespace ServiceLib.ViewModels;
public class SubSettingViewModel : MyReactiveObject

View File

@@ -8,14 +8,7 @@
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
Name="v2rayN"
x:DataType="vms:StatusBarViewModel"
RequestedThemeVariant="Default">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="Assets/GlobalResources.axaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
RequestedThemeVariant="Default">
<Application.Styles>
<semi:SemiTheme />
<semi:AvaloniaEditSemiTheme />
@@ -23,6 +16,13 @@
<StyleInclude Source="avares://Semi.Avalonia.DataGrid/Index.axaml" />
<dialogHost:DialogHostStyles />
</Application.Styles>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="Assets/GlobalResources.axaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
<TrayIcon.Icons>
<TrayIcons>

View File

@@ -1,3 +1,6 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using v2rayN.Desktop.Views;
namespace v2rayN.Desktop;

View File

@@ -13,7 +13,6 @@
<Style Selector="PathIcon">
<Setter Property="Width" Value="16" />
<Setter Property="Height" Value="16" />
<Setter Property="Foreground" Value="{DynamicResource ButtonDefaultTertiaryForeground}" />
</Style>
<Style Selector="TextBox">
@@ -27,12 +26,4 @@
<Style Selector="TabControl">
<Setter Property="Theme" Value="{StaticResource LineTabControl}" />
</Style>
<Style Selector="Button.IconButton">
<Setter Property="Width" Value="{StaticResource IconButtonWidth}" />
<Setter Property="Height" Value="{StaticResource IconButtonHeight}" />
<Setter Property="MinWidth" Value="{StaticResource IconButtonWidth}" />
<Setter Property="Theme" Value="{DynamicResource BorderlessButton}" />
</Style>
</Styles>

View File

@@ -1,3 +1,7 @@
using Avalonia;
using Avalonia.Interactivity;
using Avalonia.ReactiveUI;
namespace v2rayN.Desktop.Base;
public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewModel : class

View File

@@ -1,3 +1,6 @@
using Avalonia;
using Avalonia.Media;
namespace v2rayN.Desktop.Common;
public static class AppBuilderExtension

View File

@@ -1,4 +1,9 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
namespace v2rayN.Desktop.Common;

View File

@@ -1,3 +1,4 @@
using Avalonia.Media;
using AvaloniaEdit;
using AvaloniaEdit.Document;
using AvaloniaEdit.Rendering;

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