Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dffc6d9a9b | ||
|
|
c9989108bd | ||
|
|
386c86bfa6 | ||
|
|
925cf16c50 | ||
|
|
c561916b67 | ||
|
|
d41a73b44b | ||
|
|
7995bdd4df | ||
|
|
df95cc6af7 | ||
|
|
c669e72189 | ||
|
|
46db5efef3 | ||
|
|
610418b42b | ||
|
|
508eb24fc3 | ||
|
|
6973272dd0 | ||
|
|
d820c4367e | ||
|
|
96e1f85d6f | ||
|
|
6fa5ca5aa9 | ||
|
|
1d1f5641eb | ||
|
|
3f79df21d9 | ||
|
|
ac1231ad54 | ||
|
|
8662d94ab6 | ||
|
|
3d23f3e3a2 | ||
|
|
6715d7dce6 | ||
|
|
dad35f57d0 | ||
|
|
f779e311ed | ||
|
|
ce7c41e3ff | ||
|
|
74bb01d044 | ||
|
|
82f9698c0d | ||
|
|
6911883995 | ||
|
|
47c509faf6 | ||
|
|
8704942209 | ||
|
|
e8cdc29bb5 | ||
|
|
191a7a6574 | ||
|
|
ad5d21db5a | ||
|
|
569e939492 | ||
|
|
6a17c539d1 | ||
|
|
f8a4f946e4 | ||
|
|
0715fa85ce | ||
|
|
1360051f0c | ||
|
|
42c4f9a6c6 | ||
|
|
11691d0128 | ||
|
|
26fe9c63a3 | ||
|
|
30cd033b42 | ||
|
|
e21c0b4d62 | ||
|
|
916055d8bd | ||
|
|
683ca8af14 | ||
|
|
70151db91b | ||
|
|
da3d4c36a9 | ||
|
|
1d01476523 | ||
|
|
75ceba1b08 | ||
|
|
493c37e7d5 | ||
|
|
6d686b284d | ||
|
|
60fcf6174e | ||
|
|
4141f451b7 | ||
|
|
7a9ee6e9e2 | ||
|
|
cb28c31519 | ||
|
|
84f93f2ae6 | ||
|
|
30c09a7b54 | ||
|
|
b3874a78b9 | ||
|
|
3e71965cd4 | ||
|
|
3df57f74ba | ||
|
|
7972cb8e1f | ||
|
|
0d74452c6c | ||
|
|
f947f63e6d | ||
|
|
fefa7ded5a | ||
|
|
a46a4ad7c1 | ||
|
|
e46f680651 | ||
|
|
93a20852f5 | ||
|
|
298bb64e66 | ||
|
|
0e5ac65f55 | ||
|
|
cb6122f872 | ||
|
|
06500e0218 | ||
|
|
9ddf0b42e7 | ||
|
|
2056377f55 | ||
|
|
7065dabc94 | ||
|
|
9e2336a71e | ||
|
|
d239c627f3 | ||
|
|
984b36d34e | ||
|
|
c81bc2f536 | ||
|
|
87f7e65076 | ||
|
|
9985b68b6b | ||
|
|
fb4b8b2789 | ||
|
|
3812ccc780 | ||
|
|
608a6c387a | ||
|
|
4875b37f70 | ||
|
|
2f3fba73de | ||
|
|
2ab1b9068f | ||
|
|
b9613875ce | ||
|
|
5d2aea6b4f | ||
|
|
5824e18ed6 | ||
|
|
4f8648cbc9 | ||
|
|
01b021b2c3 | ||
|
|
331e8ce960 | ||
|
|
a2cfe6fa51 | ||
|
|
8381fefb78 | ||
|
|
d3b95d781a | ||
|
|
3a4a96f87a | ||
|
|
3d462c4be3 | ||
|
|
82b366cd9b | ||
|
|
897a4e5635 | ||
|
|
8ea76fd318 | ||
|
|
693a96fff2 | ||
|
|
8b4e2f8f23 | ||
|
|
13b164acac | ||
|
|
e590547b30 | ||
|
|
5a0fdd971a |
14
.github/workflows/winget-publish.yml
vendored
14
.github/workflows/winget-publish.yml
vendored
@@ -22,10 +22,18 @@ jobs:
|
||||
$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\.zip*' | Select -ExpandProperty browser_download_url
|
||||
|
||||
|
||||
$x64InstallerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-64\.zip' | Select -ExpandProperty browser_download_url
|
||||
$arm64InstallerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-arm64\.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
|
||||
|
||||
Write-Host "Updating with both x64 and arm64 installers"
|
||||
Write-Host "Version: $ver"
|
||||
Write-Host "x64 URL: $x64InstallerUrl"
|
||||
Write-Host "arm64 URL: $arm64InstallerUrl"
|
||||
|
||||
.\wingetcreate.exe update $wingetPackage -s -v $ver -u "$x64InstallerUrl|x64" "$arm64InstallerUrl|arm64" -t $gitToken
|
||||
|
||||
4
LICENSE
4
LICENSE
@@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
Copyright (C) 2019-Present 2dust
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
v2rayN Copyright (C) 2019-Present 2dust
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
# v2rayN
|
||||
A GUI client for Windows, Linux and macOS, support [Xray](https://github.com/XTLS/Xray-core) and [sing-box](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
|
||||
|
||||
A GUI client for Windows, Linux and macOS, support [Xray](https://github.com/XTLS/Xray-core)
|
||||
and [sing-box](https://github.com/SagerNet/sing-box)
|
||||
and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
|
||||
|
||||
[](https://github.com/2dust/v2rayN/commits/master)
|
||||
[](https://www.codefactor.io/repository/github/2dust/v2rayn)
|
||||
[](https://github.com/2dust/v2rayN/releases)
|
||||
[](https://t.me/v2rayn)
|
||||
|
||||
|
||||
## How to use
|
||||
|
||||
Read the [Wiki](https://github.com/2dust/v2rayN/wiki) for details.
|
||||
|
||||
## Telegram Channel
|
||||
|
||||
[github_2dust](https://t.me/github_2dust)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.12.0</Version>
|
||||
<Version>7.13.7</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -5,26 +5,26 @@
|
||||
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.8" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.2.8" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.8" />
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.8" />
|
||||
<PackageVersion Include="CliWrap" Version="3.8.2" />
|
||||
<PackageVersion Include="Downloader" Version="3.3.4" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.3" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="11.3.3" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.3" />
|
||||
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.3" />
|
||||
<PackageVersion Include="CliWrap" Version="3.9.0" />
|
||||
<PackageVersion Include="Downloader" Version="4.0.2" />
|
||||
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
|
||||
<PackageVersion Include="MaterialDesignThemes" Version="5.2.1" />
|
||||
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
|
||||
<PackageVersion Include="QRCoder" Version="1.6.0" />
|
||||
<PackageVersion Include="ReactiveUI" Version="20.2.45" />
|
||||
<PackageVersion Include="ReactiveUI" Version="20.4.1" />
|
||||
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
|
||||
<PackageVersion Include="ReactiveUI.WPF" Version="20.2.45" />
|
||||
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.7" />
|
||||
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.7" />
|
||||
<PackageVersion Include="Splat.NLog" Version="15.3.1" />
|
||||
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" />
|
||||
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.9" />
|
||||
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.9" />
|
||||
<PackageVersion Include="Splat.NLog" Version="15.4.1" />
|
||||
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
|
||||
<PackageVersion Include="TaskScheduler" Version="2.12.1" />
|
||||
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
|
||||
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
|
||||
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
|
||||
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1,100 +0,0 @@
|
||||
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, ' '));
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
public class DesUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Encrypt
|
||||
/// </summary>
|
||||
/// <param name="text"></param>
|
||||
/// /// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static string Encrypt(string? text, string? key = null)
|
||||
{
|
||||
if (text.IsNullOrEmpty())
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
|
||||
var dsp = DES.Create();
|
||||
using var memStream = new MemoryStream();
|
||||
using var cryStream = new CryptoStream(memStream, dsp.CreateEncryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
|
||||
using var sWriter = new StreamWriter(cryStream);
|
||||
sWriter.Write(text);
|
||||
sWriter.Flush();
|
||||
cryStream.FlushFinalBlock();
|
||||
memStream.Flush();
|
||||
return Convert.ToBase64String(memStream.GetBuffer(), 0, (int)memStream.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt
|
||||
/// </summary>
|
||||
/// <param name="encryptText"></param>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static string Decrypt(string? encryptText, string? key = null)
|
||||
{
|
||||
if (encryptText.IsNullOrEmpty())
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
|
||||
var dsp = DES.Create();
|
||||
var buffer = Convert.FromBase64String(encryptText);
|
||||
|
||||
using var memStream = new MemoryStream();
|
||||
using var cryStream = new CryptoStream(memStream, dsp.CreateDecryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
|
||||
cryStream.Write(buffer, 0, buffer.Length);
|
||||
cryStream.FlushFinalBlock();
|
||||
return Encoding.UTF8.GetString(memStream.ToArray());
|
||||
}
|
||||
|
||||
private static void GetKeyIv(string key, out byte[] rgbKey, out byte[] rgbIv)
|
||||
{
|
||||
if (key.IsNullOrEmpty())
|
||||
{
|
||||
throw new ArgumentNullException("The key cannot be null");
|
||||
}
|
||||
if (key.Length <= 8)
|
||||
{
|
||||
throw new ArgumentNullException("The key length cannot be less than 8 characters.");
|
||||
}
|
||||
|
||||
rgbKey = Encoding.ASCII.GetBytes(key.Substring(0, 8));
|
||||
rgbIv = Encoding.ASCII.GetBytes(key.Insert(0, "w").Substring(0, 8));
|
||||
}
|
||||
|
||||
private static string GetDefaultKey()
|
||||
{
|
||||
return Utils.GetMd5(Utils.GetHomePath() + "DesUtils");
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ public class DownloaderHelper
|
||||
var downloadOpt = new DownloadConfiguration()
|
||||
{
|
||||
Timeout = timeout * 1000,
|
||||
MaxTryAgainOnFailover = 2,
|
||||
MaxTryAgainOnFailure = 2,
|
||||
RequestConfiguration =
|
||||
{
|
||||
Headers = headers,
|
||||
@@ -64,7 +64,7 @@ public class DownloaderHelper
|
||||
var downloadOpt = new DownloadConfiguration()
|
||||
{
|
||||
Timeout = timeout * 1000,
|
||||
MaxTryAgainOnFailover = 2,
|
||||
MaxTryAgainOnFailure = 2,
|
||||
RequestConfiguration =
|
||||
{
|
||||
Timeout= timeout * 1000,
|
||||
@@ -135,7 +135,7 @@ public class DownloaderHelper
|
||||
var downloadOpt = new DownloadConfiguration()
|
||||
{
|
||||
Timeout = timeout * 1000,
|
||||
MaxTryAgainOnFailover = 2,
|
||||
MaxTryAgainOnFailure = 2,
|
||||
RequestConfiguration =
|
||||
{
|
||||
Timeout= timeout * 1000,
|
||||
|
||||
@@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
|
||||
public static class StringEx
|
||||
public static class Extension
|
||||
{
|
||||
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
|
||||
{
|
||||
@@ -79,4 +79,9 @@ public static class StringEx
|
||||
{
|
||||
return int.TryParse(value, out var result) ? result : defaultValue;
|
||||
}
|
||||
|
||||
public static List<string> AppendEmpty(this IEnumerable<string> source)
|
||||
{
|
||||
return source.Concat(new[] { string.Empty }).ToList();
|
||||
}
|
||||
}
|
||||
@@ -223,4 +223,28 @@ public static class FileManager
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Linux shell file with the specified contents.
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="contents"></param>
|
||||
/// <param name="overwrite"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<string> CreateLinuxShellFile(string fileName, string contents, bool overwrite)
|
||||
{
|
||||
var shFilePath = Utils.GetBinConfigPath(fileName);
|
||||
|
||||
// Check if the file already exists and if we should overwrite it
|
||||
if (!overwrite && File.Exists(shFilePath))
|
||||
{
|
||||
return shFilePath;
|
||||
}
|
||||
|
||||
File.Delete(shFilePath);
|
||||
await File.WriteAllTextAsync(shFilePath, contents);
|
||||
await Utils.SetLinuxChmod(shFilePath);
|
||||
|
||||
return shFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,5 +128,8 @@ public class JsonUtils
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj);
|
||||
public static JsonNode? SerializeToNode(object? obj, JsonSerializerOptions? options = null)
|
||||
{
|
||||
return JsonSerializer.SerializeToNode(obj, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,6 +323,14 @@ public class Utils
|
||||
return text.Replace(",", ",").Replace(Environment.NewLine, ",");
|
||||
}
|
||||
|
||||
public static List<string> GetEnumNames<TEnum>() where TEnum : Enum
|
||||
{
|
||||
return Enum.GetValues(typeof(TEnum))
|
||||
.Cast<TEnum>()
|
||||
.Select(e => e.ToString())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
#endregion 转换函数
|
||||
|
||||
#region 数据检查
|
||||
@@ -382,13 +390,44 @@ public class Utils
|
||||
{
|
||||
if (IPAddress.TryParse(ip, out var address))
|
||||
{
|
||||
// Loopback address check (127.0.0.1 for IPv4, ::1 for IPv6)
|
||||
if (IPAddress.IsLoopback(address))
|
||||
return true;
|
||||
|
||||
var ipBytes = address.GetAddressBytes();
|
||||
if (ipBytes[0] == 10)
|
||||
return true;
|
||||
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31)
|
||||
return true;
|
||||
if (ipBytes[0] == 192 && ipBytes[1] == 168)
|
||||
return true;
|
||||
if (address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
// IPv4 private address check
|
||||
if (ipBytes[0] == 10)
|
||||
return true;
|
||||
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31)
|
||||
return true;
|
||||
if (ipBytes[0] == 192 && ipBytes[1] == 168)
|
||||
return true;
|
||||
}
|
||||
else if (address.AddressFamily == AddressFamily.InterNetworkV6)
|
||||
{
|
||||
// IPv6 private address check
|
||||
// Link-local address fe80::/10
|
||||
if (ipBytes[0] == 0xfe && (ipBytes[1] & 0xc0) == 0x80)
|
||||
return true;
|
||||
|
||||
// Unique local address fc00::/7 (typically fd00::/8)
|
||||
if ((ipBytes[0] & 0xfe) == 0xfc)
|
||||
return true;
|
||||
|
||||
// Private portion in IPv4-mapped addresses ::ffff:0:0/96
|
||||
if (address.IsIPv4MappedToIPv6)
|
||||
{
|
||||
var ipv4Bytes = ipBytes.Skip(12).ToArray();
|
||||
if (ipv4Bytes[0] == 10)
|
||||
return true;
|
||||
if (ipv4Bytes[0] == 172 && ipv4Bytes[1] >= 16 && ipv4Bytes[1] <= 31)
|
||||
return true;
|
||||
if (ipv4Bytes[0] == 192 && ipv4Bytes[1] == 168)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -427,11 +466,11 @@ public class Utils
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int GetFreePort(int defaultPort = 9090)
|
||||
public static int GetFreePort(int defaultPort = 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Utils.PortInUse(defaultPort))
|
||||
if (!(defaultPort == 0 || Utils.PortInUse(defaultPort)))
|
||||
{
|
||||
return defaultPort;
|
||||
}
|
||||
@@ -582,7 +621,7 @@ public class Utils
|
||||
var result = await cmd.ExecuteBufferedAsync();
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
return result.StandardOutput.ToString();
|
||||
return result.StandardOutput ?? "";
|
||||
}
|
||||
|
||||
Logging.SaveLog(result.ToString() ?? "");
|
||||
@@ -816,18 +855,6 @@ public class Utils
|
||||
return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
return false;
|
||||
//else
|
||||
//{
|
||||
// var id = GetLinuxUserId().Result ?? "1000";
|
||||
// if (int.TryParse(id, out var userId))
|
||||
// {
|
||||
// return userId == 0;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
private static async Task<string?> GetLinuxUserId()
|
||||
@@ -839,14 +866,46 @@ public class Utils
|
||||
public static async Task<string?> SetLinuxChmod(string? fileName)
|
||||
{
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (SetUnixFileMode(fileName))
|
||||
{
|
||||
Logging.SaveLog($"Successfully set the file execution permission, {fileName}");
|
||||
return "";
|
||||
}
|
||||
|
||||
if (fileName.Contains(' '))
|
||||
{
|
||||
fileName = fileName.AppendQuotes();
|
||||
//File.SetUnixFileMode(fileName, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
|
||||
}
|
||||
var arg = new List<string>() { "-c", $"chmod +x {fileName}" };
|
||||
return await GetCliWrapOutput(Global.LinuxBash, arg);
|
||||
}
|
||||
|
||||
public static bool SetUnixFileMode(string? fileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (fileName.IsNullOrEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
var currentMode = File.GetUnixFileMode(fileName);
|
||||
File.SetUnixFileMode(fileName, currentMode | UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("SetUnixFileMode", ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static async Task<string?> GetLinuxFontFamily(string lang)
|
||||
{
|
||||
// var arg = new List<string>() { "-c", $"fc-list :lang={lang} family" };
|
||||
|
||||
@@ -11,5 +11,6 @@ public enum EConfigType
|
||||
Hysteria2 = 7,
|
||||
TUIC = 8,
|
||||
WireGuard = 9,
|
||||
HTTP = 10
|
||||
HTTP = 10,
|
||||
Anytls = 11
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@ public enum ECoreType
|
||||
hysteria2 = 26,
|
||||
brook = 27,
|
||||
overtls = 28,
|
||||
shadowquic = 29,
|
||||
v2rayN = 99
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@ public class Global
|
||||
public const string GithubUrl = "https://github.com";
|
||||
public const string GithubApiUrl = "https://api.github.com/repos";
|
||||
public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat";
|
||||
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
|
||||
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 ConfigFileName = "guiNConfig.json";
|
||||
@@ -40,6 +38,8 @@ public class Global
|
||||
public const string PacFileName = NamespaceSample + "pac";
|
||||
public const string ProxySetOSXShellFileName = NamespaceSample + "proxy_set_osx_sh";
|
||||
public const string ProxySetLinuxShellFileName = NamespaceSample + "proxy_set_linux_sh";
|
||||
public const string KillAsSudoOSXShellFileName = NamespaceSample + "kill_as_sudo_osx_sh";
|
||||
public const string KillAsSudoLinuxShellFileName = NamespaceSample + "kill_as_sudo_linux_sh";
|
||||
|
||||
public const string DefaultSecurity = "auto";
|
||||
public const string DefaultNetwork = "tcp";
|
||||
@@ -76,6 +76,13 @@ public class Global
|
||||
public const int SpeedTestPageSize = 1000;
|
||||
public const string LinuxBash = "/bin/bash";
|
||||
|
||||
public const string SingboxDirectDNSTag = "direct_dns";
|
||||
public const string SingboxRemoteDNSTag = "remote_dns";
|
||||
public const string SingboxOutboundResolverTag = "outbound_resolver";
|
||||
public const string SingboxFinalResolverTag = "final_resolver";
|
||||
public const string SingboxHostsDNSTag = "hosts_dns";
|
||||
public const string SingboxFakeDNSTag = "fake_dns";
|
||||
|
||||
public static readonly List<string> IEProxyProtocols =
|
||||
[
|
||||
"{ip}:{http_port}",
|
||||
@@ -129,24 +136,24 @@ public class Global
|
||||
];
|
||||
|
||||
public static readonly List<string> SingboxRulesetSources =
|
||||
[
|
||||
"",
|
||||
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-rules-dat@release/sing-box/rule-set-{0}/{1}.srs",
|
||||
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-sing-box-rules@rule-set/{1}.srs"
|
||||
];
|
||||
[
|
||||
"",
|
||||
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/release/sing-box/rule-set-{0}/{1}.srs",
|
||||
@"https://raw.githubusercontent.com/chocolate4u/Iran-sing-box-rules/rule-set/{1}.srs"
|
||||
];
|
||||
|
||||
public static readonly List<string> RoutingRulesSources =
|
||||
[
|
||||
"",
|
||||
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/template.json",
|
||||
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/template.json"
|
||||
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-custom-routing-list/main/v2rayN/template.json",
|
||||
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/template.json"
|
||||
];
|
||||
|
||||
public static readonly List<string> DNSTemplateSources =
|
||||
[
|
||||
"",
|
||||
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/",
|
||||
@"https://cdn.jsdelivr.net/gh/Chocolate4U/Iran-v2ray-rules@main/v2rayN/"
|
||||
@"https://raw.githubusercontent.com/runetfreedom/russia-v2ray-custom-routing-list/main/v2rayN/",
|
||||
@"https://raw.githubusercontent.com/Chocolate4U/Iran-v2ray-rules/main/v2rayN/"
|
||||
];
|
||||
|
||||
public static readonly Dictionary<string, string> UserAgentTexts = new()
|
||||
@@ -169,7 +176,8 @@ public class Global
|
||||
{ EConfigType.Trojan, "trojan://" },
|
||||
{ EConfigType.Hysteria2, "hysteria2://" },
|
||||
{ EConfigType.TUIC, "tuic://" },
|
||||
{ EConfigType.WireGuard, "wireguard://" }
|
||||
{ EConfigType.WireGuard, "wireguard://" },
|
||||
{ EConfigType.Anytls, "anytls://" }
|
||||
};
|
||||
|
||||
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
|
||||
@@ -182,7 +190,8 @@ public class Global
|
||||
{ EConfigType.Trojan, "trojan" },
|
||||
{ EConfigType.Hysteria2, "hysteria2" },
|
||||
{ EConfigType.TUIC, "tuic" },
|
||||
{ EConfigType.WireGuard, "wireguard" }
|
||||
{ EConfigType.WireGuard, "wireguard" },
|
||||
{ EConfigType.Anytls, "anytls" }
|
||||
};
|
||||
|
||||
public static readonly List<string> VmessSecurities =
|
||||
@@ -349,25 +358,42 @@ public class Global
|
||||
|
||||
public static readonly List<string> SingboxDomainStrategy4Out =
|
||||
[
|
||||
"ipv4_only",
|
||||
"",
|
||||
"ipv4_only",
|
||||
"prefer_ipv4",
|
||||
"prefer_ipv6",
|
||||
"ipv6_only",
|
||||
""
|
||||
"ipv6_only"
|
||||
];
|
||||
|
||||
public static readonly List<string> DomainDNSAddress =
|
||||
public static readonly List<string> DomainDirectDNSAddress =
|
||||
[
|
||||
"223.5.5.5",
|
||||
"223.6.6.6",
|
||||
"https://dns.alidns.com/dns-query",
|
||||
"https://doh.pub/dns-query",
|
||||
"223.5.5.5",
|
||||
"119.29.29.29",
|
||||
"localhost"
|
||||
];
|
||||
|
||||
public static readonly List<string> SingboxDomainDNSAddress =
|
||||
public static readonly List<string> DomainRemoteDNSAddress =
|
||||
[
|
||||
"https://cloudflare-dns.com/dns-query",
|
||||
"https://dns.cloudflare.com/dns-query",
|
||||
"https://dns.google/dns-query",
|
||||
"https://doh.dns.sb/dns-query",
|
||||
"https://doh.opendns.com/dns-query",
|
||||
"https://common.dot.dns.yandex.net",
|
||||
"8.8.8.8",
|
||||
"1.1.1.1",
|
||||
"185.222.222.222",
|
||||
"208.67.222.222",
|
||||
"77.88.8.8"
|
||||
];
|
||||
|
||||
public static readonly List<string> DomainPureIPDNSAddress =
|
||||
[
|
||||
"223.5.5.5",
|
||||
"223.6.6.6",
|
||||
"dhcp://auto"
|
||||
"119.29.29.29",
|
||||
"localhost"
|
||||
];
|
||||
|
||||
public static readonly List<string> Languages =
|
||||
@@ -509,6 +535,7 @@ public class Global
|
||||
{ ECoreType.juicity, "juicity/juicity" },
|
||||
{ ECoreType.brook, "txthinking/brook" },
|
||||
{ ECoreType.overtls, "ShadowsocksR-Live/overtls" },
|
||||
{ ECoreType.shadowquic, "spongebob888/shadowquic" },
|
||||
{ ECoreType.v2rayN, "2dust/v2rayN" },
|
||||
};
|
||||
|
||||
@@ -519,5 +546,47 @@ public class Global
|
||||
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
|
||||
];
|
||||
|
||||
public static readonly List<string> IPAPIUrls =
|
||||
[
|
||||
@"https://speed.cloudflare.com/meta",
|
||||
@"https://api.ip.sb/geoip",
|
||||
@"https://api-ipv4.ip.sb/geoip",
|
||||
@"https://api-ipv6.ip.sb/geoip",
|
||||
@"https://api.ipapi.is",
|
||||
@""
|
||||
];
|
||||
|
||||
public static readonly List<string> OutboundTags =
|
||||
[
|
||||
ProxyTag,
|
||||
DirectTag,
|
||||
BlockTag
|
||||
];
|
||||
|
||||
public static readonly Dictionary<string, List<string>> PredefinedHosts = new()
|
||||
{
|
||||
{ "dns.google", new List<string> { "8.8.8.8", "8.8.4.4", "2001:4860:4860::8888", "2001:4860:4860::8844" } },
|
||||
{ "dns.alidns.com", new List<string> { "223.5.5.5", "223.6.6.6", "2400:3200::1", "2400:3200:baba::1" } },
|
||||
{ "one.one.one.one", new List<string> { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } },
|
||||
{ "1dot1dot1dot1.cloudflare-dns.com", new List<string> { "1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001" } },
|
||||
{ "cloudflare-dns.com", new List<string> { "104.16.249.249", "104.16.248.249", "2606:4700::6810:f8f9", "2606:4700::6810:f9f9" } },
|
||||
{ "dns.cloudflare.com", new List<string> { "104.16.132.229", "104.16.133.229", "2606:4700::6810:84e5", "2606:4700::6810:85e5" } },
|
||||
{ "dot.pub", new List<string> { "1.12.12.12", "120.53.53.53" } },
|
||||
{ "dns.quad9.net", new List<string> { "9.9.9.9", "149.112.112.112", "2620:fe::fe", "2620:fe::9" } },
|
||||
{ "dns.yandex.net", new List<string> { "77.88.8.8", "77.88.8.1", "2a02:6b8::feed:0ff", "2a02:6b8:0:1::feed:0ff" } },
|
||||
{ "dns.sb", new List<string> { "185.222.222.222", "2a09::" } },
|
||||
{ "dns.umbrella.com", new List<string> { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } },
|
||||
{ "dns.sse.cisco.com", new List<string> { "208.67.220.220", "208.67.222.222", "2620:119:35::35", "2620:119:53::53" } },
|
||||
{ "engage.cloudflareclient.com", new List<string> { "162.159.192.1", "2606:4700:d0::a29f:c001" } }
|
||||
};
|
||||
|
||||
public static readonly List<string> ExpectedIPs =
|
||||
[
|
||||
"geoip:cn",
|
||||
"geoip:ir",
|
||||
"geoip:ru",
|
||||
""
|
||||
];
|
||||
|
||||
#endregion const
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ public class ConfigHandler
|
||||
EnableAutoAdjustMainLvColWidth = true
|
||||
};
|
||||
config.UiItem.MainColumnItem ??= new();
|
||||
config.UiItem.WindowSizeItem ??= new();
|
||||
|
||||
if (config.UiItem.CurrentLanguage.IsNullOrEmpty())
|
||||
{
|
||||
@@ -111,6 +112,8 @@ public class ConfigHandler
|
||||
|
||||
config.ConstItem ??= new ConstItem();
|
||||
|
||||
config.SimpleDNSItem ??= InitBuiltinSimpleDNS();
|
||||
|
||||
config.SpeedTestItem ??= new();
|
||||
if (config.SpeedTestItem.SpeedTestTimeout < 10)
|
||||
{
|
||||
@@ -122,7 +125,7 @@ public class ConfigHandler
|
||||
}
|
||||
if (config.SpeedTestItem.SpeedPingTestUrl.IsNullOrEmpty())
|
||||
{
|
||||
config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrl;
|
||||
config.SpeedTestItem.SpeedPingTestUrl = Global.SpeedPingTestUrls.First();
|
||||
}
|
||||
if (config.SpeedTestItem.MixedConcurrencyCount < 1)
|
||||
{
|
||||
@@ -245,7 +248,9 @@ public class ConfigHandler
|
||||
item.PublicKey = profileItem.PublicKey;
|
||||
item.ShortId = profileItem.ShortId;
|
||||
item.SpiderX = profileItem.SpiderX;
|
||||
item.Mldsa65Verify = profileItem.Mldsa65Verify;
|
||||
item.Extra = profileItem.Extra;
|
||||
item.MuxEnabled = profileItem.MuxEnabled;
|
||||
}
|
||||
|
||||
var ret = item.ConfigType switch
|
||||
@@ -259,6 +264,7 @@ public class ConfigHandler
|
||||
EConfigType.Hysteria2 => await AddHysteria2Server(config, item),
|
||||
EConfigType.TUIC => await AddTuicServer(config, item),
|
||||
EConfigType.WireGuard => await AddWireguardServer(config, item),
|
||||
EConfigType.Anytls => await AddAnytlsServer(config, item),
|
||||
_ => -1,
|
||||
};
|
||||
return ret;
|
||||
@@ -783,6 +789,35 @@ public class ConfigHandler
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add or edit a Anytls server
|
||||
/// Validates and processes Anytls-specific settings
|
||||
/// </summary>
|
||||
/// <param name="config">Current configuration</param>
|
||||
/// <param name="profileItem">Anytls profile to add</param>
|
||||
/// <param name="toFile">Whether to save to file</param>
|
||||
/// <returns>0 if successful, -1 if failed</returns>
|
||||
public static async Task<int> AddAnytlsServer(Config config, ProfileItem profileItem, bool toFile = true)
|
||||
{
|
||||
profileItem.ConfigType = EConfigType.Anytls;
|
||||
profileItem.CoreType = ECoreType.sing_box;
|
||||
|
||||
profileItem.Address = profileItem.Address.TrimEx();
|
||||
profileItem.Id = profileItem.Id.TrimEx();
|
||||
profileItem.Security = profileItem.Security.TrimEx();
|
||||
profileItem.Network = string.Empty;
|
||||
if (profileItem.StreamSecurity.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.StreamSecurity = Global.StreamSecurity;
|
||||
}
|
||||
if (profileItem.Id.IsNullOrEmpty())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
await AddServerCommon(config, profileItem, toFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sort the server list by the specified column
|
||||
/// Updates the sort order in the profile extension data
|
||||
@@ -799,8 +834,11 @@ public class ConfigHandler
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
var lstServerStat = (config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? [];
|
||||
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
|
||||
var lstProfile = (from t in lstModel
|
||||
join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b
|
||||
from t22 in t2b.DefaultIfEmpty()
|
||||
join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b
|
||||
from t33 in t3b.DefaultIfEmpty()
|
||||
select new ProfileItemModel
|
||||
@@ -815,7 +853,11 @@ public class ConfigHandler
|
||||
StreamSecurity = t.StreamSecurity,
|
||||
Delay = t33?.Delay ?? 0,
|
||||
Speed = t33?.Speed ?? 0,
|
||||
Sort = t33?.Sort ?? 0
|
||||
Sort = t33?.Sort ?? 0,
|
||||
TodayDown = (t22?.TodayDown ?? 0).ToString("D16"),
|
||||
TodayUp = (t22?.TodayUp ?? 0).ToString("D16"),
|
||||
TotalDown = (t22?.TotalDown ?? 0).ToString("D16"),
|
||||
TotalUp = (t22?.TotalUp ?? 0).ToString("D16"),
|
||||
}).ToList();
|
||||
|
||||
Enum.TryParse(colName, true, out EServerColName name);
|
||||
@@ -833,6 +875,10 @@ public class ConfigHandler
|
||||
EServerColName.DelayVal => lstProfile.OrderBy(t => t.Delay).ToList(),
|
||||
EServerColName.SpeedVal => lstProfile.OrderBy(t => t.Speed).ToList(),
|
||||
EServerColName.SubRemarks => lstProfile.OrderBy(t => t.Subid).ToList(),
|
||||
EServerColName.TodayDown => lstProfile.OrderBy(t => t.TodayDown).ToList(),
|
||||
EServerColName.TodayUp => lstProfile.OrderBy(t => t.TodayUp).ToList(),
|
||||
EServerColName.TotalDown => lstProfile.OrderBy(t => t.TotalDown).ToList(),
|
||||
EServerColName.TotalUp => lstProfile.OrderBy(t => t.TotalUp).ToList(),
|
||||
_ => lstProfile
|
||||
};
|
||||
}
|
||||
@@ -849,6 +895,10 @@ public class ConfigHandler
|
||||
EServerColName.DelayVal => lstProfile.OrderByDescending(t => t.Delay).ToList(),
|
||||
EServerColName.SpeedVal => lstProfile.OrderByDescending(t => t.Speed).ToList(),
|
||||
EServerColName.SubRemarks => lstProfile.OrderByDescending(t => t.Subid).ToList(),
|
||||
EServerColName.TodayDown => lstProfile.OrderByDescending(t => t.TodayDown).ToList(),
|
||||
EServerColName.TodayUp => lstProfile.OrderByDescending(t => t.TodayUp).ToList(),
|
||||
EServerColName.TotalDown => lstProfile.OrderByDescending(t => t.TotalDown).ToList(),
|
||||
EServerColName.TotalUp => lstProfile.OrderByDescending(t => t.TotalUp).ToList(),
|
||||
_ => lstProfile
|
||||
};
|
||||
}
|
||||
@@ -1277,6 +1327,7 @@ public class ConfigHandler
|
||||
EConfigType.Hysteria2 => await AddHysteria2Server(config, profileItem, false),
|
||||
EConfigType.TUIC => await AddTuicServer(config, profileItem, false),
|
||||
EConfigType.WireGuard => await AddWireguardServer(config, profileItem, false),
|
||||
EConfigType.Anytls => await AddAnytlsServer(config, profileItem, false),
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
@@ -1840,12 +1891,25 @@ public class ConfigHandler
|
||||
/// <returns>0 if successful</returns>
|
||||
public static async Task<int> SetDefaultRouting(Config config, RoutingItem routingItem)
|
||||
{
|
||||
if (await SQLiteHelper.Instance.TableAsync<RoutingItem>().Where(t => t.Id == routingItem.Id).CountAsync() > 0)
|
||||
var items = await AppHandler.Instance.RoutingItems();
|
||||
if (items.Any(t => t.Id == routingItem.Id && t.IsActive == true))
|
||||
{
|
||||
config.RoutingBasicItem.RoutingIndexId = routingItem.Id;
|
||||
return -1;
|
||||
}
|
||||
|
||||
await SaveConfig(config);
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item.Id == routingItem.Id)
|
||||
{
|
||||
item.IsActive = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.IsActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
await SQLiteHelper.Instance.UpdateAllAsync(items);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1858,7 +1922,7 @@ public class ConfigHandler
|
||||
/// <returns>The default routing item</returns>
|
||||
public static async Task<RoutingItem> GetDefaultRouting(Config config)
|
||||
{
|
||||
var item = await AppHandler.Instance.GetRoutingItem(config.RoutingBasicItem.RoutingIndexId);
|
||||
var item = await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.IsActive == true);
|
||||
if (item is null)
|
||||
{
|
||||
var item2 = await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync();
|
||||
@@ -1964,8 +2028,20 @@ public class ConfigHandler
|
||||
items = await AppHandler.Instance.RoutingItems();
|
||||
}
|
||||
|
||||
if (!blImportAdvancedRules && items.Where(t => t.Remarks.StartsWith(ver)).ToList().Count > 0)
|
||||
if (!blImportAdvancedRules && items.Count > 0)
|
||||
{
|
||||
//migrate
|
||||
//TODO Temporary code to be removed later
|
||||
if (config.RoutingBasicItem.RoutingIndexId.IsNotEmpty())
|
||||
{
|
||||
var item = items.FirstOrDefault(t => t.Id == config.RoutingBasicItem.RoutingIndexId);
|
||||
if (item != null)
|
||||
{
|
||||
await SetDefaultRouting(config, item);
|
||||
}
|
||||
config.RoutingBasicItem.RoutingIndexId = string.Empty;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2020,18 +2096,38 @@ public class ConfigHandler
|
||||
/// <summary>
|
||||
/// Initialize built-in DNS configurations
|
||||
/// Creates default DNS items for V2Ray and sing-box
|
||||
/// Also checks existing DNS items and disables those with empty NormalDNS
|
||||
/// </summary>
|
||||
/// <param name="config">Current configuration</param>
|
||||
/// <returns>0 if successful</returns>
|
||||
public static async Task<int> InitBuiltinDNS(Config config)
|
||||
{
|
||||
var items = await AppHandler.Instance.DNSItems();
|
||||
|
||||
// Check existing DNS items and disable those with empty NormalDNS
|
||||
var needsUpdate = false;
|
||||
foreach (var existingItem in items)
|
||||
{
|
||||
if (existingItem.NormalDNS.IsNullOrEmpty() && existingItem.Enabled)
|
||||
{
|
||||
existingItem.Enabled = false;
|
||||
needsUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Update items if any changes were made
|
||||
if (needsUpdate)
|
||||
{
|
||||
await SQLiteHelper.Instance.UpdateAllAsync(items);
|
||||
}
|
||||
|
||||
if (items.Count <= 0)
|
||||
{
|
||||
var item = new DNSItem()
|
||||
{
|
||||
Remarks = "V2ray",
|
||||
CoreType = ECoreType.Xray,
|
||||
Enabled = false,
|
||||
};
|
||||
await SaveDNSItems(config, item);
|
||||
|
||||
@@ -2039,6 +2135,7 @@ public class ConfigHandler
|
||||
{
|
||||
Remarks = "sing-box",
|
||||
CoreType = ECoreType.sing_box,
|
||||
Enabled = false,
|
||||
};
|
||||
await SaveDNSItems(config, item2);
|
||||
}
|
||||
@@ -2110,6 +2207,37 @@ public class ConfigHandler
|
||||
|
||||
#endregion DNS
|
||||
|
||||
#region Simple DNS
|
||||
|
||||
public static SimpleDNSItem InitBuiltinSimpleDNS()
|
||||
{
|
||||
return new SimpleDNSItem()
|
||||
{
|
||||
UseSystemHosts = false,
|
||||
AddCommonHosts = true,
|
||||
FakeIP = false,
|
||||
BlockBindingQuery = true,
|
||||
DirectDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
|
||||
RemoteDNS = Global.DomainRemoteDNSAddress.FirstOrDefault(),
|
||||
SingboxOutboundsResolveDNS = Global.DomainDirectDNSAddress.FirstOrDefault(),
|
||||
SingboxFinalResolveDNS = Global.DomainPureIPDNSAddress.FirstOrDefault()
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task<SimpleDNSItem> GetExternalSimpleDNSItem(string url)
|
||||
{
|
||||
var downloadHandle = new DownloadService();
|
||||
var templateContent = await downloadHandle.TryDownloadString(url, true, "");
|
||||
if (templateContent.IsNullOrEmpty())
|
||||
return null;
|
||||
var template = JsonUtils.Deserialize<SimpleDNSItem>(templateContent);
|
||||
if (template == null)
|
||||
return null;
|
||||
return template;
|
||||
}
|
||||
|
||||
#endregion Simple DNS
|
||||
|
||||
#region Regional Presets
|
||||
|
||||
/// <summary>
|
||||
@@ -2131,7 +2259,8 @@ public class ConfigHandler
|
||||
await SQLiteHelper.Instance.DeleteAllAsync<DNSItem>();
|
||||
await InitBuiltinDNS(config);
|
||||
|
||||
return true;
|
||||
config.SimpleDNSItem = InitBuiltinSimpleDNS();
|
||||
break;
|
||||
|
||||
case EPresetType.Russia:
|
||||
config.ConstItem.GeoSourceUrl = Global.GeoFilesSources[1];
|
||||
@@ -2141,7 +2270,8 @@ public class ConfigHandler
|
||||
await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.Xray, Global.DNSTemplateSources[1] + "v2ray.json"));
|
||||
await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.sing_box, Global.DNSTemplateSources[1] + "sing_box.json"));
|
||||
|
||||
return true;
|
||||
config.SimpleDNSItem = await GetExternalSimpleDNSItem(Global.DNSTemplateSources[1] + "simple_dns.json") ?? InitBuiltinSimpleDNS();
|
||||
break;
|
||||
|
||||
case EPresetType.Iran:
|
||||
config.ConstItem.GeoSourceUrl = Global.GeoFilesSources[2];
|
||||
@@ -2151,11 +2281,52 @@ public class ConfigHandler
|
||||
await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.Xray, Global.DNSTemplateSources[2] + "v2ray.json"));
|
||||
await SaveDNSItems(config, await GetExternalDNSItem(ECoreType.sing_box, Global.DNSTemplateSources[2] + "sing_box.json"));
|
||||
|
||||
return true;
|
||||
config.SimpleDNSItem = await GetExternalSimpleDNSItem(Global.DNSTemplateSources[2] + "simple_dns.json") ?? InitBuiltinSimpleDNS();
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion Regional Presets
|
||||
|
||||
#region UIItem
|
||||
|
||||
public static WindowSizeItem? GetWindowSizeItem(Config config, string typeName)
|
||||
{
|
||||
var sizeItem = config?.UiItem?.WindowSizeItem?.FirstOrDefault(t => t.TypeName == typeName);
|
||||
if (sizeItem == null || sizeItem.Width <= 0 || sizeItem.Height <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return sizeItem;
|
||||
}
|
||||
|
||||
public static int SaveWindowSizeItem(Config config, string typeName, double width, double height)
|
||||
{
|
||||
var sizeItem = config?.UiItem?.WindowSizeItem?.FirstOrDefault(t => t.TypeName == typeName);
|
||||
if (sizeItem == null)
|
||||
{
|
||||
sizeItem = new WindowSizeItem { TypeName = typeName };
|
||||
config.UiItem.WindowSizeItem.Add(sizeItem);
|
||||
}
|
||||
|
||||
sizeItem.Width = (int)width;
|
||||
sizeItem.Height = (int)height;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static int SaveMainGirdHeight(Config config, double height1, double height2)
|
||||
{
|
||||
var uiItem = config.UiItem ?? new();
|
||||
|
||||
uiItem.MainGirdHeight1 = (int)(height1 + 0.1);
|
||||
uiItem.MainGirdHeight2 = (int)(height2 + 0.1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endregion UIItem
|
||||
}
|
||||
|
||||
42
v2rayN/ServiceLib/Handler/ConnectionHandler.cs
Normal file
42
v2rayN/ServiceLib/Handler/ConnectionHandler.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
namespace ServiceLib.Handler;
|
||||
|
||||
public class ConnectionHandler
|
||||
{
|
||||
private static readonly Lazy<ConnectionHandler> _instance = new(() => new());
|
||||
public static ConnectionHandler Instance => _instance.Value;
|
||||
|
||||
public async Task<string> RunAvailabilityCheck()
|
||||
{
|
||||
var downloadHandle = new DownloadService();
|
||||
var time = await downloadHandle.RunAvailabilityCheck(null);
|
||||
var ip = time > 0 ? await GetIPInfo(downloadHandle) ?? Global.None : Global.None;
|
||||
|
||||
return string.Format(ResUI.TestMeOutput, time, ip);
|
||||
}
|
||||
|
||||
private async Task<string?> GetIPInfo(DownloadService downloadHandle)
|
||||
{
|
||||
var url = AppHandler.Instance.Config.SpeedTestItem.IPAPIUrl;
|
||||
if (url.IsNullOrEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = await downloadHandle.TryDownloadString(url, true, "");
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
|
||||
if (ipInfo == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var ip = ipInfo.ip ?? ipInfo.clientIp ?? ipInfo.ip_addr ?? ipInfo.query;
|
||||
var country = ipInfo.country_code ?? ipInfo.country ?? ipInfo.countryCode ?? ipInfo.location?.country_code;
|
||||
|
||||
return $"({country ?? "unknown"}) {ip}";
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using CliWrap;
|
||||
using CliWrap.Buffered;
|
||||
|
||||
namespace ServiceLib.Handler;
|
||||
|
||||
@@ -11,6 +12,7 @@ public class CoreAdminHandler
|
||||
private Config _config;
|
||||
private Action<bool, string>? _updateFunc;
|
||||
private int _linuxSudoPid = -1;
|
||||
private const string _tag = "CoreAdminHandler";
|
||||
|
||||
public async Task Init(Config config, Action<bool, string> updateFunc)
|
||||
{
|
||||
@@ -20,6 +22,8 @@ public class CoreAdminHandler
|
||||
}
|
||||
_config = config;
|
||||
_updateFunc = updateFunc;
|
||||
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void UpdateFunc(bool notify, string msg)
|
||||
@@ -29,8 +33,11 @@ public class CoreAdminHandler
|
||||
|
||||
public async Task<Process?> RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
|
||||
{
|
||||
StringBuilder sb = new();
|
||||
sb.AppendLine("#!/bin/bash");
|
||||
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
|
||||
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
|
||||
sb.AppendLine($"sudo -S {cmdLine}");
|
||||
var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
|
||||
|
||||
Process proc = new()
|
||||
{
|
||||
@@ -44,33 +51,26 @@ public class CoreAdminHandler
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true,
|
||||
StandardInputEncoding = Encoding.UTF8,
|
||||
StandardOutputEncoding = Encoding.UTF8,
|
||||
StandardErrorEncoding = Encoding.UTF8,
|
||||
}
|
||||
};
|
||||
|
||||
proc.OutputDataReceived += (sender, e) =>
|
||||
void dataHandler(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (e.Data.IsNotEmpty())
|
||||
{
|
||||
UpdateFunc(false, e.Data + Environment.NewLine);
|
||||
}
|
||||
};
|
||||
proc.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (e.Data.IsNotEmpty())
|
||||
{
|
||||
UpdateFunc(false, e.Data + Environment.NewLine);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
proc.OutputDataReceived += dataHandler;
|
||||
proc.ErrorDataReceived += dataHandler;
|
||||
|
||||
proc.Start();
|
||||
proc.BeginOutputReadLine();
|
||||
proc.BeginErrorReadLine();
|
||||
|
||||
await Task.Delay(10);
|
||||
await proc.StandardInput.WriteLineAsync();
|
||||
await Task.Delay(10);
|
||||
await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd);
|
||||
|
||||
@@ -92,35 +92,27 @@ public class CoreAdminHandler
|
||||
return;
|
||||
}
|
||||
|
||||
var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}";
|
||||
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
|
||||
try
|
||||
{
|
||||
var shellFileName = Utils.IsOSX() ? Global.KillAsSudoOSXShellFileName : Global.KillAsSudoLinuxShellFileName;
|
||||
var shFilePath = await FileManager.CreateLinuxShellFile("kill_as_sudo.sh", EmbedUtils.GetEmbedText(shellFileName), true);
|
||||
if (shFilePath.Contains(' '))
|
||||
{
|
||||
shFilePath = shFilePath.AppendQuotes();
|
||||
}
|
||||
var arg = new List<string>() { "-c", $"sudo -S {shFilePath} {_linuxSudoPid}" };
|
||||
var result = await Cli.Wrap(Global.LinuxBash)
|
||||
.WithArguments(arg)
|
||||
.WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd))
|
||||
.ExecuteBufferedAsync();
|
||||
|
||||
await Cli.Wrap(shFilePath)
|
||||
.WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd))
|
||||
.ExecuteAsync();
|
||||
UpdateFunc(false, result.StandardOutput.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
_linuxSudoPid = -1;
|
||||
}
|
||||
|
||||
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
|
||||
{
|
||||
var shFilePath = Utils.GetBinConfigPath(fileName);
|
||||
File.Delete(shFilePath);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("#!/bin/sh");
|
||||
if (Utils.IsAdministrator())
|
||||
{
|
||||
sb.AppendLine($"{cmdLine}");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($"sudo -S {cmdLine}");
|
||||
}
|
||||
|
||||
await File.WriteAllTextAsync(shFilePath, sb.ToString());
|
||||
await Utils.SetLinuxChmod(shFilePath);
|
||||
|
||||
return shFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public class CoreHandler
|
||||
|
||||
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
|
||||
{
|
||||
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC) ? ECoreType.sing_box : ECoreType.Xray;
|
||||
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) ? ECoreType.sing_box : ECoreType.Xray;
|
||||
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
|
||||
var configPath = Utils.GetBinConfigPath(fileName);
|
||||
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
|
||||
@@ -280,20 +280,15 @@ public class CoreHandler
|
||||
|
||||
if (displayLog)
|
||||
{
|
||||
proc.OutputDataReceived += (sender, e) =>
|
||||
void dataHandler(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (e.Data.IsNotEmpty())
|
||||
{
|
||||
UpdateFunc(false, e.Data + Environment.NewLine);
|
||||
}
|
||||
};
|
||||
proc.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (e.Data.IsNotEmpty())
|
||||
{
|
||||
UpdateFunc(false, e.Data + Environment.NewLine);
|
||||
}
|
||||
};
|
||||
}
|
||||
proc.OutputDataReceived += dataHandler;
|
||||
proc.ErrorDataReceived += dataHandler;
|
||||
}
|
||||
proc.Start();
|
||||
|
||||
|
||||
@@ -200,6 +200,15 @@ public sealed class CoreInfoHandler
|
||||
Arguments = "-r client -c {0}",
|
||||
Url = GetCoreUrl(ECoreType.overtls),
|
||||
AbsolutePath = false,
|
||||
},
|
||||
|
||||
new CoreInfo
|
||||
{
|
||||
CoreType = ECoreType.shadowquic,
|
||||
CoreExes = [ "shadowquic", "shadowquic"],
|
||||
Arguments = "-c {0}",
|
||||
Url = GetCoreUrl(ECoreType.shadowquic),
|
||||
AbsolutePath = false,
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
48
v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs
Normal file
48
v2rayN/ServiceLib/Handler/Fmt/AnytlsFmt.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
namespace ServiceLib.Handler.Fmt;
|
||||
|
||||
public class AnytlsFmt : BaseFmt
|
||||
{
|
||||
public static ProfileItem? Resolve(string str, out string msg)
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
|
||||
var parsedUrl = Utils.TryUri(str);
|
||||
if (parsedUrl == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ProfileItem item = new()
|
||||
{
|
||||
ConfigType = EConfigType.Anytls,
|
||||
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
|
||||
Address = parsedUrl.IdnHost,
|
||||
Port = parsedUrl.Port,
|
||||
};
|
||||
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
|
||||
item.Id = rawUserInfo;
|
||||
|
||||
var query = Utils.ParseQueryString(parsedUrl.Query);
|
||||
_ = ResolveStdTransport(query, ref item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public static string? ToUri(ProfileItem? item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var remark = string.Empty;
|
||||
if (item.Remarks.IsNotEmpty())
|
||||
{
|
||||
remark = "#" + Utils.UrlEncode(item.Remarks);
|
||||
}
|
||||
var pw = item.Id;
|
||||
var dicQuery = new Dictionary<string, string>();
|
||||
_ = GetStdTransport(item, Global.None, ref dicQuery);
|
||||
|
||||
return ToUri(EConfigType.Anytls, item.Address, item.Port, pw, dicQuery, remark);
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,10 @@ public class BaseFmt
|
||||
{
|
||||
dicQuery.Add("spx", Utils.UrlEncode(item.SpiderX));
|
||||
}
|
||||
if (item.Mldsa65Verify.IsNotEmpty())
|
||||
{
|
||||
dicQuery.Add("pqv", Utils.UrlEncode(item.Mldsa65Verify));
|
||||
}
|
||||
if (item.AllowInsecure.Equals("true"))
|
||||
{
|
||||
dicQuery.Add("allowInsecure", "1");
|
||||
@@ -159,6 +163,7 @@ public class BaseFmt
|
||||
item.PublicKey = Utils.UrlDecode(query["pbk"] ?? "");
|
||||
item.ShortId = Utils.UrlDecode(query["sid"] ?? "");
|
||||
item.SpiderX = Utils.UrlDecode(query["spx"] ?? "");
|
||||
item.Mldsa65Verify = Utils.UrlDecode(query["pqv"] ?? "");
|
||||
item.AllowInsecure = (query["allowInsecure"] ?? "") == "1" ? "true" : "";
|
||||
|
||||
item.Network = query["type"] ?? nameof(ETransport.tcp);
|
||||
|
||||
@@ -18,6 +18,7 @@ public class FmtHandler
|
||||
EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item),
|
||||
EConfigType.TUIC => TuicFmt.ToUri(item),
|
||||
EConfigType.WireGuard => WireguardFmt.ToUri(item),
|
||||
EConfigType.Anytls => AnytlsFmt.ToUri(item),
|
||||
_ => null,
|
||||
};
|
||||
|
||||
@@ -75,6 +76,10 @@ public class FmtHandler
|
||||
{
|
||||
return WireguardFmt.Resolve(str, out msg);
|
||||
}
|
||||
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Anytls]))
|
||||
{
|
||||
return AnytlsFmt.Resolve(str, out msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = ResUI.NonvmessOrssProtocol;
|
||||
|
||||
@@ -24,7 +24,7 @@ public class Hysteria2Fmt : BaseFmt
|
||||
item.Path = Utils.UrlDecode(query["obfs-password"] ?? "");
|
||||
item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
|
||||
|
||||
item.Ports = Utils.UrlDecode(query["mport"] ?? "").Replace('-', ':');
|
||||
item.Ports = Utils.UrlDecode(query["mport"] ?? "");
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ public class VmessFmt : BaseFmt
|
||||
{
|
||||
msg = ResUI.ConfigurationFormatIncorrect;
|
||||
ProfileItem? item;
|
||||
if (str.IndexOf('?') > 0 && str.IndexOf('&') > 0)
|
||||
if (str.IndexOf('@') > 0)
|
||||
{
|
||||
item = ResolveStdVmess(str);
|
||||
item = ResolveStdVmess(str) ?? ResolveVmess(str, out msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -18,14 +18,7 @@ public class ProxySettingLinux
|
||||
|
||||
private static async Task ExecCmd(List<string> args)
|
||||
{
|
||||
var fileName = Utils.GetBinConfigPath(_proxySetFileName);
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
var contents = EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName);
|
||||
await File.AppendAllTextAsync(fileName, contents);
|
||||
|
||||
await Utils.SetLinuxChmod(fileName);
|
||||
}
|
||||
var fileName = await FileManager.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetLinuxShellFileName), false);
|
||||
|
||||
await Utils.GetCliWrapOutput(fileName, args);
|
||||
}
|
||||
|
||||
@@ -23,14 +23,7 @@ public class ProxySettingOSX
|
||||
|
||||
private static async Task ExecCmd(List<string> args)
|
||||
{
|
||||
var fileName = Utils.GetBinConfigPath(_proxySetFileName);
|
||||
if (!File.Exists(fileName))
|
||||
{
|
||||
var contents = EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName);
|
||||
await File.AppendAllTextAsync(fileName, contents);
|
||||
|
||||
await Utils.SetLinuxChmod(fileName);
|
||||
}
|
||||
var fileName = await FileManager.CreateLinuxShellFile(_proxySetFileName, EmbedUtils.GetEmbedText(Global.ProxySetOSXShellFileName), false);
|
||||
|
||||
await Utils.GetCliWrapOutput(fileName, args);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ public class Config
|
||||
public List<InItem> Inbound { get; set; }
|
||||
public List<KeyEventItem> GlobalHotkeys { get; set; }
|
||||
public List<CoreTypeItem> CoreTypeItem { get; set; }
|
||||
public SimpleDNSItem SimpleDNSItem { get; set; }
|
||||
|
||||
#endregion other entities
|
||||
}
|
||||
|
||||
@@ -89,10 +89,8 @@ public class UIItem
|
||||
{
|
||||
public bool EnableAutoAdjustMainLvColWidth { get; set; }
|
||||
public bool EnableUpdateSubOnlyRemarksExist { get; set; }
|
||||
public double MainWidth { get; set; }
|
||||
public double MainHeight { get; set; }
|
||||
public double MainGirdHeight1 { get; set; }
|
||||
public double MainGirdHeight2 { get; set; }
|
||||
public int MainGirdHeight1 { get; set; }
|
||||
public int MainGirdHeight2 { get; set; }
|
||||
public EGirdOrientation MainGirdOrientation { get; set; } = EGirdOrientation.Vertical;
|
||||
public string? ColorPrimaryName { get; set; }
|
||||
public string? CurrentTheme { get; set; }
|
||||
@@ -103,8 +101,10 @@ public class UIItem
|
||||
public bool DoubleClick2Activate { get; set; }
|
||||
public bool AutoHideStartup { get; set; }
|
||||
public bool Hide2TrayWhenClose { get; set; }
|
||||
public List<ColumnItem> MainColumnItem { get; set; }
|
||||
public bool ShowInTaskbar { get; set; }
|
||||
public bool MacOSShowInDock { get; set; }
|
||||
public List<ColumnItem> MainColumnItem { get; set; }
|
||||
public List<WindowSizeItem> WindowSizeItem { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -156,6 +156,7 @@ public class SpeedTestItem
|
||||
public string SpeedTestUrl { get; set; }
|
||||
public string SpeedPingTestUrl { get; set; }
|
||||
public int MixedConcurrencyCount { get; set; }
|
||||
public string IPAPIUrl { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -244,3 +245,29 @@ public class Fragment4RayItem
|
||||
public string? Length { get; set; }
|
||||
public string? Interval { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class WindowSizeItem
|
||||
{
|
||||
public string TypeName { get; set; }
|
||||
public int Width { get; set; }
|
||||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SimpleDNSItem
|
||||
{
|
||||
public bool? UseSystemHosts { get; set; }
|
||||
public bool? AddCommonHosts { get; set; }
|
||||
public bool? FakeIP { get; set; }
|
||||
public bool? BlockBindingQuery { get; set; }
|
||||
public string? DirectDNS { get; set; }
|
||||
public string? RemoteDNS { get; set; }
|
||||
public string? SingboxOutboundsResolveDNS { get; set; }
|
||||
public string? SingboxFinalResolveDNS { get; set; }
|
||||
public string? RayStrategy4Freedom { get; set; }
|
||||
public string? SingboxStrategy4Direct { get; set; }
|
||||
public string? SingboxStrategy4Proxy { get; set; }
|
||||
public string? Hosts { get; set; }
|
||||
public string? DirectExpectedIPs { get; set; }
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ public class DNSItem
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Remarks { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
public bool Enabled { get; set; } = false;
|
||||
public ECoreType CoreType { get; set; }
|
||||
public bool UseSystemHosts { get; set; }
|
||||
public string? NormalDNS { get; set; }
|
||||
|
||||
@@ -3,10 +3,17 @@ 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? clientIp { get; set; }
|
||||
public string? ip_addr { get; set; }
|
||||
public string? query { get; set; }
|
||||
public string? country { get; set; }
|
||||
public string? country_name { get; set; }
|
||||
public string? country_code { get; set; }
|
||||
public string? countryCode { get; set; }
|
||||
public LocationInfo? location { get; set; }
|
||||
}
|
||||
|
||||
public class LocationInfo
|
||||
{
|
||||
public string? country_code { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using ReactiveUI;
|
||||
using SQLite;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
public class ProfileItem
|
||||
public class ProfileItem : ReactiveObject
|
||||
{
|
||||
public ProfileItem()
|
||||
{
|
||||
@@ -31,7 +32,7 @@ public class ProfileItem
|
||||
public string GetSummary()
|
||||
{
|
||||
var summary = $"[{(ConfigType).ToString()}] ";
|
||||
var arrAddr = Address.Split('.');
|
||||
var arrAddr = Address.Contains(':') ? Address.Split(':') : Address.Split('.');
|
||||
var addr = arrAddr.Length switch
|
||||
{
|
||||
> 2 => $"{arrAddr.First()}***{arrAddr.Last()}",
|
||||
@@ -92,5 +93,7 @@ public class ProfileItem
|
||||
public string PublicKey { get; set; }
|
||||
public string ShortId { get; set; }
|
||||
public string SpiderX { get; set; }
|
||||
public string Mldsa65Verify { get; set; }
|
||||
public string Extra { get; set; }
|
||||
public bool? MuxEnabled { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using ReactiveUI.Fody.Helpers;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
[Serializable]
|
||||
@@ -5,13 +7,28 @@ public class ProfileItemModel : ProfileItem
|
||||
{
|
||||
public bool IsActive { get; set; }
|
||||
public string SubRemarks { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public int Delay { get; set; }
|
||||
|
||||
public decimal Speed { get; set; }
|
||||
public int Sort { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string DelayVal { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string SpeedVal { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string TodayUp { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string TodayDown { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string TotalUp { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public string TotalDown { get; set; }
|
||||
}
|
||||
|
||||
@@ -19,4 +19,5 @@ public class RoutingItem
|
||||
public string DomainStrategy { get; set; }
|
||||
public string DomainStrategy4Singbox { get; set; }
|
||||
public int Sort { get; set; }
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
@@ -3,5 +3,4 @@ namespace ServiceLib.Models;
|
||||
[Serializable]
|
||||
public class RoutingItemModel : RoutingItem
|
||||
{
|
||||
public bool IsActive { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ServiceLib.Models;
|
||||
|
||||
public class SingboxConfig
|
||||
@@ -6,6 +8,7 @@ public class SingboxConfig
|
||||
public Dns4Sbox? dns { get; set; }
|
||||
public List<Inbound4Sbox> inbounds { get; set; }
|
||||
public List<Outbound4Sbox> outbounds { get; set; }
|
||||
public List<Endpoints4Sbox>? endpoints { get; set; }
|
||||
public Route4Sbox route { get; set; }
|
||||
public Experimental4Sbox? experimental { get; set; }
|
||||
}
|
||||
@@ -29,14 +32,15 @@ public class Dns4Sbox
|
||||
public bool? independent_cache { get; set; }
|
||||
public bool? reverse_mapping { get; set; }
|
||||
public string? client_subnet { get; set; }
|
||||
public Fakeip4Sbox? fakeip { get; set; }
|
||||
}
|
||||
|
||||
public class Route4Sbox
|
||||
{
|
||||
public Rule4Sbox? default_domain_resolver { get; set; } // or string
|
||||
public bool? auto_detect_interface { get; set; }
|
||||
public List<Rule4Sbox> rules { get; set; }
|
||||
public List<Ruleset4Sbox>? rule_set { get; set; }
|
||||
public string? final { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -49,6 +53,7 @@ public class Rule4Sbox
|
||||
public string? mode { get; set; }
|
||||
public bool? ip_is_private { get; set; }
|
||||
public string? client_subnet { get; set; }
|
||||
public int? rewrite_ttl { get; set; }
|
||||
public bool? invert { get; set; }
|
||||
public string? clash_mode { get; set; }
|
||||
public List<string>? inbound { get; set; }
|
||||
@@ -67,6 +72,27 @@ public class Rule4Sbox
|
||||
public List<string>? process_name { get; set; }
|
||||
public List<string>? rule_set { get; set; }
|
||||
public List<Rule4Sbox>? rules { get; set; }
|
||||
public string? action { get; set; }
|
||||
public string? strategy { get; set; }
|
||||
public List<string>? sniffer { get; set; }
|
||||
public string? rcode { get; set; }
|
||||
public List<int>? query_type { get; set; }
|
||||
public List<string>? answer { get; set; }
|
||||
public List<string>? ns { get; set; }
|
||||
public List<string>? extra { get; set; }
|
||||
public string? method { get; set; }
|
||||
public bool? no_drop { get; set; }
|
||||
public bool? source_ip_is_private { get; set; }
|
||||
public bool? ip_accept_any { get; set; }
|
||||
public int? source_port { get; set; }
|
||||
public List<string>? source_port_range { get; set; }
|
||||
public List<string>? network_type { get; set; }
|
||||
public bool? network_is_expensive { get; set; }
|
||||
public bool? network_is_constrained { get; set; }
|
||||
public List<string>? wifi_ssid { get; set; }
|
||||
public List<string>? wifi_bssid { get; set; }
|
||||
public bool? rule_set_ip_cidr_match_source { get; set; }
|
||||
public bool? rule_set_ip_cidr_accept_empty { get; set; }
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
@@ -76,7 +102,6 @@ public class Inbound4Sbox
|
||||
public string tag { get; set; }
|
||||
public string listen { get; set; }
|
||||
public int? listen_port { get; set; }
|
||||
public string? domain_strategy { get; set; }
|
||||
public string interface_name { get; set; }
|
||||
public List<string>? address { get; set; }
|
||||
public int? mtu { get; set; }
|
||||
@@ -84,8 +109,6 @@ public class Inbound4Sbox
|
||||
public bool? strict_route { get; set; }
|
||||
public bool? endpoint_independent_nat { get; set; }
|
||||
public string? stack { get; set; }
|
||||
public bool? sniff { get; set; }
|
||||
public bool? sniff_override_destination { get; set; }
|
||||
public List<User4Sbox> users { get; set; }
|
||||
}
|
||||
|
||||
@@ -95,10 +118,8 @@ public class User4Sbox
|
||||
public string password { get; set; }
|
||||
}
|
||||
|
||||
public class Outbound4Sbox
|
||||
public class Outbound4Sbox : BaseServer4Sbox
|
||||
{
|
||||
public string type { get; set; }
|
||||
public string tag { get; set; }
|
||||
public string? server { get; set; }
|
||||
public int? server_port { get; set; }
|
||||
public List<string>? server_ports { get; set; }
|
||||
@@ -113,7 +134,6 @@ public class Outbound4Sbox
|
||||
public int? recv_window_conn { get; set; }
|
||||
public int? recv_window { get; set; }
|
||||
public bool? disable_mtu_discovery { get; set; }
|
||||
public string? detour { get; set; }
|
||||
public string? method { get; set; }
|
||||
public string? username { get; set; }
|
||||
public string? password { get; set; }
|
||||
@@ -121,21 +141,36 @@ public class Outbound4Sbox
|
||||
public string? version { get; set; }
|
||||
public string? network { get; set; }
|
||||
public string? packet_encoding { get; set; }
|
||||
public List<string>? local_address { get; set; }
|
||||
public string? private_key { get; set; }
|
||||
public string? peer_public_key { get; set; }
|
||||
public List<int>? reserved { get; set; }
|
||||
public int? mtu { get; set; }
|
||||
public string? plugin { get; set; }
|
||||
public string? plugin_opts { get; set; }
|
||||
public Tls4Sbox? tls { get; set; }
|
||||
public Multiplex4Sbox? multiplex { get; set; }
|
||||
public Transport4Sbox? transport { get; set; }
|
||||
public HyObfs4Sbox? obfs { get; set; }
|
||||
public List<string>? outbounds { get; set; }
|
||||
public bool? interrupt_exist_connections { get; set; }
|
||||
}
|
||||
|
||||
public class Endpoints4Sbox : BaseServer4Sbox
|
||||
{
|
||||
public bool? system { get; set; }
|
||||
public string? name { get; set; }
|
||||
public int? mtu { get; set; }
|
||||
public List<string> address { get; set; }
|
||||
public string private_key { get; set; }
|
||||
public int? listen_port { get; set; }
|
||||
public string? udp_timeout { get; set; }
|
||||
public int? workers { get; set; }
|
||||
public List<Peer4Sbox> peers { get; set; }
|
||||
}
|
||||
|
||||
public class Peer4Sbox
|
||||
{
|
||||
public string address { get; set; }
|
||||
public int port { get; set; }
|
||||
public string public_key { get; set; }
|
||||
public string? pre_shared_key { get; set; }
|
||||
public List<string> allowed_ips { get; set; }
|
||||
public int? persistent_keepalive_interval { get; set; }
|
||||
public List<int> reserved { get; set; }
|
||||
}
|
||||
|
||||
public class Tls4Sbox
|
||||
{
|
||||
public bool enabled { get; set; }
|
||||
@@ -144,6 +179,9 @@ public class Tls4Sbox
|
||||
public List<string>? alpn { get; set; }
|
||||
public Utls4Sbox? utls { get; set; }
|
||||
public Reality4Sbox? reality { get; set; }
|
||||
public bool? fragment { get; set; }
|
||||
public string? fragment_fallback_delay { get; set; }
|
||||
public bool? record_fragment { get; set; }
|
||||
}
|
||||
|
||||
public class Multiplex4Sbox
|
||||
@@ -191,15 +229,28 @@ public class HyObfs4Sbox
|
||||
public string? password { get; set; }
|
||||
}
|
||||
|
||||
public class Server4Sbox
|
||||
public class Server4Sbox : BaseServer4Sbox
|
||||
{
|
||||
public string? tag { get; set; }
|
||||
public string? inet4_range { get; set; }
|
||||
public string? inet6_range { get; set; }
|
||||
public string? client_subnet { get; set; }
|
||||
public string? server { get; set; }
|
||||
public new string? domain_resolver { get; set; }
|
||||
[JsonPropertyName("interface")] public string? Interface { get; set; }
|
||||
public int? server_port { get; set; }
|
||||
public string? path { get; set; }
|
||||
public Headers4Sbox? headers { get; set; }
|
||||
|
||||
// public List<string>? path { get; set; } // hosts
|
||||
public Dictionary<string, List<string>>? predefined { get; set; }
|
||||
|
||||
// Deprecated
|
||||
public string? address { get; set; }
|
||||
|
||||
public string? address_resolver { get; set; }
|
||||
public string? address_strategy { get; set; }
|
||||
public string? strategy { get; set; }
|
||||
public string? detour { get; set; }
|
||||
public string? client_subnet { get; set; }
|
||||
// Deprecated End
|
||||
}
|
||||
|
||||
public class Experimental4Sbox
|
||||
@@ -229,13 +280,6 @@ public class Stats4Sbox
|
||||
public List<string>? users { get; set; }
|
||||
}
|
||||
|
||||
public class Fakeip4Sbox
|
||||
{
|
||||
public bool enabled { get; set; }
|
||||
public string inet4_range { get; set; }
|
||||
public string inet6_range { get; set; }
|
||||
}
|
||||
|
||||
public class CacheFile4Sbox
|
||||
{
|
||||
public bool enabled { get; set; }
|
||||
@@ -254,3 +298,33 @@ public class Ruleset4Sbox
|
||||
public string? download_detour { get; set; }
|
||||
public string? update_interval { get; set; }
|
||||
}
|
||||
|
||||
public abstract class DialFields4Sbox
|
||||
{
|
||||
public string? detour { get; set; }
|
||||
public string? bind_interface { get; set; }
|
||||
public string? inet4_bind_address { get; set; }
|
||||
public string? inet6_bind_address { get; set; }
|
||||
public int? routing_mark { get; set; }
|
||||
public bool? reuse_addr { get; set; }
|
||||
public string? netns { get; set; }
|
||||
public string? connect_timeout { get; set; }
|
||||
public bool? tcp_fast_open { get; set; }
|
||||
public bool? tcp_multi_path { get; set; }
|
||||
public bool? udp_fragment { get; set; }
|
||||
public Rule4Sbox? domain_resolver { get; set; } // or string
|
||||
public string? network_strategy { get; set; }
|
||||
public List<string>? network_type { get; set; }
|
||||
public List<string>? fallback_network_type { get; set; }
|
||||
public string? fallback_delay { get; set; }
|
||||
public Tls4Sbox? tls { get; set; }
|
||||
public Multiplex4Sbox? multiplex { get; set; }
|
||||
public Transport4Sbox? transport { get; set; }
|
||||
public HyObfs4Sbox? obfs { get; set; }
|
||||
}
|
||||
|
||||
public abstract class BaseServer4Sbox : DialFields4Sbox
|
||||
{
|
||||
public string type { get; set; }
|
||||
public string tag { get; set; }
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace ServiceLib.Models;
|
||||
public class V2rayConfig
|
||||
{
|
||||
public Log4Ray log { get; set; }
|
||||
public object dns { get; set; }
|
||||
public Dns4Ray dns { get; set; }
|
||||
public List<Inbounds4Ray> inbounds { get; set; }
|
||||
public List<Outbounds4Ray> outbounds { get; set; }
|
||||
public Routing4Ray routing { get; set; }
|
||||
@@ -203,7 +203,8 @@ public class Response4Ray
|
||||
|
||||
public class Dns4Ray
|
||||
{
|
||||
public List<string> servers { get; set; }
|
||||
public Dictionary<string, List<string>>? hosts { get; set; }
|
||||
public List<object> servers { get; set; }
|
||||
}
|
||||
|
||||
public class DnsServer4Ray
|
||||
@@ -211,6 +212,8 @@ public class DnsServer4Ray
|
||||
public string? address { get; set; }
|
||||
public List<string>? domains { get; set; }
|
||||
public bool? skipFallback { get; set; }
|
||||
public List<string>? expectedIPs { get; set; }
|
||||
public List<string>? unexpectedIPs { get; set; }
|
||||
}
|
||||
|
||||
public class Routing4Ray
|
||||
@@ -340,6 +343,7 @@ public class TlsSettings4Ray
|
||||
public string? publicKey { get; set; }
|
||||
public string? shortId { get; set; }
|
||||
public string? spiderX { get; set; }
|
||||
public string? mldsa65Verify { get; set; }
|
||||
}
|
||||
|
||||
public class TcpSettings4Ray
|
||||
|
||||
328
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
328
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -132,24 +132,6 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Download speed 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string downloadSpeed {
|
||||
get {
|
||||
return ResourceManager.GetString("downloadSpeed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Do you want to download {0}? 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string DownloadYesNo {
|
||||
get {
|
||||
return ResourceManager.GetString("DownloadYesNo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Failed to convert configuration file 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -672,6 +654,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add [Anytls] Configuration 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuAddAnytlsServer {
|
||||
get {
|
||||
return ResourceManager.GetString("menuAddAnytlsServer", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add a custom configuration Configuration 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -1347,15 +1338,6 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Advanced Function 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string menuRoutingAdvanced {
|
||||
get {
|
||||
return ResourceManager.GetString("menuRoutingAdvanced", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2229,6 +2211,24 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Incorrect password, please try again. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string SudoIncorrectPasswordTip {
|
||||
get {
|
||||
return ResourceManager.GetString("SudoIncorrectPasswordTip", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Add Common DNS Hosts 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbAddCommonDNSHosts {
|
||||
get {
|
||||
return ResourceManager.GetString("TbAddCommonDNSHosts", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Address 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2265,6 +2265,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Apply to Proxy Domains Only 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbApplyProxyDomainsOnly {
|
||||
get {
|
||||
return ResourceManager.GetString("TbApplyProxyDomainsOnly", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Auto refresh 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2292,6 +2301,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Block SVCB and HTTPS Queries 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbBlockSVCBHTTPSQueries {
|
||||
get {
|
||||
return ResourceManager.GetString("TbBlockSVCBHTTPSQueries", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Browse 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2346,6 +2364,24 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Enable Custom DNS 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbCustomDNSEnable {
|
||||
get {
|
||||
return ResourceManager.GetString("TbCustomDNSEnable", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Custom DNS Enabled, This Page's Settings Invalid 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbCustomDNSEnabledPageInvalid {
|
||||
get {
|
||||
return ResourceManager.GetString("TbCustomDNSEnabledPageInvalid", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Display GUI 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2364,6 +2400,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 DNS Hosts: ("domain1 ip1 ip2" per line) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbDNSHostsConfig {
|
||||
get {
|
||||
return ResourceManager.GetString("TbDNSHostsConfig", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Supports DNS Object; Click to view documentation 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2409,6 +2454,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Domestic DNS 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbDomesticDNS {
|
||||
get {
|
||||
return ResourceManager.GetString("TbDomesticDNS", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Edit 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2427,6 +2481,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 FakeIP 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbFakeIP {
|
||||
get {
|
||||
return ResourceManager.GetString("TbFakeIP", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Fingerprint 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2535,6 +2598,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Mldsa65Verify 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbMldsa65Verify {
|
||||
get {
|
||||
return ResourceManager.GetString("TbMldsa65Verify", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Transport protocol(network) 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2616,6 +2688,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Prevent DNS Leaks 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbPreventDNSLeaks {
|
||||
get {
|
||||
return ResourceManager.GetString("TbPreventDNSLeaks", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Private Key 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2652,6 +2733,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Remote DNS 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbRemoteDNS {
|
||||
get {
|
||||
return ResourceManager.GetString("TbRemoteDNS", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Camouflage domain(host) 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2715,33 +2805,6 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 3. Block Domain or IP 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbRoutingTabBlock {
|
||||
get {
|
||||
return ResourceManager.GetString("TbRoutingTabBlock", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 2. Direct Domain or IP 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbRoutingTabDirect {
|
||||
get {
|
||||
return ResourceManager.GetString("TbRoutingTabDirect", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 1. Proxy Domain or IP 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbRoutingTabProxy {
|
||||
get {
|
||||
return ResourceManager.GetString("TbRoutingTabProxy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Pre-defined Rule Set List 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -2778,6 +2841,78 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Can fill in the configuration remarks, please make sure it exist and are unique 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbRuleOutboundTagTip {
|
||||
get {
|
||||
return ResourceManager.GetString("TbRuleOutboundTagTip", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 sing-box Direct Resolution Strategy 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBDirectResolveStrategy {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBDirectResolveStrategy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 The sing-box DoH resolution server can be overwritten 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBDoHOverride {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBDoHOverride", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 sing-box DoH Resolver Server 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBDoHResolverServer {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBDoHResolverServer", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Fallback DNS Resolution, Suggest IP 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBFallbackDNSResolve {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBFallbackDNSResolve", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Resolve Outbound Domains 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBOutboundDomainResolve {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBOutboundDomainResolve", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Outbound DNS Resolution (sing-box) 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBOutboundsResolverDNS {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBOutboundsResolverDNS", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 sing-box Remote Resolution Strategy 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSBRemoteResolveStrategy {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSBRemoteResolveStrategy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Encryption method (security) 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -3103,7 +3238,7 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Use Xray and enable non-Tun mode, which conflicts with the group previous proxy 的本地化字符串。
|
||||
/// 查找类似 which conflicts with the group previous proxy 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsEnableFragmentTips {
|
||||
get {
|
||||
@@ -3201,6 +3336,15 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Current connection info test URL 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsIPAPIUrl {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSettingsIPAPIUrl", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Keep older entries when de-duplicating 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -3229,25 +3373,7 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Please set the sudo password in Tun mode settings first 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsLinuxSudoPasswordIsEmpty {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSettingsLinuxSudoPasswordIsEmpty", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Please do not run this app with sudo 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsLinuxSudoPasswordNotSudoRunApp {
|
||||
get {
|
||||
return ResourceManager.GetString("TbSettingsLinuxSudoPasswordNotSudoRunApp", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 The password you entered cannot be verified, so make sure you enter it correctly. If the application does not work properly due to an incorrect input, please restart the application. The password will not be stored and you will need to enter it again after each restart. 的本地化字符串。
|
||||
/// 查找类似 The password will be validated via the command line. If a validation error causes the application to malfunction, please restart the application. The password will not be stored and must be entered again after each restart. 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbSettingsLinuxSudoPasswordTip {
|
||||
get {
|
||||
@@ -3732,6 +3858,33 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Validate Regional Domain IPs 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbValidateDirectExpectedIPs {
|
||||
get {
|
||||
return ResourceManager.GetString("TbValidateDirectExpectedIPs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbValidateDirectExpectedIPsDesc {
|
||||
get {
|
||||
return ResourceManager.GetString("TbValidateDirectExpectedIPsDesc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 xray Freedom Resolution Strategy 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string TbXrayFreedomResolveStrategy {
|
||||
get {
|
||||
return ResourceManager.GetString("TbXrayFreedomResolveStrategy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 The delay: {0} ms, {1} 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -3741,6 +3894,24 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Advanced DNS Settings 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string ThAdvancedDNSSettings {
|
||||
get {
|
||||
return ResourceManager.GetString("ThAdvancedDNSSettings", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Basic DNS Settings 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string ThBasicDNSSettings {
|
||||
get {
|
||||
return ResourceManager.GetString("ThBasicDNSSettings", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Active 的本地化字符串。
|
||||
/// </summary>
|
||||
@@ -3939,15 +4110,6 @@ namespace ServiceLib.Resx {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Ungrouped 的本地化字符串。
|
||||
/// </summary>
|
||||
public static string UngroupedServers {
|
||||
get {
|
||||
return ResourceManager.GetString("UngroupedServers", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找类似 Upgrade App does not exist 的本地化字符串。
|
||||
/// </summary>
|
||||
|
||||
@@ -132,12 +132,6 @@
|
||||
<data name="Downloading" xml:space="preserve">
|
||||
<value>درحال دانلود...</value>
|
||||
</data>
|
||||
<data name="downloadSpeed" xml:space="preserve">
|
||||
<value>دانلود</value>
|
||||
</data>
|
||||
<data name="DownloadYesNo" xml:space="preserve">
|
||||
<value>آیا می خواهید {0} را دانلود کنید؟</value>
|
||||
</data>
|
||||
<data name="FailedConversionConfiguration" xml:space="preserve">
|
||||
<value>تبدیل فایل پیکربندی انجام نشد</value>
|
||||
</data>
|
||||
@@ -387,9 +381,6 @@
|
||||
<data name="RegisterGlobalHotkeySuccessfully" xml:space="preserve">
|
||||
<value>کلید میانبر جهانی {0} با موفقیت ثبت شد</value>
|
||||
</data>
|
||||
<data name="UngroupedServers" xml:space="preserve">
|
||||
<value>گروه بندی نشده</value>
|
||||
</data>
|
||||
<data name="AllGroupServers" xml:space="preserve">
|
||||
<value>همه سرورها</value>
|
||||
</data>
|
||||
@@ -822,9 +813,6 @@
|
||||
<data name="menuWebsiteItem" xml:space="preserve">
|
||||
<value>{0} وب سایت</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvanced" xml:space="preserve">
|
||||
<value>عملکرد پیشرفته</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvancedAdd" xml:space="preserve">
|
||||
<value>اضافه کردن</value>
|
||||
</data>
|
||||
@@ -843,15 +831,6 @@
|
||||
<data name="TbdomainStrategy" xml:space="preserve">
|
||||
<value>استراتژی دامنه</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabBlock" xml:space="preserve">
|
||||
<value>3. مسدود کردن دامنه یا آیپی</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabDirect" xml:space="preserve">
|
||||
<value>2. دایرکت کردن دامنه یا IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabProxy" xml:space="preserve">
|
||||
<value>1. پروکسی کردن دامنه یا IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabRuleList" xml:space="preserve">
|
||||
<value>لیست مجموعه قوانین از پیش تعریف شده</value>
|
||||
</data>
|
||||
@@ -1126,7 +1105,7 @@
|
||||
<value>افزودن سرور [HTTP]</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>از Xray استفاده کنید و حالت non-Tun را فعال کنید، که با پراکسی قبلی گروه در تضاد است</value>
|
||||
<value>which conflicts with the group previous proxy</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>فعال کردن فرگمنت</value>
|
||||
@@ -1339,13 +1318,7 @@
|
||||
<value>رمز عبور sudo سیستم</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>رمز عبوری که وارد کرده اید تایید نمی شود، بنابراین مطمئن شوید که آن را به درستی وارد کرده اید. اگر برنامه به دلیل ورودی نادرست به درستی کار نمی کند، لطفاً برنامه را مجدداً راه اندازی کنید. رمز عبور ذخیره نمی شود و پس از هر بار راه اندازی مجدد باید آن را دوباره وارد کنید.</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
|
||||
<value>لطفاً ابتدا رمز عبور sudo را در تنظیمات حالت Tun تنظیم کنید</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
|
||||
<value>لطفا این برنامه را با sudo اجرا نکنید</value>
|
||||
<value>The password will be validated via the command line. If a validation error causes the application to malfunction, please restart the application. The password will not be stored and must be entered again after each restart.</value>
|
||||
</data>
|
||||
<data name="TransportHeaderTypeTip5" xml:space="preserve">
|
||||
<value>*حالت xhttp</value>
|
||||
@@ -1416,4 +1389,85 @@
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>صادر کردن سرور</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
||||
<value>URL آزمایش اطلاعات اتصال فعلی</value>
|
||||
</data>
|
||||
<data name="TbRuleOutboundTagTip" xml:space="preserve">
|
||||
<value>Can fill in the configuration remarks, please make sure it exist and are unique</value>
|
||||
</data>
|
||||
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
||||
<value>Incorrect password, please try again.</value>
|
||||
</data>
|
||||
<data name="TbMldsa65Verify" xml:space="preserve">
|
||||
<value>Mldsa65Verify</value>
|
||||
</data>
|
||||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>Add [Anytls] Configuration</value>
|
||||
</data>
|
||||
<data name="TbRemoteDNS" xml:space="preserve">
|
||||
<value>Remote DNS</value>
|
||||
</data>
|
||||
<data name="TbDomesticDNS" xml:space="preserve">
|
||||
<value>Domestic DNS</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
||||
<value>Outbound DNS Resolution (sing-box)</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
||||
<value>Resolve Outbound Domains</value>
|
||||
</data>
|
||||
<data name="TbSBDoHResolverServer" xml:space="preserve">
|
||||
<value>sing-box DoH Resolver Server</value>
|
||||
</data>
|
||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
||||
<value>Fallback DNS Resolution, Suggest IP</value>
|
||||
</data>
|
||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
||||
<value>xray Freedom Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box Direct Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box Remote Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||
<value>Add Common DNS Hosts</value>
|
||||
</data>
|
||||
<data name="TbSBDoHOverride" xml:space="preserve">
|
||||
<value>The sing-box DoH resolution server can be overwritten</value>
|
||||
</data>
|
||||
<data name="TbFakeIP" xml:space="preserve">
|
||||
<value>FakeIP</value>
|
||||
</data>
|
||||
<data name="TbBlockSVCBHTTPSQueries" xml:space="preserve">
|
||||
<value>Block SVCB and HTTPS Queries</value>
|
||||
</data>
|
||||
<data name="TbPreventDNSLeaks" xml:space="preserve">
|
||||
<value>Prevent DNS Leaks</value>
|
||||
</data>
|
||||
<data name="TbDNSHostsConfig" xml:space="preserve">
|
||||
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
|
||||
</data>
|
||||
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
|
||||
<value>Apply to Proxy Domains Only</value>
|
||||
</data>
|
||||
<data name="ThBasicDNSSettings" xml:space="preserve">
|
||||
<value>Basic DNS Settings</value>
|
||||
</data>
|
||||
<data name="ThAdvancedDNSSettings" xml:space="preserve">
|
||||
<value>Advanced DNS Settings</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPs" xml:space="preserve">
|
||||
<value>Validate Regional Domain IPs</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||
<value>Enable Custom DNS</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve">
|
||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||
</data>
|
||||
</root>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -132,12 +132,6 @@
|
||||
<data name="Downloading" xml:space="preserve">
|
||||
<value>Downloading...</value>
|
||||
</data>
|
||||
<data name="downloadSpeed" xml:space="preserve">
|
||||
<value>Download speed</value>
|
||||
</data>
|
||||
<data name="DownloadYesNo" xml:space="preserve">
|
||||
<value>Do you want to download {0}?</value>
|
||||
</data>
|
||||
<data name="FailedConversionConfiguration" xml:space="preserve">
|
||||
<value>Failed to convert configuration file</value>
|
||||
</data>
|
||||
@@ -387,9 +381,6 @@
|
||||
<data name="RegisterGlobalHotkeySuccessfully" xml:space="preserve">
|
||||
<value>Global hotkey {0} registered successfully</value>
|
||||
</data>
|
||||
<data name="UngroupedServers" xml:space="preserve">
|
||||
<value>Ungrouped</value>
|
||||
</data>
|
||||
<data name="AllGroupServers" xml:space="preserve">
|
||||
<value>All</value>
|
||||
</data>
|
||||
@@ -822,9 +813,6 @@
|
||||
<data name="menuWebsiteItem" xml:space="preserve">
|
||||
<value>{0} Website</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvanced" xml:space="preserve">
|
||||
<value>Advanced Function</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvancedAdd" xml:space="preserve">
|
||||
<value>Add</value>
|
||||
</data>
|
||||
@@ -843,15 +831,6 @@
|
||||
<data name="TbdomainStrategy" xml:space="preserve">
|
||||
<value>Domain strategy</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabBlock" xml:space="preserve">
|
||||
<value>3. Block Domain or IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabDirect" xml:space="preserve">
|
||||
<value>2. Direct Domain or IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabProxy" xml:space="preserve">
|
||||
<value>1. Proxy Domain or IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabRuleList" xml:space="preserve">
|
||||
<value>Pre-defined Rule Set List</value>
|
||||
</data>
|
||||
@@ -1126,7 +1105,7 @@
|
||||
<value>Add [HTTP] Configuration</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>Use Xray and enable non-Tun mode, which conflicts with the group previous proxy</value>
|
||||
<value>which conflicts with the group previous proxy</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>Enable fragment</value>
|
||||
@@ -1339,13 +1318,7 @@
|
||||
<value>System sudo password</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>The password you entered cannot be verified, so make sure you enter it correctly. If the application does not work properly due to an incorrect input, please restart the application. The password will not be stored and you will need to enter it again after each restart.</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
|
||||
<value>Please set the sudo password in Tun mode settings first</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
|
||||
<value>Please do not run this app with sudo</value>
|
||||
<value>The password will be validated via the command line. If a validation error causes the application to malfunction, please restart the application. The password will not be stored and must be entered again after each restart.</value>
|
||||
</data>
|
||||
<data name="TransportHeaderTypeTip5" xml:space="preserve">
|
||||
<value>*xhttp mode</value>
|
||||
@@ -1416,4 +1389,85 @@
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>Export Configuration</value>
|
||||
</data>
|
||||
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
||||
<value>Current connection info test URL</value>
|
||||
</data>
|
||||
<data name="TbRuleOutboundTagTip" xml:space="preserve">
|
||||
<value>Can fill in the configuration remarks, please make sure it exist and are unique</value>
|
||||
</data>
|
||||
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
||||
<value>Incorrect password, please try again.</value>
|
||||
</data>
|
||||
<data name="TbMldsa65Verify" xml:space="preserve">
|
||||
<value>Mldsa65Verify</value>
|
||||
</data>
|
||||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>Add [Anytls] Configuration</value>
|
||||
</data>
|
||||
<data name="TbRemoteDNS" xml:space="preserve">
|
||||
<value>Remote DNS</value>
|
||||
</data>
|
||||
<data name="TbDomesticDNS" xml:space="preserve">
|
||||
<value>Domestic DNS</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
||||
<value>Outbound DNS Resolution (sing-box)</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
||||
<value>Resolve Outbound Domains</value>
|
||||
</data>
|
||||
<data name="TbSBDoHResolverServer" xml:space="preserve">
|
||||
<value>sing-box DoH Resolver Server</value>
|
||||
</data>
|
||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
||||
<value>Fallback DNS Resolution, Suggest IP</value>
|
||||
</data>
|
||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
||||
<value>xray Freedom Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box Direct Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box Remote Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||
<value>Add Common DNS Hosts</value>
|
||||
</data>
|
||||
<data name="TbSBDoHOverride" xml:space="preserve">
|
||||
<value>The sing-box DoH resolution server can be overwritten</value>
|
||||
</data>
|
||||
<data name="TbFakeIP" xml:space="preserve">
|
||||
<value>FakeIP</value>
|
||||
</data>
|
||||
<data name="TbBlockSVCBHTTPSQueries" xml:space="preserve">
|
||||
<value>Block SVCB and HTTPS Queries</value>
|
||||
</data>
|
||||
<data name="TbPreventDNSLeaks" xml:space="preserve">
|
||||
<value>Prevent DNS Leaks</value>
|
||||
</data>
|
||||
<data name="TbDNSHostsConfig" xml:space="preserve">
|
||||
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
|
||||
</data>
|
||||
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
|
||||
<value>Apply to Proxy Domains Only</value>
|
||||
</data>
|
||||
<data name="ThBasicDNSSettings" xml:space="preserve">
|
||||
<value>Basic DNS Settings</value>
|
||||
</data>
|
||||
<data name="ThAdvancedDNSSettings" xml:space="preserve">
|
||||
<value>Advanced DNS Settings</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPs" xml:space="preserve">
|
||||
<value>Validate Regional Domain IPs</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||
<value>Enable Custom DNS</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve">
|
||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -118,31 +118,25 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="BatchExportURLSuccessfully" xml:space="preserve">
|
||||
<value>Экспортирование URL в буфер обмена успешно завершено</value>
|
||||
<value>Ссылка успешно скопирована в буфер обмена</value>
|
||||
</data>
|
||||
<data name="CheckServerSettings" xml:space="preserve">
|
||||
<value>Пожалуйста, сначала проверьте настройки сервера</value>
|
||||
<value>Сначала проверьте настройки сервера</value>
|
||||
</data>
|
||||
<data name="ConfigurationFormatIncorrect" xml:space="preserve">
|
||||
<value>Недопустимый формат конфигурации</value>
|
||||
</data>
|
||||
<data name="CustomServerTips" xml:space="preserve">
|
||||
<value>Обратите внимание, что пользовательская конфигурация полностью зависит от вашей собственной конфигурации и работает не со всеми настройками. Если вы хотите использовать системный прокси, пожалуйста, измените порт прослушивания вручную.</value>
|
||||
<value>Обратите внимание, что пользовательская конфигурация полностью зависит от вашей собственной конфигурации и работает не со всеми настройками. Если вы хотите использовать системный прокси, то измените порт прослушивания вручную</value>
|
||||
</data>
|
||||
<data name="Downloading" xml:space="preserve">
|
||||
<value>Загрузка...</value>
|
||||
</data>
|
||||
<data name="downloadSpeed" xml:space="preserve">
|
||||
<value>Скорость загрузки</value>
|
||||
</data>
|
||||
<data name="DownloadYesNo" xml:space="preserve">
|
||||
<value>Хотите загрузить? {0}</value>
|
||||
</data>
|
||||
<data name="FailedConversionConfiguration" xml:space="preserve">
|
||||
<value>Не удалось преобразовать файл конфигурации</value>
|
||||
</data>
|
||||
<data name="FailedGenDefaultConfiguration" xml:space="preserve">
|
||||
<value>Не удалось создать файл конфигурации по умолчаниюe</value>
|
||||
<value>Не удалось создать файл конфигурации по умолчанию</value>
|
||||
</data>
|
||||
<data name="FailedGetDefaultConfiguration" xml:space="preserve">
|
||||
<value>Не удалось получить конфигурацию по умолчанию</value>
|
||||
@@ -154,37 +148,37 @@
|
||||
<value>Не удалось прочитать файл конфигурации</value>
|
||||
</data>
|
||||
<data name="FillCorrectServerPort" xml:space="preserve">
|
||||
<value>Пожалуйста, укажите порт сервера в правильном формате</value>
|
||||
<value>Укажите порт сервера в правильном формате</value>
|
||||
</data>
|
||||
<data name="FillLocalListeningPort" xml:space="preserve">
|
||||
<value>Пожалуйста, укажите локальный порт прослушивания</value>
|
||||
<value>Введите локальный порт для прослушивания</value>
|
||||
</data>
|
||||
<data name="FillPassword" xml:space="preserve">
|
||||
<value>Пожалуйста, введите пароль</value>
|
||||
<value>Введите пароль</value>
|
||||
</data>
|
||||
<data name="FillServerAddress" xml:space="preserve">
|
||||
<value>Пожалуйста, заполните адрес сервера</value>
|
||||
<value>Введите адрес сервера</value>
|
||||
</data>
|
||||
<data name="FillUUID" xml:space="preserve">
|
||||
<value>Пожалуйста, заполните идентификатор пользователя</value>
|
||||
<value>Введите идентификатор пользователя</value>
|
||||
</data>
|
||||
<data name="Incorrectconfiguration" xml:space="preserve">
|
||||
<value>Некорректная конфигурация, пожалуйста, проверьте</value>
|
||||
<value>Некорректная конфигурация. Проверьте</value>
|
||||
</data>
|
||||
<data name="InitialConfiguration" xml:space="preserve">
|
||||
<value>Исходная конфигурация</value>
|
||||
</data>
|
||||
<data name="IsLatestCore" xml:space="preserve">
|
||||
<value>{0} {1} является последней версией.</value>
|
||||
<value>{0} {1} является последней версией</value>
|
||||
</data>
|
||||
<data name="IsLatestN" xml:space="preserve">
|
||||
<value>{0} {1} является последней версией.</value>
|
||||
<value>{0} {1} является последней версией</value>
|
||||
</data>
|
||||
<data name="LvAddress" xml:space="preserve">
|
||||
<value>Адрес</value>
|
||||
</data>
|
||||
<data name="LvEncryptionMethod" xml:space="preserve">
|
||||
<value>Безопасность</value>
|
||||
<value>Метод шифрования</value>
|
||||
</data>
|
||||
<data name="LvPort" xml:space="preserve">
|
||||
<value>Порт</value>
|
||||
@@ -199,7 +193,7 @@
|
||||
<value>Загружено трафика сегодня</value>
|
||||
</data>
|
||||
<data name="LvTodayUploadDataAmount" xml:space="preserve">
|
||||
<value>Отдано трафика сегодня</value>
|
||||
<value>Отправлено сегодня</value>
|
||||
</data>
|
||||
<data name="LvTotalDownloadDataAmount" xml:space="preserve">
|
||||
<value>Всего загружено</value>
|
||||
@@ -214,13 +208,13 @@
|
||||
<value>Ядро успешно загружено</value>
|
||||
</data>
|
||||
<data name="MsgFailedImportSubscription" xml:space="preserve">
|
||||
<value>Не удалось импортировать содержимое подписки</value>
|
||||
<value>Не удалось импортировать подписку</value>
|
||||
</data>
|
||||
<data name="MsgGetSubscriptionSuccessfully" xml:space="preserve">
|
||||
<value>Содержимое подписки успешно импортировано</value>
|
||||
</data>
|
||||
<data name="MsgNoValidSubscription" xml:space="preserve">
|
||||
<value>Нет установлены подписки</value>
|
||||
<value>Нет установленных подписок</value>
|
||||
</data>
|
||||
<data name="MsgParsingSuccessfully" xml:space="preserve">
|
||||
<value>Парсинг {0} прошел успешно</value>
|
||||
@@ -235,7 +229,7 @@
|
||||
<value>Некорректное содержимое подписки</value>
|
||||
</data>
|
||||
<data name="MsgUnpacking" xml:space="preserve">
|
||||
<value>распаковывается...</value>
|
||||
<value>Распаковка…</value>
|
||||
</data>
|
||||
<data name="MsgUpdateSubscriptionEnd" xml:space="preserve">
|
||||
<value>Обновление подписки закончено</value>
|
||||
@@ -244,37 +238,37 @@
|
||||
<value>Обновление подписки начинается</value>
|
||||
</data>
|
||||
<data name="MsgUpdateV2rayCoreSuccessfully" xml:space="preserve">
|
||||
<value>Успешное обновление ядра</value>
|
||||
<value>Ядро успешно обновлено</value>
|
||||
</data>
|
||||
<data name="MsgUpdateV2rayCoreSuccessfullyMore" xml:space="preserve">
|
||||
<value>Успешное обновление ядра! Перезапуск службы...</value>
|
||||
</data>
|
||||
<data name="NonvmessOrssProtocol" xml:space="preserve">
|
||||
<value>Не является протоколом Vmess или SS</value>
|
||||
<value>Не является протоколом VMess или Shadowsocks</value>
|
||||
</data>
|
||||
<data name="NotFoundCore" xml:space="preserve">
|
||||
<value>Файл Core (имя файла: {1}) не найден в папке ({0}), загрузите и поместите его в папку, адрес загрузки: {2}</value>
|
||||
<value>Файл ядра ({1}) не найден в папке {0}. Скачайте по адресу {2} и поместите его туда</value>
|
||||
</data>
|
||||
<data name="NoValidQRcodeFound" xml:space="preserve">
|
||||
<value>Сканирование завершено, не найден корректный QR код</value>
|
||||
</data>
|
||||
<data name="OperationFailed" xml:space="preserve">
|
||||
<value> операция безуспешна, проверьте и попробуйте ещё раз</value>
|
||||
<value>Операция безуспешна, проверьте и попробуйте ещё раз</value>
|
||||
</data>
|
||||
<data name="PleaseFillRemarks" xml:space="preserve">
|
||||
<value>Пожалуйста, заполните примечания</value>
|
||||
<value>Введите примечания</value>
|
||||
</data>
|
||||
<data name="PleaseSelectEncryption" xml:space="preserve">
|
||||
<value>Пожалуйста, выберите метод шифрования</value>
|
||||
<value>Выберите метод шифрования</value>
|
||||
</data>
|
||||
<data name="PleaseSelectProtocol" xml:space="preserve">
|
||||
<value>Пожалуйста, выберите протокол</value>
|
||||
<value>Выберите протокол</value>
|
||||
</data>
|
||||
<data name="PleaseSelectServer" xml:space="preserve">
|
||||
<value>Сначала выберите сервер</value>
|
||||
</data>
|
||||
<data name="RemoveDuplicateServerResult" xml:space="preserve">
|
||||
<value>Удаление дублей завершено. Старая: {0}, Новая: {1}.</value>
|
||||
<value>Удаление дублей завершено. Старая: {0}, Новая: {1}</value>
|
||||
</data>
|
||||
<data name="RemoveServer" xml:space="preserve">
|
||||
<value>Вы уверены, что хотите удалить сервер?</value>
|
||||
@@ -286,16 +280,16 @@
|
||||
<value>Запуск сервиса ({0})...</value>
|
||||
</data>
|
||||
<data name="SuccessfulConfiguration" xml:space="preserve">
|
||||
<value>Конфигурация успешна {0}</value>
|
||||
<value>Конфигурация выполнена успешно {0}</value>
|
||||
</data>
|
||||
<data name="SuccessfullyImportedCustomServer" xml:space="preserve">
|
||||
<value>Пользовательский сервер конфигурации успешно импортирован.</value>
|
||||
<value>Пользовательская конфигурация сервера успешно импортирована</value>
|
||||
</data>
|
||||
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
|
||||
<value>{0} серверов импортировано из буфера обмена.</value>
|
||||
<value>{0} серверов импортировано из буфера обмена</value>
|
||||
</data>
|
||||
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
|
||||
<value>Сканирование URL-адреса импорта успешна.</value>
|
||||
<value>Сканирование URL-адреса импорта прошло успешно</value>
|
||||
</data>
|
||||
<data name="TestMeOutput" xml:space="preserve">
|
||||
<value>Задержка текущего сервера: {0} мс, {1}</value>
|
||||
@@ -310,7 +304,7 @@
|
||||
<value>Вы уверены, что хотите удалить правила?</value>
|
||||
</data>
|
||||
<data name="RoutingRuleDetailRequiredTips" xml:space="preserve">
|
||||
<value>{0}, Один из обязательных..</value>
|
||||
<value>{0}: одно из обязательных полей</value>
|
||||
</data>
|
||||
<data name="LvRemarks" xml:space="preserve">
|
||||
<value>Примечания</value>
|
||||
@@ -322,13 +316,13 @@
|
||||
<value>Количество</value>
|
||||
</data>
|
||||
<data name="MsgNeedUrl" xml:space="preserve">
|
||||
<value>Пожалуйста, заполните адрес (Url)</value>
|
||||
<value>Введите URL-адрес</value>
|
||||
</data>
|
||||
<data name="AddBatchRoutingRulesYesNo" xml:space="preserve">
|
||||
<value>Вы хотите добавить правила? Выберите «Да», чтобы добавить, выберите иное, чтобы заменить</value>
|
||||
<value>Хотите добавить правила? Выберите «Да» для добавления или «Нет» для замены</value>
|
||||
</data>
|
||||
<data name="MsgDownloadGeoFileSuccessfully" xml:space="preserve">
|
||||
<value>Загрузка GeoFile: {0} успешна</value>
|
||||
<value>Загрузка GeoFile {0} прошла успешно</value>
|
||||
</data>
|
||||
<data name="MsgInformationTitle" xml:space="preserve">
|
||||
<value>Информация</value>
|
||||
@@ -337,49 +331,49 @@
|
||||
<value>Пользовательская иконка</value>
|
||||
</data>
|
||||
<data name="FillCorrectDNSText" xml:space="preserve">
|
||||
<value>Пожалуйста, заполните правильный пользовательский DNS</value>
|
||||
<value>Введите корректный пользовательский DNS</value>
|
||||
</data>
|
||||
<data name="TransportPathTip1" xml:space="preserve">
|
||||
<value>*ws путь</value>
|
||||
<value>*WebSocket-путь</value>
|
||||
</data>
|
||||
<data name="TransportPathTip2" xml:space="preserve">
|
||||
<value>*h2 путь</value>
|
||||
<value>*HTTP2-путь</value>
|
||||
</data>
|
||||
<data name="TransportPathTip3" xml:space="preserve">
|
||||
<value>*QUIC ключ/Kcp seed</value>
|
||||
<value>*QUIC-ключ / KCP-seed</value>
|
||||
</data>
|
||||
<data name="TransportPathTip4" xml:space="preserve">
|
||||
<value>*grpc serviceName</value>
|
||||
<value>Имя сервиса *gRPC</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip1" xml:space="preserve">
|
||||
<value>*http хосты разделенные запятыми (,)</value>
|
||||
<value>*http-хосты, разделённые запятыми (,)</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip2" xml:space="preserve">
|
||||
<value>*ws хост</value>
|
||||
<value>*WebSocket-хост</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip3" xml:space="preserve">
|
||||
<value>*h2 хосты разделенные запятыми (,)</value>
|
||||
<value>*HTTP2-хосты, разделённые запятыми (,)</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip4" xml:space="preserve">
|
||||
<value>* безопасность QUIC</value>
|
||||
<value>Безопасность *QUIC</value>
|
||||
</data>
|
||||
<data name="TransportHeaderTypeTip1" xml:space="preserve">
|
||||
<value>* тип TCP камуфляжа</value>
|
||||
<value>Тип *TCP-камуфляжа</value>
|
||||
</data>
|
||||
<data name="TransportHeaderTypeTip2" xml:space="preserve">
|
||||
<value>* тип KCP камуфляжа</value>
|
||||
<value>Тип *KCP-камуфляжа</value>
|
||||
</data>
|
||||
<data name="TransportHeaderTypeTip3" xml:space="preserve">
|
||||
<value>* тип QUIC камуфляжа</value>
|
||||
<value>Тип *QUIC-камуфляжа</value>
|
||||
</data>
|
||||
<data name="TransportHeaderTypeTip4" xml:space="preserve">
|
||||
<value>* режим grpc</value>
|
||||
<value>Режим *gRPC</value>
|
||||
</data>
|
||||
<data name="LvTLS" xml:space="preserve">
|
||||
<value>TLS</value>
|
||||
</data>
|
||||
<data name="TransportPathTip5" xml:space="preserve">
|
||||
<value>*Kcp seed</value>
|
||||
<value>*KCP-seed</value>
|
||||
</data>
|
||||
<data name="RegisterGlobalHotkeyFailed" xml:space="preserve">
|
||||
<value>Не удалось зарегистрировать глобальную горячую клавишу {0}, причина: {1}</value>
|
||||
@@ -387,14 +381,11 @@
|
||||
<data name="RegisterGlobalHotkeySuccessfully" xml:space="preserve">
|
||||
<value>Глобальная горячая клавиша {0} зарегистрирована успешно</value>
|
||||
</data>
|
||||
<data name="UngroupedServers" xml:space="preserve">
|
||||
<value>Разгруппировано</value>
|
||||
</data>
|
||||
<data name="AllGroupServers" xml:space="preserve">
|
||||
<value>Все сервера</value>
|
||||
<value>Все серверы</value>
|
||||
</data>
|
||||
<data name="FillServerAddressCustom" xml:space="preserve">
|
||||
<value>Пожалуйста, просмотрите, чтобы импортировать конфигурацию сервера</value>
|
||||
<value>Выберите файл конфигурации сервера для импорта</value>
|
||||
</data>
|
||||
<data name="Speedtesting" xml:space="preserve">
|
||||
<value>Тестирование...</value>
|
||||
@@ -436,7 +427,7 @@
|
||||
<value>Настройки маршрутизации</value>
|
||||
</data>
|
||||
<data name="menuServers" xml:space="preserve">
|
||||
<value>Сервера</value>
|
||||
<value>Серверы</value>
|
||||
</data>
|
||||
<data name="menuSetting" xml:space="preserve">
|
||||
<value>Настройки</value>
|
||||
@@ -445,7 +436,7 @@
|
||||
<value>Обновить текущую подписку без прокси</value>
|
||||
</data>
|
||||
<data name="menuSubGroupUpdateViaProxy" xml:space="preserve">
|
||||
<value>Обновить текущую подписку с прокси</value>
|
||||
<value>Обновить подписку через прокси</value>
|
||||
</data>
|
||||
<data name="menuSubscription" xml:space="preserve">
|
||||
<value>Группа подписки</value>
|
||||
@@ -571,7 +562,7 @@
|
||||
<value>Сортировка</value>
|
||||
</data>
|
||||
<data name="LvUserAgent" xml:space="preserve">
|
||||
<value>User Agent</value>
|
||||
<value>Заголовок User-Agent</value>
|
||||
</data>
|
||||
<data name="TbCancel" xml:space="preserve">
|
||||
<value>Отмена</value>
|
||||
@@ -628,7 +619,7 @@
|
||||
<value>TLS</value>
|
||||
</data>
|
||||
<data name="TipNetwork" xml:space="preserve">
|
||||
<value>* По-умолчанию TCP</value>
|
||||
<value>*По-умолчанию TCP</value>
|
||||
</data>
|
||||
<data name="TbCoreType" xml:space="preserve">
|
||||
<value>Ядро</value>
|
||||
@@ -658,10 +649,10 @@
|
||||
<value>Шифрования</value>
|
||||
</data>
|
||||
<data name="TbPreSocksPort" xml:space="preserve">
|
||||
<value>txtPreSocksPort</value>
|
||||
<value>Порт SOCKS</value>
|
||||
</data>
|
||||
<data name="TipPreSocksPort" xml:space="preserve">
|
||||
<value>* После установки этого значения служба socks будет запущена с использованием Xray/sing-box(Tun) для обеспечения таких функций, как отображение скорости</value>
|
||||
<value>* После установки этого значения служба SOCKS будет запущена с использованием Xray/sing-box(TUN) для обеспечения таких функций, как отображение скорости</value>
|
||||
</data>
|
||||
<data name="TbBrowse" xml:space="preserve">
|
||||
<value>Просмотр</value>
|
||||
@@ -697,7 +688,7 @@
|
||||
<value>Разрешить небезопасные</value>
|
||||
</data>
|
||||
<data name="TbSettingsDomainStrategy4Freedom" xml:space="preserve">
|
||||
<value>Outbound Freedom domainStrategy</value>
|
||||
<value>«Freedom»: стратегия обработки доменов исходящего трафика</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
|
||||
<value>Автоматически регулировать ширину столбца после обновления подписки</value>
|
||||
@@ -712,7 +703,7 @@
|
||||
<value>Исключение. Не используйте прокси-сервер для адресов, начинающихся с (,), используйте точку с запятой (;)</value>
|
||||
</data>
|
||||
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
|
||||
<value>Display real-time speed</value>
|
||||
<value>Показывать скорость в реальном времени</value>
|
||||
</data>
|
||||
<data name="TbSettingsKeepOlderDedupl" xml:space="preserve">
|
||||
<value>Сохранить старые при удалении дублей</value>
|
||||
@@ -721,10 +712,10 @@
|
||||
<value>Записывать логи</value>
|
||||
</data>
|
||||
<data name="TbSettingsLogLevel" xml:space="preserve">
|
||||
<value>Уровень логгирования</value>
|
||||
<value>Уровень записи логов</value>
|
||||
</data>
|
||||
<data name="TbSettingsMuxEnabled" xml:space="preserve">
|
||||
<value>Включите мультиплексирование Mux</value>
|
||||
<value>Включить мультиплексирование Mux</value>
|
||||
</data>
|
||||
<data name="TbSettingsN" xml:space="preserve">
|
||||
<value>Настройки v2rayN</value>
|
||||
@@ -733,16 +724,16 @@
|
||||
<value>Пароль аутентификации</value>
|
||||
</data>
|
||||
<data name="TbSettingsRemoteDNS" xml:space="preserve">
|
||||
<value>Пользовательский DNS (если несколько делите с запятыми (,))</value>
|
||||
<value>Пользовательский DNS (если несколько, то делите запятыми (,))</value>
|
||||
</data>
|
||||
<data name="TbSettingsSetUWP" xml:space="preserve">
|
||||
<value>Set Win10 UWP Loopback</value>
|
||||
<value>Разрешить loopback для приложений UWP (Win10)</value>
|
||||
</data>
|
||||
<data name="TbSettingsSniffingEnabled" xml:space="preserve">
|
||||
<value>Включить сниффинг</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPort" xml:space="preserve">
|
||||
<value>Mixed Port</value>
|
||||
<value>Смешанный порт</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBoot" xml:space="preserve">
|
||||
<value>Автозапуск</value>
|
||||
@@ -751,7 +742,7 @@
|
||||
<value>Включить статистику (требуется перезагрузка)</value>
|
||||
</data>
|
||||
<data name="TbSettingsSubConvert" xml:space="preserve">
|
||||
<value>URL-адрес конверсии подписки</value>
|
||||
<value>URL конвертации подписок</value>
|
||||
</data>
|
||||
<data name="TbSettingsSystemproxy" xml:space="preserve">
|
||||
<value>Настройки системного прокси</value>
|
||||
@@ -766,7 +757,7 @@
|
||||
<value>Включить UDP</value>
|
||||
</data>
|
||||
<data name="TbSettingsUser" xml:space="preserve">
|
||||
<value>Auth user</value>
|
||||
<value>Имя пользователя (логин)</value>
|
||||
</data>
|
||||
<data name="TbClearSystemProxy" xml:space="preserve">
|
||||
<value>Очистить системный прокси</value>
|
||||
@@ -822,9 +813,6 @@
|
||||
<data name="menuWebsiteItem" xml:space="preserve">
|
||||
<value>{0} веб-сайт</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvanced" xml:space="preserve">
|
||||
<value>Расширенная функция</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvancedAdd" xml:space="preserve">
|
||||
<value>Добавить</value>
|
||||
</data>
|
||||
@@ -843,20 +831,11 @@
|
||||
<data name="TbdomainStrategy" xml:space="preserve">
|
||||
<value>Доменная стратегия</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabBlock" xml:space="preserve">
|
||||
<value>3.Заблокировать домен или IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabDirect" xml:space="preserve">
|
||||
<value>2.Прямой домен или IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabProxy" xml:space="preserve">
|
||||
<value>1.Прокси-домен или IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabRuleList" xml:space="preserve">
|
||||
<value>Предустановленный список наборов правил</value>
|
||||
</data>
|
||||
<data name="TbRoutingTips" xml:space="preserve">
|
||||
<value>* Установите правила, разделенные запятыми (,); Запятая в регулярке заменена на <COMMA></value>
|
||||
<value>*Разделяйте правила запятыми (,). Литерал «,» — <COMMA>. Префикс # — отключить правило</value>
|
||||
</data>
|
||||
<data name="menuImportRulesFromClipboard" xml:space="preserve">
|
||||
<value>Импорт правил из буфера обмена</value>
|
||||
@@ -883,16 +862,16 @@
|
||||
<value>Удалить правила (Delete)</value>
|
||||
</data>
|
||||
<data name="menuRoutingRuleDetailsSetting" xml:space="preserve">
|
||||
<value>RoutingRuleDetailsSetting</value>
|
||||
<value>Детальные настройки правил маршрутизации</value>
|
||||
</data>
|
||||
<data name="TbAutoSort" xml:space="preserve">
|
||||
<value>Домен и IP автоматически сортируются при сохранении</value>
|
||||
</data>
|
||||
<data name="TbRuleobjectDoc" xml:space="preserve">
|
||||
<value>Ruleobject Doc</value>
|
||||
<value>Документация RuleObject</value>
|
||||
</data>
|
||||
<data name="TbDnsObjectDoc" xml:space="preserve">
|
||||
<value>Поддержка DnsObject</value>
|
||||
<value>Поддерживаются DNS-объекты, нажмите для просмотра документации</value>
|
||||
</data>
|
||||
<data name="SubUrlTips" xml:space="preserve">
|
||||
<value>Необязательное поле</value>
|
||||
@@ -913,16 +892,16 @@
|
||||
<value>Тест задержки и скорости всех серверов (Ctrl+E)</value>
|
||||
</data>
|
||||
<data name="LvTestDelay" xml:space="preserve">
|
||||
<value>Задержка (ms)</value>
|
||||
<value>Задержка (мс)</value>
|
||||
</data>
|
||||
<data name="LvTestSpeed" xml:space="preserve">
|
||||
<value>Скорость (M/s)</value>
|
||||
<value>Скорость (МБ/с)</value>
|
||||
</data>
|
||||
<data name="FailedToRunCore" xml:space="preserve">
|
||||
<value>Не удалось запустить ядро, посмотрите логи</value>
|
||||
</data>
|
||||
<data name="LvFilter" xml:space="preserve">
|
||||
<value>Remarks regular filter</value>
|
||||
<value>Фильтр примечаний (Regex)</value>
|
||||
</data>
|
||||
<data name="TbDisplayLog" xml:space="preserve">
|
||||
<value>Показать логи</value>
|
||||
@@ -934,7 +913,7 @@
|
||||
<value>Новый порт для локальной сети</value>
|
||||
</data>
|
||||
<data name="TbSettingsTunMode" xml:space="preserve">
|
||||
<value>Настройки TunMode</value>
|
||||
<value>Настройки режима TUN</value>
|
||||
</data>
|
||||
<data name="menuMoveToGroup" xml:space="preserve">
|
||||
<value>Перейти в группу</value>
|
||||
@@ -958,22 +937,22 @@
|
||||
<value>Тест завершен</value>
|
||||
</data>
|
||||
<data name="TbSettingsDefFingerprint" xml:space="preserve">
|
||||
<value>TLS отпечаток по умлочанию</value>
|
||||
<value>TLS отпечаток по умолчанию</value>
|
||||
</data>
|
||||
<data name="TbSettingsDefUserAgent" xml:space="preserve">
|
||||
<value>User-Agent</value>
|
||||
</data>
|
||||
<data name="TbSettingsDefUserAgentTips" xml:space="preserve">
|
||||
<value>Этот параметр действителен только для TCP/HTTP и WS</value>
|
||||
<value>Параметр действует только для TCP/HTTP и WebSocket (WS)</value>
|
||||
</data>
|
||||
<data name="TbSettingsCurrentFontFamily" xml:space="preserve">
|
||||
<value>Шрифт (требуется перезагрузка)</value>
|
||||
</data>
|
||||
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
|
||||
<value>Скопируйте файл шрифта TTF/TTC в каталог guiFonts, перезапустите настройки</value>
|
||||
<value>Скопируйте файл шрифта TTF/TTC в каталог guiFonts и заново откройте окно настроек</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>Pac порт = +3; Xray API порт = +4; mihomo API порт = +5;</value>
|
||||
<value>Pac порт = +3,Xray API порт = +4, mihomo API порт = +5</value>
|
||||
</data>
|
||||
<data name="TbSettingsStartBootTip" xml:space="preserve">
|
||||
<value>Установите это с правами администратора</value>
|
||||
@@ -982,13 +961,13 @@
|
||||
<value>Размер шрифта</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestTimeout" xml:space="preserve">
|
||||
<value>Таймаут одиночного спидтеста</value>
|
||||
<value>Тайм-аут одиночного тестирования скорости</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedTestUrl" xml:space="preserve">
|
||||
<value>URL спидтеста</value>
|
||||
<value>URL для тестирования скорости</value>
|
||||
</data>
|
||||
<data name="menuMoveTo" xml:space="preserve">
|
||||
<value>Move up and down</value>
|
||||
<value>Переместить вверх/вниз</value>
|
||||
</data>
|
||||
<data name="TbPublicKey" xml:space="preserve">
|
||||
<value>PublicKey</value>
|
||||
@@ -1003,19 +982,19 @@
|
||||
<value>Включить аппаратное ускорение (требуется перезагрузка)</value>
|
||||
</data>
|
||||
<data name="SpeedtestingWait" xml:space="preserve">
|
||||
<value>Ожидание тестирования (нажмите ESC для отмены)...</value>
|
||||
<value>Ожидание тестирования (нажмите ESC для отмены)…</value>
|
||||
</data>
|
||||
<data name="TipDisplayLog" xml:space="preserve">
|
||||
<value>Please turn off when there is an abnormal disconnection</value>
|
||||
<value>Отключите при аномальном разрыве соединения</value>
|
||||
</data>
|
||||
<data name="MsgSkipSubscriptionUpdate" xml:space="preserve">
|
||||
<value>Updates are not enabled, skip this subscription</value>
|
||||
<value>Обновления не включены — подписка пропущена</value>
|
||||
</data>
|
||||
<data name="menuRebootAsAdmin" xml:space="preserve">
|
||||
<value>Перезагрузить как администратор</value>
|
||||
</data>
|
||||
<data name="LvMoreUrl" xml:space="preserve">
|
||||
<value>More URLs, separated by commas; Subscription conversion will be invalid</value>
|
||||
<value>Дополнительные URL через запятую, конвертация подписки недоступна</value>
|
||||
</data>
|
||||
<data name="SpeedDisplayText" xml:space="preserve">
|
||||
<value>{0} : {1}/s↑ | {2}/s↓</value>
|
||||
@@ -1024,13 +1003,13 @@
|
||||
<value>Интервал автоматического обновления в минутах</value>
|
||||
</data>
|
||||
<data name="TbSettingsLogEnabledToFile" xml:space="preserve">
|
||||
<value>Включить логгирование в файл</value>
|
||||
<value>Включить запись логов в файл</value>
|
||||
</data>
|
||||
<data name="LvConvertTarget" xml:space="preserve">
|
||||
<value>Преобразовать тип цели</value>
|
||||
</data>
|
||||
<data name="LvConvertTargetTip" xml:space="preserve">
|
||||
<value>Если преобразование не требуется, оставьте поле пустым.</value>
|
||||
<value>Если преобразование не требуется, оставьте поле пустым</value>
|
||||
</data>
|
||||
<data name="menuDNSSetting" xml:space="preserve">
|
||||
<value>Настройки DNS</value>
|
||||
@@ -1039,7 +1018,7 @@
|
||||
<value>Настройки DNS sing-box</value>
|
||||
</data>
|
||||
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
|
||||
<value>Пожалуйста, заполните структуру DNS. Нажмите, чтобы просмотреть документ.</value>
|
||||
<value>Заполните структуру DNS, нажмите, чтобы открыть документ</value>
|
||||
</data>
|
||||
<data name="TbSettingDnsImportDefConfig" xml:space="preserve">
|
||||
<value>Нажмите, чтобы импортировать конфигурацию DNS по умолчанию</value>
|
||||
@@ -1048,88 +1027,88 @@
|
||||
<value>Стратегия домена для sing-box</value>
|
||||
</data>
|
||||
<data name="TbSettingsMux4SboxProtocol" xml:space="preserve">
|
||||
<value>sing-box Mux Protocol</value>
|
||||
<value>Протокол Mux для sing-box</value>
|
||||
</data>
|
||||
<data name="TbRoutingRuleProcess" xml:space="preserve">
|
||||
<value>Full process name (Tun mode)</value>
|
||||
<value>Полное имя процесса (режим TUN)</value>
|
||||
</data>
|
||||
<data name="TbRoutingRuleIP" xml:space="preserve">
|
||||
<value>IP or IP CIDR</value>
|
||||
<value>IP-адрес или сеть CIDR</value>
|
||||
</data>
|
||||
<data name="TbRoutingRuleDomain" xml:space="preserve">
|
||||
<value>Домен</value>
|
||||
</data>
|
||||
<data name="menuAddHysteria2Server" xml:space="preserve">
|
||||
<value>Добавить [Hysteria2] сервер</value>
|
||||
<value>Добавить сервер [Hysteria2]</value>
|
||||
</data>
|
||||
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
|
||||
<value>Hysteria Max bandwidth (Up/Dw)</value>
|
||||
<value>Максимальная пропускная способность Hysteria (загрузка/отдача)</value>
|
||||
</data>
|
||||
<data name="TbSettingsUseSystemHosts" xml:space="preserve">
|
||||
<value>Использовать системные узлы</value>
|
||||
</data>
|
||||
<data name="menuAddTuicServer" xml:space="preserve">
|
||||
<value>Добавить [TUIC] сервер</value>
|
||||
<value>Добавить сервер [TUIC]</value>
|
||||
</data>
|
||||
<data name="TbHeaderType8" xml:space="preserve">
|
||||
<value>Контроль перегрузок</value>
|
||||
</data>
|
||||
<data name="LvPrevProfile" xml:space="preserve">
|
||||
<value>Previous proxy remarks</value>
|
||||
<value>Примечания к предыдущему прокси</value>
|
||||
</data>
|
||||
<data name="LvNextProfile" xml:space="preserve">
|
||||
<value>Next proxy remarks</value>
|
||||
<value>Примечания к следующему прокси</value>
|
||||
</data>
|
||||
<data name="LvPrevProfileTip" xml:space="preserve">
|
||||
<value>Пожалуйста, убедитесь, что примечание существует и является уникальным.</value>
|
||||
<value>Убедитесь, что примечание существует и является уникальным</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableExInbound" xml:space="preserve">
|
||||
<value>Enable additional Inbound</value>
|
||||
<value>Включить дополнительный входящий канал</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableIPv6Address" xml:space="preserve">
|
||||
<value>Включить IPv6 адреса</value>
|
||||
</data>
|
||||
<data name="menuAddWireguardServer" xml:space="preserve">
|
||||
<value>Добавить [WireGuard] сервер</value>
|
||||
<value>Добавить сервер [WireGuard]</value>
|
||||
</data>
|
||||
<data name="TbPrivateKey" xml:space="preserve">
|
||||
<value>PrivateKey</value>
|
||||
<value>Приватный ключ</value>
|
||||
</data>
|
||||
<data name="TbReserved" xml:space="preserve">
|
||||
<value>Reserved(2,3,4)</value>
|
||||
<value>Зарезервировано (2, 3, 4)</value>
|
||||
</data>
|
||||
<data name="TbLocalAddress" xml:space="preserve">
|
||||
<value>Адрес(Ipv4,Ipv6)</value>
|
||||
<value>Адрес (Ipv4,Ipv6)</value>
|
||||
</data>
|
||||
<data name="TbPath7" xml:space="preserve">
|
||||
<value>obfs password</value>
|
||||
<value>Пароль obfs</value>
|
||||
</data>
|
||||
<data name="TbRuleMatchingTips" xml:space="preserve">
|
||||
<value>(Domain or IP or ProcName) and Port and Protocol and InboundTag => OutboundTag</value>
|
||||
<value>(Домен или IP или имя процесса) и порт, и протокол, и InboundTag => OutboundTag</value>
|
||||
</data>
|
||||
<data name="TbAutoScrollToEnd" xml:space="preserve">
|
||||
<value>Автоматическая прокрутка в конец</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedPingTestUrl" xml:space="preserve">
|
||||
<value>URL-адрес для проверки скорости пинга</value>
|
||||
<value>URL для быстрой проверки реальной задержки</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableUpdateSubOnlyRemarksExist" xml:space="preserve">
|
||||
<value>Updating subscription, only determine remarks exists</value>
|
||||
<value>Обновляя подписку, проверять лишь наличие примечаний</value>
|
||||
</data>
|
||||
<data name="SpeedtestingStop" xml:space="preserve">
|
||||
<value>Отмена тестирования...</value>
|
||||
</data>
|
||||
<data name="TransportRequestHostTip5" xml:space="preserve">
|
||||
<value>*grpc Authority</value>
|
||||
<value>*gRPC Authority</value>
|
||||
</data>
|
||||
<data name="menuAddHttpServer" xml:space="preserve">
|
||||
<value>Добавить [HTTP] сервер</value>
|
||||
<value>Добавить сервер [HTTP]</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>Use Xray and enable non-Tun mode, which conflicts with the group previous proxy</value>
|
||||
<value>which conflicts with the group previous proxy</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>Включить фрагмент</value>
|
||||
<value>Включить фрагментацию (Fragment)</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>Включить файл кэша для sing-box (файлы наборов правил)</value>
|
||||
@@ -1138,7 +1117,7 @@
|
||||
<value>Пользовательский набор правил для sing-box</value>
|
||||
</data>
|
||||
<data name="NeedRebootTips" xml:space="preserve">
|
||||
<value>Successful operation. Click the settings menu to reboot the app.</value>
|
||||
<value>Операция успешна. Перезапустите приложение</value>
|
||||
</data>
|
||||
<data name="menuOpenTheFileLocation" xml:space="preserve">
|
||||
<value>Открыть место хранения</value>
|
||||
@@ -1147,7 +1126,7 @@
|
||||
<value>Сортировка</value>
|
||||
</data>
|
||||
<data name="TbSortingChain" xml:space="preserve">
|
||||
<value>Chain</value>
|
||||
<value>Цепочка</value>
|
||||
</data>
|
||||
<data name="TbSortingDefault" xml:space="preserve">
|
||||
<value>По умолчанию</value>
|
||||
@@ -1159,7 +1138,7 @@
|
||||
<value>Скорость загрузки</value>
|
||||
</data>
|
||||
<data name="TbSortingDownTraffic" xml:space="preserve">
|
||||
<value>Download Traffic</value>
|
||||
<value>Скачанный трафик</value>
|
||||
</data>
|
||||
<data name="TbSortingHost" xml:space="preserve">
|
||||
<value>Узел</value>
|
||||
@@ -1177,10 +1156,10 @@
|
||||
<value>Тип</value>
|
||||
</data>
|
||||
<data name="TbSortingUpSpeed" xml:space="preserve">
|
||||
<value>Скорость загрузки</value>
|
||||
<value>Скорость отдачи</value>
|
||||
</data>
|
||||
<data name="TbSortingUpTraffic" xml:space="preserve">
|
||||
<value>Upload Traffic</value>
|
||||
<value>Отправленный трафик</value>
|
||||
</data>
|
||||
<data name="TbConnections" xml:space="preserve">
|
||||
<value>Соединения</value>
|
||||
@@ -1198,13 +1177,13 @@
|
||||
<value>Режим правила</value>
|
||||
</data>
|
||||
<data name="menuModeDirect" xml:space="preserve">
|
||||
<value>Direct</value>
|
||||
<value>Прямое соединение</value>
|
||||
</data>
|
||||
<data name="menuModeGlobal" xml:space="preserve">
|
||||
<value>Global</value>
|
||||
<value>Глобальный режим</value>
|
||||
</data>
|
||||
<data name="menuModeNothing" xml:space="preserve">
|
||||
<value>Do not change</value>
|
||||
<value>Не менять</value>
|
||||
</data>
|
||||
<data name="menuModeRule" xml:space="preserve">
|
||||
<value>Правила</value>
|
||||
@@ -1213,13 +1192,13 @@
|
||||
<value>Тест задержки</value>
|
||||
</data>
|
||||
<data name="menuProxiesDelaytestPart" xml:space="preserve">
|
||||
<value>Part Node Latency Test</value>
|
||||
<value>Тест задержки выбранных узлов</value>
|
||||
</data>
|
||||
<data name="menuProxiesReload" xml:space="preserve">
|
||||
<value>Обновить прокси</value>
|
||||
</data>
|
||||
<data name="menuProxiesSelectActivity" xml:space="preserve">
|
||||
<value>Активировать узел (Enter)</value>
|
||||
<value>Сделать узел активным (Enter)</value>
|
||||
</data>
|
||||
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
|
||||
<value>Стратегия домена по умолчанию для исходящих</value>
|
||||
@@ -1234,7 +1213,7 @@
|
||||
<value>Автоматическая регулировка ширины столбца</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrlBase64" xml:space="preserve">
|
||||
<value>Export Base64-encoded Share Links to Clipboard</value>
|
||||
<value>Экспорт ссылок в формате Base64 в буфер обмена</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
||||
<value>Экспортировать выбранный сервер для полной конфигурации в буфер обмена</value>
|
||||
@@ -1243,7 +1222,7 @@
|
||||
<value>Показать или скрыть главное окно</value>
|
||||
</data>
|
||||
<data name="TbPreSocksPort4Sub" xml:space="preserve">
|
||||
<value>Пользовательская конфигурация порта socks</value>
|
||||
<value>Пользовательская конфигурация порта SOCKS</value>
|
||||
</data>
|
||||
<data name="menuBackupAndRestore" xml:space="preserve">
|
||||
<value>Резервное копирование и восстановление</value>
|
||||
@@ -1255,28 +1234,28 @@
|
||||
<value>Восстановить из файла</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackup" xml:space="preserve">
|
||||
<value>Backup to remote (WebDAV)</value>
|
||||
<value>Резервное копирование на удалённый сервер (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuRemoteRestore" xml:space="preserve">
|
||||
<value>Restore from remote (WebDAV)</value>
|
||||
<value>Восстановить с удалённого сервера (WebDAV)</value>
|
||||
</data>
|
||||
<data name="menuLocalBackupAndRestore" xml:space="preserve">
|
||||
<value>Local</value>
|
||||
<value>Локально</value>
|
||||
</data>
|
||||
<data name="menuRemoteBackupAndRestore" xml:space="preserve">
|
||||
<value>Remote (WebDAV)</value>
|
||||
<value>Удалённо (WebDAV)</value>
|
||||
</data>
|
||||
<data name="LvWebDavUrl" xml:space="preserve">
|
||||
<value>WebDav Url</value>
|
||||
<value>URL WebDAV</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav User Name</value>
|
||||
<value>Имя пользователя WebDAV</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav Password</value>
|
||||
<value>Пароль WebDAV</value>
|
||||
</data>
|
||||
<data name="LvWebDavCheck" xml:space="preserve">
|
||||
<value>WebDav Check</value>
|
||||
<value>Проверить WebDAV</value>
|
||||
</data>
|
||||
<data name="LvWebDavDirName" xml:space="preserve">
|
||||
<value>Имя удаленной папки (необязательно)</value>
|
||||
@@ -1285,16 +1264,16 @@
|
||||
<value>Неверный файл резервной копии</value>
|
||||
</data>
|
||||
<data name="ConnectionsHostFilterTitle" xml:space="preserve">
|
||||
<value>Host filter</value>
|
||||
<value>Фильтр хостов</value>
|
||||
</data>
|
||||
<data name="TipActiveServer" xml:space="preserve">
|
||||
<value>Active</value>
|
||||
<value>Активный</value>
|
||||
</data>
|
||||
<data name="TbSettingsGeoFilesSource" xml:space="preserve">
|
||||
<value>Источник geo файлов</value>
|
||||
<value>Источник файлов Geo (необязательно)</value>
|
||||
</data>
|
||||
<data name="TbSettingsSrsFilesSource" xml:space="preserve">
|
||||
<value>Источник sing-box srs файлов</value>
|
||||
<value>Источник файлов наборов правил sing-box (необязательно)</value>
|
||||
</data>
|
||||
<data name="UpgradeAppNotExistTip" xml:space="preserve">
|
||||
<value>Программы для обновления не существует</value>
|
||||
@@ -1315,7 +1294,7 @@
|
||||
<value>Иран</value>
|
||||
</data>
|
||||
<data name="TbSettingsChinaUserTip" xml:space="preserve">
|
||||
<value>Используйте Настройки -> Региональные пресеты вместо изменения этого поля</value>
|
||||
<value>Пользователи из Китая могут пропустить этот пункт</value>
|
||||
</data>
|
||||
<data name="menuAddServerViaImage" xml:space="preserve">
|
||||
<value>Сканировать QR-код с изображения</value>
|
||||
@@ -1324,7 +1303,7 @@
|
||||
<value>Неверный адрес (Url)</value>
|
||||
</data>
|
||||
<data name="InsecureUrlProtocol" xml:space="preserve">
|
||||
<value>Пожалуйста, не используйте небезопасный адрес подписки по протоколу HTTP.</value>
|
||||
<value>Не используйте небезопасный адрес подписки по протоколу HTTP</value>
|
||||
</data>
|
||||
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
|
||||
<value>Установите шрифт в систему и перезапустите настройки</value>
|
||||
@@ -1333,43 +1312,37 @@
|
||||
<value>Вы уверены, что хотите выйти?</value>
|
||||
</data>
|
||||
<data name="LvMemo" xml:space="preserve">
|
||||
<value>Remarks Memo</value>
|
||||
<value>Заметка (Memo)</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPassword" xml:space="preserve">
|
||||
<value>System sudo password</value>
|
||||
<value>Пароль sudo системы</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>The password you entered cannot be verified, so make sure you enter it correctly. If the application does not work properly due to an incorrect input, please restart the application. The password will not be stored and you will need to enter it again after each restart.</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
|
||||
<value>Please set the sudo password in Tun mode settings first</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
|
||||
<value>Пожалуйста, не запускайте программу как суперпользователь.</value>
|
||||
<value>The password will be validated via the command line. If a validation error causes the application to malfunction, please restart the application. The password will not be stored and must be entered again after each restart.</value>
|
||||
</data>
|
||||
<data name="TransportHeaderTypeTip5" xml:space="preserve">
|
||||
<value>*xhttp mode</value>
|
||||
<value>*XHTTP-режим</value>
|
||||
</data>
|
||||
<data name="TransportExtraTip" xml:space="preserve">
|
||||
<value>XHTTP Extra raw JSON, format: { XHTTPObject }</value>
|
||||
<value>Дополнительный XHTTP сырой JSON, формат: { XHTTPObject }</value>
|
||||
</data>
|
||||
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
|
||||
<value>Скрыть в трее при закрытии окна</value>
|
||||
</data>
|
||||
<data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
|
||||
<value>The number of concurrent during multi-test</value>
|
||||
<value>Количество одновременно выполняемых тестов при многоэтапном тестировании</value>
|
||||
</data>
|
||||
<data name="TbSettingsExceptionTip2" xml:space="preserve">
|
||||
<value>Исключение. Не используйте прокси-сервер для адресов с запятой (,)</value>
|
||||
</data>
|
||||
<data name="TbSettingsDestOverride" xml:space="preserve">
|
||||
<value>Sniffing type</value>
|
||||
<value>Тип сниффинга</value>
|
||||
</data>
|
||||
<data name="TbSettingsSecondLocalPortEnabled" xml:space="preserve">
|
||||
<value>Enable second mixed port</value>
|
||||
<value>Включить второй смешанный порт</value>
|
||||
</data>
|
||||
<data name="TbRoutingInboundTagTips" xml:space="preserve">
|
||||
<value>socks: local port, socks2: second local port, socks3: LAN port</value>
|
||||
<value>socks: локальный порт, socks2: второй локальный порт, socks3: LAN порт</value>
|
||||
</data>
|
||||
<data name="TbSettingsTheme" xml:space="preserve">
|
||||
<value>Темы</value>
|
||||
@@ -1378,7 +1351,7 @@
|
||||
<value>Копировать команду прокси в буфер обмена</value>
|
||||
</data>
|
||||
<data name="SpeedtestingTestFailedPart" xml:space="preserve">
|
||||
<value>Starting retesting failed parts, {0} remaining. Press ESC to terminate...</value>
|
||||
<value>Повторное тестирование неудачных элементов, осталось {0}. Нажмите ESC для остановки…</value>
|
||||
</data>
|
||||
<data name="menuTestServerResult" xml:space="preserve">
|
||||
<value>По результату теста</value>
|
||||
@@ -1387,33 +1360,114 @@
|
||||
<value>Удалить недействительные по результатам теста</value>
|
||||
</data>
|
||||
<data name="RemoveInvalidServerResultTip" xml:space="preserve">
|
||||
<value>Удалено {0} недействительных.</value>
|
||||
<value>Удалено {0} недействительных</value>
|
||||
</data>
|
||||
<data name="TbPorts7" xml:space="preserve">
|
||||
<value>Диапазон портов сервера</value>
|
||||
</data>
|
||||
<data name="TbPorts7Tips" xml:space="preserve">
|
||||
<value>Will cover the port, separate with commas (,)</value>
|
||||
<value>Заменит указанный порт, перечисляйте через запятую (,)</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
||||
<value>Multi-server to custom configuration</value>
|
||||
<value>От мультиконфигурации к пользовательской конфигурации</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServerXrayRandom" xml:space="preserve">
|
||||
<value>Multi-server Random by Xray</value>
|
||||
<value>Случайный (Xray)</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||
<value>Multi-server RoundRobin by Xray</value>
|
||||
<value>Круговой (Xray)</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve">
|
||||
<value>Multi-server LeastPing by Xray</value>
|
||||
<value>Минимальное RTT (минимальное время туда-обратно) (Xray)</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||
<value>Multi-server LeastLoad by Xray</value>
|
||||
<value>Минимальная нагрузка (Xray)</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||
<value>Multi-server LeastPing by sing-box</value>
|
||||
<value>Минимальное RTT (минимальное время туда-обратно) (sing-box)</value>
|
||||
</data>
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>Export server</value>
|
||||
<value>Экспортировать конфигурацию</value>
|
||||
</data>
|
||||
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
||||
<value>URL для тестирования текущего соединения</value>
|
||||
</data>
|
||||
<data name="TbRuleOutboundTagTip" xml:space="preserve">
|
||||
<value>Can fill in the configuration remarks, please make sure it exist and are unique</value>
|
||||
</data>
|
||||
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
||||
<value>Incorrect password, please try again.</value>
|
||||
</data>
|
||||
<data name="TbMldsa65Verify" xml:space="preserve">
|
||||
<value>Mldsa65Verify</value>
|
||||
</data>
|
||||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>Добавить сервер [Anytls]</value>
|
||||
</data>
|
||||
<data name="TbRemoteDNS" xml:space="preserve">
|
||||
<value>Remote DNS</value>
|
||||
</data>
|
||||
<data name="TbDomesticDNS" xml:space="preserve">
|
||||
<value>Domestic DNS</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
||||
<value>Outbound DNS Resolution (sing-box)</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
||||
<value>Resolve Outbound Domains</value>
|
||||
</data>
|
||||
<data name="TbSBDoHResolverServer" xml:space="preserve">
|
||||
<value>sing-box DoH Resolver Server</value>
|
||||
</data>
|
||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
||||
<value>Fallback DNS Resolution, Suggest IP</value>
|
||||
</data>
|
||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
||||
<value>xray Freedom Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box Direct Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box Remote Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||
<value>Add Common DNS Hosts</value>
|
||||
</data>
|
||||
<data name="TbSBDoHOverride" xml:space="preserve">
|
||||
<value>The sing-box DoH resolution server can be overwritten</value>
|
||||
</data>
|
||||
<data name="TbFakeIP" xml:space="preserve">
|
||||
<value>FakeIP</value>
|
||||
</data>
|
||||
<data name="TbBlockSVCBHTTPSQueries" xml:space="preserve">
|
||||
<value>Block SVCB and HTTPS Queries</value>
|
||||
</data>
|
||||
<data name="TbPreventDNSLeaks" xml:space="preserve">
|
||||
<value>Prevent DNS Leaks</value>
|
||||
</data>
|
||||
<data name="TbDNSHostsConfig" xml:space="preserve">
|
||||
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
|
||||
</data>
|
||||
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
|
||||
<value>Apply to Proxy Domains Only</value>
|
||||
</data>
|
||||
<data name="ThBasicDNSSettings" xml:space="preserve">
|
||||
<value>Basic DNS Settings</value>
|
||||
</data>
|
||||
<data name="ThAdvancedDNSSettings" xml:space="preserve">
|
||||
<value>Advanced DNS Settings</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPs" xml:space="preserve">
|
||||
<value>Validate Regional Domain IPs</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||
<value>Enable Custom DNS</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve">
|
||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -132,12 +132,6 @@
|
||||
<data name="Downloading" xml:space="preserve">
|
||||
<value>下载开始...</value>
|
||||
</data>
|
||||
<data name="downloadSpeed" xml:space="preserve">
|
||||
<value>下载</value>
|
||||
</data>
|
||||
<data name="DownloadYesNo" xml:space="preserve">
|
||||
<value>是否下载?{0}</value>
|
||||
</data>
|
||||
<data name="FailedConversionConfiguration" xml:space="preserve">
|
||||
<value>转换配置文件失败</value>
|
||||
</data>
|
||||
@@ -387,9 +381,6 @@
|
||||
<data name="RegisterGlobalHotkeySuccessfully" xml:space="preserve">
|
||||
<value>注册全局热键 {0} 成功</value>
|
||||
</data>
|
||||
<data name="UngroupedServers" xml:space="preserve">
|
||||
<value>未分组配置文件</value>
|
||||
</data>
|
||||
<data name="AllGroupServers" xml:space="preserve">
|
||||
<value>所有</value>
|
||||
</data>
|
||||
@@ -712,7 +703,7 @@
|
||||
<value>例外:对于下列字符开头的地址,不使用代理配置文件。使用分号 (;) 分隔。</value>
|
||||
</data>
|
||||
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
|
||||
<value>显示实时速度(需重启)</value>
|
||||
<value>显示实时速度 (需重启)</value>
|
||||
</data>
|
||||
<data name="TbSettingsKeepOlderDedupl" xml:space="preserve">
|
||||
<value>去重时保留序号较小的项</value>
|
||||
@@ -748,7 +739,7 @@
|
||||
<value>开机启动 (可能会不成功)</value>
|
||||
</data>
|
||||
<data name="TbSettingsStatistics" xml:space="preserve">
|
||||
<value>启用流量统计(需重启)</value>
|
||||
<value>启用流量统计 (需重启)</value>
|
||||
</data>
|
||||
<data name="TbSettingsSubConvert" xml:space="preserve">
|
||||
<value>订阅转换网址 (可选)</value>
|
||||
@@ -822,9 +813,6 @@
|
||||
<data name="menuWebsiteItem" xml:space="preserve">
|
||||
<value>{0} 官网</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvanced" xml:space="preserve">
|
||||
<value>高级功能</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvancedAdd" xml:space="preserve">
|
||||
<value>添加规则集</value>
|
||||
</data>
|
||||
@@ -843,15 +831,6 @@
|
||||
<data name="TbdomainStrategy" xml:space="preserve">
|
||||
<value>域名解析策略</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabBlock" xml:space="preserve">
|
||||
<value>3.阻止的 Domain 或 IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabDirect" xml:space="preserve">
|
||||
<value>2.直连的 Domain 或 IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabProxy" xml:space="preserve">
|
||||
<value>1.代理的 Domain 或 IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabRuleList" xml:space="preserve">
|
||||
<value>预定义规则集列表</value>
|
||||
</data>
|
||||
@@ -907,7 +886,7 @@
|
||||
<value>仅限路由 (routeOnly)</value>
|
||||
</data>
|
||||
<data name="TbSettingsNotProxyLocalAddress" xml:space="preserve">
|
||||
<value>请勿将代理服务器用于本地(Intranet)地址</value>
|
||||
<value>请勿将代理服务器用于本地 (Intranet) 地址</value>
|
||||
</data>
|
||||
<data name="menuMixedTestServer" xml:space="preserve">
|
||||
<value>一键多线程测试延迟和速度 (Ctrl+E)</value>
|
||||
@@ -940,7 +919,7 @@
|
||||
<value>移至订阅分组</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
|
||||
<value>启用配置文件拖放排序(需重启)</value>
|
||||
<value>启用配置文件拖放排序 (需重启)</value>
|
||||
</data>
|
||||
<data name="TbAutoRefresh" xml:space="preserve">
|
||||
<value>自动刷新</value>
|
||||
@@ -967,10 +946,10 @@
|
||||
<value>仅对 tcp/http、ws 协议生效</value>
|
||||
</data>
|
||||
<data name="TbSettingsCurrentFontFamily" xml:space="preserve">
|
||||
<value>当前字体(需重启)</value>
|
||||
<value>当前字体 (需重启)</value>
|
||||
</data>
|
||||
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
|
||||
<value>拷贝字体 TTF/TTC 文件到目录 guiFonts,重启设置</value>
|
||||
<value>拷贝字体 TTF/TTC 文件到目录 guiFonts,重启生效</value>
|
||||
</data>
|
||||
<data name="TbSettingsSocksPortTip" xml:space="preserve">
|
||||
<value>Pac 端口 = +3;Xray API 端口 = +4;mihomo API 端口 = +5;</value>
|
||||
@@ -1000,10 +979,10 @@
|
||||
<value>SpiderX</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableHWA" xml:space="preserve">
|
||||
<value>启用硬件加速(需重启)</value>
|
||||
<value>启用硬件加速 (需重启)</value>
|
||||
</data>
|
||||
<data name="SpeedtestingWait" xml:space="preserve">
|
||||
<value>等待测试中(按 ESC 终止)...</value>
|
||||
<value>等待测试中 (按 ESC 终止)...</value>
|
||||
</data>
|
||||
<data name="TipDisplayLog" xml:space="preserve">
|
||||
<value>当有异常断流时请关闭</value>
|
||||
@@ -1096,7 +1075,7 @@
|
||||
<value>Reserved (2,3,4)</value>
|
||||
</data>
|
||||
<data name="TbLocalAddress" xml:space="preserve">
|
||||
<value>Address (Ipv4,Ipv6)</value>
|
||||
<value>Address (IPv4,IPv6)</value>
|
||||
</data>
|
||||
<data name="TbPath7" xml:space="preserve">
|
||||
<value>混淆密码 (obfs password)</value>
|
||||
@@ -1123,13 +1102,13 @@
|
||||
<value>添加 [HTTP] 配置文件</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>使用 Xray 且非 Tun 模式启用,和分组前置代理冲突</value>
|
||||
<value>和分组前置代理冲突</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>启用分片(Fragment)</value>
|
||||
<value>启用分片 (Fragment)</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
|
||||
<value>启用 sing-box(规则集文件)的缓存文件</value>
|
||||
<value>启用 sing-box (规则集文件) 的缓存文件</value>
|
||||
</data>
|
||||
<data name="LvCustomRulesetPath4Singbox" xml:space="preserve">
|
||||
<value>自定义 sing-box rule-set</value>
|
||||
@@ -1222,7 +1201,7 @@
|
||||
<value>Outbound 默认解析策略</value>
|
||||
</data>
|
||||
<data name="TbSettingsMainGirdOrientation" xml:space="preserve">
|
||||
<value>主界面布局方向(需重启)</value>
|
||||
<value>主界面布局方向 (需重启)</value>
|
||||
</data>
|
||||
<data name="TbSettingsDomainDNSAddress" xml:space="preserve">
|
||||
<value>Outbound 域名解析地址</value>
|
||||
@@ -1324,7 +1303,7 @@
|
||||
<value>请不要使用不安全的 HTTP 协议订阅地址</value>
|
||||
</data>
|
||||
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
|
||||
<value>安装字体到系统中,选择或填入字体名称,重启设置</value>
|
||||
<value>安装字体到系统中,选择或填入字体名称,重启生效</value>
|
||||
</data>
|
||||
<data name="menuExitTips" xml:space="preserve">
|
||||
<value>是否确定退出?</value>
|
||||
@@ -1336,16 +1315,10 @@
|
||||
<value>系统的 sudo 密码</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>输入的密码无法校验,所以请确保输入正确。如果因为输入错误导致无法正常运行时,请重启本应用。 密码不会存储,每次重启后都需要再次输入。</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
|
||||
<value>请先在 Tun 模式设置中设置 sudo 密码</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
|
||||
<value>请不要用 sudo 运行本程序</value>
|
||||
<value>密码将调用命令行校验,如果因为校验错误导致无法正常运行时,请重启本应用。 密码不会存储,每次重启后都需要再次输入。</value>
|
||||
</data>
|
||||
<data name="TransportHeaderTypeTip5" xml:space="preserve">
|
||||
<value>*xhttp 模式</value>
|
||||
<value>*XHTTP 模式</value>
|
||||
</data>
|
||||
<data name="TransportExtraTip" xml:space="preserve">
|
||||
<value>XHTTP Extra 原始 JSON,格式: { XHTTPObject }</value>
|
||||
@@ -1366,7 +1339,7 @@
|
||||
<value>开启第二个本地监听端口</value>
|
||||
</data>
|
||||
<data name="TbRoutingInboundTagTips" xml:space="preserve">
|
||||
<value>socks:本地端口,socks2:第二个本地端口,socks3:局域网端口</value>
|
||||
<value>Socks:本地端口,Socks2:第二个本地端口,Socks3:局域网端口</value>
|
||||
</data>
|
||||
<data name="TbSettingsTheme" xml:space="preserve">
|
||||
<value>主题</value>
|
||||
@@ -1399,13 +1372,13 @@
|
||||
<value>多配置文件随机 Xray</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServerXrayRoundRobin" xml:space="preserve">
|
||||
<value>多配置文件负载均衡 Xray</value>
|
||||
<value>多配置文件负载均衡 Xray</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServerXrayLeastPing" xml:space="preserve">
|
||||
<value>多配置文件最低延迟 Xray</value>
|
||||
<value>多配置文件最低延迟 Xray</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServerXrayLeastLoad" xml:space="preserve">
|
||||
<value>多配置文件最稳定 Xray</value>
|
||||
<value>多配置文件最稳定 Xray</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServerSingBoxLeastPing" xml:space="preserve">
|
||||
<value>多配置文件最低延迟 sing-box</value>
|
||||
@@ -1413,4 +1386,85 @@
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>导出配置文件</value>
|
||||
</data>
|
||||
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
||||
<value>当前连接信息测试地址</value>
|
||||
</data>
|
||||
<data name="TbRuleOutboundTagTip" xml:space="preserve">
|
||||
<value>可以填写配置文件别名,请确保存在并唯一</value>
|
||||
</data>
|
||||
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
||||
<value>密码错误,请重试。</value>
|
||||
</data>
|
||||
<data name="TbMldsa65Verify" xml:space="preserve">
|
||||
<value>Mldsa65Verify</value>
|
||||
</data>
|
||||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>添加 [Anytls] 配置文件</value>
|
||||
</data>
|
||||
<data name="TbRemoteDNS" xml:space="preserve">
|
||||
<value>远程 DNS</value>
|
||||
</data>
|
||||
<data name="TbDomesticDNS" xml:space="preserve">
|
||||
<value>直连 DNS</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
||||
<value>出站 DNS 解析(sing-box)</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
||||
<value>解析出站域名</value>
|
||||
</data>
|
||||
<data name="TbSBDoHResolverServer" xml:space="preserve">
|
||||
<value>sing-box DoH 解析服务器</value>
|
||||
</data>
|
||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
||||
<value>兜底解析其他 DNS 域名,建议设为 ip</value>
|
||||
</data>
|
||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
||||
<value>xray freedom 解析策略</value>
|
||||
</data>
|
||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box 直连解析策略</value>
|
||||
</data>
|
||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box 远程解析策略</value>
|
||||
</data>
|
||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||
<value>添加常用 DNS Hosts</value>
|
||||
</data>
|
||||
<data name="TbSBDoHOverride" xml:space="preserve">
|
||||
<value>开启后可覆盖 sing-box DoH 解析服务器</value>
|
||||
</data>
|
||||
<data name="TbFakeIP" xml:space="preserve">
|
||||
<value>FakeIP</value>
|
||||
</data>
|
||||
<data name="TbBlockSVCBHTTPSQueries" xml:space="preserve">
|
||||
<value>阻止 SVCB 和 HTTPS 查询</value>
|
||||
</data>
|
||||
<data name="TbPreventDNSLeaks" xml:space="preserve">
|
||||
<value>避免 DNS 泄漏</value>
|
||||
</data>
|
||||
<data name="TbDNSHostsConfig" xml:space="preserve">
|
||||
<value>DNS Hosts:(“域名1 ip1 ip2” 一行一个)</value>
|
||||
</data>
|
||||
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
|
||||
<value>仅对代理域名生效</value>
|
||||
</data>
|
||||
<data name="ThBasicDNSSettings" xml:space="preserve">
|
||||
<value>DNS 基础设置</value>
|
||||
</data>
|
||||
<data name="ThAdvancedDNSSettings" xml:space="preserve">
|
||||
<value>DNS 进阶设置</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPs" xml:space="preserve">
|
||||
<value>校验相应地区域名 IP</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||
<value>配置后,会对相应地区域名(如 geosite:cn)的返回 IP 进行校验,仅返回期望 IP</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||
<value>启用自定义 DNS</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve">
|
||||
<value>自定义 DNS 已启用,此页面配置将无效</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -118,7 +118,7 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="BatchExportURLSuccessfully" xml:space="preserve">
|
||||
<value>匯出分享链接至剪貼簿成功</value>
|
||||
<value>匯出分享連結至剪貼簿成功</value>
|
||||
</data>
|
||||
<data name="CheckServerSettings" xml:space="preserve">
|
||||
<value>請先檢查設定檔設定</value>
|
||||
@@ -132,12 +132,6 @@
|
||||
<data name="Downloading" xml:space="preserve">
|
||||
<value>下載開始...</value>
|
||||
</data>
|
||||
<data name="downloadSpeed" xml:space="preserve">
|
||||
<value>下載</value>
|
||||
</data>
|
||||
<data name="DownloadYesNo" xml:space="preserve">
|
||||
<value>是否下載?{0}</value>
|
||||
</data>
|
||||
<data name="FailedConversionConfiguration" xml:space="preserve">
|
||||
<value>轉換設定檔失敗</value>
|
||||
</data>
|
||||
@@ -295,7 +289,7 @@
|
||||
<value>成功從剪貼簿匯入 {0} 個設定檔</value>
|
||||
</data>
|
||||
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
|
||||
<value>掃描匯入分享链接成功</value>
|
||||
<value>掃描匯入分享連結成功</value>
|
||||
</data>
|
||||
<data name="TestMeOutput" xml:space="preserve">
|
||||
<value>目前延遲: {0} ms,{1}</value>
|
||||
@@ -387,9 +381,6 @@
|
||||
<data name="RegisterGlobalHotkeySuccessfully" xml:space="preserve">
|
||||
<value>註冊全域快速鍵 {0} 成功</value>
|
||||
</data>
|
||||
<data name="UngroupedServers" xml:space="preserve">
|
||||
<value>未分組設定檔</value>
|
||||
</data>
|
||||
<data name="AllGroupServers" xml:space="preserve">
|
||||
<value>所有</value>
|
||||
</data>
|
||||
@@ -481,7 +472,7 @@
|
||||
<value>語言 (需重啟)</value>
|
||||
</data>
|
||||
<data name="menuAddServerViaClipboard" xml:space="preserve">
|
||||
<value>從剪貼簿導入分享鏈接 (Ctrl+V)</value>
|
||||
<value>從剪貼簿導入分享連結 (Ctrl+V)</value>
|
||||
</data>
|
||||
<data name="menuAddServerViaScan" xml:space="preserve">
|
||||
<value>掃描螢幕上的二維碼 (Ctrl+S)</value>
|
||||
@@ -517,7 +508,7 @@
|
||||
<value>匯出所選設定檔完整設定</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrl" xml:space="preserve">
|
||||
<value>匯出分享链接至剪貼簿 (多選) (Ctrl+C)</value>
|
||||
<value>匯出分享連結至剪貼簿 (多選) (Ctrl+C)</value>
|
||||
</data>
|
||||
<data name="menuAddCustomServer" xml:space="preserve">
|
||||
<value>新增自訂設定設定檔</value>
|
||||
@@ -822,9 +813,6 @@
|
||||
<data name="menuWebsiteItem" xml:space="preserve">
|
||||
<value>{0} 官網</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvanced" xml:space="preserve">
|
||||
<value>進階功能</value>
|
||||
</data>
|
||||
<data name="menuRoutingAdvancedAdd" xml:space="preserve">
|
||||
<value>新增規則集</value>
|
||||
</data>
|
||||
@@ -843,15 +831,6 @@
|
||||
<data name="TbdomainStrategy" xml:space="preserve">
|
||||
<value>域名解析策略</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabBlock" xml:space="preserve">
|
||||
<value>3.阻止的 Domain 或 IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabDirect" xml:space="preserve">
|
||||
<value>2.直連的 Domain 或 IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabProxy" xml:space="preserve">
|
||||
<value>1.代理的 Domain 或 IP</value>
|
||||
</data>
|
||||
<data name="TbRoutingTabRuleList" xml:space="preserve">
|
||||
<value>預定義規則集列表</value>
|
||||
</data>
|
||||
@@ -892,7 +871,7 @@
|
||||
<value>規則詳細說明檔案</value>
|
||||
</data>
|
||||
<data name="TbDnsObjectDoc" xml:space="preserve">
|
||||
<value>支援填寫 DnsObject,JSON 格式,點擊查看説明</value>
|
||||
<value>支援填寫 DnsObject,JSON 格式,點擊查看說明</value>
|
||||
</data>
|
||||
<data name="SubUrlTips" xml:space="preserve">
|
||||
<value>普通分組此處請留空</value>
|
||||
@@ -1003,7 +982,7 @@
|
||||
<value>啟用硬體加速 (需重啟)</value>
|
||||
</data>
|
||||
<data name="SpeedtestingWait" xml:space="preserve">
|
||||
<value>等待测试中(按 ESC 终止)...</value>
|
||||
<value>等待測試中(按 ESC 終止)...</value>
|
||||
</data>
|
||||
<data name="TipDisplayLog" xml:space="preserve">
|
||||
<value>當有異常斷流時請關閉</value>
|
||||
@@ -1102,10 +1081,10 @@
|
||||
<value>混淆密碼 (obfs password)</value>
|
||||
</data>
|
||||
<data name="TbRuleMatchingTips" xml:space="preserve">
|
||||
<value>(Domain 或 IP 或 行程名) 与 Port 与 Protocol 与 InboundTag => OutboundTag</value>
|
||||
<value>(Domain 或 IP 或 行程名) 與 Port 與 Protocol 與 InboundTag => OutboundTag</value>
|
||||
</data>
|
||||
<data name="TbAutoScrollToEnd" xml:space="preserve">
|
||||
<value>自动滚动到末尾</value>
|
||||
<value>自動滾動到末尾</value>
|
||||
</data>
|
||||
<data name="TbSettingsSpeedPingTestUrl" xml:space="preserve">
|
||||
<value>真連線測試位址</value>
|
||||
@@ -1123,7 +1102,7 @@
|
||||
<value>新增 [HTTP] 設定檔</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
|
||||
<value>使用 Xray 且非 Tun 模式啟用,和分組前置代理衝突</value>
|
||||
<value>和分組前置代理衝突</value>
|
||||
</data>
|
||||
<data name="TbSettingsEnableFragment" xml:space="preserve">
|
||||
<value>啟用分片(Fragment)</value>
|
||||
@@ -1201,7 +1180,7 @@
|
||||
<value>全局</value>
|
||||
</data>
|
||||
<data name="menuModeNothing" xml:space="preserve">
|
||||
<value>随原配置</value>
|
||||
<value>隨原配置</value>
|
||||
</data>
|
||||
<data name="menuModeRule" xml:space="preserve">
|
||||
<value>規則</value>
|
||||
@@ -1231,7 +1210,7 @@
|
||||
<value>自動調整列寬</value>
|
||||
</data>
|
||||
<data name="menuExport2ShareUrlBase64" xml:space="preserve">
|
||||
<value>匯出分享链接至剪貼簿 (多選) Base64 编码</value>
|
||||
<value>匯出分享連結至剪貼簿 (多選) Base64 編碼</value>
|
||||
</data>
|
||||
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
|
||||
<value>匯出所選設定檔完整設定至剪貼簿</value>
|
||||
@@ -1267,7 +1246,7 @@
|
||||
<value>WebDav 伺服器位址</value>
|
||||
</data>
|
||||
<data name="LvWebDavUserName" xml:space="preserve">
|
||||
<value>WebDav 賬戶</value>
|
||||
<value>WebDav 帳戶</value>
|
||||
</data>
|
||||
<data name="LvWebDavPassword" xml:space="preserve">
|
||||
<value>WebDav 密碼</value>
|
||||
@@ -1336,13 +1315,7 @@
|
||||
<value>系統的 sudo 密碼</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordTip" xml:space="preserve">
|
||||
<value>輸入的密碼無法校驗,所以請確保輸入正確。如果因為輸入錯誤導致無法正常運作時,請重新啟動本應用。 密碼不會存儲,每次重啟後都需要再次輸入。</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordIsEmpty" xml:space="preserve">
|
||||
<value>請先在 Tun 模式設定中設定 sudo 密碼</value>
|
||||
</data>
|
||||
<data name="TbSettingsLinuxSudoPasswordNotSudoRunApp" xml:space="preserve">
|
||||
<value>請不要用 sudo 來運行此 App</value>
|
||||
<value>密碼將調用命令行校驗,如果因為校驗錯誤導致無法正常運行時,請重啟本應用。密碼不會存儲,每次重啟後都需要再次輸入。</value>
|
||||
</data>
|
||||
<data name="TransportHeaderTypeTip5" xml:space="preserve">
|
||||
<value>*xhttp 模式</value>
|
||||
@@ -1366,7 +1339,7 @@
|
||||
<value>開啟第二個本機監聽埠</value>
|
||||
</data>
|
||||
<data name="TbRoutingInboundTagTips" xml:space="preserve">
|
||||
<value>socks:本地端口,socks2:第二個本地端口,socks3:區域網路端口</value>
|
||||
<value>socks:本地埠,socks2:第二個本地埠,socks3:區域網路埠</value>
|
||||
</data>
|
||||
<data name="TbSettingsTheme" xml:space="preserve">
|
||||
<value>主題</value>
|
||||
@@ -1387,10 +1360,10 @@
|
||||
<value>移除無效測試結果 {0} 個。</value>
|
||||
</data>
|
||||
<data name="TbPorts7" xml:space="preserve">
|
||||
<value>跳躍端口範圍</value>
|
||||
<value>跳躍埠範圍</value>
|
||||
</data>
|
||||
<data name="TbPorts7Tips" xml:space="preserve">
|
||||
<value>會覆蓋端口,多組時用逗號 (,) 隔開</value>
|
||||
<value>會覆蓋埠,多組時用逗號 (,) 隔開</value>
|
||||
</data>
|
||||
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
|
||||
<value>多設定檔產生自訂配置 (多選)</value>
|
||||
@@ -1413,4 +1386,85 @@
|
||||
<data name="menuExportConfig" xml:space="preserve">
|
||||
<value>匯出設定檔</value>
|
||||
</data>
|
||||
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
|
||||
<value>目前連接資訊測試地址</value>
|
||||
</data>
|
||||
<data name="TbRuleOutboundTagTip" xml:space="preserve">
|
||||
<value>可以填寫設定檔別名,請確保存在並唯一</value>
|
||||
</data>
|
||||
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
|
||||
<value>密碼錯誤,請重試。</value>
|
||||
</data>
|
||||
<data name="TbMldsa65Verify" xml:space="preserve">
|
||||
<value>Mldsa65Verify</value>
|
||||
</data>
|
||||
<data name="menuAddAnytlsServer" xml:space="preserve">
|
||||
<value>新增 [Anytls] 設定檔</value>
|
||||
</data>
|
||||
<data name="TbRemoteDNS" xml:space="preserve">
|
||||
<value>Remote DNS</value>
|
||||
</data>
|
||||
<data name="TbDomesticDNS" xml:space="preserve">
|
||||
<value>Domestic DNS</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundsResolverDNS" xml:space="preserve">
|
||||
<value>Outbound DNS Resolution (sing-box)</value>
|
||||
</data>
|
||||
<data name="TbSBOutboundDomainResolve" xml:space="preserve">
|
||||
<value>Resolve Outbound Domains</value>
|
||||
</data>
|
||||
<data name="TbSBDoHResolverServer" xml:space="preserve">
|
||||
<value>sing-box DoH Resolver Server</value>
|
||||
</data>
|
||||
<data name="TbSBFallbackDNSResolve" xml:space="preserve">
|
||||
<value>Fallback DNS Resolution, Suggest IP</value>
|
||||
</data>
|
||||
<data name="TbXrayFreedomResolveStrategy" xml:space="preserve">
|
||||
<value>xray Freedom Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box Direct Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
|
||||
<value>sing-box Remote Resolution Strategy</value>
|
||||
</data>
|
||||
<data name="TbAddCommonDNSHosts" xml:space="preserve">
|
||||
<value>Add Common DNS Hosts</value>
|
||||
</data>
|
||||
<data name="TbSBDoHOverride" xml:space="preserve">
|
||||
<value>The sing-box DoH resolution server can be overwritten</value>
|
||||
</data>
|
||||
<data name="TbFakeIP" xml:space="preserve">
|
||||
<value>FakeIP</value>
|
||||
</data>
|
||||
<data name="TbBlockSVCBHTTPSQueries" xml:space="preserve">
|
||||
<value>Block SVCB and HTTPS Queries</value>
|
||||
</data>
|
||||
<data name="TbPreventDNSLeaks" xml:space="preserve">
|
||||
<value>Prevent DNS Leaks</value>
|
||||
</data>
|
||||
<data name="TbDNSHostsConfig" xml:space="preserve">
|
||||
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
|
||||
</data>
|
||||
<data name="TbApplyProxyDomainsOnly" xml:space="preserve">
|
||||
<value>Apply to Proxy Domains Only</value>
|
||||
</data>
|
||||
<data name="ThBasicDNSSettings" xml:space="preserve">
|
||||
<value>Basic DNS Settings</value>
|
||||
</data>
|
||||
<data name="ThAdvancedDNSSettings" xml:space="preserve">
|
||||
<value>Advanced DNS Settings</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPs" xml:space="preserve">
|
||||
<value>Validate Regional Domain IPs</value>
|
||||
</data>
|
||||
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
|
||||
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnable" xml:space="preserve">
|
||||
<value>Enable Custom DNS</value>
|
||||
</data>
|
||||
<data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve">
|
||||
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
{
|
||||
"log": {
|
||||
"level": "debug",
|
||||
"timestamp": true
|
||||
@@ -14,22 +14,10 @@
|
||||
{
|
||||
"type": "direct",
|
||||
"tag": "direct"
|
||||
},
|
||||
{
|
||||
"type": "block",
|
||||
"tag": "block"
|
||||
},
|
||||
{
|
||||
"tag": "dns_out",
|
||||
"type": "dns"
|
||||
}
|
||||
],
|
||||
"route": {
|
||||
"rules": [
|
||||
{
|
||||
"protocol": [ "dns" ],
|
||||
"outbound": "dns_out"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -2,28 +2,33 @@
|
||||
"servers": [
|
||||
{
|
||||
"tag": "remote",
|
||||
"address": "tcp://8.8.8.8",
|
||||
"strategy": "prefer_ipv4",
|
||||
"type": "tcp",
|
||||
"server": "8.8.8.8",
|
||||
"detour": "proxy"
|
||||
},
|
||||
{
|
||||
"tag": "local",
|
||||
"address": "223.5.5.5",
|
||||
"strategy": "prefer_ipv4",
|
||||
"detour": "direct"
|
||||
},
|
||||
{
|
||||
"tag": "block",
|
||||
"address": "rcode://success"
|
||||
"type": "udp",
|
||||
"server": "223.5.5.5"
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"domain_suffix": [
|
||||
"googleapis.cn",
|
||||
"gstatic.com"
|
||||
],
|
||||
"server": "remote",
|
||||
"strategy": "prefer_ipv4"
|
||||
},
|
||||
{
|
||||
"rule_set": [
|
||||
"geosite-cn"
|
||||
],
|
||||
"server": "local"
|
||||
"server": "local",
|
||||
"strategy": "prefer_ipv4"
|
||||
}
|
||||
],
|
||||
"final": "remote"
|
||||
"final": "remote",
|
||||
"strategy": "prefer_ipv4"
|
||||
}
|
||||
|
||||
@@ -6,11 +6,10 @@
|
||||
"servers": [
|
||||
{
|
||||
"address": "1.1.1.1",
|
||||
"skipFallback": true,
|
||||
"domains": [
|
||||
"geosite:geolocation-!cn"
|
||||
],
|
||||
"expectIPs": [
|
||||
"geoip:!cn"
|
||||
"domain:googleapis.cn",
|
||||
"domain:gstatic.com"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -23,6 +22,7 @@
|
||||
"geoip:cn"
|
||||
]
|
||||
},
|
||||
"1.1.1.1",
|
||||
"8.8.8.8",
|
||||
"https://dns.google/dns-query"
|
||||
]
|
||||
|
||||
61
v2rayN/ServiceLib/Sample/kill_as_sudo_linux_sh
Normal file
61
v2rayN/ServiceLib/Sample/kill_as_sudo_linux_sh
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Process Terminator Script for Linux
|
||||
# This script forcibly terminates a process and all its child processes
|
||||
#
|
||||
|
||||
# Check if PID argument is provided
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 <PID>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PID=$1
|
||||
|
||||
# Validate that input is a valid PID (numeric)
|
||||
if ! [[ "$PID" =~ ^[0-9]+$ ]]; then
|
||||
echo "Error: The PID must be a numeric value"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if the process exists
|
||||
if ! ps -p $PID > /dev/null; then
|
||||
echo "Warning: No process found with PID $PID"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Recursive function to find and kill all child processes
|
||||
kill_children() {
|
||||
local parent=$1
|
||||
local children=$(ps -o pid --no-headers --ppid "$parent")
|
||||
|
||||
# Output information about processes being terminated
|
||||
echo "Processing children of PID: $parent..."
|
||||
|
||||
# Process each child
|
||||
for child in $children; do
|
||||
# Recursively find and kill child's children first
|
||||
kill_children "$child"
|
||||
|
||||
# Force kill the child process
|
||||
echo "Terminating child process: $child"
|
||||
kill -9 "$child" 2>/dev/null || true
|
||||
done
|
||||
}
|
||||
|
||||
echo "============================================"
|
||||
echo "Starting termination of process $PID and all its children"
|
||||
echo "============================================"
|
||||
|
||||
# Find and kill all child processes
|
||||
kill_children "$PID"
|
||||
|
||||
# Finally kill the main process
|
||||
echo "Terminating main process: $PID"
|
||||
kill -9 "$PID" 2>/dev/null || true
|
||||
|
||||
echo "============================================"
|
||||
echo "Process $PID and all its children have been terminated"
|
||||
echo "============================================"
|
||||
|
||||
exit 0
|
||||
56
v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh
Normal file
56
v2rayN/ServiceLib/Sample/kill_as_sudo_osx_sh
Normal file
@@ -0,0 +1,56 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Process Terminator Script for macOS
|
||||
# This script forcibly terminates a process and all its descendant processes
|
||||
#
|
||||
|
||||
# Check if PID argument is provided
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 <PID>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PID=$1
|
||||
|
||||
# Validate that input is a valid PID (numeric)
|
||||
if ! [[ "$PID" =~ ^[0-9]+$ ]]; then
|
||||
echo "Error: The PID must be a numeric value"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if the process exists - using kill -0 which is more reliable on macOS
|
||||
if ! kill -0 $PID 2>/dev/null; then
|
||||
echo "Warning: No process found with PID $PID"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Recursive function to find and kill all descendant processes
|
||||
kill_descendants() {
|
||||
local parent=$1
|
||||
# Use ps -axo for macOS to ensure all processes are included
|
||||
local children=$(ps -axo pid=,ppid= | awk -v ppid=$parent '$2==ppid {print $1}')
|
||||
|
||||
echo "Processing children of PID: $parent..."
|
||||
for child in $children; do
|
||||
kill_descendants "$child"
|
||||
echo "Terminating child process: $child"
|
||||
kill -9 "$child" 2>/dev/null || true
|
||||
done
|
||||
}
|
||||
|
||||
echo "============================================"
|
||||
echo "Starting termination of process $PID and all its descendants"
|
||||
echo "============================================"
|
||||
|
||||
# Find and kill all descendant processes
|
||||
kill_descendants "$PID"
|
||||
|
||||
# Finally kill the main process
|
||||
echo "Terminating main process: $PID"
|
||||
kill -9 "$PID" 2>/dev/null || true
|
||||
|
||||
echo "============================================"
|
||||
echo "Process $PID and all its descendants have been terminated"
|
||||
echo "============================================"
|
||||
|
||||
exit 0
|
||||
@@ -7,4 +7,4 @@ X-GNOME-Autostart-enabled=true
|
||||
Name[en_US]=v2rayN
|
||||
Name=v2rayN
|
||||
Comment[en_US]=v2rayN
|
||||
Comment=v2rayN
|
||||
Comment=v2rayN
|
||||
|
||||
@@ -29,9 +29,6 @@ set_gnome_proxy() {
|
||||
echo "Ignored Hosts: $IGNORE_HOSTS"
|
||||
elif [ "$MODE" == "none" ]; then
|
||||
echo "GNOME: Proxy disabled."
|
||||
else
|
||||
echo "GNOME: Invalid mode. Use 'none' or 'manual'."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -69,9 +66,6 @@ set_kde_proxy() {
|
||||
# Disable proxy
|
||||
$KWRITECONFIG --file kioslaverc --group "Proxy Settings" --key ProxyType 0
|
||||
echo "KDE: Proxy disabled."
|
||||
else
|
||||
echo "KDE: Invalid mode. Use 'none' or 'manual'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Apply changes by restarting KDE's network settings
|
||||
@@ -84,7 +78,7 @@ detect_desktop_environment() {
|
||||
echo "gnome"
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
if [[ "$XDG_CURRENT_DESKTOP" == *"XFCE"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"XFCE"* ]]; then
|
||||
echo "gnome"
|
||||
return
|
||||
@@ -94,7 +88,22 @@ detect_desktop_environment() {
|
||||
echo "gnome"
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
if [[ "$XDG_CURRENT_DESKTOP" == *"UKUI"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"ukui"* ]]; then
|
||||
echo "gnome"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$XDG_CURRENT_DESKTOP" == *"DDE"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"dde"* ]]; then
|
||||
echo "gnome"
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ "$XDG_CURRENT_DESKTOP" == *"MATE"* ]] || [[ "$XDG_SESSION_DESKTOP" == *"mate"* ]]; then
|
||||
echo "gnome"
|
||||
return
|
||||
fi
|
||||
|
||||
local KDE_ENVIRONMENTS=("KDE" "plasma")
|
||||
for ENV in "${KDE_ENVIRONMENTS[@]}"; do
|
||||
if [ "$XDG_CURRENT_DESKTOP" == "$ENV" ] || [ "$XDG_SESSION_DESKTOP" == "$ENV" ]; then
|
||||
@@ -102,6 +111,15 @@ detect_desktop_environment() {
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
# Fallback to GNOME method if CLI utility is available. This solves the
|
||||
# proxy configuration issues on minimal installation systems, like setups
|
||||
# with only window managers, that borrow some parts from big DEs.
|
||||
if command -v gsettings >/dev/null 2>&1; then
|
||||
echo "gnome"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "unsupported"
|
||||
}
|
||||
|
||||
@@ -119,6 +137,11 @@ PROXY_IP=$2
|
||||
PROXY_PORT=$3
|
||||
IGNORE_HOSTS=$4
|
||||
|
||||
if ! [[ "$MODE" =~ ^(manual|none)$ ]]; then
|
||||
echo "Invalid mode. Use 'none' or 'manual'." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Detect desktop environment
|
||||
DE=$(detect_desktop_environment)
|
||||
|
||||
@@ -129,6 +152,6 @@ elif [ "$DE" == "kde" ]; then
|
||||
set_gnome_proxy "$MODE" "$PROXY_IP" "$PROXY_PORT" "$IGNORE_HOSTS"
|
||||
set_kde_proxy "$MODE" "$PROXY_IP" "$PROXY_PORT" "$IGNORE_HOSTS"
|
||||
else
|
||||
echo "Unsupported desktop environment: $DE"
|
||||
echo "Unsupported desktop environment: $DE" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -2,29 +2,33 @@
|
||||
"servers": [
|
||||
{
|
||||
"tag": "remote",
|
||||
"address": "tcp://8.8.8.8",
|
||||
"strategy": "prefer_ipv4",
|
||||
"type": "tcp",
|
||||
"server": "8.8.8.8",
|
||||
"detour": "proxy"
|
||||
},
|
||||
{
|
||||
"tag": "local",
|
||||
"address": "223.5.5.5",
|
||||
"strategy": "prefer_ipv4",
|
||||
"detour": "direct"
|
||||
},
|
||||
{
|
||||
"tag": "block",
|
||||
"address": "rcode://success"
|
||||
"type": "udp",
|
||||
"server": "223.5.5.5"
|
||||
}
|
||||
],
|
||||
"rules": [
|
||||
{
|
||||
"rule_set": [
|
||||
"geosite-cn",
|
||||
"geosite-geolocation-cn"
|
||||
"domain_suffix": [
|
||||
"googleapis.cn",
|
||||
"gstatic.com"
|
||||
],
|
||||
"server": "local"
|
||||
"server": "remote",
|
||||
"strategy": "prefer_ipv4"
|
||||
},
|
||||
{
|
||||
"rule_set": [
|
||||
"geosite-cn"
|
||||
],
|
||||
"server": "local",
|
||||
"strategy": "prefer_ipv4"
|
||||
}
|
||||
],
|
||||
"final": "remote"
|
||||
}
|
||||
"final": "remote",
|
||||
"strategy": "prefer_ipv4"
|
||||
}
|
||||
@@ -8,13 +8,13 @@
|
||||
139,
|
||||
5353
|
||||
],
|
||||
"outbound": "block"
|
||||
"action": "reject"
|
||||
},
|
||||
{
|
||||
"ip_cidr": [
|
||||
"224.0.0.0/3",
|
||||
"ff00::/8"
|
||||
],
|
||||
"outbound": "block"
|
||||
"action": "reject"
|
||||
}
|
||||
]
|
||||
@@ -28,6 +28,8 @@
|
||||
<EmbeddedResource Include="Sample\custom_routing_white" />
|
||||
<EmbeddedResource Include="Sample\dns_singbox_normal" />
|
||||
<EmbeddedResource Include="Sample\dns_v2ray_normal" />
|
||||
<EmbeddedResource Include="Sample\kill_as_sudo_linux_sh" />
|
||||
<EmbeddedResource Include="Sample\kill_as_sudo_osx_sh" />
|
||||
<EmbeddedResource Include="Sample\pac" />
|
||||
<EmbeddedResource Include="Sample\proxy_set_linux_sh" />
|
||||
<EmbeddedResource Include="Sample\proxy_set_osx_sh" />
|
||||
|
||||
@@ -12,7 +12,7 @@ public class CoreConfigClashService
|
||||
{
|
||||
_config = config;
|
||||
}
|
||||
|
||||
|
||||
public async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
|
||||
{
|
||||
var ret = new RetResult();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,8 @@
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace ServiceLib.Services.CoreConfig;
|
||||
|
||||
@@ -54,12 +56,12 @@ public class CoreConfigV2rayService
|
||||
|
||||
await GenInbounds(v2rayConfig);
|
||||
|
||||
await GenRouting(v2rayConfig);
|
||||
|
||||
await GenOutbound(node, v2rayConfig.outbounds.First());
|
||||
|
||||
await GenMoreOutbounds(node, v2rayConfig);
|
||||
|
||||
await GenRouting(v2rayConfig);
|
||||
|
||||
await GenDns(node, v2rayConfig);
|
||||
|
||||
await GenStatistic(v2rayConfig);
|
||||
@@ -113,14 +115,14 @@ public class CoreConfigV2rayService
|
||||
await GenStatistic(v2rayConfig);
|
||||
v2rayConfig.outbounds.RemoveAt(0);
|
||||
|
||||
var tagProxy = new List<string>();
|
||||
var proxyProfiles = new List<ProfileItem>();
|
||||
foreach (var it in selecteds)
|
||||
{
|
||||
if (it.ConfigType == EConfigType.Custom)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC)
|
||||
if (it.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -151,17 +153,14 @@ public class CoreConfigV2rayService
|
||||
}
|
||||
|
||||
//outbound
|
||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(item, outbound);
|
||||
outbound.tag = $"{Global.ProxyTag}-{tagProxy.Count + 1}";
|
||||
v2rayConfig.outbounds.Insert(0, outbound);
|
||||
tagProxy.Add(outbound.tag);
|
||||
proxyProfiles.Add(item);
|
||||
}
|
||||
if (tagProxy.Count <= 0)
|
||||
if (proxyProfiles.Count <= 0)
|
||||
{
|
||||
ret.Msg = ResUI.FailedGenDefaultConfiguration;
|
||||
return ret;
|
||||
}
|
||||
await GenOutboundsList(proxyProfiles, v2rayConfig);
|
||||
|
||||
//add balancers
|
||||
await GenBalancer(v2rayConfig, multipleLoad);
|
||||
@@ -328,7 +327,7 @@ public class CoreConfigV2rayService
|
||||
{
|
||||
listen = Global.Loopback,
|
||||
port = port,
|
||||
protocol = EInboundProtocol.socks.ToString(),
|
||||
protocol = EInboundProtocol.mixed.ToString(),
|
||||
};
|
||||
inbound.tag = inbound.protocol + inbound.port.ToString();
|
||||
v2rayConfig.inbounds.Add(inbound);
|
||||
@@ -403,7 +402,7 @@ public class CoreConfigV2rayService
|
||||
tag = $"{EInboundProtocol.socks}{port}",
|
||||
listen = Global.Loopback,
|
||||
port = port,
|
||||
protocol = EInboundProtocol.socks.ToString(),
|
||||
protocol = EInboundProtocol.mixed.ToString(),
|
||||
});
|
||||
|
||||
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
|
||||
@@ -507,7 +506,7 @@ public class CoreConfigV2rayService
|
||||
}
|
||||
inbound.tag = protocol.ToString();
|
||||
inbound.port = inItem.LocalPort + (int)protocol;
|
||||
inbound.protocol = EInboundProtocol.socks.ToString();
|
||||
inbound.protocol = EInboundProtocol.mixed.ToString();
|
||||
inbound.settings.udp = inItem.UdpEnabled;
|
||||
inbound.sniffing.enabled = inItem.SniffingEnabled;
|
||||
inbound.sniffing.destOverride = inItem.DestOverride;
|
||||
@@ -559,6 +558,8 @@ public class CoreConfigV2rayService
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
rule.outboundTag = await GenRoutingUserRuleOutbound(rule.outboundTag, v2rayConfig);
|
||||
|
||||
if (rule.port.IsNullOrEmpty())
|
||||
{
|
||||
rule.port = null;
|
||||
@@ -614,6 +615,7 @@ public class CoreConfigV2rayService
|
||||
if (rule.port.IsNotEmpty()
|
||||
|| rule.protocol?.Count > 0
|
||||
|| rule.inboundTag?.Count > 0
|
||||
|| rule.network != null
|
||||
)
|
||||
{
|
||||
var it = JsonUtils.DeepCopy(rule);
|
||||
@@ -629,11 +631,37 @@ public class CoreConfigV2rayService
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<string?> GenRoutingUserRuleOutbound(string outboundTag, V2rayConfig v2rayConfig)
|
||||
{
|
||||
if (Global.OutboundTags.Contains(outboundTag))
|
||||
{
|
||||
return outboundTag;
|
||||
}
|
||||
|
||||
var node = await AppHandler.Instance.GetProfileItemViaRemarks(outboundTag);
|
||||
if (node == null
|
||||
|| node.ConfigType == EConfigType.Custom
|
||||
|| node.ConfigType == EConfigType.Hysteria2
|
||||
|| node.ConfigType == EConfigType.TUIC
|
||||
|| node.ConfigType == EConfigType.Anytls)
|
||||
{
|
||||
return Global.ProxyTag;
|
||||
}
|
||||
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(node, outbound);
|
||||
outbound.tag = Global.ProxyTag + node.IndexId.ToString();
|
||||
v2rayConfig.outbounds.Add(outbound);
|
||||
|
||||
return outbound.tag;
|
||||
}
|
||||
|
||||
private async Task<int> GenOutbound(ProfileItem node, Outbounds4Ray outbound)
|
||||
{
|
||||
try
|
||||
{
|
||||
var muxEnabled = _config.CoreBasicItem.MuxEnabled;
|
||||
var muxEnabled = node.MuxEnabled ?? _config.CoreBasicItem.MuxEnabled;
|
||||
switch (node.ConfigType)
|
||||
{
|
||||
case EConfigType.VMess:
|
||||
@@ -661,7 +689,7 @@ public class CoreConfigV2rayService
|
||||
{
|
||||
usersItem = vnextItem.users.First();
|
||||
}
|
||||
|
||||
|
||||
usersItem.id = node.Id;
|
||||
usersItem.alterId = node.AlterId;
|
||||
usersItem.email = Global.UserEMail;
|
||||
@@ -919,6 +947,7 @@ public class CoreConfigV2rayService
|
||||
publicKey = node.PublicKey,
|
||||
shortId = node.ShortId,
|
||||
spiderX = node.SpiderX,
|
||||
mldsa65Verify = node.Mldsa65Verify,
|
||||
show = false,
|
||||
};
|
||||
|
||||
@@ -1107,6 +1136,264 @@ public class CoreConfigV2rayService
|
||||
}
|
||||
|
||||
private async Task<int> GenDns(ProfileItem? node, V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
|
||||
if (item != null && item.Enabled == true)
|
||||
{
|
||||
return await GenDnsCompatible(node, v2rayConfig);
|
||||
}
|
||||
var simpleDNSItem = _config.SimpleDNSItem;
|
||||
var domainStrategy4Freedom = simpleDNSItem?.RayStrategy4Freedom;
|
||||
|
||||
//Outbound Freedom domainStrategy
|
||||
if (domainStrategy4Freedom.IsNotEmpty())
|
||||
{
|
||||
var outbound = v2rayConfig.outbounds.FirstOrDefault(t => t is { protocol: "freedom", tag: Global.DirectTag });
|
||||
if (outbound != null)
|
||||
{
|
||||
outbound.settings = new()
|
||||
{
|
||||
domainStrategy = domainStrategy4Freedom,
|
||||
userLevel = 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
await GenDnsServers(node, v2rayConfig, simpleDNSItem);
|
||||
await GenDnsHosts(v2rayConfig, simpleDNSItem);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsServers(ProfileItem? node, V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem)
|
||||
{
|
||||
static List<string> ParseDnsAddresses(string? dnsInput, string defaultAddress)
|
||||
{
|
||||
var addresses = dnsInput?.Split(dnsInput.Contains(',') ? ',' : ';')
|
||||
.Select(addr => addr.Trim())
|
||||
.Where(addr => !string.IsNullOrEmpty(addr))
|
||||
.Select(addr => addr.StartsWith("dhcp", StringComparison.OrdinalIgnoreCase) ? "localhost" : addr)
|
||||
.Distinct()
|
||||
.ToList() ?? new List<string> { defaultAddress };
|
||||
return addresses.Count > 0 ? addresses : new List<string> { defaultAddress };
|
||||
}
|
||||
|
||||
static object CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
|
||||
{
|
||||
var dnsServer = new DnsServer4Ray
|
||||
{
|
||||
address = dnsAddress,
|
||||
skipFallback = true,
|
||||
domains = domains.Count > 0 ? domains : null,
|
||||
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
|
||||
};
|
||||
return JsonUtils.SerializeToNode(dnsServer, new JsonSerializerOptions
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
});
|
||||
}
|
||||
|
||||
var directDNSAddress = ParseDnsAddresses(simpleDNSItem?.DirectDNS, Global.DomainDirectDNSAddress.FirstOrDefault());
|
||||
var remoteDNSAddress = ParseDnsAddresses(simpleDNSItem?.RemoteDNS, Global.DomainRemoteDNSAddress.FirstOrDefault());
|
||||
|
||||
var directDomainList = new List<string>();
|
||||
var directGeositeList = new List<string>();
|
||||
var proxyDomainList = new List<string>();
|
||||
var proxyGeositeList = new List<string>();
|
||||
var expectedDomainList = new List<string>();
|
||||
var expectedIPs = new List<string>();
|
||||
var regionNames = new HashSet<string>();
|
||||
|
||||
if (!string.IsNullOrEmpty(simpleDNSItem?.DirectExpectedIPs))
|
||||
{
|
||||
expectedIPs = simpleDNSItem.DirectExpectedIPs
|
||||
.Split(new[] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(s => s.Trim())
|
||||
.Where(s => !string.IsNullOrEmpty(s))
|
||||
.ToList();
|
||||
|
||||
foreach (var ip in expectedIPs)
|
||||
{
|
||||
if (ip.StartsWith("geoip:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var region = ip["geoip:".Length..];
|
||||
if (!string.IsNullOrEmpty(region))
|
||||
{
|
||||
regionNames.Add($"geosite:{region}");
|
||||
regionNames.Add($"geosite:geolocation-{region}");
|
||||
regionNames.Add($"geosite:tld-{region}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var routing = await ConfigHandler.GetDefaultRouting(_config);
|
||||
List<RulesItem>? rules = null;
|
||||
if (routing != null)
|
||||
{
|
||||
rules = JsonUtils.Deserialize<List<RulesItem>>(routing.RuleSet) ?? [];
|
||||
foreach (var item in rules)
|
||||
{
|
||||
if (!item.Enabled || item.Domain is null || item.Domain.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var domain in item.Domain)
|
||||
{
|
||||
if (domain.StartsWith('#'))
|
||||
continue;
|
||||
var normalizedDomain = domain.Replace(Global.RoutingRuleComma, ",");
|
||||
|
||||
if (item.OutboundTag == Global.DirectTag)
|
||||
{
|
||||
if (normalizedDomain.StartsWith("geosite:"))
|
||||
{
|
||||
(regionNames.Contains(normalizedDomain) ? expectedDomainList : directGeositeList).Add(normalizedDomain);
|
||||
}
|
||||
else
|
||||
{
|
||||
directDomainList.Add(normalizedDomain);
|
||||
}
|
||||
}
|
||||
else if (item.OutboundTag == Global.ProxyTag)
|
||||
{
|
||||
if (normalizedDomain.StartsWith("geosite:"))
|
||||
{
|
||||
proxyGeositeList.Add(normalizedDomain);
|
||||
}
|
||||
else
|
||||
{
|
||||
proxyDomainList.Add(normalizedDomain);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Utils.IsDomain(node?.Address))
|
||||
{
|
||||
directDomainList.Add(node.Address);
|
||||
}
|
||||
|
||||
if (node?.Subid is not null)
|
||||
{
|
||||
var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
|
||||
if (subItem is not null)
|
||||
{
|
||||
foreach (var profile in new[] { subItem.PrevProfile, subItem.NextProfile })
|
||||
{
|
||||
var profileNode = await AppHandler.Instance.GetProfileItemViaRemarks(profile);
|
||||
if (profileNode is not null &&
|
||||
profileNode.ConfigType is not (EConfigType.Custom or EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls) &&
|
||||
Utils.IsDomain(profileNode.Address))
|
||||
{
|
||||
directDomainList.Add(profileNode.Address);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v2rayConfig.dns ??= new Dns4Ray();
|
||||
v2rayConfig.dns.servers ??= new List<object>();
|
||||
|
||||
void AddDnsServers(List<string> dnsAddresses, List<string> domains, List<string>? expectedIPs = null)
|
||||
{
|
||||
if (domains.Count > 0)
|
||||
{
|
||||
foreach (var dnsAddress in dnsAddresses)
|
||||
{
|
||||
v2rayConfig.dns.servers.Add(CreateDnsServer(dnsAddress, domains, expectedIPs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AddDnsServers(remoteDNSAddress, proxyDomainList);
|
||||
AddDnsServers(directDNSAddress, directDomainList);
|
||||
AddDnsServers(remoteDNSAddress, proxyGeositeList);
|
||||
AddDnsServers(directDNSAddress, directGeositeList);
|
||||
AddDnsServers(directDNSAddress, expectedDomainList, expectedIPs);
|
||||
|
||||
var useDirectDns = rules?.LastOrDefault() is { } lastRule &&
|
||||
lastRule.OutboundTag == Global.DirectTag &&
|
||||
(lastRule.Port == "0-65535" ||
|
||||
lastRule.Network == "tcp,udp" ||
|
||||
lastRule.Ip?.Contains("0.0.0.0/0") == true);
|
||||
|
||||
var defaultDnsServers = useDirectDns ? directDNSAddress : remoteDNSAddress;
|
||||
v2rayConfig.dns.servers.AddRange(defaultDnsServers);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsHosts(V2rayConfig v2rayConfig, SimpleDNSItem simpleDNSItem)
|
||||
{
|
||||
if (simpleDNSItem.AddCommonHosts == false && simpleDNSItem.UseSystemHosts == false && simpleDNSItem.Hosts.IsNullOrEmpty())
|
||||
{
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
v2rayConfig.dns ??= new Dns4Ray();
|
||||
v2rayConfig.dns.hosts ??= new Dictionary<string, List<string>>();
|
||||
if (simpleDNSItem.AddCommonHosts == true)
|
||||
{
|
||||
v2rayConfig.dns.hosts = Global.PredefinedHosts;
|
||||
}
|
||||
|
||||
if (simpleDNSItem.UseSystemHosts == true)
|
||||
{
|
||||
var systemHosts = Utils.GetSystemHosts();
|
||||
if (systemHosts.Count > 0)
|
||||
{
|
||||
var normalHost = v2rayConfig.dns.hosts;
|
||||
if (normalHost != null)
|
||||
{
|
||||
foreach (var host in systemHosts)
|
||||
{
|
||||
if (normalHost[host.Key] != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
normalHost[host.Key] = new List<string> { host.Value };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var userHostsMap = simpleDNSItem.Hosts?
|
||||
.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(line => !string.IsNullOrWhiteSpace(line))
|
||||
.Where(line => line.Contains(' '))
|
||||
.ToDictionary(
|
||||
line =>
|
||||
{
|
||||
var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
return parts[0];
|
||||
},
|
||||
line =>
|
||||
{
|
||||
var parts = line.Trim().Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var values = parts.Skip(1).ToList();
|
||||
return values;
|
||||
}
|
||||
);
|
||||
|
||||
if (userHostsMap != null)
|
||||
{
|
||||
foreach (var kvp in userHostsMap)
|
||||
{
|
||||
v2rayConfig.dns.hosts[kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
return await Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsCompatible(ProfileItem? node, V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1149,22 +1436,33 @@ public class CoreConfigV2rayService
|
||||
var systemHosts = Utils.GetSystemHosts();
|
||||
if (systemHosts.Count > 0)
|
||||
{
|
||||
var normalHost = obj["hosts"];
|
||||
if (normalHost != null)
|
||||
var normalHost1 = obj["hosts"];
|
||||
if (normalHost1 != null)
|
||||
{
|
||||
foreach (var host in systemHosts)
|
||||
{
|
||||
if (normalHost[host.Key] != null)
|
||||
if (normalHost1[host.Key] != null)
|
||||
continue;
|
||||
normalHost[host.Key] = host.Value;
|
||||
normalHost1[host.Key] = host.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var normalHost = obj["hosts"];
|
||||
if (normalHost != null)
|
||||
{
|
||||
foreach (var hostProp in normalHost.AsObject().ToList())
|
||||
{
|
||||
if (hostProp.Value is JsonValue value && value.TryGetValue<string>(out var ip))
|
||||
{
|
||||
normalHost[hostProp.Key] = new JsonArray(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await GenDnsDomains(node, obj, item);
|
||||
await GenDnsDomainsCompatible(node, obj, item);
|
||||
|
||||
v2rayConfig.dns = obj;
|
||||
v2rayConfig.dns = JsonUtils.Deserialize<Dns4Ray>(obj.ToJsonString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -1173,7 +1471,7 @@ public class CoreConfigV2rayService
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenDnsDomains(ProfileItem? node, JsonNode dns, DNSItem? dNSItem)
|
||||
private async Task<int> GenDnsDomainsCompatible(ProfileItem? node, JsonNode dns, DNSItem? dNSItem)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
@@ -1216,7 +1514,7 @@ public class CoreConfigV2rayService
|
||||
{
|
||||
var dnsServer = new DnsServer4Ray()
|
||||
{
|
||||
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
|
||||
address = string.IsNullOrEmpty(dNSItem?.DomainDNSAddress) ? Global.DomainPureIPDNSAddress.FirstOrDefault() : dNSItem?.DomainDNSAddress,
|
||||
skipFallback = true,
|
||||
domains = domainList
|
||||
};
|
||||
@@ -1320,39 +1618,24 @@ public class CoreConfigV2rayService
|
||||
|
||||
//Previous proxy
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
string? prevOutboundTag = null;
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC)
|
||||
&& prevNode.ConfigType != EConfigType.TUIC
|
||||
&& prevNode.ConfigType != EConfigType.Anytls)
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevOutbound.tag = $"{Global.ProxyTag}2";
|
||||
prevOutboundTag = $"prev-{Global.ProxyTag}";
|
||||
prevOutbound.tag = prevOutboundTag;
|
||||
v2rayConfig.outbounds.Add(prevOutbound);
|
||||
|
||||
outbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = prevOutbound.tag
|
||||
};
|
||||
}
|
||||
var nextOutbound = await GenChainOutbounds(subItem, outbound, prevOutboundTag);
|
||||
|
||||
//Next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC)
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
var nextOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
nextOutbound.tag = Global.ProxyTag;
|
||||
v2rayConfig.outbounds.Insert(0, nextOutbound);
|
||||
|
||||
outbound.tag = $"{Global.ProxyTag}1";
|
||||
nextOutbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = outbound.tag
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -1363,18 +1646,181 @@ public class CoreConfigV2rayService
|
||||
return 0;
|
||||
}
|
||||
|
||||
private async Task<int> GenOutboundsList(List<ProfileItem> nodes, V2rayConfig v2rayConfig)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get template and initialize list
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
if (txtOutbound.IsNullOrEmpty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var resultOutbounds = new List<Outbounds4Ray>();
|
||||
var prevOutbounds = new List<Outbounds4Ray>(); // Separate list for prev outbounds and fragment
|
||||
|
||||
// Cache for chain proxies to avoid duplicate generation
|
||||
var nextProxyCache = new Dictionary<string, Outbounds4Ray?>();
|
||||
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
|
||||
int prevIndex = 0; // Index for prev outbounds
|
||||
|
||||
// Process nodes
|
||||
int index = 0;
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
index++;
|
||||
|
||||
// Handle proxy chain
|
||||
string? prevTag = null;
|
||||
var currentOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
var nextOutbound = nextProxyCache.GetValueOrDefault(node.Subid, null);
|
||||
if (nextOutbound != null)
|
||||
{
|
||||
nextOutbound = JsonUtils.DeepCopy(nextOutbound);
|
||||
}
|
||||
|
||||
var subItem = await AppHandler.Instance.GetSubItem(node.Subid);
|
||||
|
||||
// current proxy
|
||||
await GenOutbound(node, currentOutbound);
|
||||
currentOutbound.tag = $"{Global.ProxyTag}-{index}";
|
||||
|
||||
if (!node.Subid.IsNullOrEmpty())
|
||||
{
|
||||
if (prevProxyTags.TryGetValue(node.Subid, out var value))
|
||||
{
|
||||
prevTag = value; // maybe null
|
||||
}
|
||||
else
|
||||
{
|
||||
var prevNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.PrevProfile);
|
||||
if (prevNode is not null
|
||||
&& prevNode.ConfigType != EConfigType.Custom
|
||||
&& prevNode.ConfigType != EConfigType.Hysteria2
|
||||
&& prevNode.ConfigType != EConfigType.TUIC
|
||||
&& prevNode.ConfigType != EConfigType.Anytls)
|
||||
{
|
||||
var prevOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(prevNode, prevOutbound);
|
||||
prevTag = $"prev-{Global.ProxyTag}-{++prevIndex}";
|
||||
prevOutbound.tag = prevTag;
|
||||
prevOutbounds.Add(prevOutbound);
|
||||
}
|
||||
prevProxyTags[node.Subid] = prevTag;
|
||||
}
|
||||
|
||||
nextOutbound = await GenChainOutbounds(subItem, currentOutbound, prevTag, nextOutbound);
|
||||
if (!nextProxyCache.ContainsKey(node.Subid))
|
||||
{
|
||||
nextProxyCache[node.Subid] = nextOutbound;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextOutbound is not null)
|
||||
{
|
||||
resultOutbounds.Add(nextOutbound);
|
||||
}
|
||||
resultOutbounds.Add(currentOutbound);
|
||||
}
|
||||
|
||||
// Merge results: first the main chain outbounds, then other outbounds, and finally utility outbounds
|
||||
resultOutbounds.AddRange(prevOutbounds);
|
||||
resultOutbounds.AddRange(v2rayConfig.outbounds);
|
||||
v2rayConfig.outbounds = resultOutbounds;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a chained outbound configuration for the given subItem and outbound.
|
||||
/// The outbound's tag must be set before calling this method.
|
||||
/// Returns the next proxy's outbound configuration, which may be null if no next proxy exists.
|
||||
/// </summary>
|
||||
/// <param name="subItem">The subscription item containing proxy chain information.</param>
|
||||
/// <param name="outbound">The current outbound configuration. Its tag must be set before calling this method.</param>
|
||||
/// <param name="prevOutboundTag">The tag of the previous outbound in the chain, if any.</param>
|
||||
/// <param name="nextOutbound">The outbound for the next proxy in the chain, if already created. If null, will be created inside.</param>
|
||||
/// <returns>
|
||||
/// The outbound configuration for the next proxy in the chain, or null if no next proxy exists.
|
||||
/// </returns>
|
||||
private async Task<Outbounds4Ray?> GenChainOutbounds(SubItem subItem, Outbounds4Ray outbound, string? prevOutboundTag, Outbounds4Ray? nextOutbound = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
|
||||
|
||||
if (!prevOutboundTag.IsNullOrEmpty())
|
||||
{
|
||||
outbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = prevOutboundTag
|
||||
};
|
||||
}
|
||||
|
||||
// Next proxy
|
||||
var nextNode = await AppHandler.Instance.GetProfileItemViaRemarks(subItem.NextProfile);
|
||||
if (nextNode is not null
|
||||
&& nextNode.ConfigType != EConfigType.Custom
|
||||
&& nextNode.ConfigType != EConfigType.Hysteria2
|
||||
&& nextNode.ConfigType != EConfigType.TUIC
|
||||
&& nextNode.ConfigType != EConfigType.Anytls)
|
||||
{
|
||||
if (nextOutbound == null)
|
||||
{
|
||||
nextOutbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
|
||||
await GenOutbound(nextNode, nextOutbound);
|
||||
}
|
||||
nextOutbound.tag = outbound.tag;
|
||||
|
||||
outbound.tag = $"mid-{outbound.tag}";
|
||||
nextOutbound.streamSettings.sockopt = new()
|
||||
{
|
||||
dialerProxy = outbound.tag
|
||||
};
|
||||
}
|
||||
return nextOutbound;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog(_tag, ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<int> GenBalancer(V2rayConfig v2rayConfig, EMultipleLoad multipleLoad)
|
||||
{
|
||||
if (multipleLoad is EMultipleLoad.LeastLoad or EMultipleLoad.LeastPing)
|
||||
if (multipleLoad == EMultipleLoad.LeastPing)
|
||||
{
|
||||
var observatory = new Observatory4Ray
|
||||
{
|
||||
subjectSelector = [Global.ProxyTag],
|
||||
probeUrl = AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
||||
probeInterval = "3m"
|
||||
probeInterval = "3m",
|
||||
enableConcurrency = true,
|
||||
};
|
||||
v2rayConfig.observatory = observatory;
|
||||
}
|
||||
else if (multipleLoad == EMultipleLoad.LeastLoad)
|
||||
{
|
||||
var burstObservatory = new BurstObservatory4Ray
|
||||
{
|
||||
subjectSelector = [Global.ProxyTag],
|
||||
pingConfig = new()
|
||||
{
|
||||
destination = AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl,
|
||||
interval = "5m",
|
||||
timeout = "30s",
|
||||
sampling = 2,
|
||||
}
|
||||
};
|
||||
v2rayConfig.burstObservatory = burstObservatory;
|
||||
}
|
||||
var strategyType = multipleLoad switch
|
||||
{
|
||||
EMultipleLoad.Random => "random",
|
||||
|
||||
@@ -196,6 +196,7 @@ public class SpeedtestService
|
||||
{
|
||||
return false;
|
||||
}
|
||||
await Task.Delay(1000);
|
||||
|
||||
var downloadHandle = new DownloadService();
|
||||
|
||||
@@ -255,9 +256,13 @@ public class SpeedtestService
|
||||
try
|
||||
{
|
||||
pid = await CoreHandler.Instance.LoadCoreConfigSpeedtest(it);
|
||||
if (pid > 0)
|
||||
if (pid < 0)
|
||||
{
|
||||
await Task.Delay(500);
|
||||
UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
var delay = await DoRealPing(downloadHandle, it);
|
||||
if (blSpeedTest)
|
||||
{
|
||||
@@ -271,10 +276,6 @@ public class SpeedtestService
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -357,8 +358,8 @@ public class SpeedtestService
|
||||
private List<List<ServerTestItem>> GetTestBatchItem(List<ServerTestItem> lstSelected, int pageSize)
|
||||
{
|
||||
List<List<ServerTestItem>> lstTest = new();
|
||||
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC)).ToList();
|
||||
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC).ToList();
|
||||
var lst1 = lstSelected.Where(t => t.ConfigType is not (EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls)).ToList();
|
||||
var lst2 = lstSelected.Where(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.Anytls).ToList();
|
||||
|
||||
for (var num = 0; num < (int)Math.Ceiling(lst1.Count * 1.0 / pageSize); num++)
|
||||
{
|
||||
|
||||
@@ -194,11 +194,11 @@ public class UpdateService
|
||||
{
|
||||
if (Utils.IsBase64String(result2))
|
||||
{
|
||||
result += Utils.Base64Decode(result2);
|
||||
result += Environment.NewLine + Utils.Base64Decode(result2);
|
||||
}
|
||||
else
|
||||
{
|
||||
result += result2;
|
||||
result += Environment.NewLine + result2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,21 +243,6 @@ public class UpdateService
|
||||
_updateFunc?.Invoke(true, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, "geo"));
|
||||
}
|
||||
|
||||
public async Task<string> RunAvailabilityCheck()
|
||||
{
|
||||
var downloadHandle = new DownloadService();
|
||||
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}";
|
||||
}
|
||||
|
||||
return string.Format(ResUI.TestMeOutput, time, ip);
|
||||
}
|
||||
|
||||
#region CheckUpdate private
|
||||
|
||||
private async Task<RetResult> CheckUpdateAsync(DownloadService downloadHandle, ECoreType type, bool preRelease)
|
||||
@@ -500,6 +485,12 @@ public class UpdateService
|
||||
|
||||
private async Task UpdateOtherFiles(Config config, Action<bool, string> updateFunc)
|
||||
{
|
||||
//If it is not in China area, no update is required
|
||||
if (config.ConstItem.GeoSourceUrl.IsNotEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_updateFunc = updateFunc;
|
||||
|
||||
foreach (var url in Global.OtherGeoUrls)
|
||||
@@ -545,6 +536,11 @@ public class UpdateService
|
||||
}
|
||||
}
|
||||
|
||||
//append dns items TODO
|
||||
geoSiteFiles.Add("cn");
|
||||
geoSiteFiles.Add("geolocation-cn");
|
||||
geoSiteFiles.Add("category-ads-all");
|
||||
|
||||
var path = Utils.GetBinPath("srss");
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
|
||||
@@ -234,7 +234,7 @@ public class ClashProxiesViewModel : MyReactiveObject
|
||||
else
|
||||
{
|
||||
SelectedGroup = _proxyGroups.First();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -6,39 +6,52 @@ namespace ServiceLib.ViewModels;
|
||||
|
||||
public class DNSSettingViewModel : MyReactiveObject
|
||||
{
|
||||
[Reactive] public bool UseSystemHosts { get; set; }
|
||||
[Reactive] public string DomainStrategy4Freedom { get; set; }
|
||||
[Reactive] public string DomainDNSAddress { get; set; }
|
||||
[Reactive] public string NormalDNS { get; set; }
|
||||
[Reactive] public bool? UseSystemHosts { get; set; }
|
||||
[Reactive] public bool? AddCommonHosts { get; set; }
|
||||
[Reactive] public bool? FakeIP { get; set; }
|
||||
[Reactive] public bool? BlockBindingQuery { get; set; }
|
||||
[Reactive] public string? DirectDNS { get; set; }
|
||||
[Reactive] public string? RemoteDNS { get; set; }
|
||||
[Reactive] public string? SingboxOutboundsResolveDNS { get; set; }
|
||||
[Reactive] public string? SingboxFinalResolveDNS { get; set; }
|
||||
[Reactive] public string? RayStrategy4Freedom { get; set; }
|
||||
[Reactive] public string? SingboxStrategy4Direct { get; set; }
|
||||
[Reactive] public string? SingboxStrategy4Proxy { get; set; }
|
||||
[Reactive] public string? Hosts { get; set; }
|
||||
[Reactive] public string? DirectExpectedIPs { get; set; }
|
||||
|
||||
[Reactive] public string DomainStrategy4Freedom2 { get; set; }
|
||||
[Reactive] public string DomainDNSAddress2 { get; set; }
|
||||
[Reactive] public string NormalDNS2 { get; set; }
|
||||
[Reactive] public string TunDNS2 { get; set; }
|
||||
[Reactive] public bool UseSystemHostsCompatible { get; set; }
|
||||
[Reactive] public string DomainStrategy4FreedomCompatible { get; set; }
|
||||
[Reactive] public string DomainDNSAddressCompatible { get; set; }
|
||||
[Reactive] public string NormalDNSCompatible { get; set; }
|
||||
|
||||
[Reactive] public string DomainStrategy4Freedom2Compatible { get; set; }
|
||||
[Reactive] public string DomainDNSAddress2Compatible { get; set; }
|
||||
[Reactive] public string NormalDNS2Compatible { get; set; }
|
||||
[Reactive] public string TunDNS2Compatible { get; set; }
|
||||
[Reactive] public bool RayCustomDNSEnableCompatible { get; set; }
|
||||
[Reactive] public bool SBCustomDNSEnableCompatible { get; set; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4V2rayCompatibleCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ImportDefConfig4SingboxCompatibleCmd { get; }
|
||||
|
||||
public DNSSettingViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
{
|
||||
_config = AppHandler.Instance.Config;
|
||||
_updateView = updateView;
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await SaveSettingAsync();
|
||||
});
|
||||
SaveCmd = ReactiveCommand.CreateFromTask(SaveSettingAsync);
|
||||
|
||||
ImportDefConfig4V2rayCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
ImportDefConfig4V2rayCompatibleCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
NormalDNS = EmbedUtils.GetEmbedText(Global.DNSV2rayNormalFileName);
|
||||
NormalDNSCompatible = EmbedUtils.GetEmbedText(Global.DNSV2rayNormalFileName);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
|
||||
ImportDefConfig4SingboxCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
ImportDefConfig4SingboxCompatibleCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
NormalDNS2 = EmbedUtils.GetEmbedText(Global.DNSSingboxNormalFileName);
|
||||
TunDNS2 = EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName);
|
||||
NormalDNS2Compatible = EmbedUtils.GetEmbedText(Global.DNSSingboxNormalFileName);
|
||||
TunDNS2Compatible = EmbedUtils.GetEmbedText(Global.TunSingboxDNSFileName);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
|
||||
@@ -47,48 +60,80 @@ public class DNSSettingViewModel : MyReactiveObject
|
||||
|
||||
private async Task Init()
|
||||
{
|
||||
var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
|
||||
_config = AppHandler.Instance.Config;
|
||||
var item = _config.SimpleDNSItem;
|
||||
UseSystemHosts = item.UseSystemHosts;
|
||||
DomainStrategy4Freedom = item?.DomainStrategy4Freedom ?? string.Empty;
|
||||
DomainDNSAddress = item?.DomainDNSAddress ?? string.Empty;
|
||||
NormalDNS = item?.NormalDNS ?? string.Empty;
|
||||
AddCommonHosts = item.AddCommonHosts;
|
||||
FakeIP = item.FakeIP;
|
||||
BlockBindingQuery = item.BlockBindingQuery;
|
||||
DirectDNS = item.DirectDNS;
|
||||
RemoteDNS = item.RemoteDNS;
|
||||
RayStrategy4Freedom = item.RayStrategy4Freedom;
|
||||
SingboxOutboundsResolveDNS = item.SingboxOutboundsResolveDNS;
|
||||
SingboxFinalResolveDNS = item.SingboxFinalResolveDNS;
|
||||
SingboxStrategy4Direct = item.SingboxStrategy4Direct;
|
||||
SingboxStrategy4Proxy = item.SingboxStrategy4Proxy;
|
||||
Hosts = item.Hosts;
|
||||
DirectExpectedIPs = item.DirectExpectedIPs;
|
||||
|
||||
var item1 = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
|
||||
RayCustomDNSEnableCompatible = item1.Enabled;
|
||||
UseSystemHostsCompatible = item1.UseSystemHosts;
|
||||
DomainStrategy4FreedomCompatible = item1?.DomainStrategy4Freedom ?? string.Empty;
|
||||
DomainDNSAddressCompatible = item1?.DomainDNSAddress ?? string.Empty;
|
||||
NormalDNSCompatible = item1?.NormalDNS ?? string.Empty;
|
||||
|
||||
var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
DomainStrategy4Freedom2 = item2?.DomainStrategy4Freedom ?? string.Empty;
|
||||
DomainDNSAddress2 = item2?.DomainDNSAddress ?? string.Empty;
|
||||
NormalDNS2 = item2?.NormalDNS ?? string.Empty;
|
||||
TunDNS2 = item2?.TunDNS ?? string.Empty;
|
||||
SBCustomDNSEnableCompatible = item2.Enabled;
|
||||
DomainStrategy4Freedom2Compatible = item2?.DomainStrategy4Freedom ?? string.Empty;
|
||||
DomainDNSAddress2Compatible = item2?.DomainDNSAddress ?? string.Empty;
|
||||
NormalDNS2Compatible = item2?.NormalDNS ?? string.Empty;
|
||||
TunDNS2Compatible = item2?.TunDNS ?? string.Empty;
|
||||
}
|
||||
|
||||
private async Task SaveSettingAsync()
|
||||
{
|
||||
if (NormalDNS.IsNotEmpty())
|
||||
_config.SimpleDNSItem.UseSystemHosts = UseSystemHosts;
|
||||
_config.SimpleDNSItem.AddCommonHosts = AddCommonHosts;
|
||||
_config.SimpleDNSItem.FakeIP = FakeIP;
|
||||
_config.SimpleDNSItem.BlockBindingQuery = BlockBindingQuery;
|
||||
_config.SimpleDNSItem.DirectDNS = DirectDNS;
|
||||
_config.SimpleDNSItem.RemoteDNS = RemoteDNS;
|
||||
_config.SimpleDNSItem.RayStrategy4Freedom = RayStrategy4Freedom;
|
||||
_config.SimpleDNSItem.SingboxOutboundsResolveDNS = SingboxOutboundsResolveDNS;
|
||||
_config.SimpleDNSItem.SingboxFinalResolveDNS = SingboxFinalResolveDNS;
|
||||
_config.SimpleDNSItem.SingboxStrategy4Direct = SingboxStrategy4Direct;
|
||||
_config.SimpleDNSItem.SingboxStrategy4Proxy = SingboxStrategy4Proxy;
|
||||
_config.SimpleDNSItem.Hosts = Hosts;
|
||||
_config.SimpleDNSItem.DirectExpectedIPs = DirectExpectedIPs;
|
||||
|
||||
if (NormalDNSCompatible.IsNotEmpty())
|
||||
{
|
||||
var obj = JsonUtils.ParseJson(NormalDNS);
|
||||
var obj = JsonUtils.ParseJson(NormalDNSCompatible);
|
||||
if (obj != null && obj["servers"] != null)
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NormalDNS.Contains('{') || NormalDNS.Contains('}'))
|
||||
if (NormalDNSCompatible.Contains('{') || NormalDNSCompatible.Contains('}'))
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (NormalDNS2.IsNotEmpty())
|
||||
if (NormalDNS2Compatible.IsNotEmpty())
|
||||
{
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(NormalDNS2);
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(NormalDNS2Compatible);
|
||||
if (obj2 == null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (TunDNS2.IsNotEmpty())
|
||||
if (TunDNS2Compatible.IsNotEmpty())
|
||||
{
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(TunDNS2);
|
||||
var obj2 = JsonUtils.Deserialize<Dns4Sbox>(TunDNS2Compatible);
|
||||
if (obj2 == null)
|
||||
{
|
||||
NoticeHandler.Instance.Enqueue(ResUI.FillCorrectDNSText);
|
||||
@@ -96,21 +141,26 @@ public class DNSSettingViewModel : MyReactiveObject
|
||||
}
|
||||
}
|
||||
|
||||
var item = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
|
||||
item.DomainStrategy4Freedom = DomainStrategy4Freedom;
|
||||
item.DomainDNSAddress = DomainDNSAddress;
|
||||
item.UseSystemHosts = UseSystemHosts;
|
||||
item.NormalDNS = NormalDNS;
|
||||
await ConfigHandler.SaveDNSItems(_config, item);
|
||||
var item1 = await AppHandler.Instance.GetDNSItem(ECoreType.Xray);
|
||||
item1.Enabled = RayCustomDNSEnableCompatible;
|
||||
item1.DomainStrategy4Freedom = DomainStrategy4FreedomCompatible;
|
||||
item1.DomainDNSAddress = DomainDNSAddressCompatible;
|
||||
item1.UseSystemHosts = UseSystemHostsCompatible;
|
||||
item1.NormalDNS = NormalDNSCompatible;
|
||||
await ConfigHandler.SaveDNSItems(_config, item1);
|
||||
|
||||
var item2 = await AppHandler.Instance.GetDNSItem(ECoreType.sing_box);
|
||||
item2.DomainStrategy4Freedom = DomainStrategy4Freedom2;
|
||||
item2.DomainDNSAddress = DomainDNSAddress2;
|
||||
item2.NormalDNS = JsonUtils.Serialize(JsonUtils.ParseJson(NormalDNS2));
|
||||
item2.TunDNS = JsonUtils.Serialize(JsonUtils.ParseJson(TunDNS2));
|
||||
item2.Enabled = RayCustomDNSEnableCompatible;
|
||||
item2.DomainStrategy4Freedom = DomainStrategy4Freedom2Compatible;
|
||||
item2.DomainDNSAddress = DomainDNSAddress2Compatible;
|
||||
item2.NormalDNS = JsonUtils.Serialize(JsonUtils.ParseJson(NormalDNS2Compatible));
|
||||
item2.TunDNS = JsonUtils.Serialize(JsonUtils.ParseJson(TunDNS2Compatible));
|
||||
await ConfigHandler.SaveDNSItems(_config, item2);
|
||||
|
||||
NoticeHandler.Instance.Enqueue(ResUI.OperationSuccess);
|
||||
_ = _updateView?.Invoke(EViewAction.CloseWindow, null);
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
if (_updateView != null)
|
||||
{
|
||||
await _updateView(EViewAction.CloseWindow, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ public class MainWindowViewModel : MyReactiveObject
|
||||
public ReactiveCommand<Unit, Unit> AddHysteria2ServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddTuicServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddWireguardServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddAnytlsServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddCustomServerCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddServerViaClipboardCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> AddServerViaScanCmd { get; }
|
||||
@@ -111,6 +112,10 @@ public class MainWindowViewModel : MyReactiveObject
|
||||
{
|
||||
await AddServerAsync(true, EConfigType.WireGuard);
|
||||
});
|
||||
AddAnytlsServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerAsync(true, EConfigType.Anytls);
|
||||
});
|
||||
AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerAsync(true, EConfigType.Custom);
|
||||
@@ -552,6 +557,7 @@ public class MainWindowViewModel : MyReactiveObject
|
||||
{
|
||||
await LoadCore();
|
||||
await SysProxyHandler.UpdateSysProxy(_config, false);
|
||||
await Task.Delay(1000);
|
||||
});
|
||||
Locator.Current.GetService<StatusBarViewModel>()?.TestServerAvailability();
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
[Reactive] public string GeoFileSourceUrl { get; set; }
|
||||
[Reactive] public string SrsFileSourceUrl { get; set; }
|
||||
[Reactive] public string RoutingRulesSourceUrl { get; set; }
|
||||
[Reactive] public string IPAPIUrl { get; set; }
|
||||
|
||||
#endregion UI
|
||||
|
||||
@@ -186,6 +187,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
GeoFileSourceUrl = _config.ConstItem.GeoSourceUrl;
|
||||
SrsFileSourceUrl = _config.ConstItem.SrsSourceUrl;
|
||||
RoutingRulesSourceUrl = _config.ConstItem.RouteRulesTemplateSourceUrl;
|
||||
IPAPIUrl = _config.SpeedTestItem.IPAPIUrl;
|
||||
|
||||
#endregion UI
|
||||
|
||||
@@ -344,6 +346,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
_config.ConstItem.GeoSourceUrl = GeoFileSourceUrl;
|
||||
_config.ConstItem.SrsSourceUrl = SrsFileSourceUrl;
|
||||
_config.ConstItem.RouteRulesTemplateSourceUrl = RoutingRulesSourceUrl;
|
||||
_config.SpeedTestItem.IPAPIUrl = IPAPIUrl;
|
||||
|
||||
//systemProxy
|
||||
_config.SystemProxyItem.SystemProxyExceptions = systemProxyExceptions;
|
||||
@@ -356,7 +359,7 @@ public class OptionSettingViewModel : MyReactiveObject
|
||||
_config.TunModeItem.Mtu = TunMtu;
|
||||
_config.TunModeItem.EnableExInbound = TunEnableExInbound;
|
||||
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
|
||||
|
||||
|
||||
//coreType
|
||||
await SaveCoreType();
|
||||
|
||||
|
||||
@@ -304,7 +304,7 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
{
|
||||
item.SpeedVal = result.Speed ?? string.Empty;
|
||||
}
|
||||
_profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
||||
//_profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
||||
}
|
||||
|
||||
public void UpdateStatistics(ServerSpeedItem update)
|
||||
@@ -319,16 +319,16 @@ public class ProfilesViewModel : MyReactiveObject
|
||||
item.TotalDown = Utils.HumanFy(update.TotalDown);
|
||||
item.TotalUp = Utils.HumanFy(update.TotalUp);
|
||||
|
||||
if (SelectedProfile?.IndexId == item.IndexId)
|
||||
{
|
||||
var temp = JsonUtils.DeepCopy(item);
|
||||
_profileItems.Replace(item, temp);
|
||||
SelectedProfile = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
_profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
||||
}
|
||||
//if (SelectedProfile?.IndexId == item.IndexId)
|
||||
//{
|
||||
// var temp = JsonUtils.DeepCopy(item);
|
||||
// _profileItems.Replace(item, temp);
|
||||
// SelectedProfile = temp;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// _profileItems.Replace(item, JsonUtils.DeepCopy(item));
|
||||
//}
|
||||
}
|
||||
}
|
||||
catch
|
||||
|
||||
@@ -91,11 +91,9 @@ public class RoutingSettingViewModel : MyReactiveObject
|
||||
var routings = await AppHandler.Instance.RoutingItems();
|
||||
foreach (var item in routings)
|
||||
{
|
||||
var def = item.Id == _config.RoutingBasicItem.RoutingIndexId;
|
||||
|
||||
var it = new RoutingItemModel()
|
||||
{
|
||||
IsActive = def,
|
||||
IsActive = item.IsActive,
|
||||
RuleNum = item.RuleNum,
|
||||
Id = item.Id,
|
||||
Remarks = item.Remarks,
|
||||
|
||||
@@ -34,6 +34,8 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
public ReactiveCommand<Unit, Unit> SubUpdateViaProxyCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> CopyProxyCmdToClipboardCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> NotifyLeftClickCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> ShowWindowCmd { get; }
|
||||
public ReactiveCommand<Unit, Unit> HideWindowCmd { get; }
|
||||
|
||||
#region System Proxy
|
||||
|
||||
@@ -91,6 +93,9 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
[Reactive]
|
||||
public bool EnableTun { get; set; }
|
||||
|
||||
[Reactive]
|
||||
public bool BlIsNonWindows { get; set; }
|
||||
|
||||
#endregion UI
|
||||
|
||||
public StatusBarViewModel(Func<EViewAction, object?, Task<bool>>? updateView)
|
||||
@@ -100,6 +105,7 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
SelectedServer = new();
|
||||
RunningServerToolTipText = "-";
|
||||
BlSystemProxyPacVisible = Utils.IsWindows();
|
||||
BlIsNonWindows = Utils.IsNonWindows();
|
||||
|
||||
if (_config.TunModeItem.EnableTun && AllowEnableTun())
|
||||
{
|
||||
@@ -143,11 +149,21 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(null);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
ShowWindowCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(true);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
HideWindowCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.ShowHideWindow(false);
|
||||
await Task.CompletedTask;
|
||||
});
|
||||
|
||||
AddServerViaClipboardCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerViaClipboard();
|
||||
});
|
||||
{
|
||||
await AddServerViaClipboard();
|
||||
});
|
||||
AddServerViaScanCmd = ReactiveCommand.CreateFromTask(async () =>
|
||||
{
|
||||
await AddServerViaScan();
|
||||
@@ -320,7 +336,7 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
|
||||
var msg = await Task.Run(async () =>
|
||||
{
|
||||
return await (new UpdateService()).RunAvailabilityCheck();
|
||||
return await ConnectionHandler.Instance.RunAvailabilityCheck();
|
||||
});
|
||||
|
||||
NoticeHandler.Instance.SendMessageEx(msg);
|
||||
@@ -372,7 +388,7 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
foreach (var item in routings)
|
||||
{
|
||||
_routingItems.Add(item);
|
||||
if (item.Id == _config.RoutingBasicItem.RoutingIndexId)
|
||||
if (item.IsActive)
|
||||
{
|
||||
SelectedRouting = item;
|
||||
}
|
||||
@@ -396,10 +412,6 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_config.RoutingBasicItem.RoutingIndexId == item.Id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (await ConfigHandler.SetDefaultRouting(_config, item) == 0)
|
||||
{
|
||||
@@ -424,30 +436,34 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
|
||||
private async Task DoEnableTun(bool c)
|
||||
{
|
||||
if (_config.TunModeItem.EnableTun != EnableTun)
|
||||
if (_config.TunModeItem.EnableTun == EnableTun)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_config.TunModeItem.EnableTun = EnableTun;
|
||||
|
||||
if (EnableTun && AllowEnableTun() == false)
|
||||
{
|
||||
_config.TunModeItem.EnableTun = EnableTun;
|
||||
// When running as a non-administrator, reboot to administrator mode
|
||||
if (EnableTun && AllowEnableTun() == false)
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
_config.TunModeItem.EnableTun = false;
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool? passwordResult = await _updateView?.Invoke(EViewAction.PasswordInput, null);
|
||||
if (passwordResult == false)
|
||||
{
|
||||
_config.TunModeItem.EnableTun = false;
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.RebootAsAdmin();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (await _updateView?.Invoke(EViewAction.PasswordInput, null) == false)
|
||||
{
|
||||
_config.TunModeItem.EnableTun = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
}
|
||||
await ConfigHandler.SaveConfig(_config);
|
||||
Locator.Current.GetService<MainWindowViewModel>()?.Reload();
|
||||
}
|
||||
|
||||
private bool AllowEnableTun()
|
||||
@@ -500,8 +516,16 @@ public class StatusBarViewModel : MyReactiveObject
|
||||
{
|
||||
try
|
||||
{
|
||||
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.ProxyTag, Utils.HumanFy(update.ProxyUp), Utils.HumanFy(update.ProxyDown));
|
||||
SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.DirectTag, Utils.HumanFy(update.DirectUp), Utils.HumanFy(update.DirectDown));
|
||||
if (_config.IsRunningCore(ECoreType.sing_box))
|
||||
{
|
||||
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, EInboundProtocol.mixed, Utils.HumanFy(update.ProxyUp), Utils.HumanFy(update.ProxyDown));
|
||||
SpeedDirectDisplay = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
SpeedProxyDisplay = string.Format(ResUI.SpeedDisplayText, Global.ProxyTag, Utils.HumanFy(update.ProxyUp), Utils.HumanFy(update.ProxyDown));
|
||||
SpeedDirectDisplay = string.Format(ResUI.SpeedDisplayText, Global.DirectTag, Utils.HumanFy(update.DirectUp), Utils.HumanFy(update.DirectDown));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
xmlns:semi="https://irihi.tech/semi"
|
||||
xmlns:vms="clr-namespace:ServiceLib.ViewModels;assembly=ServiceLib"
|
||||
x:DataType="vms:StatusBarViewModel"
|
||||
Name="v2rayN"
|
||||
x:DataType="vms:StatusBarViewModel"
|
||||
RequestedThemeVariant="Default">
|
||||
<Application.Styles>
|
||||
<semi:SemiTheme />
|
||||
@@ -32,6 +32,16 @@
|
||||
ToolTipText="{Binding RunningServerToolTipText}">
|
||||
<TrayIcon.Menu>
|
||||
<NativeMenu>
|
||||
<NativeMenuItem
|
||||
Command="{Binding ShowWindowCmd}"
|
||||
Header="{x:Static resx:ResUI.TbDisplayGUI}"
|
||||
IsVisible="{Binding BlIsNonWindows}" />
|
||||
<NativeMenuItem
|
||||
Command="{Binding NotifyLeftClickCmd}"
|
||||
Header="{x:Static resx:ResUI.menuShowOrHideMainWindow}"
|
||||
IsVisible="{Binding BlIsNonWindows}" />
|
||||
<NativeMenuItem Command="{Binding CopyProxyCmdToClipboardCmd}" Header="{x:Static resx:ResUI.menuCopyProxyCmdToClipboard}" />
|
||||
<NativeMenuItemSeparator />
|
||||
<NativeMenuItem
|
||||
Command="{Binding SystemProxyClearCmd}"
|
||||
Header="{x:Static resx:ResUI.menuSystemProxyClear}"
|
||||
@@ -55,13 +65,9 @@
|
||||
ToggleType="Radio" />
|
||||
<NativeMenuItemSeparator />
|
||||
<NativeMenuItem Click="MenuAddServerViaClipboardClick" Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" />
|
||||
<NativeMenuItem Header="{x:Static resx:ResUI.menuAddServerViaScan}" IsVisible="False" />
|
||||
<NativeMenuItem Command="{Binding SubUpdateCmd}" Header="{x:Static resx:ResUI.menuSubUpdate}" />
|
||||
<NativeMenuItem Command="{Binding SubUpdateViaProxyCmd}" Header="{x:Static resx:ResUI.menuSubUpdateViaProxy}" />
|
||||
<NativeMenuItemSeparator />
|
||||
<NativeMenuItem Command="{Binding CopyProxyCmdToClipboardCmd}" Header="{x:Static resx:ResUI.menuCopyProxyCmdToClipboard}" />
|
||||
<NativeMenuItemSeparator />
|
||||
<NativeMenuItem Command="{Binding NotifyLeftClickCmd}" Header="{x:Static resx:ResUI.menuShowOrHideMainWindow}" />
|
||||
<NativeMenuItem Click="MenuExit_Click" Header="{x:Static resx:ResUI.menuExit}" />
|
||||
</NativeMenu>
|
||||
</TrayIcon.Menu>
|
||||
|
||||
@@ -11,11 +11,6 @@ public partial class App : Application
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
if (!AppHandler.Instance.InitApp())
|
||||
{
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
AvaloniaXamlLoader.Load(this);
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
|
||||
@@ -43,7 +38,7 @@ public partial class App : Application
|
||||
{
|
||||
if (e.ExceptionObject != null)
|
||||
{
|
||||
Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!);
|
||||
Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
52
v2rayN/v2rayN.Desktop/Base/WindowBase.cs
Normal file
52
v2rayN/v2rayN.Desktop/Base/WindowBase.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace v2rayN.Desktop.Base;
|
||||
|
||||
public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewModel : class
|
||||
{
|
||||
public WindowBase()
|
||||
{
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
private void ReactiveWindowBase_Closed(object? sender, EventArgs e)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected virtual void OnLoaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sizeItem = ConfigHandler.GetWindowSizeItem(AppHandler.Instance.Config, GetType().Name);
|
||||
if (sizeItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Width = sizeItem.Width;
|
||||
Height = sizeItem.Height;
|
||||
|
||||
var workingArea = (Screens.ScreenFromWindow(this) ?? Screens.Primary).WorkingArea;
|
||||
var scaling = (Utils.IsOSX() ? null : VisualRoot?.RenderScaling) ?? 1.0;
|
||||
|
||||
var x = workingArea.X + ((workingArea.Width - (Width * scaling)) / 2);
|
||||
var y = workingArea.Y + ((workingArea.Height - (Height * scaling)) / 2);
|
||||
|
||||
Position = new PixelPoint((int)x, (int)y);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
try
|
||||
{
|
||||
ConfigHandler.SaveWindowSizeItem(AppHandler.Instance.Config, GetType().Name, Width, Height);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
@@ -14,13 +14,17 @@ internal class Program
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
OnStartup(args);
|
||||
if (OnStartup(args) == false)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
BuildAvaloniaApp()
|
||||
.StartWithClassicDesktopLifetime(args);
|
||||
}
|
||||
|
||||
private static void OnStartup(string[]? Args)
|
||||
private static bool OnStartup(string[]? Args)
|
||||
{
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
@@ -30,8 +34,7 @@ internal class Program
|
||||
if (!rebootas && !bCreatedNew)
|
||||
{
|
||||
ProgramStarted.Set();
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -39,19 +42,26 @@ internal class Program
|
||||
_ = new Mutex(true, "v2rayN", out var bOnlyOneInstance);
|
||||
if (!bOnlyOneInstance)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!AppHandler.Instance.InitApp())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Avalonia configuration, don't remove; also used by visual designer.
|
||||
public static AppBuilder BuildAvaloniaApp()
|
||||
=> AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
//.WithInterFont()
|
||||
.WithFontByDefault()
|
||||
.LogToTrace()
|
||||
.UseReactiveUI()
|
||||
.With(new MacOSPlatformOptions { ShowInDock = false });
|
||||
{
|
||||
return AppBuilder.Configure<App>()
|
||||
.UsePlatformDetect()
|
||||
//.WithInterFont()
|
||||
.WithFontByDefault()
|
||||
.LogToTrace()
|
||||
.UseReactiveUI()
|
||||
.With(new MacOSPlatformOptions { ShowInDock = AppHandler.Instance.Config.UiItem.MacOSShowInDock });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||
x.OfType<Button>(),
|
||||
x.OfType<TextBox>(),
|
||||
x.OfType<TextBlock>(),
|
||||
x.OfType<SelectableTextBlock>(),
|
||||
x.OfType<Menu>(),
|
||||
x.OfType<ContextMenu>(),
|
||||
x.OfType<DataGridRow>(),
|
||||
@@ -146,6 +147,7 @@ public class ThemeSettingViewModel : MyReactiveObject
|
||||
x.OfType<Button>(),
|
||||
x.OfType<TextBox>(),
|
||||
x.OfType<TextBlock>(),
|
||||
x.OfType<SelectableTextBlock>(),
|
||||
x.OfType<Menu>(),
|
||||
x.OfType<ContextMenu>(),
|
||||
x.OfType<DataGridRow>(),
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System.Reactive.Disposables;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class AddServer2Window : ReactiveWindow<AddServer2ViewModel>
|
||||
public partial class AddServer2Window : WindowBase<AddServer2ViewModel>
|
||||
{
|
||||
public AddServer2Window()
|
||||
{
|
||||
@@ -21,13 +21,7 @@ public partial class AddServer2Window : ReactiveWindow<AddServer2ViewModel>
|
||||
btnCancel.Click += (s, e) => this.Close();
|
||||
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
|
||||
|
||||
foreach (ECoreType it in Enum.GetValues(typeof(ECoreType)))
|
||||
{
|
||||
if (it == ECoreType.v2rayN)
|
||||
continue;
|
||||
cmbCoreType.Items.Add(it.ToString());
|
||||
}
|
||||
cmbCoreType.Items.Add(string.Empty);
|
||||
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
@@ -152,13 +152,26 @@
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togmuxEnabled"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridSs"
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
@@ -185,6 +198,19 @@
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togmuxEnabled3"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridSocks"
|
||||
@@ -224,7 +250,7 @@
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
@@ -271,13 +297,26 @@
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togmuxEnabled5"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridTrojan"
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
@@ -304,6 +343,19 @@
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMuxEnabled}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togmuxEnabled6"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridHysteria2"
|
||||
@@ -481,6 +533,26 @@
|
||||
HorizontalAlignment="Left"
|
||||
Watermark="1500" />
|
||||
</Grid>
|
||||
<Grid
|
||||
x:Name="gridAnytls"
|
||||
Grid.Row="2"
|
||||
ColumnDefinitions="180,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbId3}" />
|
||||
<TextBox
|
||||
x:Name="txtId10"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</Grid>
|
||||
|
||||
<Separator
|
||||
x:Name="sepa2"
|
||||
@@ -701,7 +773,7 @@
|
||||
Grid.Row="7"
|
||||
ColumnDefinitions="180,Auto"
|
||||
IsVisible="False"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
@@ -771,6 +843,20 @@
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbMldsa65Verify}" />
|
||||
<TextBox
|
||||
x:Name="txtMldsa65Verify"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="400"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</Grid>
|
||||
<Separator Grid.Row="8" Margin="{StaticResource MarginTb8}" />
|
||||
</Grid>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
public partial class AddServerWindow : WindowBase<AddServerViewModel>
|
||||
{
|
||||
public AddServerWindow()
|
||||
{
|
||||
@@ -26,41 +26,22 @@ public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
|
||||
ViewModel = new AddServerViewModel(profileItem, UpdateViewHandler);
|
||||
|
||||
Global.CoreTypes.ForEach(it =>
|
||||
{
|
||||
cmbCoreType.Items.Add(it);
|
||||
});
|
||||
cmbCoreType.Items.Add(string.Empty);
|
||||
cmbCoreType.ItemsSource = Global.CoreTypes.AppendEmpty();
|
||||
cmbNetwork.ItemsSource = Global.Networks;
|
||||
cmbFingerprint.ItemsSource = Global.Fingerprints;
|
||||
cmbFingerprint2.ItemsSource = Global.Fingerprints;
|
||||
cmbAllowInsecure.ItemsSource = Global.AllowInsecure;
|
||||
cmbAlpn.ItemsSource = Global.Alpns;
|
||||
|
||||
cmbStreamSecurity.Items.Add(string.Empty);
|
||||
cmbStreamSecurity.Items.Add(Global.StreamSecurity);
|
||||
|
||||
Global.Networks.ForEach(it =>
|
||||
{
|
||||
cmbNetwork.Items.Add(it);
|
||||
});
|
||||
Global.Fingerprints.ForEach(it =>
|
||||
{
|
||||
cmbFingerprint.Items.Add(it);
|
||||
cmbFingerprint2.Items.Add(it);
|
||||
});
|
||||
Global.AllowInsecure.ForEach(it =>
|
||||
{
|
||||
cmbAllowInsecure.Items.Add(it);
|
||||
});
|
||||
Global.Alpns.ForEach(it =>
|
||||
{
|
||||
cmbAlpn.Items.Add(it);
|
||||
});
|
||||
var lstStreamSecurity = new List<string>();
|
||||
lstStreamSecurity.Add(string.Empty);
|
||||
lstStreamSecurity.Add(Global.StreamSecurity);
|
||||
|
||||
switch (profileItem.ConfigType)
|
||||
{
|
||||
case EConfigType.VMess:
|
||||
gridVMess.IsVisible = true;
|
||||
Global.VmessSecurities.ForEach(it =>
|
||||
{
|
||||
cmbSecurity.Items.Add(it);
|
||||
});
|
||||
cmbSecurity.ItemsSource = Global.VmessSecurities;
|
||||
if (profileItem.Security.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.Security = Global.DefaultSecurity;
|
||||
@@ -69,10 +50,7 @@ public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
|
||||
case EConfigType.Shadowsocks:
|
||||
gridSs.IsVisible = true;
|
||||
AppHandler.Instance.GetShadowsocksSecurities(profileItem).ForEach(it =>
|
||||
{
|
||||
cmbSecurity3.Items.Add(it);
|
||||
});
|
||||
cmbSecurity3.ItemsSource = AppHandler.Instance.GetShadowsocksSecurities(profileItem);
|
||||
break;
|
||||
|
||||
case EConfigType.SOCKS:
|
||||
@@ -82,11 +60,8 @@ public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
|
||||
case EConfigType.VLESS:
|
||||
gridVLESS.IsVisible = true;
|
||||
cmbStreamSecurity.Items.Add(Global.StreamSecurityReality);
|
||||
Global.Flows.ForEach(it =>
|
||||
{
|
||||
cmbFlow5.Items.Add(it);
|
||||
});
|
||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||
cmbFlow5.ItemsSource = Global.Flows;
|
||||
if (profileItem.Security.IsNullOrEmpty())
|
||||
{
|
||||
profileItem.Security = Global.None;
|
||||
@@ -95,11 +70,8 @@ public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
|
||||
case EConfigType.Trojan:
|
||||
gridTrojan.IsVisible = true;
|
||||
cmbStreamSecurity.Items.Add(Global.StreamSecurityReality);
|
||||
Global.Flows.ForEach(it =>
|
||||
{
|
||||
cmbFlow6.Items.Add(it);
|
||||
});
|
||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||
cmbFlow6.ItemsSource = Global.Flows;
|
||||
break;
|
||||
|
||||
case EConfigType.Hysteria2:
|
||||
@@ -119,10 +91,7 @@ public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
cmbFingerprint.IsEnabled = false;
|
||||
cmbFingerprint.SelectedValue = string.Empty;
|
||||
|
||||
Global.TuicCongestionControls.ForEach(it =>
|
||||
{
|
||||
cmbHeaderType8.Items.Add(it);
|
||||
});
|
||||
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
|
||||
break;
|
||||
|
||||
case EConfigType.WireGuard:
|
||||
@@ -133,7 +102,14 @@ public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
gridTls.IsVisible = false;
|
||||
|
||||
break;
|
||||
|
||||
case EConfigType.Anytls:
|
||||
gridAnytls.IsVisible = true;
|
||||
lstStreamSecurity.Add(Global.StreamSecurityReality);
|
||||
cmbCoreType.IsEnabled = false;
|
||||
break;
|
||||
}
|
||||
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
|
||||
|
||||
gridTlsMore.IsVisible = false;
|
||||
|
||||
@@ -150,11 +126,13 @@ public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.AlterId, v => v.txtAlterId.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Shadowsocks:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId3.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.SOCKS:
|
||||
@@ -167,11 +145,13 @@ public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow5.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Security, v => v.txtSecurity5.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled5.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Trojan:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId6.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Flow, v => v.cmbFlow6.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled6.IsChecked).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Hysteria2:
|
||||
@@ -193,6 +173,10 @@ public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.RequestHost, v => v.txtRequestHost9.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId9.Text).DisposeWith(disposables);
|
||||
break;
|
||||
|
||||
case EConfigType.Anytls:
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Id, v => v.txtId10.Text).DisposeWith(disposables);
|
||||
break;
|
||||
}
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType.SelectedValue).DisposeWith(disposables);
|
||||
@@ -211,6 +195,7 @@ public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.PublicKey, v => v.txtPublicKey.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.ShortId, v => v.txtShortId.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.SpiderX, v => v.txtSpiderX.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Mldsa65Verify, v => v.txtMldsa65Verify.Text).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
|
||||
});
|
||||
@@ -268,44 +253,41 @@ public partial class AddServerWindow : ReactiveWindow<AddServerViewModel>
|
||||
|
||||
private void SetHeaderType()
|
||||
{
|
||||
cmbHeaderType.Items.Clear();
|
||||
var lstHeaderType = new List<string>();
|
||||
|
||||
var network = cmbNetwork.SelectedItem.ToString();
|
||||
if (network.IsNullOrEmpty())
|
||||
{
|
||||
cmbHeaderType.Items.Add(Global.None);
|
||||
lstHeaderType.Add(Global.None);
|
||||
cmbHeaderType.ItemsSource = lstHeaderType;
|
||||
cmbHeaderType.SelectedIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (network == nameof(ETransport.tcp))
|
||||
{
|
||||
cmbHeaderType.Items.Add(Global.None);
|
||||
cmbHeaderType.Items.Add(Global.TcpHeaderHttp);
|
||||
lstHeaderType.Add(Global.None);
|
||||
lstHeaderType.Add(Global.TcpHeaderHttp);
|
||||
}
|
||||
else if (network is nameof(ETransport.kcp) or nameof(ETransport.quic))
|
||||
{
|
||||
cmbHeaderType.Items.Add(Global.None);
|
||||
Global.KcpHeaderTypes.ForEach(it =>
|
||||
{
|
||||
cmbHeaderType.Items.Add(it);
|
||||
});
|
||||
lstHeaderType.Add(Global.None);
|
||||
lstHeaderType.AddRange(Global.KcpHeaderTypes);
|
||||
}
|
||||
else if (network is nameof(ETransport.xhttp))
|
||||
{
|
||||
Global.XhttpMode.ForEach(it =>
|
||||
{
|
||||
cmbHeaderType.Items.Add(it);
|
||||
});
|
||||
lstHeaderType.AddRange(Global.XhttpMode);
|
||||
}
|
||||
else if (network == nameof(ETransport.grpc))
|
||||
{
|
||||
cmbHeaderType.Items.Add(Global.GrpcGunMode);
|
||||
cmbHeaderType.Items.Add(Global.GrpcMultiMode);
|
||||
lstHeaderType.Add(Global.GrpcGunMode);
|
||||
lstHeaderType.Add(Global.GrpcMultiMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmbHeaderType.Items.Add(Global.None);
|
||||
lstHeaderType.Add(Global.None);
|
||||
}
|
||||
cmbHeaderType.ItemsSource = lstHeaderType;
|
||||
cmbHeaderType.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,9 +52,16 @@ public partial class ClashConnectionsView : ReactiveUserControl<ClashConnections
|
||||
|
||||
private void AutofitColumnWidth()
|
||||
{
|
||||
foreach (var it in lstConnections.Columns)
|
||||
try
|
||||
{
|
||||
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
||||
foreach (var it in lstConnections.Columns)
|
||||
{
|
||||
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("ClashConnectionsView", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
x:Class="v2rayN.Desktop.Views.DNSSettingWindow"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
@@ -34,26 +35,300 @@
|
||||
IsCancel="True" />
|
||||
</StackPanel>
|
||||
|
||||
<TabControl HorizontalContentAlignment="Left">
|
||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDns}">
|
||||
<DockPanel Margin="{StaticResource Margin8}">
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
|
||||
<TabControl HorizontalContentAlignment="Stretch">
|
||||
<TabItem Header="{x:Static resx:ResUI.ThBasicDNSSettings}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||
<Grid
|
||||
Margin="{StaticResource Margin8}"
|
||||
ColumnDefinitions="Auto,Auto,*"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
x:Name="txtBasicDNSSettingsInvalid"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsRemoteDNS}" />
|
||||
Text="{x:Static resx:ResUI.TbCustomDNSEnabledPageInvalid}" />
|
||||
|
||||
<TextBlock Margin="{StaticResource Margin4}" VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkDnsObjectDoc_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbDnsObjectDoc}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<Button
|
||||
x:Name="btnImportDefConfig4V2ray"
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Content="{x:Static resx:ResUI.TbSettingDnsImportDefConfig}"
|
||||
Cursor="Hand" />
|
||||
</StackPanel>
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbDomesticDNS}" />
|
||||
<ctrls:AutoCompleteBox
|
||||
x:Name="cmbDirectDNS"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding DirectDNS, Mode=TwoWay}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbRemoteDNS}" />
|
||||
<ctrls:AutoCompleteBox
|
||||
x:Name="cmbRemoteDNS"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding RemoteDNS, Mode=TwoWay}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSBOutboundsResolverDNS}" />
|
||||
<ctrls:AutoCompleteBox
|
||||
x:Name="cmbSBResolverDNS"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding SingboxOutboundsResolveDNS, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSBOutboundDomainResolve}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSBDoHResolverServer}" />
|
||||
<ctrls:AutoCompleteBox
|
||||
x:Name="cmbSBFinalResolverDNS"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding SingboxFinalResolveDNS, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSBFallbackDNSResolve}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbXrayFreedomResolveStrategy}" />
|
||||
<ComboBox
|
||||
x:Name="cmbRayFreedomDNSStrategy"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
PlaceholderText="Default" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSBDirectResolveStrategy}" />
|
||||
<ComboBox
|
||||
x:Name="cmbSBDirectDNSStrategy"
|
||||
Grid.Row="6"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
PlaceholderText="Default" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="7"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSBRemoteResolveStrategy}" />
|
||||
<ComboBox
|
||||
x:Name="cmbSBRemoteDNSStrategy"
|
||||
Grid.Row="7"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
PlaceholderText="Default" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbAddCommonDNSHosts}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togAddCommonHosts"
|
||||
Grid.Row="8"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="8"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSBDoHOverride}" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="{x:Static resx:ResUI.ThAdvancedDNSSettings}">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Visible">
|
||||
<Grid
|
||||
Margin="{StaticResource Margin8}"
|
||||
ColumnDefinitions="Auto,Auto,*"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,*">
|
||||
|
||||
<TextBlock
|
||||
x:Name="txtAdvancedDNSSettingsInvalid"
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbCustomDNSEnabledPageInvalid}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsUseSystemHosts}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togUseSystemHosts"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbFakeIP}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togFakeIP"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbApplyProxyDomainsOnly}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbBlockSVCBHTTPSQueries}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togBlockBindingQuery"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbPreventDNSLeaks}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPs}" />
|
||||
<ctrls:AutoCompleteBox
|
||||
x:Name="cmbDirectExpectedIPs"
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding DirectExpectedIPs, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbValidateDirectExpectedIPsDesc}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbDNSHostsConfig}" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txtHosts"
|
||||
Grid.Row="6"
|
||||
Grid.Column="0"
|
||||
Grid.ColumnSpan="3"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderThickness="1"
|
||||
Classes="TextArea"
|
||||
TextWrapping="Wrap"
|
||||
Watermark="{x:Static resx:ResUI.TbDNSHostsConfig}" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDns}">
|
||||
<DockPanel Margin="{StaticResource Margin8}">
|
||||
<Grid DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbCustomDNSEnable}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togRayCustomDNSEnableCompatible"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsRemoteDNS}" />
|
||||
|
||||
<TextBlock Margin="{StaticResource Margin4}" VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkDnsObjectDoc_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbDnsObjectDoc}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<Button
|
||||
x:Name="btnImportDefConfig4V2rayCompatible"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Content="{x:Static resx:ResUI.TbSettingDnsImportDefConfig}"
|
||||
Cursor="Hand" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
@@ -62,7 +337,7 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsUseSystemHosts}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togUseSystemHosts"
|
||||
x:Name="togUseSystemHostsCompatible"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
@@ -73,7 +348,7 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsDomainStrategy4Freedom}" />
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy4Freedom"
|
||||
x:Name="cmbdomainStrategy4FreedomCompatible"
|
||||
Width="150"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</StackPanel>
|
||||
@@ -83,40 +358,61 @@
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" />
|
||||
<ComboBox
|
||||
x:Name="cmbdomainDNSAddress"
|
||||
<ctrls:AutoCompleteBox
|
||||
x:Name="cmbdomainDNSAddressCompatible"
|
||||
Width="150"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding DomainDNSAddressCompatible, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
|
||||
<Grid Margin="{StaticResource Margin4}">
|
||||
<HeaderedContentControl
|
||||
Margin="{StaticResource Margin4}"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Header="HTTP/SOCKS">
|
||||
<TextBox
|
||||
x:Name="txtnormalDNS"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Name="txtnormalDNSCompatible"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderThickness="1"
|
||||
Classes="TextArea"
|
||||
TextWrapping="Wrap"
|
||||
Watermark="HTTP/SOCKS" />
|
||||
</Grid>
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
</HeaderedContentControl>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="{x:Static resx:ResUI.TbSettingsCoreDnsSingbox}">
|
||||
<DockPanel Margin="{StaticResource Margin8}">
|
||||
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
|
||||
<TextBlock Margin="{StaticResource Margin4}" VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkDnsSingboxObjectDoc_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbDnsSingboxObjectDoc}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<Button
|
||||
x:Name="btnImportDefConfig4Singbox"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Content="{x:Static resx:ResUI.TbSettingDnsImportDefConfig}"
|
||||
Cursor="Hand" />
|
||||
</StackPanel>
|
||||
<Grid DockPanel.Dock="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbCustomDNSEnable}" />
|
||||
<ToggleSwitch
|
||||
x:Name="togSBCustomDNSEnableCompatible"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||
<TextBlock Margin="{StaticResource Margin4}" VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkDnsSingboxObjectDoc_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbDnsSingboxObjectDoc}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<Button
|
||||
x:Name="btnImportDefConfig4SingboxCompatible"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Content="{x:Static resx:ResUI.TbSettingDnsImportDefConfig}"
|
||||
Cursor="Hand" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<WrapPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
@@ -125,7 +421,7 @@
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsDomainStrategy4Out}" />
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy4Out"
|
||||
x:Name="cmbdomainStrategy4OutCompatible"
|
||||
Width="150"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
</StackPanel>
|
||||
@@ -135,40 +431,44 @@
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsDomainDNSAddress}" />
|
||||
<ComboBox
|
||||
x:Name="cmbdomainDNSAddress2"
|
||||
<ctrls:AutoCompleteBox
|
||||
x:Name="cmbdomainDNSAddress2Compatible"
|
||||
Width="150"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{Binding DomainDNSAddress2Compatible, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</WrapPanel>
|
||||
|
||||
<Grid Margin="{StaticResource Margin4}" ColumnDefinitions="*,10,*">
|
||||
|
||||
<TextBox
|
||||
x:Name="txtnormalDNS2"
|
||||
<HeaderedContentControl
|
||||
Grid.Column="0"
|
||||
Width="400"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Classes="TextArea"
|
||||
Margin="{StaticResource Margin4}"
|
||||
TextWrapping="Wrap"
|
||||
Watermark="HTTP/SOCKS" />
|
||||
Header="HTTP/SOCKS">
|
||||
<TextBox
|
||||
Name="txtnormalDNS2Compatible"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
</HeaderedContentControl>
|
||||
|
||||
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" />
|
||||
|
||||
<TextBox
|
||||
x:Name="txttunDNS2"
|
||||
<HeaderedContentControl
|
||||
Grid.Column="2"
|
||||
Width="400"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderBrush="Gray"
|
||||
BorderThickness="1"
|
||||
Classes="TextArea"
|
||||
Margin="{StaticResource Margin4}"
|
||||
TextWrapping="Wrap"
|
||||
Watermark="{x:Static resx:ResUI.TbSettingsTunMode}" />
|
||||
Header="{x:Static resx:ResUI.TbSettingsTunMode}">
|
||||
<TextBox
|
||||
Name="txttunDNS2Compatible"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
MinLines="10"
|
||||
TextWrapping="Wrap" />
|
||||
</HeaderedContentControl>
|
||||
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class DNSSettingWindow : ReactiveWindow<DNSSettingViewModel>
|
||||
public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
|
||||
{
|
||||
private static Config _config;
|
||||
|
||||
@@ -17,38 +18,64 @@ public partial class DNSSettingWindow : ReactiveWindow<DNSSettingViewModel>
|
||||
btnCancel.Click += (s, e) => this.Close();
|
||||
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
|
||||
|
||||
Global.DomainStrategy4Freedoms.ForEach(it =>
|
||||
{
|
||||
cmbdomainStrategy4Freedom.Items.Add(it);
|
||||
});
|
||||
Global.SingboxDomainStrategy4Out.ForEach(it =>
|
||||
{
|
||||
cmbdomainStrategy4Out.Items.Add(it);
|
||||
});
|
||||
Global.DomainDNSAddress.ForEach(it =>
|
||||
{
|
||||
cmbdomainDNSAddress.Items.Add(it);
|
||||
});
|
||||
Global.SingboxDomainDNSAddress.ForEach(it =>
|
||||
{
|
||||
cmbdomainDNSAddress2.Items.Add(it);
|
||||
});
|
||||
cmbRayFreedomDNSStrategy.ItemsSource = Global.DomainStrategy4Freedoms;
|
||||
cmbSBDirectDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
||||
cmbSBRemoteDNSStrategy.ItemsSource = Global.SingboxDomainStrategy4Out;
|
||||
cmbDirectDNS.ItemsSource = Global.DomainDirectDNSAddress;
|
||||
cmbSBResolverDNS.ItemsSource = Global.DomainDirectDNSAddress.Concat(new[] { "dhcp://auto,localhost" });
|
||||
cmbRemoteDNS.ItemsSource = Global.DomainRemoteDNSAddress;
|
||||
cmbSBFinalResolverDNS.ItemsSource = Global.DomainPureIPDNSAddress.Concat(new[] { "dhcp://auto,localhost" });
|
||||
cmbDirectExpectedIPs.ItemsSource = Global.ExpectedIPs;
|
||||
|
||||
cmbdomainStrategy4FreedomCompatible.ItemsSource = Global.DomainStrategy4Freedoms;
|
||||
cmbdomainStrategy4OutCompatible.ItemsSource = Global.SingboxDomainStrategy4Out;
|
||||
cmbdomainDNSAddressCompatible.ItemsSource = Global.DomainPureIPDNSAddress;
|
||||
cmbdomainDNSAddress2Compatible.ItemsSource = Global.DomainPureIPDNSAddress;
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
this.Bind(ViewModel, vm => vm.UseSystemHosts, v => v.togUseSystemHosts.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom, v => v.cmbdomainStrategy4Freedom.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DomainDNSAddress, v => v.cmbdomainDNSAddress.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NormalDNS, v => v.txtnormalDNS.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom2, v => v.cmbdomainStrategy4Out.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DomainDNSAddress2, v => v.cmbdomainDNSAddress2.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NormalDNS2, v => v.txtnormalDNS2.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunDNS2, v => v.txttunDNS2.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.AddCommonHosts, v => v.togAddCommonHosts.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.FakeIP, v => v.togFakeIP.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.BlockBindingQuery, v => v.togBlockBindingQuery.IsChecked).DisposeWith(disposables);
|
||||
//this.Bind(ViewModel, vm => vm.DirectDNS, v => v.cmbDirectDNS.Text).DisposeWith(disposables);
|
||||
//this.Bind(ViewModel, vm => vm.RemoteDNS, v => v.cmbRemoteDNS.Text).DisposeWith(disposables);
|
||||
//this.Bind(ViewModel, vm => vm.SingboxOutboundsResolveDNS, v => v.cmbSBResolverDNS.Text).DisposeWith(disposables);
|
||||
//this.Bind(ViewModel, vm => vm.SingboxFinalResolveDNS, v => v.cmbSBFinalResolverDNS.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.RayStrategy4Freedom, v => v.cmbRayFreedomDNSStrategy.SelectedItem).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SingboxStrategy4Direct, v => v.cmbSBDirectDNSStrategy.SelectedItem).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SingboxStrategy4Proxy, v => v.cmbSBRemoteDNSStrategy.SelectedItem).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.Hosts, v => v.txtHosts.Text).DisposeWith(disposables);
|
||||
//this.Bind(ViewModel, vm => vm.DirectExpectedIPs, v => v.cmbDirectExpectedIPs.Text).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).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.Bind(ViewModel, vm => vm.RayCustomDNSEnableCompatible, v => v.togRayCustomDNSEnableCompatible.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SBCustomDNSEnableCompatible, v => v.togSBCustomDNSEnableCompatible.IsChecked).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.UseSystemHostsCompatible, v => v.togUseSystemHostsCompatible.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.DomainStrategy4FreedomCompatible, v => v.cmbdomainStrategy4FreedomCompatible.SelectedItem).DisposeWith(disposables);
|
||||
//this.Bind(ViewModel, vm => vm.DomainDNSAddressCompatible, v => v.cmbdomainDNSAddressCompatible.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NormalDNSCompatible, v => v.txtnormalDNSCompatible.Text).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.DomainStrategy4Freedom2Compatible, v => v.cmbdomainStrategy4OutCompatible.SelectedItem).DisposeWith(disposables);
|
||||
//this.Bind(ViewModel, vm => vm.DomainDNSAddress2Compatible, v => v.cmbdomainDNSAddress2Compatible.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.NormalDNS2Compatible, v => v.txtnormalDNS2Compatible.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.TunDNS2Compatible, v => v.txttunDNS2Compatible.Text).DisposeWith(disposables);
|
||||
|
||||
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4V2rayCompatibleCmd, v => v.btnImportDefConfig4V2rayCompatible).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.ImportDefConfig4SingboxCompatibleCmd, v => v.btnImportDefConfig4SingboxCompatible).DisposeWith(disposables);
|
||||
|
||||
this.WhenAnyValue(
|
||||
x => x.ViewModel.RayCustomDNSEnableCompatible,
|
||||
x => x.ViewModel.SBCustomDNSEnableCompatible,
|
||||
(ray, sb) => ray && sb
|
||||
).BindTo(this.FindControl<TextBlock>("txtBasicDNSSettingsInvalid"), t => t.IsVisible);
|
||||
this.WhenAnyValue(
|
||||
x => x.ViewModel.RayCustomDNSEnableCompatible,
|
||||
x => x.ViewModel.SBCustomDNSEnableCompatible,
|
||||
(ray, sb) => ray && sb
|
||||
).BindTo(this.FindControl<TextBlock>("txtAdvancedDNSSettingsInvalid"), t => t.IsVisible);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@ using System.Text;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Handler;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class GlobalHotkeySettingWindow : ReactiveWindow<GlobalHotkeySettingViewModel>
|
||||
public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingViewModel>
|
||||
{
|
||||
private readonly List<object> _textBoxKeyEventItem = new();
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
<Separator />
|
||||
<MenuItem x:Name="menuAddHysteria2Server" Header="{x:Static resx:ResUI.menuAddHysteria2Server}" />
|
||||
<MenuItem x:Name="menuAddTuicServer" Header="{x:Static resx:ResUI.menuAddTuicServer}" />
|
||||
<MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
|
||||
</MenuItem>
|
||||
|
||||
<MenuItem Padding="8,0">
|
||||
|
||||
@@ -5,18 +5,18 @@ using Avalonia.Controls.ApplicationLifetimes;
|
||||
using Avalonia.Controls.Notifications;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Threading;
|
||||
using DialogHostAvalonia;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
using v2rayN.Desktop.Handler;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
|
||||
public partial class MainWindow : WindowBase<MainWindowViewModel>
|
||||
{
|
||||
private static Config _config;
|
||||
private WindowNotificationManager? _manager;
|
||||
@@ -29,7 +29,7 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
|
||||
InitializeComponent();
|
||||
|
||||
_config = AppHandler.Instance.Config;
|
||||
_manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.BottomRight };
|
||||
_manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.TopRight };
|
||||
|
||||
this.KeyDown += MainWindow_KeyDown;
|
||||
menuSettingsSetUWP.Click += menuSettingsSetUWP_Click;
|
||||
@@ -83,6 +83,7 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
|
||||
this.BindCommand(ViewModel, vm => vm.AddHysteria2ServerCmd, v => v.menuAddHysteria2Server).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddServerViaClipboardCmd, v => v.menuAddServerViaClipboard).DisposeWith(disposables);
|
||||
this.BindCommand(ViewModel, vm => vm.AddServerViaScanCmd, v => v.menuAddServerViaScan).DisposeWith(disposables);
|
||||
@@ -135,26 +136,23 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
|
||||
}
|
||||
});
|
||||
|
||||
this.Title = $"{Utils.GetVersion()}";
|
||||
if (Utils.IsWindows())
|
||||
{
|
||||
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
|
||||
|
||||
ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false);
|
||||
HotkeyHandler.Instance.Init(_config, OnHotkeyHandler);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Utils.IsAdministrator())
|
||||
{
|
||||
this.Title = $"{Utils.GetVersion()} - {ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp}";
|
||||
NoticeHandler.Instance.SendMessageAndEnqueue(ResUI.TbSettingsLinuxSudoPasswordNotSudoRunApp);
|
||||
}
|
||||
this.Title = $"{Utils.GetVersion()}";
|
||||
|
||||
menuRebootAsAdmin.IsVisible = false;
|
||||
menuSettingsSetUWP.IsVisible = false;
|
||||
menuGlobalHotkeySetting.IsVisible = false;
|
||||
}
|
||||
menuAddServerViaScan.IsVisible = false;
|
||||
|
||||
RestoreUI();
|
||||
AddHelpMenuItem();
|
||||
MessageBus.Current.Listen<string>(EMsgCommand.AppExit.ToString()).Subscribe(StorageUI);
|
||||
}
|
||||
@@ -388,7 +386,7 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
|
||||
|
||||
private async void MenuClose_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (await UI.ShowYesNo(this, ResUI.menuExitTips) == ButtonResult.No)
|
||||
if (await UI.ShowYesNo(this, ResUI.menuExitTips) != ButtonResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -436,14 +434,14 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
|
||||
_config.UiItem.ShowInTaskbar = bl;
|
||||
}
|
||||
|
||||
protected override void OnLoaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
base.OnLoaded(sender, e);
|
||||
RestoreUI();
|
||||
}
|
||||
|
||||
private void RestoreUI()
|
||||
{
|
||||
if (_config.UiItem.MainWidth > 0 && _config.UiItem.MainHeight > 0)
|
||||
{
|
||||
Width = _config.UiItem.MainWidth;
|
||||
Height = _config.UiItem.MainHeight;
|
||||
}
|
||||
|
||||
if (_config.UiItem.MainGirdHeight1 > 0 && _config.UiItem.MainGirdHeight2 > 0)
|
||||
{
|
||||
if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Horizontal)
|
||||
@@ -461,18 +459,15 @@ public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
|
||||
|
||||
private void StorageUI(string? n = null)
|
||||
{
|
||||
_config.UiItem.MainWidth = this.Width;
|
||||
_config.UiItem.MainHeight = this.Height;
|
||||
ConfigHandler.SaveWindowSizeItem(_config, GetType().Name, Width, Height);
|
||||
|
||||
if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Horizontal)
|
||||
{
|
||||
_config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain.ColumnDefinitions[0].ActualWidth + 0.1);
|
||||
_config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain.ColumnDefinitions[2].ActualWidth + 0.1);
|
||||
ConfigHandler.SaveMainGirdHeight(_config, gridMain.ColumnDefinitions[0].ActualWidth, gridMain.ColumnDefinitions[2].ActualWidth);
|
||||
}
|
||||
else if (_config.UiItem.MainGirdOrientation == EGirdOrientation.Vertical)
|
||||
{
|
||||
_config.UiItem.MainGirdHeight1 = Math.Ceiling(gridMain1.RowDefinitions[0].ActualHeight + 0.1);
|
||||
_config.UiItem.MainGirdHeight2 = Math.Ceiling(gridMain1.RowDefinitions[2].ActualHeight + 0.1);
|
||||
ConfigHandler.SaveMainGirdHeight(_config, gridMain1.RowDefinitions[0].ActualHeight, gridMain1.RowDefinitions[2].ActualHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,34 +69,36 @@
|
||||
IsChecked="True"
|
||||
Theme="{DynamicResource SimpleToggleSwitch}" />
|
||||
</WrapPanel>
|
||||
<TextBox
|
||||
Name="txtMsg"
|
||||
VerticalAlignment="Stretch"
|
||||
BorderThickness="0"
|
||||
Classes="TextArea"
|
||||
IsReadOnly="True"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="Wrap">
|
||||
<TextBox.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewSelectAll"
|
||||
Click="menuMsgViewSelectAll_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewSelectAll}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewCopy"
|
||||
Click="menuMsgViewCopy_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewCopy}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewCopyAll"
|
||||
Click="menuMsgViewCopyAll_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewCopyAll}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewClear"
|
||||
Click="menuMsgViewClear_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewClear}" />
|
||||
</ContextMenu>
|
||||
</TextBox.ContextMenu>
|
||||
</TextBox>
|
||||
|
||||
<ScrollViewer x:Name="msgScrollViewer" VerticalScrollBarVisibility="Auto">
|
||||
<SelectableTextBlock
|
||||
Name="txtMsg"
|
||||
Margin="{StaticResource Margin8}"
|
||||
VerticalAlignment="Stretch"
|
||||
Classes="TextArea"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="Wrap">
|
||||
<SelectableTextBlock.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewSelectAll"
|
||||
Click="menuMsgViewSelectAll_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewSelectAll}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewCopy"
|
||||
Click="menuMsgViewCopy_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewCopy}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewCopyAll"
|
||||
Click="menuMsgViewCopyAll_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewCopyAll}" />
|
||||
<MenuItem
|
||||
x:Name="menuMsgViewClear"
|
||||
Click="menuMsgViewClear_Click"
|
||||
Header="{x:Static resx:ResUI.menuMsgViewClear}" />
|
||||
</ContextMenu>
|
||||
</SelectableTextBlock.ContextMenu>
|
||||
</SelectableTextBlock>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using Avalonia.Threading;
|
||||
@@ -9,9 +10,12 @@ namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class MsgView : ReactiveUserControl<MsgViewModel>
|
||||
{
|
||||
private readonly ScrollViewer _scrollViewer;
|
||||
|
||||
public MsgView()
|
||||
{
|
||||
InitializeComponent();
|
||||
_scrollViewer = this.FindControl<ScrollViewer>("msgScrollViewer");
|
||||
|
||||
ViewModel = new MsgViewModel(UpdateViewHandler);
|
||||
|
||||
@@ -43,14 +47,14 @@ public partial class MsgView : ReactiveUserControl<MsgViewModel>
|
||||
txtMsg.Text = msg.ToString();
|
||||
if (togScrollToEnd.IsChecked ?? true)
|
||||
{
|
||||
txtMsg.CaretIndex = int.MaxValue;
|
||||
_scrollViewer?.ScrollToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearMsg()
|
||||
{
|
||||
ViewModel?.ClearMsg();
|
||||
txtMsg.Clear();
|
||||
txtMsg.Text = "";
|
||||
}
|
||||
|
||||
private void menuMsgViewSelectAll_Click(object? sender, RoutedEventArgs e)
|
||||
|
||||
@@ -343,7 +343,7 @@
|
||||
<Grid
|
||||
Margin="{StaticResource Margin4}"
|
||||
ColumnDefinitions="Auto,Auto,*"
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
|
||||
|
||||
<TextBlock
|
||||
x:Name="tbAutoRun"
|
||||
@@ -575,9 +575,9 @@
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSubConvert}" />
|
||||
<ctrls:AutoCompleteBox
|
||||
x:Name="cmbSubConvertUrl"
|
||||
Text="{x:Static resx:ResUI.TbSettingsIPAPIUrl}" />
|
||||
<ComboBox
|
||||
x:Name="cmbIPAPIUrl"
|
||||
Grid.Row="20"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
@@ -588,28 +588,41 @@
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSubConvert}" />
|
||||
<ctrls:AutoCompleteBox
|
||||
x:Name="cmbSubConvertUrl"
|
||||
Grid.Row="21"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="22"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsMainGirdOrientation}" />
|
||||
<ComboBox
|
||||
x:Name="cmbMainGirdOrientation"
|
||||
Grid.Row="21"
|
||||
Grid.Row="22"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="22"
|
||||
Grid.Row="23"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsGeoFilesSource}" />
|
||||
<ComboBox
|
||||
x:Name="cmbGetFilesSourceUrl"
|
||||
Grid.Row="22"
|
||||
Grid.Row="23"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<TextBlock
|
||||
Grid.Row="22"
|
||||
Grid.Row="23"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
@@ -617,19 +630,19 @@
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="23"
|
||||
Grid.Row="24"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsSrsFilesSource}" />
|
||||
<ComboBox
|
||||
x:Name="cmbSrsFilesSourceUrl"
|
||||
Grid.Row="23"
|
||||
Grid.Row="24"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<TextBlock
|
||||
Grid.Row="23"
|
||||
Grid.Row="24"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
@@ -637,24 +650,25 @@
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="24"
|
||||
Grid.Row="25"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsRoutingRulesSource}" />
|
||||
<ComboBox
|
||||
x:Name="cmbRoutingRulesSourceUrl"
|
||||
Grid.Row="24"
|
||||
Grid.Row="25"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
<TextBlock
|
||||
Grid.Row="24"
|
||||
Grid.Row="25"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbSettingsChinaUserTip}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel>
|
||||
public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
|
||||
{
|
||||
private static Config _config;
|
||||
|
||||
@@ -19,83 +19,39 @@ public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel
|
||||
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
|
||||
|
||||
clbdestOverride.SelectionChanged += ClbdestOverride_SelectionChanged;
|
||||
Global.destOverrideProtocols.ForEach(it =>
|
||||
{
|
||||
clbdestOverride.Items.Add(it);
|
||||
});
|
||||
clbdestOverride.ItemsSource = Global.destOverrideProtocols;
|
||||
_config.Inbound.First().DestOverride?.ForEach(it =>
|
||||
{
|
||||
clbdestOverride.SelectedItems.Add(it);
|
||||
});
|
||||
Global.IEProxyProtocols.ForEach(it =>
|
||||
{
|
||||
cmbsystemProxyAdvancedProtocol.Items.Add(it);
|
||||
});
|
||||
Global.LogLevels.ForEach(it =>
|
||||
{
|
||||
cmbloglevel.Items.Add(it);
|
||||
});
|
||||
Global.Fingerprints.ForEach(it =>
|
||||
{
|
||||
cmbdefFingerprint.Items.Add(it);
|
||||
});
|
||||
Global.UserAgent.ForEach(it =>
|
||||
{
|
||||
cmbdefUserAgent.Items.Add(it);
|
||||
});
|
||||
Global.SingboxMuxs.ForEach(it =>
|
||||
{
|
||||
cmbmux4SboxProtocol.Items.Add(it);
|
||||
});
|
||||
|
||||
Global.TunMtus.ForEach(it =>
|
||||
{
|
||||
cmbMtu.Items.Add(it);
|
||||
});
|
||||
Global.TunStacks.ForEach(it =>
|
||||
{
|
||||
cmbStack.Items.Add(it);
|
||||
});
|
||||
Global.CoreTypes.ForEach(it =>
|
||||
{
|
||||
cmbCoreType1.Items.Add(it);
|
||||
cmbCoreType2.Items.Add(it);
|
||||
cmbCoreType3.Items.Add(it);
|
||||
cmbCoreType4.Items.Add(it);
|
||||
cmbCoreType5.Items.Add(it);
|
||||
cmbCoreType6.Items.Add(it);
|
||||
cmbCoreType9.Items.Add(it);
|
||||
});
|
||||
cmbsystemProxyAdvancedProtocol.ItemsSource = Global.IEProxyProtocols;
|
||||
cmbloglevel.ItemsSource = Global.LogLevels;
|
||||
cmbdefFingerprint.ItemsSource = Global.Fingerprints;
|
||||
cmbdefUserAgent.ItemsSource = Global.UserAgent;
|
||||
cmbmux4SboxProtocol.ItemsSource = Global.SingboxMuxs;
|
||||
cmbMtu.ItemsSource = Global.TunMtus;
|
||||
cmbStack.ItemsSource = Global.TunStacks;
|
||||
|
||||
for (var i = 2; i <= 8; i++)
|
||||
{
|
||||
cmbMixedConcurrencyCount.Items.Add(i);
|
||||
}
|
||||
for (var i = 2; i <= 6; i++)
|
||||
{
|
||||
cmbSpeedTestTimeout.Items.Add(i * 5);
|
||||
}
|
||||
cmbCoreType1.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreType2.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreType3.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreType4.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreType5.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreType6.ItemsSource = Global.CoreTypes;
|
||||
cmbCoreType9.ItemsSource = Global.CoreTypes;
|
||||
|
||||
cmbMixedConcurrencyCount.ItemsSource = Enumerable.Range(2, 7).ToList();
|
||||
cmbSpeedTestTimeout.ItemsSource = Enumerable.Range(2, 5).Select(i => i * 5).ToList();
|
||||
cmbSpeedTestUrl.ItemsSource = Global.SpeedTestUrls;
|
||||
cmbSpeedPingTestUrl.ItemsSource = Global.SpeedPingTestUrls;
|
||||
cmbSubConvertUrl.ItemsSource = Global.SubConvertUrls;
|
||||
cmbGetFilesSourceUrl.ItemsSource = Global.GeoFilesSources;
|
||||
cmbSrsFilesSourceUrl.ItemsSource = Global.SingboxRulesetSources;
|
||||
cmbRoutingRulesSourceUrl.ItemsSource = Global.RoutingRulesSources;
|
||||
cmbIPAPIUrl.ItemsSource = Global.IPAPIUrls;
|
||||
|
||||
Global.GeoFilesSources.ForEach(it =>
|
||||
{
|
||||
cmbGetFilesSourceUrl.Items.Add(it);
|
||||
});
|
||||
Global.SingboxRulesetSources.ForEach(it =>
|
||||
{
|
||||
cmbSrsFilesSourceUrl.Items.Add(it);
|
||||
});
|
||||
Global.RoutingRulesSources.ForEach(it =>
|
||||
{
|
||||
cmbRoutingRulesSourceUrl.Items.Add(it);
|
||||
});
|
||||
foreach (EGirdOrientation it in Enum.GetValues(typeof(EGirdOrientation)))
|
||||
{
|
||||
cmbMainGirdOrientation.Items.Add(it.ToString());
|
||||
}
|
||||
cmbMainGirdOrientation.ItemsSource = Utils.GetEnumNames<EGirdOrientation>();
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
@@ -143,6 +99,7 @@ public partial class OptionSettingWindow : ReactiveWindow<OptionSettingViewModel
|
||||
this.Bind(ViewModel, vm => vm.GeoFileSourceUrl, v => v.cmbGetFilesSourceUrl.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SrsFileSourceUrl, v => v.cmbSrsFilesSourceUrl.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.RoutingRulesSourceUrl, v => v.cmbRoutingRulesSourceUrl.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.IPAPIUrl, v => v.cmbIPAPIUrl.SelectedValue).DisposeWith(disposables);
|
||||
|
||||
this.Bind(ViewModel, vm => vm.notProxyLocalAddress, v => v.tognotProxyLocalAddress.IsChecked).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.systemProxyAdvancedProtocol, v => v.cmbsystemProxyAdvancedProtocol.SelectedValue).DisposeWith(disposables);
|
||||
|
||||
@@ -138,7 +138,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||
break;
|
||||
|
||||
case EViewAction.ShowYesNo:
|
||||
if (await UI.ShowYesNo(_window, ResUI.RemoveServer) == ButtonResult.No)
|
||||
if (await UI.ShowYesNo(_window, ResUI.RemoveServer) != ButtonResult.Yes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -215,7 +215,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||
await ViewModel.RefreshServersBiz();
|
||||
}
|
||||
|
||||
if (lstProfiles.SelectedIndex > 0)
|
||||
if (lstProfiles.SelectedIndex >= 0)
|
||||
{
|
||||
lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, null);
|
||||
}
|
||||
@@ -345,9 +345,34 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
|
||||
|
||||
private void AutofitColumnWidth()
|
||||
{
|
||||
foreach (var it in lstProfiles.Columns)
|
||||
try
|
||||
{
|
||||
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
||||
//First scroll horizontally to the initial position to avoid the control crash bug
|
||||
if (lstProfiles.SelectedIndex >= 0)
|
||||
{
|
||||
lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, lstProfiles.Columns[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
var model = lstProfiles.ItemsSource.Cast<ProfileItemModel>();
|
||||
if (model.Any())
|
||||
{
|
||||
lstProfiles.ScrollIntoView(model.First(), lstProfiles.Columns[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var it in lstProfiles.Columns)
|
||||
{
|
||||
it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("ProfilesView", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
x:Class="v2rayN.Desktop.Views.RoutingRuleDetailsWindow"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:ctrls="clr-namespace:v2rayN.Desktop.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:resx="clr-namespace:ServiceLib.Resx;assembly=ServiceLib"
|
||||
@@ -22,86 +23,95 @@
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<TextBox
|
||||
x:Name="txtRemarks"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
<ToggleSwitch
|
||||
x:Name="togEnabled"
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
VerticalAlignment="Center" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="outboundTag" />
|
||||
<ComboBox
|
||||
x:Name="cmbOutboundTag"
|
||||
<ctrls:AutoCompleteBox
|
||||
Name="cmbOutboundTag"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
MaxDropDownHeight="1000" />
|
||||
Text="{Binding SelectedSource.OutboundTag, Mode=TwoWay}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbRuleOutboundTagTip}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="port" />
|
||||
<TextBox
|
||||
x:Name="txtPort"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Margin="{StaticResource Margin4}">
|
||||
Text="{x:Static resx:ResUI.TbRuleMatchingTips}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="protocol" />
|
||||
<ListBox
|
||||
x:Name="clbProtocol"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
SelectionMode="Multiple,Toggle"
|
||||
Theme="{DynamicResource CardCheckGroupListBox}" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="2"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkRuleobjectDoc_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbRuleobjectDoc}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Margin="{StaticResource Margin4}"
|
||||
Text="protocol" />
|
||||
<ListBox
|
||||
x:Name="clbProtocol"
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="{StaticResource Margin4}"
|
||||
SelectionMode="Multiple,Toggle"
|
||||
Theme="{DynamicResource CardCheckGroupListBox}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="inboundTag" />
|
||||
<ListBox
|
||||
x:Name="clbInboundTag"
|
||||
@@ -113,35 +123,36 @@
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbRoutingInboundTagTips}" />
|
||||
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="network" />
|
||||
<ComboBox
|
||||
x:Name="cmbNetwork"
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Width="200"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
MaxDropDownHeight="1000" />
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Left"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbRoutingTips}" />
|
||||
</Grid>
|
||||
|
||||
<StackPanel
|
||||
HorizontalAlignment="Right"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Right"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<StackPanel
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class RoutingRuleDetailsWindow : ReactiveWindow<RoutingRuleDetailsViewModel>
|
||||
public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsViewModel>
|
||||
{
|
||||
public RoutingRuleDetailsWindow()
|
||||
{
|
||||
@@ -23,21 +23,11 @@ public partial class RoutingRuleDetailsWindow : ReactiveWindow<RoutingRuleDetail
|
||||
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
|
||||
|
||||
ViewModel = new RoutingRuleDetailsViewModel(rulesItem, UpdateViewHandler);
|
||||
cmbOutboundTag.Items.Add(Global.ProxyTag);
|
||||
cmbOutboundTag.Items.Add(Global.DirectTag);
|
||||
cmbOutboundTag.Items.Add(Global.BlockTag);
|
||||
Global.RuleProtocols.ForEach(it =>
|
||||
{
|
||||
clbProtocol.Items.Add(it);
|
||||
});
|
||||
Global.InboundTags.ForEach(it =>
|
||||
{
|
||||
clbInboundTag.Items.Add(it);
|
||||
});
|
||||
Global.RuleNetworks.ForEach(it =>
|
||||
{
|
||||
cmbNetwork.Items.Add(it);
|
||||
});
|
||||
|
||||
cmbOutboundTag.ItemsSource = Global.OutboundTags;
|
||||
clbProtocol.ItemsSource = Global.RuleProtocols;
|
||||
clbInboundTag.ItemsSource = Global.InboundTags;
|
||||
cmbNetwork.ItemsSource = Global.RuleNetworks;
|
||||
|
||||
if (!rulesItem.Id.IsNullOrEmpty())
|
||||
{
|
||||
@@ -54,7 +44,7 @@ public partial class RoutingRuleDetailsWindow : ReactiveWindow<RoutingRuleDetail
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Remarks, v => v.txtRemarks.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.OutboundTag, v => v.cmbOutboundTag.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Port, v => v.txtPort.Text).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
|
||||
this.Bind(ViewModel, vm => vm.SelectedSource.Enabled, v => v.togEnabled.IsChecked).DisposeWith(disposables);
|
||||
@@ -80,7 +70,7 @@ public partial class RoutingRuleDetailsWindow : ReactiveWindow<RoutingRuleDetail
|
||||
|
||||
private void Window_Loaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
cmbOutboundTag.Focus();
|
||||
txtRemarks.Focus();
|
||||
}
|
||||
|
||||
private void ClbProtocol_SelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
|
||||
@@ -2,14 +2,14 @@ using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class RoutingRuleSettingWindow : ReactiveWindow<RoutingRuleSettingViewModel>
|
||||
public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingViewModel>
|
||||
{
|
||||
public RoutingRuleSettingWindow()
|
||||
{
|
||||
@@ -30,15 +30,9 @@ public partial class RoutingRuleSettingWindow : ReactiveWindow<RoutingRuleSettin
|
||||
btnBrowseCustomRulesetPath4Singbox.Click += btnBrowseCustomRulesetPath4Singbox_ClickAsync;
|
||||
|
||||
ViewModel = new RoutingRuleSettingViewModel(routingItem, UpdateViewHandler);
|
||||
Global.DomainStrategies.ForEach(it =>
|
||||
{
|
||||
cmbdomainStrategy.Items.Add(it);
|
||||
});
|
||||
cmbdomainStrategy.Items.Add(string.Empty);
|
||||
Global.DomainStrategies4Singbox.ForEach(it =>
|
||||
{
|
||||
cmbdomainStrategy4Singbox.Items.Add(it);
|
||||
});
|
||||
|
||||
cmbdomainStrategy.ItemsSource = Global.DomainStrategies.AppendEmpty();
|
||||
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
@@ -80,14 +74,14 @@ public partial class RoutingRuleSettingWindow : ReactiveWindow<RoutingRuleSettin
|
||||
break;
|
||||
|
||||
case EViewAction.ShowYesNo:
|
||||
if (await UI.ShowYesNo(this, ResUI.RemoveServer) == ButtonResult.No)
|
||||
if (await UI.ShowYesNo(this, ResUI.RemoveServer) != ButtonResult.Yes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case EViewAction.AddBatchRoutingRulesYesNo:
|
||||
if (await UI.ShowYesNo(this, ResUI.AddBatchRoutingRulesYesNo) == ButtonResult.No)
|
||||
if (await UI.ShowYesNo(this, ResUI.AddBatchRoutingRulesYesNo) != ButtonResult.Yes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -24,36 +24,13 @@
|
||||
<MenuItem x:Name="menuRoutingAdvancedAdd2" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedImportRules2" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
|
||||
</Menu>
|
||||
|
||||
<TextBlock VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox x:Name="cmbdomainStrategy" Width="110" />
|
||||
<Separator />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Static resx:ResUI.TbdomainMatcher}" />
|
||||
<ComboBox x:Name="cmbdomainMatcher" Width="60" />
|
||||
<Separator />
|
||||
<TextBlock VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy4Singbox_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox x:Name="cmbdomainStrategy4Singbox" Width="100" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
HorizontalAlignment="Right"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Right"
|
||||
DockPanel.Dock="Bottom"
|
||||
Orientation="Horizontal">
|
||||
<StackPanel
|
||||
Width="600"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbRoutingTips}" />
|
||||
</StackPanel>
|
||||
<Button
|
||||
x:Name="btnSave"
|
||||
Width="100"
|
||||
@@ -69,58 +46,112 @@
|
||||
IsCancel="True" />
|
||||
</StackPanel>
|
||||
|
||||
<DockPanel>
|
||||
<TabControl x:Name="tabAdvanced">
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.TbRoutingTabRuleList}">
|
||||
<DataGrid
|
||||
x:Name="lstRoutings"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserResizeColumns="True"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="Column"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding RoutingItems}">
|
||||
<DataGrid.KeyBindings>
|
||||
<KeyBinding Command="{Binding RoutingAdvancedSetDefaultCmd}" Gesture="Enter" />
|
||||
</DataGrid.KeyBindings>
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem x:Name="menuRoutingAdvancedAdd" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedRemove" Header="{x:Static resx:ResUI.menuRoutingAdvancedRemove}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedSelectAll" Header="{x:Static resx:ResUI.menuSelectAll}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedSetDefault" Header="{x:Static resx:ResUI.menuRoutingAdvancedSetDefault}" />
|
||||
<Separator />
|
||||
<MenuItem x:Name="menuRoutingAdvancedImportRules" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
<Grid
|
||||
Margin="{StaticResource Margin4}"
|
||||
ColumnDefinitions="Auto,Auto"
|
||||
DockPanel.Dock="Top"
|
||||
RowDefinitions="Auto,Auto,Auto">
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridCheckBoxColumn Width="40" Binding="{Binding IsActive}" />
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding RuleNum}"
|
||||
Header="{x:Static resx:ResUI.LvCount}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding Sort}"
|
||||
Header="{x:Static resx:ResUI.LvSort}" />
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Url}"
|
||||
Header="{x:Static resx:ResUI.LvUrl}" />
|
||||
<DataGridTextColumn
|
||||
Width="300"
|
||||
Binding="{Binding CustomIcon}"
|
||||
Header="{x:Static resx:ResUI.LvCustomIcon}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Static resx:ResUI.TbdomainMatcher}" />
|
||||
<ComboBox
|
||||
x:Name="cmbdomainMatcher"
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}" />
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Margin="{StaticResource Margin4}"
|
||||
VerticalAlignment="Center">
|
||||
<HyperlinkButton Classes="WithIcon" Click="linkdomainStrategy4Singbox_Click">
|
||||
<TextBlock Text="{x:Static resx:ResUI.TbdomainStrategy4Singbox}" />
|
||||
</HyperlinkButton>
|
||||
</TextBlock>
|
||||
<ComboBox
|
||||
x:Name="cmbdomainStrategy4Singbox"
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Width="300"
|
||||
Margin="{StaticResource Margin4}"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" />
|
||||
</Grid>
|
||||
|
||||
<TabControl x:Name="tabAdvanced">
|
||||
<TabItem HorizontalAlignment="Left" Header="{x:Static resx:ResUI.TbRoutingTabRuleList}">
|
||||
<DataGrid
|
||||
x:Name="lstRoutings"
|
||||
AutoGenerateColumns="False"
|
||||
BorderThickness="1"
|
||||
CanUserResizeColumns="True"
|
||||
GridLinesVisibility="All"
|
||||
HeadersVisibility="Column"
|
||||
IsReadOnly="True"
|
||||
ItemsSource="{Binding RoutingItems}">
|
||||
<DataGrid.KeyBindings>
|
||||
<KeyBinding Command="{Binding RoutingAdvancedSetDefaultCmd}" Gesture="Enter" />
|
||||
</DataGrid.KeyBindings>
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem x:Name="menuRoutingAdvancedAdd" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedRemove" Header="{x:Static resx:ResUI.menuRoutingAdvancedRemove}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedSelectAll" Header="{x:Static resx:ResUI.menuSelectAll}" />
|
||||
<MenuItem x:Name="menuRoutingAdvancedSetDefault" Header="{x:Static resx:ResUI.menuRoutingAdvancedSetDefault}" />
|
||||
<Separator />
|
||||
<MenuItem x:Name="menuRoutingAdvancedImportRules" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridCheckBoxColumn Width="40" Binding="{Binding IsActive}" />
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Remarks}"
|
||||
Header="{x:Static resx:ResUI.LvRemarks}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding RuleNum}"
|
||||
Header="{x:Static resx:ResUI.LvCount}" />
|
||||
<DataGridTextColumn
|
||||
Width="60"
|
||||
Binding="{Binding Sort}"
|
||||
Header="{x:Static resx:ResUI.LvSort}" />
|
||||
<DataGridTextColumn
|
||||
Width="*"
|
||||
Binding="{Binding Url}"
|
||||
Header="{x:Static resx:ResUI.LvUrl}" />
|
||||
<DataGridTextColumn
|
||||
Width="300"
|
||||
Binding="{Binding CustomIcon}"
|
||||
Header="{x:Static resx:ResUI.LvCustomIcon}" />
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</DockPanel>
|
||||
</Window>
|
||||
|
||||
@@ -2,14 +2,14 @@ using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class RoutingSettingWindow : ReactiveWindow<RoutingSettingViewModel>
|
||||
public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
|
||||
{
|
||||
private bool _manualClose = false;
|
||||
|
||||
@@ -26,18 +26,9 @@ public partial class RoutingSettingWindow : ReactiveWindow<RoutingSettingViewMod
|
||||
|
||||
ViewModel = new RoutingSettingViewModel(UpdateViewHandler);
|
||||
|
||||
Global.DomainStrategies.ForEach(it =>
|
||||
{
|
||||
cmbdomainStrategy.Items.Add(it);
|
||||
});
|
||||
Global.DomainMatchers.ForEach(it =>
|
||||
{
|
||||
cmbdomainMatcher.Items.Add(it);
|
||||
});
|
||||
Global.DomainStrategies4Singbox.ForEach(it =>
|
||||
{
|
||||
cmbdomainStrategy4Singbox.Items.Add(it);
|
||||
});
|
||||
cmbdomainStrategy.ItemsSource = Global.DomainStrategies;
|
||||
cmbdomainMatcher.ItemsSource = Global.DomainMatchers;
|
||||
cmbdomainStrategy4Singbox.ItemsSource = Global.DomainStrategies4Singbox;
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
@@ -68,7 +59,7 @@ public partial class RoutingSettingWindow : ReactiveWindow<RoutingSettingViewMod
|
||||
break;
|
||||
|
||||
case EViewAction.ShowYesNo:
|
||||
if (await UI.ShowYesNo(this, ResUI.RemoveRules) == ButtonResult.No)
|
||||
if (await UI.ShowYesNo(this, ResUI.RemoveRules) != ButtonResult.Yes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -126,7 +117,7 @@ public partial class RoutingSettingWindow : ReactiveWindow<RoutingSettingViewMod
|
||||
|
||||
private void linkdomainStrategy4Singbox_Click(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/shared/listen/#domain_strategy");
|
||||
ProcUtils.ProcessStart("https://sing-box.sagernet.org/zh/configuration/route/rule_action/#strategy");
|
||||
}
|
||||
|
||||
private void btnCancel_Click(object? sender, RoutedEventArgs e)
|
||||
|
||||
@@ -85,7 +85,6 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
||||
|
||||
case EViewAction.PasswordInput:
|
||||
return await PasswordInputAsync();
|
||||
break;
|
||||
}
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
@@ -105,13 +104,15 @@ public partial class StatusBarView : ReactiveUserControl<StatusBarViewModel>
|
||||
{
|
||||
var dialog = new SudoPasswordInputView();
|
||||
var obj = await DialogHost.Show(dialog);
|
||||
if (obj == null)
|
||||
|
||||
var password = obj?.ToString();
|
||||
if (password.IsNullOrEmpty())
|
||||
{
|
||||
togEnableTun.IsChecked = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
AppHandler.Instance.LinuxSudoPwd = obj.ToString() ?? string.Empty;
|
||||
AppHandler.Instance.LinuxSudoPwd = password;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System.Reactive.Disposables;
|
||||
using Avalonia;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class SubEditWindow : ReactiveWindow<SubEditViewModel>
|
||||
public partial class SubEditWindow : WindowBase<SubEditViewModel>
|
||||
{
|
||||
public SubEditWindow()
|
||||
{
|
||||
@@ -22,10 +22,7 @@ public partial class SubEditWindow : ReactiveWindow<SubEditViewModel>
|
||||
|
||||
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
|
||||
|
||||
Global.SubConvertTargets.ForEach(it =>
|
||||
{
|
||||
cmbConvertTarget.Items.Add(it);
|
||||
});
|
||||
cmbConvertTarget.ItemsSource = Global.SubConvertTargets;
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using System.Reactive.Disposables;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.ReactiveUI;
|
||||
using DialogHostAvalonia;
|
||||
using DynamicData;
|
||||
using MsBox.Avalonia.Enums;
|
||||
using ReactiveUI;
|
||||
using v2rayN.Desktop.Base;
|
||||
using v2rayN.Desktop.Common;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
|
||||
public partial class SubSettingWindow : ReactiveWindow<SubSettingViewModel>
|
||||
public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
|
||||
{
|
||||
private bool _manualClose = false;
|
||||
|
||||
@@ -45,7 +45,7 @@ public partial class SubSettingWindow : ReactiveWindow<SubSettingViewModel>
|
||||
break;
|
||||
|
||||
case EViewAction.ShowYesNo:
|
||||
if (await UI.ShowYesNo(this, ResUI.RemoveServer) == ButtonResult.No)
|
||||
if (await UI.ShowYesNo(this, ResUI.RemoveServer) != ButtonResult.Yes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Threading;
|
||||
using CliWrap.Buffered;
|
||||
using DialogHostAvalonia;
|
||||
|
||||
namespace v2rayN.Desktop.Views;
|
||||
@@ -12,22 +13,70 @@ public partial class SudoPasswordInputView : UserControl
|
||||
|
||||
this.Loaded += (s, e) => txtPassword.Focus();
|
||||
|
||||
btnSave.Click += (_, _) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(txtPassword.Text))
|
||||
{
|
||||
txtPassword.Focus();
|
||||
return;
|
||||
}
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
DialogHost.Close(null, txtPassword.Text);
|
||||
});
|
||||
};
|
||||
btnSave.Click += async (_, _) => await SavePasswordAsync();
|
||||
|
||||
btnCancel.Click += (_, _) =>
|
||||
{
|
||||
DialogHost.Close(null);
|
||||
};
|
||||
}
|
||||
|
||||
private async Task SavePasswordAsync()
|
||||
{
|
||||
if (txtPassword.Text.IsNullOrEmpty())
|
||||
{
|
||||
txtPassword.Focus();
|
||||
return;
|
||||
}
|
||||
|
||||
var password = txtPassword.Text;
|
||||
btnSave.IsEnabled = false;
|
||||
|
||||
try
|
||||
{
|
||||
// Verify if the password is correct
|
||||
if (await CheckSudoPasswordAsync(password))
|
||||
{
|
||||
// Password verification successful, return password and close dialog
|
||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
DialogHost.Close(null, password);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Password verification failed, display error and let user try again
|
||||
NoticeHandler.Instance.Enqueue(ResUI.SudoIncorrectPasswordTip);
|
||||
txtPassword.Focus();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("SudoPassword", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
btnSave.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> CheckSudoPasswordAsync(string password)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Use sudo echo command to verify password
|
||||
var arg = new List<string>() { "-c", "sudo -S echo SUDO_CHECK" };
|
||||
var result = await CliWrap.Cli.Wrap(Global.LinuxBash)
|
||||
.WithArguments(arg)
|
||||
.WithStandardInputPipe(CliWrap.PipeSource.FromString(password))
|
||||
.ExecuteBufferedAsync();
|
||||
|
||||
return result.ExitCode == 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logging.SaveLog("CheckSudoPassword", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,20 +16,9 @@ public partial class ThemeSettingView : ReactiveUserControl<ThemeSettingViewMode
|
||||
InitializeComponent();
|
||||
ViewModel = new ThemeSettingViewModel();
|
||||
|
||||
foreach (ETheme it in Enum.GetValues(typeof(ETheme)))
|
||||
{
|
||||
cmbCurrentTheme.Items.Add(it.ToString());
|
||||
}
|
||||
|
||||
for (int i = Global.MinFontSize; i <= Global.MinFontSize + 10; i++)
|
||||
{
|
||||
cmbCurrentFontSize.Items.Add(i);
|
||||
}
|
||||
|
||||
Global.Languages.ForEach(it =>
|
||||
{
|
||||
cmbCurrentLanguage.Items.Add(it);
|
||||
});
|
||||
cmbCurrentTheme.ItemsSource = Utils.GetEnumNames<ETheme>();
|
||||
cmbCurrentFontSize.ItemsSource = Enumerable.Range(Global.MinFontSize, 11).ToList();
|
||||
cmbCurrentLanguage.ItemsSource = Global.Languages;
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
|
||||
@@ -215,7 +215,7 @@
|
||||
<Setter Property="Background" Value="{DynamicResource MaterialDesignPaper}" />
|
||||
<Setter Property="TextElement.FontFamily" Value="{x:Static conv:MaterialDesignFonts.MyFont}" />
|
||||
<Setter Property="FontFamily" Value="{x:Static conv:MaterialDesignFonts.MyFont}" />
|
||||
<Setter Property="ResizeMode" Value="NoResize" />
|
||||
<Setter Property="ResizeMode" Value="CanResize" />
|
||||
</Style>
|
||||
<Style
|
||||
x:Key="ViewGlobal"
|
||||
|
||||
@@ -56,7 +56,7 @@ public partial class App : Application
|
||||
{
|
||||
if (e.ExceptionObject != null)
|
||||
{
|
||||
Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject!);
|
||||
Logging.SaveLog("CurrentDomain_UnhandledException", (Exception)e.ExceptionObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
42
v2rayN/v2rayN/Base/WindowBase.cs
Normal file
42
v2rayN/v2rayN/Base/WindowBase.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Windows;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace v2rayN.Base;
|
||||
|
||||
public class WindowBase<TViewModel> : ReactiveWindow<TViewModel> where TViewModel : class
|
||||
{
|
||||
public WindowBase()
|
||||
{
|
||||
Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
protected virtual void OnLoaded(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sizeItem = ConfigHandler.GetWindowSizeItem(AppHandler.Instance.Config, GetType().Name);
|
||||
if (sizeItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Width = Math.Min(sizeItem.Width, SystemParameters.WorkArea.Width);
|
||||
Height = Math.Min(sizeItem.Height, SystemParameters.WorkArea.Height);
|
||||
|
||||
Left = SystemParameters.WorkArea.Left + ((SystemParameters.WorkArea.Width - Width) / 2);
|
||||
Top = SystemParameters.WorkArea.Top + ((SystemParameters.WorkArea.Height - Height) / 2);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
|
||||
try
|
||||
{
|
||||
ConfigHandler.SaveWindowSizeItem(AppHandler.Instance.Config, GetType().Name, Width, Height);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
<reactiveui:ReactiveWindow
|
||||
<base:WindowBase
|
||||
x:Class="v2rayN.Views.AddServer2Window"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:base="clr-namespace:v2rayN.Base"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
@@ -198,4 +199,4 @@
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</DockPanel>
|
||||
</reactiveui:ReactiveWindow>
|
||||
</base:WindowBase>
|
||||
|
||||
@@ -14,13 +14,7 @@ public partial class AddServer2Window
|
||||
this.Loaded += Window_Loaded;
|
||||
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
|
||||
|
||||
foreach (ECoreType it in Enum.GetValues(typeof(ECoreType)))
|
||||
{
|
||||
if (it == ECoreType.v2rayN)
|
||||
continue;
|
||||
cmbCoreType.Items.Add(it.ToString());
|
||||
}
|
||||
cmbCoreType.Items.Add(string.Empty);
|
||||
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
|
||||
|
||||
this.WhenActivated(disposables =>
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user