mirror of
https://github.com/2dust/v2rayN.git
synced 2026-05-18 23:54:49 +03:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8774e302b2 | |||
| df016dd55c | |||
| 9ea80671d3 | |||
| 449849d8e8 | |||
| 03b62b3d78 | |||
| 9f9b90cb97 | |||
| c42dcd2876 | |||
| 2fefafdd37 | |||
| 2c9a90c878 | |||
| 4e5f1838a2 | |||
| a45a1dc982 | |||
| fe183798b6 | |||
| 947c84cf10 | |||
| 9c74b51d74 | |||
| abd962ab31 | |||
| f3b894015e | |||
| 4562d4cf00 | |||
| bc36cf8a47 | |||
| cbdfe2e15a | |||
| 68583e20bc | |||
| 6d6459b009 | |||
| 807562b69e | |||
| 654d7d83d0 | |||
| 027252e687 | |||
| 5478c90180 | |||
| 28f30d7e97 | |||
| ae7d54c2e5 | |||
| 56d0d65b06 | |||
| 5e8e189c27 | |||
| 3fee86d44a | |||
| dd77eb79c6 | |||
| d26a2559a6 | |||
| e5ba1759aa | |||
| bfdee37cc1 | |||
| cf89cfcd95 | |||
| 39a988c704 | |||
| 2b28254fbc | |||
| 6e27dca6cd | |||
| 7cee98887b | |||
| 3885ff8b31 | |||
| 12abf383e9 | |||
| 5bef02bd6d | |||
| 592f1260b5 | |||
| 18303688d7 | |||
| 5c4b7f6636 | |||
| 37cce2fa35 | |||
| 6f8b65c75b | |||
| 83c63b914a | |||
| 1ca2485d2a | |||
| cc4154bb0d | |||
| d7f77f220c | |||
| 86f45d103d | |||
| 0077751f75 | |||
| fa2b4b3dc9 | |||
| 23cacb8339 | |||
| 9ffa6a4eb6 | |||
| 386209b835 | |||
| 830dc89c32 | |||
| 693afe3560 | |||
| 95361e8b65 | |||
| 3ff7299aca | |||
| 34fc4de0c2 | |||
| 91536d3923 | |||
| 6b87c09a96 | |||
| ecaac2ac61 | |||
| ad74b1584d | |||
| 514d76953a | |||
| 5b82f17995 | |||
| 20ce35bc30 | |||
| c0fca0dddd | |||
| 2ba896e17e | |||
| f61e6d8c63 | |||
| d3e2e55ecf |
@@ -31,26 +31,26 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5.0.1
|
uses: actions/checkout@v6.0.1
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v5.0.0
|
uses: actions/setup-dotnet@v5.0.1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.x'
|
dotnet-version: '8.0.x'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd v2rayN
|
cd v2rayN
|
||||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o "$OutputPath64"
|
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 -p:SelfContained=true -o "$OutputPath64"
|
||||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o "$OutputPathArm64"
|
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 -p:SelfContained=true -o "$OutputPathArm64"
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPath64"
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 -p:SelfContained=true -p:PublishTrimmed=true -o "$OutputPath64"
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPathArm64"
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 -p:SelfContained=true -p:PublishTrimmed=true -o "$OutputPathArm64"
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v5.0.0
|
uses: actions/upload-artifact@v6.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-linux
|
name: v2rayN-linux
|
||||||
path: |
|
path: |
|
||||||
@@ -97,26 +97,26 @@ jobs:
|
|||||||
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))
|
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
container:
|
container:
|
||||||
image: quay.io/almalinuxorg/10-base:latest
|
image: registry.access.redhat.com/ubi10/ubi
|
||||||
options: --platform=linux/amd64/v2
|
|
||||||
env:
|
env:
|
||||||
RELEASE_TAG: ${{ github.event.inputs.release_tag != '' && github.event.inputs.release_tag || github.ref_name }}
|
RELEASE_TAG: ${{ github.event.inputs.release_tag != '' && github.event.inputs.release_tag || github.ref_name }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Prepare tools (Red Hat)
|
- name: Prepare tools (Red Hat)
|
||||||
run: |
|
run: |
|
||||||
|
dnf repolist all
|
||||||
dnf -y makecache
|
dnf -y makecache
|
||||||
dnf -y install epel-release
|
dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm
|
||||||
dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which
|
dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which
|
||||||
|
|
||||||
- name: Checkout repo (for scripts)
|
- name: Checkout repo (for scripts)
|
||||||
uses: actions/checkout@v5.0.1
|
uses: actions/checkout@v6.0.1
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
||||||
- name: Restore build artifacts
|
- name: Restore build artifacts
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: v2rayN-linux
|
name: v2rayN-linux
|
||||||
path: ${{ github.workspace }}/v2rayN/Release
|
path: ${{ github.workspace }}/v2rayN/Release
|
||||||
@@ -137,7 +137,7 @@ jobs:
|
|||||||
ls -R "$GITHUB_WORKSPACE/dist/rpm" || true
|
ls -R "$GITHUB_WORKSPACE/dist/rpm" || true
|
||||||
|
|
||||||
- name: Upload RPM artifacts
|
- name: Upload RPM artifacts
|
||||||
uses: actions/upload-artifact@v5.0.0
|
uses: actions/upload-artifact@v6.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-rpm
|
name: v2rayN-rpm
|
||||||
path: dist/rpm/**/*.rpm
|
path: dist/rpm/**/*.rpm
|
||||||
|
|||||||
@@ -26,26 +26,26 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5.0.1
|
uses: actions/checkout@v6.0.1
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
||||||
- name: Setup
|
- name: Setup
|
||||||
uses: actions/setup-dotnet@v5.0.0
|
uses: actions/setup-dotnet@v5.0.1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.x'
|
dotnet-version: '8.0.x'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd v2rayN
|
cd v2rayN
|
||||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 --self-contained=true -o $OutputPath64
|
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 -p:SelfContained=true -o $OutputPath64
|
||||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 --self-contained=true -o $OutputPathArm64
|
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 -p:SelfContained=true -o $OutputPathArm64
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-x64 -p:SelfContained=true -p:PublishTrimmed=true -o $OutputPath64
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 -p:SelfContained=true -p:PublishTrimmed=true -o $OutputPathArm64
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v5.0.0
|
uses: actions/upload-artifact@v6.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-macos
|
name: v2rayN-macos
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
@@ -26,26 +26,26 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5.0.1
|
uses: actions/checkout@v6.0.1
|
||||||
with:
|
with:
|
||||||
submodules: 'recursive'
|
submodules: 'recursive'
|
||||||
fetch-depth: '0'
|
fetch-depth: '0'
|
||||||
|
|
||||||
- name: Setup
|
- name: Setup
|
||||||
uses: actions/setup-dotnet@v5.0.0
|
uses: actions/setup-dotnet@v5.0.1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.x'
|
dotnet-version: '8.0.x'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd v2rayN
|
cd v2rayN
|
||||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPath64
|
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPath64
|
||||||
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
|
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v5.0.0
|
uses: actions/upload-artifact@v6.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-windows-desktop
|
name: v2rayN-windows-desktop
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
@@ -27,26 +27,26 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5.0.1
|
uses: actions/checkout@v6.0.1
|
||||||
|
|
||||||
- name: Setup
|
- name: Setup
|
||||||
uses: actions/setup-dotnet@v5.0.0
|
uses: actions/setup-dotnet@v5.0.1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.x'
|
dotnet-version: '8.0.x'
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
cd v2rayN
|
cd v2rayN
|
||||||
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPath64
|
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPath64
|
||||||
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
|
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPathArm64
|
||||||
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
|
dotnet publish ./v2rayN/v2rayN.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -o $OutputPath64Sc
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPath64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=false -p:EnableWindowsTargeting=true -o $OutputPathArm64
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
|
||||||
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64Sc
|
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-x64 -p:SelfContained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPath64Sc
|
||||||
|
|
||||||
|
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v5.0.0
|
uses: actions/upload-artifact@v6.0.0
|
||||||
with:
|
with:
|
||||||
name: v2rayN-windows
|
name: v2rayN-windows
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
+9
-78
@@ -1,11 +1,11 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# == Require Red Hat Enterprise Linux/FedoraLinux/RockyLinux/AlmaLinux/CentOS OR Ubuntu/Debian ==
|
# ====== Require Red Hat Enterprise Linux/FedoraLinux/RockyLinux/AlmaLinux/CentOS ======
|
||||||
if [[ -r /etc/os-release ]]; then
|
if [[ -r /etc/os-release ]]; then
|
||||||
. /etc/os-release
|
. /etc/os-release
|
||||||
case "$ID" in
|
case "$ID" in
|
||||||
rhel|rocky|almalinux|fedora|centos|ubuntu|debian)
|
rhel|rocky|almalinux|fedora|centos)
|
||||||
echo "[OK] Detected supported system: $NAME $VERSION_ID"
|
echo "[OK] Detected supported system: $NAME $VERSION_ID"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -30,7 +30,7 @@ echo "[INFO] Detected kernel version: $KERNEL_FULL"
|
|||||||
|
|
||||||
if (( KERNEL_MAJOR < MIN_KERNEL_MAJOR )) || { (( KERNEL_MAJOR == MIN_KERNEL_MAJOR )) && (( KERNEL_MINOR < MIN_KERNEL_MINOR )); }; then
|
if (( KERNEL_MAJOR < MIN_KERNEL_MAJOR )) || { (( KERNEL_MAJOR == MIN_KERNEL_MAJOR )) && (( KERNEL_MINOR < MIN_KERNEL_MINOR )); }; then
|
||||||
echo "[ERROR] Kernel $KERNEL_FULL is too old. Requires Linux >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}."
|
echo "[ERROR] Kernel $KERNEL_FULL is too old. Requires Linux >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}."
|
||||||
echo "Please upgrade your system or use a newer container (e.g. Fedora 42+, RHEL 10+, Debian 13+)."
|
echo "Please upgrade your system or use a newer container (e.g. Fedora 42+, RHEL 10+)."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -80,7 +80,6 @@ host_arch="$(uname -m)"
|
|||||||
|
|
||||||
install_ok=0
|
install_ok=0
|
||||||
case "$ID" in
|
case "$ID" in
|
||||||
# ------------------------------ RHEL family (UNCHANGED) ------------------------------
|
|
||||||
rhel|rocky|almalinux|centos)
|
rhel|rocky|almalinux|centos)
|
||||||
if command -v dnf >/dev/null 2>&1; then
|
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-8.0 rpm-build rpmdevtools curl unzip tar rsync || \
|
||||||
@@ -92,58 +91,7 @@ case "$ID" in
|
|||||||
install_ok=1
|
install_ok=1
|
||||||
fi
|
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)
|
|
||||||
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
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@@ -154,7 +102,7 @@ fi
|
|||||||
|
|
||||||
command -v curl >/dev/null
|
command -v curl >/dev/null
|
||||||
|
|
||||||
# Root directory = the script's location
|
# Root directory
|
||||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
cd "$SCRIPT_DIR"
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
@@ -164,14 +112,14 @@ if [[ -f .gitmodules ]]; then
|
|||||||
git submodule update --init --recursive || true
|
git submodule update --init --recursive || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ===== Locate project ================================================================
|
# Locate project
|
||||||
PROJECT="v2rayN.Desktop/v2rayN.Desktop.csproj"
|
PROJECT="v2rayN.Desktop/v2rayN.Desktop.csproj"
|
||||||
if [[ ! -f "$PROJECT" ]]; then
|
if [[ ! -f "$PROJECT" ]]; then
|
||||||
PROJECT="$(find . -maxdepth 3 -name 'v2rayN.Desktop.csproj' | head -n1 || true)"
|
PROJECT="$(find . -maxdepth 3 -name 'v2rayN.Desktop.csproj' | head -n1 || true)"
|
||||||
fi
|
fi
|
||||||
[[ -f "$PROJECT" ]] || { echo "v2rayN.Desktop.csproj not found"; exit 1; }
|
[[ -f "$PROJECT" ]] || { echo "v2rayN.Desktop.csproj not found"; exit 1; }
|
||||||
|
|
||||||
# ===== Resolve GUI version & auto checkout ============================================
|
# Resolve GUI version & auto checkout
|
||||||
VERSION=""
|
VERSION=""
|
||||||
|
|
||||||
choose_channel() {
|
choose_channel() {
|
||||||
@@ -391,22 +339,6 @@ download_singbox() {
|
|||||||
install -Dm755 "$bin" "$outdir/sing-box"
|
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
|
# Move geo files to a unified path: outroot/bin
|
||||||
unify_geo_layout() {
|
unify_geo_layout() {
|
||||||
local outroot="$1"
|
local outroot="$1"
|
||||||
@@ -491,8 +423,7 @@ download_v2rayn_bundle() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
rm -f "$outroot/v2rayn.zip" 2>/dev/null || true
|
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
|
||||||
# find "$outroot" -type d -name "mihomo" -prune -exec rm -rf {} + 2>/dev/null || true
|
|
||||||
|
|
||||||
local nested_dir
|
local nested_dir
|
||||||
nested_dir="$(find "$outroot" -maxdepth 1 -type d -name 'v2rayN-linux-*' | head -n1 || true)"
|
nested_dir="$(find "$outroot" -maxdepth 1 -type d -name 'v2rayN-linux-*' | head -n1 || true)"
|
||||||
@@ -603,7 +534,7 @@ build_for_arch() {
|
|||||||
fi
|
fi
|
||||||
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
|
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
|
||||||
# ---- REQUIRED: always fetch mihomo in netcore mode, per-arch ----
|
# ---- REQUIRED: always fetch mihomo in netcore mode, per-arch ----
|
||||||
download_mihomo "$WORKDIR/$PKGROOT" || echo "[!] mihomo download failed (skipped)"
|
# download_mihomo "$WORKDIR/$PKGROOT" || echo "[!] mihomo download failed (skipped)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Tarball
|
# Tarball
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>7.16.3</Version>
|
<Version>7.17.2</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|||||||
@@ -6,23 +6,23 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
|
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.11" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.8" />
|
<PackageVersion Include="Avalonia.Desktop" Version="11.3.11" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.8" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.11" />
|
||||||
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.3.8" />
|
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.3.8" />
|
||||||
<PackageVersion Include="CliWrap" Version="3.9.0" />
|
<PackageVersion Include="CliWrap" Version="3.10.0" />
|
||||||
<PackageVersion Include="Downloader" Version="4.0.3" />
|
<PackageVersion Include="Downloader" Version="4.0.3" />
|
||||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.2" />
|
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.4.1" />
|
||||||
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
|
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
|
||||||
<PackageVersion Include="MessageBox.Avalonia" Version="3.3.0" />
|
<PackageVersion Include="MessageBox.Avalonia" Version="3.3.1.1" />
|
||||||
<PackageVersion Include="QRCoder" Version="1.7.0" />
|
<PackageVersion Include="QRCoder" Version="1.7.0" />
|
||||||
<PackageVersion Include="ReactiveUI" Version="22.2.1" />
|
<PackageVersion Include="ReactiveUI" Version="22.3.1" />
|
||||||
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||||
<PackageVersion Include="ReactiveUI.WPF" Version="22.2.1" />
|
<PackageVersion Include="ReactiveUI.WPF" Version="22.3.1" />
|
||||||
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.1" />
|
<PackageVersion Include="Semi.Avalonia" Version="11.3.7.2" />
|
||||||
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
|
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
|
||||||
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.1" />
|
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.2" />
|
||||||
<PackageVersion Include="NLog" Version="6.0.6" />
|
<PackageVersion Include="NLog" Version="6.0.7" />
|
||||||
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
||||||
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
||||||
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ public static class Extension
|
|||||||
{
|
{
|
||||||
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
|
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
|
||||||
{
|
{
|
||||||
return string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value);
|
return string.IsNullOrWhiteSpace(value) || string.IsNullOrEmpty(value);
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value)
|
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
|
public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
|
||||||
{
|
{
|
||||||
return !string.IsNullOrEmpty(value);
|
return !string.IsNullOrWhiteSpace(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string? NullIfEmpty(this string? value)
|
||||||
|
{
|
||||||
|
return string.IsNullOrWhiteSpace(value) ? null : value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
||||||
@@ -94,4 +94,28 @@ public static class Extension
|
|||||||
{
|
{
|
||||||
return configType is EConfigType.Custom or EConfigType.PolicyGroup or EConfigType.ProxyChain;
|
return configType is EConfigType.Custom or EConfigType.PolicyGroup or EConfigType.ProxyChain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Safely adds elements from a collection to the list. Does nothing if the source is null.
|
||||||
|
/// </summary>
|
||||||
|
public static void AddRangeSafe<T>(this ICollection<T> destination, IEnumerable<T>? source)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(destination);
|
||||||
|
|
||||||
|
if (source is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destination is List<T> list)
|
||||||
|
{
|
||||||
|
list.AddRange(source);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in source)
|
||||||
|
{
|
||||||
|
destination.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+272
-260
@@ -73,6 +73,7 @@ public class Global
|
|||||||
public const string GrpcMultiMode = "multi";
|
public const string GrpcMultiMode = "multi";
|
||||||
public const int MaxPort = 65536;
|
public const int MaxPort = 65536;
|
||||||
public const int MinFontSize = 8;
|
public const int MinFontSize = 8;
|
||||||
|
public const int MinFontSizeCount = 13;
|
||||||
public const string RebootAs = "rebootas";
|
public const string RebootAs = "rebootas";
|
||||||
public const string AvaAssets = "avares://v2rayN/Assets/";
|
public const string AvaAssets = "avares://v2rayN/Assets/";
|
||||||
public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA_V2";
|
public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA_V2";
|
||||||
@@ -87,232 +88,236 @@ public class Global
|
|||||||
public const string SingboxLocalDNSTag = "local_local";
|
public const string SingboxLocalDNSTag = "local_local";
|
||||||
public const string SingboxHostsDNSTag = "hosts_dns";
|
public const string SingboxHostsDNSTag = "hosts_dns";
|
||||||
public const string SingboxFakeDNSTag = "fake_dns";
|
public const string SingboxFakeDNSTag = "fake_dns";
|
||||||
|
public const string SingboxEchDNSTag = "ech_dns";
|
||||||
|
|
||||||
public static readonly List<string> IEProxyProtocols =
|
public static readonly List<string> IEProxyProtocols =
|
||||||
[
|
[
|
||||||
"{ip}:{http_port}",
|
"{ip}:{http_port}",
|
||||||
"socks={ip}:{socks_port}",
|
"socks={ip}:{socks_port}",
|
||||||
"http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}",
|
"http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}",
|
||||||
"http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
|
"http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SubConvertUrls =
|
public static readonly List<string> SubConvertUrls =
|
||||||
[
|
[
|
||||||
@"https://sub.xeton.dev/sub?url={0}",
|
@"https://sub.xeton.dev/sub?url={0}",
|
||||||
@"https://api.dler.io/sub?url={0}",
|
@"https://api.dler.io/sub?url={0}",
|
||||||
@"http://127.0.0.1:25500/sub?url={0}",
|
@"http://127.0.0.1:25500/sub?url={0}",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SubConvertConfig =
|
public static readonly List<string> SubConvertConfig =
|
||||||
[@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"];
|
[
|
||||||
|
@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"
|
||||||
|
];
|
||||||
|
|
||||||
public static readonly List<string> SubConvertTargets =
|
public static readonly List<string> SubConvertTargets =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"mixed",
|
"mixed",
|
||||||
"v2ray",
|
"v2ray",
|
||||||
"clash",
|
"clash",
|
||||||
"ss"
|
"ss"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SpeedTestUrls =
|
public static readonly List<string> SpeedTestUrls =
|
||||||
[
|
[
|
||||||
@"https://cachefly.cachefly.net/50mb.test",
|
@"https://cachefly.cachefly.net/50mb.test",
|
||||||
@"https://speed.cloudflare.com/__down?bytes=10000000",
|
@"https://speed.cloudflare.com/__down?bytes=10000000",
|
||||||
@"https://speed.cloudflare.com/__down?bytes=50000000",
|
@"https://speed.cloudflare.com/__down?bytes=50000000",
|
||||||
@"https://speed.cloudflare.com/__down?bytes=100000000",
|
@"https://speed.cloudflare.com/__down?bytes=100000000",
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SpeedPingTestUrls =
|
public static readonly List<string> SpeedPingTestUrls =
|
||||||
[
|
[
|
||||||
@"https://www.google.com/generate_204",
|
@"https://www.google.com/generate_204",
|
||||||
@"https://www.gstatic.com/generate_204",
|
@"https://www.gstatic.com/generate_204",
|
||||||
@"https://www.apple.com/library/test/success.html",
|
@"https://www.apple.com/library/test/success.html",
|
||||||
@"http://www.msftconnecttest.com/connecttest.txt"
|
@"http://www.msftconnecttest.com/connecttest.txt"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> GeoFilesSources =
|
public static readonly List<string> GeoFilesSources =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
|
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
|
||||||
@"https://github.com/Chocolate4U/Iran-v2ray-rules/releases/latest/download/{0}.dat"
|
@"https://github.com/Chocolate4U/Iran-v2ray-rules/releases/latest/download/{0}.dat"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SingboxRulesetSources =
|
public static readonly List<string> SingboxRulesetSources =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/release/sing-box/rule-set-{0}/{1}.srs",
|
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/release/sing-box/rule-set-{0}/{1}.srs",
|
||||||
@"https://raw.githubusercontent.com/chocolate4u/Iran-sing-box-rules/rule-set/{1}.srs"
|
@"https://raw.githubusercontent.com/chocolate4u/Iran-sing-box-rules/rule-set/{1}.srs"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> RoutingRulesSources =
|
public static readonly List<string> RoutingRulesSources =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-custom-routing-list/main/v2rayN/template.json",
|
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-custom-routing-list/main/v2rayN/template.json",
|
||||||
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/template.json"
|
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/template.json"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DNSTemplateSources =
|
public static readonly List<string> DNSTemplateSources =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-custom-routing-list/main/v2rayN/",
|
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-custom-routing-list/main/v2rayN/",
|
||||||
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/"
|
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly Dictionary<string, string> UserAgentTexts = new()
|
public static readonly Dictionary<string, string> UserAgentTexts = new()
|
||||||
{
|
{
|
||||||
{"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" },
|
{"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" },
|
||||||
{"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" },
|
{"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" },
|
||||||
{"safari","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" },
|
{"safari","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" },
|
||||||
{"edge","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70" },
|
{"edge","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70" },
|
||||||
{"none",""}
|
{"none",""}
|
||||||
};
|
};
|
||||||
|
|
||||||
public const string Hysteria2ProtocolShare = "hy2://";
|
public const string Hysteria2ProtocolShare = "hy2://";
|
||||||
|
|
||||||
public static readonly Dictionary<EConfigType, string> ProtocolShares = new()
|
public static readonly Dictionary<EConfigType, string> ProtocolShares = new()
|
||||||
{
|
{
|
||||||
{ EConfigType.VMess, "vmess://" },
|
{ EConfigType.VMess, "vmess://" },
|
||||||
{ EConfigType.Shadowsocks, "ss://" },
|
{ EConfigType.Shadowsocks, "ss://" },
|
||||||
{ EConfigType.SOCKS, "socks://" },
|
{ EConfigType.SOCKS, "socks://" },
|
||||||
{ EConfigType.VLESS, "vless://" },
|
{ EConfigType.VLESS, "vless://" },
|
||||||
{ EConfigType.Trojan, "trojan://" },
|
{ EConfigType.Trojan, "trojan://" },
|
||||||
{ EConfigType.Hysteria2, "hysteria2://" },
|
{ EConfigType.Hysteria2, "hysteria2://" },
|
||||||
{ EConfigType.TUIC, "tuic://" },
|
{ EConfigType.TUIC, "tuic://" },
|
||||||
{ EConfigType.WireGuard, "wireguard://" },
|
{ EConfigType.WireGuard, "wireguard://" },
|
||||||
{ EConfigType.Anytls, "anytls://" }
|
{ EConfigType.Anytls, "anytls://" }
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
|
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
|
||||||
{
|
{
|
||||||
{ EConfigType.VMess, "vmess" },
|
{ EConfigType.VMess, "vmess" },
|
||||||
{ EConfigType.Shadowsocks, "shadowsocks" },
|
{ EConfigType.Shadowsocks, "shadowsocks" },
|
||||||
{ EConfigType.SOCKS, "socks" },
|
{ EConfigType.SOCKS, "socks" },
|
||||||
{ EConfigType.HTTP, "http" },
|
{ EConfigType.HTTP, "http" },
|
||||||
{ EConfigType.VLESS, "vless" },
|
{ EConfigType.VLESS, "vless" },
|
||||||
{ EConfigType.Trojan, "trojan" },
|
{ EConfigType.Trojan, "trojan" },
|
||||||
{ EConfigType.Hysteria2, "hysteria2" },
|
{ EConfigType.Hysteria2, "hysteria2" },
|
||||||
{ EConfigType.TUIC, "tuic" },
|
{ EConfigType.TUIC, "tuic" },
|
||||||
{ EConfigType.WireGuard, "wireguard" },
|
{ EConfigType.WireGuard, "wireguard" },
|
||||||
{ EConfigType.Anytls, "anytls" }
|
{ EConfigType.Anytls, "anytls" }
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly List<string> VmessSecurities =
|
public static readonly List<string> VmessSecurities =
|
||||||
[
|
[
|
||||||
"aes-128-gcm",
|
"aes-128-gcm",
|
||||||
"chacha20-poly1305",
|
"chacha20-poly1305",
|
||||||
"auto",
|
"auto",
|
||||||
"none",
|
"none",
|
||||||
"zero"
|
"zero"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SsSecurities =
|
public static readonly List<string> SsSecurities =
|
||||||
[
|
[
|
||||||
"aes-256-gcm",
|
"aes-256-gcm",
|
||||||
"aes-128-gcm",
|
"aes-128-gcm",
|
||||||
"chacha20-poly1305",
|
"chacha20-poly1305",
|
||||||
"chacha20-ietf-poly1305",
|
"chacha20-ietf-poly1305",
|
||||||
"none",
|
"none",
|
||||||
"plain"
|
"plain"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SsSecuritiesInXray =
|
public static readonly List<string> SsSecuritiesInXray =
|
||||||
[
|
[
|
||||||
"aes-256-gcm",
|
"aes-256-gcm",
|
||||||
"aes-128-gcm",
|
"aes-128-gcm",
|
||||||
"chacha20-poly1305",
|
"chacha20-poly1305",
|
||||||
"chacha20-ietf-poly1305",
|
"chacha20-ietf-poly1305",
|
||||||
"xchacha20-poly1305",
|
"xchacha20-poly1305",
|
||||||
"xchacha20-ietf-poly1305",
|
"xchacha20-ietf-poly1305",
|
||||||
"none",
|
"none",
|
||||||
"plain",
|
"plain",
|
||||||
"2022-blake3-aes-128-gcm",
|
"2022-blake3-aes-128-gcm",
|
||||||
"2022-blake3-aes-256-gcm",
|
"2022-blake3-aes-256-gcm",
|
||||||
"2022-blake3-chacha20-poly1305"
|
"2022-blake3-chacha20-poly1305"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SsSecuritiesInSingbox =
|
public static readonly List<string> SsSecuritiesInSingbox =
|
||||||
[
|
[
|
||||||
"aes-256-gcm",
|
"aes-256-gcm",
|
||||||
"aes-192-gcm",
|
"aes-192-gcm",
|
||||||
"aes-128-gcm",
|
"aes-128-gcm",
|
||||||
"chacha20-ietf-poly1305",
|
"chacha20-ietf-poly1305",
|
||||||
"xchacha20-ietf-poly1305",
|
"xchacha20-ietf-poly1305",
|
||||||
"none",
|
"none",
|
||||||
"2022-blake3-aes-128-gcm",
|
"2022-blake3-aes-128-gcm",
|
||||||
"2022-blake3-aes-256-gcm",
|
"2022-blake3-aes-256-gcm",
|
||||||
"2022-blake3-chacha20-poly1305",
|
"2022-blake3-chacha20-poly1305",
|
||||||
"aes-128-ctr",
|
"aes-128-ctr",
|
||||||
"aes-192-ctr",
|
"aes-192-ctr",
|
||||||
"aes-256-ctr",
|
"aes-256-ctr",
|
||||||
"aes-128-cfb",
|
"aes-128-cfb",
|
||||||
"aes-192-cfb",
|
"aes-192-cfb",
|
||||||
"aes-256-cfb",
|
"aes-256-cfb",
|
||||||
"rc4-md5",
|
"rc4-md5",
|
||||||
"chacha20-ietf",
|
"chacha20-ietf",
|
||||||
"xchacha20"
|
"xchacha20"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> Flows =
|
public static readonly List<string> Flows =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"xtls-rprx-vision",
|
"xtls-rprx-vision",
|
||||||
"xtls-rprx-vision-udp443"
|
"xtls-rprx-vision-udp443"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> Networks =
|
public static readonly List<string> Networks =
|
||||||
[
|
[
|
||||||
"tcp",
|
"tcp",
|
||||||
"kcp",
|
"kcp",
|
||||||
"ws",
|
"ws",
|
||||||
"httpupgrade",
|
"httpupgrade",
|
||||||
"xhttp",
|
"xhttp",
|
||||||
"h2",
|
"h2",
|
||||||
"quic",
|
"quic",
|
||||||
"grpc"
|
"grpc"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> KcpHeaderTypes =
|
public static readonly List<string> KcpHeaderTypes =
|
||||||
[
|
[
|
||||||
"srtp",
|
"srtp",
|
||||||
"utp",
|
"utp",
|
||||||
"wechat-video",
|
"wechat-video",
|
||||||
"dtls",
|
"dtls",
|
||||||
"wireguard",
|
"wireguard",
|
||||||
"dns"
|
"dns"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> CoreTypes =
|
public static readonly List<string> CoreTypes =
|
||||||
[
|
[
|
||||||
"Xray",
|
"Xray",
|
||||||
"sing_box"
|
"sing_box"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly HashSet<EConfigType> XraySupportConfigType =
|
public static readonly HashSet<EConfigType> XraySupportConfigType =
|
||||||
[
|
[
|
||||||
EConfigType.VMess,
|
EConfigType.VMess,
|
||||||
EConfigType.VLESS,
|
EConfigType.VLESS,
|
||||||
EConfigType.Shadowsocks,
|
EConfigType.Shadowsocks,
|
||||||
EConfigType.Trojan,
|
EConfigType.Trojan,
|
||||||
EConfigType.WireGuard,
|
EConfigType.Hysteria2,
|
||||||
EConfigType.SOCKS,
|
EConfigType.WireGuard,
|
||||||
EConfigType.HTTP,
|
EConfigType.SOCKS,
|
||||||
|
EConfigType.HTTP,
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly HashSet<EConfigType> SingboxSupportConfigType =
|
public static readonly HashSet<EConfigType> SingboxSupportConfigType =
|
||||||
[
|
[
|
||||||
EConfigType.VMess,
|
EConfigType.VMess,
|
||||||
EConfigType.VLESS,
|
EConfigType.VLESS,
|
||||||
EConfigType.Shadowsocks,
|
EConfigType.Shadowsocks,
|
||||||
EConfigType.Trojan,
|
EConfigType.Trojan,
|
||||||
EConfigType.Hysteria2,
|
EConfigType.Hysteria2,
|
||||||
EConfigType.TUIC,
|
EConfigType.TUIC,
|
||||||
EConfigType.Anytls,
|
EConfigType.Anytls,
|
||||||
EConfigType.WireGuard,
|
EConfigType.WireGuard,
|
||||||
EConfigType.SOCKS,
|
EConfigType.SOCKS,
|
||||||
EConfigType.HTTP,
|
EConfigType.HTTP,
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly HashSet<EConfigType> SingboxOnlyConfigType = SingboxSupportConfigType.Except(XraySupportConfigType).ToHashSet();
|
public static readonly HashSet<EConfigType> SingboxOnlyConfigType = SingboxSupportConfigType.Except(XraySupportConfigType).ToHashSet();
|
||||||
@@ -327,129 +332,129 @@ public class Global
|
|||||||
public static readonly List<string> DomainStrategies4Singbox =
|
public static readonly List<string> DomainStrategies4Singbox =
|
||||||
[
|
[
|
||||||
"ipv4_only",
|
"ipv4_only",
|
||||||
"ipv6_only",
|
"ipv6_only",
|
||||||
"prefer_ipv4",
|
"prefer_ipv4",
|
||||||
"prefer_ipv6",
|
"prefer_ipv6",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> Fingerprints =
|
public static readonly List<string> Fingerprints =
|
||||||
[
|
[
|
||||||
"chrome",
|
"chrome",
|
||||||
"firefox",
|
"firefox",
|
||||||
"safari",
|
"safari",
|
||||||
"ios",
|
"ios",
|
||||||
"android",
|
"android",
|
||||||
"edge",
|
"edge",
|
||||||
"360",
|
"360",
|
||||||
"qq",
|
"qq",
|
||||||
"random",
|
"random",
|
||||||
"randomized",
|
"randomized",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> UserAgent =
|
public static readonly List<string> UserAgent =
|
||||||
[
|
[
|
||||||
"chrome",
|
"chrome",
|
||||||
"firefox",
|
"firefox",
|
||||||
"safari",
|
"safari",
|
||||||
"edge",
|
"edge",
|
||||||
"none"
|
"none"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> XhttpMode =
|
public static readonly List<string> XhttpMode =
|
||||||
[
|
[
|
||||||
"auto",
|
"auto",
|
||||||
"packet-up",
|
"packet-up",
|
||||||
"stream-up",
|
"stream-up",
|
||||||
"stream-one"
|
"stream-one"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> AllowInsecure =
|
public static readonly List<string> AllowInsecure =
|
||||||
[
|
[
|
||||||
"true",
|
"true",
|
||||||
"false",
|
"false",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DomainStrategy4Freedoms =
|
public static readonly List<string> DomainStrategy4Freedoms =
|
||||||
[
|
[
|
||||||
"AsIs",
|
"AsIs",
|
||||||
"UseIP",
|
"UseIP",
|
||||||
"UseIPv4",
|
"UseIPv4",
|
||||||
"UseIPv6",
|
"UseIPv6",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SingboxDomainStrategy4Out =
|
public static readonly List<string> SingboxDomainStrategy4Out =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"ipv4_only",
|
"ipv4_only",
|
||||||
"prefer_ipv4",
|
"prefer_ipv4",
|
||||||
"prefer_ipv6",
|
"prefer_ipv6",
|
||||||
"ipv6_only"
|
"ipv6_only"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DomainDirectDNSAddress =
|
public static readonly List<string> DomainDirectDNSAddress =
|
||||||
[
|
[
|
||||||
"https://dns.alidns.com/dns-query",
|
"https://dns.alidns.com/dns-query",
|
||||||
"https://doh.pub/dns-query",
|
"https://doh.pub/dns-query",
|
||||||
"223.5.5.5",
|
"223.5.5.5",
|
||||||
"119.29.29.29",
|
"119.29.29.29",
|
||||||
"localhost"
|
"localhost"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DomainRemoteDNSAddress =
|
public static readonly List<string> DomainRemoteDNSAddress =
|
||||||
[
|
[
|
||||||
"https://cloudflare-dns.com/dns-query",
|
"https://cloudflare-dns.com/dns-query",
|
||||||
"https://dns.cloudflare.com/dns-query",
|
"https://dns.cloudflare.com/dns-query",
|
||||||
"https://dns.google/dns-query",
|
"https://dns.google/dns-query",
|
||||||
"https://doh.dns.sb/dns-query",
|
"https://doh.dns.sb/dns-query",
|
||||||
"https://doh.opendns.com/dns-query",
|
"https://doh.opendns.com/dns-query",
|
||||||
"https://common.dot.dns.yandex.net",
|
"https://common.dot.dns.yandex.net",
|
||||||
"8.8.8.8",
|
"8.8.8.8",
|
||||||
"1.1.1.1",
|
"1.1.1.1",
|
||||||
"185.222.222.222",
|
"185.222.222.222",
|
||||||
"208.67.222.222",
|
"208.67.222.222",
|
||||||
"77.88.8.8"
|
"77.88.8.8"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> DomainPureIPDNSAddress =
|
public static readonly List<string> DomainPureIPDNSAddress =
|
||||||
[
|
[
|
||||||
"223.5.5.5",
|
"223.5.5.5",
|
||||||
"119.29.29.29",
|
"119.29.29.29",
|
||||||
"localhost"
|
"localhost"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> Languages =
|
public static readonly List<string> Languages =
|
||||||
[
|
[
|
||||||
"zh-Hans",
|
"zh-Hans",
|
||||||
"zh-Hant",
|
"zh-Hant",
|
||||||
"en",
|
"en",
|
||||||
"fa-Ir",
|
"fa-Ir",
|
||||||
"fr",
|
"fr",
|
||||||
"ru",
|
"ru",
|
||||||
"hu"
|
"hu"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> Alpns =
|
public static readonly List<string> Alpns =
|
||||||
[
|
[
|
||||||
"h3",
|
"h3",
|
||||||
"h2",
|
"h2",
|
||||||
"http/1.1",
|
"http/1.1",
|
||||||
"h3,h2",
|
"h3,h2",
|
||||||
"h2,http/1.1",
|
"h2,http/1.1",
|
||||||
"h3,h2,http/1.1",
|
"h3,h2,http/1.1",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> LogLevels =
|
public static readonly List<string> LogLevels =
|
||||||
[
|
[
|
||||||
"debug",
|
"debug",
|
||||||
"info",
|
"info",
|
||||||
"warning",
|
"warning",
|
||||||
"error",
|
"error",
|
||||||
"none"
|
"none"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly Dictionary<string, string> LogLevelColors = new()
|
public static readonly Dictionary<string, string> LogLevelColors = new()
|
||||||
@@ -463,32 +468,32 @@ public class Global
|
|||||||
public static readonly List<string> InboundTags =
|
public static readonly List<string> InboundTags =
|
||||||
[
|
[
|
||||||
"socks",
|
"socks",
|
||||||
"socks2",
|
"socks2",
|
||||||
"socks3"
|
"socks3"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> RuleProtocols =
|
public static readonly List<string> RuleProtocols =
|
||||||
[
|
[
|
||||||
"http",
|
"http",
|
||||||
"tls",
|
"tls",
|
||||||
"bittorrent"
|
"bittorrent"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> RuleNetworks =
|
public static readonly List<string> RuleNetworks =
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
"tcp",
|
"tcp",
|
||||||
"udp",
|
"udp",
|
||||||
"tcp,udp"
|
"tcp,udp"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> destOverrideProtocols =
|
public static readonly List<string> destOverrideProtocols =
|
||||||
[
|
[
|
||||||
"http",
|
"http",
|
||||||
"tls",
|
"tls",
|
||||||
"quic",
|
"quic",
|
||||||
"fakedns",
|
"fakedns",
|
||||||
"fakedns+others"
|
"fakedns+others"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<int> TunMtus =
|
public static readonly List<int> TunMtus =
|
||||||
@@ -504,88 +509,87 @@ public class Global
|
|||||||
public static readonly List<string> TunStacks =
|
public static readonly List<string> TunStacks =
|
||||||
[
|
[
|
||||||
"gvisor",
|
"gvisor",
|
||||||
"system",
|
"system",
|
||||||
"mixed"
|
"mixed"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> PresetMsgFilters =
|
public static readonly List<string> PresetMsgFilters =
|
||||||
[
|
[
|
||||||
"proxy",
|
"proxy",
|
||||||
"direct",
|
"direct",
|
||||||
"block",
|
"block",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> SingboxMuxs =
|
public static readonly List<string> SingboxMuxs =
|
||||||
[
|
[
|
||||||
"h2mux",
|
"h2mux",
|
||||||
"smux",
|
"smux",
|
||||||
"yamux",
|
"yamux",
|
||||||
""
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> TuicCongestionControls =
|
public static readonly List<string> TuicCongestionControls =
|
||||||
[
|
[
|
||||||
"cubic",
|
"cubic",
|
||||||
"new_reno",
|
"new_reno",
|
||||||
"bbr"
|
"bbr"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> allowSelectType =
|
public static readonly List<string> allowSelectType =
|
||||||
[
|
[
|
||||||
"selector",
|
"selector",
|
||||||
"urltest",
|
"urltest",
|
||||||
"loadbalance",
|
"loadbalance",
|
||||||
"fallback"
|
"fallback"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> notAllowTestType =
|
public static readonly List<string> notAllowTestType =
|
||||||
[
|
[
|
||||||
"selector",
|
"selector",
|
||||||
"urltest",
|
"urltest",
|
||||||
"direct",
|
"direct",
|
||||||
"reject",
|
"reject",
|
||||||
"compatible",
|
"compatible",
|
||||||
"pass",
|
"pass",
|
||||||
"loadbalance",
|
"loadbalance",
|
||||||
"fallback"
|
"fallback"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> proxyVehicleType =
|
public static readonly List<string> proxyVehicleType =
|
||||||
[
|
[
|
||||||
"file",
|
"file",
|
||||||
"http"
|
"http"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly Dictionary<ECoreType, string> CoreUrls = new()
|
public static readonly Dictionary<ECoreType, string> CoreUrls = new()
|
||||||
{
|
{
|
||||||
{ ECoreType.v2fly, "v2fly/v2ray-core" },
|
{ ECoreType.v2fly, "v2fly/v2ray-core" },
|
||||||
{ ECoreType.v2fly_v5, "v2fly/v2ray-core" },
|
{ ECoreType.v2fly_v5, "v2fly/v2ray-core" },
|
||||||
{ ECoreType.Xray, "XTLS/Xray-core" },
|
{ ECoreType.Xray, "XTLS/Xray-core" },
|
||||||
{ ECoreType.sing_box, "SagerNet/sing-box" },
|
{ ECoreType.sing_box, "SagerNet/sing-box" },
|
||||||
{ ECoreType.mihomo, "MetaCubeX/mihomo" },
|
{ ECoreType.mihomo, "MetaCubeX/mihomo" },
|
||||||
{ ECoreType.hysteria, "apernet/hysteria" },
|
{ ECoreType.hysteria, "apernet/hysteria" },
|
||||||
{ ECoreType.hysteria2, "apernet/hysteria" },
|
{ ECoreType.hysteria2, "apernet/hysteria" },
|
||||||
{ ECoreType.naiveproxy, "klzgrad/naiveproxy" },
|
{ ECoreType.naiveproxy, "klzgrad/naiveproxy" },
|
||||||
{ ECoreType.tuic, "EAimTY/tuic" },
|
{ ECoreType.tuic, "EAimTY/tuic" },
|
||||||
{ ECoreType.juicity, "juicity/juicity" },
|
{ ECoreType.juicity, "juicity/juicity" },
|
||||||
{ ECoreType.brook, "txthinking/brook" },
|
{ ECoreType.brook, "txthinking/brook" },
|
||||||
{ ECoreType.overtls, "ShadowsocksR-Live/overtls" },
|
{ ECoreType.overtls, "ShadowsocksR-Live/overtls" },
|
||||||
{ ECoreType.shadowquic, "spongebob888/shadowquic" },
|
{ ECoreType.shadowquic, "spongebob888/shadowquic" },
|
||||||
{ ECoreType.mieru, "enfein/mieru" },
|
{ ECoreType.mieru, "enfein/mieru" },
|
||||||
{ ECoreType.v2rayN, "2dust/v2rayN" },
|
{ ECoreType.v2rayN, "2dust/v2rayN" },
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly List<string> OtherGeoUrls =
|
public static readonly List<string> OtherGeoUrls =
|
||||||
[
|
[
|
||||||
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat",
|
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat",
|
||||||
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb",
|
@"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb",
|
||||||
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
|
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
|
||||||
];
|
];
|
||||||
|
|
||||||
public static readonly List<string> IPAPIUrls =
|
public static readonly List<string> IPAPIUrls =
|
||||||
[
|
[
|
||||||
@"https://speed.cloudflare.com/meta",
|
|
||||||
@"https://api.ip.sb/geoip",
|
@"https://api.ip.sb/geoip",
|
||||||
@"https://api-ipv4.ip.sb/geoip",
|
@"https://api-ipv4.ip.sb/geoip",
|
||||||
@"https://api-ipv6.ip.sb/geoip",
|
@"https://api-ipv6.ip.sb/geoip",
|
||||||
@@ -601,29 +605,37 @@ public class Global
|
|||||||
];
|
];
|
||||||
|
|
||||||
public static readonly Dictionary<string, List<string>> PredefinedHosts = new()
|
public static readonly Dictionary<string, List<string>> PredefinedHosts = new()
|
||||||
{
|
{
|
||||||
{ "dns.google", new List<string> { "8.8.8.8", "8.8.4.4", "2001:4860:4860::8888", "2001:4860:4860::8844" } },
|
{ "dns.google", new List<string> { "8.8.8.8", "8.8.4.4", "2001:4860:4860::8888", "2001:4860:4860::8844" } },
|
||||||
{ "dns.alidns.com", new List<string> { "223.5.5.5", "223.6.6.6", "2400:3200::1", "2400:3200:baba::1" } },
|
{ "dns.alidns.com", new List<string> { "223.5.5.5", "223.6.6.6", "2400:3200::1", "2400:3200:baba::1" } },
|
||||||
{ "one.one.one.one", new List<string> { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } },
|
{ "one.one.one.one", new List<string> { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } },
|
||||||
{ "1dot1dot1dot1.cloudflare-dns.com", new List<string> { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } },
|
{ "1dot1dot1dot1.cloudflare-dns.com", new List<string> { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } },
|
||||||
{ "cloudflare-dns.com", new List<string> { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } },
|
{ "cloudflare-dns.com", new List<string> { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } },
|
||||||
{ "dns.cloudflare.com", new List<string> { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } },
|
{ "dns.cloudflare.com", new List<string> { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } },
|
||||||
{ "dot.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
|
{ "dot.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
|
||||||
{ "doh.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
|
{ "doh.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
|
||||||
{ "dns.quad9.net", new List<string> { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } },
|
{ "dns.quad9.net", new List<string> { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } },
|
||||||
{ "dns.yandex.net", new List<string> { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } },
|
{ "dns.yandex.net", new List<string> { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } },
|
||||||
{ "dns.sb", new List<string> { "185.222.222.222", "2a09::" } },
|
{ "dns.sb", new List<string> { "185.222.222.222", "2a09::" } },
|
||||||
{ "dns.umbrella.com", new List<string> { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } },
|
{ "dns.umbrella.com", new List<string> { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } },
|
||||||
{ "dns.sse.cisco.com", new List<string> { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } },
|
{ "dns.sse.cisco.com", new List<string> { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } },
|
||||||
{ "engage.cloudflareclient.com", new List<string> { "162.159.192.1", "2606:4700:d0::a29f:c001" } }
|
{ "engage.cloudflareclient.com", new List<string> { "162.159.192.1", "2606:4700:d0::a29f:c001" } }
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly List<string> ExpectedIPs =
|
public static readonly List<string> ExpectedIPs =
|
||||||
[
|
[
|
||||||
"geoip:cn",
|
"geoip:cn",
|
||||||
"geoip:ir",
|
"geoip:ir",
|
||||||
"geoip:ru",
|
"geoip:ru",
|
||||||
""
|
""
|
||||||
|
];
|
||||||
|
|
||||||
|
public static readonly List<string> EchForceQuerys =
|
||||||
|
[
|
||||||
|
"none",
|
||||||
|
"half",
|
||||||
|
"full",
|
||||||
|
""
|
||||||
];
|
];
|
||||||
|
|
||||||
#endregion const
|
#endregion const
|
||||||
|
|||||||
@@ -253,6 +253,9 @@ public static class ConfigHandler
|
|||||||
item.Extra = profileItem.Extra;
|
item.Extra = profileItem.Extra;
|
||||||
item.MuxEnabled = profileItem.MuxEnabled;
|
item.MuxEnabled = profileItem.MuxEnabled;
|
||||||
item.Cert = profileItem.Cert;
|
item.Cert = profileItem.Cert;
|
||||||
|
item.CertSha = profileItem.CertSha;
|
||||||
|
item.EchConfigList = profileItem.EchConfigList;
|
||||||
|
item.EchForceQuery = profileItem.EchForceQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret = item.ConfigType switch
|
var ret = item.ConfigType switch
|
||||||
@@ -700,7 +703,7 @@ public static class ConfigHandler
|
|||||||
public static async Task<int> AddHysteria2Server(Config config, ProfileItem profileItem, bool toFile = true)
|
public static async Task<int> AddHysteria2Server(Config config, ProfileItem profileItem, bool toFile = true)
|
||||||
{
|
{
|
||||||
profileItem.ConfigType = EConfigType.Hysteria2;
|
profileItem.ConfigType = EConfigType.Hysteria2;
|
||||||
profileItem.CoreType = ECoreType.sing_box;
|
//profileItem.CoreType = ECoreType.sing_box;
|
||||||
|
|
||||||
profileItem.Address = profileItem.Address.TrimEx();
|
profileItem.Address = profileItem.Address.TrimEx();
|
||||||
profileItem.Id = profileItem.Id.TrimEx();
|
profileItem.Id = profileItem.Id.TrimEx();
|
||||||
@@ -1273,7 +1276,7 @@ public static class ConfigHandler
|
|||||||
}
|
}
|
||||||
else if (node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)
|
else if (node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0)
|
||||||
{
|
{
|
||||||
var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
var preCoreType = AppManager.Instance.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
||||||
itemSocks = new ProfileItem()
|
itemSocks = new ProfileItem()
|
||||||
{
|
{
|
||||||
CoreType = preCoreType,
|
CoreType = preCoreType,
|
||||||
|
|||||||
@@ -70,6 +70,14 @@ public class BaseFmt
|
|||||||
}
|
}
|
||||||
ToUriQueryAllowInsecure(item, ref dicQuery);
|
ToUriQueryAllowInsecure(item, ref dicQuery);
|
||||||
}
|
}
|
||||||
|
if (item.EchConfigList.IsNotEmpty())
|
||||||
|
{
|
||||||
|
dicQuery.Add("ech", Utils.UrlEncode(item.EchConfigList));
|
||||||
|
}
|
||||||
|
if (item.CertSha.IsNotEmpty())
|
||||||
|
{
|
||||||
|
dicQuery.Add("pcs", Utils.UrlEncode(item.CertSha));
|
||||||
|
}
|
||||||
|
|
||||||
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
|
dicQuery.Add("type", item.Network.IsNotEmpty() ? item.Network : nameof(ETransport.tcp));
|
||||||
|
|
||||||
@@ -118,7 +126,16 @@ public class BaseFmt
|
|||||||
}
|
}
|
||||||
if (item.Extra.IsNotEmpty())
|
if (item.Extra.IsNotEmpty())
|
||||||
{
|
{
|
||||||
dicQuery.Add("extra", Utils.UrlEncode(item.Extra));
|
var node = JsonUtils.ParseJson(item.Extra);
|
||||||
|
var extra = node != null
|
||||||
|
? JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = false,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
})
|
||||||
|
: item.Extra;
|
||||||
|
dicQuery.Add("extra", Utils.UrlEncode(extra));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -200,6 +217,8 @@ public class BaseFmt
|
|||||||
item.ShortId = GetQueryDecoded(query, "sid");
|
item.ShortId = GetQueryDecoded(query, "sid");
|
||||||
item.SpiderX = GetQueryDecoded(query, "spx");
|
item.SpiderX = GetQueryDecoded(query, "spx");
|
||||||
item.Mldsa65Verify = GetQueryDecoded(query, "pqv");
|
item.Mldsa65Verify = GetQueryDecoded(query, "pqv");
|
||||||
|
item.EchConfigList = GetQueryDecoded(query, "ech");
|
||||||
|
item.CertSha = GetQueryDecoded(query, "pcs");
|
||||||
|
|
||||||
if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1"))
|
if (_allowInsecureArray.Any(k => GetQueryDecoded(query, k) == "1"))
|
||||||
{
|
{
|
||||||
@@ -237,7 +256,21 @@ public class BaseFmt
|
|||||||
item.RequestHost = GetQueryDecoded(query, "host");
|
item.RequestHost = GetQueryDecoded(query, "host");
|
||||||
item.Path = GetQueryDecoded(query, "path", "/");
|
item.Path = GetQueryDecoded(query, "path", "/");
|
||||||
item.HeaderType = GetQueryDecoded(query, "mode");
|
item.HeaderType = GetQueryDecoded(query, "mode");
|
||||||
item.Extra = GetQueryDecoded(query, "extra");
|
var extraDecoded = GetQueryDecoded(query, "extra");
|
||||||
|
if (extraDecoded.IsNotEmpty())
|
||||||
|
{
|
||||||
|
var node = JsonUtils.ParseJson(extraDecoded);
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
extraDecoded = JsonUtils.Serialize(node, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = true,
|
||||||
|
DefaultIgnoreCondition = JsonIgnoreCondition.Never,
|
||||||
|
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.Extra = extraDecoded;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.http):
|
case nameof(ETransport.http):
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ public class Hysteria2Fmt : BaseFmt
|
|||||||
ResolveUriQuery(query, ref item);
|
ResolveUriQuery(query, ref item);
|
||||||
item.Path = GetQueryDecoded(query, "obfs-password");
|
item.Path = GetQueryDecoded(query, "obfs-password");
|
||||||
item.Ports = GetQueryDecoded(query, "mport");
|
item.Ports = GetQueryDecoded(query, "mport");
|
||||||
|
if (item.CertSha.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
item.CertSha = GetQueryDecoded(query, "pinSHA256");
|
||||||
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
@@ -55,6 +59,16 @@ public class Hysteria2Fmt : BaseFmt
|
|||||||
{
|
{
|
||||||
dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-')));
|
dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-')));
|
||||||
}
|
}
|
||||||
|
if (!item.CertSha.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
var sha = item.CertSha;
|
||||||
|
var idx = sha.IndexOf('~');
|
||||||
|
if (idx > 0)
|
||||||
|
{
|
||||||
|
sha = sha[..idx];
|
||||||
|
}
|
||||||
|
dicQuery.Add("pinSHA256", Utils.UrlEncode(sha));
|
||||||
|
}
|
||||||
|
|
||||||
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
|
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,68 @@ public class ShadowsocksFmt : BaseFmt
|
|||||||
//url = Utile.Base64Encode(url);
|
//url = Utile.Base64Encode(url);
|
||||||
//new Sip002
|
//new Sip002
|
||||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true);
|
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}", true);
|
||||||
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, null, remark);
|
|
||||||
|
// plugin
|
||||||
|
var plugin = string.Empty;
|
||||||
|
var pluginArgs = string.Empty;
|
||||||
|
|
||||||
|
if (item.Network == nameof(ETransport.tcp) && item.HeaderType == Global.TcpHeaderHttp)
|
||||||
|
{
|
||||||
|
plugin = "obfs-local";
|
||||||
|
pluginArgs = $"obfs=http;obfs-host={item.RequestHost};";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (item.Network == nameof(ETransport.ws))
|
||||||
|
{
|
||||||
|
pluginArgs += "mode=websocket;";
|
||||||
|
pluginArgs += $"host={item.RequestHost};";
|
||||||
|
// https://github.com/shadowsocks/v2ray-plugin/blob/e9af1cdd2549d528deb20a4ab8d61c5fbe51f306/args.go#L172
|
||||||
|
// Equal signs and commas [and backslashes] must be escaped with a backslash.
|
||||||
|
var path = item.Path.Replace("\\", "\\\\").Replace("=", "\\=").Replace(",", "\\,");
|
||||||
|
pluginArgs += $"path={path};";
|
||||||
|
}
|
||||||
|
else if (item.Network == nameof(ETransport.quic))
|
||||||
|
{
|
||||||
|
pluginArgs += "mode=quic;";
|
||||||
|
}
|
||||||
|
if (item.StreamSecurity == Global.StreamSecurity)
|
||||||
|
{
|
||||||
|
pluginArgs += "tls;";
|
||||||
|
var certs = CertPemManager.ParsePemChain(item.Cert);
|
||||||
|
if (certs.Count > 0)
|
||||||
|
{
|
||||||
|
var cert = certs.First();
|
||||||
|
const string beginMarker = "-----BEGIN CERTIFICATE-----\n";
|
||||||
|
const string endMarker = "\n-----END CERTIFICATE-----";
|
||||||
|
|
||||||
|
var base64Content = cert.Replace(beginMarker, "").Replace(endMarker, "").Trim();
|
||||||
|
|
||||||
|
base64Content = base64Content.Replace("=", "\\=");
|
||||||
|
|
||||||
|
pluginArgs += $"certRaw={base64Content};";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pluginArgs.Length > 0)
|
||||||
|
{
|
||||||
|
plugin = "v2ray-plugin";
|
||||||
|
pluginArgs += "mux=0;";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dicQuery = new Dictionary<string, string>();
|
||||||
|
if (plugin.IsNotEmpty())
|
||||||
|
{
|
||||||
|
var pluginStr = plugin + ";" + pluginArgs;
|
||||||
|
// pluginStr remove last ';' and url encode
|
||||||
|
if (pluginStr.EndsWith(';'))
|
||||||
|
{
|
||||||
|
pluginStr = pluginStr[..^1];
|
||||||
|
}
|
||||||
|
dicQuery["plugin"] = Utils.UrlEncode(pluginStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, dicQuery, remark);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
@@ -124,19 +185,95 @@ public class ShadowsocksFmt : BaseFmt
|
|||||||
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
||||||
if (queryParameters["plugin"] != null)
|
if (queryParameters["plugin"] != null)
|
||||||
{
|
{
|
||||||
//obfs-host exists
|
var pluginStr = queryParameters["plugin"];
|
||||||
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
|
var pluginParts = pluginStr.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
if (queryParameters["plugin"].Contains("obfs=http") && obfsHost.IsNotEmpty())
|
|
||||||
{
|
if (pluginParts.Length == 0)
|
||||||
obfsHost = obfsHost?.Replace("obfs-host=", "");
|
|
||||||
item.Network = Global.DefaultNetwork;
|
|
||||||
item.HeaderType = Global.TcpHeaderHttp;
|
|
||||||
item.RequestHost = obfsHost ?? "";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pluginName = pluginParts[0];
|
||||||
|
|
||||||
|
// A typo in https://github.com/shadowsocks/shadowsocks-org/blob/6b1c064db4129de99c516294960e731934841c94/docs/doc/sip002.md?plain=1#L15
|
||||||
|
// "simple-obfs" should be "obfs-local"
|
||||||
|
if (pluginName == "simple-obfs")
|
||||||
|
{
|
||||||
|
pluginName = "obfs-local";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse obfs-local plugin
|
||||||
|
if (pluginName == "obfs-local")
|
||||||
|
{
|
||||||
|
var obfsMode = pluginParts.FirstOrDefault(t => t.StartsWith("obfs="));
|
||||||
|
var obfsHost = pluginParts.FirstOrDefault(t => t.StartsWith("obfs-host="));
|
||||||
|
|
||||||
|
if ((!obfsMode.IsNullOrEmpty()) && obfsMode.Contains("obfs=http") && obfsHost.IsNotEmpty())
|
||||||
|
{
|
||||||
|
obfsHost = obfsHost.Replace("obfs-host=", "");
|
||||||
|
item.Network = Global.DefaultNetwork;
|
||||||
|
item.HeaderType = Global.TcpHeaderHttp;
|
||||||
|
item.RequestHost = obfsHost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Parse v2ray-plugin
|
||||||
|
else if (pluginName == "v2ray-plugin")
|
||||||
|
{
|
||||||
|
var mode = pluginParts.FirstOrDefault(t => t.StartsWith("mode="), "websocket");
|
||||||
|
var host = pluginParts.FirstOrDefault(t => t.StartsWith("host="));
|
||||||
|
var path = pluginParts.FirstOrDefault(t => t.StartsWith("path="));
|
||||||
|
var hasTls = pluginParts.Any(t => t == "tls");
|
||||||
|
var certRaw = pluginParts.FirstOrDefault(t => t.StartsWith("certRaw="));
|
||||||
|
var mux = pluginParts.FirstOrDefault(t => t.StartsWith("mux="));
|
||||||
|
|
||||||
|
var modeValue = mode.Replace("mode=", "");
|
||||||
|
if (modeValue == "websocket")
|
||||||
|
{
|
||||||
|
item.Network = nameof(ETransport.ws);
|
||||||
|
if (!host.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
item.RequestHost = host.Replace("host=", "");
|
||||||
|
item.Sni = item.RequestHost;
|
||||||
|
}
|
||||||
|
if (!path.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
var pathValue = path.Replace("path=", "");
|
||||||
|
pathValue = pathValue.Replace("\\=", "=").Replace("\\,", ",").Replace("\\\\", "\\");
|
||||||
|
item.Path = pathValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (modeValue == "quic")
|
||||||
|
{
|
||||||
|
item.Network = nameof(ETransport.quic);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasTls)
|
||||||
|
{
|
||||||
|
item.StreamSecurity = Global.StreamSecurity;
|
||||||
|
|
||||||
|
if (!certRaw.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
var certBase64 = certRaw.Replace("certRaw=", "");
|
||||||
|
|
||||||
|
certBase64 = certBase64.Replace("\\=", "=");
|
||||||
|
|
||||||
|
const string beginMarker = "-----BEGIN CERTIFICATE-----\n";
|
||||||
|
const string endMarker = "\n-----END CERTIFICATE-----";
|
||||||
|
var certPem = beginMarker + certBase64 + endMarker;
|
||||||
|
item.Cert = certPem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mux.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
var muxValue = mux.Replace("mux=", "");
|
||||||
|
var muxCount = muxValue.ToInt();
|
||||||
|
if (muxCount > 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
|
|||||||
@@ -71,28 +71,25 @@ public class DownloaderHelper
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var totalDatetime = DateTime.Now;
|
var lastUpdateTime = DateTime.Now;
|
||||||
var totalSecond = 0;
|
|
||||||
var hasValue = false;
|
var hasValue = false;
|
||||||
double maxSpeed = 0;
|
double maxSpeed = 0;
|
||||||
await using var downloader = new Downloader.DownloadService(downloadOpt);
|
await using var downloader = new Downloader.DownloadService(downloadOpt);
|
||||||
//downloader.DownloadStarted += (sender, value) =>
|
|
||||||
//{
|
|
||||||
// if (progress != null)
|
|
||||||
// {
|
|
||||||
// progress.Report("Start download data...");
|
|
||||||
// }
|
|
||||||
//};
|
|
||||||
downloader.DownloadProgressChanged += (sender, value) =>
|
downloader.DownloadProgressChanged += (sender, value) =>
|
||||||
{
|
{
|
||||||
var ts = DateTime.Now - totalDatetime;
|
if (progress != null && value.BytesPerSecondSpeed > 0)
|
||||||
if (progress != null && ts.Seconds > totalSecond)
|
|
||||||
{
|
{
|
||||||
hasValue = true;
|
hasValue = true;
|
||||||
totalSecond = ts.Seconds;
|
|
||||||
if (value.BytesPerSecondSpeed > maxSpeed)
|
if (value.BytesPerSecondSpeed > maxSpeed)
|
||||||
{
|
{
|
||||||
maxSpeed = value.BytesPerSecondSpeed;
|
maxSpeed = value.BytesPerSecondSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ts = DateTime.Now - lastUpdateTime;
|
||||||
|
if (ts.TotalMilliseconds >= 1000)
|
||||||
|
{
|
||||||
|
lastUpdateTime = DateTime.Now;
|
||||||
var speed = (maxSpeed / 1000 / 1000).ToString("#0.0");
|
var speed = (maxSpeed / 1000 / 1000).ToString("#0.0");
|
||||||
progress.Report(speed);
|
progress.Report(speed);
|
||||||
}
|
}
|
||||||
@@ -102,10 +99,19 @@ public class DownloaderHelper
|
|||||||
{
|
{
|
||||||
if (progress != null)
|
if (progress != null)
|
||||||
{
|
{
|
||||||
if (!hasValue && value.Error != null)
|
if (hasValue && maxSpeed > 0)
|
||||||
|
{
|
||||||
|
var finalSpeed = (maxSpeed / 1000 / 1000).ToString("#0.0");
|
||||||
|
progress.Report(finalSpeed);
|
||||||
|
}
|
||||||
|
else if (value.Error != null)
|
||||||
{
|
{
|
||||||
progress.Report(value.Error?.Message);
|
progress.Report(value.Error?.Message);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
progress.Report("0");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
//progress.Report("......");
|
//progress.Report("......");
|
||||||
|
|||||||
@@ -3,12 +3,19 @@ namespace ServiceLib.Manager;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Centralized pre-checks before sensitive actions (set active profile, generate config, etc.).
|
/// Centralized pre-checks before sensitive actions (set active profile, generate config, etc.).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ActionPrecheckManager(Config config)
|
public class ActionPrecheckManager
|
||||||
{
|
{
|
||||||
private static readonly Lazy<ActionPrecheckManager> _instance = new(() => new ActionPrecheckManager(AppManager.Instance.Config));
|
private static readonly Lazy<ActionPrecheckManager> _instance = new();
|
||||||
public static ActionPrecheckManager Instance => _instance.Value;
|
public static ActionPrecheckManager Instance => _instance.Value;
|
||||||
|
|
||||||
private readonly Config _config = config;
|
// sing-box supported transports for different protocol types
|
||||||
|
private static readonly HashSet<string> SingboxUnsupportedTransports = [nameof(ETransport.kcp), nameof(ETransport.xhttp)];
|
||||||
|
|
||||||
|
private static readonly HashSet<EConfigType> SingboxTransportSupportedProtocols =
|
||||||
|
[EConfigType.VMess, EConfigType.VLESS, EConfigType.Trojan, EConfigType.Shadowsocks];
|
||||||
|
|
||||||
|
private static readonly HashSet<string> SingboxShadowsocksAllowedTransports =
|
||||||
|
[nameof(ETransport.tcp), nameof(ETransport.ws), nameof(ETransport.quic)];
|
||||||
|
|
||||||
public async Task<List<string>> Check(string? indexId)
|
public async Task<List<string>> Check(string? indexId)
|
||||||
{
|
{
|
||||||
@@ -47,6 +54,7 @@ public class ActionPrecheckManager(Config config)
|
|||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
|
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
|
||||||
return await ValidateNodeAndCoreSupport(item, coreType);
|
return await ValidateNodeAndCoreSupport(item, coreType);
|
||||||
}
|
}
|
||||||
@@ -62,153 +70,209 @@ public class ActionPrecheckManager(Config config)
|
|||||||
errors.Add(string.Format(ResUI.NotSupportProtocol, item.ConfigType.ToString()));
|
errors.Add(string.Format(ResUI.NotSupportProtocol, item.ConfigType.ToString()));
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
else if (item.ConfigType.IsGroupType())
|
||||||
if (!item.IsComplex())
|
|
||||||
{
|
{
|
||||||
if (item.Address.IsNullOrEmpty())
|
var groupErrors = await ValidateGroupNode(item, coreType);
|
||||||
{
|
errors.AddRange(groupErrors);
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Address"));
|
return errors;
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.Port is <= 0 or >= 65536)
|
|
||||||
{
|
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Port"));
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (item.ConfigType)
|
|
||||||
{
|
|
||||||
case EConfigType.VMess:
|
|
||||||
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
|
||||||
{
|
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.VLESS:
|
|
||||||
if (item.Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Id) && item.Id.Length > 30))
|
|
||||||
{
|
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Global.Flows.Contains(item.Flow))
|
|
||||||
{
|
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EConfigType.Shadowsocks:
|
|
||||||
if (item.Id.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(item.Security) || !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
|
||||||
{
|
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "Security"));
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item.ConfigType is EConfigType.VLESS or EConfigType.Trojan
|
|
||||||
&& item.StreamSecurity == Global.StreamSecurityReality
|
|
||||||
&& item.PublicKey.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
errors.Add(string.Format(ResUI.InvalidProperty, "PublicKey"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errors.Count > 0)
|
|
||||||
{
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if (!item.IsComplex())
|
||||||
if (item.ConfigType.IsGroupType())
|
|
||||||
{
|
{
|
||||||
ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group);
|
var normalErrors = await ValidateNormalNode(item, coreType);
|
||||||
if (group is null || group.NotHasChild())
|
errors.AddRange(normalErrors);
|
||||||
{
|
|
||||||
errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks));
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasCycle = ProfileGroupItemManager.HasCycle(item.IndexId);
|
|
||||||
if (hasCycle)
|
|
||||||
{
|
|
||||||
errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks));
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
var childIds = Utils.String2List(group.ChildItems) ?? [];
|
|
||||||
var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group);
|
|
||||||
childIds.AddRange(subItems.Select(p => p.IndexId));
|
|
||||||
|
|
||||||
foreach (var child in childIds)
|
|
||||||
{
|
|
||||||
var childErrors = new List<string>();
|
|
||||||
if (child.IsNullOrEmpty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var childItem = await AppManager.Instance.GetProfileItem(child);
|
|
||||||
if (childItem is null)
|
|
||||||
{
|
|
||||||
childErrors.Add(string.Format(ResUI.NodeTagNotExist, child));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (childItem.ConfigType is EConfigType.Custom or EConfigType.ProxyChain)
|
|
||||||
{
|
|
||||||
childErrors.Add(string.Format(ResUI.InvalidProperty, childItem.Remarks));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
childErrors.AddRange(await ValidateNodeAndCoreSupport(childItem, coreType));
|
|
||||||
errors.AddRange(childErrors);
|
|
||||||
}
|
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
var net = item.GetNetwork() ?? item.Network;
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<List<string>> ValidateNormalNode(ProfileItem item, ECoreType? coreType = null)
|
||||||
|
{
|
||||||
|
var errors = new List<string>();
|
||||||
|
|
||||||
|
if (item.Address.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Address"));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.Port is <= 0 or > 65535)
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Port"));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
var net = item.GetNetwork();
|
||||||
|
|
||||||
if (coreType == ECoreType.sing_box)
|
if (coreType == ECoreType.sing_box)
|
||||||
{
|
{
|
||||||
// sing-box does not support xhttp / kcp
|
var transportError = ValidateSingboxTransport(item.ConfigType, net);
|
||||||
// sing-box does not support transports like ws/http/httpupgrade/etc. when the node is not vmess/trojan/vless
|
if (transportError != null)
|
||||||
if (net is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.CoreNotSupportNetwork, nameof(ECoreType.sing_box), net));
|
errors.Add(transportError);
|
||||||
return errors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.ConfigType is not (EConfigType.VMess or EConfigType.VLESS or EConfigType.Trojan))
|
if (!Global.SingboxSupportConfigType.Contains(item.ConfigType))
|
||||||
{
|
{
|
||||||
if (net is nameof(ETransport.ws) or nameof(ETransport.http) or nameof(ETransport.h2) or nameof(ETransport.quic) or nameof(ETransport.httpupgrade))
|
errors.Add(string.Format(ResUI.CoreNotSupportProtocol,
|
||||||
{
|
nameof(ECoreType.sing_box), item.ConfigType.ToString()));
|
||||||
errors.Add(string.Format(ResUI.CoreNotSupportProtocolTransport, nameof(ECoreType.sing_box), item.ConfigType.ToString(), net));
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (coreType is ECoreType.Xray)
|
else if (coreType is ECoreType.Xray)
|
||||||
{
|
{
|
||||||
// Xray core does not support these protocols
|
// Xray core does not support these protocols
|
||||||
if (!Global.XraySupportConfigType.Contains(item.ConfigType)
|
if (!Global.XraySupportConfigType.Contains(item.ConfigType))
|
||||||
&& !item.IsComplex())
|
|
||||||
{
|
{
|
||||||
errors.Add(string.Format(ResUI.CoreNotSupportProtocol, nameof(ECoreType.Xray), item.ConfigType.ToString()));
|
errors.Add(string.Format(ResUI.CoreNotSupportProtocol,
|
||||||
return errors;
|
nameof(ECoreType.Xray), item.ConfigType.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (item.ConfigType)
|
||||||
|
{
|
||||||
|
case EConfigType.VMess:
|
||||||
|
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id))
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.VLESS:
|
||||||
|
if (item.Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Id) && item.Id.Length > 30))
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Global.Flows.Contains(item.Flow))
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EConfigType.Shadowsocks:
|
||||||
|
if (item.Id.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(item.Security) || !Global.SsSecuritiesInSingbox.Contains(item.Security))
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "Security"));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.StreamSecurity == Global.StreamSecurity)
|
||||||
|
{
|
||||||
|
// check certificate validity
|
||||||
|
if (!item.Cert.IsNullOrEmpty()
|
||||||
|
&& (CertPemManager.ParsePemChain(item.Cert).Count == 0)
|
||||||
|
&& !item.CertSha.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "TLS Certificate"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.StreamSecurity == Global.StreamSecurityReality)
|
||||||
|
{
|
||||||
|
if (item.PublicKey.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "PublicKey"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.Network == nameof(ETransport.xhttp)
|
||||||
|
&& !item.Extra.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
// check xhttp extra json validity
|
||||||
|
var xhttpExtra = JsonUtils.ParseJson(item.Extra);
|
||||||
|
if (xhttpExtra is null)
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.InvalidProperty, "XHTTP Extra"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<List<string>> ValidateGroupNode(ProfileItem item, ECoreType? coreType = null)
|
||||||
|
{
|
||||||
|
var errors = new List<string>();
|
||||||
|
|
||||||
|
ProfileGroupItemManager.Instance.TryGet(item.IndexId, out var group);
|
||||||
|
if (group is null || group.NotHasChild())
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.GroupEmpty, item.Remarks));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasCycle = ProfileGroupItemManager.HasCycle(item.IndexId);
|
||||||
|
if (hasCycle)
|
||||||
|
{
|
||||||
|
errors.Add(string.Format(ResUI.GroupSelfReference, item.Remarks));
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
var childIds = new List<string>();
|
||||||
|
var subItems = await ProfileGroupItemManager.GetSubChildProfileItems(group);
|
||||||
|
childIds.AddRangeSafe(subItems.Select(p => p.IndexId));
|
||||||
|
childIds.AddRangeSafe(Utils.String2List(group.ChildItems));
|
||||||
|
|
||||||
|
foreach (var child in childIds)
|
||||||
|
{
|
||||||
|
var childErrors = new List<string>();
|
||||||
|
if (child.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var childItem = await AppManager.Instance.GetProfileItem(child);
|
||||||
|
if (childItem is null)
|
||||||
|
{
|
||||||
|
childErrors.Add(string.Format(ResUI.NodeTagNotExist, child));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childItem.ConfigType is EConfigType.Custom or EConfigType.ProxyChain)
|
||||||
|
{
|
||||||
|
childErrors.Add(string.Format(ResUI.InvalidProperty, childItem.Remarks));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
childErrors.AddRange(await ValidateNodeAndCoreSupport(childItem, coreType));
|
||||||
|
errors.AddRange(childErrors.Select(s => s.Insert(0, $"{childItem.Remarks}: ")));
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? ValidateSingboxTransport(EConfigType configType, string net)
|
||||||
|
{
|
||||||
|
// sing-box does not support xhttp / kcp transports
|
||||||
|
if (SingboxUnsupportedTransports.Contains(net))
|
||||||
|
{
|
||||||
|
return string.Format(ResUI.CoreNotSupportNetwork, nameof(ECoreType.sing_box), net);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sing-box does not support non-tcp transports for protocols other than vmess/trojan/vless/shadowsocks
|
||||||
|
if (!SingboxTransportSupportedProtocols.Contains(configType) && net != nameof(ETransport.tcp))
|
||||||
|
{
|
||||||
|
return string.Format(ResUI.CoreNotSupportProtocolTransport,
|
||||||
|
nameof(ECoreType.sing_box), configType.ToString(), net);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sing-box shadowsocks only supports tcp/ws/quic transports
|
||||||
|
if (configType == EConfigType.Shadowsocks && !SingboxShadowsocksAllowedTransports.Contains(net))
|
||||||
|
{
|
||||||
|
return string.Format(ResUI.CoreNotSupportProtocolTransport,
|
||||||
|
nameof(ECoreType.sing_box), configType.ToString(), net);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<List<string>> ValidateRelatedNodesExistAndValid(ProfileItem? item)
|
private async Task<List<string>> ValidateRelatedNodesExistAndValid(ProfileItem? item)
|
||||||
{
|
{
|
||||||
var errors = new List<string>();
|
var errors = new List<string>();
|
||||||
@@ -247,7 +311,7 @@ public class ActionPrecheckManager(Config config)
|
|||||||
if (node is not null)
|
if (node is not null)
|
||||||
{
|
{
|
||||||
var nodeErrors = await ValidateNodeAndCoreSupport(node, coreType);
|
var nodeErrors = await ValidateNodeAndCoreSupport(node, coreType);
|
||||||
errors.AddRange(nodeErrors.Select(s => ResUI.ProxyChainedPrefix + s));
|
errors.AddRange(nodeErrors.Select(s => ResUI.ProxyChainedPrefix + $"{node.Remarks}: " + s));
|
||||||
}
|
}
|
||||||
else if (tag.IsNotEmpty())
|
else if (tag.IsNotEmpty())
|
||||||
{
|
{
|
||||||
@@ -265,7 +329,7 @@ public class ActionPrecheckManager(Config config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
|
var coreType = AppManager.Instance.GetCoreType(item, item.ConfigType);
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var routing = await ConfigHandler.GetDefaultRouting(AppManager.Instance.Config);
|
||||||
if (routing == null)
|
if (routing == null)
|
||||||
{
|
{
|
||||||
return errors;
|
return errors;
|
||||||
@@ -293,7 +357,7 @@ public class ActionPrecheckManager(Config config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tagErrors = await ValidateNodeAndCoreSupport(tagItem, coreType);
|
var tagErrors = await ValidateNodeAndCoreSupport(tagItem, coreType);
|
||||||
errors.AddRange(tagErrors.Select(s => ResUI.RoutingRuleOutboundPrefix + s));
|
errors.AddRange(tagErrors.Select(s => ResUI.RoutingRuleOutboundPrefix + $"{tagItem.Remarks}: " + s));
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors;
|
return errors;
|
||||||
|
|||||||
@@ -31,6 +31,23 @@ public sealed class AppManager
|
|||||||
|
|
||||||
public string LinuxSudoPwd { get; set; }
|
public string LinuxSudoPwd { get; set; }
|
||||||
|
|
||||||
|
public bool ShowInTaskbar { get; set; }
|
||||||
|
|
||||||
|
public ECoreType RunningCoreType { get; set; }
|
||||||
|
|
||||||
|
public bool IsRunningCore(ECoreType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ECoreType.Xray when RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5:
|
||||||
|
case ECoreType.sing_box when RunningCoreType is ECoreType.sing_box or ECoreType.mihomo:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Property
|
#endregion Property
|
||||||
|
|
||||||
#region App
|
#region App
|
||||||
|
|||||||
@@ -416,4 +416,22 @@ public class CertPemManager
|
|||||||
|
|
||||||
return string.Concat(pemList);
|
return string.Concat(pemList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetCertSha256Thumbprint(string pemCert, bool includeColon = false)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var cert = X509Certificate2.CreateFromPem(pemCert);
|
||||||
|
var thumbprint = cert.GetCertHashString(HashAlgorithmName.SHA256);
|
||||||
|
if (includeColon)
|
||||||
|
{
|
||||||
|
return string.Join(":", thumbprint.Chunk(2).Select(c => new string(c)));
|
||||||
|
}
|
||||||
|
return thumbprint;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ public sealed class CoreInfoManager
|
|||||||
new CoreInfo
|
new CoreInfo
|
||||||
{
|
{
|
||||||
CoreType = ECoreType.mihomo,
|
CoreType = ECoreType.mihomo,
|
||||||
CoreExes = ["mihomo-windows-amd64-v1", "mihomo-windows-amd64-compatible", "mihomo-windows-amd64", "mihomo-linux-amd64", "clash", "mihomo"],
|
CoreExes = GetMihomoCoreExes(),
|
||||||
Arguments = "-f {0}" + PortableMode(),
|
Arguments = "-f {0}" + PortableMode(),
|
||||||
Url = GetCoreUrl(ECoreType.mihomo),
|
Url = GetCoreUrl(ECoreType.mihomo),
|
||||||
ReleaseApiUrl = urlMihomo.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
ReleaseApiUrl = urlMihomo.Replace(Global.GithubUrl, Global.GithubApiUrl),
|
||||||
@@ -248,4 +248,34 @@ public sealed class CoreInfoManager
|
|||||||
{
|
{
|
||||||
return $"{Global.GithubUrl}/{Global.CoreUrls[eCoreType]}/releases";
|
return $"{Global.GithubUrl}/{Global.CoreUrls[eCoreType]}/releases";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<string>? GetMihomoCoreExes()
|
||||||
|
{
|
||||||
|
var names = new List<string>();
|
||||||
|
|
||||||
|
if (Utils.IsWindows())
|
||||||
|
{
|
||||||
|
names.Add("mihomo-windows-amd64-v1");
|
||||||
|
names.Add("mihomo-windows-amd64-compatible");
|
||||||
|
names.Add("mihomo-windows-amd64");
|
||||||
|
names.Add("mihomo-windows-arm64");
|
||||||
|
}
|
||||||
|
else if (Utils.IsLinux())
|
||||||
|
{
|
||||||
|
names.Add("mihomo-linux-amd64-v1");
|
||||||
|
names.Add("mihomo-linux-amd64");
|
||||||
|
names.Add("mihomo-linux-arm64");
|
||||||
|
}
|
||||||
|
else if (Utils.IsMacOS())
|
||||||
|
{
|
||||||
|
names.Add("mihomo-darwin-amd64-v1");
|
||||||
|
names.Add("mihomo-darwin-amd64");
|
||||||
|
names.Add("mihomo-darwin-arm64");
|
||||||
|
}
|
||||||
|
|
||||||
|
names.Add("clash");
|
||||||
|
names.Add("mihomo");
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ public class CoreManager
|
|||||||
|
|
||||||
private async Task CoreStart(ProfileItem node)
|
private async Task CoreStart(ProfileItem node)
|
||||||
{
|
{
|
||||||
var coreType = _config.RunningCoreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
var coreType = AppManager.Instance.RunningCoreType = AppManager.Instance.GetCoreType(node, node.ConfigType);
|
||||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(coreType);
|
||||||
|
|
||||||
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
|
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
|
||||||
|
|||||||
@@ -230,9 +230,10 @@ public class ProfileGroupItemManager
|
|||||||
{
|
{
|
||||||
return (new List<ProfileItem>(), profileGroupItem);
|
return (new List<ProfileItem>(), profileGroupItem);
|
||||||
}
|
}
|
||||||
var items = await GetChildProfileItems(profileGroupItem);
|
|
||||||
var subItems = await GetSubChildProfileItems(profileGroupItem);
|
var items = new List<ProfileItem>();
|
||||||
items.AddRange(subItems);
|
items.AddRange(await GetSubChildProfileItems(profileGroupItem));
|
||||||
|
items.AddRange(await GetChildProfileItems(profileGroupItem));
|
||||||
|
|
||||||
return (items, profileGroupItem);
|
return (items, profileGroupItem);
|
||||||
}
|
}
|
||||||
@@ -316,5 +317,72 @@ public class ProfileGroupItemManager
|
|||||||
return childAddresses;
|
return childAddresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<HashSet<string>> GetAllChildEchQuerySni(string indexId)
|
||||||
|
{
|
||||||
|
// include grand children
|
||||||
|
var childAddresses = new HashSet<string>();
|
||||||
|
if (!Instance.TryGet(indexId, out var groupItem) || groupItem == null)
|
||||||
|
{
|
||||||
|
return childAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupItem.SubChildItems.IsNotEmpty())
|
||||||
|
{
|
||||||
|
var subItems = await GetSubChildProfileItems(groupItem);
|
||||||
|
foreach (var childNode in subItems)
|
||||||
|
{
|
||||||
|
if (childNode.EchConfigList.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (childNode.StreamSecurity == Global.StreamSecurity
|
||||||
|
&& childNode.EchConfigList?.Contains("://") == true)
|
||||||
|
{
|
||||||
|
var idx = childNode.EchConfigList.IndexOf('+');
|
||||||
|
childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childAddresses.Add(childNode.Sni);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var childIds = Utils.String2List(groupItem.ChildItems) ?? [];
|
||||||
|
|
||||||
|
foreach (var childId in childIds)
|
||||||
|
{
|
||||||
|
var childNode = await AppManager.Instance.GetProfileItem(childId);
|
||||||
|
if (childNode == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!childNode.IsComplex() && !childNode.EchConfigList.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
if (childNode.StreamSecurity == Global.StreamSecurity
|
||||||
|
&& childNode.EchConfigList?.Contains("://") == true)
|
||||||
|
{
|
||||||
|
var idx = childNode.EchConfigList.IndexOf('+');
|
||||||
|
childAddresses.Add(idx > 0 ? childNode.EchConfigList[..idx] : childNode.Sni);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
childAddresses.Add(childNode.Sni);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (childNode.ConfigType.IsGroupType())
|
||||||
|
{
|
||||||
|
var subAddresses = await GetAllChildDomainAddresses(childNode.IndexId);
|
||||||
|
foreach (var addr in subAddresses)
|
||||||
|
{
|
||||||
|
childAddresses.Add(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return childAddresses;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Helper
|
#endregion Helper
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,9 +43,12 @@ public sealed class WebDavManager
|
|||||||
_webDir = _config.WebDavItem.DirName.TrimEx();
|
_webDir = _config.WebDavItem.DirName.TrimEx();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure BaseAddress URL ends with a trailing slash
|
||||||
|
var baseUrl = _config.WebDavItem.Url.Trim().TrimEnd('/') + "/";
|
||||||
|
|
||||||
var clientParams = new WebDavClientParams
|
var clientParams = new WebDavClientParams
|
||||||
{
|
{
|
||||||
BaseAddress = new Uri(_config.WebDavItem.Url),
|
BaseAddress = new Uri(baseUrl),
|
||||||
Credentials = new NetworkCredential(_config.WebDavItem.UserName, _config.WebDavItem.Password)
|
Credentials = new NetworkCredential(_config.WebDavItem.UserName, _config.WebDavItem.Password)
|
||||||
};
|
};
|
||||||
_client = new WebDavClient(clientParams);
|
_client = new WebDavClient(clientParams);
|
||||||
|
|||||||
@@ -8,21 +8,6 @@ public class Config
|
|||||||
public string IndexId { get; set; }
|
public string IndexId { get; set; }
|
||||||
public string SubIndexId { get; set; }
|
public string SubIndexId { get; set; }
|
||||||
|
|
||||||
public ECoreType RunningCoreType { get; set; }
|
|
||||||
|
|
||||||
public bool IsRunningCore(ECoreType type)
|
|
||||||
{
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case ECoreType.Xray when RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5:
|
|
||||||
case ECoreType.sing_box when RunningCoreType is ECoreType.sing_box or ECoreType.mihomo:
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion property
|
#endregion property
|
||||||
|
|
||||||
#region other entities
|
#region other entities
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ public class UIItem
|
|||||||
public bool DoubleClick2Activate { get; set; }
|
public bool DoubleClick2Activate { get; set; }
|
||||||
public bool AutoHideStartup { get; set; }
|
public bool AutoHideStartup { get; set; }
|
||||||
public bool Hide2TrayWhenClose { get; set; }
|
public bool Hide2TrayWhenClose { get; set; }
|
||||||
public bool ShowInTaskbar { get; set; }
|
|
||||||
public bool MacOSShowInDock { get; set; }
|
public bool MacOSShowInDock { get; set; }
|
||||||
public List<ColumnItem> MainColumnItem { get; set; }
|
public List<ColumnItem> MainColumnItem { get; set; }
|
||||||
public List<WindowSizeItem> WindowSizeItem { get; set; }
|
public List<WindowSizeItem> WindowSizeItem { get; set; }
|
||||||
|
|||||||
@@ -161,4 +161,7 @@ public class ProfileItem : ReactiveObject
|
|||||||
public string Extra { get; set; }
|
public string Extra { get; set; }
|
||||||
public bool? MuxEnabled { get; set; }
|
public bool? MuxEnabled { get; set; }
|
||||||
public string Cert { get; set; }
|
public string Cert { get; set; }
|
||||||
|
public string CertSha { get; set; }
|
||||||
|
public string EchConfigList { get; set; }
|
||||||
|
public string EchForceQuery { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ public class Rule4Sbox
|
|||||||
public List<string>? ip_cidr { get; set; }
|
public List<string>? ip_cidr { get; set; }
|
||||||
public List<string>? source_ip_cidr { get; set; }
|
public List<string>? source_ip_cidr { get; set; }
|
||||||
public List<string>? process_name { get; set; }
|
public List<string>? process_name { get; set; }
|
||||||
|
public List<string>? process_path { get; set; }
|
||||||
public List<string>? rule_set { get; set; }
|
public List<string>? rule_set { get; set; }
|
||||||
public List<Rule4Sbox>? rules { get; set; }
|
public List<Rule4Sbox>? rules { get; set; }
|
||||||
public string? action { get; set; }
|
public string? action { get; set; }
|
||||||
@@ -182,6 +183,14 @@ public class Tls4Sbox
|
|||||||
public string? fragment_fallback_delay { get; set; }
|
public string? fragment_fallback_delay { get; set; }
|
||||||
public bool? record_fragment { get; set; }
|
public bool? record_fragment { get; set; }
|
||||||
public List<string>? certificate { get; set; }
|
public List<string>? certificate { get; set; }
|
||||||
|
public Ech4Sbox? ech { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Ech4Sbox
|
||||||
|
{
|
||||||
|
public bool enabled { get; set; }
|
||||||
|
public List<string>? config { get; set; }
|
||||||
|
public string? query_server_name { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Multiplex4Sbox
|
public class Multiplex4Sbox
|
||||||
@@ -216,6 +225,8 @@ public class Transport4Sbox
|
|||||||
public string? idle_timeout { get; set; }
|
public string? idle_timeout { get; set; }
|
||||||
public string? ping_timeout { get; set; }
|
public string? ping_timeout { get; set; }
|
||||||
public bool? permit_without_stream { get; set; }
|
public bool? permit_without_stream { get; set; }
|
||||||
|
public int? max_early_data { get; set; }
|
||||||
|
public string? early_data_header_name { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Headers4Sbox
|
public class Headers4Sbox
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
namespace ServiceLib.Models;
|
||||||
|
|
||||||
|
public class UpdateResult
|
||||||
|
{
|
||||||
|
public bool Success { get; set; }
|
||||||
|
public string? Msg { get; set; }
|
||||||
|
public SemanticVersion? Version { get; set; }
|
||||||
|
public string? Url { get; set; }
|
||||||
|
|
||||||
|
public UpdateResult(bool success, string? msg)
|
||||||
|
{
|
||||||
|
Success = success;
|
||||||
|
Msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpdateResult(bool success, SemanticVersion? version)
|
||||||
|
{
|
||||||
|
Success = success;
|
||||||
|
Version = version;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -128,7 +128,8 @@ public class Outboundsettings4Ray
|
|||||||
|
|
||||||
public string? secretKey { get; set; }
|
public string? secretKey { get; set; }
|
||||||
|
|
||||||
public List<string>? address { get; set; }
|
public Object? address { get; set; }
|
||||||
|
public int? port { get; set; }
|
||||||
|
|
||||||
public List<WireguardPeer4Ray>? peers { get; set; }
|
public List<WireguardPeer4Ray>? peers { get; set; }
|
||||||
|
|
||||||
@@ -139,6 +140,8 @@ public class Outboundsettings4Ray
|
|||||||
public List<int>? reserved { get; set; }
|
public List<int>? reserved { get; set; }
|
||||||
|
|
||||||
public int? workers { get; set; }
|
public int? workers { get; set; }
|
||||||
|
|
||||||
|
public int? version { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WireguardPeer4Ray
|
public class WireguardPeer4Ray
|
||||||
@@ -256,6 +259,8 @@ public class RulesItem4Ray
|
|||||||
public List<string>? domain { get; set; }
|
public List<string>? domain { get; set; }
|
||||||
|
|
||||||
public List<string>? protocol { get; set; }
|
public List<string>? protocol { get; set; }
|
||||||
|
|
||||||
|
public List<string>? process { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BalancersItem4Ray
|
public class BalancersItem4Ray
|
||||||
@@ -336,6 +341,10 @@ public class StreamSettings4Ray
|
|||||||
|
|
||||||
public GrpcSettings4Ray? grpcSettings { get; set; }
|
public GrpcSettings4Ray? grpcSettings { get; set; }
|
||||||
|
|
||||||
|
public HysteriaSettings4Ray? hysteriaSettings { get; set; }
|
||||||
|
|
||||||
|
public List<UdpMasks4Ray>? udpmasks { get; set; }
|
||||||
|
|
||||||
public Sockopt4Ray? sockopt { get; set; }
|
public Sockopt4Ray? sockopt { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,7 +364,10 @@ public class TlsSettings4Ray
|
|||||||
public string? spiderX { get; set; }
|
public string? spiderX { get; set; }
|
||||||
public string? mldsa65Verify { get; set; }
|
public string? mldsa65Verify { get; set; }
|
||||||
public List<CertificateSettings4Ray>? certificates { get; set; }
|
public List<CertificateSettings4Ray>? certificates { get; set; }
|
||||||
|
public string? pinnedPeerCertSha256 { get; set; }
|
||||||
public bool? disableSystemRoot { get; set; }
|
public bool? disableSystemRoot { get; set; }
|
||||||
|
public string? echConfigList { get; set; }
|
||||||
|
public string? echForceQuery { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CertificateSettings4Ray
|
public class CertificateSettings4Ray
|
||||||
@@ -411,8 +423,6 @@ public class WsSettings4Ray
|
|||||||
|
|
||||||
public class Headers4Ray
|
public class Headers4Ray
|
||||||
{
|
{
|
||||||
public string Host { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("User-Agent")]
|
[JsonPropertyName("User-Agent")]
|
||||||
public string UserAgent { get; set; }
|
public string UserAgent { get; set; }
|
||||||
}
|
}
|
||||||
@@ -459,6 +469,32 @@ public class GrpcSettings4Ray
|
|||||||
public int? initial_windows_size { get; set; }
|
public int? initial_windows_size { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class HysteriaSettings4Ray
|
||||||
|
{
|
||||||
|
public int version { get; set; }
|
||||||
|
public string? auth { get; set; }
|
||||||
|
public string? up { get; set; }
|
||||||
|
public string? down { get; set; }
|
||||||
|
public HysteriaUdpHop4Ray? udphop { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HysteriaUdpHop4Ray
|
||||||
|
{
|
||||||
|
public string? ports { get; set; }
|
||||||
|
public int? interval { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UdpMasks4Ray
|
||||||
|
{
|
||||||
|
public string type { get; set; }
|
||||||
|
public UdpMasksSettings4Ray? settings { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UdpMasksSettings4Ray
|
||||||
|
{
|
||||||
|
public string? password { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class AccountsItem4Ray
|
public class AccountsItem4Ray
|
||||||
{
|
{
|
||||||
public string user { get; set; }
|
public string user { get; set; }
|
||||||
|
|||||||
Generated
+88
-43
@@ -529,7 +529,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Next proxy Configuration remarks 的本地化字符串。
|
/// 查找类似 Next proxy remarks 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string LvNextProfile {
|
public static string LvNextProfile {
|
||||||
get {
|
get {
|
||||||
@@ -547,7 +547,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Previous proxy Configuration remarks 的本地化字符串。
|
/// 查找类似 Previous proxy remarks 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string LvPrevProfile {
|
public static string LvPrevProfile {
|
||||||
get {
|
get {
|
||||||
@@ -736,7 +736,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add [Anytls] Configuration 的本地化字符串。
|
/// 查找类似 Add [Anytls] 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddAnytlsServer {
|
public static string menuAddAnytlsServer {
|
||||||
get {
|
get {
|
||||||
@@ -745,7 +745,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add Child Configuration 的本地化字符串。
|
/// 查找类似 Add Child 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddChildServer {
|
public static string menuAddChildServer {
|
||||||
get {
|
get {
|
||||||
@@ -754,7 +754,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add a custom configuration Configuration 的本地化字符串。
|
/// 查找类似 Add a custom configuration 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddCustomServer {
|
public static string menuAddCustomServer {
|
||||||
get {
|
get {
|
||||||
@@ -763,7 +763,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add [HTTP] Configuration 的本地化字符串。
|
/// 查找类似 Add [HTTP] 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddHttpServer {
|
public static string menuAddHttpServer {
|
||||||
get {
|
get {
|
||||||
@@ -772,7 +772,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add [Hysteria2] Configuration 的本地化字符串。
|
/// 查找类似 Add [Hysteria2] 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddHysteria2Server {
|
public static string menuAddHysteria2Server {
|
||||||
get {
|
get {
|
||||||
@@ -781,7 +781,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add Policy Group Configuration 的本地化字符串。
|
/// 查找类似 Add Policy Group 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddPolicyGroupServer {
|
public static string menuAddPolicyGroupServer {
|
||||||
get {
|
get {
|
||||||
@@ -790,7 +790,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add Proxy Chain Configuration 的本地化字符串。
|
/// 查找类似 Add Proxy Chain 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddProxyChainServer {
|
public static string menuAddProxyChainServer {
|
||||||
get {
|
get {
|
||||||
@@ -826,7 +826,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add [Shadowsocks] Configuration 的本地化字符串。
|
/// 查找类似 Add [Shadowsocks] 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddShadowsocksServer {
|
public static string menuAddShadowsocksServer {
|
||||||
get {
|
get {
|
||||||
@@ -835,7 +835,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add [SOCKS] Configuration 的本地化字符串。
|
/// 查找类似 Add [SOCKS] 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddSocksServer {
|
public static string menuAddSocksServer {
|
||||||
get {
|
get {
|
||||||
@@ -844,7 +844,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add [Trojan] Configuration 的本地化字符串。
|
/// 查找类似 Add [Trojan] 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddTrojanServer {
|
public static string menuAddTrojanServer {
|
||||||
get {
|
get {
|
||||||
@@ -853,7 +853,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add [TUIC] Configuration 的本地化字符串。
|
/// 查找类似 Add [TUIC] 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddTuicServer {
|
public static string menuAddTuicServer {
|
||||||
get {
|
get {
|
||||||
@@ -862,7 +862,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add [VLESS] Configuration 的本地化字符串。
|
/// 查找类似 Add [VLESS] 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddVlessServer {
|
public static string menuAddVlessServer {
|
||||||
get {
|
get {
|
||||||
@@ -871,7 +871,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add [VMess] Configuration 的本地化字符串。
|
/// 查找类似 Add [VMess] 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddVmessServer {
|
public static string menuAddVmessServer {
|
||||||
get {
|
get {
|
||||||
@@ -880,7 +880,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Add [WireGuard] Configuration 的本地化字符串。
|
/// 查找类似 Add [WireGuard] 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuAddWireguardServer {
|
public static string menuAddWireguardServer {
|
||||||
get {
|
get {
|
||||||
@@ -952,7 +952,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Clone selected Configuration 的本地化字符串。
|
/// 查找类似 Clone selected 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuCopyServer {
|
public static string menuCopyServer {
|
||||||
get {
|
get {
|
||||||
@@ -970,7 +970,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Edit Configuration 的本地化字符串。
|
/// 查找类似 Edit 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuEditServer {
|
public static string menuEditServer {
|
||||||
get {
|
get {
|
||||||
@@ -997,7 +997,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Export selected Configuration for complete configuration 的本地化字符串。
|
/// 查找类似 Export selected for complete configuration 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuExport2ClientConfig {
|
public static string menuExport2ClientConfig {
|
||||||
get {
|
get {
|
||||||
@@ -1006,7 +1006,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Export selected Configuration for complete configuration to clipboard 的本地化字符串。
|
/// 查找类似 Export selected for complete configuration to clipboard 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuExport2ClientConfigClipboard {
|
public static string menuExport2ClientConfigClipboard {
|
||||||
get {
|
get {
|
||||||
@@ -1033,7 +1033,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Export Configuration 的本地化字符串。
|
/// 查找类似 Export 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuExportConfig {
|
public static string menuExportConfig {
|
||||||
get {
|
get {
|
||||||
@@ -1069,7 +1069,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Multi-Configuration Fallback by sing-box 的本地化字符串。
|
/// 查找类似 Fallback by sing-box 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuGenGroupMultipleServerSingBoxFallback {
|
public static string menuGenGroupMultipleServerSingBoxFallback {
|
||||||
get {
|
get {
|
||||||
@@ -1078,7 +1078,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Multi-Configuration LeastPing by sing-box 的本地化字符串。
|
/// 查找类似 LeastPing by sing-box 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuGenGroupMultipleServerSingBoxLeastPing {
|
public static string menuGenGroupMultipleServerSingBoxLeastPing {
|
||||||
get {
|
get {
|
||||||
@@ -1087,7 +1087,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Multi-Configuration Fallback by Xray 的本地化字符串。
|
/// 查找类似 Fallback by Xray 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuGenGroupMultipleServerXrayFallback {
|
public static string menuGenGroupMultipleServerXrayFallback {
|
||||||
get {
|
get {
|
||||||
@@ -1096,7 +1096,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Multi-Configuration LeastLoad by Xray 的本地化字符串。
|
/// 查找类似 LeastLoad by Xray 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuGenGroupMultipleServerXrayLeastLoad {
|
public static string menuGenGroupMultipleServerXrayLeastLoad {
|
||||||
get {
|
get {
|
||||||
@@ -1105,7 +1105,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Multi-Configuration LeastPing by Xray 的本地化字符串。
|
/// 查找类似 LeastPing by Xray 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuGenGroupMultipleServerXrayLeastPing {
|
public static string menuGenGroupMultipleServerXrayLeastPing {
|
||||||
get {
|
get {
|
||||||
@@ -1114,7 +1114,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Multi-Configuration Random by Xray 的本地化字符串。
|
/// 查找类似 Random by Xray 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuGenGroupMultipleServerXrayRandom {
|
public static string menuGenGroupMultipleServerXrayRandom {
|
||||||
get {
|
get {
|
||||||
@@ -1123,7 +1123,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Multi-Configuration RoundRobin by Xray 的本地化字符串。
|
/// 查找类似 RoundRobin by Xray 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuGenGroupMultipleServerXrayRoundRobin {
|
public static string menuGenGroupMultipleServerXrayRoundRobin {
|
||||||
get {
|
get {
|
||||||
@@ -1411,7 +1411,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Test Configurations real delay 的本地化字符串。
|
/// 查找类似 Test real delay 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuRealPingServer {
|
public static string menuRealPingServer {
|
||||||
get {
|
get {
|
||||||
@@ -1501,7 +1501,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Remove Child Configuration 的本地化字符串。
|
/// 查找类似 Remove Child 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuRemoveChildServer {
|
public static string menuRemoveChildServer {
|
||||||
get {
|
get {
|
||||||
@@ -1510,7 +1510,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Remove duplicate Configurations 的本地化字符串。
|
/// 查找类似 Remove duplicate 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuRemoveDuplicateServer {
|
public static string menuRemoveDuplicateServer {
|
||||||
get {
|
get {
|
||||||
@@ -1528,7 +1528,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Remove selected Configurations 的本地化字符串。
|
/// 查找类似 Remove selected 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuRemoveServer {
|
public static string menuRemoveServer {
|
||||||
get {
|
get {
|
||||||
@@ -1663,7 +1663,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Configuration List 的本地化字符串。
|
/// 查找类似 Configuration item 1, Auto add from subscription group 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuServerList {
|
public static string menuServerList {
|
||||||
get {
|
get {
|
||||||
@@ -1672,7 +1672,16 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Configurations 的本地化字符串。
|
/// 查找类似 Configuration Item 2, Select and add from self-built 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string menuServerList2 {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("menuServerList2", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Configuration 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuServers {
|
public static string menuServers {
|
||||||
get {
|
get {
|
||||||
@@ -1681,7 +1690,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Set as active Configuration 的本地化字符串。
|
/// 查找类似 Set as active 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuSetDefaultServer {
|
public static string menuSetDefaultServer {
|
||||||
get {
|
get {
|
||||||
@@ -1699,7 +1708,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Share Configuration 的本地化字符串。
|
/// 查找类似 Share 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuShareServer {
|
public static string menuShareServer {
|
||||||
get {
|
get {
|
||||||
@@ -1726,7 +1735,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Test Configurations download speed 的本地化字符串。
|
/// 查找类似 Test download speed 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuSpeedServer {
|
public static string menuSpeedServer {
|
||||||
get {
|
get {
|
||||||
@@ -1870,7 +1879,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Test Configurations with tcping 的本地化字符串。
|
/// 查找类似 Test tcping 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string menuTcpingServer {
|
public static string menuTcpingServer {
|
||||||
get {
|
get {
|
||||||
@@ -1978,7 +1987,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Configuration filter, press Enter to execute 的本地化字符串。
|
/// 查找类似 Filter, press Enter to execute 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string MsgServerTitle {
|
public static string MsgServerTitle {
|
||||||
get {
|
get {
|
||||||
@@ -2275,7 +2284,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Are you sure you want to remove the Configuration? 的本地化字符串。
|
/// 查找类似 Are you sure you want to remove? 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string RemoveServer {
|
public static string RemoveServer {
|
||||||
get {
|
get {
|
||||||
@@ -2608,7 +2617,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Server Certificate (PEM format, optional)
|
/// 查找类似 Pinned certificate (fill in either one)
|
||||||
///When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
|
///When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
|
||||||
///
|
///
|
||||||
///The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA. 的本地化字符串。
|
///The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA. 的本地化字符串。
|
||||||
@@ -2619,6 +2628,15 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Certificate fingerprint (SHA-256) 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbCertSha256Tips {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbCertSha256Tips", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Clear system proxy 的本地化字符串。
|
/// 查找类似 Clear system proxy 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2781,6 +2799,24 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 EchConfigList 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbEchConfigList {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbEchConfigList", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 EchForceQuery 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbEchForceQuery {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbEchForceQuery", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Edit 的本地化字符串。
|
/// 查找类似 Edit 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -2862,6 +2898,15 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Full certificate (chain), PEM format 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbFullCertTips {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbFullCertTips", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core's basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you. 的本地化字符串。
|
/// 查找类似 This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core's basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you. 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -3241,7 +3286,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Full process name (Tun mode) 的本地化字符串。
|
/// 查找类似 Process (Tun mode) 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TbRoutingRuleProcess {
|
public static string TbRoutingRuleProcess {
|
||||||
get {
|
get {
|
||||||
@@ -4105,7 +4150,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Tray right-click menu Configurations display limit 的本地化字符串。
|
/// 查找类似 Tray right-click menu display limit 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TbSettingsTrayMenuServersLimit {
|
public static string TbSettingsTrayMenuServersLimit {
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -1027,7 +1027,7 @@
|
|||||||
<value>پروتکل sing-box Mux</value>
|
<value>پروتکل sing-box Mux</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
||||||
<value>نام کامل فرانید (حالت Tun)</value>
|
<value>Process (Tun mode)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleIP" xml:space="preserve">
|
<data name="TbRoutingRuleIP" xml:space="preserve">
|
||||||
<value>IP or IP CIDR</value>
|
<value>IP or IP CIDR</value>
|
||||||
@@ -1540,7 +1540,7 @@
|
|||||||
<value>Remove Child Configuration</value>
|
<value>Remove Child Configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>Configuration List</value>
|
<value>Configuration item 1, Auto add from subscription group</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>Fallback</value>
|
<value>Fallback</value>
|
||||||
@@ -1638,4 +1638,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||||||
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
||||||
<value>macOS displays this in the Dock (requires restart)</value>
|
<value>macOS displays this in the Dock (requires restart)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
|
<value>Configuration Item 2, Select and add from self-built</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFullCertTips" xml:space="preserve">
|
||||||
|
<value>Full certificate (chain), PEM format</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
|
<value>Certificate fingerprint (SHA-256)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1024,7 +1024,7 @@
|
|||||||
<value>Protocole de multiplexage Mux (sing-box)</value>
|
<value>Protocole de multiplexage Mux (sing-box)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
||||||
<value>Nom complet du processus (mode Tun)</value>
|
<value>Process (Tun mode)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleIP" xml:space="preserve">
|
<data name="TbRoutingRuleIP" xml:space="preserve">
|
||||||
<value>IP ou IP CIDR</value>
|
<value>IP ou IP CIDR</value>
|
||||||
@@ -1537,7 +1537,7 @@
|
|||||||
<value>Supprimer une sous-configuration</value>
|
<value>Supprimer une sous-configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>Liste des configurations</value>
|
<value>Configuration item 1, Auto add from subscription group</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>Basculement (failover)</value>
|
<value>Basculement (failover)</value>
|
||||||
@@ -1606,10 +1606,10 @@
|
|||||||
<value>Certificate Pinning</value>
|
<value>Certificate Pinning</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCertPinningTips" xml:space="preserve">
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
<value>Certificat serveur (format PEM, facultatif)
|
<value>Pinned certificate (fill in either one)
|
||||||
Si le certificat est défini, il est fixé et l’option « Ignorer la vérification » est désactivée.
|
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
|
||||||
|
|
||||||
Si un certificat auto-signé est utilisé ou si le système contient une CA non fiable ou malveillante, l’action « Obtenir le certificat » peut échouer.</value>
|
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFetchCert" xml:space="preserve">
|
<data name="TbFetchCert" xml:space="preserve">
|
||||||
<value>Obtenir le certificat</value>
|
<value>Obtenir le certificat</value>
|
||||||
@@ -1635,4 +1635,19 @@ Si un certificat auto-signé est utilisé ou si le système contient une CA non
|
|||||||
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
||||||
<value>Afficher dans le Dock de macOS (redém. requis)</value>
|
<value>Afficher dans le Dock de macOS (redém. requis)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
|
<value>Élément de config 2 : choisir et ajouter depuis self-hosted</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFullCertTips" xml:space="preserve">
|
||||||
|
<value>Full certificate (chain), PEM format</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
|
<value>Certificate fingerprint (SHA-256)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1027,7 +1027,7 @@
|
|||||||
<value>sing-box Mux protokoll</value>
|
<value>sing-box Mux protokoll</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
||||||
<value>Teljes folyamatnév (Tun mód)</value>
|
<value>Process (Tun mode)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleIP" xml:space="preserve">
|
<data name="TbRoutingRuleIP" xml:space="preserve">
|
||||||
<value>IP vagy IP CIDR</value>
|
<value>IP vagy IP CIDR</value>
|
||||||
@@ -1540,7 +1540,7 @@
|
|||||||
<value>Remove Child Configuration</value>
|
<value>Remove Child Configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>Configuration List</value>
|
<value>Configuration item 1, Auto add from subscription group</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>Fallback</value>
|
<value>Fallback</value>
|
||||||
@@ -1609,7 +1609,7 @@
|
|||||||
<value>Certificate Pinning</value>
|
<value>Certificate Pinning</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCertPinningTips" xml:space="preserve">
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
<value>Server Certificate (PEM format, optional)
|
<value>Pinned certificate (fill in either one)
|
||||||
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
|
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
|
||||||
|
|
||||||
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
|
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
|
||||||
@@ -1638,4 +1638,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||||||
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
||||||
<value>macOS displays this in the Dock (requires restart)</value>
|
<value>macOS displays this in the Dock (requires restart)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
|
<value>Configuration Item 2, Select and add from self-built</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFullCertTips" xml:space="preserve">
|
||||||
|
<value>Full certificate (chain), PEM format</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
|
<value>Certificate fingerprint (SHA-256)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -271,7 +271,7 @@
|
|||||||
<value>Configurations deduplication completed. Old: {0}, New: {1}.</value>
|
<value>Configurations deduplication completed. Old: {0}, New: {1}.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="RemoveServer" xml:space="preserve">
|
<data name="RemoveServer" xml:space="preserve">
|
||||||
<value>Are you sure you want to remove the Configuration?</value>
|
<value>Are you sure you want to remove?</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="SaveClientConfigurationIn" xml:space="preserve">
|
<data name="SaveClientConfigurationIn" xml:space="preserve">
|
||||||
<value>The client configuration file is saved at: {0}</value>
|
<value>The client configuration file is saved at: {0}</value>
|
||||||
@@ -397,7 +397,7 @@
|
|||||||
<value>Local</value>
|
<value>Local</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="MsgServerTitle" xml:space="preserve">
|
<data name="MsgServerTitle" xml:space="preserve">
|
||||||
<value>Configuration filter, press Enter to execute</value>
|
<value>Filter, press Enter to execute</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuCheckUpdate" xml:space="preserve">
|
<data name="menuCheckUpdate" xml:space="preserve">
|
||||||
<value>Check Update</value>
|
<value>Check Update</value>
|
||||||
@@ -427,7 +427,7 @@
|
|||||||
<value>Routing Setting</value>
|
<value>Routing Setting</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServers" xml:space="preserve">
|
<data name="menuServers" xml:space="preserve">
|
||||||
<value>Configurations</value>
|
<value>Configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetting" xml:space="preserve">
|
<data name="menuSetting" xml:space="preserve">
|
||||||
<value>Settings</value>
|
<value>Settings</value>
|
||||||
@@ -478,55 +478,55 @@
|
|||||||
<value>Scan QR code on the screen</value>
|
<value>Scan QR code on the screen</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuCopyServer" xml:space="preserve">
|
<data name="menuCopyServer" xml:space="preserve">
|
||||||
<value>Clone selected Configuration</value>
|
<value>Clone selected</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRemoveDuplicateServer" xml:space="preserve">
|
<data name="menuRemoveDuplicateServer" xml:space="preserve">
|
||||||
<value>Remove duplicate Configurations</value>
|
<value>Remove duplicate</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRemoveServer" xml:space="preserve">
|
<data name="menuRemoveServer" xml:space="preserve">
|
||||||
<value>Remove selected Configurations</value>
|
<value>Remove selected</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSetDefaultServer" xml:space="preserve">
|
<data name="menuSetDefaultServer" xml:space="preserve">
|
||||||
<value>Set as active Configuration</value>
|
<value>Set as active</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuClearServerStatistics" xml:space="preserve">
|
<data name="menuClearServerStatistics" xml:space="preserve">
|
||||||
<value>Clear all service statistics</value>
|
<value>Clear all service statistics</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRealPingServer" xml:space="preserve">
|
<data name="menuRealPingServer" xml:space="preserve">
|
||||||
<value>Test Configurations real delay</value>
|
<value>Test real delay</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSortServerResult" xml:space="preserve">
|
<data name="menuSortServerResult" xml:space="preserve">
|
||||||
<value>Sort by test result</value>
|
<value>Sort by test result</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSpeedServer" xml:space="preserve">
|
<data name="menuSpeedServer" xml:space="preserve">
|
||||||
<value>Test Configurations download speed</value>
|
<value>Test download speed</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuTcpingServer" xml:space="preserve">
|
<data name="menuTcpingServer" xml:space="preserve">
|
||||||
<value>Test Configurations with tcping</value>
|
<value>Test tcping</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExport2ClientConfig" xml:space="preserve">
|
<data name="menuExport2ClientConfig" xml:space="preserve">
|
||||||
<value>Export selected Configuration for complete configuration</value>
|
<value>Export selected for complete configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExport2ShareUrl" xml:space="preserve">
|
<data name="menuExport2ShareUrl" xml:space="preserve">
|
||||||
<value>Export Share Link to Clipboard</value>
|
<value>Export Share Link to Clipboard</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddCustomServer" xml:space="preserve">
|
<data name="menuAddCustomServer" xml:space="preserve">
|
||||||
<value>Add a custom configuration Configuration</value>
|
<value>Add a custom configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddShadowsocksServer" xml:space="preserve">
|
<data name="menuAddShadowsocksServer" xml:space="preserve">
|
||||||
<value>Add [Shadowsocks] Configuration</value>
|
<value>Add [Shadowsocks] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddSocksServer" xml:space="preserve">
|
<data name="menuAddSocksServer" xml:space="preserve">
|
||||||
<value>Add [SOCKS] Configuration</value>
|
<value>Add [SOCKS] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddTrojanServer" xml:space="preserve">
|
<data name="menuAddTrojanServer" xml:space="preserve">
|
||||||
<value>Add [Trojan] Configuration</value>
|
<value>Add [Trojan] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddVlessServer" xml:space="preserve">
|
<data name="menuAddVlessServer" xml:space="preserve">
|
||||||
<value>Add [VLESS] Configuration</value>
|
<value>Add [VLESS] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddVmessServer" xml:space="preserve">
|
<data name="menuAddVmessServer" xml:space="preserve">
|
||||||
<value>Add [VMess] Configuration</value>
|
<value>Add [VMess] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuSelectAll" xml:space="preserve">
|
<data name="menuSelectAll" xml:space="preserve">
|
||||||
<value>Select all</value>
|
<value>Select all</value>
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
<value>System proxy settings</value>
|
<value>System proxy settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
|
||||||
<value>Tray right-click menu Configurations display limit</value>
|
<value>Tray right-click menu display limit</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsUdpEnabled" xml:space="preserve">
|
<data name="TbSettingsUdpEnabled" xml:space="preserve">
|
||||||
<value>Enable UDP</value>
|
<value>Enable UDP</value>
|
||||||
@@ -781,7 +781,7 @@
|
|||||||
<value>PAC mode</value>
|
<value>PAC mode</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuShareServer" xml:space="preserve">
|
<data name="menuShareServer" xml:space="preserve">
|
||||||
<value>Share Configuration</value>
|
<value>Share</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRouting" xml:space="preserve">
|
<data name="menuRouting" xml:space="preserve">
|
||||||
<value>Routing</value>
|
<value>Routing</value>
|
||||||
@@ -922,7 +922,7 @@
|
|||||||
<value>Skip test</value>
|
<value>Skip test</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuEditServer" xml:space="preserve">
|
<data name="menuEditServer" xml:space="preserve">
|
||||||
<value>Edit Configuration</value>
|
<value>Edit </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
|
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
|
||||||
<value>Double-clicking Configuration makes it active</value>
|
<value>Double-clicking Configuration makes it active</value>
|
||||||
@@ -1027,7 +1027,7 @@
|
|||||||
<value>sing-box Mux Protocol</value>
|
<value>sing-box Mux Protocol</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
||||||
<value>Full process name (Tun mode)</value>
|
<value>Process (Tun mode)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleIP" xml:space="preserve">
|
<data name="TbRoutingRuleIP" xml:space="preserve">
|
||||||
<value>IP or IP CIDR</value>
|
<value>IP or IP CIDR</value>
|
||||||
@@ -1036,7 +1036,7 @@
|
|||||||
<value>Domain</value>
|
<value>Domain</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddHysteria2Server" xml:space="preserve">
|
<data name="menuAddHysteria2Server" xml:space="preserve">
|
||||||
<value>Add [Hysteria2] Configuration</value>
|
<value>Add [Hysteria2] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
|
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
|
||||||
<value>Hysteria Max bandwidth (Up/Down)</value>
|
<value>Hysteria Max bandwidth (Up/Down)</value>
|
||||||
@@ -1045,16 +1045,16 @@
|
|||||||
<value>Use System Hosts</value>
|
<value>Use System Hosts</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddTuicServer" xml:space="preserve">
|
<data name="menuAddTuicServer" xml:space="preserve">
|
||||||
<value>Add [TUIC] Configuration</value>
|
<value>Add [TUIC] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbHeaderType8" xml:space="preserve">
|
<data name="TbHeaderType8" xml:space="preserve">
|
||||||
<value>Congestion control</value>
|
<value>Congestion control</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvPrevProfile" xml:space="preserve">
|
<data name="LvPrevProfile" xml:space="preserve">
|
||||||
<value>Previous proxy Configuration remarks</value>
|
<value>Previous proxy remarks</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvNextProfile" xml:space="preserve">
|
<data name="LvNextProfile" xml:space="preserve">
|
||||||
<value>Next proxy Configuration remarks</value>
|
<value>Next proxy remarks</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="LvPrevProfileTip" xml:space="preserve">
|
<data name="LvPrevProfileTip" xml:space="preserve">
|
||||||
<value>Please make sure the Configuration remarks exist and are unique</value>
|
<value>Please make sure the Configuration remarks exist and are unique</value>
|
||||||
@@ -1078,7 +1078,7 @@
|
|||||||
<value>Enable IPv6 Address</value>
|
<value>Enable IPv6 Address</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddWireguardServer" xml:space="preserve">
|
<data name="menuAddWireguardServer" xml:space="preserve">
|
||||||
<value>Add [WireGuard] Configuration</value>
|
<value>Add [WireGuard] </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbPrivateKey" xml:space="preserve">
|
<data name="TbPrivateKey" xml:space="preserve">
|
||||||
<value>Private Key</value>
|
<value>Private Key</value>
|
||||||
@@ -1111,7 +1111,7 @@
|
|||||||
<value>*grpc Authority</value>
|
<value>*grpc Authority</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddHttpServer" xml:space="preserve">
|
<data name="menuAddHttpServer" xml:space="preserve">
|
||||||
<value>Add [HTTP] Configuration</value>
|
<value>Add [HTTP]</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||||
<value>which conflicts with the group previous proxy</value>
|
<value>which conflicts with the group previous proxy</value>
|
||||||
@@ -1225,7 +1225,7 @@
|
|||||||
<value>Export Base64-encoded Share Links to Clipboard</value>
|
<value>Export Base64-encoded Share Links to Clipboard</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
||||||
<value>Export selected Configuration for complete configuration to clipboard</value>
|
<value>Export selected for complete configuration to clipboard</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuShowOrHideMainWindow" xml:space="preserve">
|
<data name="menuShowOrHideMainWindow" xml:space="preserve">
|
||||||
<value>Show or hide the main window</value>
|
<value>Show or hide the main window</value>
|
||||||
@@ -1381,22 +1381,22 @@
|
|||||||
<value>Generate Policy Group from Multiple Profiles</value>
|
<value>Generate Policy Group from Multiple Profiles</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
|
||||||
<value>Multi-Configuration Random by Xray</value>
|
<value>Random by Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||||
<value>Multi-Configuration RoundRobin by Xray</value>
|
<value>RoundRobin by Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
|
||||||
<value>Multi-Configuration LeastPing by Xray</value>
|
<value>LeastPing by Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||||
<value>Multi-Configuration LeastLoad by Xray</value>
|
<value>LeastLoad by Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||||
<value>Multi-Configuration LeastPing by sing-box</value>
|
<value>LeastPing by sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuExportConfig" xml:space="preserve">
|
<data name="menuExportConfig" xml:space="preserve">
|
||||||
<value>Export Configuration</value>
|
<value>Export</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
||||||
<value>Current connection info test URL</value>
|
<value>Current connection info test URL</value>
|
||||||
@@ -1411,7 +1411,7 @@
|
|||||||
<value>Mldsa65Verify</value>
|
<value>Mldsa65Verify</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||||
<value>Add [Anytls] Configuration</value>
|
<value>Add [Anytls]</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRemoteDNS" xml:space="preserve">
|
<data name="TbRemoteDNS" xml:space="preserve">
|
||||||
<value>Remote DNS</value>
|
<value>Remote DNS</value>
|
||||||
@@ -1528,28 +1528,28 @@
|
|||||||
<value>Policy Group Type</value>
|
<value>Policy Group Type</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
<data name="menuAddPolicyGroupServer" xml:space="preserve">
|
||||||
<value>Add Policy Group Configuration</value>
|
<value>Add Policy Group </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddProxyChainServer" xml:space="preserve">
|
<data name="menuAddProxyChainServer" xml:space="preserve">
|
||||||
<value>Add Proxy Chain Configuration</value>
|
<value>Add Proxy Chain</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuAddChildServer" xml:space="preserve">
|
<data name="menuAddChildServer" xml:space="preserve">
|
||||||
<value>Add Child Configuration</value>
|
<value>Add Child </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRemoveChildServer" xml:space="preserve">
|
<data name="menuRemoveChildServer" xml:space="preserve">
|
||||||
<value>Remove Child Configuration</value>
|
<value>Remove Child </value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>Configuration List</value>
|
<value>Configuration item 1, Auto add from subscription group</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>Fallback</value>
|
<value>Fallback</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
|
||||||
<value>Multi-Configuration Fallback by sing-box</value>
|
<value>Fallback by sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
|
||||||
<value>Multi-Configuration Fallback by Xray</value>
|
<value>Fallback by Xray</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
<data name="CoreNotSupportNetwork" xml:space="preserve">
|
||||||
<value>Core '{0}' does not support network type '{1}'.</value>
|
<value>Core '{0}' does not support network type '{1}'.</value>
|
||||||
@@ -1609,7 +1609,7 @@
|
|||||||
<value>Certificate Pinning</value>
|
<value>Certificate Pinning</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCertPinningTips" xml:space="preserve">
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
<value>Server Certificate (PEM format, optional)
|
<value>Pinned certificate (fill in either one)
|
||||||
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
|
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
|
||||||
|
|
||||||
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
|
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
|
||||||
@@ -1638,4 +1638,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||||||
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
||||||
<value>macOS displays this in the Dock (requires restart)</value>
|
<value>macOS displays this in the Dock (requires restart)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
|
<value>Configuration Item 2, Select and add from self-built</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFullCertTips" xml:space="preserve">
|
||||||
|
<value>Full certificate (chain), PEM format</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
|
<value>Certificate fingerprint (SHA-256)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1027,7 +1027,7 @@
|
|||||||
<value>Протокол Mux для sing-box</value>
|
<value>Протокол Mux для sing-box</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
||||||
<value>Полное имя процесса (режим TUN)</value>
|
<value>Process (Tun mode)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleIP" xml:space="preserve">
|
<data name="TbRoutingRuleIP" xml:space="preserve">
|
||||||
<value>IP-адрес или сеть CIDR</value>
|
<value>IP-адрес или сеть CIDR</value>
|
||||||
@@ -1540,7 +1540,7 @@
|
|||||||
<value>Remove Child Configuration</value>
|
<value>Remove Child Configuration</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>Configuration List</value>
|
<value>Configuration item 1, Auto add from subscription group</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>Fallback</value>
|
<value>Fallback</value>
|
||||||
@@ -1609,7 +1609,7 @@
|
|||||||
<value>Certificate Pinning</value>
|
<value>Certificate Pinning</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCertPinningTips" xml:space="preserve">
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
<value>Server Certificate (PEM format, optional)
|
<value>Pinned certificate (fill in either one)
|
||||||
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
|
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled.
|
||||||
|
|
||||||
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
|
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value>
|
||||||
@@ -1638,4 +1638,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
|
|||||||
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
||||||
<value>macOS displays this in the Dock (requires restart)</value>
|
<value>macOS displays this in the Dock (requires restart)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
|
<value>Configuration Item 2, Select and add from self-built</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFullCertTips" xml:space="preserve">
|
||||||
|
<value>Full certificate (chain), PEM format</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
|
<value>Certificate fingerprint (SHA-256)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1024,7 +1024,7 @@
|
|||||||
<value>sing-box Mux 多路复用协议</value>
|
<value>sing-box Mux 多路复用协议</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
||||||
<value>进程名全称 (Tun 模式)</value>
|
<value>进程 (Tun 模式)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleIP" xml:space="preserve">
|
<data name="TbRoutingRuleIP" xml:space="preserve">
|
||||||
<value>IP 或 IP CIDR</value>
|
<value>IP 或 IP CIDR</value>
|
||||||
@@ -1537,7 +1537,7 @@
|
|||||||
<value>删除子配置</value>
|
<value>删除子配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>子配置项</value>
|
<value>子配置项一,从订阅分组中自动添加</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>故障转移</value>
|
<value>故障转移</value>
|
||||||
@@ -1606,10 +1606,10 @@
|
|||||||
<value>固定证书</value>
|
<value>固定证书</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCertPinningTips" xml:space="preserve">
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
<value>服务器证书(PEM 格式,可选)
|
<value>固定证书(二选一填写即可)
|
||||||
当指定此证书后,将固定该证书,并禁用“跳过证书验证”选项。
|
当指定此证书后,将固定该证书,并禁用“跳过证书验证”选项。
|
||||||
|
|
||||||
“获取证书”操作可能失败,原因可能是使用了自签证书,或系统中存在不受信任或恶意的 CA。</value>
|
“获取证书”操作可能失败,原因包括使用了自签名证书,或系统中存在不受信任甚至恶意的 CA。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFetchCert" xml:space="preserve">
|
<data name="TbFetchCert" xml:space="preserve">
|
||||||
<value>获取证书</value>
|
<value>获取证书</value>
|
||||||
@@ -1635,4 +1635,19 @@
|
|||||||
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
||||||
<value>macOS 在 Dock 栏中显示 (需重启)</value>
|
<value>macOS 在 Dock 栏中显示 (需重启)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
|
<value>子配置项二,从自建中选择添加</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFullCertTips" xml:space="preserve">
|
||||||
|
<value>完整证书(链),PEM 格式</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
|
<value>证书指纹(SHA-256)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1024,7 +1024,7 @@
|
|||||||
<value>sing-box Mux 多路復用協定</value>
|
<value>sing-box Mux 多路復用協定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
||||||
<value>行程名全稱 (Tun 模式)</value>
|
<value>行程 (Tun 模式)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbRoutingRuleIP" xml:space="preserve">
|
<data name="TbRoutingRuleIP" xml:space="preserve">
|
||||||
<value>IP 或 IP CIDR</value>
|
<value>IP 或 IP CIDR</value>
|
||||||
@@ -1537,7 +1537,7 @@
|
|||||||
<value>刪除子配置</value>
|
<value>刪除子配置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuServerList" xml:space="preserve">
|
<data name="menuServerList" xml:space="preserve">
|
||||||
<value>子配置項</value>
|
<value>子配置項目一,從訂閱分組中自動新增</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbFallback" xml:space="preserve">
|
<data name="TbFallback" xml:space="preserve">
|
||||||
<value>容錯移轉</value>
|
<value>容錯移轉</value>
|
||||||
@@ -1606,7 +1606,7 @@
|
|||||||
<value>憑證綁定</value>
|
<value>憑證綁定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbCertPinningTips" xml:space="preserve">
|
<data name="TbCertPinningTips" xml:space="preserve">
|
||||||
<value>伺服器憑證(PEM 格式,可選)
|
<value>固定憑證(二選一填寫即可)
|
||||||
若已指定,憑證將會被綁定,並且「跳過憑證驗證」將被停用。
|
若已指定,憑證將會被綁定,並且「跳過憑證驗證」將被停用。
|
||||||
|
|
||||||
若使用自簽憑證,或系統中存在不受信任或惡意的 CA,「取得憑證」動作可能會失敗。</value>
|
若使用自簽憑證,或系統中存在不受信任或惡意的 CA,「取得憑證」動作可能會失敗。</value>
|
||||||
@@ -1635,4 +1635,19 @@
|
|||||||
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
<data name="TbSettingsMacOSShowInDock" xml:space="preserve">
|
||||||
<value>macOS 在 Dock 欄顯示 (需重啟)</value>
|
<value>macOS 在 Dock 欄顯示 (需重啟)</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="menuServerList2" xml:space="preserve">
|
||||||
|
<value>子配置項二,從自建中選擇新增</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchConfigList" xml:space="preserve">
|
||||||
|
<value>EchConfigList</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbEchForceQuery" xml:space="preserve">
|
||||||
|
<value>EchForceQuery</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbFullCertTips" xml:space="preserve">
|
||||||
|
<value>Full certificate (chain), PEM format</value>
|
||||||
|
</data>
|
||||||
|
<data name="TbCertSha256Tips" xml:space="preserve">
|
||||||
|
<value>Certificate fingerprint (SHA-256)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -371,7 +371,7 @@ public partial class CoreConfigSingboxService(Config config)
|
|||||||
|
|
||||||
await GenRouting(singboxConfig);
|
await GenRouting(singboxConfig);
|
||||||
await GenExperimental(singboxConfig);
|
await GenExperimental(singboxConfig);
|
||||||
await GenDns(null, singboxConfig);
|
await GenDns(parentNode, singboxConfig);
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
await ConvertGeo2Ruleset(singboxConfig);
|
||||||
|
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
@@ -428,7 +428,7 @@ public partial class CoreConfigSingboxService(Config config)
|
|||||||
|
|
||||||
await GenRouting(singboxConfig);
|
await GenRouting(singboxConfig);
|
||||||
await GenExperimental(singboxConfig);
|
await GenExperimental(singboxConfig);
|
||||||
await GenDns(null, singboxConfig);
|
await GenDns(parentNode, singboxConfig);
|
||||||
await ConvertGeo2Ruleset(singboxConfig);
|
await ConvertGeo2Ruleset(singboxConfig);
|
||||||
|
|
||||||
ret.Success = true;
|
ret.Success = true;
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ public partial class CoreConfigSingboxService
|
|||||||
}
|
}
|
||||||
|
|
||||||
var simpleDNSItem = _config.SimpleDNSItem;
|
var simpleDNSItem = _config.SimpleDNSItem;
|
||||||
await GenDnsServers(singboxConfig, simpleDNSItem);
|
await GenDnsServers(node, singboxConfig, simpleDNSItem);
|
||||||
await GenDnsRules(singboxConfig, simpleDNSItem);
|
await GenDnsRules(node, singboxConfig, simpleDNSItem);
|
||||||
|
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
singboxConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.independent_cache = true;
|
singboxConfig.dns.independent_cache = true;
|
||||||
@@ -52,7 +52,7 @@ public partial class CoreConfigSingboxService
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsServers(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
private async Task<int> GenDnsServers(ProfileItem? node, SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
||||||
{
|
{
|
||||||
var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem);
|
var finalDns = await GenDnsDomains(singboxConfig, simpleDNSItem);
|
||||||
|
|
||||||
@@ -133,6 +133,29 @@ public partial class CoreConfigSingboxService
|
|||||||
singboxConfig.dns.servers.Add(fakeip);
|
singboxConfig.dns.servers.Add(fakeip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ech
|
||||||
|
var (_, dnsServer) = ParseEchParam(node?.EchConfigList);
|
||||||
|
if (dnsServer is not null)
|
||||||
|
{
|
||||||
|
dnsServer.tag = Global.SingboxEchDNSTag;
|
||||||
|
if (dnsServer.server is not null
|
||||||
|
&& hostsDns.predefined.ContainsKey(dnsServer.server))
|
||||||
|
{
|
||||||
|
dnsServer.domain_resolver = Global.SingboxHostsDNSTag;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dnsServer.domain_resolver = Global.SingboxLocalDNSTag;
|
||||||
|
}
|
||||||
|
singboxConfig.dns.servers.Add(dnsServer);
|
||||||
|
}
|
||||||
|
else if (node?.ConfigType.IsGroupType() == true)
|
||||||
|
{
|
||||||
|
var echDnsObject = JsonUtils.DeepCopy(directDns);
|
||||||
|
echDnsObject.tag = Global.SingboxEchDNSTag;
|
||||||
|
singboxConfig.dns.servers.Add(echDnsObject);
|
||||||
|
}
|
||||||
|
|
||||||
return await Task.FromResult(0);
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +169,7 @@ public partial class CoreConfigSingboxService
|
|||||||
return await Task.FromResult(finalDns);
|
return await Task.FromResult(finalDns);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GenDnsRules(SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
private async Task<int> GenDnsRules(ProfileItem? node, SingboxConfig singboxConfig, SimpleDNSItem simpleDNSItem)
|
||||||
{
|
{
|
||||||
singboxConfig.dns ??= new Dns4Sbox();
|
singboxConfig.dns ??= new Dns4Sbox();
|
||||||
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
singboxConfig.dns.rules ??= new List<Rule4Sbox>();
|
||||||
@@ -157,17 +180,42 @@ public partial class CoreConfigSingboxService
|
|||||||
new Rule4Sbox
|
new Rule4Sbox
|
||||||
{
|
{
|
||||||
server = Global.SingboxRemoteDNSTag,
|
server = Global.SingboxRemoteDNSTag,
|
||||||
strategy = simpleDNSItem.SingboxStrategy4Proxy.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Proxy,
|
strategy = simpleDNSItem.SingboxStrategy4Proxy.NullIfEmpty(),
|
||||||
clash_mode = ERuleMode.Global.ToString()
|
clash_mode = ERuleMode.Global.ToString()
|
||||||
},
|
},
|
||||||
new Rule4Sbox
|
new Rule4Sbox
|
||||||
{
|
{
|
||||||
server = Global.SingboxDirectDNSTag,
|
server = Global.SingboxDirectDNSTag,
|
||||||
strategy = simpleDNSItem.SingboxStrategy4Direct.IsNullOrEmpty() ? null : simpleDNSItem.SingboxStrategy4Direct,
|
strategy = simpleDNSItem.SingboxStrategy4Direct.NullIfEmpty(),
|
||||||
clash_mode = ERuleMode.Direct.ToString()
|
clash_mode = ERuleMode.Direct.ToString()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var (ech, _) = ParseEchParam(node?.EchConfigList);
|
||||||
|
if (ech is not null)
|
||||||
|
{
|
||||||
|
var echDomain = ech.query_server_name ?? node?.Sni;
|
||||||
|
singboxConfig.dns.rules.Add(new()
|
||||||
|
{
|
||||||
|
query_type = new List<int> { 64, 65 },
|
||||||
|
server = Global.SingboxEchDNSTag,
|
||||||
|
domain = echDomain is not null ? new List<string> { echDomain } : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (node?.ConfigType.IsGroupType() == true)
|
||||||
|
{
|
||||||
|
var queryServerNames = (await ProfileGroupItemManager.GetAllChildEchQuerySni(node.IndexId)).ToList();
|
||||||
|
if (queryServerNames.Count > 0)
|
||||||
|
{
|
||||||
|
singboxConfig.dns.rules.Add(new()
|
||||||
|
{
|
||||||
|
query_type = new List<int> { 64, 65 },
|
||||||
|
server = Global.SingboxEchDNSTag,
|
||||||
|
domain = queryServerNames,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (simpleDNSItem.BlockBindingQuery == true)
|
if (simpleDNSItem.BlockBindingQuery == true)
|
||||||
{
|
{
|
||||||
singboxConfig.dns.rules.Add(new()
|
singboxConfig.dns.rules.Add(new()
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public partial class CoreConfigSingboxService
|
|||||||
singboxConfig.inbounds = [];
|
singboxConfig.inbounds = [];
|
||||||
|
|
||||||
if (!_config.TunModeItem.EnableTun
|
if (!_config.TunModeItem.EnableTun
|
||||||
|| (_config.TunModeItem.EnableTun && _config.TunModeItem.EnableExInbound && _config.RunningCoreType == ECoreType.sing_box))
|
|| (_config.TunModeItem.EnableTun && _config.TunModeItem.EnableExInbound && AppManager.Instance.RunningCoreType == ECoreType.sing_box))
|
||||||
{
|
{
|
||||||
var inbound = new Inbound4Sbox()
|
var inbound = new Inbound4Sbox()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public partial class CoreConfigSingboxService
|
|||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundMux(node, outbound);
|
await GenOutboundMux(node, outbound);
|
||||||
|
await GenOutboundTransport(node, outbound);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EConfigType.Shadowsocks:
|
case EConfigType.Shadowsocks:
|
||||||
@@ -33,6 +34,54 @@ public partial class CoreConfigSingboxService
|
|||||||
outbound.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : Global.None;
|
outbound.method = AppManager.Instance.GetShadowsocksSecurities(node).Contains(node.Security) ? node.Security : Global.None;
|
||||||
outbound.password = node.Id;
|
outbound.password = node.Id;
|
||||||
|
|
||||||
|
if (node.Network == nameof(ETransport.tcp) && node.HeaderType == Global.TcpHeaderHttp)
|
||||||
|
{
|
||||||
|
outbound.plugin = "obfs-local";
|
||||||
|
outbound.plugin_opts = $"obfs=http;obfs-host={node.RequestHost};";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var pluginArgs = string.Empty;
|
||||||
|
if (node.Network == nameof(ETransport.ws))
|
||||||
|
{
|
||||||
|
pluginArgs += "mode=websocket;";
|
||||||
|
pluginArgs += $"host={node.RequestHost};";
|
||||||
|
// https://github.com/shadowsocks/v2ray-plugin/blob/e9af1cdd2549d528deb20a4ab8d61c5fbe51f306/args.go#L172
|
||||||
|
// Equal signs and commas [and backslashes] must be escaped with a backslash.
|
||||||
|
var path = node.Path.Replace("\\", "\\\\").Replace("=", "\\=").Replace(",", "\\,");
|
||||||
|
pluginArgs += $"path={path};";
|
||||||
|
}
|
||||||
|
else if (node.Network == nameof(ETransport.quic))
|
||||||
|
{
|
||||||
|
pluginArgs += "mode=quic;";
|
||||||
|
}
|
||||||
|
if (node.StreamSecurity == Global.StreamSecurity)
|
||||||
|
{
|
||||||
|
pluginArgs += "tls;";
|
||||||
|
var certs = CertPemManager.ParsePemChain(node.Cert);
|
||||||
|
if (certs.Count > 0)
|
||||||
|
{
|
||||||
|
var cert = certs.First();
|
||||||
|
const string beginMarker = "-----BEGIN CERTIFICATE-----\n";
|
||||||
|
const string endMarker = "\n-----END CERTIFICATE-----";
|
||||||
|
|
||||||
|
var base64Content = cert.Replace(beginMarker, "").Replace(endMarker, "").Trim();
|
||||||
|
|
||||||
|
base64Content = base64Content.Replace("=", "\\=");
|
||||||
|
|
||||||
|
pluginArgs += $"certRaw={base64Content};";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pluginArgs.Length > 0)
|
||||||
|
{
|
||||||
|
outbound.plugin = "v2ray-plugin";
|
||||||
|
pluginArgs += "mux=0;";
|
||||||
|
// pluginStr remove last ';'
|
||||||
|
pluginArgs = pluginArgs[..^1];
|
||||||
|
outbound.plugin_opts = pluginArgs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await GenOutboundMux(node, outbound);
|
await GenOutboundMux(node, outbound);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -71,6 +120,8 @@ public partial class CoreConfigSingboxService
|
|||||||
{
|
{
|
||||||
outbound.flow = node.Flow;
|
outbound.flow = node.Flow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await GenOutboundTransport(node, outbound);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EConfigType.Trojan:
|
case EConfigType.Trojan:
|
||||||
@@ -78,6 +129,7 @@ public partial class CoreConfigSingboxService
|
|||||||
outbound.password = node.Id;
|
outbound.password = node.Id;
|
||||||
|
|
||||||
await GenOutboundMux(node, outbound);
|
await GenOutboundMux(node, outbound);
|
||||||
|
await GenOutboundTransport(node, outbound);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EConfigType.Hysteria2:
|
case EConfigType.Hysteria2:
|
||||||
@@ -127,8 +179,6 @@ public partial class CoreConfigSingboxService
|
|||||||
}
|
}
|
||||||
|
|
||||||
await GenOutboundTls(node, outbound);
|
await GenOutboundTls(node, outbound);
|
||||||
|
|
||||||
await GenOutboundTransport(node, outbound);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -232,54 +282,64 @@ public partial class CoreConfigSingboxService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (node.StreamSecurity is Global.StreamSecurityReality or Global.StreamSecurity)
|
if (node.StreamSecurity is not (Global.StreamSecurityReality or Global.StreamSecurity))
|
||||||
{
|
{
|
||||||
var server_name = string.Empty;
|
return await Task.FromResult(0);
|
||||||
if (node.Sni.IsNotEmpty())
|
}
|
||||||
{
|
if (node.ConfigType is EConfigType.Shadowsocks or EConfigType.SOCKS or EConfigType.WireGuard)
|
||||||
server_name = node.Sni;
|
{
|
||||||
}
|
return await Task.FromResult(0);
|
||||||
else if (node.RequestHost.IsNotEmpty())
|
}
|
||||||
{
|
var server_name = string.Empty;
|
||||||
server_name = Utils.String2List(node.RequestHost)?.First();
|
if (node.Sni.IsNotEmpty())
|
||||||
}
|
{
|
||||||
var tls = new Tls4Sbox()
|
server_name = node.Sni;
|
||||||
|
}
|
||||||
|
else if (node.RequestHost.IsNotEmpty())
|
||||||
|
{
|
||||||
|
server_name = Utils.String2List(node.RequestHost)?.First();
|
||||||
|
}
|
||||||
|
var tls = new Tls4Sbox()
|
||||||
|
{
|
||||||
|
enabled = true,
|
||||||
|
record_fragment = _config.CoreBasicItem.EnableFragment ? true : null,
|
||||||
|
server_name = server_name,
|
||||||
|
insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
|
||||||
|
alpn = node.GetAlpn(),
|
||||||
|
};
|
||||||
|
if (node.Fingerprint.IsNotEmpty())
|
||||||
|
{
|
||||||
|
tls.utls = new Utls4Sbox()
|
||||||
{
|
{
|
||||||
enabled = true,
|
enabled = true,
|
||||||
record_fragment = _config.CoreBasicItem.EnableFragment ? true : null,
|
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
|
||||||
server_name = server_name,
|
|
||||||
insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
|
|
||||||
alpn = node.GetAlpn(),
|
|
||||||
};
|
};
|
||||||
if (node.Fingerprint.IsNotEmpty())
|
}
|
||||||
|
if (node.StreamSecurity == Global.StreamSecurity)
|
||||||
|
{
|
||||||
|
var certs = CertPemManager.ParsePemChain(node.Cert);
|
||||||
|
if (certs.Count > 0)
|
||||||
{
|
{
|
||||||
tls.utls = new Utls4Sbox()
|
tls.certificate = certs;
|
||||||
{
|
|
||||||
enabled = true,
|
|
||||||
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (node.StreamSecurity == Global.StreamSecurity)
|
|
||||||
{
|
|
||||||
var certs = CertPemManager.ParsePemChain(node.Cert);
|
|
||||||
if (certs.Count > 0)
|
|
||||||
{
|
|
||||||
tls.certificate = certs;
|
|
||||||
tls.insecure = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (node.StreamSecurity == Global.StreamSecurityReality)
|
|
||||||
{
|
|
||||||
tls.reality = new Reality4Sbox()
|
|
||||||
{
|
|
||||||
enabled = true,
|
|
||||||
public_key = node.PublicKey,
|
|
||||||
short_id = node.ShortId
|
|
||||||
};
|
|
||||||
tls.insecure = false;
|
tls.insecure = false;
|
||||||
}
|
}
|
||||||
outbound.tls = tls;
|
|
||||||
}
|
}
|
||||||
|
else if (node.StreamSecurity == Global.StreamSecurityReality)
|
||||||
|
{
|
||||||
|
tls.reality = new Reality4Sbox()
|
||||||
|
{
|
||||||
|
enabled = true,
|
||||||
|
public_key = node.PublicKey,
|
||||||
|
short_id = node.ShortId
|
||||||
|
};
|
||||||
|
tls.insecure = false;
|
||||||
|
}
|
||||||
|
var (ech, _) = ParseEchParam(node.EchConfigList);
|
||||||
|
if (ech is not null)
|
||||||
|
{
|
||||||
|
tls.ech = ech;
|
||||||
|
}
|
||||||
|
outbound.tls = tls;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -299,29 +359,49 @@ public partial class CoreConfigSingboxService
|
|||||||
case nameof(ETransport.h2):
|
case nameof(ETransport.h2):
|
||||||
transport.type = nameof(ETransport.http);
|
transport.type = nameof(ETransport.http);
|
||||||
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
|
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
|
||||||
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
|
transport.path = node.Path.NullIfEmpty();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.tcp): //http
|
case nameof(ETransport.tcp): //http
|
||||||
if (node.HeaderType == Global.TcpHeaderHttp)
|
if (node.HeaderType == Global.TcpHeaderHttp)
|
||||||
{
|
{
|
||||||
if (node.ConfigType == EConfigType.Shadowsocks)
|
transport.type = nameof(ETransport.http);
|
||||||
{
|
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
|
||||||
outbound.plugin = "obfs-local";
|
transport.path = node.Path.NullIfEmpty();
|
||||||
outbound.plugin_opts = $"obfs=http;obfs-host={node.RequestHost};";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
transport.type = nameof(ETransport.http);
|
|
||||||
transport.host = node.RequestHost.IsNullOrEmpty() ? null : Utils.String2List(node.RequestHost);
|
|
||||||
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.ws):
|
case nameof(ETransport.ws):
|
||||||
transport.type = nameof(ETransport.ws);
|
transport.type = nameof(ETransport.ws);
|
||||||
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
|
var wsPath = node.Path;
|
||||||
|
|
||||||
|
// Parse eh and ed parameters from path using regex
|
||||||
|
if (!wsPath.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
var edRegex = new Regex(@"[?&]ed=(\d+)");
|
||||||
|
var edMatch = edRegex.Match(wsPath);
|
||||||
|
if (edMatch.Success && int.TryParse(edMatch.Groups[1].Value, out var edValue))
|
||||||
|
{
|
||||||
|
transport.max_early_data = edValue;
|
||||||
|
transport.early_data_header_name = "Sec-WebSocket-Protocol";
|
||||||
|
|
||||||
|
wsPath = edRegex.Replace(wsPath, "");
|
||||||
|
wsPath = wsPath.Replace("?&", "?");
|
||||||
|
if (wsPath.EndsWith('?'))
|
||||||
|
{
|
||||||
|
wsPath = wsPath.TrimEnd('?');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ehRegex = new Regex(@"[?&]eh=([^&]+)");
|
||||||
|
var ehMatch = ehRegex.Match(wsPath);
|
||||||
|
if (ehMatch.Success)
|
||||||
|
{
|
||||||
|
transport.early_data_header_name = Uri.UnescapeDataString(ehMatch.Groups[1].Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transport.path = wsPath.NullIfEmpty();
|
||||||
if (node.RequestHost.IsNotEmpty())
|
if (node.RequestHost.IsNotEmpty())
|
||||||
{
|
{
|
||||||
transport.headers = new()
|
transport.headers = new()
|
||||||
@@ -333,8 +413,8 @@ public partial class CoreConfigSingboxService
|
|||||||
|
|
||||||
case nameof(ETransport.httpupgrade):
|
case nameof(ETransport.httpupgrade):
|
||||||
transport.type = nameof(ETransport.httpupgrade);
|
transport.type = nameof(ETransport.httpupgrade);
|
||||||
transport.path = node.Path.IsNullOrEmpty() ? null : node.Path;
|
transport.path = node.Path.NullIfEmpty();
|
||||||
transport.host = node.RequestHost.IsNullOrEmpty() ? null : node.RequestHost;
|
transport.host = node.RequestHost.NullIfEmpty();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -829,4 +909,31 @@ public partial class CoreConfigSingboxService
|
|||||||
}
|
}
|
||||||
return await Task.FromResult(0);
|
return await Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (Ech4Sbox? ech, Server4Sbox? dnsServer) ParseEchParam(string? echConfig)
|
||||||
|
{
|
||||||
|
if (echConfig.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
if (!echConfig.Contains("://"))
|
||||||
|
{
|
||||||
|
return (new Ech4Sbox()
|
||||||
|
{
|
||||||
|
enabled = true,
|
||||||
|
config = [$"-----BEGIN ECH CONFIGS-----\n" +
|
||||||
|
$"{echConfig}\n" +
|
||||||
|
$"-----END ECH CONFIGS-----"],
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
var idx = echConfig.IndexOf('+');
|
||||||
|
// NOTE: query_server_name, since sing-box 1.13.0
|
||||||
|
//var queryServerName = idx > 0 ? echConfig[..idx] : null;
|
||||||
|
var echDnsServer = idx > 0 ? echConfig[(idx + 1)..] : echConfig;
|
||||||
|
return (new Ech4Sbox()
|
||||||
|
{
|
||||||
|
enabled = true,
|
||||||
|
query_server_name = null,
|
||||||
|
}, ParseDnsAddress(echDnsServer));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ public partial class CoreConfigSingboxService
|
|||||||
clash_mode = ERuleMode.Global.ToString()
|
clash_mode = ERuleMode.Global.ToString()
|
||||||
});
|
});
|
||||||
|
|
||||||
var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.IsNullOrEmpty() ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
|
var domainStrategy = _config.RoutingBasicItem.DomainStrategy4Singbox.NullIfEmpty();
|
||||||
var defaultRouting = await ConfigHandler.GetDefaultRouting(_config);
|
var defaultRouting = await ConfigHandler.GetDefaultRouting(_config);
|
||||||
if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty())
|
if (defaultRouting.DomainStrategy4Singbox.IsNotEmpty())
|
||||||
{
|
{
|
||||||
@@ -280,9 +280,49 @@ public partial class CoreConfigSingboxService
|
|||||||
|
|
||||||
if (_config.TunModeItem.EnableTun && item.Process?.Count > 0)
|
if (_config.TunModeItem.EnableTun && item.Process?.Count > 0)
|
||||||
{
|
{
|
||||||
rule3.process_name = item.Process;
|
var ruleProcName = JsonUtils.DeepCopy(rule3);
|
||||||
rules.Add(rule3);
|
var ruleProcPath = JsonUtils.DeepCopy(rule3);
|
||||||
hasDomainIp = true;
|
foreach (var process in item.Process)
|
||||||
|
{
|
||||||
|
// sing-box doesn't support this, fall back to process name match
|
||||||
|
if (process is "self/" or "xray/")
|
||||||
|
{
|
||||||
|
ruleProcName.process_name.Add(Utils.GetExeName("sing-box"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.Contains('/') || process.Contains('\\'))
|
||||||
|
{
|
||||||
|
var procPath = process;
|
||||||
|
if (Utils.IsWindows())
|
||||||
|
{
|
||||||
|
procPath = procPath.Replace('/', '\\');
|
||||||
|
}
|
||||||
|
ruleProcPath.process_path.Add(procPath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sing-box strictly matches the exe suffix on Windows
|
||||||
|
var procName = process;
|
||||||
|
if (Utils.IsWindows() && !procName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
procName += ".exe";
|
||||||
|
}
|
||||||
|
|
||||||
|
ruleProcName.process_name.Add(procName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ruleProcName.process_name.Count > 0)
|
||||||
|
{
|
||||||
|
rules.Add(ruleProcName);
|
||||||
|
hasDomainIp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ruleProcPath.process_path.Count > 0)
|
||||||
|
{
|
||||||
|
rules.Add(ruleProcPath);
|
||||||
|
hasDomainIp = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasDomainIp
|
if (!hasDomainIp
|
||||||
|
|||||||
@@ -197,7 +197,7 @@ public partial class CoreConfigV2rayService
|
|||||||
|
|
||||||
if (item.OutboundTag == Global.DirectTag)
|
if (item.OutboundTag == Global.DirectTag)
|
||||||
{
|
{
|
||||||
if (normalizedDomain.StartsWith("geosite:"))
|
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
|
||||||
{
|
{
|
||||||
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
|
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ public partial class CoreConfigV2rayService
|
|||||||
}
|
}
|
||||||
else if (item.OutboundTag != Global.BlockTag)
|
else if (item.OutboundTag != Global.BlockTag)
|
||||||
{
|
{
|
||||||
if (normalizedDomain.StartsWith("geosite:"))
|
if (normalizedDomain.StartsWith("geosite:") || normalizedDomain.StartsWith("ext:"))
|
||||||
{
|
{
|
||||||
proxyGeositeList.Add(normalizedDomain);
|
proxyGeositeList.Add(normalizedDomain);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,12 +178,29 @@ public partial class CoreConfigV2rayService
|
|||||||
outbound.settings.vnext = null;
|
outbound.settings.vnext = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case EConfigType.Hysteria2:
|
||||||
|
{
|
||||||
|
outbound.settings = new()
|
||||||
|
{
|
||||||
|
version = 2,
|
||||||
|
address = node.Address,
|
||||||
|
port = node.Port,
|
||||||
|
};
|
||||||
|
outbound.settings.vnext = null;
|
||||||
|
outbound.settings.servers = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case EConfigType.WireGuard:
|
case EConfigType.WireGuard:
|
||||||
{
|
{
|
||||||
|
var address = node.Address;
|
||||||
|
if (Utils.IsIpv6(address))
|
||||||
|
{
|
||||||
|
address = $"[{address}]";
|
||||||
|
}
|
||||||
var peer = new WireguardPeer4Ray
|
var peer = new WireguardPeer4Ray
|
||||||
{
|
{
|
||||||
publicKey = node.PublicKey,
|
publicKey = node.PublicKey,
|
||||||
endpoint = node.Address + ":" + node.Port.ToString()
|
endpoint = address + ":" + node.Port.ToString()
|
||||||
};
|
};
|
||||||
var setting = new Outboundsettings4Ray
|
var setting = new Outboundsettings4Ray
|
||||||
{
|
{
|
||||||
@@ -201,6 +218,10 @@ public partial class CoreConfigV2rayService
|
|||||||
}
|
}
|
||||||
|
|
||||||
outbound.protocol = Global.ProtocolTypes[node.ConfigType];
|
outbound.protocol = Global.ProtocolTypes[node.ConfigType];
|
||||||
|
if (node.ConfigType == EConfigType.Hysteria2)
|
||||||
|
{
|
||||||
|
outbound.protocol = "hysteria";
|
||||||
|
}
|
||||||
await GenBoundStreamSettings(node, outbound);
|
await GenBoundStreamSettings(node, outbound);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -241,7 +262,12 @@ public partial class CoreConfigV2rayService
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var streamSettings = outbound.streamSettings;
|
var streamSettings = outbound.streamSettings;
|
||||||
streamSettings.network = node.GetNetwork();
|
var network = node.GetNetwork();
|
||||||
|
if (node.ConfigType == EConfigType.Hysteria2)
|
||||||
|
{
|
||||||
|
network = "hysteria";
|
||||||
|
}
|
||||||
|
streamSettings.network = network;
|
||||||
var host = node.RequestHost.TrimEx();
|
var host = node.RequestHost.TrimEx();
|
||||||
var path = node.Path.TrimEx();
|
var path = node.Path.TrimEx();
|
||||||
var sni = node.Sni.TrimEx();
|
var sni = node.Sni.TrimEx();
|
||||||
@@ -267,7 +293,9 @@ public partial class CoreConfigV2rayService
|
|||||||
{
|
{
|
||||||
allowInsecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
|
allowInsecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
|
||||||
alpn = node.GetAlpn(),
|
alpn = node.GetAlpn(),
|
||||||
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint
|
fingerprint = node.Fingerprint.IsNullOrEmpty() ? _config.CoreBasicItem.DefFingerprint : node.Fingerprint,
|
||||||
|
echConfigList = node.EchConfigList.NullIfEmpty(),
|
||||||
|
echForceQuery = node.EchForceQuery.NullIfEmpty()
|
||||||
};
|
};
|
||||||
if (sni.IsNotEmpty())
|
if (sni.IsNotEmpty())
|
||||||
{
|
{
|
||||||
@@ -294,6 +322,10 @@ public partial class CoreConfigV2rayService
|
|||||||
tlsSettings.disableSystemRoot = true;
|
tlsSettings.disableSystemRoot = true;
|
||||||
tlsSettings.allowInsecure = false;
|
tlsSettings.allowInsecure = false;
|
||||||
}
|
}
|
||||||
|
else if (!node.CertSha.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
tlsSettings.pinnedPeerCertSha256 = node.CertSha;
|
||||||
|
}
|
||||||
streamSettings.tlsSettings = tlsSettings;
|
streamSettings.tlsSettings = tlsSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,7 +349,7 @@ public partial class CoreConfigV2rayService
|
|||||||
}
|
}
|
||||||
|
|
||||||
//streamSettings
|
//streamSettings
|
||||||
switch (node.GetNetwork())
|
switch (network)
|
||||||
{
|
{
|
||||||
case nameof(ETransport.kcp):
|
case nameof(ETransport.kcp):
|
||||||
KcpSettings4Ray kcpSettings = new()
|
KcpSettings4Ray kcpSettings = new()
|
||||||
@@ -335,7 +367,7 @@ public partial class CoreConfigV2rayService
|
|||||||
kcpSettings.header = new Header4Ray
|
kcpSettings.header = new Header4Ray
|
||||||
{
|
{
|
||||||
type = node.HeaderType,
|
type = node.HeaderType,
|
||||||
domain = host.IsNullOrEmpty() ? null : host
|
domain = host.NullIfEmpty()
|
||||||
};
|
};
|
||||||
if (path.IsNotEmpty())
|
if (path.IsNotEmpty())
|
||||||
{
|
{
|
||||||
@@ -351,7 +383,6 @@ public partial class CoreConfigV2rayService
|
|||||||
if (host.IsNotEmpty())
|
if (host.IsNotEmpty())
|
||||||
{
|
{
|
||||||
wsSettings.host = host;
|
wsSettings.host = host;
|
||||||
wsSettings.headers.Host = host;
|
|
||||||
}
|
}
|
||||||
if (path.IsNotEmpty())
|
if (path.IsNotEmpty())
|
||||||
{
|
{
|
||||||
@@ -446,7 +477,7 @@ public partial class CoreConfigV2rayService
|
|||||||
case nameof(ETransport.grpc):
|
case nameof(ETransport.grpc):
|
||||||
GrpcSettings4Ray grpcSettings = new()
|
GrpcSettings4Ray grpcSettings = new()
|
||||||
{
|
{
|
||||||
authority = host.IsNullOrEmpty() ? null : host,
|
authority = host.NullIfEmpty(),
|
||||||
serviceName = path,
|
serviceName = path,
|
||||||
multiMode = node.HeaderType == Global.GrpcMultiMode,
|
multiMode = node.HeaderType == Global.GrpcMultiMode,
|
||||||
idle_timeout = _config.GrpcItem.IdleTimeout,
|
idle_timeout = _config.GrpcItem.IdleTimeout,
|
||||||
@@ -457,6 +488,35 @@ public partial class CoreConfigV2rayService
|
|||||||
streamSettings.grpcSettings = grpcSettings;
|
streamSettings.grpcSettings = grpcSettings;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "hysteria":
|
||||||
|
HysteriaUdpHop4Ray? udpHop = null;
|
||||||
|
if (node.Ports.IsNotEmpty() &&
|
||||||
|
(node.Ports.Contains(':') || node.Ports.Contains('-') || node.Ports.Contains(',')))
|
||||||
|
{
|
||||||
|
udpHop = new()
|
||||||
|
{
|
||||||
|
ports = node.Ports.Replace(':', '-'),
|
||||||
|
interval = _config.HysteriaItem.HopInterval > 0
|
||||||
|
? _config.HysteriaItem.HopInterval
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
HysteriaSettings4Ray hysteriaSettings = new()
|
||||||
|
{
|
||||||
|
version = 2,
|
||||||
|
auth = node.Id,
|
||||||
|
up = _config.HysteriaItem.UpMbps > 0 ? $"{_config.HysteriaItem.UpMbps}mbps" : null,
|
||||||
|
down = _config.HysteriaItem.DownMbps > 0 ? $"{_config.HysteriaItem.DownMbps}mbps" : null,
|
||||||
|
udphop = udpHop,
|
||||||
|
};
|
||||||
|
streamSettings.hysteriaSettings = hysteriaSettings;
|
||||||
|
if (node.Path.IsNotEmpty())
|
||||||
|
{
|
||||||
|
streamSettings.udpmasks =
|
||||||
|
[new() { type = "salamander", settings = new() { password = node.Path.TrimEx(), } }];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
//tcp
|
//tcp
|
||||||
if (node.HeaderType == Global.TcpHeaderHttp)
|
if (node.HeaderType == Global.TcpHeaderHttp)
|
||||||
@@ -560,7 +620,7 @@ public partial class CoreConfigV2rayService
|
|||||||
var fragmentOutbound = new Outbounds4Ray
|
var fragmentOutbound = new Outbounds4Ray
|
||||||
{
|
{
|
||||||
protocol = "freedom",
|
protocol = "freedom",
|
||||||
tag = $"{Global.ProxyTag}3",
|
tag = $"frag-{Global.ProxyTag}",
|
||||||
settings = new()
|
settings = new()
|
||||||
{
|
{
|
||||||
fragment = new()
|
fragment = new()
|
||||||
|
|||||||
@@ -77,12 +77,17 @@ public partial class CoreConfigV2rayService
|
|||||||
{
|
{
|
||||||
rule.inboundTag = null;
|
rule.inboundTag = null;
|
||||||
}
|
}
|
||||||
|
if (rule.process?.Count == 0)
|
||||||
|
{
|
||||||
|
rule.process = null;
|
||||||
|
}
|
||||||
|
|
||||||
var hasDomainIp = false;
|
var hasDomainIp = false;
|
||||||
if (rule.domain?.Count > 0)
|
if (rule.domain?.Count > 0)
|
||||||
{
|
{
|
||||||
var it = JsonUtils.DeepCopy(rule);
|
var it = JsonUtils.DeepCopy(rule);
|
||||||
it.ip = null;
|
it.ip = null;
|
||||||
|
it.process = null;
|
||||||
it.type = "field";
|
it.type = "field";
|
||||||
for (var k = it.domain.Count - 1; k >= 0; k--)
|
for (var k = it.domain.Count - 1; k >= 0; k--)
|
||||||
{
|
{
|
||||||
@@ -99,6 +104,16 @@ public partial class CoreConfigV2rayService
|
|||||||
{
|
{
|
||||||
var it = JsonUtils.DeepCopy(rule);
|
var it = JsonUtils.DeepCopy(rule);
|
||||||
it.domain = null;
|
it.domain = null;
|
||||||
|
it.process = null;
|
||||||
|
it.type = "field";
|
||||||
|
v2rayConfig.routing.rules.Add(it);
|
||||||
|
hasDomainIp = true;
|
||||||
|
}
|
||||||
|
if (_config.TunModeItem.EnableTun && rule.process?.Count > 0)
|
||||||
|
{
|
||||||
|
var it = JsonUtils.DeepCopy(rule);
|
||||||
|
it.domain = null;
|
||||||
|
it.ip = null;
|
||||||
it.type = "field";
|
it.type = "field";
|
||||||
v2rayConfig.routing.rules.Add(it);
|
v2rayConfig.routing.rules.Add(it);
|
||||||
hasDomainIp = true;
|
hasDomainIp = true;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace ServiceLib.Services;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DownloadService
|
public class DownloadService
|
||||||
{
|
{
|
||||||
public event EventHandler<RetResult>? UpdateCompleted;
|
public event EventHandler<UpdateResult>? UpdateCompleted;
|
||||||
|
|
||||||
public event ErrorEventHandler? Error;
|
public event ErrorEventHandler? Error;
|
||||||
|
|
||||||
@@ -40,10 +40,10 @@ public class DownloadService
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
UpdateCompleted?.Invoke(this, new RetResult(false, $"{ResUI.Downloading} {url}"));
|
UpdateCompleted?.Invoke(this, new UpdateResult(false, $"{ResUI.Downloading} {url}"));
|
||||||
|
|
||||||
var progress = new Progress<double>();
|
var progress = new Progress<double>();
|
||||||
progress.ProgressChanged += (sender, value) => UpdateCompleted?.Invoke(this, new RetResult(value > 100, $"...{value}%"));
|
progress.ProgressChanged += (sender, value) => UpdateCompleted?.Invoke(this, new UpdateResult(value > 100, $"...{value}%"));
|
||||||
|
|
||||||
var webProxy = await GetWebProxy(blProxy);
|
var webProxy = await GetWebProxy(blProxy);
|
||||||
await DownloaderHelper.Instance.DownloadFileAsync(webProxy,
|
await DownloaderHelper.Instance.DownloadFileAsync(webProxy,
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
|||||||
{
|
{
|
||||||
if (!it.AllowTest)
|
if (!it.AllowTest)
|
||||||
{
|
{
|
||||||
|
await UpdateFunc(it.IndexId, ResUI.SpeedtestingSkip);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -323,31 +324,28 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
|
|||||||
{
|
{
|
||||||
var responseTime = -1;
|
var responseTime = -1;
|
||||||
|
|
||||||
|
if (!IPAddress.TryParse(url, out var ipAddress))
|
||||||
|
{
|
||||||
|
var ipHostInfo = await Dns.GetHostEntryAsync(url);
|
||||||
|
ipAddress = ipHostInfo.AddressList.First();
|
||||||
|
}
|
||||||
|
|
||||||
|
IPEndPoint endPoint = new(ipAddress, port);
|
||||||
|
using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||||
|
|
||||||
|
var timer = Stopwatch.StartNew();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!IPAddress.TryParse(url, out var ipAddress))
|
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
|
||||||
{
|
await clientSocket.ConnectAsync(endPoint, cts.Token).ConfigureAwait(false);
|
||||||
var ipHostInfo = await Dns.GetHostEntryAsync(url);
|
responseTime = (int)timer.ElapsedMilliseconds;
|
||||||
ipAddress = ipHostInfo.AddressList.First();
|
|
||||||
}
|
|
||||||
|
|
||||||
IPEndPoint endPoint = new(ipAddress, port);
|
|
||||||
using Socket clientSocket = new(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
|
|
||||||
var timer = Stopwatch.StartNew();
|
|
||||||
var result = clientSocket.BeginConnect(endPoint, null, null);
|
|
||||||
if (!result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(5)))
|
|
||||||
{
|
|
||||||
throw new TimeoutException("connect timeout (5s): " + url);
|
|
||||||
}
|
|
||||||
timer.Stop();
|
|
||||||
responseTime = (int)timer.Elapsed.TotalMilliseconds;
|
|
||||||
|
|
||||||
clientSocket.EndConnect(result);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
timer.Stop();
|
||||||
}
|
}
|
||||||
return responseTime;
|
return responseTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public class StatisticsSingboxService
|
|||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!_config.IsRunningCore(ECoreType.sing_box))
|
if (!AppManager.Instance.IsRunningCore(ECoreType.sing_box))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class StatisticsXrayService
|
|||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_config.RunningCoreType != ECoreType.Xray)
|
if (AppManager.Instance.RunningCoreType != ECoreType.Xray)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
|
|||||||
await UpdateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, ECoreType.v2rayN));
|
await UpdateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, ECoreType.v2rayN));
|
||||||
await UpdateFunc(false, result.Msg);
|
await UpdateFunc(false, result.Msg);
|
||||||
|
|
||||||
url = result.Data?.ToString();
|
url = result.Url.ToString();
|
||||||
fileName = Utils.GetTempPath(Utils.GetGuid());
|
fileName = Utils.GetTempPath(Utils.GetGuid());
|
||||||
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
|
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
|
|||||||
await UpdateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, type));
|
await UpdateFunc(false, string.Format(ResUI.MsgParsingSuccessfully, type));
|
||||||
await UpdateFunc(false, result.Msg);
|
await UpdateFunc(false, result.Msg);
|
||||||
|
|
||||||
url = result.Data?.ToString();
|
url = result.Url.ToString();
|
||||||
var ext = url.Contains(".tar.gz") ? ".tar.gz" : Path.GetExtension(url);
|
var ext = url.Contains(".tar.gz") ? ".tar.gz" : Path.GetExtension(url);
|
||||||
fileName = Utils.GetTempPath(Utils.GetGuid() + ext);
|
fileName = Utils.GetTempPath(Utils.GetGuid() + ext);
|
||||||
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
|
await downloadHandle.DownloadFileAsync(url, fileName, true, _timeout);
|
||||||
@@ -110,26 +110,26 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
|
|||||||
|
|
||||||
#region CheckUpdate private
|
#region CheckUpdate private
|
||||||
|
|
||||||
private async Task<RetResult> CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease)
|
private async Task<UpdateResult> CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await GetRemoteVersion(downloadHandle, type, preRelease);
|
var result = await GetRemoteVersion(downloadHandle, type, preRelease);
|
||||||
if (!result.Success || result.Data is null)
|
if (!result.Success || result.Version is null)
|
||||||
{
|
{
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return await ParseDownloadUrl(type, (SemanticVersion)result.Data);
|
return await ParseDownloadUrl(type, result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
await UpdateFunc(false, ex.Message);
|
await UpdateFunc(false, ex.Message);
|
||||||
return new RetResult(false, ex.Message);
|
return new UpdateResult(false, ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<RetResult> GetRemoteVersion(DownloadService downloadHandle, ECoreType type, bool preRelease)
|
private async Task<UpdateResult> GetRemoteVersion(DownloadService downloadHandle, ECoreType type, bool preRelease)
|
||||||
{
|
{
|
||||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
|
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
|
||||||
var tagName = string.Empty;
|
var tagName = string.Empty;
|
||||||
@@ -139,7 +139,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
|
|||||||
var result = await downloadHandle.TryDownloadString(url, true, Global.AppName);
|
var result = await downloadHandle.TryDownloadString(url, true, Global.AppName);
|
||||||
if (result.IsNullOrEmpty())
|
if (result.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
return new RetResult(false, "");
|
return new UpdateResult(false, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
var gitHubReleases = JsonUtils.Deserialize<List<GitHubRelease>>(result);
|
var gitHubReleases = JsonUtils.Deserialize<List<GitHubRelease>>(result);
|
||||||
@@ -153,12 +153,12 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
|
|||||||
var lastUrl = await downloadHandle.UrlRedirectAsync(url, true);
|
var lastUrl = await downloadHandle.UrlRedirectAsync(url, true);
|
||||||
if (lastUrl == null)
|
if (lastUrl == null)
|
||||||
{
|
{
|
||||||
return new RetResult(false, "");
|
return new UpdateResult(false, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
tagName = lastUrl?.Split("/tag/").LastOrDefault();
|
tagName = lastUrl?.Split("/tag/").LastOrDefault();
|
||||||
}
|
}
|
||||||
return new RetResult(true, "", new SemanticVersion(tagName));
|
return new UpdateResult(true, new SemanticVersion(tagName));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<SemanticVersion> GetCoreVersion(ECoreType type)
|
private async Task<SemanticVersion> GetCoreVersion(ECoreType type)
|
||||||
@@ -213,10 +213,11 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<RetResult> ParseDownloadUrl(ECoreType type, SemanticVersion version)
|
private async Task<UpdateResult> ParseDownloadUrl(ECoreType type, UpdateResult result)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
var version = result.Version ?? new SemanticVersion(0, 0, 0);
|
||||||
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
|
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
|
||||||
var coreUrl = await GetUrlFromCore(coreInfo) ?? string.Empty;
|
var coreUrl = await GetUrlFromCore(coreInfo) ?? string.Empty;
|
||||||
SemanticVersion curVersion;
|
SemanticVersion curVersion;
|
||||||
@@ -260,16 +261,17 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
|
|||||||
|
|
||||||
if (curVersion >= version && version != new SemanticVersion(0, 0, 0))
|
if (curVersion >= version && version != new SemanticVersion(0, 0, 0))
|
||||||
{
|
{
|
||||||
return new RetResult(false, message);
|
return new UpdateResult(false, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RetResult(true, "", url);
|
result.Url = url;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logging.SaveLog(_tag, ex);
|
Logging.SaveLog(_tag, ex);
|
||||||
await UpdateFunc(false, ex.Message);
|
await UpdateFunc(false, ex.Message);
|
||||||
return new RetResult(false, ex.Message);
|
return new UpdateResult(false, ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,13 +291,6 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check for standalone windows .Net version
|
|
||||||
if (File.Exists(Path.Combine(Utils.GetBaseDirectory(), "wpfgfx_cor3.dll"))
|
|
||||||
&& File.Exists(Path.Combine(Utils.GetBaseDirectory(), "D3DCompiler_47_cor3.dll")))
|
|
||||||
{
|
|
||||||
return url?.Replace(".zip", "-SelfContained.zip");
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check for avalonia desktop windows version
|
//Check for avalonia desktop windows version
|
||||||
if (File.Exists(Path.Combine(Utils.GetBaseDirectory(), "libHarfBuzzSharp.dll")))
|
if (File.Exists(Path.Combine(Utils.GetBaseDirectory(), "libHarfBuzzSharp.dll")))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public string CertTip { get; set; }
|
public string CertTip { get; set; }
|
||||||
|
|
||||||
|
[Reactive]
|
||||||
|
public string CertSha { get; set; }
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
|
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
|
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||||
@@ -39,6 +42,12 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
this.WhenAnyValue(x => x.Cert)
|
this.WhenAnyValue(x => x.Cert)
|
||||||
.Subscribe(_ => UpdateCertTip());
|
.Subscribe(_ => UpdateCertTip());
|
||||||
|
|
||||||
|
this.WhenAnyValue(x => x.CertSha)
|
||||||
|
.Subscribe(_ => UpdateCertTip());
|
||||||
|
|
||||||
|
this.WhenAnyValue(x => x.Cert)
|
||||||
|
.Subscribe(_ => UpdateCertSha());
|
||||||
|
|
||||||
if (profileItem.IndexId.IsNullOrEmpty())
|
if (profileItem.IndexId.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
profileItem.Network = Global.DefaultNetwork;
|
profileItem.Network = Global.DefaultNetwork;
|
||||||
@@ -53,6 +62,7 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
}
|
}
|
||||||
CoreType = SelectedSource?.CoreType?.ToString();
|
CoreType = SelectedSource?.CoreType?.ToString();
|
||||||
Cert = SelectedSource?.Cert?.ToString() ?? string.Empty;
|
Cert = SelectedSource?.Cert?.ToString() ?? string.Empty;
|
||||||
|
CertSha = SelectedSource?.CertSha?.ToString() ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SaveServerAsync()
|
private async Task SaveServerAsync()
|
||||||
@@ -97,7 +107,8 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? null : (ECoreType)Enum.Parse(typeof(ECoreType), CoreType);
|
||||||
SelectedSource.Cert = Cert.IsNullOrEmpty() ? null : Cert;
|
SelectedSource.Cert = Cert.IsNullOrEmpty() ? string.Empty : Cert;
|
||||||
|
SelectedSource.CertSha = CertSha.IsNullOrEmpty() ? string.Empty : CertSha;
|
||||||
|
|
||||||
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
|
||||||
{
|
{
|
||||||
@@ -113,10 +124,36 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
private void UpdateCertTip(string? errorMessage = null)
|
private void UpdateCertTip(string? errorMessage = null)
|
||||||
{
|
{
|
||||||
CertTip = errorMessage.IsNullOrEmpty()
|
CertTip = errorMessage.IsNullOrEmpty()
|
||||||
? (Cert.IsNullOrEmpty() ? ResUI.CertNotSet : ResUI.CertSet)
|
? ((Cert.IsNullOrEmpty() && CertSha.IsNullOrEmpty()) ? ResUI.CertNotSet : ResUI.CertSet)
|
||||||
: errorMessage;
|
: errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateCertSha()
|
||||||
|
{
|
||||||
|
if (Cert.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var certList = CertPemManager.ParsePemChain(Cert);
|
||||||
|
if (certList.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<string> shaList = new();
|
||||||
|
foreach (var cert in certList)
|
||||||
|
{
|
||||||
|
var sha = CertPemManager.GetCertSha256Thumbprint(cert);
|
||||||
|
if (sha.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
shaList.Add(sha);
|
||||||
|
}
|
||||||
|
CertSha = string.Join('~', shaList);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task FetchCert()
|
private async Task FetchCert()
|
||||||
{
|
{
|
||||||
if (SelectedSource.StreamSecurity != Global.StreamSecurity)
|
if (SelectedSource.StreamSecurity != Global.StreamSecurity)
|
||||||
@@ -142,8 +179,8 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
domain += $":{SelectedSource.Port}";
|
domain += $":{SelectedSource.Port}";
|
||||||
}
|
}
|
||||||
string certError;
|
|
||||||
(Cert, certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName);
|
(Cert, var certError) = await CertPemManager.Instance.GetCertPemAsync(domain, serverName);
|
||||||
UpdateCertTip(certError);
|
UpdateCertTip(certError);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,8 +209,8 @@ public class AddServerViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
domain += $":{SelectedSource.Port}";
|
domain += $":{SelectedSource.Port}";
|
||||||
}
|
}
|
||||||
string certError;
|
|
||||||
(var certs, certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName);
|
var (certs, certError) = await CertPemManager.Instance.GetCertChainPemAsync(domain, serverName);
|
||||||
Cert = CertPemManager.ConcatenatePemChain(certs);
|
Cert = CertPemManager.ConcatenatePemChain(certs);
|
||||||
UpdateCertTip(certError);
|
UpdateCertTip(certError);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ public class ClashConnectionsViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
await Task.Delay(1000 * 5);
|
await Task.Delay(1000 * 5);
|
||||||
numOfExecuted++;
|
numOfExecuted++;
|
||||||
if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box)))
|
if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar && AppManager.Instance.IsRunningCore(ECoreType.sing_box)))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -437,7 +437,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
await Task.Delay(1000 * 60);
|
await Task.Delay(1000 * 60);
|
||||||
numOfExecuted++;
|
numOfExecuted++;
|
||||||
if (!(AutoRefresh && _config.UiItem.ShowInTaskbar && _config.IsRunningCore(ECoreType.sing_box)))
|
if (!(AutoRefresh && AppManager.Instance.ShowInTaskbar && AppManager.Instance.IsRunningCore(ECoreType.sing_box)))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
private async Task Init()
|
private async Task Init()
|
||||||
{
|
{
|
||||||
_config.UiItem.ShowInTaskbar = true;
|
AppManager.Instance.ShowInTaskbar = true;
|
||||||
|
|
||||||
//await ConfigHandler.InitBuiltinRouting(_config);
|
//await ConfigHandler.InitBuiltinRouting(_config);
|
||||||
await ConfigHandler.InitBuiltinDNS(_config);
|
await ConfigHandler.InitBuiltinDNS(_config);
|
||||||
@@ -306,7 +306,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
|
|
||||||
private async Task UpdateStatisticsHandler(ServerSpeedItem update)
|
private async Task UpdateStatisticsHandler(ServerSpeedItem update)
|
||||||
{
|
{
|
||||||
if (!_config.UiItem.ShowInTaskbar)
|
if (!AppManager.Instance.ShowInTaskbar)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -560,7 +560,7 @@ public class MainWindowViewModel : MyReactiveObject
|
|||||||
});
|
});
|
||||||
AppEvents.TestServerRequested.Publish();
|
AppEvents.TestServerRequested.Publish();
|
||||||
|
|
||||||
var showClashUI = _config.IsRunningCore(ECoreType.sing_box);
|
var showClashUI = AppManager.Instance.IsRunningCore(ECoreType.sing_box);
|
||||||
if (showClashUI)
|
if (showClashUI)
|
||||||
{
|
{
|
||||||
AppEvents.ProxiesReloadRequested.Publish();
|
AppEvents.ProxiesReloadRequested.Publish();
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class MsgViewModel : MyReactiveObject
|
|||||||
|
|
||||||
EnqueueQueueMsg(msg);
|
EnqueueQueueMsg(msg);
|
||||||
|
|
||||||
if (!_config.UiItem.ShowInTaskbar)
|
if (!AppManager.Instance.ShowInTaskbar)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||||||
[Reactive] public string CoreType4 { get; set; }
|
[Reactive] public string CoreType4 { get; set; }
|
||||||
[Reactive] public string CoreType5 { get; set; }
|
[Reactive] public string CoreType5 { get; set; }
|
||||||
[Reactive] public string CoreType6 { get; set; }
|
[Reactive] public string CoreType6 { get; set; }
|
||||||
|
[Reactive] public string CoreType7 { get; set; }
|
||||||
[Reactive] public string CoreType9 { get; set; }
|
[Reactive] public string CoreType9 { get; set; }
|
||||||
|
|
||||||
#endregion CoreType
|
#endregion CoreType
|
||||||
@@ -276,6 +277,10 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||||||
CoreType6 = type;
|
CoreType6 = type;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
CoreType7 = type;
|
||||||
|
break;
|
||||||
|
|
||||||
case 9:
|
case 9:
|
||||||
CoreType9 = type;
|
CoreType9 = type;
|
||||||
break;
|
break;
|
||||||
@@ -427,6 +432,10 @@ public class OptionSettingViewModel : MyReactiveObject
|
|||||||
type = CoreType6;
|
type = CoreType6;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
type = CoreType7;
|
||||||
|
break;
|
||||||
|
|
||||||
case 9:
|
case 9:
|
||||||
type = CoreType9;
|
type = CoreType9;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
|
public ReactiveCommand<Unit, Unit> MoveUpCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
|
public ReactiveCommand<Unit, Unit> MoveDownCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
|
public ReactiveCommand<Unit, Unit> MoveBottomCmd { get; }
|
||||||
|
public ReactiveCommand<SubItem, Unit> MoveToGroupCmd { get; }
|
||||||
|
|
||||||
//servers ping
|
//servers ping
|
||||||
public ReactiveCommand<Unit, Unit> MixedTestServerCmd { get; }
|
public ReactiveCommand<Unit, Unit> MixedTestServerCmd { get; }
|
||||||
@@ -77,6 +78,7 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> AddSubCmd { get; }
|
public ReactiveCommand<Unit, Unit> AddSubCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> EditSubCmd { get; }
|
public ReactiveCommand<Unit, Unit> EditSubCmd { get; }
|
||||||
|
public ReactiveCommand<Unit, Unit> DeleteSubCmd { get; }
|
||||||
|
|
||||||
#endregion Menu
|
#endregion Menu
|
||||||
|
|
||||||
@@ -178,6 +180,10 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
await MoveServer(EMove.Bottom);
|
await MoveServer(EMove.Bottom);
|
||||||
}, canEditRemove);
|
}, canEditRemove);
|
||||||
|
MoveToGroupCmd = ReactiveCommand.CreateFromTask<SubItem>(async sub =>
|
||||||
|
{
|
||||||
|
SelectedMoveToGroup = sub;
|
||||||
|
});
|
||||||
|
|
||||||
//servers ping
|
//servers ping
|
||||||
FastRealPingCmd = ReactiveCommand.CreateFromTask(async () =>
|
FastRealPingCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
@@ -235,6 +241,10 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
await EditSubAsync(false);
|
await EditSubAsync(false);
|
||||||
});
|
});
|
||||||
|
DeleteSubCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
|
{
|
||||||
|
await DeleteSubAsync();
|
||||||
|
});
|
||||||
|
|
||||||
#endregion WhenAnyValue && ReactiveCommand
|
#endregion WhenAnyValue && ReactiveCommand
|
||||||
|
|
||||||
@@ -553,6 +563,11 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
|
|
||||||
private async Task RemoveDuplicateServer()
|
private async Task RemoveDuplicateServer()
|
||||||
{
|
{
|
||||||
|
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var tuple = await ConfigHandler.DedupServerList(_config, _config.SubIndexId);
|
var tuple = await ConfigHandler.DedupServerList(_config, _config.SubIndexId);
|
||||||
if (tuple.Item1 > 0 || tuple.Item2 > 0)
|
if (tuple.Item1 > 0 || tuple.Item2 > 0)
|
||||||
{
|
{
|
||||||
@@ -879,5 +894,23 @@ public class ProfilesViewModel : MyReactiveObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task DeleteSubAsync()
|
||||||
|
{
|
||||||
|
var item = await AppManager.Instance.GetSubItem(_config.SubIndexId);
|
||||||
|
if (item is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await _updateView?.Invoke(EViewAction.ShowYesNo, null) == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await ConfigHandler.DeleteSubItem(_config, item.Id);
|
||||||
|
|
||||||
|
await RefreshSubscriptions();
|
||||||
|
await SubSelectedChangedAsync(true);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Subscription
|
#endregion Subscription
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -549,7 +549,7 @@ public class StatusBarViewModel : MyReactiveObject
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_config.IsRunningCore(ECoreType.sing_box))
|
if (AppManager.Instance.IsRunningCore(ECoreType.sing_box))
|
||||||
{
|
{
|
||||||
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, EInboundProtocol.mixed, Utils.HumanFy(update.ProxyUp), Utils.HumanFy(update.ProxyDown));
|
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, EInboundProtocol.mixed, Utils.HumanFy(update.ProxyUp), Utils.HumanFy(update.ProxyDown));
|
||||||
SpeedDirectDisplay = string.Empty;
|
SpeedDirectDisplay = string.Empty;
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using Avalonia;
|
|
||||||
using Avalonia.Media;
|
|
||||||
|
|
||||||
namespace v2rayN.Desktop.Common;
|
namespace v2rayN.Desktop.Common;
|
||||||
|
|
||||||
public static class AppBuilderExtension
|
public static class AppBuilderExtension
|
||||||
|
|||||||
@@ -34,8 +34,7 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
Grid.Row="0"
|
ColumnDefinitions="300,Auto,Auto"
|
||||||
ColumnDefinitions="180,Auto,Auto"
|
|
||||||
DockPanel.Dock="Top"
|
DockPanel.Dock="Top"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -75,7 +74,7 @@
|
|||||||
Grid.Row="3"
|
Grid.Row="3"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="3"
|
Grid.ColumnSpan="3"
|
||||||
ColumnDefinitions="180,Auto,Auto">
|
ColumnDefinitions="300,Auto,Auto">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
@@ -89,11 +88,11 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TabControl DockPanel.Dock="Top">
|
<TabControl HorizontalContentAlignment="Stretch" DockPanel.Dock="Top">
|
||||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
||||||
<Grid
|
<Grid
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
ColumnDefinitions="180,Auto,Auto"
|
ColumnDefinitions="300,Auto,Auto"
|
||||||
RowDefinitions="Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -131,10 +130,8 @@
|
|||||||
VerticalAlignment="Center" />
|
VerticalAlignment="Center" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</TabControl>
|
|
||||||
|
|
||||||
<TabControl>
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList2}">
|
||||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
x:Name="lstChild"
|
x:Name="lstChild"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<Grid
|
<Grid
|
||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
ColumnDefinitions="180,Auto,Auto"
|
ColumnDefinitions="300,Auto,Auto"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbCoreType"
|
x:Name="cmbCoreType"
|
||||||
Width="100"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
ToolTip.Tip="{x:Static resx:ResUI.TbCoreType}" />
|
ToolTip.Tip="{x:Static resx:ResUI.TbCoreType}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridVMess"
|
x:Name="gridVMess"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
ColumnDefinitions="180,Auto,Auto"
|
ColumnDefinitions="300,Auto,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridSs"
|
x:Name="gridSs"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
ColumnDefinitions="180,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridSocks"
|
x:Name="gridSocks"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
ColumnDefinitions="180,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridVLESS"
|
x:Name="gridVLESS"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
ColumnDefinitions="180,Auto,Auto"
|
ColumnDefinitions="300,Auto,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
@@ -312,7 +312,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTrojan"
|
x:Name="gridTrojan"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
ColumnDefinitions="180,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
@@ -358,7 +358,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridHysteria2"
|
x:Name="gridHysteria2"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
ColumnDefinitions="180,Auto,Auto"
|
ColumnDefinitions="300,Auto,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
@@ -411,7 +411,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTuic"
|
x:Name="gridTuic"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
ColumnDefinitions="180,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
@@ -457,7 +457,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridWireguard"
|
x:Name="gridWireguard"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
ColumnDefinitions="180,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
@@ -534,7 +534,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridAnytls"
|
x:Name="gridAnytls"
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
ColumnDefinitions="180,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto">
|
||||||
|
|
||||||
@@ -560,7 +560,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTransport"
|
x:Name="gridTransport"
|
||||||
Grid.Row="4"
|
Grid.Row="4"
|
||||||
ColumnDefinitions="180,Auto,Auto"
|
ColumnDefinitions="300,Auto,Auto"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -692,7 +692,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTls"
|
x:Name="gridTls"
|
||||||
Grid.Row="6"
|
Grid.Row="6"
|
||||||
ColumnDefinitions="180,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@@ -711,9 +711,9 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridTlsMore"
|
x:Name="gridTlsMore"
|
||||||
Grid.Row="7"
|
Grid.Row="7"
|
||||||
ColumnDefinitions="180,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
@@ -768,15 +768,41 @@
|
|||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}" />
|
Margin="{StaticResource Margin4}" />
|
||||||
|
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="5"
|
Grid.Row="5"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbEchConfigList}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtEchConfigList"
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbEchForceQuery}" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbEchForceQuery"
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="7"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="{x:Static resx:ResUI.TbCertPinning}" />
|
Text="{x:Static resx:ResUI.TbCertPinning}" />
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Row="5"
|
Grid.Row="7"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
@@ -815,6 +841,23 @@
|
|||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
Content="{x:Static resx:ResUI.TbFetchCertChain}" />
|
Content="{x:Static resx:ResUI.TbFetchCertChain}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbCertSha256Tips}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtCertSha256Pinning"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left" />
|
||||||
|
<TextBlock
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{x:Static resx:ResUI.TbFullCertTips}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtCert"
|
x:Name="txtCert"
|
||||||
Width="400"
|
Width="400"
|
||||||
@@ -831,7 +874,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
x:Name="gridRealityMore"
|
x:Name="gridRealityMore"
|
||||||
Grid.Row="7"
|
Grid.Row="7"
|
||||||
ColumnDefinitions="180,Auto"
|
ColumnDefinitions="300,Auto"
|
||||||
IsVisible="False"
|
IsVisible="False"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||||||
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
||||||
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
|
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
|
||||||
cmbAlpn.ItemsSource = Global.Alpns;
|
cmbAlpn.ItemsSource = Global.Alpns;
|
||||||
|
cmbEchForceQuery.ItemsSource = Global.EchForceQuerys;
|
||||||
|
|
||||||
var lstStreamSecurity = new List<string>();
|
var lstStreamSecurity = new List<string>();
|
||||||
lstStreamSecurity.Add(string.Empty);
|
lstStreamSecurity.Add(string.Empty);
|
||||||
@@ -74,7 +75,6 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||||||
gridHysteria2.IsVisible = true;
|
gridHysteria2.IsVisible = true;
|
||||||
sepa2.IsVisible = false;
|
sepa2.IsVisible = false;
|
||||||
gridTransport.IsVisible = false;
|
gridTransport.IsVisible = false;
|
||||||
cmbCoreType.IsEnabled = false;
|
|
||||||
cmbFingerprint.IsEnabled = false;
|
cmbFingerprint.IsEnabled = false;
|
||||||
cmbFingerprint.SelectedValue = string.Empty;
|
cmbFingerprint.SelectedValue = string.Empty;
|
||||||
break;
|
break;
|
||||||
@@ -185,8 +185,12 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
|||||||
this.Bind(ViewModel, vm => vm.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.EchForceQuery, v => v.cmbEchForceQuery.SelectedValue).DisposeWith(disposables);
|
||||||
|
|
||||||
//reality
|
//reality
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.SelectedValue).DisposeWith(disposables);
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
xmlns:view="using:v2rayN.Desktop.Views"
|
xmlns:view="using:v2rayN.Desktop.Views"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
Title="v2rayN"
|
Title="v2rayN"
|
||||||
Width="1000"
|
Width="1200"
|
||||||
Height="600"
|
Height="800"
|
||||||
MinWidth="900"
|
MinWidth="600"
|
||||||
x:DataType="vms:MainWindowViewModel"
|
x:DataType="vms:MainWindowViewModel"
|
||||||
Icon="/Assets/NotifyIcon1.ico"
|
Icon="/Assets/NotifyIcon1.ico"
|
||||||
ShowInTaskbar="True"
|
ShowInTaskbar="True"
|
||||||
@@ -43,11 +43,11 @@
|
|||||||
<MenuItem x:Name="menuAddVlessServer" Header="{x:Static resx:ResUI.menuAddVlessServer}" />
|
<MenuItem x:Name="menuAddVlessServer" Header="{x:Static resx:ResUI.menuAddVlessServer}" />
|
||||||
<MenuItem x:Name="menuAddShadowsocksServer" Header="{x:Static resx:ResUI.menuAddShadowsocksServer}" />
|
<MenuItem x:Name="menuAddShadowsocksServer" Header="{x:Static resx:ResUI.menuAddShadowsocksServer}" />
|
||||||
<MenuItem x:Name="menuAddTrojanServer" Header="{x:Static resx:ResUI.menuAddTrojanServer}" />
|
<MenuItem x:Name="menuAddTrojanServer" Header="{x:Static resx:ResUI.menuAddTrojanServer}" />
|
||||||
|
<MenuItem x:Name="menuAddHysteria2Server" Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
|
||||||
<MenuItem x:Name="menuAddWireguardServer" Header="{x:Static resx:ResUI.menuAddWireguardServer}" />
|
<MenuItem x:Name="menuAddWireguardServer" Header="{x:Static resx:ResUI.menuAddWireguardServer}" />
|
||||||
<MenuItem x:Name="menuAddSocksServer" Header="{x:Static resx:ResUI.menuAddSocksServer}" />
|
<MenuItem x:Name="menuAddSocksServer" Header="{x:Static resx:ResUI.menuAddSocksServer}" />
|
||||||
<MenuItem x:Name="menuAddHttpServer" Header="{x:Static resx:ResUI.menuAddHttpServer}" />
|
<MenuItem x:Name="menuAddHttpServer" Header="{x:Static resx:ResUI.menuAddHttpServer}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem x:Name="menuAddHysteria2Server" Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
|
|
||||||
<MenuItem x:Name="menuAddTuicServer" Header="{x:Static resx:ResUI.menuAddTuicServer}" />
|
<MenuItem x:Name="menuAddTuicServer" Header="{x:Static resx:ResUI.menuAddTuicServer}" />
|
||||||
<MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
|
<MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
@@ -90,13 +90,13 @@
|
|||||||
<MenuItem x:Name="menuOpenTheFileLocation" Header="{x:Static resx:ResUI.menuOpenTheFileLocation}" />
|
<MenuItem x:Name="menuOpenTheFileLocation" Header="{x:Static resx:ResUI.menuOpenTheFileLocation}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
<MenuItem x:Name="menuReload" Header="{x:Static resx:ResUI.menuReload}" />
|
|
||||||
|
|
||||||
<MenuItem x:Name="menuHelp" Header="{x:Static resx:ResUI.menuHelp}">
|
<MenuItem x:Name="menuHelp" Header="{x:Static resx:ResUI.menuHelp}">
|
||||||
<MenuItem x:Name="menuCheckUpdate" Header="{x:Static resx:ResUI.menuCheckUpdate}" />
|
<MenuItem x:Name="menuCheckUpdate" Header="{x:Static resx:ResUI.menuCheckUpdate}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|
||||||
|
<MenuItem x:Name="menuReload" Header="{x:Static resx:ResUI.menuReload}" />
|
||||||
|
|
||||||
<MenuItem x:Name="menuPromotion" Header="{x:Static resx:ResUI.menuPromotion}" />
|
<MenuItem x:Name="menuPromotion" Header="{x:Static resx:ResUI.menuPromotion}" />
|
||||||
|
|
||||||
<MenuItem x:Name="menuClose" Header="{x:Static resx:ResUI.menuExit}" />
|
<MenuItem x:Name="menuClose" Header="{x:Static resx:ResUI.menuExit}" />
|
||||||
|
|||||||
@@ -409,8 +409,8 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||||||
{
|
{
|
||||||
var bl = blShow ??
|
var bl = blShow ??
|
||||||
(Utils.IsLinux()
|
(Utils.IsLinux()
|
||||||
? (!_config.UiItem.ShowInTaskbar ^ (WindowState == WindowState.Minimized))
|
? (!AppManager.Instance.ShowInTaskbar ^ (WindowState == WindowState.Minimized))
|
||||||
: !_config.UiItem.ShowInTaskbar);
|
: !AppManager.Instance.ShowInTaskbar);
|
||||||
if (bl)
|
if (bl)
|
||||||
{
|
{
|
||||||
Show();
|
Show();
|
||||||
@@ -436,7 +436,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
|
|||||||
Hide();
|
Hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
_config.UiItem.ShowInTaskbar = bl;
|
AppManager.Instance.ShowInTaskbar = bl;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnLoaded(object? sender, RoutedEventArgs e)
|
protected override void OnLoaded(object? sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -477,7 +477,7 @@
|
|||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
IsVisible="{Binding BlIsIsMacOS}"/>
|
IsVisible="{Binding BlIsIsMacOS}" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="11"
|
Grid.Row="11"
|
||||||
@@ -592,7 +592,8 @@
|
|||||||
Grid.Row="20"
|
Grid.Row="20"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="300"
|
Width="300"
|
||||||
Margin="{StaticResource Margin4}" />
|
Margin="{StaticResource Margin4}"
|
||||||
|
IsEditable="True" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="21"
|
Grid.Row="21"
|
||||||
@@ -875,7 +876,7 @@
|
|||||||
<Grid
|
<Grid
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
ColumnDefinitions="Auto,Auto"
|
ColumnDefinitions="Auto,Auto"
|
||||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
@@ -959,10 +960,23 @@
|
|||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
|
Text="Hysteria2" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbCoreType7"
|
||||||
|
Grid.Row="7"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="8"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
Text="Wireguard" />
|
Text="Wireguard" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbCoreType9"
|
x:Name="cmbCoreType9"
|
||||||
Grid.Row="7"
|
Grid.Row="8"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}" />
|
Margin="{StaticResource Margin4}" />
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
|||||||
cmbCoreType4.ItemsSource = Global.CoreTypes;
|
cmbCoreType4.ItemsSource = Global.CoreTypes;
|
||||||
cmbCoreType5.ItemsSource = Global.CoreTypes;
|
cmbCoreType5.ItemsSource = Global.CoreTypes;
|
||||||
cmbCoreType6.ItemsSource = Global.CoreTypes;
|
cmbCoreType6.ItemsSource = Global.CoreTypes;
|
||||||
|
cmbCoreType7.ItemsSource = Global.CoreTypes;
|
||||||
cmbCoreType9.ItemsSource = Global.CoreTypes;
|
cmbCoreType9.ItemsSource = Global.CoreTypes;
|
||||||
|
|
||||||
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList();
|
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList();
|
||||||
@@ -101,7 +102,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
|||||||
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.IPAPIUrl, v => v.cmbIPAPIUrl.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.IPAPIUrl, v => v.cmbIPAPIUrl.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables);
|
||||||
@@ -122,6 +123,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
|||||||
this.Bind(ViewModel, vm => vm.CoreType4, v => v.cmbCoreType4.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CoreType4, v => v.cmbCoreType4.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CoreType7, v => v.cmbCoreType7.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.SelectedValue).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
|
x:Name="Root"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
x:DataType="vms:ProfilesViewModel"
|
x:DataType="vms:ProfilesViewModel"
|
||||||
@@ -28,6 +29,14 @@
|
|||||||
<WrapPanel />
|
<WrapPanel />
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</ListBox.ItemsPanel>
|
</ListBox.ItemsPanel>
|
||||||
|
|
||||||
|
<ListBox.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem x:Name="menuSubEdit" Header="{x:Static resx:ResUI.menuSubEdit}" />
|
||||||
|
<MenuItem x:Name="menuSubAdd" Header="{x:Static resx:ResUI.menuSubAdd}" />
|
||||||
|
<MenuItem x:Name="menuSubDelete" Header="{x:Static resx:ResUI.menuSubDelete}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</ListBox.ContextMenu>
|
||||||
</ListBox>
|
</ListBox>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -133,19 +142,18 @@
|
|||||||
InputGesture="Ctrl+T" />
|
InputGesture="Ctrl+T" />
|
||||||
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
|
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
|
||||||
<Separator />
|
<Separator />
|
||||||
<MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}">
|
<MenuItem
|
||||||
<MenuItem>
|
x:Name="menuMoveToGroup"
|
||||||
<MenuItem.Header>
|
Header="{x:Static resx:ResUI.menuMoveToGroup}"
|
||||||
<DockPanel>
|
ItemsSource="{Binding DataContext.SubItems, ElementName=Root}">
|
||||||
<ComboBox
|
<MenuItem.ItemTemplate>
|
||||||
x:Name="cmbMoveToGroup"
|
<DataTemplate>
|
||||||
Width="200"
|
<MenuItem
|
||||||
DisplayMemberBinding="{Binding Remarks}"
|
Command="{Binding DataContext.MoveToGroupCmd, ElementName=Root}"
|
||||||
ItemsSource="{Binding SubItems}"
|
CommandParameter="{Binding}"
|
||||||
ToolTip.Tip="{x:Static resx:ResUI.menuSubscription}" />
|
Header="{Binding Remarks}" />
|
||||||
</DockPanel>
|
</DataTemplate>
|
||||||
</MenuItem.Header>
|
</MenuItem.ItemTemplate>
|
||||||
</MenuItem>
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="{x:Static resx:ResUI.menuMoveTo}">
|
<MenuItem Header="{x:Static resx:ResUI.menuMoveTo}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
|||||||
this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.AddSubCmd, v => v.btnAddSub).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.AddSubCmd, v => v.btnAddSub).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.EditSubCmd, v => v.btnEditSub).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.EditSubCmd, v => v.btnEditSub).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.EditSubCmd, v => v.menuSubEdit).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.AddSubCmd, v => v.menuSubAdd).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.DeleteSubCmd, v => v.menuSubDelete).DisposeWith(disposables);
|
||||||
|
|
||||||
//servers delete
|
//servers delete
|
||||||
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.menuEditServer).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.menuEditServer).DisposeWith(disposables);
|
||||||
@@ -66,7 +69,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
|||||||
|
|
||||||
//servers move
|
//servers move
|
||||||
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
//this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).DisposeWith(disposables);
|
//this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
|
||||||
|
|||||||
@@ -40,6 +40,14 @@
|
|||||||
<DataGrid.KeyBindings>
|
<DataGrid.KeyBindings>
|
||||||
<KeyBinding Command="{Binding SubDeleteCmd}" Gesture="Delete" />
|
<KeyBinding Command="{Binding SubDeleteCmd}" Gesture="Delete" />
|
||||||
</DataGrid.KeyBindings>
|
</DataGrid.KeyBindings>
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem x:Name="menuSubAdd2" Header="{x:Static resx:ResUI.menuSubAdd}" />
|
||||||
|
<MenuItem x:Name="menuSubDelete2" Header="{x:Static resx:ResUI.menuSubDelete}" />
|
||||||
|
<MenuItem x:Name="menuSubEdit2" Header="{x:Static resx:ResUI.menuSubEdit}" />
|
||||||
|
<MenuItem x:Name="menuSubShare2" Header="{x:Static resx:ResUI.menuSubShare}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="*"
|
Width="*"
|
||||||
|
|||||||
@@ -29,6 +29,11 @@ public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
|
|||||||
this.BindCommand(ViewModel, vm => vm.SubDeleteCmd, v => v.menuSubDelete).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SubDeleteCmd, v => v.menuSubDelete).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SubEditCmd, v => v.menuSubEdit).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SubEditCmd, v => v.menuSubEdit).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SubShareCmd, v => v.menuSubShare).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SubShareCmd, v => v.menuSubShare).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SubAddCmd, v => v.menuSubAdd2).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SubDeleteCmd, v => v.menuSubDelete2).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SubEditCmd, v => v.menuSubEdit2).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SubShareCmd, v => v.menuSubShare2).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public partial class ThemeSettingView : ReactiveUserControl<ThemeSettingViewMode
|
|||||||
ViewModel = new ThemeSettingViewModel();
|
ViewModel = new ThemeSettingViewModel();
|
||||||
|
|
||||||
cmbCurrentTheme.ItemsSource = Utils.GetEnumNames<ETheme>();
|
cmbCurrentTheme.ItemsSource = Utils.GetEnumNames<ETheme>();
|
||||||
cmbCurrentFontSize.ItemsSource = Enumerable.Range(Global.MinFontSize, 11).ToList();
|
cmbCurrentFontSize.ItemsSource = Enumerable.Range(Global.MinFontSize, Global.MinFontSizeCount).ToList();
|
||||||
cmbCurrentLanguage.ItemsSource = Global.Languages;
|
cmbCurrentLanguage.ItemsSource = Global.Languages;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
SecondaryColor="Lime" />
|
SecondaryColor="Lime" />
|
||||||
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign2.Defaults.xaml" />
|
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign2.Defaults.xaml" />
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
<system:Double x:Key="MenuItemHeight">26</system:Double>
|
<system:Double x:Key="MenuItemHeight">32</system:Double>
|
||||||
<system:Double x:Key="StdFontSize">12</system:Double>
|
<system:Double x:Key="StdFontSize">12</system:Double>
|
||||||
<system:Double x:Key="StdFontSize1">13</system:Double>
|
<system:Double x:Key="StdFontSize1">13</system:Double>
|
||||||
<system:Double x:Key="StdFontSize-1">11</system:Double>
|
<system:Double x:Key="StdFontSize-1">11</system:Double>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using MaterialDesignColors;
|
using MaterialDesignColors;
|
||||||
using MaterialDesignColors.ColorManipulation;
|
using MaterialDesignColors.ColorManipulation;
|
||||||
using MaterialDesignThemes.Wpf;
|
using MaterialDesignThemes.Wpf;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace v2rayN.ViewModels;
|
namespace v2rayN.ViewModels;
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
|||||||
{
|
{
|
||||||
_config = AppManager.Instance.Config;
|
_config = AppManager.Instance.Config;
|
||||||
|
|
||||||
RegisterSystemColorSet(_config, Application.Current.MainWindow, ModifyTheme);
|
RegisterSystemColorSet(_config, ModifyTheme);
|
||||||
|
|
||||||
BindingUI();
|
BindingUI();
|
||||||
RestoreUI();
|
RestoreUI();
|
||||||
@@ -158,25 +159,15 @@ public class ThemeSettingViewModel : MyReactiveObject
|
|||||||
_paletteHelper.SetTheme(theme);
|
_paletteHelper.SetTheme(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterSystemColorSet(Config config, Window window, Action updateFunc)
|
public static void RegisterSystemColorSet(Config config, Action updateFunc)
|
||||||
{
|
{
|
||||||
var helper = new WindowInteropHelper(window);
|
SystemEvents.UserPreferenceChanged += (s, e) =>
|
||||||
var hwndSource = HwndSource.FromHwnd(helper.EnsureHandle());
|
|
||||||
hwndSource.AddHook((IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) =>
|
|
||||||
{
|
{
|
||||||
if (config.UiItem.CurrentTheme == nameof(ETheme.FollowSystem))
|
if ((e.Category == UserPreferenceCategory.Color || e.Category == UserPreferenceCategory.General)
|
||||||
|
&& config.UiItem.CurrentTheme == nameof(ETheme.FollowSystem))
|
||||||
{
|
{
|
||||||
const int WM_SETTINGCHANGE = 0x001A;
|
updateFunc?.Invoke();
|
||||||
if (msg == WM_SETTINGCHANGE)
|
|
||||||
{
|
|
||||||
if (wParam == IntPtr.Zero && Marshal.PtrToStringUni(lParam) == "ImmersiveColorSet")
|
|
||||||
{
|
|
||||||
updateFunc?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
return IntPtr.Zero;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -111,7 +111,7 @@
|
|||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Grid.ColumnSpan="3">
|
Grid.ColumnSpan="3">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -134,7 +134,10 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<TabControl DockPanel.Dock="Top">
|
<TabControl
|
||||||
|
Margin="{StaticResource Margin8}"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
DockPanel.Dock="Top">
|
||||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
||||||
<Grid Margin="{StaticResource Margin8}">
|
<Grid Margin="{StaticResource Margin8}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -143,7 +146,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -188,10 +191,8 @@
|
|||||||
Style="{StaticResource DefTextBox}" />
|
Style="{StaticResource DefTextBox}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</TabControl>
|
|
||||||
|
|
||||||
<TabControl>
|
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList2}">
|
||||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.menuServerList}">
|
|
||||||
<DataGrid
|
<DataGrid
|
||||||
x:Name="lstChild"
|
x:Name="lstChild"
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
|
|||||||
@@ -70,7 +70,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -87,7 +87,7 @@
|
|||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbCoreType"
|
x:Name="cmbCoreType"
|
||||||
Width="100"
|
Width="200"
|
||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.TbCoreType}"
|
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.TbCoreType}"
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
@@ -157,7 +157,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
@@ -300,7 +300,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
@@ -346,7 +346,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -430,7 +430,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
@@ -489,7 +489,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -559,7 +559,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
@@ -621,7 +621,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
@@ -715,7 +715,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
@@ -753,7 +753,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -899,7 +899,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
@@ -929,9 +929,11 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
@@ -1003,9 +1005,40 @@
|
|||||||
Margin="{StaticResource Margin4}"
|
Margin="{StaticResource Margin4}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbEchConfigList}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtEchConfigList"
|
||||||
|
Grid.Row="5"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbEchForceQuery}" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbEchForceQuery"
|
||||||
|
Grid.Row="6"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="7"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbCertPinning}" />
|
Text="{x:Static resx:ResUI.TbCertPinning}" />
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Row="5"
|
Grid.Row="7"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
@@ -1039,6 +1072,26 @@
|
|||||||
Content="{x:Static resx:ResUI.TbFetchCertChain}"
|
Content="{x:Static resx:ResUI.TbFetchCertChain}"
|
||||||
Style="{StaticResource DefButton}" />
|
Style="{StaticResource DefButton}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<TextBlock
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbCertSha256Tips}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtCertSha256Pinning"
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
<TextBlock
|
||||||
|
Width="400"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbFullCertTips}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtCert"
|
x:Name="txtCert"
|
||||||
Width="400"
|
Width="400"
|
||||||
@@ -1067,7 +1120,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="180" />
|
<ColumnDefinition Width="300" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ public partial class AddServerWindow
|
|||||||
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
||||||
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
|
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
|
||||||
cmbAlpn.ItemsSource = Global.Alpns;
|
cmbAlpn.ItemsSource = Global.Alpns;
|
||||||
|
cmbEchForceQuery.ItemsSource = Global.EchForceQuerys;
|
||||||
|
|
||||||
var lstStreamSecurity = new List<string>();
|
var lstStreamSecurity = new List<string>();
|
||||||
lstStreamSecurity.Add(string.Empty);
|
lstStreamSecurity.Add(string.Empty);
|
||||||
@@ -69,7 +70,6 @@ public partial class AddServerWindow
|
|||||||
gridHysteria2.Visibility = Visibility.Visible;
|
gridHysteria2.Visibility = Visibility.Visible;
|
||||||
sepa2.Visibility = Visibility.Collapsed;
|
sepa2.Visibility = Visibility.Collapsed;
|
||||||
gridTransport.Visibility = Visibility.Collapsed;
|
gridTransport.Visibility = Visibility.Collapsed;
|
||||||
cmbCoreType.IsEnabled = false;
|
|
||||||
cmbFingerprint.IsEnabled = false;
|
cmbFingerprint.IsEnabled = false;
|
||||||
cmbFingerprint.Text = string.Empty;
|
cmbFingerprint.Text = string.Empty;
|
||||||
break;
|
break;
|
||||||
@@ -180,8 +180,13 @@ public partial class AddServerWindow
|
|||||||
this.Bind(ViewModel, vm => vm.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.AllowInsecure, v => v.cmbAllowInsecure.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Alpn, v => v.cmbAlpn.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CertSha, v => v.txtCertSha256Pinning.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CertTip, v => v.labCertPinning.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.Cert, v => v.txtCert.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.EchConfigList, v => v.txtEchConfigList.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SelectedSource.EchForceQuery, v => v.cmbEchForceQuery.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
//reality
|
//reality
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI2.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Fingerprint, v => v.cmbFingerprint2.Text).DisposeWith(disposables);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<reactiveui:ReactiveUserControl
|
<reactiveui:ReactiveUserControl
|
||||||
x:Class="v2rayN.Views.BackupAndRestoreView"
|
x:Class="v2rayN.Views.BackupAndRestoreView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
@@ -23,15 +23,6 @@
|
|||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<DockPanel Margin="{StaticResource Margin8}">
|
<DockPanel Margin="{StaticResource Margin8}">
|
||||||
<DockPanel Margin="{StaticResource Margin8}" DockPanel.Dock="Bottom">
|
<DockPanel Margin="{StaticResource Margin8}" DockPanel.Dock="Bottom">
|
||||||
<Button
|
|
||||||
Width="100"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
|
||||||
Content="{x:Static resx:ResUI.menuClose}"
|
|
||||||
DockPanel.Dock="Right"
|
|
||||||
IsCancel="True"
|
|
||||||
Style="{StaticResource DefButton}" />
|
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
x:Name="txtMsg"
|
x:Name="txtMsg"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<reactiveui:ReactiveUserControl
|
<reactiveui:ReactiveUserControl
|
||||||
x:Class="v2rayN.Views.CheckUpdateView"
|
x:Class="v2rayN.Views.CheckUpdateView"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
@@ -39,15 +39,6 @@
|
|||||||
Content="{x:Static resx:ResUI.menuCheckUpdate}"
|
Content="{x:Static resx:ResUI.menuCheckUpdate}"
|
||||||
IsDefault="True"
|
IsDefault="True"
|
||||||
Style="{StaticResource DefButton}" />
|
Style="{StaticResource DefButton}" />
|
||||||
|
|
||||||
<Button
|
|
||||||
Width="100"
|
|
||||||
Margin="{StaticResource Margin8}"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
|
||||||
Content="{x:Static resx:ResUI.menuClose}"
|
|
||||||
IsCancel="True"
|
|
||||||
Style="{StaticResource DefButton}" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
|
|||||||
@@ -11,9 +11,9 @@
|
|||||||
xmlns:view="clr-namespace:v2rayN.Views"
|
xmlns:view="clr-namespace:v2rayN.Views"
|
||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
Title="v2rayN"
|
Title="v2rayN"
|
||||||
Width="900"
|
Width="1200"
|
||||||
Height="700"
|
Height="800"
|
||||||
MinWidth="900"
|
MinWidth="800"
|
||||||
x:TypeArguments="vms:MainWindowViewModel"
|
x:TypeArguments="vms:MainWindowViewModel"
|
||||||
Icon="/Resources/v2rayN.ico"
|
Icon="/Resources/v2rayN.ico"
|
||||||
ResizeMode="CanResizeWithGrip"
|
ResizeMode="CanResizeWithGrip"
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
<materialDesign:DialogHost
|
<materialDesign:DialogHost
|
||||||
materialDesign:TransitionAssist.DisableTransitions="True"
|
materialDesign:TransitionAssist.DisableTransitions="True"
|
||||||
|
CloseOnClickAway="True"
|
||||||
Identifier="RootDialog"
|
Identifier="RootDialog"
|
||||||
SnackbarMessageQueue="{Binding ElementName=MainSnackbar, Path=MessageQueue}"
|
SnackbarMessageQueue="{Binding ElementName=MainSnackbar, Path=MessageQueue}"
|
||||||
Style="{StaticResource MaterialDesignEmbeddedDialogHost}">
|
Style="{StaticResource MaterialDesignEmbeddedDialogHost}">
|
||||||
@@ -98,6 +99,10 @@
|
|||||||
x:Name="menuAddTrojanServer"
|
x:Name="menuAddTrojanServer"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Header="{x:Static resx:ResUI.menuAddTrojanServer}" />
|
Header="{x:Static resx:ResUI.menuAddTrojanServer}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuAddHysteria2Server"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuAddWireguardServer"
|
x:Name="menuAddWireguardServer"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
@@ -111,10 +116,6 @@
|
|||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
Header="{x:Static resx:ResUI.menuAddHttpServer}" />
|
Header="{x:Static resx:ResUI.menuAddHttpServer}" />
|
||||||
<Separator Margin="-40,5" />
|
<Separator Margin="-40,5" />
|
||||||
<MenuItem
|
|
||||||
x:Name="menuAddHysteria2Server"
|
|
||||||
Height="{StaticResource MenuItemHeight}"
|
|
||||||
Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuAddTuicServer"
|
x:Name="menuAddTuicServer"
|
||||||
Height="{StaticResource MenuItemHeight}"
|
Height="{StaticResource MenuItemHeight}"
|
||||||
@@ -231,23 +232,6 @@
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
<Separator />
|
<Separator />
|
||||||
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
|
|
||||||
<MenuItem
|
|
||||||
x:Name="menuReload"
|
|
||||||
Padding="{StaticResource MarginLeftRight8}"
|
|
||||||
AutomationProperties.Name="{x:Static resx:ResUI.menuReload}">
|
|
||||||
<MenuItem.Header>
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<materialDesign:PackIcon
|
|
||||||
Margin="{StaticResource MarginRight8}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Kind="Reload" />
|
|
||||||
<TextBlock Text="{x:Static resx:ResUI.menuReload}" />
|
|
||||||
</StackPanel>
|
|
||||||
</MenuItem.Header>
|
|
||||||
</MenuItem>
|
|
||||||
</Menu>
|
|
||||||
<Separator />
|
|
||||||
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
|
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuHelp"
|
x:Name="menuHelp"
|
||||||
@@ -267,6 +251,23 @@
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
<Separator />
|
<Separator />
|
||||||
|
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuReload"
|
||||||
|
Padding="{StaticResource MarginLeftRight8}"
|
||||||
|
AutomationProperties.Name="{x:Static resx:ResUI.menuReload}">
|
||||||
|
<MenuItem.Header>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<materialDesign:PackIcon
|
||||||
|
Margin="{StaticResource MarginRight8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Kind="Reload" />
|
||||||
|
<TextBlock Text="{x:Static resx:ResUI.menuReload}" />
|
||||||
|
</StackPanel>
|
||||||
|
</MenuItem.Header>
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
<Separator />
|
||||||
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
|
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
x:Name="menuPromotion"
|
x:Name="menuPromotion"
|
||||||
|
|||||||
@@ -376,7 +376,7 @@ public partial class MainWindow
|
|||||||
|
|
||||||
public void ShowHideWindow(bool? blShow)
|
public void ShowHideWindow(bool? blShow)
|
||||||
{
|
{
|
||||||
var bl = blShow ?? !_config.UiItem.ShowInTaskbar;
|
var bl = blShow ?? !AppManager.Instance.ShowInTaskbar;
|
||||||
if (bl)
|
if (bl)
|
||||||
{
|
{
|
||||||
this?.Show();
|
this?.Show();
|
||||||
@@ -391,7 +391,7 @@ public partial class MainWindow
|
|||||||
{
|
{
|
||||||
this?.Hide();
|
this?.Hide();
|
||||||
}
|
}
|
||||||
_config.UiItem.ShowInTaskbar = bl;
|
AppManager.Instance.ShowInTaskbar = bl;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnLoaded(object? sender, RoutedEventArgs e)
|
protected override void OnLoaded(object? sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -1138,6 +1138,7 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
@@ -1239,10 +1240,25 @@
|
|||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="Hysteria2" />
|
||||||
|
<ComboBox
|
||||||
|
x:Name="cmbCoreType7"
|
||||||
|
Grid.Row="7"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin8}"
|
||||||
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="8"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="Wireguard" />
|
Text="Wireguard" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbCoreType9"
|
x:Name="cmbCoreType9"
|
||||||
Grid.Row="7"
|
Grid.Row="8"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public partial class OptionSettingWindow
|
|||||||
cmbCoreType4.ItemsSource = Global.CoreTypes;
|
cmbCoreType4.ItemsSource = Global.CoreTypes;
|
||||||
cmbCoreType5.ItemsSource = Global.CoreTypes;
|
cmbCoreType5.ItemsSource = Global.CoreTypes;
|
||||||
cmbCoreType6.ItemsSource = Global.CoreTypes;
|
cmbCoreType6.ItemsSource = Global.CoreTypes;
|
||||||
|
cmbCoreType7.ItemsSource = Global.CoreTypes;
|
||||||
cmbCoreType9.ItemsSource = Global.CoreTypes;
|
cmbCoreType9.ItemsSource = Global.CoreTypes;
|
||||||
|
|
||||||
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList();
|
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList();
|
||||||
@@ -127,6 +128,7 @@ public partial class OptionSettingWindow
|
|||||||
this.Bind(ViewModel, vm => vm.CoreType4, v => v.cmbCoreType4.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CoreType4, v => v.cmbCoreType4.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CoreType5, v => v.cmbCoreType5.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CoreType6, v => v.cmbCoreType6.Text).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.CoreType7, v => v.cmbCoreType7.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CoreType9, v => v.cmbCoreType9.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
|
|||||||
@@ -15,18 +15,7 @@
|
|||||||
<sys:Double x:Key="QrcodeWidth">400</sys:Double>
|
<sys:Double x:Key="QrcodeWidth">400</sys:Double>
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
<DockPanel Margin="{StaticResource Margin8}">
|
<StackPanel Margin="{StaticResource Margin8}">
|
||||||
<StackPanel Margin="{StaticResource Margin8}" DockPanel.Dock="Bottom">
|
|
||||||
<Button
|
|
||||||
Width="100"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}"
|
|
||||||
Content="{x:Static resx:ResUI.menuClose}"
|
|
||||||
IsCancel="True"
|
|
||||||
IsDefault="True"
|
|
||||||
Style="{StaticResource DefButton}" />
|
|
||||||
</StackPanel>
|
|
||||||
|
|
||||||
<Grid Margin="{StaticResource Margin8}">
|
<Grid Margin="{StaticResource Margin8}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
@@ -51,5 +40,5 @@
|
|||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
VerticalScrollBarVisibility="Auto" />
|
VerticalScrollBarVisibility="Auto" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</DockPanel>
|
</StackPanel>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<materialDesign:DialogHost
|
<materialDesign:DialogHost
|
||||||
materialDesign:TransitionAssist.DisableTransitions="True"
|
materialDesign:TransitionAssist.DisableTransitions="True"
|
||||||
|
CloseOnClickAway="True"
|
||||||
Identifier="SubDialog"
|
Identifier="SubDialog"
|
||||||
Style="{StaticResource MaterialDesignEmbeddedDialogHost}">
|
Style="{StaticResource MaterialDesignEmbeddedDialogHost}">
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
@@ -92,6 +93,26 @@
|
|||||||
HeadersVisibility="Column"
|
HeadersVisibility="Column"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
Style="{StaticResource DefDataGrid}">
|
Style="{StaticResource DefDataGrid}">
|
||||||
|
<DataGrid.ContextMenu>
|
||||||
|
<ContextMenu Style="{StaticResource DefContextMenu}">
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuSubAdd2"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuSubAdd}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuSubDelete2"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuSubDelete}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuSubEdit2"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuSubEdit}" />
|
||||||
|
<MenuItem
|
||||||
|
x:Name="menuSubShare2"
|
||||||
|
Height="{StaticResource MenuItemHeight}"
|
||||||
|
Header="{x:Static resx:ResUI.menuSubShare}" />
|
||||||
|
</ContextMenu>
|
||||||
|
</DataGrid.ContextMenu>
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="*"
|
Width="*"
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ public partial class SubSettingWindow
|
|||||||
this.BindCommand(ViewModel, vm => vm.SubDeleteCmd, v => v.menuSubDelete).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SubDeleteCmd, v => v.menuSubDelete).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SubEditCmd, v => v.menuSubEdit).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SubEditCmd, v => v.menuSubEdit).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SubShareCmd, v => v.menuSubShare).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SubShareCmd, v => v.menuSubShare).DisposeWith(disposables);
|
||||||
|
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SubAddCmd, v => v.menuSubAdd2).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SubDeleteCmd, v => v.menuSubDelete2).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SubEditCmd, v => v.menuSubEdit2).DisposeWith(disposables);
|
||||||
|
this.BindCommand(ViewModel, vm => vm.SubShareCmd, v => v.menuSubShare2).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public partial class ThemeSettingView
|
|||||||
ViewModel = new ThemeSettingViewModel();
|
ViewModel = new ThemeSettingViewModel();
|
||||||
|
|
||||||
cmbCurrentTheme.ItemsSource = Utils.GetEnumNames<ETheme>().Take(3).ToList();
|
cmbCurrentTheme.ItemsSource = Utils.GetEnumNames<ETheme>().Take(3).ToList();
|
||||||
cmbCurrentFontSize.ItemsSource = Enumerable.Range(Global.MinFontSize, 11).ToList();
|
cmbCurrentFontSize.ItemsSource = Enumerable.Range(Global.MinFontSize, Global.MinFontSizeCount).ToList();
|
||||||
cmbCurrentLanguage.ItemsSource = Global.Languages;
|
cmbCurrentLanguage.ItemsSource = Global.Languages;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
|
|||||||
Reference in New Issue
Block a user