Compare commits
3 Commits
7.15.5
...
62a2558174
| Author | SHA1 | Date | |
|---|---|---|---|
| 62a2558174 | |||
| 45b6fe4d5a | |||
| 6b8b2d0b1b |
@@ -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
|
||||
|
||||
45
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
45
.github/ISSUE_TEMPLATE/01_bug_report.yml
vendored
@@ -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
|
||||
|
||||
9
.github/ISSUE_TEMPLATE/config.yml
vendored
9
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -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
10
package-appimage.sh
Normal file → Executable 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
51
package-debian.sh
Normal file → Executable 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"
|
||||
|
||||
@@ -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
4
package-release-zip.sh
Normal file → Executable 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
657
package-rhel.sh
Normal file → Executable 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
120
utils.sh
Normal 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"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.15.5</Version>
|
||||
<Version>7.15.4</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ServiceLib.Base;
|
||||
|
||||
public class MyReactiveObject : ReactiveObject
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
public static class EmbedUtils
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Formats.Tar;
|
||||
using System.IO.Compression;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
public static class ProcUtils
|
||||
|
||||
@@ -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 数据检查
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Reactive;
|
||||
|
||||
namespace ServiceLib.Events;
|
||||
|
||||
public static class AppEvents
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Reactive.Subjects;
|
||||
|
||||
namespace ServiceLib.Events;
|
||||
|
||||
@@ -316,7 +316,7 @@ public class Global
|
||||
];
|
||||
|
||||
public static readonly HashSet<EConfigType> SingboxOnlyConfigType = SingboxSupportConfigType.Except(XraySupportConfigType).ToHashSet();
|
||||
|
||||
|
||||
public static readonly List<string> DomainStrategies =
|
||||
[
|
||||
AsIs,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Security.Principal;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -1481,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;
|
||||
@@ -2271,7 +2274,6 @@ public static class ConfigHandler
|
||||
BlockBindingQuery = true,
|
||||
DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
|
||||
RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(),
|
||||
BootstrapDNS = Global.DomainPureIPDNSAddress.FirstOrDefault(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Net;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
|
||||
public static class ConnectionHandler
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ServiceLib.Handler.Fmt;
|
||||
|
||||
public class ShadowsocksFmt : BaseFmt
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
|
||||
|
||||
namespace ServiceLib.Handler.SysProxy;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Net;
|
||||
using Downloader;
|
||||
|
||||
namespace ServiceLib.Helper;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections;
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Helper;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Text;
|
||||
using CliWrap;
|
||||
using CliWrap.Buffered;
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public class PacManager
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
//using System.Reactive.Linq;
|
||||
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
public class ProfileGroupItemManager
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Net;
|
||||
using WebDav;
|
||||
|
||||
namespace ServiceLib.Manager;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
public class CheckUpdateModel : ReactiveObject
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
public class GitHubReleaseAsset
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using ReactiveUI;
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
public class SingboxConfig
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
public class V2rayConfig
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
/// <summary>
|
||||
|
||||
27
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
27
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -2517,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>
|
||||
@@ -3984,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>
|
||||
|
||||
@@ -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,10 +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>
|
||||
</root>
|
||||
@@ -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,10 +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>
|
||||
</root>
|
||||
@@ -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,10 +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>
|
||||
</root>
|
||||
@@ -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,10 +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>
|
||||
</root>
|
||||
@@ -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,10 +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>
|
||||
</root>
|
||||
@@ -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>
|
||||
@@ -1587,10 +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>Resolve DNS server domains, requires IP</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,3 +1,7 @@
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using ServiceLib.Common;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigSingboxService(Config config)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigSingboxService
|
||||
|
||||
@@ -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,19 +459,15 @@ 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";
|
||||
@@ -474,19 +475,63 @@ public partial class CoreConfigSingboxService
|
||||
return server;
|
||||
}
|
||||
|
||||
//server.type = scheme.ToLower();
|
||||
// remove "+local" suffix
|
||||
// TODO: "+local" suffix decide server.detour = "direct" ?
|
||||
server.type = scheme.Replace("+local", "", StringComparison.OrdinalIgnoreCase).ToLower();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigV2rayService(Config config)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
public partial class CoreConfigV2rayService
|
||||
|
||||
@@ -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
|
||||
@@ -103,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
|
||||
@@ -246,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Services;
|
||||
|
||||
public class ProcessService : IDisposable
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Services.Statistics;
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ServiceLib.Services;
|
||||
|
||||
public class UpdateService
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
using System.Reactive;
|
||||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class AddGroupServerViewModel : MyReactiveObject
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class AddServer2ViewModel : MyReactiveObject
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class AddServerViewModel : MyReactiveObject
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class BackupAndRestoreViewModel : MyReactiveObject
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class FullConfigTemplateViewModel : MyReactiveObject
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using System.Reactive;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class GlobalHotkeySettingViewModel : MyReactiveObject
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using System.Reactive;
|
||||
using System.Reactive.Concurrency;
|
||||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class RoutingRuleDetailsViewModel : MyReactiveObject
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
using System.Reactive;
|
||||
using DynamicData.Binding;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class RoutingSettingViewModel : MyReactiveObject
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
using System.Reactive;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.ViewModels;
|
||||
|
||||
public class SubEditViewModel : MyReactiveObject
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using v2rayN.Desktop.Views;
|
||||
|
||||
namespace v2rayN.Desktop;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace v2rayN.Desktop.Common;
|
||||
|
||||
public static class AppBuilderExtension
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Avalonia.Media;
|
||||
using AvaloniaEdit;
|
||||
using AvaloniaEdit.Document;
|
||||
using AvaloniaEdit.Rendering;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Platform.Storage;
|
||||
using MsBox.Avalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
|
||||
namespace v2rayN.Desktop.Common;
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Globalization;
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Media;
|
||||
|
||||
namespace v2rayN.Desktop.Converters;
|
||||
|
||||
|
||||
@@ -1,29 +1,3 @@
|
||||
global using System;
|
||||
global using System.Collections.Generic;
|
||||
global using System.Globalization;
|
||||
global using System.IO;
|
||||
global using System.Linq;
|
||||
global using System.Reactive.Disposables;
|
||||
global using System.Reactive.Linq;
|
||||
global using System.Text;
|
||||
global using System.Threading;
|
||||
global using System.Threading.Tasks;
|
||||
global using Avalonia;
|
||||
global using Avalonia.Controls;
|
||||
global using Avalonia.Controls.ApplicationLifetimes;
|
||||
global using Avalonia.Input;
|
||||
global using Avalonia.Interactivity;
|
||||
global using Avalonia.Markup.Xaml;
|
||||
global using Avalonia.Media;
|
||||
global using Avalonia.Media.Imaging;
|
||||
global using Avalonia.Platform;
|
||||
global using Avalonia.ReactiveUI;
|
||||
global using Avalonia.Styling;
|
||||
global using Avalonia.Threading;
|
||||
global using ReactiveUI;
|
||||
global using ReactiveUI.Fody.Helpers;
|
||||
global using DynamicData;
|
||||
global using MsBox.Avalonia.Enums;
|
||||
global using ServiceLib;
|
||||
global using ServiceLib.Base;
|
||||
global using ServiceLib.Common;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user