Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a372d8902e | ||
|
|
c38c62e4c3 | ||
|
|
8aceff7480 | ||
|
|
736c450161 | ||
|
|
3b63a3d308 | ||
|
|
4f56174c8f | ||
|
|
4d2eb324f1 | ||
|
|
3f2ab8ddcb | ||
|
|
fd8f863c5b | ||
|
|
246f1d7df0 | ||
|
|
ccacda9bf5 | ||
|
|
5494d63878 | ||
|
|
c32b9812a7 | ||
|
|
9eb9898b61 | ||
|
|
834e05999f | ||
|
|
b4c37d9906 | ||
|
|
9326b450d7 | ||
|
|
019ee8b1ba | ||
|
|
f043645397 | ||
|
|
3b173f0b3e | ||
|
|
f36c06389d | ||
|
|
d3a0b44247 | ||
|
|
e4d3a98aa8 | ||
|
|
f685682214 | ||
|
|
1c6323315b | ||
|
|
1ff4839be1 | ||
|
|
558e5bb340 | ||
|
|
48a9d208e6 | ||
|
|
f29e1f8c45 | ||
|
|
5d2bc88bb9 | ||
|
|
298fdb1191 | ||
|
|
59b34688ea | ||
|
|
5ce0bb6e4a | ||
|
|
487b1ab182 | ||
|
|
5c144a8ba3 | ||
|
|
ad344356df | ||
|
|
a55c65374d | ||
|
|
28447a9d43 | ||
|
|
0c03550c62 | ||
|
|
6d9a84803f | ||
|
|
03b0e4e2bb | ||
|
|
a00e9a6f5e | ||
|
|
ba17f8fde9 | ||
|
|
01d35456bd | ||
|
|
672b8c48ac | ||
|
|
ac1a357740 | ||
|
|
504f8d09a6 | ||
|
|
89ce7c23c9 | ||
|
|
a5d99b1eb5 | ||
|
|
800d193acb | ||
|
|
7a1d12be76 | ||
|
|
1b9c95e801 | ||
|
|
9f44815470 | ||
|
|
ca38239bce | ||
|
|
d9c22de6b8 | ||
|
|
49a3c84fc5 | ||
|
|
5e0c28438b | ||
|
|
a6dc801bc4 | ||
|
|
22009d1b71 | ||
|
|
50f39dc40e | ||
|
|
05d446ed37 | ||
|
|
7a0b068642 | ||
|
|
c0ef8c09af | ||
|
|
a2db6dd468 | ||
|
|
471bc0f65d | ||
|
|
80f840a7c2 | ||
|
|
a2413fdf23 | ||
|
|
9cbe2b938c | ||
|
|
e915726c52 | ||
|
|
b11e68cfd8 | ||
|
|
bee66d06dd | ||
|
|
945a0add96 | ||
|
|
294cabcf05 | ||
|
|
22eb993ebf | ||
|
|
b5e1a297ae | ||
|
|
499a16feae | ||
|
|
1866a59d12 | ||
|
|
10513e0f3b | ||
|
|
2e32de2fbb | ||
|
|
30a838df77 |
32
.github/workflows/build-osx.yml
vendored
Normal file
32
.github/workflows/build-osx.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: release macos
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
configuration: [Release]
|
||||||
|
|
||||||
|
runs-on: macos-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cd v2rayN &&
|
||||||
|
./build-osx.sh
|
||||||
|
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: v2rayN-osx
|
||||||
|
path: |
|
||||||
|
./v2rayN/v2rayN-osx.zip
|
||||||
|
|
||||||
|
|
||||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cd v2rayN &&
|
run: cd v2rayN &&
|
||||||
.\build.ps1
|
./build.ps1
|
||||||
|
|
||||||
# - name: Package
|
# - name: Package
|
||||||
# shell: pwsh
|
# shell: pwsh
|
||||||
@@ -40,7 +40,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: v2rayN
|
name: v2rayN
|
||||||
path: |
|
path: |
|
||||||
.\v2rayN\v2rayN.zip
|
./v2rayN/v2rayN.zip
|
||||||
|
|
||||||
# - name: Release
|
# - name: Release
|
||||||
# uses: softprops/action-gh-release@v1
|
# uses: softprops/action-gh-release@v1
|
||||||
|
|||||||
31
.github/workflows/winget-publish.yml
vendored
Normal file
31
.github/workflows/winget-publish.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: WinGet submission on release
|
||||||
|
# based off of https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml
|
||||||
|
# inspired by https://github.com/microsoft/PowerToys/blob/main/.github/workflows/package-submissions.yml
|
||||||
|
# Modified by @MerrickZ https://github.com/anpho
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
release:
|
||||||
|
types: [released]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
winget:
|
||||||
|
name: Publish winget package
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- name: Submit v2ray package to Windows Package Manager Community Repository
|
||||||
|
run: |
|
||||||
|
|
||||||
|
$wingetPackage = "2dust.v2rayN"
|
||||||
|
$gitToken = "${{ secrets.PT_WINGET }}"
|
||||||
|
|
||||||
|
$github = Invoke-RestMethod -uri "https://api.github.com/repos/2dust/v2rayN/releases"
|
||||||
|
|
||||||
|
$targetRelease = $github | Where-Object -Property prerelease -match 'False' | Select -First 1
|
||||||
|
$installerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-64-With-Core\.zip*' | Select -ExpandProperty browser_download_url
|
||||||
|
|
||||||
|
$ver = $targetRelease.tag_name
|
||||||
|
|
||||||
|
# getting latest wingetcreate file
|
||||||
|
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
|
||||||
|
.\wingetcreate.exe update $wingetPackage -s -v $ver -u "$installerUrl|x64" -t $gitToken
|
||||||
18
README.md
18
README.md
@@ -1,5 +1,5 @@
|
|||||||
# v2rayN
|
# v2rayN
|
||||||
A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
|
A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/Xray-core) and [sing-box-core](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
|
||||||
|
|
||||||
|
|
||||||
[](https://github.com/2dust/v2rayN/commits/master)
|
[](https://github.com/2dust/v2rayN/commits/master)
|
||||||
@@ -9,13 +9,19 @@ A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/
|
|||||||
|
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
- If you are new to this, please download v2rayN-With-Core.zip from [releases](https://github.com/2dust/v2rayN/releases)
|
Check [Release files introduction](https://github.com/2dust/v2rayN/wiki/Release-files-introduction) and select the version you need to download
|
||||||
- Otherwise please download v2rayN.zip (you will also need to download cores in the bin directory)
|
### Windows
|
||||||
- Run v2rayN.exe
|
- Run `v2rayN.exe`
|
||||||
|
### Linux
|
||||||
|
- `chmod +x v2rayN` Run `./v2rayN`
|
||||||
|
```
|
||||||
|
Debian 9+
|
||||||
|
Ubuntu 16.04+
|
||||||
|
Fedora 30+
|
||||||
|
```
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
- (6.35 and above)[Microsoft .NET 8.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
|
- [Microsoft .NET 8.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
|
||||||
- (6.33 and below)[Microsoft .NET 6.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
|
|
||||||
- [Supported cores](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
|
- [Supported cores](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,20 @@
|
|||||||
<Copyright>Copyright © 2017-2024 (GPLv3)</Copyright>
|
<Copyright>Copyright © 2017-2024 (GPLv3)</Copyright>
|
||||||
<FileVersion>1.3.0</FileVersion>
|
<FileVersion>1.3.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Resx\Resource.Designer.cs">
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Resource.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Update="Resx\Resource.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
namespace AmazTool
|
|
||||||
{
|
|
||||||
public class LocalizationHelper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 获取系统当前语言的本地化字符串
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">要翻译的关键字</param>
|
|
||||||
/// <returns>对应语言的本地化字符串,如果没有找到则返回关键字</returns>
|
|
||||||
public static string GetLocalizedValue(string key)
|
|
||||||
{
|
|
||||||
// 定义支持的语言
|
|
||||||
HashSet<string> supportedLanguages = ["zh", "en"];
|
|
||||||
|
|
||||||
// 获取当前系统语言的 ISO 两字母代码
|
|
||||||
string currentLanguage = CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
|
|
||||||
|
|
||||||
// 如果当前语言不在支持的语言列表中,默认使用英文
|
|
||||||
if (!supportedLanguages.Contains(currentLanguage))
|
|
||||||
{
|
|
||||||
currentLanguage = "en";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 尝试获取对应语言的翻译
|
|
||||||
if (languageResources.TryGetValue(key, out var translations))
|
|
||||||
{
|
|
||||||
if (translations.TryGetValue(currentLanguage, out var translation))
|
|
||||||
{
|
|
||||||
return translation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果未找到翻译,返回关键字本身
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 存储不同语言的本地化资源
|
|
||||||
/// </summary>
|
|
||||||
public static Dictionary<string, Dictionary<string, string>> languageResources = new()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
"Guidelines", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "Please run it from the main application." },
|
|
||||||
{ "zh", "请从主应用运行!" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Upgrade_File_Not_Found", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "Upgrade failed, file not found." },
|
|
||||||
{ "zh", "升级失败,文件不存在!" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"In_Progress", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "In progress, please wait..." },
|
|
||||||
{ "zh", "正在进行中,请等待..." }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Try_Terminate_Process", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "Try to terminate the v2rayN process." },
|
|
||||||
{ "zh", "尝试结束 v2rayN 进程..." }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Failed_Terminate_Process", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "Failed to terminate the v2rayN.Close it manually,or the upgrade may fail." },
|
|
||||||
{ "zh", "请手动关闭正在运行的v2rayN,否则可能升级失败。" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Start_Unzipping", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "Start extracting the update package." },
|
|
||||||
{ "zh", "开始解压缩更新包..." }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Success_Unzipping", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "Successfully extracted the update package!" },
|
|
||||||
{ "zh", "解压缩更新包成功!" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Failed_Unzipping", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "Failed to extract the update package!" },
|
|
||||||
{ "zh", "解压缩更新包失败!" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Failed_Upgrade", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "Upgrade failed!" },
|
|
||||||
{ "zh", "升级失败!" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Success_Upgrade", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "Upgrade success!" },
|
|
||||||
{ "zh", "升级成功!" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Information", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "Information" },
|
|
||||||
{ "zh", "提示" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Restart_v2rayN", new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "en", "Start v2rayN, please wait..." },
|
|
||||||
{ "zh", "正在重启,请等待..." }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
using System;
|
namespace AmazTool
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace AmazTool
|
|
||||||
{
|
{
|
||||||
internal static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
@@ -13,7 +10,7 @@ namespace AmazTool
|
|||||||
{
|
{
|
||||||
if (args.Length == 0)
|
if (args.Length == 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Guidelines"));
|
Console.WriteLine(Resx.Resource.Guidelines);
|
||||||
Thread.Sleep(5000);
|
Thread.Sleep(5000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
171
v2rayN/AmazTool/Resx/Resource.Designer.cs
generated
Normal file
171
v2rayN/AmazTool/Resx/Resource.Designer.cs
generated
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// 此代码由工具生成。
|
||||||
|
// 运行时版本:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||||
|
// 重新生成代码,这些更改将会丢失。
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace AmazTool.Resx {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 一个强类型的资源类,用于查找本地化的字符串等。
|
||||||
|
/// </summary>
|
||||||
|
// 此类是由 StronglyTypedResourceBuilder
|
||||||
|
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||||
|
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||||
|
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resource {
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resource() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
|
get {
|
||||||
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AmazTool.Resx.Resource", typeof(Resource).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重写当前线程的 CurrentUICulture 属性,对
|
||||||
|
/// 使用此强类型资源类的所有资源查找执行重写。
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
|
get {
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Failed to terminate the v2rayN.Close it manually,or the upgrade may fail. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string FailedTerminateProcess {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("FailedTerminateProcess", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Failed to extract the update package. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string FailedUnzipping {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("FailedUnzipping", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Upgrade failed. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string FailedUpgrade {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("FailedUpgrade", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Please run it from the main application. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string Guidelines {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Guidelines", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Information 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string Information {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Information", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 In progress, please wait... 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string InProgress {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("InProgress", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Start v2rayN, please wait... 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string Restartv2rayN {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Restartv2rayN", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Start extracting the update package... 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string StartUnzipping {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("StartUnzipping", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Successfully extracted the update package. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string SuccessUnzipping {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SuccessUnzipping", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Upgrade success. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string SuccessUpgrade {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("SuccessUpgrade", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Try to terminate the v2rayN process... 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string TryTerminateProcess {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TryTerminateProcess", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Upgrade failed, file not found. 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
internal static string UpgradeFileNotFound {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("UpgradeFileNotFound", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
156
v2rayN/AmazTool/Resx/Resource.resx
Normal file
156
v2rayN/AmazTool/Resx/Resource.resx
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<data name="Restartv2rayN" xml:space="preserve">
|
||||||
|
<value>Start v2rayN, please wait...</value>
|
||||||
|
</data>
|
||||||
|
<data name="Guidelines" xml:space="preserve">
|
||||||
|
<value>Please run it from the main application.</value>
|
||||||
|
</data>
|
||||||
|
<data name="UpgradeFileNotFound" xml:space="preserve">
|
||||||
|
<value>Upgrade failed, file not found.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InProgress" xml:space="preserve">
|
||||||
|
<value>In progress, please wait...</value>
|
||||||
|
</data>
|
||||||
|
<data name="TryTerminateProcess" xml:space="preserve">
|
||||||
|
<value>Try to terminate the v2rayN process...</value>
|
||||||
|
</data>
|
||||||
|
<data name="FailedTerminateProcess" xml:space="preserve">
|
||||||
|
<value>Failed to terminate the v2rayN.Close it manually,or the upgrade may fail.</value>
|
||||||
|
</data>
|
||||||
|
<data name="StartUnzipping" xml:space="preserve">
|
||||||
|
<value>Start extracting the update package...</value>
|
||||||
|
</data>
|
||||||
|
<data name="SuccessUnzipping" xml:space="preserve">
|
||||||
|
<value>Successfully extracted the update package.</value>
|
||||||
|
</data>
|
||||||
|
<data name="FailedUnzipping" xml:space="preserve">
|
||||||
|
<value>Failed to extract the update package.</value>
|
||||||
|
</data>
|
||||||
|
<data name="FailedUpgrade" xml:space="preserve">
|
||||||
|
<value>Upgrade failed.</value>
|
||||||
|
</data>
|
||||||
|
<data name="SuccessUpgrade" xml:space="preserve">
|
||||||
|
<value>Upgrade success.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Information" xml:space="preserve">
|
||||||
|
<value>Information</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
@@ -117,8 +117,40 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
<data name="Restartv2rayN" xml:space="preserve">
|
||||||
<data name="pac" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
<value>正在重启,请等待...</value>
|
||||||
<value>Resources\pac.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312</value>
|
</data>
|
||||||
|
<data name="Guidelines" xml:space="preserve">
|
||||||
|
<value>请从主应用运行。</value>
|
||||||
|
</data>
|
||||||
|
<data name="UpgradeFileNotFound" xml:space="preserve">
|
||||||
|
<value>升级失败,文件不存在。</value>
|
||||||
|
</data>
|
||||||
|
<data name="InProgress" xml:space="preserve">
|
||||||
|
<value>正在进行中,请等待...</value>
|
||||||
|
</data>
|
||||||
|
<data name="TryTerminateProcess" xml:space="preserve">
|
||||||
|
<value>尝试结束 v2rayN 进程...</value>
|
||||||
|
</data>
|
||||||
|
<data name="FailedTerminateProcess" xml:space="preserve">
|
||||||
|
<value>请手动关闭正在运行的v2rayN,否则可能升级失败。</value>
|
||||||
|
</data>
|
||||||
|
<data name="StartUnzipping" xml:space="preserve">
|
||||||
|
<value>开始解压缩更新包...</value>
|
||||||
|
</data>
|
||||||
|
<data name="SuccessUnzipping" xml:space="preserve">
|
||||||
|
<value>解压缩更新包成功。</value>
|
||||||
|
</data>
|
||||||
|
<data name="FailedUnzipping" xml:space="preserve">
|
||||||
|
<value>解压缩更新包失败。</value>
|
||||||
|
</data>
|
||||||
|
<data name="FailedUpgrade" xml:space="preserve">
|
||||||
|
<value>升级失败。</value>
|
||||||
|
</data>
|
||||||
|
<data name="SuccessUpgrade" xml:space="preserve">
|
||||||
|
<value>升级成功。</value>
|
||||||
|
</data>
|
||||||
|
<data name="Information" xml:space="preserve">
|
||||||
|
<value>提示</value>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
using System;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace AmazTool
|
namespace AmazTool
|
||||||
{
|
{
|
||||||
@@ -11,33 +8,37 @@ namespace AmazTool
|
|||||||
{
|
{
|
||||||
public static void Upgrade(string fileName)
|
public static void Upgrade(string fileName)
|
||||||
{
|
{
|
||||||
Console.WriteLine(fileName);
|
Console.WriteLine($"{Resx.Resource.StartUnzipping}\n{fileName}");
|
||||||
|
|
||||||
Thread.Sleep(9000);
|
Waiting(9);
|
||||||
|
|
||||||
if (!File.Exists(fileName))
|
if (!File.Exists(fileName))
|
||||||
{
|
{
|
||||||
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Upgrade_File_Not_Found"));
|
Console.WriteLine(Resx.Resource.UpgradeFileNotFound);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Try_Terminate_Process"));
|
Console.WriteLine(Resx.Resource.TryTerminateProcess);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var existing = Process.GetProcessesByName(V2rayN);
|
var existing = Process.GetProcessesByName(V2rayN);
|
||||||
foreach (var pp in existing)
|
foreach (var pp in existing)
|
||||||
{
|
{
|
||||||
pp?.Kill();
|
var path = pp.MainModule?.FileName ?? "";
|
||||||
pp?.WaitForExit(1000);
|
if (path.StartsWith(GetPath(V2rayN)))
|
||||||
|
{
|
||||||
|
pp?.Kill();
|
||||||
|
pp?.WaitForExit(1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
// Access may be denied without admin right. The user may not be an administrator.
|
// Access may be denied without admin right. The user may not be an administrator.
|
||||||
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Failed_Terminate_Process") + ex.StackTrace);
|
Console.WriteLine(Resx.Resource.FailedTerminateProcess + ex.StackTrace);
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Start_Unzipping"));
|
Console.WriteLine(Resx.Resource.StartUnzipping);
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -80,17 +81,17 @@ namespace AmazTool
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Failed_Upgrade") + ex.StackTrace);
|
Console.WriteLine(Resx.Resource.FailedUpgrade + ex.StackTrace);
|
||||||
//return;
|
//return;
|
||||||
}
|
}
|
||||||
if (sb.Length > 0)
|
if (sb.Length > 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Failed_Upgrade") + sb.ToString());
|
Console.WriteLine(Resx.Resource.FailedUpgrade + sb.ToString());
|
||||||
//return;
|
//return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine(LocalizationHelper.GetLocalizedValue("Restart_v2rayN"));
|
Console.WriteLine(Resx.Resource.Restartv2rayN);
|
||||||
Thread.Sleep(9000);
|
Waiting(9);
|
||||||
Process process = new()
|
Process process = new()
|
||||||
{
|
{
|
||||||
StartInfo = new()
|
StartInfo = new()
|
||||||
@@ -123,6 +124,15 @@ namespace AmazTool
|
|||||||
return Path.Combine(startupPath, fileName);
|
return Path.Combine(startupPath, fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void Waiting(int second)
|
||||||
|
{
|
||||||
|
for (var i = second; i > 0; i--)
|
||||||
|
{
|
||||||
|
Console.WriteLine(i);
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static string V2rayN => "v2rayN";
|
private static string V2rayN => "v2rayN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace PacLib;
|
|
||||||
|
|
||||||
public class PacHandler
|
|
||||||
{
|
|
||||||
private static string _configPath;
|
|
||||||
private static int _httpPort;
|
|
||||||
private static int _pacPort;
|
|
||||||
private static TcpListener? _tcpListener;
|
|
||||||
private static byte[] _writeContent;
|
|
||||||
private static bool _isRunning;
|
|
||||||
private static bool _needRestart = true;
|
|
||||||
|
|
||||||
public static async Task Start(string configPath, int httpPort, int pacPort)
|
|
||||||
{
|
|
||||||
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning);
|
|
||||||
|
|
||||||
_configPath = configPath;
|
|
||||||
_httpPort = httpPort;
|
|
||||||
_pacPort = pacPort;
|
|
||||||
|
|
||||||
await InitText();
|
|
||||||
|
|
||||||
if (_needRestart)
|
|
||||||
{
|
|
||||||
Stop();
|
|
||||||
RunListener();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task InitText()
|
|
||||||
{
|
|
||||||
var path = Path.Combine(_configPath, "pac.txt");
|
|
||||||
if (!File.Exists(path))
|
|
||||||
{
|
|
||||||
await File.AppendAllTextAsync(path, Resources.ResourceManager.GetString("pac"));
|
|
||||||
}
|
|
||||||
|
|
||||||
var pacText = (await File.ReadAllTextAsync(path)).Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
|
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
sb.AppendLine("HTTP/1.0 200 OK");
|
|
||||||
sb.AppendLine("Content-type:application/x-ns-proxy-autoconfig");
|
|
||||||
sb.AppendLine("Connection:close");
|
|
||||||
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
|
|
||||||
sb.AppendLine();
|
|
||||||
sb.Append(pacText);
|
|
||||||
_writeContent = Encoding.UTF8.GetBytes(sb.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RunListener()
|
|
||||||
{
|
|
||||||
_tcpListener = TcpListener.Create(_pacPort);
|
|
||||||
_isRunning = true;
|
|
||||||
_tcpListener.Start();
|
|
||||||
Task.Factory.StartNew(async () =>
|
|
||||||
{
|
|
||||||
while (_isRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!_tcpListener.Pending())
|
|
||||||
{
|
|
||||||
await Task.Delay(10);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var client = await _tcpListener.AcceptTcpClientAsync();
|
|
||||||
await Task.Run(() => { WriteContent(client); });
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, TaskCreationOptions.LongRunning);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void WriteContent(TcpClient client)
|
|
||||||
{
|
|
||||||
var stream = client.GetStream();
|
|
||||||
stream.Write(_writeContent, 0, _writeContent.Length);
|
|
||||||
stream.Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Stop()
|
|
||||||
{
|
|
||||||
if (_tcpListener == null) return;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_isRunning = false;
|
|
||||||
_tcpListener.Stop();
|
|
||||||
_tcpListener = null;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Update="Resources.Designer.cs">
|
|
||||||
<DesignTime>True</DesignTime>
|
|
||||||
<AutoGen>True</AutoGen>
|
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
|
||||||
</Compile>
|
|
||||||
<EmbeddedResource Update="Resources.resx">
|
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
|
||||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
|
||||||
</EmbeddedResource>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
95
v2rayN/PacLib/Resources.Designer.cs
generated
95
v2rayN/PacLib/Resources.Designer.cs
generated
@@ -1,95 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
// <auto-generated>
|
|
||||||
// 此代码由工具生成。
|
|
||||||
// 运行时版本:4.0.30319.42000
|
|
||||||
//
|
|
||||||
// 对此文件的更改可能会导致不正确的行为,并且如果
|
|
||||||
// 重新生成代码,这些更改将会丢失。
|
|
||||||
// </auto-generated>
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace PacLib {
|
|
||||||
using System;
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 一个强类型的资源类,用于查找本地化的字符串等。
|
|
||||||
/// </summary>
|
|
||||||
// 此类是由 StronglyTypedResourceBuilder
|
|
||||||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
|
||||||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
|
||||||
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
|
||||||
internal class Resources {
|
|
||||||
|
|
||||||
private static global::System.Resources.ResourceManager resourceMan;
|
|
||||||
|
|
||||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
|
||||||
|
|
||||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
|
||||||
internal Resources() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 返回此类使用的缓存的 ResourceManager 实例。
|
|
||||||
/// </summary>
|
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
|
||||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
|
||||||
get {
|
|
||||||
if (object.ReferenceEquals(resourceMan, null)) {
|
|
||||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PacLib.Resources", typeof(Resources).Assembly);
|
|
||||||
resourceMan = temp;
|
|
||||||
}
|
|
||||||
return resourceMan;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 重写当前线程的 CurrentUICulture 属性,对
|
|
||||||
/// 使用此强类型资源类的所有资源查找执行重写。
|
|
||||||
/// </summary>
|
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
|
||||||
internal static global::System.Globalization.CultureInfo Culture {
|
|
||||||
get {
|
|
||||||
return resourceCulture;
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
resourceCulture = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 var proxy = '__PROXY__';
|
|
||||||
///var rules = [
|
|
||||||
/// [
|
|
||||||
/// [],
|
|
||||||
/// []
|
|
||||||
/// ],
|
|
||||||
/// [
|
|
||||||
/// [
|
|
||||||
/// "aftygh.gov.tw",
|
|
||||||
/// "aide.gov.tw",
|
|
||||||
/// "aliyun.com",
|
|
||||||
/// "arte.gov.tw",
|
|
||||||
/// "baidu.com",
|
|
||||||
/// "chinaso.com",
|
|
||||||
/// "chinaz.com",
|
|
||||||
/// "chukuang.gov.tw",
|
|
||||||
/// "cycab.gov.tw",
|
|
||||||
/// "dbnsa.gov.tw",
|
|
||||||
/// "df.gov.tw",
|
|
||||||
/// "eastcoast-nsa.gov.tw",
|
|
||||||
/// "erv-nsa.gov.tw",
|
|
||||||
/// "grb.gov.tw",
|
|
||||||
/// "haosou.com",
|
|
||||||
/// [字符串的其余部分被截断]"; 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
internal static string pac {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("pac", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Protobuf Include="Statistics.proto" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.28.3" />
|
|
||||||
<PackageReference Include="Grpc.Net.Client" Version="2.66.0" />
|
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.67.0">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
syntax = "proto3";
|
|
||||||
|
|
||||||
package v2ray.core.app.stats.command;
|
|
||||||
option csharp_namespace = "ProtosLib.Statistics";
|
|
||||||
|
|
||||||
message GetStatsRequest {
|
|
||||||
// Name of the stat counter.
|
|
||||||
string name = 1;
|
|
||||||
// Whether or not to reset the counter to fetching its value.
|
|
||||||
bool reset = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message Stat {
|
|
||||||
string name = 1;
|
|
||||||
int64 value = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message GetStatsResponse {
|
|
||||||
Stat stat = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message QueryStatsRequest {
|
|
||||||
string pattern = 1;
|
|
||||||
bool reset = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message QueryStatsResponse {
|
|
||||||
repeated Stat stat = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message SysStatsRequest {
|
|
||||||
}
|
|
||||||
|
|
||||||
message SysStatsResponse {
|
|
||||||
uint32 NumGoroutine = 1;
|
|
||||||
uint32 NumGC = 2;
|
|
||||||
uint64 Alloc = 3;
|
|
||||||
uint64 TotalAlloc = 4;
|
|
||||||
uint64 Sys = 5;
|
|
||||||
uint64 Mallocs = 6;
|
|
||||||
uint64 Frees = 7;
|
|
||||||
uint64 LiveObjects = 8;
|
|
||||||
uint64 PauseTotalNs = 9;
|
|
||||||
uint32 Uptime = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
service StatsService {
|
|
||||||
rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {}
|
|
||||||
rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {}
|
|
||||||
rpc GetSysStats(SysStatsRequest) returns (SysStatsResponse) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
message Config {}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using ProtosLib.Statistics;
|
|
||||||
|
|
||||||
namespace ProtosLib
|
|
||||||
{
|
|
||||||
public class Tests
|
|
||||||
{
|
|
||||||
private StatsService.StatsServiceClient client_;
|
|
||||||
|
|
||||||
public Tests()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
101
v2rayN/ServiceLib/Common/AesUtils.cs
Normal file
101
v2rayN/ServiceLib/Common/AesUtils.cs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ServiceLib.Common
|
||||||
|
{
|
||||||
|
public class AesUtils
|
||||||
|
{
|
||||||
|
private const int KeySize = 256; // AES-256
|
||||||
|
private const int IvSize = 16; // AES block size
|
||||||
|
private const int Iterations = 10000;
|
||||||
|
private static readonly byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' ')); // google浏览器默认盐值
|
||||||
|
private static readonly string DefaultPassword = Utils.GetMd5(Utils.GetHomePath() + "AesUtils");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encrypt
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">Plain text</param>
|
||||||
|
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
|
||||||
|
/// <returns>Base64 encoded cipher text with IV</returns>
|
||||||
|
public static string Encrypt(string text, string? password = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var plaintext = Encoding.UTF8.GetBytes(text);
|
||||||
|
var key = GetKey(password);
|
||||||
|
var iv = GenerateIv();
|
||||||
|
|
||||||
|
using var aes = Aes.Create();
|
||||||
|
aes.Key = key;
|
||||||
|
aes.IV = iv;
|
||||||
|
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
ms.Write(iv, 0, iv.Length);
|
||||||
|
|
||||||
|
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
|
||||||
|
{
|
||||||
|
cs.Write(plaintext, 0, plaintext.Length);
|
||||||
|
cs.FlushFinalBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
var cipherTextWithIv = ms.ToArray();
|
||||||
|
return Convert.ToBase64String(cipherTextWithIv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decrypt
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cipherTextWithIv">Base64 encoded cipher text with IV</param>
|
||||||
|
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
|
||||||
|
/// <returns>Plain text</returns>
|
||||||
|
public static string Decrypt(string cipherTextWithIv, string? password = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(cipherTextWithIv))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
var cipherTextWithIvBytes = Convert.FromBase64String(cipherTextWithIv);
|
||||||
|
var key = GetKey(password);
|
||||||
|
|
||||||
|
var iv = new byte[IvSize];
|
||||||
|
Buffer.BlockCopy(cipherTextWithIvBytes, 0, iv, 0, IvSize);
|
||||||
|
|
||||||
|
var cipherText = new byte[cipherTextWithIvBytes.Length - IvSize];
|
||||||
|
Buffer.BlockCopy(cipherTextWithIvBytes, IvSize, cipherText, 0, cipherText.Length);
|
||||||
|
|
||||||
|
using var aes = Aes.Create();
|
||||||
|
aes.Key = key;
|
||||||
|
aes.IV = iv;
|
||||||
|
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
|
||||||
|
{
|
||||||
|
cs.Write(cipherText, 0, cipherText.Length);
|
||||||
|
cs.FlushFinalBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
var plainText = ms.ToArray();
|
||||||
|
return Encoding.UTF8.GetString(plainText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] GetKey(string? password)
|
||||||
|
{
|
||||||
|
if (password.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
password = DefaultPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var pbkdf2 = new Rfc2898DeriveBytes(password, Salt, Iterations, HashAlgorithmName.SHA256);
|
||||||
|
return pbkdf2.GetBytes(KeySize / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] GenerateIv()
|
||||||
|
{
|
||||||
|
var randomNumber = new byte[IvSize];
|
||||||
|
|
||||||
|
using var rng = RandomNumberGenerator.Create();
|
||||||
|
rng.GetBytes(randomNumber);
|
||||||
|
return randomNumber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,8 +31,8 @@
|
|||||||
var parts = this.version.Split('.');
|
var parts = this.version.Split('.');
|
||||||
if (parts.Length == 2)
|
if (parts.Length == 2)
|
||||||
{
|
{
|
||||||
this.major = int.Parse(parts[0]);
|
this.major = int.Parse(parts.First());
|
||||||
this.minor = int.Parse(parts[1]);
|
this.minor = int.Parse(parts.Last());
|
||||||
this.patch = 0;
|
this.patch = 0;
|
||||||
}
|
}
|
||||||
else if (parts.Length is 3 or 4)
|
else if (parts.Length is 3 or 4)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace ServiceLib.Common
|
|||||||
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
|
||||||
{
|
{
|
||||||
if (s.IsNullOrEmpty()) return false;
|
if (s.IsNullOrEmpty()) return false;
|
||||||
return chars.Contains(s[0]);
|
return chars.Contains(s.First());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsWhiteSpace(this string value)
|
private static bool IsWhiteSpace(this string value)
|
||||||
@@ -61,7 +61,7 @@ namespace ServiceLib.Common
|
|||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
return char.ToUpper(value[0]) + value[1..];
|
return char.ToUpper(value.First()) + value[1..];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string AppendQuotes(this string value)
|
public static string AppendQuotes(this string value)
|
||||||
|
|||||||
@@ -313,8 +313,8 @@ namespace ServiceLib.Common
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var key = Uri.UnescapeDataString(keyValue[0]);
|
var key = Uri.UnescapeDataString(keyValue.First());
|
||||||
var val = Uri.UnescapeDataString(keyValue[1]);
|
var val = Uri.UnescapeDataString(keyValue.Last());
|
||||||
|
|
||||||
if (result[key] is null)
|
if (result[key] is null)
|
||||||
{
|
{
|
||||||
@@ -622,8 +622,8 @@ namespace ServiceLib.Common
|
|||||||
{
|
{
|
||||||
if (host.StartsWith("#")) continue;
|
if (host.StartsWith("#")) continue;
|
||||||
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
if (hostItem.Length < 2) continue;
|
if (hostItem.Length != 2) continue;
|
||||||
systemHosts.Add(hostItem[1], hostItem[0]);
|
systemHosts.Add(hostItem.Last(), hostItem.First());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,7 @@
|
|||||||
v2fly = 1,
|
v2fly = 1,
|
||||||
Xray = 2,
|
Xray = 2,
|
||||||
v2fly_v5 = 4,
|
v2fly_v5 = 4,
|
||||||
|
|
||||||
mihomo = 13,
|
mihomo = 13,
|
||||||
|
|
||||||
hysteria = 21,
|
hysteria = 21,
|
||||||
naiveproxy = 22,
|
naiveproxy = 22,
|
||||||
tuic = 23,
|
tuic = 23,
|
||||||
|
|||||||
@@ -3,12 +3,11 @@
|
|||||||
public enum EInboundProtocol
|
public enum EInboundProtocol
|
||||||
{
|
{
|
||||||
socks = 0,
|
socks = 0,
|
||||||
http,
|
|
||||||
socks2,
|
socks2,
|
||||||
http2,
|
|
||||||
pac,
|
pac,
|
||||||
api,
|
api,
|
||||||
api2,
|
api2,
|
||||||
|
mixed,
|
||||||
speedtest = 21
|
speedtest = 21
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,6 @@
|
|||||||
kcp,
|
kcp,
|
||||||
ws,
|
ws,
|
||||||
httpupgrade,
|
httpupgrade,
|
||||||
splithttp,
|
|
||||||
xhttp,
|
xhttp,
|
||||||
h2,
|
h2,
|
||||||
http,
|
http,
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases";
|
public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases";
|
||||||
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
|
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
|
||||||
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
|
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
|
||||||
|
public const string IPAPIUrl = "https://api.ip.sb/geoip";
|
||||||
|
|
||||||
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
|
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
|
||||||
public const string ConfigFileName = "guiNConfig.json";
|
public const string ConfigFileName = "guiNConfig.json";
|
||||||
@@ -46,6 +47,7 @@
|
|||||||
public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml";
|
public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml";
|
||||||
public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml";
|
public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml";
|
||||||
public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config";
|
public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config";
|
||||||
|
public const string PacFileName = NamespaceSample + "pac";
|
||||||
|
|
||||||
public const string DefaultSecurity = "auto";
|
public const string DefaultSecurity = "auto";
|
||||||
public const string DefaultNetwork = "tcp";
|
public const string DefaultNetwork = "tcp";
|
||||||
@@ -178,16 +180,15 @@
|
|||||||
public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
|
public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
|
||||||
public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" };
|
public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" };
|
||||||
public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
|
public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
|
||||||
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "xhttp", "splithttp", "h2", "quic", "grpc" };
|
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "xhttp", "h2", "quic", "grpc" };
|
||||||
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
|
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
|
||||||
public static readonly List<string> CoreTypes = new() { "v2fly", "Xray", "sing_box" };
|
public static readonly List<string> CoreTypes = new() { "Xray", "sing_box" };
|
||||||
public static readonly List<string> CoreTypes4VLESS = new() { "Xray", "sing_box" };
|
|
||||||
public static readonly List<string> DomainStrategies = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
|
public static readonly List<string> DomainStrategies = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
|
||||||
public static readonly List<string> DomainStrategies4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
|
public static readonly List<string> DomainStrategies4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
|
||||||
public static readonly List<string> DomainMatchers = new() { "linear", "mph", "" };
|
public static readonly List<string> DomainMatchers = new() { "linear", "mph", "" };
|
||||||
public static readonly List<string> Fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
|
public static readonly List<string> Fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
|
||||||
public static readonly List<string> UserAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
|
public static readonly List<string> UserAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
|
||||||
public static readonly List<string> XhttpMode = new() { "auto", "packet-up", "stream-up" };
|
public static readonly List<string> XhttpMode = new() { "auto", "packet-up", "stream-up", "stream-one" };
|
||||||
|
|
||||||
public static readonly List<string> AllowInsecure = new() { "true", "false", "" };
|
public static readonly List<string> AllowInsecure = new() { "true", "false", "" };
|
||||||
public static readonly List<string> DomainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
|
public static readonly List<string> DomainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
|
||||||
@@ -197,7 +198,7 @@
|
|||||||
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" };
|
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" };
|
||||||
public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2", "h2,http/1.1", "h3,h2,http/1.1", "" };
|
public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2", "h2,http/1.1", "h3,h2,http/1.1", "" };
|
||||||
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
|
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
|
||||||
public static readonly List<string> InboundTags = new() { "socks", "http", "socks2", "http2" };
|
public static readonly List<string> InboundTags = new() { "socks", "socks2" };
|
||||||
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" };
|
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" };
|
||||||
public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" };
|
public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" };
|
||||||
public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"];
|
public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"];
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
|
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
|
||||||
return _statePort2.Value;
|
return _statePort2.Value + (_config.TunModeItem.EnableTun ? 1 : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,11 +46,13 @@
|
|||||||
|
|
||||||
public bool InitApp()
|
public bool InitApp()
|
||||||
{
|
{
|
||||||
_config = ConfigHandler.LoadConfig();
|
Logging.Setup();
|
||||||
if (_config == null)
|
var config = ConfigHandler.LoadConfig();
|
||||||
|
if (config == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
_config = config;
|
||||||
Thread.CurrentThread.CurrentUICulture = new(_config.UiItem.CurrentLanguage);
|
Thread.CurrentThread.CurrentUICulture = new(_config.UiItem.CurrentLanguage);
|
||||||
|
|
||||||
//Under Win10
|
//Under Win10
|
||||||
@@ -70,15 +72,21 @@
|
|||||||
|
|
||||||
public bool InitComponents()
|
public bool InitComponents()
|
||||||
{
|
{
|
||||||
Logging.Setup();
|
|
||||||
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
|
|
||||||
Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
|
Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
|
||||||
Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
|
Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
|
||||||
|
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
|
||||||
Logging.ClearLogs();
|
Logging.ClearLogs();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Reset()
|
||||||
|
{
|
||||||
|
_statePort = null;
|
||||||
|
_statePort2 = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Init
|
#endregion Init
|
||||||
|
|
||||||
#region Config
|
#region Config
|
||||||
@@ -211,12 +219,12 @@
|
|||||||
|
|
||||||
public async Task<List<RoutingItem>?> RoutingItems()
|
public async Task<List<RoutingItem>?> RoutingItems()
|
||||||
{
|
{
|
||||||
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().Where(it => it.Locked == false).OrderBy(t => t.Sort).ToListAsync();
|
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RoutingItem?> GetRoutingItem(string id)
|
public async Task<RoutingItem?> GetRoutingItem(string id)
|
||||||
{
|
{
|
||||||
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.Locked == false && it.Id == id);
|
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.Id == id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<List<DNSItem>?> DNSItems()
|
public async Task<List<DNSItem>?> DNSItems()
|
||||||
@@ -257,16 +265,8 @@
|
|||||||
return (ECoreType)profileItem.CoreType;
|
return (ECoreType)profileItem.CoreType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.CoreTypeItem == null)
|
var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType);
|
||||||
{
|
return item?.CoreType ?? ECoreType.Xray;
|
||||||
return ECoreType.Xray;
|
|
||||||
}
|
|
||||||
var item = _config.CoreTypeItem.FirstOrDefault(it => it.ConfigType == eConfigType);
|
|
||||||
if (item == null)
|
|
||||||
{
|
|
||||||
return ECoreType.Xray;
|
|
||||||
}
|
|
||||||
return item.CoreType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Core Type
|
#endregion Core Type
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ namespace ServiceLib.Handler
|
|||||||
await SetTaskLinux();
|
await SetTaskLinux();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (Utils.IsOSX())
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,17 +62,15 @@ namespace ServiceLib.Handler
|
|||||||
{
|
{
|
||||||
if (config.Inbound.Count > 0)
|
if (config.Inbound.Count > 0)
|
||||||
{
|
{
|
||||||
config.Inbound[0].Protocol = EInboundProtocol.socks.ToString();
|
config.Inbound.First().Protocol = EInboundProtocol.socks.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
config.RoutingBasicItem ??= new()
|
|
||||||
{
|
config.RoutingBasicItem ??= new();
|
||||||
EnableRoutingAdvanced = true
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Utils.IsNullOrEmpty(config.RoutingBasicItem.DomainStrategy))
|
if (Utils.IsNullOrEmpty(config.RoutingBasicItem.DomainStrategy))
|
||||||
{
|
{
|
||||||
config.RoutingBasicItem.DomainStrategy = Global.DomainStrategies[0];//"IPIfNonMatch";
|
config.RoutingBasicItem.DomainStrategy = Global.DomainStrategies.First();//"IPIfNonMatch";
|
||||||
}
|
}
|
||||||
|
|
||||||
config.KcpItem ??= new KcpItem
|
config.KcpItem ??= new KcpItem
|
||||||
@@ -113,7 +111,7 @@ namespace ServiceLib.Handler
|
|||||||
{
|
{
|
||||||
if (Thread.CurrentThread.CurrentCulture.Name.Equals("zh-cn", StringComparison.CurrentCultureIgnoreCase))
|
if (Thread.CurrentThread.CurrentCulture.Name.Equals("zh-cn", StringComparison.CurrentCultureIgnoreCase))
|
||||||
{
|
{
|
||||||
config.UiItem.CurrentLanguage = Global.Languages[0];
|
config.UiItem.CurrentLanguage = Global.Languages.First();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -134,7 +132,7 @@ namespace ServiceLib.Handler
|
|||||||
}
|
}
|
||||||
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedTestUrl))
|
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedTestUrl))
|
||||||
{
|
{
|
||||||
config.SpeedTestItem.SpeedTestUrl = Global.SpeedTestUrls[0];
|
config.SpeedTestItem.SpeedTestUrl = Global.SpeedTestUrls.First();
|
||||||
}
|
}
|
||||||
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedPingTestUrl))
|
if (Utils.IsNullOrEmpty(config.SpeedTestItem.SpeedPingTestUrl))
|
||||||
{
|
{
|
||||||
@@ -150,7 +148,7 @@ namespace ServiceLib.Handler
|
|||||||
|
|
||||||
config.Mux4SboxItem ??= new()
|
config.Mux4SboxItem ??= new()
|
||||||
{
|
{
|
||||||
Protocol = Global.SingboxMuxs[0],
|
Protocol = Global.SingboxMuxs.First(),
|
||||||
MaxConnections = 8
|
MaxConnections = 8
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -385,7 +383,7 @@ namespace ServiceLib.Handler
|
|||||||
}
|
}
|
||||||
|
|
||||||
var item = await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(t => t.Port > 0);
|
var item = await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(t => t.Port > 0);
|
||||||
return await SetDefaultServerIndex(config, item.IndexId);
|
return await SetDefaultServerIndex(config, item?.IndexId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<ProfileItem?> GetDefaultServer(Config config)
|
public static async Task<ProfileItem?> GetDefaultServer(Config config)
|
||||||
@@ -431,7 +429,7 @@ namespace ServiceLib.Handler
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
sort = ProfileExHandler.Instance.GetSort(lstProfile[0].IndexId) - 1;
|
sort = ProfileExHandler.Instance.GetSort(lstProfile.First().IndexId) - 1;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1028,6 +1026,35 @@ namespace ServiceLib.Handler
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<ProfileItem?> GetPreSocksItem(Config config, ProfileItem node, ECoreType coreType)
|
||||||
|
{
|
||||||
|
ProfileItem? itemSocks = null;
|
||||||
|
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && config.TunModeItem.EnableTun)
|
||||||
|
{
|
||||||
|
itemSocks = new ProfileItem()
|
||||||
|
{
|
||||||
|
CoreType = ECoreType.sing_box,
|
||||||
|
ConfigType = EConfigType.SOCKS,
|
||||||
|
Address = Global.Loopback,
|
||||||
|
Sni = node.Address, //Tun2SocksAddress
|
||||||
|
Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
|
||||||
|
{
|
||||||
|
var preCoreType = config.RunningCoreType = config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
||||||
|
itemSocks = new ProfileItem()
|
||||||
|
{
|
||||||
|
CoreType = preCoreType,
|
||||||
|
ConfigType = EConfigType.SOCKS,
|
||||||
|
Address = Global.Loopback,
|
||||||
|
Port = node.PreSocksPort.Value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemSocks;
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Server
|
#endregion Server
|
||||||
|
|
||||||
#region Batch add servers
|
#region Batch add servers
|
||||||
@@ -1298,6 +1325,20 @@ namespace ServiceLib.Handler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Keep the last traffic statistics
|
||||||
|
if (lstOriSub != null)
|
||||||
|
{
|
||||||
|
var lstSub = await AppHandler.Instance.ProfileItems(subid);
|
||||||
|
foreach (var item in lstSub)
|
||||||
|
{
|
||||||
|
var existItem = lstOriSub?.FirstOrDefault(t => config.UiItem.EnableUpdateSubOnlyRemarksExist ? t.Remarks == item.Remarks : CompareProfileItem(t, item, true));
|
||||||
|
if (existItem != null)
|
||||||
|
{
|
||||||
|
await StatisticsHandler.Instance.CloneServerStatItem(existItem.IndexId, item.IndexId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return counter;
|
return counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1600,7 +1641,7 @@ namespace ServiceLib.Handler
|
|||||||
var item = await AppHandler.Instance.GetRoutingItem(config.RoutingBasicItem.RoutingIndexId);
|
var item = await AppHandler.Instance.GetRoutingItem(config.RoutingBasicItem.RoutingIndexId);
|
||||||
if (item is null)
|
if (item is null)
|
||||||
{
|
{
|
||||||
var item2 = await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(t => t.Locked == false);
|
var item2 = await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync();
|
||||||
await SetDefaultRouting(config, item2);
|
await SetDefaultRouting(config, item2);
|
||||||
return item2;
|
return item2;
|
||||||
}
|
}
|
||||||
@@ -1674,6 +1715,15 @@ namespace ServiceLib.Handler
|
|||||||
{
|
{
|
||||||
var ver = "V3-";
|
var ver = "V3-";
|
||||||
var items = await AppHandler.Instance.RoutingItems();
|
var items = await AppHandler.Instance.RoutingItems();
|
||||||
|
|
||||||
|
//TODO Temporary code to be removed later
|
||||||
|
var lockItem = items?.FirstOrDefault(t => t.Locked == true);
|
||||||
|
if (lockItem != null)
|
||||||
|
{
|
||||||
|
await ConfigHandler.RemoveRoutingItem(lockItem);
|
||||||
|
items = await AppHandler.Instance.RoutingItems();
|
||||||
|
}
|
||||||
|
|
||||||
if (!blImportAdvancedRules && items.Where(t => t.Remarks.StartsWith(ver)).ToList().Count > 0)
|
if (!blImportAdvancedRules && items.Where(t => t.Remarks.StartsWith(ver)).ToList().Count > 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1714,11 +1764,6 @@ namespace ServiceLib.Handler
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<RoutingItem?> GetLockedRoutingItem(Config config)
|
|
||||||
{
|
|
||||||
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.Locked == true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task RemoveRoutingItem(RoutingItem routingItem)
|
public static async Task RemoveRoutingItem(RoutingItem routingItem)
|
||||||
{
|
{
|
||||||
await SQLiteHelper.Instance.DeleteAsync(routingItem);
|
await SQLiteHelper.Instance.DeleteAsync(routingItem);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace ServiceLib.Handler
|
|||||||
private Config _config;
|
private Config _config;
|
||||||
private Process? _process;
|
private Process? _process;
|
||||||
private Process? _processPre;
|
private Process? _processPre;
|
||||||
|
private int _linuxSudoPid = -1;
|
||||||
private Action<bool, string>? _updateFunc;
|
private Action<bool, string>? _updateFunc;
|
||||||
|
|
||||||
public async Task Init(Config config, Action<bool, string> updateFunc)
|
public async Task Init(Config config, Action<bool, string> updateFunc)
|
||||||
@@ -23,7 +24,7 @@ namespace ServiceLib.Handler
|
|||||||
Environment.SetEnvironmentVariable("V2RAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
Environment.SetEnvironmentVariable("V2RAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||||
Environment.SetEnvironmentVariable("XRAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
Environment.SetEnvironmentVariable("XRAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
|
||||||
|
|
||||||
if (Utils.IsLinux())
|
if (Utils.IsLinux() || Utils.IsOSX())
|
||||||
{
|
{
|
||||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
|
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
|
||||||
foreach (var it in coreInfo)
|
foreach (var it in coreInfo)
|
||||||
@@ -64,49 +65,30 @@ namespace ServiceLib.Handler
|
|||||||
ShowMsg(true, result.Msg);
|
ShowMsg(true, result.Msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
ShowMsg(true, $"{node.GetSummary()}");
|
|
||||||
ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
|
|
||||||
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
|
||||||
await CoreStop();
|
|
||||||
await Task.Delay(100);
|
|
||||||
await CoreStart(node);
|
|
||||||
|
|
||||||
//In tun mode, do a delay check and restart the core
|
ShowMsg(true, $"{node.GetSummary()}");
|
||||||
//if (_config.tunModeItem.enableTun)
|
ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
|
||||||
//{
|
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||||
// Observable.Range(1, 1)
|
await CoreStop();
|
||||||
// .Delay(TimeSpan.FromSeconds(15))
|
await Task.Delay(100);
|
||||||
// .Subscribe(x =>
|
await CoreStart(node);
|
||||||
// {
|
await CoreStartPreService(node);
|
||||||
// {
|
|
||||||
// if (_process == null || _process.HasExited)
|
|
||||||
// {
|
|
||||||
// CoreStart(node);
|
|
||||||
// ShowMsg(false, "Tun mode restart the core once");
|
|
||||||
// Logging.SaveLog("Tun mode restart the core once");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
|
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
|
||||||
{
|
{
|
||||||
var pid = -1;
|
|
||||||
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
|
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
|
||||||
var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
|
var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
|
||||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
|
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
|
||||||
ShowMsg(false, result.Msg);
|
ShowMsg(false, result.Msg);
|
||||||
if (result.Success)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
return -1;
|
||||||
ShowMsg(false, configPath);
|
|
||||||
pid = await CoreStartSpeedtest(configPath, coreType);
|
|
||||||
}
|
}
|
||||||
return pid;
|
|
||||||
|
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
|
||||||
|
ShowMsg(false, configPath);
|
||||||
|
return await CoreStartSpeedtest(configPath, coreType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CoreStop()
|
public async Task CoreStop()
|
||||||
@@ -115,17 +97,19 @@ namespace ServiceLib.Handler
|
|||||||
{
|
{
|
||||||
if (_process != null)
|
if (_process != null)
|
||||||
{
|
{
|
||||||
await KillProcess(_process);
|
_process = await KillProcess(_process);
|
||||||
_process.Dispose();
|
|
||||||
_process = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_processPre != null)
|
if (_processPre != null)
|
||||||
{
|
{
|
||||||
await KillProcess(_processPre);
|
_processPre = await KillProcess(_processPre);
|
||||||
_processPre.Dispose();
|
|
||||||
_processPre = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_linuxSudoPid > 0)
|
||||||
|
{
|
||||||
|
await KillProcessAsLinuxSudo();
|
||||||
|
}
|
||||||
|
_linuxSudoPid = -1;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -137,8 +121,7 @@ namespace ServiceLib.Handler
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var _p = Process.GetProcessById(pid);
|
await KillProcess(Process.GetProcessById(pid));
|
||||||
await KillProcess(_p);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -171,8 +154,7 @@ namespace ServiceLib.Handler
|
|||||||
|
|
||||||
private async Task CoreStart(ProfileItem node)
|
private async Task CoreStart(ProfileItem node)
|
||||||
{
|
{
|
||||||
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||||
_config.RunningCoreType = coreType;
|
|
||||||
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
|
||||||
|
|
||||||
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
|
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
|
||||||
@@ -182,47 +164,28 @@ namespace ServiceLib.Handler
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_process = proc;
|
_process = proc;
|
||||||
|
}
|
||||||
|
|
||||||
//start a pre service
|
private async Task CoreStartPreService(ProfileItem node)
|
||||||
|
{
|
||||||
if (_process != null && !_process.HasExited)
|
if (_process != null && !_process.HasExited)
|
||||||
{
|
{
|
||||||
ProfileItem? itemSocks = null;
|
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
|
||||||
var preCoreType = ECoreType.sing_box;
|
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
|
||||||
if (node.ConfigType != EConfigType.Custom && coreType != ECoreType.sing_box && _config.TunModeItem.EnableTun)
|
|
||||||
{
|
|
||||||
itemSocks = new ProfileItem()
|
|
||||||
{
|
|
||||||
CoreType = preCoreType,
|
|
||||||
ConfigType = EConfigType.SOCKS,
|
|
||||||
Address = Global.Loopback,
|
|
||||||
Sni = node.Address, //Tun2SocksAddress
|
|
||||||
Port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if ((node.ConfigType == EConfigType.Custom && node.PreSocksPort > 0))
|
|
||||||
{
|
|
||||||
preCoreType = _config.TunModeItem.EnableTun ? ECoreType.sing_box : ECoreType.Xray;
|
|
||||||
itemSocks = new ProfileItem()
|
|
||||||
{
|
|
||||||
CoreType = preCoreType,
|
|
||||||
ConfigType = EConfigType.SOCKS,
|
|
||||||
Address = Global.Loopback,
|
|
||||||
Port = node.PreSocksPort.Value,
|
|
||||||
};
|
|
||||||
_config.RunningCoreType = preCoreType;
|
|
||||||
}
|
|
||||||
if (itemSocks != null)
|
if (itemSocks != null)
|
||||||
{
|
{
|
||||||
var fileName2 = Utils.GetConfigPath(Global.CorePreConfigFileName);
|
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName2);
|
var fileName = Utils.GetConfigPath(Global.CorePreConfigFileName);
|
||||||
|
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
var coreInfo2 = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
|
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
|
||||||
var proc2 = await RunProcess(coreInfo2, Global.CorePreConfigFileName, true, true);
|
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
|
||||||
if (proc2 is not null)
|
if (proc is null)
|
||||||
{
|
{
|
||||||
_processPre = proc2;
|
return;
|
||||||
}
|
}
|
||||||
|
_processPre = proc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,8 +221,8 @@ namespace ServiceLib.Handler
|
|||||||
{
|
{
|
||||||
return _config.TunModeItem.EnableTun
|
return _config.TunModeItem.EnableTun
|
||||||
&& eCoreType == ECoreType.sing_box
|
&& eCoreType == ECoreType.sing_box
|
||||||
&& Utils.IsLinux()
|
&& (Utils.IsLinux() || Utils.IsOSX())
|
||||||
&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()
|
//&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,7 +238,6 @@ namespace ServiceLib.Handler
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Process proc = new()
|
Process proc = new()
|
||||||
@@ -294,14 +256,10 @@ namespace ServiceLib.Handler
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
|
||||||
if (isNeedSudo)
|
if (isNeedSudo)
|
||||||
{
|
{
|
||||||
proc.StartInfo.FileName = $"/bin/sudo";
|
await RunProcessAsLinuxSudo(proc, fileName, coreInfo, configPath);
|
||||||
proc.StartInfo.Arguments = $"-S {fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetConfigPath(configPath).AppendQuotes())}";
|
|
||||||
proc.StartInfo.WorkingDirectory = null;
|
|
||||||
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
|
|
||||||
proc.StartInfo.RedirectStandardInput = true;
|
|
||||||
Logging.SaveLog(proc.StartInfo.Arguments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var startUpErrorMessage = new StringBuilder();
|
var startUpErrorMessage = new StringBuilder();
|
||||||
@@ -326,7 +284,7 @@ namespace ServiceLib.Handler
|
|||||||
}
|
}
|
||||||
proc.Start();
|
proc.Start();
|
||||||
|
|
||||||
if (isNeedSudo)
|
if (isNeedSudo && _config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
|
||||||
{
|
{
|
||||||
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
|
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
|
||||||
await Task.Delay(10);
|
await Task.Delay(10);
|
||||||
@@ -334,6 +292,7 @@ namespace ServiceLib.Handler
|
|||||||
await Task.Delay(10);
|
await Task.Delay(10);
|
||||||
await proc.StandardInput.WriteLineAsync(pwd);
|
await proc.StandardInput.WriteLineAsync(pwd);
|
||||||
}
|
}
|
||||||
|
if (isNeedSudo) _linuxSudoPid = proc.Id;
|
||||||
|
|
||||||
if (displayLog)
|
if (displayLog)
|
||||||
{
|
{
|
||||||
@@ -362,34 +321,104 @@ namespace ServiceLib.Handler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task KillProcess(Process? proc)
|
private async Task<Process?> KillProcess(Process? proc)
|
||||||
{
|
{
|
||||||
if (proc is null)
|
if (proc is null)
|
||||||
{
|
{
|
||||||
return;
|
return null;
|
||||||
}
|
|
||||||
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(1));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await proc.WaitForExitAsync(timeout.Token);
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
proc.Kill();
|
|
||||||
}
|
|
||||||
if (!proc.HasExited)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await proc.WaitForExitAsync(timeout.Token);
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
proc.Kill();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
try { proc?.Kill(true); } catch { }
|
||||||
|
try { proc?.Close(); } catch { }
|
||||||
|
try { proc?.Dispose(); } catch { }
|
||||||
|
|
||||||
|
await Task.Delay(100);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion Process
|
#endregion Process
|
||||||
|
|
||||||
|
#region Linux
|
||||||
|
|
||||||
|
private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath)
|
||||||
|
{
|
||||||
|
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetConfigPath(configPath).AppendQuotes())}";
|
||||||
|
|
||||||
|
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
|
||||||
|
proc.StartInfo.FileName = shFilePath;
|
||||||
|
proc.StartInfo.Arguments = "";
|
||||||
|
proc.StartInfo.WorkingDirectory = "";
|
||||||
|
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
|
||||||
|
{
|
||||||
|
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
|
||||||
|
proc.StartInfo.RedirectStandardInput = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task KillProcessAsLinuxSudo()
|
||||||
|
{
|
||||||
|
var cmdLine = $"kill {_linuxSudoPid}";
|
||||||
|
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
|
||||||
|
Process proc = new()
|
||||||
|
{
|
||||||
|
StartInfo = new()
|
||||||
|
{
|
||||||
|
FileName = shFilePath,
|
||||||
|
UseShellExecute = false,
|
||||||
|
CreateNoWindow = true,
|
||||||
|
StandardInputEncoding = Encoding.UTF8,
|
||||||
|
RedirectStandardInput = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
proc.Start();
|
||||||
|
|
||||||
|
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
|
||||||
|
await Task.Delay(10);
|
||||||
|
await proc.StandardInput.WriteLineAsync(pwd);
|
||||||
|
await Task.Delay(10);
|
||||||
|
await proc.StandardInput.WriteLineAsync(pwd);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||||
|
await proc.WaitForExitAsync(timeout.Token);
|
||||||
|
await Task.Delay(3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
|
||||||
|
{
|
||||||
|
//Shell scripts
|
||||||
|
var shFilePath = Utils.GetBinPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
|
||||||
|
File.Delete(shFilePath);
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine("#!/bin/sh");
|
||||||
|
if (AppHandler.Instance.IsAdministrator)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"{cmdLine}");
|
||||||
|
}
|
||||||
|
else if (_config.TunModeItem.LinuxSudoPwd.IsNullOrEmpty())
|
||||||
|
{
|
||||||
|
sb.AppendLine($"pkexec {cmdLine}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"sudo -S {cmdLine}");
|
||||||
|
}
|
||||||
|
|
||||||
|
await File.WriteAllTextAsync(shFilePath, sb.ToString());
|
||||||
|
await Utils.SetLinuxChmod(shFilePath);
|
||||||
|
Logging.SaveLog(shFilePath);
|
||||||
|
|
||||||
|
return shFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Linux
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,6 +42,8 @@
|
|||||||
DownloadUrlWinArm64 = Global.NUrl + "/download/{0}/v2rayN-windows-arm64.zip",
|
DownloadUrlWinArm64 = Global.NUrl + "/download/{0}/v2rayN-windows-arm64.zip",
|
||||||
DownloadUrlLinux64 = Global.NUrl + "/download/{0}/v2rayN-linux-64.zip",
|
DownloadUrlLinux64 = Global.NUrl + "/download/{0}/v2rayN-linux-64.zip",
|
||||||
DownloadUrlLinuxArm64 = Global.NUrl + "/download/{0}/v2rayN-linux-arm64.zip",
|
DownloadUrlLinuxArm64 = Global.NUrl + "/download/{0}/v2rayN-linux-arm64.zip",
|
||||||
|
DownloadUrlOSX64 = Global.NUrl + "/download/{0}/v2rayN-macos-64.zip",
|
||||||
|
DownloadUrlOSXArm64 = Global.NUrl + "/download/{0}/v2rayN-macos-arm64.zip",
|
||||||
});
|
});
|
||||||
|
|
||||||
_coreInfo.Add(new CoreInfo
|
_coreInfo.Add(new CoreInfo
|
||||||
@@ -79,6 +81,8 @@
|
|||||||
DownloadUrlWinArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-arm64-v8a.zip",
|
DownloadUrlWinArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-arm64-v8a.zip",
|
||||||
DownloadUrlLinux64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-64.zip",
|
DownloadUrlLinux64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-64.zip",
|
||||||
DownloadUrlLinuxArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-arm64-v8a.zip",
|
DownloadUrlLinuxArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-arm64-v8a.zip",
|
||||||
|
DownloadUrlOSX64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-64.zip",
|
||||||
|
DownloadUrlOSXArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-arm64-v8a.zip",
|
||||||
Match = "Xray",
|
Match = "Xray",
|
||||||
VersionArg = "-version",
|
VersionArg = "-version",
|
||||||
RedirectInfo = true,
|
RedirectInfo = true,
|
||||||
@@ -95,6 +99,8 @@
|
|||||||
DownloadUrlWinArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip",
|
DownloadUrlWinArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip",
|
||||||
DownloadUrlLinux64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz",
|
DownloadUrlLinux64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz",
|
||||||
DownloadUrlLinuxArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-arm64-{0}.gz",
|
DownloadUrlLinuxArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-arm64-{0}.gz",
|
||||||
|
DownloadUrlOSX64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-amd64-compatible-{0}.gz",
|
||||||
|
DownloadUrlOSXArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-arm64-{0}.gz",
|
||||||
Match = "Mihomo",
|
Match = "Mihomo",
|
||||||
VersionArg = "-v",
|
VersionArg = "-v",
|
||||||
RedirectInfo = true,
|
RedirectInfo = true,
|
||||||
@@ -140,6 +146,8 @@
|
|||||||
DownloadUrlWinArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip",
|
DownloadUrlWinArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip",
|
||||||
DownloadUrlLinux64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz",
|
DownloadUrlLinux64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz",
|
||||||
DownloadUrlLinuxArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz",
|
DownloadUrlLinuxArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz",
|
||||||
|
DownloadUrlOSX64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-amd64.tar.gz",
|
||||||
|
DownloadUrlOSXArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-arm64.tar.gz",
|
||||||
Match = "sing-box",
|
Match = "sing-box",
|
||||||
VersionArg = "version",
|
VersionArg = "version",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ namespace ServiceLib.Handler.Fmt
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.splithttp):
|
|
||||||
case nameof(ETransport.xhttp):
|
case nameof(ETransport.xhttp):
|
||||||
if (Utils.IsNotEmpty(item.RequestHost))
|
if (Utils.IsNotEmpty(item.RequestHost))
|
||||||
{
|
{
|
||||||
@@ -179,7 +178,6 @@ namespace ServiceLib.Handler.Fmt
|
|||||||
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.splithttp):
|
|
||||||
case nameof(ETransport.xhttp):
|
case nameof(ETransport.xhttp):
|
||||||
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
|
||||||
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
item.Path = Utils.UrlDecode(query["path"] ?? "/");
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ namespace ServiceLib.Handler.Fmt
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
item.Security = userInfoParts[0];
|
item.Security = userInfoParts.First();
|
||||||
item.Id = Utils.UrlDecode(userInfoParts[1]);
|
item.Id = Utils.UrlDecode(userInfoParts.Last());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -111,8 +111,8 @@ namespace ServiceLib.Handler.Fmt
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
item.Security = userInfoParts[0];
|
item.Security = userInfoParts.First();
|
||||||
item.Id = userInfoParts[1];
|
item.Id = userInfoParts.Last();
|
||||||
}
|
}
|
||||||
|
|
||||||
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
|
||||||
|
|||||||
@@ -5,9 +5,8 @@
|
|||||||
public static ProfileItem? Resolve(string str, out string msg)
|
public static ProfileItem? Resolve(string str, out string msg)
|
||||||
{
|
{
|
||||||
msg = ResUI.ConfigurationFormatIncorrect;
|
msg = ResUI.ConfigurationFormatIncorrect;
|
||||||
ProfileItem? item;
|
|
||||||
|
|
||||||
item = ResolveSocksNew(str) ?? ResolveSocks(str);
|
var item = ResolveSocksNew(str) ?? ResolveSocks(str);
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -25,19 +24,13 @@
|
|||||||
public static string? ToUri(ProfileItem? item)
|
public static string? ToUri(ProfileItem? item)
|
||||||
{
|
{
|
||||||
if (item == null) return null;
|
if (item == null) return null;
|
||||||
string url = string.Empty;
|
var url = string.Empty;
|
||||||
|
|
||||||
string remark = string.Empty;
|
var remark = string.Empty;
|
||||||
if (Utils.IsNotEmpty(item.Remarks))
|
if (Utils.IsNotEmpty(item.Remarks))
|
||||||
{
|
{
|
||||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||||
}
|
}
|
||||||
//url = string.Format("{0}:{1}@{2}:{3}",
|
|
||||||
// item.security,
|
|
||||||
// item.id,
|
|
||||||
// item.address,
|
|
||||||
// item.port);
|
|
||||||
//url = Utile.Base64Encode(url);
|
|
||||||
//new
|
//new
|
||||||
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
|
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
|
||||||
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
|
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
|
||||||
@@ -51,7 +44,7 @@
|
|||||||
};
|
};
|
||||||
result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..];
|
result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..];
|
||||||
//remark
|
//remark
|
||||||
int indexRemark = result.IndexOf("#");
|
var indexRemark = result.IndexOf("#");
|
||||||
if (indexRemark > 0)
|
if (indexRemark > 0)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -62,7 +55,7 @@
|
|||||||
result = result[..indexRemark];
|
result = result[..indexRemark];
|
||||||
}
|
}
|
||||||
//part decode
|
//part decode
|
||||||
int indexS = result.IndexOf("@");
|
var indexS = result.IndexOf("@");
|
||||||
if (indexS > 0)
|
if (indexS > 0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -71,21 +64,20 @@
|
|||||||
result = Utils.Base64Decode(result);
|
result = Utils.Base64Decode(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
string[] arr1 = result.Split('@');
|
var arr1 = result.Split('@');
|
||||||
if (arr1.Length != 2)
|
if (arr1.Length != 2)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
string[] arr21 = arr1[0].Split(':');
|
var arr21 = arr1.First().Split(':');
|
||||||
//string[] arr22 = arr1[1].Split(':');
|
var indexPort = arr1.Last().LastIndexOf(":");
|
||||||
int indexPort = arr1[1].LastIndexOf(":");
|
|
||||||
if (arr21.Length != 2 || indexPort < 0)
|
if (arr21.Length != 2 || indexPort < 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
item.Address = arr1[1][..indexPort];
|
item.Address = arr1[1][..indexPort];
|
||||||
item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
|
item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
|
||||||
item.Security = arr21[0];
|
item.Security = arr21.First();
|
||||||
item.Id = arr21[1];
|
item.Id = arr21[1];
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
@@ -106,10 +98,10 @@
|
|||||||
// parse base64 UserInfo
|
// parse base64 UserInfo
|
||||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||||
var userInfo = Utils.Base64Decode(rawUserInfo);
|
var userInfo = Utils.Base64Decode(rawUserInfo);
|
||||||
var userInfoParts = userInfo.Split(new[] { ':' }, 2);
|
var userInfoParts = userInfo.Split([':'], 2);
|
||||||
if (userInfoParts.Length == 2)
|
if (userInfoParts.Length == 2)
|
||||||
{
|
{
|
||||||
item.Security = userInfoParts[0];
|
item.Security = userInfoParts.First();
|
||||||
item.Id = userInfoParts[1];
|
item.Id = userInfoParts[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
|
||||||
if (userInfoParts.Length == 2)
|
if (userInfoParts.Length == 2)
|
||||||
{
|
{
|
||||||
item.Id = userInfoParts[0];
|
item.Id = userInfoParts.First();
|
||||||
item.Security = userInfoParts[1];
|
item.Security = userInfoParts.Last();
|
||||||
}
|
}
|
||||||
|
|
||||||
var query = Utils.ParseQueryString(url.Query);
|
var query = Utils.ParseQueryString(url.Query);
|
||||||
|
|||||||
105
v2rayN/ServiceLib/Handler/PacHandler.cs
Normal file
105
v2rayN/ServiceLib/Handler/PacHandler.cs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace ServiceLib.Handler
|
||||||
|
{
|
||||||
|
public class PacHandler
|
||||||
|
{
|
||||||
|
private static string _configPath;
|
||||||
|
private static int _httpPort;
|
||||||
|
private static int _pacPort;
|
||||||
|
private static TcpListener? _tcpListener;
|
||||||
|
private static byte[] _writeContent;
|
||||||
|
private static bool _isRunning;
|
||||||
|
private static bool _needRestart = true;
|
||||||
|
|
||||||
|
public static async Task Start(string configPath, int httpPort, int pacPort)
|
||||||
|
{
|
||||||
|
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning);
|
||||||
|
|
||||||
|
_configPath = configPath;
|
||||||
|
_httpPort = httpPort;
|
||||||
|
_pacPort = pacPort;
|
||||||
|
|
||||||
|
await InitText();
|
||||||
|
|
||||||
|
if (_needRestart)
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
RunListener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task InitText()
|
||||||
|
{
|
||||||
|
var path = Path.Combine(_configPath, "pac.txt");
|
||||||
|
if (!File.Exists(path))
|
||||||
|
{
|
||||||
|
var pac = Utils.GetEmbedText(Global.PacFileName);
|
||||||
|
await File.AppendAllTextAsync(path, pac);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pacText =
|
||||||
|
(await File.ReadAllTextAsync(path)).Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
sb.AppendLine("HTTP/1.0 200 OK");
|
||||||
|
sb.AppendLine("Content-type:application/x-ns-proxy-autoconfig");
|
||||||
|
sb.AppendLine("Connection:close");
|
||||||
|
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
|
||||||
|
sb.AppendLine();
|
||||||
|
sb.Append(pacText);
|
||||||
|
_writeContent = Encoding.UTF8.GetBytes(sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunListener()
|
||||||
|
{
|
||||||
|
_tcpListener = TcpListener.Create(_pacPort);
|
||||||
|
_isRunning = true;
|
||||||
|
_tcpListener.Start();
|
||||||
|
Task.Factory.StartNew(async () =>
|
||||||
|
{
|
||||||
|
while (_isRunning)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!_tcpListener.Pending())
|
||||||
|
{
|
||||||
|
await Task.Delay(10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var client = await _tcpListener.AcceptTcpClientAsync();
|
||||||
|
await Task.Run(() => { WriteContent(client); });
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, TaskCreationOptions.LongRunning);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void WriteContent(TcpClient client)
|
||||||
|
{
|
||||||
|
var stream = client.GetStream();
|
||||||
|
stream.Write(_writeContent, 0, _writeContent.Length);
|
||||||
|
stream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Stop()
|
||||||
|
{
|
||||||
|
if (_tcpListener == null) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_isRunning = false;
|
||||||
|
_tcpListener.Stop();
|
||||||
|
_tcpListener = null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
private ServerStatItem? _serverStatItem;
|
private ServerStatItem? _serverStatItem;
|
||||||
private List<ServerStatItem> _lstServerStat;
|
private List<ServerStatItem> _lstServerStat;
|
||||||
private Action<ServerSpeedItem>? _updateFunc;
|
private Action<ServerSpeedItem>? _updateFunc;
|
||||||
//private StatisticsV2rayService? _statisticsV2Ray;
|
|
||||||
private StatisticsXrayService? _statisticsXray;
|
private StatisticsXrayService? _statisticsXray;
|
||||||
private StatisticsSingboxService? _statisticsSingbox;
|
private StatisticsSingboxService? _statisticsSingbox;
|
||||||
|
|
||||||
@@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
await InitData();
|
await InitData();
|
||||||
|
|
||||||
//_statisticsV2Ray = new StatisticsV2rayService(config, UpdateServerStatHandler);
|
|
||||||
_statisticsXray = new StatisticsXrayService(config, UpdateServerStatHandler);
|
_statisticsXray = new StatisticsXrayService(config, UpdateServerStatHandler);
|
||||||
_statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler);
|
_statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler);
|
||||||
}
|
}
|
||||||
@@ -35,7 +34,6 @@
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//_statisticsV2Ray?.Close();
|
|
||||||
_statisticsXray?.Close();
|
_statisticsXray?.Close();
|
||||||
_statisticsSingbox?.Close();
|
_statisticsSingbox?.Close();
|
||||||
}
|
}
|
||||||
@@ -67,6 +65,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task CloneServerStatItem(string indexId, string toIndexId)
|
||||||
|
{
|
||||||
|
if (_lstServerStat == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId);
|
||||||
|
if (stat == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var toStat = JsonUtils.DeepCopy(stat);
|
||||||
|
toStat.IndexId = toIndexId;
|
||||||
|
await SQLiteHelper.Instance.ReplaceAsync(toStat);
|
||||||
|
_lstServerStat.Add(toStat);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task InitData()
|
private async Task InitData()
|
||||||
{
|
{
|
||||||
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
|
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
/*
|
/*
|
||||||
* 仅测试了,MacOS 13.7.1 x86 版本,其他版本有待确认
|
* 仅测试了,MacOS 13.7.1 x86 版本,其他版本有待确认
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 应用接口类型
|
/// 应用接口类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -21,14 +22,12 @@
|
|||||||
await ExecCmd(lstCmd);
|
await ExecCmd(lstCmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static async Task UnsetProxy()
|
public static async Task UnsetProxy()
|
||||||
{
|
{
|
||||||
var lstCmd = GetUnsetCmds();
|
var lstCmd = GetUnsetCmds();
|
||||||
await ExecCmd(lstCmd);
|
await ExecCmd(lstCmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static async Task ExecCmd(List<CmdItem> lstCmd)
|
private static async Task ExecCmd(List<CmdItem> lstCmd)
|
||||||
{
|
{
|
||||||
foreach (var cmd in lstCmd)
|
foreach (var cmd in lstCmd)
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using PacLib;
|
namespace ServiceLib.Handler.SysProxy
|
||||||
|
|
||||||
namespace ServiceLib.Handler.SysProxy
|
|
||||||
{
|
{
|
||||||
public static class SysProxyHandler
|
public static class SysProxyHandler
|
||||||
{
|
{
|
||||||
@@ -15,8 +13,7 @@ namespace ServiceLib.Handler.SysProxy
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.http);
|
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||||
var portSocks = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
|
||||||
if (port <= 0)
|
if (port <= 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -25,7 +22,7 @@ namespace ServiceLib.Handler.SysProxy
|
|||||||
{
|
{
|
||||||
case ESysProxyType.ForcedChange when Utils.IsWindows():
|
case ESysProxyType.ForcedChange when Utils.IsWindows():
|
||||||
{
|
{
|
||||||
GetWindowsProxyString(config, port, portSocks, out var strProxy, out var strExceptions);
|
GetWindowsProxyString(config, port, out var strProxy, out var strExceptions);
|
||||||
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
|
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -66,12 +63,12 @@ namespace ServiceLib.Handler.SysProxy
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GetWindowsProxyString(Config config, int port, int portSocks, out string strProxy, out string strExceptions)
|
private static void GetWindowsProxyString(Config config, int port, out string strProxy, out string strExceptions)
|
||||||
{
|
{
|
||||||
strExceptions = "";
|
strExceptions = $"{config.ConstItem.DefIEProxyExceptions};{config.SystemProxyItem.SystemProxyExceptions}";
|
||||||
if (config.SystemProxyItem.NotProxyLocalAddress)
|
if (config.SystemProxyItem.NotProxyLocalAddress)
|
||||||
{
|
{
|
||||||
strExceptions = $"<local>;{config.ConstItem.DefIEProxyExceptions};{config.SystemProxyItem.SystemProxyExceptions}";
|
strExceptions = $"<local>;{strExceptions}";
|
||||||
}
|
}
|
||||||
|
|
||||||
strProxy = string.Empty;
|
strProxy = string.Empty;
|
||||||
@@ -84,7 +81,7 @@ namespace ServiceLib.Handler.SysProxy
|
|||||||
strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol
|
strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol
|
||||||
.Replace("{ip}", Global.Loopback)
|
.Replace("{ip}", Global.Loopback)
|
||||||
.Replace("{http_port}", port.ToString())
|
.Replace("{http_port}", port.ToString())
|
||||||
.Replace("{socks_port}", portSocks.ToString());
|
.Replace("{socks_port}", port.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
case ECoreType.Xray when RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5:
|
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:
|
case ECoreType.sing_box when RunningCoreType is ECoreType.sing_box or ECoreType.mihomo:
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
public int SpeedTestTimeout { get; set; }
|
public int SpeedTestTimeout { get; set; }
|
||||||
public string SpeedTestUrl { get; set; }
|
public string SpeedTestUrl { get; set; }
|
||||||
public string SpeedPingTestUrl { get; set; }
|
public string SpeedPingTestUrl { get; set; }
|
||||||
|
public int SpeedTestPageSize { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
@@ -180,7 +181,6 @@
|
|||||||
public string DomainStrategy4Singbox { get; set; }
|
public string DomainStrategy4Singbox { get; set; }
|
||||||
public string DomainMatcher { get; set; }
|
public string DomainMatcher { get; set; }
|
||||||
public string RoutingIndexId { get; set; }
|
public string RoutingIndexId { get; set; }
|
||||||
public bool EnableRoutingAdvanced { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
@@ -223,7 +223,6 @@
|
|||||||
public int ProxiesSorting { get; set; }
|
public int ProxiesSorting { get; set; }
|
||||||
public bool ProxiesAutoRefresh { get; set; }
|
public bool ProxiesAutoRefresh { get; set; }
|
||||||
public int ProxiesAutoDelayTestInterval { get; set; } = 10;
|
public int ProxiesAutoDelayTestInterval { get; set; } = 10;
|
||||||
public int ConnectionsSorting { get; set; }
|
|
||||||
public bool ConnectionsAutoRefresh { get; set; }
|
public bool ConnectionsAutoRefresh { get; set; }
|
||||||
public int ConnectionsRefreshInterval { get; set; } = 2;
|
public int ConnectionsRefreshInterval { get; set; } = 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
public string? DownloadUrlWinArm64 { get; set; }
|
public string? DownloadUrlWinArm64 { get; set; }
|
||||||
public string? DownloadUrlLinux64 { get; set; }
|
public string? DownloadUrlLinux64 { get; set; }
|
||||||
public string? DownloadUrlLinuxArm64 { get; set; }
|
public string? DownloadUrlLinuxArm64 { get; set; }
|
||||||
|
public string? DownloadUrlOSX64 { get; set; }
|
||||||
|
public string? DownloadUrlOSXArm64 { get; set; }
|
||||||
public string? Match { get; set; }
|
public string? Match { get; set; }
|
||||||
public string? VersionArg { get; set; }
|
public string? VersionArg { get; set; }
|
||||||
public bool RedirectInfo { get; set; }
|
public bool RedirectInfo { get; set; }
|
||||||
|
|||||||
13
v2rayN/ServiceLib/Models/IPAPIInfo.cs
Normal file
13
v2rayN/ServiceLib/Models/IPAPIInfo.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace ServiceLib.Models
|
||||||
|
{
|
||||||
|
internal class IPAPIInfo
|
||||||
|
{
|
||||||
|
public string? ip { get; set; }
|
||||||
|
public string? city { get; set; }
|
||||||
|
public string? region { get; set; }
|
||||||
|
public string? region_code { get; set; }
|
||||||
|
public string? country { get; set; }
|
||||||
|
public string? country_name { get; set; }
|
||||||
|
public string? country_code { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -349,20 +349,20 @@ namespace ServiceLib.Models
|
|||||||
public string? path { get; set; }
|
public string? path { get; set; }
|
||||||
public string? host { get; set; }
|
public string? host { get; set; }
|
||||||
public string? mode { get; set; }
|
public string? mode { get; set; }
|
||||||
public string? scMaxEachPostBytes { get; set; }
|
public object? scMaxEachPostBytes { get; set; }
|
||||||
public string? scMaxConcurrentPosts { get; set; }
|
public object? scMaxConcurrentPosts { get; set; }
|
||||||
public string? scMinPostsIntervalMs { get; set; }
|
public object? scMinPostsIntervalMs { get; set; }
|
||||||
public Xmux4Ray? xmux { get; set; }
|
//public Xmux4Ray? xmux { get; set; }
|
||||||
public object? extra { get; set; }
|
public object? extra { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Xmux4Ray
|
//public class Xmux4Ray
|
||||||
{
|
//{
|
||||||
public int? maxConcurrency { get; set; }
|
// public object? maxConcurrency { get; set; }
|
||||||
public int? maxConnections { get; set; }
|
// public object? maxConnections { get; set; }
|
||||||
public int? cMaxReuseTimes { get; set; }
|
// public object? cMaxReuseTimes { get; set; }
|
||||||
public int? cMaxLifetimeMs { get; set; }
|
// public object? cMaxLifetimeMs { get; set; }
|
||||||
}
|
//}
|
||||||
|
|
||||||
public class HttpSettings4Ray
|
public class HttpSettings4Ray
|
||||||
{
|
{
|
||||||
|
|||||||
35
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
35
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -1365,24 +1365,6 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Basic Function 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string menuRoutingBasic {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("menuRoutingBasic", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找类似 Import Basic Rules 的本地化字符串。
|
|
||||||
/// </summary>
|
|
||||||
public static string menuRoutingBasicImportRules {
|
|
||||||
get {
|
|
||||||
return ResourceManager.GetString("menuRoutingBasicImportRules", resourceCulture);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 RoutingRuleDetailsSetting 的本地化字符串。
|
/// 查找类似 RoutingRuleDetailsSetting 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -3158,7 +3140,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 Linux system sudo password 的本地化字符串。
|
/// 查找类似 System sudo password 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TbSettingsLinuxSudoPassword {
|
public static string TbSettingsLinuxSudoPassword {
|
||||||
get {
|
get {
|
||||||
@@ -3329,7 +3311,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 SOCKS Port 的本地化字符串。
|
/// 查找类似 Mixed Port 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TbSettingsSocksPort {
|
public static string TbSettingsSocksPort {
|
||||||
get {
|
get {
|
||||||
@@ -3338,7 +3320,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6; 的本地化字符串。
|
/// 查找类似 Pac port = +2; Xray API port = +3; mihomo API port = +4; 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TbSettingsSocksPortTip {
|
public static string TbSettingsSocksPortTip {
|
||||||
get {
|
get {
|
||||||
@@ -3355,6 +3337,15 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找类似 Number per time for auto batch during speedtest(max 1000) 的本地化字符串。
|
||||||
|
/// </summary>
|
||||||
|
public static string TbSettingsSpeedTestPageSize {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TbSettingsSpeedTestPageSize", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 SpeedTest Single Timeout Value 的本地化字符串。
|
/// 查找类似 SpeedTest Single Timeout Value 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -3644,7 +3635,7 @@ namespace ServiceLib.Resx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查找类似 The ping of current service: {0} ms 的本地化字符串。
|
/// 查找类似 The delay : {0} ms, {1} 的本地化字符串。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string TestMeOutput {
|
public static string TestMeOutput {
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -302,7 +302,7 @@
|
|||||||
<value>اسکن URL وارد کردن با موفقیت</value>
|
<value>اسکن URL وارد کردن با موفقیت</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TestMeOutput" xml:space="preserve">
|
<data name="TestMeOutput" xml:space="preserve">
|
||||||
<value>پینگ سرویس فعلی: {0} ms</value>
|
<value>پینگ سرویس فعلی: {0} ms, {1}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OperationSuccess" xml:space="preserve">
|
<data name="OperationSuccess" xml:space="preserve">
|
||||||
<value>موفقیت عملیات</value>
|
<value>موفقیت عملیات</value>
|
||||||
@@ -752,7 +752,7 @@
|
|||||||
<value>Turn on Sniffing</value>
|
<value>Turn on Sniffing</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsSocksPort" xml:space="preserve">
|
<data name="TbSettingsSocksPort" xml:space="preserve">
|
||||||
<value>ساکس Port</value>
|
<value>Mixed Port</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||||
<value>درهنگام راه ائدازی شروع شود</value>
|
<value>درهنگام راه ائدازی شروع شود</value>
|
||||||
@@ -847,12 +847,6 @@
|
|||||||
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
|
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
|
||||||
<value>Set as active rule</value>
|
<value>Set as active rule</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRoutingBasic" xml:space="preserve">
|
|
||||||
<value>عملکرد پایه</value>
|
|
||||||
</data>
|
|
||||||
<data name="menuRoutingBasicImportRules" xml:space="preserve">
|
|
||||||
<value>واردات قوانین اساسی</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbdomainMatcher" xml:space="preserve">
|
<data name="TbdomainMatcher" xml:space="preserve">
|
||||||
<value>تطبیق دامنه</value>
|
<value>تطبیق دامنه</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1250,7 +1244,7 @@
|
|||||||
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value>
|
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||||
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value>
|
<value>Pac port = +2; Xray API port = +3; mihomo API port = +4;</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||||
<value>Set this with admin privileges, get admin privileges after startup</value>
|
<value>Set this with admin privileges, get admin privileges after startup</value>
|
||||||
@@ -1370,7 +1364,7 @@
|
|||||||
<value>(Domain or IP or ProcName) and Port and Protocol and InboundTag => OutboundTag</value>
|
<value>(Domain or IP or ProcName) and Port and Protocol and InboundTag => OutboundTag</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
||||||
<value>Linux system sudo password</value>
|
<value>System sudo password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||||
<value>The password is encrypted and stored only in local files.</value>
|
<value>The password is encrypted and stored only in local files.</value>
|
||||||
@@ -1390,4 +1384,7 @@
|
|||||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||||
<value>Hide to tray when closing the window</value>
|
<value>Hide to tray when closing the window</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
|
||||||
|
<value>Number per time for auto batch during speedtest(max 1000)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -302,7 +302,7 @@
|
|||||||
<value>Scan import the shared link successfully</value>
|
<value>Scan import the shared link successfully</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TestMeOutput" xml:space="preserve">
|
<data name="TestMeOutput" xml:space="preserve">
|
||||||
<value>The ping of current service: {0} ms</value>
|
<value>The delay : {0} ms, {1}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OperationSuccess" xml:space="preserve">
|
<data name="OperationSuccess" xml:space="preserve">
|
||||||
<value>Operation success</value>
|
<value>Operation success</value>
|
||||||
@@ -755,7 +755,7 @@
|
|||||||
<value>Turn on Sniffing</value>
|
<value>Turn on Sniffing</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsSocksPort" xml:space="preserve">
|
<data name="TbSettingsSocksPort" xml:space="preserve">
|
||||||
<value>SOCKS Port</value>
|
<value>Mixed Port</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||||
<value>Start on boot</value>
|
<value>Start on boot</value>
|
||||||
@@ -850,12 +850,6 @@
|
|||||||
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
|
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
|
||||||
<value>Set as active rule(Enter)</value>
|
<value>Set as active rule(Enter)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRoutingBasic" xml:space="preserve">
|
|
||||||
<value>Basic Function</value>
|
|
||||||
</data>
|
|
||||||
<data name="menuRoutingBasicImportRules" xml:space="preserve">
|
|
||||||
<value>Import Basic Rules</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbdomainMatcher" xml:space="preserve">
|
<data name="TbdomainMatcher" xml:space="preserve">
|
||||||
<value>Domain Matcher</value>
|
<value>Domain Matcher</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -998,7 +992,7 @@
|
|||||||
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value>
|
<value>Copy the font TTF/TTC file to the directory guiFonts, restart the settings</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||||
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value>
|
<value>Pac port = +2; Xray API port = +3; mihomo API port = +4;</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||||
<value>Set this with admin privileges, get admin privileges after startup</value>
|
<value>Set this with admin privileges, get admin privileges after startup</value>
|
||||||
@@ -1370,7 +1364,7 @@
|
|||||||
<value>Remarks Memo</value>
|
<value>Remarks Memo</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
||||||
<value>Linux system sudo password</value>
|
<value>System sudo password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||||
<value>The password is encrypted and stored only in local files.</value>
|
<value>The password is encrypted and stored only in local files.</value>
|
||||||
@@ -1390,4 +1384,7 @@
|
|||||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||||
<value>Hide to tray when closing the window</value>
|
<value>Hide to tray when closing the window</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
|
||||||
|
<value>Number per time for auto batch during speedtest(max 1000)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -302,7 +302,7 @@
|
|||||||
<value>Сканирование URL-адреса импорта успешна.</value>
|
<value>Сканирование URL-адреса импорта успешна.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TestMeOutput" xml:space="preserve">
|
<data name="TestMeOutput" xml:space="preserve">
|
||||||
<value>Задержка текущего сервера: {0} мс</value>
|
<value>Задержка текущего сервера: {0} мс, {1}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OperationSuccess" xml:space="preserve">
|
<data name="OperationSuccess" xml:space="preserve">
|
||||||
<value>Операция успешна</value>
|
<value>Операция успешна</value>
|
||||||
@@ -761,7 +761,7 @@
|
|||||||
<value>Включить сниффинг</value>
|
<value>Включить сниффинг</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsSocksPort" xml:space="preserve">
|
<data name="TbSettingsSocksPort" xml:space="preserve">
|
||||||
<value>Порт Socks</value>
|
<value>Mixed Port</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||||
<value>Автозапуск</value>
|
<value>Автозапуск</value>
|
||||||
@@ -856,12 +856,6 @@
|
|||||||
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
|
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
|
||||||
<value>Установить как активное правило</value>
|
<value>Установить как активное правило</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRoutingBasic" xml:space="preserve">
|
|
||||||
<value>Основные функции</value>
|
|
||||||
</data>
|
|
||||||
<data name="menuRoutingBasicImportRules" xml:space="preserve">
|
|
||||||
<value>Импорт основных правил</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbdomainMatcher" xml:space="preserve">
|
<data name="TbdomainMatcher" xml:space="preserve">
|
||||||
<value>Сопоставитель доменов</value>
|
<value>Сопоставитель доменов</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -1316,7 +1310,7 @@
|
|||||||
<value>Move up and down</value>
|
<value>Move up and down</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||||
<value>http port = +1; Pac port = +4; *ray API port = +5; mihomo API port = +6;</value>
|
<value>Pac port = +2; Xray API port = +3; mihomo API port = +4;</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
|
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
|
||||||
<value>Install the font to the system and restart the settings</value>
|
<value>Install the font to the system and restart the settings</value>
|
||||||
@@ -1370,7 +1364,7 @@
|
|||||||
<value>Remote (WebDAV)</value>
|
<value>Remote (WebDAV)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
||||||
<value>Linux system sudo password</value>
|
<value>System sudo password</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||||
<value>The password is encrypted and stored only in local files.</value>
|
<value>The password is encrypted and stored only in local files.</value>
|
||||||
@@ -1390,4 +1384,7 @@
|
|||||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||||
<value>Hide to tray when closing the window</value>
|
<value>Hide to tray when closing the window</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
|
||||||
|
<value>Number per time for auto batch during speedtest(max 1000)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -302,7 +302,7 @@
|
|||||||
<value>扫描导入分享链接成功</value>
|
<value>扫描导入分享链接成功</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TestMeOutput" xml:space="preserve">
|
<data name="TestMeOutput" xml:space="preserve">
|
||||||
<value>当前服务的真连接延迟: {0} ms</value>
|
<value>当前延迟: {0} ms,{1}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OperationSuccess" xml:space="preserve">
|
<data name="OperationSuccess" xml:space="preserve">
|
||||||
<value>操作成功</value>
|
<value>操作成功</value>
|
||||||
@@ -755,7 +755,7 @@
|
|||||||
<value>开启流量探测</value>
|
<value>开启流量探测</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsSocksPort" xml:space="preserve">
|
<data name="TbSettingsSocksPort" xml:space="preserve">
|
||||||
<value>本地socks监听端口</value>
|
<value>本地混合监听端口</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||||
<value>开机启动(可能会不成功)</value>
|
<value>开机启动(可能会不成功)</value>
|
||||||
@@ -850,12 +850,6 @@
|
|||||||
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
|
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
|
||||||
<value>设为活动规则 (Enter)</value>
|
<value>设为活动规则 (Enter)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRoutingBasic" xml:space="preserve">
|
|
||||||
<value>基础功能</value>
|
|
||||||
</data>
|
|
||||||
<data name="menuRoutingBasicImportRules" xml:space="preserve">
|
|
||||||
<value>一键导入基础规则</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbdomainMatcher" xml:space="preserve">
|
<data name="TbdomainMatcher" xml:space="preserve">
|
||||||
<value>域名匹配算法</value>
|
<value>域名匹配算法</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -998,7 +992,7 @@
|
|||||||
<value>拷贝字体TTF/TTC文件到目录guiFonts,重启设置</value>
|
<value>拷贝字体TTF/TTC文件到目录guiFonts,重启设置</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||||
<value>http端口= +1;Pac端口= +4;*ray API端口= +5;mihomo API端口= +6;</value>
|
<value>Pac端口= +2;Xray API端口= +3;mihomo API端口= +4;</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||||
<value>以管理员权限设置此项,在启动后获得管理员权限</value>
|
<value>以管理员权限设置此项,在启动后获得管理员权限</value>
|
||||||
@@ -1367,10 +1361,10 @@
|
|||||||
<value>备注备忘</value>
|
<value>备注备忘</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
||||||
<value>Linux系统的sudo密码</value>
|
<value>系统的sudo密码</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||||
<value>密码已加密且只存储在本地文件中,无密码无法开启Tun</value>
|
<value>密码已加密且只存储在本地文件中,无密码则每次都要输入</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
|
||||||
<value>请先在Tun模式设置中设置sudo密码</value>
|
<value>请先在Tun模式设置中设置sudo密码</value>
|
||||||
@@ -1387,4 +1381,7 @@
|
|||||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||||
<value>关闭窗口时隐藏至托盘</value>
|
<value>关闭窗口时隐藏至托盘</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
|
||||||
|
<value>测速时自动分批的每批数量(最大1000)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -301,7 +301,7 @@
|
|||||||
<value>掃描匯入分享链接成功</value>
|
<value>掃描匯入分享链接成功</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TestMeOutput" xml:space="preserve">
|
<data name="TestMeOutput" xml:space="preserve">
|
||||||
<value>目前服務的真連線延遲: {0} ms</value>
|
<value>目前延遲: {0} ms,{1}</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="OperationSuccess" xml:space="preserve">
|
<data name="OperationSuccess" xml:space="preserve">
|
||||||
<value>操作成功</value>
|
<value>操作成功</value>
|
||||||
@@ -755,7 +755,7 @@
|
|||||||
<value>開啟流量探測</value>
|
<value>開啟流量探測</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsSocksPort" xml:space="preserve">
|
<data name="TbSettingsSocksPort" xml:space="preserve">
|
||||||
<value>本機SOCKS偵聽埠</value>
|
<value>本機混合偵聽埠</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||||
<value>開機啟動(可能會不成功)</value>
|
<value>開機啟動(可能會不成功)</value>
|
||||||
@@ -850,12 +850,6 @@
|
|||||||
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
|
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
|
||||||
<value>設為活動規則 (Enter)</value>
|
<value>設為活動規則 (Enter)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="menuRoutingBasic" xml:space="preserve">
|
|
||||||
<value>基礎功能</value>
|
|
||||||
</data>
|
|
||||||
<data name="menuRoutingBasicImportRules" xml:space="preserve">
|
|
||||||
<value>一鍵匯入基礎規則</value>
|
|
||||||
</data>
|
|
||||||
<data name="TbdomainMatcher" xml:space="preserve">
|
<data name="TbdomainMatcher" xml:space="preserve">
|
||||||
<value>域名匹配演算法</value>
|
<value>域名匹配演算法</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -998,7 +992,7 @@
|
|||||||
<value>複製字型TTF/TTC檔案到目錄guiFonts,重啟設定</value>
|
<value>複製字型TTF/TTC檔案到目錄guiFonts,重啟設定</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||||
<value>http連接埠= +1;Pac連接埠= +4;*ray API連接埠= +5;mihomo API連接埠= +6;</value>
|
<value>Pac連接埠= +2;Xray API連接埠= +3;mihomo API連接埠= +4;</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||||
<value>以管理員權限設定此項,在啟動後獲得管理員權限</value>
|
<value>以管理員權限設定此項,在啟動後獲得管理員權限</value>
|
||||||
@@ -1367,10 +1361,10 @@
|
|||||||
<value>混淆密碼(obfs password)</value>
|
<value>混淆密碼(obfs password)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
||||||
<value>Linux系統的sudo密碼</value>
|
<value>系統的sudo密碼</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||||
<value>密碼已加密且只儲存在本機檔案中,無密碼無法開啟Tun</value>
|
<value>密碼已加密且只儲存在本機檔案中,無密碼則每次都要輸入</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
|
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
|
||||||
<value>請先在Tun模式設定中設定sudo密碼</value>
|
<value>請先在Tun模式設定中設定sudo密碼</value>
|
||||||
@@ -1387,4 +1381,7 @@
|
|||||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||||
<value>關閉視窗時隱藏至托盤</value>
|
<value>關閉視窗時隱藏至托盤</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="TbSettingsSpeedTestPageSize" xml:space="preserve">
|
||||||
|
<value>測速時自動分批的每批數量(最大1000)</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -4,17 +4,17 @@
|
|||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Version>7.1.2</Version>
|
<Version>7.3.0</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Downloader" Version="3.2.1" />
|
<PackageReference Include="Downloader" Version="3.3.1" />
|
||||||
<PackageReference Include="ReactiveUI" Version="20.1.63" />
|
<PackageReference Include="ReactiveUI" Version="20.1.63" />
|
||||||
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
|
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||||
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
|
<PackageReference Include="sqlite-net-pcl" Version="1.9.172" />
|
||||||
<PackageReference Include="Splat.NLog" Version="15.2.22" />
|
<PackageReference Include="Splat.NLog" Version="15.2.22" />
|
||||||
<PackageReference Include="WebDav.Client" Version="2.8.0" />
|
<PackageReference Include="WebDav.Client" Version="2.8.0" />
|
||||||
<PackageReference Include="YamlDotNet" Version="16.2.0" />
|
<PackageReference Include="YamlDotNet" Version="16.2.1" />
|
||||||
<PackageReference Include="QRCoder" Version="1.6.0" />
|
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||||
<PackageReference Include="CliWrap" Version="3.6.7" />
|
<PackageReference Include="CliWrap" Version="3.6.7" />
|
||||||
<PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" />
|
<PackageReference Include="SkiaSharp.QrCode" Version="0.7.0" />
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
<EmbeddedResource Include="Sample\custom_routing_white" />
|
<EmbeddedResource Include="Sample\custom_routing_white" />
|
||||||
<EmbeddedResource Include="Sample\dns_singbox_normal" />
|
<EmbeddedResource Include="Sample\dns_singbox_normal" />
|
||||||
<EmbeddedResource Include="Sample\dns_v2ray_normal" />
|
<EmbeddedResource Include="Sample\dns_v2ray_normal" />
|
||||||
|
<EmbeddedResource Include="Sample\pac" />
|
||||||
<EmbeddedResource Include="Sample\SampleClientConfig" />
|
<EmbeddedResource Include="Sample\SampleClientConfig" />
|
||||||
<EmbeddedResource Include="Sample\SampleHttpRequest" />
|
<EmbeddedResource Include="Sample\SampleHttpRequest" />
|
||||||
<EmbeddedResource Include="Sample\SampleHttpResponse" />
|
<EmbeddedResource Include="Sample\SampleHttpResponse" />
|
||||||
@@ -46,12 +47,6 @@
|
|||||||
<EmbeddedResource Include="Sample\linux_autostart_config" />
|
<EmbeddedResource Include="Sample\linux_autostart_config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\PacLib\PacLib.csproj" />
|
|
||||||
<ProjectReference Include="..\ProtosLib\ProtosLib.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Resx\ResUI.Designer.cs">
|
<Compile Update="Resx\ResUI.Designer.cs">
|
||||||
<DependentUpon>ResUI.resx</DependentUpon>
|
<DependentUpon>ResUI.resx</DependentUpon>
|
||||||
|
|||||||
@@ -78,17 +78,15 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
//port
|
//mixed-port
|
||||||
fileContent["port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.http);
|
fileContent["mixed-port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||||
//socks-port
|
|
||||||
fileContent["socks-port"] = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
|
||||||
//log-level
|
//log-level
|
||||||
fileContent["log-level"] = GetLogLevel(_config.CoreBasicItem.Loglevel);
|
fileContent["log-level"] = GetLogLevel(_config.CoreBasicItem.Loglevel);
|
||||||
|
|
||||||
//external-controller
|
//external-controller
|
||||||
fileContent["external-controller"] = $"{Global.Loopback}:{AppHandler.Instance.StatePort2}";
|
fileContent["external-controller"] = $"{Global.Loopback}:{AppHandler.Instance.StatePort2}";
|
||||||
//allow-lan
|
//allow-lan
|
||||||
if (_config.Inbound[0].AllowLANConn)
|
if (_config.Inbound.First().AllowLANConn)
|
||||||
{
|
{
|
||||||
fileContent["allow-lan"] = "true";
|
fileContent["allow-lan"] = "true";
|
||||||
fileContent["bind-address"] = "*";
|
fileContent["bind-address"] = "*";
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
ret.Msg = ResUI.CheckServerSettings;
|
ret.Msg = ResUI.CheckServerSettings;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.splithttp) or nameof(ETransport.xhttp))
|
if (node.GetNetwork() is nameof(ETransport.kcp) or nameof(ETransport.xhttp))
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
ret.Msg = ResUI.Incorrectconfiguration + $" - {node.GetNetwork()}";
|
||||||
return ret;
|
return ret;
|
||||||
@@ -52,7 +52,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
|
|
||||||
await GenInbounds(singboxConfig);
|
await GenInbounds(singboxConfig);
|
||||||
|
|
||||||
await GenOutbound(node, singboxConfig.outbounds[0]);
|
await GenOutbound(node, singboxConfig.outbounds.First());
|
||||||
|
|
||||||
await GenMoreOutbounds(node, singboxConfig);
|
await GenMoreOutbounds(node, singboxConfig);
|
||||||
|
|
||||||
@@ -90,8 +90,8 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
string result = Utils.GetEmbedText(Global.SingboxSampleClient);
|
var result = Utils.GetEmbedText(Global.SingboxSampleClient);
|
||||||
string txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
|
var txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||||
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
|
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||||
@@ -119,10 +119,10 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
|
|
||||||
await GenLog(singboxConfig);
|
await GenLog(singboxConfig);
|
||||||
//GenDns(new(), singboxConfig);
|
//GenDns(new(), singboxConfig);
|
||||||
singboxConfig.inbounds.Clear(); // Remove "proxy" service for speedtest, avoiding port conflicts.
|
singboxConfig.inbounds.Clear();
|
||||||
singboxConfig.outbounds.RemoveAt(0);
|
singboxConfig.outbounds.RemoveAt(0);
|
||||||
|
|
||||||
int httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
var httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||||
|
|
||||||
foreach (var it in selecteds)
|
foreach (var it in selecteds)
|
||||||
{
|
{
|
||||||
@@ -174,7 +174,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
{
|
{
|
||||||
listen = Global.Loopback,
|
listen = Global.Loopback,
|
||||||
listen_port = port,
|
listen_port = port,
|
||||||
type = EInboundProtocol.http.ToString(),
|
type = EInboundProtocol.mixed.ToString(),
|
||||||
};
|
};
|
||||||
inbound.tag = inbound.type + inbound.listen_port.ToString();
|
inbound.tag = inbound.type + inbound.listen_port.ToString();
|
||||||
singboxConfig.inbounds.Add(inbound);
|
singboxConfig.inbounds.Add(inbound);
|
||||||
@@ -488,53 +488,40 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
{
|
{
|
||||||
var inbound = new Inbound4Sbox()
|
var inbound = new Inbound4Sbox()
|
||||||
{
|
{
|
||||||
type = EInboundProtocol.socks.ToString(),
|
type = EInboundProtocol.mixed.ToString(),
|
||||||
tag = EInboundProtocol.socks.ToString(),
|
tag = EInboundProtocol.socks.ToString(),
|
||||||
listen = Global.Loopback,
|
listen = Global.Loopback,
|
||||||
};
|
};
|
||||||
singboxConfig.inbounds.Add(inbound);
|
singboxConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
inbound.listen_port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||||
inbound.sniff = _config.Inbound[0].SniffingEnabled;
|
inbound.sniff = _config.Inbound.First().SniffingEnabled;
|
||||||
inbound.sniff_override_destination = _config.Inbound[0].RouteOnly ? false : _config.Inbound[0].SniffingEnabled;
|
inbound.sniff_override_destination = _config.Inbound.First().RouteOnly ? false : _config.Inbound.First().SniffingEnabled;
|
||||||
inbound.domain_strategy = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainStrategy4Singbox) ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
|
inbound.domain_strategy = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainStrategy4Singbox) ? null : _config.RoutingBasicItem.DomainStrategy4Singbox;
|
||||||
|
|
||||||
if (_config.RoutingBasicItem.EnableRoutingAdvanced)
|
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||||
|
if (Utils.IsNotEmpty(routing.DomainStrategy4Singbox))
|
||||||
{
|
{
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
inbound.domain_strategy = routing.DomainStrategy4Singbox;
|
||||||
if (Utils.IsNotEmpty(routing.DomainStrategy4Singbox))
|
|
||||||
{
|
|
||||||
inbound.domain_strategy = routing.DomainStrategy4Singbox;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//http
|
if (_config.Inbound.First().AllowLANConn)
|
||||||
var inbound2 = GetInbound(inbound, EInboundProtocol.http, false);
|
|
||||||
singboxConfig.inbounds.Add(inbound2);
|
|
||||||
|
|
||||||
if (_config.Inbound[0].AllowLANConn)
|
|
||||||
{
|
{
|
||||||
if (_config.Inbound[0].NewPort4LAN)
|
if (_config.Inbound.First().NewPort4LAN)
|
||||||
{
|
{
|
||||||
var inbound3 = GetInbound(inbound, EInboundProtocol.socks2, true);
|
var inbound3 = GetInbound(inbound, EInboundProtocol.socks2, true);
|
||||||
inbound3.listen = listen;
|
inbound3.listen = listen;
|
||||||
singboxConfig.inbounds.Add(inbound3);
|
singboxConfig.inbounds.Add(inbound3);
|
||||||
|
|
||||||
var inbound4 = GetInbound(inbound, EInboundProtocol.http2, false);
|
|
||||||
inbound4.listen = listen;
|
|
||||||
singboxConfig.inbounds.Add(inbound4);
|
|
||||||
|
|
||||||
//auth
|
//auth
|
||||||
if (Utils.IsNotEmpty(_config.Inbound[0].User) && Utils.IsNotEmpty(_config.Inbound[0].Pass))
|
if (Utils.IsNotEmpty(_config.Inbound.First().User) && Utils.IsNotEmpty(_config.Inbound.First().Pass))
|
||||||
{
|
{
|
||||||
inbound3.users = new() { new() { username = _config.Inbound[0].User, password = _config.Inbound[0].Pass } };
|
inbound3.users = new() { new() { username = _config.Inbound.First().User, password = _config.Inbound.First().Pass } };
|
||||||
inbound4.users = new() { new() { username = _config.Inbound[0].User, password = _config.Inbound[0].Pass } };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inbound.listen = listen;
|
inbound.listen = listen;
|
||||||
inbound2.listen = listen;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,19 +530,20 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
{
|
{
|
||||||
if (_config.TunModeItem.Mtu <= 0)
|
if (_config.TunModeItem.Mtu <= 0)
|
||||||
{
|
{
|
||||||
_config.TunModeItem.Mtu = Utils.ToInt(Global.TunMtus[0]);
|
_config.TunModeItem.Mtu = Utils.ToInt(Global.TunMtus.First());
|
||||||
}
|
}
|
||||||
if (Utils.IsNullOrEmpty(_config.TunModeItem.Stack))
|
if (Utils.IsNullOrEmpty(_config.TunModeItem.Stack))
|
||||||
{
|
{
|
||||||
_config.TunModeItem.Stack = Global.TunStacks[0];
|
_config.TunModeItem.Stack = Global.TunStacks.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(Utils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { };
|
var tunInbound = JsonUtils.Deserialize<Inbound4Sbox>(Utils.GetEmbedText(Global.TunSingboxInboundFileName)) ?? new Inbound4Sbox { };
|
||||||
|
tunInbound.interface_name = Utils.IsOSX() ? $"utun{new Random().Next(99)}" : "singbox_tun";
|
||||||
tunInbound.mtu = _config.TunModeItem.Mtu;
|
tunInbound.mtu = _config.TunModeItem.Mtu;
|
||||||
tunInbound.strict_route = _config.TunModeItem.StrictRoute;
|
tunInbound.strict_route = _config.TunModeItem.StrictRoute;
|
||||||
tunInbound.stack = _config.TunModeItem.Stack;
|
tunInbound.stack = _config.TunModeItem.Stack;
|
||||||
tunInbound.sniff = _config.Inbound[0].SniffingEnabled;
|
tunInbound.sniff = _config.Inbound.First().SniffingEnabled;
|
||||||
//tunInbound.sniff_override_destination = _config.inbound[0].routeOnly ? false : _config.inbound[0].sniffingEnabled;
|
//tunInbound.sniff_override_destination = _config.inbound.First().routeOnly ? false : _config.inbound.First().sniffingEnabled;
|
||||||
if (_config.TunModeItem.EnableIPv6Address == false)
|
if (_config.TunModeItem.EnableIPv6Address == false)
|
||||||
{
|
{
|
||||||
tunInbound.address = ["172.18.0.1/30"];
|
tunInbound.address = ["172.18.0.1/30"];
|
||||||
@@ -576,7 +564,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
var inbound = JsonUtils.DeepCopy(inItem);
|
var inbound = JsonUtils.DeepCopy(inItem);
|
||||||
inbound.tag = protocol.ToString();
|
inbound.tag = protocol.ToString();
|
||||||
inbound.listen_port = inItem.listen_port + (int)protocol;
|
inbound.listen_port = inItem.listen_port + (int)protocol;
|
||||||
inbound.type = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString();
|
inbound.type = EInboundProtocol.mixed.ToString();
|
||||||
return inbound;
|
return inbound;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -869,7 +857,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
//current proxy
|
//current proxy
|
||||||
var outbound = singboxConfig.outbounds[0];
|
var outbound = singboxConfig.outbounds.First();
|
||||||
var txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
|
var txtOutbound = Utils.GetEmbedText(Global.SingboxSampleOutbound);
|
||||||
|
|
||||||
//Previous proxy
|
//Previous proxy
|
||||||
@@ -912,7 +900,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dnsOutbound = "dns_out";
|
var dnsOutbound = "dns_out";
|
||||||
if (!_config.Inbound[0].SniffingEnabled)
|
if (!_config.Inbound.First().SniffingEnabled)
|
||||||
{
|
{
|
||||||
singboxConfig.route.rules.Add(new()
|
singboxConfig.route.rules.Add(new()
|
||||||
{
|
{
|
||||||
@@ -958,28 +946,13 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.RoutingBasicItem.EnableRoutingAdvanced)
|
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||||
|
if (routing != null)
|
||||||
{
|
{
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
||||||
if (routing != null)
|
foreach (var item in rules ?? [])
|
||||||
{
|
{
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
if (item.Enabled)
|
||||||
foreach (var item in rules ?? [])
|
|
||||||
{
|
|
||||||
if (item.Enabled)
|
|
||||||
{
|
|
||||||
await GenRoutingUserRule(item, singboxConfig.route.rules);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var lockedItem = await ConfigHandler.GetLockedRoutingItem(_config);
|
|
||||||
if (lockedItem != null)
|
|
||||||
{
|
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(lockedItem.RuleSet);
|
|
||||||
foreach (var item in rules ?? [])
|
|
||||||
{
|
{
|
||||||
await GenRoutingUserRule(item, singboxConfig.route.rules);
|
await GenRoutingUserRule(item, singboxConfig.route.rules);
|
||||||
}
|
}
|
||||||
@@ -1334,20 +1307,18 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
|
|
||||||
//load custom ruleset file
|
//load custom ruleset file
|
||||||
List<Ruleset4Sbox> customRulesets = [];
|
List<Ruleset4Sbox> customRulesets = [];
|
||||||
if (_config.RoutingBasicItem.EnableRoutingAdvanced)
|
|
||||||
|
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||||
|
if (Utils.IsNotEmpty(routing.CustomRulesetPath4Singbox))
|
||||||
{
|
{
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
var result = Utils.LoadResource(routing.CustomRulesetPath4Singbox);
|
||||||
if (Utils.IsNotEmpty(routing.CustomRulesetPath4Singbox))
|
if (Utils.IsNotEmpty(result))
|
||||||
{
|
{
|
||||||
var result = Utils.LoadResource(routing.CustomRulesetPath4Singbox);
|
customRulesets = (JsonUtils.Deserialize<List<Ruleset4Sbox>>(result) ?? [])
|
||||||
if (Utils.IsNotEmpty(result))
|
.Where(t => t.tag != null)
|
||||||
{
|
.Where(t => t.type != null)
|
||||||
customRulesets = (JsonUtils.Deserialize<List<Ruleset4Sbox>>(result) ?? [])
|
.Where(t => t.format != null)
|
||||||
.Where(t => t.tag != null)
|
.ToList();
|
||||||
.Where(t => t.type != null)
|
|
||||||
.Where(t => t.format != null)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
|
|
||||||
await GenRouting(v2rayConfig);
|
await GenRouting(v2rayConfig);
|
||||||
|
|
||||||
await GenOutbound(node, v2rayConfig.outbounds[0]);
|
await GenOutbound(node, v2rayConfig.outbounds.First());
|
||||||
|
|
||||||
await GenMoreOutbounds(node, v2rayConfig);
|
await GenMoreOutbounds(node, v2rayConfig);
|
||||||
|
|
||||||
@@ -216,8 +216,8 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
|
|
||||||
ret.Msg = ResUI.InitialConfiguration;
|
ret.Msg = ResUI.InitialConfiguration;
|
||||||
|
|
||||||
string result = Utils.GetEmbedText(Global.V2raySampleClient);
|
var result = Utils.GetEmbedText(Global.V2raySampleClient);
|
||||||
string txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
|
var txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
|
||||||
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
|
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
ret.Msg = ResUI.FailedGetDefaultConfiguration;
|
||||||
@@ -244,10 +244,11 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
await GenLog(v2rayConfig);
|
await GenLog(v2rayConfig);
|
||||||
v2rayConfig.inbounds.Clear(); // Remove "proxy" service for speedtest, avoiding port conflicts.
|
v2rayConfig.inbounds.Clear();
|
||||||
v2rayConfig.outbounds.RemoveAt(0);
|
v2rayConfig.outbounds.Clear();
|
||||||
|
v2rayConfig.routing.rules.Clear();
|
||||||
|
|
||||||
int httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
var httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.speedtest);
|
||||||
|
|
||||||
foreach (var it in selecteds)
|
foreach (var it in selecteds)
|
||||||
{
|
{
|
||||||
@@ -270,7 +271,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
|
|
||||||
//find unused port
|
//find unused port
|
||||||
var port = httpPort;
|
var port = httpPort;
|
||||||
for (int k = httpPort; k < Global.MaxPort; k++)
|
for (var k = httpPort; k < Global.MaxPort; k++)
|
||||||
{
|
{
|
||||||
if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0)
|
if (lstIpEndPoints?.FindIndex(_it => _it.Port == k) >= 0)
|
||||||
{
|
{
|
||||||
@@ -294,16 +295,6 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
it.Port = port;
|
it.Port = port;
|
||||||
it.AllowTest = true;
|
it.AllowTest = true;
|
||||||
|
|
||||||
//inbound
|
|
||||||
Inbounds4Ray inbound = new()
|
|
||||||
{
|
|
||||||
listen = Global.Loopback,
|
|
||||||
port = port,
|
|
||||||
protocol = EInboundProtocol.http.ToString(),
|
|
||||||
};
|
|
||||||
inbound.tag = inbound.protocol + inbound.port.ToString();
|
|
||||||
v2rayConfig.inbounds.Add(inbound);
|
|
||||||
|
|
||||||
//outbound
|
//outbound
|
||||||
if (item is null)
|
if (item is null)
|
||||||
{
|
{
|
||||||
@@ -326,6 +317,16 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//inbound
|
||||||
|
Inbounds4Ray inbound = new()
|
||||||
|
{
|
||||||
|
listen = Global.Loopback,
|
||||||
|
port = port,
|
||||||
|
protocol = EInboundProtocol.socks.ToString(),
|
||||||
|
};
|
||||||
|
inbound.tag = inbound.protocol + inbound.port.ToString();
|
||||||
|
v2rayConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||||
await GenOutbound(item, outbound);
|
await GenOutbound(item, outbound);
|
||||||
outbound.tag = Global.ProxyTag + inbound.port.ToString();
|
outbound.tag = Global.ProxyTag + inbound.port.ToString();
|
||||||
@@ -390,39 +391,27 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
var listen = "0.0.0.0";
|
var listen = "0.0.0.0";
|
||||||
v2rayConfig.inbounds = [];
|
v2rayConfig.inbounds = [];
|
||||||
|
|
||||||
Inbounds4Ray? inbound = GetInbound(_config.Inbound[0], EInboundProtocol.socks, true);
|
var inbound = GetInbound(_config.Inbound.First(), EInboundProtocol.socks, true);
|
||||||
v2rayConfig.inbounds.Add(inbound);
|
v2rayConfig.inbounds.Add(inbound);
|
||||||
|
|
||||||
//http
|
if (_config.Inbound.First().AllowLANConn)
|
||||||
Inbounds4Ray? inbound2 = GetInbound(_config.Inbound[0], EInboundProtocol.http, false);
|
|
||||||
v2rayConfig.inbounds.Add(inbound2);
|
|
||||||
|
|
||||||
if (_config.Inbound[0].AllowLANConn)
|
|
||||||
{
|
{
|
||||||
if (_config.Inbound[0].NewPort4LAN)
|
if (_config.Inbound.First().NewPort4LAN)
|
||||||
{
|
{
|
||||||
var inbound3 = GetInbound(_config.Inbound[0], EInboundProtocol.socks2, true);
|
var inbound3 = GetInbound(_config.Inbound.First(), EInboundProtocol.socks2, true);
|
||||||
inbound3.listen = listen;
|
inbound3.listen = listen;
|
||||||
v2rayConfig.inbounds.Add(inbound3);
|
v2rayConfig.inbounds.Add(inbound3);
|
||||||
|
|
||||||
var inbound4 = GetInbound(_config.Inbound[0], EInboundProtocol.http2, false);
|
|
||||||
inbound4.listen = listen;
|
|
||||||
v2rayConfig.inbounds.Add(inbound4);
|
|
||||||
|
|
||||||
//auth
|
//auth
|
||||||
if (Utils.IsNotEmpty(_config.Inbound[0].User) && Utils.IsNotEmpty(_config.Inbound[0].Pass))
|
if (Utils.IsNotEmpty(_config.Inbound.First().User) && Utils.IsNotEmpty(_config.Inbound.First().Pass))
|
||||||
{
|
{
|
||||||
inbound3.settings.auth = "password";
|
inbound3.settings.auth = "password";
|
||||||
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound[0].User, pass = _config.Inbound[0].Pass } };
|
inbound3.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound.First().User, pass = _config.Inbound.First().Pass } };
|
||||||
|
|
||||||
inbound4.settings.auth = "password";
|
|
||||||
inbound4.settings.accounts = new List<AccountsItem4Ray> { new AccountsItem4Ray() { user = _config.Inbound[0].User, pass = _config.Inbound[0].Pass } };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inbound.listen = listen;
|
inbound.listen = listen;
|
||||||
inbound2.listen = listen;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -448,7 +437,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
}
|
}
|
||||||
inbound.tag = protocol.ToString();
|
inbound.tag = protocol.ToString();
|
||||||
inbound.port = inItem.LocalPort + (int)protocol;
|
inbound.port = inItem.LocalPort + (int)protocol;
|
||||||
inbound.protocol = bSocks ? EInboundProtocol.socks.ToString() : EInboundProtocol.http.ToString();
|
inbound.protocol = EInboundProtocol.socks.ToString();
|
||||||
inbound.settings.udp = inItem.UdpEnabled;
|
inbound.settings.udp = inItem.UdpEnabled;
|
||||||
inbound.sniffing.enabled = inItem.SniffingEnabled;
|
inbound.sniffing.enabled = inItem.SniffingEnabled;
|
||||||
inbound.sniffing.destOverride = inItem.DestOverride;
|
inbound.sniffing.destOverride = inItem.DestOverride;
|
||||||
@@ -466,33 +455,17 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
v2rayConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy;
|
v2rayConfig.routing.domainStrategy = _config.RoutingBasicItem.DomainStrategy;
|
||||||
v2rayConfig.routing.domainMatcher = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainMatcher) ? null : _config.RoutingBasicItem.DomainMatcher;
|
v2rayConfig.routing.domainMatcher = Utils.IsNullOrEmpty(_config.RoutingBasicItem.DomainMatcher) ? null : _config.RoutingBasicItem.DomainMatcher;
|
||||||
|
|
||||||
if (_config.RoutingBasicItem.EnableRoutingAdvanced)
|
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||||
|
if (routing != null)
|
||||||
{
|
{
|
||||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
if (Utils.IsNotEmpty(routing.DomainStrategy))
|
||||||
if (routing != null)
|
|
||||||
{
|
{
|
||||||
if (Utils.IsNotEmpty(routing.DomainStrategy))
|
v2rayConfig.routing.domainStrategy = routing.DomainStrategy;
|
||||||
{
|
|
||||||
v2rayConfig.routing.domainStrategy = routing.DomainStrategy;
|
|
||||||
}
|
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
|
||||||
foreach (var item in rules)
|
|
||||||
{
|
|
||||||
if (item.Enabled)
|
|
||||||
{
|
|
||||||
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
|
|
||||||
await GenRoutingUserRule(item2, v2rayConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
var rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet);
|
||||||
else
|
foreach (var item in rules)
|
||||||
{
|
|
||||||
var lockedItem = await ConfigHandler.GetLockedRoutingItem(_config);
|
|
||||||
if (lockedItem != null)
|
|
||||||
{
|
{
|
||||||
var rules = JsonUtils.Deserialize<List<RulesItem>>(lockedItem.RuleSet);
|
if (item.Enabled)
|
||||||
foreach (var item in rules)
|
|
||||||
{
|
{
|
||||||
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
|
var item2 = JsonUtils.Deserialize<RulesItem4Ray>(JsonUtils.Serialize(item));
|
||||||
await GenRoutingUserRule(item2, v2rayConfig);
|
await GenRoutingUserRule(item2, v2rayConfig);
|
||||||
@@ -602,7 +575,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
vnextItem = outbound.settings.vnext[0];
|
vnextItem = outbound.settings.vnext.First();
|
||||||
}
|
}
|
||||||
vnextItem.address = node.Address;
|
vnextItem.address = node.Address;
|
||||||
vnextItem.port = node.Port;
|
vnextItem.port = node.Port;
|
||||||
@@ -615,7 +588,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
usersItem = vnextItem.users[0];
|
usersItem = vnextItem.users.First();
|
||||||
}
|
}
|
||||||
//远程服务器用户ID
|
//远程服务器用户ID
|
||||||
usersItem.id = node.Id;
|
usersItem.id = node.Id;
|
||||||
@@ -645,7 +618,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
serversItem = outbound.settings.servers[0];
|
serversItem = outbound.settings.servers.First();
|
||||||
}
|
}
|
||||||
serversItem.address = node.Address;
|
serversItem.address = node.Address;
|
||||||
serversItem.port = node.Port;
|
serversItem.port = node.Port;
|
||||||
@@ -671,7 +644,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
serversItem = outbound.settings.servers[0];
|
serversItem = outbound.settings.servers.First();
|
||||||
}
|
}
|
||||||
serversItem.address = node.Address;
|
serversItem.address = node.Address;
|
||||||
serversItem.port = node.Port;
|
serversItem.port = node.Port;
|
||||||
@@ -706,7 +679,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
vnextItem = outbound.settings.vnext[0];
|
vnextItem = outbound.settings.vnext.First();
|
||||||
}
|
}
|
||||||
vnextItem.address = node.Address;
|
vnextItem.address = node.Address;
|
||||||
vnextItem.port = node.Port;
|
vnextItem.port = node.Port;
|
||||||
@@ -719,7 +692,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
usersItem = vnextItem.users[0];
|
usersItem = vnextItem.users.First();
|
||||||
}
|
}
|
||||||
usersItem.id = node.Id;
|
usersItem.id = node.Id;
|
||||||
usersItem.email = Global.UserEMail;
|
usersItem.email = Global.UserEMail;
|
||||||
@@ -755,7 +728,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
serversItem = outbound.settings.servers[0];
|
serversItem = outbound.settings.servers.First();
|
||||||
}
|
}
|
||||||
serversItem.address = node.Address;
|
serversItem.address = node.Address;
|
||||||
serversItem.port = node.Port;
|
serversItem.port = node.Port;
|
||||||
@@ -926,8 +899,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
streamSettings.httpupgradeSettings = httpupgradeSettings;
|
streamSettings.httpupgradeSettings = httpupgradeSettings;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
//splithttp //xhttp
|
//xhttp
|
||||||
case nameof(ETransport.splithttp):
|
|
||||||
case nameof(ETransport.xhttp):
|
case nameof(ETransport.xhttp):
|
||||||
streamSettings.network = ETransport.xhttp.ToString();
|
streamSettings.network = ETransport.xhttp.ToString();
|
||||||
XhttpSettings4Ray xhttpSettings = new()
|
XhttpSettings4Ray xhttpSettings = new()
|
||||||
@@ -1183,7 +1155,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
{
|
{
|
||||||
//fragment proxy
|
//fragment proxy
|
||||||
if (_config.CoreBasicItem.EnableFragment
|
if (_config.CoreBasicItem.EnableFragment
|
||||||
&& Utils.IsNotEmpty(v2rayConfig.outbounds[0].streamSettings?.security))
|
&& Utils.IsNotEmpty(v2rayConfig.outbounds.First().streamSettings?.security))
|
||||||
{
|
{
|
||||||
var fragmentOutbound = new Outbounds4Ray
|
var fragmentOutbound = new Outbounds4Ray
|
||||||
{
|
{
|
||||||
@@ -1201,7 +1173,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
};
|
};
|
||||||
|
|
||||||
v2rayConfig.outbounds.Add(fragmentOutbound);
|
v2rayConfig.outbounds.Add(fragmentOutbound);
|
||||||
v2rayConfig.outbounds[0].streamSettings.sockopt = new()
|
v2rayConfig.outbounds.First().streamSettings.sockopt = new()
|
||||||
{
|
{
|
||||||
dialerProxy = fragmentOutbound.tag
|
dialerProxy = fragmentOutbound.tag
|
||||||
};
|
};
|
||||||
@@ -1221,7 +1193,7 @@ namespace ServiceLib.Services.CoreConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
//current proxy
|
//current proxy
|
||||||
var outbound = v2rayConfig.outbounds[0];
|
var outbound = v2rayConfig.outbounds.First();
|
||||||
var txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
|
var txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
|
||||||
|
|
||||||
//Previous proxy
|
//Previous proxy
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ namespace ServiceLib.Services
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.http);
|
var httpPort = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
|
||||||
if (await SocketCheck(Global.Loopback, httpPort) == false)
|
if (await SocketCheck(Global.Loopback, httpPort) == false)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -8,19 +8,16 @@ namespace ServiceLib.Services
|
|||||||
public class SpeedtestService
|
public class SpeedtestService
|
||||||
{
|
{
|
||||||
private Config? _config;
|
private Config? _config;
|
||||||
private List<ServerTestItem> _selecteds;
|
|
||||||
private ESpeedActionType _actionType;
|
|
||||||
private Action<SpeedTestResult>? _updateFunc;
|
private Action<SpeedTestResult>? _updateFunc;
|
||||||
|
|
||||||
private bool _exitLoop = false;
|
private bool _exitLoop = false;
|
||||||
|
|
||||||
public SpeedtestService(Config config, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<SpeedTestResult> updateFunc)
|
public SpeedtestService(Config config, List<ProfileItem> selecteds, ESpeedActionType actionType, Action<SpeedTestResult> updateFunc)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
|
|
||||||
_actionType = actionType;
|
|
||||||
_updateFunc = updateFunc;
|
_updateFunc = updateFunc;
|
||||||
|
|
||||||
_selecteds = new List<ServerTestItem>();
|
var lstSelected = new List<ServerTestItem>();
|
||||||
foreach (var it in selecteds)
|
foreach (var it in selecteds)
|
||||||
{
|
{
|
||||||
if (it.ConfigType == EConfigType.Custom)
|
if (it.ConfigType == EConfigType.Custom)
|
||||||
@@ -31,7 +28,7 @@ namespace ServiceLib.Services
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
_selecteds.Add(new ServerTestItem()
|
lstSelected.Add(new ServerTestItem()
|
||||||
{
|
{
|
||||||
IndexId = it.IndexId,
|
IndexId = it.IndexId,
|
||||||
Address = it.Address,
|
Address = it.Address,
|
||||||
@@ -39,8 +36,9 @@ namespace ServiceLib.Services
|
|||||||
ConfigType = it.ConfigType
|
ConfigType = it.ConfigType
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//clear test result
|
//clear test result
|
||||||
foreach (var it in _selecteds)
|
foreach (var it in lstSelected)
|
||||||
{
|
{
|
||||||
switch (actionType)
|
switch (actionType)
|
||||||
{
|
{
|
||||||
@@ -63,25 +61,59 @@ namespace ServiceLib.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (actionType)
|
|
||||||
{
|
|
||||||
case ESpeedActionType.Tcping:
|
|
||||||
Task.Run(RunTcping);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ESpeedActionType.Realping:
|
|
||||||
Task.Run(RunRealPing);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ESpeedActionType.Speedtest:
|
|
||||||
Task.Run(RunSpeedTestAsync);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ESpeedActionType.Mixedtest:
|
|
||||||
Task.Run(RunMixedtestAsync);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
MessageBus.Current.Listen<string>(EMsgCommand.StopSpeedtest.ToString()).Subscribe(ExitLoop);
|
MessageBus.Current.Listen<string>(EMsgCommand.StopSpeedtest.ToString()).Subscribe(ExitLoop);
|
||||||
|
|
||||||
|
Task.Run(async () => { await RunAsync(actionType, lstSelected); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RunAsync(ESpeedActionType actionType, List<ServerTestItem> lstSelected)
|
||||||
|
{
|
||||||
|
if (actionType == ESpeedActionType.Tcping)
|
||||||
|
{
|
||||||
|
await RunTcpingAsync(lstSelected);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pageSize = _config.SpeedTestItem.SpeedTestPageSize;
|
||||||
|
if (pageSize is <= 0 or > 1000)
|
||||||
|
{
|
||||||
|
pageSize = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<List<ServerTestItem>> lstTest = new();
|
||||||
|
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard)).ToList();
|
||||||
|
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard).ToList();
|
||||||
|
|
||||||
|
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
|
||||||
|
{
|
||||||
|
lstTest.Add(lst1.Skip(num * pageSize).Take(pageSize).ToList());
|
||||||
|
}
|
||||||
|
for (var num = 0; num < (int)Math.Ceiling(lst2.Count * 1.0 / pageSize); num++)
|
||||||
|
{
|
||||||
|
lstTest.Add(lst2.Skip(num * pageSize).Take(pageSize).ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var lst in lstTest)
|
||||||
|
{
|
||||||
|
switch (actionType)
|
||||||
|
{
|
||||||
|
case ESpeedActionType.Realping:
|
||||||
|
await RunRealPingAsync(lst);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESpeedActionType.Speedtest:
|
||||||
|
await RunSpeedTestAsync(lst);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ESpeedActionType.Mixedtest:
|
||||||
|
await RunMixedTestAsync(lst);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateFunc("", ResUI.SpeedtestingCompleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExitLoop(string x)
|
private void ExitLoop(string x)
|
||||||
@@ -91,12 +123,12 @@ namespace ServiceLib.Services
|
|||||||
UpdateFunc("", ResUI.SpeedtestingStop);
|
UpdateFunc("", ResUI.SpeedtestingStop);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunTcping()
|
private async Task RunTcpingAsync(List<ServerTestItem> selecteds)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
List<Task> tasks = [];
|
List<Task> tasks = [];
|
||||||
foreach (var it in _selecteds)
|
foreach (var it in selecteds)
|
||||||
{
|
{
|
||||||
if (it.ConfigType == EConfigType.Custom)
|
if (it.ConfigType == EConfigType.Custom)
|
||||||
{
|
{
|
||||||
@@ -106,7 +138,7 @@ namespace ServiceLib.Services
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int time = await GetTcpingTime(it.Address, it.Port);
|
var time = await GetTcpingTime(it.Address, it.Port);
|
||||||
var output = FormatOut(time, Global.DelayUnit);
|
var output = FormatOut(time, Global.DelayUnit);
|
||||||
|
|
||||||
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
|
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
|
||||||
@@ -130,24 +162,22 @@ namespace ServiceLib.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunRealPing()
|
private async Task RunRealPingAsync(List<ServerTestItem> selecteds)
|
||||||
{
|
{
|
||||||
int pid = -1;
|
var pid = -1;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string msg = string.Empty;
|
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
|
||||||
|
|
||||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds);
|
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
{
|
{
|
||||||
UpdateFunc("", ResUI.FailedToRunCore);
|
UpdateFunc("", ResUI.FailedToRunCore);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadService downloadHandle = new DownloadService();
|
var downloadHandle = new DownloadService();
|
||||||
|
|
||||||
List<Task> tasks = new();
|
List<Task> tasks = new();
|
||||||
foreach (var it in _selecteds)
|
foreach (var it in selecteds)
|
||||||
{
|
{
|
||||||
if (!it.AllowTest)
|
if (!it.AllowTest)
|
||||||
{
|
{
|
||||||
@@ -162,11 +192,11 @@ namespace ServiceLib.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
WebProxy webProxy = new(Global.Loopback, it.Port);
|
WebProxy webProxy = new(Global.Loopback, it.Port);
|
||||||
string output = await GetRealPingTime(downloadHandle, webProxy);
|
var output = await GetRealPingTime(downloadHandle, webProxy);
|
||||||
|
|
||||||
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
|
ProfileExHandler.Instance.SetTestDelay(it.IndexId, output);
|
||||||
UpdateFunc(it.IndexId, output);
|
UpdateFunc(it.IndexId, output);
|
||||||
int.TryParse(output, out int delay);
|
int.TryParse(output, out var delay);
|
||||||
it.Delay = delay;
|
it.Delay = delay;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -185,33 +215,28 @@ namespace ServiceLib.Services
|
|||||||
{
|
{
|
||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
{
|
{
|
||||||
CoreHandler.Instance.CoreStopPid(pid);
|
await CoreHandler.Instance.CoreStopPid(pid);
|
||||||
}
|
}
|
||||||
await ProfileExHandler.Instance.SaveTo();
|
await ProfileExHandler.Instance.SaveTo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunSpeedTestAsync()
|
private async Task RunSpeedTestAsync(List<ServerTestItem> selecteds)
|
||||||
{
|
{
|
||||||
int pid = -1;
|
var pid = -1;
|
||||||
//if (_actionType == ESpeedActionType.Mixedtest)
|
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
|
||||||
//{
|
|
||||||
// _selecteds = _selecteds.OrderBy(t => t.delay).ToList();
|
|
||||||
//}
|
|
||||||
|
|
||||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds);
|
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
{
|
{
|
||||||
UpdateFunc("", ResUI.FailedToRunCore);
|
UpdateFunc("", ResUI.FailedToRunCore);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string url = _config.SpeedTestItem.SpeedTestUrl;
|
var url = _config.SpeedTestItem.SpeedTestUrl;
|
||||||
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
|
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
|
||||||
|
|
||||||
DownloadService downloadHandle = new();
|
DownloadService downloadHandle = new();
|
||||||
|
|
||||||
foreach (var it in _selecteds)
|
foreach (var it in selecteds)
|
||||||
{
|
{
|
||||||
if (_exitLoop)
|
if (_exitLoop)
|
||||||
{
|
{
|
||||||
@@ -241,7 +266,7 @@ namespace ServiceLib.Services
|
|||||||
|
|
||||||
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
|
await downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
|
||||||
{
|
{
|
||||||
decimal.TryParse(msg, out decimal dec);
|
decimal.TryParse(msg, out var dec);
|
||||||
if (dec > 0)
|
if (dec > 0)
|
||||||
{
|
{
|
||||||
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
|
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
|
||||||
@@ -252,28 +277,27 @@ namespace ServiceLib.Services
|
|||||||
|
|
||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
{
|
{
|
||||||
CoreHandler.Instance.CoreStopPid(pid);
|
await CoreHandler.Instance.CoreStopPid(pid);
|
||||||
}
|
}
|
||||||
UpdateFunc("", ResUI.SpeedtestingCompleted);
|
|
||||||
await ProfileExHandler.Instance.SaveTo();
|
await ProfileExHandler.Instance.SaveTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunSpeedTestMulti()
|
private async Task RunSpeedTestMulti(List<ServerTestItem> selecteds)
|
||||||
{
|
{
|
||||||
int pid = -1;
|
var pid = -1;
|
||||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(_selecteds);
|
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(selecteds);
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
{
|
{
|
||||||
UpdateFunc("", ResUI.FailedToRunCore);
|
UpdateFunc("", ResUI.FailedToRunCore);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string url = _config.SpeedTestItem.SpeedTestUrl;
|
var url = _config.SpeedTestItem.SpeedTestUrl;
|
||||||
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
|
var timeout = _config.SpeedTestItem.SpeedTestTimeout;
|
||||||
|
|
||||||
DownloadService downloadHandle = new();
|
DownloadService downloadHandle = new();
|
||||||
|
|
||||||
foreach (var it in _selecteds)
|
foreach (var it in selecteds)
|
||||||
{
|
{
|
||||||
if (_exitLoop)
|
if (_exitLoop)
|
||||||
{
|
{
|
||||||
@@ -303,7 +327,7 @@ namespace ServiceLib.Services
|
|||||||
WebProxy webProxy = new(Global.Loopback, it.Port);
|
WebProxy webProxy = new(Global.Loopback, it.Port);
|
||||||
_ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
|
_ = downloadHandle.DownloadDataAsync(url, webProxy, timeout, (success, msg) =>
|
||||||
{
|
{
|
||||||
decimal.TryParse(msg, out decimal dec);
|
decimal.TryParse(msg, out var dec);
|
||||||
if (dec > 0)
|
if (dec > 0)
|
||||||
{
|
{
|
||||||
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
|
ProfileExHandler.Instance.SetTestSpeed(it.IndexId, msg);
|
||||||
@@ -317,38 +341,37 @@ namespace ServiceLib.Services
|
|||||||
|
|
||||||
if (pid > 0)
|
if (pid > 0)
|
||||||
{
|
{
|
||||||
CoreHandler.Instance.CoreStopPid(pid);
|
await CoreHandler.Instance.CoreStopPid(pid);
|
||||||
}
|
}
|
||||||
UpdateFunc("", ResUI.SpeedtestingCompleted);
|
|
||||||
await ProfileExHandler.Instance.SaveTo();
|
await ProfileExHandler.Instance.SaveTo();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunMixedtestAsync()
|
private async Task RunMixedTestAsync(List<ServerTestItem> selecteds)
|
||||||
{
|
{
|
||||||
await RunRealPing();
|
await RunRealPingAsync(selecteds);
|
||||||
|
|
||||||
await Task.Delay(1000);
|
await Task.Delay(1000);
|
||||||
|
|
||||||
await RunSpeedTestMulti();
|
await RunSpeedTestMulti(selecteds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<string> GetRealPingTime(DownloadService downloadHandle, IWebProxy webProxy)
|
private async Task<string> GetRealPingTime(DownloadService downloadHandle, IWebProxy webProxy)
|
||||||
{
|
{
|
||||||
int responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
|
var responseTime = await downloadHandle.GetRealPingTime(_config.SpeedTestItem.SpeedPingTestUrl, webProxy, 10);
|
||||||
//string output = Utile.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status;
|
//string output = Utile.IsNullOrEmpty(status) ? FormatOut(responseTime, "ms") : status;
|
||||||
return FormatOut(responseTime, Global.DelayUnit);
|
return FormatOut(responseTime, Global.DelayUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<int> GetTcpingTime(string url, int port)
|
private async Task<int> GetTcpingTime(string url, int port)
|
||||||
{
|
{
|
||||||
int responseTime = -1;
|
var responseTime = -1;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!IPAddress.TryParse(url, out IPAddress? ipAddress))
|
if (!IPAddress.TryParse(url, out var ipAddress))
|
||||||
{
|
{
|
||||||
IPHostEntry ipHostInfo = Dns.GetHostEntry(url);
|
var ipHostInfo = await Dns.GetHostEntryAsync(url);
|
||||||
ipAddress = ipHostInfo.AddressList[0];
|
ipAddress = ipHostInfo.AddressList.First();
|
||||||
}
|
}
|
||||||
|
|
||||||
var timer = Stopwatch.StartNew();
|
var timer = Stopwatch.StartNew();
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ namespace ServiceLib.Services.Statistics
|
|||||||
private Config _config;
|
private Config _config;
|
||||||
private bool _exitFlag;
|
private bool _exitFlag;
|
||||||
private ClientWebSocket? webSocket;
|
private ClientWebSocket? webSocket;
|
||||||
private string url = string.Empty;
|
|
||||||
private Action<ServerSpeedItem>? _updateFunc;
|
private Action<ServerSpeedItem>? _updateFunc;
|
||||||
|
private string Url => $"ws://{Global.Loopback}:{AppHandler.Instance.StatePort2}/traffic";
|
||||||
|
|
||||||
public StatisticsSingboxService(Config config, Action<ServerSpeedItem> updateFunc)
|
public StatisticsSingboxService(Config config, Action<ServerSpeedItem> updateFunc)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
@@ -26,12 +26,10 @@ namespace ServiceLib.Services.Statistics
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
url = $"ws://{Global.Loopback}:{AppHandler.Instance.StatePort2}/traffic";
|
|
||||||
|
|
||||||
if (webSocket == null)
|
if (webSocket == null)
|
||||||
{
|
{
|
||||||
webSocket = new ClientWebSocket();
|
webSocket = new ClientWebSocket();
|
||||||
await webSocket.ConnectAsync(new Uri(url), CancellationToken.None);
|
await webSocket.ConnectAsync(new Uri(Url), CancellationToken.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
using Grpc.Core;
|
|
||||||
using Grpc.Net.Client;
|
|
||||||
using ProtosLib.Statistics;
|
|
||||||
|
|
||||||
namespace ServiceLib.Services.Statistics
|
|
||||||
{
|
|
||||||
public class StatisticsV2rayService
|
|
||||||
{
|
|
||||||
private Models.Config _config;
|
|
||||||
private GrpcChannel? _channel;
|
|
||||||
private StatsService.StatsServiceClient? _client;
|
|
||||||
private bool _exitFlag;
|
|
||||||
private Action<ServerSpeedItem>? _updateFunc;
|
|
||||||
|
|
||||||
public StatisticsV2rayService(Models.Config config, Action<ServerSpeedItem> updateFunc)
|
|
||||||
{
|
|
||||||
_config = config;
|
|
||||||
_updateFunc = updateFunc;
|
|
||||||
_exitFlag = false;
|
|
||||||
|
|
||||||
GrpcInit();
|
|
||||||
|
|
||||||
Task.Run(Run);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GrpcInit()
|
|
||||||
{
|
|
||||||
if (_channel is null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_channel = GrpcChannel.ForAddress($"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}");
|
|
||||||
_client = new StatsService.StatsServiceClient(_channel);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logging.SaveLog(ex.Message, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
_exitFlag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void Run()
|
|
||||||
{
|
|
||||||
while (!_exitFlag)
|
|
||||||
{
|
|
||||||
await Task.Delay(1000);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!_config.IsRunningCore(ECoreType.Xray))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (_channel?.State == ConnectivityState.Ready)
|
|
||||||
{
|
|
||||||
QueryStatsResponse? res = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_client != null)
|
|
||||||
res = await _client.QueryStatsAsync(new QueryStatsRequest() { Pattern = "", Reset = true });
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res != null)
|
|
||||||
{
|
|
||||||
ParseOutput(res.Stat, out ServerSpeedItem server);
|
|
||||||
_updateFunc?.Invoke(server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_channel != null)
|
|
||||||
await _channel.ConnectAsync();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ParseOutput(Google.Protobuf.Collections.RepeatedField<Stat> source, out ServerSpeedItem server)
|
|
||||||
{
|
|
||||||
server = new();
|
|
||||||
long aggregateProxyUp = 0;
|
|
||||||
long aggregateProxyDown = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (Stat stat in source)
|
|
||||||
{
|
|
||||||
string name = stat.Name;
|
|
||||||
long value = stat.Value / 1024; //KByte
|
|
||||||
string[] nStr = name.Split(">>>".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
string type = "";
|
|
||||||
|
|
||||||
name = name.Trim();
|
|
||||||
|
|
||||||
name = nStr[1];
|
|
||||||
type = nStr[3];
|
|
||||||
|
|
||||||
if (name.StartsWith(Global.ProxyTag))
|
|
||||||
{
|
|
||||||
if (type == "uplink")
|
|
||||||
{
|
|
||||||
aggregateProxyUp += value;
|
|
||||||
}
|
|
||||||
else if (type == "downlink")
|
|
||||||
{
|
|
||||||
aggregateProxyDown += value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (name == Global.DirectTag)
|
|
||||||
{
|
|
||||||
if (type == "uplink")
|
|
||||||
{
|
|
||||||
server.DirectUp = value;
|
|
||||||
}
|
|
||||||
else if (type == "downlink")
|
|
||||||
{
|
|
||||||
server.DirectDown = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
server.ProxyUp = aggregateProxyUp;
|
|
||||||
server.ProxyDown = aggregateProxyDown;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,18 +3,17 @@
|
|||||||
public class StatisticsXrayService
|
public class StatisticsXrayService
|
||||||
{
|
{
|
||||||
private const long linkBase = 1024;
|
private const long linkBase = 1024;
|
||||||
private string _url;
|
|
||||||
private ServerSpeedItem _serverSpeedItem = new();
|
private ServerSpeedItem _serverSpeedItem = new();
|
||||||
private Config _config;
|
private Config _config;
|
||||||
private bool _exitFlag;
|
private bool _exitFlag;
|
||||||
private Action<ServerSpeedItem>? _updateFunc;
|
private Action<ServerSpeedItem>? _updateFunc;
|
||||||
|
private string Url => $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars";
|
||||||
|
|
||||||
public StatisticsXrayService(Config config, Action<ServerSpeedItem> updateFunc)
|
public StatisticsXrayService(Config config, Action<ServerSpeedItem> updateFunc)
|
||||||
{
|
{
|
||||||
_config = config;
|
_config = config;
|
||||||
_updateFunc = updateFunc;
|
_updateFunc = updateFunc;
|
||||||
_exitFlag = false;
|
_exitFlag = false;
|
||||||
_url = $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort}/debug/vars";
|
|
||||||
|
|
||||||
Task.Run(Run);
|
Task.Run(Run);
|
||||||
}
|
}
|
||||||
@@ -36,7 +35,7 @@
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = await HttpClientHelper.Instance.TryGetAsync(_url);
|
var result = await HttpClientHelper.Instance.TryGetAsync(Url);
|
||||||
if (result != null)
|
if (result != null)
|
||||||
{
|
{
|
||||||
var server = ParseOutput(result) ?? new ServerSpeedItem();
|
var server = ParseOutput(result) ?? new ServerSpeedItem();
|
||||||
|
|||||||
@@ -244,8 +244,17 @@ namespace ServiceLib.Services
|
|||||||
|
|
||||||
public async Task RunAvailabilityCheck(Action<bool, string> updateFunc)
|
public async Task RunAvailabilityCheck(Action<bool, string> updateFunc)
|
||||||
{
|
{
|
||||||
var time = await new DownloadService().RunAvailabilityCheck(null);
|
var downloadHandle = new DownloadService();
|
||||||
updateFunc?.Invoke(false, string.Format(ResUI.TestMeOutput, time));
|
var time = await downloadHandle.RunAvailabilityCheck(null);
|
||||||
|
var ip = Global.None;
|
||||||
|
if (time > 0)
|
||||||
|
{
|
||||||
|
var result = await downloadHandle.TryDownloadString(Global.IPAPIUrl, true, Global.IPAPIUrl);
|
||||||
|
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
|
||||||
|
ip = $"({ipInfo?.country_code}) {ipInfo?.ip}";
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFunc?.Invoke(false, string.Format(ResUI.TestMeOutput, time, ip));
|
||||||
}
|
}
|
||||||
|
|
||||||
#region CheckUpdate private
|
#region CheckUpdate private
|
||||||
@@ -441,6 +450,15 @@ namespace ServiceLib.Services
|
|||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else if (Utils.IsOSX())
|
||||||
|
{
|
||||||
|
return RuntimeInformation.ProcessArchitecture switch
|
||||||
|
{
|
||||||
|
Architecture.Arm64 => coreInfo?.DownloadUrlOSXArm64,
|
||||||
|
Architecture.X64 => coreInfo?.DownloadUrlOSX64,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ namespace ServiceLib.ViewModels
|
|||||||
FileManager.ZipExtractToFile(fileName, toPath, _config.GuiItem.IgnoreGeoUpdateCore ? "geo" : "");
|
FileManager.ZipExtractToFile(fileName, toPath, _config.GuiItem.IgnoreGeoUpdateCore ? "geo" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Utils.IsLinux())
|
if (Utils.IsLinux() || Utils.IsOSX())
|
||||||
{
|
{
|
||||||
var filesList = (new DirectoryInfo(toPath)).GetFiles().Select(u => u.FullName).ToList();
|
var filesList = (new DirectoryInfo(toPath)).GetFiles().Select(u => u.FullName).ToList();
|
||||||
foreach (var file in filesList)
|
foreach (var file in filesList)
|
||||||
|
|||||||
@@ -21,9 +21,6 @@ namespace ServiceLib.ViewModels
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public string HostFilter { get; set; }
|
public string HostFilter { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public int SortingSelected { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public bool AutoRefresh { get; set; }
|
public bool AutoRefresh { get; set; }
|
||||||
|
|
||||||
@@ -31,18 +28,12 @@ namespace ServiceLib.ViewModels
|
|||||||
{
|
{
|
||||||
_config = AppHandler.Instance.Config;
|
_config = AppHandler.Instance.Config;
|
||||||
_updateView = updateView;
|
_updateView = updateView;
|
||||||
SortingSelected = _config.ClashUIItem.ConnectionsSorting;
|
|
||||||
AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh;
|
AutoRefresh = _config.ClashUIItem.ConnectionsAutoRefresh;
|
||||||
|
|
||||||
var canEditRemove = this.WhenAnyValue(
|
var canEditRemove = this.WhenAnyValue(
|
||||||
x => x.SelectedSource,
|
x => x.SelectedSource,
|
||||||
selectedSource => selectedSource != null && Utils.IsNotEmpty(selectedSource.Id));
|
selectedSource => selectedSource != null && Utils.IsNotEmpty(selectedSource.Id));
|
||||||
|
|
||||||
this.WhenAnyValue(
|
|
||||||
x => x.SortingSelected,
|
|
||||||
y => y >= 0)
|
|
||||||
.Subscribe(async c => await DoSortingSelected(c));
|
|
||||||
|
|
||||||
this.WhenAnyValue(
|
this.WhenAnyValue(
|
||||||
x => x.AutoRefresh,
|
x => x.AutoRefresh,
|
||||||
y => y == true)
|
y => y == true)
|
||||||
@@ -84,20 +75,6 @@ namespace ServiceLib.ViewModels
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task DoSortingSelected(bool c)
|
|
||||||
{
|
|
||||||
if (!c)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (SortingSelected != _config.ClashUIItem.ConnectionsSorting)
|
|
||||||
{
|
|
||||||
_config.ClashUIItem.ConnectionsSorting = SortingSelected;
|
|
||||||
}
|
|
||||||
|
|
||||||
await GetClashConnections();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task GetClashConnections()
|
private async Task GetClashConnections()
|
||||||
{
|
{
|
||||||
var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync(_config);
|
var ret = await ClashApiHandler.Instance.GetClashConnectionsAsync(_config);
|
||||||
@@ -115,7 +92,7 @@ namespace ServiceLib.ViewModels
|
|||||||
|
|
||||||
var dtNow = DateTime.Now;
|
var dtNow = DateTime.Now;
|
||||||
var lstModel = new List<ClashConnectionModel>();
|
var lstModel = new List<ClashConnectionModel>();
|
||||||
foreach (var item in connections ?? [])
|
foreach (var item in connections ?? new())
|
||||||
{
|
{
|
||||||
var host = $"{(Utils.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
|
var host = $"{(Utils.IsNullOrEmpty(item.metadata.host) ? item.metadata.destinationIP : item.metadata.host)}:{item.metadata.destinationPort}";
|
||||||
if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter))
|
if (HostFilter.IsNotEmpty() && !host.Contains(HostFilter))
|
||||||
@@ -131,45 +108,14 @@ namespace ServiceLib.ViewModels
|
|||||||
model.Host = host;
|
model.Host = host;
|
||||||
var sp = (dtNow - item.start);
|
var sp = (dtNow - item.start);
|
||||||
model.Time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds;
|
model.Time = sp.TotalSeconds < 0 ? 1 : sp.TotalSeconds;
|
||||||
model.Upload = item.upload;
|
|
||||||
model.Download = item.download;
|
|
||||||
model.UploadTraffic = $"{Utils.HumanFy((long)item.upload)}";
|
|
||||||
model.DownloadTraffic = $"{Utils.HumanFy((long)item.download)}";
|
|
||||||
model.Elapsed = sp.ToString(@"hh\:mm\:ss");
|
model.Elapsed = sp.ToString(@"hh\:mm\:ss");
|
||||||
model.Chain = item.chains?.Count > 0 ? item.chains[0] : string.Empty;
|
item.chains?.Reverse();
|
||||||
|
model.Chain = $"{item.rule} , {string.Join("->", item.chains ?? new())}";
|
||||||
|
|
||||||
lstModel.Add(model);
|
lstModel.Add(model);
|
||||||
}
|
}
|
||||||
if (lstModel.Count <= 0) { return; }
|
if (lstModel.Count <= 0) { return; }
|
||||||
|
|
||||||
//sort
|
|
||||||
switch (SortingSelected)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
lstModel = lstModel.OrderBy(t => t.Upload / t.Time).ToList();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
lstModel = lstModel.OrderBy(t => t.Download / t.Time).ToList();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
lstModel = lstModel.OrderBy(t => t.Upload).ToList();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
lstModel = lstModel.OrderBy(t => t.Download).ToList();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
lstModel = lstModel.OrderBy(t => t.Time).ToList();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
|
||||||
lstModel = lstModel.OrderBy(t => t.Host).ToList();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_connectionItems.AddRange(lstModel);
|
_connectionItems.AddRange(lstModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ namespace ServiceLib.ViewModels
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SelectedGroup = _proxyGroups[0];
|
SelectedGroup = _proxyGroups.First();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -283,7 +283,6 @@ namespace ServiceLib.ViewModels
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logging.SaveLog("MyAppExit Begin");
|
Logging.SaveLog("MyAppExit Begin");
|
||||||
//if (blWindowsShutDown)
|
|
||||||
await SysProxyHandler.UpdateSysProxy(_config, true);
|
await SysProxyHandler.UpdateSysProxy(_config, true);
|
||||||
|
|
||||||
await ConfigHandler.SaveConfig(_config);
|
await ConfigHandler.SaveConfig(_config);
|
||||||
@@ -297,7 +296,10 @@ namespace ServiceLib.ViewModels
|
|||||||
catch { }
|
catch { }
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_updateView?.Invoke(EViewAction.Shutdown, null);
|
if (!blWindowsShutDown)
|
||||||
|
{
|
||||||
|
_updateView?.Invoke(EViewAction.Shutdown, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,6 +326,7 @@ namespace ServiceLib.ViewModels
|
|||||||
if (process.Id > 0)
|
if (process.Id > 0)
|
||||||
{
|
{
|
||||||
await MyAppExitAsync(false);
|
await MyAppExitAsync(false);
|
||||||
|
await MyAppExitAsync(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ namespace ServiceLib.ViewModels
|
|||||||
[Reactive] public int SpeedTestTimeout { get; set; }
|
[Reactive] public int SpeedTestTimeout { get; set; }
|
||||||
[Reactive] public string SpeedTestUrl { get; set; }
|
[Reactive] public string SpeedTestUrl { get; set; }
|
||||||
[Reactive] public string SpeedPingTestUrl { get; set; }
|
[Reactive] public string SpeedPingTestUrl { get; set; }
|
||||||
|
[Reactive] public int SpeedTestPageSize { get; set; }
|
||||||
[Reactive] public bool EnableHWA { get; set; }
|
[Reactive] public bool EnableHWA { get; set; }
|
||||||
[Reactive] public string SubConvertUrl { get; set; }
|
[Reactive] public string SubConvertUrl { get; set; }
|
||||||
[Reactive] public int MainGirdOrientation { get; set; }
|
[Reactive] public int MainGirdOrientation { get; set; }
|
||||||
@@ -122,7 +123,7 @@ namespace ServiceLib.ViewModels
|
|||||||
|
|
||||||
#region Core
|
#region Core
|
||||||
|
|
||||||
var inbound = _config.Inbound[0];
|
var inbound = _config.Inbound.First();
|
||||||
localPort = inbound.LocalPort;
|
localPort = inbound.LocalPort;
|
||||||
udpEnabled = inbound.UdpEnabled;
|
udpEnabled = inbound.UdpEnabled;
|
||||||
sniffingEnabled = inbound.SniffingEnabled;
|
sniffingEnabled = inbound.SniffingEnabled;
|
||||||
@@ -175,6 +176,7 @@ namespace ServiceLib.ViewModels
|
|||||||
CurrentFontFamily = _config.UiItem.CurrentFontFamily;
|
CurrentFontFamily = _config.UiItem.CurrentFontFamily;
|
||||||
SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout;
|
SpeedTestTimeout = _config.SpeedTestItem.SpeedTestTimeout;
|
||||||
SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl;
|
SpeedTestUrl = _config.SpeedTestItem.SpeedTestUrl;
|
||||||
|
SpeedTestPageSize = _config.SpeedTestItem.SpeedTestPageSize;
|
||||||
SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl;
|
SpeedPingTestUrl = _config.SpeedTestItem.SpeedPingTestUrl;
|
||||||
EnableHWA = _config.GuiItem.EnableHWA;
|
EnableHWA = _config.GuiItem.EnableHWA;
|
||||||
SubConvertUrl = _config.ConstItem.SubConvertUrl;
|
SubConvertUrl = _config.ConstItem.SubConvertUrl;
|
||||||
@@ -285,15 +287,15 @@ namespace ServiceLib.ViewModels
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
//Core
|
//Core
|
||||||
_config.Inbound[0].LocalPort = localPort;
|
_config.Inbound.First().LocalPort = localPort;
|
||||||
_config.Inbound[0].UdpEnabled = udpEnabled;
|
_config.Inbound.First().UdpEnabled = udpEnabled;
|
||||||
_config.Inbound[0].SniffingEnabled = sniffingEnabled;
|
_config.Inbound.First().SniffingEnabled = sniffingEnabled;
|
||||||
_config.Inbound[0].DestOverride = destOverride?.ToList();
|
_config.Inbound.First().DestOverride = destOverride?.ToList();
|
||||||
_config.Inbound[0].RouteOnly = routeOnly;
|
_config.Inbound.First().RouteOnly = routeOnly;
|
||||||
_config.Inbound[0].AllowLANConn = allowLANConn;
|
_config.Inbound.First().AllowLANConn = allowLANConn;
|
||||||
_config.Inbound[0].NewPort4LAN = newPort4LAN;
|
_config.Inbound.First().NewPort4LAN = newPort4LAN;
|
||||||
_config.Inbound[0].User = user;
|
_config.Inbound.First().User = user;
|
||||||
_config.Inbound[0].Pass = pass;
|
_config.Inbound.First().Pass = pass;
|
||||||
if (_config.Inbound.Count > 1)
|
if (_config.Inbound.Count > 1)
|
||||||
{
|
{
|
||||||
_config.Inbound.RemoveAt(1);
|
_config.Inbound.RemoveAt(1);
|
||||||
@@ -325,6 +327,7 @@ namespace ServiceLib.ViewModels
|
|||||||
_config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit;
|
_config.GuiItem.TrayMenuServersLimit = TrayMenuServersLimit;
|
||||||
_config.UiItem.CurrentFontFamily = CurrentFontFamily;
|
_config.UiItem.CurrentFontFamily = CurrentFontFamily;
|
||||||
_config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout;
|
_config.SpeedTestItem.SpeedTestTimeout = SpeedTestTimeout;
|
||||||
|
_config.SpeedTestItem.SpeedTestPageSize = SpeedTestPageSize;
|
||||||
_config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl;
|
_config.SpeedTestItem.SpeedTestUrl = SpeedTestUrl;
|
||||||
_config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl;
|
_config.SpeedTestItem.SpeedPingTestUrl = SpeedPingTestUrl;
|
||||||
_config.GuiItem.EnableHWA = EnableHWA;
|
_config.GuiItem.EnableHWA = EnableHWA;
|
||||||
@@ -356,6 +359,7 @@ namespace ServiceLib.ViewModels
|
|||||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||||
{
|
{
|
||||||
await AutoStartupHandler.UpdateTask(_config);
|
await AutoStartupHandler.UpdateTask(_config);
|
||||||
|
AppHandler.Instance.Reset();
|
||||||
|
|
||||||
NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
|
NoticeHandler.Instance.Enqueue(needReboot ? ResUI.NeedRebootTips : ResUI.OperationSuccess);
|
||||||
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
_updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||||
|
|||||||
@@ -374,7 +374,7 @@ namespace ServiceLib.ViewModels
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SelectedProfile = lstModel[0];
|
SelectedProfile = lstModel.First();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,7 +395,7 @@ namespace ServiceLib.ViewModels
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SelectedSub = _subItems[0];
|
SelectedSub = _subItems.First();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -639,6 +639,7 @@ namespace ServiceLib.ViewModels
|
|||||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
|
|
||||||
RefreshServers();
|
RefreshServers();
|
||||||
|
SelectedMoveToGroup = null;
|
||||||
SelectedMoveToGroup = new();
|
SelectedMoveToGroup = new();
|
||||||
//Reload();
|
//Reload();
|
||||||
}
|
}
|
||||||
@@ -729,7 +730,7 @@ namespace ServiceLib.ViewModels
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var result = await CoreConfigHandler.GenerateClientConfig(item, null);
|
var result = await CoreConfigHandler.GenerateClientConfig(item, fileName);
|
||||||
if (result.Success != true)
|
if (result.Success != true)
|
||||||
{
|
{
|
||||||
NoticeHandler.Instance.Enqueue(result.Msg);
|
NoticeHandler.Instance.Enqueue(result.Msg);
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ namespace ServiceLib.ViewModels
|
|||||||
{
|
{
|
||||||
public class RoutingSettingViewModel : MyReactiveObject
|
public class RoutingSettingViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
private RoutingItem _lockedItem;
|
|
||||||
private List<RulesItem> _lockedRules;
|
|
||||||
|
|
||||||
#region Reactive
|
#region Reactive
|
||||||
|
|
||||||
private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>();
|
private IObservableCollection<RoutingItemModel> _routingItems = new ObservableCollectionExtended<RoutingItemModel>();
|
||||||
@@ -20,12 +17,6 @@ namespace ServiceLib.ViewModels
|
|||||||
|
|
||||||
public IList<RoutingItemModel> SelectedSources { get; set; }
|
public IList<RoutingItemModel> SelectedSources { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public bool enableRoutingAdvanced { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public bool enableRoutingBasic { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
public string domainStrategy { get; set; }
|
public string domainStrategy { get; set; }
|
||||||
|
|
||||||
@@ -35,25 +26,6 @@ namespace ServiceLib.ViewModels
|
|||||||
[Reactive]
|
[Reactive]
|
||||||
public string domainStrategy4Singbox { get; set; }
|
public string domainStrategy4Singbox { get; set; }
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public string ProxyDomain { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public string ProxyIP { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public string DirectDomain { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public string DirectIP { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public string BlockDomain { get; set; }
|
|
||||||
|
|
||||||
[Reactive]
|
|
||||||
public string BlockIP { get; set; }
|
|
||||||
|
|
||||||
public ReactiveCommand<Unit, Unit> RoutingBasicImportRulesCmd { get; }
|
|
||||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedAddCmd { get; }
|
public ReactiveCommand<Unit, Unit> RoutingAdvancedAddCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedRemoveCmd { get; }
|
public ReactiveCommand<Unit, Unit> RoutingAdvancedRemoveCmd { get; }
|
||||||
public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; }
|
public ReactiveCommand<Unit, Unit> RoutingAdvancedSetDefaultCmd { get; }
|
||||||
@@ -73,15 +45,6 @@ namespace ServiceLib.ViewModels
|
|||||||
x => x.SelectedSource,
|
x => x.SelectedSource,
|
||||||
selectedSource => selectedSource != null && !selectedSource.Remarks.IsNullOrEmpty());
|
selectedSource => selectedSource != null && !selectedSource.Remarks.IsNullOrEmpty());
|
||||||
|
|
||||||
this.WhenAnyValue(
|
|
||||||
x => x.enableRoutingAdvanced)
|
|
||||||
.Subscribe(c => enableRoutingBasic = !enableRoutingAdvanced);
|
|
||||||
|
|
||||||
RoutingBasicImportRulesCmd = ReactiveCommand.CreateFromTask(async () =>
|
|
||||||
{
|
|
||||||
await RoutingBasicImportRules();
|
|
||||||
});
|
|
||||||
|
|
||||||
RoutingAdvancedAddCmd = ReactiveCommand.CreateFromTask(async () =>
|
RoutingAdvancedAddCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||||
{
|
{
|
||||||
await RoutingAdvancedEditAsync(true);
|
await RoutingAdvancedEditAsync(true);
|
||||||
@@ -111,67 +74,14 @@ namespace ServiceLib.ViewModels
|
|||||||
{
|
{
|
||||||
SelectedSource = new();
|
SelectedSource = new();
|
||||||
|
|
||||||
enableRoutingAdvanced = true;//TODO _config.RoutingBasicItem.EnableRoutingAdvanced;
|
|
||||||
domainStrategy = _config.RoutingBasicItem.DomainStrategy;
|
domainStrategy = _config.RoutingBasicItem.DomainStrategy;
|
||||||
domainMatcher = _config.RoutingBasicItem.DomainMatcher;
|
domainMatcher = _config.RoutingBasicItem.DomainMatcher;
|
||||||
domainStrategy4Singbox = _config.RoutingBasicItem.DomainStrategy4Singbox;
|
domainStrategy4Singbox = _config.RoutingBasicItem.DomainStrategy4Singbox;
|
||||||
|
|
||||||
await ConfigHandler.InitBuiltinRouting(_config);
|
await ConfigHandler.InitBuiltinRouting(_config);
|
||||||
await RefreshRoutingItems();
|
await RefreshRoutingItems();
|
||||||
await BindingLockedData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region locked
|
|
||||||
|
|
||||||
private async Task BindingLockedData()
|
|
||||||
{
|
|
||||||
_lockedItem = await ConfigHandler.GetLockedRoutingItem(_config);
|
|
||||||
if (_lockedItem == null)
|
|
||||||
{
|
|
||||||
_lockedItem = new RoutingItem()
|
|
||||||
{
|
|
||||||
Remarks = "locked",
|
|
||||||
Url = string.Empty,
|
|
||||||
Locked = true,
|
|
||||||
};
|
|
||||||
await ConfigHandler.AddBatchRoutingRules(_lockedItem, Utils.GetEmbedText(Global.CustomRoutingFileName + "locked"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_lockedItem != null)
|
|
||||||
{
|
|
||||||
_lockedRules = JsonUtils.Deserialize<List<RulesItem>>(_lockedItem.RuleSet);
|
|
||||||
ProxyDomain = Utils.List2String(_lockedRules[0].Domain, true);
|
|
||||||
ProxyIP = Utils.List2String(_lockedRules[0].Ip, true);
|
|
||||||
|
|
||||||
DirectDomain = Utils.List2String(_lockedRules[1].Domain, true);
|
|
||||||
DirectIP = Utils.List2String(_lockedRules[1].Ip, true);
|
|
||||||
|
|
||||||
BlockDomain = Utils.List2String(_lockedRules[2].Domain, true);
|
|
||||||
BlockIP = Utils.List2String(_lockedRules[2].Ip, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task EndBindingLockedData()
|
|
||||||
{
|
|
||||||
if (_lockedItem != null)
|
|
||||||
{
|
|
||||||
_lockedRules[0].Domain = Utils.String2List(Utils.Convert2Comma(ProxyDomain.TrimEx()));
|
|
||||||
_lockedRules[0].Ip = Utils.String2List(Utils.Convert2Comma(ProxyIP.TrimEx()));
|
|
||||||
|
|
||||||
_lockedRules[1].Domain = Utils.String2List(Utils.Convert2Comma(DirectDomain.TrimEx()));
|
|
||||||
_lockedRules[1].Ip = Utils.String2List(Utils.Convert2Comma(DirectIP.TrimEx()));
|
|
||||||
|
|
||||||
_lockedRules[2].Domain = Utils.String2List(Utils.Convert2Comma(BlockDomain.TrimEx()));
|
|
||||||
_lockedRules[2].Ip = Utils.String2List(Utils.Convert2Comma(BlockIP.TrimEx()));
|
|
||||||
|
|
||||||
_lockedItem.RuleSet = JsonUtils.Serialize(_lockedRules, false);
|
|
||||||
|
|
||||||
await ConfigHandler.SaveRoutingItem(_config, _lockedItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion locked
|
|
||||||
|
|
||||||
#region Refresh Save
|
#region Refresh Save
|
||||||
|
|
||||||
public async Task RefreshRoutingItems()
|
public async Task RefreshRoutingItems()
|
||||||
@@ -205,12 +115,9 @@ namespace ServiceLib.ViewModels
|
|||||||
private async Task SaveRoutingAsync()
|
private async Task SaveRoutingAsync()
|
||||||
{
|
{
|
||||||
_config.RoutingBasicItem.DomainStrategy = domainStrategy;
|
_config.RoutingBasicItem.DomainStrategy = domainStrategy;
|
||||||
_config.RoutingBasicItem.EnableRoutingAdvanced = enableRoutingAdvanced;
|
|
||||||
_config.RoutingBasicItem.DomainMatcher = domainMatcher;
|
_config.RoutingBasicItem.DomainMatcher = domainMatcher;
|
||||||
_config.RoutingBasicItem.DomainStrategy4Singbox = domainStrategy4Singbox;
|
_config.RoutingBasicItem.DomainStrategy4Singbox = domainStrategy4Singbox;
|
||||||
|
|
||||||
await EndBindingLockedData();
|
|
||||||
|
|
||||||
if (await ConfigHandler.SaveConfig(_config) == 0)
|
if (await ConfigHandler.SaveConfig(_config) == 0)
|
||||||
{
|
{
|
||||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||||
@@ -224,18 +131,6 @@ namespace ServiceLib.ViewModels
|
|||||||
|
|
||||||
#endregion Refresh Save
|
#endregion Refresh Save
|
||||||
|
|
||||||
private async Task RoutingBasicImportRules()
|
|
||||||
{
|
|
||||||
//Extra to bypass the mainland
|
|
||||||
ProxyDomain = "geosite:google";
|
|
||||||
DirectDomain = "geosite:cn";
|
|
||||||
DirectIP = "geoip:private,geoip:cn";
|
|
||||||
BlockDomain = "geosite:category-ads-all";
|
|
||||||
|
|
||||||
//NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
|
||||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RoutingAdvancedEditAsync(bool blNew)
|
public async Task RoutingAdvancedEditAsync(bool blNew)
|
||||||
{
|
{
|
||||||
RoutingItem item;
|
RoutingItem item;
|
||||||
|
|||||||
@@ -347,11 +347,6 @@ namespace ServiceLib.ViewModels
|
|||||||
public async Task RefreshRoutingsMenu()
|
public async Task RefreshRoutingsMenu()
|
||||||
{
|
{
|
||||||
_routingItems.Clear();
|
_routingItems.Clear();
|
||||||
if (!_config.RoutingBasicItem.EnableRoutingAdvanced)
|
|
||||||
{
|
|
||||||
BlRouting = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlRouting = true;
|
BlRouting = true;
|
||||||
var routings = await AppHandler.Instance.RoutingItems();
|
var routings = await AppHandler.Instance.RoutingItems();
|
||||||
@@ -416,16 +411,16 @@ namespace ServiceLib.ViewModels
|
|||||||
// When running as a non-administrator, reboot to administrator mode
|
// When running as a non-administrator, reboot to administrator mode
|
||||||
if (EnableTun && AllowEnableTun() == false)
|
if (EnableTun && AllowEnableTun() == false)
|
||||||
{
|
{
|
||||||
_config.TunModeItem.EnableTun = false;
|
|
||||||
if (Utils.IsWindows())
|
if (Utils.IsWindows())
|
||||||
{
|
{
|
||||||
|
_config.TunModeItem.EnableTun = false;
|
||||||
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
|
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (Utils.IsLinux())
|
//else if (Utils.IsLinux())
|
||||||
{
|
//{
|
||||||
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordIsEmpty);
|
// NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordIsEmpty);
|
||||||
}
|
//}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
await ConfigHandler.SaveConfig(_config);
|
await ConfigHandler.SaveConfig(_config);
|
||||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||||
@@ -442,6 +437,10 @@ namespace ServiceLib.ViewModels
|
|||||||
{
|
{
|
||||||
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty();
|
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty();
|
||||||
}
|
}
|
||||||
|
else if (Utils.IsOSX())
|
||||||
|
{
|
||||||
|
return _config.TunModeItem.LinuxSudoPwd.IsNotEmpty();
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,19 +451,15 @@ namespace ServiceLib.ViewModels
|
|||||||
public async Task InboundDisplayStatus()
|
public async Task InboundDisplayStatus()
|
||||||
{
|
{
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
sb.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]");
|
sb.Append($"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks)}]");
|
||||||
sb.Append(" | ");
|
|
||||||
sb.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http)}]");
|
|
||||||
InboundDisplay = $"{ResUI.LabLocal}:{sb}";
|
InboundDisplay = $"{ResUI.LabLocal}:{sb}";
|
||||||
|
|
||||||
if (_config.Inbound[0].AllowLANConn)
|
if (_config.Inbound.First().AllowLANConn)
|
||||||
{
|
{
|
||||||
if (_config.Inbound[0].NewPort4LAN)
|
if (_config.Inbound.First().NewPort4LAN)
|
||||||
{
|
{
|
||||||
StringBuilder sb2 = new();
|
StringBuilder sb2 = new();
|
||||||
sb2.Append($"[{EInboundProtocol.socks}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}]");
|
sb2.Append($"[{EInboundProtocol.mixed}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.socks2)}]");
|
||||||
sb2.Append(" | ");
|
|
||||||
sb2.Append($"[{EInboundProtocol.http}:{AppHandler.Instance.GetLocalPort(EInboundProtocol.http2)}]");
|
|
||||||
InboundLanDisplay = $"{ResUI.LabLAN}:{sb2}";
|
InboundLanDisplay = $"{ResUI.LabLAN}:{sb2}";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -474,7 +469,7 @@ namespace ServiceLib.ViewModels
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InboundLanDisplay = $"{ResUI.LabLAN}:None";
|
InboundLanDisplay = $"{ResUI.LabLAN}:{Global.None}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
v2rayN/build-osx.sh
Executable file
17
v2rayN/build-osx.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo 'Building'
|
||||||
|
|
||||||
|
OutputPath='./bin/v2rayN'
|
||||||
|
|
||||||
|
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-x64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o "${OutputPath}/osx-x64"
|
||||||
|
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r osx-arm64 --self-contained true -p:PublishReadyToRun=false -p:PublishSingleFile=true -o "${OutputPath}/osx-arm64"
|
||||||
|
|
||||||
|
rm -rf "$OutputPath/osx-x64/*.pdb"
|
||||||
|
rm -rf "$OutputPath/osx-arm64/*.pdb"
|
||||||
|
|
||||||
|
echo 'Build done'
|
||||||
|
|
||||||
|
ls $OutputPath
|
||||||
|
7z a v2rayN-osx.zip $OutputPath
|
||||||
|
exit 0
|
||||||
@@ -2,37 +2,57 @@ param (
|
|||||||
[Parameter()]
|
[Parameter()]
|
||||||
[ValidateNotNullOrEmpty()]
|
[ValidateNotNullOrEmpty()]
|
||||||
[string]
|
[string]
|
||||||
$OutputPath = '.\bin\v2rayN'
|
$OutputPath = './bin/v2rayN'
|
||||||
)
|
)
|
||||||
|
|
||||||
Write-Host 'Building'
|
Write-Host 'Building'
|
||||||
|
|
||||||
dotnet publish `
|
dotnet publish `
|
||||||
.\v2rayN\v2rayN.csproj `
|
./v2rayN/v2rayN.csproj `
|
||||||
-c Release `
|
-c Release `
|
||||||
-r win-x64 `
|
-r win-x64 `
|
||||||
--self-contained false `
|
--self-contained false `
|
||||||
-p:PublishReadyToRun=false `
|
-p:PublishReadyToRun=false `
|
||||||
-p:PublishSingleFile=true `
|
-p:PublishSingleFile=true `
|
||||||
-o "$OutputPath\win-x64"
|
-o "$OutputPath/win-x64"
|
||||||
|
|
||||||
dotnet publish `
|
dotnet publish `
|
||||||
.\v2rayN.Desktop\v2rayN.Desktop.csproj `
|
./v2rayN/v2rayN.csproj `
|
||||||
|
-c Release `
|
||||||
|
-r win-arm64 `
|
||||||
|
--self-contained false `
|
||||||
|
-p:PublishReadyToRun=false `
|
||||||
|
-p:PublishSingleFile=true `
|
||||||
|
-o "$OutputPath/win-arm64"
|
||||||
|
|
||||||
|
dotnet publish `
|
||||||
|
./v2rayN.Desktop/v2rayN.Desktop.csproj `
|
||||||
-c Release `
|
-c Release `
|
||||||
-r linux-x64 `
|
-r linux-x64 `
|
||||||
--self-contained true `
|
--self-contained true `
|
||||||
-p:PublishReadyToRun=false `
|
-p:PublishReadyToRun=false `
|
||||||
-p:PublishSingleFile=true `
|
-p:PublishSingleFile=true `
|
||||||
-o "$OutputPath\linux-x64"
|
-o "$OutputPath/linux-x64"
|
||||||
|
|
||||||
|
dotnet publish `
|
||||||
|
./v2rayN.Desktop/v2rayN.Desktop.csproj `
|
||||||
|
-c Release `
|
||||||
|
-r linux-arm64 `
|
||||||
|
--self-contained true `
|
||||||
|
-p:PublishReadyToRun=false `
|
||||||
|
-p:PublishSingleFile=true `
|
||||||
|
-o "$OutputPath/linux-arm64"
|
||||||
|
|
||||||
|
|
||||||
if ( -Not $? ) {
|
if ( -Not $? ) {
|
||||||
exit $lastExitCode
|
exit $lastExitCode
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( Test-Path -Path .\bin\v2rayN ) {
|
if ( Test-Path -Path ./bin/v2rayN ) {
|
||||||
rm -Force "$OutputPath\win-x64\*.pdb"
|
rm -Force "$OutputPath/win-x64/*.pdb"
|
||||||
rm -Force "$OutputPath\linux-x64\*.pdb"
|
rm -Force "$OutputPath/win-arm64/*.pdb"
|
||||||
|
rm -Force "$OutputPath/linux-x64/*.pdb"
|
||||||
|
rm -Force "$OutputPath/linux-arm64/*.pdb"
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host 'Build done'
|
Write-Host 'Build done'
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace v2rayN.Desktop.ViewModels
|
|||||||
public class ThemeSettingViewModel : MyReactiveObject
|
public class ThemeSettingViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
[Reactive] public bool ColorModeDark { get; set; }
|
[Reactive] public bool ColorModeDark { get; set; }
|
||||||
|
[Reactive] public bool FollowSystemTheme { get; set; }
|
||||||
|
|
||||||
[Reactive] public int CurrentFontSize { get; set; }
|
[Reactive] public int CurrentFontSize { get; set; }
|
||||||
|
|
||||||
@@ -28,13 +29,14 @@ namespace v2rayN.Desktop.ViewModels
|
|||||||
|
|
||||||
private void RestoreUI()
|
private void RestoreUI()
|
||||||
{
|
{
|
||||||
ModifyTheme(_config.UiItem.ColorModeDark);
|
ModifyTheme();
|
||||||
ModifyFontFamily();
|
ModifyFontFamily();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BindingUI()
|
private void BindingUI()
|
||||||
{
|
{
|
||||||
ColorModeDark = _config.UiItem.ColorModeDark;
|
ColorModeDark = _config.UiItem.ColorModeDark;
|
||||||
|
FollowSystemTheme = _config.UiItem.FollowSystemTheme;
|
||||||
CurrentFontSize = _config.UiItem.CurrentFontSize;
|
CurrentFontSize = _config.UiItem.CurrentFontSize;
|
||||||
CurrentLanguage = _config.UiItem.CurrentLanguage;
|
CurrentLanguage = _config.UiItem.CurrentLanguage;
|
||||||
|
|
||||||
@@ -44,7 +46,18 @@ namespace v2rayN.Desktop.ViewModels
|
|||||||
if (_config.UiItem.ColorModeDark != ColorModeDark)
|
if (_config.UiItem.ColorModeDark != ColorModeDark)
|
||||||
{
|
{
|
||||||
_config.UiItem.ColorModeDark = ColorModeDark;
|
_config.UiItem.ColorModeDark = ColorModeDark;
|
||||||
ModifyTheme(ColorModeDark);
|
ModifyTheme();
|
||||||
|
ConfigHandler.SaveConfig(_config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.WhenAnyValue(x => x.FollowSystemTheme,
|
||||||
|
y => y == true)
|
||||||
|
.Subscribe(c =>
|
||||||
|
{
|
||||||
|
if (_config.UiItem.FollowSystemTheme != FollowSystemTheme)
|
||||||
|
{
|
||||||
|
_config.UiItem.FollowSystemTheme = FollowSystemTheme;
|
||||||
|
ModifyTheme();
|
||||||
ConfigHandler.SaveConfig(_config);
|
ConfigHandler.SaveConfig(_config);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -59,7 +72,6 @@ namespace v2rayN.Desktop.ViewModels
|
|||||||
_config.UiItem.CurrentFontSize = CurrentFontSize;
|
_config.UiItem.CurrentFontSize = CurrentFontSize;
|
||||||
double size = CurrentFontSize;
|
double size = CurrentFontSize;
|
||||||
ModifyFontSize(size);
|
ModifyFontSize(size);
|
||||||
|
|
||||||
ConfigHandler.SaveConfig(_config);
|
ConfigHandler.SaveConfig(_config);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -79,12 +91,12 @@ namespace v2rayN.Desktop.ViewModels
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ModifyTheme(bool isDarkTheme)
|
private void ModifyTheme()
|
||||||
{
|
{
|
||||||
var app = Application.Current;
|
var app = Application.Current;
|
||||||
if (app is not null)
|
if (app is not null)
|
||||||
{
|
{
|
||||||
app.RequestedThemeVariant = isDarkTheme ? ThemeVariant.Dark : ThemeVariant.Light;
|
app.RequestedThemeVariant = FollowSystemTheme ? ThemeVariant.Default : (ColorModeDark ? ThemeVariant.Dark : ThemeVariant.Light);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,20 +26,10 @@ namespace v2rayN.Desktop.Views
|
|||||||
|
|
||||||
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
|
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
|
||||||
|
|
||||||
if (profileItem.ConfigType == EConfigType.VLESS)
|
Global.CoreTypes.ForEach(it =>
|
||||||
{
|
{
|
||||||
Global.CoreTypes4VLESS.ForEach(it =>
|
cmbCoreType.Items.Add(it);
|
||||||
{
|
});
|
||||||
cmbCoreType.Items.Add(it);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Global.CoreTypes.ForEach(it =>
|
|
||||||
{
|
|
||||||
cmbCoreType.Items.Add(it);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cmbCoreType.Items.Add(string.Empty);
|
cmbCoreType.Items.Add(string.Empty);
|
||||||
|
|
||||||
cmbStreamSecurity.Items.Add(string.Empty);
|
cmbStreamSecurity.Items.Add(string.Empty);
|
||||||
@@ -209,7 +199,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Extra, v => v.txtExtra.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Extra, v => v.txtExtra.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
|
||||||
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);
|
||||||
@@ -300,7 +290,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
cmbHeaderType.Items.Add(it);
|
cmbHeaderType.Items.Add(it);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (network is nameof(ETransport.splithttp) or nameof(ETransport.xhttp))
|
else if (network is nameof(ETransport.xhttp))
|
||||||
{
|
{
|
||||||
Global.XhttpMode.ForEach(it =>
|
Global.XhttpMode.ForEach(it =>
|
||||||
{
|
{
|
||||||
@@ -350,7 +340,6 @@ namespace v2rayN.Desktop.Views
|
|||||||
tipPath.Text = ResUI.TransportPathTip1;
|
tipPath.Text = ResUI.TransportPathTip1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.splithttp):
|
|
||||||
case nameof(ETransport.xhttp):
|
case nameof(ETransport.xhttp):
|
||||||
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
|
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
|
||||||
tipPath.Text = ResUI.TransportPathTip1;
|
tipPath.Text = ResUI.TransportPathTip1;
|
||||||
|
|||||||
@@ -25,22 +25,6 @@
|
|||||||
VerticalContentAlignment="Center"
|
VerticalContentAlignment="Center"
|
||||||
Watermark="{x:Static resx:ResUI.ConnectionsHostFilterTitle}" />
|
Watermark="{x:Static resx:ResUI.ConnectionsHostFilterTitle}" />
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
Margin="8,0"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Text="{x:Static resx:ResUI.TbSorting}" />
|
|
||||||
<ComboBox
|
|
||||||
x:Name="cmbSorting"
|
|
||||||
Width="100"
|
|
||||||
Margin="8,0">
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpSpeed}" />
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownSpeed}" />
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpTraffic}" />
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownTraffic}" />
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingTime}" />
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingHost}" />
|
|
||||||
</ComboBox>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnConnectionCloseAll"
|
x:Name="btnConnectionCloseAll"
|
||||||
Width="30"
|
Width="30"
|
||||||
@@ -58,6 +42,23 @@
|
|||||||
</Button.Content>
|
</Button.Content>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
x:Name="btnAutofitColumnWidth"
|
||||||
|
Width="30"
|
||||||
|
Height="30"
|
||||||
|
Margin="8,0"
|
||||||
|
Classes="Success"
|
||||||
|
Theme="{DynamicResource BorderlessButton}"
|
||||||
|
ToolTip.Tip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
|
||||||
|
<Button.Content>
|
||||||
|
<PathIcon
|
||||||
|
Width="20"
|
||||||
|
Height="20"
|
||||||
|
Data="{StaticResource building_fit}"
|
||||||
|
Foreground="{DynamicResource ButtonDefaultTertiaryForeground}" />
|
||||||
|
</Button.Content>
|
||||||
|
</Button>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="8,0"
|
Margin="8,0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@@ -85,11 +86,11 @@
|
|||||||
</DataGrid.ContextMenu>
|
</DataGrid.ContextMenu>
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="240"
|
Width="300"
|
||||||
Binding="{Binding Host}"
|
Binding="{Binding Host}"
|
||||||
Header="{x:Static resx:ResUI.TbSortingHost}" />
|
Header="{x:Static resx:ResUI.TbSortingHost}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="160"
|
Width="500"
|
||||||
Binding="{Binding Chain}"
|
Binding="{Binding Chain}"
|
||||||
Header="{x:Static resx:ResUI.TbSortingChain}" />
|
Header="{x:Static resx:ResUI.TbSortingChain}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
@@ -97,17 +98,9 @@
|
|||||||
Binding="{Binding Network}"
|
Binding="{Binding Network}"
|
||||||
Header="{x:Static resx:ResUI.TbSortingNetwork}" />
|
Header="{x:Static resx:ResUI.TbSortingNetwork}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="100"
|
Width="160"
|
||||||
Binding="{Binding Type}"
|
Binding="{Binding Type}"
|
||||||
Header="{x:Static resx:ResUI.TbSortingType}" />
|
Header="{x:Static resx:ResUI.TbSortingType}" />
|
||||||
<DataGridTextColumn
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding UploadTraffic}"
|
|
||||||
Header="{x:Static resx:ResUI.TbSortingUpTraffic}" />
|
|
||||||
<DataGridTextColumn
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding DownloadTraffic}"
|
|
||||||
Header="{x:Static resx:ResUI.TbSortingDownTraffic}" />
|
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="100"
|
Width="100"
|
||||||
Binding="{Binding Elapsed}"
|
Binding="{Binding Elapsed}"
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
@@ -12,6 +13,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
|
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
|
||||||
|
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
@@ -22,7 +24,6 @@ namespace v2rayN.Desktop.Views
|
|||||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
@@ -43,6 +44,19 @@ namespace v2rayN.Desktop.Views
|
|||||||
return await Task.FromResult(true);
|
return await Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BtnAutofitColumnWidth_Click(object? sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
AutofitColumnWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AutofitColumnWidth()
|
||||||
|
{
|
||||||
|
foreach (var it in lstConnections.Columns)
|
||||||
|
{
|
||||||
|
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void btnClose_Click(object? sender, RoutedEventArgs e)
|
private void btnClose_Click(object? sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel?.ClashConnectionClose(false);
|
ViewModel?.ClashConnectionClose(false);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
private WindowNotificationManager? _manager;
|
private WindowNotificationManager? _manager;
|
||||||
private CheckUpdateView? _checkUpdateView;
|
private CheckUpdateView? _checkUpdateView;
|
||||||
private BackupAndRestoreView? _backupAndRestoreView;
|
private BackupAndRestoreView? _backupAndRestoreView;
|
||||||
|
private bool _blCloseByUser = false;
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
@@ -40,7 +41,34 @@ namespace v2rayN.Desktop.Views
|
|||||||
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
||||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
|
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
|
||||||
|
|
||||||
//WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
|
switch (_config.UiItem.MainGirdOrientation)
|
||||||
|
{
|
||||||
|
case EGirdOrientation.Horizontal:
|
||||||
|
tabProfiles.Content ??= new ProfilesView(this);
|
||||||
|
tabMsgView.Content ??= new MsgView();
|
||||||
|
tabClashProxies.Content ??= new ClashProxiesView();
|
||||||
|
tabClashConnections.Content ??= new ClashConnectionsView();
|
||||||
|
gridMain.IsVisible = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EGirdOrientation.Vertical:
|
||||||
|
tabProfiles1.Content ??= new ProfilesView(this);
|
||||||
|
tabMsgView1.Content ??= new MsgView();
|
||||||
|
tabClashProxies1.Content ??= new ClashProxiesView();
|
||||||
|
tabClashConnections1.Content ??= new ClashConnectionsView();
|
||||||
|
gridMain1.IsVisible = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EGirdOrientation.Tab:
|
||||||
|
default:
|
||||||
|
tabProfiles2.Content ??= new ProfilesView(this);
|
||||||
|
tabMsgView2.Content ??= new MsgView();
|
||||||
|
tabClashProxies2.Content ??= new ClashProxiesView();
|
||||||
|
tabClashConnections2.Content ??= new ClashConnectionsView();
|
||||||
|
gridMain2.IsVisible = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
conTheme.Content ??= new ThemeSettingView();
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
@@ -83,7 +111,6 @@ namespace v2rayN.Desktop.Views
|
|||||||
switch (_config.UiItem.MainGirdOrientation)
|
switch (_config.UiItem.MainGirdOrientation)
|
||||||
{
|
{
|
||||||
case EGirdOrientation.Horizontal:
|
case EGirdOrientation.Horizontal:
|
||||||
gridMain.IsVisible = true;
|
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.IsVisible).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.IsVisible).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.IsVisible).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.IsVisible).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections.IsVisible).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections.IsVisible).DisposeWith(disposables);
|
||||||
@@ -91,7 +118,6 @@ namespace v2rayN.Desktop.Views
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case EGirdOrientation.Vertical:
|
case EGirdOrientation.Vertical:
|
||||||
gridMain1.IsVisible = true;
|
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.IsVisible).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.IsVisible).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.IsVisible).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.IsVisible).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.IsVisible).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.IsVisible).DisposeWith(disposables);
|
||||||
@@ -100,7 +126,6 @@ namespace v2rayN.Desktop.Views
|
|||||||
|
|
||||||
case EGirdOrientation.Tab:
|
case EGirdOrientation.Tab:
|
||||||
default:
|
default:
|
||||||
gridMain2.IsVisible = true;
|
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.IsVisible).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.IsVisible).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.IsVisible).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.IsVisible).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
|
||||||
@@ -127,35 +152,10 @@ namespace v2rayN.Desktop.Views
|
|||||||
menuGlobalHotkeySetting.IsVisible = false;
|
menuGlobalHotkeySetting.IsVisible = false;
|
||||||
}
|
}
|
||||||
menuAddServerViaScan.IsVisible = false;
|
menuAddServerViaScan.IsVisible = false;
|
||||||
|
|
||||||
switch (_config.UiItem.MainGirdOrientation)
|
|
||||||
{
|
|
||||||
case EGirdOrientation.Horizontal:
|
|
||||||
tabProfiles.Content ??= new ProfilesView(this);
|
|
||||||
tabMsgView.Content ??= new MsgView();
|
|
||||||
tabClashProxies.Content ??= new ClashProxiesView();
|
|
||||||
tabClashConnections.Content ??= new ClashConnectionsView();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EGirdOrientation.Vertical:
|
|
||||||
tabProfiles1.Content ??= new ProfilesView(this);
|
|
||||||
tabMsgView1.Content ??= new MsgView();
|
|
||||||
tabClashProxies1.Content ??= new ClashProxiesView();
|
|
||||||
tabClashConnections1.Content ??= new ClashConnectionsView();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case EGirdOrientation.Tab:
|
|
||||||
default:
|
|
||||||
tabProfiles2.Content ??= new ProfilesView(this);
|
|
||||||
tabMsgView2.Content ??= new MsgView();
|
|
||||||
tabClashProxies2.Content ??= new ClashProxiesView();
|
|
||||||
tabClashConnections2.Content ??= new ClashConnectionsView();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
conTheme.Content ??= new ThemeSettingView();
|
|
||||||
|
|
||||||
RestoreUI();
|
RestoreUI();
|
||||||
AddHelpMenuItem();
|
AddHelpMenuItem();
|
||||||
|
//WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Event
|
#region Event
|
||||||
@@ -279,6 +279,11 @@ namespace v2rayN.Desktop.Views
|
|||||||
|
|
||||||
protected override async void OnClosing(WindowClosingEventArgs e)
|
protected override async void OnClosing(WindowClosingEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (_blCloseByUser)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Logging.SaveLog("OnClosing -> " + e.CloseReason.ToString());
|
Logging.SaveLog("OnClosing -> " + e.CloseReason.ToString());
|
||||||
|
|
||||||
switch (e.CloseReason)
|
switch (e.CloseReason)
|
||||||
@@ -374,6 +379,8 @@ namespace v2rayN.Desktop.Views
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_blCloseByUser = true;
|
||||||
StorageUI();
|
StorageUI();
|
||||||
|
|
||||||
await ViewModel?.MyAppExitAsync(false);
|
await ViewModel?.MyAppExitAsync(false);
|
||||||
@@ -382,7 +389,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
#endregion Event
|
#endregion Event
|
||||||
|
|
||||||
#region UI
|
#region UI
|
||||||
|
|
||||||
public void ShowHideWindow(bool? blShow)
|
public void ShowHideWindow(bool? blShow)
|
||||||
{
|
{
|
||||||
var bl = blShow ?? !_config.UiItem.ShowInTaskbar;
|
var bl = blShow ?? !_config.UiItem.ShowInTaskbar;
|
||||||
|
|||||||
@@ -379,6 +379,7 @@
|
|||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
x:Name="tbAutoRun"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@@ -531,26 +532,40 @@
|
|||||||
Classes="Margin8" />
|
Classes="Margin8" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="16"
|
Grid.Row="15"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Classes="Margin8"
|
Classes="Margin8"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
|
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
|
||||||
<ComboBox
|
<ComboBox
|
||||||
x:Name="cmbcurrentFontFamily"
|
x:Name="cmbcurrentFontFamily"
|
||||||
Grid.Row="16"
|
Grid.Row="15"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Classes="Margin8"
|
Classes="Margin8"
|
||||||
MaxDropDownHeight="1000" />
|
MaxDropDownHeight="1000" />
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="16"
|
Grid.Row="15"
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Classes="Margin8"
|
Classes="Margin8"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamilyLinuxTip}"
|
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamilyLinuxTip}"
|
||||||
TextWrapping="Wrap" />
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="16"
|
||||||
|
Grid.Column="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Classes="Margin8"
|
||||||
|
Text="{x:Static resx:ResUI.TbSettingsSpeedTestPageSize}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txtSpeedTestPageSize"
|
||||||
|
Grid.Row="16"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Classes="Margin8" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="17"
|
Grid.Row="17"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
{
|
{
|
||||||
clbdestOverride.Items.Add(it);
|
clbdestOverride.Items.Add(it);
|
||||||
});
|
});
|
||||||
_config.Inbound[0].DestOverride?.ForEach(it =>
|
_config.Inbound.First().DestOverride?.ForEach(it =>
|
||||||
{
|
{
|
||||||
clbdestOverride.SelectedItems.Add(it);
|
clbdestOverride.SelectedItems.Add(it);
|
||||||
});
|
});
|
||||||
@@ -138,6 +138,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SpeedTestTimeout, v => v.cmbSpeedTestTimeout.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SpeedTestUrl, v => v.cmbSpeedTestUrl.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SpeedPingTestUrl, v => v.cmbSpeedPingTestUrl.SelectedValue).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.SpeedTestPageSize, v => v.txtSpeedTestPageSize.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SubConvertUrl, v => v.cmbSubConvertUrl.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.MainGirdOrientation, v => v.cmbMainGirdOrientation.SelectedIndex).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables);
|
||||||
@@ -169,6 +170,13 @@ namespace v2rayN.Desktop.Views
|
|||||||
{
|
{
|
||||||
tabSystemproxy.IsVisible = false;
|
tabSystemproxy.IsVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Utils.IsOSX())
|
||||||
|
{
|
||||||
|
tbAutoRun.IsVisible = false;
|
||||||
|
togAutoRun.IsVisible = false;
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||||
@@ -202,7 +210,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
{
|
{
|
||||||
return lstFonts;
|
return lstFonts;
|
||||||
}
|
}
|
||||||
else if (Utils.IsLinux())
|
else if (Utils.IsLinux() || Utils.IsOSX())
|
||||||
{
|
{
|
||||||
var result = await Utils.GetLinuxFontFamily("zh");
|
var result = await Utils.GetLinuxFontFamily("zh");
|
||||||
if (result.IsNullOrEmpty())
|
if (result.IsNullOrEmpty())
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
item2.Width = new DataGridLength(item.Width, DataGridLengthUnitType.Pixel);
|
item2.Width = new DataGridLength(item.Width, DataGridLengthUnitType.Pixel);
|
||||||
item2.DisplayIndex = displayIndex++;
|
item2.DisplayIndex = displayIndex++;
|
||||||
}
|
}
|
||||||
if (item.Name.StartsWith("to"))
|
if (item.Name.ToLower().StartsWith("to"))
|
||||||
{
|
{
|
||||||
if (!_config.GuiItem.EnableStatistics)
|
if (!_config.GuiItem.EnableStatistics)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
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"
|
||||||
Title="{x:Static resx:ResUI.menuRoutingRuleSetting}"
|
Title="{x:Static resx:ResUI.menuRoutingRuleSetting}"
|
||||||
Width="960"
|
Width="1000"
|
||||||
Height="700"
|
Height="700"
|
||||||
x:DataType="vms:RoutingRuleSettingViewModel"
|
x:DataType="vms:RoutingRuleSettingViewModel"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
@@ -237,11 +237,11 @@
|
|||||||
Binding="{Binding Network}"
|
Binding="{Binding Network}"
|
||||||
Header="network" />
|
Header="network" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="200"
|
Width="*"
|
||||||
Binding="{Binding Domains}"
|
Binding="{Binding Domains}"
|
||||||
Header="domain" />
|
Header="domain" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="200"
|
Width="*"
|
||||||
Binding="{Binding Ips}"
|
Binding="{Binding Ips}"
|
||||||
Header="ip" />
|
Header="ip" />
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
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"
|
||||||
Title="{x:Static resx:ResUI.menuRoutingSetting}"
|
Title="{x:Static resx:ResUI.menuRoutingSetting}"
|
||||||
Width="990"
|
Width="1000"
|
||||||
Height="700"
|
Height="700"
|
||||||
x:DataType="vms:RoutingSettingViewModel"
|
x:DataType="vms:RoutingSettingViewModel"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|
||||||
<DockPanel>
|
<DockPanel>
|
||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Classes="Margin8"
|
Classes="Margin8"
|
||||||
DockPanel.Dock="Top"
|
DockPanel.Dock="Top"
|
||||||
@@ -55,7 +54,6 @@
|
|||||||
Margin="8,0,0,0" />
|
Margin="8,0,0,0" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
|
||||||
<StackPanel
|
<StackPanel
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Classes="Margin8"
|
Classes="Margin8"
|
||||||
@@ -111,7 +109,7 @@
|
|||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridCheckBoxColumn Width="40" Binding="{Binding IsActive}" />
|
<DataGridCheckBoxColumn Width="40" Binding="{Binding IsActive}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="250"
|
Width="*"
|
||||||
Binding="{Binding Remarks}"
|
Binding="{Binding Remarks}"
|
||||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
@@ -123,7 +121,7 @@
|
|||||||
Binding="{Binding Sort}"
|
Binding="{Binding Sort}"
|
||||||
Header="{x:Static resx:ResUI.LvSort}" />
|
Header="{x:Static resx:ResUI.LvSort}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="300"
|
Width="*"
|
||||||
Binding="{Binding Url}"
|
Binding="{Binding Url}"
|
||||||
Header="{x:Static resx:ResUI.LvUrl}" />
|
Header="{x:Static resx:ResUI.LvUrl}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
@@ -134,9 +132,6 @@
|
|||||||
</DataGrid>
|
</DataGrid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
|
|
||||||
|
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
|
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</Window>
|
</Window>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
using Avalonia.ReactiveUI;
|
using Avalonia.ReactiveUI;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
@@ -77,6 +78,9 @@ namespace v2rayN.Desktop.Views
|
|||||||
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
desktop.MainWindow.Icon = AvaUtils.GetAppIcon(_config.SystemProxyItem.SysProxyType);
|
desktop.MainWindow.Icon = AvaUtils.GetAppIcon(_config.SystemProxyItem.SysProxyType);
|
||||||
|
var iconslist = TrayIcon.GetIcons(Application.Current);
|
||||||
|
iconslist[0].Icon = desktop.MainWindow.Icon;
|
||||||
|
TrayIcon.SetIcons(Application.Current, iconslist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,7 @@
|
|||||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||||
Title="{x:Static resx:ResUI.menuSubSetting}"
|
Title="{x:Static resx:ResUI.menuSubSetting}"
|
||||||
Width="700"
|
Width="700"
|
||||||
Height="600"
|
Height="650"
|
||||||
d:DesignHeight="600"
|
|
||||||
d:DesignWidth="800"
|
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
|
|||||||
@@ -8,10 +8,8 @@
|
|||||||
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"
|
||||||
Title="{x:Static resx:ResUI.menuSubSetting}"
|
Title="{x:Static resx:ResUI.menuSubSetting}"
|
||||||
Width="800"
|
Width="1000"
|
||||||
Height="600"
|
Height="700"
|
||||||
d:DesignHeight="450"
|
|
||||||
d:DesignWidth="800"
|
|
||||||
x:DataType="vms:SubSettingViewModel"
|
x:DataType="vms:SubSettingViewModel"
|
||||||
ShowInTaskbar="False"
|
ShowInTaskbar="False"
|
||||||
WindowStartupLocation="CenterScreen"
|
WindowStartupLocation="CenterScreen"
|
||||||
@@ -49,11 +47,11 @@
|
|||||||
</DataGrid.KeyBindings>
|
</DataGrid.KeyBindings>
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="120"
|
Width="*"
|
||||||
Binding="{Binding Remarks}"
|
Binding="{Binding Remarks}"
|
||||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="150"
|
Width="*"
|
||||||
Binding="{Binding Url}"
|
Binding="{Binding Url}"
|
||||||
Header="{x:Static resx:ResUI.LvUrl}" />
|
Header="{x:Static resx:ResUI.LvUrl}" />
|
||||||
<DataGridCheckBoxColumn
|
<DataGridCheckBoxColumn
|
||||||
|
|||||||
@@ -33,7 +33,14 @@
|
|||||||
Text="{x:Static resx:ResUI.TbSettingsColorMode}" />
|
Text="{x:Static resx:ResUI.TbSettingsColorMode}" />
|
||||||
<ToggleSwitch x:Name="togDarkMode" Classes="Margin8" />
|
<ToggleSwitch x:Name="togDarkMode" Classes="Margin8" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock
|
||||||
|
Width="100"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Classes="Margin8"
|
||||||
|
Text="{x:Static resx:ResUI.TbSettingsFollowSystemTheme}" />
|
||||||
|
<ToggleSwitch x:Name="togFollowSystemTheme" Classes="Margin8" />
|
||||||
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Width="100"
|
Width="100"
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace v2rayN.Desktop.Views
|
|||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
this.Bind(ViewModel, vm => vm.ColorModeDark, v => v.togDarkMode.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.ColorModeDark, v => v.togDarkMode.IsChecked).DisposeWith(disposables);
|
||||||
|
this.Bind(ViewModel, vm => vm.FollowSystemTheme, v => v.togFollowSystemTheme.IsChecked).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CurrentFontSize, v => v.cmbCurrentFontSize.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CurrentFontSize, v => v.cmbCurrentFontSize.SelectedValue).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.CurrentLanguage, v => v.cmbCurrentLanguage.SelectedValue).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.CurrentLanguage, v => v.cmbCurrentLanguage.SelectedValue).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,16 +20,16 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="11.2.1" />
|
<PackageReference Include="Avalonia" Version="11.2.2" />
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.2.1" />
|
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.2.2" />
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="11.2.1" />
|
<PackageReference Include="Avalonia.Desktop" Version="11.2.2" />
|
||||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.1" />
|
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.2" />
|
||||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.1" />
|
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.2" />
|
||||||
<PackageReference Include="Avalonia.ReactiveUI" Version="11.2.1" />
|
<PackageReference Include="Avalonia.ReactiveUI" Version="11.2.2" />
|
||||||
<PackageReference Include="DialogHost.Avalonia" Version="0.8.1" />
|
<PackageReference Include="DialogHost.Avalonia" Version="0.8.1" />
|
||||||
<PackageReference Include="MessageBox.Avalonia" Version="3.2.0" />
|
<PackageReference Include="MessageBox.Avalonia" Version="3.2.0" />
|
||||||
<PackageReference Include="Semi.Avalonia" Version="11.2.1" />
|
<PackageReference Include="Semi.Avalonia" Version="11.2.1.1" />
|
||||||
<PackageReference Include="Semi.Avalonia.DataGrid" Version="11.2.1" />
|
<PackageReference Include="Semi.Avalonia.DataGrid" Version="11.2.1.1" />
|
||||||
<PackageReference Include="ReactiveUI" Version="20.1.63" />
|
<PackageReference Include="ReactiveUI" Version="20.1.63" />
|
||||||
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
|
<PackageReference Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -5,10 +5,6 @@ VisualStudioVersion = 17.3.32811.315
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "v2rayN", "v2rayN\v2rayN.csproj", "{6DE127CA-1763-4236-B297-D2EF9CB2EC9B}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "v2rayN", "v2rayN\v2rayN.csproj", "{6DE127CA-1763-4236-B297-D2EF9CB2EC9B}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProtosLib", "ProtosLib\ProtosLib.csproj", "{C5F24BB0-9CC1-44DD-82FF-D545F081819B}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PacLib", "PacLib\PacLib.csproj", "{EE4E6CD8-8353-446B-8F29-A841A02AE5EC}"
|
|
||||||
EndProject
|
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceLib", "ServiceLib\ServiceLib.csproj", "{1B6456C4-FFAA-4298-80F6-7B689A6E9243}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceLib", "ServiceLib\ServiceLib.csproj", "{1B6456C4-FFAA-4298-80F6-7B689A6E9243}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "v2rayN.Desktop", "v2rayN.Desktop\v2rayN.Desktop.csproj", "{5D16541A-F971-4C17-9315-BB8955E3F984}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "v2rayN.Desktop", "v2rayN.Desktop\v2rayN.Desktop.csproj", "{5D16541A-F971-4C17-9315-BB8955E3F984}"
|
||||||
@@ -25,14 +21,6 @@ Global
|
|||||||
{6DE127CA-1763-4236-B297-D2EF9CB2EC9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{6DE127CA-1763-4236-B297-D2EF9CB2EC9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{6DE127CA-1763-4236-B297-D2EF9CB2EC9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{6DE127CA-1763-4236-B297-D2EF9CB2EC9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{6DE127CA-1763-4236-B297-D2EF9CB2EC9B}.Release|Any CPU.Build.0 = Release|Any CPU
|
{6DE127CA-1763-4236-B297-D2EF9CB2EC9B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{C5F24BB0-9CC1-44DD-82FF-D545F081819B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{C5F24BB0-9CC1-44DD-82FF-D545F081819B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{C5F24BB0-9CC1-44DD-82FF-D545F081819B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{C5F24BB0-9CC1-44DD-82FF-D545F081819B}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{EE4E6CD8-8353-446B-8F29-A841A02AE5EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{EE4E6CD8-8353-446B-8F29-A841A02AE5EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{EE4E6CD8-8353-446B-8F29-A841A02AE5EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{EE4E6CD8-8353-446B-8F29-A841A02AE5EC}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{1B6456C4-FFAA-4298-80F6-7B689A6E9243}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{1B6456C4-FFAA-4298-80F6-7B689A6E9243}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{1B6456C4-FFAA-4298-80F6-7B689A6E9243}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{1B6456C4-FFAA-4298-80F6-7B689A6E9243}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{1B6456C4-FFAA-4298-80F6-7B689A6E9243}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{1B6456C4-FFAA-4298-80F6-7B689A6E9243}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
|||||||
@@ -62,11 +62,12 @@ namespace v2rayN
|
|||||||
BitmapSizeOptions.FromEmptyOptions());
|
BitmapSizeOptions.FromEmptyOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsLightTheme()
|
public static bool IsDarkTheme()
|
||||||
{
|
{
|
||||||
using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
|
using var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize");
|
||||||
var value = key?.GetValue("AppsUseLightTheme");
|
var obj = key?.GetValue("AppsUseLightTheme");
|
||||||
return value is int i && i > 0;
|
int.TryParse(obj?.ToString(), out var value);
|
||||||
|
return value == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RemoveTunDevice()
|
public static void RemoveTunDevice()
|
||||||
|
|||||||
@@ -54,11 +54,6 @@ namespace v2rayN.Handler
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!config.RoutingBasicItem.EnableRoutingAdvanced)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var item = await ConfigHandler.GetDefaultRouting(config);
|
var item = await ConfigHandler.GetDefaultRouting(config);
|
||||||
if (item == null || Utils.IsNullOrEmpty(item.CustomIcon) || !File.Exists(item.CustomIcon))
|
if (item == null || Utils.IsNullOrEmpty(item.CustomIcon) || !File.Exists(item.CustomIcon))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace v2rayN.ViewModels
|
|||||||
{
|
{
|
||||||
_config = AppHandler.Instance.Config;
|
_config = AppHandler.Instance.Config;
|
||||||
|
|
||||||
RegisterSystemColorSet(_config, Application.Current.MainWindow, (bool bl) => { ModifyTheme(bl); });
|
RegisterSystemColorSet(_config, Application.Current.MainWindow, ModifyTheme);
|
||||||
|
|
||||||
BindingUI();
|
BindingUI();
|
||||||
RestoreUI();
|
RestoreUI();
|
||||||
@@ -46,15 +46,7 @@ namespace v2rayN.ViewModels
|
|||||||
|
|
||||||
private void RestoreUI()
|
private void RestoreUI()
|
||||||
{
|
{
|
||||||
if (FollowSystemTheme)
|
ModifyTheme();
|
||||||
{
|
|
||||||
ModifyTheme(!WindowsUtils.IsLightTheme());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ModifyTheme(_config.UiItem.ColorModeDark);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_config.UiItem.ColorPrimaryName.IsNullOrEmpty())
|
if (!_config.UiItem.ColorPrimaryName.IsNullOrEmpty())
|
||||||
{
|
{
|
||||||
var swatch = new SwatchesProvider().Swatches.FirstOrDefault(t => t.Name == _config.UiItem.ColorPrimaryName);
|
var swatch = new SwatchesProvider().Swatches.FirstOrDefault(t => t.Name == _config.UiItem.ColorPrimaryName);
|
||||||
@@ -87,7 +79,7 @@ namespace v2rayN.ViewModels
|
|||||||
if (_config.UiItem.ColorModeDark != ColorModeDark)
|
if (_config.UiItem.ColorModeDark != ColorModeDark)
|
||||||
{
|
{
|
||||||
_config.UiItem.ColorModeDark = ColorModeDark;
|
_config.UiItem.ColorModeDark = ColorModeDark;
|
||||||
ModifyTheme(ColorModeDark);
|
ModifyTheme();
|
||||||
ConfigHandler.SaveConfig(_config);
|
ConfigHandler.SaveConfig(_config);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -99,15 +91,8 @@ namespace v2rayN.ViewModels
|
|||||||
if (_config.UiItem.FollowSystemTheme != FollowSystemTheme)
|
if (_config.UiItem.FollowSystemTheme != FollowSystemTheme)
|
||||||
{
|
{
|
||||||
_config.UiItem.FollowSystemTheme = FollowSystemTheme;
|
_config.UiItem.FollowSystemTheme = FollowSystemTheme;
|
||||||
|
ModifyTheme();
|
||||||
ConfigHandler.SaveConfig(_config);
|
ConfigHandler.SaveConfig(_config);
|
||||||
if (FollowSystemTheme)
|
|
||||||
{
|
|
||||||
ModifyTheme(!WindowsUtils.IsLightTheme());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ModifyTheme(ColorModeDark);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -163,10 +148,11 @@ namespace v2rayN.ViewModels
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ModifyTheme(bool isDarkTheme)
|
public void ModifyTheme()
|
||||||
{
|
{
|
||||||
var theme = _paletteHelper.GetTheme();
|
var theme = _paletteHelper.GetTheme();
|
||||||
|
|
||||||
|
var isDarkTheme = FollowSystemTheme ? WindowsUtils.IsDarkTheme() : ColorModeDark;
|
||||||
theme.SetBaseTheme(isDarkTheme ? BaseTheme.Dark : BaseTheme.Light);
|
theme.SetBaseTheme(isDarkTheme ? BaseTheme.Dark : BaseTheme.Light);
|
||||||
_paletteHelper.SetTheme(theme);
|
_paletteHelper.SetTheme(theme);
|
||||||
WindowsUtils.SetDarkBorder(Application.Current.MainWindow, isDarkTheme);
|
WindowsUtils.SetDarkBorder(Application.Current.MainWindow, isDarkTheme);
|
||||||
@@ -183,7 +169,7 @@ namespace v2rayN.ViewModels
|
|||||||
_paletteHelper.SetTheme(theme);
|
_paletteHelper.SetTheme(theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RegisterSystemColorSet(Config config, Window window, Action<bool> updateFunc)
|
public void RegisterSystemColorSet(Config config, Window window, Action updateFunc)
|
||||||
{
|
{
|
||||||
var helper = new WindowInteropHelper(window);
|
var helper = new WindowInteropHelper(window);
|
||||||
var hwndSource = HwndSource.FromHwnd(helper.EnsureHandle());
|
var hwndSource = HwndSource.FromHwnd(helper.EnsureHandle());
|
||||||
@@ -196,7 +182,7 @@ namespace v2rayN.ViewModels
|
|||||||
{
|
{
|
||||||
if (wParam == IntPtr.Zero && Marshal.PtrToStringUni(lParam) == "ImmersiveColorSet")
|
if (wParam == IntPtr.Zero && Marshal.PtrToStringUni(lParam) == "ImmersiveColorSet")
|
||||||
{
|
{
|
||||||
updateFunc?.Invoke(!WindowsUtils.IsLightTheme());
|
updateFunc?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace v2rayN.Views
|
|||||||
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.btnEdit).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.btnEdit).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.SaveServerCmd, v => v.btnSave).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.SaveServerCmd, v => v.btnSave).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.FollowSystemTheme ? !WindowsUtils.IsLightTheme() : AppHandler.Instance.Config.UiItem.ColorModeDark);
|
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.FollowSystemTheme ? WindowsUtils.IsDarkTheme() : AppHandler.Instance.Config.UiItem.ColorModeDark);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||||
|
|||||||
@@ -20,20 +20,10 @@ namespace v2rayN.Views
|
|||||||
|
|
||||||
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
|
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
|
||||||
|
|
||||||
if (profileItem.ConfigType == EConfigType.VLESS)
|
Global.CoreTypes.ForEach(it =>
|
||||||
{
|
{
|
||||||
Global.CoreTypes4VLESS.ForEach(it =>
|
cmbCoreType.Items.Add(it);
|
||||||
{
|
});
|
||||||
cmbCoreType.Items.Add(it);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Global.CoreTypes.ForEach(it =>
|
|
||||||
{
|
|
||||||
cmbCoreType.Items.Add(it);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cmbCoreType.Items.Add(string.Empty);
|
cmbCoreType.Items.Add(string.Empty);
|
||||||
|
|
||||||
cmbStreamSecurity.Items.Add(string.Empty);
|
cmbStreamSecurity.Items.Add(string.Empty);
|
||||||
@@ -203,7 +193,7 @@ namespace v2rayN.Views
|
|||||||
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Path, v => v.txtPath.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Extra, v => v.txtExtra.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Extra, v => v.txtExtra.Text).DisposeWith(disposables);
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.StreamSecurity, v => v.cmbStreamSecurity.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.SelectedSource.Sni, v => v.txtSNI.Text).DisposeWith(disposables);
|
||||||
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);
|
||||||
@@ -220,7 +210,7 @@ namespace v2rayN.Views
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.Title = $"{profileItem.ConfigType}";
|
this.Title = $"{profileItem.ConfigType}";
|
||||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.FollowSystemTheme ? !WindowsUtils.IsLightTheme() : AppHandler.Instance.Config.UiItem.ColorModeDark);
|
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.FollowSystemTheme ? WindowsUtils.IsDarkTheme() : AppHandler.Instance.Config.UiItem.ColorModeDark);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||||
@@ -295,7 +285,7 @@ namespace v2rayN.Views
|
|||||||
cmbHeaderType.Items.Add(it);
|
cmbHeaderType.Items.Add(it);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (network is nameof(ETransport.splithttp) or nameof(ETransport.xhttp))
|
else if (network is nameof(ETransport.xhttp))
|
||||||
{
|
{
|
||||||
Global.XhttpMode.ForEach(it =>
|
Global.XhttpMode.ForEach(it =>
|
||||||
{
|
{
|
||||||
@@ -345,7 +335,6 @@ namespace v2rayN.Views
|
|||||||
tipPath.Text = ResUI.TransportPathTip1;
|
tipPath.Text = ResUI.TransportPathTip1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case nameof(ETransport.splithttp):
|
|
||||||
case nameof(ETransport.xhttp):
|
case nameof(ETransport.xhttp):
|
||||||
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
|
tipRequestHost.Text = ResUI.TransportRequestHostTip2;
|
||||||
tipPath.Text = ResUI.TransportPathTip1;
|
tipPath.Text = ResUI.TransportPathTip1;
|
||||||
|
|||||||
@@ -29,24 +29,6 @@
|
|||||||
materialDesign:TextFieldAssist.HasClearButton="True"
|
materialDesign:TextFieldAssist.HasClearButton="True"
|
||||||
Style="{StaticResource DefTextBox}" />
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
|
||||||
<TextBlock
|
|
||||||
Margin="{StaticResource MarginLeftRight8}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
|
||||||
Text="{x:Static resx:ResUI.TbSorting}" />
|
|
||||||
<ComboBox
|
|
||||||
x:Name="cmbSorting"
|
|
||||||
Width="100"
|
|
||||||
Margin="{StaticResource MarginLeftRight8}"
|
|
||||||
Style="{StaticResource DefComboBox}">
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpSpeed}" />
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownSpeed}" />
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpTraffic}" />
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownTraffic}" />
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingTime}" />
|
|
||||||
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingHost}" />
|
|
||||||
</ComboBox>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
x:Name="btnConnectionCloseAll"
|
x:Name="btnConnectionCloseAll"
|
||||||
Width="24"
|
Width="24"
|
||||||
@@ -57,6 +39,16 @@
|
|||||||
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Close" />
|
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Close" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
x:Name="btnAutofitColumnWidth"
|
||||||
|
Width="24"
|
||||||
|
Height="24"
|
||||||
|
Margin="{StaticResource MarginLeftRight8}"
|
||||||
|
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}"
|
||||||
|
ToolTip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
|
||||||
|
<materialDesign:PackIcon VerticalAlignment="Center" Kind="ArrowSplitVertical" />
|
||||||
|
</Button>
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="{StaticResource MarginLeftRight8}"
|
Margin="{StaticResource MarginLeftRight8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@@ -74,7 +66,6 @@
|
|||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CanUserAddRows="False"
|
CanUserAddRows="False"
|
||||||
CanUserResizeRows="False"
|
CanUserResizeRows="False"
|
||||||
CanUserSortColumns="False"
|
|
||||||
EnableRowVirtualization="True"
|
EnableRowVirtualization="True"
|
||||||
GridLinesVisibility="All"
|
GridLinesVisibility="All"
|
||||||
HeadersVisibility="Column"
|
HeadersVisibility="Column"
|
||||||
@@ -88,11 +79,11 @@
|
|||||||
</DataGrid.ContextMenu>
|
</DataGrid.ContextMenu>
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="240"
|
Width="300"
|
||||||
Binding="{Binding Host}"
|
Binding="{Binding Host}"
|
||||||
Header="{x:Static resx:ResUI.TbSortingHost}" />
|
Header="{x:Static resx:ResUI.TbSortingHost}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="160"
|
Width="500"
|
||||||
Binding="{Binding Chain}"
|
Binding="{Binding Chain}"
|
||||||
Header="{x:Static resx:ResUI.TbSortingChain}" />
|
Header="{x:Static resx:ResUI.TbSortingChain}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
@@ -100,17 +91,9 @@
|
|||||||
Binding="{Binding Network}"
|
Binding="{Binding Network}"
|
||||||
Header="{x:Static resx:ResUI.TbSortingNetwork}" />
|
Header="{x:Static resx:ResUI.TbSortingNetwork}" />
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="100"
|
Width="160"
|
||||||
Binding="{Binding Type}"
|
Binding="{Binding Type}"
|
||||||
Header="{x:Static resx:ResUI.TbSortingType}" />
|
Header="{x:Static resx:ResUI.TbSortingType}" />
|
||||||
<DataGridTextColumn
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding UploadTraffic}"
|
|
||||||
Header="{x:Static resx:ResUI.TbSortingUpTraffic}" />
|
|
||||||
<DataGridTextColumn
|
|
||||||
Width="100"
|
|
||||||
Binding="{Binding DownloadTraffic}"
|
|
||||||
Header="{x:Static resx:ResUI.TbSortingDownTraffic}" />
|
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn
|
||||||
Width="100"
|
Width="100"
|
||||||
Binding="{Binding Elapsed}"
|
Binding="{Binding Elapsed}"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using System.Reactive.Disposables;
|
using System.Reactive.Disposables;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
|
||||||
namespace v2rayN.Views
|
namespace v2rayN.Views
|
||||||
@@ -14,6 +15,7 @@ namespace v2rayN.Views
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
|
ViewModel = new ClashConnectionsViewModel(UpdateViewHandler);
|
||||||
|
btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
|
||||||
|
|
||||||
this.WhenActivated(disposables =>
|
this.WhenActivated(disposables =>
|
||||||
{
|
{
|
||||||
@@ -24,7 +26,6 @@ namespace v2rayN.Views
|
|||||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.menuConnectionCloseAll).DisposeWith(disposables);
|
||||||
|
|
||||||
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.HostFilter, v => v.txtHostFilter.Text).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.SortingSelected, v => v.cmbSorting.SelectedIndex).DisposeWith(disposables);
|
|
||||||
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.ConnectionCloseAllCmd, v => v.btnConnectionCloseAll).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.AutoRefresh, v => v.togAutoRefresh.IsChecked).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
@@ -46,6 +47,18 @@ namespace v2rayN.Views
|
|||||||
return await Task.FromResult(true);
|
return await Task.FromResult(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void BtnAutofitColumnWidth_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
AutofitColumnWidth();
|
||||||
|
}
|
||||||
|
private void AutofitColumnWidth()
|
||||||
|
{
|
||||||
|
foreach (var it in lstConnections.Columns)
|
||||||
|
{
|
||||||
|
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void btnClose_Click(object sender, System.Windows.RoutedEventArgs e)
|
private void btnClose_Click(object sender, System.Windows.RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
ViewModel?.ClashConnectionClose(false);
|
ViewModel?.ClashConnectionClose(false);
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
<reactiveui:ReactiveUserControl
|
<reactiveui:ReactiveUserControl
|
||||||
x:Class="v2rayN.Views.ClashProxiesView"
|
x:Class="v2rayN.Views.ClashProxiesView"
|
||||||
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:converters="clr-namespace:v2rayN.Converters"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:reactiveui="http://reactiveui.net"
|
xmlns:reactiveui="http://reactiveui.net"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
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"
|
||||||
|
xmlns:converters="clr-namespace:v2rayN.Converters"
|
||||||
d:DesignHeight="450"
|
d:DesignHeight="450"
|
||||||
d:DesignWidth="800"
|
d:DesignWidth="800"
|
||||||
x:TypeArguments="vms:ClashProxiesViewModel"
|
x:TypeArguments="vms:ClashProxiesViewModel"
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace v2rayN.Views
|
|||||||
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4V2rayCmd, v => v.btnImportDefConfig4V2ray).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4V2rayCmd, v => v.btnImportDefConfig4V2ray).DisposeWith(disposables);
|
||||||
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCmd, v => v.btnImportDefConfig4Singbox).DisposeWith(disposables);
|
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCmd, v => v.btnImportDefConfig4Singbox).DisposeWith(disposables);
|
||||||
});
|
});
|
||||||
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.FollowSystemTheme ? !WindowsUtils.IsLightTheme() : AppHandler.Instance.Config.UiItem.ColorModeDark);
|
WindowsUtils.SetDarkBorder(this, AppHandler.Instance.Config.UiItem.FollowSystemTheme ? WindowsUtils.IsDarkTheme() : AppHandler.Instance.Config.UiItem.ColorModeDark);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace v2rayN.Views
|
|||||||
|
|
||||||
HotkeyHandler.Instance.IsPause = true;
|
HotkeyHandler.Instance.IsPause = true;
|
||||||
this.Closing += (s, e) => HotkeyHandler.Instance.IsPause = false;
|
this.Closing += (s, e) => HotkeyHandler.Instance.IsPause = false;
|
||||||
WindowsUtils.SetDarkBorder(this, _config.UiItem.FollowSystemTheme ? !WindowsUtils.IsLightTheme() : _config.UiItem.ColorModeDark);
|
WindowsUtils.SetDarkBorder(this, _config.UiItem.FollowSystemTheme ? WindowsUtils.IsDarkTheme() : _config.UiItem.ColorModeDark);
|
||||||
InitData();
|
InitData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ namespace v2rayN.Views
|
|||||||
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
ViewModel = new MainWindowViewModel(UpdateViewHandler);
|
||||||
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
|
Locator.CurrentMutable.RegisterLazySingleton(() => ViewModel, typeof(MainWindowViewModel));
|
||||||
|
|
||||||
WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
|
|
||||||
switch (_config.UiItem.MainGirdOrientation)
|
switch (_config.UiItem.MainGirdOrientation)
|
||||||
{
|
{
|
||||||
case EGirdOrientation.Horizontal:
|
case EGirdOrientation.Horizontal:
|
||||||
@@ -48,6 +47,7 @@ namespace v2rayN.Views
|
|||||||
tabMsgView.Content ??= new MsgView();
|
tabMsgView.Content ??= new MsgView();
|
||||||
tabClashProxies.Content ??= new ClashProxiesView();
|
tabClashProxies.Content ??= new ClashProxiesView();
|
||||||
tabClashConnections.Content ??= new ClashConnectionsView();
|
tabClashConnections.Content ??= new ClashConnectionsView();
|
||||||
|
gridMain.Visibility = Visibility.Visible;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EGirdOrientation.Vertical:
|
case EGirdOrientation.Vertical:
|
||||||
@@ -55,6 +55,7 @@ namespace v2rayN.Views
|
|||||||
tabMsgView1.Content ??= new MsgView();
|
tabMsgView1.Content ??= new MsgView();
|
||||||
tabClashProxies1.Content ??= new ClashProxiesView();
|
tabClashProxies1.Content ??= new ClashProxiesView();
|
||||||
tabClashConnections1.Content ??= new ClashConnectionsView();
|
tabClashConnections1.Content ??= new ClashConnectionsView();
|
||||||
|
gridMain1.Visibility = Visibility.Visible;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EGirdOrientation.Tab:
|
case EGirdOrientation.Tab:
|
||||||
@@ -63,6 +64,7 @@ namespace v2rayN.Views
|
|||||||
tabMsgView2.Content ??= new MsgView();
|
tabMsgView2.Content ??= new MsgView();
|
||||||
tabClashProxies2.Content ??= new ClashProxiesView();
|
tabClashProxies2.Content ??= new ClashProxiesView();
|
||||||
tabClashConnections2.Content ??= new ClashConnectionsView();
|
tabClashConnections2.Content ??= new ClashConnectionsView();
|
||||||
|
gridMain2.Visibility = Visibility.Visible;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pbTheme.Content ??= new ThemeSettingView();
|
pbTheme.Content ??= new ThemeSettingView();
|
||||||
@@ -108,7 +110,6 @@ namespace v2rayN.Views
|
|||||||
switch (_config.UiItem.MainGirdOrientation)
|
switch (_config.UiItem.MainGirdOrientation)
|
||||||
{
|
{
|
||||||
case EGirdOrientation.Horizontal:
|
case EGirdOrientation.Horizontal:
|
||||||
gridMain.Visibility = Visibility.Visible;
|
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.Visibility).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView.Visibility).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.Visibility).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies.Visibility).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections.Visibility).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections.Visibility).DisposeWith(disposables);
|
||||||
@@ -116,7 +117,6 @@ namespace v2rayN.Views
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case EGirdOrientation.Vertical:
|
case EGirdOrientation.Vertical:
|
||||||
gridMain1.Visibility = Visibility.Visible;
|
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.Visibility).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabMsgView1.Visibility).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.Visibility).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies1.Visibility).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.Visibility).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections1.Visibility).DisposeWith(disposables);
|
||||||
@@ -125,7 +125,6 @@ namespace v2rayN.Views
|
|||||||
|
|
||||||
case EGirdOrientation.Tab:
|
case EGirdOrientation.Tab:
|
||||||
default:
|
default:
|
||||||
gridMain2.Visibility = Visibility.Visible;
|
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.Visibility).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashProxies2.Visibility).DisposeWith(disposables);
|
||||||
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.Visibility).DisposeWith(disposables);
|
this.OneWayBind(ViewModel, vm => vm.ShowClashUI, v => v.tabClashConnections2.Visibility).DisposeWith(disposables);
|
||||||
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
|
this.Bind(ViewModel, vm => vm.TabMainSelectedIndex, v => v.tabMain2.SelectedIndex).DisposeWith(disposables);
|
||||||
@@ -142,6 +141,7 @@ namespace v2rayN.Views
|
|||||||
|
|
||||||
RestoreUI();
|
RestoreUI();
|
||||||
AddHelpMenuItem();
|
AddHelpMenuItem();
|
||||||
|
WindowsHandler.Instance.RegisterGlobalHotkey(_config, OnHotkeyHandler, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Event
|
#region Event
|
||||||
@@ -283,13 +283,6 @@ namespace v2rayN.Views
|
|||||||
{
|
{
|
||||||
switch (e.Key)
|
switch (e.Key)
|
||||||
{
|
{
|
||||||
case Key.V:
|
|
||||||
if (_backupAndRestoreView?.IsVisible == true) return;
|
|
||||||
|
|
||||||
var clipboardData = WindowsUtils.GetClipboardData();
|
|
||||||
ViewModel?.AddServerViaClipboardAsync(clipboardData);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Key.S:
|
case Key.S:
|
||||||
ScanScreenTaskAsync().ContinueWith(_ => { });
|
ScanScreenTaskAsync().ContinueWith(_ => { });
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -76,8 +76,10 @@
|
|||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
IsReadOnly="True"
|
IsReadOnly="True"
|
||||||
IsReadOnlyCaretVisible="True"
|
IsReadOnlyCaretVisible="True"
|
||||||
|
IsUndoEnabled="False"
|
||||||
TextAlignment="Left"
|
TextAlignment="Left"
|
||||||
TextWrapping="Wrap"
|
TextWrapping="Wrap"
|
||||||
|
UndoLimit="0"
|
||||||
VerticalScrollBarVisibility="Visible">
|
VerticalScrollBarVisibility="Visible">
|
||||||
<TextBox.ContextMenu>
|
<TextBox.ContextMenu>
|
||||||
<ContextMenu Style="{StaticResource DefContextMenu}">
|
<ContextMenu Style="{StaticResource DefContextMenu}">
|
||||||
|
|||||||
@@ -698,7 +698,7 @@
|
|||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="14"
|
Grid.Row="13"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
@@ -706,6 +706,22 @@
|
|||||||
Text="{x:Static resx:ResUI.TbSettingsAutoUpdateInterval}" />
|
Text="{x:Static resx:ResUI.TbSettingsAutoUpdateInterval}" />
|
||||||
<TextBox
|
<TextBox
|
||||||
x:Name="txtautoUpdateInterval"
|
x:Name="txtautoUpdateInterval"
|
||||||
|
Grid.Row="13"
|
||||||
|
Grid.Column="1"
|
||||||
|
Width="200"
|
||||||
|
Margin="{StaticResource Margin8}"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
Style="{StaticResource DefTextBox}" />
|
||||||
|
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="14"
|
||||||
|
Grid.Column="0"
|
||||||
|
Margin="{StaticResource Margin8}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbSettingsTrayMenuServersLimit}" />
|
||||||
|
<TextBox
|
||||||
|
x:Name="txttrayMenuServersLimit"
|
||||||
Grid.Row="14"
|
Grid.Row="14"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
@@ -719,15 +735,23 @@
|
|||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsTrayMenuServersLimit}" />
|
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
|
||||||
<TextBox
|
<ComboBox
|
||||||
x:Name="txttrayMenuServersLimit"
|
x:Name="cmbcurrentFontFamily"
|
||||||
Grid.Row="15"
|
Grid.Row="15"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
HorizontalAlignment="Left"
|
MaxDropDownHeight="1000"
|
||||||
Style="{StaticResource DefTextBox}" />
|
Style="{StaticResource DefComboBox}" />
|
||||||
|
<TextBlock
|
||||||
|
Grid.Row="15"
|
||||||
|
Grid.Column="2"
|
||||||
|
Margin="{StaticResource Margin4}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
|
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamilyTip}"
|
||||||
|
TextWrapping="Wrap" />
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="16"
|
Grid.Row="16"
|
||||||
@@ -735,23 +759,15 @@
|
|||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
Style="{StaticResource ToolbarTextBlock}"
|
||||||
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamily}" />
|
Text="{x:Static resx:ResUI.TbSettingsSpeedTestPageSize}" />
|
||||||
<ComboBox
|
<TextBox
|
||||||
x:Name="cmbcurrentFontFamily"
|
x:Name="txtSpeedTestPageSize"
|
||||||
Grid.Row="16"
|
Grid.Row="16"
|
||||||
Grid.Column="1"
|
Grid.Column="1"
|
||||||
Width="200"
|
Width="200"
|
||||||
Margin="{StaticResource Margin8}"
|
Margin="{StaticResource Margin8}"
|
||||||
MaxDropDownHeight="1000"
|
HorizontalAlignment="Left"
|
||||||
Style="{StaticResource DefComboBox}" />
|
Style="{StaticResource DefTextBox}" />
|
||||||
<TextBlock
|
|
||||||
Grid.Row="16"
|
|
||||||
Grid.Column="2"
|
|
||||||
Margin="{StaticResource Margin4}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Style="{StaticResource ToolbarTextBlock}"
|
|
||||||
Text="{x:Static resx:ResUI.TbSettingsCurrentFontFamilyTip}"
|
|
||||||
TextWrapping="Wrap" />
|
|
||||||
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Grid.Row="17"
|
Grid.Row="17"
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user